kdbus: the driver, original and non-working 08/251408/4
authorAdrian Szyndela <adrian.s@samsung.com>
Fri, 9 Sep 2016 11:35:49 +0000 (13:35 +0200)
committerSeung-Woo Kim <sw0312.kim@samsung.com>
Fri, 15 Jan 2021 10:10:59 +0000 (10:10 +0000)
[based on commit 216823ac83c0ab89348e2ed6f66179f53626586e]

Introduce the kdbus driver again. This driver worked previously
on kernel 4.1. This is the source code taken from the working driver.
It is non-working and disabled. It is a base for porting.

The documentation is moved from Documentation to ipc/kdbus/Documentation.

The references to kdbus source code are commented out or removed in Makefiles.

Original authors of the files are those from commit 216823ac83c0ab8934.

Cherry-picked from 4.14 commit 970070c4f68f113284f86cf7b6fbd23d6b35b511.

Change-Id: Id60af5faf794fc4ae7122976621076f1021f6c38
Signed-off-by: Adrian Szyndela <adrian.s@samsung.com>
Signed-off-by: Hyotaek Shim <hyotaek.shim@samsung.com>
Signed-off-by: Ɓukasz Stelmach <l.stelmach@samsung.com>
90 files changed:
MAINTAINERS
include/uapi/linux/kdbus.h [new file with mode: 0644]
include/uapi/linux/magic.h
init/Kconfig
ipc/Makefile
ipc/kdbus/Documentation/kdbus.bus.xml [new file with mode: 0644]
ipc/kdbus/Documentation/kdbus.connection.xml [new file with mode: 0644]
ipc/kdbus/Documentation/kdbus.endpoint.xml [new file with mode: 0644]
ipc/kdbus/Documentation/kdbus.fs.xml [new file with mode: 0644]
ipc/kdbus/Documentation/kdbus.item.xml [new file with mode: 0644]
ipc/kdbus/Documentation/kdbus.match.xml [new file with mode: 0644]
ipc/kdbus/Documentation/kdbus.message.xml [new file with mode: 0644]
ipc/kdbus/Documentation/kdbus.name.xml [new file with mode: 0644]
ipc/kdbus/Documentation/kdbus.policy.xml [new file with mode: 0644]
ipc/kdbus/Documentation/kdbus.pool.xml [new file with mode: 0644]
ipc/kdbus/Documentation/kdbus.xml [new file with mode: 0644]
ipc/kdbus/Documentation/stylesheet.xsl [new file with mode: 0644]
ipc/kdbus/Makefile [new file with mode: 0644]
ipc/kdbus/bus.c [new file with mode: 0644]
ipc/kdbus/bus.h [new file with mode: 0644]
ipc/kdbus/connection.c [new file with mode: 0644]
ipc/kdbus/connection.h [new file with mode: 0644]
ipc/kdbus/domain.c [new file with mode: 0644]
ipc/kdbus/domain.h [new file with mode: 0644]
ipc/kdbus/endpoint.c [new file with mode: 0644]
ipc/kdbus/endpoint.h [new file with mode: 0644]
ipc/kdbus/fs.c [new file with mode: 0644]
ipc/kdbus/fs.h [new file with mode: 0644]
ipc/kdbus/handle.c [new file with mode: 0644]
ipc/kdbus/handle.h [new file with mode: 0644]
ipc/kdbus/item.c [new file with mode: 0644]
ipc/kdbus/item.h [new file with mode: 0644]
ipc/kdbus/limits.h [new file with mode: 0644]
ipc/kdbus/main.c [new file with mode: 0644]
ipc/kdbus/match.c [new file with mode: 0644]
ipc/kdbus/match.h [new file with mode: 0644]
ipc/kdbus/message.c [new file with mode: 0644]
ipc/kdbus/message.h [new file with mode: 0644]
ipc/kdbus/metadata.c [new file with mode: 0644]
ipc/kdbus/metadata.h [new file with mode: 0644]
ipc/kdbus/names.c [new file with mode: 0644]
ipc/kdbus/names.h [new file with mode: 0644]
ipc/kdbus/node.c [new file with mode: 0644]
ipc/kdbus/node.h [new file with mode: 0644]
ipc/kdbus/notify.c [new file with mode: 0644]
ipc/kdbus/notify.h [new file with mode: 0644]
ipc/kdbus/policy.c [new file with mode: 0644]
ipc/kdbus/policy.h [new file with mode: 0644]
ipc/kdbus/pool.c [new file with mode: 0644]
ipc/kdbus/pool.h [new file with mode: 0644]
ipc/kdbus/queue.c [new file with mode: 0644]
ipc/kdbus/queue.h [new file with mode: 0644]
ipc/kdbus/reply.c [new file with mode: 0644]
ipc/kdbus/reply.h [new file with mode: 0644]
ipc/kdbus/util.c [new file with mode: 0644]
ipc/kdbus/util.h [new file with mode: 0644]
samples/Kconfig
samples/kdbus/.gitignore [new file with mode: 0644]
samples/kdbus/Makefile [new file with mode: 0644]
samples/kdbus/kdbus-api.h [new file with mode: 0644]
samples/kdbus/kdbus-workers.c [new file with mode: 0644]
tools/testing/selftests/Makefile
tools/testing/selftests/kdbus/Makefile [new file with mode: 0644]
tools/testing/selftests/kdbus/kdbus-enum.c [new file with mode: 0644]
tools/testing/selftests/kdbus/kdbus-enum.h [new file with mode: 0644]
tools/testing/selftests/kdbus/kdbus-test.c [new file with mode: 0644]
tools/testing/selftests/kdbus/kdbus-test.h [new file with mode: 0644]
tools/testing/selftests/kdbus/kdbus-util.c [new file with mode: 0644]
tools/testing/selftests/kdbus/kdbus-util.h [new file with mode: 0644]
tools/testing/selftests/kdbus/test-activator.c [new file with mode: 0644]
tools/testing/selftests/kdbus/test-attach-flags.c [new file with mode: 0644]
tools/testing/selftests/kdbus/test-benchmark.c [new file with mode: 0644]
tools/testing/selftests/kdbus/test-bus.c [new file with mode: 0644]
tools/testing/selftests/kdbus/test-chat.c [new file with mode: 0644]
tools/testing/selftests/kdbus/test-connection.c [new file with mode: 0644]
tools/testing/selftests/kdbus/test-daemon.c [new file with mode: 0644]
tools/testing/selftests/kdbus/test-endpoint.c [new file with mode: 0644]
tools/testing/selftests/kdbus/test-fd.c [new file with mode: 0644]
tools/testing/selftests/kdbus/test-free.c [new file with mode: 0644]
tools/testing/selftests/kdbus/test-match.c [new file with mode: 0644]
tools/testing/selftests/kdbus/test-message.c [new file with mode: 0644]
tools/testing/selftests/kdbus/test-metadata-ns.c [new file with mode: 0644]
tools/testing/selftests/kdbus/test-monitor.c [new file with mode: 0644]
tools/testing/selftests/kdbus/test-names.c [new file with mode: 0644]
tools/testing/selftests/kdbus/test-policy-ns.c [new file with mode: 0644]
tools/testing/selftests/kdbus/test-policy-priv.c [new file with mode: 0644]
tools/testing/selftests/kdbus/test-policy.c [new file with mode: 0644]
tools/testing/selftests/kdbus/test-send.c [new file with mode: 0644]
tools/testing/selftests/kdbus/test-sync.c [new file with mode: 0644]
tools/testing/selftests/kdbus/test-timeout.c [new file with mode: 0644]

index 6521ad7..a38ec1d 100644 (file)
@@ -8894,6 +8894,19 @@ F:       Documentation/kbuild/kconfig*
 F:     scripts/kconfig/
 F:     scripts/Kconfig.include
 
+KDBUS
+M:     Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+M:     Daniel Mack <daniel@zonque.org>
+M:     David Herrmann <dh.herrmann@googlemail.com>
+M:     Djalal Harouni <tixxdz@opendz.org>
+L:     linux-kernel@vger.kernel.org
+S:     Maintained
+F:     ipc/kdbus/*
+F:     samples/kdbus/*
+F:     Documentation/kdbus/*
+F:     include/uapi/linux/kdbus.h
+F:     tools/testing/selftests/kdbus/
+
 KDUMP
 M:     Dave Young <dyoung@redhat.com>
 M:     Baoquan He <bhe@redhat.com>
diff --git a/include/uapi/linux/kdbus.h b/include/uapi/linux/kdbus.h
new file mode 100644 (file)
index 0000000..4fc44cb
--- /dev/null
@@ -0,0 +1,984 @@
+/*
+ * 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 _UAPI_KDBUS_H_
+#define _UAPI_KDBUS_H_
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+#define KDBUS_IOCTL_MAGIC              0x95
+#define KDBUS_SRC_ID_KERNEL            (0)
+#define KDBUS_DST_ID_NAME              (0)
+#define KDBUS_MATCH_ID_ANY             (~0ULL)
+#define KDBUS_DST_ID_BROADCAST         (~0ULL)
+#define KDBUS_FLAG_NEGOTIATE           (1ULL << 63)
+
+/**
+ * struct kdbus_notify_id_change - name registry change message
+ * @id:                        New or former owner of the name
+ * @flags:             flags field from KDBUS_HELLO_*
+ *
+ * Sent from kernel to userspace when the owner or activator of
+ * a well-known name changes.
+ *
+ * Attached to:
+ *   KDBUS_ITEM_ID_ADD
+ *   KDBUS_ITEM_ID_REMOVE
+ */
+struct kdbus_notify_id_change {
+       __u64 id;
+       __u64 flags;
+} __attribute__((__aligned__(8)));
+
+/**
+ * struct kdbus_notify_name_change - name registry change message
+ * @old_id:            ID and flags of former owner of a name
+ * @new_id:            ID and flags of new owner of a name
+ * @name:              Well-known name
+ *
+ * Sent from kernel to userspace when the owner or activator of
+ * a well-known name changes.
+ *
+ * Attached to:
+ *   KDBUS_ITEM_NAME_ADD
+ *   KDBUS_ITEM_NAME_REMOVE
+ *   KDBUS_ITEM_NAME_CHANGE
+ */
+struct kdbus_notify_name_change {
+       struct kdbus_notify_id_change old_id;
+       struct kdbus_notify_id_change new_id;
+       char name[0];
+} __attribute__((__aligned__(8)));
+
+/**
+ * struct kdbus_creds - process credentials
+ * @uid:               User ID
+ * @euid:              Effective UID
+ * @suid:              Saved UID
+ * @fsuid:             Filesystem UID
+ * @gid:               Group ID
+ * @egid:              Effective GID
+ * @sgid:              Saved GID
+ * @fsgid:             Filesystem GID
+ *
+ * Attached to:
+ *   KDBUS_ITEM_CREDS
+ */
+struct kdbus_creds {
+       __u64 uid;
+       __u64 euid;
+       __u64 suid;
+       __u64 fsuid;
+       __u64 gid;
+       __u64 egid;
+       __u64 sgid;
+       __u64 fsgid;
+} __attribute__((__aligned__(8)));
+
+/**
+ * struct kdbus_pids - process identifiers
+ * @pid:               Process ID
+ * @tid:               Thread ID
+ * @ppid:              Parent process ID
+ *
+ * The PID and TID of a process.
+ *
+ * Attached to:
+ *   KDBUS_ITEM_PIDS
+ */
+struct kdbus_pids {
+       __u64 pid;
+       __u64 tid;
+       __u64 ppid;
+} __attribute__((__aligned__(8)));
+
+/**
+ * struct kdbus_caps - process capabilities
+ * @last_cap:  Highest currently known capability bit
+ * @caps:      Variable number of 32-bit capabilities flags
+ *
+ * Contains a variable number of 32-bit capabilities flags.
+ *
+ * Attached to:
+ *   KDBUS_ITEM_CAPS
+ */
+struct kdbus_caps {
+       __u32 last_cap;
+       __u32 caps[0];
+} __attribute__((__aligned__(8)));
+
+/**
+ * struct kdbus_audit - audit information
+ * @sessionid:         The audit session ID
+ * @loginuid:          The audit login uid
+ *
+ * Attached to:
+ *   KDBUS_ITEM_AUDIT
+ */
+struct kdbus_audit {
+       __u32 sessionid;
+       __u32 loginuid;
+} __attribute__((__aligned__(8)));
+
+/**
+ * struct kdbus_timestamp
+ * @seqnum:            Global per-domain message sequence number
+ * @monotonic_ns:      Monotonic timestamp, in nanoseconds
+ * @realtime_ns:       Realtime timestamp, in nanoseconds
+ *
+ * Attached to:
+ *   KDBUS_ITEM_TIMESTAMP
+ */
+struct kdbus_timestamp {
+       __u64 seqnum;
+       __u64 monotonic_ns;
+       __u64 realtime_ns;
+} __attribute__((__aligned__(8)));
+
+/**
+ * struct kdbus_vec - I/O vector for kdbus payload items
+ * @size:              The size of the vector
+ * @address:           Memory address of data buffer
+ * @offset:            Offset in the in-message payload memory,
+ *                     relative to the message head
+ *
+ * Attached to:
+ *   KDBUS_ITEM_PAYLOAD_VEC, KDBUS_ITEM_PAYLOAD_OFF
+ */
+struct kdbus_vec {
+       __u64 size;
+       union {
+               __u64 address;
+               __u64 offset;
+       };
+} __attribute__((__aligned__(8)));
+
+/**
+ * struct kdbus_bloom_parameter - bus-wide bloom parameters
+ * @size:              Size of the bit field in bytes (m / 8)
+ * @n_hash:            Number of hash functions used (k)
+ */
+struct kdbus_bloom_parameter {
+       __u64 size;
+       __u64 n_hash;
+} __attribute__((__aligned__(8)));
+
+/**
+ * struct kdbus_bloom_filter - bloom filter containing n elements
+ * @generation:                Generation of the element set in the filter
+ * @data:              Bit field, multiple of 8 bytes
+ */
+struct kdbus_bloom_filter {
+       __u64 generation;
+       __u64 data[0];
+} __attribute__((__aligned__(8)));
+
+/**
+ * struct kdbus_memfd - a kdbus memfd
+ * @start:             The offset into the memfd where the segment starts
+ * @size:              The size of the memfd segment
+ * @fd:                        The file descriptor number
+ * @__pad:             Padding to ensure proper alignment and size
+ *
+ * Attached to:
+ *   KDBUS_ITEM_PAYLOAD_MEMFD
+ */
+struct kdbus_memfd {
+       __u64 start;
+       __u64 size;
+       int fd;
+       __u32 __pad;
+} __attribute__((__aligned__(8)));
+
+/**
+ * struct kdbus_name - a registered well-known name with its flags
+ * @flags:             Flags from KDBUS_NAME_*
+ * @name:              Well-known name
+ *
+ * Attached to:
+ *   KDBUS_ITEM_OWNED_NAME
+ */
+struct kdbus_name {
+       __u64 flags;
+       char name[0];
+} __attribute__((__aligned__(8)));
+
+/**
+ * enum kdbus_policy_access_type - permissions of a policy record
+ * @_KDBUS_POLICY_ACCESS_NULL: Uninitialized/invalid
+ * @KDBUS_POLICY_ACCESS_USER:  Grant access to a uid
+ * @KDBUS_POLICY_ACCESS_GROUP: Grant access to gid
+ * @KDBUS_POLICY_ACCESS_WORLD: World-accessible
+ */
+enum kdbus_policy_access_type {
+       _KDBUS_POLICY_ACCESS_NULL,
+       KDBUS_POLICY_ACCESS_USER,
+       KDBUS_POLICY_ACCESS_GROUP,
+       KDBUS_POLICY_ACCESS_WORLD,
+};
+
+/**
+ * enum kdbus_policy_access_flags - mode flags
+ * @KDBUS_POLICY_OWN:          Allow to own a well-known name
+ *                             Implies KDBUS_POLICY_TALK and KDBUS_POLICY_SEE
+ * @KDBUS_POLICY_TALK:         Allow communication to a well-known name
+ *                             Implies KDBUS_POLICY_SEE
+ * @KDBUS_POLICY_SEE:          Allow to see a well-known name
+ */
+enum kdbus_policy_type {
+       KDBUS_POLICY_SEE        = 0,
+       KDBUS_POLICY_TALK,
+       KDBUS_POLICY_OWN,
+};
+
+/**
+ * struct kdbus_policy_access - policy access item
+ * @type:              One of KDBUS_POLICY_ACCESS_* types
+ * @access:            Access to grant
+ * @id:                        For KDBUS_POLICY_ACCESS_USER, the uid
+ *                     For KDBUS_POLICY_ACCESS_GROUP, the gid
+ */
+struct kdbus_policy_access {
+       __u64 type;     /* USER, GROUP, WORLD */
+       __u64 access;   /* OWN, TALK, SEE */
+       __u64 id;       /* uid, gid, 0 */
+} __attribute__((__aligned__(8)));
+
+/**
+ * enum kdbus_attach_flags - flags for metadata attachments
+ * @KDBUS_ATTACH_TIMESTAMP:            Timestamp
+ * @KDBUS_ATTACH_CREDS:                        Credentials
+ * @KDBUS_ATTACH_PIDS:                 PIDs
+ * @KDBUS_ATTACH_AUXGROUPS:            Auxiliary groups
+ * @KDBUS_ATTACH_NAMES:                        Well-known names
+ * @KDBUS_ATTACH_TID_COMM:             The "comm" process identifier of the TID
+ * @KDBUS_ATTACH_PID_COMM:             The "comm" process identifier of the PID
+ * @KDBUS_ATTACH_EXE:                  The path of the executable
+ * @KDBUS_ATTACH_CMDLINE:              The process command line
+ * @KDBUS_ATTACH_CGROUP:               The croup membership
+ * @KDBUS_ATTACH_CAPS:                 The process capabilities
+ * @KDBUS_ATTACH_SECLABEL:             The security label
+ * @KDBUS_ATTACH_AUDIT:                        The audit IDs
+ * @KDBUS_ATTACH_CONN_DESCRIPTION:     The human-readable connection name
+ * @_KDBUS_ATTACH_ALL:                 All of the above
+ * @_KDBUS_ATTACH_ANY:                 Wildcard match to enable any kind of
+ *                                     metatdata.
+ */
+enum kdbus_attach_flags {
+       KDBUS_ATTACH_TIMESTAMP          =  1ULL <<  0,
+       KDBUS_ATTACH_CREDS              =  1ULL <<  1,
+       KDBUS_ATTACH_PIDS               =  1ULL <<  2,
+       KDBUS_ATTACH_AUXGROUPS          =  1ULL <<  3,
+       KDBUS_ATTACH_NAMES              =  1ULL <<  4,
+       KDBUS_ATTACH_TID_COMM           =  1ULL <<  5,
+       KDBUS_ATTACH_PID_COMM           =  1ULL <<  6,
+       KDBUS_ATTACH_EXE                =  1ULL <<  7,
+       KDBUS_ATTACH_CMDLINE            =  1ULL <<  8,
+       KDBUS_ATTACH_CGROUP             =  1ULL <<  9,
+       KDBUS_ATTACH_CAPS               =  1ULL << 10,
+       KDBUS_ATTACH_SECLABEL           =  1ULL << 11,
+       KDBUS_ATTACH_AUDIT              =  1ULL << 12,
+       KDBUS_ATTACH_CONN_DESCRIPTION   =  1ULL << 13,
+       _KDBUS_ATTACH_ALL               =  (1ULL << 14) - 1,
+       _KDBUS_ATTACH_ANY               =  ~0ULL
+};
+
+/**
+ * enum kdbus_item_type - item types to chain data in a list
+ * @_KDBUS_ITEM_NULL:                  Uninitialized/invalid
+ * @_KDBUS_ITEM_USER_BASE:             Start of user items
+ * @KDBUS_ITEM_NEGOTIATE:              Negotiate supported items
+ * @KDBUS_ITEM_PAYLOAD_VEC:            Vector to data
+ * @KDBUS_ITEM_PAYLOAD_OFF:            Data at returned offset to message head
+ * @KDBUS_ITEM_PAYLOAD_MEMFD:          Data as sealed memfd
+ * @KDBUS_ITEM_FDS:                    Attached file descriptors
+ * @KDBUS_ITEM_CANCEL_FD:              FD used to cancel a synchronous
+ *                                     operation by writing to it from
+ *                                     userspace
+ * @KDBUS_ITEM_BLOOM_PARAMETER:                Bus-wide bloom parameters, used with
+ *                                     KDBUS_CMD_BUS_MAKE, carries a
+ *                                     struct kdbus_bloom_parameter
+ * @KDBUS_ITEM_BLOOM_FILTER:           Bloom filter carried with a message,
+ *                                     used to match against a bloom mask of a
+ *                                     connection, carries a struct
+ *                                     kdbus_bloom_filter
+ * @KDBUS_ITEM_BLOOM_MASK:             Bloom mask used to match against a
+ *                                     message'sbloom filter
+ * @KDBUS_ITEM_DST_NAME:               Destination's well-known name
+ * @KDBUS_ITEM_MAKE_NAME:              Name of domain, bus, endpoint
+ * @KDBUS_ITEM_ATTACH_FLAGS_SEND:      Attach-flags, used for updating which
+ *                                     metadata a connection opts in to send
+ * @KDBUS_ITEM_ATTACH_FLAGS_RECV:      Attach-flags, used for updating which
+ *                                     metadata a connection requests to
+ *                                     receive for each reeceived message
+ * @KDBUS_ITEM_ID:                     Connection ID
+ * @KDBUS_ITEM_NAME:                   Well-know name with flags
+ * @_KDBUS_ITEM_ATTACH_BASE:           Start of metadata attach items
+ * @KDBUS_ITEM_TIMESTAMP:              Timestamp
+ * @KDBUS_ITEM_CREDS:                  Process credentials
+ * @KDBUS_ITEM_PIDS:                   Process identifiers
+ * @KDBUS_ITEM_AUXGROUPS:              Auxiliary process groups
+ * @KDBUS_ITEM_OWNED_NAME:             A name owned by the associated
+ *                                     connection
+ * @KDBUS_ITEM_TID_COMM:               Thread ID "comm" identifier
+ *                                     (Don't trust this, see below.)
+ * @KDBUS_ITEM_PID_COMM:               Process ID "comm" identifier
+ *                                     (Don't trust this, see below.)
+ * @KDBUS_ITEM_EXE:                    The path of the executable
+ *                                     (Don't trust this, see below.)
+ * @KDBUS_ITEM_CMDLINE:                        The process command line
+ *                                     (Don't trust this, see below.)
+ * @KDBUS_ITEM_CGROUP:                 The croup membership
+ * @KDBUS_ITEM_CAPS:                   The process capabilities
+ * @KDBUS_ITEM_SECLABEL:               The security label
+ * @KDBUS_ITEM_AUDIT:                  The audit IDs
+ * @KDBUS_ITEM_CONN_DESCRIPTION:       The connection's human-readable name
+ *                                     (debugging)
+ * @_KDBUS_ITEM_POLICY_BASE:           Start of policy items
+ * @KDBUS_ITEM_POLICY_ACCESS:          Policy access block
+ * @_KDBUS_ITEM_KERNEL_BASE:           Start of kernel-generated message items
+ * @KDBUS_ITEM_NAME_ADD:               Notification in kdbus_notify_name_change
+ * @KDBUS_ITEM_NAME_REMOVE:            Notification in kdbus_notify_name_change
+ * @KDBUS_ITEM_NAME_CHANGE:            Notification in kdbus_notify_name_change
+ * @KDBUS_ITEM_ID_ADD:                 Notification in kdbus_notify_id_change
+ * @KDBUS_ITEM_ID_REMOVE:              Notification in kdbus_notify_id_change
+ * @KDBUS_ITEM_REPLY_TIMEOUT:          Timeout has been reached
+ * @KDBUS_ITEM_REPLY_DEAD:             Destination died
+ *
+ * N.B: The process and thread COMM fields, as well as the CMDLINE and
+ * EXE fields may be altered by unprivileged processes und should
+ * hence *not* used for security decisions. Peers should make use of
+ * these items only for informational purposes, such as generating log
+ * records.
+ */
+enum kdbus_item_type {
+       _KDBUS_ITEM_NULL,
+       _KDBUS_ITEM_USER_BASE,
+       KDBUS_ITEM_NEGOTIATE    = _KDBUS_ITEM_USER_BASE,
+       KDBUS_ITEM_PAYLOAD_VEC,
+       KDBUS_ITEM_PAYLOAD_OFF,
+       KDBUS_ITEM_PAYLOAD_MEMFD,
+       KDBUS_ITEM_FDS,
+       KDBUS_ITEM_CANCEL_FD,
+       KDBUS_ITEM_BLOOM_PARAMETER,
+       KDBUS_ITEM_BLOOM_FILTER,
+       KDBUS_ITEM_BLOOM_MASK,
+       KDBUS_ITEM_DST_NAME,
+       KDBUS_ITEM_MAKE_NAME,
+       KDBUS_ITEM_ATTACH_FLAGS_SEND,
+       KDBUS_ITEM_ATTACH_FLAGS_RECV,
+       KDBUS_ITEM_ID,
+       KDBUS_ITEM_NAME,
+       KDBUS_ITEM_DST_ID,
+
+       /* keep these item types in sync with KDBUS_ATTACH_* flags */
+       _KDBUS_ITEM_ATTACH_BASE = 0x1000,
+       KDBUS_ITEM_TIMESTAMP    = _KDBUS_ITEM_ATTACH_BASE,
+       KDBUS_ITEM_CREDS,
+       KDBUS_ITEM_PIDS,
+       KDBUS_ITEM_AUXGROUPS,
+       KDBUS_ITEM_OWNED_NAME,
+       KDBUS_ITEM_TID_COMM,
+       KDBUS_ITEM_PID_COMM,
+       KDBUS_ITEM_EXE,
+       KDBUS_ITEM_CMDLINE,
+       KDBUS_ITEM_CGROUP,
+       KDBUS_ITEM_CAPS,
+       KDBUS_ITEM_SECLABEL,
+       KDBUS_ITEM_AUDIT,
+       KDBUS_ITEM_CONN_DESCRIPTION,
+
+       _KDBUS_ITEM_POLICY_BASE = 0x2000,
+       KDBUS_ITEM_POLICY_ACCESS = _KDBUS_ITEM_POLICY_BASE,
+
+       _KDBUS_ITEM_KERNEL_BASE = 0x8000,
+       KDBUS_ITEM_NAME_ADD     = _KDBUS_ITEM_KERNEL_BASE,
+       KDBUS_ITEM_NAME_REMOVE,
+       KDBUS_ITEM_NAME_CHANGE,
+       KDBUS_ITEM_ID_ADD,
+       KDBUS_ITEM_ID_REMOVE,
+       KDBUS_ITEM_REPLY_TIMEOUT,
+       KDBUS_ITEM_REPLY_DEAD,
+};
+
+/**
+ * struct kdbus_item - chain of data blocks
+ * @size:              Overall data record size
+ * @type:              Kdbus_item type of data
+ * @data:              Generic bytes
+ * @data32:            Generic 32 bit array
+ * @data64:            Generic 64 bit array
+ * @str:               Generic string
+ * @id:                        Connection ID
+ * @vec:               KDBUS_ITEM_PAYLOAD_VEC
+ * @creds:             KDBUS_ITEM_CREDS
+ * @audit:             KDBUS_ITEM_AUDIT
+ * @timestamp:         KDBUS_ITEM_TIMESTAMP
+ * @name:              KDBUS_ITEM_NAME
+ * @bloom_parameter:   KDBUS_ITEM_BLOOM_PARAMETER
+ * @bloom_filter:      KDBUS_ITEM_BLOOM_FILTER
+ * @memfd:             KDBUS_ITEM_PAYLOAD_MEMFD
+ * @name_change:       KDBUS_ITEM_NAME_ADD
+ *                     KDBUS_ITEM_NAME_REMOVE
+ *                     KDBUS_ITEM_NAME_CHANGE
+ * @id_change:         KDBUS_ITEM_ID_ADD
+ *                     KDBUS_ITEM_ID_REMOVE
+ * @policy:            KDBUS_ITEM_POLICY_ACCESS
+ */
+struct kdbus_item {
+       __u64 size;
+       __u64 type;
+       union {
+               __u8 data[0];
+               __u32 data32[0];
+               __u64 data64[0];
+               char str[0];
+
+               __u64 id;
+               struct kdbus_vec vec;
+               struct kdbus_creds creds;
+               struct kdbus_pids pids;
+               struct kdbus_audit audit;
+               struct kdbus_caps caps;
+               struct kdbus_timestamp timestamp;
+               struct kdbus_name name;
+               struct kdbus_bloom_parameter bloom_parameter;
+               struct kdbus_bloom_filter bloom_filter;
+               struct kdbus_memfd memfd;
+               int fds[0];
+               struct kdbus_notify_name_change name_change;
+               struct kdbus_notify_id_change id_change;
+               struct kdbus_policy_access policy_access;
+       };
+} __attribute__((__aligned__(8)));
+
+/**
+ * enum kdbus_msg_flags - type of message
+ * @KDBUS_MSG_EXPECT_REPLY:    Expect a reply message, used for
+ *                             method calls. The userspace-supplied
+ *                             cookie identifies the message and the
+ *                             respective reply carries the cookie
+ *                             in cookie_reply
+ * @KDBUS_MSG_NO_AUTO_START:   Do not start a service if the addressed
+ *                             name is not currently active. This flag is
+ *                             not looked at by the kernel but only
+ *                             serves as hint for userspace implementations.
+ * @KDBUS_MSG_SIGNAL:          Treat this message as signal
+ */
+enum kdbus_msg_flags {
+       KDBUS_MSG_EXPECT_REPLY  = 1ULL << 0,
+       KDBUS_MSG_NO_AUTO_START = 1ULL << 1,
+       KDBUS_MSG_SIGNAL        = 1ULL << 2,
+};
+
+/**
+ * enum kdbus_payload_type - type of payload carried by message
+ * @KDBUS_PAYLOAD_KERNEL:      Kernel-generated simple message
+ * @KDBUS_PAYLOAD_DBUS:                D-Bus marshalling "DBusDBus"
+ *
+ * Any payload-type is accepted. Common types will get added here once
+ * established.
+ */
+enum kdbus_payload_type {
+       KDBUS_PAYLOAD_KERNEL,
+       KDBUS_PAYLOAD_DBUS      = 0x4442757344427573ULL,
+};
+
+/**
+ * struct kdbus_msg - the representation of a kdbus message
+ * @size:              Total size of the message
+ * @flags:             Message flags (KDBUS_MSG_*), userspace â†’ kernel
+ * @priority:          Message queue priority value
+ * @dst_id:            64-bit ID of the destination connection
+ * @src_id:            64-bit ID of the source connection
+ * @payload_type:      Payload type (KDBUS_PAYLOAD_*)
+ * @cookie:            Userspace-supplied cookie, for the connection
+ *                     to identify its messages
+ * @timeout_ns:                The time to wait for a message reply from the peer.
+ *                     If there is no reply, and the send command is
+ *                     executed asynchronously, a kernel-generated message
+ *                     with an attached KDBUS_ITEM_REPLY_TIMEOUT item
+ *                     is sent to @src_id. For synchronously executed send
+ *                     command, the value denotes the maximum time the call
+ *                     blocks to wait for a reply. The timeout is expected in
+ *                     nanoseconds and as absolute CLOCK_MONOTONIC value.
+ * @cookie_reply:      A reply to the requesting message with the same
+ *                     cookie. The requesting connection can match its
+ *                     request and the reply with this value
+ * @items:             A list of kdbus_items containing the message payload
+ */
+struct kdbus_msg {
+       __u64 size;
+       __u64 flags;
+       __s64 priority;
+       __u64 dst_id;
+       __u64 src_id;
+       __u64 payload_type;
+       __u64 cookie;
+       union {
+               __u64 timeout_ns;
+               __u64 cookie_reply;
+       };
+       struct kdbus_item items[0];
+} __attribute__((__aligned__(8)));
+
+/**
+ * struct kdbus_msg_info - returned message container
+ * @offset:            Offset of kdbus_msg slice in pool
+ * @msg_size:          Copy of the kdbus_msg.size field
+ * @return_flags:      Command return flags, kernel â†’ userspace
+ */
+struct kdbus_msg_info {
+       __u64 offset;
+       __u64 msg_size;
+       __u64 return_flags;
+} __attribute__((__aligned__(8)));
+
+/**
+ * enum kdbus_send_flags - flags for sending messages
+ * @KDBUS_SEND_SYNC_REPLY:     Wait for destination connection to
+ *                             reply to this message. The
+ *                             KDBUS_CMD_SEND ioctl() will block
+ *                             until the reply is received, and
+ *                             reply in struct kdbus_cmd_send will
+ *                             yield the offset in the sender's pool
+ *                             where the reply can be found.
+ *                             This flag is only valid if
+ *                             @KDBUS_MSG_EXPECT_REPLY is set as well.
+ */
+enum kdbus_send_flags {
+       KDBUS_SEND_SYNC_REPLY           = 1ULL << 0,
+};
+
+/**
+ * struct kdbus_cmd_send - send message
+ * @size:              Overall size of this structure
+ * @flags:             Flags to change send behavior (KDBUS_SEND_*)
+ * @return_flags:      Command return flags, kernel â†’ userspace
+ * @msg_address:       Storage address of the kdbus_msg to send
+ * @reply:             Storage for message reply if KDBUS_SEND_SYNC_REPLY
+ *                     was given
+ * @items:             Additional items for this command
+ */
+struct kdbus_cmd_send {
+       __u64 size;
+       __u64 flags;
+       __u64 return_flags;
+       __u64 msg_address;
+       struct kdbus_msg_info reply;
+       struct kdbus_item items[0];
+} __attribute__((__aligned__(8)));
+
+/**
+ * enum kdbus_recv_flags - flags for de-queuing messages
+ * @KDBUS_RECV_PEEK:           Return the next queued message without
+ *                             actually de-queuing it, and without installing
+ *                             any file descriptors or other resources. It is
+ *                             usually used to determine the activating
+ *                             connection of a bus name.
+ * @KDBUS_RECV_DROP:           Drop and free the next queued message and all
+ *                             its resources without actually receiving it.
+ * @KDBUS_RECV_USE_PRIORITY:   Only de-queue messages with the specified or
+ *                             higher priority (lowest values); if not set,
+ *                             the priority value is ignored.
+ */
+enum kdbus_recv_flags {
+       KDBUS_RECV_PEEK         = 1ULL <<  0,
+       KDBUS_RECV_DROP         = 1ULL <<  1,
+       KDBUS_RECV_USE_PRIORITY = 1ULL <<  2,
+};
+
+/**
+ * enum kdbus_recv_return_flags - return flags for message receive commands
+ * @KDBUS_RECV_RETURN_INCOMPLETE_FDS:  One or more file descriptors could not
+ *                                     be installed. These descriptors in
+ *                                     KDBUS_ITEM_FDS will carry the value -1.
+ * @KDBUS_RECV_RETURN_DROPPED_MSGS:    There have been dropped messages since
+ *                                     the last time a message was received.
+ *                                     The 'dropped_msgs' counter contains the
+ *                                     number of messages dropped pool
+ *                                     overflows or other missed broadcasts.
+ */
+enum kdbus_recv_return_flags {
+       KDBUS_RECV_RETURN_INCOMPLETE_FDS        = 1ULL <<  0,
+       KDBUS_RECV_RETURN_DROPPED_MSGS          = 1ULL <<  1,
+};
+
+/**
+ * struct kdbus_cmd_recv - struct to de-queue a buffered message
+ * @size:              Overall size of this object
+ * @flags:             KDBUS_RECV_* flags, userspace â†’ kernel
+ * @return_flags:      Command return flags, kernel â†’ userspace
+ * @priority:          Minimum priority of the messages to de-queue. Lowest
+ *                     values have the highest priority.
+ * @dropped_msgs:      In case there were any dropped messages since the last
+ *                     time a message was received, this will be set to the
+ *                     number of lost messages and
+ *                     KDBUS_RECV_RETURN_DROPPED_MSGS will be set in
+ *                     'return_flags'. This can only happen if the ioctl
+ *                     returns 0 or EAGAIN.
+ * @msg:               Return storage for received message.
+ * @items:             Additional items for this command.
+ *
+ * This struct is used with the KDBUS_CMD_RECV ioctl.
+ */
+struct kdbus_cmd_recv {
+       __u64 size;
+       __u64 flags;
+       __u64 return_flags;
+       __s64 priority;
+       __u64 dropped_msgs;
+       struct kdbus_msg_info msg;
+       struct kdbus_item items[0];
+} __attribute__((__aligned__(8)));
+
+/**
+ * struct kdbus_cmd_free - struct to free a slice of memory in the pool
+ * @size:              Overall size of this structure
+ * @flags:             Flags for the free command, userspace â†’ kernel
+ * @return_flags:      Command return flags, kernel â†’ userspace
+ * @offset:            The offset of the memory slice, as returned by other
+ *                     ioctls
+ * @items:             Additional items to modify the behavior
+ *
+ * This struct is used with the KDBUS_CMD_FREE ioctl.
+ */
+struct kdbus_cmd_free {
+       __u64 size;
+       __u64 flags;
+       __u64 return_flags;
+       __u64 offset;
+       struct kdbus_item items[0];
+} __attribute__((__aligned__(8)));
+
+/**
+ * enum kdbus_hello_flags - flags for struct kdbus_cmd_hello
+ * @KDBUS_HELLO_ACCEPT_FD:     The connection allows the reception of
+ *                             any passed file descriptors
+ * @KDBUS_HELLO_ACTIVATOR:     Special-purpose connection which registers
+ *                             a well-know name for a process to be started
+ *                             when traffic arrives
+ * @KDBUS_HELLO_POLICY_HOLDER: Special-purpose connection which registers
+ *                             policy entries for a name. The provided name
+ *                             is not activated and not registered with the
+ *                             name database, it only allows unprivileged
+ *                             connections to acquire a name, talk or discover
+ *                             a service
+ * @KDBUS_HELLO_MONITOR:       Special-purpose connection to monitor
+ *                             bus traffic
+ */
+enum kdbus_hello_flags {
+       KDBUS_HELLO_ACCEPT_FD           =  1ULL <<  0,
+       KDBUS_HELLO_ACTIVATOR           =  1ULL <<  1,
+       KDBUS_HELLO_POLICY_HOLDER       =  1ULL <<  2,
+       KDBUS_HELLO_MONITOR             =  1ULL <<  3,
+};
+
+/**
+ * struct kdbus_cmd_hello - struct to say hello to kdbus
+ * @size:              The total size of the structure
+ * @flags:             Connection flags (KDBUS_HELLO_*), userspace â†’ kernel
+ * @return_flags:      Command return flags, kernel â†’ userspace
+ * @attach_flags_send: Mask of metadata to attach to each message sent
+ *                     off by this connection (KDBUS_ATTACH_*)
+ * @attach_flags_recv: Mask of metadata to attach to each message receieved
+ *                     by the new connection (KDBUS_ATTACH_*)
+ * @bus_flags:         The flags field copied verbatim from the original
+ *                     KDBUS_CMD_BUS_MAKE ioctl. It's intended to be useful
+ *                     to do negotiation of features of the payload that is
+ *                     transferred (kernel â†’ userspace)
+ * @id:                        The ID of this connection (kernel â†’ userspace)
+ * @pool_size:         Size of the connection's buffer where the received
+ *                     messages are placed
+ * @offset:            Pool offset where items are returned to report
+ *                     additional information about the bus and the newly
+ *                     created connection.
+ * @items_size:                Size of buffer returned in the pool slice at @offset.
+ * @id128:             Unique 128-bit ID of the bus (kernel â†’ userspace)
+ * @items:             A list of items
+ *
+ * This struct is used with the KDBUS_CMD_HELLO ioctl.
+ */
+struct kdbus_cmd_hello {
+       __u64 size;
+       __u64 flags;
+       __u64 return_flags;
+       __u64 attach_flags_send;
+       __u64 attach_flags_recv;
+       __u64 bus_flags;
+       __u64 id;
+       __u64 pool_size;
+       __u64 offset;
+       __u64 items_size;
+       __u8 id128[16];
+       struct kdbus_item items[0];
+} __attribute__((__aligned__(8)));
+
+/**
+ * struct kdbus_info - connection information
+ * @size:              total size of the struct
+ * @id:                        64bit object ID
+ * @flags:             object creation flags
+ * @items:             list of items
+ *
+ * Note that the user is responsible for freeing the allocated memory with
+ * the KDBUS_CMD_FREE ioctl.
+ */
+struct kdbus_info {
+       __u64 size;
+       __u64 id;
+       __u64 flags;
+       struct kdbus_item items[0];
+} __attribute__((__aligned__(8)));
+
+/**
+ * enum kdbus_list_flags - what to include into the returned list
+ * @KDBUS_LIST_UNIQUE:         active connections
+ * @KDBUS_LIST_ACTIVATORS:     activator connections
+ * @KDBUS_LIST_NAMES:          known well-known names
+ * @KDBUS_LIST_QUEUED:         queued-up names
+ */
+enum kdbus_list_flags {
+       KDBUS_LIST_UNIQUE               = 1ULL <<  0,
+       KDBUS_LIST_NAMES                = 1ULL <<  1,
+       KDBUS_LIST_ACTIVATORS           = 1ULL <<  2,
+       KDBUS_LIST_QUEUED               = 1ULL <<  3,
+};
+
+/**
+ * struct kdbus_cmd_list - list connections
+ * @size:              overall size of this object
+ * @flags:             flags for the query (KDBUS_LIST_*), userspace â†’ kernel
+ * @return_flags:      command return flags, kernel â†’ userspace
+ * @offset:            Offset in the caller's pool buffer where an array of
+ *                     kdbus_info objects is stored.
+ *                     The user must use KDBUS_CMD_FREE to free the
+ *                     allocated memory.
+ * @list_size:         size of returned list in bytes
+ * @items:             Items for the command. Reserved for future use.
+ *
+ * This structure is used with the KDBUS_CMD_LIST ioctl.
+ */
+struct kdbus_cmd_list {
+       __u64 size;
+       __u64 flags;
+       __u64 return_flags;
+       __u64 offset;
+       __u64 list_size;
+       struct kdbus_item items[0];
+} __attribute__((__aligned__(8)));
+
+/**
+ * struct kdbus_cmd_info - struct used for KDBUS_CMD_CONN_INFO ioctl
+ * @size:              The total size of the struct
+ * @flags:             Flags for this ioctl, userspace â†’ kernel
+ * @return_flags:      Command return flags, kernel â†’ userspace
+ * @id:                        The 64-bit ID of the connection. If set to zero, passing
+ *                     @name is required. kdbus will look up the name to
+ *                     determine the ID in this case.
+ * @attach_flags:      Set of attach flags to specify the set of information
+ *                     to receive, userspace â†’ kernel
+ * @offset:            Returned offset in the caller's pool buffer where the
+ *                     kdbus_info struct result is stored. The user must
+ *                     use KDBUS_CMD_FREE to free the allocated memory.
+ * @info_size:         Output buffer to report size of data at @offset.
+ * @items:             The optional item list, containing the
+ *                     well-known name to look up as a KDBUS_ITEM_NAME.
+ *                     Only needed in case @id is zero.
+ *
+ * On success, the KDBUS_CMD_CONN_INFO ioctl will return 0 and @offset will
+ * tell the user the offset in the connection pool buffer at which to find the
+ * result in a struct kdbus_info.
+ */
+struct kdbus_cmd_info {
+       __u64 size;
+       __u64 flags;
+       __u64 return_flags;
+       __u64 id;
+       __u64 attach_flags;
+       __u64 offset;
+       __u64 info_size;
+       struct kdbus_item items[0];
+} __attribute__((__aligned__(8)));
+
+/**
+ * enum kdbus_cmd_match_flags - flags to control the KDBUS_CMD_MATCH_ADD ioctl
+ * @KDBUS_MATCH_REPLACE:       If entries with the supplied cookie already
+ *                             exists, remove them before installing the new
+ *                             matches.
+ */
+enum kdbus_cmd_match_flags {
+       KDBUS_MATCH_REPLACE     = 1ULL <<  0,
+};
+
+/**
+ * struct kdbus_cmd_match - struct to add or remove matches
+ * @size:              The total size of the struct
+ * @flags:             Flags for match command (KDBUS_MATCH_*),
+ *                     userspace â†’ kernel
+ * @return_flags:      Command return flags, kernel â†’ userspace
+ * @cookie:            Userspace supplied cookie. When removing, the cookie
+ *                     identifies the match to remove
+ * @items:             A list of items for additional information
+ *
+ * This structure is used with the KDBUS_CMD_MATCH_ADD and
+ * KDBUS_CMD_MATCH_REMOVE ioctl.
+ */
+struct kdbus_cmd_match {
+       __u64 size;
+       __u64 flags;
+       __u64 return_flags;
+       __u64 cookie;
+       struct kdbus_item items[0];
+} __attribute__((__aligned__(8)));
+
+/**
+ * enum kdbus_make_flags - Flags for KDBUS_CMD_{BUS,ENDPOINT}_MAKE
+ * @KDBUS_MAKE_ACCESS_GROUP:   Make the bus or endpoint node group-accessible
+ * @KDBUS_MAKE_ACCESS_WORLD:   Make the bus or endpoint node world-accessible
+ */
+enum kdbus_make_flags {
+       KDBUS_MAKE_ACCESS_GROUP         = 1ULL <<  0,
+       KDBUS_MAKE_ACCESS_WORLD         = 1ULL <<  1,
+};
+
+/**
+ * enum kdbus_name_flags - flags for KDBUS_CMD_NAME_ACQUIRE
+ * @KDBUS_NAME_REPLACE_EXISTING:       Try to replace name of other connections
+ * @KDBUS_NAME_ALLOW_REPLACEMENT:      Allow the replacement of the name
+ * @KDBUS_NAME_QUEUE:                  Name should be queued if busy
+ * @KDBUS_NAME_IN_QUEUE:               Name is queued
+ * @KDBUS_NAME_ACTIVATOR:              Name is owned by a activator connection
+ * @KDBUS_NAME_PRIMARY:                        Primary owner of the name
+ * @KDBUS_NAME_ACQUIRED:               Name was acquired/queued _now_
+ */
+enum kdbus_name_flags {
+       KDBUS_NAME_REPLACE_EXISTING     = 1ULL <<  0,
+       KDBUS_NAME_ALLOW_REPLACEMENT    = 1ULL <<  1,
+       KDBUS_NAME_QUEUE                = 1ULL <<  2,
+       KDBUS_NAME_IN_QUEUE             = 1ULL <<  3,
+       KDBUS_NAME_ACTIVATOR            = 1ULL <<  4,
+       KDBUS_NAME_PRIMARY              = 1ULL <<  5,
+       KDBUS_NAME_ACQUIRED             = 1ULL <<  6,
+};
+
+/**
+ * struct kdbus_cmd - generic ioctl payload
+ * @size:              Overall size of this structure
+ * @flags:             Flags for this ioctl, userspace â†’ kernel
+ * @return_flags:      Ioctl return flags, kernel â†’ userspace
+ * @items:             Additional items to modify the behavior
+ *
+ * This is a generic ioctl payload object. It's used by all ioctls that only
+ * take flags and items as input.
+ */
+struct kdbus_cmd {
+       __u64 size;
+       __u64 flags;
+       __u64 return_flags;
+       struct kdbus_item items[0];
+} __attribute__((__aligned__(8)));
+
+/**
+ * Ioctl API
+ *
+ * KDBUS_CMD_BUS_MAKE:         After opening the "control" node, this command
+ *                             creates a new bus with the specified
+ *                             name. The bus is immediately shut down and
+ *                             cleaned up when the opened file descriptor is
+ *                             closed.
+ *
+ * KDBUS_CMD_ENDPOINT_MAKE:    Creates a new named special endpoint to talk to
+ *                             the bus. Such endpoints usually carry a more
+ *                             restrictive policy and grant restricted access
+ *                             to specific applications.
+ * KDBUS_CMD_ENDPOINT_UPDATE:  Update the properties of a custom enpoint. Used
+ *                             to update the policy.
+ *
+ * KDBUS_CMD_HELLO:            By opening the bus node, a connection is
+ *                             created. After a HELLO the opened connection
+ *                             becomes an active peer on the bus.
+ * KDBUS_CMD_UPDATE:           Update the properties of a connection. Used to
+ *                             update the metadata subscription mask and
+ *                             policy.
+ * KDBUS_CMD_BYEBYE:           Disconnect a connection. If there are no
+ *                             messages queued up in the connection's pool,
+ *                             the call succeeds, and the handle is rendered
+ *                             unusable. Otherwise, -EBUSY is returned without
+ *                             any further side-effects.
+ * KDBUS_CMD_FREE:             Release the allocated memory in the receiver's
+ *                             pool.
+ * KDBUS_CMD_CONN_INFO:                Retrieve credentials and properties of the
+ *                             initial creator of the connection. The data was
+ *                             stored at registration time and does not
+ *                             necessarily represent the connected process or
+ *                             the actual state of the process.
+ * KDBUS_CMD_BUS_CREATOR_INFO: Retrieve information of the creator of the bus
+ *                             a connection is attached to.
+ *
+ * KDBUS_CMD_SEND:             Send a message and pass data from userspace to
+ *                             the kernel.
+ * KDBUS_CMD_RECV:             Receive a message from the kernel which is
+ *                             placed in the receiver's pool.
+ *
+ * KDBUS_CMD_NAME_ACQUIRE:     Request a well-known bus name to associate with
+ *                             the connection. Well-known names are used to
+ *                             address a peer on the bus.
+ * KDBUS_CMD_NAME_RELEASE:     Release a well-known name the connection
+ *                             currently owns.
+ * KDBUS_CMD_LIST:             Retrieve the list of all currently registered
+ *                             well-known and unique names.
+ *
+ * KDBUS_CMD_MATCH_ADD:                Install a match which broadcast messages should
+ *                             be delivered to the connection.
+ * KDBUS_CMD_MATCH_REMOVE:     Remove a current match for broadcast messages.
+ */
+enum kdbus_ioctl_type {
+       /* bus owner (00-0f) */
+       KDBUS_CMD_BUS_MAKE =            _IOW(KDBUS_IOCTL_MAGIC, 0x00,
+                                            struct kdbus_cmd),
+
+       /* endpoint owner (10-1f) */
+       KDBUS_CMD_ENDPOINT_MAKE =       _IOW(KDBUS_IOCTL_MAGIC, 0x10,
+                                            struct kdbus_cmd),
+       KDBUS_CMD_ENDPOINT_UPDATE =     _IOW(KDBUS_IOCTL_MAGIC, 0x11,
+                                            struct kdbus_cmd),
+
+       /* connection owner (80-ff) */
+       KDBUS_CMD_HELLO =               _IOWR(KDBUS_IOCTL_MAGIC, 0x80,
+                                             struct kdbus_cmd_hello),
+       KDBUS_CMD_UPDATE =              _IOW(KDBUS_IOCTL_MAGIC, 0x81,
+                                            struct kdbus_cmd),
+       KDBUS_CMD_BYEBYE =              _IOW(KDBUS_IOCTL_MAGIC, 0x82,
+                                            struct kdbus_cmd),
+       KDBUS_CMD_FREE =                _IOW(KDBUS_IOCTL_MAGIC, 0x83,
+                                            struct kdbus_cmd_free),
+       KDBUS_CMD_CONN_INFO =           _IOR(KDBUS_IOCTL_MAGIC, 0x84,
+                                            struct kdbus_cmd_info),
+       KDBUS_CMD_BUS_CREATOR_INFO =    _IOR(KDBUS_IOCTL_MAGIC, 0x85,
+                                            struct kdbus_cmd_info),
+       KDBUS_CMD_LIST =                _IOR(KDBUS_IOCTL_MAGIC, 0x86,
+                                            struct kdbus_cmd_list),
+
+       KDBUS_CMD_SEND =                _IOW(KDBUS_IOCTL_MAGIC, 0x90,
+                                            struct kdbus_cmd_send),
+       KDBUS_CMD_RECV =                _IOR(KDBUS_IOCTL_MAGIC, 0x91,
+                                            struct kdbus_cmd_recv),
+
+       KDBUS_CMD_NAME_ACQUIRE =        _IOW(KDBUS_IOCTL_MAGIC, 0xa0,
+                                            struct kdbus_cmd),
+       KDBUS_CMD_NAME_RELEASE =        _IOW(KDBUS_IOCTL_MAGIC, 0xa1,
+                                            struct kdbus_cmd),
+
+       KDBUS_CMD_MATCH_ADD =           _IOW(KDBUS_IOCTL_MAGIC, 0xb0,
+                                            struct kdbus_cmd_match),
+       KDBUS_CMD_MATCH_REMOVE =        _IOW(KDBUS_IOCTL_MAGIC, 0xb1,
+                                            struct kdbus_cmd_match),
+};
+
+#endif /* _UAPI_KDBUS_H_ */
index 84ae605..aa0e4de 100644 (file)
@@ -96,4 +96,6 @@
 #define DEVMEM_MAGIC           0x454d444d      /* "DMEM" */
 #define Z3FOLD_MAGIC           0x33
 
+#define KDBUS_SUPER_MAGIC      0x44427573
+
 #endif /* __LINUX_MAGIC_H__ */
index 6db3e31..b90c51a 100644 (file)
@@ -323,6 +323,19 @@ config POSIX_MQUEUE_SYSCTL
        depends on SYSCTL
        default y
 
+config KDBUS
+       tristate "kdbus interprocess communication"
+       depends on TMPFS
+       help
+         D-Bus is a system for low-latency, low-overhead, easy to use
+         interprocess communication (IPC).
+
+         See the man-pages and HTML files in Documentation/kdbus/
+         that are generated by 'make mandocs' and 'make htmldocs'.
+
+         If you have an ordinary machine, select M here. The module
+         will be called kdbus.
+
 config CROSS_MEMORY_ATTACH
        bool "Enable process_vm_readv/writev syscalls"
        depends on MMU
index c2558c4..227b96f 100644 (file)
@@ -9,4 +9,4 @@ obj-$(CONFIG_SYSVIPC_SYSCTL) += ipc_sysctl.o
 obj-$(CONFIG_POSIX_MQUEUE) += mqueue.o msgutil.o
 obj-$(CONFIG_IPC_NS) += namespace.o
 obj-$(CONFIG_POSIX_MQUEUE_SYSCTL) += mq_sysctl.o
-
+#obj-$(CONFIG_KDBUS) += kdbus/
diff --git a/ipc/kdbus/Documentation/kdbus.bus.xml b/ipc/kdbus/Documentation/kdbus.bus.xml
new file mode 100644 (file)
index 0000000..4b9a0ac
--- /dev/null
@@ -0,0 +1,359 @@
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+        "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<refentry id="kdbus.bus">
+
+  <refentryinfo>
+    <title>kdbus.bus</title>
+    <productname>kdbus.bus</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>kdbus.bus</refentrytitle>
+    <manvolnum>7</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>kdbus.bus</refname>
+    <refpurpose>kdbus bus</refpurpose>
+  </refnamediv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>
+      A bus is a resource that is shared between connections in order to
+      transmit messages (see
+      <citerefentry>
+        <refentrytitle>kdbus.message</refentrytitle>
+        <manvolnum>7</manvolnum>
+      </citerefentry>).
+      Each bus is independent, and operations on the bus will not have any
+      effect on other buses. A bus is a management entity that controls the
+      addresses of its connections, their policies and message transactions
+      performed via this bus.
+    </para>
+    <para>
+      Each bus is bound to the mount instance it was created on. It has a
+      custom name that is unique across all buses of a domain. In
+      <citerefentry>
+        <refentrytitle>kdbus.fs</refentrytitle>
+        <manvolnum>7</manvolnum>
+      </citerefentry>
+      a bus is presented as a directory. No operations can be performed on
+      the bus itself; instead you need to perform the operations on an endpoint
+      associated with the bus. Endpoints are accessible as files underneath the
+      bus directory. A default endpoint called <constant>bus</constant> is
+      provided on each bus.
+    </para>
+    <para>
+      Bus names may be chosen freely except for one restriction: the name must
+      be prefixed with the numeric effective UID of the creator and a dash. This
+      is required to avoid namespace clashes between different users. When
+      creating a bus, the name that is passed in must be properly formatted, or
+      the kernel will refuse creation of the bus. Example:
+      <literal>1047-foobar</literal> is an acceptable name for a bus
+      registered by a user with UID 1047. However,
+      <literal>1024-foobar</literal> is not, and neither is
+      <literal>foobar</literal>. The UID must be provided in the
+      user-namespace of the bus owner.
+    </para>
+    <para>
+      To create a new bus, you need to open the control file of a domain and
+      employ the <constant>KDBUS_CMD_BUS_MAKE</constant> ioctl. The control
+      file descriptor that was used to issue
+      <constant>KDBUS_CMD_BUS_MAKE</constant> must not previously have been
+      used for any other control-ioctl and must be kept open for the entire
+      life-time of the created bus. Closing it will immediately cleanup the
+      entire bus and all its associated resources and endpoints. Every control
+      file descriptor can only be used to create a single new bus; from that
+      point on, it is not used for any further communication until the final
+      <citerefentry>
+        <refentrytitle>close</refentrytitle>
+        <manvolnum>2</manvolnum>
+      </citerefentry>
+      .
+    </para>
+    <para>
+      Each bus will generate a random, 128-bit UUID upon creation. This UUID
+      will be returned to creators of connections through
+      <varname>kdbus_cmd_hello.id128</varname> and can be used to uniquely
+      identify buses, even across different machines or containers. The UUID
+      will have its variant bits set to <literal>DCE</literal>, and denote
+      version 4 (random). For more details on UUIDs, see <ulink
+      url="https://en.wikipedia.org/wiki/Universally_unique_identifier">
+      the Wikipedia article on UUIDs</ulink>.
+    </para>
+
+  </refsect1>
+
+  <refsect1>
+    <title>Creating buses</title>
+    <para>
+      To create a new bus, the <constant>KDBUS_CMD_BUS_MAKE</constant>
+      command is used. It takes a <type>struct kdbus_cmd</type> argument.
+    </para>
+    <programlisting>
+struct kdbus_cmd {
+  __u64 size;
+  __u64 flags;
+  __u64 return_flags;
+  struct kdbus_item items[0];
+};
+    </programlisting>
+
+    <para>The fields in this struct are described below.</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><varname>size</varname></term>
+        <listitem><para>
+          The overall size of the struct, including its items.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>flags</varname></term>
+        <listitem><para>The flags for creation.</para>
+          <variablelist>
+            <varlistentry>
+              <term><constant>KDBUS_MAKE_ACCESS_GROUP</constant></term>
+              <listitem>
+                <para>Make the bus file group-accessible.</para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_MAKE_ACCESS_WORLD</constant></term>
+              <listitem>
+                <para>Make the bus file world-accessible.</para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_FLAG_NEGOTIATE</constant></term>
+              <listitem>
+                <para>
+                  Requests a set of valid flags for this ioctl. When this bit is
+                  set, no action is taken; the ioctl will return
+                  <errorcode>0</errorcode>, and the <varname>flags</varname>
+                  field will have all bits set that are valid for this command.
+                  The <constant>KDBUS_FLAG_NEGOTIATE</constant> bit will be
+                  cleared by the operation.
+                </para>
+              </listitem>
+            </varlistentry>
+          </variablelist>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>return_flags</varname></term>
+        <listitem><para>
+          Flags returned by the kernel. Currently unused and always set to
+          <constant>0</constant> by the kernel.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>items</varname></term>
+        <listitem>
+          <para>
+            The following items (see
+            <citerefentry>
+              <refentrytitle>kdbus.item</refentrytitle>
+              <manvolnum>7</manvolnum>
+            </citerefentry>)
+            are expected for <constant>KDBUS_CMD_BUS_MAKE</constant>.
+          </para>
+          <variablelist>
+            <varlistentry>
+              <term><constant>KDBUS_ITEM_MAKE_NAME</constant></term>
+              <listitem>
+                <para>
+                  Contains a null-terminated string that identifies the
+                  bus. The name must be unique across the kdbus domain and
+                  must start with the effective UID of the caller, followed by
+                  a '<literal>-</literal>' (dash). This item is mandatory.
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_ITEM_BLOOM_PARAMETER</constant></term>
+              <listitem>
+                <para>
+                  Bus-wide bloom parameters passed in a
+                  <type>struct kdbus_bloom_parameter</type>. These settings are
+                  copied back to new connections verbatim. This item is
+                  mandatory. See
+                  <citerefentry>
+                    <refentrytitle>kdbus.item</refentrytitle>
+                    <manvolnum>7</manvolnum>
+                  </citerefentry>
+                  for a more detailed description of this item.
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_ITEM_ATTACH_FLAGS_RECV</constant></term>
+              <listitem>
+                <para>
+                  An optional item that contains a set of required attach flags
+                  that connections must allow. This item is used as a
+                  negotiation measure during connection creation. If connections
+                  do not satisfy the bus requirements, they are not allowed on
+                  the bus. If not set, the bus does not require any metadata to
+                  be attached; in this case connections are free to set their
+                  own attach flags.
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_ITEM_ATTACH_FLAGS_SEND</constant></term>
+              <listitem>
+                <para>
+                  An optional item that contains a set of attach flags that are
+                  returned to connections when they query the bus creator
+                  metadata. If not set, no metadata is returned.
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_ITEM_NEGOTIATE</constant></term>
+              <listitem><para>
+                With this item, programs can <emphasis>probe</emphasis> the
+                kernel for known item types. See
+                <citerefentry>
+                  <refentrytitle>kdbus.item</refentrytitle>
+                  <manvolnum>7</manvolnum>
+                </citerefentry>
+                for more details.
+              </para></listitem>
+            </varlistentry>
+          </variablelist>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <para>
+      Unrecognized items are rejected, and the ioctl will fail with
+      <varname>errno</varname> set to <constant>EINVAL</constant>.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return value</title>
+    <para>
+      On success, all mentioned ioctl commands return <errorcode>0</errorcode>;
+      on error, <errorcode>-1</errorcode> is returned, and
+      <varname>errno</varname> is set to indicate the error.
+      If the issued ioctl is illegal for the file descriptor used,
+      <varname>errno</varname> will be set to <constant>ENOTTY</constant>.
+    </para>
+
+    <refsect2>
+      <title>
+        <constant>KDBUS_CMD_BUS_MAKE</constant> may fail with the following
+        errors
+      </title>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>EBADMSG</constant></term>
+          <listitem><para>
+            A mandatory item is missing.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>EINVAL</constant></term>
+          <listitem><para>
+            The flags supplied in the <constant>struct kdbus_cmd</constant>
+            are invalid or the supplied name does not start with the current
+            UID and a '<literal>-</literal>' (dash).
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>EEXIST</constant></term>
+          <listitem><para>
+            A bus of that name already exists.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>ESHUTDOWN</constant></term>
+          <listitem><para>
+            The kdbus mount instance for the bus was already shut down.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>EMFILE</constant></term>
+          <listitem><para>
+            The maximum number of buses for the current user is exhausted.
+          </para></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+    <simplelist type="inline">
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.connection</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.endpoint</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.fs</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.item</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.message</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.name</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.pool</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+    </simplelist>
+  </refsect1>
+</refentry>
diff --git a/ipc/kdbus/Documentation/kdbus.connection.xml b/ipc/kdbus/Documentation/kdbus.connection.xml
new file mode 100644 (file)
index 0000000..cefb419
--- /dev/null
@@ -0,0 +1,1250 @@
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+        "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<refentry id="kdbus.connection">
+
+  <refentryinfo>
+    <title>kdbus.connection</title>
+    <productname>kdbus.connection</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>kdbus.connection</refentrytitle>
+    <manvolnum>7</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>kdbus.connection</refname>
+    <refpurpose>kdbus connection</refpurpose>
+  </refnamediv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>
+      Connections are identified by their <emphasis>connection ID</emphasis>,
+      internally implemented as a <type>uint64_t</type> counter.
+      The IDs of every newly created bus start at <constant>1</constant>, and
+      every new connection will increment the counter by <constant>1</constant>.
+      The IDs are not reused.
+    </para>
+    <para>
+      In higher level tools, the user visible representation of a connection is
+      defined by the D-Bus protocol specification as
+      <constant>":1.&lt;ID&gt;"</constant>.
+    </para>
+    <para>
+      Messages with a specific <type>uint64_t</type> destination ID are
+      directly delivered to the connection with the corresponding ID. Signal
+      messages (see
+      <citerefentry>
+        <refentrytitle>kdbus.message</refentrytitle>
+        <manvolnum>7</manvolnum>
+      </citerefentry>)
+      may be addressed to the special destination ID
+      <constant>KDBUS_DST_ID_BROADCAST</constant> (~0ULL) and will then
+      potentially be delivered to all currently active connections on the bus.
+      However, in order to receive any signal messages, clients must subscribe
+      to them by installing a match (see
+      <citerefentry>
+        <refentrytitle>kdbus.match</refentrytitle>
+        <manvolnum>7</manvolnum>
+      </citerefentry>).
+    </para>
+    <para>
+      Messages synthesized and sent directly by the kernel will carry the
+      special source ID <constant>KDBUS_SRC_ID_KERNEL</constant> (0).
+    </para>
+    <para>
+      In addition to the unique <type>uint64_t</type> connection ID,
+      established connections can request the ownership of
+      <emphasis>well-known names</emphasis>, under which they can be found and
+      addressed by other bus clients. A well-known name is associated with one
+      and only one connection at a time. See
+      <citerefentry>
+        <refentrytitle>kdbus.name</refentrytitle>
+        <manvolnum>7</manvolnum>
+      </citerefentry>
+      on name acquisition, the name registry, and the validity of names.
+    </para>
+    <para>
+      Messages can specify the special destination ID
+      <constant>KDBUS_DST_ID_NAME</constant> (0) and carry a well-known name
+      in the message data. Such a message is delivered to the destination
+      connection which owns that well-known name.
+    </para>
+
+    <programlisting><![CDATA[
+  +-------------------------------------------------------------------------+
+  | +---------------+     +---------------------------+                     |
+  | | Connection    |     | Message                   | -----------------+  |
+  | | :1.22         | --> | src: 22                   |                  |  |
+  | |               |     | dst: 25                   |                  |  |
+  | |               |     |                           |                  |  |
+  | |               |     |                           |                  |  |
+  | |               |     +---------------------------+                  |  |
+  | |               |                                                    |  |
+  | |               | <--------------------------------------+           |  |
+  | +---------------+                                        |           |  |
+  |                                                          |           |  |
+  | +---------------+     +---------------------------+      |           |  |
+  | | Connection    |     | Message                   | -----+           |  |
+  | | :1.25         | --> | src: 25                   |                  |  |
+  | |               |     | dst: 0xffffffffffffffff   | -------------+   |  |
+  | |               |     |  (KDBUS_DST_ID_BROADCAST) |              |   |  |
+  | |               |     |                           | ---------+   |   |  |
+  | |               |     +---------------------------+          |   |   |  |
+  | |               |                                            |   |   |  |
+  | |               | <--------------------------------------------------+  |
+  | +---------------+                                            |   |      |
+  |                                                              |   |      |
+  | +---------------+     +---------------------------+          |   |      |
+  | | Connection    |     | Message                   | --+      |   |      |
+  | | :1.55         | --> | src: 55                   |   |      |   |      |
+  | |               |     | dst: 0 / org.foo.bar      |   |      |   |      |
+  | |               |     |                           |   |      |   |      |
+  | |               |     |                           |   |      |   |      |
+  | |               |     +---------------------------+   |      |   |      |
+  | |               |                                     |      |   |      |
+  | |               | <------------------------------------------+   |      |
+  | +---------------+                                     |          |      |
+  |                                                       |          |      |
+  | +---------------+                                     |          |      |
+  | | Connection    |                                     |          |      |
+  | | :1.81         |                                     |          |      |
+  | | org.foo.bar   |                                     |          |      |
+  | |               |                                     |          |      |
+  | |               |                                     |          |      |
+  | |               | <-----------------------------------+          |      |
+  | |               |                                                |      |
+  | |               | <----------------------------------------------+      |
+  | +---------------+                                                       |
+  +-------------------------------------------------------------------------+
+    ]]></programlisting>
+  </refsect1>
+
+  <refsect1>
+    <title>Privileged connections</title>
+    <para>
+      A connection is considered <emphasis>privileged</emphasis> if the user
+      it was created by is the same that created the bus, or if the creating
+      task had <constant>CAP_IPC_OWNER</constant> set when it called
+      <constant>KDBUS_CMD_HELLO</constant> (see below).
+    </para>
+    <para>
+      Privileged connections have permission to employ certain restricted
+      functions and commands, which are explained below and in other kdbus
+      man-pages.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>Activator and policy holder connection</title>
+    <para>
+      An <emphasis>activator</emphasis> connection is a placeholder for a
+      <emphasis>well-known name</emphasis>. Messages sent to such a connection
+      can be used to start an implementer connection, which will then get all
+      the messages from the activator copied over. An activator connection
+      cannot be used to send any message.
+    </para>
+    <para>
+      A <emphasis>policy holder</emphasis> connection only installs a policy
+      for one or more names. These policy entries are kept active as long as
+      the connection is alive, and are removed once it terminates. Such a
+      policy connection type can be used to deploy restrictions for names that
+      are not yet active on the bus. A policy holder connection cannot be used
+      to send any message.
+    </para>
+    <para>
+      The creation of activator or policy holder connections is restricted to
+      privileged users on the bus (see above).
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>Monitor connections</title>
+    <para>
+      Monitors are eavesdropping connections that receive all the traffic on the
+      bus, but is invisible to other connections. Such connections have all
+      properties of any other, regular connection, except for the following
+      details:
+    </para>
+
+    <itemizedlist>
+      <listitem><para>
+        They will get every message sent over the bus, both unicasts and
+        broadcasts.
+      </para></listitem>
+
+      <listitem><para>
+        Installing matches for signal messages is neither necessary
+        nor allowed.
+      </para></listitem>
+
+      <listitem><para>
+        They cannot send messages or be directly addressed as receiver.
+      </para></listitem>
+
+      <listitem><para>
+        They cannot own well-known names. Therefore, they also can't operate as
+        activators.
+      </para></listitem>
+
+      <listitem><para>
+        Their creation and destruction will not cause
+        <constant>KDBUS_ITEM_ID_{ADD,REMOVE}</constant> (see
+        <citerefentry>
+          <refentrytitle>kdbus.item</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>).
+      </para></listitem>
+
+      <listitem><para>
+        They are not listed with their unique name in name registry dumps
+        (see <constant>KDBUS_CMD_NAME_LIST</constant> in
+        <citerefentry>
+          <refentrytitle>kdbus.name</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>), so other connections cannot detect the presence of
+       a monitor.
+      </para></listitem>
+    </itemizedlist>
+    <para>
+      The creation of monitor connections is restricted to privileged users on
+      the bus (see above).
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>Creating connections</title>
+    <para>
+      A connection to a bus is created by opening an endpoint file (see
+      <citerefentry>
+        <refentrytitle>kdbus.endpoint</refentrytitle>
+        <manvolnum>7</manvolnum>
+      </citerefentry>)
+      of a bus and becoming an active client with the
+      <constant>KDBUS_CMD_HELLO</constant> ioctl. Every connection has a unique
+      identifier on the bus and can address messages to every other connection
+      on the same bus by using the peer's connection ID as the destination.
+    </para>
+    <para>
+      The <constant>KDBUS_CMD_HELLO</constant> ioctl takes a <type>struct
+      kdbus_cmd_hello</type> as argument.
+    </para>
+
+    <programlisting>
+struct kdbus_cmd_hello {
+  __u64 size;
+  __u64 flags;
+  __u64 return_flags;
+  __u64 attach_flags_send;
+  __u64 attach_flags_recv;
+  __u64 bus_flags;
+  __u64 id;
+  __u64 pool_size;
+  __u64 offset;
+  __u8 id128[16];
+  struct kdbus_item items[0];
+};
+    </programlisting>
+
+    <para>The fields in this struct are described below.</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><varname>size</varname></term>
+        <listitem><para>
+          The overall size of the struct, including its items.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>flags</varname></term>
+        <listitem>
+          <para>Flags to apply to this connection</para>
+          <variablelist>
+            <varlistentry>
+              <term><constant>KDBUS_HELLO_ACCEPT_FD</constant></term>
+              <listitem>
+                <para>
+                  When this flag is set, the connection can be sent file
+                  descriptors as message payload of unicast messages. If it's
+                  not set, an attempt to send file descriptors will result in
+                  <constant>-ECOMM</constant> on the sender's side.
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_HELLO_ACTIVATOR</constant></term>
+              <listitem>
+                <para>
+                  Make this connection an activator (see above). With this bit
+                  set, an item of type <constant>KDBUS_ITEM_NAME</constant> has
+                  to be attached. This item describes the well-known name this
+                  connection should be an activator for.
+                  A connection can not be an activator and a policy holder at
+                  the same time time, so this bit is not allowed together with
+                  <constant>KDBUS_HELLO_POLICY_HOLDER</constant>.
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_HELLO_POLICY_HOLDER</constant></term>
+              <listitem>
+                <para>
+                  Make this connection a policy holder (see above). With this
+                  bit set, an item of type <constant>KDBUS_ITEM_NAME</constant>
+                  has to be attached. This item describes the well-known name
+                  this connection should hold a policy for.
+                  A connection can not be an activator and a policy holder at
+                  the same time time, so this bit is not allowed together with
+                  <constant>KDBUS_HELLO_ACTIVATOR</constant>.
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_HELLO_MONITOR</constant></term>
+              <listitem>
+                <para>
+                  Make this connection a monitor connection (see above).
+                </para>
+                <para>
+                  This flag can only be set by privileged bus connections. See
+                  below for more information.
+                  A connection can not be monitor and an activator or a policy
+                  holder at the same time time, so this bit is not allowed
+                  together with <constant>KDBUS_HELLO_ACTIVATOR</constant> or
+                  <constant>KDBUS_HELLO_POLICY_HOLDER</constant>.
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_FLAG_NEGOTIATE</constant></term>
+              <listitem>
+                <para>
+                  Requests a set of valid flags for this ioctl. When this bit is
+                  set, no action is taken; the ioctl will return
+                  <errorcode>0</errorcode>, and the <varname>flags</varname>
+                  field will have all bits set that are valid for this command.
+                  The <constant>KDBUS_FLAG_NEGOTIATE</constant> bit will be
+                  cleared by the operation.
+                </para>
+              </listitem>
+            </varlistentry>
+          </variablelist>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>return_flags</varname></term>
+        <listitem><para>
+          Flags returned by the kernel. Currently unused and always set to
+          <constant>0</constant> by the kernel.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>attach_flags_send</varname></term>
+        <listitem><para>
+          Set the bits for metadata this connection permits to be sent to the
+          receiving peer. Only metadata items that are both allowed to be sent
+          by the sender and that are requested by the receiver will be attached
+          to the message. Note, however, that the bus may optionally require
+          some of those bits to be set. If the match fails, the ioctl will fail
+          with <varname>errno</varname> set to
+          <constant>ECONNREFUSED</constant>. In either case, when returning the
+          field will be set to the mask of metadata items that are enforced by
+          the bus with the <constant>KDBUS_FLAGS_KERNEL</constant> bit set as
+          well.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>attach_flags_recv</varname></term>
+        <listitem><para>
+          Request the attachment of metadata for each message received by this
+          connection. See
+          <citerefentry>
+            <refentrytitle>kdbus</refentrytitle>
+            <manvolnum>7</manvolnum>
+          </citerefentry>
+          for information about metadata, and
+          <citerefentry>
+            <refentrytitle>kdbus.item</refentrytitle>
+            <manvolnum>7</manvolnum>
+          </citerefentry>
+          regarding items in general.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>bus_flags</varname></term>
+        <listitem><para>
+          Upon successful completion of the ioctl, this member will contain the
+          flags of the bus it connected to.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>id</varname></term>
+        <listitem><para>
+          Upon successful completion of the command, this member will contain
+          the numerical ID of the new connection.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>pool_size</varname></term>
+        <listitem><para>
+          The size of the communication pool, in bytes. The pool can be
+          accessed by calling
+          <citerefentry>
+            <refentrytitle>mmap</refentrytitle>
+            <manvolnum>2</manvolnum>
+          </citerefentry>
+          on the file descriptor that was used to issue the
+          <constant>KDBUS_CMD_HELLO</constant> ioctl.
+          The pool size of a connection must be greater than
+          <constant>0</constant> and a multiple of
+          <constant>PAGE_SIZE</constant>. See
+          <citerefentry>
+            <refentrytitle>kdbus.pool</refentrytitle>
+            <manvolnum>7</manvolnum>
+          </citerefentry>
+          for more information.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>offset</varname></term>
+        <listitem><para>
+          The kernel will return the offset in the pool where returned details
+          will be stored. See below.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>id128</varname></term>
+        <listitem><para>
+          Upon successful completion of the ioctl, this member will contain the
+          <emphasis>128-bit UUID</emphasis> of the connected bus.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>items</varname></term>
+        <listitem>
+          <para>
+            Variable list of items containing optional additional information.
+            The following items are currently expected/valid:
+          </para>
+          <variablelist>
+            <varlistentry>
+              <term><constant>KDBUS_ITEM_CONN_DESCRIPTION</constant></term>
+              <listitem>
+                <para>
+                  Contains a string that describes this connection, so it can
+                  be identified later.
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_ITEM_NAME</constant></term>
+              <term><constant>KDBUS_ITEM_POLICY_ACCESS</constant></term>
+              <listitem>
+                <para>
+                  For activators and policy holders only, combinations of
+                  these two items describe policy access entries. See
+                  <citerefentry>
+                    <refentrytitle>kdbus.policy</refentrytitle>
+                    <manvolnum>7</manvolnum>
+                  </citerefentry>
+                  for further details.
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_ITEM_CREDS</constant></term>
+              <term><constant>KDBUS_ITEM_PIDS</constant></term>
+              <term><constant>KDBUS_ITEM_SECLABEL</constant></term>
+              <listitem>
+                <para>
+                  Privileged bus users may submit these types in order to
+                  create connections with faked credentials. This information
+                  will be returned when peer information is queried by
+                  <constant>KDBUS_CMD_CONN_INFO</constant>. See below for more
+                  information on retrieving information on connections.
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_ITEM_NEGOTIATE</constant></term>
+              <listitem><para>
+                With this item, programs can <emphasis>probe</emphasis> the
+                kernel for known item types. See
+                <citerefentry>
+                  <refentrytitle>kdbus.item</refentrytitle>
+                  <manvolnum>7</manvolnum>
+                </citerefentry>
+                for more details.
+              </para></listitem>
+            </varlistentry>
+          </variablelist>
+
+          <para>
+            Unrecognized items are rejected, and the ioctl will fail with
+            <varname>errno</varname> set to <constant>EINVAL</constant>.
+          </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <para>
+      At the offset returned in the <varname>offset</varname> field of
+      <type>struct kdbus_cmd_hello</type>, the kernel will store items
+      of the following types:
+    </para>
+
+    <variablelist>
+      <varlistentry>
+        <term><constant>KDBUS_ITEM_BLOOM_PARAMETER</constant></term>
+        <listitem>
+          <para>
+            Bloom filter parameter as defined by the bus creator.
+          </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <para>
+      The offset in the pool has to be freed with the
+      <constant>KDBUS_CMD_FREE</constant> ioctl. See
+      <citerefentry>
+        <refentrytitle>kdbus.pool</refentrytitle>
+        <manvolnum>7</manvolnum>
+      </citerefentry>
+      for further information.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>Retrieving information on a connection</title>
+    <para>
+      The <constant>KDBUS_CMD_CONN_INFO</constant> ioctl can be used to
+      retrieve credentials and properties of the initial creator of a
+      connection. This ioctl uses the following struct.
+    </para>
+
+    <programlisting>
+struct kdbus_cmd_info {
+  __u64 size;
+  __u64 flags;
+  __u64 return_flags;
+  __u64 id;
+  __u64 attach_flags;
+  __u64 offset;
+  __u64 info_size;
+  struct kdbus_item items[0];
+};
+    </programlisting>
+
+    <variablelist>
+      <varlistentry>
+        <term><varname>size</varname></term>
+        <listitem><para>
+          The overall size of the struct, including its items.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>flags</varname></term>
+        <listitem><para>
+          Currently, no flags are supported.
+          <constant>KDBUS_FLAG_NEGOTIATE</constant> is accepted to probe for
+          valid flags. If set, the ioctl will return <errorcode>0</errorcode>,
+          and the <varname>flags</varname> field is set to
+          <constant>0</constant>.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>return_flags</varname></term>
+        <listitem><para>
+          Flags returned by the kernel. Currently unused and always set to
+          <constant>0</constant> by the kernel.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>id</varname></term>
+        <listitem><para>
+          The numerical ID of the connection for which information is to be
+          retrieved. If set to a non-zero value, the
+          <constant>KDBUS_ITEM_OWNED_NAME</constant> item is ignored.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>attach_flags</varname></term>
+        <listitem><para>
+          Specifies which metadata items should be attached to the answer. See
+          <citerefentry>
+            <refentrytitle>kdbus.message</refentrytitle>
+            <manvolnum>7</manvolnum>
+          </citerefentry>.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>offset</varname></term>
+        <listitem><para>
+          When the ioctl returns, this field will contain the offset of the
+          connection information inside the caller's pool. See
+          <citerefentry>
+            <refentrytitle>kdbus.pool</refentrytitle>
+            <manvolnum>7</manvolnum>
+          </citerefentry>
+          for further information.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>info_size</varname></term>
+        <listitem><para>
+          The kernel will return the size of the returned information, so
+          applications can optionally
+          <citerefentry>
+            <refentrytitle>mmap</refentrytitle>
+            <manvolnum>2</manvolnum>
+          </citerefentry>
+          specific parts of the pool. See
+          <citerefentry>
+            <refentrytitle>kdbus.pool</refentrytitle>
+            <manvolnum>7</manvolnum>
+          </citerefentry>
+          for further information.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>items</varname></term>
+        <listitem>
+          <para>
+            The following items are expected for
+            <constant>KDBUS_CMD_CONN_INFO</constant>.
+          </para>
+          <variablelist>
+            <varlistentry>
+              <term><constant>KDBUS_ITEM_OWNED_NAME</constant></term>
+              <listitem>
+                <para>
+                  Contains the well-known name of the connection to look up as.
+                  This item is mandatory if the <varname>id</varname> field is
+                  set to 0.
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_ITEM_NEGOTIATE</constant></term>
+              <listitem><para>
+                With this item, programs can <emphasis>probe</emphasis> the
+                kernel for known item types. See
+                <citerefentry>
+                  <refentrytitle>kdbus.item</refentrytitle>
+                  <manvolnum>7</manvolnum>
+                </citerefentry>
+                for more details.
+              </para></listitem>
+            </varlistentry>
+          </variablelist>
+          <para>
+            Unrecognized items are rejected, and the ioctl will fail with
+            <varname>errno</varname> set to <constant>EINVAL</constant>.
+          </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <para>
+      When the ioctl returns, the following struct will be stored in the
+      caller's pool at <varname>offset</varname>. The fields in this struct
+      are described below.
+    </para>
+
+    <programlisting>
+struct kdbus_info {
+  __u64 size;
+  __u64 id;
+  __u64 flags;
+  struct kdbus_item items[0];
+};
+    </programlisting>
+
+    <variablelist>
+      <varlistentry>
+        <term><varname>size</varname></term>
+        <listitem><para>
+          The overall size of the struct, including its items.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>id</varname></term>
+        <listitem><para>
+          The connection's unique ID.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>flags</varname></term>
+        <listitem><para>
+          The connection's flags as specified when it was created.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>items</varname></term>
+        <listitem>
+          <para>
+            Depending on the <varname>flags</varname> field in
+            <type>struct kdbus_cmd_info</type>, items of types
+            <constant>KDBUS_ITEM_OWNED_NAME</constant> and
+            <constant>KDBUS_ITEM_CONN_DESCRIPTION</constant> may follow here.
+            <constant>KDBUS_ITEM_NEGOTIATE</constant> is also allowed.
+          </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <para>
+      Once the caller is finished with parsing the return buffer, it needs to
+      employ the <constant>KDBUS_CMD_FREE</constant> command for the offset, in
+      order to free the buffer part. See
+      <citerefentry>
+        <refentrytitle>kdbus.pool</refentrytitle>
+        <manvolnum>7</manvolnum>
+      </citerefentry>
+      for further information.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>Getting information about a connection's bus creator</title>
+    <para>
+      The <constant>KDBUS_CMD_BUS_CREATOR_INFO</constant> ioctl takes the same
+      struct as <constant>KDBUS_CMD_CONN_INFO</constant>, but is used to
+      retrieve information about the creator of the bus the connection is
+      attached to. The metadata returned by this call is collected during the
+      creation of the bus and is never altered afterwards, so it provides
+      pristine information on the task that created the bus, at the moment when
+      it did so.
+    </para>
+    <para>
+      In response to this call, a slice in the connection's pool is allocated
+      and filled with an object of type <type>struct kdbus_info</type>,
+      pointed to by the ioctl's <varname>offset</varname> field.
+    </para>
+
+    <programlisting>
+struct kdbus_info {
+  __u64 size;
+  __u64 id;
+  __u64 flags;
+  struct kdbus_item items[0];
+};
+    </programlisting>
+
+    <variablelist>
+      <varlistentry>
+        <term><varname>size</varname></term>
+        <listitem><para>
+          The overall size of the struct, including its items.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>id</varname></term>
+        <listitem><para>
+          The bus ID.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>flags</varname></term>
+        <listitem><para>
+          The bus flags as specified when it was created.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>items</varname></term>
+        <listitem>
+          <para>
+            Metadata information is stored in items here. The item list
+            contains a <constant>KDBUS_ITEM_MAKE_NAME</constant> item that
+            indicates the bus name of the calling connection.
+            <constant>KDBUS_ITEM_NEGOTIATE</constant> is allowed to probe
+            for known item types.
+          </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <para>
+      Once the caller is finished with parsing the return buffer, it needs to
+      employ the <constant>KDBUS_CMD_FREE</constant> command for the offset, in
+      order to free the buffer part. See
+      <citerefentry>
+        <refentrytitle>kdbus.pool</refentrytitle>
+        <manvolnum>7</manvolnum>
+      </citerefentry>
+      for further information.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>Updating connection details</title>
+    <para>
+      Some of a connection's details can be updated with the
+      <constant>KDBUS_CMD_CONN_UPDATE</constant> ioctl, using the file
+      descriptor that was used to create the connection. The update command
+      uses the following struct.
+    </para>
+
+    <programlisting>
+struct kdbus_cmd {
+  __u64 size;
+  __u64 flags;
+  __u64 return_flags;
+  struct kdbus_item items[0];
+};
+    </programlisting>
+
+    <variablelist>
+      <varlistentry>
+        <term><varname>size</varname></term>
+        <listitem><para>
+          The overall size of the struct, including its items.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>flags</varname></term>
+        <listitem><para>
+          Currently, no flags are supported.
+          <constant>KDBUS_FLAG_NEGOTIATE</constant> is accepted to probe for
+          valid flags. If set, the ioctl will return <errorcode>0</errorcode>,
+          and the <varname>flags</varname> field is set to
+          <constant>0</constant>.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>return_flags</varname></term>
+        <listitem><para>
+          Flags returned by the kernel. Currently unused and always set to
+          <constant>0</constant> by the kernel.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>items</varname></term>
+        <listitem>
+          <para>
+            Items to describe the connection details to be updated. The
+            following item types are supported.
+          </para>
+          <variablelist>
+            <varlistentry>
+              <term><constant>KDBUS_ITEM_ATTACH_FLAGS_SEND</constant></term>
+              <listitem>
+                <para>
+                  Supply a new set of metadata items that this connection
+                  permits to be sent along with messages.
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_ITEM_ATTACH_FLAGS_RECV</constant></term>
+              <listitem>
+                <para>
+                  Supply a new set of metadata items that this connection
+                  requests to be attached to each message.
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_ITEM_NAME</constant></term>
+              <term><constant>KDBUS_ITEM_POLICY_ACCESS</constant></term>
+              <listitem>
+                <para>
+                  Policy holder connections may supply a new set of policy
+                  information with these items. For other connection types,
+                  <constant>EOPNOTSUPP</constant> is returned in
+                  <varname>errno</varname>.
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_ITEM_NEGOTIATE</constant></term>
+              <listitem><para>
+                With this item, programs can <emphasis>probe</emphasis> the
+                kernel for known item types. See
+                <citerefentry>
+                  <refentrytitle>kdbus.item</refentrytitle>
+                  <manvolnum>7</manvolnum>
+                </citerefentry>
+                for more details.
+              </para></listitem>
+            </varlistentry>
+          </variablelist>
+
+          <para>
+            Unrecognized items are rejected, and the ioctl will fail with
+            <varname>errno</varname> set to <constant>EINVAL</constant>.
+          </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Termination of connections</title>
+    <para>
+      A connection can be terminated by simply calling
+      <citerefentry>
+        <refentrytitle>close</refentrytitle>
+        <manvolnum>2</manvolnum>
+      </citerefentry>
+      on its file descriptor. All pending incoming messages will be discarded,
+      and the memory allocated by the pool will be freed.
+    </para>
+
+    <para>
+      An alternative way of closing down a connection is via the
+      <constant>KDBUS_CMD_BYEBYE</constant> ioctl. This ioctl will succeed only
+      if the message queue of the connection is empty at the time of closing;
+      otherwise, the ioctl will fail with <varname>errno</varname> set to
+      <constant>EBUSY</constant>. When this ioctl returns
+      successfully, the connection has been terminated and won't accept any new
+      messages from remote peers. This way, a connection can be terminated
+      race-free, without losing any messages. The ioctl takes an argument of
+      type <type>struct kdbus_cmd</type>.
+    </para>
+
+    <programlisting>
+struct kdbus_cmd {
+  __u64 size;
+  __u64 flags;
+  __u64 return_flags;
+  struct kdbus_item items[0];
+};
+    </programlisting>
+
+    <variablelist>
+      <varlistentry>
+        <term><varname>size</varname></term>
+        <listitem><para>
+          The overall size of the struct, including its items.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>flags</varname></term>
+        <listitem><para>
+          Currently, no flags are supported.
+          <constant>KDBUS_FLAG_NEGOTIATE</constant> is accepted to probe for
+          valid flags. If set, the ioctl will fail with
+          <varname>errno</varname> set to <constant>EPROTO</constant>, and
+          the <varname>flags</varname> field is set to <constant>0</constant>.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>return_flags</varname></term>
+        <listitem><para>
+          Flags returned by the kernel. Currently unused and always set to
+          <constant>0</constant> by the kernel.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>items</varname></term>
+        <listitem>
+          <para>
+            The following item types are supported.
+          </para>
+          <variablelist>
+            <varlistentry>
+              <term><constant>KDBUS_ITEM_NEGOTIATE</constant></term>
+              <listitem><para>
+                With this item, programs can <emphasis>probe</emphasis> the
+                kernel for known item types. See
+                <citerefentry>
+                  <refentrytitle>kdbus.item</refentrytitle>
+                  <manvolnum>7</manvolnum>
+                </citerefentry>
+                for more details.
+              </para></listitem>
+            </varlistentry>
+          </variablelist>
+
+          <para>
+            Unrecognized items are rejected, and the ioctl will fail with
+            <varname>errno</varname> set to <constant>EINVAL</constant>.
+          </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Return value</title>
+    <para>
+      On success, all mentioned ioctl commands return <errorcode>0</errorcode>;
+      on error, <errorcode>-1</errorcode> is returned, and
+      <varname>errno</varname> is set to indicate the error.
+      If the issued ioctl is illegal for the file descriptor used,
+      <varname>errno</varname> will be set to <constant>ENOTTY</constant>.
+    </para>
+
+    <refsect2>
+      <title>
+        <constant>KDBUS_CMD_HELLO</constant> may fail with the following
+        errors
+      </title>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>EFAULT</constant></term>
+          <listitem><para>
+            The supplied pool size was 0 or not a multiple of the page size.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>EINVAL</constant></term>
+          <listitem><para>
+            The flags supplied in <type>struct kdbus_cmd_hello</type>
+            are invalid.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>EINVAL</constant></term>
+          <listitem><para>
+            An illegal combination of
+            <constant>KDBUS_HELLO_MONITOR</constant>,
+            <constant>KDBUS_HELLO_ACTIVATOR</constant> and
+            <constant>KDBUS_HELLO_POLICY_HOLDER</constant> was passed in
+            <varname>flags</varname>.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>EINVAL</constant></term>
+          <listitem><para>
+            An invalid set of items was supplied.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>ECONNREFUSED</constant></term>
+          <listitem><para>
+            The attach_flags_send field did not satisfy the requirements of
+            the bus.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>EPERM</constant></term>
+          <listitem><para>
+            A <constant>KDBUS_ITEM_CREDS</constant> items was supplied, but the
+            current user is not privileged.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>ESHUTDOWN</constant></term>
+          <listitem><para>
+            The bus you were trying to connect to has already been shut down.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>EMFILE</constant></term>
+          <listitem><para>
+            The maximum number of connections on the bus has been reached.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>EOPNOTSUPP</constant></term>
+          <listitem><para>
+            The endpoint does not support the connection flags supplied in
+            <type>struct kdbus_cmd_hello</type>.
+          </para></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+
+    <refsect2>
+      <title>
+        <constant>KDBUS_CMD_BYEBYE</constant> may fail with the following
+        errors
+      </title>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>EALREADY</constant></term>
+          <listitem><para>
+            The connection has already been shut down.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>EBUSY</constant></term>
+          <listitem><para>
+            There are still messages queued up in the connection's pool.
+          </para></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+
+    <refsect2>
+      <title>
+        <constant>KDBUS_CMD_CONN_INFO</constant> may fail with the following
+        errors
+      </title>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>EINVAL</constant></term>
+          <listitem><para>
+            Invalid flags, or neither an ID nor a name was provided, or the
+            name is invalid.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>ESRCH</constant></term>
+          <listitem><para>
+            Connection lookup by name failed.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>ENXIO</constant></term>
+          <listitem><para>
+            No connection with the provided connection ID found.
+          </para></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+
+    <refsect2>
+      <title>
+        <constant>KDBUS_CMD_CONN_UPDATE</constant> may fail with the following
+        errors
+      </title>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>EINVAL</constant></term>
+          <listitem><para>
+            Illegal flags or items.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>EINVAL</constant></term>
+          <listitem><para>
+            Wildcards submitted in policy entries, or illegal sequence
+            of policy items.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>EOPNOTSUPP</constant></term>
+          <listitem><para>
+            Operation not supported by connection.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>E2BIG</constant></term>
+          <listitem><para>
+            Too many policy items attached.
+          </para></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+    <simplelist type="inline">
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.bus</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.endpoint</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.message</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.name</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.policy</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.pool</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.item</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+    </simplelist>
+  </refsect1>
+</refentry>
diff --git a/ipc/kdbus/Documentation/kdbus.endpoint.xml b/ipc/kdbus/Documentation/kdbus.endpoint.xml
new file mode 100644 (file)
index 0000000..6632485
--- /dev/null
@@ -0,0 +1,429 @@
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+        "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<refentry id="kdbus.endpoint">
+
+  <refentryinfo>
+    <title>kdbus.endpoint</title>
+    <productname>kdbus.endpoint</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>kdbus.endpoint</refentrytitle>
+    <manvolnum>7</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>kdbus.endpoint</refname>
+    <refpurpose>kdbus endpoint</refpurpose>
+  </refnamediv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>
+      Endpoints are entry points to a bus (see
+      <citerefentry>
+        <refentrytitle>kdbus.bus</refentrytitle>
+        <manvolnum>7</manvolnum>
+      </citerefentry>).
+      By default, each bus has a default
+      endpoint called 'bus'. The bus owner has the ability to create custom
+      endpoints with specific names, permissions, and policy databases
+      (see below). An endpoint is presented as file underneath the directory
+      of the parent bus.
+    </para>
+    <para>
+      To create a custom endpoint, open the default endpoint
+      (<literal>bus</literal>) and use the
+      <constant>KDBUS_CMD_ENDPOINT_MAKE</constant> ioctl with
+      <type>struct kdbus_cmd</type>. Custom endpoints always have a policy
+      database that, by default, forbids any operation. You have to explicitly
+      install policy entries to allow any operation on this endpoint.
+    </para>
+    <para>
+      Once <constant>KDBUS_CMD_ENDPOINT_MAKE</constant> succeeded, the new
+      endpoint will appear in the filesystem
+      (<citerefentry>
+        <refentrytitle>kdbus.bus</refentrytitle>
+        <manvolnum>7</manvolnum>
+      </citerefentry>), and the used file descriptor will manage the
+      newly created endpoint resource. It cannot be used to manage further
+      resources and must be kept open as long as the endpoint is needed. The
+      endpoint will be terminated as soon as the file descriptor is closed.
+    </para>
+    <para>
+      Endpoint names may be chosen freely except for one restriction: the name
+      must be prefixed with the numeric effective UID of the creator and a dash.
+      This is required to avoid namespace clashes between different users. When
+      creating an endpoint, the name that is passed in must be properly
+      formatted or the kernel will refuse creation of the endpoint. Example:
+      <literal>1047-my-endpoint</literal> is an acceptable name for an
+      endpoint registered by a user with UID 1047. However,
+      <literal>1024-my-endpoint</literal> is not, and neither is
+      <literal>my-endpoint</literal>. The UID must be provided in the
+      user-namespace of the bus.
+    </para>
+    <para>
+      To create connections to a bus, use <constant>KDBUS_CMD_HELLO</constant>
+      on a file descriptor returned by <function>open()</function> on an
+      endpoint node. See
+      <citerefentry>
+        <refentrytitle>kdbus.connection</refentrytitle>
+        <manvolnum>7</manvolnum>
+      </citerefentry>
+      for further details.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>Creating custom endpoints</title>
+    <para>
+      To create a new endpoint, the
+      <constant>KDBUS_CMD_ENDPOINT_MAKE</constant> command is used. Along with
+      the endpoint's name, which will be used to expose the endpoint in the
+      <citerefentry>
+        <refentrytitle>kdbus.fs</refentrytitle>
+        <manvolnum>7</manvolnum>
+      </citerefentry>,
+      the command also optionally takes items to set up the endpoint's
+      <citerefentry>
+        <refentrytitle>kdbus.policy</refentrytitle>
+        <manvolnum>7</manvolnum>
+      </citerefentry>.
+      <constant>KDBUS_CMD_ENDPOINT_MAKE</constant> takes a
+      <type>struct kdbus_cmd</type> argument.
+    </para>
+    <programlisting>
+struct kdbus_cmd {
+  __u64 size;
+  __u64 flags;
+  __u64 return_flags;
+  struct kdbus_item items[0];
+};
+    </programlisting>
+
+    <para>The fields in this struct are described below.</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><varname>size</varname></term>
+        <listitem><para>
+          The overall size of the struct, including its items.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>flags</varname></term>
+        <listitem><para>The flags for creation.</para>
+          <variablelist>
+            <varlistentry>
+              <term><constant>KDBUS_MAKE_ACCESS_GROUP</constant></term>
+              <listitem>
+                <para>Make the endpoint file group-accessible.</para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_MAKE_ACCESS_WORLD</constant></term>
+              <listitem>
+                <para>Make the endpoint file world-accessible.</para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_FLAG_NEGOTIATE</constant></term>
+              <listitem>
+                <para>
+                  Requests a set of valid flags for this ioctl. When this bit is
+                  set, no action is taken; the ioctl will return
+                  <errorcode>0</errorcode>, and the <varname>flags</varname>
+                  field will have all bits set that are valid for this command.
+                  The <constant>KDBUS_FLAG_NEGOTIATE</constant> bit will be
+                  cleared by the operation.
+                </para>
+              </listitem>
+            </varlistentry>
+          </variablelist>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>return_flags</varname></term>
+        <listitem><para>
+          Flags returned by the kernel. Currently unused and always set to
+          <constant>0</constant> by the kernel.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>items</varname></term>
+        <listitem>
+          <para>
+            The following items are expected for
+            <constant>KDBUS_CMD_ENDPOINT_MAKE</constant>.
+          </para>
+          <variablelist>
+            <varlistentry>
+              <term><constant>KDBUS_ITEM_MAKE_NAME</constant></term>
+              <listitem>
+                <para>Contains a string to identify the endpoint name.</para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_ITEM_NAME</constant></term>
+              <term><constant>KDBUS_ITEM_POLICY_ACCESS</constant></term>
+              <listitem>
+                <para>
+                  These items are used to set the policy attached to the
+                  endpoint. For more details on bus and endpoint policies, see
+                  <citerefentry>
+                    <refentrytitle>kdbus.policy</refentrytitle>
+                    <manvolnum>7</manvolnum>
+                  </citerefentry>.
+                </para>
+              </listitem>
+            </varlistentry>
+          </variablelist>
+          <para>
+            Unrecognized items are rejected, and the ioctl will fail with
+            <varname>errno</varname> set to <varname>EINVAL</varname>.
+          </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Updating endpoints</title>
+    <para>
+      To update an existing endpoint, the
+      <constant>KDBUS_CMD_ENDPOINT_UPDATE</constant> command is used on the file
+      descriptor that was used to create the endpoint, using
+      <constant>KDBUS_CMD_ENDPOINT_MAKE</constant>. The only relevant detail of
+      the endpoint that can be updated is the policy. When the command is
+      employed, the policy of the endpoint is <emphasis>replaced</emphasis>
+      atomically with the new set of rules.
+      The command takes a <type>struct kdbus_cmd</type> argument.
+    </para>
+    <programlisting>
+struct kdbus_cmd {
+  __u64 size;
+  __u64 flags;
+  __u64 return_flags;
+  struct kdbus_item items[0];
+};
+    </programlisting>
+
+    <para>The fields in this struct are described below.</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><varname>size</varname></term>
+        <listitem><para>
+          The overall size of the struct, including its items.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>flags</varname></term>
+        <listitem><para>
+          Unused for this command.
+          <constant>KDBUS_FLAG_NEGOTIATE</constant> is accepted to probe for
+          valid flags. If set, the ioctl will return <errorcode>0</errorcode>,
+          and the <varname>flags</varname> field is set to
+          <constant>0</constant>.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>return_flags</varname></term>
+        <listitem><para>
+          Flags returned by the kernel. Currently unused and always set to
+          <constant>0</constant> by the kernel.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>items</varname></term>
+        <listitem>
+          <para>
+            The following items are expected for
+            <constant>KDBUS_CMD_ENDPOINT_UPDATE</constant>.
+          </para>
+          <variablelist>
+            <varlistentry>
+              <term><constant>KDBUS_ITEM_NAME</constant></term>
+              <term><constant>KDBUS_ITEM_POLICY_ACCESS</constant></term>
+              <listitem>
+                <para>
+                  These items are used to set the policy attached to the
+                  endpoint. For more details on bus and endpoint policies, see
+                  <citerefentry>
+                    <refentrytitle>kdbus.policy</refentrytitle>
+                    <manvolnum>7</manvolnum>
+                  </citerefentry>.
+                  Existing policy is atomically replaced with the new rules
+                  provided.
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_ITEM_NEGOTIATE</constant></term>
+              <listitem><para>
+                With this item, programs can <emphasis>probe</emphasis> the
+                kernel for known item types. See
+                <citerefentry>
+                  <refentrytitle>kdbus.item</refentrytitle>
+                  <manvolnum>7</manvolnum>
+                </citerefentry>
+                for more details.
+              </para></listitem>
+            </varlistentry>
+          </variablelist>
+          <para>
+            Unrecognized items are rejected, and the ioctl will fail with
+            <varname>errno</varname> set to <constant>EINVAL</constant>.
+          </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Return value</title>
+    <para>
+      On success, all mentioned ioctl commands return <errorcode>0</errorcode>;
+      on error, <errorcode>-1</errorcode> is returned, and
+      <varname>errno</varname> is set to indicate the error.
+      If the issued ioctl is illegal for the file descriptor used,
+      <varname>errno</varname> will be set to <constant>ENOTTY</constant>.
+    </para>
+
+    <refsect2>
+      <title>
+        <constant>KDBUS_CMD_ENDPOINT_MAKE</constant> may fail with the
+        following errors
+      </title>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>EINVAL</constant></term>
+          <listitem><para>
+            The flags supplied in the <type>struct kdbus_cmd</type>
+            are invalid.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>EINVAL</constant></term>
+          <listitem><para>
+            Illegal combination of <constant>KDBUS_ITEM_NAME</constant> and
+            <constant>KDBUS_ITEM_POLICY_ACCESS</constant> was provided.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>EEXIST</constant></term>
+          <listitem><para>
+            An endpoint of that name already exists.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>EPERM</constant></term>
+          <listitem><para>
+            The calling user is not privileged. See
+            <citerefentry>
+              <refentrytitle>kdbus</refentrytitle>
+              <manvolnum>7</manvolnum>
+            </citerefentry>
+            for information about privileged users.
+          </para></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+
+    <refsect2>
+      <title>
+        <constant>KDBUS_CMD_ENDPOINT_UPDATE</constant> may fail with the
+        following errors
+      </title>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>EINVAL</constant></term>
+          <listitem><para>
+            The flags supplied in <type>struct kdbus_cmd</type>
+            are invalid.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>EINVAL</constant></term>
+          <listitem><para>
+            Illegal combination of <constant>KDBUS_ITEM_NAME</constant> and
+            <constant>KDBUS_ITEM_POLICY_ACCESS</constant> was provided.
+          </para></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+    <simplelist type="inline">
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.bus</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.endpoint</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.fs</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.item</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.message</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.name</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.pool</refentrytitle>
+           <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+    </simplelist>
+  </refsect1>
+</refentry>
diff --git a/ipc/kdbus/Documentation/kdbus.fs.xml b/ipc/kdbus/Documentation/kdbus.fs.xml
new file mode 100644 (file)
index 0000000..8c2a90e
--- /dev/null
@@ -0,0 +1,124 @@
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+        "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<refentry id="kdbus_fs">
+
+  <refentryinfo>
+    <title>kdbus.fs</title>
+    <productname>kdbus.fs</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>kdbus.fs</refentrytitle>
+    <manvolnum>7</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>kdbus.fs</refname>
+    <refpurpose>kdbus file system</refpurpose>
+  </refnamediv>
+
+  <refsect1>
+    <title>File-system Layout</title>
+
+    <para>
+      The <emphasis>kdbusfs</emphasis> pseudo filesystem provides access to
+      kdbus entities, such as <emphasis>buses</emphasis> and
+      <emphasis>endpoints</emphasis>. Each time the filesystem is mounted,
+      a new, isolated kdbus instance is created, which is independent from the
+      other instances.
+    </para>
+    <para>
+      The system-wide standard mount point for <emphasis>kdbusfs</emphasis> is
+      <constant>/sys/fs/kdbus</constant>.
+    </para>
+
+    <para>
+      Buses are represented as directories in the file system layout, whereas
+      endpoints are exposed as files inside these directories. At the top-level,
+      a <emphasis>control</emphasis> node is present, which can be opened to
+      create new buses via the <constant>KDBUS_CMD_BUS_MAKE</constant> ioctl.
+      Each <emphasis>bus</emphasis> shows a default endpoint called
+      <varname>bus</varname>, which can be opened to either create a connection
+      with the <constant>KDBUS_CMD_HELLO</constant> ioctl, or to create new
+      custom endpoints for the bus with
+      <constant>KDBUS_CMD_ENDPOINT_MAKE</constant>. See
+      <citerefentry>
+        <refentrytitle>kdbus.bus</refentrytitle>
+        <manvolnum>7</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+        <refentrytitle>kdbus.connection</refentrytitle>
+        <manvolnum>7</manvolnum>
+      </citerefentry> and
+      <citerefentry>
+        <refentrytitle>kdbus.endpoint</refentrytitle>
+        <manvolnum>7</manvolnum>
+      </citerefentry>
+      for more details.
+    </para>
+
+    <para>Following, you can see an example layout of the
+    <emphasis>kdbusfs</emphasis> filesystem:</para>
+
+<programlisting>
+        /sys/fs/kdbus/                          ; mount-point
+        |-- 0-system                            ; bus directory
+        |   |-- bus                             ; default endpoint
+        |   `-- 1017-custom                     ; custom endpoint
+        |-- 1000-user                           ; bus directory
+        |   |-- bus                             ; default endpoint
+        |   |-- 1000-service-A                  ; custom endpoint
+        |   `-- 1000-service-B                  ; custom endpoint
+        `-- control                             ; control file
+</programlisting>
+  </refsect1>
+
+  <refsect1>
+    <title>Mounting instances</title>
+    <para>
+      In order to get a new and separate kdbus environment, a new instance
+      of <emphasis>kdbusfs</emphasis> can be mounted like this:
+    </para>
+<programlisting>
+  # mount -t kdbusfs kdbusfs /tmp/new_kdbus/
+</programlisting>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+    <simplelist type="inline">
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.bus</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.connection</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.endpoint</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>mount</refentrytitle>
+          <manvolnum>8</manvolnum>
+        </citerefentry>
+      </member>
+    </simplelist>
+  </refsect1>
+</refentry>
diff --git a/ipc/kdbus/Documentation/kdbus.item.xml b/ipc/kdbus/Documentation/kdbus.item.xml
new file mode 100644 (file)
index 0000000..09f8b90
--- /dev/null
@@ -0,0 +1,839 @@
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+        "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<refentry id="kdbus">
+
+  <refentryinfo>
+    <title>kdbus.item</title>
+    <productname>kdbus item</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>kdbus.item</refentrytitle>
+    <manvolnum>7</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>kdbus.item</refname>
+    <refpurpose>kdbus item structure, layout and usage</refpurpose>
+  </refnamediv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>
+      To flexibly augment transport structures, data blobs of type
+      <type>struct kdbus_item</type> can be attached to the structs passed
+      into the ioctls. Some ioctls make items of certain types mandatory,
+      others are optional. Items that are unsupported by ioctls they are
+      attached to will cause the ioctl to fail with <varname>errno</varname>
+      set to <constant>EINVAL</constant>.
+      Items are also used for information stored in a connection's
+      <emphasis>pool</emphasis>, such as received messages, name lists or
+      requested connection or bus owner information. Depending on the type of
+      an item, its total size is either fixed or variable.
+    </para>
+
+    <refsect2>
+      <title>Chaining items</title>
+      <para>
+        Whenever items are used as part of the kdbus kernel API, they are
+        embedded in structs that are embedded inside structs that themselves
+        include a size field containing the overall size of the structure.
+        This allows multiple items to be chained up, and an item iterator
+        (see below) is capable of detecting the end of an item chain.
+      </para>
+    </refsect2>
+
+    <refsect2>
+      <title>Alignment</title>
+      <para>
+        The kernel expects all items to be aligned to 8-byte boundaries.
+        Unaligned items will cause the ioctl they are used with to fail
+        with <varname>errno</varname> set to <constant>EINVAL</constant>.
+        An item that has an unaligned size itself hence needs to be padded
+        if it is followed by another item.
+      </para>
+    </refsect2>
+
+    <refsect2>
+      <title>Iterating items</title>
+      <para>
+        A simple iterator would iterate over the items until the items have
+        reached the embedding structure's overall size. An example
+        implementation is shown below.
+      </para>
+
+      <programlisting><![CDATA[
+#define KDBUS_ALIGN8(val) (((val) + 7) & ~7)
+
+#define KDBUS_ITEM_NEXT(item) \
+    (typeof(item))(((uint8_t *)item) + KDBUS_ALIGN8((item)->size))
+
+#define KDBUS_ITEM_FOREACH(item, head, first)                      \
+    for (item = (head)->first;                                     \
+         ((uint8_t *)(item) < (uint8_t *)(head) + (head)->size) && \
+          ((uint8_t *)(item) >= (uint8_t *)(head));                \
+         item = KDBUS_ITEM_NEXT(item))
+      ]]></programlisting>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>Item layout</title>
+    <para>
+      A <type>struct kdbus_item</type> consists of a
+      <varname>size</varname> field, describing its overall size, and a
+      <varname>type</varname> field, both 64 bit wide. They are followed by
+      a union to store information that is specific to the item's type.
+      The struct layout is shown below.
+    </para>
+
+    <programlisting>
+struct kdbus_item {
+  __u64 size;
+  __u64 type;
+  /* item payload - see below */
+  union {
+    __u8 data[0];
+    __u32 data32[0];
+    __u64 data64[0];
+    char str[0];
+
+    __u64 id;
+    struct kdbus_vec vec;
+    struct kdbus_creds creds;
+    struct kdbus_pids pids;
+    struct kdbus_audit audit;
+    struct kdbus_caps caps;
+    struct kdbus_timestamp timestamp;
+    struct kdbus_name name;
+    struct kdbus_bloom_parameter bloom_parameter;
+    struct kdbus_bloom_filter bloom_filter;
+    struct kdbus_memfd memfd;
+    int fds[0];
+    struct kdbus_notify_name_change name_change;
+    struct kdbus_notify_id_change id_change;
+    struct kdbus_policy_access policy_access;
+  };
+};
+    </programlisting>
+
+    <para>
+      <type>struct kdbus_item</type> should never be used to allocate
+      an item instance, as its size may grow in future releases of the API.
+      Instead, it should be manually assembled by storing the
+      <varname>size</varname>, <varname>type</varname> and payload to a
+      struct of its own.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>Item types</title>
+
+    <refsect2>
+      <title>Negotiation item</title>
+      <variablelist>
+        <varlistentry>
+          <term><constant>KDBUS_ITEM_NEGOTIATE</constant></term>
+          <listitem><para>
+            With this item is attached to any ioctl, programs can
+            <emphasis>probe</emphasis> the kernel for known item types.
+            The item carries an array of <type>uint64_t</type> values in
+            <varname>item.data64</varname>, each set to an item type to
+            probe. The kernel will reset each member of this array that is
+            not recognized as valid item type to <constant>0</constant>.
+            This way, users can negotiate kernel features at start-up to
+            keep newer userspace compatible with older kernels. This item
+            is never attached by the kernel in response to any command.
+          </para></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+
+    <refsect2>
+      <title>Command specific items</title>
+      <variablelist>
+        <varlistentry>
+          <term><constant>KDBUS_ITEM_PAYLOAD_VEC</constant></term>
+          <term><constant>KDBUS_ITEM_PAYLOAD_OFF</constant></term>
+          <listitem><para>
+            Messages are directly copied by the sending process into the
+            receiver's
+            <citerefentry>
+              <refentrytitle>kdbus.pool</refentrytitle>
+              <manvolnum>7</manvolnum>
+            </citerefentry>.
+            This way, two peers can exchange data by effectively doing a
+            single-copy from one process to another; the kernel will not buffer
+            the data anywhere else. <constant>KDBUS_ITEM_PAYLOAD_VEC</constant>
+            is used when <emphasis>sending</emphasis> message. The item
+            references a memory address when the payload data can be found.
+            <constant>KDBUS_ITEM_PAYLOAD_OFF</constant> is used when messages
+            are <emphasis>received</emphasis>, and the
+            <constant>offset</constant> value describes the offset inside the
+            receiving connection's
+            <citerefentry>
+              <refentrytitle>kdbus.pool</refentrytitle>
+              <manvolnum>7</manvolnum>
+            </citerefentry>
+            where the message payload can be found. See
+            <citerefentry>
+              <refentrytitle>kdbus.message</refentrytitle>
+              <manvolnum>7</manvolnum>
+            </citerefentry>
+            for more information on passing of payload data along with a
+            message.
+            <programlisting>
+struct kdbus_vec {
+  __u64 size;
+  union {
+    __u64 address;
+    __u64 offset;
+  };
+};
+            </programlisting>
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>KDBUS_ITEM_PAYLOAD_MEMFD</constant></term>
+          <listitem><para>
+            Transports a file descriptor of a <emphasis>memfd</emphasis> in
+            <type>struct kdbus_memfd</type> in <varname>item.memfd</varname>.
+            The <varname>size</varname> field has to match the actual size of
+            the memfd that was specified when it was created. The
+            <varname>start</varname> parameter denotes the offset inside the
+            memfd at which the referenced payload starts. See
+            <citerefentry>
+              <refentrytitle>kdbus.message</refentrytitle>
+              <manvolnum>7</manvolnum>
+            </citerefentry>
+            for more information on passing of payload data along with a
+            message.
+            <programlisting>
+struct kdbus_memfd {
+  __u64 start;
+  __u64 size;
+  int fd;
+  __u32 __pad;
+};
+            </programlisting>
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>KDBUS_ITEM_FDS</constant></term>
+          <listitem><para>
+            Contains an array of <emphasis>file descriptors</emphasis>.
+            When used with <constant>KDBUS_CMD_SEND</constant>, the values of
+            this array must be filled with valid file descriptor numbers.
+            When received as item attached to a message, the array will
+            contain the numbers of the installed file descriptors, or
+            <constant>-1</constant> in case an error occurred.
+            In either case, the number of entries in the array is derived from
+            the item's total size. See
+            <citerefentry>
+              <refentrytitle>kdbus.message</refentrytitle>
+              <manvolnum>7</manvolnum>
+            </citerefentry>
+            for more information.
+          </para></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+
+    <refsect2>
+      <title>Items specific to some commands</title>
+      <variablelist>
+        <varlistentry>
+          <term><constant>KDBUS_ITEM_CANCEL_FD</constant></term>
+          <listitem><para>
+            Transports a file descriptor that can be used to cancel a
+            synchronous <constant>KDBUS_CMD_SEND</constant> operation by
+            writing to it. The file descriptor is stored in
+            <varname>item.fd[0]</varname>. The item may only contain one
+            file descriptor. See
+            <citerefentry>
+              <refentrytitle>kdbus.message</refentrytitle>
+              <manvolnum>7</manvolnum>
+            </citerefentry>
+            for more information on this item and how to use it.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>KDBUS_ITEM_BLOOM_PARAMETER</constant></term>
+          <listitem><para>
+            Contains a set of <emphasis>bloom parameters</emphasis> as
+            <type>struct kdbus_bloom_parameter</type> in
+            <varname>item.bloom_parameter</varname>.
+            The item is passed from userspace to kernel during the
+            <constant>KDBUS_CMD_BUS_MAKE</constant> ioctl, and returned
+            verbatim when <constant>KDBUS_CMD_HELLO</constant> is called.
+            The kernel does not use the bloom parameters, but they need to
+            be known by each connection on the bus in order to define the
+            bloom filter hash details. See
+            <citerefentry>
+              <refentrytitle>kdbus.match</refentrytitle>
+              <manvolnum>7</manvolnum>
+            </citerefentry>
+            for more information on matching and bloom filters.
+            <programlisting>
+struct kdbus_bloom_parameter {
+  __u64 size;
+  __u64 n_hash;
+};
+            </programlisting>
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>KDBUS_ITEM_BLOOM_FILTER</constant></term>
+          <listitem><para>
+            Carries a <emphasis>bloom filter</emphasis> as
+            <type>struct kdbus_bloom_filter</type> in
+            <varname>item.bloom_filter</varname>. It is mandatory to send this
+            item attached to a <type>struct kdbus_msg</type>, in case the
+            message is a signal. This item is never transported from kernel to
+            userspace. See
+            <citerefentry>
+              <refentrytitle>kdbus.match</refentrytitle>
+              <manvolnum>7</manvolnum>
+            </citerefentry>
+            for more information on matching and bloom filters.
+            <programlisting>
+struct kdbus_bloom_filter {
+  __u64 generation;
+  __u64 data[0];
+};
+            </programlisting>
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>KDBUS_ITEM_BLOOM_MASK</constant></term>
+          <listitem><para>
+            Transports a <emphasis>bloom mask</emphasis> as binary data blob
+            stored in <varname>item.data</varname>. This item is used to
+            describe a match into a connection's match database. See
+            <citerefentry>
+              <refentrytitle>kdbus.match</refentrytitle>
+              <manvolnum>7</manvolnum>
+            </citerefentry>
+            for more information on matching and bloom filters.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>KDBUS_ITEM_DST_NAME</constant></term>
+          <listitem><para>
+            Contains a <emphasis>well-known name</emphasis> to send a
+            message to, as null-terminated string in
+            <varname>item.str</varname>. This item is used with
+            <constant>KDBUS_CMD_SEND</constant>. See
+            <citerefentry>
+              <refentrytitle>kdbus.message</refentrytitle>
+              <manvolnum>7</manvolnum>
+            </citerefentry>
+            for more information on how to send a message.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>KDBUS_ITEM_MAKE_NAME</constant></term>
+          <listitem><para>
+            Contains a <emphasis>bus name</emphasis> or
+            <emphasis>endpoint name</emphasis>, stored as null-terminated
+            string in <varname>item.str</varname>. This item is sent from
+            userspace to kernel when buses or endpoints are created, and
+            returned back to userspace when the bus creator information is
+            queried. See
+            <citerefentry>
+              <refentrytitle>kdbus.bus</refentrytitle>
+              <manvolnum>7</manvolnum>
+            </citerefentry>
+            and
+            <citerefentry>
+              <refentrytitle>kdbus.endpoint</refentrytitle>
+              <manvolnum>7</manvolnum>
+            </citerefentry>.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>KDBUS_ITEM_ATTACH_FLAGS_SEND</constant></term>
+          <term><constant>KDBUS_ITEM_ATTACH_FLAGS_RECV</constant></term>
+          <listitem><para>
+            Contains a set of <emphasis>attach flags</emphasis> at
+            <emphasis>send</emphasis> or <emphasis>receive</emphasis> time. See
+            <citerefentry>
+              <refentrytitle>kdbus</refentrytitle>
+              <manvolnum>7</manvolnum>
+            </citerefentry>,
+            <citerefentry>
+              <refentrytitle>kdbus.bus</refentrytitle>
+              <manvolnum>7</manvolnum>
+            </citerefentry> and
+            <citerefentry>
+              <refentrytitle>kdbus.connection</refentrytitle>
+              <manvolnum>7</manvolnum>
+            </citerefentry>
+            for more information on attach flags.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>KDBUS_ITEM_ID</constant></term>
+          <listitem><para>
+            Transports a connection's <emphasis>numerical ID</emphasis> of
+            a connection as <type>uint64_t</type> value in
+            <varname>item.id</varname>.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>KDBUS_ITEM_NAME</constant></term>
+          <listitem><para>
+            Transports a name associated with the
+            <emphasis>name registry</emphasis> as null-terminated string as
+            <type>struct kdbus_name</type> in
+            <varname>item.name</varname>. The <varname>flags</varname>
+            contains the flags of the name. See
+            <citerefentry>
+              <refentrytitle>kdbus.name</refentrytitle>
+              <manvolnum>7</manvolnum>
+            </citerefentry>
+            for more information on how to access the name registry of a bus.
+            <programlisting>
+struct kdbus_name {
+  __u64 flags;
+  char name[0];
+};
+            </programlisting>
+          </para></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+
+    <refsect2>
+      <title>Items attached by the kernel as metadata</title>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>KDBUS_ITEM_TIMESTAMP</constant></term>
+          <listitem><para>
+            Contains both the <emphasis>monotonic</emphasis> and the
+            <emphasis>realtime</emphasis> timestamp, taken when the message
+            was processed on the kernel side.
+            Stored as <type>struct kdbus_timestamp</type> in
+            <varname>item.timestamp</varname>.
+            <programlisting>
+struct kdbus_timestamp {
+  __u64 seqnum;
+  __u64 monotonic_ns;
+  __u64 realtime_ns;
+};
+            </programlisting>
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>KDBUS_ITEM_CREDS</constant></term>
+          <listitem><para>
+            Contains a set of <emphasis>user</emphasis> and
+            <emphasis>group</emphasis> information as 32-bit values, in the
+            usual four flavors: real, effective, saved and filesystem related.
+            Stored as <type>struct kdbus_creds</type> in
+            <varname>item.creds</varname>.
+            <programlisting>
+struct kdbus_creds {
+  __u32 uid;
+  __u32 euid;
+  __u32 suid;
+  __u32 fsuid;
+  __u32 gid;
+  __u32 egid;
+  __u32 sgid;
+  __u32 fsgid;
+};
+            </programlisting>
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>KDBUS_ITEM_PIDS</constant></term>
+          <listitem><para>
+            Contains the <emphasis>PID</emphasis>, <emphasis>TID</emphasis>
+            and <emphasis>parent PID (PPID)</emphasis> of a remote peer.
+            Stored as <type>struct kdbus_pids</type> in
+            <varname>item.pids</varname>.
+            <programlisting>
+struct kdbus_pids {
+  __u64 pid;
+  __u64 tid;
+  __u64 ppid;
+};
+            </programlisting>
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>KDBUS_ITEM_AUXGROUPS</constant></term>
+          <listitem><para>
+            Contains the <emphasis>auxiliary (supplementary) groups</emphasis>
+            a remote peer is a member of, stored as array of
+            <type>uint32_t</type> values in <varname>item.data32</varname>.
+            The array length can be determined by looking at the item's total
+            size, subtracting the size of the header and dividing the
+            remainder by <constant>sizeof(uint32_t)</constant>.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>KDBUS_ITEM_OWNED_NAME</constant></term>
+          <listitem><para>
+            Contains a <emphasis>well-known name</emphasis> currently owned
+            by a connection. The name is stored as null-terminated string in
+            <varname>item.str</varname>. Its length can also be derived from
+            the item's total size.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>KDBUS_ITEM_TID_COMM</constant> [*]</term>
+          <listitem><para>
+            Contains the <emphasis>comm</emphasis> string of a task's
+            <emphasis>TID</emphasis> (thread ID), stored as null-terminated
+            string in <varname>item.str</varname>. Its length can also be
+            derived from the item's total size. Receivers of this item should
+            not use its contents for any kind of security measures. See below.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>KDBUS_ITEM_PID_COMM</constant> [*]</term>
+          <listitem><para>
+            Contains the <emphasis>comm</emphasis> string of a task's
+            <emphasis>PID</emphasis> (process ID), stored as null-terminated
+            string in <varname>item.str</varname>. Its length can also be
+            derived from the item's total size. Receivers of this item should
+            not use its contents for any kind of security measures. See below.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>KDBUS_ITEM_EXE</constant> [*]</term>
+          <listitem><para>
+            Contains the <emphasis>path to the executable</emphasis> of a task,
+            stored as null-terminated string in <varname>item.str</varname>. Its
+            length can also be derived from the item's total size. Receivers of
+            this item should not use its contents for any kind of security
+            measures. See below.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>KDBUS_ITEM_CMDLINE</constant> [*]</term>
+          <listitem><para>
+            Contains the <emphasis>command line arguments</emphasis> of a
+            task, stored as an <emphasis>array</emphasis> of null-terminated
+            strings in <varname>item.str</varname>. The total length of all
+            strings in the array can be derived from the item's total size.
+            Receivers of this item should not use its contents for any kind
+            of security measures. See below.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>KDBUS_ITEM_CGROUP</constant></term>
+          <listitem><para>
+            Contains the <emphasis>cgroup path</emphasis> of a task, stored
+            as null-terminated string in <varname>item.str</varname>. Its
+            length can also be derived from the item's total size.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>KDBUS_ITEM_CAPS</constant></term>
+          <listitem><para>
+            Contains sets of <emphasis>capabilities</emphasis>, stored as
+            <type>struct kdbus_caps</type> in <varname>item.caps</varname>.
+            As the item size may increase in the future, programs should be
+            written in a way that it takes
+            <varname>item.caps.last_cap</varname> into account, and derive
+            the number of sets and rows from the item size and the reported
+            number of valid capability bits.
+            <programlisting>
+struct kdbus_caps {
+  __u32 last_cap;
+  __u32 caps[0];
+};
+            </programlisting>
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>KDBUS_ITEM_SECLABEL</constant></term>
+          <listitem><para>
+            Contains the <emphasis>LSM label</emphasis> of a task, stored as
+            null-terminated string in <varname>item.str</varname>. Its length
+            can also be derived from the item's total size.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>KDBUS_ITEM_AUDIT</constant></term>
+          <listitem><para>
+            Contains the audit <emphasis>sessionid</emphasis> and
+            <emphasis>loginuid</emphasis> of a task, stored as
+            <type>struct kdbus_audit</type> in
+            <varname>item.audit</varname>.
+            <programlisting>
+struct kdbus_audit {
+  __u32 sessionid;
+  __u32 loginuid;
+};
+            </programlisting>
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>KDBUS_ITEM_CONN_DESCRIPTION</constant></term>
+          <listitem><para>
+            Contains the <emphasis>connection description</emphasis>, as set
+            by <constant>KDBUS_CMD_HELLO</constant> or
+            <constant>KDBUS_CMD_CONN_UPDATE</constant>, stored as
+            null-terminated string in <varname>item.str</varname>. Its length
+            can also be derived from the item's total size.
+          </para></listitem>
+        </varlistentry>
+      </variablelist>
+
+      <para>
+        All metadata is automatically translated into the
+        <emphasis>namespaces</emphasis> of the task that receives them. See
+        <citerefentry>
+          <refentrytitle>kdbus.message</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+        for more information.
+      </para>
+
+      <para>
+        [*] Note that the content stored in metadata items of type
+        <constant>KDBUS_ITEM_TID_COMM</constant>,
+        <constant>KDBUS_ITEM_PID_COMM</constant>,
+        <constant>KDBUS_ITEM_EXE</constant> and
+        <constant>KDBUS_ITEM_CMDLINE</constant>
+        can easily be tampered by the sending tasks. Therefore, they should
+        <emphasis>not</emphasis> be used for any sort of security relevant
+        assumptions. The only reason they are transmitted is to let
+        receivers know about details that were set when metadata was
+        collected, even though the task they were collected from is not
+        active any longer when the items are received.
+      </para>
+    </refsect2>
+
+    <refsect2>
+      <title>Items used for policy entries, matches and notifications</title>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>KDBUS_ITEM_POLICY_ACCESS</constant></term>
+          <listitem><para>
+            This item describes a <emphasis>policy access</emphasis> entry to
+            access the policy database of a
+            <citerefentry>
+              <refentrytitle>kdbus.bus</refentrytitle>
+              <manvolnum>7</manvolnum>
+            </citerefentry> or
+            <citerefentry>
+              <refentrytitle>kdbus.endpoint</refentrytitle>
+              <manvolnum>7</manvolnum>
+            </citerefentry>.
+            Please refer to
+            <citerefentry>
+              <refentrytitle>kdbus.policy</refentrytitle>
+              <manvolnum>7</manvolnum>
+            </citerefentry>
+            for more information on the policy database and how to access it.
+            <programlisting>
+struct kdbus_policy_access {
+  __u64 type;
+  __u64 access;
+  __u64 id;
+};
+            </programlisting>
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>KDBUS_ITEM_ID_ADD</constant></term>
+          <term><constant>KDBUS_ITEM_ID_REMOVE</constant></term>
+          <listitem><para>
+            This item is sent as attachment to a
+            <emphasis>kernel notification</emphasis> and indicates that a
+            new connection was created on the bus, or that a connection was
+            disconnected, respectively. It stores a
+            <type>struct kdbus_notify_id_change</type> in
+            <varname>item.id_change</varname>.
+            The <varname>id</varname> field contains the numeric ID of the
+            connection that was added or removed, and <varname>flags</varname>
+            is set to the connection flags, as passed by
+            <constant>KDBUS_CMD_HELLO</constant>. See
+            <citerefentry>
+              <refentrytitle>kdbus.match</refentrytitle>
+              <manvolnum>7</manvolnum>
+            </citerefentry>
+            and
+            <citerefentry>
+              <refentrytitle>kdbus.message</refentrytitle>
+              <manvolnum>7</manvolnum>
+            </citerefentry>
+            for more information on matches and notification messages.
+            <programlisting>
+struct kdbus_notify_id_change {
+  __u64 id;
+  __u64 flags;
+};
+            </programlisting>
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>KDBUS_ITEM_NAME_ADD</constant></term>
+          <term><constant>KDBUS_ITEM_NAME_REMOVE</constant></term>
+          <term><constant>KDBUS_ITEM_NAME_CHANGE</constant></term>
+          <listitem><para>
+            This item is sent as attachment to a
+            <emphasis>kernel notification</emphasis> and indicates that a
+            <emphasis>well-known name</emphasis> appeared, disappeared or
+            transferred to another owner on the bus. It stores a
+            <type>struct kdbus_notify_name_change</type> in
+            <varname>item.name_change</varname>.
+            <varname>old_id</varname> describes the former owner of the name
+            and is set to <constant>0</constant> values in case of
+            <constant>KDBUS_ITEM_NAME_ADD</constant>.
+            <varname>new_id</varname> describes the new owner of the name and
+            is set to <constant>0</constant> values in case of
+            <constant>KDBUS_ITEM_NAME_REMOVE</constant>.
+            The <varname>name</varname> field contains the well-known name the
+            notification is about, as null-terminated string. See
+            <citerefentry>
+              <refentrytitle>kdbus.match</refentrytitle>
+              <manvolnum>7</manvolnum>
+            </citerefentry>
+            and
+            <citerefentry>
+              <refentrytitle>kdbus.message</refentrytitle>
+              <manvolnum>7</manvolnum>
+            </citerefentry>
+            for more information on matches and notification messages.
+            <programlisting>
+struct kdbus_notify_name_change {
+  struct kdbus_notify_id_change old_id;
+  struct kdbus_notify_id_change new_id;
+  char name[0];
+};
+            </programlisting>
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>KDBUS_ITEM_REPLY_TIMEOUT</constant></term>
+          <listitem><para>
+            This item is sent as attachment to a
+            <emphasis>kernel notification</emphasis>. It informs the receiver
+            that an expected reply to a message was not received in time.
+            The remote peer ID and the message cookie are stored in the message
+            header. See
+            <citerefentry>
+              <refentrytitle>kdbus.message</refentrytitle>
+              <manvolnum>7</manvolnum>
+            </citerefentry>
+            for more information about messages, timeouts and notifications.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>KDBUS_ITEM_REPLY_DEAD</constant></term>
+          <listitem><para>
+            This item is sent as attachment to a
+            <emphasis>kernel notification</emphasis>. It informs the receiver
+            that a remote connection a reply is expected from was disconnected
+            before that reply was sent. The remote peer ID and the message
+            cookie are stored in the message header. See
+            <citerefentry>
+              <refentrytitle>kdbus.message</refentrytitle>
+              <manvolnum>7</manvolnum>
+            </citerefentry>
+            for more information about messages, timeouts and notifications.
+          </para></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+    <simplelist type="inline">
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.bus</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.connection</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.endpoint</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.fs</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.message</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.name</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.pool</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>memfd_create</refentrytitle>
+          <manvolnum>2</manvolnum>
+        </citerefentry>
+      </member>
+    </simplelist>
+  </refsect1>
+
+</refentry>
diff --git a/ipc/kdbus/Documentation/kdbus.match.xml b/ipc/kdbus/Documentation/kdbus.match.xml
new file mode 100644 (file)
index 0000000..ae38e04
--- /dev/null
@@ -0,0 +1,555 @@
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+        "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<refentry id="kdbus.match">
+
+  <refentryinfo>
+    <title>kdbus.match</title>
+    <productname>kdbus.match</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>kdbus.match</refentrytitle>
+    <manvolnum>7</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>kdbus.match</refname>
+    <refpurpose>kdbus match</refpurpose>
+  </refnamediv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>
+      kdbus connections can install matches in order to subscribe to signal
+      messages sent on the bus. Such signal messages can be either directed
+      to a single connection (by setting a specific connection ID in
+      <varname>struct kdbus_msg.dst_id</varname> or by sending it to a
+      well-known name), or to potentially <emphasis>all</emphasis> currently
+      active connections on the bus (by setting
+      <varname>struct kdbus_msg.dst_id</varname> to
+      <constant>KDBUS_DST_ID_BROADCAST</constant>).
+      A signal message always has the <constant>KDBUS_MSG_SIGNAL</constant>
+      bit set in the <varname>flags</varname> bitfield.
+      Also, signal messages can originate from either the kernel (called
+      <emphasis>notifications</emphasis>), or from other bus connections.
+      In either case, a bus connection needs to have a suitable
+      <emphasis>match</emphasis> installed in order to receive any signal
+      message. Without any rules installed in the connection, no signal message
+      will be received.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>Matches for signal messages from other connections</title>
+    <para>
+      Matches for messages from other connections (not kernel notifications)
+      are implemented as bloom filters (see below). The sender adds certain
+      properties of the message as elements to a bloom filter bit field, and
+      sends that along with the signal message.
+
+      The receiving connection adds the message properties it is interested in
+      as elements to a bloom mask bit field, and uploads the mask as match rule,
+      possibly along with some other rules to further limit the match.
+
+      The kernel will match the signal message's bloom filter against the
+      connection's bloom mask (simply by &amp;-ing it), and will decide whether
+      the message should be delivered to a connection.
+    </para>
+    <para>
+      The kernel has no notion of any specific properties of the signal message,
+      all it sees are the bit fields of the bloom filter and the mask to match
+      against. The use of bloom filters allows simple and efficient matching,
+      without exposing any message properties or internals to the kernel side.
+      Clients need to deal with the fact that they might receive signal messages
+      which they did not subscribe to, as the bloom filter might allow
+      false-positives to pass the filter.
+
+      To allow the future extension of the set of elements in the bloom filter,
+      the filter specifies a <emphasis>generation</emphasis> number. A later
+      generation must always contain all elements of the set of the previous
+      generation, but can add new elements to the set. The match rules mask can
+      carry an array with all previous generations of masks individually stored.
+      When the filter and mask are matched by the kernel, the mask with the
+      closest matching generation is selected as the index into the mask array.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>Bloom filters</title>
+    <para>
+      Bloom filters allow checking whether a given word is present in a
+      dictionary.  This allows connections to set up a mask for information it
+      is interested in, and will be delivered signal messages that have a
+      matching filter.
+
+      For general information, see
+      <ulink url="https://en.wikipedia.org/wiki/Bloom_filter">the Wikipedia
+      article on bloom filters</ulink>.
+    </para>
+    <para>
+      The size of the bloom filter is defined per bus when it is created, in
+      <varname>kdbus_bloom_parameter.size</varname>. All bloom filters attached
+      to signal messages on the bus must match this size, and all bloom filter
+      matches uploaded by connections must also match the size, or a multiple
+      thereof (see below).
+
+      The calculation of the mask has to be done in userspace applications. The
+      kernel just checks the bitmasks to decide whether or not to let the
+      message pass. All bits in the mask must match the filter in and bit-wise
+      <emphasis>AND</emphasis> logic, but the mask may have more bits set than
+      the filter. Consequently, false positive matches are expected to happen,
+      and programs must deal with that fact by checking the contents of the
+      payload again at receive time.
+    </para>
+    <para>
+      Masks are entities that are always passed to the kernel as part of a
+      match (with an item of type <constant>KDBUS_ITEM_BLOOM_MASK</constant>),
+      and filters can be attached to signals, with an item of type
+      <constant>KDBUS_ITEM_BLOOM_FILTER</constant>. For a filter to match, all
+      its bits have to be set in the match mask as well.
+    </para>
+    <para>
+      For example, consider a bus that has a bloom size of 8 bytes, and the
+      following mask/filter combinations:
+    </para>
+    <programlisting><![CDATA[
+          filter  0x0101010101010101
+          mask    0x0101010101010101
+                  -> matches
+
+          filter  0x0303030303030303
+          mask    0x0101010101010101
+                  -> doesn't match
+
+          filter  0x0101010101010101
+          mask    0x0303030303030303
+                  -> matches
+    ]]></programlisting>
+
+    <para>
+      Hence, in order to catch all messages, a mask filled with
+      <constant>0xff</constant> bytes can be installed as a wildcard match rule.
+    </para>
+
+    <refsect2>
+      <title>Generations</title>
+
+      <para>
+        Uploaded matches may contain multiple masks, which have to be as large
+        as the bloom filter size defined by the bus. Each block of a mask is
+        called a <emphasis>generation</emphasis>, starting at index 0.
+
+        At match time, when a signal is about to be delivered, a bloom mask
+        generation is passed, which denotes which of the bloom masks the filter
+        should be matched against. This allows programs to provide backward
+        compatible masks at upload time, while older clients can still match
+        against older versions of filters.
+      </para>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>Matches for kernel notifications</title>
+    <para>
+      To receive kernel generated notifications (see
+      <citerefentry>
+        <refentrytitle>kdbus.message</refentrytitle>
+        <manvolnum>7</manvolnum>
+      </citerefentry>),
+      a connection must install match rules that are different from
+      the bloom filter matches described in the section above. They can be
+      filtered by the connection ID that caused the notification to be sent, by
+      one of the names it currently owns, or by the type of the notification
+      (ID/name add/remove/change).
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>Adding a match</title>
+    <para>
+      To add a match, the <constant>KDBUS_CMD_MATCH_ADD</constant> ioctl is
+      used, which takes a <type>struct kdbus_cmd_match</type> as an argument
+      described below.
+
+      Note that each of the items attached to this command will internally
+      create one match <emphasis>rule</emphasis>, and the collection of them,
+      which is submitted as one block via the ioctl, is called a
+      <emphasis>match</emphasis>. To allow a message to pass, all rules of a
+      match have to be satisfied. Hence, adding more items to the command will
+      only narrow the possibility of a match to effectively let the message
+      pass, and will decrease the chance that the connection's process will be
+      woken up needlessly.
+
+      Multiple matches can be installed per connection. As long as one of it has
+      a set of rules which allows the message to pass, this one will be
+      decisive.
+    </para>
+
+    <programlisting>
+struct kdbus_cmd_match {
+  __u64 size;
+  __u64 flags;
+  __u64 return_flags;
+  __u64 cookie;
+  struct kdbus_item items[0];
+};
+    </programlisting>
+
+    <para>The fields in this struct are described below.</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><varname>size</varname></term>
+        <listitem><para>
+          The overall size of the struct, including its items.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>flags</varname></term>
+        <listitem><para>Flags to control the behavior of the ioctl.</para>
+          <variablelist>
+            <varlistentry>
+              <term><constant>KDBUS_MATCH_REPLACE</constant></term>
+              <listitem>
+                <para>Make the endpoint file group-accessible</para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_FLAG_NEGOTIATE</constant></term>
+              <listitem>
+                <para>
+                  Requests a set of valid flags for this ioctl. When this bit is
+                  set, no action is taken; the ioctl will return
+                  <errorcode>0</errorcode>, and the <varname>flags</varname>
+                  field will have all bits set that are valid for this command.
+                  The <constant>KDBUS_FLAG_NEGOTIATE</constant> bit will be
+                  cleared by the operation.
+                </para>
+              </listitem>
+            </varlistentry>
+          </variablelist>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>return_flags</varname></term>
+        <listitem><para>
+          Flags returned by the kernel. Currently unused and always set to
+          <constant>0</constant> by the kernel.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>cookie</varname></term>
+        <listitem><para>
+          A cookie which identifies the match, so it can be referred to when
+          removing it.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>items</varname></term>
+        <listitem>
+        <para>
+          Items to define the actual rules of the matches. The following item
+          types are expected. Each item will create one new match rule.
+        </para>
+          <variablelist>
+            <varlistentry>
+              <term><constant>KDBUS_ITEM_BLOOM_MASK</constant></term>
+              <listitem>
+                <para>
+                  An item that carries the bloom filter mask to match against
+                  in its data field. The payload size must match the bloom
+                  filter size that was specified when the bus was created.
+                  See the "Bloom filters" section above for more information on
+                  bloom filters.
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_ITEM_NAME</constant></term>
+              <listitem>
+                <para>
+                  When used as part of kernel notifications, this item specifies
+                  a name that is acquired, lost or that changed its owner (see
+                  below). When used as part of a match for user-generated signal
+                  messages, it specifies a name that the sending connection must
+                  own at the time of sending the signal.
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_ITEM_ID</constant></term>
+              <listitem>
+                <para>
+                  Specify a sender connection's ID that will match this rule.
+                  For kernel notifications, this specifies the ID of a
+                  connection that was added to or removed from the bus.
+                  For used-generated signals, it specifies the ID of the
+                  connection that sent the signal message.
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_ITEM_NAME_ADD</constant></term>
+              <term><constant>KDBUS_ITEM_NAME_REMOVE</constant></term>
+              <term><constant>KDBUS_ITEM_NAME_CHANGE</constant></term>
+              <listitem>
+                <para>
+                  These items request delivery of kernel notifications that
+                  describe a name acquisition, loss, or change. The details
+                  are stored in the item's
+                  <varname>kdbus_notify_name_change</varname> member.
+                  All information specified must be matched in order to make
+                  the message pass. Use
+                  <constant>KDBUS_MATCH_ID_ANY</constant> to
+                  match against any unique connection ID.
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_ITEM_ID_ADD</constant></term>
+              <term><constant>KDBUS_ITEM_ID_REMOVE</constant></term>
+              <listitem>
+                <para>
+                  These items request delivery of kernel notifications that are
+                  generated when a connection is created or terminated.
+                  <type>struct kdbus_notify_id_change</type> is used to
+                  store the actual match information. This item can be used to
+                  monitor one particular connection ID, or, when the ID field
+                  is set to <constant>KDBUS_MATCH_ID_ANY</constant>,
+                  all of them.
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_ITEM_NEGOTIATE</constant></term>
+              <listitem><para>
+                With this item, programs can <emphasis>probe</emphasis> the
+                kernel for known item types. See
+                <citerefentry>
+                  <refentrytitle>kdbus.item</refentrytitle>
+                  <manvolnum>7</manvolnum>
+                </citerefentry>
+                for more details.
+              </para></listitem>
+            </varlistentry>
+          </variablelist>
+
+          <para>
+            Unrecognized items are rejected, and the ioctl will fail with
+            <varname>errno</varname> set to <constant>EINVAL</constant>.
+          </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <para>
+      Refer to
+      <citerefentry>
+        <refentrytitle>kdbus.message</refentrytitle>
+        <manvolnum>7</manvolnum>
+      </citerefentry>
+      for more information on message types.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>Removing a match</title>
+    <para>
+      Matches can be removed with the
+      <constant>KDBUS_CMD_MATCH_REMOVE</constant> ioctl, which takes
+      <type>struct kdbus_cmd_match</type> as argument, but its fields
+      usage slightly differs compared to that of
+      <constant>KDBUS_CMD_MATCH_ADD</constant>.
+    </para>
+
+    <programlisting>
+struct kdbus_cmd_match {
+  __u64 size;
+  __u64 cookie;
+  __u64 flags;
+  __u64 return_flags;
+  struct kdbus_item items[0];
+};
+    </programlisting>
+
+    <para>The fields in this struct are described below.</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><varname>size</varname></term>
+        <listitem><para>
+          The overall size of the struct, including its items.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>cookie</varname></term>
+        <listitem><para>
+          The cookie of the match, as it was passed when the match was added.
+          All matches that have this cookie will be removed.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>flags</varname></term>
+        <listitem><para>
+          No flags are supported for this use case.
+          <constant>KDBUS_FLAG_NEGOTIATE</constant> is accepted to probe for
+          valid flags. If set, the ioctl will fail with
+          <errorcode>-1</errorcode>, <varname>errno</varname> is set to
+          <constant>EPROTO</constant>, and the <varname>flags</varname> field
+          is set to <constant>0</constant>.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>return_flags</varname></term>
+        <listitem><para>
+          Flags returned by the kernel. Currently unused and always set to
+          <constant>0</constant> by the kernel.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>items</varname></term>
+        <listitem>
+          <para>
+            No items are supported for this use case, but
+            <constant>KDBUS_ITEM_NEGOTIATE</constant> is allowed nevertheless.
+          </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Return value</title>
+    <para>
+      On success, all mentioned ioctl commands return <errorcode>0</errorcode>;
+      on error, <errorcode>-1</errorcode> is returned, and
+      <varname>errno</varname> is set to indicate the error.
+      If the issued ioctl is illegal for the file descriptor used,
+      <varname>errno</varname> will be set to <constant>ENOTTY</constant>.
+    </para>
+
+    <refsect2>
+      <title>
+        <constant>KDBUS_CMD_MATCH_ADD</constant> may fail with the following
+        errors
+      </title>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>EINVAL</constant></term>
+          <listitem><para>
+            Illegal flags or items.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>EDOM</constant></term>
+          <listitem><para>
+            Illegal bloom filter size.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>EMFILE</constant></term>
+          <listitem><para>
+            Too many matches for this connection.
+          </para></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+
+    <refsect2>
+      <title>
+        <constant>KDBUS_CMD_MATCH_REMOVE</constant> may fail with the following
+        errors
+      </title>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>EINVAL</constant></term>
+          <listitem><para>
+            Illegal flags.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>EBADSLT</constant></term>
+          <listitem><para>
+            A match entry with the given cookie could not be found.
+          </para></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+    <simplelist type="inline">
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.bus</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.match</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.fs</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.item</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.message</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.name</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.pool</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+    </simplelist>
+  </refsect1>
+</refentry>
diff --git a/ipc/kdbus/Documentation/kdbus.message.xml b/ipc/kdbus/Documentation/kdbus.message.xml
new file mode 100644 (file)
index 0000000..0115d9d
--- /dev/null
@@ -0,0 +1,1276 @@
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+        "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<refentry id="kdbus.message">
+
+  <refentryinfo>
+    <title>kdbus.message</title>
+    <productname>kdbus.message</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>kdbus.message</refentrytitle>
+    <manvolnum>7</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>kdbus.message</refname>
+    <refpurpose>kdbus message</refpurpose>
+  </refnamediv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>
+      A kdbus message is used to exchange information between two connections
+      on a bus, or to transport notifications from the kernel to one or many
+      connections. This document describes the layout of messages, how payload
+      is added to them and how they are sent and received.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>Message layout</title>
+
+    <para>The layout of a message is shown below.</para>
+
+    <programlisting>
+  +-------------------------------------------------------------------------+
+  | Message                                                                 |
+  | +---------------------------------------------------------------------+ |
+  | | Header                                                              | |
+  | | size:          overall message size, including the data records     | |
+  | | destination:   connection ID of the receiver                        | |
+  | | source:        connection ID of the sender (set by kernel)          | |
+  | | payload_type:  "DBusDBus" textual identifier stored as uint64_t     | |
+  | +---------------------------------------------------------------------+ |
+  | +---------------------------------------------------------------------+ |
+  | | Data Record                                                         | |
+  | | size:  overall record size (without padding)                        | |
+  | | type:  type of data                                                 | |
+  | | data:  reference to data (address or file descriptor)               | |
+  | +---------------------------------------------------------------------+ |
+  | +---------------------------------------------------------------------+ |
+  | | padding bytes to the next 8 byte alignment                          | |
+  | +---------------------------------------------------------------------+ |
+  | +---------------------------------------------------------------------+ |
+  | | Data Record                                                         | |
+  | | size:  overall record size (without padding)                        | |
+  | | ...                                                                 | |
+  | +---------------------------------------------------------------------+ |
+  | +---------------------------------------------------------------------+ |
+  | | padding bytes to the next 8 byte alignment                          | |
+  | +---------------------------------------------------------------------+ |
+  | +---------------------------------------------------------------------+ |
+  | | Data Record                                                         | |
+  | | size:  overall record size                                          | |
+  | | ...                                                                 | |
+  | +---------------------------------------------------------------------+ |
+  |   ... further data records ...                                          |
+  +-------------------------------------------------------------------------+
+    </programlisting>
+  </refsect1>
+
+  <refsect1>
+    <title>Message payload</title>
+
+    <para>
+      When connecting to the bus, receivers request a memory pool of a given
+      size, large enough to carry all backlog of data enqueued for the
+      connection. The pool is internally backed by a shared memory file which
+      can be <function>mmap()</function>ed by the receiver. See
+      <citerefentry>
+        <refentrytitle>kdbus.pool</refentrytitle>
+        <manvolnum>7</manvolnum>
+      </citerefentry>
+      for more information.
+    </para>
+
+    <para>
+      Message payload must be described in items attached to a message when
+      it is sent. A receiver can access the payload by looking at the items
+      that are attached to a message in its pool. The following items are used.
+    </para>
+
+    <variablelist>
+      <varlistentry>
+        <term><constant>KDBUS_ITEM_PAYLOAD_VEC</constant></term>
+        <listitem>
+          <para>
+            This item references a piece of memory on the sender side which is
+            directly copied into the receiver's pool. This way, two peers can
+            exchange data by effectively doing a single-copy from one process
+            to another; the kernel will not buffer the data anywhere else.
+            This item is never found in a message received by a connection.
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>KDBUS_ITEM_PAYLOAD_OFF</constant></term>
+        <listitem>
+          <para>
+            This item is attached to messages on the receiving side and points
+            to a memory area inside the receiver's pool. The
+            <varname>offset</varname> variable in the item denotes the memory
+            location relative to the message itself.
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>KDBUS_ITEM_PAYLOAD_MEMFD</constant></term>
+        <listitem>
+          <para>
+            Messages can reference <emphasis>memfd</emphasis> files which
+            contain the data. memfd files are tmpfs-backed files that allow
+            sealing of the content of the file, which prevents all writable
+            access to the file content.
+          </para>
+          <para>
+            Only memfds that have
+            <constant>(F_SEAL_SHRINK|F_SEAL_GROW|F_SEAL_WRITE|F_SEAL_SEAL)
+            </constant>
+            set are accepted as payload data, which enforces reliable passing of
+            data. The receiver can assume that neither the sender nor anyone
+            else can alter the content after the message is sent. If those
+            seals are not set on the memfd, the ioctl will fail with
+            <errorcode>-1</errorcode>, and <varname>errno</varname> will be
+            set to <constant>ETXTBUSY</constant>.
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>KDBUS_ITEM_FDS</constant></term>
+        <listitem>
+          <para>
+            Messages can transport regular file descriptors via
+            <constant>KDBUS_ITEM_FDS</constant>. This item carries an array
+            of <type>int</type> values in <varname>item.fd</varname>. The
+            maximum number of file descriptors in the item is
+            <constant>253</constant>, and only one item of this type is
+            accepted per message. All passed values must be valid file
+            descriptors; the open count of each file descriptors is increased
+            by installing it to the receiver's task. This item can only be
+            used for directed messages, not for broadcasts, and only to
+            remote peers that have opted-in for receiving file descriptors
+            at connection time (<constant>KDBUS_HELLO_ACCEPT_FD</constant>).
+          </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <para>
+      The sender must not make any assumptions on the type in which data is
+      received by the remote peer. The kernel is free to re-pack multiple
+      <constant>KDBUS_ITEM_PAYLOAD_VEC</constant> and
+      <constant>KDBUS_ITEM_PAYLOAD_MEMFD</constant> payloads. For instance, the
+      kernel may decide to merge multiple <constant>VECs</constant> into a
+      single <constant>VEC</constant>, inline <constant>MEMFD</constant>
+      payloads into memory, or merge all passed <constant>VECs</constant> into a
+      single <constant>MEMFD</constant>. However, the kernel preserves the order
+      of passed data. This means that the order of all <constant>VEC</constant>
+      and <constant>MEMFD</constant> items is not changed in respect to each
+      other. In other words: All passed <constant>VEC</constant> and
+      <constant>MEMFD</constant> data payloads are treated as a single stream
+      of data that may be received by the remote peer in a different set of
+      chunks than it was sent as.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>Sending messages</title>
+
+    <para>
+      Messages are passed to the kernel with the
+      <constant>KDBUS_CMD_SEND</constant> ioctl. Depending on the destination
+      address of the message, the kernel delivers the message to the specific
+      destination connection, or to some subset of all connections on the same
+      bus. Sending messages across buses is not possible. Messages are always
+      queued in the memory pool of the destination connection (see above).
+    </para>
+
+    <para>
+      The <constant>KDBUS_CMD_SEND</constant> ioctl uses a
+      <type>struct kdbus_cmd_send</type> to describe the message
+      transfer.
+    </para>
+    <programlisting>
+struct kdbus_cmd_send {
+  __u64 size;
+  __u64 flags;
+  __u64 return_flags;
+  __u64 msg_address;
+  struct kdbus_msg_info reply;
+  struct kdbus_item items[0];
+};
+    </programlisting>
+
+    <para>The fields in this struct are described below.</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><varname>size</varname></term>
+        <listitem><para>
+          The overall size of the struct, including its items.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>flags</varname></term>
+        <listitem><para>Flags for message delivery</para>
+          <variablelist>
+            <varlistentry>
+              <term><constant>KDBUS_SEND_SYNC_REPLY</constant></term>
+              <listitem>
+                <para>
+                  By default, all calls to kdbus are considered asynchronous,
+                  non-blocking. However, as there are many use cases that need
+                  to wait for a remote peer to answer a method call, there's a
+                  way to send a message and wait for a reply in a synchronous
+                  fashion. This is what the
+                  <constant>KDBUS_SEND_SYNC_REPLY</constant> controls. The
+                  <constant>KDBUS_CMD_SEND</constant> ioctl will block until the
+                  reply has arrived, the timeout limit is reached, in case the
+                  remote connection was shut down, or if interrupted by a signal
+                  before any reply; see
+                  <citerefentry>
+                    <refentrytitle>signal</refentrytitle>
+                    <manvolnum>7</manvolnum>
+                  </citerefentry>.
+
+                  The offset of the reply message in the sender's pool is stored
+                  in <varname>reply</varname> when the ioctl has returned without
+                  error. Hence, there is no need for another
+                  <constant>KDBUS_CMD_RECV</constant> ioctl or anything else to
+                  receive the reply.
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_FLAG_NEGOTIATE</constant></term>
+              <listitem>
+                <para>
+                  Request a set of valid flags for this ioctl. When this bit is
+                  set, no action is taken; the ioctl will fail with
+                  <errorcode>-1</errorcode>, <varname>errno</varname>
+                  is set to <constant>EPROTO</constant>.
+                  Once the ioctl returned, the <varname>flags</varname>
+                  field will have all bits set that the kernel recognizes as
+                  valid for this command.
+                  The <constant>KDBUS_FLAG_NEGOTIATE</constant> bit will be
+                  cleared by the operation.
+                </para>
+              </listitem>
+            </varlistentry>
+          </variablelist>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>return_flags</varname></term>
+        <listitem><para>
+          Flags returned by the kernel. Currently unused and always set to
+          <constant>0</constant> by the kernel.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>msg_address</varname></term>
+        <listitem><para>
+          In this field, users have to provide a pointer to a message
+          (<type>struct kdbus_msg</type>) to send. See below for a
+          detailed description.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>reply</varname></term>
+        <listitem><para>
+          Only used for synchronous replies. See description of
+          <type>struct kdbus_cmd_recv</type> for more details.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>items</varname></term>
+        <listitem>
+          <para>
+            The following items are currently recognized.
+          </para>
+          <variablelist>
+            <varlistentry>
+              <term><constant>KDBUS_ITEM_CANCEL_FD</constant></term>
+              <listitem>
+                <para>
+                  When this optional item is passed in, and the call is
+                  executed as SYNC call, the passed in file descriptor can be
+                  used as alternative cancellation point. The kernel will call
+                  <citerefentry>
+                    <refentrytitle>poll</refentrytitle>
+                    <manvolnum>2</manvolnum>
+                  </citerefentry>
+                  on this file descriptor, and once it reports any incoming
+                  bytes, the blocking send operation will be canceled; the
+                  blocking, synchronous ioctl call will return
+                  <errorcode>-1</errorcode>, and <varname>errno</varname> will
+                  be set to <errorname>ECANCELED</errorname>.
+                  Any type of file descriptor on which
+                  <citerefentry>
+                    <refentrytitle>poll</refentrytitle>
+                    <manvolnum>2</manvolnum>
+                  </citerefentry>
+                  can be called on can be used as payload to this item; for
+                  example, an eventfd can be used for this purpose, see
+                  <citerefentry>
+                    <refentrytitle>eventfd</refentrytitle>
+                    <manvolnum>2</manvolnum>
+                  </citerefentry>.
+                  For asynchronous message sending, this item is allowed but
+                  ignored.
+                </para>
+              </listitem>
+            </varlistentry>
+          </variablelist>
+          <para>
+            Unrecognized items are rejected, and the ioctl will fail with
+            <varname>errno</varname> set to <constant>EINVAL</constant>.
+          </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <para>
+      The message referenced by the <varname>msg_address</varname> above has
+      the following layout.
+    </para>
+
+    <programlisting>
+struct kdbus_msg {
+  __u64 size;
+  __u64 flags;
+  __s64 priority;
+  __u64 dst_id;
+  __u64 src_id;
+  __u64 payload_type;
+  __u64 cookie;
+  __u64 timeout_ns;
+  __u64 cookie_reply;
+  struct kdbus_item items[0];
+};
+    </programlisting>
+
+    <para>The fields in this struct are described below.</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><varname>size</varname></term>
+        <listitem><para>
+          The overall size of the struct, including its items.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>flags</varname></term>
+        <listitem><para>Flags to describe message details.</para>
+          <variablelist>
+            <varlistentry>
+              <term><constant>KDBUS_MSG_EXPECT_REPLY</constant></term>
+              <listitem>
+                <para>
+                  Expect a reply to this message from the remote peer. With
+                  this bit set, the timeout_ns field must be set to a non-zero
+                  number of nanoseconds in which the receiving peer is expected
+                  to reply. If such a reply is not received in time, the sender
+                  will be notified with a timeout message (see below). The
+                  value must be an absolute value, in nanoseconds and based on
+                  <constant>CLOCK_MONOTONIC</constant>.
+                </para><para>
+                  For a message to be accepted as reply, it must be a direct
+                  message to the original sender (not a broadcast and not a
+                  signal message), and its
+                  <varname>kdbus_msg.cookie_reply</varname> must match the
+                  previous message's <varname>kdbus_msg.cookie</varname>.
+                </para><para>
+                  Expected replies also temporarily open the policy of the
+                  sending connection, so the other peer is allowed to respond
+                  within the given time window.
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_MSG_NO_AUTO_START</constant></term>
+              <listitem>
+                <para>
+                  By default, when a message is sent to an activator
+                  connection, the activator is notified and will start an
+                  implementer. This flag inhibits that behavior. With this bit
+                  set, and the remote being an activator, the ioctl will fail
+                  with <varname>errno</varname> set to
+                  <constant>EADDRNOTAVAIL</constant>.
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_FLAG_NEGOTIATE</constant></term>
+              <listitem>
+                <para>
+                  Requests a set of valid flags for this ioctl. When this bit is
+                  set, no action is taken; the ioctl will return
+                  <errorcode>0</errorcode>, and the <varname>flags</varname>
+                  field will have all bits set that are valid for this command.
+                  The <constant>KDBUS_FLAG_NEGOTIATE</constant> bit will be
+                  cleared by the operation.
+                </para>
+              </listitem>
+            </varlistentry>
+          </variablelist>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>priority</varname></term>
+        <listitem><para>
+          The priority of this message. Receiving messages (see below) may
+          optionally be constrained to messages of a minimal priority. This
+          allows for use cases where timing critical data is interleaved with
+          control data on the same connection. If unused, the priority field
+          should be set to <constant>0</constant>.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>dst_id</varname></term>
+        <listitem><para>
+          The numeric ID of the destination connection, or
+          <constant>KDBUS_DST_ID_BROADCAST</constant>
+          (~0ULL) to address every peer on the bus, or
+          <constant>KDBUS_DST_ID_NAME</constant> (0) to look
+          it up dynamically from the bus' name registry.
+          In the latter case, an item of type
+          <constant>KDBUS_ITEM_DST_NAME</constant> is mandatory.
+          Also see
+          <citerefentry>
+            <refentrytitle>kdbus.name</refentrytitle>
+            <manvolnum>7</manvolnum>
+          </citerefentry>
+          .
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>src_id</varname></term>
+        <listitem><para>
+          Upon return of the ioctl, this member will contain the sending
+          connection's numerical ID. Should be 0 at send time.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>payload_type</varname></term>
+        <listitem><para>
+          Type of the payload in the actual data records. Currently, only
+          <constant>KDBUS_PAYLOAD_DBUS</constant> is accepted as input value
+          of this field. When receiving messages that are generated by the
+          kernel (notifications), this field will contain
+          <constant>KDBUS_PAYLOAD_KERNEL</constant>.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>cookie</varname></term>
+        <listitem><para>
+          Cookie of this message, for later recognition. Also, when replying
+          to a message (see above), the <varname>cookie_reply</varname>
+          field must match this value.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>timeout_ns</varname></term>
+        <listitem><para>
+          If the message sent requires a reply from the remote peer (see above),
+          this field contains the timeout in absolute nanoseconds based on
+          <constant>CLOCK_MONOTONIC</constant>. Also see
+          <citerefentry>
+            <refentrytitle>clock_gettime</refentrytitle>
+            <manvolnum>2</manvolnum>
+          </citerefentry>.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>cookie_reply</varname></term>
+        <listitem><para>
+          If the message sent is a reply to another message, this field must
+          match the cookie of the formerly received message.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>items</varname></term>
+        <listitem>
+          <para>
+            A dynamically sized list of items to contain additional information.
+            The following items are expected/valid:
+          </para>
+          <variablelist>
+            <varlistentry>
+              <term><constant>KDBUS_ITEM_PAYLOAD_VEC</constant></term>
+              <term><constant>KDBUS_ITEM_PAYLOAD_MEMFD</constant></term>
+              <term><constant>KDBUS_ITEM_FDS</constant></term>
+              <listitem>
+                <para>
+                  Actual data records containing the payload. See section
+                  "Message payload".
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_ITEM_BLOOM_FILTER</constant></term>
+              <listitem>
+                <para>
+                  Bloom filter for matches (see below).
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_ITEM_DST_NAME</constant></term>
+              <listitem>
+                <para>
+                  Well-known name to send this message to. Required if
+                  <varname>dst_id</varname> is set to
+                  <constant>KDBUS_DST_ID_NAME</constant>.
+                  If a connection holding the given name can't be found,
+                  the ioctl will fail with <varname>errno</varname> set to
+                  <constant>ESRCH</constant> is returned.
+                </para>
+                <para>
+                  For messages to a unique name (ID), this item is optional. If
+                  present, the kernel will make sure the name owner matches the
+                  given unique name. This allows programs to tie the message
+                  sending to the condition that a name is currently owned by a
+                  certain unique name.
+                </para>
+              </listitem>
+            </varlistentry>
+          </variablelist>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <para>
+      The message will be augmented by the requested metadata items when
+      queued into the receiver's pool. See
+      <citerefentry>
+        <refentrytitle>kdbus.connection</refentrytitle>
+        <manvolnum>7</manvolnum>
+      </citerefentry>
+      and
+      <citerefentry>
+        <refentrytitle>kdbus.item</refentrytitle>
+        <manvolnum>7</manvolnum>
+      </citerefentry>
+      for more information on metadata.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>Receiving messages</title>
+
+    <para>
+      Messages are received by the client with the
+      <constant>KDBUS_CMD_RECV</constant> ioctl. The endpoint file of the bus
+      supports <function>poll()/epoll()/select()</function>; when new messages
+      are available on the connection's file descriptor,
+      <constant>POLLIN</constant> is reported. For compatibility reasons,
+      <constant>POLLOUT</constant> is always reported as well. Note, however,
+      that the latter does not guarantee that a message can in fact be sent, as
+      this depends on how many pending messages the receiver has in its pool.
+    </para>
+
+    <para>
+      With the <constant>KDBUS_CMD_RECV</constant> ioctl, a
+      <type>struct kdbus_cmd_recv</type> is used.
+    </para>
+
+    <programlisting>
+struct kdbus_cmd_recv {
+  __u64 size;
+  __u64 flags;
+  __u64 return_flags;
+  __s64 priority;
+  __u64 dropped_msgs;
+  struct kdbus_msg_info msg;
+  struct kdbus_item items[0];
+};
+    </programlisting>
+
+    <para>The fields in this struct are described below.</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><varname>size</varname></term>
+        <listitem><para>
+          The overall size of the struct, including its items.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>flags</varname></term>
+        <listitem><para>Flags to control the receive command.</para>
+          <variablelist>
+            <varlistentry>
+              <term><constant>KDBUS_RECV_PEEK</constant></term>
+              <listitem>
+                <para>
+                  Just return the location of the next message. Do not install
+                  file descriptors or anything else. This is usually used to
+                  determine the sender of the next queued message.
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_RECV_DROP</constant></term>
+              <listitem>
+                <para>
+                  Drop the next message without doing anything else with it,
+                  and free the pool slice. This a short-cut for
+                  <constant>KDBUS_RECV_PEEK</constant> and
+                  <constant>KDBUS_CMD_FREE</constant>.
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_RECV_USE_PRIORITY</constant></term>
+              <listitem>
+                <para>
+                  Dequeue the messages ordered by their priority, and filtering
+                  them with the priority field (see below).
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_FLAG_NEGOTIATE</constant></term>
+              <listitem>
+                <para>
+                  Request a set of valid flags for this ioctl. When this bit is
+                  set, no action is taken; the ioctl will fail with
+                  <errorcode>-1</errorcode>, <varname>errno</varname>
+                  is set to <constant>EPROTO</constant>.
+                  Once the ioctl returned, the <varname>flags</varname>
+                  field will have all bits set that the kernel recognizes as
+                  valid for this command.
+                </para>
+              </listitem>
+            </varlistentry>
+          </variablelist>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>return_flags</varname></term>
+        <listitem><para>
+          Flags returned by the kernel. If the <varname>dropped_msgs</varname>
+          field is non-zero, <constant>KDBUS_RECV_RETURN_DROPPED_MSGS</constant>
+          is set. If a file descriptor could not be installed, the
+          <constant>KDBUS_RECV_RETURN_INCOMPLETE_FDS</constant> flag is set.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>priority</varname></term>
+        <listitem><para>
+          With <constant>KDBUS_RECV_USE_PRIORITY</constant> set in
+          <varname>flags</varname>, messages will be dequeued ordered by their
+          priority, starting with the highest value. Also, messages will be
+          filtered by the value given in this field, so the returned message
+          will at least have the requested priority. If no such message is
+          waiting in the queue, the ioctl will fail, and
+          <varname>errno</varname> will be set to <constant>EAGAIN</constant>.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>dropped_msgs</varname></term>
+        <listitem><para>
+          Whenever a message with <constant>KDBUS_MSG_SIGNAL</constant> is sent
+          but cannot be queued on a peer (e.g., as it contains FDs but the peer
+          does not support FDs, or there is no space left in the peer's pool)
+          the 'dropped_msgs' counter of the peer is incremented. On the next
+          RECV ioctl, the 'dropped_msgs' field is copied into the ioctl struct
+          and cleared on the peer. If it was non-zero, the
+          <constant>KDBUS_RECV_RETURN_DROPPED_MSGS</constant> flag will be set
+          in <varname>return_flags</varname>. Note that this will only happen
+          if the ioctl succeeded or failed with <constant>EAGAIN</constant>. In
+          other error cases, the 'dropped_msgs' field of the peer is left
+          untouched.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>msg</varname></term>
+        <listitem><para>
+          Embedded struct containing information on the received message when
+          this command succeeded (see below).
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>items</varname></term>
+        <listitem><para>
+          Items to specify further details for the receive command.
+          Currently unused, and all items will be rejected with
+          <varname>errno</varname> set to <constant>EINVAL</constant>.
+        </para></listitem>
+      </varlistentry>
+    </variablelist>
+
+    <para>
+      Both <type>struct kdbus_cmd_recv</type> and
+      <type>struct kdbus_cmd_send</type> embed
+      <type>struct kdbus_msg_info</type>.
+      For the <constant>KDBUS_CMD_SEND</constant> ioctl, it is used to catch
+      synchronous replies, if one was requested, and is unused otherwise.
+    </para>
+
+    <programlisting>
+struct kdbus_msg_info {
+  __u64 offset;
+  __u64 msg_size;
+  __u64 return_flags;
+};
+    </programlisting>
+
+    <para>The fields in this struct are described below.</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><varname>offset</varname></term>
+        <listitem><para>
+          Upon return of the ioctl, this field contains the offset in the
+          receiver's memory pool. The memory must be freed with
+          <constant>KDBUS_CMD_FREE</constant>. See
+          <citerefentry>
+            <refentrytitle>kdbus.pool</refentrytitle>
+            <manvolnum>7</manvolnum>
+          </citerefentry>
+          for further details.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>msg_size</varname></term>
+        <listitem><para>
+          Upon successful return of the ioctl, this field contains the size of
+          the allocated slice at offset <varname>offset</varname>.
+          It is the combination of the size of the stored
+          <type>struct kdbus_msg</type> object plus all appended VECs.
+          You can use it in combination with <varname>offset</varname> to map
+          a single message, instead of mapping the entire pool. See
+          <citerefentry>
+            <refentrytitle>kdbus.pool</refentrytitle>
+            <manvolnum>7</manvolnum>
+          </citerefentry>
+          for further details.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>return_flags</varname></term>
+        <listitem>
+          <para>
+            Kernel-provided return flags. Currently, the following flags are
+            defined.
+          </para>
+          <variablelist>
+            <varlistentry>
+              <term><constant>KDBUS_RECV_RETURN_INCOMPLETE_FDS</constant></term>
+              <listitem>
+                <para>
+                  The message contained memfds or file descriptors, and the
+                  kernel failed to install one or more of them at receive time.
+                  Most probably that happened because the maximum number of
+                  file descriptors for the receiver's task were exceeded.
+                  In such cases, the message is still delivered, so this is not
+                  a fatal condition. File descriptors numbers inside the
+                  <constant>KDBUS_ITEM_FDS</constant> item or memfd files
+                  referenced by <constant>KDBUS_ITEM_PAYLOAD_MEMFD</constant>
+                  items which could not be installed will be set to
+                  <constant>-1</constant>.
+                </para>
+              </listitem>
+            </varlistentry>
+          </variablelist>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <para>
+      Unless <constant>KDBUS_RECV_DROP</constant> was passed, the
+      <varname>offset</varname> field contains the location of the new message
+      inside the receiver's pool after the <constant>KDBUS_CMD_RECV</constant>
+      ioctl was employed. The message is stored as <type>struct kdbus_msg</type>
+      at this offset, and can be interpreted with the semantics described above.
+    </para>
+    <para>
+      Also, if the connection allowed for file descriptor to be passed
+      (<constant>KDBUS_HELLO_ACCEPT_FD</constant>), and if the message contained
+      any, they will be installed into the receiving process when the
+      <constant>KDBUS_CMD_RECV</constant> ioctl is called.
+      <emphasis>memfds</emphasis> may always be part of the message payload.
+      The receiving task is obliged to close all file descriptors appropriately
+      once no longer needed. If <constant>KDBUS_RECV_PEEK</constant> is set, no
+      file descriptors are installed. This allows for peeking at a message,
+      looking at its metadata only and dropping it via
+      <constant>KDBUS_RECV_DROP</constant>, without installing any of the file
+      descriptors into the receiving process.
+    </para>
+    <para>
+      The caller is obliged to call the <constant>KDBUS_CMD_FREE</constant>
+      ioctl with the returned offset when the memory is no longer needed.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>Notifications</title>
+    <para>
+      A kernel notification is a regular kdbus message with the following
+      details.
+    </para>
+
+    <itemizedlist>
+      <listitem><para>
+          kdbus_msg.src_id == <constant>KDBUS_SRC_ID_KERNEL</constant>
+      </para></listitem>
+      <listitem><para>
+        kdbus_msg.dst_id == <constant>KDBUS_DST_ID_BROADCAST</constant>
+      </para></listitem>
+      <listitem><para>
+        kdbus_msg.payload_type == <constant>KDBUS_PAYLOAD_KERNEL</constant>
+      </para></listitem>
+      <listitem><para>
+        Has exactly one of the items attached that are described below.
+      </para></listitem>
+      <listitem><para>
+        Always has a timestamp item (<constant>KDBUS_ITEM_TIMESTAMP</constant>)
+        attached.
+      </para></listitem>
+    </itemizedlist>
+
+    <para>
+      The kernel will notify its users of the following events.
+    </para>
+
+    <itemizedlist>
+      <listitem><para>
+        When connection <emphasis>A</emphasis> is terminated while connection
+        <emphasis>B</emphasis> is waiting for a reply from it, connection
+        <emphasis>B</emphasis> is notified with a message with an item of
+        type <constant>KDBUS_ITEM_REPLY_DEAD</constant>.
+      </para></listitem>
+
+      <listitem><para>
+        When connection <emphasis>A</emphasis> does not receive a reply from
+        connection <emphasis>B</emphasis> within the specified timeout window,
+        connection <emphasis>A</emphasis> will receive a message with an
+        item of type <constant>KDBUS_ITEM_REPLY_TIMEOUT</constant>.
+      </para></listitem>
+
+      <listitem><para>
+        When an ordinary connection (not a monitor) is created on or removed
+        from a bus, messages with an item of type
+        <constant>KDBUS_ITEM_ID_ADD</constant> or
+        <constant>KDBUS_ITEM_ID_REMOVE</constant>, respectively, are delivered
+        to all bus members that match these messages through their match
+        database. Eavesdroppers (monitor connections) do not cause such
+        notifications to be sent. They are invisible on the bus.
+      </para></listitem>
+
+      <listitem><para>
+        When a connection gains or loses ownership of a name, messages with an
+        item of type <constant>KDBUS_ITEM_NAME_ADD</constant>,
+        <constant>KDBUS_ITEM_NAME_REMOVE</constant> or
+        <constant>KDBUS_ITEM_NAME_CHANGE</constant> are delivered to all bus
+        members that match these messages through their match database.
+      </para></listitem>
+    </itemizedlist>
+  </refsect1>
+
+  <refsect1>
+    <title>Return value</title>
+    <para>
+      On success, all mentioned ioctl commands return <errorcode>0</errorcode>;
+      on error, <errorcode>-1</errorcode> is returned, and
+      <varname>errno</varname> is set to indicate the error.
+      If the issued ioctl is illegal for the file descriptor used,
+      <varname>errno</varname> will be set to <constant>ENOTTY</constant>.
+    </para>
+
+    <refsect2>
+      <title>
+        <constant>KDBUS_CMD_SEND</constant> may fail with the following
+        errors
+      </title>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>EOPNOTSUPP</constant></term>
+          <listitem><para>
+            The connection is not an ordinary connection, or the passed
+            file descriptors in <constant>KDBUS_ITEM_FDS</constant> item are
+            either kdbus handles or unix domain sockets. Both are currently
+            unsupported.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>EINVAL</constant></term>
+          <listitem><para>
+            The submitted payload type is
+            <constant>KDBUS_PAYLOAD_KERNEL</constant>,
+            <constant>KDBUS_MSG_EXPECT_REPLY</constant> was set without timeout
+            or cookie values, <constant>KDBUS_SEND_SYNC_REPLY</constant> was
+            set without <constant>KDBUS_MSG_EXPECT_REPLY</constant>, an invalid
+            item was supplied, <constant>src_id</constant> was non-zero and was
+            different from the current connection's ID, a supplied memfd had a
+            size of 0, or a string was not properly null-terminated.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>ENOTUNIQ</constant></term>
+          <listitem><para>
+            The supplied destination is
+            <constant>KDBUS_DST_ID_BROADCAST</constant> and either
+            file descriptors were passed, or
+            <constant>KDBUS_MSG_EXPECT_REPLY</constant> was set,
+            or a timeout was given.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>E2BIG</constant></term>
+          <listitem><para>
+            Too many items.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>EMSGSIZE</constant></term>
+          <listitem><para>
+            The size of the message header and items or the payload vector
+            is excessive.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>EEXIST</constant></term>
+          <listitem><para>
+            Multiple <constant>KDBUS_ITEM_FDS</constant>,
+            <constant>KDBUS_ITEM_BLOOM_FILTER</constant> or
+            <constant>KDBUS_ITEM_DST_NAME</constant> items were supplied.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>EBADF</constant></term>
+          <listitem><para>
+            The supplied <constant>KDBUS_ITEM_FDS</constant> or
+            <constant>KDBUS_ITEM_PAYLOAD_MEMFD</constant> items
+            contained an illegal file descriptor.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>EMEDIUMTYPE</constant></term>
+          <listitem><para>
+            The supplied memfd is not a sealed kdbus memfd.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>EMFILE</constant></term>
+          <listitem><para>
+            Too many file descriptors inside a
+            <constant>KDBUS_ITEM_FDS</constant>.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>EBADMSG</constant></term>
+          <listitem><para>
+            An item had illegal size, both a <constant>dst_id</constant> and a
+            <constant>KDBUS_ITEM_DST_NAME</constant> was given, or both a name
+            and a bloom filter was given.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>ETXTBSY</constant></term>
+          <listitem><para>
+            The supplied kdbus memfd file cannot be sealed or the seal
+            was removed, because it is shared with other processes or
+            still mapped with
+            <citerefentry>
+              <refentrytitle>mmap</refentrytitle>
+              <manvolnum>2</manvolnum>
+            </citerefentry>.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>ECOMM</constant></term>
+          <listitem><para>
+            A peer does not accept the file descriptors addressed to it.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>EFAULT</constant></term>
+          <listitem><para>
+            The supplied bloom filter size was not 64-bit aligned, or supplied
+            memory could not be accessed by the kernel.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>EDOM</constant></term>
+          <listitem><para>
+            The supplied bloom filter size did not match the bloom filter
+            size of the bus.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>EDESTADDRREQ</constant></term>
+          <listitem><para>
+            <constant>dst_id</constant> was set to
+            <constant>KDBUS_DST_ID_NAME</constant>, but no
+            <constant>KDBUS_ITEM_DST_NAME</constant> was attached.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>ESRCH</constant></term>
+          <listitem><para>
+            The name to look up was not found in the name registry.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>EADDRNOTAVAIL</constant></term>
+          <listitem><para>
+            <constant>KDBUS_MSG_NO_AUTO_START</constant> was given but the
+            destination connection is an activator.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>ENXIO</constant></term>
+          <listitem><para>
+            The passed numeric destination connection ID couldn't be found,
+            or is not connected.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>ECONNRESET</constant></term>
+          <listitem><para>
+            The destination connection is no longer active.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>ETIMEDOUT</constant></term>
+          <listitem><para>
+            Timeout while synchronously waiting for a reply.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>EINTR</constant></term>
+          <listitem><para>
+            Interrupted system call while synchronously waiting for a reply.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>EPIPE</constant></term>
+          <listitem><para>
+            When sending a message, a synchronous reply from the receiving
+            connection was expected but the connection died before answering.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>ENOBUFS</constant></term>
+          <listitem><para>
+            Too many pending messages on the receiver side.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>EREMCHG</constant></term>
+          <listitem><para>
+            Both a well-known name and a unique name (ID) was given, but
+            the name is not currently owned by that connection.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>EXFULL</constant></term>
+          <listitem><para>
+            The memory pool of the receiver is full.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>EREMOTEIO</constant></term>
+          <listitem><para>
+            While synchronously waiting for a reply, the remote peer
+            failed with an I/O error.
+          </para></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+
+    <refsect2>
+      <title>
+        <constant>KDBUS_CMD_RECV</constant> may fail with the following
+        errors
+      </title>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>EOPNOTSUPP</constant></term>
+          <listitem><para>
+            The connection is not an ordinary connection, or the passed
+            file descriptors are either kdbus handles or unix domain
+            sockets. Both are currently unsupported.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>EINVAL</constant></term>
+          <listitem><para>
+            Invalid flags or offset.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>EAGAIN</constant></term>
+          <listitem><para>
+            No message found in the queue.
+          </para></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+    <simplelist type="inline">
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.bus</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.connection</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.endpoint</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.fs</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.item</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.name</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.pool</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>clock_gettime</refentrytitle>
+          <manvolnum>2</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>ioctl</refentrytitle>
+          <manvolnum>2</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>poll</refentrytitle>
+          <manvolnum>2</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>select</refentrytitle>
+          <manvolnum>2</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>epoll</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>eventfd</refentrytitle>
+          <manvolnum>2</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>memfd_create</refentrytitle>
+          <manvolnum>2</manvolnum>
+        </citerefentry>
+      </member>
+    </simplelist>
+  </refsect1>
+</refentry>
diff --git a/ipc/kdbus/Documentation/kdbus.name.xml b/ipc/kdbus/Documentation/kdbus.name.xml
new file mode 100644 (file)
index 0000000..3f5f6a6
--- /dev/null
@@ -0,0 +1,711 @@
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+        "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<refentry id="kdbus.name">
+
+  <refentryinfo>
+    <title>kdbus.name</title>
+    <productname>kdbus.name</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>kdbus.name</refentrytitle>
+    <manvolnum>7</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>kdbus.name</refname>
+    <refpurpose>kdbus.name</refpurpose>
+  </refnamediv>
+
+  <refsect1>
+    <title>Description</title>
+    <para>
+      Each
+      <citerefentry>
+        <refentrytitle>kdbus.bus</refentrytitle>
+        <manvolnum>7</manvolnum>
+      </citerefentry>
+      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 <varname>kdbus_msg.dst_id</varname> set to
+      <constant>KDBUS_DST_ID_NAME</constant>, or when a registry dump is
+      requested with <constant>KDBUS_CMD_NAME_LIST</constant>.
+    </para>
+
+    <para>
+      All of the below is subject to policy rules for <emphasis>SEE</emphasis>
+      and <emphasis>OWN</emphasis> permissions. See
+      <citerefentry>
+        <refentrytitle>kdbus.policy</refentrytitle>
+        <manvolnum>7</manvolnum>
+      </citerefentry>
+      for more information.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>Name validity</title>
+    <para>
+      A name has to comply with the following rules in order to be considered
+      valid.
+    </para>
+
+    <itemizedlist>
+      <listitem>
+        <para>
+          The name has two or more elements separated by a
+          '<literal>.</literal>' (period) character.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          All elements must contain at least one character.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          Each element must only contain the ASCII characters
+          <literal>[A-Z][a-z][0-9]_</literal> and must not begin with a
+          digit.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          The name must contain at least one '<literal>.</literal>' (period)
+          character (and thus at least two elements).
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          The name must not begin with a '<literal>.</literal>' (period)
+          character.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          The name must not exceed <constant>255</constant> characters in
+          length.
+        </para>
+      </listitem>
+    </itemizedlist>
+  </refsect1>
+
+  <refsect1>
+    <title>Acquiring a name</title>
+    <para>
+      To acquire a name, a client uses the
+      <constant>KDBUS_CMD_NAME_ACQUIRE</constant> ioctl with
+      <type>struct kdbus_cmd</type> as argument.
+    </para>
+
+    <programlisting>
+struct kdbus_cmd {
+  __u64 size;
+  __u64 flags;
+  __u64 return_flags;
+  struct kdbus_item items[0];
+};
+    </programlisting>
+
+    <para>The fields in this struct are described below.</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><varname>size</varname></term>
+        <listitem><para>
+          The overall size of the struct, including its items.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>flags</varname></term>
+        <listitem><para>Flags to control details in the name acquisition.</para>
+          <variablelist>
+            <varlistentry>
+              <term><constant>KDBUS_NAME_REPLACE_EXISTING</constant></term>
+              <listitem>
+                <para>
+                  Acquiring a name that is already present usually fails,
+                  unless this flag is set in the call, and
+                  <constant>KDBUS_NAME_ALLOW_REPLACEMENT</constant> (see below)
+                  was set when the current owner of the name acquired it, or
+                  if the current owner is an activator connection (see
+                  <citerefentry>
+                    <refentrytitle>kdbus.connection</refentrytitle>
+                    <manvolnum>7</manvolnum>
+                  </citerefentry>).
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_NAME_ALLOW_REPLACEMENT</constant></term>
+              <listitem>
+                <para>
+                  Allow other connections to take over this name. When this
+                  happens, the former owner of the connection will be notified
+                  of the name loss.
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_NAME_QUEUE</constant></term>
+              <listitem>
+                <para>
+                  A name that is already acquired by a connection can not be
+                  acquired again (unless the
+                  <constant>KDBUS_NAME_ALLOW_REPLACEMENT</constant> flag was
+                  set during acquisition; see above).
+                  However, a connection can put itself in a queue of
+                  connections waiting for the name to be released. Once that
+                  happens, the first connection in that queue becomes the new
+                  owner and is notified accordingly.
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_FLAG_NEGOTIATE</constant></term>
+              <listitem>
+                <para>
+                  Request a set of valid flags for this ioctl. When this bit is
+                  set, no action is taken; the ioctl will fail with
+                  <errorcode>-1</errorcode>, and <varname>errno</varname>
+                  is set to <constant>EPROTO</constant>.
+                  Once the ioctl returned, the <varname>flags</varname>
+                  field will have all bits set that the kernel recognizes as
+                  valid for this command.
+                  The <constant>KDBUS_FLAG_NEGOTIATE</constant> bit will be
+                  cleared by the operation.
+                </para>
+              </listitem>
+            </varlistentry>
+          </variablelist>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>return_flags</varname></term>
+        <listitem>
+          <para>
+            Flags returned by the kernel. Currently, the following may be
+            returned by the kernel.
+          </para>
+          <variablelist>
+            <varlistentry>
+              <term><constant>KDBUS_NAME_IN_QUEUE</constant></term>
+              <listitem>
+                <para>
+                  The name was not acquired yet, but the connection was
+                  placed in the queue of peers waiting for the name.
+                  This can only happen if <constant>KDBUS_NAME_QUEUE</constant>
+                  was set in the <varname>flags</varname> member (see above).
+                  The connection will receive a name owner change notification
+                  once the current owner has given up the name and its
+                  ownership was transferred.
+                </para>
+              </listitem>
+            </varlistentry>
+          </variablelist>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>items</varname></term>
+        <listitem>
+          <para>
+            Items to submit the name. Currently, one item of type
+            <constant>KDBUS_ITEM_NAME</constant> is expected and allowed, and
+            the contained string must be a valid bus name.
+            <constant>KDBUS_ITEM_NEGOTIATE</constant> may be used to probe for
+            valid item types. See
+            <citerefentry>
+              <refentrytitle>kdbus.item</refentrytitle>
+              <manvolnum>7</manvolnum>
+            </citerefentry>
+            for a detailed description of how this item is used.
+          </para>
+          <para>
+            Unrecognized items are rejected, and the ioctl will fail with
+            <varname>errno</varname> set to <errorname>>EINVAL</errorname>.
+          </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Releasing a name</title>
+    <para>
+      A connection may release a name explicitly with the
+      <constant>KDBUS_CMD_NAME_RELEASE</constant> ioctl. If the connection was
+      an implementer of an activatable name, its pending messages are moved
+      back to the activator. If there are any connections queued up as waiters
+      for the name, the first one in the queue (the oldest entry) will become
+      the new owner. The same happens implicitly for all names once a
+      connection terminates. See
+      <citerefentry>
+        <refentrytitle>kdbus.connection</refentrytitle>
+        <manvolnum>7</manvolnum>
+      </citerefentry>
+      for more information on connections.
+    </para>
+    <para>
+      The <constant>KDBUS_CMD_NAME_RELEASE</constant> ioctl uses the same data
+      structure as the acquisition call
+      (<constant>KDBUS_CMD_NAME_ACQUIRE</constant>),
+      but with slightly different field usage.
+    </para>
+
+    <programlisting>
+struct kdbus_cmd {
+  __u64 size;
+  __u64 flags;
+  __u64 return_flags;
+  struct kdbus_item items[0];
+};
+    </programlisting>
+
+    <para>The fields in this struct are described below.</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><varname>size</varname></term>
+        <listitem><para>
+          The overall size of the struct, including its items.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>flags</varname></term>
+        <listitem><para>
+          Flags to the command. Currently unused.
+          <constant>KDBUS_FLAG_NEGOTIATE</constant> is accepted to probe for
+          valid flags. If set, the ioctl will return <errorcode>0</errorcode>,
+          and the <varname>flags</varname> field is set to
+          <constant>0</constant>.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>return_flags</varname></term>
+        <listitem><para>
+          Flags returned by the kernel. Currently unused and always set to
+          <constant>0</constant> by the kernel.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>items</varname></term>
+        <listitem>
+          <para>
+            Items to submit the name. Currently, one item of type
+            <constant>KDBUS_ITEM_NAME</constant> is expected and allowed, and
+            the contained string must be a valid bus name.
+            <constant>KDBUS_ITEM_NEGOTIATE</constant> may be used to probe for
+            valid item types. See
+            <citerefentry>
+              <refentrytitle>kdbus.item</refentrytitle>
+              <manvolnum>7</manvolnum>
+            </citerefentry>
+            for a detailed description of how this item is used.
+          </para>
+          <para>
+            Unrecognized items are rejected, and the ioctl will fail with
+            <varname>errno</varname> set to <constant>EINVAL</constant>.
+          </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Dumping the name registry</title>
+    <para>
+      A connection may request a complete or filtered dump of currently active
+      bus names with the <constant>KDBUS_CMD_LIST</constant> ioctl, which
+      takes a <type>struct kdbus_cmd_list</type> as argument.
+    </para>
+
+    <programlisting>
+struct kdbus_cmd_list {
+  __u64 flags;
+  __u64 return_flags;
+  __u64 offset;
+};
+    </programlisting>
+
+    <para>The fields in this struct are described below.</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><varname>flags</varname></term>
+        <listitem>
+          <para>
+            Any combination of flags to specify which names should be dumped.
+          </para>
+          <variablelist>
+            <varlistentry>
+              <term><constant>KDBUS_LIST_UNIQUE</constant></term>
+              <listitem>
+                <para>
+                  List the unique (numeric) IDs of the connection, whether it
+                  owns a name or not.
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_LIST_NAMES</constant></term>
+              <listitem>
+                <para>
+                  List well-known names stored in the database which are
+                  actively owned by a real connection (not an activator).
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_LIST_ACTIVATORS</constant></term>
+              <listitem>
+                <para>
+                  List names that are owned by an activator.
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_LIST_QUEUED</constant></term>
+              <listitem>
+                <para>
+                  List connections that are not yet owning a name but are
+                  waiting for it to become available.
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_FLAG_NEGOTIATE</constant></term>
+              <listitem>
+                <para>
+                  Request a set of valid flags for this ioctl. When this bit is
+                  set, no action is taken; the ioctl will fail with
+                  <errorcode>-1</errorcode>, and <varname>errno</varname>
+                  is set to <constant>EPROTO</constant>.
+                  Once the ioctl returned, the <varname>flags</varname>
+                  field will have all bits set that the kernel recognizes as
+                  valid for this command.
+                  The <constant>KDBUS_FLAG_NEGOTIATE</constant> bit will be
+                  cleared by the operation.
+                </para>
+              </listitem>
+            </varlistentry>
+          </variablelist>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>return_flags</varname></term>
+        <listitem><para>
+          Flags returned by the kernel. Currently unused and always set to
+          <constant>0</constant> by the kernel.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>offset</varname></term>
+        <listitem><para>
+          When the ioctl returns successfully, the offset to the name registry
+          dump inside the connection's pool will be stored in this field.
+        </para></listitem>
+      </varlistentry>
+    </variablelist>
+
+    <para>
+      The returned list of names is stored in a <type>struct kdbus_list</type>
+      that in turn contains an array of type <type>struct kdbus_info</type>,
+      The array-size in bytes is given as <varname>list_size</varname>.
+      The fields inside <type>struct kdbus_info</type> is described next.
+    </para>
+
+    <programlisting>
+struct kdbus_info {
+  __u64 size;
+  __u64 id;
+  __u64 flags;
+  struct kdbus_item items[0];
+};
+    </programlisting>
+
+    <para>The fields in this struct are described below.</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><varname>size</varname></term>
+        <listitem><para>
+          The overall size of the struct, including its items.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>id</varname></term>
+        <listitem><para>
+          The owning connection's unique ID.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>flags</varname></term>
+        <listitem><para>
+          The flags of the owning connection.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>items</varname></term>
+        <listitem>
+          <para>
+            Items containing the actual name. Currently, one item of type
+            <constant>KDBUS_ITEM_OWNED_NAME</constant> will be attached,
+            including the name's flags. In that item, the flags field of the
+            name may carry the following bits:
+          </para>
+          <variablelist>
+            <varlistentry>
+              <term><constant>KDBUS_NAME_ALLOW_REPLACEMENT</constant></term>
+              <listitem>
+                <para>
+                  Other connections are allowed to take over this name from the
+                  connection that owns it.
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_NAME_IN_QUEUE</constant></term>
+              <listitem>
+                <para>
+                  When retrieving a list of currently acquired names in the
+                  registry, this flag indicates whether the connection
+                  actually owns the name or is currently waiting for it to
+                  become available.
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_NAME_ACTIVATOR</constant></term>
+              <listitem>
+                <para>
+                  An activator connection owns a name as a placeholder for an
+                  implementer, which is started on demand by programs as soon
+                  as the first message arrives. There's some more information
+                  on this topic in
+                  <citerefentry>
+                    <refentrytitle>kdbus.connection</refentrytitle>
+                    <manvolnum>7</manvolnum>
+                  </citerefentry>
+                  .
+                </para>
+                <para>
+                  In contrast to
+                  <constant>KDBUS_NAME_REPLACE_EXISTING</constant>,
+                  when a name is taken over from an activator connection, all
+                  the messages that have been queued in the activator
+                  connection will be moved over to the new owner. The activator
+                  connection will still be tracked for the name and will take
+                  control again if the implementer connection terminates.
+                </para>
+                <para>
+                  This flag can not be used when acquiring a name, but is
+                  implicitly set through <constant>KDBUS_CMD_HELLO</constant>
+                  with <constant>KDBUS_HELLO_ACTIVATOR</constant> set in
+                  <varname>kdbus_cmd_hello.conn_flags</varname>.
+                </para>
+              </listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_FLAG_NEGOTIATE</constant></term>
+              <listitem>
+                <para>
+                  Requests a set of valid flags for this ioctl. When this bit is
+                  set, no action is taken; the ioctl will return
+                  <errorcode>0</errorcode>, and the <varname>flags</varname>
+                  field will have all bits set that are valid for this command.
+                  The <constant>KDBUS_FLAG_NEGOTIATE</constant> bit will be
+                  cleared by the operation.
+                </para>
+              </listitem>
+            </varlistentry>
+          </variablelist>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <para>
+      The returned buffer must be freed with the
+      <constant>KDBUS_CMD_FREE</constant> ioctl when the user is finished with
+      it. See
+      <citerefentry>
+        <refentrytitle>kdbus.pool</refentrytitle>
+        <manvolnum>7</manvolnum>
+      </citerefentry>
+      for more information.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return value</title>
+    <para>
+      On success, all mentioned ioctl commands return <errorcode>0</errorcode>;
+      on error, <errorcode>-1</errorcode> is returned, and
+      <varname>errno</varname> is set to indicate the error.
+      If the issued ioctl is illegal for the file descriptor used,
+      <varname>errno</varname> will be set to <constant>ENOTTY</constant>.
+    </para>
+
+    <refsect2>
+      <title>
+        <constant>KDBUS_CMD_NAME_ACQUIRE</constant> may fail with the following
+        errors
+      </title>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>EINVAL</constant></term>
+          <listitem><para>
+            Illegal command flags, illegal name provided, or an activator
+            tried to acquire a second name.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>EPERM</constant></term>
+          <listitem><para>
+            Policy prohibited name ownership.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>EALREADY</constant></term>
+          <listitem><para>
+            Connection already owns that name.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>EEXIST</constant></term>
+          <listitem><para>
+            The name already exists and can not be taken over.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>E2BIG</constant></term>
+          <listitem><para>
+            The maximum number of well-known names per connection is exhausted.
+          </para></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+
+    <refsect2>
+      <title>
+        <constant>KDBUS_CMD_NAME_RELEASE</constant>
+        may fail with the following errors
+      </title>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>EINVAL</constant></term>
+          <listitem><para>
+            Invalid command flags, or invalid name provided.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>ESRCH</constant></term>
+          <listitem><para>
+            Name is not found in the registry.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>EADDRINUSE</constant></term>
+          <listitem><para>
+            Name is owned by a different connection and can't be released.
+          </para></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+
+    <refsect2>
+      <title>
+        <constant>KDBUS_CMD_LIST</constant> may fail with the following
+        errors
+      </title>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>EINVAL</constant></term>
+          <listitem><para>
+            Invalid command flags
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>ENOBUFS</constant></term>
+          <listitem><para>
+            No available memory in the connection's pool.
+          </para></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+    <simplelist type="inline">
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.bus</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.connection</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.item</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.policy</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.pool</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+    </simplelist>
+  </refsect1>
+</refentry>
diff --git a/ipc/kdbus/Documentation/kdbus.policy.xml b/ipc/kdbus/Documentation/kdbus.policy.xml
new file mode 100644 (file)
index 0000000..6732416
--- /dev/null
@@ -0,0 +1,406 @@
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+        "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<refentry id="kdbus.policy">
+
+  <refentryinfo>
+    <title>kdbus.policy</title>
+    <productname>kdbus.policy</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>kdbus.policy</refentrytitle>
+    <manvolnum>7</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>kdbus.policy</refname>
+    <refpurpose>kdbus policy</refpurpose>
+  </refnamediv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>
+      A kdbus policy restricts the possibilities of connections to own, see and
+      talk to well-known names. A policy can be associated with a bus (through a
+      policy holder connection) or a custom endpoint. kdbus stores its policy
+      information in a database that can be accessed through the following
+      ioctl commands:
+    </para>
+
+    <variablelist>
+      <varlistentry>
+        <term><constant>KDBUS_CMD_HELLO</constant></term>
+        <listitem><para>
+          When creating, or updating, a policy holder connection. See
+          <citerefentry>
+            <refentrytitle>kdbus.connection</refentrytitle>
+            <manvolnum>7</manvolnum>
+          </citerefentry>.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>KDBUS_CMD_ENDPOINT_MAKE</constant></term>
+        <term><constant>KDBUS_CMD_ENDPOINT_UPDATE</constant></term>
+        <listitem><para>
+          When creating, or updating, a bus custom endpoint. See
+          <citerefentry>
+            <refentrytitle>kdbus.endpoint</refentrytitle>
+            <manvolnum>7</manvolnum>
+          </citerefentry>.
+        </para></listitem>
+      </varlistentry>
+    </variablelist>
+
+    <para>
+      In all cases, the name and policy access information is stored in items
+      of type <constant>KDBUS_ITEM_NAME</constant> and
+      <constant>KDBUS_ITEM_POLICY_ACCESS</constant>. For this transport, the
+      following rules apply.
+    </para>
+
+    <itemizedlist>
+      <listitem>
+        <para>
+          An item of type <constant>KDBUS_ITEM_NAME</constant> must be followed
+          by at least one <constant>KDBUS_ITEM_POLICY_ACCESS</constant> item.
+        </para>
+      </listitem>
+
+      <listitem>
+        <para>
+          An item of type <constant>KDBUS_ITEM_NAME</constant> can be followed
+          by an arbitrary number of
+          <constant>KDBUS_ITEM_POLICY_ACCESS</constant> items.
+        </para>
+      </listitem>
+
+      <listitem>
+        <para>
+          An arbitrary number of groups of names and access levels can be given.
+        </para>
+      </listitem>
+    </itemizedlist>
+
+    <para>
+      Names passed in items of type <constant>KDBUS_ITEM_NAME</constant> must
+      comply to the rules of valid kdbus.name. See
+      <citerefentry>
+        <refentrytitle>kdbus.name</refentrytitle>
+        <manvolnum>7</manvolnum>
+      </citerefentry>
+      for more information.
+
+      The payload of an item of type
+      <constant>KDBUS_ITEM_POLICY_ACCESS</constant> is defined by the following
+      struct. For more information on the layout of items, please refer to
+      <citerefentry>
+        <refentrytitle>kdbus.item</refentrytitle>
+        <manvolnum>7</manvolnum>
+      </citerefentry>.
+    </para>
+
+    <programlisting>
+struct kdbus_policy_access {
+  __u64 type;
+  __u64 access;
+  __u64 id;
+};
+    </programlisting>
+
+    <para>The fields in this struct are described below.</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><varname>type</varname></term>
+        <listitem>
+          <para>
+            One of the following.
+          </para>
+
+          <variablelist>
+            <varlistentry>
+              <term><constant>KDBUS_POLICY_ACCESS_USER</constant></term>
+              <listitem><para>
+                Grant access to a user with the UID stored in the
+                <varname>id</varname> field.
+              </para></listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_POLICY_ACCESS_GROUP</constant></term>
+              <listitem><para>
+                Grant access to a user with the GID stored in the
+                <varname>id</varname> field.
+              </para></listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_POLICY_ACCESS_WORLD</constant></term>
+              <listitem><para>
+                Grant access to everyone. The <varname>id</varname> field
+                is ignored.
+              </para></listitem>
+            </varlistentry>
+          </variablelist>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>access</varname></term>
+        <listitem>
+          <para>
+            The access to grant. One of the following.
+          </para>
+
+          <variablelist>
+            <varlistentry>
+              <term><constant>KDBUS_POLICY_SEE</constant></term>
+              <listitem><para>
+                Allow the name to be seen.
+              </para></listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_POLICY_TALK</constant></term>
+              <listitem><para>
+                Allow the name to be talked to.
+              </para></listitem>
+            </varlistentry>
+
+            <varlistentry>
+              <term><constant>KDBUS_POLICY_OWN</constant></term>
+              <listitem><para>
+                Allow the name to be owned.
+              </para></listitem>
+            </varlistentry>
+          </variablelist>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>id</varname></term>
+        <listitem><para>
+           For <constant>KDBUS_POLICY_ACCESS_USER</constant>, stores the UID.
+           For <constant>KDBUS_POLICY_ACCESS_GROUP</constant>, stores the GID.
+        </para></listitem>
+      </varlistentry>
+
+    </variablelist>
+
+    <para>
+      All endpoints of buses have an empty policy database by default.
+      Therefore, unless policy rules are added, all operations will also be
+      denied by default. Also see
+      <citerefentry>
+        <refentrytitle>kdbus.endpoint</refentrytitle>
+        <manvolnum>7</manvolnum>
+      </citerefentry>.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>Wildcard names</title>
+    <para>
+      Policy holder connections may upload names that contain the wildcard
+      suffix (<literal>".*"</literal>). Such a policy entry is effective for
+      every well-known name that extends the provided name by exactly one more
+      level.
+
+      For example, the name <literal>foo.bar.*</literal> matches both
+      <literal>"foo.bar.baz"</literal> and
+      <literal>"foo.bar.bazbaz"</literal> are, but not
+      <literal>"foo.bar.baz.baz"</literal>.
+
+      This allows connections to take control over multiple names that the
+      policy holder doesn't need to know about when uploading the policy.
+
+      Such wildcard entries are not allowed for custom endpoints.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>Privileged connections</title>
+    <para>
+      The policy database is overruled when action is taken by a privileged
+      connection. Please refer to
+      <citerefentry>
+        <refentrytitle>kdbus.connection</refentrytitle>
+        <manvolnum>7</manvolnum>
+      </citerefentry>
+      for more information on what makes a connection privileged.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>Examples</title>
+    <para>
+      For instance, a set of policy rules may look like this:
+    </para>
+
+    <programlisting>
+KDBUS_ITEM_NAME: str='org.foo.bar'
+KDBUS_ITEM_POLICY_ACCESS: type=USER, access=OWN, ID=1000
+KDBUS_ITEM_POLICY_ACCESS: type=USER, access=TALK, ID=1001
+KDBUS_ITEM_POLICY_ACCESS: type=WORLD, access=SEE
+
+KDBUS_ITEM_NAME: str='org.blah.baz'
+KDBUS_ITEM_POLICY_ACCESS: type=USER, access=OWN, ID=0
+KDBUS_ITEM_POLICY_ACCESS: type=WORLD, access=TALK
+    </programlisting>
+
+    <para>
+      That means that 'org.foo.bar' may only be owned by UID 1000, but every
+      user on the bus is allowed to see the name. However, only UID 1001 may
+      actually send a message to the connection and receive a reply from it.
+
+      The second rule allows 'org.blah.baz' to be owned by UID 0 only, but
+      every user may talk to it.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>TALK access and multiple well-known names per connection</title>
+    <para>
+      Note that TALK access is checked against all names of a connection. For
+      example, if a connection owns both <constant>'org.foo.bar'</constant> and
+      <constant>'org.blah.baz'</constant>, and the policy database allows
+      <constant>'org.blah.baz'</constant> to be talked to by WORLD, then this
+      permission is also granted to <constant>'org.foo.bar'</constant>. That
+      might sound illogical, but after all, we allow messages to be directed to
+      either the ID or a well-known name, and policy is applied to the
+      connection, not the name. In other words, the effective TALK policy for a
+      connection is the most permissive of all names the connection owns.
+
+      For broadcast messages, the receiver needs TALK permissions to the sender
+      to receive the broadcast.
+    </para>
+    <para>
+      Both the endpoint and the bus policy databases are consulted to allow
+      name registry listing, owning a well-known name and message delivery.
+      If either one fails, the operation is failed with
+      <varname>errno</varname> set to <constant>EPERM</constant>.
+
+      For best practices, connections that own names with a restricted TALK
+      access should not install matches. This avoids cases where the sent
+      message may pass the bloom filter due to false-positives and may also
+      satisfy the policy rules.
+
+      Also see
+      <citerefentry>
+        <refentrytitle>kdbus.match</refentrytitle>
+        <manvolnum>7</manvolnum>
+      </citerefentry>.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>Implicit policies</title>
+    <para>
+      Depending on the type of the endpoint, a set of implicit rules that
+      override installed policies might be enforced.
+
+      On default endpoints, the following set is enforced and checked before
+      any user-supplied policy is checked.
+    </para>
+
+    <itemizedlist>
+      <listitem>
+        <para>
+          Privileged connections always override any installed policy. Those
+          connections could easily install their own policies, so there is no
+          reason to enforce installed policies.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          Connections can always talk to connections of the same user. This
+          includes broadcast messages.
+        </para>
+      </listitem>
+    </itemizedlist>
+
+    <para>
+      Custom endpoints have stricter policies. The following rules apply:
+    </para>
+
+    <itemizedlist>
+      <listitem>
+        <para>
+          Policy rules are always enforced, even if the connection is a
+          privileged connection.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          Policy rules are always enforced for <constant>TALK</constant> access,
+          even if both ends are running under the same user. This includes
+          broadcast messages.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          To restrict the set of names that can be seen, endpoint policies can
+          install <constant>SEE</constant> policies.
+        </para>
+      </listitem>
+    </itemizedlist>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+    <simplelist type="inline">
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.bus</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.endpoint</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.fs</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.item</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.message</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.name</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.pool</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+    </simplelist>
+  </refsect1>
+</refentry>
diff --git a/ipc/kdbus/Documentation/kdbus.pool.xml b/ipc/kdbus/Documentation/kdbus.pool.xml
new file mode 100644 (file)
index 0000000..a9e16f1
--- /dev/null
@@ -0,0 +1,326 @@
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+        "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<refentry id="kdbus.pool">
+
+  <refentryinfo>
+    <title>kdbus.pool</title>
+    <productname>kdbus.pool</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>kdbus.pool</refentrytitle>
+    <manvolnum>7</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>kdbus.pool</refname>
+    <refpurpose>kdbus pool</refpurpose>
+  </refnamediv>
+
+  <refsect1>
+    <title>Description</title>
+    <para>
+      A pool for data received from the kernel is installed for every
+      <emphasis>connection</emphasis> of the <emphasis>bus</emphasis>, and
+      is sized according to the information stored in the
+      <varname>pool_size</varname> member of <type>struct kdbus_cmd_hello</type>
+      when <constant>KDBUS_CMD_HELLO</constant> is employed. Internally, the
+      pool is segmented into <emphasis>slices</emphasis>, each referenced by its
+      <emphasis>offset</emphasis> in the pool, expressed in <type>bytes</type>.
+      See
+      <citerefentry>
+        <refentrytitle>kdbus.connection</refentrytitle>
+        <manvolnum>7</manvolnum>
+      </citerefentry>
+      for more information about <constant>KDBUS_CMD_HELLO</constant>.
+    </para>
+
+    <para>
+      The pool is written to by the kernel when one of the following
+      <emphasis>ioctls</emphasis> is issued:
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>KDBUS_CMD_HELLO</constant></term>
+          <listitem><para>
+            ... to receive details about the bus the connection was made to
+          </para></listitem>
+        </varlistentry>
+        <varlistentry>
+          <term><constant>KDBUS_CMD_RECV</constant></term>
+          <listitem><para>
+            ... to receive a message
+          </para></listitem>
+        </varlistentry>
+        <varlistentry>
+          <term><constant>KDBUS_CMD_LIST</constant></term>
+          <listitem><para>
+            ... to dump the name registry
+          </para></listitem>
+        </varlistentry>
+        <varlistentry>
+          <term><constant>KDBUS_CMD_CONN_INFO</constant></term>
+          <listitem><para>
+            ... to retrieve information on a connection
+          </para></listitem>
+        </varlistentry>
+        <varlistentry>
+          <term><constant>KDBUS_CMD_BUS_CREATOR_INFO</constant></term>
+          <listitem><para>
+            ... to retrieve information about a connection's bus creator
+          </para></listitem>
+        </varlistentry>
+      </variablelist>
+
+    </para>
+    <para>
+      The <varname>offset</varname> fields returned by either one of the
+      aforementioned ioctls describe offsets inside the pool. In order to make
+      the slice available for subsequent calls,
+      <constant>KDBUS_CMD_FREE</constant> has to be called on that offset
+      (see below). Otherwise, the pool will fill up, and the connection won't
+      be able to receive any more information through its pool.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>Pool slice allocation</title>
+    <para>
+      Pool slices are allocated by the kernel in order to report information
+      back to a task, such as messages, returned name list etc.
+      Allocation of pool slices cannot be initiated by userspace. See
+      <citerefentry>
+        <refentrytitle>kdbus.connection</refentrytitle>
+        <manvolnum>7</manvolnum>
+      </citerefentry>
+      and
+      <citerefentry>
+        <refentrytitle>kdbus.name</refentrytitle>
+        <manvolnum>7</manvolnum>
+      </citerefentry>
+      for examples of commands that use the <emphasis>pool</emphasis> to
+      return data.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>Accessing the pool memory</title>
+    <para>
+      Memory in the pool is read-only for userspace and may only be written
+      to by the kernel. To read from the pool memory, the caller is expected to
+      <citerefentry>
+        <refentrytitle>mmap</refentrytitle>
+        <manvolnum>2</manvolnum>
+      </citerefentry>
+      the buffer into its task, like this:
+    </para>
+    <programlisting>
+uint8_t *buf = mmap(NULL, size, PROT_READ, MAP_SHARED, conn_fd, 0);
+    </programlisting>
+
+    <para>
+      In order to map the entire pool, the <varname>size</varname> parameter in
+      the example above should be set to the value of the
+      <varname>pool_size</varname> member of
+      <type>struct kdbus_cmd_hello</type> when
+      <constant>KDBUS_CMD_HELLO</constant> was employed to create the
+      connection (see above).
+    </para>
+
+    <para>
+      The <emphasis>file descriptor</emphasis> used to map the memory must be
+      the one that was used to create the <emphasis>connection</emphasis>.
+      In other words, the one that was used to call
+      <constant>KDBUS_CMD_HELLO</constant>. See
+      <citerefentry>
+        <refentrytitle>kdbus.connection</refentrytitle>
+        <manvolnum>7</manvolnum>
+      </citerefentry>
+      for more details.
+    </para>
+
+    <para>
+      Alternatively, instead of mapping the entire pool buffer, only parts
+      of it can be mapped. Every kdbus command that returns an
+      <emphasis>offset</emphasis> (see above) also reports a
+      <emphasis>size</emphasis> along with it, so programs can be written
+      in a way that it only maps portions of the pool to access a specific
+      <emphasis>slice</emphasis>.
+    </para>
+
+    <para>
+      When access to the pool memory is no longer needed, programs should
+      call <function>munmap()</function> on the pointer returned by
+      <function>mmap()</function>.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>Freeing pool slices</title>
+    <para>
+      The <constant>KDBUS_CMD_FREE</constant> ioctl is used to free a slice
+      inside the pool, describing an offset that was returned in an
+      <varname>offset</varname> field of another ioctl struct.
+      The <constant>KDBUS_CMD_FREE</constant> command takes a
+      <type>struct kdbus_cmd_free</type> as argument.
+    </para>
+
+<programlisting>
+struct kdbus_cmd_free {
+  __u64 size;
+  __u64 flags;
+  __u64 return_flags;
+  __u64 offset;
+  struct kdbus_item items[0];
+};
+</programlisting>
+
+    <para>The fields in this struct are described below.</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><varname>size</varname></term>
+        <listitem><para>
+          The overall size of the struct, including its items.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>flags</varname></term>
+        <listitem><para>
+          Currently unused.
+          <constant>KDBUS_FLAG_NEGOTIATE</constant> is accepted to probe for
+          valid flags. If set, the ioctl will return <errorcode>0</errorcode>,
+          and the <varname>flags</varname> field is set to
+          <constant>0</constant>.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>return_flags</varname></term>
+        <listitem><para>
+          Flags returned by the kernel. Currently unused and always set to
+          <constant>0</constant> by the kernel.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>offset</varname></term>
+        <listitem><para>
+          The offset to free, as returned by other ioctls that allocated
+          memory for returned information.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>items</varname></term>
+        <listitem><para>
+          Items to specify further details for the receive command.
+          Currently unused.
+          Unrecognized items are rejected, and the ioctl will fail with
+          <varname>errno</varname> set to <constant>EINVAL</constant>.
+          All items except for
+          <constant>KDBUS_ITEM_NEGOTIATE</constant> (see
+            <citerefentry>
+              <refentrytitle>kdbus.item</refentrytitle>
+              <manvolnum>7</manvolnum>
+            </citerefentry>
+          ) will be rejected.
+        </para></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Return value</title>
+    <para>
+      On success, all mentioned ioctl commands return <errorcode>0</errorcode>;
+      on error, <errorcode>-1</errorcode> is returned, and
+      <varname>errno</varname> is set to indicate the error.
+      If the issued ioctl is illegal for the file descriptor used,
+      <varname>errno</varname> will be set to <constant>ENOTTY</constant>.
+    </para>
+
+    <refsect2>
+      <title>
+        <constant>KDBUS_CMD_FREE</constant> may fail with the following
+        errors
+      </title>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>ENXIO</constant></term>
+          <listitem><para>
+            No pool slice found at given offset.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>EINVAL</constant></term>
+          <listitem><para>
+            Invalid flags provided.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>EINVAL</constant></term>
+          <listitem><para>
+            The offset is valid, but the user is not allowed to free the slice.
+            This happens, for example, if the offset was retrieved with
+            <constant>KDBUS_RECV_PEEK</constant>.
+          </para></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+    <simplelist type="inline">
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.bus</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.connection</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.endpoint</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.name</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>mmap</refentrytitle>
+            <manvolnum>2</manvolnum>
+          </citerefentry>
+        </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>munmap</refentrytitle>
+          <manvolnum>2</manvolnum>
+        </citerefentry>
+      </member>
+    </simplelist>
+  </refsect1>
+</refentry>
diff --git a/ipc/kdbus/Documentation/kdbus.xml b/ipc/kdbus/Documentation/kdbus.xml
new file mode 100644 (file)
index 0000000..d8e7400
--- /dev/null
@@ -0,0 +1,1012 @@
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+        "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<refentry id="kdbus">
+
+  <refentryinfo>
+    <title>kdbus</title>
+    <productname>kdbus</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>kdbus</refentrytitle>
+    <manvolnum>7</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>kdbus</refname>
+    <refpurpose>Kernel Message Bus</refpurpose>
+  </refnamediv>
+
+  <refsect1>
+    <title>Synopsis</title>
+    <para>
+      kdbus is an inter-process communication bus system controlled by the
+      kernel. It provides user-space with an API to create buses and send
+      unicast and multicast messages to one, or many, peers connected to the
+      same bus. It does not enforce any layout on the transmitted data, but
+      only provides the transport layer used for message interchange between
+      peers.
+    </para>
+    <para>
+      This set of man-pages gives a comprehensive overview of the kernel-level
+      API, with all ioctl commands, associated structs and bit masks. However,
+      most people will not use this API level directly, but rather let one of
+      the high-level abstraction libraries help them integrate D-Bus
+      functionality into their applications.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>Description</title>
+    <para>
+      kdbus provides a pseudo filesystem called <emphasis>kdbusfs</emphasis>,
+      which is usually mounted on <filename>/sys/fs/kdbus</filename>. Bus
+      primitives can be accessed as files and sub-directories underneath this
+      mount-point. Any advanced operations are done via
+      <function>ioctl()</function> on files created by
+      <emphasis>kdbusfs</emphasis>. Multiple mount-points of
+      <emphasis>kdbusfs</emphasis> are independent of each other. This allows
+      namespacing of kdbus by mounting a new instance of
+      <emphasis>kdbusfs</emphasis> in a new mount-namespace. kdbus calls these
+      mount instances domains and each bus belongs to exactly one domain.
+    </para>
+
+    <para>
+      kdbus was designed as a transport layer for D-Bus, but is in no way
+      limited, nor controlled by the D-Bus protocol specification. The D-Bus
+      protocol is one possible application layer on top of kdbus.
+    </para>
+
+    <para>
+      For the general D-Bus protocol specification, its payload format, its
+      marshaling, and its communication semantics, please refer to the
+      <ulink url="http://dbus.freedesktop.org/doc/dbus-specification.html">
+      D-Bus specification</ulink>.
+    </para>
+
+  </refsect1>
+
+  <refsect1>
+    <title>Terminology</title>
+
+    <refsect2>
+      <title>Domain</title>
+      <para>
+        A domain is a <emphasis>kdbusfs</emphasis> mount-point containing all
+        the bus primitives. Each domain is independent, and separate domains
+        do not affect each other.
+      </para>
+    </refsect2>
+
+    <refsect2>
+      <title>Bus</title>
+      <para>
+        A bus is a named object inside a domain. Clients exchange messages
+        over a bus. Multiple buses themselves have no connection to each other;
+        messages can only be exchanged on the same bus. The default endpoint of
+        a bus, to which clients establish connections, is the "bus" file
+        /sys/fs/kdbus/&lt;bus name&gt;/bus.
+        Common operating system setups create one "system bus" per system,
+        and one "user bus" for every logged-in user. Applications or services
+        may create their own private buses. The kernel driver does not
+        distinguish between different bus types, they are all handled the same
+        way. See
+        <citerefentry>
+          <refentrytitle>kdbus.bus</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+        for more details.
+      </para>
+    </refsect2>
+
+    <refsect2>
+      <title>Endpoint</title>
+      <para>
+        An endpoint provides a file to talk to a bus. Opening an endpoint
+        creates a new connection to the bus to which the endpoint belongs. All
+        endpoints have unique names and are accessible as files underneath the
+        directory of a bus, e.g., /sys/fs/kdbus/&lt;bus&gt;/&lt;endpoint&gt;
+        Every bus has a default endpoint called "bus".
+        A bus can optionally offer additional endpoints with custom names
+        to provide restricted access to the bus. Custom endpoints carry
+        additional policy which can be used to create sandboxes with
+        locked-down, limited, filtered access to a bus. See
+        <citerefentry>
+          <refentrytitle>kdbus.endpoint</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+        for more details.
+      </para>
+    </refsect2>
+
+    <refsect2>
+      <title>Connection</title>
+      <para>
+        A connection to a bus is created by opening an endpoint file of a
+        bus. Every ordinary client connection has a unique identifier on the
+        bus and can address messages to every other connection on the same
+        bus by using the peer's connection ID as the destination. See
+        <citerefentry>
+          <refentrytitle>kdbus.connection</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+        for more details.
+      </para>
+    </refsect2>
+
+    <refsect2>
+      <title>Pool</title>
+      <para>
+        Each connection allocates a piece of shmem-backed memory that is
+        used to receive messages and answers to ioctl commands from the kernel.
+        It is never used to send anything to the kernel. In order to access that
+        memory, an application must mmap() it into its address space. See
+        <citerefentry>
+          <refentrytitle>kdbus.pool</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+        for more details.
+      </para>
+    </refsect2>
+
+    <refsect2>
+      <title>Well-known Name</title>
+      <para>
+        A connection can, in addition to its implicit unique connection ID,
+        request the ownership of a textual well-known name. Well-known names are
+        noted in reverse-domain notation, such as com.example.service1. A
+        connection that offers a service on a bus is usually reached by its
+        well-known name. An analogy of connection ID and well-known name is an
+        IP address and a DNS name associated with that address. See
+        <citerefentry>
+          <refentrytitle>kdbus.name</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+        for more details.
+      </para>
+    </refsect2>
+
+    <refsect2>
+      <title>Message</title>
+      <para>
+        Connections can exchange messages with other connections by addressing
+        the peers with their connection ID or well-known name. A message
+        consists of a message header with information on how to route the
+        message, and the message payload, which is a logical byte stream of
+        arbitrary size. Messages can carry additional file descriptors to be
+        passed from one connection to another, just like passing file
+        descriptors over UNIX domain sockets. Every connection can specify which
+        set of metadata the kernel should attach to the message when it is
+        delivered to the receiving connection. Metadata contains information
+        like: system time stamps, UID, GID, TID, proc-starttime, well-known
+        names, process comm, process exe, process argv, cgroup, capabilities,
+        seclabel, audit session, loginuid and the connection's human-readable
+        name. See
+        <citerefentry>
+          <refentrytitle>kdbus.message</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+        for more details.
+      </para>
+    </refsect2>
+
+    <refsect2>
+      <title>Item</title>
+      <para>
+        The API of kdbus implements the notion of items, submitted through and
+        returned by most ioctls, and stored inside data structures in the
+        connection's pool. See
+        <citerefentry>
+          <refentrytitle>kdbus.item</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+        for more details.
+      </para>
+    </refsect2>
+
+    <refsect2>
+      <title>Broadcast, signal, filter, match</title>
+      <para>
+        Signals are messages that a receiver opts in for by installing a blob of
+        bytes, called a 'match'. Signal messages must always carry a
+        counter-part blob, called a 'filter', and signals are only delivered to
+        peers which have a match that white-lists the message's filter. Senders
+        of signal messages can use either a single connection ID as receiver,
+        or the special connection ID
+        <constant>KDBUS_DST_ID_BROADCAST</constant> to potentially send it to
+        all connections of a bus, following the logic described above. See
+        <citerefentry>
+          <refentrytitle>kdbus.match</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+        and
+        <citerefentry>
+          <refentrytitle>kdbus.message</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+        for more details.
+      </para>
+    </refsect2>
+
+    <refsect2>
+      <title>Policy</title>
+      <para>
+        A policy is a set of rules that define which connections can see, talk
+        to, or register a well-known name on the bus. A policy is attached to
+        buses and custom endpoints, and modified by policy holder connections or
+        owners of custom endpoints. See
+        <citerefentry>
+          <refentrytitle>kdbus.policy</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+        for more details.
+      </para>
+    </refsect2>
+
+    <refsect2>
+      <title>Privileged bus users</title>
+      <para>
+        A user connecting to the bus is considered privileged if it is either
+        the creator of the bus, or if it has the CAP_IPC_OWNER capability flag
+        set. See
+        <citerefentry>
+          <refentrytitle>kdbus.connection</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+        for more details.
+      </para>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>Bus Layout</title>
+
+    <para>
+      A <emphasis>bus</emphasis> provides and defines an environment that peers
+      can connect to for message interchange. A bus is created via the kdbus
+      control interface and can be modified by the bus creator. It applies the
+      policy that control all bus operations. The bus creator itself does not
+      participate as a peer. To establish a peer
+      <emphasis>connection</emphasis>, you have to open one of the
+      <emphasis>endpoints</emphasis> of a bus. Each bus provides a default
+      endpoint, but further endpoints can be created on-demand. Endpoints are
+      used to apply additional policies for all connections on this endpoint.
+      Thus, they provide additional filters to further restrict access of
+      specific connections to the bus.
+    </para>
+
+    <para>
+      Following, you can see an example bus layout:
+    </para>
+
+    <programlisting><![CDATA[
+                                  Bus Creator
+                                       |
+                                       |
+                                    +-----+
+                                    | Bus |
+                                    +-----+
+                                       |
+                    __________________/ \__________________
+                   /                                       \
+                   |                                       |
+             +----------+                             +----------+
+             | Endpoint |                             | Endpoint |
+             +----------+                             +----------+
+         _________/|\_________                   _________/|\_________
+        /          |          \                 /          |          \
+        |          |          |                 |          |          |
+        |          |          |                 |          |          |
+   Connection  Connection  Connection      Connection  Connection  Connection
+    ]]></programlisting>
+
+  </refsect1>
+
+  <refsect1>
+    <title>Data structures and interconnections</title>
+    <programlisting><![CDATA[
+  +--------------------------------------------------------------------------+
+  | Domain (Mount Point)                                                     |
+  | /sys/fs/kdbus/control                                                    |
+  | +----------------------------------------------------------------------+ |
+  | | Bus (System Bus)                                                     | |
+  | | /sys/fs/kdbus/0-system/                                              | |
+  | | +-------------------------------+ +--------------------------------+ | |
+  | | | Endpoint                      | | Endpoint                       | | |
+  | | | /sys/fs/kdbus/0-system/bus    | | /sys/fs/kdbus/0-system/ep.app  | | |
+  | | +-------------------------------+ +--------------------------------+ | |
+  | | +--------------+ +--------------+ +--------------+ +---------------+ | |
+  | | | Connection   | | Connection   | | Connection   | | Connection    | | |
+  | | | :1.22        | | :1.25        | | :1.55        | | :1.81         | | |
+  | | +--------------+ +--------------+ +--------------+ +---------------+ | |
+  | +----------------------------------------------------------------------+ |
+  |                                                                          |
+  | +----------------------------------------------------------------------+ |
+  | | Bus (User Bus for UID 2702)                                          | |
+  | | /sys/fs/kdbus/2702-user/                                             | |
+  | | +-------------------------------+ +--------------------------------+ | |
+  | | | Endpoint                      | | Endpoint                       | | |
+  | | | /sys/fs/kdbus/2702-user/bus   | | /sys/fs/kdbus/2702-user/ep.app | | |
+  | | +-------------------------------+ +--------------------------------+ | |
+  | | +--------------+ +--------------+ +--------------+ +---------------+ | |
+  | | | Connection   | | Connection   | | Connection   | | Connection    | | |
+  | | | :1.22        | | :1.25        | | :1.55        | | :1.81         | | |
+  | | +--------------+ +--------------+ +--------------------------------+ | |
+  | +----------------------------------------------------------------------+ |
+  +--------------------------------------------------------------------------+
+    ]]></programlisting>
+  </refsect1>
+
+  <refsect1>
+    <title>Metadata</title>
+
+    <refsect2>
+      <title>When metadata is collected</title>
+      <para>
+        kdbus records data about the system in certain situations. Such metadata
+        can refer to the currently active process (creds, PIDs, current user
+        groups, process names and its executable path, cgroup membership,
+        capabilities, security label and audit information), connection
+        information (description string, currently owned names) and time stamps.
+      </para>
+      <para>
+        Metadata is collected at the following times.
+      </para>
+
+      <itemizedlist>
+        <listitem><para>
+          When a bus is created (<constant>KDBUS_CMD_MAKE</constant>),
+          information about the calling task is collected. This data is returned
+          by the kernel via the <constant>KDBUS_CMD_BUS_CREATOR_INFO</constant>
+          call.
+        </para></listitem>
+
+        <listitem>
+          <para>
+            When a connection is created (<constant>KDBUS_CMD_HELLO</constant>),
+            information about the calling task is collected. Alternatively, a
+            privileged connection may provide 'faked' information about
+            credentials, PIDs and security labels which will be stored instead.
+            This data is returned by the kernel as information on a connection
+            (<constant>KDBUS_CMD_CONN_INFO</constant>). Only metadata that a
+            connection allowed to be sent (by setting its bit in
+            <varname>attach_flags_send</varname>) will be exported in this way.
+          </para>
+        </listitem>
+
+        <listitem>
+          <para>
+            When a message is sent (<constant>KDBUS_CMD_SEND</constant>),
+            information about the sending task and the sending connection is
+            collected. This metadata will be attached to the message when it
+            arrives in the receiver's pool. If the connection sending the
+            message installed faked credentials (see
+            <citerefentry>
+              <refentrytitle>kdbus.connection</refentrytitle>
+              <manvolnum>7</manvolnum>
+            </citerefentry>),
+            the message will not be augmented by any information about the
+            currently sending task. Note that only metadata that was requested
+            by the receiving connection will be collected and attached to
+            messages.
+          </para>
+        </listitem>
+      </itemizedlist>
+
+      <para>
+        Which metadata items are actually delivered depends on the following
+        sets and masks:
+      </para>
+
+      <itemizedlist>
+        <listitem><para>
+          (a) the system-wide kmod creds mask
+          (module parameter <varname>attach_flags_mask</varname>)
+        </para></listitem>
+
+        <listitem><para>
+          (b) the per-connection send creds mask, set by the connecting client
+        </para></listitem>
+
+        <listitem><para>
+          (c) the per-connection receive creds mask, set by the connecting
+          client
+        </para></listitem>
+
+        <listitem><para>
+          (d) the per-bus minimal creds mask, set by the bus creator
+        </para></listitem>
+
+        <listitem><para>
+          (e) the per-bus owner creds mask, set by the bus creator
+        </para></listitem>
+
+        <listitem><para>
+          (f) the mask specified when querying creds of a bus peer
+        </para></listitem>
+
+        <listitem><para>
+          (g) the mask specified when querying creds of a bus owner
+        </para></listitem>
+      </itemizedlist>
+
+      <para>
+        With the following rules:
+      </para>
+
+      <itemizedlist>
+        <listitem>
+          <para>
+            [1] The creds attached to messages are determined as
+            <constant>a &amp; b &amp; c</constant>.
+          </para>
+        </listitem>
+
+        <listitem>
+          <para>
+            [2] When connecting to a bus (<constant>KDBUS_CMD_HELLO</constant>),
+            and <constant>~b &amp; d != 0</constant>, the call will fail with,
+            <errorcode>-1</errorcode>, and <varname>errno</varname> is set to
+            <constant>ECONNREFUSED</constant>.
+          </para>
+        </listitem>
+
+        <listitem>
+          <para>
+            [3] When querying creds of a bus peer, the creds returned are
+            <constant>a &amp; b &amp; f</constant>.
+          </para>
+        </listitem>
+
+        <listitem>
+          <para>
+            [4] When querying creds of a bus owner, the creds returned are
+            <constant>a &amp; e &amp; g</constant>.
+          </para>
+        </listitem>
+      </itemizedlist>
+
+      <para>
+        Hence, programs might not always get all requested metadata items that
+        it requested. Code must be written so that it can cope with this fact.
+      </para>
+    </refsect2>
+
+    <refsect2>
+      <title>Benefits and heads-up</title>
+      <para>
+        Attaching metadata to messages has two major benefits.
+
+        <itemizedlist>
+          <listitem>
+            <para>
+              Metadata attached to messages is gathered at the moment when the
+              other side calls <constant>KDBUS_CMD_SEND</constant>, or,
+              respectively, then the kernel notification is generated. There is
+              no need for the receiving peer to retrieve information about the
+              task in a second step. This closes a race gap that would otherwise
+              be inherent.
+            </para>
+          </listitem>
+          <listitem>
+            <para>
+              As metadata is delivered along with messages in the same data
+              blob, no extra calls to kernel functions etc. are needed to gather
+              them.
+            </para>
+          </listitem>
+        </itemizedlist>
+
+        Note, however, that collecting metadata does come at a price for
+        performance, so developers should carefully assess which metadata to
+        really opt-in for. For best practice, data that is not needed as part
+        of a message should not be requested by the connection in the first
+        place (see <varname>attach_flags_recv</varname> in
+        <constant>KDBUS_CMD_HELLO</constant>).
+      </para>
+    </refsect2>
+
+    <refsect2>
+      <title>Attach flags for metadata items</title>
+      <para>
+        To let the kernel know which metadata information to attach as items
+        to the aforementioned commands, it uses a bitmask. In those, the
+        following <emphasis>attach flags</emphasis> are currently supported.
+        Both the <varname>attach_flags_recv</varname> and
+        <varname>attach_flags_send</varname> fields of
+        <type>struct kdbus_cmd_hello</type>, as well as the payload of the
+        <constant>KDBUS_ITEM_ATTACH_FLAGS_SEND</constant> and
+        <constant>KDBUS_ITEM_ATTACH_FLAGS_RECV</constant> items follow this
+        scheme.
+      </para>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>KDBUS_ATTACH_TIMESTAMP</constant></term>
+            <listitem><para>
+              Requests the attachment of an item of type
+              <constant>KDBUS_ITEM_TIMESTAMP</constant>.
+            </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>KDBUS_ATTACH_CREDS</constant></term>
+            <listitem><para>
+              Requests the attachment of an item of type
+              <constant>KDBUS_ITEM_CREDS</constant>.
+            </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>KDBUS_ATTACH_PIDS</constant></term>
+            <listitem><para>
+              Requests the attachment of an item of type
+              <constant>KDBUS_ITEM_PIDS</constant>.
+            </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>KDBUS_ATTACH_AUXGROUPS</constant></term>
+            <listitem><para>
+              Requests the attachment of an item of type
+              <constant>KDBUS_ITEM_AUXGROUPS</constant>.
+            </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>KDBUS_ATTACH_NAMES</constant></term>
+            <listitem><para>
+              Requests the attachment of an item of type
+              <constant>KDBUS_ITEM_OWNED_NAME</constant>.
+            </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>KDBUS_ATTACH_TID_COMM</constant></term>
+            <listitem><para>
+              Requests the attachment of an item of type
+              <constant>KDBUS_ITEM_TID_COMM</constant>.
+            </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>KDBUS_ATTACH_PID_COMM</constant></term>
+            <listitem><para>
+              Requests the attachment of an item of type
+              <constant>KDBUS_ITEM_PID_COMM</constant>.
+            </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>KDBUS_ATTACH_EXE</constant></term>
+            <listitem><para>
+              Requests the attachment of an item of type
+              <constant>KDBUS_ITEM_EXE</constant>.
+            </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>KDBUS_ATTACH_CMDLINE</constant></term>
+            <listitem><para>
+              Requests the attachment of an item of type
+              <constant>KDBUS_ITEM_CMDLINE</constant>.
+            </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>KDBUS_ATTACH_CGROUP</constant></term>
+            <listitem><para>
+              Requests the attachment of an item of type
+              <constant>KDBUS_ITEM_CGROUP</constant>.
+            </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>KDBUS_ATTACH_CAPS</constant></term>
+            <listitem><para>
+              Requests the attachment of an item of type
+              <constant>KDBUS_ITEM_CAPS</constant>.
+            </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>KDBUS_ATTACH_SECLABEL</constant></term>
+            <listitem><para>
+              Requests the attachment of an item of type
+              <constant>KDBUS_ITEM_SECLABEL</constant>.
+            </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>KDBUS_ATTACH_AUDIT</constant></term>
+            <listitem><para>
+              Requests the attachment of an item of type
+              <constant>KDBUS_ITEM_AUDIT</constant>.
+            </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>KDBUS_ATTACH_CONN_DESCRIPTION</constant></term>
+            <listitem><para>
+              Requests the attachment of an item of type
+              <constant>KDBUS_ITEM_CONN_DESCRIPTION</constant>.
+            </para></listitem>
+        </varlistentry>
+      </variablelist>
+
+      <para>
+        Please refer to
+        <citerefentry>
+          <refentrytitle>kdbus.item</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+        for detailed information about the layout and payload of items and
+        what metadata should be used to.
+      </para>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>The ioctl interface</title>
+
+    <para>
+      As stated in the 'synopsis' section above, application developers are
+      strongly encouraged to use kdbus through one of the high-level D-Bus
+      abstraction libraries, rather than using the low-level API directly.
+    </para>
+
+    <para>
+      kdbus on the kernel level exposes its functions exclusively through
+      <citerefentry>
+        <refentrytitle>ioctl</refentrytitle>
+        <manvolnum>2</manvolnum>
+      </citerefentry>,
+      employed on file descriptors returned by
+      <citerefentry>
+        <refentrytitle>open</refentrytitle>
+        <manvolnum>2</manvolnum>
+      </citerefentry>
+      on pseudo files exposed by
+      <citerefentry>
+        <refentrytitle>kdbus.fs</refentrytitle>
+        <manvolnum>7</manvolnum>
+      </citerefentry>.
+    </para>
+    <para>
+      Following is a list of all the ioctls, along with the command structs
+      they must be used with.
+    </para>
+
+    <informaltable frame="none">
+      <tgroup cols="3" colsep="1">
+        <thead>
+          <row>
+            <entry>ioctl signature</entry>
+            <entry>command</entry>
+            <entry>transported struct</entry>
+          </row>
+        </thead>
+        <tbody>
+          <row>
+            <entry><constant>0x40189500</constant></entry>
+            <entry><constant>KDBUS_CMD_BUS_MAKE</constant></entry>
+            <entry><type>struct kdbus_cmd *</type></entry>
+          </row><row>
+            <entry><constant>0x40189510</constant></entry>
+            <entry><constant>KDBUS_CMD_ENDPOINT_MAKE</constant></entry>
+            <entry><type>struct kdbus_cmd *</type></entry>
+          </row><row>
+            <entry><constant>0xc0609580</constant></entry>
+            <entry><constant>KDBUS_CMD_HELLO</constant></entry>
+            <entry><type>struct kdbus_cmd_hello *</type></entry>
+          </row><row>
+            <entry><constant>0x40189582</constant></entry>
+            <entry><constant>KDBUS_CMD_BYEBYE</constant></entry>
+            <entry><type>struct kdbus_cmd *</type></entry>
+          </row><row>
+            <entry><constant>0x40389590</constant></entry>
+            <entry><constant>KDBUS_CMD_SEND</constant></entry>
+            <entry><type>struct kdbus_cmd_send *</type></entry>
+          </row><row>
+            <entry><constant>0x80409591</constant></entry>
+            <entry><constant>KDBUS_CMD_RECV</constant></entry>
+            <entry><type>struct kdbus_cmd_recv *</type></entry>
+          </row><row>
+            <entry><constant>0x40209583</constant></entry>
+            <entry><constant>KDBUS_CMD_FREE</constant></entry>
+            <entry><type>struct kdbus_cmd_free *</type></entry>
+          </row><row>
+            <entry><constant>0x401895a0</constant></entry>
+            <entry><constant>KDBUS_CMD_NAME_ACQUIRE</constant></entry>
+            <entry><type>struct kdbus_cmd *</type></entry>
+          </row><row>
+            <entry><constant>0x401895a1</constant></entry>
+            <entry><constant>KDBUS_CMD_NAME_RELEASE</constant></entry>
+            <entry><type>struct kdbus_cmd *</type></entry>
+          </row><row>
+            <entry><constant>0x80289586</constant></entry>
+            <entry><constant>KDBUS_CMD_LIST</constant></entry>
+            <entry><type>struct kdbus_cmd_list *</type></entry>
+          </row><row>
+            <entry><constant>0x80309584</constant></entry>
+            <entry><constant>KDBUS_CMD_CONN_INFO</constant></entry>
+            <entry><type>struct kdbus_cmd_info *</type></entry>
+          </row><row>
+            <entry><constant>0x40209551</constant></entry>
+            <entry><constant>KDBUS_CMD_UPDATE</constant></entry>
+            <entry><type>struct kdbus_cmd *</type></entry>
+          </row><row>
+            <entry><constant>0x80309585</constant></entry>
+            <entry><constant>KDBUS_CMD_BUS_CREATOR_INFO</constant></entry>
+            <entry><type>struct kdbus_cmd_info *</type></entry>
+          </row><row>
+            <entry><constant>0x40189511</constant></entry>
+            <entry><constant>KDBUS_CMD_ENDPOINT_UPDATE</constant></entry>
+            <entry><type>struct kdbus_cmd *</type></entry>
+          </row><row>
+            <entry><constant>0x402095b0</constant></entry>
+            <entry><constant>KDBUS_CMD_MATCH_ADD</constant></entry>
+            <entry><type>struct kdbus_cmd_match *</type></entry>
+          </row><row>
+            <entry><constant>0x402095b1</constant></entry>
+            <entry><constant>KDBUS_CMD_MATCH_REMOVE</constant></entry>
+            <entry><type>struct kdbus_cmd_match *</type></entry>
+          </row>
+        </tbody>
+      </tgroup>
+    </informaltable>
+
+    <para>
+      Depending on the type of <emphasis>kdbusfs</emphasis> node that was
+      opened and what ioctls have been executed on a file descriptor before,
+      a different sub-set of ioctl commands is allowed.
+    </para>
+
+    <itemizedlist>
+      <listitem>
+        <para>
+          On a file descriptor resulting from opening a
+          <emphasis>control node</emphasis>, only the
+          <constant>KDBUS_CMD_BUS_MAKE</constant> ioctl may be executed.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          On a file descriptor resulting from opening a
+          <emphasis>bus endpoint node</emphasis>, only the
+          <constant>KDBUS_CMD_ENDPOINT_MAKE</constant> and
+          <constant>KDBUS_CMD_HELLO</constant> ioctls may be executed.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          A file descriptor that was used to create a bus
+          (via <constant>KDBUS_CMD_BUS_MAKE</constant>) is called a
+          <emphasis>bus owner</emphasis> file descriptor. The bus will be
+          active as long as the file descriptor is kept open.
+          A bus owner file descriptor can not be used to
+          employ any further ioctls. As soon as
+          <citerefentry>
+            <refentrytitle>close</refentrytitle>
+            <manvolnum>2</manvolnum>
+          </citerefentry>
+          is called on it, the bus will be shut down, along will all associated
+          endpoints and connections. See
+          <citerefentry>
+            <refentrytitle>kdbus.bus</refentrytitle>
+            <manvolnum>7</manvolnum>
+          </citerefentry>
+          for more details.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          A file descriptor that was used to create an endpoint
+          (via <constant>KDBUS_CMD_ENDPOINT_MAKE</constant>) is called an
+          <emphasis>endpoint owner</emphasis> file descriptor. The endpoint
+          will be active as long as the file descriptor is kept open.
+          An endpoint owner file descriptor can only be used
+          to update details of an endpoint through the
+          <constant>KDBUS_CMD_ENDPOINT_UPDATE</constant> ioctl. As soon as
+          <citerefentry>
+            <refentrytitle>close</refentrytitle>
+            <manvolnum>2</manvolnum>
+          </citerefentry>
+          is called on it, the endpoint will be removed from the bus, and all
+          connections that are connected to the bus through it are shut down.
+          See
+          <citerefentry>
+            <refentrytitle>kdbus.endpoint</refentrytitle>
+            <manvolnum>7</manvolnum>
+          </citerefentry>
+          for more details.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          A file descriptor that was used to create a connection
+          (via <constant>KDBUS_CMD_HELLO</constant>) is called a
+          <emphasis>connection owner</emphasis> file descriptor. The connection
+          will be active as long as the file descriptor is kept open.
+          A connection owner file descriptor may be used to
+          issue any of the following ioctls.
+        </para>
+
+        <itemizedlist>
+          <listitem><para>
+            <constant>KDBUS_CMD_UPDATE</constant> to tweak details of the
+            connection. See
+            <citerefentry>
+              <refentrytitle>kdbus.connection</refentrytitle>
+              <manvolnum>7</manvolnum>
+            </citerefentry>.
+          </para></listitem>
+
+          <listitem><para>
+            <constant>KDBUS_CMD_BYEBYE</constant> to shut down a connection
+            without losing messages. See
+            <citerefentry>
+              <refentrytitle>kdbus.connection</refentrytitle>
+              <manvolnum>7</manvolnum>
+            </citerefentry>.
+          </para></listitem>
+
+          <listitem><para>
+            <constant>KDBUS_CMD_FREE</constant> to free a slice of memory in
+            the pool. See
+            <citerefentry>
+              <refentrytitle>kdbus.pool</refentrytitle>
+              <manvolnum>7</manvolnum>
+            </citerefentry>.
+          </para></listitem>
+
+          <listitem><para>
+            <constant>KDBUS_CMD_CONN_INFO</constant> to retrieve information
+            on other connections on the bus. See
+            <citerefentry>
+              <refentrytitle>kdbus.connection</refentrytitle>
+              <manvolnum>7</manvolnum>
+            </citerefentry>.
+          </para></listitem>
+
+          <listitem><para>
+            <constant>KDBUS_CMD_BUS_CREATOR_INFO</constant> to retrieve
+            information on the bus creator. See
+            <citerefentry>
+              <refentrytitle>kdbus.connection</refentrytitle>
+              <manvolnum>7</manvolnum>
+            </citerefentry>.
+          </para></listitem>
+
+          <listitem><para>
+            <constant>KDBUS_CMD_LIST</constant> to retrieve a list of
+            currently active well-known names and unique IDs on the bus. See
+            <citerefentry>
+              <refentrytitle>kdbus.name</refentrytitle>
+              <manvolnum>7</manvolnum>
+            </citerefentry>.
+          </para></listitem>
+
+          <listitem><para>
+            <constant>KDBUS_CMD_SEND</constant> and
+            <constant>KDBUS_CMD_RECV</constant> to send or receive a message.
+            See
+            <citerefentry>
+              <refentrytitle>kdbus.message</refentrytitle>
+              <manvolnum>7</manvolnum>
+            </citerefentry>.
+          </para></listitem>
+
+          <listitem><para>
+            <constant>KDBUS_CMD_NAME_ACQUIRE</constant> and
+            <constant>KDBUS_CMD_NAME_RELEASE</constant> to acquire or release
+            a well-known name on the bus. See
+            <citerefentry>
+              <refentrytitle>kdbus.name</refentrytitle>
+              <manvolnum>7</manvolnum>
+            </citerefentry>.
+          </para></listitem>
+
+          <listitem><para>
+            <constant>KDBUS_CMD_MATCH_ADD</constant> and
+            <constant>KDBUS_CMD_MATCH_REMOVE</constant> to add or remove
+            a match for signal messages. See
+            <citerefentry>
+              <refentrytitle>kdbus.match</refentrytitle>
+              <manvolnum>7</manvolnum>
+            </citerefentry>.
+          </para></listitem>
+        </itemizedlist>
+      </listitem>
+    </itemizedlist>
+
+    <para>
+      These ioctls, along with the structs they transport, are explained in
+      detail in the other documents linked to in the "See Also" section below.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+    <simplelist type="inline">
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.bus</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.connection</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.endpoint</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.fs</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.item</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.message</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.name</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>kdbus.pool</refentrytitle>
+          <manvolnum>7</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>ioctl</refentrytitle>
+          <manvolnum>2</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>mmap</refentrytitle>
+          <manvolnum>2</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>open</refentrytitle>
+          <manvolnum>2</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <citerefentry>
+          <refentrytitle>close</refentrytitle>
+          <manvolnum>2</manvolnum>
+        </citerefentry>
+      </member>
+      <member>
+        <ulink url="http://freedesktop.org/wiki/Software/dbus">D-Bus</ulink>
+      </member>
+    </simplelist>
+  </refsect1>
+
+</refentry>
diff --git a/ipc/kdbus/Documentation/stylesheet.xsl b/ipc/kdbus/Documentation/stylesheet.xsl
new file mode 100644 (file)
index 0000000..52565ea
--- /dev/null
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<stylesheet xmlns="http://www.w3.org/1999/XSL/Transform" version="1.0">
+       <param name="chunk.quietly">1</param>
+       <param name="funcsynopsis.style">ansi</param>
+       <param name="funcsynopsis.tabular.threshold">80</param>
+       <param name="callout.graphics">0</param>
+       <param name="paper.type">A4</param>
+       <param name="generate.section.toc.level">2</param>
+       <param name="use.id.as.filename">1</param>
+       <param name="citerefentry.link">1</param>
+       <strip-space elements="*"/>
+       <template name="generate.citerefentry.link">
+               <value-of select="refentrytitle"/>
+               <text>.html</text>
+       </template>
+</stylesheet>
diff --git a/ipc/kdbus/Makefile b/ipc/kdbus/Makefile
new file mode 100644 (file)
index 0000000..7ee9271
--- /dev/null
@@ -0,0 +1,22 @@
+kdbus-y := \
+       bus.o \
+       connection.o \
+       endpoint.o \
+       fs.o \
+       handle.o \
+       item.o \
+       main.o \
+       match.o \
+       message.o \
+       metadata.o \
+       names.o \
+       node.o \
+       notify.o \
+       domain.o \
+       policy.o \
+       pool.o \
+       reply.o \
+       queue.o \
+       util.o
+
+obj-$(CONFIG_KDBUS) += kdbus.o
diff --git a/ipc/kdbus/bus.c b/ipc/kdbus/bus.c
new file mode 100644 (file)
index 0000000..a67f825
--- /dev/null
@@ -0,0 +1,514 @@
+/*
+ * 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
+ *
+ * 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/hashtable.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/random.h>
+#include <linux/sched.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/uio.h>
+
+#include "bus.h"
+#include "notify.h"
+#include "connection.h"
+#include "domain.h"
+#include "endpoint.h"
+#include "handle.h"
+#include "item.h"
+#include "match.h"
+#include "message.h"
+#include "metadata.h"
+#include "names.h"
+#include "policy.h"
+#include "util.h"
+
+static void kdbus_bus_free(struct kdbus_node *node)
+{
+       struct kdbus_bus *bus = container_of(node, struct kdbus_bus, node);
+
+       WARN_ON(!list_empty(&bus->monitors_list));
+       WARN_ON(!hash_empty(bus->conn_hash));
+
+       kdbus_notify_free(bus);
+
+       kdbus_user_unref(bus->creator);
+       kdbus_name_registry_free(bus->name_registry);
+       kdbus_domain_unref(bus->domain);
+       kdbus_policy_db_clear(&bus->policy_db);
+       kdbus_meta_proc_unref(bus->creator_meta);
+       kfree(bus);
+}
+
+static void kdbus_bus_release(struct kdbus_node *node, bool was_active)
+{
+       struct kdbus_bus *bus = container_of(node, struct kdbus_bus, node);
+
+       if (was_active)
+               atomic_dec(&bus->creator->buses);
+}
+
+static struct kdbus_bus *kdbus_bus_new(struct kdbus_domain *domain,
+                                      const char *name,
+                                      struct kdbus_bloom_parameter *bloom,
+                                      const u64 *pattach_owner,
+                                      u64 flags, kuid_t uid, kgid_t gid)
+{
+       struct kdbus_bus *b;
+       u64 attach_owner;
+       int ret;
+
+       if (bloom->size < 8 || bloom->size > KDBUS_BUS_BLOOM_MAX_SIZE ||
+           !KDBUS_IS_ALIGNED8(bloom->size) || bloom->n_hash < 1)
+               return ERR_PTR(-EINVAL);
+
+       ret = kdbus_sanitize_attach_flags(pattach_owner ? *pattach_owner : 0,
+                                         &attach_owner);
+       if (ret < 0)
+               return ERR_PTR(ret);
+
+       ret = kdbus_verify_uid_prefix(name, domain->user_namespace, uid);
+       if (ret < 0)
+               return ERR_PTR(ret);
+
+       b = kzalloc(sizeof(*b), GFP_KERNEL);
+       if (!b)
+               return ERR_PTR(-ENOMEM);
+
+       kdbus_node_init(&b->node, KDBUS_NODE_BUS);
+
+       b->node.free_cb = kdbus_bus_free;
+       b->node.release_cb = kdbus_bus_release;
+       b->node.uid = uid;
+       b->node.gid = gid;
+       b->node.mode = S_IRUSR | S_IXUSR;
+
+       if (flags & (KDBUS_MAKE_ACCESS_GROUP | KDBUS_MAKE_ACCESS_WORLD))
+               b->node.mode |= S_IRGRP | S_IXGRP;
+       if (flags & KDBUS_MAKE_ACCESS_WORLD)
+               b->node.mode |= S_IROTH | S_IXOTH;
+
+       b->id = atomic64_inc_return(&domain->last_id);
+       b->bus_flags = flags;
+       b->attach_flags_owner = attach_owner;
+       generate_random_uuid(b->id128);
+       b->bloom = *bloom;
+       b->domain = kdbus_domain_ref(domain);
+
+       kdbus_policy_db_init(&b->policy_db);
+
+       init_rwsem(&b->conn_rwlock);
+       hash_init(b->conn_hash);
+       INIT_LIST_HEAD(&b->monitors_list);
+
+       INIT_LIST_HEAD(&b->notify_list);
+       spin_lock_init(&b->notify_lock);
+       mutex_init(&b->notify_flush_lock);
+
+       ret = kdbus_node_link(&b->node, &domain->node, name);
+       if (ret < 0)
+               goto exit_unref;
+
+       /* cache the metadata/credentials of the creator */
+       b->creator_meta = kdbus_meta_proc_new();
+       if (IS_ERR(b->creator_meta)) {
+               ret = PTR_ERR(b->creator_meta);
+               b->creator_meta = NULL;
+               goto exit_unref;
+       }
+
+       ret = kdbus_meta_proc_collect(b->creator_meta,
+                                     KDBUS_ATTACH_CREDS |
+                                     KDBUS_ATTACH_PIDS |
+                                     KDBUS_ATTACH_AUXGROUPS |
+                                     KDBUS_ATTACH_TID_COMM |
+                                     KDBUS_ATTACH_PID_COMM |
+                                     KDBUS_ATTACH_EXE |
+                                     KDBUS_ATTACH_CMDLINE |
+                                     KDBUS_ATTACH_CGROUP |
+                                     KDBUS_ATTACH_CAPS |
+                                     KDBUS_ATTACH_SECLABEL |
+                                     KDBUS_ATTACH_AUDIT);
+       if (ret < 0)
+               goto exit_unref;
+
+       b->name_registry = kdbus_name_registry_new();
+       if (IS_ERR(b->name_registry)) {
+               ret = PTR_ERR(b->name_registry);
+               b->name_registry = NULL;
+               goto exit_unref;
+       }
+
+       /*
+        * Bus-limits of the creator are accounted on its real UID, just like
+        * all other per-user limits.
+        */
+       b->creator = kdbus_user_lookup(domain, current_uid());
+       if (IS_ERR(b->creator)) {
+               ret = PTR_ERR(b->creator);
+               b->creator = NULL;
+               goto exit_unref;
+       }
+
+       return b;
+
+exit_unref:
+       kdbus_node_deactivate(&b->node);
+       kdbus_node_unref(&b->node);
+       return ERR_PTR(ret);
+}
+
+/**
+ * kdbus_bus_ref() - increase the reference counter of a kdbus_bus
+ * @bus:               The bus to reference
+ *
+ * Every user of a bus, except for its creator, must add a reference to the
+ * kdbus_bus using this function.
+ *
+ * Return: the bus itself
+ */
+struct kdbus_bus *kdbus_bus_ref(struct kdbus_bus *bus)
+{
+       if (bus)
+               kdbus_node_ref(&bus->node);
+       return bus;
+}
+
+/**
+ * kdbus_bus_unref() - decrease the reference counter of a kdbus_bus
+ * @bus:               The bus to unref
+ *
+ * Release a reference. If the reference count drops to 0, the bus will be
+ * freed.
+ *
+ * Return: NULL
+ */
+struct kdbus_bus *kdbus_bus_unref(struct kdbus_bus *bus)
+{
+       if (bus)
+               kdbus_node_unref(&bus->node);
+       return NULL;
+}
+
+/**
+ * kdbus_bus_find_conn_by_id() - find a connection with a given id
+ * @bus:               The bus to look for the connection
+ * @id:                        The 64-bit connection id
+ *
+ * Looks up a connection with a given id. The returned connection
+ * is ref'ed, and needs to be unref'ed by the user. Returns NULL if
+ * the connection can't be found.
+ */
+struct kdbus_conn *kdbus_bus_find_conn_by_id(struct kdbus_bus *bus, u64 id)
+{
+       struct kdbus_conn *conn, *found = NULL;
+
+       down_read(&bus->conn_rwlock);
+       hash_for_each_possible(bus->conn_hash, conn, hentry, id)
+               if (conn->id == id) {
+                       found = kdbus_conn_ref(conn);
+                       break;
+               }
+       up_read(&bus->conn_rwlock);
+
+       return found;
+}
+
+/**
+ * kdbus_bus_broadcast() - send a message to all subscribed connections
+ * @bus:       The bus the connections are connected to
+ * @conn_src:  The source connection, may be %NULL for kernel notifications
+ * @staging:   Staging object containing the message to send
+ *
+ * Send message to all connections that are currently active on the bus.
+ * Connections must still have matches installed in order to let the message
+ * pass.
+ *
+ * The caller must hold the name-registry lock of @bus.
+ */
+void kdbus_bus_broadcast(struct kdbus_bus *bus,
+                        struct kdbus_conn *conn_src,
+                        struct kdbus_staging *staging)
+{
+       struct kdbus_conn *conn_dst;
+       unsigned int i;
+       int ret;
+
+       lockdep_assert_held(&bus->name_registry->rwlock);
+
+       /*
+        * Make sure broadcast are queued on monitors before we send it out to
+        * anyone else. Otherwise, connections might react to broadcasts before
+        * the monitor gets the broadcast queued. In the worst case, the
+        * monitor sees a reaction to the broadcast before the broadcast itself.
+        * We don't give ordering guarantees across connections (and monitors
+        * can re-construct order via sequence numbers), but we should at least
+        * try to avoid re-ordering for monitors.
+        */
+       kdbus_bus_eavesdrop(bus, conn_src, staging);
+
+       down_read(&bus->conn_rwlock);
+       hash_for_each(bus->conn_hash, i, conn_dst, hentry) {
+               if (!kdbus_conn_is_ordinary(conn_dst))
+                       continue;
+
+               /*
+                * Check if there is a match for the kmsg object in
+                * the destination connection match db
+                */
+               if (!kdbus_match_db_match_msg(conn_dst->match_db, conn_src,
+                                             staging))
+                       continue;
+
+               if (conn_src) {
+                       /*
+                        * Anyone can send broadcasts, as they have no
+                        * destination. But a receiver needs TALK access to
+                        * the sender in order to receive broadcasts.
+                        */
+                       if (!kdbus_conn_policy_talk(conn_dst, NULL, conn_src))
+                               continue;
+               } else {
+                       /*
+                        * Check if there is a policy db that prevents the
+                        * destination connection from receiving this kernel
+                        * notification
+                        */
+                       if (!kdbus_conn_policy_see_notification(conn_dst, NULL,
+                                                               staging->msg))
+                               continue;
+               }
+
+               ret = kdbus_conn_entry_insert(conn_src, conn_dst, staging,
+                                             NULL, NULL);
+               if (ret < 0)
+                       kdbus_conn_lost_message(conn_dst);
+       }
+       up_read(&bus->conn_rwlock);
+}
+
+/**
+ * kdbus_bus_eavesdrop() - send a message to all subscribed monitors
+ * @bus:       The bus the monitors are connected to
+ * @conn_src:  The source connection, may be %NULL for kernel notifications
+ * @staging:   Staging object containing the message to send
+ *
+ * Send message to all monitors that are currently active on the bus. Monitors
+ * must still have matches installed in order to let the message pass.
+ *
+ * The caller must hold the name-registry lock of @bus.
+ */
+void kdbus_bus_eavesdrop(struct kdbus_bus *bus,
+                        struct kdbus_conn *conn_src,
+                        struct kdbus_staging *staging)
+{
+       struct kdbus_conn *conn_dst;
+       int ret;
+
+       /*
+        * Monitor connections get all messages; ignore possible errors
+        * when sending messages to monitor connections.
+        */
+
+       lockdep_assert_held(&bus->name_registry->rwlock);
+
+       down_read(&bus->conn_rwlock);
+       list_for_each_entry(conn_dst, &bus->monitors_list, monitor_entry) {
+               ret = kdbus_conn_entry_insert(conn_src, conn_dst, staging,
+                                             NULL, NULL);
+               if (ret < 0)
+                       kdbus_conn_lost_message(conn_dst);
+       }
+       up_read(&bus->conn_rwlock);
+}
+
+/**
+ * kdbus_cmd_bus_make() - handle KDBUS_CMD_BUS_MAKE
+ * @domain:            domain to operate on
+ * @argp:              command payload
+ *
+ * Return: NULL or newly created bus on success, ERR_PTR on failure.
+ */
+struct kdbus_bus *kdbus_cmd_bus_make(struct kdbus_domain *domain,
+                                    void __user *argp)
+{
+       struct kdbus_bus *bus = NULL;
+       struct kdbus_cmd *cmd;
+       struct kdbus_ep *ep = NULL;
+       int ret;
+
+       struct kdbus_arg argv[] = {
+               { .type = KDBUS_ITEM_NEGOTIATE },
+               { .type = KDBUS_ITEM_MAKE_NAME, .mandatory = true },
+               { .type = KDBUS_ITEM_BLOOM_PARAMETER, .mandatory = true },
+               { .type = KDBUS_ITEM_ATTACH_FLAGS_SEND },
+       };
+       struct kdbus_args args = {
+               .allowed_flags = KDBUS_FLAG_NEGOTIATE |
+                                KDBUS_MAKE_ACCESS_GROUP |
+                                KDBUS_MAKE_ACCESS_WORLD,
+               .argv = argv,
+               .argc = ARRAY_SIZE(argv),
+       };
+
+       ret = kdbus_args_parse(&args, argp, &cmd);
+       if (ret < 0)
+               return ERR_PTR(ret);
+       if (ret > 0)
+               return NULL;
+
+       bus = kdbus_bus_new(domain,
+                           argv[1].item->str, &argv[2].item->bloom_parameter,
+                           argv[3].item ? argv[3].item->data64 : NULL,
+                           cmd->flags, current_euid(), current_egid());
+       if (IS_ERR(bus)) {
+               ret = PTR_ERR(bus);
+               bus = NULL;
+               goto exit;
+       }
+
+       if (atomic_inc_return(&bus->creator->buses) > KDBUS_USER_MAX_BUSES) {
+               atomic_dec(&bus->creator->buses);
+               ret = -EMFILE;
+               goto exit;
+       }
+
+       if (!kdbus_node_activate(&bus->node)) {
+               atomic_dec(&bus->creator->buses);
+               ret = -ESHUTDOWN;
+               goto exit;
+       }
+
+       ep = kdbus_ep_new(bus, "bus", cmd->flags, bus->node.uid, bus->node.gid,
+                         false);
+       if (IS_ERR(ep)) {
+               ret = PTR_ERR(ep);
+               ep = NULL;
+               goto exit;
+       }
+
+       if (!kdbus_node_activate(&ep->node)) {
+               ret = -ESHUTDOWN;
+               goto exit;
+       }
+
+       /*
+        * Drop our own reference, effectively causing the endpoint to be
+        * deactivated and released when the parent bus is.
+        */
+       ep = kdbus_ep_unref(ep);
+
+exit:
+       ret = kdbus_args_clear(&args, ret);
+       if (ret < 0) {
+               if (ep) {
+                       kdbus_node_deactivate(&ep->node);
+                       kdbus_ep_unref(ep);
+               }
+               if (bus) {
+                       kdbus_node_deactivate(&bus->node);
+                       kdbus_bus_unref(bus);
+               }
+               return ERR_PTR(ret);
+       }
+       return bus;
+}
+
+/**
+ * kdbus_cmd_bus_creator_info() - handle KDBUS_CMD_BUS_CREATOR_INFO
+ * @conn:              connection to operate on
+ * @argp:              command payload
+ *
+ * Return: >=0 on success, negative error code on failure.
+ */
+int kdbus_cmd_bus_creator_info(struct kdbus_conn *conn, void __user *argp)
+{
+       struct kdbus_cmd_info *cmd;
+       struct kdbus_bus *bus = conn->ep->bus;
+       struct kdbus_pool_slice *slice = NULL;
+       struct kdbus_item *meta_items = NULL;
+       struct kdbus_item_header item_hdr;
+       struct kdbus_info info = {};
+       size_t meta_size, name_len, cnt = 0;
+       struct kvec kvec[6];
+       u64 attach_flags, size = 0;
+       int ret;
+
+       struct kdbus_arg argv[] = {
+               { .type = KDBUS_ITEM_NEGOTIATE },
+       };
+       struct kdbus_args args = {
+               .allowed_flags = KDBUS_FLAG_NEGOTIATE,
+               .argv = argv,
+               .argc = ARRAY_SIZE(argv),
+       };
+
+       ret = kdbus_args_parse(&args, argp, &cmd);
+       if (ret != 0)
+               return ret;
+
+       ret = kdbus_sanitize_attach_flags(cmd->attach_flags, &attach_flags);
+       if (ret < 0)
+               goto exit;
+
+       attach_flags &= bus->attach_flags_owner;
+
+       ret = kdbus_meta_emit(bus->creator_meta, NULL, NULL, conn,
+                             attach_flags, &meta_items, &meta_size);
+       if (ret < 0)
+               goto exit;
+
+       name_len = strlen(bus->node.name) + 1;
+       info.id = bus->id;
+       info.flags = bus->bus_flags;
+       item_hdr.type = KDBUS_ITEM_MAKE_NAME;
+       item_hdr.size = KDBUS_ITEM_HEADER_SIZE + name_len;
+
+       kdbus_kvec_set(&kvec[cnt++], &info, sizeof(info), &size);
+       kdbus_kvec_set(&kvec[cnt++], &item_hdr, sizeof(item_hdr), &size);
+       kdbus_kvec_set(&kvec[cnt++], bus->node.name, name_len, &size);
+       cnt += !!kdbus_kvec_pad(&kvec[cnt], &size);
+       if (meta_size > 0) {
+               kdbus_kvec_set(&kvec[cnt++], meta_items, meta_size, &size);
+               cnt += !!kdbus_kvec_pad(&kvec[cnt], &size);
+       }
+
+       info.size = size;
+
+       slice = kdbus_pool_slice_alloc(conn->pool, size, false);
+       if (IS_ERR(slice)) {
+               ret = PTR_ERR(slice);
+               slice = NULL;
+               goto exit;
+       }
+
+       ret = kdbus_pool_slice_copy_kvec(slice, 0, kvec, cnt, size);
+       if (ret < 0)
+               goto exit;
+
+       kdbus_pool_slice_publish(slice, &cmd->offset, &cmd->info_size);
+
+       if (kdbus_member_set_user(&cmd->offset, argp, typeof(*cmd), offset) ||
+           kdbus_member_set_user(&cmd->info_size, argp,
+                                 typeof(*cmd), info_size))
+               ret = -EFAULT;
+
+exit:
+       kdbus_pool_slice_release(slice);
+       kfree(meta_items);
+       return kdbus_args_clear(&args, ret);
+}
diff --git a/ipc/kdbus/bus.h b/ipc/kdbus/bus.h
new file mode 100644 (file)
index 0000000..8c2acae
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ */
+
+#ifndef __KDBUS_BUS_H
+#define __KDBUS_BUS_H
+
+#include <linux/hashtable.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/rwsem.h>
+#include <linux/spinlock.h>
+#include <uapi/linux/kdbus.h>
+
+#include "metadata.h"
+#include "names.h"
+#include "node.h"
+#include "policy.h"
+
+struct kdbus_conn;
+struct kdbus_domain;
+struct kdbus_staging;
+struct kdbus_user;
+
+/**
+ * struct kdbus_bus - bus in a domain
+ * @node:              kdbus_node
+ * @id:                        ID of this bus in the domain
+ * @bus_flags:         Simple pass-through flags from userspace to userspace
+ * @attach_flags_owner:        KDBUS_ATTACH_* flags of bus creator that other
+ *                     connections can see or query
+ * @id128:             Unique random 128 bit ID of this bus
+ * @bloom:             Bloom parameters
+ * @domain:            Domain of this bus
+ * @creator:           Creator of the bus
+ * @creator_meta:      Meta information about the bus creator
+ * @last_message_id:   Last used message id
+ * @policy_db:         Policy database for this bus
+ * @name_registry:     Name registry of this bus
+ * @conn_rwlock:       Read/Write lock for all lists of child connections
+ * @conn_hash:         Map of connection IDs
+ * @monitors_list:     Connections that monitor this bus
+ * @notify_list:       List of pending kernel-generated messages
+ * @notify_lock:       Notification list lock
+ * @notify_flush_lock: Notification flushing lock
+ */
+struct kdbus_bus {
+       struct kdbus_node node;
+
+       /* static */
+       u64 id;
+       u64 bus_flags;
+       u64 attach_flags_owner;
+       u8 id128[16];
+       struct kdbus_bloom_parameter bloom;
+       struct kdbus_domain *domain;
+       struct kdbus_user *creator;
+       struct kdbus_meta_proc *creator_meta;
+
+       /* protected by own locks */
+       atomic64_t last_message_id;
+       struct kdbus_policy_db policy_db;
+       struct kdbus_name_registry *name_registry;
+
+       /* protected by conn_rwlock */
+       struct rw_semaphore conn_rwlock;
+       DECLARE_HASHTABLE(conn_hash, 8);
+       struct list_head monitors_list;
+
+       /* protected by notify_lock */
+       struct list_head notify_list;
+       spinlock_t notify_lock;
+       struct mutex notify_flush_lock;
+};
+
+struct kdbus_bus *kdbus_bus_ref(struct kdbus_bus *bus);
+struct kdbus_bus *kdbus_bus_unref(struct kdbus_bus *bus);
+
+struct kdbus_conn *kdbus_bus_find_conn_by_id(struct kdbus_bus *bus, u64 id);
+void kdbus_bus_broadcast(struct kdbus_bus *bus,
+                        struct kdbus_conn *conn_src,
+                        struct kdbus_staging *staging);
+void kdbus_bus_eavesdrop(struct kdbus_bus *bus,
+                        struct kdbus_conn *conn_src,
+                        struct kdbus_staging *staging);
+
+struct kdbus_bus *kdbus_cmd_bus_make(struct kdbus_domain *domain,
+                                    void __user *argp);
+int kdbus_cmd_bus_creator_info(struct kdbus_conn *conn, void __user *argp);
+
+#endif
diff --git a/ipc/kdbus/connection.c b/ipc/kdbus/connection.c
new file mode 100644 (file)
index 0000000..02deba3
--- /dev/null
@@ -0,0 +1,2245 @@
+/*
+ * 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/audit.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/fs_struct.h>
+#include <linux/hashtable.h>
+#include <linux/idr.h>
+#include <linux/init.h>
+#include <linux/math64.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/path.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/shmem_fs.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+#include <linux/syscalls.h>
+#include <linux/uio.h>
+
+#include "bus.h"
+#include "connection.h"
+#include "endpoint.h"
+#include "handle.h"
+#include "match.h"
+#include "message.h"
+#include "metadata.h"
+#include "names.h"
+#include "domain.h"
+#include "item.h"
+#include "notify.h"
+#include "policy.h"
+#include "pool.h"
+#include "reply.h"
+#include "util.h"
+#include "queue.h"
+
+#define KDBUS_CONN_ACTIVE_BIAS (INT_MIN + 2)
+#define KDBUS_CONN_ACTIVE_NEW  (INT_MIN + 1)
+
+/* Disable internal kdbus policy - possibilities of connections to own, see and
+ * talk to names are restricted by libdbuspolicy library and LSM hooks
+ */
+#define DISABLE_KDBUS_POLICY
+
+static struct kdbus_conn *kdbus_conn_new(struct kdbus_ep *ep,
+                                        struct file *file,
+                                        struct kdbus_cmd_hello *hello,
+                                        const char *name,
+                                        const struct kdbus_creds *creds,
+                                        const struct kdbus_pids *pids,
+                                        const char *seclabel,
+                                        const char *conn_description)
+{
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+       static struct lock_class_key __key;
+#endif
+       struct kdbus_pool_slice *slice = NULL;
+       struct kdbus_bus *bus = ep->bus;
+       struct kdbus_conn *conn;
+       u64 attach_flags_send;
+       u64 attach_flags_recv;
+       u64 items_size = 0;
+       bool is_policy_holder;
+       bool is_activator;
+       bool is_monitor;
+       bool privileged;
+       bool owner;
+       struct kvec kvec;
+       int ret;
+
+       struct {
+               u64 size;
+               u64 type;
+               struct kdbus_bloom_parameter bloom;
+       } bloom_item;
+
+       privileged = kdbus_ep_is_privileged(ep, file);
+       owner = kdbus_ep_is_owner(ep, file);
+
+       is_monitor = hello->flags & KDBUS_HELLO_MONITOR;
+       is_activator = hello->flags & KDBUS_HELLO_ACTIVATOR;
+       is_policy_holder = hello->flags & KDBUS_HELLO_POLICY_HOLDER;
+
+       if (!hello->pool_size || !IS_ALIGNED(hello->pool_size, PAGE_SIZE))
+               return ERR_PTR(-EINVAL);
+       if (is_monitor + is_activator + is_policy_holder > 1)
+               return ERR_PTR(-EINVAL);
+       if (name && !is_activator && !is_policy_holder)
+               return ERR_PTR(-EINVAL);
+       if (!name && (is_activator || is_policy_holder))
+               return ERR_PTR(-EINVAL);
+       if (name && !kdbus_name_is_valid(name, true))
+               return ERR_PTR(-EINVAL);
+       if (is_monitor && ep->user)
+               return ERR_PTR(-EOPNOTSUPP);
+       if (!owner && (is_activator || is_policy_holder || is_monitor))
+               return ERR_PTR(-EPERM);
+       if (!owner && (creds || pids || seclabel))
+               return ERR_PTR(-EPERM);
+
+       ret = kdbus_sanitize_attach_flags(hello->attach_flags_send,
+                                         &attach_flags_send);
+       if (ret < 0)
+               return ERR_PTR(ret);
+
+       ret = kdbus_sanitize_attach_flags(hello->attach_flags_recv,
+                                         &attach_flags_recv);
+       if (ret < 0)
+               return ERR_PTR(ret);
+
+       conn = kzalloc(sizeof(*conn), GFP_KERNEL);
+       if (!conn)
+               return ERR_PTR(-ENOMEM);
+
+       kref_init(&conn->kref);
+       atomic_set(&conn->active, KDBUS_CONN_ACTIVE_NEW);
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+       lockdep_init_map(&conn->dep_map, "s_active", &__key, 0);
+#endif
+       mutex_init(&conn->lock);
+       INIT_LIST_HEAD(&conn->names_list);
+       INIT_LIST_HEAD(&conn->reply_list);
+       atomic_set(&conn->request_count, 0);
+       atomic_set(&conn->lost_count, 0);
+       INIT_DELAYED_WORK(&conn->work, kdbus_reply_list_scan_work);
+       conn->cred = get_cred(file->f_cred);
+       conn->pid = get_pid(task_pid(current));
+       get_fs_root(current->fs, &conn->root_path);
+       init_waitqueue_head(&conn->wait);
+       kdbus_queue_init(&conn->queue);
+       conn->privileged = privileged;
+       conn->owner = owner;
+       conn->ep = kdbus_ep_ref(ep);
+       conn->id = atomic64_inc_return(&bus->domain->last_id);
+       conn->flags = hello->flags;
+       atomic64_set(&conn->attach_flags_send, attach_flags_send);
+       atomic64_set(&conn->attach_flags_recv, attach_flags_recv);
+       INIT_LIST_HEAD(&conn->monitor_entry);
+
+       if (conn_description) {
+               conn->description = kstrdup(conn_description, GFP_KERNEL);
+               if (!conn->description) {
+                       ret = -ENOMEM;
+                       goto exit_unref;
+               }
+       }
+
+       conn->pool = kdbus_pool_new(conn->description, hello->pool_size);
+       if (IS_ERR(conn->pool)) {
+               ret = PTR_ERR(conn->pool);
+               conn->pool = NULL;
+               goto exit_unref;
+       }
+
+       conn->match_db = kdbus_match_db_new();
+       if (IS_ERR(conn->match_db)) {
+               ret = PTR_ERR(conn->match_db);
+               conn->match_db = NULL;
+               goto exit_unref;
+       }
+
+       /* return properties of this connection to the caller */
+       hello->bus_flags = bus->bus_flags;
+       hello->id = conn->id;
+
+       BUILD_BUG_ON(sizeof(bus->id128) != sizeof(hello->id128));
+       memcpy(hello->id128, bus->id128, sizeof(hello->id128));
+
+       /* privileged processes can impersonate somebody else */
+       if (creds || pids || seclabel) {
+               conn->meta_fake = kdbus_meta_fake_new();
+               if (IS_ERR(conn->meta_fake)) {
+                       ret = PTR_ERR(conn->meta_fake);
+                       conn->meta_fake = NULL;
+                       goto exit_unref;
+               }
+
+               ret = kdbus_meta_fake_collect(conn->meta_fake,
+                                             creds, pids, seclabel);
+               if (ret < 0)
+                       goto exit_unref;
+       } else {
+               conn->meta_proc = kdbus_meta_proc_new();
+               if (IS_ERR(conn->meta_proc)) {
+                       ret = PTR_ERR(conn->meta_proc);
+                       conn->meta_proc = NULL;
+                       goto exit_unref;
+               }
+
+               ret = kdbus_meta_proc_collect(conn->meta_proc,
+                                             KDBUS_ATTACH_CREDS |
+                                             KDBUS_ATTACH_PIDS |
+                                             KDBUS_ATTACH_AUXGROUPS |
+                                             KDBUS_ATTACH_TID_COMM |
+                                             KDBUS_ATTACH_PID_COMM |
+                                             KDBUS_ATTACH_EXE |
+                                             KDBUS_ATTACH_CMDLINE |
+                                             KDBUS_ATTACH_CGROUP |
+                                             KDBUS_ATTACH_CAPS |
+                                             KDBUS_ATTACH_SECLABEL |
+                                             KDBUS_ATTACH_AUDIT);
+               if (ret < 0)
+                       goto exit_unref;
+       }
+
+       /*
+        * Account the connection against the current user (UID), or for
+        * custom endpoints use the anonymous user assigned to the endpoint.
+        * Note that limits are always accounted against the real UID, not
+        * the effective UID (cred->user always points to the accounting of
+        * cred->uid, not cred->euid).
+        * In case the caller is privileged, we allow changing the accounting
+        * to the faked user.
+        */
+       if (ep->user) {
+               conn->user = kdbus_user_ref(ep->user);
+       } else {
+               kuid_t uid;
+
+               if (conn->meta_fake && uid_valid(conn->meta_fake->uid) &&
+                   conn->privileged)
+                       uid = conn->meta_fake->uid;
+               else
+                       uid = conn->cred->uid;
+
+               conn->user = kdbus_user_lookup(ep->bus->domain, uid);
+               if (IS_ERR(conn->user)) {
+                       ret = PTR_ERR(conn->user);
+                       conn->user = NULL;
+                       goto exit_unref;
+               }
+       }
+
+       if (atomic_inc_return(&conn->user->connections) > KDBUS_USER_MAX_CONN) {
+               /* decremented by destructor as conn->user is valid */
+               ret = -EMFILE;
+               goto exit_unref;
+       }
+
+       bloom_item.size = sizeof(bloom_item);
+       bloom_item.type = KDBUS_ITEM_BLOOM_PARAMETER;
+       bloom_item.bloom = bus->bloom;
+       kdbus_kvec_set(&kvec, &bloom_item, bloom_item.size, &items_size);
+
+       slice = kdbus_pool_slice_alloc(conn->pool, items_size, false);
+       if (IS_ERR(slice)) {
+               ret = PTR_ERR(slice);
+               slice = NULL;
+               goto exit_unref;
+       }
+
+       ret = kdbus_pool_slice_copy_kvec(slice, 0, &kvec, 1, items_size);
+       if (ret < 0)
+               goto exit_unref;
+
+       kdbus_pool_slice_publish(slice, &hello->offset, &hello->items_size);
+       kdbus_pool_slice_release(slice);
+
+       return conn;
+
+exit_unref:
+       kdbus_pool_slice_release(slice);
+       kdbus_conn_unref(conn);
+       return ERR_PTR(ret);
+}
+
+static void __kdbus_conn_free(struct kref *kref)
+{
+       struct kdbus_conn *conn = container_of(kref, struct kdbus_conn, kref);
+
+       WARN_ON(kdbus_conn_active(conn));
+       WARN_ON(delayed_work_pending(&conn->work));
+       WARN_ON(!list_empty(&conn->queue.msg_list));
+       WARN_ON(!list_empty(&conn->names_list));
+       WARN_ON(!list_empty(&conn->reply_list));
+
+       if (conn->user) {
+               atomic_dec(&conn->user->connections);
+               kdbus_user_unref(conn->user);
+       }
+
+       kdbus_meta_fake_free(conn->meta_fake);
+       kdbus_meta_proc_unref(conn->meta_proc);
+       kdbus_match_db_free(conn->match_db);
+       kdbus_pool_free(conn->pool);
+       kdbus_ep_unref(conn->ep);
+       path_put(&conn->root_path);
+       put_pid(conn->pid);
+       put_cred(conn->cred);
+       kfree(conn->description);
+       kfree(conn->quota);
+       kfree(conn);
+}
+
+/**
+ * kdbus_conn_ref() - take a connection reference
+ * @conn:              Connection, may be %NULL
+ *
+ * Return: the connection itself
+ */
+struct kdbus_conn *kdbus_conn_ref(struct kdbus_conn *conn)
+{
+       if (conn)
+               kref_get(&conn->kref);
+       return conn;
+}
+
+/**
+ * kdbus_conn_unref() - drop a connection reference
+ * @conn:              Connection (may be NULL)
+ *
+ * When the last reference is dropped, the connection's internal structure
+ * is freed.
+ *
+ * Return: NULL
+ */
+struct kdbus_conn *kdbus_conn_unref(struct kdbus_conn *conn)
+{
+       if (conn)
+               kref_put(&conn->kref, __kdbus_conn_free);
+       return NULL;
+}
+
+/**
+ * kdbus_conn_active() - connection is not disconnected
+ * @conn:              Connection to check
+ *
+ * Return true if the connection was not disconnected, yet. Note that a
+ * connection might be disconnected asynchronously, unless you hold the
+ * connection lock. If that's not suitable for you, see kdbus_conn_acquire() to
+ * suppress connection shutdown for a short period.
+ *
+ * Return: true if the connection is still active
+ */
+bool kdbus_conn_active(const struct kdbus_conn *conn)
+{
+       return atomic_read(&conn->active) >= 0;
+}
+
+/**
+ * kdbus_conn_acquire() - acquire an active connection reference
+ * @conn:              Connection
+ *
+ * Users can close a connection via KDBUS_BYEBYE (or by destroying the
+ * endpoint/bus/...) at any time. Whenever this happens, we should deny any
+ * user-visible action on this connection and signal ECONNRESET instead.
+ * To avoid testing for connection availability everytime you take the
+ * connection-lock, you can acquire a connection for short periods.
+ *
+ * By calling kdbus_conn_acquire(), you gain an "active reference" to the
+ * connection. You must also hold a regular reference at any time! As long as
+ * you hold the active-ref, the connection will not be shut down. However, if
+ * the connection was shut down, you can never acquire an active-ref again.
+ *
+ * kdbus_conn_disconnect() disables the connection and then waits for all active
+ * references to be dropped. It will also wake up any pending operation.
+ * However, you must not sleep for an indefinite period while holding an
+ * active-reference. Otherwise, kdbus_conn_disconnect() might stall. If you need
+ * to sleep for an indefinite period, either release the reference and try to
+ * acquire it again after waking up, or make kdbus_conn_disconnect() wake up
+ * your wait-queue.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int kdbus_conn_acquire(struct kdbus_conn *conn)
+{
+       if (!atomic_inc_unless_negative(&conn->active))
+               return -ECONNRESET;
+
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+       rwsem_acquire_read(&conn->dep_map, 0, 1, _RET_IP_);
+#endif
+
+       return 0;
+}
+
+/**
+ * kdbus_conn_release() - release an active connection reference
+ * @conn:              Connection
+ *
+ * This releases an active reference that has been acquired via
+ * kdbus_conn_acquire(). If the connection was already disabled and this is the
+ * last active-ref that is dropped, the disconnect-waiter will be woken up and
+ * properly close the connection.
+ */
+void kdbus_conn_release(struct kdbus_conn *conn)
+{
+       int v;
+
+       if (!conn)
+               return;
+
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+       rwsem_release(&conn->dep_map, 1, _RET_IP_);
+#endif
+
+       v = atomic_dec_return(&conn->active);
+       if (v != KDBUS_CONN_ACTIVE_BIAS)
+               return;
+
+       wake_up_all(&conn->wait);
+}
+
+static int kdbus_conn_connect(struct kdbus_conn *conn, const char *name)
+{
+       struct kdbus_ep *ep = conn->ep;
+       struct kdbus_bus *bus = ep->bus;
+       int ret;
+
+       if (WARN_ON(atomic_read(&conn->active) != KDBUS_CONN_ACTIVE_NEW))
+               return -EALREADY;
+
+       /* make sure the ep-node is active while we add our connection */
+       if (!kdbus_node_acquire(&ep->node))
+               return -ESHUTDOWN;
+
+       /* lock order: domain -> bus -> ep -> names -> conn */
+       mutex_lock(&ep->lock);
+       down_write(&bus->conn_rwlock);
+
+       /* link into monitor list */
+       if (kdbus_conn_is_monitor(conn))
+               list_add_tail(&conn->monitor_entry, &bus->monitors_list);
+
+       /* link into bus and endpoint */
+       list_add_tail(&conn->ep_entry, &ep->conn_list);
+       hash_add(bus->conn_hash, &conn->hentry, conn->id);
+
+       /* enable lookups and acquire active ref */
+       atomic_set(&conn->active, 1);
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+       rwsem_acquire_read(&conn->dep_map, 0, 1, _RET_IP_);
+#endif
+
+       up_write(&bus->conn_rwlock);
+       mutex_unlock(&ep->lock);
+
+       kdbus_node_release(&ep->node);
+
+       /*
+        * Notify subscribers about the new active connection, unless it is
+        * a monitor. Monitors are invisible on the bus, can't be addressed
+        * directly, and won't cause any notifications.
+        */
+       if (!kdbus_conn_is_monitor(conn)) {
+               ret = kdbus_notify_id_change(bus, KDBUS_ITEM_ID_ADD,
+                                            conn->id, conn->flags);
+               if (ret < 0)
+                       goto exit_disconnect;
+       }
+
+       if (kdbus_conn_is_activator(conn)) {
+               u64 flags = KDBUS_NAME_ACTIVATOR;
+
+               if (WARN_ON(!name)) {
+                       ret = -EINVAL;
+                       goto exit_disconnect;
+               }
+
+               ret = kdbus_name_acquire(bus->name_registry, conn, name,
+                                        flags, NULL);
+               if (ret < 0)
+                       goto exit_disconnect;
+       }
+
+       kdbus_conn_release(conn);
+       kdbus_notify_flush(bus);
+       return 0;
+
+exit_disconnect:
+       kdbus_conn_release(conn);
+       kdbus_conn_disconnect(conn, false);
+       return ret;
+}
+
+/**
+ * kdbus_conn_disconnect() - disconnect a connection
+ * @conn:              The connection to disconnect
+ * @ensure_queue_empty:        Flag to indicate if the call should fail in
+ *                     case the connection's message list is not
+ *                     empty
+ *
+ * If @ensure_msg_list_empty is true, and the connection has pending messages,
+ * -EBUSY is returned.
+ *
+ * Return: 0 on success, negative errno on failure
+ */
+int kdbus_conn_disconnect(struct kdbus_conn *conn, bool ensure_queue_empty)
+{
+       struct kdbus_queue_entry *entry, *tmp;
+       struct kdbus_bus *bus = conn->ep->bus;
+       struct kdbus_reply *r, *r_tmp;
+       struct kdbus_conn *c;
+       int i, v;
+
+       mutex_lock(&conn->lock);
+       v = atomic_read(&conn->active);
+       if (v == KDBUS_CONN_ACTIVE_NEW) {
+               /* was never connected */
+               mutex_unlock(&conn->lock);
+               return 0;
+       }
+       if (v < 0) {
+               /* already dead */
+               mutex_unlock(&conn->lock);
+               return -ECONNRESET;
+       }
+       if (ensure_queue_empty && !list_empty(&conn->queue.msg_list)) {
+               /* still busy */
+               mutex_unlock(&conn->lock);
+               return -EBUSY;
+       }
+
+       atomic_add(KDBUS_CONN_ACTIVE_BIAS, &conn->active);
+       mutex_unlock(&conn->lock);
+
+       wake_up_interruptible(&conn->wait);
+
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+       rwsem_acquire(&conn->dep_map, 0, 0, _RET_IP_);
+       if (atomic_read(&conn->active) != KDBUS_CONN_ACTIVE_BIAS)
+               lock_contended(&conn->dep_map, _RET_IP_);
+#endif
+
+       wait_event(conn->wait,
+                  atomic_read(&conn->active) == KDBUS_CONN_ACTIVE_BIAS);
+
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+       lock_acquired(&conn->dep_map, _RET_IP_);
+       rwsem_release(&conn->dep_map, 1, _RET_IP_);
+#endif
+
+       cancel_delayed_work_sync(&conn->work);
+       kdbus_policy_remove_owner(&conn->ep->bus->policy_db, conn);
+
+       /* lock order: domain -> bus -> ep -> names -> conn */
+       mutex_lock(&conn->ep->lock);
+       down_write(&bus->conn_rwlock);
+
+       /* remove from bus and endpoint */
+       hash_del(&conn->hentry);
+       list_del(&conn->monitor_entry);
+       list_del(&conn->ep_entry);
+
+       up_write(&bus->conn_rwlock);
+       mutex_unlock(&conn->ep->lock);
+
+       /*
+        * Remove all names associated with this connection; this possibly
+        * moves queued messages back to the activator connection.
+        */
+       kdbus_name_release_all(bus->name_registry, conn);
+
+       /* if we die while other connections wait for our reply, notify them */
+       mutex_lock(&conn->lock);
+       list_for_each_entry_safe(entry, tmp, &conn->queue.msg_list, entry) {
+               if (entry->reply)
+                       kdbus_notify_reply_dead(bus,
+                                               entry->reply->reply_dst->id,
+                                               entry->reply->cookie);
+               kdbus_queue_entry_free(entry);
+       }
+
+       list_for_each_entry_safe(r, r_tmp, &conn->reply_list, entry)
+               kdbus_reply_unlink(r);
+       mutex_unlock(&conn->lock);
+
+       /* lock order: domain -> bus -> ep -> names -> conn */
+       down_read(&bus->conn_rwlock);
+       hash_for_each(bus->conn_hash, i, c, hentry) {
+               mutex_lock(&c->lock);
+               list_for_each_entry_safe(r, r_tmp, &c->reply_list, entry) {
+                       if (r->reply_src != conn)
+                               continue;
+
+                       if (r->sync)
+                               kdbus_sync_reply_wakeup(r, -EPIPE);
+                       else
+                               /* send a 'connection dead' notification */
+                               kdbus_notify_reply_dead(bus, c->id, r->cookie);
+
+                       kdbus_reply_unlink(r);
+               }
+               mutex_unlock(&c->lock);
+       }
+       up_read(&bus->conn_rwlock);
+
+       if (!kdbus_conn_is_monitor(conn))
+               kdbus_notify_id_change(bus, KDBUS_ITEM_ID_REMOVE,
+                                      conn->id, conn->flags);
+
+       kdbus_notify_flush(bus);
+
+       return 0;
+}
+
+/**
+ * kdbus_conn_has_name() - check if a connection owns a name
+ * @conn:              Connection
+ * @name:              Well-know name to check for
+ *
+ * The caller must hold the registry lock of conn->ep->bus.
+ *
+ * Return: true if the name is currently owned by the connection
+ */
+bool kdbus_conn_has_name(struct kdbus_conn *conn, const char *name)
+{
+       struct kdbus_name_owner *owner;
+
+       lockdep_assert_held(&conn->ep->bus->name_registry->rwlock);
+
+       list_for_each_entry(owner, &conn->names_list, conn_entry)
+               if (!(owner->flags & KDBUS_NAME_IN_QUEUE) &&
+                   !strcmp(name, owner->name->name))
+                       return true;
+
+       return false;
+}
+
+struct kdbus_quota {
+       u32 memory;
+       u16 msgs;
+       u8 fds;
+};
+
+/**
+ * kdbus_conn_quota_inc() - increase quota accounting
+ * @c:         connection owning the quota tracking
+ * @u:         user to account for (or NULL for kernel accounting)
+ * @memory:    size of memory to account for
+ * @fds:       number of FDs to account for
+ *
+ * This call manages the quotas on resource @c. That is, it's used if other
+ * users want to use the resources of connection @c, which so far only concerns
+ * the receive queue of the destination.
+ *
+ * This increases the quota-accounting for user @u by @memory bytes and @fds
+ * file descriptors. If the user has already reached the quota limits, this call
+ * will not do any accounting but return a negative error code indicating the
+ * failure.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int kdbus_conn_quota_inc(struct kdbus_conn *c, struct kdbus_user *u,
+                        size_t memory, size_t fds)
+{
+       struct kdbus_quota *quota;
+       size_t available, accounted;
+       unsigned int id;
+
+       /*
+        * Pool Layout:
+        * 50% of a pool is always owned by the connection. It is reserved for
+        * kernel queries, handling received messages and other tasks that are
+        * under control of the pool owner. The other 50% of the pool are used
+        * as incoming queue.
+        * As we optionally support user-space based policies, we need fair
+        * allocation schemes. Furthermore, resource utilization should be
+        * maximized, so only minimal resources stay reserved. However, we need
+        * to adapt to a dynamic number of users, as we cannot know how many
+        * users will talk to a connection. Therefore, the current allocation
+        * works like this:
+        * We limit the number of bytes in a destination's pool per sending
+        * user. The space available for a user is 33% of the unused pool space
+        * (whereas the space used by the user itself is also treated as
+        * 'unused'). This way, we favor users coming first, but keep enough
+        * pool space available for any following users. Given that messages are
+        * dequeued in FIFO order, this should balance nicely if the number of
+        * users grows. At the same time, this algorithm guarantees that the
+        * space available to a connection is reduced dynamically, the more
+        * concurrent users talk to a connection.
+        */
+
+       /* per user-accounting is expensive, so we keep state small */
+       BUILD_BUG_ON(sizeof(quota->memory) != 4);
+       BUILD_BUG_ON(sizeof(quota->msgs) != 2);
+       BUILD_BUG_ON(sizeof(quota->fds) != 1);
+       BUILD_BUG_ON(KDBUS_CONN_MAX_MSGS > U16_MAX);
+       BUILD_BUG_ON(KDBUS_CONN_MAX_FDS_PER_USER > U8_MAX);
+
+       id = u ? u->id : KDBUS_USER_KERNEL_ID;
+       if (id >= c->n_quota) {
+               unsigned int users;
+
+               users = max(KDBUS_ALIGN8(id) + 8, id);
+               quota = krealloc(c->quota, users * sizeof(*quota),
+                                GFP_KERNEL | __GFP_ZERO);
+               if (!quota)
+                       return -ENOMEM;
+
+               c->n_quota = users;
+               c->quota = quota;
+       }
+
+       quota = &c->quota[id];
+       kdbus_pool_accounted(c->pool, &available, &accounted);
+
+       /* half the pool is _always_ reserved for the pool owner */
+       available /= 2;
+
+       /*
+        * Pool owner slices are un-accounted slices; they can claim more
+        * than 50% of the queue. However, the slices we're dealing with here
+        * belong to the incoming queue, hence they are 'accounted' slices
+        * to which the 50%-limit applies.
+        */
+       if (available < accounted)
+               return -ENOBUFS;
+
+       /* 1/3 of the remaining space (including your own memory) */
+       available = (available - accounted + quota->memory) / 3;
+
+       if (available < quota->memory ||
+           available - quota->memory < memory ||
+           quota->memory + memory > U32_MAX)
+               return -ENOBUFS;
+       if (quota->msgs >= KDBUS_CONN_MAX_MSGS)
+               return -ENOBUFS;
+       if (quota->fds + fds < quota->fds ||
+           quota->fds + fds > KDBUS_CONN_MAX_FDS_PER_USER)
+               return -EMFILE;
+
+       quota->memory += memory;
+       quota->fds += fds;
+       ++quota->msgs;
+       return 0;
+}
+
+/**
+ * kdbus_conn_quota_dec() - decrease quota accounting
+ * @c:         connection owning the quota tracking
+ * @u:         user which was accounted for (or NULL for kernel accounting)
+ * @memory:    size of memory which was accounted for
+ * @fds:       number of FDs which were accounted for
+ *
+ * This does the reverse of kdbus_conn_quota_inc(). You have to release any
+ * accounted resources that you called kdbus_conn_quota_inc() for. However, you
+ * must not call kdbus_conn_quota_dec() if the accounting failed (that is,
+ * kdbus_conn_quota_inc() failed).
+ */
+void kdbus_conn_quota_dec(struct kdbus_conn *c, struct kdbus_user *u,
+                         size_t memory, size_t fds)
+{
+       struct kdbus_quota *quota;
+       unsigned int id;
+
+       id = u ? u->id : KDBUS_USER_KERNEL_ID;
+       if (WARN_ON(id >= c->n_quota))
+               return;
+
+       quota = &c->quota[id];
+
+       if (!WARN_ON(quota->msgs == 0))
+               --quota->msgs;
+       if (!WARN_ON(quota->memory < memory))
+               quota->memory -= memory;
+       if (!WARN_ON(quota->fds < fds))
+               quota->fds -= fds;
+}
+
+/**
+ * kdbus_conn_lost_message() - handle lost messages
+ * @c:         connection that lost a message
+ *
+ * kdbus is reliable. That means, we try hard to never lose messages. However,
+ * memory is limited, so we cannot rely on transmissions to never fail.
+ * Therefore, we use quota-limits to let callers know if their unicast message
+ * cannot be transmitted to a peer. This works fine for unicasts, but for
+ * broadcasts we cannot make the caller handle the transmission failure.
+ * Instead, we must let the destination know that it couldn't receive a
+ * broadcast.
+ * As this is an unlikely scenario, we keep it simple. A single lost-counter
+ * remembers the number of lost messages since the last call to RECV. The next
+ * message retrieval will notify the connection that it lost messages since the
+ * last message retrieval and thus should resync its state.
+ */
+void kdbus_conn_lost_message(struct kdbus_conn *c)
+{
+       if (atomic_inc_return(&c->lost_count) == 1)
+               wake_up_interruptible(&c->wait);
+}
+
+/* Callers should take the conn_dst lock */
+static struct kdbus_queue_entry *
+kdbus_conn_entry_make(struct kdbus_conn *conn_src,
+                     struct kdbus_conn *conn_dst,
+                     struct kdbus_staging *staging)
+{
+       /* The remote connection was disconnected */
+       if (!kdbus_conn_active(conn_dst))
+               return ERR_PTR(-ECONNRESET);
+
+       /*
+        * If the connection does not accept file descriptors but the message
+        * has some attached, refuse it.
+        *
+        * If this is a monitor connection, accept the message. In that
+        * case, all file descriptors will be set to -1 at receive time.
+        */
+       if (!kdbus_conn_is_monitor(conn_dst) &&
+           !(conn_dst->flags & KDBUS_HELLO_ACCEPT_FD) &&
+           staging->gaps && staging->gaps->n_fds > 0)
+               return ERR_PTR(-ECOMM);
+
+       return kdbus_queue_entry_new(conn_src, conn_dst, staging);
+}
+
+/*
+ * Synchronously responding to a message, allocate a queue entry
+ * and attach it to the reply tracking object.
+ * The connection's queue will never get to see it.
+ */
+static int kdbus_conn_entry_sync_attach(struct kdbus_conn *conn_dst,
+                                       struct kdbus_staging *staging,
+                                       struct kdbus_reply *reply_wake)
+{
+       struct kdbus_queue_entry *entry;
+       int remote_ret, ret = 0;
+
+       mutex_lock(&reply_wake->reply_dst->lock);
+
+       /*
+        * If we are still waiting then proceed, allocate a queue
+        * entry and attach it to the reply object
+        */
+       if (reply_wake->waiting) {
+               entry = kdbus_conn_entry_make(reply_wake->reply_src, conn_dst,
+                                             staging);
+               if (IS_ERR(entry))
+                       ret = PTR_ERR(entry);
+               else
+                       /* Attach the entry to the reply object */
+                       reply_wake->queue_entry = entry;
+       } else {
+               ret = -ECONNRESET;
+       }
+
+       /*
+        * Update the reply object and wake up remote peer only
+        * on appropriate return codes
+        *
+        * * -ECOMM: if the replying connection failed with -ECOMM
+        *           then wakeup remote peer with -EREMOTEIO
+        *
+        *           We do this to differenciate between -ECOMM errors
+        *           from the original sender perspective:
+        *           -ECOMM error during the sync send and
+        *           -ECOMM error during the sync reply, this last
+        *           one is rewritten to -EREMOTEIO
+        *
+        * * Wake up on all other return codes.
+        */
+       remote_ret = ret;
+
+       if (ret == -ECOMM)
+               remote_ret = -EREMOTEIO;
+
+       kdbus_sync_reply_wakeup(reply_wake, remote_ret);
+       mutex_unlock(&reply_wake->reply_dst->lock);
+
+       return ret;
+}
+
+/**
+ * kdbus_conn_entry_insert() - enqueue a message into the receiver's pool
+ * @conn_src:          The sending connection
+ * @conn_dst:          The connection to queue into
+ * @staging:           Message to send
+ * @reply:             The reply tracker to attach to the queue entry
+ * @name:              Destination name this msg is sent to, or NULL
+ *
+ * Return: 0 on success. negative error otherwise.
+ */
+int kdbus_conn_entry_insert(struct kdbus_conn *conn_src,
+                           struct kdbus_conn *conn_dst,
+                           struct kdbus_staging *staging,
+                           struct kdbus_reply *reply,
+                           const struct kdbus_name_entry *name)
+{
+       struct kdbus_queue_entry *entry;
+       int ret;
+
+       kdbus_conn_lock2(conn_src, conn_dst);
+
+       entry = kdbus_conn_entry_make(conn_src, conn_dst, staging);
+       if (IS_ERR(entry)) {
+               ret = PTR_ERR(entry);
+               goto exit_unlock;
+       }
+
+       if (reply) {
+               kdbus_reply_link(reply);
+               if (!reply->sync)
+                       schedule_delayed_work(&conn_src->work, 0);
+       }
+
+       /*
+        * Record the sequence number of the registered name; it will
+        * be remembered by the queue, in case messages addressed to a
+        * name need to be moved from or to an activator.
+        */
+       if (name)
+               entry->dst_name_id = name->name_id;
+
+       kdbus_queue_entry_enqueue(entry, reply);
+       wake_up_interruptible(&conn_dst->wait);
+
+       ret = 0;
+
+exit_unlock:
+       kdbus_conn_unlock2(conn_src, conn_dst);
+       return ret;
+}
+
+static int kdbus_conn_wait_reply(struct kdbus_conn *conn_src,
+                                struct kdbus_cmd_send *cmd_send,
+                                struct file *ioctl_file,
+                                struct file *cancel_fd,
+                                struct kdbus_reply *reply_wait,
+                                ktime_t expire)
+{
+       struct kdbus_queue_entry *entry;
+       struct poll_wqueues pwq = {};
+       int ret;
+
+       if (WARN_ON(!reply_wait))
+               return -EIO;
+
+       /*
+        * Block until the reply arrives. reply_wait is left untouched
+        * by the timeout scans that might be conducted for other,
+        * asynchronous replies of conn_src.
+        */
+
+       poll_initwait(&pwq);
+       poll_wait(ioctl_file, &conn_src->wait, &pwq.pt);
+
+       for (;;) {
+               /*
+                * Any of the following conditions will stop our synchronously
+                * blocking SEND command:
+                *
+                * a) The origin sender closed its connection
+                * b) The remote peer answered, setting reply_wait->waiting = 0
+                * c) The cancel FD was written to
+                * d) A signal was received
+                * e) The specified timeout was reached, and none of the above
+                *    conditions kicked in.
+                */
+
+               /*
+                * We have already acquired an active reference when
+                * entering here, but another thread may call
+                * KDBUS_CMD_BYEBYE which does not acquire an active
+                * reference, therefore kdbus_conn_disconnect() will
+                * not wait for us.
+                */
+               if (!kdbus_conn_active(conn_src)) {
+                       ret = -ECONNRESET;
+                       break;
+               }
+
+               /*
+                * After the replying peer unset the waiting variable
+                * it will wake up us.
+                */
+               if (!reply_wait->waiting) {
+                       ret = reply_wait->err;
+                       break;
+               }
+
+               if (cancel_fd) {
+                       unsigned int r;
+
+                       r = cancel_fd->f_op->poll(cancel_fd, &pwq.pt);
+                       if (r & POLLIN) {
+                               ret = -ECANCELED;
+                               break;
+                       }
+               }
+
+               if (signal_pending(current)) {
+                       ret = -EINTR;
+                       break;
+               }
+
+               if (!poll_schedule_timeout(&pwq, TASK_INTERRUPTIBLE,
+                                          &expire, 0)) {
+                       ret = -ETIMEDOUT;
+                       break;
+               }
+
+               /*
+                * Reset the poll worker func, so the waitqueues are not
+                * added to the poll table again. We just reuse what we've
+                * collected earlier for further iterations.
+                */
+               init_poll_funcptr(&pwq.pt, NULL);
+       }
+
+       poll_freewait(&pwq);
+
+       if (ret == -EINTR) {
+               /*
+                * Interrupted system call. Unref the reply object, and pass
+                * the return value down the chain. Mark the reply as
+                * interrupted, so the cleanup work can remove it, but do not
+                * unlink it from the list. Once the syscall restarts, we'll
+                * pick it up and wait on it again.
+                */
+               mutex_lock(&conn_src->lock);
+               reply_wait->interrupted = true;
+               schedule_delayed_work(&conn_src->work, 0);
+               mutex_unlock(&conn_src->lock);
+
+               return -ERESTARTSYS;
+       }
+
+       mutex_lock(&conn_src->lock);
+       reply_wait->waiting = false;
+       entry = reply_wait->queue_entry;
+       if (entry) {
+               ret = kdbus_queue_entry_install(entry,
+                                               &cmd_send->reply.return_flags,
+                                               true);
+               kdbus_pool_slice_publish(entry->slice, &cmd_send->reply.offset,
+                                        &cmd_send->reply.msg_size);
+               kdbus_queue_entry_free(entry);
+       }
+       kdbus_reply_unlink(reply_wait);
+       mutex_unlock(&conn_src->lock);
+
+       return ret;
+}
+
+static int kdbus_pin_dst(struct kdbus_bus *bus,
+                        struct kdbus_staging *staging,
+                        struct kdbus_name_entry **out_name,
+                        struct kdbus_conn **out_dst)
+{
+       const struct kdbus_msg *msg = staging->msg;
+       struct kdbus_name_owner *owner = NULL;
+       struct kdbus_name_entry *name = NULL;
+       struct kdbus_conn *dst = NULL;
+       int ret;
+
+       lockdep_assert_held(&bus->name_registry->rwlock);
+
+       if (!staging->dst_name) {
+               dst = kdbus_bus_find_conn_by_id(bus, msg->dst_id);
+               if (!dst)
+                       return -ENXIO;
+
+               if (!kdbus_conn_is_ordinary(dst)) {
+                       ret = -ENXIO;
+                       goto error;
+               }
+       } else {
+               name = kdbus_name_lookup_unlocked(bus->name_registry,
+                                                 staging->dst_name);
+               if (name)
+                       owner = kdbus_name_get_owner(name);
+               if (!owner)
+                       return -ESRCH;
+
+               /*
+                * If both a name and a connection ID are given as destination
+                * of a message, check that the currently owning connection of
+                * the name matches the specified ID.
+                * This way, we allow userspace to send the message to a
+                * specific connection by ID only if the connection currently
+                * owns the given name.
+                */
+               if (msg->dst_id != KDBUS_DST_ID_NAME &&
+                   msg->dst_id != owner->conn->id)
+                       return -EREMCHG;
+
+               if ((msg->flags & KDBUS_MSG_NO_AUTO_START) &&
+                   kdbus_conn_is_activator(owner->conn))
+                       return -EADDRNOTAVAIL;
+
+               dst = kdbus_conn_ref(owner->conn);
+       }
+
+       *out_name = name;
+       *out_dst = dst;
+       return 0;
+
+error:
+       kdbus_conn_unref(dst);
+       return ret;
+}
+
+static int kdbus_conn_reply(struct kdbus_conn *src,
+                           struct kdbus_staging *staging)
+{
+       const struct kdbus_msg *msg = staging->msg;
+       struct kdbus_name_entry *name = NULL;
+       struct kdbus_reply *reply, *wake = NULL;
+       struct kdbus_conn *dst = NULL;
+       struct kdbus_bus *bus = src->ep->bus;
+       int ret;
+
+       if (WARN_ON(msg->dst_id == KDBUS_DST_ID_BROADCAST) ||
+           WARN_ON(msg->flags & KDBUS_MSG_EXPECT_REPLY) ||
+           WARN_ON(msg->flags & KDBUS_MSG_SIGNAL))
+               return -EINVAL;
+
+       /* name-registry must be locked for lookup *and* collecting data */
+       down_read(&bus->name_registry->rwlock);
+
+       /* find and pin destination */
+
+       ret = kdbus_pin_dst(bus, staging, &name, &dst);
+       if (ret < 0)
+               goto exit;
+
+       mutex_lock(&dst->lock);
+       reply = kdbus_reply_find(src, dst, msg->cookie_reply);
+       if (reply) {
+               if (reply->sync)
+                       wake = kdbus_reply_ref(reply);
+               kdbus_reply_unlink(reply);
+       }
+       mutex_unlock(&dst->lock);
+
+
+       /* send message */
+
+       kdbus_bus_eavesdrop(bus, src, staging);
+
+       if (wake)
+               ret = kdbus_conn_entry_sync_attach(dst, staging, wake);
+       else
+               ret = kdbus_conn_entry_insert(src, dst, staging, NULL, name);
+
+exit:
+       up_read(&bus->name_registry->rwlock);
+       kdbus_reply_unref(wake);
+       kdbus_conn_unref(dst);
+       return ret;
+}
+
+static struct kdbus_reply *kdbus_conn_call(struct kdbus_conn *src,
+                                          struct kdbus_staging *staging,
+                                          ktime_t exp)
+{
+       const struct kdbus_msg *msg = staging->msg;
+       struct kdbus_name_entry *name = NULL;
+       struct kdbus_reply *wait = NULL;
+       struct kdbus_conn *dst = NULL;
+       struct kdbus_bus *bus = src->ep->bus;
+       int ret;
+
+       if (WARN_ON(msg->dst_id == KDBUS_DST_ID_BROADCAST) ||
+           WARN_ON(msg->flags & KDBUS_MSG_SIGNAL) ||
+           WARN_ON(!(msg->flags & KDBUS_MSG_EXPECT_REPLY)))
+               return ERR_PTR(-EINVAL);
+
+       /* resume previous wait-context, if available */
+
+       mutex_lock(&src->lock);
+       wait = kdbus_reply_find(NULL, src, msg->cookie);
+       if (wait) {
+               if (wait->interrupted) {
+                       kdbus_reply_ref(wait);
+                       wait->interrupted = false;
+               } else {
+                       wait = NULL;
+               }
+       }
+       mutex_unlock(&src->lock);
+
+       if (wait)
+               return wait;
+
+       if (ktime_compare(ktime_get(), exp) >= 0)
+               return ERR_PTR(-ETIMEDOUT);
+
+       /* name-registry must be locked for lookup *and* collecting data */
+       down_read(&bus->name_registry->rwlock);
+
+       /* find and pin destination */
+
+       ret = kdbus_pin_dst(bus, staging, &name, &dst);
+       if (ret < 0)
+               goto exit;
+
+       if (!kdbus_conn_policy_talk(src, current_cred(), dst)) {
+               ret = -EPERM;
+               goto exit;
+       }
+
+       wait = kdbus_reply_new(dst, src, msg, name, true);
+       if (IS_ERR(wait)) {
+               ret = PTR_ERR(wait);
+               wait = NULL;
+               goto exit;
+       }
+
+       /* send message */
+
+       kdbus_bus_eavesdrop(bus, src, staging);
+
+       ret = kdbus_conn_entry_insert(src, dst, staging, wait, name);
+       if (ret < 0)
+               goto exit;
+
+       ret = 0;
+
+exit:
+       up_read(&bus->name_registry->rwlock);
+       if (ret < 0) {
+               kdbus_reply_unref(wait);
+               wait = ERR_PTR(ret);
+       }
+       kdbus_conn_unref(dst);
+       return wait;
+}
+
+static int kdbus_conn_unicast(struct kdbus_conn *src,
+                             struct kdbus_staging *staging)
+{
+       const struct kdbus_msg *msg = staging->msg;
+       struct kdbus_name_entry *name = NULL;
+       struct kdbus_reply *wait = NULL;
+       struct kdbus_conn *dst = NULL;
+       struct kdbus_bus *bus = src->ep->bus;
+       bool is_signal = (msg->flags & KDBUS_MSG_SIGNAL);
+       int ret = 0;
+
+       if (WARN_ON(msg->dst_id == KDBUS_DST_ID_BROADCAST) ||
+           WARN_ON(!(msg->flags & KDBUS_MSG_EXPECT_REPLY) &&
+                   msg->cookie_reply != 0))
+               return -EINVAL;
+
+       /* name-registry must be locked for lookup *and* collecting data */
+       down_read(&bus->name_registry->rwlock);
+
+       /* find and pin destination */
+
+       ret = kdbus_pin_dst(bus, staging, &name, &dst);
+       if (ret < 0)
+               goto exit;
+
+       if (is_signal) {
+               /* like broadcasts we eavesdrop even if the msg is dropped */
+               kdbus_bus_eavesdrop(bus, src, staging);
+
+               /* drop silently if peer is not interested or not privileged */
+               if (!kdbus_match_db_match_msg(dst->match_db, src, staging) ||
+                   !kdbus_conn_policy_talk(dst, NULL, src))
+                       goto exit;
+       } else if (!kdbus_conn_policy_talk(src, current_cred(), dst)) {
+               ret = -EPERM;
+               goto exit;
+       } else if (msg->flags & KDBUS_MSG_EXPECT_REPLY) {
+               wait = kdbus_reply_new(dst, src, msg, name, false);
+               if (IS_ERR(wait)) {
+                       ret = PTR_ERR(wait);
+                       wait = NULL;
+                       goto exit;
+               }
+       }
+
+       /* send message */
+
+       if (!is_signal)
+               kdbus_bus_eavesdrop(bus, src, staging);
+
+       ret = kdbus_conn_entry_insert(src, dst, staging, wait, name);
+       if (ret < 0 && !is_signal)
+               goto exit;
+
+       /* signals are treated like broadcasts, recv-errors are ignored */
+       ret = 0;
+
+exit:
+       up_read(&bus->name_registry->rwlock);
+       kdbus_reply_unref(wait);
+       kdbus_conn_unref(dst);
+       return ret;
+}
+
+/**
+ * kdbus_conn_move_messages() - move messages from one connection to another
+ * @conn_dst:          Connection to copy to
+ * @conn_src:          Connection to copy from
+ * @name_id:           Filter for the sequence number of the registered
+ *                     name, 0 means no filtering.
+ *
+ * Move all messages from one connection to another. This is used when
+ * an implementer connection is taking over/giving back a well-known name
+ * from/to an activator connection.
+ */
+void kdbus_conn_move_messages(struct kdbus_conn *conn_dst,
+                             struct kdbus_conn *conn_src,
+                             u64 name_id)
+{
+       struct kdbus_queue_entry *e, *e_tmp;
+       struct kdbus_reply *r, *r_tmp;
+       struct kdbus_bus *bus;
+       struct kdbus_conn *c;
+       LIST_HEAD(msg_list);
+       int i, ret = 0;
+
+       if (WARN_ON(conn_src == conn_dst))
+               return;
+
+       bus = conn_src->ep->bus;
+
+       /* lock order: domain -> bus -> ep -> names -> conn */
+       down_read(&bus->conn_rwlock);
+       hash_for_each(bus->conn_hash, i, c, hentry) {
+               if (c == conn_src || c == conn_dst)
+                       continue;
+
+               mutex_lock(&c->lock);
+               list_for_each_entry_safe(r, r_tmp, &c->reply_list, entry) {
+                       if (r->reply_src != conn_src)
+                               continue;
+
+                       /* filter messages for a specific name */
+                       if (name_id > 0 && r->name_id != name_id)
+                               continue;
+
+                       kdbus_conn_unref(r->reply_src);
+                       r->reply_src = kdbus_conn_ref(conn_dst);
+               }
+               mutex_unlock(&c->lock);
+       }
+       up_read(&bus->conn_rwlock);
+
+       kdbus_conn_lock2(conn_src, conn_dst);
+       list_for_each_entry_safe(e, e_tmp, &conn_src->queue.msg_list, entry) {
+               /* filter messages for a specific name */
+               if (name_id > 0 && e->dst_name_id != name_id)
+                       continue;
+
+               if (!(conn_dst->flags & KDBUS_HELLO_ACCEPT_FD) &&
+                   e->gaps && e->gaps->n_fds > 0) {
+                       kdbus_conn_lost_message(conn_dst);
+                       kdbus_queue_entry_free(e);
+                       continue;
+               }
+
+               ret = kdbus_queue_entry_move(e, conn_dst);
+               if (ret < 0) {
+                       kdbus_conn_lost_message(conn_dst);
+                       kdbus_queue_entry_free(e);
+                       continue;
+               }
+       }
+       kdbus_conn_unlock2(conn_src, conn_dst);
+
+       /* wake up poll() */
+       wake_up_interruptible(&conn_dst->wait);
+}
+
+/* query the policy-database for all names of @whom */
+static bool kdbus_conn_policy_query_all(struct kdbus_conn *conn,
+                                       const struct cred *conn_creds,
+                                       struct kdbus_policy_db *db,
+                                       struct kdbus_conn *whom,
+                                       unsigned int access)
+{
+       struct kdbus_name_owner *owner;
+       bool pass = false;
+       int res;
+
+       lockdep_assert_held(&conn->ep->bus->name_registry->rwlock);
+
+       down_read(&db->entries_rwlock);
+       mutex_lock(&whom->lock);
+
+       list_for_each_entry(owner, &whom->names_list, conn_entry) {
+               if (owner->flags & KDBUS_NAME_IN_QUEUE)
+                       continue;
+
+               res = kdbus_policy_query_unlocked(db,
+                                       conn_creds ? : conn->cred,
+                                       owner->name->name,
+                                       kdbus_strhash(owner->name->name));
+               if (res >= (int)access) {
+                       pass = true;
+                       break;
+               }
+       }
+
+       mutex_unlock(&whom->lock);
+       up_read(&db->entries_rwlock);
+
+       return pass;
+}
+
+/**
+ * kdbus_conn_policy_own_name() - verify a connection can own the given name
+ * @conn:              Connection
+ * @conn_creds:                Credentials of @conn to use for policy check
+ * @name:              Name
+ *
+ * This verifies that @conn is allowed to acquire the well-known name @name.
+ *
+ * Return: true if allowed, false if not.
+ */
+bool kdbus_conn_policy_own_name(struct kdbus_conn *conn,
+                               const struct cred *conn_creds,
+                               const char *name)
+{
+       unsigned int hash = kdbus_strhash(name);
+       int res;
+
+#ifdef DISABLE_KDBUS_POLICY
+       return true;
+#endif
+
+       if (!conn_creds)
+               conn_creds = conn->cred;
+
+       if (conn->ep->user) {
+               res = kdbus_policy_query(&conn->ep->policy_db, conn_creds,
+                                        name, hash);
+               if (res < KDBUS_POLICY_OWN)
+                       return false;
+       }
+
+       if (conn->owner)
+               return true;
+
+       res = kdbus_policy_query(&conn->ep->bus->policy_db, conn_creds,
+                                name, hash);
+       return res >= KDBUS_POLICY_OWN;
+}
+
+/**
+ * kdbus_conn_policy_talk() - verify a connection can talk to a given peer
+ * @conn:              Connection that tries to talk
+ * @conn_creds:                Credentials of @conn to use for policy check
+ * @to:                        Connection that is talked to
+ *
+ * This verifies that @conn is allowed to talk to @to.
+ *
+ * Return: true if allowed, false if not.
+ */
+bool kdbus_conn_policy_talk(struct kdbus_conn *conn,
+                           const struct cred *conn_creds,
+                           struct kdbus_conn *to)
+{
+
+#ifdef DISABLE_KDBUS_POLICY
+       return true;
+#endif
+
+       if (!conn_creds)
+               conn_creds = conn->cred;
+
+       if (conn->ep->user &&
+           !kdbus_conn_policy_query_all(conn, conn_creds, &conn->ep->policy_db,
+                                        to, KDBUS_POLICY_TALK))
+               return false;
+
+       if (conn->owner)
+               return true;
+       if (uid_eq(conn_creds->euid, to->cred->uid))
+               return true;
+
+       return kdbus_conn_policy_query_all(conn, conn_creds,
+                                          &conn->ep->bus->policy_db, to,
+                                          KDBUS_POLICY_TALK);
+}
+
+/**
+ * kdbus_conn_policy_see_name_unlocked() - verify a connection can see a given
+ *                                        name
+ * @conn:              Connection
+ * @conn_creds:                Credentials of @conn to use for policy check
+ * @name:              Name
+ *
+ * This verifies that @conn is allowed to see the well-known name @name. Caller
+ * must hold policy-lock.
+ *
+ * Return: true if allowed, false if not.
+ */
+bool kdbus_conn_policy_see_name_unlocked(struct kdbus_conn *conn,
+                                        const struct cred *conn_creds,
+                                        const char *name)
+{
+       int res;
+
+#ifdef DISABLE_KDBUS_POLICY
+       return true;
+#endif
+
+       /*
+        * By default, all names are visible on a bus. SEE policies can only be
+        * installed on custom endpoints, where by default no name is visible.
+        */
+       if (!conn->ep->user)
+               return true;
+
+       res = kdbus_policy_query_unlocked(&conn->ep->policy_db,
+                                         conn_creds ? : conn->cred,
+                                         name, kdbus_strhash(name));
+       return res >= KDBUS_POLICY_SEE;
+}
+
+static bool kdbus_conn_policy_see_name(struct kdbus_conn *conn,
+                                      const struct cred *conn_creds,
+                                      const char *name)
+{
+       bool res;
+
+       down_read(&conn->ep->policy_db.entries_rwlock);
+       res = kdbus_conn_policy_see_name_unlocked(conn, conn_creds, name);
+       up_read(&conn->ep->policy_db.entries_rwlock);
+
+       return res;
+}
+
+static bool kdbus_conn_policy_see(struct kdbus_conn *conn,
+                                 const struct cred *conn_creds,
+                                 struct kdbus_conn *whom)
+{
+
+#ifdef DISABLE_KDBUS_POLICY
+       return true;
+#endif
+
+       /*
+        * By default, all names are visible on a bus, so a connection can
+        * always see other connections. SEE policies can only be installed on
+        * custom endpoints, where by default no name is visible and we hide
+        * peers from each other, unless you see at least _one_ name of the
+        * peer.
+        */
+       return !conn->ep->user ||
+              kdbus_conn_policy_query_all(conn, conn_creds,
+                                          &conn->ep->policy_db, whom,
+                                          KDBUS_POLICY_SEE);
+}
+
+/**
+ * kdbus_conn_policy_see_notification() - verify a connection is allowed to
+ *                                       receive a given kernel notification
+ * @conn:              Connection
+ * @conn_creds:                Credentials of @conn to use for policy check
+ * @msg:               Notification message
+ *
+ * This checks whether @conn is allowed to see the kernel notification.
+ *
+ * Return: true if allowed, false if not.
+ */
+bool kdbus_conn_policy_see_notification(struct kdbus_conn *conn,
+                                       const struct cred *conn_creds,
+                                       const struct kdbus_msg *msg)
+{
+       /*
+        * Depending on the notification type, broadcasted kernel notifications
+        * have to be filtered:
+        *
+        * KDBUS_ITEM_NAME_{ADD,REMOVE,CHANGE}: This notification is forwarded
+        *     to a peer if, and only if, that peer can see the name this
+        *     notification is for.
+        *
+        * KDBUS_ITEM_ID_{ADD,REMOVE}: Notifications for ID changes are
+        *     broadcast to everyone, to allow tracking peers.
+        */
+
+       switch (msg->items[0].type) {
+       case KDBUS_ITEM_NAME_ADD:
+       case KDBUS_ITEM_NAME_REMOVE:
+       case KDBUS_ITEM_NAME_CHANGE:
+               return kdbus_conn_policy_see_name(conn, conn_creds,
+                                       msg->items[0].name_change.name);
+
+       case KDBUS_ITEM_ID_ADD:
+       case KDBUS_ITEM_ID_REMOVE:
+               return true;
+
+       default:
+               WARN(1, "Invalid type for notification broadcast: %llu\n",
+                    (unsigned long long)msg->items[0].type);
+               return false;
+       }
+}
+
+/**
+ * kdbus_cmd_hello() - handle KDBUS_CMD_HELLO
+ * @ep:                        Endpoint to operate on
+ * @file:              File this connection is opened on
+ * @argp:              Command payload
+ *
+ * Return: NULL or newly created connection on success, ERR_PTR on failure.
+ */
+struct kdbus_conn *kdbus_cmd_hello(struct kdbus_ep *ep, struct file *file,
+                                  void __user *argp)
+{
+       struct kdbus_cmd_hello *cmd;
+       struct kdbus_conn *c = NULL;
+       const char *item_name;
+       int ret;
+
+       struct kdbus_arg argv[] = {
+               { .type = KDBUS_ITEM_NEGOTIATE },
+               { .type = KDBUS_ITEM_NAME },
+               { .type = KDBUS_ITEM_CREDS },
+               { .type = KDBUS_ITEM_PIDS },
+               { .type = KDBUS_ITEM_SECLABEL },
+               { .type = KDBUS_ITEM_CONN_DESCRIPTION },
+               { .type = KDBUS_ITEM_POLICY_ACCESS, .multiple = true },
+       };
+       struct kdbus_args args = {
+               .allowed_flags = KDBUS_FLAG_NEGOTIATE |
+                                KDBUS_HELLO_ACCEPT_FD |
+                                KDBUS_HELLO_ACTIVATOR |
+                                KDBUS_HELLO_POLICY_HOLDER |
+                                KDBUS_HELLO_MONITOR,
+               .argv = argv,
+               .argc = ARRAY_SIZE(argv),
+       };
+
+       ret = kdbus_args_parse(&args, argp, &cmd);
+       if (ret < 0)
+               return ERR_PTR(ret);
+       if (ret > 0)
+               return NULL;
+
+       item_name = argv[1].item ? argv[1].item->str : NULL;
+
+       c = kdbus_conn_new(ep, file, cmd, item_name,
+                          argv[2].item ? &argv[2].item->creds : NULL,
+                          argv[3].item ? &argv[3].item->pids : NULL,
+                          argv[4].item ? argv[4].item->str : NULL,
+                          argv[5].item ? argv[5].item->str : NULL);
+       if (IS_ERR(c)) {
+               ret = PTR_ERR(c);
+               c = NULL;
+               goto exit;
+       }
+
+       ret = kdbus_conn_connect(c, item_name);
+       if (ret < 0)
+               goto exit;
+
+       if (kdbus_conn_is_activator(c) || kdbus_conn_is_policy_holder(c)) {
+               ret = kdbus_conn_acquire(c);
+               if (ret < 0)
+                       goto exit;
+
+               ret = kdbus_policy_set(&c->ep->bus->policy_db, args.items,
+                                      args.items_size, 1,
+                                      kdbus_conn_is_policy_holder(c), c);
+               kdbus_conn_release(c);
+               if (ret < 0)
+                       goto exit;
+       }
+
+       if (copy_to_user(argp, cmd, sizeof(*cmd)))
+               ret = -EFAULT;
+
+exit:
+       ret = kdbus_args_clear(&args, ret);
+       if (ret < 0) {
+               if (c) {
+                       kdbus_conn_disconnect(c, false);
+                       kdbus_conn_unref(c);
+               }
+               return ERR_PTR(ret);
+       }
+       return c;
+}
+
+/**
+ * kdbus_cmd_byebye_unlocked() - handle KDBUS_CMD_BYEBYE
+ * @conn:              connection to operate on
+ * @argp:              command payload
+ *
+ * The caller must not hold any active reference to @conn or this will deadlock.
+ *
+ * Return: >=0 on success, negative error code on failure.
+ */
+int kdbus_cmd_byebye_unlocked(struct kdbus_conn *conn, void __user *argp)
+{
+       struct kdbus_cmd *cmd;
+       int ret;
+
+       struct kdbus_arg argv[] = {
+               { .type = KDBUS_ITEM_NEGOTIATE },
+       };
+       struct kdbus_args args = {
+               .allowed_flags = KDBUS_FLAG_NEGOTIATE,
+               .argv = argv,
+               .argc = ARRAY_SIZE(argv),
+       };
+
+       if (!kdbus_conn_is_ordinary(conn))
+               return -EOPNOTSUPP;
+
+       ret = kdbus_args_parse(&args, argp, &cmd);
+       if (ret != 0)
+               return ret;
+
+       ret = kdbus_conn_disconnect(conn, true);
+       return kdbus_args_clear(&args, ret);
+}
+
+/**
+ * kdbus_cmd_conn_info() - handle KDBUS_CMD_CONN_INFO
+ * @conn:              connection to operate on
+ * @argp:              command payload
+ *
+ * Return: >=0 on success, negative error code on failure.
+ */
+int kdbus_cmd_conn_info(struct kdbus_conn *conn, void __user *argp)
+{
+       struct kdbus_meta_conn *conn_meta = NULL;
+       struct kdbus_pool_slice *slice = NULL;
+       struct kdbus_name_entry *entry = NULL;
+       struct kdbus_name_owner *owner = NULL;
+       struct kdbus_conn *owner_conn = NULL;
+       struct kdbus_item *meta_items = NULL;
+       struct kdbus_info info = {};
+       struct kdbus_cmd_info *cmd;
+       struct kdbus_bus *bus = conn->ep->bus;
+       struct kvec kvec[3];
+       size_t meta_size, cnt = 0;
+       const char *name;
+       u64 attach_flags, size = 0;
+       int ret;
+
+       struct kdbus_arg argv[] = {
+               { .type = KDBUS_ITEM_NEGOTIATE },
+               { .type = KDBUS_ITEM_NAME },
+       };
+       struct kdbus_args args = {
+               .allowed_flags = KDBUS_FLAG_NEGOTIATE,
+               .argv = argv,
+               .argc = ARRAY_SIZE(argv),
+       };
+
+       ret = kdbus_args_parse(&args, argp, &cmd);
+       if (ret != 0)
+               return ret;
+
+       /* registry must be held throughout lookup *and* collecting data */
+       down_read(&bus->name_registry->rwlock);
+
+       ret = kdbus_sanitize_attach_flags(cmd->attach_flags, &attach_flags);
+       if (ret < 0)
+               goto exit;
+
+       name = argv[1].item ? argv[1].item->str : NULL;
+
+       if (name) {
+               entry = kdbus_name_lookup_unlocked(bus->name_registry, name);
+               if (entry)
+                       owner = kdbus_name_get_owner(entry);
+               if (!owner ||
+                   !kdbus_conn_policy_see_name(conn, current_cred(), name) ||
+                   (cmd->id != 0 && owner->conn->id != cmd->id)) {
+                       /* pretend a name doesn't exist if you cannot see it */
+                       ret = -ESRCH;
+                       goto exit;
+               }
+
+               owner_conn = kdbus_conn_ref(owner->conn);
+       } else if (cmd->id > 0) {
+               owner_conn = kdbus_bus_find_conn_by_id(bus, cmd->id);
+               if (!owner_conn || !kdbus_conn_policy_see(conn, current_cred(),
+                                                         owner_conn)) {
+                       /* pretend an id doesn't exist if you cannot see it */
+                       ret = -ENXIO;
+                       goto exit;
+               }
+       } else {
+               ret = -EINVAL;
+               goto exit;
+       }
+
+       attach_flags &= atomic64_read(&owner_conn->attach_flags_send);
+
+       conn_meta = kdbus_meta_conn_new();
+       if (IS_ERR(conn_meta)) {
+               ret = PTR_ERR(conn_meta);
+               conn_meta = NULL;
+               goto exit;
+       }
+
+       ret = kdbus_meta_conn_collect(conn_meta, owner_conn, 0, attach_flags);
+       if (ret < 0)
+               goto exit;
+
+       ret = kdbus_meta_emit(owner_conn->meta_proc, owner_conn->meta_fake,
+                             conn_meta, conn, attach_flags,
+                             &meta_items, &meta_size);
+       if (ret < 0)
+               goto exit;
+
+       info.id = owner_conn->id;
+       info.flags = owner_conn->flags;
+
+       kdbus_kvec_set(&kvec[cnt++], &info, sizeof(info), &size);
+       if (meta_size > 0) {
+               kdbus_kvec_set(&kvec[cnt++], meta_items, meta_size, &size);
+               cnt += !!kdbus_kvec_pad(&kvec[cnt], &size);
+       }
+
+       info.size = size;
+
+       slice = kdbus_pool_slice_alloc(conn->pool, size, false);
+       if (IS_ERR(slice)) {
+               ret = PTR_ERR(slice);
+               slice = NULL;
+               goto exit;
+       }
+
+       ret = kdbus_pool_slice_copy_kvec(slice, 0, kvec, cnt, size);
+       if (ret < 0)
+               goto exit;
+
+       kdbus_pool_slice_publish(slice, &cmd->offset, &cmd->info_size);
+
+       if (kdbus_member_set_user(&cmd->offset, argp, typeof(*cmd), offset) ||
+           kdbus_member_set_user(&cmd->info_size, argp,
+                                 typeof(*cmd), info_size)) {
+               ret = -EFAULT;
+               goto exit;
+       }
+
+       ret = 0;
+
+exit:
+       up_read(&bus->name_registry->rwlock);
+       kdbus_pool_slice_release(slice);
+       kfree(meta_items);
+       kdbus_meta_conn_unref(conn_meta);
+       kdbus_conn_unref(owner_conn);
+       return kdbus_args_clear(&args, ret);
+}
+
+/**
+ * kdbus_cmd_update() - handle KDBUS_CMD_UPDATE
+ * @conn:              connection to operate on
+ * @argp:              command payload
+ *
+ * Return: >=0 on success, negative error code on failure.
+ */
+int kdbus_cmd_update(struct kdbus_conn *conn, void __user *argp)
+{
+       struct kdbus_item *item_policy;
+       u64 *item_attach_send = NULL;
+       u64 *item_attach_recv = NULL;
+       struct kdbus_cmd *cmd;
+       u64 attach_send;
+       u64 attach_recv;
+       int ret;
+
+       struct kdbus_arg argv[] = {
+               { .type = KDBUS_ITEM_NEGOTIATE },
+               { .type = KDBUS_ITEM_ATTACH_FLAGS_SEND },
+               { .type = KDBUS_ITEM_ATTACH_FLAGS_RECV },
+               { .type = KDBUS_ITEM_NAME, .multiple = true },
+               { .type = KDBUS_ITEM_POLICY_ACCESS, .multiple = true },
+       };
+       struct kdbus_args args = {
+               .allowed_flags = KDBUS_FLAG_NEGOTIATE,
+               .argv = argv,
+               .argc = ARRAY_SIZE(argv),
+       };
+
+       ret = kdbus_args_parse(&args, argp, &cmd);
+       if (ret != 0)
+               return ret;
+
+       item_attach_send = argv[1].item ? &argv[1].item->data64[0] : NULL;
+       item_attach_recv = argv[2].item ? &argv[2].item->data64[0] : NULL;
+       item_policy = argv[3].item ? : argv[4].item;
+
+       if (item_attach_send) {
+               if (!kdbus_conn_is_ordinary(conn) &&
+                   !kdbus_conn_is_monitor(conn)) {
+                       ret = -EOPNOTSUPP;
+                       goto exit;
+               }
+
+               ret = kdbus_sanitize_attach_flags(*item_attach_send,
+                                                 &attach_send);
+               if (ret < 0)
+                       goto exit;
+       }
+
+       if (item_attach_recv) {
+               if (!kdbus_conn_is_ordinary(conn) &&
+                   !kdbus_conn_is_monitor(conn) &&
+                   !kdbus_conn_is_activator(conn)) {
+                       ret = -EOPNOTSUPP;
+                       goto exit;
+               }
+
+               ret = kdbus_sanitize_attach_flags(*item_attach_recv,
+                                                 &attach_recv);
+               if (ret < 0)
+                       goto exit;
+       }
+
+       if (item_policy && !kdbus_conn_is_policy_holder(conn)) {
+               ret = -EOPNOTSUPP;
+               goto exit;
+       }
+
+       /* now that we verified the input, update the connection */
+
+       if (item_policy) {
+               ret = kdbus_policy_set(&conn->ep->bus->policy_db, cmd->items,
+                                      KDBUS_ITEMS_SIZE(cmd, items),
+                                      1, true, conn);
+               if (ret < 0)
+                       goto exit;
+       }
+
+       if (item_attach_send)
+               atomic64_set(&conn->attach_flags_send, attach_send);
+
+       if (item_attach_recv)
+               atomic64_set(&conn->attach_flags_recv, attach_recv);
+
+exit:
+       return kdbus_args_clear(&args, ret);
+}
+
+/**
+ * kdbus_cmd_send() - handle KDBUS_CMD_SEND
+ * @conn:              connection to operate on
+ * @f:                 file this command was called on
+ * @argp:              command payload
+ *
+ * Return: >=0 on success, negative error code on failure.
+ */
+int kdbus_cmd_send(struct kdbus_conn *conn, struct file *f, void __user *argp)
+{
+       struct kdbus_cmd_send *cmd;
+       struct kdbus_staging *staging = NULL;
+       struct kdbus_msg *msg = NULL;
+       struct file *cancel_fd = NULL;
+       int ret, ret2;
+
+       /* command arguments */
+       struct kdbus_arg argv[] = {
+               { .type = KDBUS_ITEM_NEGOTIATE },
+               { .type = KDBUS_ITEM_CANCEL_FD },
+       };
+       struct kdbus_args args = {
+               .allowed_flags = KDBUS_FLAG_NEGOTIATE |
+                                KDBUS_SEND_SYNC_REPLY,
+               .argv = argv,
+               .argc = ARRAY_SIZE(argv),
+       };
+
+       /* message arguments */
+       struct kdbus_arg msg_argv[] = {
+               { .type = KDBUS_ITEM_NEGOTIATE },
+               { .type = KDBUS_ITEM_PAYLOAD_VEC, .multiple = true },
+               { .type = KDBUS_ITEM_PAYLOAD_MEMFD, .multiple = true },
+               { .type = KDBUS_ITEM_FDS },
+               { .type = KDBUS_ITEM_BLOOM_FILTER },
+               { .type = KDBUS_ITEM_DST_NAME },
+       };
+       struct kdbus_args msg_args = {
+               .allowed_flags = KDBUS_FLAG_NEGOTIATE |
+                                KDBUS_MSG_EXPECT_REPLY |
+                                KDBUS_MSG_NO_AUTO_START |
+                                KDBUS_MSG_SIGNAL,
+               .argv = msg_argv,
+               .argc = ARRAY_SIZE(msg_argv),
+       };
+
+       if (!kdbus_conn_is_ordinary(conn))
+               return -EOPNOTSUPP;
+
+       /* make sure to parse both, @cmd and @msg on negotiation */
+
+       ret = kdbus_args_parse(&args, argp, &cmd);
+       if (ret < 0)
+               goto exit;
+       else if (ret > 0 && !cmd->msg_address) /* negotiation without msg */
+               goto exit;
+
+       ret2 = kdbus_args_parse_msg(&msg_args, KDBUS_PTR(cmd->msg_address),
+                                   &msg);
+       if (ret2 < 0) { /* cannot parse message */
+               ret = ret2;
+               goto exit;
+       } else if (ret2 > 0 && !ret) { /* msg-negot implies cmd-negot */
+               ret = -EINVAL;
+               goto exit;
+       } else if (ret > 0) { /* negotiation */
+               goto exit;
+       }
+
+       /* here we parsed both, @cmd and @msg, and neither wants negotiation */
+
+       cmd->reply.return_flags = 0;
+       kdbus_pool_publish_empty(conn->pool, &cmd->reply.offset,
+                                &cmd->reply.msg_size);
+
+       if (argv[1].item) {
+               cancel_fd = fget(argv[1].item->fds[0]);
+               if (!cancel_fd) {
+                       ret = -EBADF;
+                       goto exit;
+               }
+
+               if (!cancel_fd->f_op->poll) {
+                       ret = -EINVAL;
+                       goto exit;
+               }
+       }
+
+       /* patch-in the source of this message */
+       if (msg->src_id > 0 && msg->src_id != conn->id) {
+               ret = -EINVAL;
+               goto exit;
+       }
+       msg->src_id = conn->id;
+
+       staging = kdbus_staging_new_user(conn->ep->bus, cmd, msg);
+       if (IS_ERR(staging)) {
+               ret = PTR_ERR(staging);
+               staging = NULL;
+               goto exit;
+       }
+
+       if (msg->dst_id == KDBUS_DST_ID_BROADCAST) {
+               down_read(&conn->ep->bus->name_registry->rwlock);
+               kdbus_bus_broadcast(conn->ep->bus, conn, staging);
+               up_read(&conn->ep->bus->name_registry->rwlock);
+       } else if (cmd->flags & KDBUS_SEND_SYNC_REPLY) {
+               struct kdbus_reply *r;
+               ktime_t exp;
+
+               exp = ns_to_ktime(msg->timeout_ns);
+               r = kdbus_conn_call(conn, staging, exp);
+               if (IS_ERR(r)) {
+                       ret = PTR_ERR(r);
+                       goto exit;
+               }
+
+               ret = kdbus_conn_wait_reply(conn, cmd, f, cancel_fd, r, exp);
+               kdbus_reply_unref(r);
+               if (ret < 0)
+                       goto exit;
+       } else if ((msg->flags & KDBUS_MSG_EXPECT_REPLY) ||
+                  msg->cookie_reply == 0) {
+               ret = kdbus_conn_unicast(conn, staging);
+               if (ret < 0)
+                       goto exit;
+       } else {
+               ret = kdbus_conn_reply(conn, staging);
+               if (ret < 0)
+                       goto exit;
+       }
+
+       if (kdbus_member_set_user(&cmd->reply, argp, typeof(*cmd), reply))
+               ret = -EFAULT;
+
+exit:
+       if (cancel_fd)
+               fput(cancel_fd);
+       kdbus_staging_free(staging);
+       ret = kdbus_args_clear(&msg_args, ret);
+       return kdbus_args_clear(&args, ret);
+}
+
+/**
+ * kdbus_cmd_recv() - handle KDBUS_CMD_RECV
+ * @conn:              connection to operate on
+ * @argp:              command payload
+ *
+ * Return: >=0 on success, negative error code on failure.
+ */
+int kdbus_cmd_recv(struct kdbus_conn *conn, void __user *argp)
+{
+       struct kdbus_queue_entry *entry;
+       struct kdbus_cmd_recv *cmd;
+       int ret;
+
+       struct kdbus_arg argv[] = {
+               { .type = KDBUS_ITEM_NEGOTIATE },
+       };
+       struct kdbus_args args = {
+               .allowed_flags = KDBUS_FLAG_NEGOTIATE |
+                                KDBUS_RECV_PEEK |
+                                KDBUS_RECV_DROP |
+                                KDBUS_RECV_USE_PRIORITY,
+               .argv = argv,
+               .argc = ARRAY_SIZE(argv),
+       };
+
+       if (!kdbus_conn_is_ordinary(conn) &&
+           !kdbus_conn_is_monitor(conn) &&
+           !kdbus_conn_is_activator(conn))
+               return -EOPNOTSUPP;
+
+       ret = kdbus_args_parse(&args, argp, &cmd);
+       if (ret != 0)
+               return ret;
+
+       cmd->dropped_msgs = 0;
+       cmd->msg.return_flags = 0;
+       kdbus_pool_publish_empty(conn->pool, &cmd->msg.offset,
+                                &cmd->msg.msg_size);
+
+       /* DROP+priority is not realiably, so prevent it */
+       if ((cmd->flags & KDBUS_RECV_DROP) &&
+           (cmd->flags & KDBUS_RECV_USE_PRIORITY)) {
+               ret = -EINVAL;
+               goto exit;
+       }
+
+       mutex_lock(&conn->lock);
+
+       entry = kdbus_queue_peek(&conn->queue, cmd->priority,
+                                cmd->flags & KDBUS_RECV_USE_PRIORITY);
+       if (!entry) {
+               mutex_unlock(&conn->lock);
+               ret = -EAGAIN;
+       } else if (cmd->flags & KDBUS_RECV_DROP) {
+               struct kdbus_reply *reply = kdbus_reply_ref(entry->reply);
+
+               kdbus_queue_entry_free(entry);
+
+               mutex_unlock(&conn->lock);
+
+               if (reply) {
+                       mutex_lock(&reply->reply_dst->lock);
+                       if (!list_empty(&reply->entry)) {
+                               kdbus_reply_unlink(reply);
+                               if (reply->sync)
+                                       kdbus_sync_reply_wakeup(reply, -EPIPE);
+                               else
+                                       kdbus_notify_reply_dead(conn->ep->bus,
+                                                       reply->reply_dst->id,
+                                                       reply->cookie);
+                       }
+                       mutex_unlock(&reply->reply_dst->lock);
+                       kdbus_notify_flush(conn->ep->bus);
+               }
+
+               kdbus_reply_unref(reply);
+       } else {
+               bool install_fds;
+
+               /*
+                * PEEK just returns the location of the next message. Do not
+                * install FDs nor memfds nor anything else. The only
+                * information of interest should be the message header and
+                * metadata. Any FD numbers in the payload is undefined for
+                * PEEK'ed messages.
+                * Also make sure to never install fds into a connection that
+                * has refused to receive any. Ordinary connections will not get
+                * messages with FDs queued (the receiver will get -ECOMM), but
+                * eavesdroppers might.
+                */
+               install_fds = (conn->flags & KDBUS_HELLO_ACCEPT_FD) &&
+                             !(cmd->flags & KDBUS_RECV_PEEK);
+
+               ret = kdbus_queue_entry_install(entry,
+                                               &cmd->msg.return_flags,
+                                               install_fds);
+               if (ret < 0) {
+                       mutex_unlock(&conn->lock);
+                       goto exit;
+               }
+
+               kdbus_pool_slice_publish(entry->slice, &cmd->msg.offset,
+                                        &cmd->msg.msg_size);
+
+               if (!(cmd->flags & KDBUS_RECV_PEEK))
+                       kdbus_queue_entry_free(entry);
+
+               mutex_unlock(&conn->lock);
+       }
+
+       cmd->dropped_msgs = atomic_xchg(&conn->lost_count, 0);
+       if (cmd->dropped_msgs > 0)
+               cmd->return_flags |= KDBUS_RECV_RETURN_DROPPED_MSGS;
+
+       if (kdbus_member_set_user(&cmd->msg, argp, typeof(*cmd), msg) ||
+           kdbus_member_set_user(&cmd->dropped_msgs, argp, typeof(*cmd),
+                                 dropped_msgs))
+               ret = -EFAULT;
+
+exit:
+       return kdbus_args_clear(&args, ret);
+}
+
+/**
+ * kdbus_cmd_free() - handle KDBUS_CMD_FREE
+ * @conn:              connection to operate on
+ * @argp:              command payload
+ *
+ * Return: >=0 on success, negative error code on failure.
+ */
+int kdbus_cmd_free(struct kdbus_conn *conn, void __user *argp)
+{
+       struct kdbus_cmd_free *cmd;
+       int ret;
+
+       struct kdbus_arg argv[] = {
+               { .type = KDBUS_ITEM_NEGOTIATE },
+       };
+       struct kdbus_args args = {
+               .allowed_flags = KDBUS_FLAG_NEGOTIATE,
+               .argv = argv,
+               .argc = ARRAY_SIZE(argv),
+       };
+
+       if (!kdbus_conn_is_ordinary(conn) &&
+           !kdbus_conn_is_monitor(conn) &&
+           !kdbus_conn_is_activator(conn))
+               return -EOPNOTSUPP;
+
+       ret = kdbus_args_parse(&args, argp, &cmd);
+       if (ret != 0)
+               return ret;
+
+       ret = kdbus_pool_release_offset(conn->pool, cmd->offset);
+
+       return kdbus_args_clear(&args, ret);
+}
diff --git a/ipc/kdbus/connection.h b/ipc/kdbus/connection.h
new file mode 100644 (file)
index 0000000..1ad0820
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+ * 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
+ *
+ * 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_CONNECTION_H
+#define __KDBUS_CONNECTION_H
+
+#include <linux/atomic.h>
+#include <linux/kref.h>
+#include <linux/lockdep.h>
+#include <linux/path.h>
+
+#include "limits.h"
+#include "metadata.h"
+#include "pool.h"
+#include "queue.h"
+#include "util.h"
+
+#define KDBUS_HELLO_SPECIAL_CONN       (KDBUS_HELLO_ACTIVATOR | \
+                                        KDBUS_HELLO_POLICY_HOLDER | \
+                                        KDBUS_HELLO_MONITOR)
+
+struct kdbus_name_entry;
+struct kdbus_quota;
+struct kdbus_staging;
+
+/**
+ * struct kdbus_conn - connection to a bus
+ * @kref:              Reference count
+ * @active:            Active references to the connection
+ * @id:                        Connection ID
+ * @flags:             KDBUS_HELLO_* flags
+ * @attach_flags_send: KDBUS_ATTACH_* flags for sending
+ * @attach_flags_recv: KDBUS_ATTACH_* flags for receiving
+ * @description:       Human-readable connection description, used for
+ *                     debugging. This field is only set when the
+ *                     connection is created.
+ * @ep:                        The endpoint this connection belongs to
+ * @lock:              Connection data lock
+ * @hentry:            Entry in ID <-> connection map
+ * @ep_entry:          Entry in endpoint
+ * @monitor_entry:     Entry in monitor, if the connection is a monitor
+ * @reply_list:                List of connections this connection should
+ *                     reply to
+ * @work:              Delayed work to handle timeouts
+ *                     activator for
+ * @match_db:          Subscription filter to broadcast messages
+ * @meta_proc:         Process metadata of connection creator, or NULL
+ * @meta_fake:         Faked metadata, or NULL
+ * @pool:              The user's buffer to receive messages
+ * @user:              Owner of the connection
+ * @cred:              The credentials of the connection at creation time
+ * @pid:               Pid at creation time
+ * @root_path:         Root path at creation time
+ * @request_count:     Number of pending requests issued by this
+ *                     connection that are waiting for replies from
+ *                     other peers
+ * @lost_count:                Number of lost broadcast messages
+ * @wait:              Wake up this endpoint
+ * @queue:             The message queue associated with this connection
+ * @quota:             Array of per-user quota indexed by user->id
+ * @n_quota:           Number of elements in quota array
+ * @names_list:                List of well-known names
+ * @name_count:                Number of owned well-known names
+ * @privileged:                Whether this connection is privileged on the domain
+ * @owner:             Owned by the same user as the bus owner
+ */
+struct kdbus_conn {
+       struct kref kref;
+       atomic_t active;
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+       struct lockdep_map dep_map;
+#endif
+       u64 id;
+       u64 flags;
+       atomic64_t attach_flags_send;
+       atomic64_t attach_flags_recv;
+       const char *description;
+       struct kdbus_ep *ep;
+       struct mutex lock;
+       struct hlist_node hentry;
+       struct list_head ep_entry;
+       struct list_head monitor_entry;
+       struct list_head reply_list;
+       struct delayed_work work;
+       struct kdbus_match_db *match_db;
+       struct kdbus_meta_proc *meta_proc;
+       struct kdbus_meta_fake *meta_fake;
+       struct kdbus_pool *pool;
+       struct kdbus_user *user;
+       const struct cred *cred;
+       struct pid *pid;
+       struct path root_path;
+       atomic_t request_count;
+       atomic_t lost_count;
+       wait_queue_head_t wait;
+       struct kdbus_queue queue;
+
+       struct kdbus_quota *quota;
+       unsigned int n_quota;
+
+       /* protected by registry->rwlock */
+       struct list_head names_list;
+       unsigned int name_count;
+
+       bool privileged:1;
+       bool owner:1;
+};
+
+struct kdbus_conn *kdbus_conn_ref(struct kdbus_conn *conn);
+struct kdbus_conn *kdbus_conn_unref(struct kdbus_conn *conn);
+bool kdbus_conn_active(const struct kdbus_conn *conn);
+int kdbus_conn_acquire(struct kdbus_conn *conn);
+void kdbus_conn_release(struct kdbus_conn *conn);
+int kdbus_conn_disconnect(struct kdbus_conn *conn, bool ensure_queue_empty);
+bool kdbus_conn_has_name(struct kdbus_conn *conn, const char *name);
+int kdbus_conn_quota_inc(struct kdbus_conn *c, struct kdbus_user *u,
+                        size_t memory, size_t fds);
+void kdbus_conn_quota_dec(struct kdbus_conn *c, struct kdbus_user *u,
+                         size_t memory, size_t fds);
+void kdbus_conn_lost_message(struct kdbus_conn *c);
+int kdbus_conn_entry_insert(struct kdbus_conn *conn_src,
+                           struct kdbus_conn *conn_dst,
+                           struct kdbus_staging *staging,
+                           struct kdbus_reply *reply,
+                           const struct kdbus_name_entry *name);
+void kdbus_conn_move_messages(struct kdbus_conn *conn_dst,
+                             struct kdbus_conn *conn_src,
+                             u64 name_id);
+
+/* policy */
+bool kdbus_conn_policy_own_name(struct kdbus_conn *conn,
+                               const struct cred *conn_creds,
+                               const char *name);
+bool kdbus_conn_policy_talk(struct kdbus_conn *conn,
+                           const struct cred *conn_creds,
+                           struct kdbus_conn *to);
+bool kdbus_conn_policy_see_name_unlocked(struct kdbus_conn *conn,
+                                        const struct cred *curr_creds,
+                                        const char *name);
+bool kdbus_conn_policy_see_notification(struct kdbus_conn *conn,
+                                       const struct cred *curr_creds,
+                                       const struct kdbus_msg *msg);
+
+/* command dispatcher */
+struct kdbus_conn *kdbus_cmd_hello(struct kdbus_ep *ep, struct file *file,
+                                  void __user *argp);
+int kdbus_cmd_byebye_unlocked(struct kdbus_conn *conn, void __user *argp);
+int kdbus_cmd_conn_info(struct kdbus_conn *conn, void __user *argp);
+int kdbus_cmd_update(struct kdbus_conn *conn, void __user *argp);
+int kdbus_cmd_send(struct kdbus_conn *conn, struct file *f, void __user *argp);
+int kdbus_cmd_recv(struct kdbus_conn *conn, void __user *argp);
+int kdbus_cmd_free(struct kdbus_conn *conn, void __user *argp);
+
+/**
+ * kdbus_conn_is_ordinary() - Check if connection is ordinary
+ * @conn:              The connection to check
+ *
+ * Return: Non-zero if the connection is an ordinary connection
+ */
+static inline int kdbus_conn_is_ordinary(const struct kdbus_conn *conn)
+{
+       return !(conn->flags & KDBUS_HELLO_SPECIAL_CONN);
+}
+
+/**
+ * kdbus_conn_is_activator() - Check if connection is an activator
+ * @conn:              The connection to check
+ *
+ * Return: Non-zero if the connection is an activator
+ */
+static inline int kdbus_conn_is_activator(const struct kdbus_conn *conn)
+{
+       return conn->flags & KDBUS_HELLO_ACTIVATOR;
+}
+
+/**
+ * kdbus_conn_is_policy_holder() - Check if connection is a policy holder
+ * @conn:              The connection to check
+ *
+ * Return: Non-zero if the connection is a policy holder
+ */
+static inline int kdbus_conn_is_policy_holder(const struct kdbus_conn *conn)
+{
+       return conn->flags & KDBUS_HELLO_POLICY_HOLDER;
+}
+
+/**
+ * kdbus_conn_is_monitor() - Check if connection is a monitor
+ * @conn:              The connection to check
+ *
+ * Return: Non-zero if the connection is a monitor
+ */
+static inline int kdbus_conn_is_monitor(const struct kdbus_conn *conn)
+{
+       return conn->flags & KDBUS_HELLO_MONITOR;
+}
+
+/**
+ * kdbus_conn_lock2() - Lock two connections
+ * @a:         connection A to lock or NULL
+ * @b:         connection B to lock or NULL
+ *
+ * Lock two connections at once. As we need to have a stable locking order, we
+ * always lock the connection with lower memory address first.
+ */
+static inline void kdbus_conn_lock2(struct kdbus_conn *a, struct kdbus_conn *b)
+{
+       if (a < b) {
+               if (a)
+                       mutex_lock(&a->lock);
+               if (b && b != a)
+                       mutex_lock_nested(&b->lock, !!a);
+       } else {
+               if (b)
+                       mutex_lock(&b->lock);
+               if (a && a != b)
+                       mutex_lock_nested(&a->lock, !!b);
+       }
+}
+
+/**
+ * kdbus_conn_unlock2() - Unlock two connections
+ * @a:         connection A to unlock or NULL
+ * @b:         connection B to unlock or NULL
+ *
+ * Unlock two connections at once. See kdbus_conn_lock2().
+ */
+static inline void kdbus_conn_unlock2(struct kdbus_conn *a,
+                                     struct kdbus_conn *b)
+{
+       if (a)
+               mutex_unlock(&a->lock);
+       if (b && b != a)
+               mutex_unlock(&b->lock);
+}
+
+/**
+ * kdbus_conn_assert_active() - lockdep assert on active lock
+ * @conn:      connection that shall be active
+ *
+ * This verifies via lockdep that the caller holds an active reference to the
+ * given connection.
+ */
+static inline void kdbus_conn_assert_active(struct kdbus_conn *conn)
+{
+       lockdep_assert_held(conn);
+}
+
+#endif
diff --git a/ipc/kdbus/domain.c b/ipc/kdbus/domain.c
new file mode 100644 (file)
index 0000000..ac9f760
--- /dev/null
@@ -0,0 +1,296 @@
+/*
+ * 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/idr.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#include "bus.h"
+#include "domain.h"
+#include "handle.h"
+#include "item.h"
+#include "limits.h"
+#include "util.h"
+
+static void kdbus_domain_control_free(struct kdbus_node *node)
+{
+       kfree(node);
+}
+
+static struct kdbus_node *kdbus_domain_control_new(struct kdbus_domain *domain,
+                                                  unsigned int access)
+{
+       struct kdbus_node *node;
+       int ret;
+
+       node = kzalloc(sizeof(*node), GFP_KERNEL);
+       if (!node)
+               return ERR_PTR(-ENOMEM);
+
+       kdbus_node_init(node, KDBUS_NODE_CONTROL);
+
+       node->free_cb = kdbus_domain_control_free;
+       node->mode = domain->node.mode;
+       node->mode = S_IRUSR | S_IWUSR;
+       if (access & (KDBUS_MAKE_ACCESS_GROUP | KDBUS_MAKE_ACCESS_WORLD))
+               node->mode |= S_IRGRP | S_IWGRP;
+       if (access & KDBUS_MAKE_ACCESS_WORLD)
+               node->mode |= S_IROTH | S_IWOTH;
+
+       ret = kdbus_node_link(node, &domain->node, "control");
+       if (ret < 0)
+               goto exit_free;
+
+       return node;
+
+exit_free:
+       kdbus_node_deactivate(node);
+       kdbus_node_unref(node);
+       return ERR_PTR(ret);
+}
+
+static void kdbus_domain_free(struct kdbus_node *node)
+{
+       struct kdbus_domain *domain =
+               container_of(node, struct kdbus_domain, node);
+
+       put_user_ns(domain->user_namespace);
+       ida_destroy(&domain->user_ida);
+       idr_destroy(&domain->user_idr);
+       kfree(domain);
+}
+
+/**
+ * kdbus_domain_new() - create a new domain
+ * @access:            The access mode for this node (KDBUS_MAKE_ACCESS_*)
+ *
+ * Return: a new kdbus_domain on success, ERR_PTR on failure
+ */
+struct kdbus_domain *kdbus_domain_new(unsigned int access)
+{
+       struct kdbus_domain *d;
+       int ret;
+
+       d = kzalloc(sizeof(*d), GFP_KERNEL);
+       if (!d)
+               return ERR_PTR(-ENOMEM);
+
+       kdbus_node_init(&d->node, KDBUS_NODE_DOMAIN);
+
+       d->node.free_cb = kdbus_domain_free;
+       d->node.mode = S_IRUSR | S_IXUSR;
+       if (access & (KDBUS_MAKE_ACCESS_GROUP | KDBUS_MAKE_ACCESS_WORLD))
+               d->node.mode |= S_IRGRP | S_IXGRP;
+       if (access & KDBUS_MAKE_ACCESS_WORLD)
+               d->node.mode |= S_IROTH | S_IXOTH;
+
+       mutex_init(&d->lock);
+       idr_init(&d->user_idr);
+       ida_init(&d->user_ida);
+
+       /* Pin user namespace so we can guarantee domain-unique bus * names. */
+       d->user_namespace = get_user_ns(current_user_ns());
+
+       ret = kdbus_node_link(&d->node, NULL, NULL);
+       if (ret < 0)
+               goto exit_unref;
+
+       return d;
+
+exit_unref:
+       kdbus_node_deactivate(&d->node);
+       kdbus_node_unref(&d->node);
+       return ERR_PTR(ret);
+}
+
+/**
+ * kdbus_domain_ref() - take a domain reference
+ * @domain:            Domain
+ *
+ * Return: the domain itself
+ */
+struct kdbus_domain *kdbus_domain_ref(struct kdbus_domain *domain)
+{
+       if (domain)
+               kdbus_node_ref(&domain->node);
+       return domain;
+}
+
+/**
+ * kdbus_domain_unref() - drop a domain reference
+ * @domain:            Domain
+ *
+ * When the last reference is dropped, the domain internal structure
+ * is freed.
+ *
+ * Return: NULL
+ */
+struct kdbus_domain *kdbus_domain_unref(struct kdbus_domain *domain)
+{
+       if (domain)
+               kdbus_node_unref(&domain->node);
+       return NULL;
+}
+
+/**
+ * kdbus_domain_populate() - populate static domain nodes
+ * @domain:    domain to populate
+ * @access:    KDBUS_MAKE_ACCESS_* access restrictions for new nodes
+ *
+ * Allocate and activate static sub-nodes of the given domain. This will fail if
+ * you call it on a non-active node or if the domain was already populated.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int kdbus_domain_populate(struct kdbus_domain *domain, unsigned int access)
+{
+       struct kdbus_node *control;
+
+       /*
+        * Create a control-node for this domain. We drop our own reference
+        * immediately, effectively causing the node to be deactivated and
+        * released when the parent domain is.
+        */
+       control = kdbus_domain_control_new(domain, access);
+       if (IS_ERR(control))
+               return PTR_ERR(control);
+
+       kdbus_node_activate(control);
+       kdbus_node_unref(control);
+       return 0;
+}
+
+/**
+ * kdbus_user_lookup() - lookup a kdbus_user object
+ * @domain:            domain of the user
+ * @uid:               uid of the user; INVALID_UID for an anon user
+ *
+ * Lookup the kdbus user accounting object for the given domain. If INVALID_UID
+ * is passed, a new anonymous user is created which is private to the caller.
+ *
+ * Return: The user object is returned, ERR_PTR on failure.
+ */
+struct kdbus_user *kdbus_user_lookup(struct kdbus_domain *domain, kuid_t uid)
+{
+       struct kdbus_user *u = NULL, *old = NULL;
+       int ret;
+
+       mutex_lock(&domain->lock);
+
+       if (uid_valid(uid)) {
+               old = idr_find(&domain->user_idr, __kuid_val(uid));
+               /*
+                * If the object is about to be destroyed, ignore it and
+                * replace the slot in the IDR later on.
+                */
+               if (old && kref_get_unless_zero(&old->kref)) {
+                       mutex_unlock(&domain->lock);
+                       return old;
+               }
+       }
+
+       u = kzalloc(sizeof(*u), GFP_KERNEL);
+       if (!u) {
+               ret = -ENOMEM;
+               goto exit;
+       }
+
+       kref_init(&u->kref);
+       u->domain = kdbus_domain_ref(domain);
+       u->uid = uid;
+       atomic_set(&u->buses, 0);
+       atomic_set(&u->connections, 0);
+
+       if (uid_valid(uid)) {
+               if (old) {
+                       idr_replace(&domain->user_idr, u, __kuid_val(uid));
+                       old->uid = INVALID_UID; /* mark old as removed */
+               } else {
+                       ret = idr_alloc(&domain->user_idr, u, __kuid_val(uid),
+                                       __kuid_val(uid) + 1, GFP_KERNEL);
+                       if (ret < 0)
+                               goto exit;
+               }
+       }
+
+       /*
+        * Allocate the smallest possible index for this user; used
+        * in arrays for accounting user quota in receiver queues.
+        */
+       ret = ida_simple_get(&domain->user_ida, 1, 0, GFP_KERNEL);
+       if (ret < 0)
+               goto exit;
+
+       u->id = ret;
+       mutex_unlock(&domain->lock);
+       return u;
+
+exit:
+       if (u) {
+               if (uid_valid(u->uid))
+                       idr_remove(&domain->user_idr, __kuid_val(u->uid));
+               kdbus_domain_unref(u->domain);
+               kfree(u);
+       }
+       mutex_unlock(&domain->lock);
+       return ERR_PTR(ret);
+}
+
+static void __kdbus_user_free(struct kref *kref)
+{
+       struct kdbus_user *user = container_of(kref, struct kdbus_user, kref);
+
+       WARN_ON(atomic_read(&user->buses) > 0);
+       WARN_ON(atomic_read(&user->connections) > 0);
+
+       mutex_lock(&user->domain->lock);
+       ida_simple_remove(&user->domain->user_ida, user->id);
+       if (uid_valid(user->uid))
+               idr_remove(&user->domain->user_idr, __kuid_val(user->uid));
+       mutex_unlock(&user->domain->lock);
+
+       kdbus_domain_unref(user->domain);
+       kfree(user);
+}
+
+/**
+ * kdbus_user_ref() - take a user reference
+ * @u:         User
+ *
+ * Return: @u is returned
+ */
+struct kdbus_user *kdbus_user_ref(struct kdbus_user *u)
+{
+       if (u)
+               kref_get(&u->kref);
+       return u;
+}
+
+/**
+ * kdbus_user_unref() - drop a user reference
+ * @u:         User
+ *
+ * Return: NULL
+ */
+struct kdbus_user *kdbus_user_unref(struct kdbus_user *u)
+{
+       if (u)
+               kref_put(&u->kref, __kdbus_user_free);
+       return NULL;
+}
diff --git a/ipc/kdbus/domain.h b/ipc/kdbus/domain.h
new file mode 100644 (file)
index 0000000..447a2bd
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+
+#ifndef __KDBUS_DOMAIN_H
+#define __KDBUS_DOMAIN_H
+
+#include <linux/fs.h>
+#include <linux/idr.h>
+#include <linux/kref.h>
+#include <linux/user_namespace.h>
+
+#include "node.h"
+
+/**
+ * struct kdbus_domain - domain for buses
+ * @node:              Underlying API node
+ * @lock:              Domain data lock
+ * @last_id:           Last used object id
+ * @user_idr:          Set of all users indexed by UID
+ * @user_ida:          Set of all users to compute small indices
+ * @user_namespace:    User namespace, pinned at creation time
+ * @dentry:            Root dentry of VFS mount (don't use outside of kdbusfs)
+ */
+struct kdbus_domain {
+       struct kdbus_node node;
+       struct mutex lock;
+       atomic64_t last_id;
+       struct idr user_idr;
+       struct ida user_ida;
+       struct user_namespace *user_namespace;
+       struct dentry *dentry;
+};
+
+/**
+ * struct kdbus_user - resource accounting for users
+ * @kref:              Reference counter
+ * @domain:            Domain of the user
+ * @id:                        Index of this user
+ * @uid:               UID of the user
+ * @buses:             Number of buses the user has created
+ * @connections:       Number of connections the user has created
+ */
+struct kdbus_user {
+       struct kref kref;
+       struct kdbus_domain *domain;
+       unsigned int id;
+       kuid_t uid;
+       atomic_t buses;
+       atomic_t connections;
+};
+
+#define kdbus_domain_from_node(_node) \
+       container_of((_node), struct kdbus_domain, node)
+
+struct kdbus_domain *kdbus_domain_new(unsigned int access);
+struct kdbus_domain *kdbus_domain_ref(struct kdbus_domain *domain);
+struct kdbus_domain *kdbus_domain_unref(struct kdbus_domain *domain);
+int kdbus_domain_populate(struct kdbus_domain *domain, unsigned int access);
+
+#define KDBUS_USER_KERNEL_ID 0 /* ID 0 is reserved for kernel accounting */
+
+struct kdbus_user *kdbus_user_lookup(struct kdbus_domain *domain, kuid_t uid);
+struct kdbus_user *kdbus_user_ref(struct kdbus_user *u);
+struct kdbus_user *kdbus_user_unref(struct kdbus_user *u);
+
+#endif
diff --git a/ipc/kdbus/endpoint.c b/ipc/kdbus/endpoint.c
new file mode 100644 (file)
index 0000000..44e7a20
--- /dev/null
@@ -0,0 +1,303 @@
+/*
+ * 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/idr.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/uio.h>
+
+#include "bus.h"
+#include "connection.h"
+#include "domain.h"
+#include "endpoint.h"
+#include "handle.h"
+#include "item.h"
+#include "message.h"
+#include "policy.h"
+
+static void kdbus_ep_free(struct kdbus_node *node)
+{
+       struct kdbus_ep *ep = container_of(node, struct kdbus_ep, node);
+
+       WARN_ON(!list_empty(&ep->conn_list));
+
+       kdbus_policy_db_clear(&ep->policy_db);
+       kdbus_bus_unref(ep->bus);
+       kdbus_user_unref(ep->user);
+       kfree(ep);
+}
+
+static void kdbus_ep_release(struct kdbus_node *node, bool was_active)
+{
+       struct kdbus_ep *ep = container_of(node, struct kdbus_ep, node);
+
+       /* disconnect all connections to this endpoint */
+       for (;;) {
+               struct kdbus_conn *conn;
+
+               mutex_lock(&ep->lock);
+               conn = list_first_entry_or_null(&ep->conn_list,
+                                               struct kdbus_conn,
+                                               ep_entry);
+               if (!conn) {
+                       mutex_unlock(&ep->lock);
+                       break;
+               }
+
+               /* take reference, release lock, disconnect without lock */
+               kdbus_conn_ref(conn);
+               mutex_unlock(&ep->lock);
+
+               kdbus_conn_disconnect(conn, false);
+               kdbus_conn_unref(conn);
+       }
+}
+
+/**
+ * kdbus_ep_new() - create a new endpoint
+ * @bus:               The bus this endpoint will be created for
+ * @name:              The name of the endpoint
+ * @access:            The access flags for this node (KDBUS_MAKE_ACCESS_*)
+ * @uid:               The uid of the node
+ * @gid:               The gid of the node
+ * @is_custom:         Whether this is a custom endpoint
+ *
+ * This function will create a new endpoint with the given
+ * name and properties for a given bus.
+ *
+ * Return: a new kdbus_ep on success, ERR_PTR on failure.
+ */
+struct kdbus_ep *kdbus_ep_new(struct kdbus_bus *bus, const char *name,
+                             unsigned int access, kuid_t uid, kgid_t gid,
+                             bool is_custom)
+{
+       struct kdbus_ep *e;
+       int ret;
+
+       /*
+        * Validate only custom endpoints names, default endpoints
+        * with a "bus" name are created when the bus is created
+        */
+       if (is_custom) {
+               ret = kdbus_verify_uid_prefix(name, bus->domain->user_namespace,
+                                             uid);
+               if (ret < 0)
+                       return ERR_PTR(ret);
+       }
+
+       e = kzalloc(sizeof(*e), GFP_KERNEL);
+       if (!e)
+               return ERR_PTR(-ENOMEM);
+
+       kdbus_node_init(&e->node, KDBUS_NODE_ENDPOINT);
+
+       e->node.free_cb = kdbus_ep_free;
+       e->node.release_cb = kdbus_ep_release;
+       e->node.uid = uid;
+       e->node.gid = gid;
+       e->node.mode = S_IRUSR | S_IWUSR;
+       if (access & (KDBUS_MAKE_ACCESS_GROUP | KDBUS_MAKE_ACCESS_WORLD))
+               e->node.mode |= S_IRGRP | S_IWGRP;
+       if (access & KDBUS_MAKE_ACCESS_WORLD)
+               e->node.mode |= S_IROTH | S_IWOTH;
+
+       mutex_init(&e->lock);
+       INIT_LIST_HEAD(&e->conn_list);
+       kdbus_policy_db_init(&e->policy_db);
+       e->bus = kdbus_bus_ref(bus);
+
+       ret = kdbus_node_link(&e->node, &bus->node, name);
+       if (ret < 0)
+               goto exit_unref;
+
+       /*
+        * Transactions on custom endpoints are never accounted on the global
+        * user limits. Instead, for each custom endpoint, we create a custom,
+        * unique user, which all transactions are accounted on. Regardless of
+        * the user using that endpoint, it is always accounted on the same
+        * user-object. This budget is not shared with ordinary users on
+        * non-custom endpoints.
+        */
+       if (is_custom) {
+               e->user = kdbus_user_lookup(bus->domain, INVALID_UID);
+               if (IS_ERR(e->user)) {
+                       ret = PTR_ERR(e->user);
+                       e->user = NULL;
+                       goto exit_unref;
+               }
+       }
+
+       return e;
+
+exit_unref:
+       kdbus_node_deactivate(&e->node);
+       kdbus_node_unref(&e->node);
+       return ERR_PTR(ret);
+}
+
+/**
+ * kdbus_ep_ref() - increase the reference counter of a kdbus_ep
+ * @ep:                        The endpoint to reference
+ *
+ * Every user of an endpoint, except for its creator, must add a reference to
+ * the kdbus_ep instance using this function.
+ *
+ * Return: the ep itself
+ */
+struct kdbus_ep *kdbus_ep_ref(struct kdbus_ep *ep)
+{
+       if (ep)
+               kdbus_node_ref(&ep->node);
+       return ep;
+}
+
+/**
+ * kdbus_ep_unref() - decrease the reference counter of a kdbus_ep
+ * @ep:                The ep to unref
+ *
+ * Release a reference. If the reference count drops to 0, the ep will be
+ * freed.
+ *
+ * Return: NULL
+ */
+struct kdbus_ep *kdbus_ep_unref(struct kdbus_ep *ep)
+{
+       if (ep)
+               kdbus_node_unref(&ep->node);
+       return NULL;
+}
+
+/**
+ * kdbus_ep_is_privileged() - check whether a file is privileged
+ * @ep:                endpoint to operate on
+ * @file:      file to test
+ *
+ * Return: True if @file is privileged in the domain of @ep.
+ */
+bool kdbus_ep_is_privileged(struct kdbus_ep *ep, struct file *file)
+{
+       return !ep->user &&
+               file_ns_capable(file, ep->bus->domain->user_namespace,
+                               CAP_IPC_OWNER);
+}
+
+/**
+ * kdbus_ep_is_owner() - check whether a file should be treated as bus owner
+ * @ep:                endpoint to operate on
+ * @file:      file to test
+ *
+ * Return: True if @file should be treated as bus owner on @ep
+ */
+bool kdbus_ep_is_owner(struct kdbus_ep *ep, struct file *file)
+{
+       return !ep->user &&
+               (uid_eq(file->f_cred->euid, ep->bus->node.uid) ||
+                kdbus_ep_is_privileged(ep, file));
+}
+
+/**
+ * kdbus_cmd_ep_make() - handle KDBUS_CMD_ENDPOINT_MAKE
+ * @bus:               bus to operate on
+ * @argp:              command payload
+ *
+ * Return: NULL or newly created endpoint on success, ERR_PTR on failure.
+ */
+struct kdbus_ep *kdbus_cmd_ep_make(struct kdbus_bus *bus, void __user *argp)
+{
+       const char *item_make_name;
+       struct kdbus_ep *ep = NULL;
+       struct kdbus_cmd *cmd;
+       int ret;
+
+       struct kdbus_arg argv[] = {
+               { .type = KDBUS_ITEM_NEGOTIATE },
+               { .type = KDBUS_ITEM_MAKE_NAME, .mandatory = true },
+       };
+       struct kdbus_args args = {
+               .allowed_flags = KDBUS_FLAG_NEGOTIATE |
+                                KDBUS_MAKE_ACCESS_GROUP |
+                                KDBUS_MAKE_ACCESS_WORLD,
+               .argv = argv,
+               .argc = ARRAY_SIZE(argv),
+       };
+
+       ret = kdbus_args_parse(&args, argp, &cmd);
+       if (ret < 0)
+               return ERR_PTR(ret);
+       if (ret > 0)
+               return NULL;
+
+       item_make_name = argv[1].item->str;
+
+       ep = kdbus_ep_new(bus, item_make_name, cmd->flags,
+                         current_euid(), current_egid(), true);
+       if (IS_ERR(ep)) {
+               ret = PTR_ERR(ep);
+               ep = NULL;
+               goto exit;
+       }
+
+       if (!kdbus_node_activate(&ep->node)) {
+               ret = -ESHUTDOWN;
+               goto exit;
+       }
+
+exit:
+       ret = kdbus_args_clear(&args, ret);
+       if (ret < 0) {
+               if (ep) {
+                       kdbus_node_deactivate(&ep->node);
+                       kdbus_ep_unref(ep);
+               }
+               return ERR_PTR(ret);
+       }
+       return ep;
+}
+
+/**
+ * kdbus_cmd_ep_update() - handle KDBUS_CMD_ENDPOINT_UPDATE
+ * @ep:                        endpoint to operate on
+ * @argp:              command payload
+ *
+ * Return: >=0 on success, negative error code on failure.
+ */
+int kdbus_cmd_ep_update(struct kdbus_ep *ep, void __user *argp)
+{
+       struct kdbus_cmd *cmd;
+       int ret;
+
+       struct kdbus_arg argv[] = {
+               { .type = KDBUS_ITEM_NEGOTIATE },
+               { .type = KDBUS_ITEM_NAME, .multiple = true },
+               { .type = KDBUS_ITEM_POLICY_ACCESS, .multiple = true },
+       };
+       struct kdbus_args args = {
+               .allowed_flags = KDBUS_FLAG_NEGOTIATE,
+               .argv = argv,
+               .argc = ARRAY_SIZE(argv),
+       };
+
+       ret = kdbus_args_parse(&args, argp, &cmd);
+       if (ret != 0)
+               return ret;
+
+       ret = kdbus_policy_set(&ep->policy_db, args.items, args.items_size,
+                              0, true, ep);
+       return kdbus_args_clear(&args, ret);
+}
diff --git a/ipc/kdbus/endpoint.h b/ipc/kdbus/endpoint.h
new file mode 100644 (file)
index 0000000..e0da59f
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+#ifndef __KDBUS_ENDPOINT_H
+#define __KDBUS_ENDPOINT_H
+
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/uidgid.h>
+#include "node.h"
+#include "policy.h"
+
+struct kdbus_bus;
+struct kdbus_user;
+
+/**
+ * struct kdbus_ep - endpoint to access a bus
+ * @node:              The kdbus node
+ * @lock:              Endpoint data lock
+ * @bus:               Bus behind this endpoint
+ * @user:              Custom enpoints account against an anonymous user
+ * @policy_db:         Uploaded policy
+ * @conn_list:         Connections of this endpoint
+ *
+ * An endpoint offers access to a bus; the default endpoint node name is "bus".
+ * Additional custom endpoints to the same bus can be created and they can
+ * carry their own policies/filters.
+ */
+struct kdbus_ep {
+       struct kdbus_node node;
+       struct mutex lock;
+
+       /* static */
+       struct kdbus_bus *bus;
+       struct kdbus_user *user;
+
+       /* protected by own locks */
+       struct kdbus_policy_db policy_db;
+
+       /* protected by ep->lock */
+       struct list_head conn_list;
+};
+
+#define kdbus_ep_from_node(_node) \
+       container_of((_node), struct kdbus_ep, node)
+
+struct kdbus_ep *kdbus_ep_new(struct kdbus_bus *bus, const char *name,
+                             unsigned int access, kuid_t uid, kgid_t gid,
+                             bool policy);
+struct kdbus_ep *kdbus_ep_ref(struct kdbus_ep *ep);
+struct kdbus_ep *kdbus_ep_unref(struct kdbus_ep *ep);
+
+bool kdbus_ep_is_privileged(struct kdbus_ep *ep, struct file *file);
+bool kdbus_ep_is_owner(struct kdbus_ep *ep, struct file *file);
+
+struct kdbus_ep *kdbus_cmd_ep_make(struct kdbus_bus *bus, void __user *argp);
+int kdbus_cmd_ep_update(struct kdbus_ep *ep, void __user *argp);
+
+#endif
diff --git a/ipc/kdbus/fs.c b/ipc/kdbus/fs.c
new file mode 100644 (file)
index 0000000..09c4809
--- /dev/null
@@ -0,0 +1,508 @@
+/*
+ * 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
+ *
+ * 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/dcache.h>
+#include <linux/fs.h>
+#include <linux/fsnotify.h>
+#include <linux/init.h>
+#include <linux/ipc_namespace.h>
+#include <linux/magic.h>
+#include <linux/module.h>
+#include <linux/mount.h>
+#include <linux/mutex.h>
+#include <linux/namei.h>
+#include <linux/pagemap.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include "bus.h"
+#include "domain.h"
+#include "endpoint.h"
+#include "fs.h"
+#include "handle.h"
+#include "node.h"
+
+#define kdbus_node_from_dentry(_dentry) \
+       ((struct kdbus_node *)(_dentry)->d_fsdata)
+
+static struct inode *fs_inode_get(struct super_block *sb,
+                                 struct kdbus_node *node);
+
+/*
+ * Directory Management
+ */
+
+static inline unsigned char kdbus_dt_type(struct kdbus_node *node)
+{
+       switch (node->type) {
+       case KDBUS_NODE_DOMAIN:
+       case KDBUS_NODE_BUS:
+               return DT_DIR;
+       case KDBUS_NODE_CONTROL:
+       case KDBUS_NODE_ENDPOINT:
+               return DT_REG;
+       }
+
+       return DT_UNKNOWN;
+}
+
+static int fs_dir_fop_iterate(struct file *file, struct dir_context *ctx)
+{
+       struct dentry *dentry = file->f_path.dentry;
+       struct kdbus_node *parent = kdbus_node_from_dentry(dentry);
+       struct kdbus_node *old, *next = file->private_data;
+
+       /*
+        * kdbusfs directory iterator (modelled after sysfs/kernfs)
+        * When iterating kdbusfs directories, we iterate all children of the
+        * parent kdbus_node object. We use ctx->pos to store the hash of the
+        * child and file->private_data to store a reference to the next node
+        * object. If ctx->pos is not modified via llseek while you iterate a
+        * directory, then we use the file->private_data node pointer to
+        * directly access the next node in the tree.
+        * However, if you directly seek on the directory, we have to find the
+        * closest node to that position and cannot use our node pointer. This
+        * means iterating the rb-tree to find the closest match and start over
+        * from there.
+        * Note that hash values are not necessarily unique. Therefore, llseek
+        * is not guaranteed to seek to the same node that you got when you
+        * retrieved the position. Seeking to 0, 1, 2 and >=INT_MAX is safe,
+        * though. We could use the inode-number as position, but this would
+        * require another rb-tree for fast access. Kernfs and others already
+        * ignore those conflicts, so we should be fine, too.
+        */
+
+       if (!dir_emit_dots(file, ctx))
+               return 0;
+
+       /* acquire @next; if deactivated, or seek detected, find next node */
+       old = next;
+       if (next && ctx->pos == next->hash) {
+               if (kdbus_node_acquire(next))
+                       kdbus_node_ref(next);
+               else
+                       next = kdbus_node_next_child(parent, next);
+       } else {
+               next = kdbus_node_find_closest(parent, ctx->pos);
+       }
+       kdbus_node_unref(old);
+
+       while (next) {
+               /* emit @next */
+               file->private_data = next;
+               ctx->pos = next->hash;
+
+               kdbus_node_release(next);
+
+               if (!dir_emit(ctx, next->name, strlen(next->name), next->id,
+                             kdbus_dt_type(next)))
+                       return 0;
+
+               /* find next node after @next */
+               old = next;
+               next = kdbus_node_next_child(parent, next);
+               kdbus_node_unref(old);
+       }
+
+       file->private_data = NULL;
+       ctx->pos = INT_MAX;
+
+       return 0;
+}
+
+static loff_t fs_dir_fop_llseek(struct file *file, loff_t offset, int whence)
+{
+       struct inode *inode = file_inode(file);
+       loff_t ret;
+
+       /* protect f_off against fop_iterate */
+       mutex_lock(&inode->i_mutex);
+       ret = generic_file_llseek(file, offset, whence);
+       mutex_unlock(&inode->i_mutex);
+
+       return ret;
+}
+
+static int fs_dir_fop_release(struct inode *inode, struct file *file)
+{
+       kdbus_node_unref(file->private_data);
+       return 0;
+}
+
+static const struct file_operations fs_dir_fops = {
+       .read           = generic_read_dir,
+       .iterate        = fs_dir_fop_iterate,
+       .llseek         = fs_dir_fop_llseek,
+       .release        = fs_dir_fop_release,
+};
+
+static struct dentry *fs_dir_iop_lookup(struct inode *dir,
+                                       struct dentry *dentry,
+                                       unsigned int flags)
+{
+       struct dentry *dnew = NULL;
+       struct kdbus_node *parent;
+       struct kdbus_node *node;
+       struct inode *inode;
+
+       parent = kdbus_node_from_dentry(dentry->d_parent);
+       if (!kdbus_node_acquire(parent))
+               return NULL;
+
+       /* returns reference to _acquired_ child node */
+       node = kdbus_node_find_child(parent, dentry->d_name.name);
+       if (node) {
+               dentry->d_fsdata = node;
+               inode = fs_inode_get(dir->i_sb, node);
+               if (IS_ERR(inode))
+                       dnew = ERR_CAST(inode);
+               else
+                       dnew = d_splice_alias(inode, dentry);
+
+               kdbus_node_release(node);
+       }
+
+       kdbus_node_release(parent);
+       return dnew;
+}
+
+static const struct inode_operations fs_dir_iops = {
+       .permission     = generic_permission,
+       .lookup         = fs_dir_iop_lookup,
+};
+
+/*
+ * Inode Management
+ */
+
+static const struct inode_operations fs_inode_iops = {
+       .permission     = generic_permission,
+};
+
+static struct inode *fs_inode_get(struct super_block *sb,
+                                 struct kdbus_node *node)
+{
+       struct inode *inode;
+
+       inode = iget_locked(sb, node->id);
+       if (!inode)
+               return ERR_PTR(-ENOMEM);
+       if (!(inode->i_state & I_NEW))
+               return inode;
+
+       inode->i_private = kdbus_node_ref(node);
+       inode->i_mapping->a_ops = &empty_aops;
+       inode->i_mode = node->mode & S_IALLUGO;
+       inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+       inode->i_uid = node->uid;
+       inode->i_gid = node->gid;
+
+       switch (node->type) {
+       case KDBUS_NODE_DOMAIN:
+       case KDBUS_NODE_BUS:
+               inode->i_mode |= S_IFDIR;
+               inode->i_op = &fs_dir_iops;
+               inode->i_fop = &fs_dir_fops;
+               set_nlink(inode, 2);
+               break;
+       case KDBUS_NODE_CONTROL:
+       case KDBUS_NODE_ENDPOINT:
+               inode->i_mode |= S_IFREG;
+               inode->i_op = &fs_inode_iops;
+               inode->i_fop = &kdbus_handle_ops;
+               break;
+       }
+
+       unlock_new_inode(inode);
+
+       return inode;
+}
+
+/*
+ * Superblock Management
+ */
+
+static int fs_super_dop_revalidate(struct dentry *dentry, unsigned int flags)
+{
+       struct kdbus_node *node;
+
+       /* Force lookup on negatives */
+       if (!dentry->d_inode)
+               return 0;
+
+       node = kdbus_node_from_dentry(dentry);
+
+       /* see whether the node has been removed */
+       if (!kdbus_node_is_active(node))
+               return 0;
+
+       return 1;
+}
+
+static void fs_super_dop_release(struct dentry *dentry)
+{
+       kdbus_node_unref(dentry->d_fsdata);
+}
+
+static const struct dentry_operations fs_super_dops = {
+       .d_revalidate   = fs_super_dop_revalidate,
+       .d_release      = fs_super_dop_release,
+};
+
+static void fs_super_sop_evict_inode(struct inode *inode)
+{
+       struct kdbus_node *node = kdbus_node_from_inode(inode);
+
+       truncate_inode_pages_final(&inode->i_data);
+       clear_inode(inode);
+       kdbus_node_unref(node);
+}
+
+static const struct super_operations fs_super_sops = {
+       .statfs         = simple_statfs,
+       .drop_inode     = generic_delete_inode,
+       .evict_inode    = fs_super_sop_evict_inode,
+};
+
+static int fs_super_fill(struct super_block *sb)
+{
+       struct kdbus_domain *domain = sb->s_fs_info;
+       struct inode *inode;
+       int ret;
+
+       sb->s_blocksize = PAGE_CACHE_SIZE;
+       sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
+       sb->s_magic = KDBUS_SUPER_MAGIC;
+       sb->s_maxbytes = MAX_LFS_FILESIZE;
+       sb->s_op = &fs_super_sops;
+       sb->s_time_gran = 1;
+
+       inode = fs_inode_get(sb, &domain->node);
+       if (IS_ERR(inode))
+               return PTR_ERR(inode);
+
+       sb->s_root = d_make_root(inode);
+       if (!sb->s_root) {
+               /* d_make_root iput()s the inode on failure */
+               return -ENOMEM;
+       }
+
+       /* sb holds domain reference */
+       sb->s_root->d_fsdata = &domain->node;
+       sb->s_d_op = &fs_super_dops;
+
+       /* sb holds root reference */
+       domain->dentry = sb->s_root;
+
+       if (!kdbus_node_activate(&domain->node))
+               return -ESHUTDOWN;
+
+       ret = kdbus_domain_populate(domain, KDBUS_MAKE_ACCESS_WORLD);
+       if (ret < 0)
+               return ret;
+
+       sb->s_flags |= MS_ACTIVE;
+       return 0;
+}
+
+static void fs_super_kill(struct super_block *sb)
+{
+       struct kdbus_domain *domain = sb->s_fs_info;
+
+       if (domain) {
+               kdbus_node_deactivate(&domain->node);
+               domain->dentry = NULL;
+       }
+
+       kill_anon_super(sb);
+       kdbus_domain_unref(domain);
+}
+
+static int fs_super_set(struct super_block *sb, void *data)
+{
+       int ret;
+
+       ret = set_anon_super(sb, data);
+       if (!ret)
+               sb->s_fs_info = data;
+
+       return ret;
+}
+
+static struct dentry *fs_super_mount(struct file_system_type *fs_type,
+                                    int flags, const char *dev_name,
+                                    void *data)
+{
+       struct kdbus_domain *domain;
+       struct super_block *sb;
+       int ret;
+
+       domain = kdbus_domain_new(KDBUS_MAKE_ACCESS_WORLD);
+       if (IS_ERR(domain))
+               return ERR_CAST(domain);
+
+       sb = sget(fs_type, NULL, fs_super_set, flags, domain);
+       if (IS_ERR(sb)) {
+               kdbus_node_deactivate(&domain->node);
+               kdbus_domain_unref(domain);
+               return ERR_CAST(sb);
+       }
+
+       WARN_ON(sb->s_fs_info != domain);
+       WARN_ON(sb->s_root);
+
+       ret = fs_super_fill(sb);
+       if (ret < 0) {
+               /* calls into ->kill_sb() when done */
+               deactivate_locked_super(sb);
+               return ERR_PTR(ret);
+       }
+
+       return dget(sb->s_root);
+}
+
+static struct file_system_type fs_type = {
+       .name           = KBUILD_MODNAME "fs",
+       .owner          = THIS_MODULE,
+       .mount          = fs_super_mount,
+       .kill_sb        = fs_super_kill,
+       .fs_flags       = FS_USERNS_MOUNT,
+};
+
+/**
+ * kdbus_fs_init() - register kdbus filesystem
+ *
+ * This registers a filesystem with the VFS layer. The filesystem is called
+ * `KBUILD_MODNAME "fs"', which usually resolves to `kdbusfs'. The nameing
+ * scheme allows to set KBUILD_MODNAME to "kdbus2" and you will get an
+ * independent filesystem for developers.
+ *
+ * Each mount of the kdbusfs filesystem has an kdbus_domain attached.
+ * Operations on this mount will only affect the attached domain. On each mount
+ * a new domain is automatically created and used for this mount exclusively.
+ * If you want to share a domain across multiple mounts, you need to bind-mount
+ * it.
+ *
+ * Mounts of kdbusfs (with a different domain each) are unrelated to each other
+ * and will never have any effect on any domain but their own.
+ *
+ * Return: 0 on success, negative error otherwise.
+ */
+int kdbus_fs_init(void)
+{
+       return register_filesystem(&fs_type);
+}
+
+/**
+ * kdbus_fs_exit() - unregister kdbus filesystem
+ *
+ * This does the reverse to kdbus_fs_init(). It unregisters the kdbusfs
+ * filesystem from VFS and cleans up any allocated resources.
+ */
+void kdbus_fs_exit(void)
+{
+       unregister_filesystem(&fs_type);
+}
+
+/* acquire domain of @node, making sure all ancestors are active */
+static struct kdbus_domain *fs_acquire_domain(struct kdbus_node *node)
+{
+       struct kdbus_domain *domain;
+       struct kdbus_node *iter;
+
+       /* caller must guarantee that @node is linked */
+       for (iter = node; iter->parent; iter = iter->parent)
+               if (!kdbus_node_is_active(iter->parent))
+                       return NULL;
+
+       /* root nodes are always domains */
+       if (WARN_ON(iter->type != KDBUS_NODE_DOMAIN))
+               return NULL;
+
+       domain = kdbus_domain_from_node(iter);
+       if (!kdbus_node_acquire(&domain->node))
+               return NULL;
+
+       return domain;
+}
+
+/**
+ * kdbus_fs_flush() - flush dcache entries of a node
+ * @node:              Node to flush entries of
+ *
+ * This flushes all VFS filesystem cache entries for a node and all its
+ * children. This should be called whenever a node is destroyed during
+ * runtime. It will flush the cache entries so the linked objects can be
+ * deallocated.
+ *
+ * This is a no-op if you call it on active nodes (they really should stay in
+ * cache) or on nodes with deactivated parents (flushing the parent is enough).
+ * Furthermore, there is no need to call it on nodes whose lifetime is bound to
+ * their parents'. In those cases, the parent-flush will always also flush the
+ * children.
+ */
+void kdbus_fs_flush(struct kdbus_node *node)
+{
+       struct dentry *dentry, *parent_dentry = NULL;
+       struct kdbus_domain *domain;
+       struct qstr name;
+
+       /* active nodes should remain in cache */
+       if (!kdbus_node_is_deactivated(node))
+               return;
+
+       /* nodes that were never linked were never instantiated */
+       if (!node->parent)
+               return;
+
+       /* acquire domain and verify all ancestors are active */
+       domain = fs_acquire_domain(node);
+       if (!domain)
+               return;
+
+       switch (node->type) {
+       case KDBUS_NODE_ENDPOINT:
+               if (WARN_ON(!node->parent || !node->parent->name))
+                       goto exit;
+
+               name.name = node->parent->name;
+               name.len = strlen(node->parent->name);
+               parent_dentry = d_hash_and_lookup(domain->dentry, &name);
+               if (IS_ERR_OR_NULL(parent_dentry))
+                       goto exit;
+
+               /* fallthrough */
+       case KDBUS_NODE_BUS:
+               if (WARN_ON(!node->name))
+                       goto exit;
+
+               name.name = node->name;
+               name.len = strlen(node->name);
+               dentry = d_hash_and_lookup(parent_dentry ? : domain->dentry,
+                                          &name);
+               if (!IS_ERR_OR_NULL(dentry)) {
+                       d_invalidate(dentry);
+                       dput(dentry);
+               }
+
+               dput(parent_dentry);
+               break;
+
+       default:
+               /* all other types are bound to their parent lifetime */
+               break;
+       }
+
+exit:
+       kdbus_node_release(&domain->node);
+}
diff --git a/ipc/kdbus/fs.h b/ipc/kdbus/fs.h
new file mode 100644 (file)
index 0000000..62f7d6a
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * 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
+ *
+ * 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 __KDBUSFS_H
+#define __KDBUSFS_H
+
+#include <linux/kernel.h>
+
+struct kdbus_node;
+
+int kdbus_fs_init(void);
+void kdbus_fs_exit(void);
+void kdbus_fs_flush(struct kdbus_node *node);
+
+#define kdbus_node_from_inode(_inode) \
+       ((struct kdbus_node *)(_inode)->i_private)
+
+#endif
diff --git a/ipc/kdbus/handle.c b/ipc/kdbus/handle.c
new file mode 100644 (file)
index 0000000..fc60932
--- /dev/null
@@ -0,0 +1,691 @@
+/*
+ * 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/file.h>
+#include <linux/fs.h>
+#include <linux/idr.h>
+#include <linux/init.h>
+#include <linux/kdev_t.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/poll.h>
+#include <linux/rwsem.h>
+#include <linux/sched.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/syscalls.h>
+
+#include "bus.h"
+#include "connection.h"
+#include "endpoint.h"
+#include "fs.h"
+#include "handle.h"
+#include "item.h"
+#include "match.h"
+#include "message.h"
+#include "names.h"
+#include "domain.h"
+#include "policy.h"
+
+static int kdbus_args_verify(struct kdbus_args *args)
+{
+       struct kdbus_item *item;
+       size_t i;
+       int ret;
+
+       KDBUS_ITEMS_FOREACH(item, args->items, args->items_size) {
+               struct kdbus_arg *arg = NULL;
+
+               if (!KDBUS_ITEM_VALID(item, args->items, args->items_size))
+                       return -EINVAL;
+
+               for (i = 0; i < args->argc; ++i)
+                       if (args->argv[i].type == item->type)
+                               break;
+               if (i >= args->argc)
+                       return -EINVAL;
+
+               arg = &args->argv[i];
+
+               ret = kdbus_item_validate(item);
+               if (ret < 0)
+                       return ret;
+
+               if (arg->item && !arg->multiple)
+                       return -EINVAL;
+
+               arg->item = item;
+       }
+
+       if (!KDBUS_ITEMS_END(item, args->items, args->items_size))
+               return -EINVAL;
+
+       return 0;
+}
+
+static int kdbus_args_negotiate(struct kdbus_args *args)
+{
+       struct kdbus_item __user *user;
+       struct kdbus_item *negotiation;
+       size_t i, j, num;
+
+       /*
+        * If KDBUS_FLAG_NEGOTIATE is set, we overwrite the flags field with
+        * the set of supported flags. Furthermore, if an KDBUS_ITEM_NEGOTIATE
+        * item is passed, we iterate its payload (array of u64, each set to an
+        * item type) and clear all unsupported item-types to 0.
+        * The caller might do this recursively, if other flags or objects are
+        * embedded in the payload itself.
+        */
+
+       if (args->cmd->flags & KDBUS_FLAG_NEGOTIATE) {
+               if (put_user(args->allowed_flags & ~KDBUS_FLAG_NEGOTIATE,
+                            &args->user->flags))
+                       return -EFAULT;
+       }
+
+       if (args->argc < 1 || args->argv[0].type != KDBUS_ITEM_NEGOTIATE ||
+           !args->argv[0].item)
+               return 0;
+
+       negotiation = args->argv[0].item;
+       user = (struct kdbus_item __user *)
+               ((u8 __user *)args->user +
+                ((u8 *)negotiation - (u8 *)args->cmd));
+       num = KDBUS_ITEM_PAYLOAD_SIZE(negotiation) / sizeof(u64);
+
+       for (i = 0; i < num; ++i) {
+               for (j = 0; j < args->argc; ++j)
+                       if (negotiation->data64[i] == args->argv[j].type)
+                               break;
+
+               if (j < args->argc)
+                       continue;
+
+               /* this item is not supported, clear it out */
+               negotiation->data64[i] = 0;
+               if (put_user(negotiation->data64[i], &user->data64[i]))
+                       return -EFAULT;
+       }
+
+       return 0;
+}
+
+/**
+ * __kdbus_args_parse() - parse payload of kdbus command
+ * @args:              object to parse data into
+ * @is_cmd:            whether this is a command or msg payload
+ * @argp:              user-space location of command payload to parse
+ * @type_size:         overall size of command payload to parse
+ * @items_offset:      offset of items array in command payload
+ * @out:               output variable to store pointer to copied payload
+ *
+ * This parses the ioctl payload at user-space location @argp into @args. @args
+ * must be pre-initialized by the caller to reflect the supported flags and
+ * items of this command. This parser will then copy the command payload into
+ * kernel-space, verify correctness and consistency and cache pointers to parsed
+ * items and other data in @args.
+ *
+ * If this function succeeded, you must call kdbus_args_clear() to release
+ * allocated resources before destroying @args.
+ *
+ * This can also be used to import kdbus_msg objects. In that case, @is_cmd must
+ * be set to 'false' and the 'return_flags' field will not be touched (as it
+ * doesn't exist on kdbus_msg).
+ *
+ * Return: On failure a negative error code is returned. Otherwise, 1 is
+ * returned if negotiation was requested, 0 if not.
+ */
+int __kdbus_args_parse(struct kdbus_args *args, bool is_cmd, void __user *argp,
+                      size_t type_size, size_t items_offset, void **out)
+{
+       u64 user_size;
+       int ret, i;
+
+       ret = kdbus_copy_from_user(&user_size, argp, sizeof(user_size));
+       if (ret < 0)
+               return ret;
+
+       if (user_size < type_size)
+               return -EINVAL;
+       if (user_size > KDBUS_CMD_MAX_SIZE)
+               return -EMSGSIZE;
+
+       if (user_size <= sizeof(args->cmd_buf)) {
+               if (copy_from_user(args->cmd_buf, argp, user_size))
+                       return -EFAULT;
+               args->cmd = (void*)args->cmd_buf;
+       } else {
+               args->cmd = memdup_user(argp, user_size);
+               if (IS_ERR(args->cmd))
+                       return PTR_ERR(args->cmd);
+       }
+
+       if (args->cmd->size != user_size) {
+               ret = -EINVAL;
+               goto error;
+       }
+
+       if (is_cmd)
+               args->cmd->return_flags = 0;
+       args->user = argp;
+       args->items = (void *)((u8 *)args->cmd + items_offset);
+       args->items_size = args->cmd->size - items_offset;
+       args->is_cmd = is_cmd;
+
+       if (args->cmd->flags & ~args->allowed_flags) {
+               ret = -EINVAL;
+               goto error;
+       }
+
+       ret = kdbus_args_verify(args);
+       if (ret < 0)
+               goto error;
+
+       ret = kdbus_args_negotiate(args);
+       if (ret < 0)
+               goto error;
+
+       /* mandatory items must be given (but not on negotiation) */
+       if (!(args->cmd->flags & KDBUS_FLAG_NEGOTIATE)) {
+               for (i = 0; i < args->argc; ++i)
+                       if (args->argv[i].mandatory && !args->argv[i].item) {
+                               ret = -EINVAL;
+                               goto error;
+                       }
+       }
+
+       *out = args->cmd;
+       return !!(args->cmd->flags & KDBUS_FLAG_NEGOTIATE);
+
+error:
+       return kdbus_args_clear(args, ret);
+}
+
+/**
+ * kdbus_args_clear() - release allocated command resources
+ * @args:      object to release resources of
+ * @ret:       return value of this command
+ *
+ * This frees all allocated resources on @args and copies the command result
+ * flags into user-space. @ret is usually returned unchanged by this function,
+ * so it can be used in the final 'return' statement of the command handler.
+ *
+ * Return: -EFAULT if return values cannot be copied into user-space, otherwise
+ *         @ret is returned unchanged.
+ */
+int kdbus_args_clear(struct kdbus_args *args, int ret)
+{
+       if (!args)
+               return ret;
+
+       if (!IS_ERR_OR_NULL(args->cmd)) {
+               if (args->is_cmd && put_user(args->cmd->return_flags,
+                                            &args->user->return_flags))
+                       ret = -EFAULT;
+               if (args->cmd != (void*)args->cmd_buf)
+                       kfree(args->cmd);
+               args->cmd = NULL;
+       }
+
+       return ret;
+}
+
+/**
+ * enum kdbus_handle_type - type an handle can be of
+ * @KDBUS_HANDLE_NONE:         no type set, yet
+ * @KDBUS_HANDLE_BUS_OWNER:    bus owner
+ * @KDBUS_HANDLE_EP_OWNER:     endpoint owner
+ * @KDBUS_HANDLE_CONNECTED:    endpoint connection after HELLO
+ */
+enum kdbus_handle_type {
+       KDBUS_HANDLE_NONE,
+       KDBUS_HANDLE_BUS_OWNER,
+       KDBUS_HANDLE_EP_OWNER,
+       KDBUS_HANDLE_CONNECTED,
+};
+
+/**
+ * struct kdbus_handle - handle to the kdbus system
+ * @lock:              handle lock
+ * @type:              type of this handle (KDBUS_HANDLE_*)
+ * @bus_owner:         bus this handle owns
+ * @ep_owner:          endpoint this handle owns
+ * @conn:              connection this handle owns
+ */
+struct kdbus_handle {
+       struct mutex lock;
+
+       enum kdbus_handle_type type;
+       union {
+               struct kdbus_bus *bus_owner;
+               struct kdbus_ep *ep_owner;
+               struct kdbus_conn *conn;
+       };
+};
+
+static int kdbus_handle_open(struct inode *inode, struct file *file)
+{
+       struct kdbus_handle *handle;
+       struct kdbus_node *node;
+       int ret;
+
+       node = kdbus_node_from_inode(inode);
+       if (!kdbus_node_acquire(node))
+               return -ESHUTDOWN;
+
+       handle = kzalloc(sizeof(*handle), GFP_KERNEL);
+       if (!handle) {
+               ret = -ENOMEM;
+               goto exit;
+       }
+
+       mutex_init(&handle->lock);
+       handle->type = KDBUS_HANDLE_NONE;
+
+       file->private_data = handle;
+       ret = 0;
+
+exit:
+       kdbus_node_release(node);
+       return ret;
+}
+
+static int kdbus_handle_release(struct inode *inode, struct file *file)
+{
+       struct kdbus_handle *handle = file->private_data;
+
+       switch (handle->type) {
+       case KDBUS_HANDLE_BUS_OWNER:
+               if (handle->bus_owner) {
+                       kdbus_node_deactivate(&handle->bus_owner->node);
+                       kdbus_bus_unref(handle->bus_owner);
+               }
+               break;
+       case KDBUS_HANDLE_EP_OWNER:
+               if (handle->ep_owner) {
+                       kdbus_node_deactivate(&handle->ep_owner->node);
+                       kdbus_ep_unref(handle->ep_owner);
+               }
+               break;
+       case KDBUS_HANDLE_CONNECTED:
+               kdbus_conn_disconnect(handle->conn, false);
+               kdbus_conn_unref(handle->conn);
+               break;
+       case KDBUS_HANDLE_NONE:
+               /* nothing to clean up */
+               break;
+       }
+
+       kfree(handle);
+
+       return 0;
+}
+
+static long kdbus_handle_ioctl_control(struct file *file, unsigned int cmd,
+                                      void __user *argp)
+{
+       struct kdbus_handle *handle = file->private_data;
+       struct kdbus_node *node = file_inode(file)->i_private;
+       struct kdbus_domain *domain;
+       int ret = 0;
+
+       if (!kdbus_node_acquire(node))
+               return -ESHUTDOWN;
+
+       /*
+        * The parent of control-nodes is always a domain, make sure to pin it
+        * so the parent is actually valid.
+        */
+       domain = kdbus_domain_from_node(node->parent);
+       if (!kdbus_node_acquire(&domain->node)) {
+               kdbus_node_release(node);
+               return -ESHUTDOWN;
+       }
+
+       switch (cmd) {
+       case KDBUS_CMD_BUS_MAKE: {
+               struct kdbus_bus *bus;
+
+               bus = kdbus_cmd_bus_make(domain, argp);
+               if (IS_ERR_OR_NULL(bus)) {
+                       ret = PTR_ERR_OR_ZERO(bus);
+                       break;
+               }
+
+               handle->bus_owner = bus;
+               ret = KDBUS_HANDLE_BUS_OWNER;
+               break;
+       }
+
+       default:
+               ret = -EBADFD;
+               break;
+       }
+
+       kdbus_node_release(&domain->node);
+       kdbus_node_release(node);
+       return ret;
+}
+
+static long kdbus_handle_ioctl_ep(struct file *file, unsigned int cmd,
+                                 void __user *buf)
+{
+       struct kdbus_handle *handle = file->private_data;
+       struct kdbus_node *node = file_inode(file)->i_private;
+       struct kdbus_ep *ep, *file_ep = kdbus_ep_from_node(node);
+       struct kdbus_bus *bus = file_ep->bus;
+       struct kdbus_conn *conn;
+       int ret = 0;
+
+       if (!kdbus_node_acquire(node))
+               return -ESHUTDOWN;
+
+       switch (cmd) {
+       case KDBUS_CMD_ENDPOINT_MAKE: {
+               /* creating custom endpoints is a privileged operation */
+               if (!kdbus_ep_is_owner(file_ep, file)) {
+                       ret = -EPERM;
+                       break;
+               }
+
+               ep = kdbus_cmd_ep_make(bus, buf);
+               if (IS_ERR_OR_NULL(ep)) {
+                       ret = PTR_ERR_OR_ZERO(ep);
+                       break;
+               }
+
+               handle->ep_owner = ep;
+               ret = KDBUS_HANDLE_EP_OWNER;
+               break;
+       }
+
+       case KDBUS_CMD_HELLO:
+               conn = kdbus_cmd_hello(file_ep, file, buf);
+               if (IS_ERR_OR_NULL(conn)) {
+                       ret = PTR_ERR_OR_ZERO(conn);
+                       break;
+               }
+
+               handle->conn = conn;
+               ret = KDBUS_HANDLE_CONNECTED;
+               break;
+
+       default:
+               ret = -EBADFD;
+               break;
+       }
+
+       kdbus_node_release(node);
+       return ret;
+}
+
+static long kdbus_handle_ioctl_ep_owner(struct file *file, unsigned int command,
+                                       void __user *buf)
+{
+       struct kdbus_handle *handle = file->private_data;
+       struct kdbus_ep *ep = handle->ep_owner;
+       int ret;
+
+       if (!kdbus_node_acquire(&ep->node))
+               return -ESHUTDOWN;
+
+       switch (command) {
+       case KDBUS_CMD_ENDPOINT_UPDATE:
+               ret = kdbus_cmd_ep_update(ep, buf);
+               break;
+       default:
+               ret = -EBADFD;
+               break;
+       }
+
+       kdbus_node_release(&ep->node);
+       return ret;
+}
+
+static long kdbus_handle_ioctl_connected(struct file *file,
+                                        unsigned int command, void __user *buf)
+{
+       struct kdbus_handle *handle = file->private_data;
+       struct kdbus_conn *conn = handle->conn;
+       struct kdbus_conn *release_conn = NULL;
+       int ret;
+
+       release_conn = conn;
+       ret = kdbus_conn_acquire(release_conn);
+       if (ret < 0)
+               return ret;
+
+       switch (command) {
+       case KDBUS_CMD_BYEBYE:
+               /*
+                * BYEBYE is special; we must not acquire a connection when
+                * calling into kdbus_conn_disconnect() or we will deadlock,
+                * because kdbus_conn_disconnect() will wait for all acquired
+                * references to be dropped.
+                */
+               kdbus_conn_release(release_conn);
+               release_conn = NULL;
+               ret = kdbus_cmd_byebye_unlocked(conn, buf);
+               break;
+       case KDBUS_CMD_NAME_ACQUIRE:
+               ret = kdbus_cmd_name_acquire(conn, buf);
+               break;
+       case KDBUS_CMD_NAME_RELEASE:
+               ret = kdbus_cmd_name_release(conn, buf);
+               break;
+       case KDBUS_CMD_LIST:
+               ret = kdbus_cmd_list(conn, buf);
+               break;
+       case KDBUS_CMD_CONN_INFO:
+               ret = kdbus_cmd_conn_info(conn, buf);
+               break;
+       case KDBUS_CMD_BUS_CREATOR_INFO:
+               ret = kdbus_cmd_bus_creator_info(conn, buf);
+               break;
+       case KDBUS_CMD_UPDATE:
+               ret = kdbus_cmd_update(conn, buf);
+               break;
+       case KDBUS_CMD_MATCH_ADD:
+               ret = kdbus_cmd_match_add(conn, buf);
+               break;
+       case KDBUS_CMD_MATCH_REMOVE:
+               ret = kdbus_cmd_match_remove(conn, buf);
+               break;
+       case KDBUS_CMD_SEND:
+               ret = kdbus_cmd_send(conn, file, buf);
+               break;
+       case KDBUS_CMD_RECV:
+               ret = kdbus_cmd_recv(conn, buf);
+               break;
+       case KDBUS_CMD_FREE:
+               ret = kdbus_cmd_free(conn, buf);
+               break;
+       default:
+               ret = -EBADFD;
+               break;
+       }
+
+       kdbus_conn_release(release_conn);
+       return ret;
+}
+
+static long kdbus_handle_ioctl(struct file *file, unsigned int cmd,
+                              unsigned long arg)
+{
+       struct kdbus_handle *handle = file->private_data;
+       struct kdbus_node *node = kdbus_node_from_inode(file_inode(file));
+       void __user *argp = (void __user *)arg;
+       long ret = -EBADFD;
+
+       switch (cmd) {
+       case KDBUS_CMD_BUS_MAKE:
+       case KDBUS_CMD_ENDPOINT_MAKE:
+       case KDBUS_CMD_HELLO:
+               mutex_lock(&handle->lock);
+               if (handle->type == KDBUS_HANDLE_NONE) {
+                       if (node->type == KDBUS_NODE_CONTROL)
+                               ret = kdbus_handle_ioctl_control(file, cmd,
+                                                                argp);
+                       else if (node->type == KDBUS_NODE_ENDPOINT)
+                               ret = kdbus_handle_ioctl_ep(file, cmd, argp);
+
+                       if (ret > 0) {
+                               /*
+                                * The data given via open() is not sufficient
+                                * to setup a kdbus handle. Hence, we require
+                                * the user to perform a setup ioctl. This setup
+                                * can only be performed once and defines the
+                                * type of the handle. The different setup
+                                * ioctls are locked against each other so they
+                                * cannot race. Once the handle type is set,
+                                * the type-dependent ioctls are enabled. To
+                                * improve performance, we don't lock those via
+                                * handle->lock. Instead, we issue a
+                                * write-barrier before performing the
+                                * type-change, which pairs with smp_rmb() in
+                                * all handlers that access the type field. This
+                                * guarantees the handle is fully setup, if
+                                * handle->type is set. If handle->type is
+                                * unset, you must not make any assumptions
+                                * without taking handle->lock.
+                                * Note that handle->type is only set once. It
+                                * will never change afterwards.
+                                */
+                               smp_wmb();
+                               handle->type = ret;
+                       }
+               }
+               mutex_unlock(&handle->lock);
+               break;
+
+       case KDBUS_CMD_ENDPOINT_UPDATE:
+       case KDBUS_CMD_BYEBYE:
+       case KDBUS_CMD_NAME_ACQUIRE:
+       case KDBUS_CMD_NAME_RELEASE:
+       case KDBUS_CMD_LIST:
+       case KDBUS_CMD_CONN_INFO:
+       case KDBUS_CMD_BUS_CREATOR_INFO:
+       case KDBUS_CMD_UPDATE:
+       case KDBUS_CMD_MATCH_ADD:
+       case KDBUS_CMD_MATCH_REMOVE:
+       case KDBUS_CMD_SEND:
+       case KDBUS_CMD_RECV:
+       case KDBUS_CMD_FREE: {
+               enum kdbus_handle_type type;
+
+               /*
+                * This read-barrier pairs with smp_wmb() of the handle setup.
+                * it guarantees the handle is fully written, in case the
+                * type has been set. It allows us to access the handle without
+                * taking handle->lock, given the guarantee that the type is
+                * only ever set once, and stays constant afterwards.
+                * Furthermore, the handle object itself is not modified in any
+                * way after the type is set. That is, the type-field is the
+                * last field that is written on any handle. If it has not been
+                * set, we must not access the handle here.
+                */
+               type = handle->type;
+               smp_rmb();
+
+               if (type == KDBUS_HANDLE_EP_OWNER)
+                       ret = kdbus_handle_ioctl_ep_owner(file, cmd, argp);
+               else if (type == KDBUS_HANDLE_CONNECTED)
+                       ret = kdbus_handle_ioctl_connected(file, cmd, argp);
+
+               break;
+       }
+       default:
+               ret = -ENOTTY;
+               break;
+       }
+
+       return ret < 0 ? ret : 0;
+}
+
+static unsigned int kdbus_handle_poll(struct file *file,
+                                     struct poll_table_struct *wait)
+{
+       struct kdbus_handle *handle = file->private_data;
+       enum kdbus_handle_type type;
+       unsigned int mask = POLLOUT | POLLWRNORM;
+
+       /*
+        * This pairs with smp_wmb() during handle setup. It guarantees that
+        * _iff_ the handle type is set, handle->conn is valid. Furthermore,
+        * _iff_ the type is set, the handle object is constant and never
+        * changed again. If it's not set, we must not access the handle but
+        * bail out. We also must assume no setup has taken place, yet.
+        */
+       type = handle->type;
+       smp_rmb();
+
+       /* Only a connected endpoint can read/write data */
+       if (type != KDBUS_HANDLE_CONNECTED)
+               return POLLERR | POLLHUP;
+
+       poll_wait(file, &handle->conn->wait, wait);
+
+       /*
+        * Verify the connection hasn't been deactivated _after_ adding the
+        * wait-queue. This guarantees, that if the connection is deactivated
+        * after we checked it, the waitqueue is signaled and we're called
+        * again.
+        */
+       if (!kdbus_conn_active(handle->conn))
+               return POLLERR | POLLHUP;
+
+       if (!list_empty(&handle->conn->queue.msg_list) ||
+           atomic_read(&handle->conn->lost_count) > 0)
+               mask |= POLLIN | POLLRDNORM;
+
+       return mask;
+}
+
+static int kdbus_handle_mmap(struct file *file, struct vm_area_struct *vma)
+{
+       struct kdbus_handle *handle = file->private_data;
+       enum kdbus_handle_type type;
+       int ret = -EBADFD;
+
+       /*
+        * This pairs with smp_wmb() during handle setup. It guarantees that
+        * _iff_ the handle type is set, handle->conn is valid. Furthermore,
+        * _iff_ the type is set, the handle object is constant and never
+        * changed again. If it's not set, we must not access the handle but
+        * bail out. We also must assume no setup has taken place, yet.
+        */
+       type = handle->type;
+       smp_rmb();
+
+       /* Only connected handles have a pool we can map */
+       if (type == KDBUS_HANDLE_CONNECTED)
+               ret = kdbus_pool_mmap(handle->conn->pool, vma);
+
+       return ret;
+}
+
+const struct file_operations kdbus_handle_ops = {
+       .owner =                THIS_MODULE,
+       .open =                 kdbus_handle_open,
+       .release =              kdbus_handle_release,
+       .poll =                 kdbus_handle_poll,
+       .llseek =               noop_llseek,
+       .unlocked_ioctl =       kdbus_handle_ioctl,
+       .mmap =                 kdbus_handle_mmap,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl =         kdbus_handle_ioctl,
+#endif
+};
diff --git a/ipc/kdbus/handle.h b/ipc/kdbus/handle.h
new file mode 100644 (file)
index 0000000..5dde2c1
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * 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
+ *
+ * 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_HANDLE_H
+#define __KDBUS_HANDLE_H
+
+#include <linux/fs.h>
+#include <uapi/linux/kdbus.h>
+
+extern const struct file_operations kdbus_handle_ops;
+
+/**
+ * kdbus_arg - information and state of a single ioctl command item
+ * @type:              item type
+ * @item:              set by the parser to the first found item of this type
+ * @multiple:          whether multiple items of this type are allowed
+ * @mandatory:         whether at least one item of this type is required
+ *
+ * This structure describes a single item in an ioctl command payload. The
+ * caller has to pre-fill the type and flags, the parser will then use this
+ * information to verify the ioctl payload. @item is set by the parser to point
+ * to the first occurrence of the item.
+ */
+struct kdbus_arg {
+       u64 type;
+       struct kdbus_item *item;
+       bool multiple : 1;
+       bool mandatory : 1;
+};
+
+/**
+ * kdbus_args - information and state of ioctl command parser
+ * @allowed_flags:     set of flags this command supports
+ * @argc:              number of items in @argv
+ * @argv:              array of items this command supports
+ * @user:              set by parser to user-space location of current command
+ * @cmd:               set by parser to kernel copy of command payload
+ * @cmd_buf:           inline buf to avoid kmalloc() on small cmds
+ * @items:             points to item array in @cmd
+ * @items_size:                size of @items in bytes
+ * @is_cmd:            whether this is a command-payload or msg-payload
+ *
+ * This structure is used to parse ioctl command payloads on each invocation.
+ * The ioctl handler has to pre-fill the flags and allowed items before passing
+ * the object to kdbus_args_parse(). The parser will copy the command payload
+ * into kernel-space and verify the correctness of the data.
+ *
+ * We use a 256 bytes buffer for small command payloads, to be allocated on
+ * stack on syscall entrance.
+ */
+struct kdbus_args {
+       u64 allowed_flags;
+       size_t argc;
+       struct kdbus_arg *argv;
+
+       struct kdbus_cmd __user *user;
+       struct kdbus_cmd *cmd;
+       u8 cmd_buf[256];
+
+       struct kdbus_item *items;
+       size_t items_size;
+       bool is_cmd : 1;
+};
+
+int __kdbus_args_parse(struct kdbus_args *args, bool is_cmd, void __user *argp,
+                      size_t type_size, size_t items_offset, void **out);
+int kdbus_args_clear(struct kdbus_args *args, int ret);
+
+#define kdbus_args_parse(_args, _argp, _v)                              \
+       ({                                                              \
+               BUILD_BUG_ON(offsetof(typeof(**(_v)), size) !=          \
+                            offsetof(struct kdbus_cmd, size));         \
+               BUILD_BUG_ON(offsetof(typeof(**(_v)), flags) !=         \
+                            offsetof(struct kdbus_cmd, flags));        \
+               BUILD_BUG_ON(offsetof(typeof(**(_v)), return_flags) !=  \
+                            offsetof(struct kdbus_cmd, return_flags)); \
+               __kdbus_args_parse((_args), 1, (_argp), sizeof(**(_v)), \
+                                  offsetof(typeof(**(_v)), items),     \
+                                  (void **)(_v));                      \
+       })
+
+#define kdbus_args_parse_msg(_args, _argp, _v)                          \
+       ({                                                              \
+               BUILD_BUG_ON(offsetof(typeof(**(_v)), size) !=          \
+                            offsetof(struct kdbus_cmd, size));         \
+               BUILD_BUG_ON(offsetof(typeof(**(_v)), flags) !=         \
+                            offsetof(struct kdbus_cmd, flags));        \
+               __kdbus_args_parse((_args), 0, (_argp), sizeof(**(_v)), \
+                                  offsetof(typeof(**(_v)), items),     \
+                                  (void **)(_v));                      \
+       })
+
+#endif
diff --git a/ipc/kdbus/item.c b/ipc/kdbus/item.c
new file mode 100644 (file)
index 0000000..ce78dba
--- /dev/null
@@ -0,0 +1,293 @@
+/*
+ * 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/ctype.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+
+#include "item.h"
+#include "limits.h"
+#include "util.h"
+
+/*
+ * This verifies the string at position @str with size @size is properly
+ * zero-terminated and does not contain a 0-byte but at the end.
+ */
+static bool kdbus_str_valid(const char *str, size_t size)
+{
+       return size > 0 && memchr(str, '\0', size) == str + size - 1;
+}
+
+/**
+ * kdbus_item_validate_name() - validate an item containing a name
+ * @item:              Item to validate
+ *
+ * Return: zero on success or an negative error code on failure
+ */
+int kdbus_item_validate_name(const struct kdbus_item *item)
+{
+       const char *name = item->str;
+       unsigned int i;
+       size_t len;
+
+       if (item->size < KDBUS_ITEM_HEADER_SIZE + 2)
+               return -EINVAL;
+
+       if (item->size > KDBUS_ITEM_HEADER_SIZE +
+                        KDBUS_SYSNAME_MAX_LEN + 1)
+               return -ENAMETOOLONG;
+
+       if (!kdbus_str_valid(name, KDBUS_ITEM_PAYLOAD_SIZE(item)))
+               return -EINVAL;
+
+       len = strlen(name);
+       if (len == 0)
+               return -EINVAL;
+
+       for (i = 0; i < len; i++) {
+               if (isalpha(name[i]))
+                       continue;
+               if (isdigit(name[i]))
+                       continue;
+               if (name[i] == '_')
+                       continue;
+               if (i > 0 && i + 1 < len && (name[i] == '-' || name[i] == '.'))
+                       continue;
+
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/**
+ * kdbus_item_validate() - validate a single item
+ * @item:      item to validate
+ *
+ * Return: 0 if item is valid, negative error code if not.
+ */
+int kdbus_item_validate(const struct kdbus_item *item)
+{
+       size_t payload_size = KDBUS_ITEM_PAYLOAD_SIZE(item);
+       size_t l;
+       int ret;
+
+       BUILD_BUG_ON(KDBUS_ITEM_HEADER_SIZE !=
+                    sizeof(struct kdbus_item_header));
+
+       if (item->size < KDBUS_ITEM_HEADER_SIZE)
+               return -EINVAL;
+
+       switch (item->type) {
+       case KDBUS_ITEM_NEGOTIATE:
+               if (payload_size % sizeof(u64) != 0)
+                       return -EINVAL;
+               break;
+
+       case KDBUS_ITEM_PAYLOAD_VEC:
+       case KDBUS_ITEM_PAYLOAD_OFF:
+               if (payload_size != sizeof(struct kdbus_vec))
+                       return -EINVAL;
+               if (item->vec.size == 0 || item->vec.size > SIZE_MAX)
+                       return -EINVAL;
+               break;
+
+       case KDBUS_ITEM_PAYLOAD_MEMFD:
+               if (payload_size != sizeof(struct kdbus_memfd))
+                       return -EINVAL;
+               if (item->memfd.size == 0 || item->memfd.size > SIZE_MAX)
+                       return -EINVAL;
+               if (item->memfd.fd < 0)
+                       return -EBADF;
+               break;
+
+       case KDBUS_ITEM_FDS:
+               if (payload_size % sizeof(int) != 0)
+                       return -EINVAL;
+               break;
+
+       case KDBUS_ITEM_CANCEL_FD:
+               if (payload_size != sizeof(int))
+                       return -EINVAL;
+               break;
+
+       case KDBUS_ITEM_BLOOM_PARAMETER:
+               if (payload_size != sizeof(struct kdbus_bloom_parameter))
+                       return -EINVAL;
+               break;
+
+       case KDBUS_ITEM_BLOOM_FILTER:
+               /* followed by the bloom-mask, depends on the bloom-size */
+               if (payload_size < sizeof(struct kdbus_bloom_filter))
+                       return -EINVAL;
+               break;
+
+       case KDBUS_ITEM_BLOOM_MASK:
+               /* size depends on bloom-size of bus */
+               break;
+
+       case KDBUS_ITEM_CONN_DESCRIPTION:
+       case KDBUS_ITEM_MAKE_NAME:
+               ret = kdbus_item_validate_name(item);
+               if (ret < 0)
+                       return ret;
+               break;
+
+       case KDBUS_ITEM_ATTACH_FLAGS_SEND:
+       case KDBUS_ITEM_ATTACH_FLAGS_RECV:
+       case KDBUS_ITEM_ID:
+       case KDBUS_ITEM_DST_ID:
+               if (payload_size != sizeof(u64))
+                       return -EINVAL;
+               break;
+
+       case KDBUS_ITEM_TIMESTAMP:
+               if (payload_size != sizeof(struct kdbus_timestamp))
+                       return -EINVAL;
+               break;
+
+       case KDBUS_ITEM_CREDS:
+               if (payload_size != sizeof(struct kdbus_creds))
+                       return -EINVAL;
+               break;
+
+       case KDBUS_ITEM_AUXGROUPS:
+               if (payload_size % sizeof(u32) != 0)
+                       return -EINVAL;
+               break;
+
+       case KDBUS_ITEM_NAME:
+       case KDBUS_ITEM_DST_NAME:
+       case KDBUS_ITEM_PID_COMM:
+       case KDBUS_ITEM_TID_COMM:
+       case KDBUS_ITEM_EXE:
+       case KDBUS_ITEM_CMDLINE:
+       case KDBUS_ITEM_CGROUP:
+       case KDBUS_ITEM_SECLABEL:
+               if (!kdbus_str_valid(item->str, payload_size))
+                       return -EINVAL;
+               break;
+
+       case KDBUS_ITEM_CAPS:
+               if (payload_size < sizeof(u32))
+                       return -EINVAL;
+               if (payload_size < sizeof(u32) +
+                   4 * CAP_TO_INDEX(item->caps.last_cap) * sizeof(u32))
+                       return -EINVAL;
+               break;
+
+       case KDBUS_ITEM_AUDIT:
+               if (payload_size != sizeof(struct kdbus_audit))
+                       return -EINVAL;
+               break;
+
+       case KDBUS_ITEM_POLICY_ACCESS:
+               if (payload_size != sizeof(struct kdbus_policy_access))
+                       return -EINVAL;
+               break;
+
+       case KDBUS_ITEM_NAME_ADD:
+       case KDBUS_ITEM_NAME_REMOVE:
+       case KDBUS_ITEM_NAME_CHANGE:
+               if (payload_size < sizeof(struct kdbus_notify_name_change))
+                       return -EINVAL;
+               l = payload_size - offsetof(struct kdbus_notify_name_change,
+                                           name);
+               if (l > 0 && !kdbus_str_valid(item->name_change.name, l))
+                       return -EINVAL;
+               break;
+
+       case KDBUS_ITEM_ID_ADD:
+       case KDBUS_ITEM_ID_REMOVE:
+               if (payload_size != sizeof(struct kdbus_notify_id_change))
+                       return -EINVAL;
+               break;
+
+       case KDBUS_ITEM_REPLY_TIMEOUT:
+       case KDBUS_ITEM_REPLY_DEAD:
+               if (payload_size != 0)
+                       return -EINVAL;
+               break;
+
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+/**
+ * kdbus_items_validate() - validate items passed by user-space
+ * @items:             items to validate
+ * @items_size:                number of items
+ *
+ * This verifies that the passed items pointer is consistent and valid.
+ * Furthermore, each item is checked for:
+ *  - valid "size" value
+ *  - payload is of expected type
+ *  - payload is fully included in the item
+ *  - string payloads are zero-terminated
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int kdbus_items_validate(const struct kdbus_item *items, size_t items_size)
+{
+       const struct kdbus_item *item;
+       int ret;
+
+       KDBUS_ITEMS_FOREACH(item, items, items_size) {
+               if (!KDBUS_ITEM_VALID(item, items, items_size))
+                       return -EINVAL;
+
+               ret = kdbus_item_validate(item);
+               if (ret < 0)
+                       return ret;
+       }
+
+       if (!KDBUS_ITEMS_END(item, items, items_size))
+               return -EINVAL;
+
+       return 0;
+}
+
+/**
+ * kdbus_item_set() - Set item content
+ * @item:      The item to modify
+ * @type:      The item type to set (KDBUS_ITEM_*)
+ * @data:      Data to copy to item->data, may be %NULL
+ * @len:       Number of bytes in @data
+ *
+ * This sets type, size and data fields of an item. If @data is NULL, the data
+ * memory is cleared.
+ *
+ * Note that you must align your @data memory to 8 bytes. Trailing padding (in
+ * case @len is not 8byte aligned) is cleared by this call.
+ *
+ * Returns: Pointer to the following item.
+ */
+struct kdbus_item *kdbus_item_set(struct kdbus_item *item, u64 type,
+                                 const void *data, size_t len)
+{
+       item->type = type;
+       item->size = KDBUS_ITEM_HEADER_SIZE + len;
+
+       if (data) {
+               memcpy(item->data, data, len);
+               memset(item->data + len, 0, KDBUS_ALIGN8(len) - len);
+       } else {
+               memset(item->data, 0, KDBUS_ALIGN8(len));
+       }
+
+       return KDBUS_ITEM_NEXT(item);
+}
diff --git a/ipc/kdbus/item.h b/ipc/kdbus/item.h
new file mode 100644 (file)
index 0000000..3a7e6cc
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+#ifndef __KDBUS_ITEM_H
+#define __KDBUS_ITEM_H
+
+#include <linux/kernel.h>
+#include <uapi/linux/kdbus.h>
+
+#include "util.h"
+
+/* generic access and iterators over a stream of items */
+#define KDBUS_ITEM_NEXT(_i) (typeof(_i))((u8 *)(_i) + KDBUS_ALIGN8((_i)->size))
+#define KDBUS_ITEMS_SIZE(_h, _is) ((_h)->size - offsetof(typeof(*(_h)), _is))
+#define KDBUS_ITEM_HEADER_SIZE offsetof(struct kdbus_item, data)
+#define KDBUS_ITEM_SIZE(_s) KDBUS_ALIGN8(KDBUS_ITEM_HEADER_SIZE + (_s))
+#define KDBUS_ITEM_PAYLOAD_SIZE(_i) ((_i)->size - KDBUS_ITEM_HEADER_SIZE)
+
+#define KDBUS_ITEMS_FOREACH(_i, _is, _s)                               \
+       for ((_i) = (_is);                                              \
+            ((u8 *)(_i) < (u8 *)(_is) + (_s)) &&                       \
+              ((u8 *)(_i) >= (u8 *)(_is));                             \
+            (_i) = KDBUS_ITEM_NEXT(_i))
+
+#define KDBUS_ITEM_VALID(_i, _is, _s)                                  \
+       ((_i)->size >= KDBUS_ITEM_HEADER_SIZE &&                        \
+        (u8 *)(_i) + (_i)->size > (u8 *)(_i) &&                        \
+        (u8 *)(_i) + (_i)->size <= (u8 *)(_is) + (_s) &&               \
+        (u8 *)(_i) >= (u8 *)(_is))
+
+#define KDBUS_ITEMS_END(_i, _is, _s)                                   \
+       ((u8 *)(_i) == ((u8 *)(_is) + KDBUS_ALIGN8(_s)))
+
+/**
+ * struct kdbus_item_header - Describes the fix part of an item
+ * @size:      The total size of the item
+ * @type:      The item type, one of KDBUS_ITEM_*
+ */
+struct kdbus_item_header {
+       u64 size;
+       u64 type;
+};
+
+int kdbus_item_validate_name(const struct kdbus_item *item);
+int kdbus_item_validate(const struct kdbus_item *item);
+int kdbus_items_validate(const struct kdbus_item *items, size_t items_size);
+struct kdbus_item *kdbus_item_set(struct kdbus_item *item, u64 type,
+                                 const void *data, size_t len);
+
+#endif
diff --git a/ipc/kdbus/limits.h b/ipc/kdbus/limits.h
new file mode 100644 (file)
index 0000000..59e3608
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * 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
+ *
+ * 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_DEFAULTS_H
+#define __KDBUS_DEFAULTS_H
+
+#include <linux/kernel.h>
+
+/* maximum size of message header and items */
+#define KDBUS_MSG_MAX_SIZE             SZ_8K
+
+/* maximum number of memfd items per message */
+#define KDBUS_MSG_MAX_MEMFD_ITEMS      16
+
+/* max size of ioctl command data */
+#define KDBUS_CMD_MAX_SIZE             SZ_32K
+
+/* maximum number of inflight fds in a target queue per user */
+#define KDBUS_CONN_MAX_FDS_PER_USER    16
+
+/* maximum message payload size */
+#define KDBUS_MSG_MAX_PAYLOAD_VEC_SIZE         SZ_2M
+
+/* maximum size of bloom bit field in bytes */
+#define KDBUS_BUS_BLOOM_MAX_SIZE               SZ_4K
+
+/* maximum length of well-known bus name */
+#define KDBUS_NAME_MAX_LEN                     255
+
+/* maximum length of bus, domain, ep name */
+#define KDBUS_SYSNAME_MAX_LEN                  63
+
+/* maximum number of matches per connection */
+#define KDBUS_MATCH_MAX                                256
+
+/* maximum number of queued messages from the same individual user */
+#define KDBUS_CONN_MAX_MSGS                    256
+
+/* maximum number of well-known names per connection */
+#define KDBUS_CONN_MAX_NAMES                   256
+
+/* maximum number of queued requests waiting for a reply */
+#define KDBUS_CONN_MAX_REQUESTS_PENDING                1024
+
+/* maximum number of connections per user in one domain */
+#define KDBUS_USER_MAX_CONN                    1024
+
+/* maximum number of buses per user in one domain */
+#define KDBUS_USER_MAX_BUSES                   16
+
+#endif
diff --git a/ipc/kdbus/main.c b/ipc/kdbus/main.c
new file mode 100644 (file)
index 0000000..1ad4dc8
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * 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
+ *
+ * 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.
+ */
+
+#define pr_fmt(fmt)    KBUILD_MODNAME ": " fmt
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/module.h>
+
+#include "util.h"
+#include "fs.h"
+#include "handle.h"
+#include "metadata.h"
+#include "node.h"
+
+/*
+ * This is a simplified outline of the internal kdbus object relations, for
+ * those interested in the inner life of the driver implementation.
+ *
+ * From a mount point's (domain's) perspective:
+ *
+ * struct kdbus_domain
+ *   |» struct kdbus_user *user (many, owned)
+ *   '» struct kdbus_node node (embedded)
+ *       |» struct kdbus_node children (many, referenced)
+ *       |» struct kdbus_node *parent (pinned)
+ *       '» struct kdbus_bus (many, pinned)
+ *           |» struct kdbus_node node (embedded)
+ *           '» struct kdbus_ep (many, pinned)
+ *               |» struct kdbus_node node (embedded)
+ *               |» struct kdbus_bus *bus (pinned)
+ *               |» struct kdbus_conn conn_list (many, pinned)
+ *               |   |» struct kdbus_ep *ep (pinned)
+ *               |   |» struct kdbus_name_entry *activator_of (owned)
+ *               |   |» struct kdbus_match_db *match_db (owned)
+ *               |   |» struct kdbus_meta *meta (owned)
+ *               |   |» struct kdbus_match_db *match_db (owned)
+ *               |   |    '» struct kdbus_match_entry (many, owned)
+ *               |   |
+ *               |   |» struct kdbus_pool *pool (owned)
+ *               |   |    '» struct kdbus_pool_slice *slices (many, owned)
+ *               |   |       '» struct kdbus_pool *pool (pinned)
+ *               |   |
+ *               |   |» struct kdbus_user *user (pinned)
+ *               |   `» struct kdbus_queue_entry entries (many, embedded)
+ *               |        |» struct kdbus_pool_slice *slice (pinned)
+ *               |        |» struct kdbus_conn_reply *reply (owned)
+ *               |        '» struct kdbus_user *user (pinned)
+ *               |
+ *               '» struct kdbus_user *user (pinned)
+ *                   '» struct kdbus_policy_db policy_db (embedded)
+ *                        |» struct kdbus_policy_db_entry (many, owned)
+ *                        |   |» struct kdbus_conn (pinned)
+ *                        |   '» struct kdbus_ep (pinned)
+ *                        |
+ *                        '» struct kdbus_policy_db_cache_entry (many, owned)
+ *                            '» struct kdbus_conn (pinned)
+ *
+ * For the life-time of a file descriptor derived from calling open() on a file
+ * inside the mount point:
+ *
+ * struct kdbus_handle
+ *  |» struct kdbus_meta *meta (owned)
+ *  |» struct kdbus_ep *ep (pinned)
+ *  |» struct kdbus_conn *conn (owned)
+ *  '» struct kdbus_ep *ep (owned)
+ */
+
+/* kdbus mount-point /sys/fs/kdbus */
+static struct kobject *kdbus_dir;
+
+static int __init kdbus_init(void)
+{
+       int ret;
+
+       kdbus_dir = kobject_create_and_add(KBUILD_MODNAME, fs_kobj);
+       if (!kdbus_dir)
+               return -ENOMEM;
+
+       ret = kdbus_fs_init();
+       if (ret < 0) {
+               pr_err("cannot register filesystem: %d\n", ret);
+               goto exit_dir;
+       }
+
+       pr_info("initialized\n");
+       return 0;
+
+exit_dir:
+       kobject_put(kdbus_dir);
+       return ret;
+}
+
+static void __exit kdbus_exit(void)
+{
+       kdbus_fs_exit();
+       kobject_put(kdbus_dir);
+       ida_destroy(&kdbus_node_ida);
+}
+
+module_init(kdbus_init);
+module_exit(kdbus_exit);
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("D-Bus, powerful, easy to use interprocess communication");
+MODULE_ALIAS_FS(KBUILD_MODNAME "fs");
diff --git a/ipc/kdbus/match.c b/ipc/kdbus/match.c
new file mode 100644 (file)
index 0000000..4ee6a1f
--- /dev/null
@@ -0,0 +1,546 @@
+/*
+ * 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/hash.h>
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#include "bus.h"
+#include "connection.h"
+#include "endpoint.h"
+#include "handle.h"
+#include "item.h"
+#include "match.h"
+#include "message.h"
+#include "names.h"
+
+/**
+ * struct kdbus_match_db - message filters
+ * @entries_list:      List of matches
+ * @mdb_rwlock:                Match data lock
+ * @entries_count:     Number of entries in database
+ */
+struct kdbus_match_db {
+       struct list_head entries_list;
+       struct rw_semaphore mdb_rwlock;
+       unsigned int entries_count;
+};
+
+/**
+ * struct kdbus_match_entry - a match database entry
+ * @cookie:            User-supplied cookie to lookup the entry
+ * @list_entry:                The list entry element for the db list
+ * @rules_list:                The list head for tracking rules of this entry
+ */
+struct kdbus_match_entry {
+       u64 cookie;
+       struct list_head list_entry;
+       struct list_head rules_list;
+};
+
+/**
+ * struct kdbus_bloom_mask - mask to match against filter
+ * @generations:       Number of generations carried
+ * @data:              Array of bloom bit fields
+ */
+struct kdbus_bloom_mask {
+       u64 generations;
+       u64 *data;
+};
+
+/**
+ * struct kdbus_match_rule - a rule appended to a match entry
+ * @type:              An item type to match against
+ * @bloom_mask:                Bloom mask to match a message's filter against, used
+ *                     with KDBUS_ITEM_BLOOM_MASK
+ * @name:              Name to match against, used with KDBUS_ITEM_NAME,
+ *                     KDBUS_ITEM_NAME_{ADD,REMOVE,CHANGE}
+ * @old_id:            ID to match against, used with
+ *                     KDBUS_ITEM_NAME_{ADD,REMOVE,CHANGE},
+ *                     KDBUS_ITEM_ID_REMOVE
+ * @new_id:            ID to match against, used with
+ *                     KDBUS_ITEM_NAME_{ADD,REMOVE,CHANGE},
+ *                     KDBUS_ITEM_ID_REMOVE
+ * @src_id:            ID to match against, used with KDBUS_ITEM_ID
+ * @dst_id:            Message destination ID, used with KDBUS_ITEM_DST_ID
+ * @rules_entry:       Entry in the entry's rules list
+ */
+struct kdbus_match_rule {
+       u64 type;
+       union {
+               struct kdbus_bloom_mask bloom_mask;
+               struct {
+                       char *name;
+                       u64 old_id;
+                       u64 new_id;
+               };
+               u64 src_id;
+               u64 dst_id;
+       };
+       struct list_head rules_entry;
+};
+
+static void kdbus_match_rule_free(struct kdbus_match_rule *rule)
+{
+       if (!rule)
+               return;
+
+       switch (rule->type) {
+       case KDBUS_ITEM_BLOOM_MASK:
+               kfree(rule->bloom_mask.data);
+               break;
+
+       case KDBUS_ITEM_NAME:
+       case KDBUS_ITEM_NAME_ADD:
+       case KDBUS_ITEM_NAME_REMOVE:
+       case KDBUS_ITEM_NAME_CHANGE:
+               kfree(rule->name);
+               break;
+
+       case KDBUS_ITEM_ID:
+       case KDBUS_ITEM_DST_ID:
+       case KDBUS_ITEM_ID_ADD:
+       case KDBUS_ITEM_ID_REMOVE:
+               break;
+
+       default:
+               BUG();
+       }
+
+       list_del(&rule->rules_entry);
+       kfree(rule);
+}
+
+static void kdbus_match_entry_free(struct kdbus_match_entry *entry)
+{
+       struct kdbus_match_rule *r, *tmp;
+
+       if (!entry)
+               return;
+
+       list_for_each_entry_safe(r, tmp, &entry->rules_list, rules_entry)
+               kdbus_match_rule_free(r);
+
+       list_del(&entry->list_entry);
+       kfree(entry);
+}
+
+/**
+ * kdbus_match_db_free() - free match db resources
+ * @mdb:               The match database
+ */
+void kdbus_match_db_free(struct kdbus_match_db *mdb)
+{
+       struct kdbus_match_entry *entry, *tmp;
+
+       if (!mdb)
+               return;
+
+       list_for_each_entry_safe(entry, tmp, &mdb->entries_list, list_entry)
+               kdbus_match_entry_free(entry);
+
+       kfree(mdb);
+}
+
+/**
+ * kdbus_match_db_new() - create a new match database
+ *
+ * Return: a new kdbus_match_db on success, ERR_PTR on failure.
+ */
+struct kdbus_match_db *kdbus_match_db_new(void)
+{
+       struct kdbus_match_db *d;
+
+       d = kzalloc(sizeof(*d), GFP_KERNEL);
+       if (!d)
+               return ERR_PTR(-ENOMEM);
+
+       init_rwsem(&d->mdb_rwlock);
+       INIT_LIST_HEAD(&d->entries_list);
+
+       return d;
+}
+
+static bool kdbus_match_bloom(const struct kdbus_bloom_filter *filter,
+                             const struct kdbus_bloom_mask *mask,
+                             const struct kdbus_conn *conn)
+{
+       size_t n = conn->ep->bus->bloom.size / sizeof(u64);
+       const u64 *m;
+       size_t i;
+
+       /*
+        * The message's filter carries a generation identifier, the
+        * match's mask possibly carries an array of multiple generations
+        * of the mask. Select the mask with the closest match of the
+        * filter's generation.
+        */
+       m = mask->data + (min(filter->generation, mask->generations - 1) * n);
+
+       /*
+        * The message's filter contains the messages properties,
+        * the match's mask contains the properties to look for in the
+        * message. Check the mask bit field against the filter bit field,
+        * if the message possibly carries the properties the connection
+        * has subscribed to.
+        */
+       for (i = 0; i < n; i++)
+               if ((filter->data[i] & m[i]) != m[i])
+                       return false;
+
+       return true;
+}
+
+static bool kdbus_match_rule_conn(const struct kdbus_match_rule *r,
+                                 struct kdbus_conn *c,
+                                 const struct kdbus_staging *s)
+{
+       lockdep_assert_held(&c->ep->bus->name_registry->rwlock);
+
+       switch (r->type) {
+       case KDBUS_ITEM_BLOOM_MASK:
+               return kdbus_match_bloom(s->bloom_filter, &r->bloom_mask, c);
+       case KDBUS_ITEM_ID:
+               return r->src_id == c->id || r->src_id == KDBUS_MATCH_ID_ANY;
+       case KDBUS_ITEM_DST_ID:
+               return r->dst_id == s->msg->dst_id ||
+                      r->dst_id == KDBUS_MATCH_ID_ANY;
+       case KDBUS_ITEM_NAME:
+               return kdbus_conn_has_name(c, r->name);
+       default:
+               return false;
+       }
+}
+
+static bool kdbus_match_rule_kernel(const struct kdbus_match_rule *r,
+                                   const struct kdbus_staging *s)
+{
+       struct kdbus_item *n = s->notify;
+
+       if (WARN_ON(!n) || n->type != r->type)
+               return false;
+
+       switch (r->type) {
+       case KDBUS_ITEM_ID_ADD:
+               return r->new_id == KDBUS_MATCH_ID_ANY ||
+                      r->new_id == n->id_change.id;
+       case KDBUS_ITEM_ID_REMOVE:
+               return r->old_id == KDBUS_MATCH_ID_ANY ||
+                      r->old_id == n->id_change.id;
+       case KDBUS_ITEM_NAME_ADD:
+       case KDBUS_ITEM_NAME_CHANGE:
+       case KDBUS_ITEM_NAME_REMOVE:
+               return (r->old_id == KDBUS_MATCH_ID_ANY ||
+                       r->old_id == n->name_change.old_id.id) &&
+                      (r->new_id == KDBUS_MATCH_ID_ANY ||
+                       r->new_id == n->name_change.new_id.id) &&
+                      (!r->name || !strcmp(r->name, n->name_change.name));
+       default:
+               return false;
+       }
+}
+
+static bool kdbus_match_rules(const struct kdbus_match_entry *entry,
+                             struct kdbus_conn *c,
+                             const struct kdbus_staging *s)
+{
+       struct kdbus_match_rule *r;
+
+       list_for_each_entry(r, &entry->rules_list, rules_entry)
+               if ((c && !kdbus_match_rule_conn(r, c, s)) ||
+                   (!c && !kdbus_match_rule_kernel(r, s)))
+                       return false;
+
+       return true;
+}
+
+/**
+ * kdbus_match_db_match_msg() - match a msg object agains the database entries
+ * @mdb:               The match database
+ * @conn_src:          The connection object originating the message
+ * @staging:           Staging object containing the message to match against
+ *
+ * This function will walk through all the database entries previously uploaded
+ * with kdbus_match_db_add(). As soon as any of them has an all-satisfied rule
+ * set, this function will return true.
+ *
+ * The caller must hold the registry lock of conn_src->ep->bus, in case conn_src
+ * is non-NULL.
+ *
+ * Return: true if there was a matching database entry, false otherwise.
+ */
+bool kdbus_match_db_match_msg(struct kdbus_match_db *mdb,
+                             struct kdbus_conn *conn_src,
+                             const struct kdbus_staging *staging)
+{
+       struct kdbus_match_entry *entry;
+       bool matched = false;
+
+       down_read(&mdb->mdb_rwlock);
+       list_for_each_entry(entry, &mdb->entries_list, list_entry) {
+               matched = kdbus_match_rules(entry, conn_src, staging);
+               if (matched)
+                       break;
+       }
+       up_read(&mdb->mdb_rwlock);
+
+       return matched;
+}
+
+static int kdbus_match_db_remove_unlocked(struct kdbus_match_db *mdb,
+                                         u64 cookie)
+{
+       struct kdbus_match_entry *entry, *tmp;
+       bool found = false;
+
+       list_for_each_entry_safe(entry, tmp, &mdb->entries_list, list_entry)
+               if (entry->cookie == cookie) {
+                       kdbus_match_entry_free(entry);
+                       --mdb->entries_count;
+                       found = true;
+               }
+
+       return found ? 0 : -EBADSLT;
+}
+
+/**
+ * kdbus_cmd_match_add() - handle KDBUS_CMD_MATCH_ADD
+ * @conn:              connection to operate on
+ * @argp:              command payload
+ *
+ * One call to this function (or one ioctl(KDBUS_CMD_MATCH_ADD), respectively,
+ * adds one new database entry with n rules attached to it. Each rule is
+ * described with an kdbus_item, and an entry is considered matching if all
+ * its rules are satisfied.
+ *
+ * The items attached to a kdbus_cmd_match struct have the following mapping:
+ *
+ * KDBUS_ITEM_BLOOM_MASK:      A bloom mask
+ * KDBUS_ITEM_NAME:            A connection's source name
+ * KDBUS_ITEM_ID:              A connection ID
+ * KDBUS_ITEM_DST_ID:          A connection ID
+ * KDBUS_ITEM_NAME_ADD:
+ * KDBUS_ITEM_NAME_REMOVE:
+ * KDBUS_ITEM_NAME_CHANGE:     Well-known name changes, carry
+ *                             kdbus_notify_name_change
+ * KDBUS_ITEM_ID_ADD:
+ * KDBUS_ITEM_ID_REMOVE:       Connection ID changes, carry
+ *                             kdbus_notify_id_change
+ *
+ * For kdbus_notify_{id,name}_change structs, only the ID and name fields
+ * are looked at when adding an entry. The flags are unused.
+ *
+ * Also note that KDBUS_ITEM_BLOOM_MASK, KDBUS_ITEM_NAME, KDBUS_ITEM_ID,
+ * and KDBUS_ITEM_DST_ID are used to match messages from userspace, while the
+ * others apply to kernel-generated notifications.
+ *
+ * Return: >=0 on success, negative error code on failure.
+ */
+int kdbus_cmd_match_add(struct kdbus_conn *conn, void __user *argp)
+{
+       struct kdbus_match_db *mdb = conn->match_db;
+       struct kdbus_match_entry *entry = NULL;
+       struct kdbus_cmd_match *cmd;
+       struct kdbus_item *item;
+       int ret;
+
+       struct kdbus_arg argv[] = {
+               { .type = KDBUS_ITEM_NEGOTIATE },
+               { .type = KDBUS_ITEM_BLOOM_MASK, .multiple = true },
+               { .type = KDBUS_ITEM_NAME, .multiple = true },
+               { .type = KDBUS_ITEM_ID, .multiple = true },
+               { .type = KDBUS_ITEM_DST_ID, .multiple = true },
+               { .type = KDBUS_ITEM_NAME_ADD, .multiple = true },
+               { .type = KDBUS_ITEM_NAME_REMOVE, .multiple = true },
+               { .type = KDBUS_ITEM_NAME_CHANGE, .multiple = true },
+               { .type = KDBUS_ITEM_ID_ADD, .multiple = true },
+               { .type = KDBUS_ITEM_ID_REMOVE, .multiple = true },
+       };
+       struct kdbus_args args = {
+               .allowed_flags = KDBUS_FLAG_NEGOTIATE |
+                                KDBUS_MATCH_REPLACE,
+               .argv = argv,
+               .argc = ARRAY_SIZE(argv),
+       };
+
+       if (!kdbus_conn_is_ordinary(conn))
+               return -EOPNOTSUPP;
+
+       ret = kdbus_args_parse(&args, argp, &cmd);
+       if (ret != 0)
+               return ret;
+
+       entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+       if (!entry) {
+               ret = -ENOMEM;
+               goto exit;
+       }
+
+       entry->cookie = cmd->cookie;
+       INIT_LIST_HEAD(&entry->list_entry);
+       INIT_LIST_HEAD(&entry->rules_list);
+
+       KDBUS_ITEMS_FOREACH(item, cmd->items, KDBUS_ITEMS_SIZE(cmd, items)) {
+               struct kdbus_match_rule *rule;
+               size_t size = item->size - offsetof(struct kdbus_item, data);
+
+               rule = kzalloc(sizeof(*rule), GFP_KERNEL);
+               if (!rule) {
+                       ret = -ENOMEM;
+                       goto exit;
+               }
+
+               rule->type = item->type;
+               INIT_LIST_HEAD(&rule->rules_entry);
+
+               switch (item->type) {
+               case KDBUS_ITEM_BLOOM_MASK: {
+                       u64 bsize = conn->ep->bus->bloom.size;
+                       u64 generations;
+                       u64 remainder;
+
+                       generations = div64_u64_rem(size, bsize, &remainder);
+                       if (size < bsize || remainder > 0) {
+                               ret = -EDOM;
+                               break;
+                       }
+
+                       rule->bloom_mask.data = kmemdup(item->data,
+                                                       size, GFP_KERNEL);
+                       if (!rule->bloom_mask.data) {
+                               ret = -ENOMEM;
+                               break;
+                       }
+
+                       rule->bloom_mask.generations = generations;
+                       break;
+               }
+
+               case KDBUS_ITEM_NAME:
+                       if (!kdbus_name_is_valid(item->str, false)) {
+                               ret = -EINVAL;
+                               break;
+                       }
+
+                       rule->name = kstrdup(item->str, GFP_KERNEL);
+                       if (!rule->name)
+                               ret = -ENOMEM;
+
+                       break;
+
+               case KDBUS_ITEM_ID:
+                       rule->src_id = item->id;
+                       break;
+
+               case KDBUS_ITEM_DST_ID:
+                       rule->dst_id = item->id;
+                       break;
+
+               case KDBUS_ITEM_NAME_ADD:
+               case KDBUS_ITEM_NAME_REMOVE:
+               case KDBUS_ITEM_NAME_CHANGE:
+                       rule->old_id = item->name_change.old_id.id;
+                       rule->new_id = item->name_change.new_id.id;
+
+                       if (size > sizeof(struct kdbus_notify_name_change)) {
+                               rule->name = kstrdup(item->name_change.name,
+                                                    GFP_KERNEL);
+                               if (!rule->name)
+                                       ret = -ENOMEM;
+                       }
+
+                       break;
+
+               case KDBUS_ITEM_ID_ADD:
+               case KDBUS_ITEM_ID_REMOVE:
+                       if (item->type == KDBUS_ITEM_ID_ADD)
+                               rule->new_id = item->id_change.id;
+                       else
+                               rule->old_id = item->id_change.id;
+
+                       break;
+               }
+
+               if (ret < 0) {
+                       kdbus_match_rule_free(rule);
+                       goto exit;
+               }
+
+               list_add_tail(&rule->rules_entry, &entry->rules_list);
+       }
+
+       down_write(&mdb->mdb_rwlock);
+
+       /* Remove any entry that has the same cookie as the current one. */
+       if (cmd->flags & KDBUS_MATCH_REPLACE)
+               kdbus_match_db_remove_unlocked(mdb, entry->cookie);
+
+       /*
+        * If the above removal caught any entry, there will be room for the
+        * new one.
+        */
+       if (++mdb->entries_count > KDBUS_MATCH_MAX) {
+               --mdb->entries_count;
+               ret = -EMFILE;
+       } else {
+               list_add_tail(&entry->list_entry, &mdb->entries_list);
+               entry = NULL;
+       }
+
+       up_write(&mdb->mdb_rwlock);
+
+exit:
+       kdbus_match_entry_free(entry);
+       return kdbus_args_clear(&args, ret);
+}
+
+/**
+ * kdbus_cmd_match_remove() - handle KDBUS_CMD_MATCH_REMOVE
+ * @conn:              connection to operate on
+ * @argp:              command payload
+ *
+ * Return: >=0 on success, negative error code on failure.
+ */
+int kdbus_cmd_match_remove(struct kdbus_conn *conn, void __user *argp)
+{
+       struct kdbus_cmd_match *cmd;
+       int ret;
+
+       struct kdbus_arg argv[] = {
+               { .type = KDBUS_ITEM_NEGOTIATE },
+       };
+       struct kdbus_args args = {
+               .allowed_flags = KDBUS_FLAG_NEGOTIATE,
+               .argv = argv,
+               .argc = ARRAY_SIZE(argv),
+       };
+
+       if (!kdbus_conn_is_ordinary(conn))
+               return -EOPNOTSUPP;
+
+       ret = kdbus_args_parse(&args, argp, &cmd);
+       if (ret != 0)
+               return ret;
+
+       down_write(&conn->match_db->mdb_rwlock);
+       ret = kdbus_match_db_remove_unlocked(conn->match_db, cmd->cookie);
+       up_write(&conn->match_db->mdb_rwlock);
+
+       return kdbus_args_clear(&args, ret);
+}
diff --git a/ipc/kdbus/match.h b/ipc/kdbus/match.h
new file mode 100644 (file)
index 0000000..ceb492f
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+#ifndef __KDBUS_MATCH_H
+#define __KDBUS_MATCH_H
+
+struct kdbus_conn;
+struct kdbus_match_db;
+struct kdbus_staging;
+
+struct kdbus_match_db *kdbus_match_db_new(void);
+void kdbus_match_db_free(struct kdbus_match_db *db);
+int kdbus_match_db_add(struct kdbus_conn *conn,
+                      struct kdbus_cmd_match *cmd);
+int kdbus_match_db_remove(struct kdbus_conn *conn,
+                         struct kdbus_cmd_match *cmd);
+bool kdbus_match_db_match_msg(struct kdbus_match_db *db,
+                             struct kdbus_conn *conn_src,
+                             const struct kdbus_staging *staging);
+
+int kdbus_cmd_match_add(struct kdbus_conn *conn, void __user *argp);
+int kdbus_cmd_match_remove(struct kdbus_conn *conn, void __user *argp);
+
+#endif
diff --git a/ipc/kdbus/message.c b/ipc/kdbus/message.c
new file mode 100644 (file)
index 0000000..66b9325
--- /dev/null
@@ -0,0 +1,1033 @@
+/*
+ * 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/capability.h>
+#include <linux/cgroup.h>
+#include <linux/cred.h>
+#include <linux/file.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/shmem_fs.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <net/sock.h>
+
+#include "bus.h"
+#include "connection.h"
+#include "domain.h"
+#include "endpoint.h"
+#include "handle.h"
+#include "item.h"
+#include "match.h"
+#include "message.h"
+#include "names.h"
+#include "policy.h"
+
+static const char * const zeros = "\0\0\0\0\0\0\0";
+
+static struct kdbus_gaps *kdbus_gaps_new(size_t n_memfds, size_t n_fds)
+{
+       size_t size_offsets, size_memfds, size_fds, size;
+       struct kdbus_gaps *gaps;
+
+       size_offsets = n_memfds * sizeof(*gaps->memfd_offsets);
+       size_memfds = n_memfds * sizeof(*gaps->memfd_files);
+       size_fds = n_fds * sizeof(*gaps->fd_files);
+       size = sizeof(*gaps) + size_offsets + size_memfds + size_fds;
+
+       gaps = kzalloc(size, GFP_KERNEL);
+       if (!gaps)
+               return ERR_PTR(-ENOMEM);
+
+       kref_init(&gaps->kref);
+       gaps->n_memfds = 0; /* we reserve n_memfds, but don't enforce them */
+       gaps->memfd_offsets = (void *)(gaps + 1);
+       gaps->memfd_files = (void *)((u8 *)gaps->memfd_offsets + size_offsets);
+       gaps->n_fds = 0; /* we reserve n_fds, but don't enforce them */
+       gaps->fd_files = (void *)((u8 *)gaps->memfd_files + size_memfds);
+
+       return gaps;
+}
+
+static void kdbus_gaps_free(struct kref *kref)
+{
+       struct kdbus_gaps *gaps = container_of(kref, struct kdbus_gaps, kref);
+       size_t i;
+
+       for (i = 0; i < gaps->n_fds; ++i)
+               if (gaps->fd_files[i])
+                       fput(gaps->fd_files[i]);
+       for (i = 0; i < gaps->n_memfds; ++i)
+               if (gaps->memfd_files[i])
+                       fput(gaps->memfd_files[i]);
+
+       kfree(gaps);
+}
+
+/**
+ * kdbus_gaps_ref() - gain reference
+ * @gaps:      gaps object
+ *
+ * Return: @gaps is returned
+ */
+struct kdbus_gaps *kdbus_gaps_ref(struct kdbus_gaps *gaps)
+{
+       if (gaps)
+               kref_get(&gaps->kref);
+       return gaps;
+}
+
+/**
+ * kdbus_gaps_unref() - drop reference
+ * @gaps:      gaps object
+ *
+ * Return: NULL
+ */
+struct kdbus_gaps *kdbus_gaps_unref(struct kdbus_gaps *gaps)
+{
+       if (gaps)
+               kref_put(&gaps->kref, kdbus_gaps_free);
+       return NULL;
+}
+
+/**
+ * kdbus_gaps_install() - install file-descriptors
+ * @gaps:              gaps object, or NULL
+ * @slice:             pool slice that contains the message
+ * @out_incomplete     output variable to note incomplete fds
+ *
+ * This function installs all file-descriptors of @gaps into the current
+ * process and copies the file-descriptor numbers into the target pool slice.
+ *
+ * If the file-descriptors were only partially installed, then @out_incomplete
+ * will be set to true. Otherwise, it's set to false.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int kdbus_gaps_install(struct kdbus_gaps *gaps, struct kdbus_pool_slice *slice,
+                      bool *out_incomplete)
+{
+       bool incomplete_fds = false;
+       struct kvec kvec;
+       size_t i, n_fds;
+       int ret, *fds;
+
+       if (!gaps) {
+               /* nothing to do */
+               *out_incomplete = incomplete_fds;
+               return 0;
+       }
+
+       n_fds = gaps->n_fds + gaps->n_memfds;
+       if (n_fds < 1) {
+               /* nothing to do */
+               *out_incomplete = incomplete_fds;
+               return 0;
+       }
+
+       fds = kmalloc_array(n_fds, sizeof(*fds), GFP_TEMPORARY);
+       n_fds = 0;
+       if (!fds)
+               return -ENOMEM;
+
+       /* 1) allocate fds and copy them over */
+
+       if (gaps->n_fds > 0) {
+               for (i = 0; i < gaps->n_fds; ++i) {
+                       int fd;
+
+                       fd = get_unused_fd_flags(O_CLOEXEC);
+                       if (fd < 0)
+                               incomplete_fds = true;
+
+                       WARN_ON(!gaps->fd_files[i]);
+
+                       fds[n_fds++] = fd < 0 ? -1 : fd;
+               }
+
+               /*
+                * The file-descriptor array can only be present once per
+                * message. Hence, prepare all fds and then copy them over with
+                * a single kvec.
+                */
+
+               WARN_ON(!gaps->fd_offset);
+
+               kvec.iov_base = fds;
+               kvec.iov_len = gaps->n_fds * sizeof(*fds);
+               ret = kdbus_pool_slice_copy_kvec(slice, gaps->fd_offset,
+                                                &kvec, 1, kvec.iov_len);
+               if (ret < 0)
+                       goto exit;
+       }
+
+       for (i = 0; i < gaps->n_memfds; ++i) {
+               int memfd;
+
+               memfd = get_unused_fd_flags(O_CLOEXEC);
+               if (memfd < 0) {
+                       incomplete_fds = true;
+                       /* memfds are initialized to -1, skip copying it */
+                       continue;
+               }
+
+               fds[n_fds++] = memfd;
+
+               /*
+                * memfds have to be copied individually as they each are put
+                * into a separate item. This should not be an issue, though,
+                * as usually there is no need to send more than one memfd per
+                * message.
+                */
+
+               WARN_ON(!gaps->memfd_offsets[i]);
+               WARN_ON(!gaps->memfd_files[i]);
+
+               kvec.iov_base = &memfd;
+               kvec.iov_len = sizeof(memfd);
+               ret = kdbus_pool_slice_copy_kvec(slice, gaps->memfd_offsets[i],
+                                                &kvec, 1, kvec.iov_len);
+               if (ret < 0)
+                       goto exit;
+       }
+
+       /* 2) install fds now that everything was successful */
+
+       for (i = 0; i < gaps->n_fds; ++i)
+               if (fds[i] >= 0)
+                       fd_install(fds[i], get_file(gaps->fd_files[i]));
+       for (i = 0; i < gaps->n_memfds; ++i)
+               if (fds[gaps->n_fds + i] >= 0)
+                       fd_install(fds[gaps->n_fds + i],
+                                  get_file(gaps->memfd_files[i]));
+
+       ret = 0;
+
+exit:
+       if (ret < 0)
+               for (i = 0; i < n_fds; ++i)
+                       put_unused_fd(fds[i]);
+       kfree(fds);
+       *out_incomplete = incomplete_fds;
+       return ret;
+}
+
+static struct file *kdbus_get_fd(int fd)
+{
+       struct file *f, *ret;
+
+       if (fd < 0)
+               return ERR_PTR(-EBADF);
+
+       f = fget_raw(fd);
+       if (!f)
+               return ERR_PTR(-EBADF);
+
+       if (f->f_mode & FMODE_PATH)
+               ret = f; /* O_PATH is always allowed */
+       else if (f->f_op == &kdbus_handle_ops)
+               ret = ERR_PTR(-EOPNOTSUPP); /* disallow kdbus-fd over kdbus */
+       else
+               ret = f; /* all other are allowed */
+
+       if (f != ret)
+               fput(f);
+
+       return ret;
+}
+
+static struct file *kdbus_get_memfd(const struct kdbus_memfd *memfd)
+{
+       const int m = F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_SEAL;
+       struct file *f, *ret;
+       int s;
+
+       if (memfd->fd < 0)
+               return ERR_PTR(-EBADF);
+
+       f = fget(memfd->fd);
+       if (!f)
+               return ERR_PTR(-EBADF);
+
+       s = shmem_get_seals(f);
+       if (s < 0)
+               ret = ERR_PTR(-EMEDIUMTYPE);
+       else if ((s & m) != m)
+               ret = ERR_PTR(-ETXTBSY);
+       else if (memfd->start + memfd->size > (u64)i_size_read(file_inode(f)))
+               ret = ERR_PTR(-EFAULT);
+       else
+               ret = f;
+
+       if (f != ret)
+               fput(f);
+
+       return ret;
+}
+
+static int kdbus_msg_examine(struct kdbus_msg *msg, struct kdbus_bus *bus,
+                            struct kdbus_cmd_send *cmd, size_t *out_n_memfds,
+                            size_t *out_n_fds, size_t *out_n_parts)
+{
+       struct kdbus_item *item, *fds = NULL, *bloom = NULL, *dstname = NULL;
+       u64 n_parts, n_memfds, n_fds, vec_size;
+
+       /*
+        * Step 1:
+        * Validate the message and command parameters.
+        */
+
+       /* KDBUS_PAYLOAD_KERNEL is reserved to kernel messages */
+       if (msg->payload_type == KDBUS_PAYLOAD_KERNEL)
+               return -EINVAL;
+
+       if (msg->dst_id == KDBUS_DST_ID_BROADCAST) {
+               /* broadcasts must be marked as signals */
+               if (!(msg->flags & KDBUS_MSG_SIGNAL))
+                       return -EBADMSG;
+               /* broadcasts cannot have timeouts */
+               if (msg->timeout_ns > 0)
+                       return -ENOTUNIQ;
+       }
+
+       if (msg->flags & KDBUS_MSG_EXPECT_REPLY) {
+               /* if you expect a reply, you must specify a timeout */
+               if (msg->timeout_ns == 0)
+                       return -EINVAL;
+               /* signals cannot have replies */
+               if (msg->flags & KDBUS_MSG_SIGNAL)
+                       return -ENOTUNIQ;
+       } else {
+               /* must expect reply if sent as synchronous call */
+               if (cmd->flags & KDBUS_SEND_SYNC_REPLY)
+                       return -EINVAL;
+               /* cannot mark replies as signal */
+               if (msg->cookie_reply && (msg->flags & KDBUS_MSG_SIGNAL))
+                       return -EINVAL;
+       }
+
+       /*
+        * Step 2:
+        * Validate all passed items. While at it, select some statistics that
+        * are required to allocate state objects later on.
+        *
+        * Generic item validation has already been done via
+        * kdbus_item_validate(). Furthermore, the number of items is naturally
+        * limited by the maximum message size. Hence, only non-generic item
+        * checks are performed here (mainly integer overflow tests).
+        */
+
+       n_parts = 0;
+       n_memfds = 0;
+       n_fds = 0;
+       vec_size = 0;
+
+       KDBUS_ITEMS_FOREACH(item, msg->items, KDBUS_ITEMS_SIZE(msg, items)) {
+               switch (item->type) {
+               case KDBUS_ITEM_PAYLOAD_VEC: {
+                       void __force __user *ptr = KDBUS_PTR(item->vec.address);
+                       u64 size = item->vec.size;
+
+                       if (vec_size + size < vec_size)
+                               return -EMSGSIZE;
+                       if (vec_size + size > KDBUS_MSG_MAX_PAYLOAD_VEC_SIZE)
+                               return -EMSGSIZE;
+                       if (ptr && unlikely(!access_ok(VERIFY_READ, ptr, size)))
+                               return -EFAULT;
+
+                       if (ptr || size % 8) /* data or padding */
+                               ++n_parts;
+                       break;
+               }
+               case KDBUS_ITEM_PAYLOAD_MEMFD: {
+                       u64 start = item->memfd.start;
+                       u64 size = item->memfd.size;
+
+                       if (start + size < start)
+                               return -EMSGSIZE;
+                       if (n_memfds >= KDBUS_MSG_MAX_MEMFD_ITEMS)
+                               return -E2BIG;
+
+                       ++n_memfds;
+                       if (size % 8) /* vec-padding required */
+                               ++n_parts;
+                       break;
+               }
+               case KDBUS_ITEM_FDS: {
+                       if (fds)
+                               return -EEXIST;
+
+                       fds = item;
+                       n_fds = KDBUS_ITEM_PAYLOAD_SIZE(item) / sizeof(int);
+                       if (n_fds > KDBUS_CONN_MAX_FDS_PER_USER)
+                               return -EMFILE;
+
+                       break;
+               }
+               case KDBUS_ITEM_BLOOM_FILTER: {
+                       u64 bloom_size;
+
+                       if (bloom)
+                               return -EEXIST;
+
+                       bloom = item;
+                       bloom_size = KDBUS_ITEM_PAYLOAD_SIZE(item) -
+                                    offsetof(struct kdbus_bloom_filter, data);
+                       if (!KDBUS_IS_ALIGNED8(bloom_size))
+                               return -EFAULT;
+                       if (bloom_size != bus->bloom.size)
+                               return -EDOM;
+
+                       break;
+               }
+               case KDBUS_ITEM_DST_NAME: {
+                       if (dstname)
+                               return -EEXIST;
+
+                       dstname = item;
+                       if (!kdbus_name_is_valid(item->str, false))
+                               return -EINVAL;
+                       if (msg->dst_id == KDBUS_DST_ID_BROADCAST)
+                               return -EBADMSG;
+
+                       break;
+               }
+               default:
+                       return -EINVAL;
+               }
+       }
+
+       /*
+        * Step 3:
+        * Validate that required items were actually passed, and that no item
+        * contradicts the message flags.
+        */
+
+       /* bloom filters must be attached _iff_ it's a signal */
+       if (!(msg->flags & KDBUS_MSG_SIGNAL) != !bloom)
+               return -EBADMSG;
+       /* destination name is required if no ID is given */
+       if (msg->dst_id == KDBUS_DST_ID_NAME && !dstname)
+               return -EDESTADDRREQ;
+       /* cannot send file-descriptors attached to broadcasts */
+       if (msg->dst_id == KDBUS_DST_ID_BROADCAST && fds)
+               return -ENOTUNIQ;
+
+       *out_n_memfds = n_memfds;
+       *out_n_fds = n_fds;
+       *out_n_parts = n_parts;
+
+       return 0;
+}
+
+static bool kdbus_staging_merge_vecs(struct kdbus_staging *staging,
+                                    struct kdbus_item **prev_item,
+                                    struct iovec **prev_vec,
+                                    const struct kdbus_item *merge)
+{
+       void __user *ptr = (void __user *)KDBUS_PTR(merge->vec.address);
+       u64 padding = merge->vec.size % 8;
+       struct kdbus_item *prev = *prev_item;
+       struct iovec *vec = *prev_vec;
+
+       /* XXX: merging is disabled so far */
+       if (0 && prev && prev->type == KDBUS_ITEM_PAYLOAD_OFF &&
+           !merge->vec.address == !prev->vec.address) {
+               /*
+                * If we merge two VECs, we can always drop the second
+                * PAYLOAD_VEC item. Hence, include its size in the previous
+                * one.
+                */
+               prev->vec.size += merge->vec.size;
+
+               if (ptr) {
+                       /*
+                        * If we merge two data VECs, we need two iovecs to copy
+                        * the data. But the items can be easily merged by
+                        * summing their lengths.
+                        */
+                       vec = &staging->parts[staging->n_parts++];
+                       vec->iov_len = merge->vec.size;
+                       vec->iov_base = ptr;
+                       staging->n_payload += vec->iov_len;
+               } else if (padding) {
+                       /*
+                        * If we merge two 0-vecs with the second 0-vec
+                        * requiring padding, we need to insert an iovec to copy
+                        * the 0-padding. We try merging it with the previous
+                        * 0-padding iovec. This might end up with an
+                        * iov_len==0, in which case we simply drop the iovec.
+                        */
+                       if (vec) {
+                               staging->n_payload -= vec->iov_len;
+                               vec->iov_len = prev->vec.size % 8;
+                               if (!vec->iov_len) {
+                                       --staging->n_parts;
+                                       vec = NULL;
+                               } else {
+                                       staging->n_payload += vec->iov_len;
+                               }
+                       } else {
+                               vec = &staging->parts[staging->n_parts++];
+                               vec->iov_len = padding;
+                               vec->iov_base = (char __user *)zeros;
+                               staging->n_payload += vec->iov_len;
+                       }
+               } else {
+                       /*
+                        * If we merge two 0-vecs with the second 0-vec having
+                        * no padding, we know the padding of the first stays
+                        * the same. Hence, @vec needs no adjustment.
+                        */
+               }
+
+               /* successfully merged with previous item */
+               merge = prev;
+       } else {
+               /*
+                * If we cannot merge the payload item with the previous one,
+                * we simply insert a new iovec for the data/padding.
+                */
+               if (ptr) {
+                       vec = &staging->parts[staging->n_parts++];
+                       vec->iov_len = merge->vec.size;
+                       vec->iov_base = ptr;
+                       staging->n_payload += vec->iov_len;
+               } else if (padding) {
+                       vec = &staging->parts[staging->n_parts++];
+                       vec->iov_len = padding;
+                       vec->iov_base = (char __user *)zeros;
+                       staging->n_payload += vec->iov_len;
+               } else {
+                       vec = NULL;
+               }
+       }
+
+       *prev_item = (struct kdbus_item *)merge;
+       *prev_vec = vec;
+
+       return merge == prev;
+}
+
+static int kdbus_staging_import(struct kdbus_staging *staging)
+{
+       struct kdbus_item *it, *item, *last, *prev_payload;
+       struct kdbus_gaps *gaps = staging->gaps;
+       struct kdbus_msg *msg = staging->msg;
+       struct iovec *part, *prev_part;
+       bool drop_item;
+
+       drop_item = false;
+       last = NULL;
+       prev_payload = NULL;
+       prev_part = NULL;
+
+       /*
+        * We modify msg->items along the way; make sure to use @item as offset
+        * to the next item (instead of the iterator @it).
+        */
+       for (it = item = msg->items;
+            it >= msg->items &&
+                    (u8 *)it < (u8 *)msg + msg->size &&
+                    (u8 *)it + it->size <= (u8 *)msg + msg->size; ) {
+               /*
+                * If we dropped items along the way, move current item to
+                * front. We must not access @it afterwards, but use @item
+                * instead!
+                */
+               if (it != item)
+                       memmove(item, it, it->size);
+               it = (void *)((u8 *)it + KDBUS_ALIGN8(item->size));
+
+               switch (item->type) {
+               case KDBUS_ITEM_PAYLOAD_VEC: {
+                       size_t offset = staging->n_payload;
+
+                       if (kdbus_staging_merge_vecs(staging, &prev_payload,
+                                                    &prev_part, item)) {
+                               drop_item = true;
+                       } else if (item->vec.address) {
+                               /* real offset is patched later on */
+                               item->type = KDBUS_ITEM_PAYLOAD_OFF;
+                               item->vec.offset = offset;
+                       } else {
+                               item->type = KDBUS_ITEM_PAYLOAD_OFF;
+                               item->vec.offset = ~0ULL;
+                       }
+
+                       break;
+               }
+               case KDBUS_ITEM_PAYLOAD_MEMFD: {
+                       struct file *f;
+
+                       f = kdbus_get_memfd(&item->memfd);
+                       if (IS_ERR(f))
+                               return PTR_ERR(f);
+
+                       gaps->memfd_files[gaps->n_memfds] = f;
+                       gaps->memfd_offsets[gaps->n_memfds] =
+                                       (u8 *)&item->memfd.fd - (u8 *)msg;
+                       ++gaps->n_memfds;
+
+                       /* memfds cannot be merged */
+                       prev_payload = item;
+                       prev_part = NULL;
+
+                       /* insert padding to make following VECs aligned */
+                       if (item->memfd.size % 8) {
+                               part = &staging->parts[staging->n_parts++];
+                               part->iov_len = item->memfd.size % 8;
+                               part->iov_base = (char __user *)zeros;
+                               staging->n_payload += part->iov_len;
+                       }
+
+                       break;
+               }
+               case KDBUS_ITEM_FDS: {
+                       size_t i, n_fds;
+
+                       n_fds = KDBUS_ITEM_PAYLOAD_SIZE(item) / sizeof(int);
+                       for (i = 0; i < n_fds; ++i) {
+                               struct file *f;
+
+                               f = kdbus_get_fd(item->fds[i]);
+                               if (IS_ERR(f))
+                                       return PTR_ERR(f);
+
+                               gaps->fd_files[gaps->n_fds++] = f;
+                       }
+
+                       gaps->fd_offset = (u8 *)item->fds - (u8 *)msg;
+
+                       break;
+               }
+               case KDBUS_ITEM_BLOOM_FILTER:
+                       staging->bloom_filter = &item->bloom_filter;
+                       break;
+               case KDBUS_ITEM_DST_NAME:
+                       staging->dst_name = item->str;
+                       break;
+               }
+
+               /* drop item if we merged it with a previous one */
+               if (drop_item) {
+                       drop_item = false;
+               } else {
+                       last = item;
+                       item = KDBUS_ITEM_NEXT(item);
+               }
+       }
+
+       /* adjust message size regarding dropped items */
+       msg->size = offsetof(struct kdbus_msg, items);
+       if (last)
+               msg->size += ((u8 *)last - (u8 *)msg->items) + last->size;
+
+       return 0;
+}
+
+static void kdbus_staging_reserve(struct kdbus_staging *staging)
+{
+       struct iovec *part;
+
+       part = &staging->parts[staging->n_parts++];
+       part->iov_base = (void __user *)zeros;
+       part->iov_len = 0;
+}
+
+static struct kdbus_staging *kdbus_staging_new(struct kdbus_bus *bus,
+                                              size_t n_parts,
+                                              size_t msg_extra_size)
+{
+       const size_t reserved_parts = 5; /* see below for explanation */
+       struct kdbus_staging *staging;
+       int ret;
+
+       n_parts += reserved_parts;
+
+       staging = kzalloc(sizeof(*staging) + n_parts * sizeof(*staging->parts) +
+                         msg_extra_size, GFP_TEMPORARY);
+       if (!staging)
+               return ERR_PTR(-ENOMEM);
+
+       staging->msg_seqnum = atomic64_inc_return(&bus->last_message_id);
+       staging->n_parts = 0; /* we reserve n_parts, but don't enforce them */
+       staging->parts = (void *)(staging + 1);
+
+       if (msg_extra_size) /* if requested, allocate message, too */
+               staging->msg = (void *)((u8 *)staging->parts +
+                                       n_parts * sizeof(*staging->parts));
+
+       staging->meta_proc = kdbus_meta_proc_new();
+       if (IS_ERR(staging->meta_proc)) {
+               ret = PTR_ERR(staging->meta_proc);
+               staging->meta_proc = NULL;
+               goto error;
+       }
+
+       staging->meta_conn = kdbus_meta_conn_new();
+       if (IS_ERR(staging->meta_conn)) {
+               ret = PTR_ERR(staging->meta_conn);
+               staging->meta_conn = NULL;
+               goto error;
+       }
+
+       /*
+        * Prepare iovecs to copy the message into the target pool. We use the
+        * following iovecs:
+        *   * iovec to copy "kdbus_msg.size"
+        *   * iovec to copy "struct kdbus_msg" (minus size) plus items
+        *   * iovec for possible padding after the items
+        *   * iovec for metadata items
+        *   * iovec for possible padding after the items
+        *
+        * Make sure to update @reserved_parts if you add more parts here.
+        */
+
+       kdbus_staging_reserve(staging); /* msg.size */
+       kdbus_staging_reserve(staging); /* msg (minus msg.size) plus items */
+       kdbus_staging_reserve(staging); /* msg padding */
+       kdbus_staging_reserve(staging); /* meta */
+       kdbus_staging_reserve(staging); /* meta padding */
+
+       return staging;
+
+error:
+       kdbus_staging_free(staging);
+       return ERR_PTR(ret);
+}
+
+struct kdbus_staging *kdbus_staging_new_kernel(struct kdbus_bus *bus,
+                                              u64 dst, u64 cookie_timeout,
+                                              size_t it_size, size_t it_type)
+{
+       struct kdbus_staging *staging;
+       size_t size;
+
+       size = offsetof(struct kdbus_msg, items) +
+              KDBUS_ITEM_HEADER_SIZE + it_size;
+
+       staging = kdbus_staging_new(bus, 0, KDBUS_ALIGN8(size));
+       if (IS_ERR(staging))
+               return ERR_CAST(staging);
+
+       staging->msg->size = size;
+       staging->msg->flags = (dst == KDBUS_DST_ID_BROADCAST) ?
+                                                       KDBUS_MSG_SIGNAL : 0;
+       staging->msg->dst_id = dst;
+       staging->msg->src_id = KDBUS_SRC_ID_KERNEL;
+       staging->msg->payload_type = KDBUS_PAYLOAD_KERNEL;
+       staging->msg->cookie_reply = cookie_timeout;
+       staging->notify = staging->msg->items;
+       staging->notify->size = KDBUS_ITEM_HEADER_SIZE + it_size;
+       staging->notify->type = it_type;
+
+       return staging;
+}
+
+struct kdbus_staging *kdbus_staging_new_user(struct kdbus_bus *bus,
+                                            struct kdbus_cmd_send *cmd,
+                                            struct kdbus_msg *msg)
+{
+       const size_t reserved_parts = 1; /* see below for explanation */
+       size_t n_memfds, n_fds, n_parts;
+       struct kdbus_staging *staging;
+       int ret;
+
+       /*
+        * Examine user-supplied message and figure out how many resources we
+        * need to allocate in our staging area. This requires us to iterate
+        * the message twice, but saves us from re-allocating our resources
+        * all the time.
+        */
+
+       ret = kdbus_msg_examine(msg, bus, cmd, &n_memfds, &n_fds, &n_parts);
+       if (ret < 0)
+               return ERR_PTR(ret);
+
+       n_parts += reserved_parts;
+
+       /*
+        * Allocate staging area with the number of required resources. Make
+        * sure that we have enough iovecs for all required parts pre-allocated
+        * so this will hopefully be the only memory allocation for this
+        * message transaction.
+        */
+
+       staging = kdbus_staging_new(bus, n_parts, 0);
+       if (IS_ERR(staging))
+               return ERR_CAST(staging);
+
+       staging->msg = msg;
+
+       /*
+        * If the message contains memfds or fd items, we need to remember some
+        * state so we can fill in the requested information at RECV time.
+        * File-descriptors cannot be passed at SEND time. Hence, allocate a
+        * gaps-object to remember that state. That gaps object is linked to
+        * from the staging area, but will also be linked to from the message
+        * queue of each peer. Hence, each receiver owns a reference to it, and
+        * it will later be used to fill the 'gaps' in message that couldn't be
+        * filled at SEND time.
+        * Note that the 'gaps' object is read-only once the staging-allocator
+        * returns. There might be connections receiving a queued message while
+        * the sender still broadcasts the message to other receivers.
+        */
+
+       if (n_memfds > 0 || n_fds > 0) {
+               staging->gaps = kdbus_gaps_new(n_memfds, n_fds);
+               if (IS_ERR(staging->gaps)) {
+                       ret = PTR_ERR(staging->gaps);
+                       staging->gaps = NULL;
+                       kdbus_staging_free(staging);
+                       return ERR_PTR(ret);
+               }
+       }
+
+       /*
+        * kdbus_staging_new() already reserves parts for message setup. For
+        * user-supplied messages, we add the following iovecs:
+        *   ... variable number of iovecs for payload ...
+        *   * final iovec for possible padding of payload
+        *
+        * Make sure to update @reserved_parts if you add more parts here.
+        */
+
+       ret = kdbus_staging_import(staging); /* payload */
+       kdbus_staging_reserve(staging); /* payload padding */
+
+       if (ret < 0)
+               goto error;
+
+       return staging;
+
+error:
+       kdbus_staging_free(staging);
+       return ERR_PTR(ret);
+}
+
+struct kdbus_staging *kdbus_staging_free(struct kdbus_staging *staging)
+{
+       if (!staging)
+               return NULL;
+
+       kdbus_meta_conn_unref(staging->meta_conn);
+       kdbus_meta_proc_unref(staging->meta_proc);
+       kdbus_gaps_unref(staging->gaps);
+       kfree(staging);
+
+       return NULL;
+}
+
+static int kdbus_staging_collect_metadata(struct kdbus_staging *staging,
+                                         struct kdbus_conn *src,
+                                         struct kdbus_conn *dst,
+                                         u64 *out_attach)
+{
+       u64 attach;
+       int ret;
+
+       if (src)
+               attach = kdbus_meta_msg_mask(src, dst);
+       else
+               attach = KDBUS_ATTACH_TIMESTAMP; /* metadata for kernel msgs */
+
+       if (src && !src->meta_fake) {
+               ret = kdbus_meta_proc_collect(staging->meta_proc, attach);
+               if (ret < 0)
+                       return ret;
+       }
+
+       ret = kdbus_meta_conn_collect(staging->meta_conn, src,
+                                     staging->msg_seqnum, attach);
+       if (ret < 0)
+               return ret;
+
+       *out_attach = attach;
+       return 0;
+}
+
+/**
+ * kdbus_staging_emit() - emit linearized message in target pool
+ * @staging:           staging object to create message from
+ * @src:               sender of the message (or NULL)
+ * @dst:               target connection to allocate message for
+ *
+ * This allocates a pool-slice for @dst and copies the message provided by
+ * @staging into it. The new slice is then returned to the caller for further
+ * processing. It's not linked into any queue, yet.
+ *
+ * Return: Newly allocated slice or ERR_PTR on failure.
+ */
+struct kdbus_pool_slice *kdbus_staging_emit(struct kdbus_staging *staging,
+                                           struct kdbus_conn *src,
+                                           struct kdbus_conn *dst)
+{
+       struct kdbus_item *item, *meta_items = NULL;
+       struct kdbus_pool_slice *slice = NULL;
+       size_t off, size, meta_size;
+       struct iovec *v;
+       u64 attach, msg_size;
+       int ret;
+
+       /*
+        * Step 1:
+        * Collect metadata from @src depending on the attach-flags allowed for
+        * @dst. Translate it into the namespaces pinned by @dst.
+        */
+
+       ret = kdbus_staging_collect_metadata(staging, src, dst, &attach);
+       if (ret < 0)
+               goto error;
+
+       ret = kdbus_meta_emit(staging->meta_proc, NULL, staging->meta_conn,
+                             dst, attach, &meta_items, &meta_size);
+       if (ret < 0)
+               goto error;
+
+       /*
+        * Step 2:
+        * Setup iovecs for the message. See kdbus_staging_new() for allocation
+        * of those iovecs. All reserved iovecs have been initialized with
+        * iov_len=0 + iov_base=zeros. Furthermore, the iovecs to copy the
+        * actual message payload have already been initialized and need not be
+        * touched.
+        */
+
+       v = staging->parts;
+       msg_size = staging->msg->size;
+
+       /* msg.size */
+       v->iov_len = sizeof(msg_size);
+       v->iov_base = (void __user *)&msg_size;
+       ++v;
+
+       /* msg (after msg.size) plus items */
+       v->iov_len = staging->msg->size - sizeof(staging->msg->size);
+       v->iov_base = (void __user *)((u8 *)staging->msg +
+                                     sizeof(staging->msg->size));
+       ++v;
+
+       /* padding after msg */
+       v->iov_len = KDBUS_ALIGN8(staging->msg->size) - staging->msg->size;
+       v->iov_base = (void __user *)zeros;
+       ++v;
+
+       if (meta_size > 0) {
+               /* metadata items */
+               v->iov_len = meta_size;
+               v->iov_base = (void __user *)meta_items;
+               ++v;
+
+               /* padding after metadata */
+               v->iov_len = KDBUS_ALIGN8(meta_size) - meta_size;
+               v->iov_base = (void __user *)zeros;
+               ++v;
+
+               msg_size = KDBUS_ALIGN8(msg_size) + meta_size;
+       } else {
+               /* metadata items */
+               v->iov_len = 0;
+               v->iov_base = (void __user *)zeros;
+               ++v;
+
+               /* padding after metadata */
+               v->iov_len = 0;
+               v->iov_base = (void __user *)zeros;
+               ++v;
+       }
+
+       /* ... payload iovecs are already filled in ... */
+
+       /* compute overall size and fill in padding after payload */
+       size = KDBUS_ALIGN8(msg_size);
+
+       if (staging->n_payload > 0) {
+               size += staging->n_payload;
+
+               v = &staging->parts[staging->n_parts - 1];
+               v->iov_len = KDBUS_ALIGN8(size) - size;
+               v->iov_base = (void __user *)zeros;
+
+               size = KDBUS_ALIGN8(size);
+       }
+
+       /*
+        * Step 3:
+        * The PAYLOAD_OFF items in the message contain a relative 'offset'
+        * field that tells the receiver where to find the actual payload. This
+        * offset is relative to the start of the message, and as such depends
+        * on the size of the metadata items we inserted. This size is variable
+        * and changes for each peer we send the message to. Hence, we remember
+        * the last relative offset that was used to calculate the 'offset'
+        * fields. For each message, we re-calculate it and patch all items, in
+        * case it changed.
+        */
+
+       off = KDBUS_ALIGN8(msg_size);
+
+       if (off != staging->i_payload) {
+               KDBUS_ITEMS_FOREACH(item, staging->msg->items,
+                                   KDBUS_ITEMS_SIZE(staging->msg, items)) {
+                       if (item->type != KDBUS_ITEM_PAYLOAD_OFF)
+                               continue;
+
+                       item->vec.offset -= staging->i_payload;
+                       item->vec.offset += off;
+               }
+
+               staging->i_payload = off;
+       }
+
+       /*
+        * Step 4:
+        * Allocate pool slice and copy over all data. Make sure to properly
+        * account on user quota.
+        */
+
+       ret = kdbus_conn_quota_inc(dst, src ? src->user : NULL, size,
+                                  staging->gaps ? staging->gaps->n_fds : 0);
+       if (ret < 0)
+               goto error;
+
+       slice = kdbus_pool_slice_alloc(dst->pool, size, true);
+       if (IS_ERR(slice)) {
+               ret = PTR_ERR(slice);
+               slice = NULL;
+               goto error;
+       }
+
+       WARN_ON(kdbus_pool_slice_size(slice) != size);
+
+       ret = kdbus_pool_slice_copy_iovec(slice, 0, staging->parts,
+                                         staging->n_parts, size);
+       if (ret < 0)
+               goto error;
+
+       /* all done, return slice to caller */
+       goto exit;
+
+error:
+       if (slice)
+               kdbus_conn_quota_dec(dst, src ? src->user : NULL, size,
+                                    staging->gaps ? staging->gaps->n_fds : 0);
+       kdbus_pool_slice_release(slice);
+       slice = ERR_PTR(ret);
+exit:
+       kfree(meta_items);
+       return slice;
+}
diff --git a/ipc/kdbus/message.h b/ipc/kdbus/message.h
new file mode 100644 (file)
index 0000000..298f9c9
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * 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
+ *
+ * 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_MESSAGE_H
+#define __KDBUS_MESSAGE_H
+
+#include <linux/fs.h>
+#include <linux/kref.h>
+#include <uapi/linux/kdbus.h>
+
+struct kdbus_bus;
+struct kdbus_conn;
+struct kdbus_meta_conn;
+struct kdbus_meta_proc;
+struct kdbus_pool_slice;
+
+/**
+ * struct kdbus_gaps - gaps in message to be filled later
+ * @kref:              Reference counter
+ * @n_memfd_offs:      Number of memfds
+ * @memfd_offs:                Offsets of kdbus_memfd items in target slice
+ * @n_fds:             Number of fds
+ * @fds:               Array of sent fds
+ * @fds_offset:                Offset of fd-array in target slice
+ *
+ * The 'gaps' object is used to track data that is needed to fill gaps in a
+ * message at RECV time. Usually, we try to compile the whole message at SEND
+ * time. This has the advantage, that we don't have to cache any information and
+ * can keep the memory consumption small. Furthermore, all copy operations can
+ * be combined into a single function call, which speeds up transactions
+ * considerably.
+ * However, things like file-descriptors can only be fully installed at RECV
+ * time. The gaps object tracks this data and pins it until a message is
+ * received. The gaps object is shared between all receivers of the same
+ * message.
+ */
+struct kdbus_gaps {
+       struct kref kref;
+
+       /* state tracking for KDBUS_ITEM_PAYLOAD_MEMFD entries */
+       size_t n_memfds;
+       u64 *memfd_offsets;
+       struct file **memfd_files;
+
+       /* state tracking for KDBUS_ITEM_FDS */
+       size_t n_fds;
+       struct file **fd_files;
+       u64 fd_offset;
+};
+
+struct kdbus_gaps *kdbus_gaps_ref(struct kdbus_gaps *gaps);
+struct kdbus_gaps *kdbus_gaps_unref(struct kdbus_gaps *gaps);
+int kdbus_gaps_install(struct kdbus_gaps *gaps, struct kdbus_pool_slice *slice,
+                      bool *out_incomplete);
+
+/**
+ * struct kdbus_staging - staging area to import messages
+ * @msg:               User-supplied message
+ * @gaps:              Gaps-object created during import (or NULL if empty)
+ * @msg_seqnum:                Message sequence number
+ * @notify_entry:      Entry into list of kernel-generated notifications
+ * @i_payload:         Current relative index of start of payload
+ * @n_payload:         Total number of bytes needed for payload
+ * @n_parts:           Number of parts
+ * @parts:             Array of iovecs that make up the whole message
+ * @meta_proc:         Process metadata of the sender (or NULL if empty)
+ * @meta_conn:         Connection metadata of the sender (or NULL if empty)
+ * @bloom_filter:      Pointer to the bloom-item in @msg, or NULL
+ * @dst_name:          Pointer to the dst-name-item in @msg, or NULL
+ * @notify:            Pointer to the notification item in @msg, or NULL
+ *
+ * The kdbus_staging object is a temporary staging area to import user-supplied
+ * messages into the kernel. It is only used during SEND and dropped once the
+ * message is queued. Any data that cannot be collected during SEND, is
+ * collected in a kdbus_gaps object and attached to the message queue.
+ */
+struct kdbus_staging {
+       struct kdbus_msg *msg;
+       struct kdbus_gaps *gaps;
+       u64 msg_seqnum;
+       struct list_head notify_entry;
+
+       /* crafted iovecs to copy the message */
+       size_t i_payload;
+       size_t n_payload;
+       size_t n_parts;
+       struct iovec *parts;
+
+       /* metadata state */
+       struct kdbus_meta_proc *meta_proc;
+       struct kdbus_meta_conn *meta_conn;
+
+       /* cached pointers into @msg */
+       const struct kdbus_bloom_filter *bloom_filter;
+       const char *dst_name;
+       struct kdbus_item *notify;
+};
+
+struct kdbus_staging *kdbus_staging_new_kernel(struct kdbus_bus *bus,
+                                              u64 dst, u64 cookie_timeout,
+                                              size_t it_size, size_t it_type);
+struct kdbus_staging *kdbus_staging_new_user(struct kdbus_bus *bus,
+                                            struct kdbus_cmd_send *cmd,
+                                            struct kdbus_msg *msg);
+struct kdbus_staging *kdbus_staging_free(struct kdbus_staging *staging);
+struct kdbus_pool_slice *kdbus_staging_emit(struct kdbus_staging *staging,
+                                           struct kdbus_conn *src,
+                                           struct kdbus_conn *dst);
+
+#endif
diff --git a/ipc/kdbus/metadata.c b/ipc/kdbus/metadata.c
new file mode 100644 (file)
index 0000000..106ec26
--- /dev/null
@@ -0,0 +1,1347 @@
+/*
+ * 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/audit.h>
+#include <linux/capability.h>
+#include <linux/cgroup.h>
+#include <linux/cred.h>
+#include <linux/file.h>
+#include <linux/fs_struct.h>
+#include <linux/init.h>
+#include <linux/kref.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/security.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/uidgid.h>
+#include <linux/uio.h>
+#include <linux/user_namespace.h>
+
+#include "bus.h"
+#include "connection.h"
+#include "endpoint.h"
+#include "item.h"
+#include "message.h"
+#include "metadata.h"
+#include "names.h"
+
+/**
+ * struct kdbus_meta_proc - Process metadata
+ * @kref:              Reference counting
+ * @lock:              Object lock
+ * @collected:         Bitmask of collected items
+ * @valid:             Bitmask of collected and valid items
+ * @cred:              Credentials
+ * @pid:               PID of process
+ * @tgid:              TGID of process
+ * @ppid:              PPID of process
+ * @tid_comm:          TID comm line
+ * @pid_comm:          PID comm line
+ * @exe_path:          Executable path
+ * @root_path:         Root-FS path
+ * @cmdline:           Command-line
+ * @cgroup:            Full cgroup path
+ * @seclabel:          Seclabel
+ * @audit_loginuid:    Audit login-UID
+ * @audit_sessionid:   Audit session-ID
+ */
+struct kdbus_meta_proc {
+       struct kref kref;
+       struct mutex lock;
+       u64 collected;
+       u64 valid;
+
+       /* KDBUS_ITEM_CREDS */
+       /* KDBUS_ITEM_AUXGROUPS */
+       /* KDBUS_ITEM_CAPS */
+       const struct cred *cred;
+
+       /* KDBUS_ITEM_PIDS */
+       struct pid *pid;
+       struct pid *tgid;
+       struct pid *ppid;
+
+       /* KDBUS_ITEM_TID_COMM */
+       char tid_comm[TASK_COMM_LEN];
+       /* KDBUS_ITEM_PID_COMM */
+       char pid_comm[TASK_COMM_LEN];
+
+       /* KDBUS_ITEM_EXE */
+       struct path exe_path;
+       struct path root_path;
+
+       /* KDBUS_ITEM_CMDLINE */
+       char *cmdline;
+
+       /* KDBUS_ITEM_CGROUP */
+       char *cgroup;
+
+       /* KDBUS_ITEM_SECLABEL */
+       char *seclabel;
+
+       /* KDBUS_ITEM_AUDIT */
+       kuid_t audit_loginuid;
+       unsigned int audit_sessionid;
+};
+
+/**
+ * struct kdbus_meta_conn
+ * @kref:              Reference counting
+ * @lock:              Object lock
+ * @collected:         Bitmask of collected items
+ * @valid:             Bitmask of collected and valid items
+ * @ts:                        Timestamp values
+ * @owned_names_items: Serialized items for owned names
+ * @owned_names_size:  Size of @owned_names_items
+ * @conn_description:  Connection description
+ */
+struct kdbus_meta_conn {
+       struct kref kref;
+       struct mutex lock;
+       u64 collected;
+       u64 valid;
+
+       /* KDBUS_ITEM_TIMESTAMP */
+       struct kdbus_timestamp ts;
+
+       /* KDBUS_ITEM_OWNED_NAME */
+       struct kdbus_item *owned_names_items;
+       size_t owned_names_size;
+
+       /* KDBUS_ITEM_CONN_DESCRIPTION */
+       char *conn_description;
+};
+
+/* fixed size equivalent of "kdbus_caps" */
+struct kdbus_meta_caps {
+       u32 last_cap;
+       struct {
+               u32 caps[_KERNEL_CAPABILITY_U32S];
+       } set[4];
+};
+
+/**
+ * kdbus_meta_proc_new() - Create process metadata object
+ *
+ * Return: Pointer to new object on success, ERR_PTR on failure.
+ */
+struct kdbus_meta_proc *kdbus_meta_proc_new(void)
+{
+       struct kdbus_meta_proc *mp;
+
+       mp = kzalloc(sizeof(*mp), GFP_KERNEL);
+       if (!mp)
+               return ERR_PTR(-ENOMEM);
+
+       kref_init(&mp->kref);
+       mutex_init(&mp->lock);
+
+       return mp;
+}
+
+static void kdbus_meta_proc_free(struct kref *kref)
+{
+       struct kdbus_meta_proc *mp = container_of(kref, struct kdbus_meta_proc,
+                                                 kref);
+
+       path_put(&mp->exe_path);
+       path_put(&mp->root_path);
+       if (mp->cred)
+               put_cred(mp->cred);
+       put_pid(mp->ppid);
+       put_pid(mp->tgid);
+       put_pid(mp->pid);
+
+       kfree(mp->seclabel);
+       kfree(mp->cmdline);
+       kfree(mp->cgroup);
+       kfree(mp);
+}
+
+/**
+ * kdbus_meta_proc_ref() - Gain reference
+ * @mp:                Process metadata object
+ *
+ * Return: @mp is returned
+ */
+struct kdbus_meta_proc *kdbus_meta_proc_ref(struct kdbus_meta_proc *mp)
+{
+       if (mp)
+               kref_get(&mp->kref);
+       return mp;
+}
+
+/**
+ * kdbus_meta_proc_unref() - Drop reference
+ * @mp:                Process metadata object
+ *
+ * Return: NULL
+ */
+struct kdbus_meta_proc *kdbus_meta_proc_unref(struct kdbus_meta_proc *mp)
+{
+       if (mp)
+               kref_put(&mp->kref, kdbus_meta_proc_free);
+       return NULL;
+}
+
+static void kdbus_meta_proc_collect_pids(struct kdbus_meta_proc *mp)
+{
+       struct task_struct *parent;
+
+       mp->pid = get_pid(task_pid(current));
+       mp->tgid = get_pid(task_tgid(current));
+
+       rcu_read_lock();
+       parent = rcu_dereference(current->real_parent);
+       mp->ppid = get_pid(task_tgid(parent));
+       rcu_read_unlock();
+
+       mp->valid |= KDBUS_ATTACH_PIDS;
+}
+
+static void kdbus_meta_proc_collect_tid_comm(struct kdbus_meta_proc *mp)
+{
+       get_task_comm(mp->tid_comm, current);
+       mp->valid |= KDBUS_ATTACH_TID_COMM;
+}
+
+static void kdbus_meta_proc_collect_pid_comm(struct kdbus_meta_proc *mp)
+{
+       get_task_comm(mp->pid_comm, current->group_leader);
+       mp->valid |= KDBUS_ATTACH_PID_COMM;
+}
+
+static void kdbus_meta_proc_collect_exe(struct kdbus_meta_proc *mp)
+{
+       struct file *exe_file;
+
+       rcu_read_lock();
+       exe_file = rcu_dereference(current->mm->exe_file);
+       if (exe_file) {
+               mp->exe_path = exe_file->f_path;
+               path_get(&mp->exe_path);
+               get_fs_root(current->fs, &mp->root_path);
+               mp->valid |= KDBUS_ATTACH_EXE;
+       }
+       rcu_read_unlock();
+}
+
+static int kdbus_meta_proc_collect_cmdline(struct kdbus_meta_proc *mp)
+{
+       struct mm_struct *mm = current->mm;
+       char *cmdline;
+
+       if (mm->arg_start >= mm->arg_end)
+               return 0;
+
+       cmdline = strndup_user((const char __user *)mm->arg_start,
+                              mm->arg_end - mm->arg_start);
+       if (IS_ERR(cmdline))
+               return PTR_ERR(cmdline);
+
+       mp->cmdline = cmdline;
+       mp->valid |= KDBUS_ATTACH_CMDLINE;
+
+       return 0;
+}
+
+static int kdbus_meta_proc_collect_cgroup(struct kdbus_meta_proc *mp)
+{
+#ifdef CONFIG_CGROUPS
+       void *page;
+       char *s;
+
+       page = (void *)__get_free_page(GFP_TEMPORARY);
+       if (!page)
+               return -ENOMEM;
+
+       s = task_cgroup_path(current, page, PAGE_SIZE);
+       if (s) {
+               mp->cgroup = kstrdup(s, GFP_KERNEL);
+               if (!mp->cgroup) {
+                       free_page((unsigned long)page);
+                       return -ENOMEM;
+               }
+       }
+
+       free_page((unsigned long)page);
+       mp->valid |= KDBUS_ATTACH_CGROUP;
+#endif
+
+       return 0;
+}
+
+static int kdbus_meta_proc_collect_seclabel(struct kdbus_meta_proc *mp)
+{
+#ifdef CONFIG_SECURITY
+       char *ctx = NULL;
+       u32 sid, len;
+       int ret;
+
+       security_task_getsecid(current, &sid);
+       ret = security_secid_to_secctx(sid, &ctx, &len);
+       if (ret < 0) {
+               /*
+                * EOPNOTSUPP means no security module is active,
+                * lets skip adding the seclabel then. This effectively
+                * drops the SECLABEL item.
+                */
+               return (ret == -EOPNOTSUPP) ? 0 : ret;
+       }
+
+       mp->seclabel = kstrdup(ctx, GFP_KERNEL);
+       security_release_secctx(ctx, len);
+       if (!mp->seclabel)
+               return -ENOMEM;
+
+       mp->valid |= KDBUS_ATTACH_SECLABEL;
+#endif
+
+       return 0;
+}
+
+static void kdbus_meta_proc_collect_audit(struct kdbus_meta_proc *mp)
+{
+#ifdef CONFIG_AUDITSYSCALL
+       mp->audit_loginuid = audit_get_loginuid(current);
+       mp->audit_sessionid = audit_get_sessionid(current);
+       mp->valid |= KDBUS_ATTACH_AUDIT;
+#endif
+}
+
+/**
+ * kdbus_meta_proc_collect() - Collect process metadata
+ * @mp:                Process metadata object
+ * @what:      Attach flags to collect
+ *
+ * This collects process metadata from current and saves it in @mp.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int kdbus_meta_proc_collect(struct kdbus_meta_proc *mp, u64 what)
+{
+       int ret;
+
+       if (!mp || !(what & (KDBUS_ATTACH_CREDS |
+                            KDBUS_ATTACH_PIDS |
+                            KDBUS_ATTACH_AUXGROUPS |
+                            KDBUS_ATTACH_TID_COMM |
+                            KDBUS_ATTACH_PID_COMM |
+                            KDBUS_ATTACH_EXE |
+                            KDBUS_ATTACH_CMDLINE |
+                            KDBUS_ATTACH_CGROUP |
+                            KDBUS_ATTACH_CAPS |
+                            KDBUS_ATTACH_SECLABEL |
+                            KDBUS_ATTACH_AUDIT)))
+               return 0;
+
+       mutex_lock(&mp->lock);
+
+       /* creds, auxgrps and caps share "struct cred" as context */
+       {
+               const u64 m_cred = KDBUS_ATTACH_CREDS |
+                                  KDBUS_ATTACH_AUXGROUPS |
+                                  KDBUS_ATTACH_CAPS;
+
+               if ((what & m_cred) && !(mp->collected & m_cred)) {
+                       mp->cred = get_current_cred();
+                       mp->valid |= m_cred;
+                       mp->collected |= m_cred;
+               }
+       }
+
+       if ((what & KDBUS_ATTACH_PIDS) &&
+           !(mp->collected & KDBUS_ATTACH_PIDS)) {
+               kdbus_meta_proc_collect_pids(mp);
+               mp->collected |= KDBUS_ATTACH_PIDS;
+       }
+
+       if ((what & KDBUS_ATTACH_TID_COMM) &&
+           !(mp->collected & KDBUS_ATTACH_TID_COMM)) {
+               kdbus_meta_proc_collect_tid_comm(mp);
+               mp->collected |= KDBUS_ATTACH_TID_COMM;
+       }
+
+       if ((what & KDBUS_ATTACH_PID_COMM) &&
+           !(mp->collected & KDBUS_ATTACH_PID_COMM)) {
+               kdbus_meta_proc_collect_pid_comm(mp);
+               mp->collected |= KDBUS_ATTACH_PID_COMM;
+       }
+
+       if ((what & KDBUS_ATTACH_EXE) &&
+           !(mp->collected & KDBUS_ATTACH_EXE)) {
+               kdbus_meta_proc_collect_exe(mp);
+               mp->collected |= KDBUS_ATTACH_EXE;
+       }
+
+       if ((what & KDBUS_ATTACH_CMDLINE) &&
+           !(mp->collected & KDBUS_ATTACH_CMDLINE)) {
+               ret = kdbus_meta_proc_collect_cmdline(mp);
+               if (ret < 0)
+                       goto exit_unlock;
+               mp->collected |= KDBUS_ATTACH_CMDLINE;
+       }
+
+       if ((what & KDBUS_ATTACH_CGROUP) &&
+           !(mp->collected & KDBUS_ATTACH_CGROUP)) {
+               ret = kdbus_meta_proc_collect_cgroup(mp);
+               if (ret < 0)
+                       goto exit_unlock;
+               mp->collected |= KDBUS_ATTACH_CGROUP;
+       }
+
+       if ((what & KDBUS_ATTACH_SECLABEL) &&
+           !(mp->collected & KDBUS_ATTACH_SECLABEL)) {
+               ret = kdbus_meta_proc_collect_seclabel(mp);
+               if (ret < 0)
+                       goto exit_unlock;
+               mp->collected |= KDBUS_ATTACH_SECLABEL;
+       }
+
+       if ((what & KDBUS_ATTACH_AUDIT) &&
+           !(mp->collected & KDBUS_ATTACH_AUDIT)) {
+               kdbus_meta_proc_collect_audit(mp);
+               mp->collected |= KDBUS_ATTACH_AUDIT;
+       }
+
+       ret = 0;
+
+exit_unlock:
+       mutex_unlock(&mp->lock);
+       return ret;
+}
+
+/**
+ * kdbus_meta_fake_new() - Create fake metadata object
+ *
+ * Return: Pointer to new object on success, ERR_PTR on failure.
+ */
+struct kdbus_meta_fake *kdbus_meta_fake_new(void)
+{
+       struct kdbus_meta_fake *mf;
+
+       mf = kzalloc(sizeof(*mf), GFP_KERNEL);
+       if (!mf)
+               return ERR_PTR(-ENOMEM);
+
+       return mf;
+}
+
+/**
+ * kdbus_meta_fake_free() - Free fake metadata object
+ * @mf:                Fake metadata object
+ *
+ * Return: NULL
+ */
+struct kdbus_meta_fake *kdbus_meta_fake_free(struct kdbus_meta_fake *mf)
+{
+       if (mf) {
+               put_pid(mf->ppid);
+               put_pid(mf->tgid);
+               put_pid(mf->pid);
+               kfree(mf->seclabel);
+               kfree(mf);
+       }
+
+       return NULL;
+}
+
+/**
+ * kdbus_meta_fake_collect() - Fill fake metadata from faked credentials
+ * @mf:                Fake metadata object
+ * @creds:     Creds to set, may be %NULL
+ * @pids:      PIDs to set, may be %NULL
+ * @seclabel:  Seclabel to set, may be %NULL
+ *
+ * This function takes information stored in @creds, @pids and @seclabel and
+ * resolves them to kernel-representations, if possible. This call uses the
+ * current task's namespaces to resolve the given information.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int kdbus_meta_fake_collect(struct kdbus_meta_fake *mf,
+                           const struct kdbus_creds *creds,
+                           const struct kdbus_pids *pids,
+                           const char *seclabel)
+{
+       if (mf->valid)
+               return -EALREADY;
+
+       if (creds) {
+               struct user_namespace *ns = current_user_ns();
+
+               mf->uid         = make_kuid(ns, creds->uid);
+               mf->euid        = make_kuid(ns, creds->euid);
+               mf->suid        = make_kuid(ns, creds->suid);
+               mf->fsuid       = make_kuid(ns, creds->fsuid);
+
+               mf->gid         = make_kgid(ns, creds->gid);
+               mf->egid        = make_kgid(ns, creds->egid);
+               mf->sgid        = make_kgid(ns, creds->sgid);
+               mf->fsgid       = make_kgid(ns, creds->fsgid);
+
+               if ((creds->uid   != (uid_t)-1 && !uid_valid(mf->uid))   ||
+                   (creds->euid  != (uid_t)-1 && !uid_valid(mf->euid))  ||
+                   (creds->suid  != (uid_t)-1 && !uid_valid(mf->suid))  ||
+                   (creds->fsuid != (uid_t)-1 && !uid_valid(mf->fsuid)) ||
+                   (creds->gid   != (gid_t)-1 && !gid_valid(mf->gid))   ||
+                   (creds->egid  != (gid_t)-1 && !gid_valid(mf->egid))  ||
+                   (creds->sgid  != (gid_t)-1 && !gid_valid(mf->sgid))  ||
+                   (creds->fsgid != (gid_t)-1 && !gid_valid(mf->fsgid)))
+                       return -EINVAL;
+
+               mf->valid |= KDBUS_ATTACH_CREDS;
+       }
+
+       if (pids) {
+               mf->pid = get_pid(find_vpid(pids->tid));
+               mf->tgid = get_pid(find_vpid(pids->pid));
+               mf->ppid = get_pid(find_vpid(pids->ppid));
+
+               if ((pids->tid != 0 && !mf->pid) ||
+                   (pids->pid != 0 && !mf->tgid) ||
+                   (pids->ppid != 0 && !mf->ppid)) {
+                       put_pid(mf->pid);
+                       put_pid(mf->tgid);
+                       put_pid(mf->ppid);
+                       mf->pid = NULL;
+                       mf->tgid = NULL;
+                       mf->ppid = NULL;
+                       return -EINVAL;
+               }
+
+               mf->valid |= KDBUS_ATTACH_PIDS;
+       }
+
+       if (seclabel) {
+               mf->seclabel = kstrdup(seclabel, GFP_KERNEL);
+               if (!mf->seclabel)
+                       return -ENOMEM;
+
+               mf->valid |= KDBUS_ATTACH_SECLABEL;
+       }
+
+       return 0;
+}
+
+/**
+ * kdbus_meta_conn_new() - Create connection metadata object
+ *
+ * Return: Pointer to new object on success, ERR_PTR on failure.
+ */
+struct kdbus_meta_conn *kdbus_meta_conn_new(void)
+{
+       struct kdbus_meta_conn *mc;
+
+       mc = kzalloc(sizeof(*mc), GFP_KERNEL);
+       if (!mc)
+               return ERR_PTR(-ENOMEM);
+
+       kref_init(&mc->kref);
+       mutex_init(&mc->lock);
+
+       return mc;
+}
+
+static void kdbus_meta_conn_free(struct kref *kref)
+{
+       struct kdbus_meta_conn *mc =
+               container_of(kref, struct kdbus_meta_conn, kref);
+
+       kfree(mc->conn_description);
+       kfree(mc->owned_names_items);
+       kfree(mc);
+}
+
+/**
+ * kdbus_meta_conn_ref() - Gain reference
+ * @mc:                Connection metadata object
+ */
+struct kdbus_meta_conn *kdbus_meta_conn_ref(struct kdbus_meta_conn *mc)
+{
+       if (mc)
+               kref_get(&mc->kref);
+       return mc;
+}
+
+/**
+ * kdbus_meta_conn_unref() - Drop reference
+ * @mc:                Connection metadata object
+ */
+struct kdbus_meta_conn *kdbus_meta_conn_unref(struct kdbus_meta_conn *mc)
+{
+       if (mc)
+               kref_put(&mc->kref, kdbus_meta_conn_free);
+       return NULL;
+}
+
+static void kdbus_meta_conn_collect_timestamp(struct kdbus_meta_conn *mc,
+                                             u64 msg_seqnum)
+{
+       mc->ts.monotonic_ns = ktime_get_ns();
+       mc->ts.realtime_ns = ktime_get_real_ns();
+
+       if (msg_seqnum)
+               mc->ts.seqnum = msg_seqnum;
+
+       mc->valid |= KDBUS_ATTACH_TIMESTAMP;
+}
+
+static int kdbus_meta_conn_collect_names(struct kdbus_meta_conn *mc,
+                                        struct kdbus_conn *conn)
+{
+       const struct kdbus_name_owner *owner;
+       struct kdbus_item *item;
+       size_t slen, size;
+
+       lockdep_assert_held(&conn->ep->bus->name_registry->rwlock);
+
+       size = 0;
+       /* open-code length calculation to avoid final padding */
+       list_for_each_entry(owner, &conn->names_list, conn_entry)
+               if (!(owner->flags & KDBUS_NAME_IN_QUEUE))
+                       size = KDBUS_ALIGN8(size) + KDBUS_ITEM_HEADER_SIZE +
+                               sizeof(struct kdbus_name) +
+                               strlen(owner->name->name) + 1;
+
+       if (!size)
+               return 0;
+
+       /* make sure we include zeroed padding for convenience helpers */
+       item = kmalloc(KDBUS_ALIGN8(size), GFP_KERNEL);
+       if (!item)
+               return -ENOMEM;
+
+       mc->owned_names_items = item;
+       mc->owned_names_size = size;
+
+       list_for_each_entry(owner, &conn->names_list, conn_entry) {
+               if (owner->flags & KDBUS_NAME_IN_QUEUE)
+                       continue;
+
+               slen = strlen(owner->name->name) + 1;
+               kdbus_item_set(item, KDBUS_ITEM_OWNED_NAME, NULL,
+                              sizeof(struct kdbus_name) + slen);
+               item->name.flags = owner->flags;
+               memcpy(item->name.name, owner->name->name, slen);
+               item = KDBUS_ITEM_NEXT(item);
+       }
+
+       /* sanity check: the buffer should be completely written now */
+       WARN_ON((u8 *)item !=
+                       (u8 *)mc->owned_names_items + KDBUS_ALIGN8(size));
+
+       mc->valid |= KDBUS_ATTACH_NAMES;
+       return 0;
+}
+
+static int kdbus_meta_conn_collect_description(struct kdbus_meta_conn *mc,
+                                              struct kdbus_conn *conn)
+{
+       if (!conn->description)
+               return 0;
+
+       mc->conn_description = kstrdup(conn->description, GFP_KERNEL);
+       if (!mc->conn_description)
+               return -ENOMEM;
+
+       mc->valid |= KDBUS_ATTACH_CONN_DESCRIPTION;
+       return 0;
+}
+
+/**
+ * kdbus_meta_conn_collect() - Collect connection metadata
+ * @mc:                Message metadata object
+ * @conn:      Connection to collect data from
+ * @msg_seqnum:        Sequence number of the message to send
+ * @what:      Attach flags to collect
+ *
+ * This collects connection metadata from @msg_seqnum and @conn and saves it
+ * in @mc.
+ *
+ * If KDBUS_ATTACH_NAMES is set in @what and @conn is non-NULL, the caller must
+ * hold the name-registry read-lock of conn->ep->bus->registry.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int kdbus_meta_conn_collect(struct kdbus_meta_conn *mc,
+                           struct kdbus_conn *conn,
+                           u64 msg_seqnum, u64 what)
+{
+       int ret;
+
+       if (!mc || !(what & (KDBUS_ATTACH_TIMESTAMP |
+                            KDBUS_ATTACH_NAMES |
+                            KDBUS_ATTACH_CONN_DESCRIPTION)))
+               return 0;
+
+       mutex_lock(&mc->lock);
+
+       if (msg_seqnum && (what & KDBUS_ATTACH_TIMESTAMP) &&
+           !(mc->collected & KDBUS_ATTACH_TIMESTAMP)) {
+               kdbus_meta_conn_collect_timestamp(mc, msg_seqnum);
+               mc->collected |= KDBUS_ATTACH_TIMESTAMP;
+       }
+
+       if (conn && (what & KDBUS_ATTACH_NAMES) &&
+           !(mc->collected & KDBUS_ATTACH_NAMES)) {
+               ret = kdbus_meta_conn_collect_names(mc, conn);
+               if (ret < 0)
+                       goto exit_unlock;
+               mc->collected |= KDBUS_ATTACH_NAMES;
+       }
+
+       if (conn && (what & KDBUS_ATTACH_CONN_DESCRIPTION) &&
+           !(mc->collected & KDBUS_ATTACH_CONN_DESCRIPTION)) {
+               ret = kdbus_meta_conn_collect_description(mc, conn);
+               if (ret < 0)
+                       goto exit_unlock;
+               mc->collected |= KDBUS_ATTACH_CONN_DESCRIPTION;
+       }
+
+       ret = 0;
+
+exit_unlock:
+       mutex_unlock(&mc->lock);
+       return ret;
+}
+
+static void kdbus_meta_export_caps(struct kdbus_meta_caps *out,
+                                  const struct kdbus_meta_proc *mp,
+                                  struct user_namespace *user_ns)
+{
+       struct user_namespace *iter;
+       const struct cred *cred = mp->cred;
+       bool parent = false, owner = false;
+       int i;
+
+       /*
+        * This translates the effective capabilities of 'cred' into the given
+        * user-namespace. If the given user-namespace is a child-namespace of
+        * the user-namespace of 'cred', the mask can be copied verbatim. If
+        * not, the mask is cleared.
+        * There's one exception: If 'cred' is the owner of any user-namespace
+        * in the path between the given user-namespace and the user-namespace
+        * of 'cred', then it has all effective capabilities set. This means,
+        * the user who created a user-namespace always has all effective
+        * capabilities in any child namespaces. Note that this is based on the
+        * uid of the namespace creator, not the task hierarchy.
+        */
+       for (iter = user_ns; iter; iter = iter->parent) {
+               if (iter == cred->user_ns) {
+                       parent = true;
+                       break;
+               }
+
+               if (iter == &init_user_ns)
+                       break;
+
+               if ((iter->parent == cred->user_ns) &&
+                   uid_eq(iter->owner, cred->euid)) {
+                       owner = true;
+                       break;
+               }
+       }
+
+       out->last_cap = CAP_LAST_CAP;
+
+       CAP_FOR_EACH_U32(i) {
+               if (parent) {
+                       out->set[0].caps[i] = cred->cap_inheritable.cap[i];
+                       out->set[1].caps[i] = cred->cap_permitted.cap[i];
+                       out->set[2].caps[i] = cred->cap_effective.cap[i];
+                       out->set[3].caps[i] = cred->cap_bset.cap[i];
+               } else if (owner) {
+                       out->set[0].caps[i] = 0U;
+                       out->set[1].caps[i] = ~0U;
+                       out->set[2].caps[i] = ~0U;
+                       out->set[3].caps[i] = ~0U;
+               } else {
+                       out->set[0].caps[i] = 0U;
+                       out->set[1].caps[i] = 0U;
+                       out->set[2].caps[i] = 0U;
+                       out->set[3].caps[i] = 0U;
+               }
+       }
+
+       /* clear unused bits */
+       for (i = 0; i < 4; i++)
+               out->set[i].caps[CAP_TO_INDEX(CAP_LAST_CAP)] &=
+                                       CAP_LAST_U32_VALID_MASK;
+}
+
+/* This is equivalent to from_kuid_munged(), but maps INVALID_UID to itself */
+static uid_t kdbus_from_kuid_keep(struct user_namespace *ns, kuid_t uid)
+{
+       return uid_valid(uid) ? from_kuid_munged(ns, uid) : ((uid_t)-1);
+}
+
+/* This is equivalent to from_kgid_munged(), but maps INVALID_GID to itself */
+static gid_t kdbus_from_kgid_keep(struct user_namespace *ns, kgid_t gid)
+{
+       return gid_valid(gid) ? from_kgid_munged(ns, gid) : ((gid_t)-1);
+}
+
+struct kdbus_meta_staging {
+       const struct kdbus_meta_proc *mp;
+       const struct kdbus_meta_fake *mf;
+       const struct kdbus_meta_conn *mc;
+       const struct kdbus_conn *conn;
+       u64 mask;
+
+       void *exe;
+       const char *exe_path;
+};
+
+static size_t kdbus_meta_measure(struct kdbus_meta_staging *staging)
+{
+       const struct kdbus_meta_proc *mp = staging->mp;
+       const struct kdbus_meta_fake *mf = staging->mf;
+       const struct kdbus_meta_conn *mc = staging->mc;
+       const u64 mask = staging->mask;
+       size_t size = 0;
+
+       /* process metadata */
+
+       if (mf && (mask & KDBUS_ATTACH_CREDS))
+               size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_creds));
+       else if (mp && (mask & KDBUS_ATTACH_CREDS))
+               size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_creds));
+
+       if (mf && (mask & KDBUS_ATTACH_PIDS))
+               size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_pids));
+       else if (mp && (mask & KDBUS_ATTACH_PIDS))
+               size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_pids));
+
+       if (mp && (mask & KDBUS_ATTACH_AUXGROUPS))
+               size += KDBUS_ITEM_SIZE(mp->cred->group_info->ngroups *
+                                       sizeof(u64));
+
+       if (mp && (mask & KDBUS_ATTACH_TID_COMM))
+               size += KDBUS_ITEM_SIZE(strlen(mp->tid_comm) + 1);
+
+       if (mp && (mask & KDBUS_ATTACH_PID_COMM))
+               size += KDBUS_ITEM_SIZE(strlen(mp->pid_comm) + 1);
+
+       if (staging->exe_path && (mask & KDBUS_ATTACH_EXE))
+               size += KDBUS_ITEM_SIZE(strlen(staging->exe_path) + 1);
+
+       if (mp && (mask & KDBUS_ATTACH_CMDLINE))
+               size += KDBUS_ITEM_SIZE(strlen(mp->cmdline) + 1);
+
+       if (mp && (mask & KDBUS_ATTACH_CGROUP))
+               size += KDBUS_ITEM_SIZE(strlen(mp->cgroup) + 1);
+
+       if (mp && (mask & KDBUS_ATTACH_CAPS))
+               size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_meta_caps));
+
+       if (mf && (mask & KDBUS_ATTACH_SECLABEL))
+               size += KDBUS_ITEM_SIZE(strlen(mf->seclabel) + 1);
+       else if (mp && (mask & KDBUS_ATTACH_SECLABEL))
+               size += KDBUS_ITEM_SIZE(strlen(mp->seclabel) + 1);
+
+       if (mp && (mask & KDBUS_ATTACH_AUDIT))
+               size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_audit));
+
+       /* connection metadata */
+
+       if (mc && (mask & KDBUS_ATTACH_NAMES))
+               size += KDBUS_ALIGN8(mc->owned_names_size);
+
+       if (mc && (mask & KDBUS_ATTACH_CONN_DESCRIPTION))
+               size += KDBUS_ITEM_SIZE(strlen(mc->conn_description) + 1);
+
+       if (mc && (mask & KDBUS_ATTACH_TIMESTAMP))
+               size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_timestamp));
+
+       return size;
+}
+
+static struct kdbus_item *kdbus_write_head(struct kdbus_item **iter,
+                                          u64 type, u64 size)
+{
+       struct kdbus_item *item = *iter;
+       size_t padding;
+
+       item->type = type;
+       item->size = KDBUS_ITEM_HEADER_SIZE + size;
+
+       /* clear padding */
+       padding = KDBUS_ALIGN8(item->size) - item->size;
+       if (padding)
+               memset(item->data + size, 0, padding);
+
+       *iter = KDBUS_ITEM_NEXT(item);
+       return item;
+}
+
+static struct kdbus_item *kdbus_write_full(struct kdbus_item **iter,
+                                          u64 type, u64 size, const void *data)
+{
+       struct kdbus_item *item;
+
+       item = kdbus_write_head(iter, type, size);
+       memcpy(item->data, data, size);
+       return item;
+}
+
+static size_t kdbus_meta_write(struct kdbus_meta_staging *staging, void *mem,
+                              size_t size)
+{
+       struct user_namespace *user_ns = staging->conn->cred->user_ns;
+       struct pid_namespace *pid_ns = ns_of_pid(staging->conn->pid);
+       struct kdbus_item *item = NULL, *items = mem;
+       u8 *end, *owned_names_end = NULL;
+
+       /* process metadata */
+
+       if (staging->mf && (staging->mask & KDBUS_ATTACH_CREDS)) {
+               const struct kdbus_meta_fake *mf = staging->mf;
+
+               item = kdbus_write_head(&items, KDBUS_ITEM_CREDS,
+                                       sizeof(struct kdbus_creds));
+               item->creds = (struct kdbus_creds){
+                       .uid    = kdbus_from_kuid_keep(user_ns, mf->uid),
+                       .euid   = kdbus_from_kuid_keep(user_ns, mf->euid),
+                       .suid   = kdbus_from_kuid_keep(user_ns, mf->suid),
+                       .fsuid  = kdbus_from_kuid_keep(user_ns, mf->fsuid),
+                       .gid    = kdbus_from_kgid_keep(user_ns, mf->gid),
+                       .egid   = kdbus_from_kgid_keep(user_ns, mf->egid),
+                       .sgid   = kdbus_from_kgid_keep(user_ns, mf->sgid),
+                       .fsgid  = kdbus_from_kgid_keep(user_ns, mf->fsgid),
+               };
+       } else if (staging->mp && (staging->mask & KDBUS_ATTACH_CREDS)) {
+               const struct cred *c = staging->mp->cred;
+
+               item = kdbus_write_head(&items, KDBUS_ITEM_CREDS,
+                                       sizeof(struct kdbus_creds));
+               item->creds = (struct kdbus_creds){
+                       .uid    = kdbus_from_kuid_keep(user_ns, c->uid),
+                       .euid   = kdbus_from_kuid_keep(user_ns, c->euid),
+                       .suid   = kdbus_from_kuid_keep(user_ns, c->suid),
+                       .fsuid  = kdbus_from_kuid_keep(user_ns, c->fsuid),
+                       .gid    = kdbus_from_kgid_keep(user_ns, c->gid),
+                       .egid   = kdbus_from_kgid_keep(user_ns, c->egid),
+                       .sgid   = kdbus_from_kgid_keep(user_ns, c->sgid),
+                       .fsgid  = kdbus_from_kgid_keep(user_ns, c->fsgid),
+               };
+       }
+
+       if (staging->mf && (staging->mask & KDBUS_ATTACH_PIDS)) {
+               item = kdbus_write_head(&items, KDBUS_ITEM_PIDS,
+                                       sizeof(struct kdbus_pids));
+               item->pids = (struct kdbus_pids){
+                       .pid = pid_nr_ns(staging->mf->tgid, pid_ns),
+                       .tid = pid_nr_ns(staging->mf->pid, pid_ns),
+                       .ppid = pid_nr_ns(staging->mf->ppid, pid_ns),
+               };
+       } else if (staging->mp && (staging->mask & KDBUS_ATTACH_PIDS)) {
+               item = kdbus_write_head(&items, KDBUS_ITEM_PIDS,
+                                       sizeof(struct kdbus_pids));
+               item->pids = (struct kdbus_pids){
+                       .pid = pid_nr_ns(staging->mp->tgid, pid_ns),
+                       .tid = pid_nr_ns(staging->mp->pid, pid_ns),
+                       .ppid = pid_nr_ns(staging->mp->ppid, pid_ns),
+               };
+       }
+
+       if (staging->mp && (staging->mask & KDBUS_ATTACH_AUXGROUPS)) {
+               const struct group_info *info = staging->mp->cred->group_info;
+               size_t i;
+
+               item = kdbus_write_head(&items, KDBUS_ITEM_AUXGROUPS,
+                                       info->ngroups * sizeof(u64));
+               for (i = 0; i < info->ngroups; ++i)
+                       item->data64[i] = from_kgid_munged(user_ns,
+                                                          GROUP_AT(info, i));
+       }
+
+       if (staging->mp && (staging->mask & KDBUS_ATTACH_TID_COMM))
+               item = kdbus_write_full(&items, KDBUS_ITEM_TID_COMM,
+                                       strlen(staging->mp->tid_comm) + 1,
+                                       staging->mp->tid_comm);
+
+       if (staging->mp && (staging->mask & KDBUS_ATTACH_PID_COMM))
+               item = kdbus_write_full(&items, KDBUS_ITEM_PID_COMM,
+                                       strlen(staging->mp->pid_comm) + 1,
+                                       staging->mp->pid_comm);
+
+       if (staging->exe_path && (staging->mask & KDBUS_ATTACH_EXE))
+               item = kdbus_write_full(&items, KDBUS_ITEM_EXE,
+                                       strlen(staging->exe_path) + 1,
+                                       staging->exe_path);
+
+       if (staging->mp && (staging->mask & KDBUS_ATTACH_CMDLINE))
+               item = kdbus_write_full(&items, KDBUS_ITEM_CMDLINE,
+                                       strlen(staging->mp->cmdline) + 1,
+                                       staging->mp->cmdline);
+
+       if (staging->mp && (staging->mask & KDBUS_ATTACH_CGROUP))
+               item = kdbus_write_full(&items, KDBUS_ITEM_CGROUP,
+                                       strlen(staging->mp->cgroup) + 1,
+                                       staging->mp->cgroup);
+
+       if (staging->mp && (staging->mask & KDBUS_ATTACH_CAPS)) {
+               item = kdbus_write_head(&items, KDBUS_ITEM_CAPS,
+                                       sizeof(struct kdbus_meta_caps));
+               kdbus_meta_export_caps((void*)&item->caps, staging->mp,
+                                      user_ns);
+       }
+
+       if (staging->mf && (staging->mask & KDBUS_ATTACH_SECLABEL))
+               item = kdbus_write_full(&items, KDBUS_ITEM_SECLABEL,
+                                       strlen(staging->mf->seclabel) + 1,
+                                       staging->mf->seclabel);
+       else if (staging->mp && (staging->mask & KDBUS_ATTACH_SECLABEL))
+               item = kdbus_write_full(&items, KDBUS_ITEM_SECLABEL,
+                                       strlen(staging->mp->seclabel) + 1,
+                                       staging->mp->seclabel);
+
+       if (staging->mp && (staging->mask & KDBUS_ATTACH_AUDIT)) {
+               item = kdbus_write_head(&items, KDBUS_ITEM_AUDIT,
+                                       sizeof(struct kdbus_audit));
+               item->audit = (struct kdbus_audit){
+                       .loginuid = from_kuid(user_ns,
+                                             staging->mp->audit_loginuid),
+                       .sessionid = staging->mp->audit_sessionid,
+               };
+       }
+
+       /* connection metadata */
+
+       if (staging->mc && (staging->mask & KDBUS_ATTACH_NAMES)) {
+               memcpy(items, staging->mc->owned_names_items,
+                      KDBUS_ALIGN8(staging->mc->owned_names_size));
+               owned_names_end = (u8 *)items + staging->mc->owned_names_size;
+               items = (void *)KDBUS_ALIGN8((unsigned long)owned_names_end);
+       }
+
+       if (staging->mc && (staging->mask & KDBUS_ATTACH_CONN_DESCRIPTION))
+               item = kdbus_write_full(&items, KDBUS_ITEM_CONN_DESCRIPTION,
+                               strlen(staging->mc->conn_description) + 1,
+                               staging->mc->conn_description);
+
+       if (staging->mc && (staging->mask & KDBUS_ATTACH_TIMESTAMP))
+               item = kdbus_write_full(&items, KDBUS_ITEM_TIMESTAMP,
+                                       sizeof(staging->mc->ts),
+                                       &staging->mc->ts);
+
+       /*
+        * Return real size (minus trailing padding). In case of 'owned_names'
+        * we cannot deduce it from item->size, so treat it special.
+        */
+
+       if (items == (void *)KDBUS_ALIGN8((unsigned long)owned_names_end))
+               end = owned_names_end;
+       else if (item)
+               end = (u8 *)item + item->size;
+       else
+               end = mem;
+
+       WARN_ON((u8 *)items - (u8 *)mem != size);
+       WARN_ON((void *)KDBUS_ALIGN8((unsigned long)end) != (void *)items);
+
+       return end - (u8 *)mem;
+}
+
+int kdbus_meta_emit(struct kdbus_meta_proc *mp,
+                   struct kdbus_meta_fake *mf,
+                   struct kdbus_meta_conn *mc,
+                   struct kdbus_conn *conn,
+                   u64 mask,
+                   struct kdbus_item **out_items,
+                   size_t *out_size)
+{
+       struct kdbus_meta_staging staging = {};
+       struct kdbus_item *items = NULL;
+       size_t size = 0;
+       int ret;
+
+       if (WARN_ON(mf && mp))
+               mp = NULL;
+
+       staging.mp = mp;
+       staging.mf = mf;
+       staging.mc = mc;
+       staging.conn = conn;
+
+       /* get mask of valid items */
+       if (mf)
+               staging.mask |= mf->valid;
+       if (mp) {
+               mutex_lock(&mp->lock);
+               staging.mask |= mp->valid;
+               mutex_unlock(&mp->lock);
+       }
+       if (mc) {
+               mutex_lock(&mc->lock);
+               staging.mask |= mc->valid;
+               mutex_unlock(&mc->lock);
+       }
+
+       staging.mask &= mask;
+
+       if (!staging.mask) { /* bail out if nothing to do */
+               ret = 0;
+               goto exit;
+       }
+
+       /* EXE is special as it needs a temporary page to assemble */
+       if (mp && (staging.mask & KDBUS_ATTACH_EXE)) {
+               struct path p;
+
+               /*
+                * XXX: We need access to __d_path() so we can write the path
+                * relative to conn->root_path. Once upstream, we need
+                * EXPORT_SYMBOL(__d_path) or an equivalent of d_path() that
+                * takes the root path directly. Until then, we drop this item
+                * if the root-paths differ.
+                */
+
+               get_fs_root(current->fs, &p);
+               if (path_equal(&p, &conn->root_path)) {
+                       staging.exe = (void *)__get_free_page(GFP_TEMPORARY);
+                       if (!staging.exe) {
+                               path_put(&p);
+                               ret = -ENOMEM;
+                               goto exit;
+                       }
+
+                       staging.exe_path = d_path(&mp->exe_path, staging.exe,
+                                                 PAGE_SIZE);
+                       if (IS_ERR(staging.exe_path)) {
+                               path_put(&p);
+                               ret = PTR_ERR(staging.exe_path);
+                               goto exit;
+                       }
+               }
+               path_put(&p);
+       }
+
+       size = kdbus_meta_measure(&staging);
+       if (!size) { /* bail out if nothing to do */
+               ret = 0;
+               goto exit;
+       }
+
+       items = kmalloc(size, GFP_KERNEL);
+       if (!items) {
+               ret = -ENOMEM;
+               goto exit;
+       }
+
+       size = kdbus_meta_write(&staging, items, size);
+       if (!size) {
+               kfree(items);
+               items = NULL;
+       }
+
+       ret = 0;
+
+exit:
+       if (staging.exe)
+               free_page((unsigned long)staging.exe);
+       if (ret >= 0) {
+               *out_items = items;
+               *out_size = size;
+       }
+       return ret;
+}
+
+enum {
+       KDBUS_META_PROC_NONE,
+       KDBUS_META_PROC_NORMAL,
+};
+
+/**
+ * kdbus_proc_permission() - check /proc permissions on target pid
+ * @pid_ns:            namespace we operate in
+ * @cred:              credentials of requestor
+ * @target:            target process
+ *
+ * This checks whether a process with credentials @cred can access information
+ * of @target in the namespace @pid_ns. This tries to follow /proc permissions,
+ * but is slightly more restrictive.
+ *
+ * Return: The /proc access level (KDBUS_META_PROC_*) is returned.
+ */
+static unsigned int kdbus_proc_permission(const struct pid_namespace *pid_ns,
+                                         const struct cred *cred,
+                                         struct pid *target)
+{
+       if (pid_ns->hide_pid < 1)
+               return KDBUS_META_PROC_NORMAL;
+
+       /* XXX: we need groups_search() exported for aux-groups */
+       if (gid_eq(cred->egid, pid_ns->pid_gid))
+               return KDBUS_META_PROC_NORMAL;
+
+       /*
+        * XXX: If ptrace_may_access(PTRACE_MODE_READ) is granted, you can
+        * overwrite hide_pid. However, ptrace_may_access() only supports
+        * checking 'current', hence, we cannot use this here. But we
+        * simply decide to not support this override, so no need to worry.
+        */
+
+       return KDBUS_META_PROC_NONE;
+}
+
+/**
+ * kdbus_meta_proc_mask() - calculate which metadata would be visible to
+ *                         a connection via /proc
+ * @prv_pid:           pid of metadata provider
+ * @req_pid:           pid of metadata requestor
+ * @req_cred:          credentials of metadata reqeuestor
+ * @wanted:            metadata that is requested
+ *
+ * This checks which metadata items of @prv_pid can be read via /proc by the
+ * requestor @req_pid.
+ *
+ * Return: Set of metadata flags the requestor can see (limited by @wanted).
+ */
+static u64 kdbus_meta_proc_mask(struct pid *prv_pid,
+                               struct pid *req_pid,
+                               const struct cred *req_cred,
+                               u64 wanted)
+{
+       struct pid_namespace *prv_ns, *req_ns;
+       unsigned int proc;
+
+       prv_ns = ns_of_pid(prv_pid);
+       req_ns = ns_of_pid(req_pid);
+
+       /*
+        * If the sender is not visible in the receiver namespace, then the
+        * receiver cannot access the sender via its own procfs. Hence, we do
+        * not attach any additional metadata.
+        */
+       if (!pid_nr_ns(prv_pid, req_ns))
+               return 0;
+
+       /*
+        * If the pid-namespace of the receiver has hide_pid set, it cannot see
+        * any process but its own. We shortcut this /proc permission check if
+        * provider and requestor are the same. If not, we perform rather
+        * expensive /proc permission checks.
+        */
+       if (prv_pid == req_pid)
+               proc = KDBUS_META_PROC_NORMAL;
+       else
+               proc = kdbus_proc_permission(req_ns, req_cred, prv_pid);
+
+       /* you need /proc access to read standard process attributes */
+       if (proc < KDBUS_META_PROC_NORMAL)
+               wanted &= ~(KDBUS_ATTACH_TID_COMM |
+                           KDBUS_ATTACH_PID_COMM |
+                           KDBUS_ATTACH_SECLABEL |
+                           KDBUS_ATTACH_CMDLINE |
+                           KDBUS_ATTACH_CGROUP |
+                           KDBUS_ATTACH_AUDIT |
+                           KDBUS_ATTACH_CAPS |
+                           KDBUS_ATTACH_EXE);
+
+       /* clear all non-/proc flags */
+       return wanted & (KDBUS_ATTACH_TID_COMM |
+                        KDBUS_ATTACH_PID_COMM |
+                        KDBUS_ATTACH_SECLABEL |
+                        KDBUS_ATTACH_CMDLINE |
+                        KDBUS_ATTACH_CGROUP |
+                        KDBUS_ATTACH_AUDIT |
+                        KDBUS_ATTACH_CAPS |
+                        KDBUS_ATTACH_EXE);
+}
+
+/**
+ * kdbus_meta_get_mask() - calculate attach flags mask for metadata request
+ * @prv_pid:           pid of metadata provider
+ * @prv_mask:          mask of metadata the provide grants unchecked
+ * @req_pid:           pid of metadata requestor
+ * @req_cred:          credentials of metadata requestor
+ * @req_mask:          mask of metadata that is requested
+ *
+ * This calculates the metadata items that the requestor @req_pid can access
+ * from the metadata provider @prv_pid. This permission check consists of
+ * several different parts:
+ *  - Providers can grant metadata items unchecked. Regardless of their type,
+ *    they're always granted to the requestor. This mask is passed as @prv_mask.
+ *  - Basic items (credentials and connection metadata) are granted implicitly
+ *    to everyone. They're publicly available to any bus-user that can see the
+ *    provider.
+ *  - Process credentials that are not granted implicitly follow the same
+ *    permission checks as /proc. This means, we always assume a requestor
+ *    process has access to their *own* /proc mount, if they have access to
+ *    kdbusfs.
+ *
+ * Return: Mask of metadata that is granted.
+ */
+static u64 kdbus_meta_get_mask(struct pid *prv_pid, u64 prv_mask,
+                              struct pid *req_pid,
+                              const struct cred *req_cred, u64 req_mask)
+{
+       u64 missing, impl_mask, proc_mask = 0;
+
+       /*
+        * Connection metadata and basic unix process credentials are
+        * transmitted implicitly, and cannot be suppressed. Both are required
+        * to perform user-space policies on the receiver-side. Furthermore,
+        * connection metadata is public state, anyway, and unix credentials
+        * are needed for UDS-compatibility. We extend them slightly by
+        * auxiliary groups and additional uids/gids/pids.
+        */
+       impl_mask = /* connection metadata */
+                   KDBUS_ATTACH_CONN_DESCRIPTION |
+                   KDBUS_ATTACH_TIMESTAMP |
+                   KDBUS_ATTACH_NAMES |
+                   /* credentials and pids */
+                   KDBUS_ATTACH_AUXGROUPS |
+                   KDBUS_ATTACH_CREDS |
+                   KDBUS_ATTACH_PIDS;
+
+       /*
+        * Calculate the set of metadata that is not granted implicitly nor by
+        * the sender, but still requested by the receiver. If any are left,
+        * perform rather expensive /proc access checks for them.
+        */
+       missing = req_mask & ~((prv_mask | impl_mask) & req_mask);
+       if (missing)
+               proc_mask = kdbus_meta_proc_mask(prv_pid, req_pid, req_cred,
+                                                missing);
+
+       return (prv_mask | impl_mask | proc_mask) & req_mask;
+}
+
+/**
+ */
+u64 kdbus_meta_info_mask(const struct kdbus_conn *conn, u64 mask)
+{
+       return kdbus_meta_get_mask(conn->pid,
+                                  atomic64_read(&conn->attach_flags_send),
+                                  task_pid(current),
+                                  current_cred(),
+                                  mask);
+}
+
+/**
+ */
+u64 kdbus_meta_msg_mask(const struct kdbus_conn *snd,
+                       const struct kdbus_conn *rcv)
+{
+       return kdbus_meta_get_mask(task_pid(current),
+                                  atomic64_read(&snd->attach_flags_send),
+                                  rcv->pid,
+                                  rcv->cred,
+                                  atomic64_read(&rcv->attach_flags_recv));
+}
diff --git a/ipc/kdbus/metadata.h b/ipc/kdbus/metadata.h
new file mode 100644 (file)
index 0000000..dba7cc7
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * 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
+ *
+ * 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_METADATA_H
+#define __KDBUS_METADATA_H
+
+#include <linux/kernel.h>
+
+struct kdbus_conn;
+struct kdbus_pool_slice;
+
+struct kdbus_meta_proc;
+struct kdbus_meta_conn;
+
+/**
+ * struct kdbus_meta_fake - Fake metadata
+ * @valid:             Bitmask of collected and valid items
+ * @uid:               UID of process
+ * @euid:              EUID of process
+ * @suid:              SUID of process
+ * @fsuid:             FSUID of process
+ * @gid:               GID of process
+ * @egid:              EGID of process
+ * @sgid:              SGID of process
+ * @fsgid:             FSGID of process
+ * @pid:               PID of process
+ * @tgid:              TGID of process
+ * @ppid:              PPID of process
+ * @seclabel:          Seclabel
+ */
+struct kdbus_meta_fake {
+       u64 valid;
+
+       /* KDBUS_ITEM_CREDS */
+       kuid_t uid, euid, suid, fsuid;
+       kgid_t gid, egid, sgid, fsgid;
+
+       /* KDBUS_ITEM_PIDS */
+       struct pid *pid, *tgid, *ppid;
+
+       /* KDBUS_ITEM_SECLABEL */
+       char *seclabel;
+};
+
+struct kdbus_meta_proc *kdbus_meta_proc_new(void);
+struct kdbus_meta_proc *kdbus_meta_proc_ref(struct kdbus_meta_proc *mp);
+struct kdbus_meta_proc *kdbus_meta_proc_unref(struct kdbus_meta_proc *mp);
+int kdbus_meta_proc_collect(struct kdbus_meta_proc *mp, u64 what);
+
+struct kdbus_meta_fake *kdbus_meta_fake_new(void);
+struct kdbus_meta_fake *kdbus_meta_fake_free(struct kdbus_meta_fake *mf);
+int kdbus_meta_fake_collect(struct kdbus_meta_fake *mf,
+                           const struct kdbus_creds *creds,
+                           const struct kdbus_pids *pids,
+                           const char *seclabel);
+
+struct kdbus_meta_conn *kdbus_meta_conn_new(void);
+struct kdbus_meta_conn *kdbus_meta_conn_ref(struct kdbus_meta_conn *mc);
+struct kdbus_meta_conn *kdbus_meta_conn_unref(struct kdbus_meta_conn *mc);
+int kdbus_meta_conn_collect(struct kdbus_meta_conn *mc,
+                           struct kdbus_conn *conn,
+                           u64 msg_seqnum, u64 what);
+
+int kdbus_meta_emit(struct kdbus_meta_proc *mp,
+                   struct kdbus_meta_fake *mf,
+                   struct kdbus_meta_conn *mc,
+                   struct kdbus_conn *conn,
+                   u64 mask,
+                   struct kdbus_item **out_items,
+                   size_t *out_size);
+u64 kdbus_meta_info_mask(const struct kdbus_conn *conn, u64 mask);
+u64 kdbus_meta_msg_mask(const struct kdbus_conn *snd,
+                       const struct kdbus_conn *rcv);
+
+#endif
diff --git a/ipc/kdbus/names.c b/ipc/kdbus/names.c
new file mode 100644 (file)
index 0000000..bf44ca3
--- /dev/null
@@ -0,0 +1,854 @@
+/*
+ * 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/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 "handle.h"
+#include "item.h"
+#include "names.h"
+#include "notify.h"
+#include "policy.h"
+
+#define KDBUS_NAME_SAVED_MASK (KDBUS_NAME_ALLOW_REPLACEMENT |  \
+                              KDBUS_NAME_QUEUE)
+
+static bool kdbus_name_owner_is_used(struct kdbus_name_owner *owner)
+{
+       return !list_empty(&owner->name_entry) ||
+              owner == owner->name->activator;
+}
+
+static struct kdbus_name_owner *
+kdbus_name_owner_new(struct kdbus_conn *conn, struct kdbus_name_entry *name,
+                    u64 flags)
+{
+       struct kdbus_name_owner *owner;
+
+       kdbus_conn_assert_active(conn);
+
+       if (conn->name_count >= KDBUS_CONN_MAX_NAMES)
+               return ERR_PTR(-E2BIG);
+
+       owner = kmalloc(sizeof(*owner), GFP_KERNEL);
+       if (!owner)
+               return ERR_PTR(-ENOMEM);
+
+       owner->flags = flags & KDBUS_NAME_SAVED_MASK;
+       owner->conn = conn;
+       owner->name = name;
+       list_add_tail(&owner->conn_entry, &conn->names_list);
+       INIT_LIST_HEAD(&owner->name_entry);
+
+       ++conn->name_count;
+       return owner;
+}
+
+static void kdbus_name_owner_free(struct kdbus_name_owner *owner)
+{
+       if (!owner)
+               return;
+
+       WARN_ON(kdbus_name_owner_is_used(owner));
+       --owner->conn->name_count;
+       list_del(&owner->conn_entry);
+       kfree(owner);
+}
+
+static struct kdbus_name_owner *
+kdbus_name_owner_find(struct kdbus_name_entry *name, struct kdbus_conn *conn)
+{
+       struct kdbus_name_owner *owner;
+
+       /*
+        * Use conn->names_list over name->queue to make sure boundaries of
+        * this linear search are controlled by the connection itself.
+        * Furthermore, this will find normal owners as well as activators
+        * without any additional code.
+        */
+       list_for_each_entry(owner, &conn->names_list, conn_entry)
+               if (owner->name == name)
+                       return owner;
+
+       return NULL;
+}
+
+static bool kdbus_name_entry_is_used(struct kdbus_name_entry *name)
+{
+       return !list_empty(&name->queue) || name->activator;
+}
+
+static struct kdbus_name_owner *
+kdbus_name_entry_first(struct kdbus_name_entry *name)
+{
+       return list_first_entry_or_null(&name->queue, struct kdbus_name_owner,
+                                       name_entry);
+}
+
+static struct kdbus_name_entry *
+kdbus_name_entry_new(struct kdbus_name_registry *r, u32 hash,
+                    const char *name_str)
+{
+       struct kdbus_name_entry *name;
+       size_t namelen;
+
+       lockdep_assert_held(&r->rwlock);
+
+       namelen = strlen(name_str);
+
+       name = kmalloc(sizeof(*name) + namelen + 1, GFP_KERNEL);
+       if (!name)
+               return ERR_PTR(-ENOMEM);
+
+       name->name_id = ++r->name_seq_last;
+       name->activator = NULL;
+       INIT_LIST_HEAD(&name->queue);
+       hash_add(r->entries_hash, &name->hentry, hash);
+       memcpy(name->name, name_str, namelen + 1);
+
+       return name;
+}
+
+static void kdbus_name_entry_free(struct kdbus_name_entry *name)
+{
+       if (!name)
+               return;
+
+       WARN_ON(kdbus_name_entry_is_used(name));
+       hash_del(&name->hentry);
+       kfree(name);
+}
+
+static struct kdbus_name_entry *
+kdbus_name_entry_find(struct kdbus_name_registry *r, u32 hash,
+                     const char *name_str)
+{
+       struct kdbus_name_entry *name;
+
+       lockdep_assert_held(&r->rwlock);
+
+       hash_for_each_possible(r->entries_hash, name, hentry, hash)
+               if (!strcmp(name->name, name_str))
+                       return name;
+
+       return NULL;
+}
+
+/**
+ * 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 = kmalloc(sizeof(*r), GFP_KERNEL);
+       if (!r)
+               return ERR_PTR(-ENOMEM);
+
+       hash_init(r->entries_hash);
+       init_rwsem(&r->rwlock);
+       r->name_seq_last = 0;
+
+       return r;
+}
+
+/**
+ * kdbus_name_registry_free() - free name registry
+ * @r:         name registry to free, or NULL
+ *
+ * Free a name registry and cleanup all internal objects. This is a no-op if
+ * you pass NULL as registry.
+ */
+void kdbus_name_registry_free(struct kdbus_name_registry *r)
+{
+       if (!r)
+               return;
+
+       WARN_ON(!hash_empty(r->entries_hash));
+       kfree(r);
+}
+
+/**
+ * kdbus_name_lookup_unlocked() - lookup name in registry
+ * @reg:               name registry
+ * @name:              name to lookup
+ *
+ * This looks up @name in the given name-registry and returns the
+ * kdbus_name_entry object. The caller must hold the registry-lock and must not
+ * access the returned object after releasing the lock.
+ *
+ * Return: Pointer to name-entry, or NULL if not found.
+ */
+struct kdbus_name_entry *
+kdbus_name_lookup_unlocked(struct kdbus_name_registry *reg, const char *name)
+{
+       return kdbus_name_entry_find(reg, kdbus_strhash(name), name);
+}
+
+static int kdbus_name_become_activator(struct kdbus_name_owner *owner,
+                                      u64 *return_flags)
+{
+       if (kdbus_name_owner_is_used(owner))
+               return -EALREADY;
+       if (owner->name->activator)
+               return -EEXIST;
+
+       owner->name->activator = owner;
+       owner->flags |= KDBUS_NAME_ACTIVATOR;
+
+       if (kdbus_name_entry_first(owner->name)) {
+               owner->flags |= KDBUS_NAME_IN_QUEUE;
+       } else {
+               owner->flags |= KDBUS_NAME_PRIMARY;
+               kdbus_notify_name_change(owner->conn->ep->bus,
+                                        KDBUS_ITEM_NAME_ADD,
+                                        0, owner->conn->id,
+                                        0, owner->flags,
+                                        owner->name->name);
+       }
+
+       if (return_flags)
+               *return_flags = owner->flags | KDBUS_NAME_ACQUIRED;
+
+       return 0;
+}
+
+static int kdbus_name_update(struct kdbus_name_owner *owner, u64 flags,
+                            u64 *return_flags)
+{
+       struct kdbus_name_owner *primary, *activator;
+       struct kdbus_name_entry *name;
+       struct kdbus_bus *bus;
+       u64 nflags = 0;
+       int ret = 0;
+
+       name = owner->name;
+       bus = owner->conn->ep->bus;
+       primary = kdbus_name_entry_first(name);
+       activator = name->activator;
+
+       /* cannot be activator and acquire a name */
+       if (owner == activator)
+               return -EUCLEAN;
+
+       /* update saved flags */
+       owner->flags = flags & KDBUS_NAME_SAVED_MASK;
+
+       if (!primary) {
+               /*
+                * No primary owner (but maybe an activator). Take over the
+                * name.
+                */
+
+               list_add(&owner->name_entry, &name->queue);
+               owner->flags |= KDBUS_NAME_PRIMARY;
+               nflags |= KDBUS_NAME_ACQUIRED;
+
+               /* move messages to new owner on activation */
+               if (activator) {
+                       kdbus_conn_move_messages(owner->conn, activator->conn,
+                                                name->name_id);
+                       kdbus_notify_name_change(bus, KDBUS_ITEM_NAME_CHANGE,
+                                       activator->conn->id, owner->conn->id,
+                                       activator->flags, owner->flags,
+                                       name->name);
+                       activator->flags &= ~KDBUS_NAME_PRIMARY;
+                       activator->flags |= KDBUS_NAME_IN_QUEUE;
+               } else {
+                       kdbus_notify_name_change(bus, KDBUS_ITEM_NAME_ADD,
+                                                0, owner->conn->id,
+                                                0, owner->flags,
+                                                name->name);
+               }
+
+       } else if (owner == primary) {
+               /*
+                * Already the primary owner of the name, flags were already
+                * updated. Nothing to do.
+                */
+
+               owner->flags |= KDBUS_NAME_PRIMARY;
+
+       } else if ((primary->flags & KDBUS_NAME_ALLOW_REPLACEMENT) &&
+                  (flags & KDBUS_NAME_REPLACE_EXISTING)) {
+               /*
+                * We're not the primary owner but can replace it. Move us
+                * ahead of the primary owner and acquire the name (possibly
+                * skipping queued owners ahead of us).
+                */
+
+               list_del_init(&owner->name_entry);
+               list_add(&owner->name_entry, &name->queue);
+               owner->flags |= KDBUS_NAME_PRIMARY;
+               nflags |= KDBUS_NAME_ACQUIRED;
+
+               kdbus_notify_name_change(bus, KDBUS_ITEM_NAME_CHANGE,
+                                        primary->conn->id, owner->conn->id,
+                                        primary->flags, owner->flags,
+                                        name->name);
+
+               /* requeue old primary, or drop if queueing not wanted */
+               if (primary->flags & KDBUS_NAME_QUEUE) {
+                       primary->flags &= ~KDBUS_NAME_PRIMARY;
+                       primary->flags |= KDBUS_NAME_IN_QUEUE;
+               } else {
+                       list_del_init(&primary->name_entry);
+                       kdbus_name_owner_free(primary);
+               }
+
+       } else if (flags & KDBUS_NAME_QUEUE) {
+               /*
+                * Name is already occupied and we cannot take it over, but
+                * queuing is allowed. Put us silently on the queue, if not
+                * already there.
+                */
+
+               owner->flags |= KDBUS_NAME_IN_QUEUE;
+               if (!kdbus_name_owner_is_used(owner)) {
+                       list_add_tail(&owner->name_entry, &name->queue);
+                       nflags |= KDBUS_NAME_ACQUIRED;
+               }
+       } else if (kdbus_name_owner_is_used(owner)) {
+               /*
+                * Already queued on name, but re-queueing was not requested.
+                * Make sure to unlink it from the name, the caller is
+                * responsible for releasing it.
+                */
+
+               list_del_init(&owner->name_entry);
+       } else {
+               /*
+                * Name is already claimed and queueing is not requested.
+                * Return error to the caller.
+                */
+
+               ret = -EEXIST;
+       }
+
+       if (return_flags)
+               *return_flags = owner->flags | nflags;
+
+       return ret;
+}
+
+int kdbus_name_acquire(struct kdbus_name_registry *reg,
+                      struct kdbus_conn *conn, const char *name_str,
+                      u64 flags, u64 *return_flags)
+{
+       struct kdbus_name_entry *name = NULL;
+       struct kdbus_name_owner *owner = NULL;
+       u32 hash;
+       int ret;
+
+       kdbus_conn_assert_active(conn);
+
+       down_write(&reg->rwlock);
+
+       /*
+        * Verify the connection has access to the name. Do this before testing
+        * for double-acquisitions and other errors to make sure we do not leak
+        * information about this name through possible custom endpoints.
+        */
+       if (!kdbus_conn_policy_own_name(conn, current_cred(), name_str)) {
+               ret = -EPERM;
+               goto exit;
+       }
+
+       /*
+        * Lookup the name entry. If it already exists, search for an owner
+        * entry as we might already own that name. If either does not exist,
+        * we will allocate a fresh one.
+        */
+       hash = kdbus_strhash(name_str);
+       name = kdbus_name_entry_find(reg, hash, name_str);
+       if (name) {
+               owner = kdbus_name_owner_find(name, conn);
+       } else {
+               name = kdbus_name_entry_new(reg, hash, name_str);
+               if (IS_ERR(name)) {
+                       ret = PTR_ERR(name);
+                       name = NULL;
+                       goto exit;
+               }
+       }
+
+       /* create name owner object if not already queued */
+       if (!owner) {
+               owner = kdbus_name_owner_new(conn, name, flags);
+               if (IS_ERR(owner)) {
+                       ret = PTR_ERR(owner);
+                       owner = NULL;
+                       goto exit;
+               }
+       }
+
+       if (flags & KDBUS_NAME_ACTIVATOR)
+               ret = kdbus_name_become_activator(owner, return_flags);
+       else
+               ret = kdbus_name_update(owner, flags, return_flags);
+       if (ret < 0)
+               goto exit;
+
+exit:
+       if (owner && !kdbus_name_owner_is_used(owner))
+               kdbus_name_owner_free(owner);
+       if (name && !kdbus_name_entry_is_used(name))
+               kdbus_name_entry_free(name);
+       up_write(&reg->rwlock);
+       kdbus_notify_flush(conn->ep->bus);
+       return ret;
+}
+
+static void kdbus_name_release_unlocked(struct kdbus_name_owner *owner)
+{
+       struct kdbus_name_owner *primary, *next;
+       struct kdbus_name_entry *name;
+
+       name = owner->name;
+       primary = kdbus_name_entry_first(name);
+
+       list_del_init(&owner->name_entry);
+       if (owner == name->activator)
+               name->activator = NULL;
+
+       if (!primary || owner == primary) {
+               next = kdbus_name_entry_first(name);
+               if (!next)
+                       next = name->activator;
+
+               if (next) {
+                       /* hand to next in queue */
+                       next->flags &= ~KDBUS_NAME_IN_QUEUE;
+                       next->flags |= KDBUS_NAME_PRIMARY;
+                       if (next == name->activator)
+                               kdbus_conn_move_messages(next->conn,
+                                                        owner->conn,
+                                                        name->name_id);
+
+                       kdbus_notify_name_change(owner->conn->ep->bus,
+                                       KDBUS_ITEM_NAME_CHANGE,
+                                       owner->conn->id, next->conn->id,
+                                       owner->flags, next->flags,
+                                       name->name);
+               } else {
+                       kdbus_notify_name_change(owner->conn->ep->bus,
+                                                KDBUS_ITEM_NAME_REMOVE,
+                                                owner->conn->id, 0,
+                                                owner->flags, 0,
+                                                name->name);
+               }
+       }
+
+       kdbus_name_owner_free(owner);
+       if (!kdbus_name_entry_is_used(name))
+               kdbus_name_entry_free(name);
+}
+
+static int kdbus_name_release(struct kdbus_name_registry *reg,
+                             struct kdbus_conn *conn,
+                             const char *name_str)
+{
+       struct kdbus_name_owner *owner;
+       struct kdbus_name_entry *name;
+       int ret = 0;
+
+       down_write(&reg->rwlock);
+       name = kdbus_name_entry_find(reg, kdbus_strhash(name_str), name_str);
+       if (name) {
+               owner = kdbus_name_owner_find(name, conn);
+               if (owner)
+                       kdbus_name_release_unlocked(owner);
+               else
+                       ret = -EADDRINUSE;
+       } else {
+               ret = -ESRCH;
+       }
+       up_write(&reg->rwlock);
+
+       kdbus_notify_flush(conn->ep->bus);
+       return ret;
+}
+
+/**
+ * kdbus_name_release_all() - remove all name entries of a given connection
+ * @reg:               name registry
+ * @conn:              connection
+ */
+void kdbus_name_release_all(struct kdbus_name_registry *reg,
+                           struct kdbus_conn *conn)
+{
+       struct kdbus_name_owner *owner;
+
+       down_write(&reg->rwlock);
+
+       while ((owner = list_first_entry_or_null(&conn->names_list,
+                                                struct kdbus_name_owner,
+                                                conn_entry)))
+               kdbus_name_release_unlocked(owner);
+
+       up_write(&reg->rwlock);
+
+       kdbus_notify_flush(conn->ep->bus);
+}
+
+/**
+ * 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_cmd_name_acquire() - handle KDBUS_CMD_NAME_ACQUIRE
+ * @conn:              connection to operate on
+ * @argp:              command payload
+ *
+ * Return: >=0 on success, negative error code on failure.
+ */
+int kdbus_cmd_name_acquire(struct kdbus_conn *conn, void __user *argp)
+{
+       const char *item_name;
+       struct kdbus_cmd *cmd;
+       int ret;
+
+       struct kdbus_arg argv[] = {
+               { .type = KDBUS_ITEM_NEGOTIATE },
+               { .type = KDBUS_ITEM_NAME, .mandatory = true },
+       };
+       struct kdbus_args args = {
+               .allowed_flags = KDBUS_FLAG_NEGOTIATE |
+                                KDBUS_NAME_REPLACE_EXISTING |
+                                KDBUS_NAME_ALLOW_REPLACEMENT |
+                                KDBUS_NAME_QUEUE,
+               .argv = argv,
+               .argc = ARRAY_SIZE(argv),
+       };
+
+       if (!kdbus_conn_is_ordinary(conn))
+               return -EOPNOTSUPP;
+
+       ret = kdbus_args_parse(&args, argp, &cmd);
+       if (ret != 0)
+               return ret;
+
+       item_name = argv[1].item->str;
+       if (!kdbus_name_is_valid(item_name, false)) {
+               ret = -EINVAL;
+               goto exit;
+       }
+
+       ret = kdbus_name_acquire(conn->ep->bus->name_registry, conn, item_name,
+                                cmd->flags, &cmd->return_flags);
+
+exit:
+       return kdbus_args_clear(&args, ret);
+}
+
+/**
+ * kdbus_cmd_name_release() - handle KDBUS_CMD_NAME_RELEASE
+ * @conn:              connection to operate on
+ * @argp:              command payload
+ *
+ * Return: >=0 on success, negative error code on failure.
+ */
+int kdbus_cmd_name_release(struct kdbus_conn *conn, void __user *argp)
+{
+       struct kdbus_cmd *cmd;
+       int ret;
+
+       struct kdbus_arg argv[] = {
+               { .type = KDBUS_ITEM_NEGOTIATE },
+               { .type = KDBUS_ITEM_NAME, .mandatory = true },
+       };
+       struct kdbus_args args = {
+               .allowed_flags = KDBUS_FLAG_NEGOTIATE,
+               .argv = argv,
+               .argc = ARRAY_SIZE(argv),
+       };
+
+       if (!kdbus_conn_is_ordinary(conn))
+               return -EOPNOTSUPP;
+
+       ret = kdbus_args_parse(&args, argp, &cmd);
+       if (ret != 0)
+               return ret;
+
+       ret = kdbus_name_release(conn->ep->bus->name_registry, conn,
+                                argv[1].item->str);
+       return kdbus_args_clear(&args, ret);
+}
+
+static int kdbus_list_write(struct kdbus_conn *conn,
+                           struct kdbus_conn *c,
+                           struct kdbus_pool_slice *slice,
+                           size_t *pos,
+                           struct kdbus_name_owner *o,
+                           bool write)
+{
+       struct kvec kvec[4];
+       size_t cnt = 0;
+       int ret;
+
+       /* info header */
+       struct kdbus_info info = {
+               .size = 0,
+               .id = c->id,
+               .flags = c->flags,
+       };
+
+       /* fake the header of a kdbus_name item */
+       struct {
+               u64 size;
+               u64 type;
+               u64 flags;
+       } h = {};
+
+       if (o && !kdbus_conn_policy_see_name_unlocked(conn, current_cred(),
+                                                     o->name->name))
+               return 0;
+
+       kdbus_kvec_set(&kvec[cnt++], &info, sizeof(info), &info.size);
+
+       /* append name */
+       if (o) {
+               size_t slen = strlen(o->name->name) + 1;
+
+               h.size = offsetof(struct kdbus_item, name.name) + slen;
+               h.type = KDBUS_ITEM_OWNED_NAME;
+               h.flags = o->flags;
+
+               kdbus_kvec_set(&kvec[cnt++], &h, sizeof(h), &info.size);
+               kdbus_kvec_set(&kvec[cnt++], o->name->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_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 monitors */
+               if (kdbus_conn_is_monitor(c))
+                       continue;
+
+               /* all names the connection owns */
+               if (flags & (KDBUS_LIST_NAMES |
+                            KDBUS_LIST_ACTIVATORS |
+                            KDBUS_LIST_QUEUED)) {
+                       struct kdbus_name_owner *o;
+
+                       list_for_each_entry(o, &c->names_list, conn_entry) {
+                               if (o->flags & KDBUS_NAME_ACTIVATOR) {
+                                       if (!(flags & KDBUS_LIST_ACTIVATORS))
+                                               continue;
+
+                                       ret = kdbus_list_write(conn, c, slice,
+                                                              &p, o, write);
+                                       if (ret < 0) {
+                                               mutex_unlock(&c->lock);
+                                               return ret;
+                                       }
+
+                                       added = true;
+                               } else if (o->flags & KDBUS_NAME_IN_QUEUE) {
+                                       if (!(flags & KDBUS_LIST_QUEUED))
+                                               continue;
+
+                                       ret = kdbus_list_write(conn, c, slice,
+                                                              &p, o, write);
+                                       if (ret < 0) {
+                                               mutex_unlock(&c->lock);
+                                               return ret;
+                                       }
+
+                                       added = true;
+                               } else if (flags & KDBUS_LIST_NAMES) {
+                                       ret = kdbus_list_write(conn, c, slice,
+                                                              &p, o, write);
+                                       if (ret < 0) {
+                                               mutex_unlock(&c->lock);
+                                               return ret;
+                                       }
+
+                                       added = true;
+                               }
+                       }
+               }
+
+               /* nothing added so far, just add the unique ID */
+               if (!added && (flags & KDBUS_LIST_UNIQUE)) {
+                       ret = kdbus_list_write(conn, c, slice, &p, NULL, write);
+                       if (ret < 0)
+                               return ret;
+               }
+       }
+
+       *pos = p;
+       return 0;
+}
+
+/**
+ * kdbus_cmd_list() - handle KDBUS_CMD_LIST
+ * @conn:              connection to operate on
+ * @argp:              command payload
+ *
+ * Return: >=0 on success, negative error code on failure.
+ */
+int kdbus_cmd_list(struct kdbus_conn *conn, void __user *argp)
+{
+       struct kdbus_name_registry *reg = conn->ep->bus->name_registry;
+       struct kdbus_pool_slice *slice = NULL;
+       struct kdbus_cmd_list *cmd;
+       size_t pos, size;
+       int ret;
+
+       struct kdbus_arg argv[] = {
+               { .type = KDBUS_ITEM_NEGOTIATE },
+       };
+       struct kdbus_args args = {
+               .allowed_flags = KDBUS_FLAG_NEGOTIATE |
+                                KDBUS_LIST_UNIQUE |
+                                KDBUS_LIST_NAMES |
+                                KDBUS_LIST_ACTIVATORS |
+                                KDBUS_LIST_QUEUED,
+               .argv = argv,
+               .argc = ARRAY_SIZE(argv),
+       };
+
+       ret = kdbus_args_parse(&args, argp, &cmd);
+       if (ret != 0)
+               return ret;
+
+       /* 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 records */
+       size = 0;
+       ret = kdbus_list_all(conn, cmd->flags, NULL, &size, false);
+       if (ret < 0)
+               goto exit_unlock;
+
+       if (size == 0) {
+               kdbus_pool_publish_empty(conn->pool, &cmd->offset,
+                                        &cmd->list_size);
+       } else {
+               slice = kdbus_pool_slice_alloc(conn->pool, size, false);
+               if (IS_ERR(slice)) {
+                       ret = PTR_ERR(slice);
+                       slice = NULL;
+                       goto exit_unlock;
+               }
+
+               /* copy the records */
+               pos = 0;
+               ret = kdbus_list_all(conn, cmd->flags, slice, &pos, true);
+               if (ret < 0)
+                       goto exit_unlock;
+
+               WARN_ON(pos != size);
+               kdbus_pool_slice_publish(slice, &cmd->offset, &cmd->list_size);
+       }
+
+       if (kdbus_member_set_user(&cmd->offset, argp, typeof(*cmd), offset) ||
+           kdbus_member_set_user(&cmd->list_size, argp,
+                                 typeof(*cmd), list_size))
+               ret = -EFAULT;
+
+exit_unlock:
+       up_read(&conn->ep->policy_db.entries_rwlock);
+       up_read(&conn->ep->bus->conn_rwlock);
+       up_read(&reg->rwlock);
+       kdbus_pool_slice_release(slice);
+       return kdbus_args_clear(&args, ret);
+}
diff --git a/ipc/kdbus/names.h b/ipc/kdbus/names.h
new file mode 100644 (file)
index 0000000..edac59d
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ */
+
+#ifndef __KDBUS_NAMES_H
+#define __KDBUS_NAMES_H
+
+#include <linux/hashtable.h>
+#include <linux/rwsem.h>
+
+struct kdbus_name_entry;
+struct kdbus_name_owner;
+struct kdbus_name_registry;
+
+/**
+ * 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_id:           sequence number of name entry to be able to uniquely
+ *                     identify a name over its registration lifetime
+ * @activator:         activator of this name, or NULL
+ * @queue:             list of queued owners
+ * @hentry:            entry in registry map
+ * @name:              well-known name
+ */
+struct kdbus_name_entry {
+       u64 name_id;
+       struct kdbus_name_owner *activator;
+       struct list_head queue;
+       struct hlist_node hentry;
+       char name[];
+};
+
+/**
+ * struct kdbus_name_owner - owner of a well-known name
+ * @flags:             KDBUS_NAME_* flags of this owner
+ * @conn:              connection owning the name
+ * @name:              name that is owned
+ * @conn_entry:                link into @conn
+ * @name_entry:                link into @name
+ */
+struct kdbus_name_owner {
+       u64 flags;
+       struct kdbus_conn *conn;
+       struct kdbus_name_entry *name;
+       struct list_head conn_entry;
+       struct list_head name_entry;
+};
+
+bool kdbus_name_is_valid(const char *p, bool allow_wildcard);
+
+struct kdbus_name_registry *kdbus_name_registry_new(void);
+void kdbus_name_registry_free(struct kdbus_name_registry *reg);
+
+struct kdbus_name_entry *
+kdbus_name_lookup_unlocked(struct kdbus_name_registry *reg, const char *name);
+
+int kdbus_name_acquire(struct kdbus_name_registry *reg,
+                      struct kdbus_conn *conn, const char *name,
+                      u64 flags, u64 *return_flags);
+void kdbus_name_release_all(struct kdbus_name_registry *reg,
+                           struct kdbus_conn *conn);
+
+int kdbus_cmd_name_acquire(struct kdbus_conn *conn, void __user *argp);
+int kdbus_cmd_name_release(struct kdbus_conn *conn, void __user *argp);
+int kdbus_cmd_list(struct kdbus_conn *conn, void __user *argp);
+
+/**
+ * kdbus_name_get_owner() - get current owner of a name
+ * @name:      name to get current owner of
+ *
+ * This returns a pointer to the current owner of a name (or its activator if
+ * there is no owner). The caller must make sure @name is valid and does not
+ * vanish.
+ *
+ * Return: Pointer to current owner or NULL if there is none.
+ */
+static inline struct kdbus_name_owner *
+kdbus_name_get_owner(struct kdbus_name_entry *name)
+{
+       return list_first_entry_or_null(&name->queue, struct kdbus_name_owner,
+                                       name_entry) ? : name->activator;
+}
+
+#endif
diff --git a/ipc/kdbus/node.c b/ipc/kdbus/node.c
new file mode 100644 (file)
index 0000000..89f58bc
--- /dev/null
@@ -0,0 +1,897 @@
+/*
+ * 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
+ *
+ * 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/atomic.h>
+#include <linux/fs.h>
+#include <linux/idr.h>
+#include <linux/kdev_t.h>
+#include <linux/rbtree.h>
+#include <linux/rwsem.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+
+#include "bus.h"
+#include "domain.h"
+#include "endpoint.h"
+#include "fs.h"
+#include "handle.h"
+#include "node.h"
+#include "util.h"
+
+/**
+ * DOC: kdbus nodes
+ *
+ * Nodes unify lifetime management across exposed kdbus objects and provide a
+ * hierarchy. Each kdbus object, that might be exposed to user-space, has a
+ * kdbus_node object embedded and is linked into the hierarchy. Each node can
+ * have any number (0-n) of child nodes linked. Each child retains a reference
+ * to its parent node. For root-nodes, the parent is NULL.
+ *
+ * Each node object goes through a bunch of states during it's lifetime:
+ *     * NEW
+ *       * LINKED    (can be skipped by NEW->FREED transition)
+ *         * ACTIVE  (can be skipped by LINKED->INACTIVE transition)
+ *       * INACTIVE
+ *       * DRAINED
+ *     * FREED
+ *
+ * Each node is allocated by the caller and initialized via kdbus_node_init().
+ * This never fails and sets the object into state NEW. From now on, ref-counts
+ * on the node manage its lifetime. During init, the ref-count is set to 1. Once
+ * it drops to 0, the node goes to state FREED and the node->free_cb() callback
+ * is called to deallocate any memory.
+ *
+ * After initializing a node, you usually link it into the hierarchy. You need
+ * to provide a parent node and a name. The node will be linked as child to the
+ * parent and a globally unique ID is assigned to the child. The name of the
+ * child must be unique for all children of this parent. Otherwise, linking the
+ * child will fail with -EEXIST.
+ * Note that the child is not marked active, yet. Admittedly, it prevents any
+ * other node from being linked with the same name (thus, it reserves that
+ * name), but any child-lookup (via name or unique ID) will never return this
+ * child unless it has been marked active.
+ *
+ * Once successfully linked, you can use kdbus_node_activate() to activate a
+ * child. This will mark the child active. This state can be skipped by directly
+ * deactivating the child via kdbus_node_deactivate() (see below).
+ * By activating a child, you enable any lookups on this child to succeed from
+ * now on. Furthermore, any code that got its hands on a reference to the node,
+ * can from now on "acquire" the node.
+ *
+ *     Active References (or: 'acquiring' and 'releasing' a node)
+ *     Additionally to normal object references, nodes support something we call
+ *     "active references". An active reference can be acquired via
+ *     kdbus_node_acquire() and released via kdbus_node_release(). A caller
+ *     _must_ own a normal object reference whenever calling those functions.
+ *     Unlike object references, acquiring an active reference can fail (by
+ *     returning 'false' from kdbus_node_acquire()). An active reference can
+ *     only be acquired if the node is marked active. If it is not marked
+ *     active, yet, or if it was already deactivated, no more active references
+ *     can be acquired, ever!
+ *     Active references are used to track tasks working on a node. Whenever a
+ *     task enters kernel-space to perform an action on a node, it acquires an
+ *     active reference, performs the action and releases the reference again.
+ *     While holding an active reference, the node is guaranteed to stay active.
+ *     If the node is deactivated in parallel, the node is marked as
+ *     deactivated, then we wait for all active references to be dropped, before
+ *     we finally proceed with any cleanups. That is, if you hold an active
+ *     reference to a node, any resources that are bound to the "active" state
+ *     are guaranteed to stay accessible until you release your reference.
+ *
+ *     Active-references are very similar to rw-locks, where acquiring a node is
+ *     equal to try-read-lock and releasing to read-unlock. Deactivating a node
+ *     means write-lock and never releasing it again.
+ *     Unlike rw-locks, the 'active reference' concept is more versatile and
+ *     avoids unusual rw-lock usage (never releasing a write-lock..).
+ *
+ *     It is safe to acquire multiple active-references recursively. But you
+ *     need to check the return value of kdbus_node_acquire() on _each_ call. It
+ *     may stop granting references at _any_ time.
+ *
+ *     You're free to perform any operations you want while holding an active
+ *     reference, except sleeping for an indefinite period. Sleeping for a fixed
+ *     amount of time is fine, but you usually should not wait on wait-queues
+ *     without a timeout.
+ *     For example, if you wait for I/O to happen, you should gather all data
+ *     and schedule the I/O operation, then release your active reference and
+ *     wait for it to complete. Then try to acquire a new reference. If it
+ *     fails, perform any cleanup (the node is now dead). Otherwise, you can
+ *     finish your operation.
+ *
+ * All nodes can be deactivated via kdbus_node_deactivate() at any time. You can
+ * call this multiple times, even in parallel or on nodes that were never
+ * linked, and it will just work. The only restriction is, you must not hold an
+ * active reference when calling kdbus_node_deactivate().
+ * By deactivating a node, it is immediately marked inactive. Then, we wait for
+ * all active references to be released (called 'draining' the node). This
+ * shouldn't take very long as we don't perform long-lasting operations while
+ * holding an active reference. Note that once the node is marked inactive, no
+ * new active references can be acquired.
+ * Once all active references are dropped, the node is considered 'drained'. Now
+ * kdbus_node_deactivate() is called on each child of the node before we
+ * continue deactivating our node. That is, once all children are entirely
+ * deactivated, we call ->release_cb() of our node. ->release_cb() can release
+ * any resources on that node which are bound to the "active" state of a node.
+ * When done, we unlink the node from its parent rb-tree, mark it as
+ * 'released' and return.
+ * If kdbus_node_deactivate() is called multiple times (even in parallel), all
+ * but one caller will just wait until the node is fully deactivated. That is,
+ * one random caller of kdbus_node_deactivate() is selected to call
+ * ->release_cb() and cleanup the node. Only once all this is done, all other
+ * callers will return from kdbus_node_deactivate(). That is, it doesn't matter
+ * whether you're the selected caller or not, it will only return after
+ * everything is fully done.
+ *
+ * When a node is activated, we acquire a normal object reference to the node.
+ * This reference is dropped after deactivation is fully done (and only iff the
+ * node really was activated). This allows callers to link+activate a child node
+ * and then drop all refs. The node will be deactivated together with the
+ * parent, and then be freed when this reference is dropped.
+ *
+ * Currently, nodes provide a bunch of resources that external code can use
+ * directly. This includes:
+ *
+ *     * node->waitq: Each node has its own wait-queue that is used to manage
+ *                    the 'active' state. When a node is deactivated, we wait on
+ *                    this queue until all active refs are dropped. Analogously,
+ *                    when you release an active reference on a deactivated
+ *                    node, and the active ref-count drops to 0, we wake up a
+ *                    single thread on this queue. Furthermore, once the
+ *                    ->release_cb() callback finished, we wake up all waiters.
+ *                    The node-owner is free to re-use this wait-queue for other
+ *                    purposes. As node-management uses this queue only during
+ *                    deactivation, it is usually totally fine to re-use the
+ *                    queue for other, preferably low-overhead, use-cases.
+ *
+ *     * node->type: This field defines the type of the owner of this node. It
+ *                   must be set during node initialization and must remain
+ *                   constant. The node management never looks at this value,
+ *                   but external users might use to gain access to the owner
+ *                   object of a node.
+ *                   It is totally up to the owner of the node to define what
+ *                   their type means. Usually it means you can access the
+ *                   parent structure via container_of(), as long as you hold an
+ *                   active reference to the node.
+ *
+ *     * node->free_cb:    callback after all references are dropped
+ *       node->release_cb: callback during node deactivation
+ *                         These fields must be set by the node owner during
+ *                         node initialization. They must remain constant. If
+ *                         NULL, they're skipped.
+ *
+ *     * node->mode: filesystem access modes
+ *       node->uid:  filesystem owner uid
+ *       node->gid:  filesystem owner gid
+ *                   These fields must be set by the node owner during node
+ *                   initialization. They must remain constant and may be
+ *                   accessed by other callers to properly initialize
+ *                   filesystem nodes.
+ *
+ *     * node->id: This is an unsigned 32bit integer allocated by an IDA. It is
+ *                 always kept as small as possible during allocation and is
+ *                 globally unique across all nodes allocated by this module. 0
+ *                 is reserved as "not assigned" and is the default.
+ *                 The ID is assigned during kdbus_node_link() and is kept until
+ *                 the object is freed. Thus, the ID surpasses the active
+ *                 lifetime of a node. As long as you hold an object reference
+ *                 to a node (and the node was linked once), the ID is valid and
+ *                 unique.
+ *
+ *     * node->name: name of this node
+ *       node->hash: 31bit hash-value of @name (range [2..INT_MAX-1])
+ *                   These values follow the same lifetime rules as node->id.
+ *                   They're initialized when the node is linked and then remain
+ *                   constant until the last object reference is dropped.
+ *                   Unlike the id, the name is only unique across all siblings
+ *                   and only until the node is deactivated. Currently, the name
+ *                   is even unique if linked but not activated, yet. This might
+ *                   change in the future, though. Code should not rely on this.
+ *
+ *     * node->lock:     lock to protect node->children, node->rb, node->parent
+ *     * node->parent: Reference to parent node. This is set during LINK time
+ *                     and is dropped during destruction. You must not access
+ *                     it unless you hold an active reference to the node or if
+ *                     you know the node is dead.
+ *     * node->children: rb-tree of all linked children of this node. You must
+ *                       not access this directly, but use one of the iterator
+ *                       or lookup helpers.
+ */
+
+/*
+ * Bias values track states of "active references". They're all negative. If a
+ * node is active, its active-ref-counter is >=0 and tracks all active
+ * references. Once a node is deactivaed, we subtract NODE_BIAS. This means, the
+ * counter is now negative but still counts the active references. Once it drops
+ * to exactly NODE_BIAS, we know all active references were dropped. Exactly one
+ * thread will change it to NODE_RELEASE now, perform cleanup and then put it
+ * into NODE_DRAINED. Once drained, all other threads that tried deactivating
+ * the node will now be woken up (thus, they wait until the node is fully done).
+ * The initial state during node-setup is NODE_NEW. If a node is directly
+ * deactivated without having ever been active, it is put into
+ * NODE_RELEASE_DIRECT instead of NODE_BIAS. This tracks this one-bit state
+ * across node-deactivation. The task putting it into NODE_RELEASE now knows
+ * whether the node was active before or not.
+ *
+ * Some archs implement atomic_sub(v) with atomic_add(-v), so reserve INT_MIN
+ * to avoid overflows if multiplied by -1.
+ */
+#define KDBUS_NODE_BIAS                        (INT_MIN + 5)
+#define KDBUS_NODE_RELEASE_DIRECT      (KDBUS_NODE_BIAS - 1)
+#define KDBUS_NODE_RELEASE             (KDBUS_NODE_BIAS - 2)
+#define KDBUS_NODE_DRAINED             (KDBUS_NODE_BIAS - 3)
+#define KDBUS_NODE_NEW                 (KDBUS_NODE_BIAS - 4)
+
+/* global unique ID mapping for kdbus nodes */
+DEFINE_IDA(kdbus_node_ida);
+
+/**
+ * kdbus_node_name_hash() - hash a name
+ * @name:      The string to hash
+ *
+ * This computes the hash of @name. It is guaranteed to be in the range
+ * [2..INT_MAX-1]. The values 1, 2 and INT_MAX are unused as they are reserved
+ * for the filesystem code.
+ *
+ * Return: hash value of the passed string
+ */
+static unsigned int kdbus_node_name_hash(const char *name)
+{
+       unsigned int hash;
+
+       /* reserve hash numbers 0, 1 and >=INT_MAX for magic directories */
+       hash = kdbus_strhash(name) & INT_MAX;
+       if (hash < 2)
+               hash += 2;
+       if (hash >= INT_MAX)
+               hash = INT_MAX - 1;
+
+       return hash;
+}
+
+/**
+ * kdbus_node_name_compare() - compare a name with a node's name
+ * @hash:      hash of the string to compare the node with
+ * @name:      name to compare the node with
+ * @node:      node to compare the name with
+ *
+ * Return: 0 if @name and @hash exactly match the information in @node, or
+ * an integer less than or greater than zero if @name is found, respectively,
+ * to be less than or be greater than the string stored in @node.
+ */
+static int kdbus_node_name_compare(unsigned int hash, const char *name,
+                                  const struct kdbus_node *node)
+{
+       if (hash != node->hash)
+               return hash - node->hash;
+
+       return strcmp(name, node->name);
+}
+
+/**
+ * kdbus_node_init() - initialize a kdbus_node
+ * @node:      Pointer to the node to initialize
+ * @type:      The type the node will have (KDBUS_NODE_*)
+ *
+ * The caller is responsible of allocating @node and initializating it to zero.
+ * Once this call returns, you must use the node_ref() and node_unref()
+ * functions to manage this node.
+ */
+void kdbus_node_init(struct kdbus_node *node, unsigned int type)
+{
+       atomic_set(&node->refcnt, 1);
+       mutex_init(&node->lock);
+       node->id = 0;
+       node->type = type;
+       RB_CLEAR_NODE(&node->rb);
+       node->children = RB_ROOT;
+       init_waitqueue_head(&node->waitq);
+       atomic_set(&node->active, KDBUS_NODE_NEW);
+}
+
+/**
+ * kdbus_node_link() - link a node into the nodes system
+ * @node:      Pointer to the node to initialize
+ * @parent:    Pointer to a parent node, may be %NULL
+ * @name:      The name of the node (or NULL if root node)
+ *
+ * This links a node into the hierarchy. This must not be called multiple times.
+ * If @parent is NULL, the node becomes a new root node.
+ *
+ * This call will fail if @name is not unique across all its siblings or if no
+ * ID could be allocated. You must not activate a node if linking failed! It is
+ * safe to deactivate it, though.
+ *
+ * Once you linked a node, you must call kdbus_node_deactivate() before you drop
+ * the last reference (even if you never activate the node).
+ *
+ * Return: 0 on success. negative error otherwise.
+ */
+int kdbus_node_link(struct kdbus_node *node, struct kdbus_node *parent,
+                   const char *name)
+{
+       int ret;
+
+       if (WARN_ON(node->type != KDBUS_NODE_DOMAIN && !parent))
+               return -EINVAL;
+
+       if (WARN_ON(parent && !name))
+               return -EINVAL;
+
+       if (name) {
+               node->name = kstrdup(name, GFP_KERNEL);
+               if (!node->name)
+                       return -ENOMEM;
+
+               node->hash = kdbus_node_name_hash(name);
+       }
+
+       ret = ida_simple_get(&kdbus_node_ida, 1, 0, GFP_KERNEL);
+       if (ret < 0)
+               return ret;
+
+       node->id = ret;
+       ret = 0;
+
+       if (parent) {
+               struct rb_node **n, *prev;
+
+               if (!kdbus_node_acquire(parent))
+                       return -ESHUTDOWN;
+
+               mutex_lock(&parent->lock);
+
+               n = &parent->children.rb_node;
+               prev = NULL;
+
+               while (*n) {
+                       struct kdbus_node *pos;
+                       int result;
+
+                       pos = kdbus_node_from_rb(*n);
+                       prev = *n;
+                       result = kdbus_node_name_compare(node->hash,
+                                                        node->name,
+                                                        pos);
+                       if (result == 0) {
+                               ret = -EEXIST;
+                               goto exit_unlock;
+                       }
+
+                       if (result < 0)
+                               n = &pos->rb.rb_left;
+                       else
+                               n = &pos->rb.rb_right;
+               }
+
+               /* add new node and rebalance the tree */
+               rb_link_node(&node->rb, prev, n);
+               rb_insert_color(&node->rb, &parent->children);
+               node->parent = kdbus_node_ref(parent);
+
+exit_unlock:
+               mutex_unlock(&parent->lock);
+               kdbus_node_release(parent);
+       }
+
+       return ret;
+}
+
+/**
+ * kdbus_node_ref() - Acquire object reference
+ * @node:      node to acquire reference to (or NULL)
+ *
+ * This acquires a new reference to @node. You must already own a reference when
+ * calling this!
+ * If @node is NULL, this is a no-op.
+ *
+ * Return: @node is returned
+ */
+struct kdbus_node *kdbus_node_ref(struct kdbus_node *node)
+{
+       if (node)
+               atomic_inc(&node->refcnt);
+       return node;
+}
+
+/**
+ * kdbus_node_unref() - Drop object reference
+ * @node:      node to drop reference to (or NULL)
+ *
+ * This drops an object reference to @node. You must not access the node if you
+ * no longer own a reference.
+ * If the ref-count drops to 0, the object will be destroyed (->free_cb will be
+ * called).
+ *
+ * If you linked or activated the node, you must deactivate the node before you
+ * drop your last reference! If you didn't link or activate the node, you can
+ * drop any reference you want.
+ *
+ * Note that this calls into ->free_cb() and thus _might_ sleep. The ->free_cb()
+ * callbacks must not acquire any outer locks, though. So you can safely drop
+ * references while holding locks.
+ *
+ * If @node is NULL, this is a no-op.
+ *
+ * Return: This always returns NULL
+ */
+struct kdbus_node *kdbus_node_unref(struct kdbus_node *node)
+{
+       if (node && atomic_dec_and_test(&node->refcnt)) {
+               struct kdbus_node safe = *node;
+
+               WARN_ON(atomic_read(&node->active) != KDBUS_NODE_DRAINED);
+               WARN_ON(!RB_EMPTY_NODE(&node->rb));
+
+               if (node->free_cb)
+                       node->free_cb(node);
+               if (safe.id > 0)
+                       ida_simple_remove(&kdbus_node_ida, safe.id);
+
+               kfree(safe.name);
+
+               /*
+                * kdbusfs relies on the parent to be available even after the
+                * node was deactivated and unlinked. Therefore, we pin it
+                * until a node is destroyed.
+                */
+               kdbus_node_unref(safe.parent);
+       }
+
+       return NULL;
+}
+
+/**
+ * kdbus_node_is_active() - test whether a node is active
+ * @node:      node to test
+ *
+ * This checks whether @node is active. That means, @node was linked and
+ * activated by the node owner and hasn't been deactivated, yet. If, and only
+ * if, a node is active, kdbus_node_acquire() will be able to acquire active
+ * references.
+ *
+ * Note that this function does not give any lifetime guarantees. After this
+ * call returns, the node might be deactivated immediately. Normally, what you
+ * want is to acquire a real active reference via kdbus_node_acquire().
+ *
+ * Return: true if @node is active, false otherwise
+ */
+bool kdbus_node_is_active(struct kdbus_node *node)
+{
+       return atomic_read(&node->active) >= 0;
+}
+
+/**
+ * kdbus_node_is_deactivated() - test whether a node was already deactivated
+ * @node:      node to test
+ *
+ * This checks whether kdbus_node_deactivate() was called on @node. Note that
+ * this might be true even if you never deactivated the node directly, but only
+ * one of its ancestors.
+ *
+ * Note that even if this returns 'false', the node might get deactivated
+ * immediately after the call returns.
+ *
+ * Return: true if @node was already deactivated, false if not
+ */
+bool kdbus_node_is_deactivated(struct kdbus_node *node)
+{
+       int v;
+
+       v = atomic_read(&node->active);
+       return v != KDBUS_NODE_NEW && v < 0;
+}
+
+/**
+ * kdbus_node_activate() - activate a node
+ * @node:      node to activate
+ *
+ * This marks @node as active if, and only if, the node wasn't activated nor
+ * deactivated, yet, and the parent is still active. Any but the first call to
+ * kdbus_node_activate() is a no-op.
+ * If you called kdbus_node_deactivate() before, then even the first call to
+ * kdbus_node_activate() will be a no-op.
+ *
+ * This call doesn't give any lifetime guarantees. The node might get
+ * deactivated immediately after this call returns. Or the parent might already
+ * be deactivated, which will make this call a no-op.
+ *
+ * If this call successfully activated a node, it will take an object reference
+ * to it. This reference is dropped after the node is deactivated. Therefore,
+ * the object owner can safely drop their reference to @node iff they know that
+ * its parent node will get deactivated at some point. Once the parent node is
+ * deactivated, it will deactivate all its child and thus drop this reference
+ * again.
+ *
+ * Return: True if this call successfully activated the node, otherwise false.
+ *         Note that this might return false, even if the node is still active
+ *         (eg., if you called this a second time).
+ */
+bool kdbus_node_activate(struct kdbus_node *node)
+{
+       bool res = false;
+
+       mutex_lock(&node->lock);
+       if (atomic_read(&node->active) == KDBUS_NODE_NEW) {
+               atomic_sub(KDBUS_NODE_NEW, &node->active);
+               /* activated nodes have ref +1 */
+               kdbus_node_ref(node);
+               res = true;
+       }
+       mutex_unlock(&node->lock);
+
+       return res;
+}
+
+/**
+ * kdbus_node_deactivate() - deactivate a node
+ * @node:      The node to deactivate.
+ *
+ * This function recursively deactivates this node and all its children. It
+ * returns only once all children and the node itself were recursively disabled
+ * (even if you call this function multiple times in parallel).
+ *
+ * It is safe to call this function on _any_ node that was initialized _any_
+ * number of times.
+ *
+ * This call may sleep, as it waits for all active references to be dropped.
+ */
+void kdbus_node_deactivate(struct kdbus_node *node)
+{
+       struct kdbus_node *pos, *child;
+       struct rb_node *rb;
+       int v_pre, v_post;
+
+       pos = node;
+
+       /*
+        * To avoid recursion, we perform back-tracking while deactivating
+        * nodes. For each node we enter, we first mark the active-counter as
+        * deactivated by adding BIAS. If the node as children, we set the first
+        * child as current position and start over. If the node has no
+        * children, we drain the node by waiting for all active refs to be
+        * dropped and then releasing the node.
+        *
+        * After the node is released, we set its parent as current position
+        * and start over. If the current position was the initial node, we're
+        * done.
+        *
+        * Note that this function can be called in parallel by multiple
+        * callers. We make sure that each node is only released once, and any
+        * racing caller will wait until the other thread fully released that
+        * node.
+        */
+
+       for (;;) {
+               /*
+                * Add BIAS to node->active to mark it as inactive. If it was
+                * never active before, immediately mark it as RELEASE_INACTIVE
+                * so we remember this state.
+                * We cannot remember v_pre as we might iterate into the
+                * children, overwriting v_pre, before we can release our node.
+                */
+               mutex_lock(&pos->lock);
+               v_pre = atomic_read(&pos->active);
+               if (v_pre >= 0)
+                       atomic_add_return(KDBUS_NODE_BIAS, &pos->active);
+               else if (v_pre == KDBUS_NODE_NEW)
+                       atomic_set(&pos->active, KDBUS_NODE_RELEASE_DIRECT);
+               mutex_unlock(&pos->lock);
+
+               /* wait until all active references were dropped */
+               wait_event(pos->waitq,
+                          atomic_read(&pos->active) <= KDBUS_NODE_BIAS);
+
+               mutex_lock(&pos->lock);
+               /* recurse into first child if any */
+               rb = rb_first(&pos->children);
+               if (rb) {
+                       child = kdbus_node_ref(kdbus_node_from_rb(rb));
+                       mutex_unlock(&pos->lock);
+                       pos = child;
+                       continue;
+               }
+
+               /* mark object as RELEASE */
+               v_post = atomic_read(&pos->active);
+               if (v_post == KDBUS_NODE_BIAS ||
+                   v_post == KDBUS_NODE_RELEASE_DIRECT)
+                       atomic_set(&pos->active, KDBUS_NODE_RELEASE);
+               mutex_unlock(&pos->lock);
+
+               /*
+                * If this is the thread that marked the object as RELEASE, we
+                * perform the actual release. Otherwise, we wait until the
+                * release is done and the node is marked as DRAINED.
+                */
+               if (v_post == KDBUS_NODE_BIAS ||
+                   v_post == KDBUS_NODE_RELEASE_DIRECT) {
+                       if (pos->release_cb)
+                               pos->release_cb(pos, v_post == KDBUS_NODE_BIAS);
+
+                       if (pos->parent) {
+                               mutex_lock(&pos->parent->lock);
+                               if (!RB_EMPTY_NODE(&pos->rb)) {
+                                       rb_erase(&pos->rb,
+                                                &pos->parent->children);
+                                       RB_CLEAR_NODE(&pos->rb);
+                               }
+                               mutex_unlock(&pos->parent->lock);
+                       }
+
+                       /* mark as DRAINED */
+                       atomic_set(&pos->active, KDBUS_NODE_DRAINED);
+                       wake_up_all(&pos->waitq);
+
+                       /* drop VFS cache */
+                       kdbus_fs_flush(pos);
+
+                       /*
+                        * If the node was activated and someone subtracted BIAS
+                        * from it to deactivate it, we, and only us, are
+                        * responsible to release the extra ref-count that was
+                        * taken once in kdbus_node_activate().
+                        * If the node was never activated, no-one ever
+                        * subtracted BIAS, but instead skipped that state and
+                        * immediately went to NODE_RELEASE_DIRECT. In that case
+                        * we must not drop the reference.
+                        */
+                       if (v_post == KDBUS_NODE_BIAS)
+                               kdbus_node_unref(pos);
+               } else {
+                       /* wait until object is DRAINED */
+                       wait_event(pos->waitq,
+                           atomic_read(&pos->active) == KDBUS_NODE_DRAINED);
+               }
+
+               /*
+                * We're done with the current node. Continue on its parent
+                * again, which will try deactivating its next child, or itself
+                * if no child is left.
+                * If we've reached our initial node again, we are done and
+                * can safely return.
+                */
+               if (pos == node)
+                       break;
+
+               child = pos;
+               pos = pos->parent;
+               kdbus_node_unref(child);
+       }
+}
+
+/**
+ * kdbus_node_acquire() - Acquire an active ref on a node
+ * @node:      The node
+ *
+ * This acquires an active-reference to @node. This will only succeed if the
+ * node is active. You must release this active reference via
+ * kdbus_node_release() again.
+ *
+ * See the introduction to "active references" for more details.
+ *
+ * Return: %true if @node was non-NULL and active
+ */
+bool kdbus_node_acquire(struct kdbus_node *node)
+{
+       return node && atomic_inc_unless_negative(&node->active);
+}
+
+/**
+ * kdbus_node_release() - Release an active ref on a node
+ * @node:      The node
+ *
+ * This releases an active reference that was previously acquired via
+ * kdbus_node_acquire(). See kdbus_node_acquire() for details.
+ */
+void kdbus_node_release(struct kdbus_node *node)
+{
+       if (node && atomic_dec_return(&node->active) == KDBUS_NODE_BIAS)
+               wake_up(&node->waitq);
+}
+
+/**
+ * kdbus_node_find_child() - Find child by name
+ * @node:      parent node to search through
+ * @name:      name of child node
+ *
+ * This searches through all children of @node for a child-node with name @name.
+ * If not found, or if the child is deactivated, NULL is returned. Otherwise,
+ * the child is acquired and a new reference is returned.
+ *
+ * If you're done with the child, you need to release it and drop your
+ * reference.
+ *
+ * This function does not acquire the parent node. However, if the parent was
+ * already deactivated, then kdbus_node_deactivate() will, at some point, also
+ * deactivate the child. Therefore, we can rely on the explicit ordering during
+ * deactivation.
+ *
+ * Return: Reference to acquired child node, or NULL if not found / not active.
+ */
+struct kdbus_node *kdbus_node_find_child(struct kdbus_node *node,
+                                        const char *name)
+{
+       struct kdbus_node *child;
+       struct rb_node *rb;
+       unsigned int hash;
+       int ret;
+
+       hash = kdbus_node_name_hash(name);
+
+       mutex_lock(&node->lock);
+       rb = node->children.rb_node;
+       while (rb) {
+               child = kdbus_node_from_rb(rb);
+               ret = kdbus_node_name_compare(hash, name, child);
+               if (ret < 0)
+                       rb = rb->rb_left;
+               else if (ret > 0)
+                       rb = rb->rb_right;
+               else
+                       break;
+       }
+       if (rb && kdbus_node_acquire(child))
+               kdbus_node_ref(child);
+       else
+               child = NULL;
+       mutex_unlock(&node->lock);
+
+       return child;
+}
+
+static struct kdbus_node *node_find_closest_unlocked(struct kdbus_node *node,
+                                                    unsigned int hash,
+                                                    const char *name)
+{
+       struct kdbus_node *n, *pos = NULL;
+       struct rb_node *rb;
+       int res;
+
+       /*
+        * Find the closest child with ``node->hash >= hash'', or, if @name is
+        * valid, ``node->name >= name'' (where '>=' is the lex. order).
+        */
+
+       rb = node->children.rb_node;
+       while (rb) {
+               n = kdbus_node_from_rb(rb);
+
+               if (name)
+                       res = kdbus_node_name_compare(hash, name, n);
+               else
+                       res = hash - n->hash;
+
+               if (res <= 0) {
+                       rb = rb->rb_left;
+                       pos = n;
+               } else { /* ``hash > n->hash'', ``name > n->name'' */
+                       rb = rb->rb_right;
+               }
+       }
+
+       return pos;
+}
+
+/**
+ * kdbus_node_find_closest() - Find closest child-match
+ * @node:      parent node to search through
+ * @hash:      hash value to find closest match for
+ *
+ * Find the closest child of @node with a hash greater than or equal to @hash.
+ * The closest match is the left-most child of @node with this property. Which
+ * means, it is the first child with that hash returned by
+ * kdbus_node_next_child(), if you'd iterate the whole parent node.
+ *
+ * Return: Reference to acquired child, or NULL if none found.
+ */
+struct kdbus_node *kdbus_node_find_closest(struct kdbus_node *node,
+                                          unsigned int hash)
+{
+       struct kdbus_node *child;
+       struct rb_node *rb;
+
+       mutex_lock(&node->lock);
+
+       child = node_find_closest_unlocked(node, hash, NULL);
+       while (child && !kdbus_node_acquire(child)) {
+               rb = rb_next(&child->rb);
+               if (rb)
+                       child = kdbus_node_from_rb(rb);
+               else
+                       child = NULL;
+       }
+       kdbus_node_ref(child);
+
+       mutex_unlock(&node->lock);
+
+       return child;
+}
+
+/**
+ * kdbus_node_next_child() - Acquire next child
+ * @node:      parent node
+ * @prev:      previous child-node position or NULL
+ *
+ * This function returns a reference to the next active child of @node, after
+ * the passed position @prev. If @prev is NULL, a reference to the first active
+ * child is returned. If no more active children are found, NULL is returned.
+ *
+ * This function acquires the next child it returns. If you're done with the
+ * returned pointer, you need to release _and_ unref it.
+ *
+ * The passed in pointer @prev is not modified by this function, and it does
+ * *not* have to be active. If @prev was acquired via different means, or if it
+ * was unlinked from its parent before you pass it in, then this iterator will
+ * still return the next active child (it will have to search through the
+ * rb-tree based on the node-name, though).
+ * However, @prev must not be linked to a different parent than @node!
+ *
+ * Return: Reference to next acquired child, or NULL if at the end.
+ */
+struct kdbus_node *kdbus_node_next_child(struct kdbus_node *node,
+                                        struct kdbus_node *prev)
+{
+       struct kdbus_node *pos = NULL;
+       struct rb_node *rb;
+
+       mutex_lock(&node->lock);
+
+       if (!prev) {
+               /*
+                * New iteration; find first node in rb-tree and try to acquire
+                * it. If we got it, directly return it as first element.
+                * Otherwise, the loop below will find the next active node.
+                */
+               rb = rb_first(&node->children);
+               if (!rb)
+                       goto exit;
+               pos = kdbus_node_from_rb(rb);
+               if (kdbus_node_acquire(pos))
+                       goto exit;
+       } else if (RB_EMPTY_NODE(&prev->rb)) {
+               /*
+                * The current iterator is no longer linked to the rb-tree. Use
+                * its hash value and name to find the next _higher_ node and
+                * acquire it. If we got it, return it as next element.
+                * Otherwise, the loop below will find the next active node.
+                */
+               pos = node_find_closest_unlocked(node, prev->hash, prev->name);
+               if (!pos)
+                       goto exit;
+               if (kdbus_node_acquire(pos))
+                       goto exit;
+       } else {
+               /*
+                * The current iterator is still linked to the parent. Set it
+                * as current position and use the loop below to find the next
+                * active element.
+                */
+               pos = prev;
+       }
+
+       /* @pos was already returned or is inactive; find next active node */
+       do {
+               rb = rb_next(&pos->rb);
+               if (rb)
+                       pos = kdbus_node_from_rb(rb);
+               else
+                       pos = NULL;
+       } while (pos && !kdbus_node_acquire(pos));
+
+exit:
+       /* @pos is NULL or acquired. Take ref if non-NULL and return it */
+       kdbus_node_ref(pos);
+       mutex_unlock(&node->lock);
+       return pos;
+}
diff --git a/ipc/kdbus/node.h b/ipc/kdbus/node.h
new file mode 100644 (file)
index 0000000..970e02b
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * 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
+ *
+ * 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_NODE_H
+#define __KDBUS_NODE_H
+
+#include <linux/atomic.h>
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/wait.h>
+
+struct kdbus_node;
+
+enum kdbus_node_type {
+       KDBUS_NODE_DOMAIN,
+       KDBUS_NODE_CONTROL,
+       KDBUS_NODE_BUS,
+       KDBUS_NODE_ENDPOINT,
+};
+
+typedef void (*kdbus_node_free_t) (struct kdbus_node *node);
+typedef void (*kdbus_node_release_t) (struct kdbus_node *node, bool was_active);
+
+struct kdbus_node {
+       atomic_t refcnt;
+       atomic_t active;
+       wait_queue_head_t waitq;
+
+       /* static members */
+       unsigned int type;
+       kdbus_node_free_t free_cb;
+       kdbus_node_release_t release_cb;
+       umode_t mode;
+       kuid_t uid;
+       kgid_t gid;
+
+       /* valid once linked */
+       char *name;
+       unsigned int hash;
+       unsigned int id;
+       struct kdbus_node *parent; /* may be NULL */
+
+       /* valid iff active */
+       struct mutex lock;
+       struct rb_node rb;
+       struct rb_root children;
+};
+
+#define kdbus_node_from_rb(_node) rb_entry((_node), struct kdbus_node, rb)
+
+extern struct ida kdbus_node_ida;
+
+void kdbus_node_init(struct kdbus_node *node, unsigned int type);
+
+int kdbus_node_link(struct kdbus_node *node, struct kdbus_node *parent,
+                   const char *name);
+
+struct kdbus_node *kdbus_node_ref(struct kdbus_node *node);
+struct kdbus_node *kdbus_node_unref(struct kdbus_node *node);
+
+bool kdbus_node_is_active(struct kdbus_node *node);
+bool kdbus_node_is_deactivated(struct kdbus_node *node);
+bool kdbus_node_activate(struct kdbus_node *node);
+void kdbus_node_deactivate(struct kdbus_node *node);
+
+bool kdbus_node_acquire(struct kdbus_node *node);
+void kdbus_node_release(struct kdbus_node *node);
+
+struct kdbus_node *kdbus_node_find_child(struct kdbus_node *node,
+                                        const char *name);
+struct kdbus_node *kdbus_node_find_closest(struct kdbus_node *node,
+                                          unsigned int hash);
+struct kdbus_node *kdbus_node_next_child(struct kdbus_node *node,
+                                        struct kdbus_node *prev);
+
+#endif
diff --git a/ipc/kdbus/notify.c b/ipc/kdbus/notify.c
new file mode 100644 (file)
index 0000000..375758c
--- /dev/null
@@ -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, &notify_list);
+       spin_unlock(&bus->notify_lock);
+
+       list_for_each_entry_safe(s, tmp, &notify_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);
+       }
+}
diff --git a/ipc/kdbus/notify.h b/ipc/kdbus/notify.h
new file mode 100644 (file)
index 0000000..03df464
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+#ifndef __KDBUS_NOTIFY_H
+#define __KDBUS_NOTIFY_H
+
+struct kdbus_bus;
+
+int kdbus_notify_id_change(struct kdbus_bus *bus, u64 type, u64 id, u64 flags);
+int kdbus_notify_reply_timeout(struct kdbus_bus *bus, u64 id, u64 cookie);
+int kdbus_notify_reply_dead(struct kdbus_bus *bus, u64 id, u64 cookie);
+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);
+void kdbus_notify_flush(struct kdbus_bus *bus);
+void kdbus_notify_free(struct kdbus_bus *bus);
+
+#endif
diff --git a/ipc/kdbus/policy.c b/ipc/kdbus/policy.c
new file mode 100644 (file)
index 0000000..f2618e1
--- /dev/null
@@ -0,0 +1,489 @@
+/*
+ * 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
+ *
+ * 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/dcache.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#include "bus.h"
+#include "connection.h"
+#include "domain.h"
+#include "item.h"
+#include "names.h"
+#include "policy.h"
+
+#define KDBUS_POLICY_HASH_SIZE 64
+
+/**
+ * struct kdbus_policy_db_entry_access - a database entry access item
+ * @type:              One of KDBUS_POLICY_ACCESS_* types
+ * @access:            Access to grant. One of KDBUS_POLICY_*
+ * @uid:               For KDBUS_POLICY_ACCESS_USER, the global uid
+ * @gid:               For KDBUS_POLICY_ACCESS_GROUP, the global gid
+ * @list:              List entry item for the entry's list
+ *
+ * This is the internal version of struct kdbus_policy_db_access.
+ */
+struct kdbus_policy_db_entry_access {
+       u8 type;                /* USER, GROUP, WORLD */
+       u8 access;              /* OWN, TALK, SEE */
+       union {
+               kuid_t uid;     /* global uid */
+               kgid_t gid;     /* global gid */
+       };
+       struct list_head list;
+};
+
+/**
+ * struct kdbus_policy_db_entry - a policy database entry
+ * @name:              The name to match the policy entry against
+ * @hentry:            The hash entry for the database's entries_hash
+ * @access_list:       List head for keeping tracks of the entry's
+ *                     access items.
+ * @owner:             The owner of this entry. Can be a kdbus_conn or
+ *                     a kdbus_ep object.
+ * @wildcard:          The name is a wildcard, such as ending on '.*'
+ */
+struct kdbus_policy_db_entry {
+       char *name;
+       struct hlist_node hentry;
+       struct list_head access_list;
+       const void *owner;
+       bool wildcard:1;
+};
+
+static void kdbus_policy_entry_free(struct kdbus_policy_db_entry *e)
+{
+       struct kdbus_policy_db_entry_access *a, *tmp;
+
+       list_for_each_entry_safe(a, tmp, &e->access_list, list) {
+               list_del(&a->list);
+               kfree(a);
+       }
+
+       kfree(e->name);
+       kfree(e);
+}
+
+static unsigned int kdbus_strnhash(const char *str, size_t len)
+{
+       unsigned long hash = init_name_hash();
+
+       while (len--)
+               hash = partial_name_hash(*str++, hash);
+
+       return end_name_hash(hash);
+}
+
+static const struct kdbus_policy_db_entry *
+kdbus_policy_lookup(struct kdbus_policy_db *db, const char *name, u32 hash)
+{
+       struct kdbus_policy_db_entry *e;
+       const char *dot;
+       size_t len;
+
+       /* find exact match */
+       hash_for_each_possible(db->entries_hash, e, hentry, hash)
+               if (strcmp(e->name, name) == 0 && !e->wildcard)
+                       return e;
+
+       /* find wildcard match */
+
+       dot = strrchr(name, '.');
+       if (!dot)
+               return NULL;
+
+       len = dot - name;
+       hash = kdbus_strnhash(name, len);
+
+       hash_for_each_possible(db->entries_hash, e, hentry, hash)
+               if (e->wildcard && !strncmp(e->name, name, len) &&
+                   !e->name[len])
+                       return e;
+
+       return NULL;
+}
+
+/**
+ * kdbus_policy_db_clear - release all memory from a policy db
+ * @db:                The policy database
+ */
+void kdbus_policy_db_clear(struct kdbus_policy_db *db)
+{
+       struct kdbus_policy_db_entry *e;
+       struct hlist_node *tmp;
+       unsigned int i;
+
+       /* purge entries */
+       down_write(&db->entries_rwlock);
+       hash_for_each_safe(db->entries_hash, i, tmp, e, hentry) {
+               hash_del(&e->hentry);
+               kdbus_policy_entry_free(e);
+       }
+       up_write(&db->entries_rwlock);
+}
+
+/**
+ * kdbus_policy_db_init() - initialize a new policy database
+ * @db:                The location of the database
+ *
+ * This initializes a new policy-db. The underlying memory must have been
+ * cleared to zero by the caller.
+ */
+void kdbus_policy_db_init(struct kdbus_policy_db *db)
+{
+       hash_init(db->entries_hash);
+       init_rwsem(&db->entries_rwlock);
+}
+
+/**
+ * kdbus_policy_query_unlocked() - Query the policy database
+ * @db:                Policy database
+ * @cred:      Credentials to test against
+ * @name:      Name to query
+ * @hash:      Hash value of @name
+ *
+ * Same as kdbus_policy_query() but requires the caller to lock the policy
+ * database against concurrent writes.
+ *
+ * Return: The highest KDBUS_POLICY_* access type found, or -EPERM if none.
+ */
+int kdbus_policy_query_unlocked(struct kdbus_policy_db *db,
+                               const struct cred *cred, const char *name,
+                               unsigned int hash)
+{
+       struct kdbus_policy_db_entry_access *a;
+       const struct kdbus_policy_db_entry *e;
+       int i, highest = -EPERM;
+
+       e = kdbus_policy_lookup(db, name, hash);
+       if (!e)
+               return -EPERM;
+
+       list_for_each_entry(a, &e->access_list, list) {
+               if ((int)a->access <= highest)
+                       continue;
+
+               switch (a->type) {
+               case KDBUS_POLICY_ACCESS_USER:
+                       if (uid_eq(cred->euid, a->uid))
+                               highest = a->access;
+                       break;
+               case KDBUS_POLICY_ACCESS_GROUP:
+                       if (gid_eq(cred->egid, a->gid)) {
+                               highest = a->access;
+                               break;
+                       }
+
+                       for (i = 0; i < cred->group_info->ngroups; i++) {
+                               kgid_t gid = GROUP_AT(cred->group_info, i);
+
+                               if (gid_eq(gid, a->gid)) {
+                                       highest = a->access;
+                                       break;
+                               }
+                       }
+
+                       break;
+               case KDBUS_POLICY_ACCESS_WORLD:
+                       highest = a->access;
+                       break;
+               }
+
+               /* OWN is the highest possible policy */
+               if (highest >= KDBUS_POLICY_OWN)
+                       break;
+       }
+
+       return highest;
+}
+
+/**
+ * kdbus_policy_query() - Query the policy database
+ * @db:                Policy database
+ * @cred:      Credentials to test against
+ * @name:      Name to query
+ * @hash:      Hash value of @name
+ *
+ * Query the policy database @db for the access rights of @cred to the name
+ * @name. The access rights of @cred are returned, or -EPERM if no access is
+ * granted.
+ *
+ * This call effectively searches for the highest access-right granted to
+ * @cred. The caller should really cache those as policy lookups are rather
+ * expensive.
+ *
+ * Return: The highest KDBUS_POLICY_* access type found, or -EPERM if none.
+ */
+int kdbus_policy_query(struct kdbus_policy_db *db, const struct cred *cred,
+                      const char *name, unsigned int hash)
+{
+       int ret;
+
+       down_read(&db->entries_rwlock);
+       ret = kdbus_policy_query_unlocked(db, cred, name, hash);
+       up_read(&db->entries_rwlock);
+
+       return ret;
+}
+
+static void __kdbus_policy_remove_owner(struct kdbus_policy_db *db,
+                                       const void *owner)
+{
+       struct kdbus_policy_db_entry *e;
+       struct hlist_node *tmp;
+       int i;
+
+       hash_for_each_safe(db->entries_hash, i, tmp, e, hentry)
+               if (e->owner == owner) {
+                       hash_del(&e->hentry);
+                       kdbus_policy_entry_free(e);
+               }
+}
+
+/**
+ * kdbus_policy_remove_owner() - remove all entries related to a connection
+ * @db:                The policy database
+ * @owner:     The connection which items to remove
+ */
+void kdbus_policy_remove_owner(struct kdbus_policy_db *db,
+                              const void *owner)
+{
+       down_write(&db->entries_rwlock);
+       __kdbus_policy_remove_owner(db, owner);
+       up_write(&db->entries_rwlock);
+}
+
+/*
+ * Convert user provided policy access to internal kdbus policy
+ * access
+ */
+static struct kdbus_policy_db_entry_access *
+kdbus_policy_make_access(const struct kdbus_policy_access *uaccess)
+{
+       int ret;
+       struct kdbus_policy_db_entry_access *a;
+
+       a = kzalloc(sizeof(*a), GFP_KERNEL);
+       if (!a)
+               return ERR_PTR(-ENOMEM);
+
+       ret = -EINVAL;
+       switch (uaccess->access) {
+       case KDBUS_POLICY_SEE:
+       case KDBUS_POLICY_TALK:
+       case KDBUS_POLICY_OWN:
+               a->access = uaccess->access;
+               break;
+       default:
+               goto err;
+       }
+
+       switch (uaccess->type) {
+       case KDBUS_POLICY_ACCESS_USER:
+               a->uid = make_kuid(current_user_ns(), uaccess->id);
+               if (!uid_valid(a->uid))
+                       goto err;
+
+               break;
+       case KDBUS_POLICY_ACCESS_GROUP:
+               a->gid = make_kgid(current_user_ns(), uaccess->id);
+               if (!gid_valid(a->gid))
+                       goto err;
+
+               break;
+       case KDBUS_POLICY_ACCESS_WORLD:
+               break;
+       default:
+               goto err;
+       }
+
+       a->type = uaccess->type;
+
+       return a;
+
+err:
+       kfree(a);
+       return ERR_PTR(ret);
+}
+
+/**
+ * kdbus_policy_set() - set a connection's policy rules
+ * @db:                                The policy database
+ * @items:                     A list of kdbus_item elements that contain both
+ *                             names and access rules to set.
+ * @items_size:                        The total size of the items.
+ * @max_policies:              The maximum number of policy entries to allow.
+ *                             Pass 0 for no limit.
+ * @allow_wildcards:           Boolean value whether wildcard entries (such
+ *                             ending on '.*') should be allowed.
+ * @owner:                     The owner of the new policy items.
+ *
+ * This function sets a new set of policies for a given owner. The names and
+ * access rules are gathered by walking the list of items passed in as
+ * argument. An item of type KDBUS_ITEM_NAME is expected before any number of
+ * KDBUS_ITEM_POLICY_ACCESS items. If there are more repetitions of this
+ * pattern than denoted in @max_policies, -EINVAL is returned.
+ *
+ * In order to allow atomic replacement of rules, the function first removes
+ * all entries that have been created for the given owner previously.
+ *
+ * Callers to this function must make sure that the owner is a custom
+ * endpoint, or if the endpoint is a default endpoint, then it must be
+ * either a policy holder or an activator.
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+int kdbus_policy_set(struct kdbus_policy_db *db,
+                    const struct kdbus_item *items,
+                    size_t items_size,
+                    size_t max_policies,
+                    bool allow_wildcards,
+                    const void *owner)
+{
+       struct kdbus_policy_db_entry_access *a;
+       struct kdbus_policy_db_entry *e, *p;
+       const struct kdbus_item *item;
+       struct hlist_node *tmp;
+       HLIST_HEAD(entries);
+       HLIST_HEAD(restore);
+       size_t count = 0;
+       int i, ret = 0;
+       u32 hash;
+
+       /* Walk the list of items and look for new policies */
+       e = NULL;
+       KDBUS_ITEMS_FOREACH(item, items, items_size) {
+               switch (item->type) {
+               case KDBUS_ITEM_NAME: {
+                       size_t len;
+
+                       if (max_policies && ++count > max_policies) {
+                               ret = -E2BIG;
+                               goto exit;
+                       }
+
+                       if (!kdbus_name_is_valid(item->str, true)) {
+                               ret = -EINVAL;
+                               goto exit;
+                       }
+
+                       e = kzalloc(sizeof(*e), GFP_KERNEL);
+                       if (!e) {
+                               ret = -ENOMEM;
+                               goto exit;
+                       }
+
+                       INIT_LIST_HEAD(&e->access_list);
+                       e->owner = owner;
+                       hlist_add_head(&e->hentry, &entries);
+
+                       e->name = kstrdup(item->str, GFP_KERNEL);
+                       if (!e->name) {
+                               ret = -ENOMEM;
+                               goto exit;
+                       }
+
+                       /*
+                        * If a supplied name ends with an '.*', cut off that
+                        * part, only store anything before it, and mark the
+                        * entry as wildcard.
+                        */
+                       len = strlen(e->name);
+                       if (len > 2 &&
+                           e->name[len - 3] == '.' &&
+                           e->name[len - 2] == '*') {
+                               if (!allow_wildcards) {
+                                       ret = -EINVAL;
+                                       goto exit;
+                               }
+
+                               e->name[len - 3] = '\0';
+                               e->wildcard = true;
+                       }
+
+                       break;
+               }
+
+               case KDBUS_ITEM_POLICY_ACCESS:
+                       if (!e) {
+                               ret = -EINVAL;
+                               goto exit;
+                       }
+
+                       a = kdbus_policy_make_access(&item->policy_access);
+                       if (IS_ERR(a)) {
+                               ret = PTR_ERR(a);
+                               goto exit;
+                       }
+
+                       list_add_tail(&a->list, &e->access_list);
+                       break;
+               }
+       }
+
+       down_write(&db->entries_rwlock);
+
+       /* remember previous entries to restore in case of failure */
+       hash_for_each_safe(db->entries_hash, i, tmp, e, hentry)
+               if (e->owner == owner) {
+                       hash_del(&e->hentry);
+                       hlist_add_head(&e->hentry, &restore);
+               }
+
+       hlist_for_each_entry_safe(e, tmp, &entries, hentry) {
+               /* prevent duplicates */
+               hash = kdbus_strhash(e->name);
+               hash_for_each_possible(db->entries_hash, p, hentry, hash)
+                       if (strcmp(e->name, p->name) == 0 &&
+                           e->wildcard == p->wildcard) {
+                               ret = -EEXIST;
+                               goto restore;
+                       }
+
+               hlist_del(&e->hentry);
+               hash_add(db->entries_hash, &e->hentry, hash);
+       }
+
+restore:
+       /* if we failed, flush all entries we added so far */
+       if (ret < 0)
+               __kdbus_policy_remove_owner(db, owner);
+
+       /* if we failed, restore entries, otherwise release them */
+       hlist_for_each_entry_safe(e, tmp, &restore, hentry) {
+               hlist_del(&e->hentry);
+               if (ret < 0) {
+                       hash = kdbus_strhash(e->name);
+                       hash_add(db->entries_hash, &e->hentry, hash);
+               } else {
+                       kdbus_policy_entry_free(e);
+               }
+       }
+
+       up_write(&db->entries_rwlock);
+
+exit:
+       hlist_for_each_entry_safe(e, tmp, &entries, hentry) {
+               hlist_del(&e->hentry);
+               kdbus_policy_entry_free(e);
+       }
+
+       return ret;
+}
diff --git a/ipc/kdbus/policy.h b/ipc/kdbus/policy.h
new file mode 100644 (file)
index 0000000..15dd7bc
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * 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
+ *
+ * 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_POLICY_H
+#define __KDBUS_POLICY_H
+
+#include <linux/hashtable.h>
+#include <linux/rwsem.h>
+
+struct kdbus_conn;
+struct kdbus_item;
+
+/**
+ * struct kdbus_policy_db - policy database
+ * @entries_hash:      Hashtable of entries
+ * @entries_rwlock:    Mutex to protect the database's access entries
+ */
+struct kdbus_policy_db {
+       DECLARE_HASHTABLE(entries_hash, 6);
+       struct rw_semaphore entries_rwlock;
+};
+
+void kdbus_policy_db_init(struct kdbus_policy_db *db);
+void kdbus_policy_db_clear(struct kdbus_policy_db *db);
+
+int kdbus_policy_query_unlocked(struct kdbus_policy_db *db,
+                               const struct cred *cred, const char *name,
+                               unsigned int hash);
+int kdbus_policy_query(struct kdbus_policy_db *db, const struct cred *cred,
+                      const char *name, unsigned int hash);
+
+void kdbus_policy_remove_owner(struct kdbus_policy_db *db,
+                              const void *owner);
+int kdbus_policy_set(struct kdbus_policy_db *db,
+                    const struct kdbus_item *items,
+                    size_t items_size,
+                    size_t max_policies,
+                    bool allow_wildcards,
+                    const void *owner);
+
+#endif
diff --git a/ipc/kdbus/pool.c b/ipc/kdbus/pool.c
new file mode 100644 (file)
index 0000000..63ccd55
--- /dev/null
@@ -0,0 +1,728 @@
+/*
+ * 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/aio.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/highmem.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/pagemap.h>
+#include <linux/rbtree.h>
+#include <linux/sched.h>
+#include <linux/shmem_fs.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/uio.h>
+
+#include "pool.h"
+#include "util.h"
+
+/**
+ * struct kdbus_pool - the receiver's buffer
+ * @f:                 The backing shmem file
+ * @size:              The size of the file
+ * @accounted_size:    Currently accounted memory in bytes
+ * @lock:              Pool data lock
+ * @slices:            All slices sorted by address
+ * @slices_busy:       Tree of allocated slices
+ * @slices_free:       Tree of free slices
+ *
+ * The receiver's buffer, managed as a pool of allocated and free
+ * slices containing the queued messages.
+ *
+ * Messages sent with KDBUS_CMD_SEND are copied directly by the
+ * sending process into the receiver's pool.
+ *
+ * Messages received with KDBUS_CMD_RECV just return the offset
+ * to the data placed in the pool.
+ *
+ * The internally allocated memory needs to be returned by the receiver
+ * with KDBUS_CMD_FREE.
+ */
+struct kdbus_pool {
+       struct file *f;
+       size_t size;
+       size_t accounted_size;
+       struct mutex lock;
+
+       struct list_head slices;
+       struct rb_root slices_busy;
+       struct rb_root slices_free;
+};
+
+/**
+ * struct kdbus_pool_slice - allocated element in kdbus_pool
+ * @pool:              Pool this slice belongs to
+ * @off:               Offset of slice in the shmem file
+ * @size:              Size of slice
+ * @entry:             Entry in "all slices" list
+ * @rb_node:           Entry in free or busy list
+ * @free:              Unused slice
+ * @accounted:         Accounted as queue slice
+ * @ref_kernel:                Kernel holds a reference
+ * @ref_user:          Userspace holds a reference
+ *
+ * The pool has one or more slices, always spanning the entire size of the
+ * pool.
+ *
+ * Every slice is an element in a list sorted by the buffer address, to
+ * provide access to the next neighbor slice.
+ *
+ * Every slice is member in either the busy or the free tree. The free
+ * tree is organized by slice size, the busy tree organized by buffer
+ * offset.
+ */
+struct kdbus_pool_slice {
+       struct kdbus_pool *pool;
+       size_t off;
+       size_t size;
+
+       struct list_head entry;
+       struct rb_node rb_node;
+
+       bool free:1;
+       bool accounted:1;
+       bool ref_kernel:1;
+       bool ref_user:1;
+};
+
+static struct kdbus_pool_slice *kdbus_pool_slice_new(struct kdbus_pool *pool,
+                                                    size_t off, size_t size)
+{
+       struct kdbus_pool_slice *slice;
+
+       slice = kzalloc(sizeof(*slice), GFP_KERNEL);
+       if (!slice)
+               return NULL;
+
+       slice->pool = pool;
+       slice->off = off;
+       slice->size = size;
+       slice->free = true;
+       return slice;
+}
+
+/* insert a slice into the free tree */
+static void kdbus_pool_add_free_slice(struct kdbus_pool *pool,
+                                     struct kdbus_pool_slice *slice)
+{
+       struct rb_node **n;
+       struct rb_node *pn = NULL;
+
+       n = &pool->slices_free.rb_node;
+       while (*n) {
+               struct kdbus_pool_slice *pslice;
+
+               pn = *n;
+               pslice = rb_entry(pn, struct kdbus_pool_slice, rb_node);
+               if (slice->size < pslice->size)
+                       n = &pn->rb_left;
+               else
+                       n = &pn->rb_right;
+       }
+
+       rb_link_node(&slice->rb_node, pn, n);
+       rb_insert_color(&slice->rb_node, &pool->slices_free);
+}
+
+/* insert a slice into the busy tree */
+static void kdbus_pool_add_busy_slice(struct kdbus_pool *pool,
+                                     struct kdbus_pool_slice *slice)
+{
+       struct rb_node **n;
+       struct rb_node *pn = NULL;
+
+       n = &pool->slices_busy.rb_node;
+       while (*n) {
+               struct kdbus_pool_slice *pslice;
+
+               pn = *n;
+               pslice = rb_entry(pn, struct kdbus_pool_slice, rb_node);
+               if (slice->off < pslice->off)
+                       n = &pn->rb_left;
+               else if (slice->off > pslice->off)
+                       n = &pn->rb_right;
+               else
+                       BUG();
+       }
+
+       rb_link_node(&slice->rb_node, pn, n);
+       rb_insert_color(&slice->rb_node, &pool->slices_busy);
+}
+
+static struct kdbus_pool_slice *kdbus_pool_find_slice(struct kdbus_pool *pool,
+                                                     size_t off)
+{
+       struct rb_node *n;
+
+       n = pool->slices_busy.rb_node;
+       while (n) {
+               struct kdbus_pool_slice *s;
+
+               s = rb_entry(n, struct kdbus_pool_slice, rb_node);
+               if (off < s->off)
+                       n = n->rb_left;
+               else if (off > s->off)
+                       n = n->rb_right;
+               else
+                       return s;
+       }
+
+       return NULL;
+}
+
+/**
+ * kdbus_pool_slice_alloc() - allocate memory from a pool
+ * @pool:      The receiver's pool
+ * @size:      The number of bytes to allocate
+ * @accounted: Whether this slice should be accounted for
+ *
+ * The returned slice is used for kdbus_pool_slice_release() to
+ * free the allocated memory. If either @kvec or @iovec is non-NULL, the data
+ * will be copied from kernel or userspace memory into the new slice at
+ * offset 0.
+ *
+ * Return: the allocated slice on success, ERR_PTR on failure.
+ */
+struct kdbus_pool_slice *kdbus_pool_slice_alloc(struct kdbus_pool *pool,
+                                               size_t size, bool accounted)
+{
+       size_t slice_size = KDBUS_ALIGN8(size);
+       struct rb_node *n, *found = NULL;
+       struct kdbus_pool_slice *s;
+       int ret = 0;
+
+       if (WARN_ON(!size))
+               return ERR_PTR(-EINVAL);
+
+       /* search a free slice with the closest matching size */
+       mutex_lock(&pool->lock);
+       n = pool->slices_free.rb_node;
+       while (n) {
+               s = rb_entry(n, struct kdbus_pool_slice, rb_node);
+               if (slice_size < s->size) {
+                       found = n;
+                       n = n->rb_left;
+               } else if (slice_size > s->size) {
+                       n = n->rb_right;
+               } else {
+                       found = n;
+                       break;
+               }
+       }
+
+       /* no slice with the minimum size found in the pool */
+       if (!found) {
+               ret = -EXFULL;
+               goto exit_unlock;
+       }
+
+       /* no exact match, use the closest one */
+       if (!n) {
+               struct kdbus_pool_slice *s_new;
+
+               s = rb_entry(found, struct kdbus_pool_slice, rb_node);
+
+               /* split-off the remainder of the size to its own slice */
+               s_new = kdbus_pool_slice_new(pool, s->off + slice_size,
+                                            s->size - slice_size);
+               if (!s_new) {
+                       ret = -ENOMEM;
+                       goto exit_unlock;
+               }
+
+               list_add(&s_new->entry, &s->entry);
+               kdbus_pool_add_free_slice(pool, s_new);
+
+               /* adjust our size now that we split-off another slice */
+               s->size = slice_size;
+       }
+
+       /* move slice from free to the busy tree */
+       rb_erase(found, &pool->slices_free);
+       kdbus_pool_add_busy_slice(pool, s);
+
+       WARN_ON(s->ref_kernel || s->ref_user);
+
+       s->ref_kernel = true;
+       s->free = false;
+       s->accounted = accounted;
+       if (accounted)
+               pool->accounted_size += s->size;
+       mutex_unlock(&pool->lock);
+
+       return s;
+
+exit_unlock:
+       mutex_unlock(&pool->lock);
+       return ERR_PTR(ret);
+}
+
+static void __kdbus_pool_slice_release(struct kdbus_pool_slice *slice)
+{
+       struct kdbus_pool *pool = slice->pool;
+
+       /* don't free the slice if either has a reference */
+       if (slice->ref_kernel || slice->ref_user)
+               return;
+
+       if (WARN_ON(slice->free))
+               return;
+
+       rb_erase(&slice->rb_node, &pool->slices_busy);
+
+       /* merge with the next free slice */
+       if (!list_is_last(&slice->entry, &pool->slices)) {
+               struct kdbus_pool_slice *s;
+
+               s = list_entry(slice->entry.next,
+                              struct kdbus_pool_slice, entry);
+               if (s->free) {
+                       rb_erase(&s->rb_node, &pool->slices_free);
+                       list_del(&s->entry);
+                       slice->size += s->size;
+                       kfree(s);
+               }
+       }
+
+       /* merge with previous free slice */
+       if (pool->slices.next != &slice->entry) {
+               struct kdbus_pool_slice *s;
+
+               s = list_entry(slice->entry.prev,
+                              struct kdbus_pool_slice, entry);
+               if (s->free) {
+                       rb_erase(&s->rb_node, &pool->slices_free);
+                       list_del(&slice->entry);
+                       s->size += slice->size;
+                       kfree(slice);
+                       slice = s;
+               }
+       }
+
+       slice->free = true;
+       kdbus_pool_add_free_slice(pool, slice);
+}
+
+/**
+ * kdbus_pool_slice_release() - drop kernel-reference on allocated slice
+ * @slice:             Slice allocated from the pool
+ *
+ * This releases the kernel-reference on the given slice. If the
+ * kernel-reference and the user-reference on a slice are dropped, the slice is
+ * returned to the pool.
+ *
+ * So far, we do not implement full ref-counting on slices. Each, kernel and
+ * user-space can have exactly one reference to a slice. If both are dropped at
+ * the same time, the slice is released.
+ */
+void kdbus_pool_slice_release(struct kdbus_pool_slice *slice)
+{
+       struct kdbus_pool *pool;
+
+       if (!slice)
+               return;
+
+       /* @slice may be freed, so keep local ptr to @pool */
+       pool = slice->pool;
+
+       mutex_lock(&pool->lock);
+       /* kernel must own a ref to @slice to drop it */
+       WARN_ON(!slice->ref_kernel);
+       slice->ref_kernel = false;
+       /* no longer kernel-owned, de-account slice */
+       if (slice->accounted && !WARN_ON(pool->accounted_size < slice->size))
+               pool->accounted_size -= slice->size;
+       __kdbus_pool_slice_release(slice);
+       mutex_unlock(&pool->lock);
+}
+
+/**
+ * kdbus_pool_release_offset() - release a public offset
+ * @pool:              pool to operate on
+ * @off:               offset to release
+ *
+ * This should be called whenever user-space frees a slice given to them. It
+ * verifies the slice is available and public, and then drops it. It ensures
+ * correct locking and barriers against queues.
+ *
+ * Return: 0 on success, ENXIO if the offset is invalid or not public.
+ */
+int kdbus_pool_release_offset(struct kdbus_pool *pool, size_t off)
+{
+       struct kdbus_pool_slice *slice;
+       int ret = 0;
+
+       /* 'pool->size' is used as dummy offset for empty slices */
+       if (off == pool->size)
+               return 0;
+
+       mutex_lock(&pool->lock);
+       slice = kdbus_pool_find_slice(pool, off);
+       if (slice && slice->ref_user) {
+               slice->ref_user = false;
+               __kdbus_pool_slice_release(slice);
+       } else {
+               ret = -ENXIO;
+       }
+       mutex_unlock(&pool->lock);
+
+       return ret;
+}
+
+/**
+ * kdbus_pool_publish_empty() - publish empty slice to user-space
+ * @pool:              pool to operate on
+ * @off:               output storage for offset, or NULL
+ * @size:              output storage for size, or NULL
+ *
+ * This is the same as kdbus_pool_slice_publish(), but uses a dummy slice with
+ * size 0. The returned offset points to the end of the pool and is never
+ * returned on real slices.
+ */
+void kdbus_pool_publish_empty(struct kdbus_pool *pool, u64 *off, u64 *size)
+{
+       if (off)
+               *off = pool->size;
+       if (size)
+               *size = 0;
+}
+
+/**
+ * kdbus_pool_slice_publish() - publish slice to user-space
+ * @slice:             The slice
+ * @out_offset:                Output storage for offset, or NULL
+ * @out_size:          Output storage for size, or NULL
+ *
+ * This prepares a slice to be published to user-space.
+ *
+ * This call combines the following operations:
+ *   * the memory region is flushed so the user's memory view is consistent
+ *   * the slice is marked as referenced by user-space, so user-space has to
+ *     call KDBUS_CMD_FREE to release it
+ *   * the offset and size of the slice are written to the given output
+ *     arguments, if non-NULL
+ */
+void kdbus_pool_slice_publish(struct kdbus_pool_slice *slice,
+                             u64 *out_offset, u64 *out_size)
+{
+       mutex_lock(&slice->pool->lock);
+       /* kernel must own a ref to @slice to gain a user-space ref */
+       WARN_ON(!slice->ref_kernel);
+       slice->ref_user = true;
+       mutex_unlock(&slice->pool->lock);
+
+       if (out_offset)
+               *out_offset = slice->off;
+       if (out_size)
+               *out_size = slice->size;
+}
+
+/**
+ * kdbus_pool_slice_offset() - Get a slice's offset inside the pool
+ * @slice:     Slice to return the offset of
+ *
+ * Return: The internal offset @slice inside the pool.
+ */
+off_t kdbus_pool_slice_offset(const struct kdbus_pool_slice *slice)
+{
+       return slice->off;
+}
+
+/**
+ * kdbus_pool_slice_size() - get size of a pool slice
+ * @slice:     slice to query
+ *
+ * Return: size of the given slice
+ */
+size_t kdbus_pool_slice_size(const struct kdbus_pool_slice *slice)
+{
+       return slice->size;
+}
+
+/**
+ * kdbus_pool_new() - create a new pool
+ * @name:              Name of the (deleted) file which shows up in
+ *                     /proc, used for debugging
+ * @size:              Maximum size of the pool
+ *
+ * Return: a new kdbus_pool on success, ERR_PTR on failure.
+ */
+struct kdbus_pool *kdbus_pool_new(const char *name, size_t size)
+{
+       struct kdbus_pool_slice *s;
+       struct kdbus_pool *p;
+       struct file *f;
+       char *n = NULL;
+       int ret;
+
+       p = kzalloc(sizeof(*p), GFP_KERNEL);
+       if (!p)
+               return ERR_PTR(-ENOMEM);
+
+       if (name) {
+               n = kasprintf(GFP_KERNEL, KBUILD_MODNAME "-conn:%s", name);
+               if (!n) {
+                       ret = -ENOMEM;
+                       goto exit_free;
+               }
+       }
+
+       f = shmem_file_setup(n ?: KBUILD_MODNAME "-conn", size, 0);
+       kfree(n);
+
+       if (IS_ERR(f)) {
+               ret = PTR_ERR(f);
+               goto exit_free;
+       }
+
+       ret = get_write_access(file_inode(f));
+       if (ret < 0)
+               goto exit_put_shmem;
+
+       /* allocate first slice spanning the entire pool */
+       s = kdbus_pool_slice_new(p, 0, size);
+       if (!s) {
+               ret = -ENOMEM;
+               goto exit_put_write;
+       }
+
+       p->f = f;
+       p->size = size;
+       p->slices_free = RB_ROOT;
+       p->slices_busy = RB_ROOT;
+       mutex_init(&p->lock);
+
+       INIT_LIST_HEAD(&p->slices);
+       list_add(&s->entry, &p->slices);
+
+       kdbus_pool_add_free_slice(p, s);
+       return p;
+
+exit_put_write:
+       put_write_access(file_inode(f));
+exit_put_shmem:
+       fput(f);
+exit_free:
+       kfree(p);
+       return ERR_PTR(ret);
+}
+
+/**
+ * kdbus_pool_free() - destroy pool
+ * @pool:              The receiver's pool
+ */
+void kdbus_pool_free(struct kdbus_pool *pool)
+{
+       struct kdbus_pool_slice *s, *tmp;
+
+       if (!pool)
+               return;
+
+       list_for_each_entry_safe(s, tmp, &pool->slices, entry) {
+               list_del(&s->entry);
+               kfree(s);
+       }
+
+       put_write_access(file_inode(pool->f));
+       fput(pool->f);
+       kfree(pool);
+}
+
+/**
+ * kdbus_pool_accounted() - retrieve accounting information
+ * @pool:              pool to query
+ * @size:              output for overall pool size
+ * @acc:               output for currently accounted size
+ *
+ * This returns accounting information of the pool. Note that the data might
+ * change after the function returns, as the pool lock is dropped. You need to
+ * protect the data via other means, if you need reliable accounting.
+ */
+void kdbus_pool_accounted(struct kdbus_pool *pool, size_t *size, size_t *acc)
+{
+       mutex_lock(&pool->lock);
+       if (size)
+               *size = pool->size;
+       if (acc)
+               *acc = pool->accounted_size;
+       mutex_unlock(&pool->lock);
+}
+
+/**
+ * kdbus_pool_slice_copy_iovec() - copy user memory to a slice
+ * @slice:             The slice to write to
+ * @off:               Offset in the slice to write to
+ * @iov:               iovec array, pointing to data to copy
+ * @iov_len:           Number of elements in @iov
+ * @total_len:         Total number of bytes described in members of @iov
+ *
+ * User memory referenced by @iov will be copied into @slice at offset @off.
+ *
+ * Return: the numbers of bytes copied, negative errno on failure.
+ */
+ssize_t
+kdbus_pool_slice_copy_iovec(const struct kdbus_pool_slice *slice, loff_t off,
+                           struct iovec *iov, size_t iov_len, size_t total_len)
+{
+       struct iov_iter iter;
+       ssize_t len;
+
+       if (WARN_ON(off + total_len > slice->size))
+               return -EFAULT;
+
+       off += slice->off;
+       iov_iter_init(&iter, WRITE, iov, iov_len, total_len);
+       len = vfs_iter_write(slice->pool->f, &iter, &off);
+
+       return (len >= 0 && len != total_len) ? -EFAULT : len;
+}
+
+/**
+ * kdbus_pool_slice_copy_kvec() - copy kernel memory to a slice
+ * @slice:             The slice to write to
+ * @off:               Offset in the slice to write to
+ * @kvec:              kvec array, pointing to data to copy
+ * @kvec_len:          Number of elements in @kvec
+ * @total_len:         Total number of bytes described in members of @kvec
+ *
+ * Kernel memory referenced by @kvec will be copied into @slice at offset @off.
+ *
+ * Return: the numbers of bytes copied, negative errno on failure.
+ */
+ssize_t kdbus_pool_slice_copy_kvec(const struct kdbus_pool_slice *slice,
+                                  loff_t off, struct kvec *kvec,
+                                  size_t kvec_len, size_t total_len)
+{
+       struct iov_iter iter;
+       mm_segment_t old_fs;
+       ssize_t len;
+
+       if (WARN_ON(off + total_len > slice->size))
+               return -EFAULT;
+
+       off += slice->off;
+       iov_iter_kvec(&iter, WRITE | ITER_KVEC, kvec, kvec_len, total_len);
+
+       old_fs = get_fs();
+       set_fs(get_ds());
+       len = vfs_iter_write(slice->pool->f, &iter, &off);
+       set_fs(old_fs);
+
+       return (len >= 0 && len != total_len) ? -EFAULT : len;
+}
+
+/**
+ * kdbus_pool_slice_copy() - copy data from one slice into another
+ * @slice_dst:         destination slice
+ * @slice_src:         source slice
+ *
+ * Return: 0 on success, negative error number on failure.
+ */
+int kdbus_pool_slice_copy(const struct kdbus_pool_slice *slice_dst,
+                         const struct kdbus_pool_slice *slice_src)
+{
+       struct file *f_src = slice_src->pool->f;
+       struct file *f_dst = slice_dst->pool->f;
+       struct inode *i_dst = file_inode(f_dst);
+       struct address_space *mapping_dst = f_dst->f_mapping;
+       const struct address_space_operations *aops = mapping_dst->a_ops;
+       unsigned long len = slice_src->size;
+       loff_t off_src = slice_src->off;
+       loff_t off_dst = slice_dst->off;
+       mm_segment_t old_fs;
+       int ret = 0;
+
+       if (WARN_ON(slice_src->size != slice_dst->size) ||
+           WARN_ON(slice_src->free || slice_dst->free))
+               return -EINVAL;
+
+       mutex_lock(&i_dst->i_mutex);
+       old_fs = get_fs();
+       set_fs(get_ds());
+       while (len > 0) {
+               unsigned long page_off;
+               unsigned long copy_len;
+               char __user *kaddr;
+               struct page *page;
+               ssize_t n_read;
+               void *fsdata;
+               long status;
+
+               page_off = off_dst & (PAGE_CACHE_SIZE - 1);
+               copy_len = min_t(unsigned long,
+                                PAGE_CACHE_SIZE - page_off, len);
+
+               status = aops->write_begin(f_dst, mapping_dst, off_dst,
+                                          copy_len, 0, &page, &fsdata);
+               if (unlikely(status < 0)) {
+                       ret = status;
+                       break;
+               }
+
+               kaddr = (char __force __user *)kmap(page) + page_off;
+               n_read = __vfs_read(f_src, kaddr, copy_len, &off_src);
+               kunmap(page);
+               mark_page_accessed(page);
+               flush_dcache_page(page);
+
+               if (unlikely(n_read != copy_len)) {
+                       ret = -EFAULT;
+                       break;
+               }
+
+               status = aops->write_end(f_dst, mapping_dst, off_dst,
+                                        copy_len, copy_len, page, fsdata);
+               if (unlikely(status != copy_len)) {
+                       ret = -EFAULT;
+                       break;
+               }
+
+               off_dst += copy_len;
+               len -= copy_len;
+       }
+       set_fs(old_fs);
+       mutex_unlock(&i_dst->i_mutex);
+
+       return ret;
+}
+
+/**
+ * kdbus_pool_mmap() -  map the pool into the process
+ * @pool:              The receiver's pool
+ * @vma:               passed by mmap() syscall
+ *
+ * Return: the result of the mmap() call, negative errno on failure.
+ */
+int kdbus_pool_mmap(const struct kdbus_pool *pool, struct vm_area_struct *vma)
+{
+       /* deny write access to the pool */
+       if (vma->vm_flags & VM_WRITE)
+               return -EPERM;
+       vma->vm_flags &= ~VM_MAYWRITE;
+
+       /* do not allow to map more than the size of the file */
+       if ((vma->vm_end - vma->vm_start) > pool->size)
+               return -EFAULT;
+
+       /* replace the connection file with our shmem file */
+       if (vma->vm_file)
+               fput(vma->vm_file);
+       vma->vm_file = get_file(pool->f);
+
+       return pool->f->f_op->mmap(pool->f, vma);
+}
diff --git a/ipc/kdbus/pool.h b/ipc/kdbus/pool.h
new file mode 100644 (file)
index 0000000..a903821
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+#ifndef __KDBUS_POOL_H
+#define __KDBUS_POOL_H
+
+#include <linux/uio.h>
+
+struct kdbus_pool;
+struct kdbus_pool_slice;
+
+struct kdbus_pool *kdbus_pool_new(const char *name, size_t size);
+void kdbus_pool_free(struct kdbus_pool *pool);
+void kdbus_pool_accounted(struct kdbus_pool *pool, size_t *size, size_t *acc);
+int kdbus_pool_mmap(const struct kdbus_pool *pool, struct vm_area_struct *vma);
+int kdbus_pool_release_offset(struct kdbus_pool *pool, size_t off);
+void kdbus_pool_publish_empty(struct kdbus_pool *pool, u64 *off, u64 *size);
+
+struct kdbus_pool_slice *kdbus_pool_slice_alloc(struct kdbus_pool *pool,
+                                               size_t size, bool accounted);
+void kdbus_pool_slice_release(struct kdbus_pool_slice *slice);
+void kdbus_pool_slice_publish(struct kdbus_pool_slice *slice,
+                             u64 *out_offset, u64 *out_size);
+off_t kdbus_pool_slice_offset(const struct kdbus_pool_slice *slice);
+size_t kdbus_pool_slice_size(const struct kdbus_pool_slice *slice);
+int kdbus_pool_slice_copy(const struct kdbus_pool_slice *slice_dst,
+                         const struct kdbus_pool_slice *slice_src);
+ssize_t kdbus_pool_slice_copy_kvec(const struct kdbus_pool_slice *slice,
+                                  loff_t off, struct kvec *kvec,
+                                  size_t kvec_count, size_t total_len);
+ssize_t kdbus_pool_slice_copy_iovec(const struct kdbus_pool_slice *slice,
+                                   loff_t off, struct iovec *iov,
+                                   size_t iov_count, size_t total_len);
+
+#endif
diff --git a/ipc/kdbus/queue.c b/ipc/kdbus/queue.c
new file mode 100644 (file)
index 0000000..f9c44d7
--- /dev/null
@@ -0,0 +1,363 @@
+/*
+ * 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/audit.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/hashtable.h>
+#include <linux/idr.h>
+#include <linux/init.h>
+#include <linux/math64.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+#include <linux/syscalls.h>
+#include <linux/uio.h>
+
+#include "util.h"
+#include "domain.h"
+#include "connection.h"
+#include "item.h"
+#include "message.h"
+#include "metadata.h"
+#include "queue.h"
+#include "reply.h"
+
+/**
+ * kdbus_queue_init() - initialize data structure related to a queue
+ * @queue:     The queue to initialize
+ */
+void kdbus_queue_init(struct kdbus_queue *queue)
+{
+       INIT_LIST_HEAD(&queue->msg_list);
+       queue->msg_prio_queue = RB_ROOT;
+}
+
+/**
+ * kdbus_queue_peek() - Retrieves an entry from a queue
+ * @queue:             The queue
+ * @priority:          The minimum priority of the entry to peek
+ * @use_priority:      Boolean flag whether or not to peek by priority
+ *
+ * Look for a entry in a queue, either by priority, or the oldest one (FIFO).
+ * The entry is not freed, put off the queue's lists or anything else.
+ *
+ * Return: the peeked queue entry on success, NULL if no suitable msg is found
+ */
+struct kdbus_queue_entry *kdbus_queue_peek(struct kdbus_queue *queue,
+                                          s64 priority, bool use_priority)
+{
+       struct kdbus_queue_entry *e;
+
+       if (list_empty(&queue->msg_list))
+               return NULL;
+
+       if (use_priority) {
+               /* get next entry with highest priority */
+               e = rb_entry(queue->msg_prio_highest,
+                            struct kdbus_queue_entry, prio_node);
+
+               /* no entry with the requested priority */
+               if (e->priority > priority)
+                       return NULL;
+       } else {
+               /* ignore the priority, return the next entry in the entry */
+               e = list_first_entry(&queue->msg_list,
+                                    struct kdbus_queue_entry, entry);
+       }
+
+       return e;
+}
+
+static void kdbus_queue_entry_link(struct kdbus_queue_entry *entry)
+{
+       struct kdbus_queue *queue = &entry->conn->queue;
+       struct rb_node **n, *pn = NULL;
+       bool highest = true;
+
+       lockdep_assert_held(&entry->conn->lock);
+       if (WARN_ON(!list_empty(&entry->entry)))
+               return;
+
+       /* sort into priority entry tree */
+       n = &queue->msg_prio_queue.rb_node;
+       while (*n) {
+               struct kdbus_queue_entry *e;
+
+               pn = *n;
+               e = rb_entry(pn, struct kdbus_queue_entry, prio_node);
+
+               /* existing node for this priority, add to its list */
+               if (likely(entry->priority == e->priority)) {
+                       list_add_tail(&entry->prio_entry, &e->prio_entry);
+                       goto prio_done;
+               }
+
+               if (entry->priority < e->priority) {
+                       n = &pn->rb_left;
+               } else {
+                       n = &pn->rb_right;
+                       highest = false;
+               }
+       }
+
+       /* cache highest-priority entry */
+       if (highest)
+               queue->msg_prio_highest = &entry->prio_node;
+
+       /* new node for this priority */
+       rb_link_node(&entry->prio_node, pn, n);
+       rb_insert_color(&entry->prio_node, &queue->msg_prio_queue);
+       INIT_LIST_HEAD(&entry->prio_entry);
+
+prio_done:
+       /* add to unsorted fifo list */
+       list_add_tail(&entry->entry, &queue->msg_list);
+}
+
+static void kdbus_queue_entry_unlink(struct kdbus_queue_entry *entry)
+{
+       struct kdbus_queue *queue = &entry->conn->queue;
+
+       lockdep_assert_held(&entry->conn->lock);
+       if (list_empty(&entry->entry))
+               return;
+
+       list_del_init(&entry->entry);
+
+       if (list_empty(&entry->prio_entry)) {
+               /*
+                * Single entry for this priority, update cached
+                * highest-priority entry, remove the tree node.
+                */
+               if (queue->msg_prio_highest == &entry->prio_node)
+                       queue->msg_prio_highest = rb_next(&entry->prio_node);
+
+               rb_erase(&entry->prio_node, &queue->msg_prio_queue);
+       } else {
+               struct kdbus_queue_entry *q;
+
+               /*
+                * Multiple entries for this priority entry, get next one in
+                * the list. Update cached highest-priority entry, store the
+                * new one as the tree node.
+                */
+               q = list_first_entry(&entry->prio_entry,
+                                    struct kdbus_queue_entry, prio_entry);
+               list_del(&entry->prio_entry);
+
+               if (queue->msg_prio_highest == &entry->prio_node)
+                       queue->msg_prio_highest = &q->prio_node;
+
+               rb_replace_node(&entry->prio_node, &q->prio_node,
+                               &queue->msg_prio_queue);
+       }
+}
+
+/**
+ * kdbus_queue_entry_new() - allocate a queue entry
+ * @src:       source connection, or NULL
+ * @dst:       destination connection
+ * @s:         staging object carrying the message
+ *
+ * Allocates a queue entry based on a given msg and allocate space for
+ * the message payload and the requested metadata in the connection's pool.
+ * The entry is not actually added to the queue's lists at this point.
+ *
+ * Return: the allocated entry on success, or an ERR_PTR on failures.
+ */
+struct kdbus_queue_entry *kdbus_queue_entry_new(struct kdbus_conn *src,
+                                               struct kdbus_conn *dst,
+                                               struct kdbus_staging *s)
+{
+       struct kdbus_queue_entry *entry;
+       int ret;
+
+       entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+       if (!entry)
+               return ERR_PTR(-ENOMEM);
+
+       INIT_LIST_HEAD(&entry->entry);
+       entry->priority = s->msg->priority;
+       entry->conn = kdbus_conn_ref(dst);
+       entry->gaps = kdbus_gaps_ref(s->gaps);
+
+       entry->slice = kdbus_staging_emit(s, src, dst);
+       if (IS_ERR(entry->slice)) {
+               ret = PTR_ERR(entry->slice);
+               entry->slice = NULL;
+               goto error;
+       }
+
+       entry->user = src ? kdbus_user_ref(src->user) : NULL;
+       return entry;
+
+error:
+       kdbus_queue_entry_free(entry);
+       return ERR_PTR(ret);
+}
+
+/**
+ * kdbus_queue_entry_free() - free resources of an entry
+ * @entry:     The entry to free
+ *
+ * Removes resources allocated by a queue entry, along with the entry itself.
+ * Note that the entry's slice is not freed at this point.
+ */
+void kdbus_queue_entry_free(struct kdbus_queue_entry *entry)
+{
+       if (!entry)
+               return;
+
+       lockdep_assert_held(&entry->conn->lock);
+
+       kdbus_queue_entry_unlink(entry);
+       kdbus_reply_unref(entry->reply);
+
+       if (entry->slice) {
+               kdbus_conn_quota_dec(entry->conn, entry->user,
+                                    kdbus_pool_slice_size(entry->slice),
+                                    entry->gaps ? entry->gaps->n_fds : 0);
+               kdbus_pool_slice_release(entry->slice);
+       }
+
+       kdbus_user_unref(entry->user);
+       kdbus_gaps_unref(entry->gaps);
+       kdbus_conn_unref(entry->conn);
+       kfree(entry);
+}
+
+/**
+ * kdbus_queue_entry_install() - install message components into the
+ *                              receiver's process
+ * @entry:             The queue entry to install
+ * @return_flags:      Pointer to store the return flags for userspace
+ * @install_fds:       Whether or not to install associated file descriptors
+ *
+ * Return: 0 on success.
+ */
+int kdbus_queue_entry_install(struct kdbus_queue_entry *entry,
+                             u64 *return_flags, bool install_fds)
+{
+       bool incomplete_fds = false;
+       int ret;
+
+       lockdep_assert_held(&entry->conn->lock);
+
+       ret = kdbus_gaps_install(entry->gaps, entry->slice, &incomplete_fds);
+       if (ret < 0)
+               return ret;
+
+       if (incomplete_fds)
+               *return_flags |= KDBUS_RECV_RETURN_INCOMPLETE_FDS;
+       return 0;
+}
+
+/**
+ * kdbus_queue_entry_enqueue() - enqueue an entry
+ * @entry:             entry to enqueue
+ * @reply:             reply to link to this entry (or NULL if none)
+ *
+ * This enqueues an unqueued entry into the message queue of the linked
+ * connection. It also binds a reply object to the entry so we can remember it
+ * when the message is moved.
+ *
+ * Once this call returns (and the connection lock is released), this entry can
+ * be dequeued by the target connection. Note that the entry will not be removed
+ * from the queue until it is destroyed.
+ */
+void kdbus_queue_entry_enqueue(struct kdbus_queue_entry *entry,
+                              struct kdbus_reply *reply)
+{
+       lockdep_assert_held(&entry->conn->lock);
+
+       if (WARN_ON(entry->reply) || WARN_ON(!list_empty(&entry->entry)))
+               return;
+
+       entry->reply = kdbus_reply_ref(reply);
+       kdbus_queue_entry_link(entry);
+}
+
+/**
+ * kdbus_queue_entry_move() - move queue entry
+ * @e:         queue entry to move
+ * @dst:       destination connection to queue the entry on
+ *
+ * This moves a queue entry onto a different connection. It allocates a new
+ * slice on the target connection and copies the message over. If the copy
+ * succeeded, we move the entry from @src to @dst.
+ *
+ * On failure, the entry is left untouched.
+ *
+ * The queue entry must be queued right now, and after the call succeeds it will
+ * be queued on the destination, but no longer on the source.
+ *
+ * The caller must hold the connection lock of the source *and* destination.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int kdbus_queue_entry_move(struct kdbus_queue_entry *e,
+                          struct kdbus_conn *dst)
+{
+       struct kdbus_pool_slice *slice = NULL;
+       struct kdbus_conn *src = e->conn;
+       size_t size, fds;
+       int ret;
+
+       lockdep_assert_held(&src->lock);
+       lockdep_assert_held(&dst->lock);
+
+       if (WARN_ON(list_empty(&e->entry)))
+               return -EINVAL;
+       if (src == dst)
+               return 0;
+
+       size = kdbus_pool_slice_size(e->slice);
+       fds = e->gaps ? e->gaps->n_fds : 0;
+
+       ret = kdbus_conn_quota_inc(dst, e->user, size, fds);
+       if (ret < 0)
+               return ret;
+
+       slice = kdbus_pool_slice_alloc(dst->pool, size, true);
+       if (IS_ERR(slice)) {
+               ret = PTR_ERR(slice);
+               slice = NULL;
+               goto error;
+       }
+
+       ret = kdbus_pool_slice_copy(slice, e->slice);
+       if (ret < 0)
+               goto error;
+
+       kdbus_queue_entry_unlink(e);
+       kdbus_conn_quota_dec(src, e->user, size, fds);
+       kdbus_pool_slice_release(e->slice);
+       kdbus_conn_unref(e->conn);
+
+       e->slice = slice;
+       e->conn = kdbus_conn_ref(dst);
+       kdbus_queue_entry_link(e);
+
+       return 0;
+
+error:
+       kdbus_pool_slice_release(slice);
+       kdbus_conn_quota_dec(dst, e->user, size, fds);
+       return ret;
+}
diff --git a/ipc/kdbus/queue.h b/ipc/kdbus/queue.h
new file mode 100644 (file)
index 0000000..bf686d1
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+
+#ifndef __KDBUS_QUEUE_H
+#define __KDBUS_QUEUE_H
+
+#include <linux/list.h>
+#include <linux/rbtree.h>
+
+struct kdbus_conn;
+struct kdbus_pool_slice;
+struct kdbus_reply;
+struct kdbus_staging;
+struct kdbus_user;
+
+/**
+ * struct kdbus_queue - a connection's message queue
+ * @msg_list:          List head for kdbus_queue_entry objects
+ * @msg_prio_queue:    RB tree root for messages, sorted by priority
+ * @msg_prio_highest:  Link to the RB node referencing the message with the
+ *                     highest priority in the tree.
+ */
+struct kdbus_queue {
+       struct list_head msg_list;
+       struct rb_root msg_prio_queue;
+       struct rb_node *msg_prio_highest;
+};
+
+/**
+ * struct kdbus_queue_entry - messages waiting to be read
+ * @entry:             Entry in the connection's list
+ * @prio_node:         Entry in the priority queue tree
+ * @prio_entry:                Queue tree node entry in the list of one priority
+ * @priority:          Message priority
+ * @dst_name_id:       The sequence number of the name this message is
+ *                     addressed to, 0 for messages sent to an ID
+ * @conn:              Connection this entry is queued on
+ * @gaps:              Gaps object to fill message gaps at RECV time
+ * @user:              User used for accounting
+ * @slice:             Slice in the receiver's pool for the message
+ * @reply:             The reply block if a reply to this message is expected
+ */
+struct kdbus_queue_entry {
+       struct list_head entry;
+       struct rb_node prio_node;
+       struct list_head prio_entry;
+
+       s64 priority;
+       u64 dst_name_id;
+
+       struct kdbus_conn *conn;
+       struct kdbus_gaps *gaps;
+       struct kdbus_user *user;
+       struct kdbus_pool_slice *slice;
+       struct kdbus_reply *reply;
+};
+
+void kdbus_queue_init(struct kdbus_queue *queue);
+struct kdbus_queue_entry *kdbus_queue_peek(struct kdbus_queue *queue,
+                                          s64 priority, bool use_priority);
+
+struct kdbus_queue_entry *kdbus_queue_entry_new(struct kdbus_conn *src,
+                                               struct kdbus_conn *dst,
+                                               struct kdbus_staging *s);
+void kdbus_queue_entry_free(struct kdbus_queue_entry *entry);
+int kdbus_queue_entry_install(struct kdbus_queue_entry *entry,
+                             u64 *return_flags, bool install_fds);
+void kdbus_queue_entry_enqueue(struct kdbus_queue_entry *entry,
+                              struct kdbus_reply *reply);
+int kdbus_queue_entry_move(struct kdbus_queue_entry *entry,
+                          struct kdbus_conn *dst);
+
+#endif /* __KDBUS_QUEUE_H */
diff --git a/ipc/kdbus/reply.c b/ipc/kdbus/reply.c
new file mode 100644 (file)
index 0000000..07cb133
--- /dev/null
@@ -0,0 +1,252 @@
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/uio.h>
+
+#include "bus.h"
+#include "connection.h"
+#include "endpoint.h"
+#include "message.h"
+#include "metadata.h"
+#include "names.h"
+#include "domain.h"
+#include "item.h"
+#include "notify.h"
+#include "policy.h"
+#include "reply.h"
+#include "util.h"
+
+/**
+ * kdbus_reply_new() - Allocate and set up a new kdbus_reply object
+ * @reply_src:         The connection a reply is expected from
+ * @reply_dst:         The connection this reply object belongs to
+ * @msg:               Message associated with the reply
+ * @name_entry:                Name entry used to send the message
+ * @sync:              Whether or not to make this reply synchronous
+ *
+ * Allocate and fill a new kdbus_reply object.
+ *
+ * Return: New kdbus_conn object on success, ERR_PTR on error.
+ */
+struct kdbus_reply *kdbus_reply_new(struct kdbus_conn *reply_src,
+                                   struct kdbus_conn *reply_dst,
+                                   const struct kdbus_msg *msg,
+                                   struct kdbus_name_entry *name_entry,
+                                   bool sync)
+{
+       struct kdbus_reply *r;
+       int ret;
+
+       if (atomic_inc_return(&reply_dst->request_count) >
+           KDBUS_CONN_MAX_REQUESTS_PENDING) {
+               ret = -EMLINK;
+               goto exit_dec_request_count;
+       }
+
+       r = kzalloc(sizeof(*r), GFP_KERNEL);
+       if (!r) {
+               ret = -ENOMEM;
+               goto exit_dec_request_count;
+       }
+
+       kref_init(&r->kref);
+       INIT_LIST_HEAD(&r->entry);
+       r->reply_src = kdbus_conn_ref(reply_src);
+       r->reply_dst = kdbus_conn_ref(reply_dst);
+       r->cookie = msg->cookie;
+       r->name_id = name_entry ? name_entry->name_id : 0;
+       r->deadline_ns = msg->timeout_ns;
+
+       if (sync) {
+               r->sync = true;
+               r->waiting = true;
+       }
+
+       return r;
+
+exit_dec_request_count:
+       atomic_dec(&reply_dst->request_count);
+       return ERR_PTR(ret);
+}
+
+static void __kdbus_reply_free(struct kref *kref)
+{
+       struct kdbus_reply *reply =
+               container_of(kref, struct kdbus_reply, kref);
+
+       atomic_dec(&reply->reply_dst->request_count);
+       kdbus_conn_unref(reply->reply_src);
+       kdbus_conn_unref(reply->reply_dst);
+       kfree(reply);
+}
+
+/**
+ * kdbus_reply_ref() - Increase reference on kdbus_reply
+ * @r:         The reply, may be %NULL
+ *
+ * Return: The reply object with an extra reference
+ */
+struct kdbus_reply *kdbus_reply_ref(struct kdbus_reply *r)
+{
+       if (r)
+               kref_get(&r->kref);
+       return r;
+}
+
+/**
+ * kdbus_reply_unref() - Decrease reference on kdbus_reply
+ * @r:         The reply, may be %NULL
+ *
+ * Return: NULL
+ */
+struct kdbus_reply *kdbus_reply_unref(struct kdbus_reply *r)
+{
+       if (r)
+               kref_put(&r->kref, __kdbus_reply_free);
+       return NULL;
+}
+
+/**
+ * kdbus_reply_link() - Link reply object into target connection
+ * @r:         Reply to link
+ */
+void kdbus_reply_link(struct kdbus_reply *r)
+{
+       if (WARN_ON(!list_empty(&r->entry)))
+               return;
+
+       list_add_tail(&r->entry, &r->reply_dst->reply_list);
+       kdbus_reply_ref(r);
+}
+
+/**
+ * kdbus_reply_unlink() - Unlink reply object from target connection
+ * @r:         Reply to unlink
+ */
+void kdbus_reply_unlink(struct kdbus_reply *r)
+{
+       if (!list_empty(&r->entry)) {
+               list_del_init(&r->entry);
+               kdbus_reply_unref(r);
+       }
+}
+
+/**
+ * kdbus_sync_reply_wakeup() - Wake a synchronously blocking reply
+ * @reply:     The reply object
+ * @err:       Error code to set on the remote side
+ *
+ * Wake up remote peer (method origin) with the appropriate synchronous reply
+ * code.
+ */
+void kdbus_sync_reply_wakeup(struct kdbus_reply *reply, int err)
+{
+       if (WARN_ON(!reply->sync))
+               return;
+
+       reply->waiting = false;
+       reply->err = err;
+       wake_up_interruptible(&reply->reply_dst->wait);
+}
+
+/**
+ * kdbus_reply_find() - Find the corresponding reply object
+ * @replying:  The replying connection or NULL
+ * @reply_dst: The connection the reply will be sent to
+ *             (method origin)
+ * @cookie:    The cookie of the requesting message
+ *
+ * Lookup a reply object that should be sent as a reply by
+ * @replying to @reply_dst with the given cookie.
+ *
+ * Callers must take the @reply_dst lock.
+ *
+ * Return: the corresponding reply object or NULL if not found
+ */
+struct kdbus_reply *kdbus_reply_find(struct kdbus_conn *replying,
+                                    struct kdbus_conn *reply_dst,
+                                    u64 cookie)
+{
+       struct kdbus_reply *r;
+
+       list_for_each_entry(r, &reply_dst->reply_list, entry) {
+               if (r->cookie == cookie &&
+                   (!replying || r->reply_src == replying))
+                       return r;
+       }
+
+       return NULL;
+}
+
+/**
+ * kdbus_reply_list_scan_work() - Worker callback to scan the replies of a
+ *                               connection for exceeded timeouts
+ * @work:              Work struct of the connection to scan
+ *
+ * Walk the list of replies stored with a connection and look for entries
+ * that have exceeded their timeout. If such an entry is found, a timeout
+ * notification is sent to the waiting peer, and the reply is removed from
+ * the list.
+ *
+ * The work is rescheduled to the nearest timeout found during the list
+ * iteration.
+ */
+void kdbus_reply_list_scan_work(struct work_struct *work)
+{
+       struct kdbus_conn *conn =
+               container_of(work, struct kdbus_conn, work.work);
+       struct kdbus_reply *reply, *reply_tmp;
+       u64 deadline = ~0ULL;
+       u64 now;
+
+       now = ktime_get_ns();
+
+       mutex_lock(&conn->lock);
+       if (!kdbus_conn_active(conn)) {
+               mutex_unlock(&conn->lock);
+               return;
+       }
+
+       list_for_each_entry_safe(reply, reply_tmp, &conn->reply_list, entry) {
+               /*
+                * If the reply block is waiting for synchronous I/O,
+                * the timeout is handled by wait_event_*_timeout(),
+                * so we don't have to care for it here.
+                */
+               if (reply->sync && !reply->interrupted)
+                       continue;
+
+               WARN_ON(reply->reply_dst != conn);
+
+               if (reply->deadline_ns > now) {
+                       /* remember next timeout */
+                       if (deadline > reply->deadline_ns)
+                               deadline = reply->deadline_ns;
+
+                       continue;
+               }
+
+               /*
+                * A zero deadline means the connection died, was
+                * cleaned up already and the notification was sent.
+                * Don't send notifications for reply trackers that were
+                * left in an interrupted syscall state.
+                */
+               if (reply->deadline_ns != 0 && !reply->interrupted)
+                       kdbus_notify_reply_timeout(conn->ep->bus, conn->id,
+                                                  reply->cookie);
+
+               kdbus_reply_unlink(reply);
+       }
+
+       /* rearm delayed work with next timeout */
+       if (deadline != ~0ULL)
+               schedule_delayed_work(&conn->work,
+                                     nsecs_to_jiffies(deadline - now));
+
+       mutex_unlock(&conn->lock);
+
+       kdbus_notify_flush(conn->ep->bus);
+}
diff --git a/ipc/kdbus/reply.h b/ipc/kdbus/reply.h
new file mode 100644 (file)
index 0000000..68d5232
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+#ifndef __KDBUS_REPLY_H
+#define __KDBUS_REPLY_H
+
+/**
+ * struct kdbus_reply - an entry of kdbus_conn's list of replies
+ * @kref:              Ref-count of this object
+ * @entry:             The entry of the connection's reply_list
+ * @reply_src:         The connection the reply will be sent from
+ * @reply_dst:         The connection the reply will be sent to
+ * @queue_entry:       The queue entry item that is prepared by the replying
+ *                     connection
+ * @deadline_ns:       The deadline of the reply, in nanoseconds
+ * @cookie:            The cookie of the requesting message
+ * @name_id:           ID of the well-known name the original msg was sent to
+ * @sync:              The reply block is waiting for synchronous I/O
+ * @waiting:           The condition to synchronously wait for
+ * @interrupted:       The sync reply was left in an interrupted state
+ * @err:               The error code for the synchronous reply
+ */
+struct kdbus_reply {
+       struct kref kref;
+       struct list_head entry;
+       struct kdbus_conn *reply_src;
+       struct kdbus_conn *reply_dst;
+       struct kdbus_queue_entry *queue_entry;
+       u64 deadline_ns;
+       u64 cookie;
+       u64 name_id;
+       bool sync:1;
+       bool waiting:1;
+       bool interrupted:1;
+       int err;
+};
+
+struct kdbus_reply *kdbus_reply_new(struct kdbus_conn *reply_src,
+                                   struct kdbus_conn *reply_dst,
+                                   const struct kdbus_msg *msg,
+                                   struct kdbus_name_entry *name_entry,
+                                   bool sync);
+
+struct kdbus_reply *kdbus_reply_ref(struct kdbus_reply *r);
+struct kdbus_reply *kdbus_reply_unref(struct kdbus_reply *r);
+
+void kdbus_reply_link(struct kdbus_reply *r);
+void kdbus_reply_unlink(struct kdbus_reply *r);
+
+struct kdbus_reply *kdbus_reply_find(struct kdbus_conn *replying,
+                                    struct kdbus_conn *reply_dst,
+                                    u64 cookie);
+
+void kdbus_sync_reply_wakeup(struct kdbus_reply *reply, int err);
+void kdbus_reply_list_scan_work(struct work_struct *work);
+
+#endif /* __KDBUS_REPLY_H */
diff --git a/ipc/kdbus/util.c b/ipc/kdbus/util.c
new file mode 100644 (file)
index 0000000..72b1883
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * 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/capability.h>
+#include <linux/cred.h>
+#include <linux/ctype.h>
+#include <linux/err.h>
+#include <linux/file.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/uaccess.h>
+#include <linux/uio.h>
+#include <linux/user_namespace.h>
+
+#include "limits.h"
+#include "util.h"
+
+/**
+ * kdbus_copy_from_user() - copy aligned data from user-space
+ * @dest:      target buffer in kernel memory
+ * @user_ptr:  user-provided source buffer
+ * @size:      memory size to copy from user
+ *
+ * This copies @size bytes from @user_ptr into the kernel, just like
+ * copy_from_user() does. But we enforce an 8-byte alignment and reject any
+ * unaligned user-space pointers.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int kdbus_copy_from_user(void *dest, void __user *user_ptr, size_t size)
+{
+       if (!KDBUS_IS_ALIGNED8((uintptr_t)user_ptr))
+               return -EFAULT;
+
+       if (copy_from_user(dest, user_ptr, size))
+               return -EFAULT;
+
+       return 0;
+}
+
+/**
+ * kdbus_verify_uid_prefix() - verify UID prefix of a user-supplied name
+ * @name:      user-supplied name to verify
+ * @user_ns:   user-namespace to act in
+ * @kuid:      Kernel internal uid of user
+ *
+ * This verifies that the user-supplied name @name has their UID as prefix. This
+ * is the default name-spacing policy we enforce on user-supplied names for
+ * public kdbus entities like buses and endpoints.
+ *
+ * The user must supply names prefixed with "<UID>-", whereas the UID is
+ * interpreted in the user-namespace of the domain. If the user fails to supply
+ * such a prefixed name, we reject it.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int kdbus_verify_uid_prefix(const char *name, struct user_namespace *user_ns,
+                           kuid_t kuid)
+{
+       uid_t uid;
+       char prefix[16];
+
+       /*
+        * The kuid must have a mapping into the userns of the domain
+        * otherwise do not allow creation of buses nor endpoints.
+        */
+       uid = from_kuid(user_ns, kuid);
+       if (uid == (uid_t) -1)
+               return -EINVAL;
+
+       snprintf(prefix, sizeof(prefix), "%u-", uid);
+       if (strncmp(name, prefix, strlen(prefix)) != 0)
+               return -EINVAL;
+
+       return 0;
+}
+
+/**
+ * kdbus_sanitize_attach_flags() - Sanitize attach flags from user-space
+ * @flags:             Attach flags provided by userspace
+ * @attach_flags:      A pointer where to store the valid attach flags
+ *
+ * Convert attach-flags provided by user-space into a valid mask. If the mask
+ * is invalid, an error is returned. The sanitized attach flags are stored in
+ * the output parameter.
+ *
+ * Return: 0 on success, negative error on failure.
+ */
+int kdbus_sanitize_attach_flags(u64 flags, u64 *attach_flags)
+{
+       /* 'any' degrades to 'all' for compatibility */
+       if (flags == _KDBUS_ATTACH_ANY)
+               flags = _KDBUS_ATTACH_ALL;
+
+       /* reject unknown attach flags */
+       if (flags & ~_KDBUS_ATTACH_ALL)
+               return -EINVAL;
+
+       *attach_flags = flags;
+       return 0;
+}
+
+/**
+ * kdbus_kvec_set - helper utility to assemble kvec arrays
+ * @kvec:      kvec entry to use
+ * @src:       Source address to set in @kvec
+ * @len:       Number of bytes in @src
+ * @total_len: Pointer to total length variable
+ *
+ * Set @src and @len in @kvec, and increase @total_len by @len.
+ */
+void kdbus_kvec_set(struct kvec *kvec, void *src, size_t len, u64 *total_len)
+{
+       kvec->iov_base = src;
+       kvec->iov_len = len;
+       *total_len += len;
+}
+
+static const char * const zeros = "\0\0\0\0\0\0\0";
+
+/**
+ * kdbus_kvec_pad - conditionally write a padding kvec
+ * @kvec:      kvec entry to use
+ * @len:       Total length used for kvec array
+ *
+ * Check if the current total byte length of the array in @len is aligned to
+ * 8 bytes. If it isn't, fill @kvec with padding information and increase @len
+ * by the number of bytes stored in @kvec.
+ *
+ * Return: the number of added padding bytes.
+ */
+size_t kdbus_kvec_pad(struct kvec *kvec, u64 *len)
+{
+       size_t pad = KDBUS_ALIGN8(*len) - *len;
+
+       if (!pad)
+               return 0;
+
+       kvec->iov_base = (void *)zeros;
+       kvec->iov_len = pad;
+
+       *len += pad;
+
+       return pad;
+}
diff --git a/ipc/kdbus/util.h b/ipc/kdbus/util.h
new file mode 100644 (file)
index 0000000..5297166
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+#ifndef __KDBUS_UTIL_H
+#define __KDBUS_UTIL_H
+
+#include <linux/dcache.h>
+#include <linux/ioctl.h>
+
+#include <uapi/linux/kdbus.h>
+
+/* all exported addresses are 64 bit */
+#define KDBUS_PTR(addr) ((void __user *)(uintptr_t)(addr))
+
+/* all exported sizes are 64 bit and data aligned to 64 bit */
+#define KDBUS_ALIGN8(s) ALIGN((s), 8)
+#define KDBUS_IS_ALIGNED8(s) (IS_ALIGNED(s, 8))
+
+/**
+ * kdbus_member_set_user - write a structure member to user memory
+ * @_s:                Variable to copy from
+ * @_b:                Buffer to write to
+ * @_t:                Structure type
+ * @_m:                Member name in the passed structure
+ *
+ * Return: the result of copy_to_user()
+ */
+#define kdbus_member_set_user(_s, _b, _t, _m)                          \
+({                                                                     \
+       u64 __user *_sz =                                               \
+               (void __user *)((u8 __user *)(_b) + offsetof(_t, _m));  \
+       copy_to_user(_sz, _s, FIELD_SIZEOF(_t, _m));                    \
+})
+
+/**
+ * kdbus_strhash - calculate a hash
+ * @str:       String
+ *
+ * Return: hash value
+ */
+static inline unsigned int kdbus_strhash(const char *str)
+{
+       unsigned long hash = init_name_hash();
+
+       while (*str)
+               hash = partial_name_hash(*str++, hash);
+
+       return end_name_hash(hash);
+}
+
+int kdbus_verify_uid_prefix(const char *name, struct user_namespace *user_ns,
+                           kuid_t kuid);
+int kdbus_sanitize_attach_flags(u64 flags, u64 *attach_flags);
+
+int kdbus_copy_from_user(void *dest, void __user *user_ptr, size_t size);
+
+struct kvec;
+
+void kdbus_kvec_set(struct kvec *kvec, void *src, size_t len, u64 *total_len);
+size_t kdbus_kvec_pad(struct kvec *kvec, u64 *len);
+
+#endif
index c8dacb4..df6a38b 100644 (file)
@@ -72,6 +72,13 @@ config SAMPLE_QMI_CLIENT
          Build an QMI client sample driver, which demonstrates how to
          communicate with a remote QRTR service, using QMI encoded messages.
 
+config SAMPLE_KDBUS
+       bool "Build kdbus API example"
+       depends on KDBUS
+       help
+         Build an example of how the kdbus API can be used from
+         userspace.
+
 config SAMPLE_RPMSG_CLIENT
        tristate "Build rpmsg client sample -- loadable modules only"
        depends on RPMSG && m
diff --git a/samples/kdbus/.gitignore b/samples/kdbus/.gitignore
new file mode 100644 (file)
index 0000000..ee07d98
--- /dev/null
@@ -0,0 +1 @@
+kdbus-workers
diff --git a/samples/kdbus/Makefile b/samples/kdbus/Makefile
new file mode 100644 (file)
index 0000000..137f842
--- /dev/null
@@ -0,0 +1,9 @@
+# kbuild trick to avoid linker error. Can be omitted if a module is built.
+obj- := dummy.o
+
+hostprogs-$(CONFIG_SAMPLE_KDBUS) += kdbus-workers
+
+always := $(hostprogs-y)
+
+HOSTCFLAGS_kdbus-workers.o += -I$(objtree)/usr/include
+HOSTLOADLIBES_kdbus-workers := -lrt
diff --git a/samples/kdbus/kdbus-api.h b/samples/kdbus/kdbus-api.h
new file mode 100644 (file)
index 0000000..5ed5907
--- /dev/null
@@ -0,0 +1,114 @@
+#ifndef KDBUS_API_H
+#define KDBUS_API_H
+
+#include <sys/ioctl.h>
+#include <linux/kdbus.h>
+
+#define KDBUS_ALIGN8(l) (((l) + 7) & ~7)
+#define KDBUS_ITEM_HEADER_SIZE offsetof(struct kdbus_item, data)
+#define KDBUS_ITEM_SIZE(s) KDBUS_ALIGN8((s) + KDBUS_ITEM_HEADER_SIZE)
+#define KDBUS_ITEM_NEXT(item) \
+       (typeof(item))(((uint8_t *)item) + KDBUS_ALIGN8((item)->size))
+#define KDBUS_FOREACH(iter, first, _size)                              \
+       for (iter = (first);                                            \
+            ((uint8_t *)(iter) < (uint8_t *)(first) + (_size)) &&      \
+              ((uint8_t *)(iter) >= (uint8_t *)(first));               \
+            iter = (void*)(((uint8_t *)iter) + KDBUS_ALIGN8((iter)->size)))
+
+static inline int kdbus_cmd_bus_make(int control_fd, struct kdbus_cmd *cmd)
+{
+       int ret = ioctl(control_fd, KDBUS_CMD_BUS_MAKE, cmd);
+       return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0;
+}
+
+static inline int kdbus_cmd_endpoint_make(int bus_fd, struct kdbus_cmd *cmd)
+{
+       int ret = ioctl(bus_fd, KDBUS_CMD_ENDPOINT_MAKE, cmd);
+       return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0;
+}
+
+static inline int kdbus_cmd_endpoint_update(int ep_fd, struct kdbus_cmd *cmd)
+{
+       int ret = ioctl(ep_fd, KDBUS_CMD_ENDPOINT_UPDATE, cmd);
+       return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0;
+}
+
+static inline int kdbus_cmd_hello(int bus_fd, struct kdbus_cmd_hello *cmd)
+{
+       int ret = ioctl(bus_fd, KDBUS_CMD_HELLO, cmd);
+       return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0;
+}
+
+static inline int kdbus_cmd_update(int fd, struct kdbus_cmd *cmd)
+{
+       int ret = ioctl(fd, KDBUS_CMD_UPDATE, cmd);
+       return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0;
+}
+
+static inline int kdbus_cmd_byebye(int conn_fd, struct kdbus_cmd *cmd)
+{
+       int ret = ioctl(conn_fd, KDBUS_CMD_BYEBYE, cmd);
+       return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0;
+}
+
+static inline int kdbus_cmd_free(int conn_fd, struct kdbus_cmd_free *cmd)
+{
+       int ret = ioctl(conn_fd, KDBUS_CMD_FREE, cmd);
+       return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0;
+}
+
+static inline int kdbus_cmd_conn_info(int conn_fd, struct kdbus_cmd_info *cmd)
+{
+       int ret = ioctl(conn_fd, KDBUS_CMD_CONN_INFO, cmd);
+       return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0;
+}
+
+static inline int kdbus_cmd_bus_creator_info(int conn_fd, struct kdbus_cmd_info *cmd)
+{
+       int ret = ioctl(conn_fd, KDBUS_CMD_BUS_CREATOR_INFO, cmd);
+       return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0;
+}
+
+static inline int kdbus_cmd_list(int fd, struct kdbus_cmd_list *cmd)
+{
+       int ret = ioctl(fd, KDBUS_CMD_LIST, cmd);
+       return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0;
+}
+
+static inline int kdbus_cmd_send(int conn_fd, struct kdbus_cmd_send *cmd)
+{
+       int ret = ioctl(conn_fd, KDBUS_CMD_SEND, cmd);
+       return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0;
+}
+
+static inline int kdbus_cmd_recv(int conn_fd, struct kdbus_cmd_recv *cmd)
+{
+       int ret = ioctl(conn_fd, KDBUS_CMD_RECV, cmd);
+       return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0;
+}
+
+static inline int kdbus_cmd_name_acquire(int conn_fd, struct kdbus_cmd *cmd)
+{
+       int ret = ioctl(conn_fd, KDBUS_CMD_NAME_ACQUIRE, cmd);
+       return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0;
+}
+
+static inline int kdbus_cmd_name_release(int conn_fd, struct kdbus_cmd *cmd)
+{
+       int ret = ioctl(conn_fd, KDBUS_CMD_NAME_RELEASE, cmd);
+       return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0;
+}
+
+static inline int kdbus_cmd_match_add(int conn_fd, struct kdbus_cmd_match *cmd)
+{
+       int ret = ioctl(conn_fd, KDBUS_CMD_MATCH_ADD, cmd);
+       return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0;
+}
+
+static inline int kdbus_cmd_match_remove(int conn_fd, struct kdbus_cmd_match *cmd)
+{
+       int ret = ioctl(conn_fd, KDBUS_CMD_MATCH_REMOVE, cmd);
+       return (ret < 0) ? (errno > 0 ? -errno : -EINVAL) : 0;
+}
+
+#endif /* KDBUS_API_H */
diff --git a/samples/kdbus/kdbus-workers.c b/samples/kdbus/kdbus-workers.c
new file mode 100644 (file)
index 0000000..d331e01
--- /dev/null
@@ -0,0 +1,1326 @@
+/*
+ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com>
+ *
+ * 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.
+ */
+
+/*
+ * Example: Workers
+ * This program computes prime-numbers based on the sieve of Eratosthenes. The
+ * master sets up a shared memory region and spawns workers which clear out the
+ * non-primes. The master reacts to keyboard input and to client-requests to
+ * control what each worker does. Note that this is in no way meant as efficient
+ * way to compute primes. It should only serve as example how a master/worker
+ * concept can be implemented with kdbus used as control messages.
+ *
+ * The main process is called the 'master'. It creates a new, private bus which
+ * will be used between the master and its workers to communicate. The master
+ * then spawns a fixed number of workers. Whenever a worker dies (detected via
+ * SIGCHLD), the master spawns a new worker. When done, the master waits for all
+ * workers to exit, prints a status report and exits itself.
+ *
+ * The master process does *not* keep track of its workers. Instead, this
+ * example implements a PULL model. That is, the master acquires a well-known
+ * name on the bus which each worker uses to request tasks from the master. If
+ * there are no more tasks, the master will return an empty task-list, which
+ * casues a worker to exit immediately.
+ *
+ * As tasks can be computationally expensive, we support cancellation. Whenever
+ * the master process is interrupted, it will drop its well-known name on the
+ * bus. This causes kdbus to broadcast a name-change notification. The workers
+ * check for broadcast messages regularly and will exit if they receive one.
+ *
+ * This example exists of 4 objects:
+ *  * master: The master object contains the context of the master process. This
+ *            process manages the prime-context, spawns workers and assigns
+ *            prime-ranges to each worker to compute.
+ *            The master itself does not do any prime-computations itself.
+ *  * child:  The child object contains the context of a worker. It inherits the
+ *            prime context from its parent (the master) and then creates a new
+ *            bus context to request prime-ranges to compute.
+ *  * prime:  The "prime" object is used to abstract how we compute primes. When
+ *            allocated, it prepares a memory region to hold 1 bit for each
+ *            natural number up to a fixed maximum ('MAX_PRIMES').
+ *            The memory region is backed by a memfd which we share between
+ *            processes. Each worker now gets assigned a range of natural
+ *            numbers which it clears multiples of off the memory region. The
+ *            master process is responsible of distributing all natural numbers
+ *            up to the fixed maximum to its workers.
+ *  * bus:    The bus object is an abstraction of the kdbus API. It is pretty
+ *            straightfoward and only manages the connection-fd plus the
+ *            memory-mapped pool in a single object.
+ *
+ * This example is in reversed order, which should make it easier to read
+ * top-down, but requires some forward-declarations. Just ignore those.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/memfd.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/poll.h>
+#include <sys/signalfd.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+#include "kdbus-api.h"
+
+/* FORWARD DECLARATIONS */
+
+#define POOL_SIZE (16 * 1024 * 1024)
+#define MAX_PRIMES (2UL << 24)
+#define WORKER_COUNT (16)
+#define PRIME_STEPS (65536 * 4)
+
+static const char *arg_busname = "example-workers";
+static const char *arg_modname = "kdbus";
+static const char *arg_master = "org.freedesktop.master";
+
+static int err_assert(int r_errno, const char *msg, const char *func, int line,
+                     const char *file)
+{
+       r_errno = (r_errno != 0) ? -abs(r_errno) : -EFAULT;
+       if (r_errno < 0) {
+               errno = -r_errno;
+               fprintf(stderr, "ERR: %s: %m (%s:%d in %s)\n",
+                       msg, func, line, file);
+       }
+       return r_errno;
+}
+
+#define err_r(_r, _msg) err_assert((_r), (_msg), __func__, __LINE__, __FILE__)
+#define err(_msg) err_r(errno, (_msg))
+
+struct prime;
+struct bus;
+struct master;
+struct child;
+
+struct prime {
+       int fd;
+       uint8_t *area;
+       size_t max;
+       size_t done;
+       size_t status;
+};
+
+static int prime_new(struct prime **out);
+static void prime_free(struct prime *p);
+static bool prime_done(struct prime *p);
+static void prime_consume(struct prime *p, size_t amount);
+static int prime_run(struct prime *p, struct bus *cancel, size_t number);
+static void prime_print(struct prime *p);
+
+struct bus {
+       int fd;
+       uint8_t *pool;
+};
+
+static int bus_open_connection(struct bus **out, uid_t uid, const char *name,
+                              uint64_t recv_flags);
+static void bus_close_connection(struct bus *b);
+static void bus_poool_free_slice(struct bus *b, uint64_t offset);
+static int bus_acquire_name(struct bus *b, const char *name);
+static int bus_install_name_loss_match(struct bus *b, const char *name);
+static int bus_poll(struct bus *b);
+static int bus_make(uid_t uid, const char *name);
+
+struct master {
+       size_t n_workers;
+       size_t max_workers;
+
+       int signal_fd;
+       int control_fd;
+
+       struct prime *prime;
+       struct bus *bus;
+};
+
+static int master_new(struct master **out);
+static void master_free(struct master *m);
+static int master_run(struct master *m);
+static int master_poll(struct master *m);
+static int master_handle_stdin(struct master *m);
+static int master_handle_signal(struct master *m);
+static int master_handle_bus(struct master *m);
+static int master_reply(struct master *m, const struct kdbus_msg *msg);
+static int master_waitpid(struct master *m);
+static int master_spawn(struct master *m);
+
+struct child {
+       struct bus *bus;
+       struct prime *prime;
+};
+
+static int child_new(struct child **out, struct prime *p);
+static void child_free(struct child *c);
+static int child_run(struct child *c);
+
+/* END OF FORWARD DECLARATIONS */
+
+/*
+ * This is the main entrypoint of this example. It is pretty straightforward. We
+ * create a master object, run the computation, print a status report and then
+ * exit. Nothing particularly interesting here, so lets look into the master
+ * object...
+ */
+int main(int argc, char **argv)
+{
+       struct master *m = NULL;
+       int r;
+
+       r = master_new(&m);
+       if (r < 0)
+               goto out;
+
+       r = master_run(m);
+       if (r < 0)
+               goto out;
+
+       if (0)
+               prime_print(m->prime);
+
+out:
+       master_free(m);
+       if (r < 0 && r != -EINTR)
+               fprintf(stderr, "failed\n");
+       else
+               fprintf(stderr, "done\n");
+       return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
+
+/*
+ * ...this will allocate a new master context. It keeps track of the current
+ * number of children/workers that are running, manages a signalfd to track
+ * SIGCHLD, and creates a private kdbus bus. Afterwards, it opens its connection
+ * to the bus and acquires a well known-name (arg_master).
+ */
+static int master_new(struct master **out)
+{
+       struct master *m;
+       sigset_t smask;
+       int r;
+
+       m = calloc(1, sizeof(*m));
+       if (!m)
+               return err("cannot allocate master");
+
+       m->max_workers = WORKER_COUNT;
+       m->signal_fd = -1;
+       m->control_fd = -1;
+
+       /* Block SIGINT and SIGCHLD signals */
+       sigemptyset(&smask);
+       sigaddset(&smask, SIGINT);
+       sigaddset(&smask, SIGCHLD);
+       sigprocmask(SIG_BLOCK, &smask, NULL);
+
+       m->signal_fd = signalfd(-1, &smask, SFD_CLOEXEC);
+       if (m->signal_fd < 0) {
+               r = err("cannot create signalfd");
+               goto error;
+       }
+
+       r = prime_new(&m->prime);
+       if (r < 0)
+               goto error;
+
+       m->control_fd = bus_make(getuid(), arg_busname);
+       if (m->control_fd < 0) {
+               r = m->control_fd;
+               goto error;
+       }
+
+       /*
+        * Open a bus connection for the master, and require each received
+        * message to have a metadata item of type KDBUS_ITEM_PIDS attached.
+        * The current UID is needed to compute the name of the bus node to
+        * connect to.
+        */
+       r = bus_open_connection(&m->bus, getuid(),
+                               arg_busname, KDBUS_ATTACH_PIDS);
+       if (r < 0)
+               goto error;
+
+       /*
+        * Acquire a well-known name on the bus, so children can address
+        * messages to the master using KDBUS_DST_ID_NAME as destination-ID
+        * of messages.
+        */
+       r = bus_acquire_name(m->bus, arg_master);
+       if (r < 0)
+               goto error;
+
+       *out = m;
+       return 0;
+
+error:
+       master_free(m);
+       return r;
+}
+
+/* pretty straightforward destructor of a master object */
+static void master_free(struct master *m)
+{
+       if (!m)
+               return;
+
+       bus_close_connection(m->bus);
+       if (m->control_fd >= 0)
+               close(m->control_fd);
+       prime_free(m->prime);
+       if (m->signal_fd >= 0)
+               close(m->signal_fd);
+       free(m);
+}
+
+static int master_run(struct master *m)
+{
+       int res, r = 0;
+
+       while (!prime_done(m->prime)) {
+               while (m->n_workers < m->max_workers) {
+                       r = master_spawn(m);
+                       if (r < 0)
+                               break;
+               }
+
+               r = master_poll(m);
+               if (r < 0)
+                       break;
+       }
+
+       if (r < 0) {
+               bus_close_connection(m->bus);
+               m->bus = NULL;
+       }
+
+       while (m->n_workers > 0) {
+               res = master_poll(m);
+               if (res < 0) {
+                       if (m->bus) {
+                               bus_close_connection(m->bus);
+                               m->bus = NULL;
+                       }
+                       r = res;
+               }
+       }
+
+       return r == -EINTR ? 0 : r;
+}
+
+static int master_poll(struct master *m)
+{
+       struct pollfd fds[3] = {};
+       int r = 0, n = 0;
+
+       /*
+        * Add stdin, the eventfd and the connection owner file descriptor to
+        * the pollfd table, and handle incoming traffic on the latter in
+        * master_handle_bus().
+        */
+       fds[n].fd = STDIN_FILENO;
+       fds[n++].events = POLLIN;
+       fds[n].fd = m->signal_fd;
+       fds[n++].events = POLLIN;
+       if (m->bus) {
+               fds[n].fd = m->bus->fd;
+               fds[n++].events = POLLIN;
+       }
+
+       r = poll(fds, n, -1);
+       if (r < 0)
+               return err("poll() failed");
+
+       if (fds[0].revents & POLLIN)
+               r = master_handle_stdin(m);
+       else if (fds[0].revents)
+               r = err("ERR/HUP on stdin");
+       if (r < 0)
+               return r;
+
+       if (fds[1].revents & POLLIN)
+               r = master_handle_signal(m);
+       else if (fds[1].revents)
+               r = err("ERR/HUP on signalfd");
+       if (r < 0)
+               return r;
+
+       if (fds[2].revents & POLLIN)
+               r = master_handle_bus(m);
+       else if (fds[2].revents)
+               r = err("ERR/HUP on bus");
+
+       return r;
+}
+
+static int master_handle_stdin(struct master *m)
+{
+       char buf[128];
+       ssize_t l;
+       int r = 0;
+
+       l = read(STDIN_FILENO, buf, sizeof(buf));
+       if (l < 0)
+               return err("cannot read stdin");
+       if (l == 0)
+               return err_r(-EINVAL, "EOF on stdin");
+
+       while (l-- > 0) {
+               switch (buf[l]) {
+               case 'q':
+                       /* quit */
+                       r = -EINTR;
+                       break;
+               case '\n':
+               case ' ':
+                       /* ignore */
+                       break;
+               default:
+                       if (isgraph(buf[l]))
+                               fprintf(stderr, "invalid input '%c'\n", buf[l]);
+                       else
+                               fprintf(stderr, "invalid input 0x%x\n", buf[l]);
+                       break;
+               }
+       }
+
+       return r;
+}
+
+static int master_handle_signal(struct master *m)
+{
+       struct signalfd_siginfo val;
+       ssize_t l;
+
+       l = read(m->signal_fd, &val, sizeof(val));
+       if (l < 0)
+               return err("cannot read signalfd");
+       if (l != sizeof(val))
+               return err_r(-EINVAL, "invalid data from signalfd");
+
+       switch (val.ssi_signo) {
+       case SIGCHLD:
+               return master_waitpid(m);
+       case SIGINT:
+               return err_r(-EINTR, "interrupted");
+       default:
+               return err_r(-EINVAL, "caught invalid signal");
+       }
+}
+
+static int master_handle_bus(struct master *m)
+{
+       struct kdbus_cmd_recv recv = { .size = sizeof(recv) };
+       const struct kdbus_msg *msg = NULL;
+       const struct kdbus_item *item;
+       const struct kdbus_vec *vec = NULL;
+       int r = 0;
+
+       /*
+        * To receive a message, the KDBUS_CMD_RECV ioctl is used.
+        * It takes an argument of type 'struct kdbus_cmd_recv', which
+        * will contain information on the received message when the call
+        * returns. See kdbus.message(7).
+        */
+       r = kdbus_cmd_recv(m->bus->fd, &recv);
+       /*
+        * EAGAIN is returned when there is no message waiting on this
+        * connection. This is not an error - simply bail out.
+        */
+       if (r == -EAGAIN)
+               return 0;
+       if (r < 0)
+               return err_r(r, "cannot receive message");
+
+       /*
+        * Messages received by a connection are stored inside the connection's
+        * pool, at an offset that has been returned in the 'recv' command
+        * struct above. The value describes the relative offset from the
+        * start address of the pool. A message is described with
+        * 'struct kdbus_msg'. See kdbus.message(7).
+        */
+       msg = (void *)(m->bus->pool + recv.msg.offset);
+
+       /*
+        * A messages describes its actual payload in an array of items.
+        * KDBUS_FOREACH() is a simple iterator that walks such an array.
+        * struct kdbus_msg has a field to denote its total size, which is
+        * needed to determine the number of items in the array.
+        */
+       KDBUS_FOREACH(item, msg->items,
+                     msg->size - offsetof(struct kdbus_msg, items)) {
+               /*
+                * An item of type PAYLOAD_OFF describes in-line memory
+                * stored in the pool at a described offset. That offset is
+                * relative to the start address of the message header.
+                * This example program only expects one single item of that
+                * type, remembers the struct kdbus_vec member of the item
+                * when it sees it, and bails out if there is more than one
+                * of them.
+                */
+               if (item->type == KDBUS_ITEM_PAYLOAD_OFF) {
+                       if (vec) {
+                               r = err_r(-EEXIST,
+                                         "message with multiple vecs");
+                               break;
+                       }
+                       vec = &item->vec;
+                       if (vec->size != 1) {
+                               r = err_r(-EINVAL, "invalid message size");
+                               break;
+                       }
+
+               /*
+                * MEMFDs are transported as items of type PAYLOAD_MEMFD.
+                * If such an item is attached, a new file descriptor was
+                * installed into the task when KDBUS_CMD_RECV was called, and
+                * its number is stored in item->memfd.fd.
+                * Implementers *must* handle this item type and close the
+                * file descriptor when no longer needed in order to prevent
+                * file descriptor exhaustion. This example program just bails
+                * out with an error in this case, as memfds are not expected
+                * in this context.
+                */
+               } else if (item->type == KDBUS_ITEM_PAYLOAD_MEMFD) {
+                       r = err_r(-EINVAL, "message with memfd");
+                       break;
+               }
+       }
+       if (r < 0)
+               goto exit;
+       if (!vec) {
+               r = err_r(-EINVAL, "empty message");
+               goto exit;
+       }
+
+       switch (*((const uint8_t *)msg + vec->offset)) {
+       case 'r': {
+               r = master_reply(m, msg);
+               break;
+       }
+       default:
+               r = err_r(-EINVAL, "invalid message type");
+               break;
+       }
+
+exit:
+       /*
+        * We are done with the memory slice that was given to us through
+        * recv.msg.offset. Tell the kernel it can use it for other content
+        * in the future. See kdbus.pool(7).
+        */
+       bus_poool_free_slice(m->bus, recv.msg.offset);
+       return r;
+}
+
+static int master_reply(struct master *m, const struct kdbus_msg *msg)
+{
+       struct kdbus_cmd_send cmd;
+       struct kdbus_item *item;
+       struct kdbus_msg *reply;
+       size_t size, status, p[2];
+       int r;
+
+       /*
+        * This functions sends a message over kdbus. To do this, it uses the
+        * KDBUS_CMD_SEND ioctl, which takes a command struct argument of type
+        * 'struct kdbus_cmd_send'. This struct stores a pointer to the actual
+        * message to send. See kdbus.message(7).
+        */
+       p[0] = m->prime->done;
+       p[1] = prime_done(m->prime) ? 0 : PRIME_STEPS;
+
+       size = sizeof(*reply);
+       size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_vec));
+
+       /* Prepare the message to send */
+       reply = alloca(size);
+       memset(reply, 0, size);
+       reply->size = size;
+
+       /* Each message has a cookie that can be used to send replies */
+       reply->cookie = 1;
+
+       /* The payload_type is arbitrary, but it must be non-zero */
+       reply->payload_type = 0xdeadbeef;
+
+       /*
+        * We are sending a reply. Let the kernel know the cookie of the
+        * message we are replying to.
+        */
+       reply->cookie_reply = msg->cookie;
+
+       /*
+        * Messages can either be directed to a well-known name (stored as
+        * string) or to a unique name (stored as number). This example does
+        * the latter. If the message would be directed to a well-known name
+        * instead, the message's dst_id field would be set to
+        * KDBUS_DST_ID_NAME, and the name would be attaches in an item of type
+        * KDBUS_ITEM_DST_NAME. See below for an example, and also refer to
+        * kdbus.message(7).
+        */
+       reply->dst_id = msg->src_id;
+
+       /* Our message has exactly one item to store its payload */
+       item = reply->items;
+       item->type = KDBUS_ITEM_PAYLOAD_VEC;
+       item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(struct kdbus_vec);
+       item->vec.address = (uintptr_t)p;
+       item->vec.size = sizeof(p);
+
+       /*
+        * Now prepare the command struct, and reference the message we want
+        * to send.
+        */
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.size = sizeof(cmd);
+       cmd.msg_address = (uintptr_t)reply;
+
+       /*
+        * Finally, employ the command on the connection owner
+        * file descriptor.
+        */
+       r = kdbus_cmd_send(m->bus->fd, &cmd);
+       if (r < 0)
+               return err_r(r, "cannot send reply");
+
+       if (p[1]) {
+               prime_consume(m->prime, p[1]);
+               status = m->prime->done * 10000 / m->prime->max;
+               if (status != m->prime->status) {
+                       m->prime->status = status;
+                       fprintf(stderr, "status: %7.3lf%%\n",
+                               (double)status / 100);
+               }
+       }
+
+       return 0;
+}
+
+static int master_waitpid(struct master *m)
+{
+       pid_t pid;
+       int r;
+
+       while ((pid = waitpid(-1, &r, WNOHANG)) > 0) {
+               if (m->n_workers > 0)
+                       --m->n_workers;
+               if (!WIFEXITED(r))
+                       r = err_r(-EINVAL, "child died unexpectedly");
+               else if (WEXITSTATUS(r) != 0)
+                       r = err_r(-WEXITSTATUS(r), "child failed");
+       }
+
+       return r;
+}
+
+static int master_spawn(struct master *m)
+{
+       struct child *c = NULL;
+       struct prime *p = NULL;
+       pid_t pid;
+       int r;
+
+       /* Spawn off one child and call child_run() inside it */
+
+       pid = fork();
+       if (pid < 0)
+               return err("cannot fork");
+       if (pid > 0) {
+               /* parent */
+               ++m->n_workers;
+               return 0;
+       }
+
+       /* child */
+
+       p = m->prime;
+       m->prime = NULL;
+       master_free(m);
+
+       r = child_new(&c, p);
+       if (r < 0)
+               goto exit;
+
+       r = child_run(c);
+
+exit:
+       child_free(c);
+       exit(abs(r));
+}
+
+static int child_new(struct child **out, struct prime *p)
+{
+       struct child *c;
+       int r;
+
+       c = calloc(1, sizeof(*c));
+       if (!c)
+               return err("cannot allocate child");
+
+       c->prime = p;
+
+       /*
+        * Open a connection to the bus and require each received message to
+        * carry a list of the well-known names the sendind connection currently
+        * owns. The current UID is needed in order to determine the name of the
+        * bus node to connect to.
+        */
+       r = bus_open_connection(&c->bus, getuid(),
+                               arg_busname, KDBUS_ATTACH_NAMES);
+       if (r < 0)
+               goto error;
+
+       /*
+        * Install a kdbus match so the child's connection gets notified when
+        * the master loses its well-known name.
+        */
+       r = bus_install_name_loss_match(c->bus, arg_master);
+       if (r < 0)
+               goto error;
+
+       *out = c;
+       return 0;
+
+error:
+       child_free(c);
+       return r;
+}
+
+static void child_free(struct child *c)
+{
+       if (!c)
+               return;
+
+       bus_close_connection(c->bus);
+       prime_free(c->prime);
+       free(c);
+}
+
+static int child_run(struct child *c)
+{
+       struct kdbus_cmd_send cmd;
+       struct kdbus_item *item;
+       struct kdbus_vec *vec = NULL;
+       struct kdbus_msg *msg;
+       struct timespec spec;
+       size_t n, steps, size;
+       int r = 0;
+
+       /*
+        * Let's send a message to the master and ask for work. To do this,
+        * we use the KDBUS_CMD_SEND ioctl, which takes an argument of type
+        * 'struct kdbus_cmd_send'. This struct stores a pointer to the actual
+        * message to send. See kdbus.message(7).
+        */
+       size = sizeof(*msg);
+       size += KDBUS_ITEM_SIZE(strlen(arg_master) + 1);
+       size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_vec));
+
+       msg = alloca(size);
+       memset(msg, 0, size);
+       msg->size = size;
+
+       /*
+        * Tell the kernel that we expect a reply to this message. This means
+        * that
+        *
+        * a) The remote peer will gain temporary permission to talk to us
+        *    even if it would not be allowed to normally.
+        *
+        * b) A timeout value is required.
+        *
+        *    For asynchronous send commands, if no reply is received, we will
+        *    get a kernel notification with an item of type
+        *    KDBUS_ITEM_REPLY_TIMEOUT attached.
+        *
+        *    For synchronous send commands (which this example does), the
+        *    ioctl will block until a reply is received or the timeout is
+        *    exceeded.
+        */
+       msg->flags = KDBUS_MSG_EXPECT_REPLY;
+
+       /* Set our cookie. Replies must use this cookie to send their reply. */
+       msg->cookie = 1;
+
+       /* The payload_type is arbitrary, but it must be non-zero */
+       msg->payload_type = 0xdeadbeef;
+
+       /*
+        * We are sending our message to the current owner of a well-known
+        * name. This makes an item of type KDBUS_ITEM_DST_NAME mandatory.
+        */
+       msg->dst_id = KDBUS_DST_ID_NAME;
+
+       /*
+        * Set the reply timeout to 5 seconds. Timeouts are always set in
+        * absolute timestamps, based con CLOCK_MONOTONIC. See kdbus.message(7).
+        */
+       clock_gettime(CLOCK_MONOTONIC_COARSE, &spec);
+       msg->timeout_ns += (5 + spec.tv_sec) * 1000ULL * 1000ULL * 1000ULL;
+       msg->timeout_ns += spec.tv_nsec;
+
+       /*
+        * Fill the appended items. First, set the well-known name of the
+        * destination we want to talk to.
+        */
+       item = msg->items;
+       item->type = KDBUS_ITEM_DST_NAME;
+       item->size = KDBUS_ITEM_HEADER_SIZE + strlen(arg_master) + 1;
+       strcpy(item->str, arg_master);
+
+       /*
+        * The 2nd item contains a vector to memory we want to send. It
+        * can be content of any type. In our case, we're sending a one-byte
+        * string only. The memory referenced by this item will be copied into
+        * the pool of the receiver connection, and does not need to be valid
+        * after the command is employed.
+        */
+       item = KDBUS_ITEM_NEXT(item);
+       item->type = KDBUS_ITEM_PAYLOAD_VEC;
+       item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(struct kdbus_vec);
+       item->vec.address = (uintptr_t)"r";
+       item->vec.size = 1;
+
+       /* Set up the command struct and reference the message we prepared */
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.size = sizeof(cmd);
+       cmd.msg_address = (uintptr_t)msg;
+
+       /*
+        * The send commands knows a mode in which it will block until a
+        * reply to a message is received. This example uses that mode.
+        * The pool offset to the received reply will be stored in the command
+        * struct after the send command returned. See below.
+        */
+       cmd.flags = KDBUS_SEND_SYNC_REPLY;
+
+       /*
+        * Finally, employ the command on the connection owner
+        * file descriptor.
+        */
+       r = kdbus_cmd_send(c->bus->fd, &cmd);
+       if (r == -ESRCH || r == -EPIPE || r == -ECONNRESET)
+               return 0;
+       if (r < 0)
+               return err_r(r, "cannot send request to master");
+
+       /*
+        * The command was sent with the KDBUS_SEND_SYNC_REPLY flag set,
+        * and returned successfully, which means that cmd.reply.offset now
+        * points to a message inside our connection's pool where the reply
+        * is found. This is equivalent to receiving the reply with
+        * KDBUS_CMD_RECV, but it doesn't require waiting for the reply with
+        * poll() and also saves the ioctl to receive the message.
+        */
+       msg = (void *)(c->bus->pool + cmd.reply.offset);
+
+       /*
+        * A messages describes its actual payload in an array of items.
+        * KDBUS_FOREACH() is a simple iterator that walks such an array.
+        * struct kdbus_msg has a field to denote its total size, which is
+        * needed to determine the number of items in the array.
+        */
+       KDBUS_FOREACH(item, msg->items,
+                     msg->size - offsetof(struct kdbus_msg, items)) {
+               /*
+                * An item of type PAYLOAD_OFF describes in-line memory
+                * stored in the pool at a described offset. That offset is
+                * relative to the start address of the message header.
+                * This example program only expects one single item of that
+                * type, remembers the struct kdbus_vec member of the item
+                * when it sees it, and bails out if there is more than one
+                * of them.
+                */
+               if (item->type == KDBUS_ITEM_PAYLOAD_OFF) {
+                       if (vec) {
+                               r = err_r(-EEXIST,
+                                         "message with multiple vecs");
+                               break;
+                       }
+                       vec = &item->vec;
+                       if (vec->size != 2 * sizeof(size_t)) {
+                               r = err_r(-EINVAL, "invalid message size");
+                               break;
+                       }
+               /*
+                * MEMFDs are transported as items of type PAYLOAD_MEMFD.
+                * If such an item is attached, a new file descriptor was
+                * installed into the task when KDBUS_CMD_RECV was called, and
+                * its number is stored in item->memfd.fd.
+                * Implementers *must* handle this item type close the
+                * file descriptor when no longer needed in order to prevent
+                * file descriptor exhaustion. This example program just bails
+                * out with an error in this case, as memfds are not expected
+                * in this context.
+                */
+               } else if (item->type == KDBUS_ITEM_PAYLOAD_MEMFD) {
+                       r = err_r(-EINVAL, "message with memfd");
+                       break;
+               }
+       }
+       if (r < 0)
+               goto exit;
+       if (!vec) {
+               r = err_r(-EINVAL, "empty message");
+               goto exit;
+       }
+
+       n = ((size_t *)((const uint8_t *)msg + vec->offset))[0];
+       steps = ((size_t *)((const uint8_t *)msg + vec->offset))[1];
+
+       while (steps-- > 0) {
+               ++n;
+               r = prime_run(c->prime, c->bus, n);
+               if (r < 0)
+                       break;
+               r = bus_poll(c->bus);
+               if (r != 0) {
+                       r = r < 0 ? r : -EINTR;
+                       break;
+               }
+       }
+
+exit:
+       /*
+        * We are done with the memory slice that was given to us through
+        * cmd.reply.offset. Tell the kernel it can use it for other content
+        * in the future. See kdbus.pool(7).
+        */
+       bus_poool_free_slice(c->bus, cmd.reply.offset);
+       return r;
+}
+
+/*
+ * Prime Computation
+ *
+ */
+
+static int prime_new(struct prime **out)
+{
+       struct prime *p;
+       int r;
+
+       p = calloc(1, sizeof(*p));
+       if (!p)
+               return err("cannot allocate prime memory");
+
+       p->fd = -1;
+       p->area = MAP_FAILED;
+       p->max = MAX_PRIMES;
+
+       /*
+        * Prepare and map a memfd to store the bit-fields for the number
+        * ranges we want to perform the prime detection on.
+        */
+       p->fd = syscall(__NR_memfd_create, "prime-area", MFD_CLOEXEC);
+       if (p->fd < 0) {
+               r = err("cannot create memfd");
+               goto error;
+       }
+
+       r = ftruncate(p->fd, p->max / 8 + 1);
+       if (r < 0) {
+               r = err("cannot ftruncate area");
+               goto error;
+       }
+
+       p->area = mmap(NULL, p->max / 8 + 1, PROT_READ | PROT_WRITE,
+                      MAP_SHARED, p->fd, 0);
+       if (p->area == MAP_FAILED) {
+               r = err("cannot mmap memfd");
+               goto error;
+       }
+
+       *out = p;
+       return 0;
+
+error:
+       prime_free(p);
+       return r;
+}
+
+static void prime_free(struct prime *p)
+{
+       if (!p)
+               return;
+
+       if (p->area != MAP_FAILED)
+               munmap(p->area, p->max / 8 + 1);
+       if (p->fd >= 0)
+               close(p->fd);
+       free(p);
+}
+
+static bool prime_done(struct prime *p)
+{
+       return p->done >= p->max;
+}
+
+static void prime_consume(struct prime *p, size_t amount)
+{
+       p->done += amount;
+}
+
+static int prime_run(struct prime *p, struct bus *cancel, size_t number)
+{
+       size_t i, n = 0;
+       int r;
+
+       if (number < 2 || number > 65535)
+               return 0;
+
+       for (i = number * number;
+            i < p->max && i > number;
+            i += number) {
+               p->area[i / 8] |= 1 << (i % 8);
+
+               if (!(++n % (1 << 20))) {
+                       r = bus_poll(cancel);
+                       if (r != 0)
+                               return r < 0 ? r : -EINTR;
+               }
+       }
+
+       return 0;
+}
+
+static void prime_print(struct prime *p)
+{
+       size_t i, l = 0;
+
+       fprintf(stderr, "PRIMES:");
+       for (i = 0; i < p->max; ++i) {
+               if (!(p->area[i / 8] & (1 << (i % 8))))
+                       fprintf(stderr, "%c%7zu", !(l++ % 16) ? '\n' : ' ', i);
+       }
+       fprintf(stderr, "\nEND\n");
+}
+
+static int bus_open_connection(struct bus **out, uid_t uid, const char *name,
+                              uint64_t recv_flags)
+{
+       struct kdbus_cmd_hello hello;
+       char path[128];
+       struct bus *b;
+       int r;
+
+       /*
+        * The 'bus' object is our representation of a kdbus connection which
+        * stores two details: the connection owner file descriptor, and the
+        * mmap()ed memory of its associated pool. See kdbus.connection(7) and
+        * kdbus.pool(7).
+        */
+       b = calloc(1, sizeof(*b));
+       if (!b)
+               return err("cannot allocate bus memory");
+
+       b->fd = -1;
+       b->pool = MAP_FAILED;
+
+       /* Compute the name of the bus node to connect to. */
+       snprintf(path, sizeof(path), "/sys/fs/%s/%lu-%s/bus",
+                arg_modname, (unsigned long)uid, name);
+       b->fd = open(path, O_RDWR | O_CLOEXEC);
+       if (b->fd < 0) {
+               r = err("cannot open bus");
+               goto error;
+       }
+
+       /*
+        * To make a connection to the bus, the KDBUS_CMD_HELLO ioctl is used.
+        * It takes an argument of type 'struct kdbus_cmd_hello'.
+        */
+       memset(&hello, 0, sizeof(hello));
+       hello.size = sizeof(hello);
+
+       /*
+        * Specify a mask of metadata attach flags, describing metadata items
+        * that this new connection allows to be sent.
+        */
+       hello.attach_flags_send = _KDBUS_ATTACH_ALL;
+
+       /*
+        * Specify a mask of metadata attach flags, describing metadata items
+        * that this new connection wants to be receive along with each message.
+        */
+       hello.attach_flags_recv = recv_flags;
+
+       /*
+        * A connection may choose the size of its pool, but the number has to
+        * comply with two rules: a) it must be greater than 0, and b) it must
+        * be a mulitple of PAGE_SIZE. See kdbus.pool(7).
+        */
+       hello.pool_size = POOL_SIZE;
+
+       /*
+        * Now employ the command on the file descriptor opened above.
+        * This command will turn the file descriptor into a connection-owner
+        * file descriptor that controls the life-time of the connection; once
+        * it's closed, the connection is shut down.
+        */
+       r = kdbus_cmd_hello(b->fd, &hello);
+       if (r < 0) {
+               err_r(r, "HELLO failed");
+               goto error;
+       }
+
+       bus_poool_free_slice(b, hello.offset);
+
+       /*
+        * Map the pool of the connection. Its size has been set in the
+        * command struct above. See kdbus.pool(7).
+        */
+       b->pool = mmap(NULL, POOL_SIZE, PROT_READ, MAP_SHARED, b->fd, 0);
+       if (b->pool == MAP_FAILED) {
+               r = err("cannot mmap pool");
+               goto error;
+       }
+
+       *out = b;
+       return 0;
+
+error:
+       bus_close_connection(b);
+       return r;
+}
+
+static void bus_close_connection(struct bus *b)
+{
+       if (!b)
+               return;
+
+       /*
+        * A bus connection is closed by simply calling close() on the
+        * connection owner file descriptor. The unique name and all owned
+        * well-known names of the conneciton will disappear.
+        * See kdbus.connection(7).
+        */
+       if (b->pool != MAP_FAILED)
+               munmap(b->pool, POOL_SIZE);
+       if (b->fd >= 0)
+               close(b->fd);
+       free(b);
+}
+
+static void bus_poool_free_slice(struct bus *b, uint64_t offset)
+{
+       struct kdbus_cmd_free cmd = {
+               .size = sizeof(cmd),
+               .offset = offset,
+       };
+       int r;
+
+       /*
+        * Once we're done with a piece of pool memory that was returned
+        * by a command, we have to call the KDBUS_CMD_FREE ioctl on it so it
+        * can be reused. The command takes an argument of type
+        * 'struct kdbus_cmd_free', in which the pool offset of the slice to
+        * free is stored. The ioctl is employed on the connection owner
+        * file descriptor. See kdbus.pool(7),
+        */
+       r = kdbus_cmd_free(b->fd, &cmd);
+       if (r < 0)
+               err_r(r, "cannot free pool slice");
+}
+
+static int bus_acquire_name(struct bus *b, const char *name)
+{
+       struct kdbus_item *item;
+       struct kdbus_cmd *cmd;
+       size_t size;
+       int r;
+
+       /*
+        * This function acquires a well-known name on the bus through the
+        * KDBUS_CMD_NAME_ACQUIRE ioctl. This ioctl takes an argument of type
+        * 'struct kdbus_cmd', which is assembled below. See kdbus.name(7).
+        */
+       size = sizeof(*cmd);
+       size += KDBUS_ITEM_SIZE(strlen(name) + 1);
+
+       cmd = alloca(size);
+       memset(cmd, 0, size);
+       cmd->size = size;
+
+       /*
+        * The command requires an item of type KDBUS_ITEM_NAME, and its
+        * content must be a valid bus name.
+        */
+       item = cmd->items;
+       item->type = KDBUS_ITEM_NAME;
+       item->size = KDBUS_ITEM_HEADER_SIZE + strlen(name) + 1;
+       strcpy(item->str, name);
+
+       /*
+        * Employ the command on the connection owner file descriptor.
+        */
+       r = kdbus_cmd_name_acquire(b->fd, cmd);
+       if (r < 0)
+               return err_r(r, "cannot acquire name");
+
+       return 0;
+}
+
+static int bus_install_name_loss_match(struct bus *b, const char *name)
+{
+       struct kdbus_cmd_match *match;
+       struct kdbus_item *item;
+       size_t size;
+       int r;
+
+       /*
+        * In order to install a match for signal messages, we have to
+        * assemble a 'struct kdbus_cmd_match' and use it along with the
+        * KDBUS_CMD_MATCH_ADD ioctl. See kdbus.match(7).
+        */
+       size = sizeof(*match);
+       size += KDBUS_ITEM_SIZE(sizeof(item->name_change) + strlen(name) + 1);
+
+       match = alloca(size);
+       memset(match, 0, size);
+       match->size = size;
+
+       /*
+        * A match is comprised of many 'rules', each of which describes a
+        * mandatory detail of the message. All rules of a match must be
+        * satified in order to make a message pass.
+        */
+       item = match->items;
+
+       /*
+        * In this case, we're interested in notifications that inform us
+        * about a well-known name being removed from the bus.
+        */
+       item->type = KDBUS_ITEM_NAME_REMOVE;
+       item->size = KDBUS_ITEM_HEADER_SIZE +
+                       sizeof(item->name_change) + strlen(name) + 1;
+
+       /*
+        * We could limit the match further and require a specific unique-ID
+        * to be the new or the old owner of the name. In this case, however,
+        * we don't, and allow 'any' id.
+        */
+       item->name_change.old_id.id = KDBUS_MATCH_ID_ANY;
+       item->name_change.new_id.id = KDBUS_MATCH_ID_ANY;
+
+       /* Copy in the well-known name we're interested in */
+       strcpy(item->name_change.name, name);
+
+       /*
+        * Add the match through the KDBUS_CMD_MATCH_ADD ioctl, employed on
+        * the connection owner fd.
+        */
+       r = kdbus_cmd_match_add(b->fd, match);
+       if (r < 0)
+               return err_r(r, "cannot add match");
+
+       return 0;
+}
+
+static int bus_poll(struct bus *b)
+{
+       struct pollfd fds[1] = {};
+       int r;
+
+       /*
+        * A connection endpoint supports poll() and will wake-up the
+        * task with POLLIN set once a message has arrived.
+        */
+       fds[0].fd = b->fd;
+       fds[0].events = POLLIN;
+       r = poll(fds, sizeof(fds) / sizeof(*fds), 0);
+       if (r < 0)
+               return err("cannot poll bus");
+       return !!(fds[0].revents & POLLIN);
+}
+
+static int bus_make(uid_t uid, const char *name)
+{
+       struct kdbus_item *item;
+       struct kdbus_cmd *make;
+       char path[128], busname[128];
+       size_t size;
+       int r, fd;
+
+       /*
+        * Compute the full path to the 'control' node. 'arg_modname' may be
+        * set to a different value than 'kdbus' for development purposes.
+        * The 'control' node is the primary entry point to kdbus that must be
+        * used in order to create a bus. See kdbus(7) and kdbus.bus(7).
+        */
+       snprintf(path, sizeof(path), "/sys/fs/%s/control", arg_modname);
+
+       /*
+        * Compute the bus name. A valid bus name must always be prefixed with
+        * the EUID of the currently running process in order to avoid name
+        * conflicts. See kdbus.bus(7).
+        */
+       snprintf(busname, sizeof(busname), "%lu-%s", (unsigned long)uid, name);
+
+       fd = open(path, O_RDWR | O_CLOEXEC);
+       if (fd < 0)
+               return err("cannot open control file");
+
+       /*
+        * The KDBUS_CMD_BUS_MAKE ioctl takes an argument of type
+        * 'struct kdbus_cmd', and expects at least two items attached to
+        * it: one to decribe the bloom parameters to be propagated to
+        * connections of the bus, and the name of the bus that was computed
+        * above. Assemble this struct now, and fill it with values.
+        */
+       size = sizeof(*make);
+       size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_bloom_parameter));
+       size += KDBUS_ITEM_SIZE(strlen(busname) + 1);
+
+       make = alloca(size);
+       memset(make, 0, size);
+       make->size = size;
+
+       /*
+        * Each item has a 'type' and 'size' field, and must be stored at an
+        * 8-byte aligned address. The KDBUS_ITEM_NEXT macro is used to advance
+        * the pointer. See kdbus.item(7) for more details.
+        */
+       item = make->items;
+       item->type = KDBUS_ITEM_BLOOM_PARAMETER;
+       item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(item->bloom_parameter);
+       item->bloom_parameter.size = 8;
+       item->bloom_parameter.n_hash = 1;
+
+       /* The name of the new bus is stored in the next item. */
+       item = KDBUS_ITEM_NEXT(item);
+       item->type = KDBUS_ITEM_MAKE_NAME;
+       item->size = KDBUS_ITEM_HEADER_SIZE + strlen(busname) + 1;
+       strcpy(item->str, busname);
+
+       /*
+        * Now create the bus via the KDBUS_CMD_BUS_MAKE ioctl and return the
+        * fd that was used back to the caller of this function. This fd is now
+        * called a 'bus owner file descriptor', and it controls the life-time
+        * of the newly created bus; once the file descriptor is closed, the
+        * bus goes away, and all connections are shut down. See kdbus.bus(7).
+        */
+       r = kdbus_cmd_bus_make(fd, make);
+       if (r < 0) {
+               err_r(r, "cannot make bus");
+               close(fd);
+               return r;
+       }
+
+       return fd;
+}
index 612f675..02d4b86 100644 (file)
@@ -21,6 +21,7 @@ TARGETS += ir
 TARGETS += kcmp
 TARGETS += kexec
 TARGETS += kvm
+#TARGETS += kdbus
 TARGETS += lib
 TARGETS += livepatch
 TARGETS += membarrier
diff --git a/tools/testing/selftests/kdbus/Makefile b/tools/testing/selftests/kdbus/Makefile
new file mode 100644 (file)
index 0000000..0a5afe3
--- /dev/null
@@ -0,0 +1,49 @@
+CFLAGS += -I../../../../usr/include/
+CFLAGS += -I../../../../samples/kdbus/
+CFLAGS += -I../../../../include/uapi/
+CFLAGS += -std=gnu99
+CFLAGS += -DKBUILD_MODNAME=\"kdbus\" -D_GNU_SOURCE
+LDLIBS = -pthread -lcap -lm
+
+OBJS= \
+       kdbus-enum.o            \
+       kdbus-util.o            \
+       kdbus-test.o            \
+       kdbus-test.o            \
+       test-activator.o        \
+       test-attach-flags.o     \
+       test-benchmark.o        \
+       test-bus.o              \
+       test-chat.o             \
+       test-connection.o       \
+       test-daemon.o           \
+       test-endpoint.o         \
+       test-fd.o               \
+       test-free.o             \
+       test-match.o            \
+       test-message.o          \
+       test-metadata-ns.o      \
+       test-monitor.o          \
+       test-names.o            \
+       test-policy.o           \
+       test-policy-ns.o        \
+       test-policy-priv.o      \
+       test-send.o             \
+       test-sync.o             \
+       test-timeout.o
+
+all: kdbus-test
+
+include ../lib.mk
+
+%.o: %.c
+       $(CC) $(CFLAGS) -c $< -o $@
+
+kdbus-test: $(OBJS)
+       $(CC) $(CFLAGS) $^ $(LDLIBS) -o $@
+
+run_tests:
+       ./kdbus-test --tap
+
+clean:
+       rm -f *.o kdbus-test
diff --git a/tools/testing/selftests/kdbus/kdbus-enum.c b/tools/testing/selftests/kdbus/kdbus-enum.c
new file mode 100644 (file)
index 0000000..4f1e579
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2013-2015 Kay Sievers
+ *
+ * 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 <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include "kdbus-util.h"
+#include "kdbus-enum.h"
+
+struct kdbus_enum_table {
+       long long id;
+       const char *name;
+};
+
+#define TABLE(what) static struct kdbus_enum_table kdbus_table_##what[]
+#define ENUM(_id) { .id = _id, .name = STRINGIFY(_id) }
+#define LOOKUP(what)                                                   \
+       const char *enum_##what(long long id)                           \
+       {                                                               \
+               for (size_t i = 0; i < ELEMENTSOF(kdbus_table_##what); i++) \
+                       if (id == kdbus_table_##what[i].id)             \
+                               return kdbus_table_##what[i].name;      \
+               return "UNKNOWN";                                       \
+       }
+
+TABLE(CMD) = {
+       ENUM(KDBUS_CMD_BUS_MAKE),
+       ENUM(KDBUS_CMD_ENDPOINT_MAKE),
+       ENUM(KDBUS_CMD_HELLO),
+       ENUM(KDBUS_CMD_SEND),
+       ENUM(KDBUS_CMD_RECV),
+       ENUM(KDBUS_CMD_LIST),
+       ENUM(KDBUS_CMD_NAME_RELEASE),
+       ENUM(KDBUS_CMD_CONN_INFO),
+       ENUM(KDBUS_CMD_MATCH_ADD),
+       ENUM(KDBUS_CMD_MATCH_REMOVE),
+};
+LOOKUP(CMD);
+
+TABLE(MSG) = {
+       ENUM(_KDBUS_ITEM_NULL),
+       ENUM(KDBUS_ITEM_PAYLOAD_VEC),
+       ENUM(KDBUS_ITEM_PAYLOAD_OFF),
+       ENUM(KDBUS_ITEM_PAYLOAD_MEMFD),
+       ENUM(KDBUS_ITEM_FDS),
+       ENUM(KDBUS_ITEM_BLOOM_PARAMETER),
+       ENUM(KDBUS_ITEM_BLOOM_FILTER),
+       ENUM(KDBUS_ITEM_DST_NAME),
+       ENUM(KDBUS_ITEM_MAKE_NAME),
+       ENUM(KDBUS_ITEM_ATTACH_FLAGS_SEND),
+       ENUM(KDBUS_ITEM_ATTACH_FLAGS_RECV),
+       ENUM(KDBUS_ITEM_ID),
+       ENUM(KDBUS_ITEM_NAME),
+       ENUM(KDBUS_ITEM_TIMESTAMP),
+       ENUM(KDBUS_ITEM_CREDS),
+       ENUM(KDBUS_ITEM_PIDS),
+       ENUM(KDBUS_ITEM_AUXGROUPS),
+       ENUM(KDBUS_ITEM_OWNED_NAME),
+       ENUM(KDBUS_ITEM_TID_COMM),
+       ENUM(KDBUS_ITEM_PID_COMM),
+       ENUM(KDBUS_ITEM_EXE),
+       ENUM(KDBUS_ITEM_CMDLINE),
+       ENUM(KDBUS_ITEM_CGROUP),
+       ENUM(KDBUS_ITEM_CAPS),
+       ENUM(KDBUS_ITEM_SECLABEL),
+       ENUM(KDBUS_ITEM_AUDIT),
+       ENUM(KDBUS_ITEM_CONN_DESCRIPTION),
+       ENUM(KDBUS_ITEM_NAME_ADD),
+       ENUM(KDBUS_ITEM_NAME_REMOVE),
+       ENUM(KDBUS_ITEM_NAME_CHANGE),
+       ENUM(KDBUS_ITEM_ID_ADD),
+       ENUM(KDBUS_ITEM_ID_REMOVE),
+       ENUM(KDBUS_ITEM_REPLY_TIMEOUT),
+       ENUM(KDBUS_ITEM_REPLY_DEAD),
+};
+LOOKUP(MSG);
+
+TABLE(PAYLOAD) = {
+       ENUM(KDBUS_PAYLOAD_KERNEL),
+       ENUM(KDBUS_PAYLOAD_DBUS),
+};
+LOOKUP(PAYLOAD);
diff --git a/tools/testing/selftests/kdbus/kdbus-enum.h b/tools/testing/selftests/kdbus/kdbus-enum.h
new file mode 100644 (file)
index 0000000..a67cec3
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * Copyright (C) 2013-2015 Kay Sievers
+ *
+ * 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.
+ */
+#pragma once
+
+const char *enum_CMD(long long id);
+const char *enum_MSG(long long id);
+const char *enum_MATCH(long long id);
+const char *enum_PAYLOAD(long long id);
diff --git a/tools/testing/selftests/kdbus/kdbus-test.c b/tools/testing/selftests/kdbus/kdbus-test.c
new file mode 100644 (file)
index 0000000..356a1c7
--- /dev/null
@@ -0,0 +1,956 @@
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <assert.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <signal.h>
+#include <sys/mount.h>
+#include <sys/prctl.h>
+#include <sys/wait.h>
+#include <sys/syscall.h>
+#include <sys/eventfd.h>
+#include <linux/sched.h>
+
+#include "kdbus-util.h"
+#include "kdbus-enum.h"
+#include "kdbus-test.h"
+
+enum {
+       TEST_CREATE_BUS         = 1 << 0,
+       TEST_CREATE_CONN        = 1 << 1,
+       TEST_CREATE_CAN_FAIL    = 1 << 2,
+};
+
+struct kdbus_test {
+       const char *name;
+       const char *desc;
+       int (*func)(struct kdbus_test_env *env);
+       unsigned int flags;
+};
+
+struct kdbus_test_args {
+       bool mntns;
+       bool pidns;
+       bool userns;
+       char *uid_map;
+       char *gid_map;
+       int loop;
+       int wait;
+       int fork;
+       int tap_output;
+       char *module;
+       char *root;
+       char *test;
+       char *busname;
+       char *mask_param_path;
+};
+
+static const struct kdbus_test tests[] = {
+       {
+               .name   = "bus-make",
+               .desc   = "bus make functions",
+               .func   = kdbus_test_bus_make,
+               .flags  = 0,
+       },
+       {
+               .name   = "hello",
+               .desc   = "the HELLO command",
+               .func   = kdbus_test_hello,
+               .flags  = TEST_CREATE_BUS,
+       },
+       {
+               .name   = "byebye",
+               .desc   = "the BYEBYE command",
+               .func   = kdbus_test_byebye,
+               .flags  = TEST_CREATE_BUS | TEST_CREATE_CONN,
+       },
+       {
+               .name   = "chat",
+               .desc   = "a chat pattern",
+               .func   = kdbus_test_chat,
+               .flags  = TEST_CREATE_BUS,
+       },
+       {
+               .name   = "daemon",
+               .desc   = "a simple daemon",
+               .func   = kdbus_test_daemon,
+               .flags  = TEST_CREATE_BUS | TEST_CREATE_CONN,
+       },
+       {
+               .name   = "fd-passing",
+               .desc   = "file descriptor passing",
+               .func   = kdbus_test_fd_passing,
+               .flags  = TEST_CREATE_BUS,
+       },
+       {
+               .name   = "endpoint",
+               .desc   = "custom endpoint",
+               .func   = kdbus_test_custom_endpoint,
+               .flags  = TEST_CREATE_BUS | TEST_CREATE_CONN,
+       },
+       {
+               .name   = "monitor",
+               .desc   = "monitor functionality",
+               .func   = kdbus_test_monitor,
+               .flags  = TEST_CREATE_BUS | TEST_CREATE_CONN,
+       },
+       {
+               .name   = "name-basics",
+               .desc   = "basic name registry functions",
+               .func   = kdbus_test_name_basic,
+               .flags  = TEST_CREATE_BUS | TEST_CREATE_CONN,
+       },
+       {
+               .name   = "name-conflict",
+               .desc   = "name registry conflict details",
+               .func   = kdbus_test_name_conflict,
+               .flags  = TEST_CREATE_BUS | TEST_CREATE_CONN,
+       },
+       {
+               .name   = "name-queue",
+               .desc   = "queuing of names",
+               .func   = kdbus_test_name_queue,
+               .flags  = TEST_CREATE_BUS | TEST_CREATE_CONN,
+       },
+       {
+               .name   = "message-basic",
+               .desc   = "basic message handling",
+               .func   = kdbus_test_message_basic,
+               .flags  = TEST_CREATE_BUS | TEST_CREATE_CONN,
+       },
+       {
+               .name   = "message-prio",
+               .desc   = "handling of messages with priority",
+               .func   = kdbus_test_message_prio,
+               .flags  = TEST_CREATE_BUS,
+       },
+       {
+               .name   = "message-quota",
+               .desc   = "message quotas are enforced",
+               .func   = kdbus_test_message_quota,
+               .flags  = TEST_CREATE_BUS,
+       },
+       {
+               .name   = "memory-access",
+               .desc   = "memory access",
+               .func   = kdbus_test_memory_access,
+               .flags  = TEST_CREATE_BUS,
+       },
+       {
+               .name   = "timeout",
+               .desc   = "timeout",
+               .func   = kdbus_test_timeout,
+               .flags  = TEST_CREATE_BUS,
+       },
+       {
+               .name   = "send",
+               .desc   = "send",
+               .func   = kdbus_test_send,
+               .flags  = TEST_CREATE_CONN | TEST_CREATE_CAN_FAIL,
+       },
+       {
+               .name   = "sync-byebye",
+               .desc   = "synchronous replies vs. BYEBYE",
+               .func   = kdbus_test_sync_byebye,
+               .flags  = TEST_CREATE_BUS,
+       },
+       {
+               .name   = "sync-reply",
+               .desc   = "synchronous replies",
+               .func   = kdbus_test_sync_reply,
+               .flags  = TEST_CREATE_BUS,
+       },
+       {
+               .name   = "message-free",
+               .desc   = "freeing of memory",
+               .func   = kdbus_test_free,
+               .flags  = TEST_CREATE_BUS | TEST_CREATE_CONN,
+       },
+       {
+               .name   = "connection-info",
+               .desc   = "retrieving connection information",
+               .func   = kdbus_test_conn_info,
+               .flags  = TEST_CREATE_BUS | TEST_CREATE_CONN,
+       },
+       {
+               .name   = "connection-update",
+               .desc   = "updating connection information",
+               .func   = kdbus_test_conn_update,
+               .flags  = TEST_CREATE_BUS | TEST_CREATE_CONN,
+       },
+       {
+               .name   = "writable-pool",
+               .desc   = "verifying pools are never writable",
+               .func   = kdbus_test_writable_pool,
+               .flags  = TEST_CREATE_BUS,
+       },
+       {
+               .name   = "policy",
+               .desc   = "policy",
+               .func   = kdbus_test_policy,
+               .flags  = TEST_CREATE_BUS,
+       },
+       {
+               .name   = "policy-priv",
+               .desc   = "unprivileged bus access",
+               .func   = kdbus_test_policy_priv,
+               .flags  = TEST_CREATE_BUS,
+       },
+       {
+               .name   = "policy-ns",
+               .desc   = "policy in user namespaces",
+               .func   = kdbus_test_policy_ns,
+               .flags  = TEST_CREATE_BUS,
+       },
+       {
+               .name   = "metadata-ns",
+               .desc   = "metadata in different namespaces",
+               .func   = kdbus_test_metadata_ns,
+               .flags  = TEST_CREATE_BUS | TEST_CREATE_CONN,
+       },
+       {
+               .name   = "match-id-add",
+               .desc   = "adding of matches by id",
+               .func   = kdbus_test_match_id_add,
+               .flags  = TEST_CREATE_BUS | TEST_CREATE_CONN,
+       },
+       {
+               .name   = "match-id-remove",
+               .desc   = "removing of matches by id",
+               .func   = kdbus_test_match_id_remove,
+               .flags  = TEST_CREATE_BUS | TEST_CREATE_CONN,
+       },
+       {
+               .name   = "match-replace",
+               .desc   = "replace of matches with the same cookie",
+               .func   = kdbus_test_match_replace,
+               .flags  = TEST_CREATE_BUS | TEST_CREATE_CONN,
+       },
+       {
+               .name   = "match-name-add",
+               .desc   = "adding of matches by name",
+               .func   = kdbus_test_match_name_add,
+               .flags  = TEST_CREATE_BUS | TEST_CREATE_CONN,
+       },
+       {
+               .name   = "match-name-remove",
+               .desc   = "removing of matches by name",
+               .func   = kdbus_test_match_name_remove,
+               .flags  = TEST_CREATE_BUS | TEST_CREATE_CONN,
+       },
+       {
+               .name   = "match-name-change",
+               .desc   = "matching for name changes",
+               .func   = kdbus_test_match_name_change,
+               .flags  = TEST_CREATE_BUS | TEST_CREATE_CONN,
+       },
+       {
+               .name   = "match-bloom",
+               .desc   = "matching with bloom filters",
+               .func   = kdbus_test_match_bloom,
+               .flags  = TEST_CREATE_BUS | TEST_CREATE_CONN,
+       },
+       {
+               .name   = "activator",
+               .desc   = "activator connections",
+               .func   = kdbus_test_activator,
+               .flags  = TEST_CREATE_BUS | TEST_CREATE_CONN,
+       },
+       {
+               .name   = "benchmark",
+               .desc   = "benchmark",
+               .func   = kdbus_test_benchmark,
+               .flags  = TEST_CREATE_BUS,
+       },
+       {
+               .name   = "benchmark-nomemfds",
+               .desc   = "benchmark without using memfds",
+               .func   = kdbus_test_benchmark_nomemfds,
+               .flags  = TEST_CREATE_BUS,
+       },
+       {
+               .name   = "benchmark-uds",
+               .desc   = "benchmark comparison to UDS",
+               .func   = kdbus_test_benchmark_uds,
+               .flags  = TEST_CREATE_BUS,
+       },
+       {
+               /* Last test */
+               .name   = "attach-flags",
+               .desc   = "attach flags mask",
+               .func   = kdbus_test_attach_flags,
+               .flags  = 0,
+       },
+};
+
+#define N_TESTS ((int) (sizeof(tests) / sizeof(tests[0])))
+
+static int test_prepare_env(const struct kdbus_test *t,
+                           const struct kdbus_test_args *args,
+                           struct kdbus_test_env *env)
+{
+       if (t->flags & TEST_CREATE_BUS) {
+               char *s;
+               char *n = NULL;
+               int ret;
+
+               asprintf(&s, "%s/control", args->root);
+
+               env->control_fd = open(s, O_RDWR);
+               free(s);
+               ASSERT_RETURN(env->control_fd >= 0);
+
+               if (!args->busname) {
+                       n = unique_name("test-bus");
+                       ASSERT_RETURN(n);
+               }
+
+               ret = kdbus_create_bus(env->control_fd,
+                                      args->busname ?: n,
+                                      _KDBUS_ATTACH_ALL,
+                                      _KDBUS_ATTACH_ALL, &s);
+               free(n);
+               ASSERT_RETURN((ret == 0) || (t->flags & TEST_CREATE_CAN_FAIL));
+
+               asprintf(&env->buspath, "%s/%s/bus", args->root, s);
+               free(s);
+       }
+
+       if (t->flags & TEST_CREATE_CONN) {
+               if (!env->buspath) {
+                       char *s = NULL;
+                       char *n = NULL;
+                       int ret;
+
+                       if (!args->busname) {
+                               n = unique_name("test-bus");
+                               ASSERT_RETURN(n);
+                       }
+
+                       ret = kdbus_create_bus(-1,
+                                              args->busname ?: n,
+                                              0,
+                                              0, &s);
+                       free(n);
+                       ASSERT_RETURN(ret == 0);
+
+                       asprintf(&env->buspath, "%s/%s/bus", args->root, s);
+                       free(s);
+               }
+               ASSERT_RETURN(env->buspath);
+               env->conn = kdbus_hello(env->buspath, 0, NULL, 0);
+               ASSERT_RETURN(env->conn || (t->flags & TEST_CREATE_CAN_FAIL));
+       }
+
+       env->root = args->root;
+       env->module = args->module;
+       env->mask_param_path = args->mask_param_path;
+
+       return 0;
+}
+
+void test_unprepare_env(const struct kdbus_test *t, struct kdbus_test_env *env)
+{
+       if (env->conn) {
+               kdbus_conn_free(env->conn);
+               env->conn = NULL;
+       }
+
+       if (env->control_fd >= 0) {
+               close(env->control_fd);
+               env->control_fd = -1;
+       }
+
+       if (env->buspath) {
+               free(env->buspath);
+               env->buspath = NULL;
+       }
+}
+
+static int test_run(const struct kdbus_test *t,
+                   const struct kdbus_test_args *kdbus_args,
+                   int wait)
+{
+       int ret;
+       struct kdbus_test_env env = {};
+
+       ret = test_prepare_env(t, kdbus_args, &env);
+       if (ret != TEST_OK)
+               return ret;
+
+       if (wait > 0) {
+               printf("Sleeping %d seconds before running test ...\n", wait);
+               sleep(wait);
+       }
+
+       ret = t->func(&env);
+       test_unprepare_env(t, &env);
+       return ret;
+}
+
+static int test_run_forked(const struct kdbus_test *t,
+                          const struct kdbus_test_args *kdbus_args,
+                          int wait)
+{
+       int ret;
+       pid_t pid;
+
+       pid = fork();
+       if (pid < 0) {
+               return TEST_ERR;
+       } else if (pid == 0) {
+               ret = test_run(t, kdbus_args, wait);
+               _exit(ret);
+       }
+
+       pid = waitpid(pid, &ret, 0);
+       if (pid <= 0)
+               return TEST_ERR;
+       else if (!WIFEXITED(ret))
+               return TEST_ERR;
+       else
+               return WEXITSTATUS(ret);
+}
+
+static void print_test_result(int ret)
+{
+       switch (ret) {
+       case TEST_OK:
+               printf("OK");
+               break;
+       case TEST_SKIP:
+               printf("SKIPPED");
+               break;
+       case TEST_ERR:
+               printf("ERROR");
+               break;
+       }
+}
+
+static int start_all_tests(struct kdbus_test_args *kdbus_args)
+{
+       int ret;
+       unsigned int fail_cnt = 0;
+       unsigned int skip_cnt = 0;
+       unsigned int ok_cnt = 0;
+       unsigned int i;
+
+       if (kdbus_args->tap_output) {
+               printf("1..%d\n", N_TESTS);
+               fflush(stdout);
+       }
+
+       kdbus_util_verbose = false;
+
+       for (i = 0; i < N_TESTS; i++) {
+               const struct kdbus_test *t = tests + i;
+
+               if (!kdbus_args->tap_output) {
+                       unsigned int n;
+
+                       printf("Testing %s (%s) ", t->desc, t->name);
+                       for (n = 0; n < 60 - strlen(t->desc) - strlen(t->name); n++)
+                               printf(".");
+                       printf(" ");
+               }
+
+               ret = test_run_forked(t, kdbus_args, 0);
+               switch (ret) {
+               case TEST_OK:
+                       ok_cnt++;
+                       break;
+               case TEST_SKIP:
+                       skip_cnt++;
+                       break;
+               case TEST_ERR:
+                       fail_cnt++;
+                       break;
+               }
+
+               if (kdbus_args->tap_output) {
+                       printf("%sok %d - %s%s (%s)\n",
+                              (ret == TEST_ERR) ? "not " : "", i + 1,
+                              (ret == TEST_SKIP) ? "# SKIP " : "",
+                              t->desc, t->name);
+                       fflush(stdout);
+               } else {
+                       print_test_result(ret);
+                       printf("\n");
+               }
+       }
+
+       if (kdbus_args->tap_output)
+               printf("Failed %d/%d tests, %.2f%% okay\n", fail_cnt, N_TESTS,
+                      100.0 - (fail_cnt * 100.0) / ((float) N_TESTS));
+       else
+               printf("\nSUMMARY: %u tests passed, %u skipped, %u failed\n",
+                      ok_cnt, skip_cnt, fail_cnt);
+
+       return fail_cnt > 0 ? TEST_ERR : TEST_OK;
+}
+
+static int start_one_test(struct kdbus_test_args *kdbus_args)
+{
+       int i, ret;
+       bool test_found = false;
+
+       for (i = 0; i < N_TESTS; i++) {
+               const struct kdbus_test *t = tests + i;
+
+               if (strcmp(t->name, kdbus_args->test))
+                       continue;
+
+               do {
+                       test_found = true;
+                       if (kdbus_args->fork)
+                               ret = test_run_forked(t, kdbus_args,
+                                                     kdbus_args->wait);
+                       else
+                               ret = test_run(t, kdbus_args,
+                                              kdbus_args->wait);
+
+                       printf("Testing %s: ", t->desc);
+                       print_test_result(ret);
+                       printf("\n");
+
+                       if (ret != TEST_OK)
+                               break;
+               } while (kdbus_args->loop);
+
+               return ret;
+       }
+
+       if (!test_found) {
+               printf("Unknown test-id '%s'\n", kdbus_args->test);
+               return TEST_ERR;
+       }
+
+       return TEST_OK;
+}
+
+static void usage(const char *argv0)
+{
+       unsigned int i, j;
+
+       printf("Usage: %s [options]\n"
+              "Options:\n"
+              "\t-a, --tap             Output test results in TAP format\n"
+              "\t-m, --module <module> Kdbus module name\n"
+              "\t-x, --loop            Run in a loop\n"
+              "\t-f, --fork            Fork before running a test\n"
+              "\t-h, --help            Print this help\n"
+              "\t-r, --root <root>     Toplevel of the kdbus hierarchy\n"
+              "\t-t, --test <test-id>  Run one specific test only, in verbose mode\n"
+              "\t-b, --bus <busname>   Instead of generating a random bus name, take <busname>.\n"
+              "\t-w, --wait <secs>     Wait <secs> before actually starting test\n"
+              "\t    --mntns           New mount namespace\n"
+              "\t    --pidns           New PID namespace\n"
+              "\t    --userns          New user namespace\n"
+              "\t    --uidmap uid_map  UID map for user namespace\n"
+              "\t    --gidmap gid_map  GID map for user namespace\n"
+              "\n", argv0);
+
+       printf("By default, all test are run once, and a summary is printed.\n"
+              "Available tests for --test:\n\n");
+
+       for (i = 0; i < N_TESTS; i++) {
+               const struct kdbus_test *t = tests + i;
+
+               printf("\t%s", t->name);
+
+               for (j = 0; j < 24 - strlen(t->name); j++)
+                       printf(" ");
+
+               printf("Test %s\n", t->desc);
+       }
+
+       printf("\n");
+       printf("Note that some tests may, if run specifically by --test, "
+              "behave differently, and not terminate by themselves.\n");
+
+       exit(EXIT_FAILURE);
+}
+
+void print_kdbus_test_args(struct kdbus_test_args *args)
+{
+       if (args->userns || args->pidns || args->mntns)
+               printf("# Starting tests in new %s%s%s namespaces%s\n",
+                       args->mntns ? "MOUNT " : "",
+                       args->pidns ? "PID " : "",
+                       args->userns ? "USER " : "",
+                       args->mntns ? ", kdbusfs will be remounted" : "");
+       else
+               printf("# Starting tests in the same namespaces\n");
+}
+
+void print_metadata_support(void)
+{
+       bool no_meta_audit, no_meta_cgroups, no_meta_seclabel;
+
+       /*
+        * KDBUS_ATTACH_CGROUP, KDBUS_ATTACH_AUDIT and
+        * KDBUS_ATTACH_SECLABEL
+        */
+       no_meta_audit = !config_auditsyscall_is_enabled();
+       no_meta_cgroups = !config_cgroups_is_enabled();
+       no_meta_seclabel = !config_security_is_enabled();
+
+       if (no_meta_audit | no_meta_cgroups | no_meta_seclabel)
+               printf("# Starting tests without %s%s%s metadata support\n",
+                      no_meta_audit ? "AUDIT " : "",
+                      no_meta_cgroups ? "CGROUP " : "",
+                      no_meta_seclabel ? "SECLABEL " : "");
+       else
+               printf("# Starting tests with full metadata support\n");
+}
+
+int run_tests(struct kdbus_test_args *kdbus_args)
+{
+       int ret;
+       static char control[4096];
+
+       snprintf(control, sizeof(control), "%s/control", kdbus_args->root);
+
+       if (access(control, W_OK) < 0) {
+               printf("Unable to locate control node at '%s'.\n",
+                       control);
+               return TEST_ERR;
+       }
+
+       if (kdbus_args->test) {
+               ret = start_one_test(kdbus_args);
+       } else {
+               do {
+                       ret = start_all_tests(kdbus_args);
+                       if (ret != TEST_OK)
+                               break;
+               } while (kdbus_args->loop);
+       }
+
+       return ret;
+}
+
+static void nop_handler(int sig) {}
+
+static int test_prepare_mounts(struct kdbus_test_args *kdbus_args)
+{
+       int ret;
+       char kdbusfs[64] = {'\0'};
+
+       snprintf(kdbusfs, sizeof(kdbusfs), "%sfs", kdbus_args->module);
+
+       /* make current mount slave */
+       ret = mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL);
+       if (ret < 0) {
+               ret = -errno;
+               printf("error mount() root: %d (%m)\n", ret);
+               return ret;
+       }
+
+       /* Remount procfs since we need it in our tests */
+       if (kdbus_args->pidns) {
+               ret = mount("proc", "/proc", "proc",
+                           MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL);
+               if (ret < 0) {
+                       ret = -errno;
+                       printf("error mount() /proc : %d (%m)\n", ret);
+                       return ret;
+               }
+       }
+
+       /* Remount kdbusfs */
+       ret = mount(kdbusfs, kdbus_args->root, kdbusfs,
+                   MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL);
+       if (ret < 0) {
+               ret = -errno;
+               printf("error mount() %s :%d (%m)\n", kdbusfs, ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+int run_tests_in_namespaces(struct kdbus_test_args *kdbus_args)
+{
+       int ret;
+       int efd = -1;
+       int status;
+       pid_t pid, rpid;
+       struct sigaction oldsa;
+       struct sigaction sa = {
+               .sa_handler = nop_handler,
+               .sa_flags = SA_NOCLDSTOP,
+       };
+
+       efd = eventfd(0, EFD_CLOEXEC);
+       if (efd < 0) {
+               ret = -errno;
+               printf("eventfd() failed: %d (%m)\n", ret);
+               return TEST_ERR;
+       }
+
+       ret = sigaction(SIGCHLD, &sa, &oldsa);
+       if (ret < 0) {
+               ret = -errno;
+               printf("sigaction() failed: %d (%m)\n", ret);
+               return TEST_ERR;
+       }
+
+       /* setup namespaces */
+       pid = syscall(__NR_clone, SIGCHLD|
+                     (kdbus_args->userns ? CLONE_NEWUSER : 0) |
+                     (kdbus_args->mntns ? CLONE_NEWNS : 0) |
+                     (kdbus_args->pidns ? CLONE_NEWPID : 0), NULL);
+       if (pid < 0) {
+               printf("clone() failed: %d (%m)\n", -errno);
+               return TEST_ERR;
+       }
+
+       if (pid == 0) {
+               eventfd_t event_status = 0;
+
+               ret = prctl(PR_SET_PDEATHSIG, SIGKILL);
+               if (ret < 0) {
+                       ret = -errno;
+                       printf("error prctl(): %d (%m)\n", ret);
+                       _exit(TEST_ERR);
+               }
+
+               /* reset sighandlers of childs */
+               ret = sigaction(SIGCHLD, &oldsa, NULL);
+               if (ret < 0) {
+                       ret = -errno;
+                       printf("sigaction() failed: %d (%m)\n", ret);
+                       _exit(TEST_ERR);
+               }
+
+               ret = eventfd_read(efd, &event_status);
+               if (ret < 0 || event_status != 1) {
+                       printf("error eventfd_read()\n");
+                       _exit(TEST_ERR);
+               }
+
+               if (kdbus_args->mntns) {
+                       ret = test_prepare_mounts(kdbus_args);
+                       if (ret < 0) {
+                               printf("error preparing mounts\n");
+                               _exit(TEST_ERR);
+                       }
+               }
+
+               ret = run_tests(kdbus_args);
+               _exit(ret);
+       }
+
+       /* Setup userns mapping */
+       if (kdbus_args->userns) {
+               ret = userns_map_uid_gid(pid, kdbus_args->uid_map,
+                                        kdbus_args->gid_map);
+               if (ret < 0) {
+                       printf("error mapping uid and gid in userns\n");
+                       eventfd_write(efd, 2);
+                       return TEST_ERR;
+               }
+       }
+
+       ret = eventfd_write(efd, 1);
+       if (ret < 0) {
+               ret = -errno;
+               printf("error eventfd_write(): %d (%m)\n", ret);
+               return TEST_ERR;
+       }
+
+       rpid = waitpid(pid, &status, 0);
+       ASSERT_RETURN_VAL(rpid == pid, TEST_ERR);
+
+       close(efd);
+
+       if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
+               return TEST_ERR;
+
+       return TEST_OK;
+}
+
+int start_tests(struct kdbus_test_args *kdbus_args)
+{
+       int ret;
+       bool namespaces;
+       uint64_t kdbus_param_mask;
+       static char fspath[4096], parampath[4096];
+
+       namespaces = (kdbus_args->mntns || kdbus_args->pidns ||
+                     kdbus_args->userns);
+
+       /* for pidns we need mntns set */
+       if (kdbus_args->pidns && !kdbus_args->mntns) {
+               printf("Failed: please set both pid and mnt namesapces\n");
+               return TEST_ERR;
+       }
+
+       if (kdbus_args->userns) {
+               if (!config_user_ns_is_enabled()) {
+                       printf("User namespace not supported\n");
+                       return TEST_ERR;
+               }
+
+               if (!kdbus_args->uid_map || !kdbus_args->gid_map) {
+                       printf("Failed: please specify uid or gid mapping\n");
+                       return TEST_ERR;
+               }
+       }
+
+       print_kdbus_test_args(kdbus_args);
+       print_metadata_support();
+
+       /* setup kdbus paths */
+       if (!kdbus_args->module)
+               kdbus_args->module = "kdbus";
+
+       if (!kdbus_args->root) {
+               snprintf(fspath, sizeof(fspath), "/sys/fs/%s",
+                        kdbus_args->module);
+               kdbus_args->root = fspath;
+       }
+
+       snprintf(parampath, sizeof(parampath),
+                "/sys/module/%s/parameters/attach_flags_mask",
+                kdbus_args->module);
+       kdbus_args->mask_param_path = parampath;
+
+       ret = kdbus_sysfs_get_parameter_mask(kdbus_args->mask_param_path,
+                                            &kdbus_param_mask);
+       if (ret < 0)
+               return TEST_ERR;
+
+       printf("# Starting tests with an attach_flags_mask=0x%llx\n",
+               (unsigned long long)kdbus_param_mask);
+
+       /* Start tests */
+       if (namespaces)
+               ret = run_tests_in_namespaces(kdbus_args);
+       else
+               ret = run_tests(kdbus_args);
+
+       return ret;
+}
+
+int main(int argc, char *argv[])
+{
+       int t, ret = 0;
+       struct kdbus_test_args *kdbus_args;
+       enum {
+               ARG_MNTNS = 0x100,
+               ARG_PIDNS,
+               ARG_USERNS,
+               ARG_UIDMAP,
+               ARG_GIDMAP,
+       };
+       char *exec = basename(argv[0]);
+
+       kdbus_args = malloc(sizeof(*kdbus_args));
+       if (!kdbus_args) {
+               printf("unable to malloc() kdbus_args\n");
+               return EXIT_FAILURE;
+       }
+
+       memset(kdbus_args, 0, sizeof(*kdbus_args));
+
+       static const struct option options[] = {
+               { "loop",       no_argument,            NULL, 'x' },
+               { "help",       no_argument,            NULL, 'h' },
+               { "root",       required_argument,      NULL, 'r' },
+               { "test",       required_argument,      NULL, 't' },
+               { "bus",        required_argument,      NULL, 'b' },
+               { "wait",       required_argument,      NULL, 'w' },
+               { "fork",       no_argument,            NULL, 'f' },
+               { "module",     required_argument,      NULL, 'm' },
+               { "tap",        no_argument,            NULL, 'a' },
+               { "mntns",      no_argument,            NULL, ARG_MNTNS },
+               { "pidns",      no_argument,            NULL, ARG_PIDNS },
+               { "userns",     no_argument,            NULL, ARG_USERNS },
+               { "uidmap",     required_argument,      NULL, ARG_UIDMAP },
+               { "gidmap",     required_argument,      NULL, ARG_GIDMAP },
+               {}
+       };
+
+       srand(time(NULL));
+
+       if (strcmp(exec, "kdbus-test") != 0) {
+               kdbus_args->test = exec;
+       }
+
+       while ((t = getopt_long(argc, argv, "hxfm:r:t:b:w:a", options, NULL)) >= 0) {
+               switch (t) {
+               case 'x':
+                       kdbus_args->loop = 1;
+                       break;
+
+               case 'm':
+                       kdbus_args->module = optarg;
+                       break;
+
+               case 'r':
+                       kdbus_args->root = optarg;
+                       break;
+
+               case 't':
+                       kdbus_args->test = optarg;
+                       break;
+
+               case 'b':
+                       kdbus_args->busname = optarg;
+                       break;
+
+               case 'w':
+                       kdbus_args->wait = strtol(optarg, NULL, 10);
+                       break;
+
+               case 'f':
+                       kdbus_args->fork = 1;
+                       break;
+
+               case 'a':
+                       kdbus_args->tap_output = 1;
+                       break;
+
+               case ARG_MNTNS:
+                       kdbus_args->mntns = true;
+                       break;
+
+               case ARG_PIDNS:
+                       kdbus_args->pidns = true;
+                       break;
+
+               case ARG_USERNS:
+                       kdbus_args->userns = true;
+                       break;
+
+               case ARG_UIDMAP:
+                       kdbus_args->uid_map = optarg;
+                       break;
+
+               case ARG_GIDMAP:
+                       kdbus_args->gid_map = optarg;
+                       break;
+
+               default:
+               case 'h':
+                       usage(argv[0]);
+               }
+       }
+
+       ret = start_tests(kdbus_args);
+       if (ret == TEST_ERR)
+               return EXIT_FAILURE;
+
+       free(kdbus_args);
+
+       return 0;
+}
diff --git a/tools/testing/selftests/kdbus/kdbus-test.h b/tools/testing/selftests/kdbus/kdbus-test.h
new file mode 100644 (file)
index 0000000..826b9f4
--- /dev/null
@@ -0,0 +1,86 @@
+#ifndef _TEST_KDBUS_H_
+#define _TEST_KDBUS_H_
+
+struct kdbus_test_env {
+       char *buspath;
+       const char *root;
+       const char *module;
+       const char *mask_param_path;
+       int control_fd;
+       struct kdbus_conn *conn;
+};
+
+enum {
+       TEST_OK,
+       TEST_SKIP,
+       TEST_ERR,
+};
+
+#define ASSERT_RETURN_VAL(cond, val)           \
+       if (!(cond)) {                  \
+               fprintf(stderr, "Assertion '%s' failed in %s(), %s:%d\n", \
+                       #cond, __func__, __FILE__, __LINE__);   \
+               return val;     \
+       }
+
+#define ASSERT_EXIT_VAL(cond, val)             \
+       if (!(cond)) {                  \
+               fprintf(stderr, "Assertion '%s' failed in %s(), %s:%d\n", \
+                       #cond, __func__, __FILE__, __LINE__);   \
+               _exit(val);     \
+       }
+
+#define ASSERT_BREAK(cond)             \
+       if (!(cond)) {                  \
+               fprintf(stderr, "Assertion '%s' failed in %s(), %s:%d\n", \
+                       #cond, __func__, __FILE__, __LINE__);   \
+               break; \
+       }
+
+#define ASSERT_RETURN(cond)            \
+       ASSERT_RETURN_VAL(cond, TEST_ERR)
+
+#define ASSERT_EXIT(cond)              \
+       ASSERT_EXIT_VAL(cond, EXIT_FAILURE)
+
+int kdbus_test_activator(struct kdbus_test_env *env);
+int kdbus_test_attach_flags(struct kdbus_test_env *env);
+int kdbus_test_benchmark(struct kdbus_test_env *env);
+int kdbus_test_benchmark_nomemfds(struct kdbus_test_env *env);
+int kdbus_test_benchmark_uds(struct kdbus_test_env *env);
+int kdbus_test_bus_make(struct kdbus_test_env *env);
+int kdbus_test_byebye(struct kdbus_test_env *env);
+int kdbus_test_chat(struct kdbus_test_env *env);
+int kdbus_test_conn_info(struct kdbus_test_env *env);
+int kdbus_test_conn_update(struct kdbus_test_env *env);
+int kdbus_test_daemon(struct kdbus_test_env *env);
+int kdbus_test_custom_endpoint(struct kdbus_test_env *env);
+int kdbus_test_fd_passing(struct kdbus_test_env *env);
+int kdbus_test_free(struct kdbus_test_env *env);
+int kdbus_test_hello(struct kdbus_test_env *env);
+int kdbus_test_match_bloom(struct kdbus_test_env *env);
+int kdbus_test_match_id_add(struct kdbus_test_env *env);
+int kdbus_test_match_id_remove(struct kdbus_test_env *env);
+int kdbus_test_match_replace(struct kdbus_test_env *env);
+int kdbus_test_match_name_add(struct kdbus_test_env *env);
+int kdbus_test_match_name_change(struct kdbus_test_env *env);
+int kdbus_test_match_name_remove(struct kdbus_test_env *env);
+int kdbus_test_message_basic(struct kdbus_test_env *env);
+int kdbus_test_message_prio(struct kdbus_test_env *env);
+int kdbus_test_message_quota(struct kdbus_test_env *env);
+int kdbus_test_memory_access(struct kdbus_test_env *env);
+int kdbus_test_metadata_ns(struct kdbus_test_env *env);
+int kdbus_test_monitor(struct kdbus_test_env *env);
+int kdbus_test_name_basic(struct kdbus_test_env *env);
+int kdbus_test_name_conflict(struct kdbus_test_env *env);
+int kdbus_test_name_queue(struct kdbus_test_env *env);
+int kdbus_test_policy(struct kdbus_test_env *env);
+int kdbus_test_policy_ns(struct kdbus_test_env *env);
+int kdbus_test_policy_priv(struct kdbus_test_env *env);
+int kdbus_test_send(struct kdbus_test_env *env);
+int kdbus_test_sync_byebye(struct kdbus_test_env *env);
+int kdbus_test_sync_reply(struct kdbus_test_env *env);
+int kdbus_test_timeout(struct kdbus_test_env *env);
+int kdbus_test_writable_pool(struct kdbus_test_env *env);
+
+#endif /* _TEST_KDBUS_H_ */
diff --git a/tools/testing/selftests/kdbus/kdbus-util.c b/tools/testing/selftests/kdbus/kdbus-util.c
new file mode 100644 (file)
index 0000000..6677dad
--- /dev/null
@@ -0,0 +1,1636 @@
+/*
+ * Copyright (C) 2013-2015 Daniel Mack
+ * Copyright (C) 2013-2015 Kay Sievers
+ * Copyright (C) 2014-2015 Djalal Harouni
+ *
+ * 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 <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <time.h>
+#include <inttypes.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <assert.h>
+#include <poll.h>
+#include <grp.h>
+#include <sys/capability.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <linux/unistd.h>
+#include <linux/memfd.h>
+
+#ifndef __NR_memfd_create
+  #ifdef __x86_64__
+    #define __NR_memfd_create 319
+  #elif defined __arm__
+    #define __NR_memfd_create 385
+  #else
+    #define __NR_memfd_create 356
+  #endif
+#endif
+
+#include "kdbus-api.h"
+#include "kdbus-util.h"
+#include "kdbus-enum.h"
+
+#ifndef F_ADD_SEALS
+#define F_LINUX_SPECIFIC_BASE  1024
+#define F_ADD_SEALS     (F_LINUX_SPECIFIC_BASE + 9)
+#define F_GET_SEALS     (F_LINUX_SPECIFIC_BASE + 10)
+
+#define F_SEAL_SEAL     0x0001  /* prevent further seals from being set */
+#define F_SEAL_SHRINK   0x0002  /* prevent file from shrinking */
+#define F_SEAL_GROW     0x0004  /* prevent file from growing */
+#define F_SEAL_WRITE    0x0008  /* prevent writes */
+#endif
+
+int kdbus_util_verbose = true;
+
+int kdbus_sysfs_get_parameter_mask(const char *path, uint64_t *mask)
+{
+       int ret;
+       FILE *file;
+       unsigned long long value;
+
+       file = fopen(path, "r");
+       if (!file) {
+               ret = -errno;
+               kdbus_printf("--- error fopen(): %d (%m)\n", ret);
+               return ret;
+       }
+
+       ret = fscanf(file, "%llu", &value);
+       if (ret != 1) {
+               if (ferror(file))
+                       ret = -errno;
+               else
+                       ret = -EIO;
+
+               kdbus_printf("--- error fscanf(): %d\n", ret);
+               fclose(file);
+               return ret;
+       }
+
+       *mask = (uint64_t)value;
+
+       fclose(file);
+
+       return 0;
+}
+
+int kdbus_sysfs_set_parameter_mask(const char *path, uint64_t mask)
+{
+       int ret;
+       FILE *file;
+
+       file = fopen(path, "w");
+       if (!file) {
+               ret = -errno;
+               kdbus_printf("--- error open(): %d (%m)\n", ret);
+               return ret;
+       }
+
+       ret = fprintf(file, "%llu", (unsigned long long)mask);
+       if (ret <= 0) {
+               ret = -EIO;
+               kdbus_printf("--- error fprintf(): %d\n", ret);
+       }
+
+       fclose(file);
+
+       return ret > 0 ? 0 : ret;
+}
+
+int kdbus_create_bus(int control_fd, const char *name,
+                    uint64_t req_meta, uint64_t owner_meta,
+                    char **path)
+{
+       struct {
+               struct kdbus_cmd cmd;
+
+               /* bloom size item */
+               struct {
+                       uint64_t size;
+                       uint64_t type;
+                       struct kdbus_bloom_parameter bloom;
+               } bp;
+
+               /* required and owner metadata items */
+               struct {
+                       uint64_t size;
+                       uint64_t type;
+                       uint64_t flags;
+               } attach[2];
+
+               /* name item */
+               struct {
+                       uint64_t size;
+                       uint64_t type;
+                       char str[64];
+               } name;
+       } bus_make;
+       int ret = 0;
+
+       memset(&bus_make, 0, sizeof(bus_make));
+       bus_make.bp.size = sizeof(bus_make.bp);
+       bus_make.bp.type = KDBUS_ITEM_BLOOM_PARAMETER;
+       bus_make.bp.bloom.size = 64;
+       bus_make.bp.bloom.n_hash = 1;
+
+       snprintf(bus_make.name.str, sizeof(bus_make.name.str),
+                "%u-%s", getuid(), name);
+
+       bus_make.attach[0].type = KDBUS_ITEM_ATTACH_FLAGS_RECV;
+       bus_make.attach[0].size = sizeof(bus_make.attach[0]);
+       bus_make.attach[0].flags = req_meta;
+
+       bus_make.attach[1].type = KDBUS_ITEM_ATTACH_FLAGS_SEND;
+       bus_make.attach[1].size = sizeof(bus_make.attach[0]);
+       bus_make.attach[1].flags = owner_meta;
+
+       bus_make.name.type = KDBUS_ITEM_MAKE_NAME;
+       bus_make.name.size = KDBUS_ITEM_HEADER_SIZE +
+                            strlen(bus_make.name.str) + 1;
+
+       bus_make.cmd.flags = KDBUS_MAKE_ACCESS_WORLD;
+       bus_make.cmd.size = sizeof(bus_make.cmd) +
+                            bus_make.bp.size +
+                            bus_make.attach[0].size +
+                            bus_make.attach[1].size +
+                            bus_make.name.size;
+
+       if (control_fd != -1) {
+               kdbus_printf(
+                       "Creating bus with name >%s< on control fd %d ...\n",
+                       name, control_fd);
+
+               ret = kdbus_cmd_bus_make(control_fd, &bus_make.cmd);
+               if (ret < 0) {
+                       kdbus_printf("--- error when making bus: %d (%m)\n",
+                                    ret);
+                       return ret;
+               }
+       }
+
+       if (ret == 0 && path)
+               *path = strdup(bus_make.name.str);
+
+       return ret;
+}
+
+struct kdbus_conn *
+kdbus_hello(const char *path, uint64_t flags,
+           const struct kdbus_item *item, size_t item_size)
+{
+       struct kdbus_cmd_free cmd_free = {};
+       int fd, ret;
+       struct {
+               struct kdbus_cmd_hello hello;
+
+               struct {
+                       uint64_t size;
+                       uint64_t type;
+                       char str[16];
+               } conn_name;
+
+               uint8_t extra_items[item_size];
+       } h;
+       struct kdbus_conn *conn;
+
+       memset(&h, 0, sizeof(h));
+
+       if (item_size > 0)
+               memcpy(h.extra_items, item, item_size);
+
+       kdbus_printf("-- opening bus connection %s\n", path);
+       fd = open(path, O_RDWR|O_CLOEXEC);
+       if (fd < 0) {
+               kdbus_printf("--- error %d (%m)\n", fd);
+               return NULL;
+       }
+
+       h.hello.flags = flags | KDBUS_HELLO_ACCEPT_FD;
+       h.hello.attach_flags_send = _KDBUS_ATTACH_ALL;
+       h.hello.attach_flags_recv = _KDBUS_ATTACH_ALL;
+       h.conn_name.type = KDBUS_ITEM_CONN_DESCRIPTION;
+       strcpy(h.conn_name.str, "this-is-my-name");
+       h.conn_name.size = KDBUS_ITEM_HEADER_SIZE + strlen(h.conn_name.str) + 1;
+
+       h.hello.size = sizeof(h);
+       h.hello.pool_size = POOL_SIZE;
+
+       ret = kdbus_cmd_hello(fd, (struct kdbus_cmd_hello *) &h.hello);
+       if (ret < 0) {
+               kdbus_printf("--- error when saying hello: %d (%m)\n", ret);
+               return NULL;
+       }
+       kdbus_printf("-- Our peer ID for %s: %llu -- bus uuid: '%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x'\n",
+                    path, (unsigned long long)h.hello.id,
+                    h.hello.id128[0],  h.hello.id128[1],  h.hello.id128[2],
+                    h.hello.id128[3],  h.hello.id128[4],  h.hello.id128[5],
+                    h.hello.id128[6],  h.hello.id128[7],  h.hello.id128[8],
+                    h.hello.id128[9],  h.hello.id128[10], h.hello.id128[11],
+                    h.hello.id128[12], h.hello.id128[13], h.hello.id128[14],
+                    h.hello.id128[15]);
+
+       cmd_free.size = sizeof(cmd_free);
+       cmd_free.offset = h.hello.offset;
+       kdbus_cmd_free(fd, &cmd_free);
+
+       conn = malloc(sizeof(*conn));
+       if (!conn) {
+               kdbus_printf("unable to malloc()!?\n");
+               return NULL;
+       }
+
+       conn->buf = mmap(NULL, POOL_SIZE, PROT_READ, MAP_SHARED, fd, 0);
+       if (conn->buf == MAP_FAILED) {
+               free(conn);
+               close(fd);
+               kdbus_printf("--- error mmap (%m)\n");
+               return NULL;
+       }
+
+       conn->fd = fd;
+       conn->id = h.hello.id;
+       return conn;
+}
+
+struct kdbus_conn *
+kdbus_hello_registrar(const char *path, const char *name,
+                     const struct kdbus_policy_access *access,
+                     size_t num_access, uint64_t flags)
+{
+       struct kdbus_item *item, *items;
+       size_t i, size;
+
+       size = KDBUS_ITEM_SIZE(strlen(name) + 1) +
+               num_access * KDBUS_ITEM_SIZE(sizeof(*access));
+
+       items = alloca(size);
+
+       item = items;
+       item->size = KDBUS_ITEM_HEADER_SIZE + strlen(name) + 1;
+       item->type = KDBUS_ITEM_NAME;
+       strcpy(item->str, name);
+       item = KDBUS_ITEM_NEXT(item);
+
+       for (i = 0; i < num_access; i++) {
+               item->size = KDBUS_ITEM_HEADER_SIZE +
+                            sizeof(struct kdbus_policy_access);
+               item->type = KDBUS_ITEM_POLICY_ACCESS;
+
+               item->policy_access.type = access[i].type;
+               item->policy_access.access = access[i].access;
+               item->policy_access.id = access[i].id;
+
+               item = KDBUS_ITEM_NEXT(item);
+       }
+
+       return kdbus_hello(path, flags, items, size);
+}
+
+struct kdbus_conn *kdbus_hello_activator(const char *path, const char *name,
+                                  const struct kdbus_policy_access *access,
+                                  size_t num_access)
+{
+       return kdbus_hello_registrar(path, name, access, num_access,
+                                    KDBUS_HELLO_ACTIVATOR);
+}
+
+bool kdbus_item_in_message(struct kdbus_msg *msg, uint64_t type)
+{
+       const struct kdbus_item *item;
+
+       KDBUS_ITEM_FOREACH(item, msg, items)
+               if (item->type == type)
+                       return true;
+
+       return false;
+}
+
+int kdbus_bus_creator_info(struct kdbus_conn *conn,
+                          uint64_t flags,
+                          uint64_t *offset)
+{
+       struct kdbus_cmd_info *cmd;
+       size_t size = sizeof(*cmd);
+       int ret;
+
+       cmd = alloca(size);
+       memset(cmd, 0, size);
+       cmd->size = size;
+       cmd->attach_flags = flags;
+
+       ret = kdbus_cmd_bus_creator_info(conn->fd, cmd);
+       if (ret < 0) {
+               kdbus_printf("--- error when requesting info: %d (%m)\n", ret);
+               return ret;
+       }
+
+       if (offset)
+               *offset = cmd->offset;
+       else
+               kdbus_free(conn, cmd->offset);
+
+       return 0;
+}
+
+int kdbus_conn_info(struct kdbus_conn *conn, uint64_t id,
+                   const char *name, uint64_t flags,
+                   uint64_t *offset)
+{
+       struct kdbus_cmd_info *cmd;
+       size_t size = sizeof(*cmd);
+       struct kdbus_info *info;
+       int ret;
+
+       if (name)
+               size += KDBUS_ITEM_HEADER_SIZE + strlen(name) + 1;
+
+       cmd = alloca(size);
+       memset(cmd, 0, size);
+       cmd->size = size;
+       cmd->attach_flags = flags;
+
+       if (name) {
+               cmd->items[0].size = KDBUS_ITEM_HEADER_SIZE + strlen(name) + 1;
+               cmd->items[0].type = KDBUS_ITEM_NAME;
+               strcpy(cmd->items[0].str, name);
+       } else {
+               cmd->id = id;
+       }
+
+       ret = kdbus_cmd_conn_info(conn->fd, cmd);
+       if (ret < 0) {
+               kdbus_printf("--- error when requesting info: %d (%m)\n", ret);
+               return ret;
+       }
+
+       info = (struct kdbus_info *) (conn->buf + cmd->offset);
+       if (info->size != cmd->info_size) {
+               kdbus_printf("%s(): size mismatch: %d != %d\n", __func__,
+                               (int) info->size, (int) cmd->info_size);
+               return -EIO;
+       }
+
+       if (offset)
+               *offset = cmd->offset;
+       else
+               kdbus_free(conn, cmd->offset);
+
+       return 0;
+}
+
+void kdbus_conn_free(struct kdbus_conn *conn)
+{
+       if (!conn)
+               return;
+
+       if (conn->buf)
+               munmap(conn->buf, POOL_SIZE);
+
+       if (conn->fd >= 0)
+               close(conn->fd);
+
+       free(conn);
+}
+
+int sys_memfd_create(const char *name, __u64 size)
+{
+       int ret, fd;
+
+       ret = syscall(__NR_memfd_create, name, MFD_ALLOW_SEALING);
+       if (ret < 0)
+               return ret;
+
+       fd = ret;
+
+       ret = ftruncate(fd, size);
+       if (ret < 0) {
+               close(fd);
+               return ret;
+       }
+
+       return fd;
+}
+
+int sys_memfd_seal_set(int fd)
+{
+       return fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK |
+                        F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_SEAL);
+}
+
+off_t sys_memfd_get_size(int fd, off_t *size)
+{
+       struct stat stat;
+       int ret;
+
+       ret = fstat(fd, &stat);
+       if (ret < 0) {
+               kdbus_printf("stat() failed: %m\n");
+               return ret;
+       }
+
+       *size = stat.st_size;
+       return 0;
+}
+
+static int __kdbus_msg_send(const struct kdbus_conn *conn,
+                           const char *name,
+                           uint64_t cookie,
+                           uint64_t flags,
+                           uint64_t timeout,
+                           int64_t priority,
+                           uint64_t dst_id,
+                           uint64_t cmd_flags,
+                           int cancel_fd,
+                           int fds_count,
+                           int fds[])
+{
+       struct kdbus_cmd_send *cmd;
+       struct kdbus_msg *msg;
+       const char ref1[1024 * 128 + 3] = "0123456789_0";
+       const char ref2[] = "0123456789_1";
+       struct kdbus_item *item;
+       struct timespec now;
+       uint64_t size;
+       int memfd = -1;
+       int ret;
+
+       size = sizeof(*msg);
+       size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_vec));
+       size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_vec));
+       size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_vec));
+       size += fds_count > 0 ? KDBUS_ITEM_SIZE(sizeof(int) * fds_count) : 0;
+
+       if (dst_id == KDBUS_DST_ID_BROADCAST)
+               size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_bloom_filter)) + 64;
+       else {
+               memfd = sys_memfd_create("my-name-is-nice", 1024 * 1024);
+               if (memfd < 0) {
+                       kdbus_printf("failed to create memfd: %m\n");
+                       return memfd;
+               }
+
+               if (write(memfd, "kdbus memfd 1234567", 19) != 19) {
+                       ret = -errno;
+                       kdbus_printf("writing to memfd failed: %m\n");
+                       return ret;
+               }
+
+               ret = sys_memfd_seal_set(memfd);
+               if (ret < 0) {
+                       ret = -errno;
+                       kdbus_printf("memfd sealing failed: %m\n");
+                       return ret;
+               }
+
+               size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_memfd));
+       }
+
+       if (name)
+               size += KDBUS_ITEM_SIZE(strlen(name) + 1);
+
+       msg = malloc(size);
+       if (!msg) {
+               ret = -errno;
+               kdbus_printf("unable to malloc()!?\n");
+               return ret;
+       }
+
+       if (dst_id == KDBUS_DST_ID_BROADCAST)
+               flags |= KDBUS_MSG_SIGNAL;
+
+       memset(msg, 0, size);
+       msg->flags = flags;
+       msg->priority = priority;
+       msg->size = size;
+       msg->src_id = conn->id;
+       msg->dst_id = name ? 0 : dst_id;
+       msg->cookie = cookie;
+       msg->payload_type = KDBUS_PAYLOAD_DBUS;
+
+       if (timeout) {
+               ret = clock_gettime(CLOCK_MONOTONIC_COARSE, &now);
+               if (ret < 0)
+                       return ret;
+
+               msg->timeout_ns = now.tv_sec * 1000000000ULL +
+                                 now.tv_nsec + timeout;
+       }
+
+       item = msg->items;
+
+       if (name) {
+               item->type = KDBUS_ITEM_DST_NAME;
+               item->size = KDBUS_ITEM_HEADER_SIZE + strlen(name) + 1;
+               strcpy(item->str, name);
+               item = KDBUS_ITEM_NEXT(item);
+       }
+
+       item->type = KDBUS_ITEM_PAYLOAD_VEC;
+       item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(struct kdbus_vec);
+       item->vec.address = (uintptr_t)&ref1;
+       item->vec.size = sizeof(ref1);
+       item = KDBUS_ITEM_NEXT(item);
+
+       /* data padding for ref1 */
+       item->type = KDBUS_ITEM_PAYLOAD_VEC;
+       item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(struct kdbus_vec);
+       item->vec.address = (uintptr_t)NULL;
+       item->vec.size =  KDBUS_ALIGN8(sizeof(ref1)) - sizeof(ref1);
+       item = KDBUS_ITEM_NEXT(item);
+
+       item->type = KDBUS_ITEM_PAYLOAD_VEC;
+       item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(struct kdbus_vec);
+       item->vec.address = (uintptr_t)&ref2;
+       item->vec.size = sizeof(ref2);
+       item = KDBUS_ITEM_NEXT(item);
+
+       if (dst_id == KDBUS_DST_ID_BROADCAST) {
+               item->type = KDBUS_ITEM_BLOOM_FILTER;
+               item->size = KDBUS_ITEM_SIZE(sizeof(struct kdbus_bloom_filter)) + 64;
+               item->bloom_filter.generation = 0;
+       } else {
+               item->type = KDBUS_ITEM_PAYLOAD_MEMFD;
+               item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(struct kdbus_memfd);
+               item->memfd.size = 16;
+               item->memfd.fd = memfd;
+       }
+       item = KDBUS_ITEM_NEXT(item);
+
+       if (fds_count > 0) {
+               item->type = KDBUS_ITEM_FDS;
+               item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(int) * fds_count;
+               memcpy(&item->fds, fds, sizeof(int) * fds_count);
+               item = KDBUS_ITEM_NEXT(item);
+       }
+
+       size = sizeof(*cmd);
+       if (cancel_fd != -1)
+               size += KDBUS_ITEM_SIZE(sizeof(cancel_fd));
+
+       cmd = malloc(size);
+       if (!cmd) {
+               ret = -errno;
+               kdbus_printf("unable to malloc(%ld)!?\n", (long)size);
+               return ret;
+       }
+       cmd->size = size;
+       cmd->flags = cmd_flags;
+       cmd->msg_address = (uintptr_t)msg;
+
+       item = cmd->items;
+
+       if (cancel_fd != -1) {
+               item->type = KDBUS_ITEM_CANCEL_FD;
+               item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(cancel_fd);
+               item->fds[0] = cancel_fd;
+               item = KDBUS_ITEM_NEXT(item);
+       }
+
+
+       ret = kdbus_cmd_send(conn->fd, cmd);
+       if (memfd >= 0)
+               close(memfd);
+
+       if (ret < 0) {
+               kdbus_printf("error sending message: %d (%m)\n", ret);
+               return ret;
+       }
+
+       if (cmd_flags & KDBUS_SEND_SYNC_REPLY) {
+               struct kdbus_msg *reply;
+
+               kdbus_printf("SYNC REPLY @offset %llu:\n", cmd->reply.offset);
+               reply = (struct kdbus_msg *)(conn->buf + cmd->reply.offset);
+               kdbus_msg_dump(conn, reply);
+
+               kdbus_msg_free(reply);
+
+               ret = kdbus_free(conn, cmd->reply.offset);
+               if (ret < 0)
+                       return ret;
+       }
+
+       free(msg);
+       free(cmd);
+
+       return 0;
+}
+
+int kdbus_msg_send(const struct kdbus_conn *conn, const char *name,
+                  uint64_t cookie, uint64_t flags, uint64_t timeout,
+                  int64_t priority, uint64_t dst_id, int fds_count, int fds[])
+{
+       return __kdbus_msg_send(conn, name, cookie, flags, timeout, priority,
+                               dst_id, 0, -1, fds_count, fds);
+}
+
+int kdbus_msg_send_sync(const struct kdbus_conn *conn, const char *name,
+                       uint64_t cookie, uint64_t flags, uint64_t timeout,
+                       int64_t priority, uint64_t dst_id, int cancel_fd)
+{
+       return __kdbus_msg_send(conn, name, cookie, flags, timeout, priority,
+                               dst_id, KDBUS_SEND_SYNC_REPLY, cancel_fd,
+                               0, NULL);
+}
+
+int kdbus_msg_send_reply(const struct kdbus_conn *conn,
+                        uint64_t reply_cookie,
+                        uint64_t dst_id)
+{
+       struct kdbus_cmd_send cmd = {};
+       struct kdbus_msg *msg;
+       const char ref1[1024 * 128 + 3] = "0123456789_0";
+       struct kdbus_item *item;
+       uint64_t size;
+       int ret;
+
+       size = sizeof(struct kdbus_msg);
+       size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_vec));
+
+       msg = malloc(size);
+       if (!msg) {
+               kdbus_printf("unable to malloc()!?\n");
+               return -ENOMEM;
+       }
+
+       memset(msg, 0, size);
+       msg->size = size;
+       msg->src_id = conn->id;
+       msg->dst_id = dst_id;
+       msg->cookie_reply = reply_cookie;
+       msg->payload_type = KDBUS_PAYLOAD_DBUS;
+
+       item = msg->items;
+
+       item->type = KDBUS_ITEM_PAYLOAD_VEC;
+       item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(struct kdbus_vec);
+       item->vec.address = (uintptr_t)&ref1;
+       item->vec.size = sizeof(ref1);
+       item = KDBUS_ITEM_NEXT(item);
+
+       cmd.size = sizeof(cmd);
+       cmd.msg_address = (uintptr_t)msg;
+
+       ret = kdbus_cmd_send(conn->fd, &cmd);
+       if (ret < 0)
+               kdbus_printf("error sending message: %d (%m)\n", ret);
+
+       free(msg);
+
+       return ret;
+}
+
+static char *msg_id(uint64_t id, char *buf)
+{
+       if (id == 0)
+               return "KERNEL";
+       if (id == ~0ULL)
+               return "BROADCAST";
+       sprintf(buf, "%llu", (unsigned long long)id);
+       return buf;
+}
+
+int kdbus_msg_dump(const struct kdbus_conn *conn, const struct kdbus_msg *msg)
+{
+       const struct kdbus_item *item = msg->items;
+       char buf_src[32];
+       char buf_dst[32];
+       uint64_t timeout = 0;
+       uint64_t cookie_reply = 0;
+       int ret = 0;
+
+       if (msg->flags & KDBUS_MSG_EXPECT_REPLY)
+               timeout = msg->timeout_ns;
+       else
+               cookie_reply = msg->cookie_reply;
+
+       kdbus_printf("MESSAGE: %s (%llu bytes) flags=0x%08llx, %s â†’ %s, "
+                    "cookie=%llu, timeout=%llu cookie_reply=%llu priority=%lli\n",
+               enum_PAYLOAD(msg->payload_type), (unsigned long long)msg->size,
+               (unsigned long long)msg->flags,
+               msg_id(msg->src_id, buf_src), msg_id(msg->dst_id, buf_dst),
+               (unsigned long long)msg->cookie, (unsigned long long)timeout,
+               (unsigned long long)cookie_reply, (long long)msg->priority);
+
+       KDBUS_ITEM_FOREACH(item, msg, items) {
+               if (item->size < KDBUS_ITEM_HEADER_SIZE) {
+                       kdbus_printf("  +%s (%llu bytes) invalid data record\n",
+                                    enum_MSG(item->type), item->size);
+                       ret = -EINVAL;
+                       break;
+               }
+
+               switch (item->type) {
+               case KDBUS_ITEM_PAYLOAD_OFF: {
+                       char *s;
+
+                       if (item->vec.offset == ~0ULL)
+                               s = "[\\0-bytes]";
+                       else
+                               s = (char *)msg + item->vec.offset;
+
+                       kdbus_printf("  +%s (%llu bytes) off=%llu size=%llu '%s'\n",
+                              enum_MSG(item->type), item->size,
+                              (unsigned long long)item->vec.offset,
+                              (unsigned long long)item->vec.size, s);
+                       break;
+               }
+
+               case KDBUS_ITEM_FDS: {
+                       int i, n = (item->size - KDBUS_ITEM_HEADER_SIZE) /
+                                       sizeof(int);
+
+                       kdbus_printf("  +%s (%llu bytes, %d fds)\n",
+                              enum_MSG(item->type), item->size, n);
+
+                       for (i = 0; i < n; i++)
+                               kdbus_printf("    fd[%d] = %d\n",
+                                            i, item->fds[i]);
+
+                       break;
+               }
+
+               case KDBUS_ITEM_PAYLOAD_MEMFD: {
+                       char *buf;
+                       off_t size;
+
+                       buf = mmap(NULL, item->memfd.size, PROT_READ,
+                                  MAP_PRIVATE, item->memfd.fd, 0);
+                       if (buf == MAP_FAILED) {
+                               kdbus_printf("mmap() fd=%i size=%llu failed: %m\n",
+                                            item->memfd.fd, item->memfd.size);
+                               break;
+                       }
+
+                       if (sys_memfd_get_size(item->memfd.fd, &size) < 0) {
+                               kdbus_printf("KDBUS_CMD_MEMFD_SIZE_GET failed: %m\n");
+                               break;
+                       }
+
+                       kdbus_printf("  +%s (%llu bytes) fd=%i size=%llu filesize=%llu '%s'\n",
+                              enum_MSG(item->type), item->size, item->memfd.fd,
+                              (unsigned long long)item->memfd.size,
+                              (unsigned long long)size, buf);
+                       munmap(buf, item->memfd.size);
+                       break;
+               }
+
+               case KDBUS_ITEM_CREDS:
+                       kdbus_printf("  +%s (%llu bytes) uid=%lld, euid=%lld, suid=%lld, fsuid=%lld, "
+                                                       "gid=%lld, egid=%lld, sgid=%lld, fsgid=%lld\n",
+                               enum_MSG(item->type), item->size,
+                               item->creds.uid, item->creds.euid,
+                               item->creds.suid, item->creds.fsuid,
+                               item->creds.gid, item->creds.egid,
+                               item->creds.sgid, item->creds.fsgid);
+                       break;
+
+               case KDBUS_ITEM_PIDS:
+                       kdbus_printf("  +%s (%llu bytes) pid=%lld, tid=%lld, ppid=%lld\n",
+                               enum_MSG(item->type), item->size,
+                               item->pids.pid, item->pids.tid,
+                               item->pids.ppid);
+                       break;
+
+               case KDBUS_ITEM_AUXGROUPS: {
+                       int i, n;
+
+                       kdbus_printf("  +%s (%llu bytes)\n",
+                                    enum_MSG(item->type), item->size);
+                       n = (item->size - KDBUS_ITEM_HEADER_SIZE) /
+                               sizeof(uint64_t);
+
+                       for (i = 0; i < n; i++)
+                               kdbus_printf("    gid[%d] = %lld\n",
+                                            i, item->data64[i]);
+                       break;
+               }
+
+               case KDBUS_ITEM_NAME:
+               case KDBUS_ITEM_PID_COMM:
+               case KDBUS_ITEM_TID_COMM:
+               case KDBUS_ITEM_EXE:
+               case KDBUS_ITEM_CGROUP:
+               case KDBUS_ITEM_SECLABEL:
+               case KDBUS_ITEM_DST_NAME:
+               case KDBUS_ITEM_CONN_DESCRIPTION:
+                       kdbus_printf("  +%s (%llu bytes) '%s' (%zu)\n",
+                                    enum_MSG(item->type), item->size,
+                                    item->str, strlen(item->str));
+                       break;
+
+               case KDBUS_ITEM_OWNED_NAME: {
+                       kdbus_printf("  +%s (%llu bytes) '%s' (%zu) flags=0x%08llx\n",
+                                    enum_MSG(item->type), item->size,
+                                    item->name.name, strlen(item->name.name),
+                                    item->name.flags);
+                       break;
+               }
+
+               case KDBUS_ITEM_CMDLINE: {
+                       size_t size = item->size - KDBUS_ITEM_HEADER_SIZE;
+                       const char *str = item->str;
+                       int count = 0;
+
+                       kdbus_printf("  +%s (%llu bytes) ",
+                                    enum_MSG(item->type), item->size);
+                       while (size) {
+                               kdbus_printf("'%s' ", str);
+                               size -= strlen(str) + 1;
+                               str += strlen(str) + 1;
+                               count++;
+                       }
+
+                       kdbus_printf("(%d string%s)\n",
+                                    count, (count == 1) ? "" : "s");
+                       break;
+               }
+
+               case KDBUS_ITEM_AUDIT:
+                       kdbus_printf("  +%s (%llu bytes) loginuid=%u sessionid=%u\n",
+                              enum_MSG(item->type), item->size,
+                              item->audit.loginuid, item->audit.sessionid);
+                       break;
+
+               case KDBUS_ITEM_CAPS: {
+                       const uint32_t *cap;
+                       int n, i;
+
+                       kdbus_printf("  +%s (%llu bytes) len=%llu bytes, last_cap %d\n",
+                                    enum_MSG(item->type), item->size,
+                                    (unsigned long long)item->size -
+                                       KDBUS_ITEM_HEADER_SIZE,
+                                    (int) item->caps.last_cap);
+
+                       cap = item->caps.caps;
+                       n = (item->size - offsetof(struct kdbus_item, caps.caps))
+                               / 4 / sizeof(uint32_t);
+
+                       kdbus_printf("    CapInh=");
+                       for (i = 0; i < n; i++)
+                               kdbus_printf("%08x", cap[(0 * n) + (n - i - 1)]);
+
+                       kdbus_printf(" CapPrm=");
+                       for (i = 0; i < n; i++)
+                               kdbus_printf("%08x", cap[(1 * n) + (n - i - 1)]);
+
+                       kdbus_printf(" CapEff=");
+                       for (i = 0; i < n; i++)
+                               kdbus_printf("%08x", cap[(2 * n) + (n - i - 1)]);
+
+                       kdbus_printf(" CapBnd=");
+                       for (i = 0; i < n; i++)
+                               kdbus_printf("%08x", cap[(3 * n) + (n - i - 1)]);
+                       kdbus_printf("\n");
+                       break;
+               }
+
+               case KDBUS_ITEM_TIMESTAMP:
+                       kdbus_printf("  +%s (%llu bytes) seq=%llu realtime=%lluns monotonic=%lluns\n",
+                              enum_MSG(item->type), item->size,
+                              (unsigned long long)item->timestamp.seqnum,
+                              (unsigned long long)item->timestamp.realtime_ns,
+                              (unsigned long long)item->timestamp.monotonic_ns);
+                       break;
+
+               case KDBUS_ITEM_REPLY_TIMEOUT:
+                       kdbus_printf("  +%s (%llu bytes) cookie=%llu\n",
+                              enum_MSG(item->type), item->size,
+                              msg->cookie_reply);
+                       break;
+
+               case KDBUS_ITEM_NAME_ADD:
+               case KDBUS_ITEM_NAME_REMOVE:
+               case KDBUS_ITEM_NAME_CHANGE:
+                       kdbus_printf("  +%s (%llu bytes) '%s', old id=%lld, now id=%lld, old_flags=0x%llx new_flags=0x%llx\n",
+                               enum_MSG(item->type),
+                               (unsigned long long) item->size,
+                               item->name_change.name,
+                               item->name_change.old_id.id,
+                               item->name_change.new_id.id,
+                               item->name_change.old_id.flags,
+                               item->name_change.new_id.flags);
+                       break;
+
+               case KDBUS_ITEM_ID_ADD:
+               case KDBUS_ITEM_ID_REMOVE:
+                       kdbus_printf("  +%s (%llu bytes) id=%llu flags=%llu\n",
+                              enum_MSG(item->type),
+                              (unsigned long long) item->size,
+                              (unsigned long long) item->id_change.id,
+                              (unsigned long long) item->id_change.flags);
+                       break;
+
+               default:
+                       kdbus_printf("  +%s (%llu bytes)\n",
+                                    enum_MSG(item->type), item->size);
+                       break;
+               }
+       }
+
+       if ((char *)item - ((char *)msg + msg->size) >= 8) {
+               kdbus_printf("invalid padding at end of message\n");
+               ret = -EINVAL;
+       }
+
+       kdbus_printf("\n");
+
+       return ret;
+}
+
+void kdbus_msg_free(struct kdbus_msg *msg)
+{
+       const struct kdbus_item *item;
+       int nfds, i;
+
+       if (!msg)
+               return;
+
+       KDBUS_ITEM_FOREACH(item, msg, items) {
+               switch (item->type) {
+               /* close all memfds */
+               case KDBUS_ITEM_PAYLOAD_MEMFD:
+                       close(item->memfd.fd);
+                       break;
+               case KDBUS_ITEM_FDS:
+                       nfds = (item->size - KDBUS_ITEM_HEADER_SIZE) /
+                               sizeof(int);
+
+                       for (i = 0; i < nfds; i++)
+                               close(item->fds[i]);
+
+                       break;
+               }
+       }
+}
+
+int kdbus_msg_recv(struct kdbus_conn *conn,
+                  struct kdbus_msg **msg_out,
+                  uint64_t *offset)
+{
+       struct kdbus_cmd_recv recv = { .size = sizeof(recv) };
+       struct kdbus_msg *msg;
+       int ret;
+
+       ret = kdbus_cmd_recv(conn->fd, &recv);
+       if (ret < 0)
+               return ret;
+
+       msg = (struct kdbus_msg *)(conn->buf + recv.msg.offset);
+       ret = kdbus_msg_dump(conn, msg);
+       if (ret < 0) {
+               kdbus_msg_free(msg);
+               return ret;
+       }
+
+       if (msg_out) {
+               *msg_out = msg;
+
+               if (offset)
+                       *offset = recv.msg.offset;
+       } else {
+               kdbus_msg_free(msg);
+
+               ret = kdbus_free(conn, recv.msg.offset);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return 0;
+}
+
+/*
+ * Returns: 0 on success, negative errno on failure.
+ *
+ * We must return -ETIMEDOUT, -ECONNREST, -EAGAIN and other errors.
+ * We must return the result of kdbus_msg_recv()
+ */
+int kdbus_msg_recv_poll(struct kdbus_conn *conn,
+                       int timeout_ms,
+                       struct kdbus_msg **msg_out,
+                       uint64_t *offset)
+{
+       int ret;
+
+       do {
+               struct timeval before, after, diff;
+               struct pollfd fd;
+
+               fd.fd = conn->fd;
+               fd.events = POLLIN | POLLPRI | POLLHUP;
+               fd.revents = 0;
+
+               gettimeofday(&before, NULL);
+               ret = poll(&fd, 1, timeout_ms);
+               gettimeofday(&after, NULL);
+
+               if (ret == 0) {
+                       ret = -ETIMEDOUT;
+                       break;
+               }
+
+               if (ret > 0) {
+                       if (fd.revents & POLLIN)
+                               ret = kdbus_msg_recv(conn, msg_out, offset);
+
+                       if (fd.revents & (POLLHUP | POLLERR))
+                               ret = -ECONNRESET;
+               }
+
+               if (ret == 0 || ret != -EAGAIN)
+                       break;
+
+               timersub(&after, &before, &diff);
+               timeout_ms -= diff.tv_sec * 1000UL +
+                             diff.tv_usec / 1000UL;
+       } while (timeout_ms > 0);
+
+       return ret;
+}
+
+int kdbus_free(const struct kdbus_conn *conn, uint64_t offset)
+{
+       struct kdbus_cmd_free cmd_free = {};
+       int ret;
+
+       cmd_free.size = sizeof(cmd_free);
+       cmd_free.offset = offset;
+       cmd_free.flags = 0;
+
+       ret = kdbus_cmd_free(conn->fd, &cmd_free);
+       if (ret < 0) {
+               kdbus_printf("KDBUS_CMD_FREE failed: %d (%m)\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+int kdbus_name_acquire(struct kdbus_conn *conn,
+                      const char *name, uint64_t *flags)
+{
+       struct kdbus_cmd *cmd_name;
+       size_t name_len = strlen(name) + 1;
+       uint64_t size = sizeof(*cmd_name) + KDBUS_ITEM_SIZE(name_len);
+       struct kdbus_item *item;
+       int ret;
+
+       cmd_name = alloca(size);
+
+       memset(cmd_name, 0, size);
+
+       item = cmd_name->items;
+       item->size = KDBUS_ITEM_HEADER_SIZE + name_len;
+       item->type = KDBUS_ITEM_NAME;
+       strcpy(item->str, name);
+
+       cmd_name->size = size;
+       if (flags)
+               cmd_name->flags = *flags;
+
+       ret = kdbus_cmd_name_acquire(conn->fd, cmd_name);
+       if (ret < 0) {
+               kdbus_printf("error aquiring name: %s\n", strerror(-ret));
+               return ret;
+       }
+
+       kdbus_printf("%s(): flags after call: 0x%llx\n", __func__,
+                    cmd_name->return_flags);
+
+       if (flags)
+               *flags = cmd_name->return_flags;
+
+       return 0;
+}
+
+int kdbus_name_release(struct kdbus_conn *conn, const char *name)
+{
+       struct kdbus_cmd *cmd_name;
+       size_t name_len = strlen(name) + 1;
+       uint64_t size = sizeof(*cmd_name) + KDBUS_ITEM_SIZE(name_len);
+       struct kdbus_item *item;
+       int ret;
+
+       cmd_name = alloca(size);
+
+       memset(cmd_name, 0, size);
+
+       item = cmd_name->items;
+       item->size = KDBUS_ITEM_HEADER_SIZE + name_len;
+       item->type = KDBUS_ITEM_NAME;
+       strcpy(item->str, name);
+
+       cmd_name->size = size;
+
+       kdbus_printf("conn %lld giving up name '%s'\n",
+                    (unsigned long long) conn->id, name);
+
+       ret = kdbus_cmd_name_release(conn->fd, cmd_name);
+       if (ret < 0) {
+               kdbus_printf("error releasing name: %s\n", strerror(-ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+int kdbus_list(struct kdbus_conn *conn, uint64_t flags)
+{
+       struct kdbus_cmd_list cmd_list = {};
+       struct kdbus_info *list, *name;
+       int ret;
+
+       cmd_list.size = sizeof(cmd_list);
+       cmd_list.flags = flags;
+
+       ret = kdbus_cmd_list(conn->fd, &cmd_list);
+       if (ret < 0) {
+               kdbus_printf("error listing names: %d (%m)\n", ret);
+               return ret;
+       }
+
+       kdbus_printf("REGISTRY:\n");
+       list = (struct kdbus_info *)(conn->buf + cmd_list.offset);
+
+       KDBUS_FOREACH(name, list, cmd_list.list_size) {
+               uint64_t flags = 0;
+               struct kdbus_item *item;
+               const char *n = "MISSING-NAME";
+
+               if (name->size == sizeof(struct kdbus_cmd))
+                       continue;
+
+               KDBUS_ITEM_FOREACH(item, name, items)
+                       if (item->type == KDBUS_ITEM_OWNED_NAME) {
+                               n = item->name.name;
+                               flags = item->name.flags;
+                       }
+
+               kdbus_printf("%8llu flags=0x%08llx conn=0x%08llx '%s'\n",
+                            name->id, (unsigned long long) flags,
+                            name->flags, n);
+       }
+       kdbus_printf("\n");
+
+       ret = kdbus_free(conn, cmd_list.offset);
+
+       return ret;
+}
+
+int kdbus_conn_update_attach_flags(struct kdbus_conn *conn,
+                                  uint64_t attach_flags_send,
+                                  uint64_t attach_flags_recv)
+{
+       int ret;
+       size_t size;
+       struct kdbus_cmd *update;
+       struct kdbus_item *item;
+
+       size = sizeof(struct kdbus_cmd);
+       size += KDBUS_ITEM_SIZE(sizeof(uint64_t)) * 2;
+
+       update = malloc(size);
+       if (!update) {
+               kdbus_printf("error malloc: %m\n");
+               return -ENOMEM;
+       }
+
+       memset(update, 0, size);
+       update->size = size;
+
+       item = update->items;
+
+       item->type = KDBUS_ITEM_ATTACH_FLAGS_SEND;
+       item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(uint64_t);
+       item->data64[0] = attach_flags_send;
+       item = KDBUS_ITEM_NEXT(item);
+
+       item->type = KDBUS_ITEM_ATTACH_FLAGS_RECV;
+       item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(uint64_t);
+       item->data64[0] = attach_flags_recv;
+       item = KDBUS_ITEM_NEXT(item);
+
+       ret = kdbus_cmd_update(conn->fd, update);
+       if (ret < 0)
+               kdbus_printf("error conn update: %d (%m)\n", ret);
+
+       free(update);
+
+       return ret;
+}
+
+int kdbus_conn_update_policy(struct kdbus_conn *conn, const char *name,
+                            const struct kdbus_policy_access *access,
+                            size_t num_access)
+{
+       struct kdbus_cmd *update;
+       struct kdbus_item *item;
+       size_t i, size;
+       int ret;
+
+       size = sizeof(struct kdbus_cmd);
+       size += KDBUS_ITEM_SIZE(strlen(name) + 1);
+       size += num_access * KDBUS_ITEM_SIZE(sizeof(struct kdbus_policy_access));
+
+       update = malloc(size);
+       if (!update) {
+               kdbus_printf("error malloc: %m\n");
+               return -ENOMEM;
+       }
+
+       memset(update, 0, size);
+       update->size = size;
+
+       item = update->items;
+
+       item->type = KDBUS_ITEM_NAME;
+       item->size = KDBUS_ITEM_HEADER_SIZE + strlen(name) + 1;
+       strcpy(item->str, name);
+       item = KDBUS_ITEM_NEXT(item);
+
+       for (i = 0; i < num_access; i++) {
+               item->size = KDBUS_ITEM_HEADER_SIZE +
+                            sizeof(struct kdbus_policy_access);
+               item->type = KDBUS_ITEM_POLICY_ACCESS;
+
+               item->policy_access.type = access[i].type;
+               item->policy_access.access = access[i].access;
+               item->policy_access.id = access[i].id;
+
+               item = KDBUS_ITEM_NEXT(item);
+       }
+
+       ret = kdbus_cmd_update(conn->fd, update);
+       if (ret < 0)
+               kdbus_printf("error conn update: %d (%m)\n", ret);
+
+       free(update);
+
+       return ret;
+}
+
+int kdbus_add_match_id(struct kdbus_conn *conn, uint64_t cookie,
+                      uint64_t type, uint64_t id)
+{
+       struct {
+               struct kdbus_cmd_match cmd;
+               struct {
+                       uint64_t size;
+                       uint64_t type;
+                       struct kdbus_notify_id_change chg;
+               } item;
+       } buf;
+       int ret;
+
+       memset(&buf, 0, sizeof(buf));
+
+       buf.cmd.size = sizeof(buf);
+       buf.cmd.cookie = cookie;
+       buf.item.size = sizeof(buf.item);
+       buf.item.type = type;
+       buf.item.chg.id = id;
+
+       ret = kdbus_cmd_match_add(conn->fd, &buf.cmd);
+       if (ret < 0)
+               kdbus_printf("--- error adding conn match: %d (%m)\n", ret);
+
+       return ret;
+}
+
+int kdbus_add_match_empty(struct kdbus_conn *conn)
+{
+       struct {
+               struct kdbus_cmd_match cmd;
+               struct kdbus_item item;
+       } buf;
+       int ret;
+
+       memset(&buf, 0, sizeof(buf));
+
+       buf.item.size = sizeof(uint64_t) * 3;
+       buf.item.type = KDBUS_ITEM_ID;
+       buf.item.id = KDBUS_MATCH_ID_ANY;
+
+       buf.cmd.size = sizeof(buf.cmd) + buf.item.size;
+
+       ret = kdbus_cmd_match_add(conn->fd, &buf.cmd);
+       if (ret < 0)
+               kdbus_printf("--- error adding conn match: %d (%m)\n", ret);
+
+       return ret;
+}
+
+static int all_ids_are_mapped(const char *path)
+{
+       int ret;
+       FILE *file;
+       uint32_t inside_id, length;
+
+       file = fopen(path, "r");
+       if (!file) {
+               ret = -errno;
+               kdbus_printf("error fopen() %s: %d (%m)\n",
+                            path, ret);
+               return ret;
+       }
+
+       ret = fscanf(file, "%u\t%*u\t%u", &inside_id, &length);
+       if (ret != 2) {
+               if (ferror(file))
+                       ret = -errno;
+               else
+                       ret = -EIO;
+
+               kdbus_printf("--- error fscanf(): %d\n", ret);
+               fclose(file);
+               return ret;
+       }
+
+       fclose(file);
+
+       /*
+        * If length is 4294967295 which means the invalid uid
+        * (uid_t) -1 then we are able to map all uid/gids
+        */
+       if (inside_id == 0 && length == (uid_t) -1)
+               return 1;
+
+       return 0;
+}
+
+int all_uids_gids_are_mapped()
+{
+       int ret;
+
+       ret = all_ids_are_mapped("/proc/self/uid_map");
+       if (ret <= 0) {
+               kdbus_printf("--- error not all uids are mapped\n");
+               return 0;
+       }
+
+       ret = all_ids_are_mapped("/proc/self/gid_map");
+       if (ret <= 0) {
+               kdbus_printf("--- error not all gids are mapped\n");
+               return 0;
+       }
+
+       return 1;
+}
+
+int drop_privileges(uid_t uid, gid_t gid)
+{
+       int ret;
+
+       ret = setgroups(0, NULL);
+       if (ret < 0) {
+               ret = -errno;
+               kdbus_printf("error setgroups: %d (%m)\n", ret);
+               return ret;
+       }
+
+       ret = setresgid(gid, gid, gid);
+       if (ret < 0) {
+               ret = -errno;
+               kdbus_printf("error setresgid: %d (%m)\n", ret);
+               return ret;
+       }
+
+       ret = setresuid(uid, uid, uid);
+       if (ret < 0) {
+               ret = -errno;
+               kdbus_printf("error setresuid: %d (%m)\n", ret);
+               return ret;
+       }
+
+       return ret;
+}
+
+uint64_t now(clockid_t clock)
+{
+       struct timespec spec;
+
+       clock_gettime(clock, &spec);
+       return spec.tv_sec * 1000ULL * 1000ULL * 1000ULL + spec.tv_nsec;
+}
+
+char *unique_name(const char *prefix)
+{
+       unsigned int i;
+       uint64_t u_now;
+       char n[17];
+       char *str;
+       int r;
+
+       /*
+        * This returns a random string which is guaranteed to be
+        * globally unique across all calls to unique_name(). We
+        * compose the string as:
+        *   <prefix>-<random>-<time>
+        * With:
+        *   <prefix>: string provided by the caller
+        *   <random>: a random alpha string of 16 characters
+        *   <time>: the current time in micro-seconds since last boot
+        *
+        * The <random> part makes the string always look vastly different,
+        * the <time> part makes sure no two calls return the same string.
+        */
+
+       u_now = now(CLOCK_MONOTONIC);
+
+       for (i = 0; i < sizeof(n) - 1; ++i)
+               n[i] = 'a' + (rand() % ('z' - 'a'));
+       n[sizeof(n) - 1] = 0;
+
+       r = asprintf(&str, "%s-%s-%" PRIu64, prefix, n, u_now);
+       if (r < 0)
+               return NULL;
+
+       return str;
+}
+
+static int do_userns_map_id(pid_t pid,
+                           const char *map_file,
+                           const char *map_id)
+{
+       int ret;
+       int fd;
+       char *map;
+       unsigned int i;
+
+       map = strndupa(map_id, strlen(map_id));
+       if (!map) {
+               ret = -errno;
+               kdbus_printf("error strndupa %s: %d (%m)\n",
+                       map_file, ret);
+               return ret;
+       }
+
+       for (i = 0; i < strlen(map); i++)
+               if (map[i] == ',')
+                       map[i] = '\n';
+
+       fd = open(map_file, O_RDWR);
+       if (fd < 0) {
+               ret = -errno;
+               kdbus_printf("error open %s: %d (%m)\n",
+                       map_file, ret);
+               return ret;
+       }
+
+       ret = write(fd, map, strlen(map));
+       if (ret < 0) {
+               ret = -errno;
+               kdbus_printf("error write to %s: %d (%m)\n",
+                            map_file, ret);
+               goto out;
+       }
+
+       ret = 0;
+
+out:
+       close(fd);
+       return ret;
+}
+
+int userns_map_uid_gid(pid_t pid,
+                      const char *map_uid,
+                      const char *map_gid)
+{
+       int fd, ret;
+       char file_id[128] = {'\0'};
+
+       snprintf(file_id, sizeof(file_id), "/proc/%ld/uid_map",
+                (long) pid);
+
+       ret = do_userns_map_id(pid, file_id, map_uid);
+       if (ret < 0)
+               return ret;
+
+       snprintf(file_id, sizeof(file_id), "/proc/%ld/setgroups",
+                (long) pid);
+
+       fd = open(file_id, O_WRONLY);
+       if (fd >= 0) {
+               write(fd, "deny\n", 5);
+               close(fd);
+       }
+
+       snprintf(file_id, sizeof(file_id), "/proc/%ld/gid_map",
+                (long) pid);
+
+       return do_userns_map_id(pid, file_id, map_gid);
+}
+
+static int do_cap_get_flag(cap_t caps, cap_value_t cap)
+{
+       int ret;
+       cap_flag_value_t flag_set;
+
+       ret = cap_get_flag(caps, cap, CAP_EFFECTIVE, &flag_set);
+       if (ret < 0) {
+               ret = -errno;
+               kdbus_printf("error cap_get_flag(): %d (%m)\n", ret);
+               return ret;
+       }
+
+       return (flag_set == CAP_SET);
+}
+
+/*
+ * Returns:
+ *  1 in case all the requested effective capabilities are set.
+ *  0 in case we do not have the requested capabilities. This value
+ *    will be used to abort tests with TEST_SKIP
+ *  Negative errno on failure.
+ *
+ *  Terminate args with a negative value.
+ */
+int test_is_capable(int cap, ...)
+{
+       int ret;
+       va_list ap;
+       cap_t caps;
+
+       caps = cap_get_proc();
+       if (!cap) {
+               ret = -errno;
+               kdbus_printf("error cap_get_proc(): %d (%m)\n", ret);
+               return ret;
+       }
+
+       ret = do_cap_get_flag(caps, (cap_value_t)cap);
+       if (ret <= 0)
+               goto out;
+
+       va_start(ap, cap);
+       while ((cap = va_arg(ap, int)) > 0) {
+               ret = do_cap_get_flag(caps, (cap_value_t)cap);
+               if (ret <= 0)
+                       break;
+       }
+       va_end(ap);
+
+out:
+       cap_free(caps);
+       return ret;
+}
+
+int config_user_ns_is_enabled(void)
+{
+       return (access("/proc/self/uid_map", F_OK) == 0);
+}
+
+int config_auditsyscall_is_enabled(void)
+{
+       return (access("/proc/self/loginuid", F_OK) == 0);
+}
+
+int config_cgroups_is_enabled(void)
+{
+       return (access("/proc/self/cgroup", F_OK) == 0);
+}
+
+int config_security_is_enabled(void)
+{
+       int fd;
+       int ret;
+       char buf[128];
+
+       /* CONFIG_SECURITY is disabled */
+       if (access("/proc/self/attr/current", F_OK) != 0)
+               return 0;
+
+       /*
+        * Now only if read() fails with -EINVAL then we assume
+        * that SECLABEL and LSM are disabled
+        */
+       fd = open("/proc/self/attr/current", O_RDONLY|O_CLOEXEC);
+       if (fd < 0)
+               return 1;
+
+       ret = read(fd, buf, sizeof(buf));
+       if (ret == -1 && errno == EINVAL)
+               ret = 0;
+       else
+               ret = 1;
+
+       close(fd);
+
+       return ret;
+}
diff --git a/tools/testing/selftests/kdbus/kdbus-util.h b/tools/testing/selftests/kdbus/kdbus-util.h
new file mode 100644 (file)
index 0000000..652d957
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2013-2015 Kay Sievers
+ * Copyright (C) 2013-2015 Daniel Mack
+ *
+ * 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.
+ */
+#pragma once
+
+#define BIT(X) (1 << (X))
+
+#include <time.h>
+#include <stdbool.h>
+#include <linux/kdbus.h>
+
+#define _STRINGIFY(x) #x
+#define STRINGIFY(x) _STRINGIFY(x)
+#define ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0]))
+
+#define KDBUS_PTR(addr) ((void *)(uintptr_t)(addr))
+
+#define KDBUS_ALIGN8(l) (((l) + 7) & ~7)
+#define KDBUS_ITEM_HEADER_SIZE offsetof(struct kdbus_item, data)
+#define KDBUS_ITEM_SIZE(s) KDBUS_ALIGN8((s) + KDBUS_ITEM_HEADER_SIZE)
+
+#define KDBUS_ITEM_NEXT(item) \
+       (typeof(item))(((uint8_t *)item) + KDBUS_ALIGN8((item)->size))
+#define KDBUS_ITEM_FOREACH(item, head, first)                          \
+       for (item = (head)->first;                                      \
+            ((uint8_t *)(item) < (uint8_t *)(head) + (head)->size) &&  \
+              ((uint8_t *)(item) >= (uint8_t *)(head));        \
+            item = KDBUS_ITEM_NEXT(item))
+#define KDBUS_FOREACH(iter, first, _size)                              \
+       for (iter = (first);                                            \
+            ((uint8_t *)(iter) < (uint8_t *)(first) + (_size)) &&      \
+              ((uint8_t *)(iter) >= (uint8_t *)(first));               \
+            iter = (void*)(((uint8_t *)iter) + KDBUS_ALIGN8((iter)->size)))
+
+
+#define _KDBUS_ATTACH_BITS_SET_NR  (__builtin_popcountll(_KDBUS_ATTACH_ALL))
+
+/* Sum of KDBUS_ITEM_* that reflects _KDBUS_ATTACH_ALL */
+#define KDBUS_ATTACH_ITEMS_TYPE_SUM \
+       ((((_KDBUS_ATTACH_BITS_SET_NR - 1) * \
+       ((_KDBUS_ATTACH_BITS_SET_NR - 1) + 1)) / 2 ) + \
+       (_KDBUS_ITEM_ATTACH_BASE * _KDBUS_ATTACH_BITS_SET_NR))
+
+
+#define POOL_SIZE (16 * 1024LU * 1024LU)
+
+#define UNPRIV_UID 65534
+#define UNPRIV_GID 65534
+
+/* Dump as user of process, useful for user namespace testing */
+#define SUID_DUMP_USER 1
+
+extern int kdbus_util_verbose;
+
+#define kdbus_printf(X...) \
+       if (kdbus_util_verbose) \
+               printf(X)
+
+#define RUN_UNPRIVILEGED(child_uid, child_gid, _child_, _parent_) ({   \
+               pid_t pid, rpid;                                        \
+               int ret;                                                \
+                                                                       \
+               pid = fork();                                           \
+               if (pid == 0) {                                         \
+                       ret = drop_privileges(child_uid, child_gid);    \
+                       ASSERT_EXIT_VAL(ret == 0, ret);                 \
+                                                                       \
+                       _child_;                                        \
+                       _exit(0);                                       \
+               } else if (pid > 0) {                                   \
+                       _parent_;                                       \
+                       rpid = waitpid(pid, &ret, 0);                   \
+                       ASSERT_RETURN(rpid == pid);                     \
+                       ASSERT_RETURN(WIFEXITED(ret));                  \
+                       ASSERT_RETURN(WEXITSTATUS(ret) == 0);           \
+                       ret = TEST_OK;                                  \
+               } else {                                                \
+                       ret = pid;                                      \
+               }                                                       \
+                                                                       \
+               ret;                                                    \
+       })
+
+#define RUN_UNPRIVILEGED_CONN(_var_, _bus_, _code_)                    \
+       RUN_UNPRIVILEGED(UNPRIV_UID, UNPRIV_GID, ({                     \
+               struct kdbus_conn *_var_;                               \
+               _var_ = kdbus_hello(_bus_, 0, NULL, 0);                 \
+               ASSERT_EXIT(_var_);                                     \
+               _code_;                                                 \
+               kdbus_conn_free(_var_);                                 \
+       }), ({ 0; }))
+
+#define RUN_CLONE_CHILD(clone_ret, flags, _setup_, _child_body_,       \
+                       _parent_setup_, _parent_body_) ({               \
+       pid_t pid, rpid;                                                \
+       int ret;                                                        \
+       int efd = -1;                                                   \
+                                                                       \
+       _setup_;                                                        \
+       efd = eventfd(0, EFD_CLOEXEC);                                  \
+       ASSERT_RETURN(efd >= 0);                                        \
+       *clone_ret = 0;                                                 \
+       pid = syscall(__NR_clone, flags, NULL);                         \
+       if (pid == 0) {                                                 \
+               eventfd_t event_status = 0;                             \
+               ret = prctl(PR_SET_PDEATHSIG, SIGKILL);                 \
+               ASSERT_EXIT(ret == 0);                                  \
+               ret = eventfd_read(efd, &event_status);                 \
+               if (ret < 0 || event_status != 1) {                     \
+                       kdbus_printf("error eventfd_read()\n");         \
+                       _exit(EXIT_FAILURE);                            \
+               }                                                       \
+               _child_body_;                                           \
+               _exit(0);                                               \
+       } else if (pid > 0) {                                           \
+               _parent_setup_;                                         \
+               ret = eventfd_write(efd, 1);                            \
+               ASSERT_RETURN(ret >= 0);                                \
+               _parent_body_;                                          \
+               rpid = waitpid(pid, &ret, 0);                           \
+               ASSERT_RETURN(rpid == pid);                             \
+               ASSERT_RETURN(WIFEXITED(ret));                          \
+               ASSERT_RETURN(WEXITSTATUS(ret) == 0);                   \
+               ret = TEST_OK;                                          \
+       } else {                                                        \
+               ret = -errno;                                           \
+               *clone_ret = -errno;                                    \
+       }                                                               \
+       close(efd);                                                     \
+       ret;                                                            \
+})
+
+/* Enums for parent if it should drop privs or not */
+enum kdbus_drop_parent {
+       DO_NOT_DROP,
+       DROP_SAME_UNPRIV,
+       DROP_OTHER_UNPRIV,
+};
+
+struct kdbus_conn {
+       int fd;
+       uint64_t id;
+       unsigned char *buf;
+};
+
+int kdbus_sysfs_get_parameter_mask(const char *path, uint64_t *mask);
+int kdbus_sysfs_set_parameter_mask(const char *path, uint64_t mask);
+
+int sys_memfd_create(const char *name, __u64 size);
+int sys_memfd_seal_set(int fd);
+off_t sys_memfd_get_size(int fd, off_t *size);
+
+int kdbus_list(struct kdbus_conn *conn, uint64_t flags);
+int kdbus_name_release(struct kdbus_conn *conn, const char *name);
+int kdbus_name_acquire(struct kdbus_conn *conn, const char *name,
+                      uint64_t *flags);
+void kdbus_msg_free(struct kdbus_msg *msg);
+int kdbus_msg_recv(struct kdbus_conn *conn,
+                  struct kdbus_msg **msg, uint64_t *offset);
+int kdbus_msg_recv_poll(struct kdbus_conn *conn, int timeout_ms,
+                       struct kdbus_msg **msg_out, uint64_t *offset);
+int kdbus_free(const struct kdbus_conn *conn, uint64_t offset);
+int kdbus_msg_dump(const struct kdbus_conn *conn,
+                  const struct kdbus_msg *msg);
+int kdbus_create_bus(int control_fd, const char *name,
+                    uint64_t req_meta, uint64_t owner_meta,
+                    char **path);
+int kdbus_msg_send(const struct kdbus_conn *conn, const char *name,
+                  uint64_t cookie, uint64_t flags, uint64_t timeout,
+                  int64_t priority, uint64_t dst_id, int fds_count, int fds[]);
+int kdbus_msg_send_sync(const struct kdbus_conn *conn, const char *name,
+                       uint64_t cookie, uint64_t flags, uint64_t timeout,
+                       int64_t priority, uint64_t dst_id, int cancel_fd);
+int kdbus_msg_send_reply(const struct kdbus_conn *conn,
+                        uint64_t reply_cookie,
+                        uint64_t dst_id);
+struct kdbus_conn *kdbus_hello(const char *path, uint64_t hello_flags,
+                              const struct kdbus_item *item,
+                              size_t item_size);
+struct kdbus_conn *kdbus_hello_registrar(const char *path, const char *name,
+                                        const struct kdbus_policy_access *access,
+                                        size_t num_access, uint64_t flags);
+struct kdbus_conn *kdbus_hello_activator(const char *path, const char *name,
+                                        const struct kdbus_policy_access *access,
+                                        size_t num_access);
+bool kdbus_item_in_message(struct kdbus_msg *msg, uint64_t type);
+int kdbus_bus_creator_info(struct kdbus_conn *conn,
+                          uint64_t flags,
+                          uint64_t *offset);
+int kdbus_conn_info(struct kdbus_conn *conn, uint64_t id,
+                   const char *name, uint64_t flags, uint64_t *offset);
+void kdbus_conn_free(struct kdbus_conn *conn);
+int kdbus_conn_update_attach_flags(struct kdbus_conn *conn,
+                                  uint64_t attach_flags_send,
+                                  uint64_t attach_flags_recv);
+int kdbus_conn_update_policy(struct kdbus_conn *conn, const char *name,
+                            const struct kdbus_policy_access *access,
+                            size_t num_access);
+
+int kdbus_add_match_id(struct kdbus_conn *conn, uint64_t cookie,
+                      uint64_t type, uint64_t id);
+int kdbus_add_match_empty(struct kdbus_conn *conn);
+
+int all_uids_gids_are_mapped();
+int drop_privileges(uid_t uid, gid_t gid);
+uint64_t now(clockid_t clock);
+char *unique_name(const char *prefix);
+
+int userns_map_uid_gid(pid_t pid,
+                      const char *map_uid,
+                      const char *map_gid);
+int test_is_capable(int cap, ...);
+int config_user_ns_is_enabled(void);
+int config_auditsyscall_is_enabled(void);
+int config_cgroups_is_enabled(void);
+int config_security_is_enabled(void);
diff --git a/tools/testing/selftests/kdbus/test-activator.c b/tools/testing/selftests/kdbus/test-activator.c
new file mode 100644 (file)
index 0000000..c70fafe
--- /dev/null
@@ -0,0 +1,320 @@
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <errno.h>
+#include <assert.h>
+#include <poll.h>
+#include <sys/capability.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "kdbus-test.h"
+#include "kdbus-util.h"
+#include "kdbus-enum.h"
+
+static int kdbus_starter_poll(struct kdbus_conn *conn)
+{
+       int ret;
+       struct pollfd fd;
+
+       fd.fd = conn->fd;
+       fd.events = POLLIN | POLLPRI | POLLHUP;
+       fd.revents = 0;
+
+       ret = poll(&fd, 1, 100);
+       if (ret == 0)
+               return -ETIMEDOUT;
+       else if (ret > 0) {
+               if (fd.revents & POLLIN)
+                       return 0;
+
+               if (fd.revents & (POLLHUP | POLLERR))
+                       ret = -ECONNRESET;
+       }
+
+       return ret;
+}
+
+/* Ensure that kdbus activator logic is safe */
+static int kdbus_priv_activator(struct kdbus_test_env *env)
+{
+       int ret;
+       struct kdbus_msg *msg = NULL;
+       uint64_t cookie = 0xdeadbeef;
+       uint64_t flags = KDBUS_NAME_REPLACE_EXISTING;
+       struct kdbus_conn *activator;
+       struct kdbus_conn *service;
+       struct kdbus_conn *client;
+       struct kdbus_conn *holder;
+       struct kdbus_policy_access *access;
+
+       access = (struct kdbus_policy_access[]){
+               {
+                       .type = KDBUS_POLICY_ACCESS_USER,
+                       .id = getuid(),
+                       .access = KDBUS_POLICY_OWN,
+               },
+               {
+                       .type = KDBUS_POLICY_ACCESS_USER,
+                       .id = getuid(),
+                       .access = KDBUS_POLICY_TALK,
+               },
+       };
+
+       activator = kdbus_hello_activator(env->buspath, "foo.priv.activator",
+                                         access, 2);
+       ASSERT_RETURN(activator);
+
+       service = kdbus_hello(env->buspath, 0, NULL, 0);
+       ASSERT_RETURN(service);
+
+       client = kdbus_hello(env->buspath, 0, NULL, 0);
+       ASSERT_RETURN(client);
+
+       /*
+        * Make sure that other users can't TALK to the activator
+        */
+
+       ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({
+               /* Try to talk using the ID */
+               ret = kdbus_msg_send(unpriv, NULL, 0xdeadbeef, 0, 0,
+                                    0, activator->id, 0, NULL);
+               ASSERT_EXIT(ret == -ENXIO);
+
+               /* Try to talk to the name */
+               ret = kdbus_msg_send(unpriv, "foo.priv.activator",
+                                    0xdeadbeef, 0, 0, 0,
+                                    KDBUS_DST_ID_NAME, 0, NULL);
+               ASSERT_EXIT(ret == -EPERM);
+       }));
+       ASSERT_RETURN(ret >= 0);
+
+       /*
+        * Make sure that we did not receive anything, so the
+        * service will not be started automatically
+        */
+
+       ret = kdbus_starter_poll(activator);
+       ASSERT_RETURN(ret == -ETIMEDOUT);
+
+       /*
+        * Now try to emulate the starter/service logic and
+        * acquire the name.
+        */
+
+       cookie++;
+       ret = kdbus_msg_send(service, "foo.priv.activator", cookie,
+                            0, 0, 0, KDBUS_DST_ID_NAME, 0, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_starter_poll(activator);
+       ASSERT_RETURN(ret == 0);
+
+       /* Policies are still checked, access denied */
+
+       ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({
+               ret = kdbus_name_acquire(unpriv, "foo.priv.activator",
+                                        &flags);
+               ASSERT_RETURN(ret == -EPERM);
+       }));
+       ASSERT_RETURN(ret >= 0);
+
+       ret = kdbus_name_acquire(service, "foo.priv.activator",
+                                &flags);
+       ASSERT_RETURN(ret == 0);
+
+       /* We read our previous starter message */
+
+       ret = kdbus_msg_recv_poll(service, 100, NULL, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       /* Try to talk, we still fail */
+
+       cookie++;
+       ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({
+               /* Try to talk to the name */
+               ret = kdbus_msg_send(unpriv, "foo.priv.activator",
+                                    cookie, 0, 0, 0,
+                                    KDBUS_DST_ID_NAME, 0, NULL);
+               ASSERT_EXIT(ret == -EPERM);
+       }));
+       ASSERT_RETURN(ret >= 0);
+
+       /* Still nothing to read */
+
+       ret = kdbus_msg_recv_poll(service, 100, NULL, NULL);
+       ASSERT_RETURN(ret == -ETIMEDOUT);
+
+       /* We receive every thing now */
+
+       cookie++;
+       ret = kdbus_msg_send(client, "foo.priv.activator", cookie,
+                            0, 0, 0, KDBUS_DST_ID_NAME, 0, NULL);
+       ASSERT_RETURN(ret == 0);
+       ret = kdbus_msg_recv_poll(service, 100, &msg, NULL);
+       ASSERT_RETURN(ret == 0 && msg->cookie == cookie);
+
+       kdbus_msg_free(msg);
+
+       /* Policies default to deny TALK now */
+       kdbus_conn_free(activator);
+
+       cookie++;
+       ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({
+               /* Try to talk to the name */
+               ret = kdbus_msg_send(unpriv, "foo.priv.activator",
+                                    cookie, 0, 0, 0,
+                                    KDBUS_DST_ID_NAME,
+                                    0, NULL);
+               ASSERT_EXIT(ret == -EPERM);
+       }));
+       ASSERT_RETURN(ret >= 0);
+
+       ret = kdbus_msg_recv_poll(service, 100, NULL, NULL);
+       ASSERT_RETURN(ret == -ETIMEDOUT);
+
+       /* Same user is able to TALK */
+       cookie++;
+       ret = kdbus_msg_send(client, "foo.priv.activator", cookie,
+                            0, 0, 0, KDBUS_DST_ID_NAME, 0, NULL);
+       ASSERT_RETURN(ret == 0);
+       ret = kdbus_msg_recv_poll(service, 100, &msg, NULL);
+       ASSERT_RETURN(ret == 0 && msg->cookie == cookie);
+
+       kdbus_msg_free(msg);
+
+       access = (struct kdbus_policy_access []){
+               {
+                       .type = KDBUS_POLICY_ACCESS_WORLD,
+                       .id = getuid(),
+                       .access = KDBUS_POLICY_TALK,
+               },
+       };
+
+       holder = kdbus_hello_registrar(env->buspath, "foo.priv.activator",
+                                      access, 1, KDBUS_HELLO_POLICY_HOLDER);
+       ASSERT_RETURN(holder);
+
+       /* Now we are able to TALK to the name */
+
+       cookie++;
+       ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({
+               /* Try to talk to the name */
+               ret = kdbus_msg_send(unpriv, "foo.priv.activator",
+                                    cookie, 0, 0, 0,
+                                    KDBUS_DST_ID_NAME,
+                                    0, NULL);
+               ASSERT_EXIT(ret == 0);
+       }));
+       ASSERT_RETURN(ret >= 0);
+
+       ret = kdbus_msg_recv_poll(service, 100, NULL, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({
+               ret = kdbus_name_acquire(unpriv, "foo.priv.activator",
+                                        &flags);
+               ASSERT_RETURN(ret == -EPERM);
+       }));
+       ASSERT_RETURN(ret >= 0);
+
+       kdbus_conn_free(service);
+       kdbus_conn_free(client);
+       kdbus_conn_free(holder);
+
+       return 0;
+}
+
+int kdbus_test_activator(struct kdbus_test_env *env)
+{
+       int ret;
+       struct kdbus_conn *activator;
+       struct pollfd fds[2];
+       bool activator_done = false;
+       struct kdbus_policy_access access[2];
+
+       access[0].type = KDBUS_POLICY_ACCESS_USER;
+       access[0].id = getuid();
+       access[0].access = KDBUS_POLICY_OWN;
+
+       access[1].type = KDBUS_POLICY_ACCESS_WORLD;
+       access[1].access = KDBUS_POLICY_TALK;
+
+       activator = kdbus_hello_activator(env->buspath, "foo.test.activator",
+                                         access, 2);
+       ASSERT_RETURN(activator);
+
+       ret = kdbus_add_match_empty(env->conn);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_list(env->conn, KDBUS_LIST_NAMES |
+                                   KDBUS_LIST_UNIQUE |
+                                   KDBUS_LIST_ACTIVATORS |
+                                   KDBUS_LIST_QUEUED);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_msg_send(env->conn, "foo.test.activator", 0xdeafbeef,
+                            0, 0, 0, KDBUS_DST_ID_NAME, 0, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       fds[0].fd = activator->fd;
+       fds[1].fd = env->conn->fd;
+
+       kdbus_printf("-- entering poll loop ...\n");
+
+       for (;;) {
+               int i, nfds = sizeof(fds) / sizeof(fds[0]);
+
+               for (i = 0; i < nfds; i++) {
+                       fds[i].events = POLLIN | POLLPRI;
+                       fds[i].revents = 0;
+               }
+
+               ret = poll(fds, nfds, 3000);
+               ASSERT_RETURN(ret >= 0);
+
+               ret = kdbus_list(env->conn, KDBUS_LIST_NAMES);
+               ASSERT_RETURN(ret == 0);
+
+               if ((fds[0].revents & POLLIN) && !activator_done) {
+                       uint64_t flags = KDBUS_NAME_REPLACE_EXISTING;
+
+                       kdbus_printf("Starter was called back!\n");
+
+                       ret = kdbus_name_acquire(env->conn,
+                                                "foo.test.activator", &flags);
+                       ASSERT_RETURN(ret == 0);
+
+                       activator_done = true;
+               }
+
+               if (fds[1].revents & POLLIN) {
+                       kdbus_msg_recv(env->conn, NULL, NULL);
+                       break;
+               }
+       }
+
+       /* Check if all uids/gids are mapped */
+       if (!all_uids_gids_are_mapped())
+               return TEST_SKIP;
+
+       /* Check now capabilities, so we run the previous tests */
+       ret = test_is_capable(CAP_SETUID, CAP_SETGID, -1);
+       ASSERT_RETURN(ret >= 0);
+
+       if (!ret)
+               return TEST_SKIP;
+
+       ret = kdbus_priv_activator(env);
+       ASSERT_RETURN(ret == 0);
+
+       kdbus_conn_free(activator);
+
+       return TEST_OK;
+}
diff --git a/tools/testing/selftests/kdbus/test-attach-flags.c b/tools/testing/selftests/kdbus/test-attach-flags.c
new file mode 100644 (file)
index 0000000..deee7c3
--- /dev/null
@@ -0,0 +1,750 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/capability.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <linux/unistd.h>
+
+#include "kdbus-api.h"
+#include "kdbus-test.h"
+#include "kdbus-util.h"
+#include "kdbus-enum.h"
+
+/*
+ * Should be the sum of the currently supported and compiled-in
+ * KDBUS_ITEMS_* that reflect KDBUS_ATTACH_* flags.
+ */
+static unsigned int KDBUS_TEST_ITEMS_SUM = KDBUS_ATTACH_ITEMS_TYPE_SUM;
+
+static struct kdbus_conn *__kdbus_hello(const char *path, uint64_t flags,
+                                       uint64_t attach_flags_send,
+                                       uint64_t attach_flags_recv)
+{
+       struct kdbus_cmd_free cmd_free = {};
+       int ret, fd;
+       struct kdbus_conn *conn;
+       struct {
+               struct kdbus_cmd_hello hello;
+
+               struct {
+                       uint64_t size;
+                       uint64_t type;
+                       char str[16];
+               } conn_name;
+
+               uint8_t extra_items[0];
+       } h;
+
+       memset(&h, 0, sizeof(h));
+
+       kdbus_printf("-- opening bus connection %s\n", path);
+       fd = open(path, O_RDWR|O_CLOEXEC);
+       if (fd < 0) {
+               kdbus_printf("--- error %d (%m)\n", fd);
+               return NULL;
+       }
+
+       h.hello.flags = flags | KDBUS_HELLO_ACCEPT_FD;
+       h.hello.attach_flags_send = attach_flags_send;
+       h.hello.attach_flags_recv = attach_flags_recv;
+       h.conn_name.type = KDBUS_ITEM_CONN_DESCRIPTION;
+       strcpy(h.conn_name.str, "this-is-my-name");
+       h.conn_name.size = KDBUS_ITEM_HEADER_SIZE + strlen(h.conn_name.str) + 1;
+
+       h.hello.size = sizeof(h);
+       h.hello.pool_size = POOL_SIZE;
+
+       ret = kdbus_cmd_hello(fd, (struct kdbus_cmd_hello *) &h.hello);
+       if (ret < 0) {
+               kdbus_printf("--- error when saying hello: %d (%m)\n", ret);
+               return NULL;
+       }
+
+       kdbus_printf("-- New connection ID : %llu\n",
+                    (unsigned long long)h.hello.id);
+
+       cmd_free.size = sizeof(cmd_free);
+       cmd_free.offset = h.hello.offset;
+       ret = kdbus_cmd_free(fd, &cmd_free);
+       if (ret < 0)
+               return NULL;
+
+       conn = malloc(sizeof(*conn));
+       if (!conn) {
+               kdbus_printf("unable to malloc()!?\n");
+               return NULL;
+       }
+
+       conn->buf = mmap(NULL, POOL_SIZE, PROT_READ, MAP_SHARED, fd, 0);
+       if (conn->buf == MAP_FAILED) {
+               ret = -errno;
+               free(conn);
+               close(fd);
+               kdbus_printf("--- error mmap: %d (%m)\n", ret);
+               return NULL;
+       }
+
+       conn->fd = fd;
+       conn->id = h.hello.id;
+       return conn;
+}
+
+static int kdbus_test_peers_creation(struct kdbus_test_env *env)
+{
+       int ret;
+       int control_fd;
+       char *path;
+       char *busname;
+       char buspath[2048];
+       char control_path[2048];
+       uint64_t attach_flags_mask;
+       struct kdbus_conn *conn;
+
+       snprintf(control_path, sizeof(control_path),
+                "%s/control", env->root);
+
+       /*
+        * Set kdbus system-wide mask to 0, this has nothing
+        * to do with the following tests, bus and connection
+        * creation nor connection update, but we do it so we are
+        * sure that everything work as expected
+        */
+
+       attach_flags_mask = 0;
+       ret = kdbus_sysfs_set_parameter_mask(env->mask_param_path,
+                                            attach_flags_mask);
+       ASSERT_RETURN(ret == 0);
+
+
+       /*
+        * Create bus with a full set of ATTACH flags
+        */
+
+       control_fd = open(control_path, O_RDWR);
+       ASSERT_RETURN(control_fd >= 0);
+
+       busname = unique_name("test-peers-creation-bus");
+       ASSERT_RETURN(busname);
+
+       ret = kdbus_create_bus(control_fd, busname, _KDBUS_ATTACH_ALL,
+                              0, &path);
+       ASSERT_RETURN(ret == 0);
+
+       snprintf(buspath, sizeof(buspath), "%s/%s/bus", env->root, path);
+
+       /*
+        * Create a connection with an empty send attach flags, or
+        * with just KDBUS_ATTACH_CREDS, this should fail
+        */
+       conn = __kdbus_hello(buspath, 0, 0, 0);
+       ASSERT_RETURN(conn == NULL);
+       ASSERT_RETURN(errno == ECONNREFUSED);
+
+       conn = __kdbus_hello(buspath, 0, KDBUS_ATTACH_CREDS,
+                            _KDBUS_ATTACH_ALL);
+       ASSERT_RETURN(conn == NULL);
+       ASSERT_RETURN(errno == ECONNREFUSED);
+
+       conn = __kdbus_hello(buspath, 0, _KDBUS_ATTACH_ALL, 0);
+       ASSERT_RETURN(conn);
+
+       /* Try to cut back some send attach flags */
+       ret = kdbus_conn_update_attach_flags(conn,
+                                            KDBUS_ATTACH_CREDS|
+                                            KDBUS_ATTACH_PIDS,
+                                            _KDBUS_ATTACH_ALL);
+       ASSERT_RETURN(ret == -EINVAL);
+
+       ret = kdbus_conn_update_attach_flags(conn,
+                                            _KDBUS_ATTACH_ALL, 0);
+       ASSERT_RETURN(ret == 0);
+
+       kdbus_conn_free(conn);
+       free(path);
+       free(busname);
+       close(control_fd);
+
+
+       /* Test a new bus with KDBUS_ATTACH_PIDS */
+
+       control_fd = open(control_path, O_RDWR);
+       ASSERT_RETURN(control_fd >= 0);
+
+       busname = unique_name("test-peer-flags-bus");
+       ASSERT_RETURN(busname);
+
+       ret = kdbus_create_bus(control_fd, busname, KDBUS_ATTACH_PIDS,
+                              0, &path);
+       ASSERT_RETURN(ret == 0);
+
+       snprintf(buspath, sizeof(buspath), "%s/%s/bus", env->root, path);
+
+       /*
+        * Create a connection with an empty send attach flags, or
+        * all flags except KDBUS_ATTACH_PIDS
+        */
+       conn = __kdbus_hello(buspath, 0, 0, 0);
+       ASSERT_RETURN(conn == NULL);
+       ASSERT_RETURN(errno == ECONNREFUSED);
+
+       conn = __kdbus_hello(buspath, 0,
+                            _KDBUS_ATTACH_ALL & ~KDBUS_ATTACH_PIDS,
+                            _KDBUS_ATTACH_ALL);
+       ASSERT_RETURN(conn == NULL);
+       ASSERT_RETURN(errno == ECONNREFUSED);
+
+       /* The following should succeed */
+       conn = __kdbus_hello(buspath, 0, KDBUS_ATTACH_PIDS, 0);
+       ASSERT_RETURN(conn);
+       kdbus_conn_free(conn);
+
+       conn = __kdbus_hello(buspath, 0, _KDBUS_ATTACH_ALL, 0);
+       ASSERT_RETURN(conn);
+
+       ret = kdbus_conn_update_attach_flags(conn,
+                                            _KDBUS_ATTACH_ALL &
+                                            ~KDBUS_ATTACH_PIDS,
+                                            _KDBUS_ATTACH_ALL);
+       ASSERT_RETURN(ret == -EINVAL);
+
+       ret = kdbus_conn_update_attach_flags(conn, 0,
+                                            _KDBUS_ATTACH_ALL);
+       ASSERT_RETURN(ret == -EINVAL);
+
+       /* Now we want only KDBUS_ATTACH_PIDS */
+       ret = kdbus_conn_update_attach_flags(conn,
+                                            KDBUS_ATTACH_PIDS, 0);
+       ASSERT_RETURN(ret == 0);
+
+       kdbus_conn_free(conn);
+       free(path);
+       free(busname);
+       close(control_fd);
+
+
+       /*
+        * Create bus with 0 as ATTACH flags, the bus does not
+        * require any attach flags
+        */
+
+       control_fd = open(control_path, O_RDWR);
+       ASSERT_RETURN(control_fd >= 0);
+
+       busname = unique_name("test-peer-flags-bus");
+       ASSERT_RETURN(busname);
+
+       ret = kdbus_create_bus(control_fd, busname, 0, 0, &path);
+       ASSERT_RETURN(ret == 0);
+
+       snprintf(buspath, sizeof(buspath), "%s/%s/bus", env->root, path);
+
+       /* Bus is open it does not require any send attach flags */
+       conn = __kdbus_hello(buspath, 0, 0, 0);
+       ASSERT_RETURN(conn);
+       kdbus_conn_free(conn);
+
+       conn = __kdbus_hello(buspath, 0, _KDBUS_ATTACH_ALL, 0);
+       ASSERT_RETURN(conn);
+
+       ret = kdbus_conn_update_attach_flags(conn, 0, 0);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_conn_update_attach_flags(conn, KDBUS_ATTACH_CREDS, 0);
+       ASSERT_RETURN(ret == 0);
+
+       kdbus_conn_free(conn);
+       free(path);
+       free(busname);
+       close(control_fd);
+
+       return 0;
+}
+
+static int kdbus_test_peers_info(struct kdbus_test_env *env)
+{
+       int ret;
+       int control_fd;
+       char *path;
+       char *busname;
+       unsigned int i = 0;
+       uint64_t offset = 0;
+       char buspath[2048];
+       char control_path[2048];
+       uint64_t attach_flags_mask;
+       struct kdbus_item *item;
+       struct kdbus_info *info;
+       struct kdbus_conn *conn;
+       struct kdbus_conn *reader;
+       unsigned long long attach_count = 0;
+
+       snprintf(control_path, sizeof(control_path),
+                "%s/control", env->root);
+
+       attach_flags_mask = 0;
+       ret = kdbus_sysfs_set_parameter_mask(env->mask_param_path,
+                                            attach_flags_mask);
+       ASSERT_RETURN(ret == 0);
+
+       control_fd = open(control_path, O_RDWR);
+       ASSERT_RETURN(control_fd >= 0);
+
+       busname = unique_name("test-peers-info-bus");
+       ASSERT_RETURN(busname);
+
+       ret = kdbus_create_bus(control_fd, busname, _KDBUS_ATTACH_ALL,
+                              0, &path);
+       ASSERT_RETURN(ret == 0);
+
+       snprintf(buspath, sizeof(buspath), "%s/%s/bus", env->root, path);
+
+       /* Create connections with the appropriate flags */
+       conn = __kdbus_hello(buspath, 0, _KDBUS_ATTACH_ALL, 0);
+       ASSERT_RETURN(conn);
+
+       reader = __kdbus_hello(buspath, 0, _KDBUS_ATTACH_ALL, 0);
+       ASSERT_RETURN(reader);
+
+       ret = kdbus_conn_info(reader, conn->id, NULL,
+                             _KDBUS_ATTACH_ALL, &offset);
+       ASSERT_RETURN(ret == 0);
+
+       info = (struct kdbus_info *)(reader->buf + offset);
+       ASSERT_RETURN(info->id == conn->id);
+
+       /* all attach flags are masked, no metadata */
+       KDBUS_ITEM_FOREACH(item, info, items)
+               i++;
+
+       ASSERT_RETURN(i == 0);
+
+       kdbus_free(reader, offset);
+
+       /* Set the mask to _KDBUS_ATTACH_ANY */
+       attach_flags_mask = _KDBUS_ATTACH_ANY;
+       ret = kdbus_sysfs_set_parameter_mask(env->mask_param_path,
+                                            attach_flags_mask);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_conn_info(reader, conn->id, NULL,
+                             _KDBUS_ATTACH_ALL, &offset);
+       ASSERT_RETURN(ret == 0);
+
+       info = (struct kdbus_info *)(reader->buf + offset);
+       ASSERT_RETURN(info->id == conn->id);
+
+       attach_count = 0;
+       KDBUS_ITEM_FOREACH(item, info, items)
+                   attach_count += item->type;
+
+       /*
+        * All flags have been returned except for:
+        * KDBUS_ITEM_TIMESTAMP and
+        * KDBUS_ITEM_OWNED_NAME we do not own any name.
+        */
+       ASSERT_RETURN(attach_count == (KDBUS_TEST_ITEMS_SUM -
+                                      KDBUS_ITEM_OWNED_NAME -
+                                      KDBUS_ITEM_TIMESTAMP));
+
+       kdbus_free(reader, offset);
+
+       /* Request only OWNED names */
+       ret = kdbus_conn_info(reader, conn->id, NULL,
+                             KDBUS_ATTACH_NAMES, &offset);
+       ASSERT_RETURN(ret == 0);
+
+       info = (struct kdbus_info *)(reader->buf + offset);
+       ASSERT_RETURN(info->id == conn->id);
+
+       attach_count = 0;
+       KDBUS_ITEM_FOREACH(item, info, items)
+               attach_count += item->type;
+
+       /* we should not get any metadata since we do not own names */
+       ASSERT_RETURN(attach_count == 0);
+
+       kdbus_free(reader, offset);
+
+       kdbus_conn_free(conn);
+       kdbus_conn_free(reader);
+
+       return 0;
+}
+
+/**
+ * @kdbus_mask_param:  kdbus module mask parameter (system-wide)
+ * @requested_meta:    The bus owner metadata that we want
+ * @expected_items:    The returned KDBUS_ITEMS_* sum. Used to
+ *                     validate the returned metadata items
+ */
+static int kdbus_cmp_bus_creator_metadata(struct kdbus_test_env *env,
+                                         struct kdbus_conn *conn,
+                                         uint64_t kdbus_mask_param,
+                                         uint64_t requested_meta,
+                                         unsigned long expected_items)
+{
+       int ret;
+       uint64_t offset = 0;
+       struct kdbus_info *info;
+       struct kdbus_item *item;
+       unsigned long attach_count = 0;
+
+       ret = kdbus_sysfs_set_parameter_mask(env->mask_param_path,
+                                            kdbus_mask_param);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_bus_creator_info(conn, requested_meta, &offset);
+       ASSERT_RETURN(ret == 0);
+
+       info = (struct kdbus_info *)(conn->buf + offset);
+
+       KDBUS_ITEM_FOREACH(item, info, items)
+               attach_count += item->type;
+
+       ASSERT_RETURN(attach_count == expected_items);
+
+       ret = kdbus_free(conn, offset);
+       ASSERT_RETURN(ret == 0);
+
+       return 0;
+}
+
+static int kdbus_test_bus_creator_info(struct kdbus_test_env *env)
+{
+       int ret;
+       int control_fd;
+       char *path;
+       char *busname;
+       char buspath[2048];
+       char control_path[2048];
+       uint64_t attach_flags_mask;
+       struct kdbus_conn *conn;
+       unsigned long expected_items = 0;
+
+       snprintf(control_path, sizeof(control_path),
+                "%s/control", env->root);
+
+       control_fd = open(control_path, O_RDWR);
+       ASSERT_RETURN(control_fd >= 0);
+
+       busname = unique_name("test-peers-info-bus");
+       ASSERT_RETURN(busname);
+
+       /*
+        * Now the bus allows us to see all its KDBUS_ATTACH_*
+        * items
+        */
+       ret = kdbus_create_bus(control_fd, busname, 0,
+                              _KDBUS_ATTACH_ALL, &path);
+       ASSERT_RETURN(ret == 0);
+
+       snprintf(buspath, sizeof(buspath), "%s/%s/bus", env->root, path);
+
+       conn = __kdbus_hello(buspath, 0, 0, 0);
+       ASSERT_RETURN(conn);
+
+       /*
+        * Start with a kdbus module mask set to _KDBUS_ATTACH_ANY
+        */
+       attach_flags_mask = _KDBUS_ATTACH_ANY;
+
+       /*
+        * All flags will be returned except for:
+        * KDBUS_ITEM_TIMESTAMP
+        * KDBUS_ITEM_OWNED_NAME
+        * KDBUS_ITEM_CONN_DESCRIPTION
+        *
+        * An extra flags is always returned KDBUS_ITEM_MAKE_NAME
+        * which contains the bus name
+        */
+       expected_items = KDBUS_TEST_ITEMS_SUM + KDBUS_ITEM_MAKE_NAME;
+       expected_items -= KDBUS_ITEM_TIMESTAMP +
+                         KDBUS_ITEM_OWNED_NAME +
+                         KDBUS_ITEM_CONN_DESCRIPTION;
+       ret = kdbus_cmp_bus_creator_metadata(env, conn,
+                                            attach_flags_mask,
+                                            _KDBUS_ATTACH_ALL,
+                                            expected_items);
+       ASSERT_RETURN(ret == 0);
+
+       /*
+        * We should have:
+        * KDBUS_ITEM_PIDS + KDBUS_ITEM_CREDS + KDBUS_ITEM_MAKE_NAME
+        */
+       expected_items = KDBUS_ITEM_PIDS + KDBUS_ITEM_CREDS +
+                        KDBUS_ITEM_MAKE_NAME;
+       ret = kdbus_cmp_bus_creator_metadata(env, conn,
+                                            attach_flags_mask,
+                                            KDBUS_ATTACH_PIDS |
+                                            KDBUS_ATTACH_CREDS,
+                                            expected_items);
+       ASSERT_RETURN(ret == 0);
+
+       /* KDBUS_ITEM_MAKE_NAME is always returned */
+       expected_items = KDBUS_ITEM_MAKE_NAME;
+       ret = kdbus_cmp_bus_creator_metadata(env, conn,
+                                            attach_flags_mask,
+                                            0, expected_items);
+       ASSERT_RETURN(ret == 0);
+
+       /*
+        * Restrict kdbus system-wide mask to KDBUS_ATTACH_PIDS
+        */
+
+       attach_flags_mask = KDBUS_ATTACH_PIDS;
+
+       /*
+        * We should have:
+        * KDBUS_ITEM_PIDS + KDBUS_ITEM_MAKE_NAME
+        */
+       expected_items = KDBUS_ITEM_PIDS + KDBUS_ITEM_MAKE_NAME;
+       ret = kdbus_cmp_bus_creator_metadata(env, conn,
+                                            attach_flags_mask,
+                                            _KDBUS_ATTACH_ALL,
+                                            expected_items);
+       ASSERT_RETURN(ret == 0);
+
+
+       /* system-wide mask to 0 */
+       attach_flags_mask = 0;
+
+       /* we should only see: KDBUS_ITEM_MAKE_NAME */
+       expected_items = KDBUS_ITEM_MAKE_NAME;
+       ret = kdbus_cmp_bus_creator_metadata(env, conn,
+                                            attach_flags_mask,
+                                            _KDBUS_ATTACH_ALL,
+                                            expected_items);
+       ASSERT_RETURN(ret == 0);
+
+       kdbus_conn_free(conn);
+       free(path);
+       free(busname);
+       close(control_fd);
+
+
+       /*
+        * A new bus that hides all its owner metadata
+        */
+
+       control_fd = open(control_path, O_RDWR);
+       ASSERT_RETURN(control_fd >= 0);
+
+       busname = unique_name("test-peers-info-bus");
+       ASSERT_RETURN(busname);
+
+       ret = kdbus_create_bus(control_fd, busname, 0, 0, &path);
+       ASSERT_RETURN(ret == 0);
+
+       snprintf(buspath, sizeof(buspath), "%s/%s/bus", env->root, path);
+
+       conn = __kdbus_hello(buspath, 0, 0, 0);
+       ASSERT_RETURN(conn);
+
+       /*
+        * Start with a kdbus module mask set to _KDBUS_ATTACH_ANY
+        */
+       attach_flags_mask = _KDBUS_ATTACH_ANY;
+
+       /*
+        * We only get the KDBUS_ITEM_MAKE_NAME
+        */
+       expected_items = KDBUS_ITEM_MAKE_NAME;
+       ret = kdbus_cmp_bus_creator_metadata(env, conn,
+                                            attach_flags_mask,
+                                            _KDBUS_ATTACH_ALL,
+                                            expected_items);
+       ASSERT_RETURN(ret == 0);
+
+       /*
+        * We still get only kdbus_ITEM_MAKE_NAME
+        */
+       attach_flags_mask = 0;
+       expected_items = KDBUS_ITEM_MAKE_NAME;
+       ret = kdbus_cmp_bus_creator_metadata(env, conn,
+                                            attach_flags_mask,
+                                            _KDBUS_ATTACH_ALL,
+                                            expected_items);
+       ASSERT_RETURN(ret == 0);
+
+       kdbus_conn_free(conn);
+       free(path);
+       free(busname);
+       close(control_fd);
+
+
+       /*
+        * A new bus that shows only the PID and CREDS metadata
+        * of the bus owner.
+        */
+       control_fd = open(control_path, O_RDWR);
+       ASSERT_RETURN(control_fd >= 0);
+
+       busname = unique_name("test-peers-info-bus");
+       ASSERT_RETURN(busname);
+
+       ret = kdbus_create_bus(control_fd, busname, 0,
+                              KDBUS_ATTACH_PIDS|
+                              KDBUS_ATTACH_CREDS, &path);
+       ASSERT_RETURN(ret == 0);
+
+       snprintf(buspath, sizeof(buspath), "%s/%s/bus", env->root, path);
+
+       conn = __kdbus_hello(buspath, 0, 0, 0);
+       ASSERT_RETURN(conn);
+
+       /*
+        * Start with a kdbus module mask set to _KDBUS_ATTACH_ANY
+        */
+       attach_flags_mask = _KDBUS_ATTACH_ANY;
+
+       /*
+        * We should have:
+        * KDBUS_ITEM_PIDS + KDBUS_ITEM_CREDS + KDBUS_ITEM_MAKE_NAME
+        */
+       expected_items = KDBUS_ITEM_PIDS + KDBUS_ITEM_CREDS +
+                        KDBUS_ITEM_MAKE_NAME;
+       ret = kdbus_cmp_bus_creator_metadata(env, conn,
+                                            attach_flags_mask,
+                                            _KDBUS_ATTACH_ALL,
+                                            expected_items);
+       ASSERT_RETURN(ret == 0);
+
+       expected_items = KDBUS_ITEM_CREDS + KDBUS_ITEM_MAKE_NAME;
+       ret = kdbus_cmp_bus_creator_metadata(env, conn,
+                                            attach_flags_mask,
+                                            KDBUS_ATTACH_CREDS,
+                                            expected_items);
+       ASSERT_RETURN(ret == 0);
+
+       /* KDBUS_ITEM_MAKE_NAME is always returned */
+       expected_items = KDBUS_ITEM_MAKE_NAME;
+       ret = kdbus_cmp_bus_creator_metadata(env, conn,
+                                            attach_flags_mask,
+                                            0, expected_items);
+       ASSERT_RETURN(ret == 0);
+
+       /*
+        * Restrict kdbus system-wide mask to KDBUS_ATTACH_PIDS
+        */
+
+       attach_flags_mask = KDBUS_ATTACH_PIDS;
+       /*
+        * We should have:
+        * KDBUS_ITEM_PIDS + KDBUS_ITEM_MAKE_NAME
+        */
+       expected_items = KDBUS_ITEM_PIDS + KDBUS_ITEM_MAKE_NAME;
+       ret = kdbus_cmp_bus_creator_metadata(env, conn,
+                                            attach_flags_mask,
+                                            _KDBUS_ATTACH_ALL,
+                                            expected_items);
+       ASSERT_RETURN(ret == 0);
+
+       /* No KDBUS_ATTACH_CREDS */
+       expected_items = KDBUS_ITEM_MAKE_NAME;
+       ret = kdbus_cmp_bus_creator_metadata(env, conn,
+                                            attach_flags_mask,
+                                            KDBUS_ATTACH_CREDS,
+                                            expected_items);
+       ASSERT_RETURN(ret == 0);
+
+       /* system-wide mask to 0 */
+       attach_flags_mask = 0;
+
+       /* we should only see: KDBUS_ITEM_MAKE_NAME */
+       expected_items = KDBUS_ITEM_MAKE_NAME;
+       ret = kdbus_cmp_bus_creator_metadata(env, conn,
+                                            attach_flags_mask,
+                                            _KDBUS_ATTACH_ALL,
+                                            expected_items);
+       ASSERT_RETURN(ret == 0);
+
+
+       kdbus_conn_free(conn);
+       free(path);
+       free(busname);
+       close(control_fd);
+
+       return 0;
+}
+
+int kdbus_test_attach_flags(struct kdbus_test_env *env)
+{
+       int ret;
+       uint64_t flags_mask;
+       uint64_t old_kdbus_flags_mask;
+
+       /* We need CAP_DAC_OVERRIDE to overwrite the kdbus mask */
+       ret = test_is_capable(CAP_DAC_OVERRIDE, -1);
+       ASSERT_RETURN(ret >= 0);
+
+       /* no enough privileges, SKIP test */
+       if (!ret)
+               return TEST_SKIP;
+
+       /*
+        * We need to be able to write to
+        * "/sys/module/kdbus/parameters/attach_flags_mask"
+        * perhaps we are unprvileged/privileged in its userns
+        */
+       ret = access(env->mask_param_path, W_OK);
+       if (ret < 0) {
+               kdbus_printf("--- access() '%s' failed: %d (%m)\n",
+                            env->mask_param_path, -errno);
+               return TEST_SKIP;
+       }
+
+       ret = kdbus_sysfs_get_parameter_mask(env->mask_param_path,
+                                            &old_kdbus_flags_mask);
+       ASSERT_RETURN(ret == 0);
+
+       /* setup the right KDBUS_TEST_ITEMS_SUM */
+       if (!config_auditsyscall_is_enabled())
+               KDBUS_TEST_ITEMS_SUM -= KDBUS_ITEM_AUDIT;
+
+       if (!config_cgroups_is_enabled())
+               KDBUS_TEST_ITEMS_SUM -= KDBUS_ITEM_CGROUP;
+
+       if (!config_security_is_enabled())
+               KDBUS_TEST_ITEMS_SUM -= KDBUS_ITEM_SECLABEL;
+
+       /*
+        * Test the connection creation attach flags
+        */
+       ret = kdbus_test_peers_creation(env);
+       /* Restore previous kdbus mask */
+       kdbus_sysfs_set_parameter_mask(env->mask_param_path,
+                                      old_kdbus_flags_mask);
+       ASSERT_RETURN(ret == 0);
+
+       /*
+        * Test the CONN_INFO attach flags
+        */
+       ret = kdbus_test_peers_info(env);
+       /* Restore previous kdbus mask */
+       kdbus_sysfs_set_parameter_mask(env->mask_param_path,
+                                      old_kdbus_flags_mask);
+       ASSERT_RETURN(ret == 0);
+
+       /*
+        * Test the Bus creator info and its attach flags
+        */
+       ret = kdbus_test_bus_creator_info(env);
+       /* Restore previous kdbus mask */
+       kdbus_sysfs_set_parameter_mask(env->mask_param_path,
+                                      old_kdbus_flags_mask);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_sysfs_get_parameter_mask(env->mask_param_path,
+                                            &flags_mask);
+       ASSERT_RETURN(ret == 0 && old_kdbus_flags_mask == flags_mask);
+
+       return TEST_OK;
+}
diff --git a/tools/testing/selftests/kdbus/test-benchmark.c b/tools/testing/selftests/kdbus/test-benchmark.c
new file mode 100644 (file)
index 0000000..8a9744b
--- /dev/null
@@ -0,0 +1,451 @@
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <fcntl.h>
+#include <locale.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <assert.h>
+#include <poll.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <math.h>
+
+#include "kdbus-api.h"
+#include "kdbus-test.h"
+#include "kdbus-util.h"
+#include "kdbus-enum.h"
+
+#define SERVICE_NAME "foo.bar.echo"
+
+/*
+ * To have a banchmark comparison with unix socket, set:
+ * user_memfd  = false;
+ * compare_uds = true;
+ * attach_none = true;         do not attached metadata
+ */
+
+static bool use_memfd = true;          /* transmit memfd? */
+static bool compare_uds = false;               /* unix-socket comparison? */
+static bool attach_none = false;               /* clear attach-flags? */
+static char stress_payload[8192];
+
+struct stats {
+       uint64_t count;
+       uint64_t latency_acc;
+       uint64_t latency_low;
+       uint64_t latency_high;
+       uint64_t latency_avg;
+       uint64_t latency_ssquares;
+};
+
+static struct stats stats;
+
+static void reset_stats(void)
+{
+       stats.count = 0;
+       stats.latency_acc = 0;
+       stats.latency_low = UINT64_MAX;
+       stats.latency_high = 0;
+       stats.latency_avg = 0;
+       stats.latency_ssquares = 0;
+}
+
+static void dump_stats(bool is_uds)
+{
+       if (stats.count > 0) {
+               kdbus_printf("stats %s: %'llu packets processed, latency (nsecs) min/max/avg/dev %'7llu // %'7llu // %'7llu // %'7.f\n",
+                            is_uds ? " (UNIX)" : "(KDBUS)",
+                            (unsigned long long) stats.count,
+                            (unsigned long long) stats.latency_low,
+                            (unsigned long long) stats.latency_high,
+                            (unsigned long long) stats.latency_avg,
+                            sqrt(stats.latency_ssquares / stats.count));
+       } else {
+               kdbus_printf("*** no packets received. bus stuck?\n");
+       }
+}
+
+static void add_stats(uint64_t prev)
+{
+       uint64_t diff, latency_avg_prev;
+
+       diff = now(CLOCK_THREAD_CPUTIME_ID) - prev;
+
+       stats.count++;
+       stats.latency_acc += diff;
+
+       /* see Welford62 */
+       latency_avg_prev = stats.latency_avg;
+       stats.latency_avg = stats.latency_acc / stats.count;
+       stats.latency_ssquares += (diff - latency_avg_prev) * (diff - stats.latency_avg);
+
+       if (stats.latency_low > diff)
+               stats.latency_low = diff;
+
+       if (stats.latency_high < diff)
+               stats.latency_high = diff;
+}
+
+static int setup_simple_kdbus_msg(struct kdbus_conn *conn,
+                                 uint64_t dst_id,
+                                 struct kdbus_msg **msg_out)
+{
+       struct kdbus_msg *msg;
+       struct kdbus_item *item;
+       uint64_t size;
+
+       size = sizeof(struct kdbus_msg);
+       size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_vec));
+
+       msg = malloc(size);
+       ASSERT_RETURN_VAL(msg, -ENOMEM);
+
+       memset(msg, 0, size);
+       msg->size = size;
+       msg->src_id = conn->id;
+       msg->dst_id = dst_id;
+       msg->payload_type = KDBUS_PAYLOAD_DBUS;
+
+       item = msg->items;
+
+       item->type = KDBUS_ITEM_PAYLOAD_VEC;
+       item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(struct kdbus_vec);
+       item->vec.address = (uintptr_t) stress_payload;
+       item->vec.size = sizeof(stress_payload);
+       item = KDBUS_ITEM_NEXT(item);
+
+       *msg_out = msg;
+
+       return 0;
+}
+
+static int setup_memfd_kdbus_msg(struct kdbus_conn *conn,
+                                uint64_t dst_id,
+                                off_t *memfd_item_offset,
+                                struct kdbus_msg **msg_out)
+{
+       struct kdbus_msg *msg;
+       struct kdbus_item *item;
+       uint64_t size;
+
+       size = sizeof(struct kdbus_msg);
+       size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_vec));
+       size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_memfd));
+
+       msg = malloc(size);
+       ASSERT_RETURN_VAL(msg, -ENOMEM);
+
+       memset(msg, 0, size);
+       msg->size = size;
+       msg->src_id = conn->id;
+       msg->dst_id = dst_id;
+       msg->payload_type = KDBUS_PAYLOAD_DBUS;
+
+       item = msg->items;
+
+       item->type = KDBUS_ITEM_PAYLOAD_VEC;
+       item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(struct kdbus_vec);
+       item->vec.address = (uintptr_t) stress_payload;
+       item->vec.size = sizeof(stress_payload);
+       item = KDBUS_ITEM_NEXT(item);
+
+       item->type = KDBUS_ITEM_PAYLOAD_MEMFD;
+       item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(struct kdbus_memfd);
+       item->memfd.size = sizeof(uint64_t);
+
+       *memfd_item_offset = (unsigned char *)item - (unsigned char *)msg;
+       *msg_out = msg;
+
+       return 0;
+}
+
+static int
+send_echo_request(struct kdbus_conn *conn, uint64_t dst_id,
+                 void *kdbus_msg, off_t memfd_item_offset)
+{
+       struct kdbus_cmd_send cmd = {};
+       int memfd = -1;
+       int ret;
+
+       if (use_memfd) {
+               uint64_t now_ns = now(CLOCK_THREAD_CPUTIME_ID);
+               struct kdbus_item *item = memfd_item_offset + kdbus_msg;
+               memfd = sys_memfd_create("memfd-name", 0);
+               ASSERT_RETURN_VAL(memfd >= 0, memfd);
+
+               ret = write(memfd, &now_ns, sizeof(now_ns));
+               ASSERT_RETURN_VAL(ret == sizeof(now_ns), -EAGAIN);
+
+               ret = sys_memfd_seal_set(memfd);
+               ASSERT_RETURN_VAL(ret == 0, -errno);
+
+               item->memfd.fd = memfd;
+       }
+
+       cmd.size = sizeof(cmd);
+       cmd.msg_address = (uintptr_t)kdbus_msg;
+
+       ret = kdbus_cmd_send(conn->fd, &cmd);
+       ASSERT_RETURN_VAL(ret == 0, ret);
+
+       close(memfd);
+
+       return 0;
+}
+
+static int
+handle_echo_reply(struct kdbus_conn *conn, uint64_t send_ns)
+{
+       int ret;
+       struct kdbus_cmd_recv recv = { .size = sizeof(recv) };
+       struct kdbus_msg *msg;
+       const struct kdbus_item *item;
+       bool has_memfd = false;
+
+       ret = kdbus_cmd_recv(conn->fd, &recv);
+       if (ret == -EAGAIN)
+               return ret;
+
+       ASSERT_RETURN_VAL(ret == 0, ret);
+
+       if (!use_memfd)
+               goto out;
+
+       msg = (struct kdbus_msg *)(conn->buf + recv.msg.offset);
+
+       KDBUS_ITEM_FOREACH(item, msg, items) {
+               switch (item->type) {
+               case KDBUS_ITEM_PAYLOAD_MEMFD: {
+                       char *buf;
+
+                       buf = mmap(NULL, item->memfd.size, PROT_READ,
+                                  MAP_PRIVATE, item->memfd.fd, 0);
+                       ASSERT_RETURN_VAL(buf != MAP_FAILED, -EINVAL);
+                       ASSERT_RETURN_VAL(item->memfd.size == sizeof(uint64_t),
+                                         -EINVAL);
+
+                       add_stats(*(uint64_t*)buf);
+                       munmap(buf, item->memfd.size);
+                       close(item->memfd.fd);
+                       has_memfd = true;
+                       break;
+               }
+
+               case KDBUS_ITEM_PAYLOAD_OFF:
+                       /* ignore */
+                       break;
+               }
+       }
+
+out:
+       if (!has_memfd)
+               add_stats(send_ns);
+
+       ret = kdbus_free(conn, recv.msg.offset);
+       ASSERT_RETURN_VAL(ret == 0, -errno);
+
+       return 0;
+}
+
+static int benchmark(struct kdbus_test_env *env)
+{
+       static char buf[sizeof(stress_payload)];
+       struct kdbus_msg *kdbus_msg = NULL;
+       off_t memfd_cached_offset = 0;
+       int ret;
+       struct kdbus_conn *conn_a, *conn_b;
+       struct pollfd fds[2];
+       uint64_t start, send_ns, now_ns, diff;
+       unsigned int i;
+       int uds[2];
+
+       setlocale(LC_ALL, "");
+
+       for (i = 0; i < sizeof(stress_payload); i++)
+               stress_payload[i] = i;
+
+       /* setup kdbus pair */
+
+       conn_a = kdbus_hello(env->buspath, 0, NULL, 0);
+       conn_b = kdbus_hello(env->buspath, 0, NULL, 0);
+       ASSERT_RETURN(conn_a && conn_b);
+
+       ret = kdbus_add_match_empty(conn_a);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_add_match_empty(conn_b);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_name_acquire(conn_a, SERVICE_NAME, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       if (attach_none) {
+               ret = kdbus_conn_update_attach_flags(conn_a,
+                                                    _KDBUS_ATTACH_ALL,
+                                                    0);
+               ASSERT_RETURN(ret == 0);
+       }
+
+       /* setup UDS pair */
+
+       ret = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK, 0, uds);
+       ASSERT_RETURN(ret == 0);
+
+       /* setup a kdbus msg now */
+       if (use_memfd) {
+               ret = setup_memfd_kdbus_msg(conn_b, conn_a->id,
+                                           &memfd_cached_offset,
+                                           &kdbus_msg);
+               ASSERT_RETURN(ret == 0);
+       } else {
+               ret = setup_simple_kdbus_msg(conn_b, conn_a->id, &kdbus_msg);
+               ASSERT_RETURN(ret == 0);
+       }
+
+       /* start benchmark */
+
+       kdbus_printf("-- entering poll loop ...\n");
+
+       do {
+               /* run kdbus benchmark */
+               fds[0].fd = conn_a->fd;
+               fds[1].fd = conn_b->fd;
+
+               /* cancel any pending message */
+               handle_echo_reply(conn_a, 0);
+
+               start = now(CLOCK_THREAD_CPUTIME_ID);
+               reset_stats();
+
+               send_ns = now(CLOCK_THREAD_CPUTIME_ID);
+               ret = send_echo_request(conn_b, conn_a->id,
+                                       kdbus_msg, memfd_cached_offset);
+               ASSERT_RETURN(ret == 0);
+
+               while (1) {
+                       unsigned int nfds = sizeof(fds) / sizeof(fds[0]);
+                       unsigned int i;
+
+                       for (i = 0; i < nfds; i++) {
+                               fds[i].events = POLLIN | POLLPRI | POLLHUP;
+                               fds[i].revents = 0;
+                       }
+
+                       ret = poll(fds, nfds, 10);
+                       if (ret < 0)
+                               break;
+
+                       if (fds[0].revents & POLLIN) {
+                               ret = handle_echo_reply(conn_a, send_ns);
+                               ASSERT_RETURN(ret == 0);
+
+                               send_ns = now(CLOCK_THREAD_CPUTIME_ID);
+                               ret = send_echo_request(conn_b, conn_a->id,
+                                                       kdbus_msg,
+                                                       memfd_cached_offset);
+                               ASSERT_RETURN(ret == 0);
+                       }
+
+                       now_ns = now(CLOCK_THREAD_CPUTIME_ID);
+                       diff = now_ns - start;
+                       if (diff > 1000000000ULL) {
+                               start = now_ns;
+
+                               dump_stats(false);
+                               break;
+                       }
+               }
+
+               if (!compare_uds)
+                       continue;
+
+               /* run unix-socket benchmark as comparison */
+
+               fds[0].fd = uds[0];
+               fds[1].fd = uds[1];
+
+               /* cancel any pendign message */
+               read(uds[1], buf, sizeof(buf));
+
+               start = now(CLOCK_THREAD_CPUTIME_ID);
+               reset_stats();
+
+               send_ns = now(CLOCK_THREAD_CPUTIME_ID);
+               ret = write(uds[0], stress_payload, sizeof(stress_payload));
+               ASSERT_RETURN(ret == sizeof(stress_payload));
+
+               while (1) {
+                       unsigned int nfds = sizeof(fds) / sizeof(fds[0]);
+                       unsigned int i;
+
+                       for (i = 0; i < nfds; i++) {
+                               fds[i].events = POLLIN | POLLPRI | POLLHUP;
+                               fds[i].revents = 0;
+                       }
+
+                       ret = poll(fds, nfds, 10);
+                       if (ret < 0)
+                               break;
+
+                       if (fds[1].revents & POLLIN) {
+                               ret = read(uds[1], buf, sizeof(buf));
+                               ASSERT_RETURN(ret == sizeof(buf));
+
+                               add_stats(send_ns);
+
+                               send_ns = now(CLOCK_THREAD_CPUTIME_ID);
+                               ret = write(uds[0], buf, sizeof(buf));
+                               ASSERT_RETURN(ret == sizeof(buf));
+                       }
+
+                       now_ns = now(CLOCK_THREAD_CPUTIME_ID);
+                       diff = now_ns - start;
+                       if (diff > 1000000000ULL) {
+                               start = now_ns;
+
+                               dump_stats(true);
+                               break;
+                       }
+               }
+
+       } while (kdbus_util_verbose);
+
+       kdbus_printf("-- closing bus connections\n");
+
+       free(kdbus_msg);
+
+       kdbus_conn_free(conn_a);
+       kdbus_conn_free(conn_b);
+
+       return (stats.count > 1) ? TEST_OK : TEST_ERR;
+}
+
+int kdbus_test_benchmark(struct kdbus_test_env *env)
+{
+       use_memfd = true;
+       attach_none = false;
+       compare_uds = false;
+       return benchmark(env);
+}
+
+int kdbus_test_benchmark_nomemfds(struct kdbus_test_env *env)
+{
+       use_memfd = false;
+       attach_none = false;
+       compare_uds = false;
+       return benchmark(env);
+}
+
+int kdbus_test_benchmark_uds(struct kdbus_test_env *env)
+{
+       use_memfd = false;
+       attach_none = true;
+       compare_uds = true;
+       return benchmark(env);
+}
diff --git a/tools/testing/selftests/kdbus/test-bus.c b/tools/testing/selftests/kdbus/test-bus.c
new file mode 100644 (file)
index 0000000..762fb30
--- /dev/null
@@ -0,0 +1,175 @@
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <errno.h>
+#include <assert.h>
+#include <limits.h>
+#include <sys/mman.h>
+#include <stdbool.h>
+
+#include "kdbus-api.h"
+#include "kdbus-util.h"
+#include "kdbus-enum.h"
+#include "kdbus-test.h"
+
+static struct kdbus_item *kdbus_get_item(struct kdbus_info *info,
+                                        uint64_t type)
+{
+       struct kdbus_item *item;
+
+       KDBUS_ITEM_FOREACH(item, info, items)
+               if (item->type == type)
+                       return item;
+
+       return NULL;
+}
+
+static int test_bus_creator_info(const char *bus_path)
+{
+       int ret;
+       uint64_t offset;
+       struct kdbus_conn *conn;
+       struct kdbus_info *info;
+       struct kdbus_item *item;
+       char *tmp, *busname;
+
+       /* extract the bus-name from @bus_path */
+       tmp = strdup(bus_path);
+       ASSERT_RETURN(tmp);
+       busname = strrchr(tmp, '/');
+       ASSERT_RETURN(busname);
+       *busname = 0;
+       busname = strrchr(tmp, '/');
+       ASSERT_RETURN(busname);
+       ++busname;
+
+       conn = kdbus_hello(bus_path, 0, NULL, 0);
+       ASSERT_RETURN(conn);
+
+       ret = kdbus_bus_creator_info(conn, _KDBUS_ATTACH_ALL, &offset);
+       ASSERT_RETURN(ret == 0);
+
+       info = (struct kdbus_info *)(conn->buf + offset);
+
+       item = kdbus_get_item(info, KDBUS_ITEM_MAKE_NAME);
+       ASSERT_RETURN(item);
+       ASSERT_RETURN(!strcmp(item->str, busname));
+
+       ret = kdbus_free(conn, offset);
+       ASSERT_RETURN_VAL(ret == 0, ret);
+
+       free(tmp);
+       kdbus_conn_free(conn);
+       return 0;
+}
+
+int kdbus_test_bus_make(struct kdbus_test_env *env)
+{
+       struct {
+               struct kdbus_cmd cmd;
+
+               /* bloom size item */
+               struct {
+                       uint64_t size;
+                       uint64_t type;
+                       struct kdbus_bloom_parameter bloom;
+               } bs;
+
+               /* name item */
+               uint64_t n_size;
+               uint64_t n_type;
+               char name[64];
+       } bus_make;
+       char s[PATH_MAX], *name;
+       int ret, control_fd2;
+       uid_t uid;
+
+       name = unique_name("");
+       ASSERT_RETURN(name);
+
+       snprintf(s, sizeof(s), "%s/control", env->root);
+       env->control_fd = open(s, O_RDWR|O_CLOEXEC);
+       ASSERT_RETURN(env->control_fd >= 0);
+
+       control_fd2 = open(s, O_RDWR|O_CLOEXEC);
+       ASSERT_RETURN(control_fd2 >= 0);
+
+       memset(&bus_make, 0, sizeof(bus_make));
+
+       bus_make.bs.size = sizeof(bus_make.bs);
+       bus_make.bs.type = KDBUS_ITEM_BLOOM_PARAMETER;
+       bus_make.bs.bloom.size = 64;
+       bus_make.bs.bloom.n_hash = 1;
+
+       bus_make.n_type = KDBUS_ITEM_MAKE_NAME;
+
+       uid = getuid();
+
+       /* missing uid prefix */
+       snprintf(bus_make.name, sizeof(bus_make.name), "foo");
+       bus_make.n_size = KDBUS_ITEM_HEADER_SIZE + strlen(bus_make.name) + 1;
+       bus_make.cmd.size = sizeof(struct kdbus_cmd) +
+                           sizeof(bus_make.bs) + bus_make.n_size;
+       ret = kdbus_cmd_bus_make(env->control_fd, &bus_make.cmd);
+       ASSERT_RETURN(ret == -EINVAL);
+
+       /* non alphanumeric character */
+       snprintf(bus_make.name, sizeof(bus_make.name), "%u-blah@123", uid);
+       bus_make.n_size = KDBUS_ITEM_HEADER_SIZE + strlen(bus_make.name) + 1;
+       bus_make.cmd.size = sizeof(struct kdbus_cmd) +
+                           sizeof(bus_make.bs) + bus_make.n_size;
+       ret = kdbus_cmd_bus_make(env->control_fd, &bus_make.cmd);
+       ASSERT_RETURN(ret == -EINVAL);
+
+       /* '-' at the end */
+       snprintf(bus_make.name, sizeof(bus_make.name), "%u-blah-", uid);
+       bus_make.n_size = KDBUS_ITEM_HEADER_SIZE + strlen(bus_make.name) + 1;
+       bus_make.cmd.size = sizeof(struct kdbus_cmd) +
+                           sizeof(bus_make.bs) + bus_make.n_size;
+       ret = kdbus_cmd_bus_make(env->control_fd, &bus_make.cmd);
+       ASSERT_RETURN(ret == -EINVAL);
+
+       /* create a new bus */
+       snprintf(bus_make.name, sizeof(bus_make.name), "%u-%s-1", uid, name);
+       bus_make.n_size = KDBUS_ITEM_HEADER_SIZE + strlen(bus_make.name) + 1;
+       bus_make.cmd.size = sizeof(struct kdbus_cmd) +
+                           sizeof(bus_make.bs) + bus_make.n_size;
+       ret = kdbus_cmd_bus_make(env->control_fd, &bus_make.cmd);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_cmd_bus_make(control_fd2, &bus_make.cmd);
+       ASSERT_RETURN(ret == -EEXIST);
+
+       snprintf(s, sizeof(s), "%s/%u-%s-1/bus", env->root, uid, name);
+       ASSERT_RETURN(access(s, F_OK) == 0);
+
+       ret = test_bus_creator_info(s);
+       ASSERT_RETURN(ret == 0);
+
+       /* can't use the same fd for bus make twice, even though a different
+        * bus name is used
+        */
+       snprintf(bus_make.name, sizeof(bus_make.name), "%u-%s-2", uid, name);
+       bus_make.n_size = KDBUS_ITEM_HEADER_SIZE + strlen(bus_make.name) + 1;
+       bus_make.cmd.size = sizeof(struct kdbus_cmd) +
+                           sizeof(bus_make.bs) + bus_make.n_size;
+       ret = kdbus_cmd_bus_make(env->control_fd, &bus_make.cmd);
+       ASSERT_RETURN(ret == -EBADFD);
+
+       /* create a new bus, with different fd and different bus name */
+       snprintf(bus_make.name, sizeof(bus_make.name), "%u-%s-2", uid, name);
+       bus_make.n_size = KDBUS_ITEM_HEADER_SIZE + strlen(bus_make.name) + 1;
+       bus_make.cmd.size = sizeof(struct kdbus_cmd) +
+                           sizeof(bus_make.bs) + bus_make.n_size;
+       ret = kdbus_cmd_bus_make(control_fd2, &bus_make.cmd);
+       ASSERT_RETURN(ret == 0);
+
+       close(control_fd2);
+       free(name);
+
+       return TEST_OK;
+}
diff --git a/tools/testing/selftests/kdbus/test-chat.c b/tools/testing/selftests/kdbus/test-chat.c
new file mode 100644 (file)
index 0000000..e19f595
--- /dev/null
@@ -0,0 +1,122 @@
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <errno.h>
+#include <assert.h>
+#include <poll.h>
+#include <stdbool.h>
+
+#include "kdbus-test.h"
+#include "kdbus-util.h"
+#include "kdbus-enum.h"
+
+int kdbus_test_chat(struct kdbus_test_env *env)
+{
+       int ret, cookie;
+       struct kdbus_conn *conn_a, *conn_b;
+       struct pollfd fds[2];
+       uint64_t flags;
+       int count;
+
+       conn_a = kdbus_hello(env->buspath, 0, NULL, 0);
+       conn_b = kdbus_hello(env->buspath, 0, NULL, 0);
+       ASSERT_RETURN(conn_a && conn_b);
+
+       flags = KDBUS_NAME_ALLOW_REPLACEMENT;
+       ret = kdbus_name_acquire(conn_a, "foo.bar.test", &flags);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_name_acquire(conn_a, "foo.bar.baz", NULL);
+       ASSERT_RETURN(ret == 0);
+
+       flags = KDBUS_NAME_QUEUE;
+       ret = kdbus_name_acquire(conn_b, "foo.bar.baz", &flags);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_name_acquire(conn_a, "foo.bar.double", NULL);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_name_acquire(conn_a, "foo.bar.double", NULL);
+       ASSERT_RETURN(ret == -EALREADY);
+
+       ret = kdbus_name_release(conn_a, "foo.bar.double");
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_name_release(conn_a, "foo.bar.double");
+       ASSERT_RETURN(ret == -ESRCH);
+
+       ret = kdbus_list(conn_b, KDBUS_LIST_UNIQUE |
+                                KDBUS_LIST_NAMES  |
+                                KDBUS_LIST_QUEUED |
+                                KDBUS_LIST_ACTIVATORS);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_add_match_empty(conn_a);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_add_match_empty(conn_b);
+       ASSERT_RETURN(ret == 0);
+
+       cookie = 0;
+       ret = kdbus_msg_send(conn_b, NULL, 0xc0000000 | cookie, 0, 0, 0,
+                            KDBUS_DST_ID_BROADCAST, 0, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       fds[0].fd = conn_a->fd;
+       fds[1].fd = conn_b->fd;
+
+       kdbus_printf("-- entering poll loop ...\n");
+
+       for (count = 0;; count++) {
+               int i, nfds = sizeof(fds) / sizeof(fds[0]);
+
+               for (i = 0; i < nfds; i++) {
+                       fds[i].events = POLLIN | POLLPRI | POLLHUP;
+                       fds[i].revents = 0;
+               }
+
+               ret = poll(fds, nfds, 3000);
+               ASSERT_RETURN(ret >= 0);
+
+               if (fds[0].revents & POLLIN) {
+                       if (count > 2)
+                               kdbus_name_release(conn_a, "foo.bar.baz");
+
+                       ret = kdbus_msg_recv(conn_a, NULL, NULL);
+                       ASSERT_RETURN(ret == 0);
+                       ret = kdbus_msg_send(conn_a, NULL,
+                                            0xc0000000 | cookie++,
+                                            0, 0, 0, conn_b->id, 0, NULL);
+                       ASSERT_RETURN(ret == 0);
+               }
+
+               if (fds[1].revents & POLLIN) {
+                       ret = kdbus_msg_recv(conn_b, NULL, NULL);
+                       ASSERT_RETURN(ret == 0);
+                       ret = kdbus_msg_send(conn_b, NULL,
+                                            0xc0000000 | cookie++,
+                                            0, 0, 0, conn_a->id, 0, NULL);
+                       ASSERT_RETURN(ret == 0);
+               }
+
+               ret = kdbus_list(conn_b, KDBUS_LIST_UNIQUE |
+                                        KDBUS_LIST_NAMES  |
+                                        KDBUS_LIST_QUEUED |
+                                        KDBUS_LIST_ACTIVATORS);
+               ASSERT_RETURN(ret == 0);
+
+               if (count > 10)
+                       break;
+       }
+
+       kdbus_printf("-- closing bus connections\n");
+       kdbus_conn_free(conn_a);
+       kdbus_conn_free(conn_b);
+
+       return TEST_OK;
+}
diff --git a/tools/testing/selftests/kdbus/test-connection.c b/tools/testing/selftests/kdbus/test-connection.c
new file mode 100644 (file)
index 0000000..7afb76f
--- /dev/null
@@ -0,0 +1,618 @@
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <errno.h>
+#include <assert.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/capability.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <sys/wait.h>
+#include <stdbool.h>
+
+#include "kdbus-api.h"
+#include "kdbus-util.h"
+#include "kdbus-enum.h"
+#include "kdbus-test.h"
+
+int kdbus_test_hello(struct kdbus_test_env *env)
+{
+       struct kdbus_cmd_free cmd_free = {};
+       struct kdbus_cmd_hello hello;
+       int fd, ret;
+
+       memset(&hello, 0, sizeof(hello));
+
+       fd = open(env->buspath, O_RDWR|O_CLOEXEC);
+       ASSERT_RETURN(fd >= 0);
+
+       hello.flags = KDBUS_HELLO_ACCEPT_FD;
+       hello.attach_flags_send = _KDBUS_ATTACH_ALL;
+       hello.attach_flags_recv = _KDBUS_ATTACH_ALL;
+       hello.size = sizeof(struct kdbus_cmd_hello);
+       hello.pool_size = POOL_SIZE;
+
+       /* an unaligned hello must result in -EFAULT */
+       ret = kdbus_cmd_hello(fd, (struct kdbus_cmd_hello *) ((char *) &hello + 1));
+       ASSERT_RETURN(ret == -EFAULT);
+
+       /* a size of 0 must return EMSGSIZE */
+       hello.size = 1;
+       hello.flags = KDBUS_HELLO_ACCEPT_FD;
+       hello.attach_flags_send = _KDBUS_ATTACH_ALL;
+       ret = kdbus_cmd_hello(fd, &hello);
+       ASSERT_RETURN(ret == -EINVAL);
+
+       hello.size = sizeof(struct kdbus_cmd_hello);
+
+       /* check faulty flags */
+       hello.flags = 1ULL << 32;
+       hello.attach_flags_send = _KDBUS_ATTACH_ALL;
+       ret = kdbus_cmd_hello(fd, &hello);
+       ASSERT_RETURN(ret == -EINVAL);
+
+       /* check for faulty pool sizes */
+       hello.pool_size = 0;
+       hello.flags = KDBUS_HELLO_ACCEPT_FD;
+       hello.attach_flags_send = _KDBUS_ATTACH_ALL;
+       ret = kdbus_cmd_hello(fd, &hello);
+       ASSERT_RETURN(ret == -EINVAL);
+
+       hello.pool_size = 4097;
+       hello.attach_flags_send = _KDBUS_ATTACH_ALL;
+       ret = kdbus_cmd_hello(fd, &hello);
+       ASSERT_RETURN(ret == -EINVAL);
+
+       hello.pool_size = POOL_SIZE;
+
+       /*
+        * The connection created by the core requires ALL meta flags
+        * to be sent. An attempt to send less than that should result in
+        * -ECONNREFUSED.
+        */
+       hello.attach_flags_send = _KDBUS_ATTACH_ALL & ~KDBUS_ATTACH_TIMESTAMP;
+       ret = kdbus_cmd_hello(fd, &hello);
+       ASSERT_RETURN(ret == -ECONNREFUSED);
+
+       hello.attach_flags_send = _KDBUS_ATTACH_ALL;
+       hello.offset = (__u64)-1;
+
+       /* success test */
+       ret = kdbus_cmd_hello(fd, &hello);
+       ASSERT_RETURN(ret == 0);
+
+       /* The kernel should have returned some items */
+       ASSERT_RETURN(hello.offset != (__u64)-1);
+       cmd_free.size = sizeof(cmd_free);
+       cmd_free.offset = hello.offset;
+       ret = kdbus_cmd_free(fd, &cmd_free);
+       ASSERT_RETURN(ret >= 0);
+
+       close(fd);
+
+       fd = open(env->buspath, O_RDWR|O_CLOEXEC);
+       ASSERT_RETURN(fd >= 0);
+
+       /* no ACTIVATOR flag without a name */
+       hello.flags = KDBUS_HELLO_ACTIVATOR;
+       ret = kdbus_cmd_hello(fd, &hello);
+       ASSERT_RETURN(ret == -EINVAL);
+
+       close(fd);
+
+       return TEST_OK;
+}
+
+int kdbus_test_byebye(struct kdbus_test_env *env)
+{
+       struct kdbus_conn *conn;
+       struct kdbus_cmd_recv cmd_recv = { .size = sizeof(cmd_recv) };
+       struct kdbus_cmd cmd_byebye = { .size = sizeof(cmd_byebye) };
+       int ret;
+
+       /* create a 2nd connection */
+       conn = kdbus_hello(env->buspath, 0, NULL, 0);
+       ASSERT_RETURN(conn != NULL);
+
+       ret = kdbus_add_match_empty(conn);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_add_match_empty(env->conn);
+       ASSERT_RETURN(ret == 0);
+
+       /* send over 1st connection */
+       ret = kdbus_msg_send(env->conn, NULL, 0, 0, 0, 0,
+                            KDBUS_DST_ID_BROADCAST, 0, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       /* say byebye on the 2nd, which must fail */
+       ret = kdbus_cmd_byebye(conn->fd, &cmd_byebye);
+       ASSERT_RETURN(ret == -EBUSY);
+
+       /* receive the message */
+       ret = kdbus_cmd_recv(conn->fd, &cmd_recv);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_free(conn, cmd_recv.msg.offset);
+       ASSERT_RETURN(ret == 0);
+
+       /* and try again */
+       ret = kdbus_cmd_byebye(conn->fd, &cmd_byebye);
+       ASSERT_RETURN(ret == 0);
+
+       /* a 2nd try should result in -ECONNRESET */
+       ret = kdbus_cmd_byebye(conn->fd, &cmd_byebye);
+       ASSERT_RETURN(ret == -ECONNRESET);
+
+       kdbus_conn_free(conn);
+
+       return TEST_OK;
+}
+
+/* Get only the first item */
+static struct kdbus_item *kdbus_get_item(struct kdbus_info *info,
+                                        uint64_t type)
+{
+       struct kdbus_item *item;
+
+       KDBUS_ITEM_FOREACH(item, info, items)
+               if (item->type == type)
+                       return item;
+
+       return NULL;
+}
+
+static unsigned int kdbus_count_item(struct kdbus_info *info,
+                                    uint64_t type)
+{
+       unsigned int i = 0;
+       const struct kdbus_item *item;
+
+       KDBUS_ITEM_FOREACH(item, info, items)
+               if (item->type == type)
+                       i++;
+
+       return i;
+}
+
+static int kdbus_fuzz_conn_info(struct kdbus_test_env *env, int capable)
+{
+       int ret;
+       unsigned int cnt = 0;
+       uint64_t offset = 0;
+       uint64_t kdbus_flags_mask;
+       struct kdbus_info *info;
+       struct kdbus_conn *conn;
+       struct kdbus_conn *privileged;
+       const struct kdbus_item *item;
+       uint64_t valid_flags_set;
+       uint64_t invalid_flags_set;
+       uint64_t valid_flags = KDBUS_ATTACH_NAMES |
+                              KDBUS_ATTACH_CREDS |
+                              KDBUS_ATTACH_PIDS |
+                              KDBUS_ATTACH_CONN_DESCRIPTION;
+
+       uint64_t invalid_flags = KDBUS_ATTACH_NAMES     |
+                                KDBUS_ATTACH_CREDS     |
+                                KDBUS_ATTACH_PIDS      |
+                                KDBUS_ATTACH_CAPS      |
+                                KDBUS_ATTACH_CGROUP    |
+                                KDBUS_ATTACH_CONN_DESCRIPTION;
+
+       struct kdbus_creds cached_creds;
+       uid_t ruid, euid, suid;
+       gid_t rgid, egid, sgid;
+
+       getresuid(&ruid, &euid, &suid);
+       getresgid(&rgid, &egid, &sgid);
+
+       cached_creds.uid = ruid;
+       cached_creds.euid = euid;
+       cached_creds.suid = suid;
+       cached_creds.fsuid = ruid;
+
+       cached_creds.gid = rgid;
+       cached_creds.egid = egid;
+       cached_creds.sgid = sgid;
+       cached_creds.fsgid = rgid;
+
+       struct kdbus_pids cached_pids = {
+               .pid    = getpid(),
+               .tid    = syscall(SYS_gettid),
+               .ppid   = getppid(),
+       };
+
+       ret = kdbus_sysfs_get_parameter_mask(env->mask_param_path,
+                                            &kdbus_flags_mask);
+       ASSERT_RETURN(ret == 0);
+
+       valid_flags_set = valid_flags & kdbus_flags_mask;
+       invalid_flags_set = invalid_flags & kdbus_flags_mask;
+
+       ret = kdbus_conn_info(env->conn, env->conn->id, NULL,
+                             valid_flags, &offset);
+       ASSERT_RETURN(ret == 0);
+
+       info = (struct kdbus_info *)(env->conn->buf + offset);
+       ASSERT_RETURN(info->id == env->conn->id);
+
+       /* We do not have any well-known name */
+       item = kdbus_get_item(info, KDBUS_ITEM_NAME);
+       ASSERT_RETURN(item == NULL);
+
+       item = kdbus_get_item(info, KDBUS_ITEM_CONN_DESCRIPTION);
+       if (valid_flags_set & KDBUS_ATTACH_CONN_DESCRIPTION) {
+               ASSERT_RETURN(item);
+       } else {
+               ASSERT_RETURN(item == NULL);
+       }
+
+       kdbus_free(env->conn, offset);
+
+       conn = kdbus_hello(env->buspath, 0, NULL, 0);
+       ASSERT_RETURN(conn);
+
+       privileged = kdbus_hello(env->buspath, 0, NULL, 0);
+       ASSERT_RETURN(privileged);
+
+       ret = kdbus_conn_info(conn, conn->id, NULL, valid_flags, &offset);
+       ASSERT_RETURN(ret == 0);
+
+       info = (struct kdbus_info *)(conn->buf + offset);
+       ASSERT_RETURN(info->id == conn->id);
+
+       /* We do not have any well-known name */
+       item = kdbus_get_item(info, KDBUS_ITEM_NAME);
+       ASSERT_RETURN(item == NULL);
+
+       cnt = kdbus_count_item(info, KDBUS_ITEM_CREDS);
+       if (valid_flags_set & KDBUS_ATTACH_CREDS) {
+               ASSERT_RETURN(cnt == 1);
+
+               item = kdbus_get_item(info, KDBUS_ITEM_CREDS);
+               ASSERT_RETURN(item);
+
+               /* Compare received items with cached creds */
+               ASSERT_RETURN(memcmp(&item->creds, &cached_creds,
+                                     sizeof(struct kdbus_creds)) == 0);
+       } else {
+               ASSERT_RETURN(cnt == 0);
+       }
+
+       item = kdbus_get_item(info, KDBUS_ITEM_PIDS);
+       if (valid_flags_set & KDBUS_ATTACH_PIDS) {
+               ASSERT_RETURN(item);
+
+               /* Compare item->pids with cached PIDs */
+               ASSERT_RETURN(item->pids.pid == cached_pids.pid &&
+                             item->pids.tid == cached_pids.tid &&
+                             item->pids.ppid == cached_pids.ppid);
+       } else {
+               ASSERT_RETURN(item == NULL);
+       }
+
+       /* We did not request KDBUS_ITEM_CAPS */
+       item = kdbus_get_item(info, KDBUS_ITEM_CAPS);
+       ASSERT_RETURN(item == NULL);
+
+       kdbus_free(conn, offset);
+
+       ret = kdbus_name_acquire(conn, "com.example.a", NULL);
+       ASSERT_RETURN(ret >= 0);
+
+       ret = kdbus_conn_info(conn, conn->id, NULL, valid_flags, &offset);
+       ASSERT_RETURN(ret == 0);
+
+       info = (struct kdbus_info *)(conn->buf + offset);
+       ASSERT_RETURN(info->id == conn->id);
+
+       item = kdbus_get_item(info, KDBUS_ITEM_OWNED_NAME);
+       if (valid_flags_set & KDBUS_ATTACH_NAMES) {
+               ASSERT_RETURN(item && !strcmp(item->name.name, "com.example.a"));
+       } else {
+               ASSERT_RETURN(item == NULL);
+       }
+
+       kdbus_free(conn, offset);
+
+       ret = kdbus_conn_info(conn, 0, "com.example.a", valid_flags, &offset);
+       ASSERT_RETURN(ret == 0);
+
+       info = (struct kdbus_info *)(conn->buf + offset);
+       ASSERT_RETURN(info->id == conn->id);
+
+       kdbus_free(conn, offset);
+
+       /* does not have the necessary caps to drop to unprivileged */
+       if (!capable)
+               goto continue_test;
+
+       ret = RUN_UNPRIVILEGED(UNPRIV_UID, UNPRIV_GID, ({
+               ret = kdbus_conn_info(conn, conn->id, NULL,
+                                     valid_flags, &offset);
+               ASSERT_EXIT(ret == 0);
+
+               info = (struct kdbus_info *)(conn->buf + offset);
+               ASSERT_EXIT(info->id == conn->id);
+
+               if (valid_flags_set & KDBUS_ATTACH_NAMES) {
+                       item = kdbus_get_item(info, KDBUS_ITEM_OWNED_NAME);
+                       ASSERT_EXIT(item &&
+                                   strcmp(item->name.name,
+                                          "com.example.a") == 0);
+               }
+
+               if (valid_flags_set & KDBUS_ATTACH_CREDS) {
+                       item = kdbus_get_item(info, KDBUS_ITEM_CREDS);
+                       ASSERT_EXIT(item);
+
+                       /* Compare received items with cached creds */
+                       ASSERT_EXIT(memcmp(&item->creds, &cached_creds,
+                                   sizeof(struct kdbus_creds)) == 0);
+               }
+
+               if (valid_flags_set & KDBUS_ATTACH_PIDS) {
+                       item = kdbus_get_item(info, KDBUS_ITEM_PIDS);
+                       ASSERT_EXIT(item);
+
+                       /*
+                        * Compare item->pids with cached pids of
+                        * privileged one.
+                        *
+                        * cmd_info will always return cached pids.
+                        */
+                       ASSERT_EXIT(item->pids.pid == cached_pids.pid &&
+                                   item->pids.tid == cached_pids.tid);
+               }
+
+               kdbus_free(conn, offset);
+
+               /*
+                * Use invalid_flags and make sure that userspace
+                * do not play with us.
+                */
+               ret = kdbus_conn_info(conn, conn->id, NULL,
+                                     invalid_flags, &offset);
+               ASSERT_EXIT(ret == 0);
+
+               /*
+                * Make sure that we return only one creds item and
+                * it points to the cached creds.
+                */
+               cnt = kdbus_count_item(info, KDBUS_ITEM_CREDS);
+               if (invalid_flags_set & KDBUS_ATTACH_CREDS) {
+                       ASSERT_EXIT(cnt == 1);
+
+                       item = kdbus_get_item(info, KDBUS_ITEM_CREDS);
+                       ASSERT_EXIT(item);
+
+                       /* Compare received items with cached creds */
+                       ASSERT_EXIT(memcmp(&item->creds, &cached_creds,
+                                   sizeof(struct kdbus_creds)) == 0);
+               } else {
+                       ASSERT_EXIT(cnt == 0);
+               }
+
+               if (invalid_flags_set & KDBUS_ATTACH_PIDS) {
+                       cnt = kdbus_count_item(info, KDBUS_ITEM_PIDS);
+                       ASSERT_EXIT(cnt == 1);
+
+                       item = kdbus_get_item(info, KDBUS_ITEM_PIDS);
+                       ASSERT_EXIT(item);
+
+                       /* Compare item->pids with cached pids */
+                       ASSERT_EXIT(item->pids.pid == cached_pids.pid &&
+                                   item->pids.tid == cached_pids.tid);
+               }
+
+               cnt = kdbus_count_item(info, KDBUS_ITEM_CGROUP);
+               if (invalid_flags_set & KDBUS_ATTACH_CGROUP) {
+                       ASSERT_EXIT(cnt == 1);
+               } else {
+                       ASSERT_EXIT(cnt == 0);
+               }
+
+               cnt = kdbus_count_item(info, KDBUS_ITEM_CAPS);
+               if (invalid_flags_set & KDBUS_ATTACH_CAPS) {
+                       ASSERT_EXIT(cnt == 1);
+               } else {
+                       ASSERT_EXIT(cnt == 0);
+               }
+
+               kdbus_free(conn, offset);
+       }),
+       ({ 0; }));
+       ASSERT_RETURN(ret == 0);
+
+continue_test:
+
+       /* A second name */
+       ret = kdbus_name_acquire(conn, "com.example.b", NULL);
+       ASSERT_RETURN(ret >= 0);
+
+       ret = kdbus_conn_info(conn, conn->id, NULL, valid_flags, &offset);
+       ASSERT_RETURN(ret == 0);
+
+       info = (struct kdbus_info *)(conn->buf + offset);
+       ASSERT_RETURN(info->id == conn->id);
+
+       cnt = kdbus_count_item(info, KDBUS_ITEM_OWNED_NAME);
+       if (valid_flags_set & KDBUS_ATTACH_NAMES) {
+               ASSERT_RETURN(cnt == 2);
+       } else {
+               ASSERT_RETURN(cnt == 0);
+       }
+
+       kdbus_free(conn, offset);
+
+       ASSERT_RETURN(ret == 0);
+
+       return 0;
+}
+
+int kdbus_test_conn_info(struct kdbus_test_env *env)
+{
+       int ret;
+       int have_caps;
+       struct {
+               struct kdbus_cmd_info cmd_info;
+
+               struct {
+                       uint64_t size;
+                       uint64_t type;
+                       char str[64];
+               } name;
+       } buf;
+
+       buf.cmd_info.size = sizeof(struct kdbus_cmd_info);
+       buf.cmd_info.flags = 0;
+       buf.cmd_info.attach_flags = 0;
+       buf.cmd_info.id = env->conn->id;
+
+       ret = kdbus_conn_info(env->conn, env->conn->id, NULL, 0, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       /* try to pass a name that is longer than the buffer's size */
+       buf.name.size = KDBUS_ITEM_HEADER_SIZE + 1;
+       buf.name.type = KDBUS_ITEM_NAME;
+       strcpy(buf.name.str, "foo.bar.bla");
+
+       buf.cmd_info.id = 0;
+       buf.cmd_info.size = sizeof(buf.cmd_info) + buf.name.size;
+       ret = kdbus_cmd_conn_info(env->conn->fd, (struct kdbus_cmd_info *) &buf);
+       ASSERT_RETURN(ret == -EINVAL);
+
+       /* Pass a non existent name */
+       ret = kdbus_conn_info(env->conn, 0, "non.existent.name", 0, NULL);
+       ASSERT_RETURN(ret == -ESRCH);
+
+       if (!all_uids_gids_are_mapped())
+               return TEST_SKIP;
+
+       /* Test for caps here, so we run the previous test */
+       have_caps = test_is_capable(CAP_SETUID, CAP_SETGID, -1);
+       ASSERT_RETURN(have_caps >= 0);
+
+       ret = kdbus_fuzz_conn_info(env, have_caps);
+       ASSERT_RETURN(ret == 0);
+
+       /* Now if we have skipped some tests then let the user know */
+       if (!have_caps)
+               return TEST_SKIP;
+
+       return TEST_OK;
+}
+
+int kdbus_test_conn_update(struct kdbus_test_env *env)
+{
+       struct kdbus_conn *conn;
+       struct kdbus_msg *msg;
+       int found = 0;
+       int ret;
+
+       /*
+        * kdbus_hello() sets all attach flags. Receive a message by this
+        * connection, and make sure a timestamp item (just to pick one) is
+        * present.
+        */
+       conn = kdbus_hello(env->buspath, 0, NULL, 0);
+       ASSERT_RETURN(conn);
+
+       ret = kdbus_msg_send(env->conn, NULL, 0x12345678, 0, 0, 0, conn->id,
+                            0, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_msg_recv(conn, &msg, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       found = kdbus_item_in_message(msg, KDBUS_ITEM_TIMESTAMP);
+       ASSERT_RETURN(found == 1);
+
+       kdbus_msg_free(msg);
+
+       /*
+        * Now, modify the attach flags and repeat the action. The item must
+        * now be missing.
+        */
+       found = 0;
+
+       ret = kdbus_conn_update_attach_flags(conn,
+                                            _KDBUS_ATTACH_ALL,
+                                            _KDBUS_ATTACH_ALL &
+                                            ~KDBUS_ATTACH_TIMESTAMP);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_msg_send(env->conn, NULL, 0x12345678, 0, 0, 0, conn->id,
+                            0, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_msg_recv(conn, &msg, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       found = kdbus_item_in_message(msg, KDBUS_ITEM_TIMESTAMP);
+       ASSERT_RETURN(found == 0);
+
+       /* Provide a bogus attach_flags value */
+       ret = kdbus_conn_update_attach_flags(conn,
+                                            _KDBUS_ATTACH_ALL + 1,
+                                            _KDBUS_ATTACH_ALL);
+       ASSERT_RETURN(ret == -EINVAL);
+
+       kdbus_msg_free(msg);
+
+       kdbus_conn_free(conn);
+
+       return TEST_OK;
+}
+
+int kdbus_test_writable_pool(struct kdbus_test_env *env)
+{
+       struct kdbus_cmd_free cmd_free = {};
+       struct kdbus_cmd_hello hello;
+       int fd, ret;
+       void *map;
+
+       fd = open(env->buspath, O_RDWR | O_CLOEXEC);
+       ASSERT_RETURN(fd >= 0);
+
+       memset(&hello, 0, sizeof(hello));
+       hello.flags = KDBUS_HELLO_ACCEPT_FD;
+       hello.attach_flags_send = _KDBUS_ATTACH_ALL;
+       hello.attach_flags_recv = _KDBUS_ATTACH_ALL;
+       hello.size = sizeof(struct kdbus_cmd_hello);
+       hello.pool_size = POOL_SIZE;
+       hello.offset = (__u64)-1;
+
+       /* success test */
+       ret = kdbus_cmd_hello(fd, &hello);
+       ASSERT_RETURN(ret == 0);
+
+       /* The kernel should have returned some items */
+       ASSERT_RETURN(hello.offset != (__u64)-1);
+       cmd_free.size = sizeof(cmd_free);
+       cmd_free.offset = hello.offset;
+       ret = kdbus_cmd_free(fd, &cmd_free);
+       ASSERT_RETURN(ret >= 0);
+
+       /* pools cannot be mapped writable */
+       map = mmap(NULL, POOL_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+       ASSERT_RETURN(map == MAP_FAILED);
+
+       /* pools can always be mapped readable */
+       map = mmap(NULL, POOL_SIZE, PROT_READ, MAP_SHARED, fd, 0);
+       ASSERT_RETURN(map != MAP_FAILED);
+
+       /* make sure we cannot change protection masks to writable */
+       ret = mprotect(map, POOL_SIZE, PROT_READ | PROT_WRITE);
+       ASSERT_RETURN(ret < 0);
+
+       munmap(map, POOL_SIZE);
+       close(fd);
+
+       return TEST_OK;
+}
diff --git a/tools/testing/selftests/kdbus/test-daemon.c b/tools/testing/selftests/kdbus/test-daemon.c
new file mode 100644 (file)
index 0000000..8bc2386
--- /dev/null
@@ -0,0 +1,65 @@
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <errno.h>
+#include <assert.h>
+#include <poll.h>
+#include <stdbool.h>
+
+#include "kdbus-test.h"
+#include "kdbus-util.h"
+#include "kdbus-enum.h"
+
+int kdbus_test_daemon(struct kdbus_test_env *env)
+{
+       struct pollfd fds[2];
+       int count;
+       int ret;
+
+       /* This test doesn't make any sense in non-interactive mode */
+       if (!kdbus_util_verbose)
+               return TEST_OK;
+
+       printf("Created connection %llu on bus '%s'\n",
+               (unsigned long long) env->conn->id, env->buspath);
+
+       ret = kdbus_name_acquire(env->conn, "com.example.kdbus-test", NULL);
+       ASSERT_RETURN(ret == 0);
+       printf("  Aquired name: com.example.kdbus-test\n");
+
+       fds[0].fd = env->conn->fd;
+       fds[1].fd = STDIN_FILENO;
+
+       printf("Monitoring connections:\n");
+
+       for (count = 0;; count++) {
+               int i, nfds = sizeof(fds) / sizeof(fds[0]);
+
+               for (i = 0; i < nfds; i++) {
+                       fds[i].events = POLLIN | POLLPRI | POLLHUP;
+                       fds[i].revents = 0;
+               }
+
+               ret = poll(fds, nfds, -1);
+               if (ret <= 0)
+                       break;
+
+               if (fds[0].revents & POLLIN) {
+                       ret = kdbus_msg_recv(env->conn, NULL, NULL);
+                       ASSERT_RETURN(ret == 0);
+               }
+
+               /* stdin */
+               if (fds[1].revents & POLLIN)
+                       break;
+       }
+
+       printf("Closing bus connection\n");
+
+       return TEST_OK;
+}
diff --git a/tools/testing/selftests/kdbus/test-endpoint.c b/tools/testing/selftests/kdbus/test-endpoint.c
new file mode 100644 (file)
index 0000000..dcc6ab9
--- /dev/null
@@ -0,0 +1,341 @@
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <errno.h>
+#include <assert.h>
+#include <libgen.h>
+#include <sys/capability.h>
+#include <sys/wait.h>
+#include <stdbool.h>
+
+#include "kdbus-api.h"
+#include "kdbus-util.h"
+#include "kdbus-enum.h"
+#include "kdbus-test.h"
+
+#define KDBUS_SYSNAME_MAX_LEN                  63
+
+static int install_name_add_match(struct kdbus_conn *conn, const char *name)
+{
+       struct {
+               struct kdbus_cmd_match cmd;
+               struct {
+                       uint64_t size;
+                       uint64_t type;
+                       struct kdbus_notify_name_change chg;
+               } item;
+               char name[64];
+       } buf;
+       int ret;
+
+       /* install the match rule */
+       memset(&buf, 0, sizeof(buf));
+       buf.item.type = KDBUS_ITEM_NAME_ADD;
+       buf.item.chg.old_id.id = KDBUS_MATCH_ID_ANY;
+       buf.item.chg.new_id.id = KDBUS_MATCH_ID_ANY;
+       strncpy(buf.name, name, sizeof(buf.name) - 1);
+       buf.item.size = sizeof(buf.item) + strlen(buf.name) + 1;
+       buf.cmd.size = sizeof(buf.cmd) + buf.item.size;
+
+       ret = kdbus_cmd_match_add(conn->fd, &buf.cmd);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static int create_endpoint(const char *buspath, uid_t uid, const char *name,
+                          uint64_t flags)
+{
+       struct {
+               struct kdbus_cmd cmd;
+
+               /* name item */
+               struct {
+                       uint64_t size;
+                       uint64_t type;
+                       /* max should be KDBUS_SYSNAME_MAX_LEN */
+                       char str[128];
+               } name;
+       } ep_make;
+       int fd, ret;
+
+       fd = open(buspath, O_RDWR);
+       if (fd < 0)
+               return fd;
+
+       memset(&ep_make, 0, sizeof(ep_make));
+
+       snprintf(ep_make.name.str,
+                /* Use the KDBUS_SYSNAME_MAX_LEN or sizeof(str) */
+                KDBUS_SYSNAME_MAX_LEN > strlen(name) ?
+                KDBUS_SYSNAME_MAX_LEN : sizeof(ep_make.name.str),
+                "%u-%s", uid, name);
+
+       ep_make.name.type = KDBUS_ITEM_MAKE_NAME;
+       ep_make.name.size = KDBUS_ITEM_HEADER_SIZE +
+                           strlen(ep_make.name.str) + 1;
+
+       ep_make.cmd.flags = flags;
+       ep_make.cmd.size = sizeof(ep_make.cmd) + ep_make.name.size;
+
+       ret = kdbus_cmd_endpoint_make(fd, &ep_make.cmd);
+       if (ret < 0) {
+               kdbus_printf("error creating endpoint: %d (%m)\n", ret);
+               return ret;
+       }
+
+       return fd;
+}
+
+static int unpriv_test_custom_ep(const char *buspath)
+{
+       int ret, ep_fd1, ep_fd2;
+       char *ep1, *ep2, *tmp1, *tmp2;
+
+       tmp1 = strdup(buspath);
+       tmp2 = strdup(buspath);
+       ASSERT_RETURN(tmp1 && tmp2);
+
+       ret = asprintf(&ep1, "%s/%u-%s", dirname(tmp1), getuid(), "apps1");
+       ASSERT_RETURN(ret >= 0);
+
+       ret = asprintf(&ep2, "%s/%u-%s", dirname(tmp2), getuid(), "apps2");
+       ASSERT_RETURN(ret >= 0);
+
+       free(tmp1);
+       free(tmp2);
+
+       /* endpoint only accessible to current uid */
+       ep_fd1 = create_endpoint(buspath, getuid(), "apps1", 0);
+       ASSERT_RETURN(ep_fd1 >= 0);
+
+       /* endpoint world accessible */
+       ep_fd2 = create_endpoint(buspath, getuid(), "apps2",
+                                 KDBUS_MAKE_ACCESS_WORLD);
+       ASSERT_RETURN(ep_fd2 >= 0);
+
+       ret = RUN_UNPRIVILEGED(UNPRIV_UID, UNPRIV_UID, ({
+               int ep_fd;
+               struct kdbus_conn *ep_conn;
+
+               /*
+                * Make sure that we are not able to create custom
+                * endpoints
+                */
+               ep_fd = create_endpoint(buspath, getuid(),
+                                       "unpriv_costum_ep", 0);
+               ASSERT_EXIT(ep_fd == -EPERM);
+
+               /*
+                * Endpoint "apps1" only accessible to same users,
+                * that own the endpoint. Access denied by VFS
+                */
+               ep_conn = kdbus_hello(ep1, 0, NULL, 0);
+               ASSERT_EXIT(!ep_conn && errno == EACCES);
+
+               /* Endpoint "apps2" world accessible */
+               ep_conn = kdbus_hello(ep2, 0, NULL, 0);
+               ASSERT_EXIT(ep_conn);
+
+               kdbus_conn_free(ep_conn);
+
+               _exit(EXIT_SUCCESS);
+       }),
+       ({ 0; }));
+       ASSERT_RETURN(ret == 0);
+
+       close(ep_fd1);
+       close(ep_fd2);
+       free(ep1);
+       free(ep2);
+
+       return 0;
+}
+
+static int update_endpoint(int fd, const char *name)
+{
+       int len = strlen(name) + 1;
+       struct {
+               struct kdbus_cmd cmd;
+
+               /* name item */
+               struct {
+                       uint64_t size;
+                       uint64_t type;
+                       char str[KDBUS_ALIGN8(len)];
+               } name;
+
+               struct {
+                       uint64_t size;
+                       uint64_t type;
+                       struct kdbus_policy_access access;
+               } access;
+       } ep_update;
+       int ret;
+
+       memset(&ep_update, 0, sizeof(ep_update));
+
+       ep_update.name.size = KDBUS_ITEM_HEADER_SIZE + len;
+       ep_update.name.type = KDBUS_ITEM_NAME;
+       strncpy(ep_update.name.str, name, sizeof(ep_update.name.str) - 1);
+
+       ep_update.access.size = sizeof(ep_update.access);
+       ep_update.access.type = KDBUS_ITEM_POLICY_ACCESS;
+       ep_update.access.access.type = KDBUS_POLICY_ACCESS_WORLD;
+       ep_update.access.access.access = KDBUS_POLICY_SEE;
+
+       ep_update.cmd.size = sizeof(ep_update);
+
+       ret = kdbus_cmd_endpoint_update(fd, &ep_update.cmd);
+       if (ret < 0) {
+               kdbus_printf("error updating endpoint: %d (%m)\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+int kdbus_test_custom_endpoint(struct kdbus_test_env *env)
+{
+       char *ep, *tmp;
+       int ret, ep_fd;
+       struct kdbus_msg *msg;
+       struct kdbus_conn *ep_conn;
+       struct kdbus_conn *reader;
+       const char *name = "foo.bar.baz";
+       const char *epname = "foo";
+       char fake_ep[KDBUS_SYSNAME_MAX_LEN + 1] = {'\0'};
+
+       memset(fake_ep, 'X', sizeof(fake_ep) - 1);
+
+       /* Try to create a custom endpoint with a long name */
+       ret = create_endpoint(env->buspath, getuid(), fake_ep, 0);
+       ASSERT_RETURN(ret == -ENAMETOOLONG);
+
+       /* Try to create a custom endpoint with a different uid */
+       ret = create_endpoint(env->buspath, getuid() + 1, "foobar", 0);
+       ASSERT_RETURN(ret == -EINVAL);
+
+       /* create a custom endpoint, and open a connection on it */
+       ep_fd = create_endpoint(env->buspath, getuid(), "foo", 0);
+       ASSERT_RETURN(ep_fd >= 0);
+
+       tmp = strdup(env->buspath);
+       ASSERT_RETURN(tmp);
+
+       ret = asprintf(&ep, "%s/%u-%s", dirname(tmp), getuid(), epname);
+       free(tmp);
+       ASSERT_RETURN(ret >= 0);
+
+       /* Register a connection that listen to broadcasts */
+       reader = kdbus_hello(ep, 0, NULL, 0);
+       ASSERT_RETURN(reader);
+
+       /* Register to kernel signals */
+       ret = kdbus_add_match_id(reader, 0x1, KDBUS_ITEM_ID_ADD,
+                                KDBUS_MATCH_ID_ANY);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_add_match_id(reader, 0x2, KDBUS_ITEM_ID_REMOVE,
+                                KDBUS_MATCH_ID_ANY);
+       ASSERT_RETURN(ret == 0);
+
+       ret = install_name_add_match(reader, name);
+       ASSERT_RETURN(ret == 0);
+
+       /* Monitor connections are not supported on custom endpoints */
+       ep_conn = kdbus_hello(ep, KDBUS_HELLO_MONITOR, NULL, 0);
+       ASSERT_RETURN(!ep_conn && errno == EOPNOTSUPP);
+
+       ep_conn = kdbus_hello(ep, 0, NULL, 0);
+       ASSERT_RETURN(ep_conn);
+
+       /*
+        * Add a name add match on the endpoint connection, acquire name from
+        * the unfiltered connection, and make sure the filtered connection
+        * did not get the notification on the name owner change. Also, the
+        * endpoint connection may not be able to call conn_info, neither on
+        * the name nor on the ID.
+        */
+       ret = install_name_add_match(ep_conn, name);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_name_acquire(env->conn, name, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_msg_recv(ep_conn, NULL, NULL);
+       ASSERT_RETURN(ret == -EAGAIN);
+
+       ret = kdbus_conn_info(ep_conn, 0, name, 0, NULL);
+       ASSERT_RETURN(ret == -ESRCH);
+
+       ret = kdbus_conn_info(ep_conn, 0, "random.crappy.name", 0, NULL);
+       ASSERT_RETURN(ret == -ESRCH);
+
+       ret = kdbus_conn_info(ep_conn, env->conn->id, NULL, 0, NULL);
+       ASSERT_RETURN(ret == -ENXIO);
+
+       ret = kdbus_conn_info(ep_conn, 0x0fffffffffffffffULL, NULL, 0, NULL);
+       ASSERT_RETURN(ret == -ENXIO);
+
+       /* Check that the reader did not receive anything */
+       ret = kdbus_msg_recv(reader, NULL, NULL);
+       ASSERT_RETURN(ret == -EAGAIN);
+
+       /*
+        * Release the name again, update the custom endpoint policy,
+        * and try again. This time, the connection on the custom endpoint
+        * should have gotten it.
+        */
+       ret = kdbus_name_release(env->conn, name);
+       ASSERT_RETURN(ret == 0);
+
+       ret = update_endpoint(ep_fd, name);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_name_acquire(env->conn, name, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_msg_recv(ep_conn, &msg, NULL);
+       ASSERT_RETURN(ret == 0);
+       ASSERT_RETURN(msg->items[0].type == KDBUS_ITEM_NAME_ADD);
+       ASSERT_RETURN(msg->items[0].name_change.old_id.id == 0);
+       ASSERT_RETURN(msg->items[0].name_change.new_id.id == env->conn->id);
+       ASSERT_RETURN(strcmp(msg->items[0].name_change.name, name) == 0);
+       kdbus_msg_free(msg);
+
+       ret = kdbus_msg_recv(reader, &msg, NULL);
+       ASSERT_RETURN(ret == 0);
+       ASSERT_RETURN(strcmp(msg->items[0].name_change.name, name) == 0);
+
+       kdbus_msg_free(msg);
+
+       ret = kdbus_conn_info(ep_conn, 0, name, 0, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_conn_info(ep_conn, env->conn->id, NULL, 0, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       /* If we have privileges test custom endpoints */
+       ret = test_is_capable(CAP_SETUID, CAP_SETGID, -1);
+       ASSERT_RETURN(ret >= 0);
+
+       /*
+        * All uids/gids are mapped and we have the necessary caps
+        */
+       if (ret && all_uids_gids_are_mapped()) {
+               ret = unpriv_test_custom_ep(env->buspath);
+               ASSERT_RETURN(ret == 0);
+       }
+
+       kdbus_conn_free(reader);
+       kdbus_conn_free(ep_conn);
+       close(ep_fd);
+
+       return TEST_OK;
+}
diff --git a/tools/testing/selftests/kdbus/test-fd.c b/tools/testing/selftests/kdbus/test-fd.c
new file mode 100644 (file)
index 0000000..bfd8980
--- /dev/null
@@ -0,0 +1,789 @@
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+
+#include "kdbus-api.h"
+#include "kdbus-test.h"
+#include "kdbus-util.h"
+#include "kdbus-enum.h"
+
+#define KDBUS_MSG_MAX_ITEMS     128
+#define KDBUS_USER_MAX_CONN    256
+
+/* maximum number of inflight fds in a target queue per user */
+#define KDBUS_CONN_MAX_FDS_PER_USER    16
+
+/* maximum number of memfd items per message */
+#define KDBUS_MSG_MAX_MEMFD_ITEMS       16
+
+static int make_msg_payload_dbus(uint64_t src_id, uint64_t dst_id,
+                                uint64_t msg_size,
+                                struct kdbus_msg **msg_dbus)
+{
+       struct kdbus_msg *msg;
+
+       msg = malloc(msg_size);
+       ASSERT_RETURN_VAL(msg, -ENOMEM);
+
+       memset(msg, 0, msg_size);
+       msg->size = msg_size;
+       msg->src_id = src_id;
+       msg->dst_id = dst_id;
+       msg->payload_type = KDBUS_PAYLOAD_DBUS;
+
+       *msg_dbus = msg;
+
+       return 0;
+}
+
+static void make_item_memfds(struct kdbus_item *item,
+                            int *memfds, size_t memfd_size)
+{
+       size_t i;
+
+       for (i = 0; i < memfd_size; i++) {
+               item->type = KDBUS_ITEM_PAYLOAD_MEMFD;
+               item->size = KDBUS_ITEM_HEADER_SIZE +
+                            sizeof(struct kdbus_memfd);
+               item->memfd.fd = memfds[i];
+               item->memfd.size = sizeof(uint64_t); /* const size */
+               item = KDBUS_ITEM_NEXT(item);
+       }
+}
+
+static void make_item_fds(struct kdbus_item *item,
+                         int *fd_array, size_t fd_size)
+{
+       size_t i;
+       item->type = KDBUS_ITEM_FDS;
+       item->size = KDBUS_ITEM_HEADER_SIZE + (sizeof(int) * fd_size);
+
+       for (i = 0; i < fd_size; i++)
+               item->fds[i] = fd_array[i];
+}
+
+static int memfd_write(const char *name, void *buf, size_t bufsize)
+{
+       ssize_t ret;
+       int memfd;
+
+       memfd = sys_memfd_create(name, 0);
+       ASSERT_RETURN_VAL(memfd >= 0, memfd);
+
+       ret = write(memfd, buf, bufsize);
+       ASSERT_RETURN_VAL(ret == (ssize_t)bufsize, -EAGAIN);
+
+       ret = sys_memfd_seal_set(memfd);
+       ASSERT_RETURN_VAL(ret == 0, -errno);
+
+       return memfd;
+}
+
+static int send_memfds(struct kdbus_conn *conn, uint64_t dst_id,
+                      int *memfds_array, size_t memfd_count)
+{
+       struct kdbus_cmd_send cmd = {};
+       struct kdbus_item *item;
+       struct kdbus_msg *msg;
+       uint64_t size;
+       int ret;
+
+       size = sizeof(struct kdbus_msg);
+       size += memfd_count * KDBUS_ITEM_SIZE(sizeof(struct kdbus_memfd));
+
+       if (dst_id == KDBUS_DST_ID_BROADCAST)
+               size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_bloom_filter)) + 64;
+
+       ret = make_msg_payload_dbus(conn->id, dst_id, size, &msg);
+       ASSERT_RETURN_VAL(ret == 0, ret);
+
+       item = msg->items;
+
+       if (dst_id == KDBUS_DST_ID_BROADCAST) {
+               item->type = KDBUS_ITEM_BLOOM_FILTER;
+               item->size = KDBUS_ITEM_SIZE(sizeof(struct kdbus_bloom_filter)) + 64;
+               item = KDBUS_ITEM_NEXT(item);
+
+               msg->flags |= KDBUS_MSG_SIGNAL;
+       }
+
+       make_item_memfds(item, memfds_array, memfd_count);
+
+       cmd.size = sizeof(cmd);
+       cmd.msg_address = (uintptr_t)msg;
+
+       ret = kdbus_cmd_send(conn->fd, &cmd);
+       if (ret < 0) {
+               kdbus_printf("error sending message: %d (%m)\n", ret);
+               return ret;
+       }
+
+       free(msg);
+       return 0;
+}
+
+static int send_fds(struct kdbus_conn *conn, uint64_t dst_id,
+                   int *fd_array, size_t fd_count)
+{
+       struct kdbus_cmd_send cmd = {};
+       struct kdbus_item *item;
+       struct kdbus_msg *msg;
+       uint64_t size;
+       int ret;
+
+       size = sizeof(struct kdbus_msg);
+       size += KDBUS_ITEM_SIZE(sizeof(int) * fd_count);
+
+       if (dst_id == KDBUS_DST_ID_BROADCAST)
+               size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_bloom_filter)) + 64;
+
+       ret = make_msg_payload_dbus(conn->id, dst_id, size, &msg);
+       ASSERT_RETURN_VAL(ret == 0, ret);
+
+       item = msg->items;
+
+       if (dst_id == KDBUS_DST_ID_BROADCAST) {
+               item->type = KDBUS_ITEM_BLOOM_FILTER;
+               item->size = KDBUS_ITEM_SIZE(sizeof(struct kdbus_bloom_filter)) + 64;
+               item = KDBUS_ITEM_NEXT(item);
+
+               msg->flags |= KDBUS_MSG_SIGNAL;
+       }
+
+       make_item_fds(item, fd_array, fd_count);
+
+       cmd.size = sizeof(cmd);
+       cmd.msg_address = (uintptr_t)msg;
+
+       ret = kdbus_cmd_send(conn->fd, &cmd);
+       if (ret < 0) {
+               kdbus_printf("error sending message: %d (%m)\n", ret);
+               return ret;
+       }
+
+       free(msg);
+       return ret;
+}
+
+static int send_fds_memfds(struct kdbus_conn *conn, uint64_t dst_id,
+                          int *fds_array, size_t fd_count,
+                          int *memfds_array, size_t memfd_count)
+{
+       struct kdbus_cmd_send cmd = {};
+       struct kdbus_item *item;
+       struct kdbus_msg *msg;
+       uint64_t size;
+       int ret;
+
+       size = sizeof(struct kdbus_msg);
+       size += memfd_count * KDBUS_ITEM_SIZE(sizeof(struct kdbus_memfd));
+       size += KDBUS_ITEM_SIZE(sizeof(int) * fd_count);
+
+       ret = make_msg_payload_dbus(conn->id, dst_id, size, &msg);
+       ASSERT_RETURN_VAL(ret == 0, ret);
+
+       item = msg->items;
+
+       make_item_fds(item, fds_array, fd_count);
+       item = KDBUS_ITEM_NEXT(item);
+       make_item_memfds(item, memfds_array, memfd_count);
+
+       cmd.size = sizeof(cmd);
+       cmd.msg_address = (uintptr_t)msg;
+
+       ret = kdbus_cmd_send(conn->fd, &cmd);
+       if (ret < 0) {
+               kdbus_printf("error sending message: %d (%m)\n", ret);
+               return ret;
+       }
+
+       free(msg);
+       return ret;
+}
+
+/* Return the number of received fds */
+static unsigned int kdbus_item_get_nfds(struct kdbus_msg *msg)
+{
+       unsigned int fds = 0;
+       const struct kdbus_item *item;
+
+       KDBUS_ITEM_FOREACH(item, msg, items) {
+               switch (item->type) {
+               case KDBUS_ITEM_FDS: {
+                       fds += (item->size - KDBUS_ITEM_HEADER_SIZE) /
+                               sizeof(int);
+                       break;
+               }
+
+               case KDBUS_ITEM_PAYLOAD_MEMFD:
+                       fds++;
+                       break;
+
+               default:
+                       break;
+               }
+       }
+
+       return fds;
+}
+
+static struct kdbus_msg *
+get_kdbus_msg_with_fd(struct kdbus_conn *conn_src,
+                     uint64_t dst_id, uint64_t cookie, int fd)
+{
+       int ret;
+       uint64_t size;
+       struct kdbus_item *item;
+       struct kdbus_msg *msg;
+
+       size = sizeof(struct kdbus_msg);
+       if (fd >= 0)
+               size += KDBUS_ITEM_SIZE(sizeof(int));
+
+       ret = make_msg_payload_dbus(conn_src->id, dst_id, size, &msg);
+       ASSERT_RETURN_VAL(ret == 0, NULL);
+
+       msg->cookie = cookie;
+
+       if (fd >= 0) {
+               item = msg->items;
+
+               make_item_fds(item, (int *)&fd, 1);
+       }
+
+       return msg;
+}
+
+static int kdbus_test_no_fds(struct kdbus_test_env *env,
+                            int *fds, int *memfd)
+{
+       pid_t pid;
+       int ret, status;
+       uint64_t cookie;
+       int connfd1, connfd2;
+       struct kdbus_msg *msg, *msg_sync_reply;
+       struct kdbus_cmd_hello hello;
+       struct kdbus_conn *conn_src, *conn_dst, *conn_dummy;
+       struct kdbus_cmd_send cmd = {};
+       struct kdbus_cmd_free cmd_free = {};
+
+       conn_src = kdbus_hello(env->buspath, 0, NULL, 0);
+       ASSERT_RETURN(conn_src);
+
+       connfd1 = open(env->buspath, O_RDWR|O_CLOEXEC);
+       ASSERT_RETURN(connfd1 >= 0);
+
+       connfd2 = open(env->buspath, O_RDWR|O_CLOEXEC);
+       ASSERT_RETURN(connfd2 >= 0);
+
+       /*
+        * Create connections without KDBUS_HELLO_ACCEPT_FD
+        * to test if send fd operations are blocked
+        */
+       conn_dst = malloc(sizeof(*conn_dst));
+       ASSERT_RETURN(conn_dst);
+
+       conn_dummy = malloc(sizeof(*conn_dummy));
+       ASSERT_RETURN(conn_dummy);
+
+       memset(&hello, 0, sizeof(hello));
+       hello.size = sizeof(struct kdbus_cmd_hello);
+       hello.pool_size = POOL_SIZE;
+       hello.attach_flags_send = _KDBUS_ATTACH_ALL;
+
+       ret = kdbus_cmd_hello(connfd1, &hello);
+       ASSERT_RETURN(ret == 0);
+
+       cmd_free.size = sizeof(cmd_free);
+       cmd_free.offset = hello.offset;
+       ret = kdbus_cmd_free(connfd1, &cmd_free);
+       ASSERT_RETURN(ret >= 0);
+
+       conn_dst->fd = connfd1;
+       conn_dst->id = hello.id;
+
+       memset(&hello, 0, sizeof(hello));
+       hello.size = sizeof(struct kdbus_cmd_hello);
+       hello.pool_size = POOL_SIZE;
+       hello.attach_flags_send = _KDBUS_ATTACH_ALL;
+
+       ret = kdbus_cmd_hello(connfd2, &hello);
+       ASSERT_RETURN(ret == 0);
+
+       cmd_free.size = sizeof(cmd_free);
+       cmd_free.offset = hello.offset;
+       ret = kdbus_cmd_free(connfd2, &cmd_free);
+       ASSERT_RETURN(ret >= 0);
+
+       conn_dummy->fd = connfd2;
+       conn_dummy->id = hello.id;
+
+       conn_dst->buf = mmap(NULL, POOL_SIZE, PROT_READ,
+                            MAP_SHARED, connfd1, 0);
+       ASSERT_RETURN(conn_dst->buf != MAP_FAILED);
+
+       conn_dummy->buf = mmap(NULL, POOL_SIZE, PROT_READ,
+                              MAP_SHARED, connfd2, 0);
+       ASSERT_RETURN(conn_dummy->buf != MAP_FAILED);
+
+       /*
+        * Send fds to connection that do not accept fd passing
+        */
+       ret = send_fds(conn_src, conn_dst->id, fds, 1);
+       ASSERT_RETURN(ret == -ECOMM);
+
+       /*
+        * memfd are kdbus payload
+        */
+       ret = send_memfds(conn_src, conn_dst->id, memfd, 1);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_msg_recv_poll(conn_dst, 100, NULL, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       cookie = time(NULL);
+
+       pid = fork();
+       ASSERT_RETURN_VAL(pid >= 0, pid);
+
+       if (pid == 0) {
+               struct timespec now;
+
+               /*
+                * A sync send/reply to a connection that do not
+                * accept fds should fail if it contains an fd
+                */
+               msg_sync_reply = get_kdbus_msg_with_fd(conn_dst,
+                                                      conn_dummy->id,
+                                                      cookie, fds[0]);
+               ASSERT_EXIT(msg_sync_reply);
+
+               ret = clock_gettime(CLOCK_MONOTONIC_COARSE, &now);
+               ASSERT_EXIT(ret == 0);
+
+               msg_sync_reply->timeout_ns = now.tv_sec * 1000000000ULL +
+                                            now.tv_nsec + 100000000ULL;
+               msg_sync_reply->flags = KDBUS_MSG_EXPECT_REPLY;
+
+               memset(&cmd, 0, sizeof(cmd));
+               cmd.size = sizeof(cmd);
+               cmd.msg_address = (uintptr_t)msg_sync_reply;
+               cmd.flags = KDBUS_SEND_SYNC_REPLY;
+
+               ret = kdbus_cmd_send(conn_dst->fd, &cmd);
+               ASSERT_EXIT(ret == -ECOMM);
+
+               /*
+                * Now send a normal message, but the sync reply
+                * will fail since it contains an fd that the
+                * original sender do not want.
+                *
+                * The original sender will fail with -ETIMEDOUT
+                */
+               cookie++;
+               ret = kdbus_msg_send_sync(conn_dst, NULL, cookie,
+                                         KDBUS_MSG_EXPECT_REPLY,
+                                         5000000000ULL, 0, conn_src->id, -1);
+               ASSERT_EXIT(ret == -EREMOTEIO);
+
+               cookie++;
+               ret = kdbus_msg_recv_poll(conn_dst, 100, &msg, NULL);
+               ASSERT_EXIT(ret == 0);
+               ASSERT_EXIT(msg->cookie == cookie);
+
+               free(msg_sync_reply);
+               kdbus_msg_free(msg);
+
+               _exit(EXIT_SUCCESS);
+       }
+
+       ret = kdbus_msg_recv_poll(conn_dummy, 100, NULL, NULL);
+       ASSERT_RETURN(ret == -ETIMEDOUT);
+
+       cookie++;
+       ret = kdbus_msg_recv_poll(conn_src, 100, &msg, NULL);
+       ASSERT_RETURN(ret == 0 && msg->cookie == cookie);
+
+       kdbus_msg_free(msg);
+
+       /*
+        * Try to reply with a kdbus connection handle, this should
+        * fail with -EOPNOTSUPP
+        */
+       msg_sync_reply = get_kdbus_msg_with_fd(conn_src,
+                                              conn_dst->id,
+                                              cookie, conn_dst->fd);
+       ASSERT_RETURN(msg_sync_reply);
+
+       msg_sync_reply->cookie_reply = cookie;
+
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.size = sizeof(cmd);
+       cmd.msg_address = (uintptr_t)msg_sync_reply;
+
+       ret = kdbus_cmd_send(conn_src->fd, &cmd);
+       ASSERT_RETURN(ret == -EOPNOTSUPP);
+
+       free(msg_sync_reply);
+
+       /*
+        * Try to reply with a normal fd, this should fail even
+        * if the response is a sync reply
+        *
+        * From the sender view we fail with -ECOMM
+        */
+       msg_sync_reply = get_kdbus_msg_with_fd(conn_src,
+                                              conn_dst->id,
+                                              cookie, fds[0]);
+       ASSERT_RETURN(msg_sync_reply);
+
+       msg_sync_reply->cookie_reply = cookie;
+
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.size = sizeof(cmd);
+       cmd.msg_address = (uintptr_t)msg_sync_reply;
+
+       ret = kdbus_cmd_send(conn_src->fd, &cmd);
+       ASSERT_RETURN(ret == -ECOMM);
+
+       free(msg_sync_reply);
+
+       /*
+        * Resend another normal message and check if the queue
+        * is clear
+        */
+       cookie++;
+       ret = kdbus_msg_send(conn_src, NULL, cookie, 0, 0, 0,
+                            conn_dst->id, 0, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       ret = waitpid(pid, &status, 0);
+       ASSERT_RETURN_VAL(ret >= 0, ret);
+
+       kdbus_conn_free(conn_dummy);
+       kdbus_conn_free(conn_dst);
+       kdbus_conn_free(conn_src);
+
+       return (status == EXIT_SUCCESS) ? TEST_OK : TEST_ERR;
+}
+
+static int kdbus_send_multiple_fds(struct kdbus_conn *conn_src,
+                                  struct kdbus_conn *conn_dst)
+{
+       int ret, i;
+       unsigned int nfds;
+       int fds[KDBUS_CONN_MAX_FDS_PER_USER + 1];
+       int memfds[KDBUS_MSG_MAX_ITEMS + 1];
+       struct kdbus_msg *msg;
+       uint64_t dummy_value;
+
+       dummy_value = time(NULL);
+
+       for (i = 0; i < KDBUS_CONN_MAX_FDS_PER_USER + 1; i++) {
+               fds[i] = open("/dev/null", O_RDWR|O_CLOEXEC);
+               ASSERT_RETURN_VAL(fds[i] >= 0, -errno);
+       }
+
+       /* Send KDBUS_CONN_MAX_FDS_PER_USER with one more fd */
+       ret = send_fds(conn_src, conn_dst->id, fds,
+                      KDBUS_CONN_MAX_FDS_PER_USER + 1);
+       ASSERT_RETURN(ret == -EMFILE);
+
+       /* Retry with the correct KDBUS_CONN_MAX_FDS_PER_USER */
+       ret = send_fds(conn_src, conn_dst->id, fds,
+                      KDBUS_CONN_MAX_FDS_PER_USER);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_msg_recv(conn_dst, &msg, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       /* Check we got the right number of fds */
+       nfds = kdbus_item_get_nfds(msg);
+       ASSERT_RETURN(nfds == KDBUS_CONN_MAX_FDS_PER_USER);
+
+       kdbus_msg_free(msg);
+
+       for (i = 0; i < KDBUS_MSG_MAX_ITEMS + 1; i++, dummy_value++) {
+               memfds[i] = memfd_write("memfd-name",
+                                       &dummy_value,
+                                       sizeof(dummy_value));
+               ASSERT_RETURN_VAL(memfds[i] >= 0, memfds[i]);
+       }
+
+       /* Send KDBUS_MSG_MAX_ITEMS with one more memfd */
+       ret = send_memfds(conn_src, conn_dst->id,
+                         memfds, KDBUS_MSG_MAX_ITEMS + 1);
+       ASSERT_RETURN(ret == -E2BIG);
+
+       ret = send_memfds(conn_src, conn_dst->id,
+                         memfds, KDBUS_MSG_MAX_MEMFD_ITEMS + 1);
+       ASSERT_RETURN(ret == -E2BIG);
+
+       /* Retry with the correct KDBUS_MSG_MAX_ITEMS */
+       ret = send_memfds(conn_src, conn_dst->id,
+                         memfds, KDBUS_MSG_MAX_MEMFD_ITEMS);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_msg_recv(conn_dst, &msg, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       /* Check we got the right number of fds */
+       nfds = kdbus_item_get_nfds(msg);
+       ASSERT_RETURN(nfds == KDBUS_MSG_MAX_MEMFD_ITEMS);
+
+       kdbus_msg_free(msg);
+
+
+       /*
+        * Combine multiple KDBUS_CONN_MAX_FDS_PER_USER+1 fds and
+        * 10 memfds
+        */
+       ret = send_fds_memfds(conn_src, conn_dst->id,
+                             fds, KDBUS_CONN_MAX_FDS_PER_USER + 1,
+                             memfds, 10);
+       ASSERT_RETURN(ret == -EMFILE);
+
+       ret = kdbus_msg_recv(conn_dst, NULL, NULL);
+       ASSERT_RETURN(ret == -EAGAIN);
+
+       /*
+        * Combine multiple KDBUS_CONN_MAX_FDS_PER_USER fds and
+        * (128 - 1) + 1 memfds, all fds take one item, while each
+        * memfd takes one item
+        */
+       ret = send_fds_memfds(conn_src, conn_dst->id,
+                             fds, KDBUS_CONN_MAX_FDS_PER_USER,
+                             memfds, (KDBUS_MSG_MAX_ITEMS - 1) + 1);
+       ASSERT_RETURN(ret == -E2BIG);
+
+       ret = send_fds_memfds(conn_src, conn_dst->id,
+                             fds, KDBUS_CONN_MAX_FDS_PER_USER,
+                             memfds, KDBUS_MSG_MAX_MEMFD_ITEMS + 1);
+       ASSERT_RETURN(ret == -E2BIG);
+
+       ret = kdbus_msg_recv(conn_dst, NULL, NULL);
+       ASSERT_RETURN(ret == -EAGAIN);
+
+       /*
+        * Send KDBUS_CONN_MAX_FDS_PER_USER fds +
+        * KDBUS_MSG_MAX_MEMFD_ITEMS memfds
+        */
+       ret = send_fds_memfds(conn_src, conn_dst->id,
+                             fds, KDBUS_CONN_MAX_FDS_PER_USER,
+                             memfds, KDBUS_MSG_MAX_MEMFD_ITEMS);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_msg_recv(conn_dst, &msg, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       /* Check we got the right number of fds */
+       nfds = kdbus_item_get_nfds(msg);
+       ASSERT_RETURN(nfds == KDBUS_CONN_MAX_FDS_PER_USER +
+                             KDBUS_MSG_MAX_MEMFD_ITEMS);
+
+       kdbus_msg_free(msg);
+
+
+       /*
+        * Re-send fds + memfds, close them, but do not receive them
+        * and try to queue more
+        */
+       ret = send_fds_memfds(conn_src, conn_dst->id,
+                             fds, KDBUS_CONN_MAX_FDS_PER_USER,
+                             memfds, KDBUS_MSG_MAX_MEMFD_ITEMS);
+       ASSERT_RETURN(ret == 0);
+
+       /* close old references and get a new ones */
+       for (i = 0; i < KDBUS_CONN_MAX_FDS_PER_USER + 1; i++) {
+               close(fds[i]);
+               fds[i] = open("/dev/null", O_RDWR|O_CLOEXEC);
+               ASSERT_RETURN_VAL(fds[i] >= 0, -errno);
+       }
+
+       /* should fail since we have already fds in the queue */
+       ret = send_fds(conn_src, conn_dst->id, fds,
+                      KDBUS_CONN_MAX_FDS_PER_USER);
+       ASSERT_RETURN(ret == -EMFILE);
+
+       /* This should succeed */
+       ret = send_memfds(conn_src, conn_dst->id,
+                         memfds, KDBUS_MSG_MAX_MEMFD_ITEMS);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_msg_recv(conn_dst, &msg, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       nfds = kdbus_item_get_nfds(msg);
+       ASSERT_RETURN(nfds == KDBUS_CONN_MAX_FDS_PER_USER +
+                             KDBUS_MSG_MAX_MEMFD_ITEMS);
+
+       kdbus_msg_free(msg);
+
+       ret = kdbus_msg_recv(conn_dst, &msg, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       nfds = kdbus_item_get_nfds(msg);
+       ASSERT_RETURN(nfds == KDBUS_MSG_MAX_MEMFD_ITEMS);
+
+       kdbus_msg_free(msg);
+
+       ret = kdbus_msg_recv(conn_dst, NULL, NULL);
+       ASSERT_RETURN(ret == -EAGAIN);
+
+       for (i = 0; i < KDBUS_CONN_MAX_FDS_PER_USER + 1; i++)
+               close(fds[i]);
+
+       for (i = 0; i < KDBUS_MSG_MAX_ITEMS + 1; i++)
+               close(memfds[i]);
+
+       return 0;
+}
+
+int kdbus_test_fd_passing(struct kdbus_test_env *env)
+{
+       struct kdbus_conn *conn_src, *conn_dst;
+       const char *str = "stackenblocken";
+       const struct kdbus_item *item;
+       struct kdbus_msg *msg;
+       unsigned int i;
+       uint64_t now;
+       int fds_conn[2];
+       int sock_pair[2];
+       int fds[2];
+       int memfd;
+       int ret;
+
+       now = (uint64_t) time(NULL);
+
+       /* create two connections */
+       conn_src = kdbus_hello(env->buspath, 0, NULL, 0);
+       conn_dst = kdbus_hello(env->buspath, 0, NULL, 0);
+       ASSERT_RETURN(conn_src && conn_dst);
+
+       fds_conn[0] = conn_src->fd;
+       fds_conn[1] = conn_dst->fd;
+
+       ret = socketpair(AF_UNIX, SOCK_STREAM, 0, sock_pair);
+       ASSERT_RETURN(ret == 0);
+
+       /* Setup memfd */
+       memfd = memfd_write("memfd-name", &now, sizeof(now));
+       ASSERT_RETURN(memfd >= 0);
+
+       /* Setup pipes */
+       ret = pipe(fds);
+       ASSERT_RETURN(ret == 0);
+
+       i = write(fds[1], str, strlen(str));
+       ASSERT_RETURN(i == strlen(str));
+
+       /*
+        * Try to ass the handle of a connection as message payload.
+        * This must fail.
+        */
+       ret = send_fds(conn_src, conn_dst->id, fds_conn, 2);
+       ASSERT_RETURN(ret == -ENOTSUP);
+
+       ret = send_fds(conn_dst, conn_src->id, fds_conn, 2);
+       ASSERT_RETURN(ret == -ENOTSUP);
+
+       ret = send_fds(conn_src, conn_dst->id, sock_pair, 2);
+       ASSERT_RETURN(ret == -ENOTSUP);
+
+       /*
+        * Send fds and memfds to connection that do not accept fds
+        */
+       ret = kdbus_test_no_fds(env, fds, (int *)&memfd);
+       ASSERT_RETURN(ret == 0);
+
+       /* Try to broadcast file descriptors. This must fail. */
+       ret = send_fds(conn_src, KDBUS_DST_ID_BROADCAST, fds, 1);
+       ASSERT_RETURN(ret == -ENOTUNIQ);
+
+       /* Try to broadcast memfd. This must succeed. */
+       ret = send_memfds(conn_src, KDBUS_DST_ID_BROADCAST, (int *)&memfd, 1);
+       ASSERT_RETURN(ret == 0);
+
+       /* Open code this loop */
+loop_send_fds:
+
+       /*
+        * Send the read end of the pipe and close it.
+        */
+       ret = send_fds(conn_src, conn_dst->id, fds, 1);
+       ASSERT_RETURN(ret == 0);
+       close(fds[0]);
+
+       ret = kdbus_msg_recv(conn_dst, &msg, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       KDBUS_ITEM_FOREACH(item, msg, items) {
+               if (item->type == KDBUS_ITEM_FDS) {
+                       char tmp[14];
+                       int nfds = (item->size - KDBUS_ITEM_HEADER_SIZE) /
+                                       sizeof(int);
+                       ASSERT_RETURN(nfds == 1);
+
+                       i = read(item->fds[0], tmp, sizeof(tmp));
+                       if (i != 0) {
+                               ASSERT_RETURN(i == sizeof(tmp));
+                               ASSERT_RETURN(memcmp(tmp, str, sizeof(tmp)) == 0);
+
+                               /* Write EOF */
+                               close(fds[1]);
+
+                               /*
+                                * Resend the read end of the pipe,
+                                * the receiver still holds a reference
+                                * to it...
+                                */
+                               goto loop_send_fds;
+                       }
+
+                       /* Got EOF */
+
+                       /*
+                        * Close the last reference to the read end
+                        * of the pipe, other references are
+                        * automatically closed just after send.
+                        */
+                       close(item->fds[0]);
+               }
+       }
+
+       /*
+        * Try to resend the read end of the pipe. Must fail with
+        * -EBADF since both the sender and receiver closed their
+        * references to it. We assume the above since sender and
+        * receiver are on the same process.
+        */
+       ret = send_fds(conn_src, conn_dst->id, fds, 1);
+       ASSERT_RETURN(ret == -EBADF);
+
+       /* Then we clear out received any data... */
+       kdbus_msg_free(msg);
+
+       ret = kdbus_send_multiple_fds(conn_src, conn_dst);
+       ASSERT_RETURN(ret == 0);
+
+       close(sock_pair[0]);
+       close(sock_pair[1]);
+       close(memfd);
+
+       kdbus_conn_free(conn_src);
+       kdbus_conn_free(conn_dst);
+
+       return TEST_OK;
+}
diff --git a/tools/testing/selftests/kdbus/test-free.c b/tools/testing/selftests/kdbus/test-free.c
new file mode 100644 (file)
index 0000000..f666da3
--- /dev/null
@@ -0,0 +1,64 @@
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <errno.h>
+#include <assert.h>
+#include <stdbool.h>
+
+#include "kdbus-api.h"
+#include "kdbus-util.h"
+#include "kdbus-enum.h"
+#include "kdbus-test.h"
+
+static int sample_ioctl_call(struct kdbus_test_env *env)
+{
+       int ret;
+       struct kdbus_cmd_list cmd_list = {
+               .flags = KDBUS_LIST_QUEUED,
+               .size = sizeof(cmd_list),
+       };
+
+       ret = kdbus_cmd_list(env->conn->fd, &cmd_list);
+       ASSERT_RETURN(ret == 0);
+
+       /* DON'T FREE THIS SLICE OF MEMORY! */
+
+       return TEST_OK;
+}
+
+int kdbus_test_free(struct kdbus_test_env *env)
+{
+       int ret;
+       struct kdbus_cmd_free cmd_free = {};
+
+       /* free an unallocated buffer */
+       cmd_free.size = sizeof(cmd_free);
+       cmd_free.flags = 0;
+       cmd_free.offset = 0;
+       ret = kdbus_cmd_free(env->conn->fd, &cmd_free);
+       ASSERT_RETURN(ret == -ENXIO);
+
+       /* free a buffer out of the pool's bounds */
+       cmd_free.size = sizeof(cmd_free);
+       cmd_free.offset = POOL_SIZE + 1;
+       ret = kdbus_cmd_free(env->conn->fd, &cmd_free);
+       ASSERT_RETURN(ret == -ENXIO);
+
+       /*
+        * The user application is responsible for freeing the allocated
+        * memory with the KDBUS_CMD_FREE ioctl, so let's test what happens
+        * if we forget about it.
+        */
+
+       ret = sample_ioctl_call(env);
+       ASSERT_RETURN(ret == 0);
+
+       ret = sample_ioctl_call(env);
+       ASSERT_RETURN(ret == 0);
+
+       return TEST_OK;
+}
diff --git a/tools/testing/selftests/kdbus/test-match.c b/tools/testing/selftests/kdbus/test-match.c
new file mode 100644 (file)
index 0000000..2360dc1
--- /dev/null
@@ -0,0 +1,441 @@
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <errno.h>
+#include <assert.h>
+#include <stdbool.h>
+
+#include "kdbus-api.h"
+#include "kdbus-util.h"
+#include "kdbus-enum.h"
+#include "kdbus-test.h"
+
+int kdbus_test_match_id_add(struct kdbus_test_env *env)
+{
+       struct {
+               struct kdbus_cmd_match cmd;
+               struct {
+                       uint64_t size;
+                       uint64_t type;
+                       struct kdbus_notify_id_change chg;
+               } item;
+       } buf;
+       struct kdbus_conn *conn;
+       struct kdbus_msg *msg;
+       int ret;
+
+       memset(&buf, 0, sizeof(buf));
+
+       buf.cmd.size = sizeof(buf);
+       buf.cmd.cookie = 0xdeafbeefdeaddead;
+       buf.item.size = sizeof(buf.item);
+       buf.item.type = KDBUS_ITEM_ID_ADD;
+       buf.item.chg.id = KDBUS_MATCH_ID_ANY;
+
+       /* match on id add */
+       ret = kdbus_cmd_match_add(env->conn->fd, &buf.cmd);
+       ASSERT_RETURN(ret == 0);
+
+       /* create 2nd connection */
+       conn = kdbus_hello(env->buspath, 0, NULL, 0);
+       ASSERT_RETURN(conn != NULL);
+
+       /* 1st connection should have received a notification */
+       ret = kdbus_msg_recv(env->conn, &msg, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       ASSERT_RETURN(msg->items[0].type == KDBUS_ITEM_ID_ADD);
+       ASSERT_RETURN(msg->items[0].id_change.id == conn->id);
+
+       kdbus_conn_free(conn);
+
+       return TEST_OK;
+}
+
+int kdbus_test_match_id_remove(struct kdbus_test_env *env)
+{
+       struct {
+               struct kdbus_cmd_match cmd;
+               struct {
+                       uint64_t size;
+                       uint64_t type;
+                       struct kdbus_notify_id_change chg;
+               } item;
+       } buf;
+       struct kdbus_conn *conn;
+       struct kdbus_msg *msg;
+       size_t id;
+       int ret;
+
+       /* create 2nd connection */
+       conn = kdbus_hello(env->buspath, 0, NULL, 0);
+       ASSERT_RETURN(conn != NULL);
+       id = conn->id;
+
+       memset(&buf, 0, sizeof(buf));
+       buf.cmd.size = sizeof(buf);
+       buf.cmd.cookie = 0xdeafbeefdeaddead;
+       buf.item.size = sizeof(buf.item);
+       buf.item.type = KDBUS_ITEM_ID_REMOVE;
+       buf.item.chg.id = id;
+
+       /* register match on 2nd connection */
+       ret = kdbus_cmd_match_add(env->conn->fd, &buf.cmd);
+       ASSERT_RETURN(ret == 0);
+
+       /* remove 2nd connection again */
+       kdbus_conn_free(conn);
+
+       /* 1st connection should have received a notification */
+       ret = kdbus_msg_recv(env->conn, &msg, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       ASSERT_RETURN(msg->items[0].type == KDBUS_ITEM_ID_REMOVE);
+       ASSERT_RETURN(msg->items[0].id_change.id == id);
+
+       return TEST_OK;
+}
+
+int kdbus_test_match_replace(struct kdbus_test_env *env)
+{
+       struct {
+               struct kdbus_cmd_match cmd;
+               struct {
+                       uint64_t size;
+                       uint64_t type;
+                       struct kdbus_notify_id_change chg;
+               } item;
+       } buf;
+       struct kdbus_conn *conn;
+       struct kdbus_msg *msg;
+       size_t id;
+       int ret;
+
+       /* add a match to id_add */
+       ASSERT_RETURN(kdbus_test_match_id_add(env) == TEST_OK);
+
+       /* do a replace of the match from id_add to id_remove */
+       memset(&buf, 0, sizeof(buf));
+
+       buf.cmd.size = sizeof(buf);
+       buf.cmd.cookie = 0xdeafbeefdeaddead;
+       buf.cmd.flags = KDBUS_MATCH_REPLACE;
+       buf.item.size = sizeof(buf.item);
+       buf.item.type = KDBUS_ITEM_ID_REMOVE;
+       buf.item.chg.id = KDBUS_MATCH_ID_ANY;
+
+       ret = kdbus_cmd_match_add(env->conn->fd, &buf.cmd);
+
+       /* create 2nd connection */
+       conn = kdbus_hello(env->buspath, 0, NULL, 0);
+       ASSERT_RETURN(conn != NULL);
+       id = conn->id;
+
+       /* 1st connection should _not_ have received a notification */
+       ret = kdbus_msg_recv(env->conn, &msg, NULL);
+       ASSERT_RETURN(ret != 0);
+
+       /* remove 2nd connection */
+       kdbus_conn_free(conn);
+
+       /* 1st connection should _now_ have received a notification */
+       ret = kdbus_msg_recv(env->conn, &msg, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       ASSERT_RETURN(msg->items[0].type == KDBUS_ITEM_ID_REMOVE);
+       ASSERT_RETURN(msg->items[0].id_change.id == id);
+
+       return TEST_OK;
+}
+
+int kdbus_test_match_name_add(struct kdbus_test_env *env)
+{
+       struct {
+               struct kdbus_cmd_match cmd;
+               struct {
+                       uint64_t size;
+                       uint64_t type;
+                       struct kdbus_notify_name_change chg;
+               } item;
+               char name[64];
+       } buf;
+       struct kdbus_msg *msg;
+       char *name;
+       int ret;
+
+       name = "foo.bla.blaz";
+
+       /* install the match rule */
+       memset(&buf, 0, sizeof(buf));
+       buf.item.type = KDBUS_ITEM_NAME_ADD;
+       buf.item.chg.old_id.id = KDBUS_MATCH_ID_ANY;
+       buf.item.chg.new_id.id = KDBUS_MATCH_ID_ANY;
+       strncpy(buf.name, name, sizeof(buf.name) - 1);
+       buf.item.size = sizeof(buf.item) + strlen(buf.name) + 1;
+       buf.cmd.size = sizeof(buf.cmd) + buf.item.size;
+
+       ret = kdbus_cmd_match_add(env->conn->fd, &buf.cmd);
+       ASSERT_RETURN(ret == 0);
+
+       /* acquire the name */
+       ret = kdbus_name_acquire(env->conn, name, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       /* we should have received a notification */
+       ret = kdbus_msg_recv(env->conn, &msg, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       ASSERT_RETURN(msg->items[0].type == KDBUS_ITEM_NAME_ADD);
+       ASSERT_RETURN(msg->items[0].name_change.old_id.id == 0);
+       ASSERT_RETURN(msg->items[0].name_change.new_id.id == env->conn->id);
+       ASSERT_RETURN(strcmp(msg->items[0].name_change.name, name) == 0);
+
+       return TEST_OK;
+}
+
+int kdbus_test_match_name_remove(struct kdbus_test_env *env)
+{
+       struct {
+               struct kdbus_cmd_match cmd;
+               struct {
+                       uint64_t size;
+                       uint64_t type;
+                       struct kdbus_notify_name_change chg;
+               } item;
+               char name[64];
+       } buf;
+       struct kdbus_msg *msg;
+       char *name;
+       int ret;
+
+       name = "foo.bla.blaz";
+
+       /* acquire the name */
+       ret = kdbus_name_acquire(env->conn, name, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       /* install the match rule */
+       memset(&buf, 0, sizeof(buf));
+       buf.item.type = KDBUS_ITEM_NAME_REMOVE;
+       buf.item.chg.old_id.id = KDBUS_MATCH_ID_ANY;
+       buf.item.chg.new_id.id = KDBUS_MATCH_ID_ANY;
+       strncpy(buf.name, name, sizeof(buf.name) - 1);
+       buf.item.size = sizeof(buf.item) + strlen(buf.name) + 1;
+       buf.cmd.size = sizeof(buf.cmd) + buf.item.size;
+
+       ret = kdbus_cmd_match_add(env->conn->fd, &buf.cmd);
+       ASSERT_RETURN(ret == 0);
+
+       /* release the name again */
+       kdbus_name_release(env->conn, name);
+       ASSERT_RETURN(ret == 0);
+
+       /* we should have received a notification */
+       ret = kdbus_msg_recv(env->conn, &msg, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       ASSERT_RETURN(msg->items[0].type == KDBUS_ITEM_NAME_REMOVE);
+       ASSERT_RETURN(msg->items[0].name_change.old_id.id == env->conn->id);
+       ASSERT_RETURN(msg->items[0].name_change.new_id.id == 0);
+       ASSERT_RETURN(strcmp(msg->items[0].name_change.name, name) == 0);
+
+       return TEST_OK;
+}
+
+int kdbus_test_match_name_change(struct kdbus_test_env *env)
+{
+       struct {
+               struct kdbus_cmd_match cmd;
+               struct {
+                       uint64_t size;
+                       uint64_t type;
+                       struct kdbus_notify_name_change chg;
+               } item;
+               char name[64];
+       } buf;
+       struct kdbus_conn *conn;
+       struct kdbus_msg *msg;
+       uint64_t flags;
+       char *name = "foo.bla.baz";
+       int ret;
+
+       /* acquire the name */
+       ret = kdbus_name_acquire(env->conn, name, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       /* install the match rule */
+       memset(&buf, 0, sizeof(buf));
+       buf.item.type = KDBUS_ITEM_NAME_CHANGE;
+       buf.item.chg.old_id.id = KDBUS_MATCH_ID_ANY;
+       buf.item.chg.new_id.id = KDBUS_MATCH_ID_ANY;
+       strncpy(buf.name, name, sizeof(buf.name) - 1);
+       buf.item.size = sizeof(buf.item) + strlen(buf.name) + 1;
+       buf.cmd.size = sizeof(buf.cmd) + buf.item.size;
+
+       ret = kdbus_cmd_match_add(env->conn->fd, &buf.cmd);
+       ASSERT_RETURN(ret == 0);
+
+       /* create a 2nd connection */
+       conn = kdbus_hello(env->buspath, 0, NULL, 0);
+       ASSERT_RETURN(conn != NULL);
+
+       /* allow the new connection to own the same name */
+       /* queue the 2nd connection as waiting owner */
+       flags = KDBUS_NAME_QUEUE;
+       ret = kdbus_name_acquire(conn, name, &flags);
+       ASSERT_RETURN(ret == 0);
+       ASSERT_RETURN(flags & KDBUS_NAME_IN_QUEUE);
+
+       /* release name from 1st connection */
+       ret = kdbus_name_release(env->conn, name);
+       ASSERT_RETURN(ret == 0);
+
+       /* we should have received a notification */
+       ret = kdbus_msg_recv(env->conn, &msg, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       ASSERT_RETURN(msg->items[0].type == KDBUS_ITEM_NAME_CHANGE);
+       ASSERT_RETURN(msg->items[0].name_change.old_id.id == env->conn->id);
+       ASSERT_RETURN(msg->items[0].name_change.new_id.id == conn->id);
+       ASSERT_RETURN(strcmp(msg->items[0].name_change.name, name) == 0);
+
+       kdbus_conn_free(conn);
+
+       return TEST_OK;
+}
+
+static int send_bloom_filter(const struct kdbus_conn *conn,
+                            uint64_t cookie,
+                            const uint8_t *filter,
+                            size_t filter_size,
+                            uint64_t filter_generation)
+{
+       struct kdbus_cmd_send cmd = {};
+       struct kdbus_msg *msg;
+       struct kdbus_item *item;
+       uint64_t size;
+       int ret;
+
+       size = sizeof(struct kdbus_msg);
+       size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_bloom_filter)) + filter_size;
+
+       msg = alloca(size);
+
+       memset(msg, 0, size);
+       msg->size = size;
+       msg->src_id = conn->id;
+       msg->dst_id = KDBUS_DST_ID_BROADCAST;
+       msg->flags = KDBUS_MSG_SIGNAL;
+       msg->payload_type = KDBUS_PAYLOAD_DBUS;
+       msg->cookie = cookie;
+
+       item = msg->items;
+       item->type = KDBUS_ITEM_BLOOM_FILTER;
+       item->size = KDBUS_ITEM_SIZE(sizeof(struct kdbus_bloom_filter)) +
+                               filter_size;
+
+       item->bloom_filter.generation = filter_generation;
+       memcpy(item->bloom_filter.data, filter, filter_size);
+
+       cmd.size = sizeof(cmd);
+       cmd.msg_address = (uintptr_t)msg;
+
+       ret = kdbus_cmd_send(conn->fd, &cmd);
+       if (ret < 0) {
+               kdbus_printf("error sending message: %d (%m)\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+int kdbus_test_match_bloom(struct kdbus_test_env *env)
+{
+       struct {
+               struct kdbus_cmd_match cmd;
+               struct {
+                       uint64_t size;
+                       uint64_t type;
+                       uint8_t data_gen0[64];
+                       uint8_t data_gen1[64];
+               } item;
+       } buf;
+       struct kdbus_conn *conn;
+       struct kdbus_msg *msg;
+       uint64_t cookie = 0xf000f00f;
+       uint8_t filter[64];
+       int ret;
+
+       /* install the match rule */
+       memset(&buf, 0, sizeof(buf));
+       buf.cmd.size = sizeof(buf);
+
+       buf.item.size = sizeof(buf.item);
+       buf.item.type = KDBUS_ITEM_BLOOM_MASK;
+       buf.item.data_gen0[0] = 0x55;
+       buf.item.data_gen0[63] = 0x80;
+
+       buf.item.data_gen1[1] = 0xaa;
+       buf.item.data_gen1[9] = 0x02;
+
+       ret = kdbus_cmd_match_add(env->conn->fd, &buf.cmd);
+       ASSERT_RETURN(ret == 0);
+
+       /* create a 2nd connection */
+       conn = kdbus_hello(env->buspath, 0, NULL, 0);
+       ASSERT_RETURN(conn != NULL);
+
+       /* a message with a 0'ed out filter must not reach the other peer */
+       memset(filter, 0, sizeof(filter));
+       ret = send_bloom_filter(conn, ++cookie, filter, sizeof(filter), 0);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_msg_recv(env->conn, &msg, NULL);
+       ASSERT_RETURN(ret == -EAGAIN);
+
+       /* now set the filter to the connection's mask and expect success */
+       filter[0] = 0x55;
+       filter[63] = 0x80;
+       ret = send_bloom_filter(conn, ++cookie, filter, sizeof(filter), 0);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_msg_recv(env->conn, &msg, NULL);
+       ASSERT_RETURN(ret == 0);
+       ASSERT_RETURN(msg->cookie == cookie);
+
+       /* broaden the filter and try again. this should also succeed. */
+       filter[0] = 0xff;
+       filter[8] = 0xff;
+       filter[63] = 0xff;
+       ret = send_bloom_filter(conn, ++cookie, filter, sizeof(filter), 0);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_msg_recv(env->conn, &msg, NULL);
+       ASSERT_RETURN(ret == 0);
+       ASSERT_RETURN(msg->cookie == cookie);
+
+       /* the same filter must not match against bloom generation 1 */
+       ret = send_bloom_filter(conn, ++cookie, filter, sizeof(filter), 1);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_msg_recv(env->conn, &msg, NULL);
+       ASSERT_RETURN(ret == -EAGAIN);
+
+       /* set a different filter and try again */
+       filter[1] = 0xaa;
+       filter[9] = 0x02;
+       ret = send_bloom_filter(conn, ++cookie, filter, sizeof(filter), 1);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_msg_recv(env->conn, &msg, NULL);
+       ASSERT_RETURN(ret == 0);
+       ASSERT_RETURN(msg->cookie == cookie);
+
+       kdbus_conn_free(conn);
+
+       return TEST_OK;
+}
diff --git a/tools/testing/selftests/kdbus/test-message.c b/tools/testing/selftests/kdbus/test-message.c
new file mode 100644 (file)
index 0000000..d9b8fbd
--- /dev/null
@@ -0,0 +1,748 @@
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <errno.h>
+#include <assert.h>
+#include <time.h>
+#include <stdbool.h>
+#include <sys/eventfd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "kdbus-api.h"
+#include "kdbus-util.h"
+#include "kdbus-enum.h"
+#include "kdbus-test.h"
+
+/* maximum number of queued messages from the same individual user */
+#define KDBUS_CONN_MAX_MSGS                    256
+
+/* maximum number of queued requests waiting for a reply */
+#define KDBUS_CONN_MAX_REQUESTS_PENDING                128
+
+/* maximum message payload size */
+#define KDBUS_MSG_MAX_PAYLOAD_VEC_SIZE         (2 * 1024UL * 1024UL)
+
+int kdbus_test_message_basic(struct kdbus_test_env *env)
+{
+       struct kdbus_conn *conn;
+       struct kdbus_conn *sender;
+       struct kdbus_msg *msg;
+       uint64_t cookie = 0x1234abcd5678eeff;
+       uint64_t offset;
+       int ret;
+
+       sender = kdbus_hello(env->buspath, 0, NULL, 0);
+       ASSERT_RETURN(sender != NULL);
+
+       /* create a 2nd connection */
+       conn = kdbus_hello(env->buspath, 0, NULL, 0);
+       ASSERT_RETURN(conn != NULL);
+
+       ret = kdbus_add_match_empty(conn);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_add_match_empty(sender);
+       ASSERT_RETURN(ret == 0);
+
+       /* send over 1st connection */
+       ret = kdbus_msg_send(sender, NULL, cookie, 0, 0, 0,
+                            KDBUS_DST_ID_BROADCAST, 0, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       /* Make sure that we do not get our own broadcasts */
+       ret = kdbus_msg_recv(sender, NULL, NULL);
+       ASSERT_RETURN(ret == -EAGAIN);
+
+       /* ... and receive on the 2nd */
+       ret = kdbus_msg_recv_poll(conn, 100, &msg, &offset);
+       ASSERT_RETURN(ret == 0);
+       ASSERT_RETURN(msg->cookie == cookie);
+
+       kdbus_msg_free(msg);
+
+       /* Msgs that expect a reply must have timeout and cookie */
+       ret = kdbus_msg_send(sender, NULL, 0, KDBUS_MSG_EXPECT_REPLY,
+                            0, 0, conn->id, 0, NULL);
+       ASSERT_RETURN(ret == -EINVAL);
+
+       /* Faked replies with a valid reply cookie are rejected */
+       ret = kdbus_msg_send_reply(conn, time(NULL) ^ cookie, sender->id);
+       ASSERT_RETURN(ret == -EPERM);
+
+       ret = kdbus_free(conn, offset);
+       ASSERT_RETURN(ret == 0);
+
+       kdbus_conn_free(sender);
+       kdbus_conn_free(conn);
+
+       return TEST_OK;
+}
+
+static int msg_recv_prio(struct kdbus_conn *conn,
+                        int64_t requested_prio,
+                        int64_t expected_prio)
+{
+       struct kdbus_cmd_recv recv = {
+               .size = sizeof(recv),
+               .flags = KDBUS_RECV_USE_PRIORITY,
+               .priority = requested_prio,
+       };
+       struct kdbus_msg *msg;
+       int ret;
+
+       ret = kdbus_cmd_recv(conn->fd, &recv);
+       if (ret < 0) {
+               kdbus_printf("error receiving message: %d (%m)\n", -errno);
+               return ret;
+       }
+
+       msg = (struct kdbus_msg *)(conn->buf + recv.msg.offset);
+       kdbus_msg_dump(conn, msg);
+
+       if (msg->priority != expected_prio) {
+               kdbus_printf("expected message prio %lld, got %lld\n",
+                            (unsigned long long) expected_prio,
+                            (unsigned long long) msg->priority);
+               return -EINVAL;
+       }
+
+       kdbus_msg_free(msg);
+       ret = kdbus_free(conn, recv.msg.offset);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+int kdbus_test_message_prio(struct kdbus_test_env *env)
+{
+       struct kdbus_conn *a, *b;
+       uint64_t cookie = 0;
+       int ret;
+
+       a = kdbus_hello(env->buspath, 0, NULL, 0);
+       b = kdbus_hello(env->buspath, 0, NULL, 0);
+       ASSERT_RETURN(a && b);
+
+       ret = kdbus_msg_send(b, NULL, ++cookie, 0, 0,   25, a->id, 0, NULL);
+       ASSERT_RETURN(ret == 0);
+       ret = kdbus_msg_send(b, NULL, ++cookie, 0, 0, -600, a->id, 0, NULL);
+       ASSERT_RETURN(ret == 0);
+       ret = kdbus_msg_send(b, NULL, ++cookie, 0, 0,   10, a->id, 0, NULL);
+       ASSERT_RETURN(ret == 0);
+       ret = kdbus_msg_send(b, NULL, ++cookie, 0, 0,  -35, a->id, 0, NULL);
+       ASSERT_RETURN(ret == 0);
+       ret = kdbus_msg_send(b, NULL, ++cookie, 0, 0, -100, a->id, 0, NULL);
+       ASSERT_RETURN(ret == 0);
+       ret = kdbus_msg_send(b, NULL, ++cookie, 0, 0,   20, a->id, 0, NULL);
+       ASSERT_RETURN(ret == 0);
+       ret = kdbus_msg_send(b, NULL, ++cookie, 0, 0,  -15, a->id, 0, NULL);
+       ASSERT_RETURN(ret == 0);
+       ret = kdbus_msg_send(b, NULL, ++cookie, 0, 0, -800, a->id, 0, NULL);
+       ASSERT_RETURN(ret == 0);
+       ret = kdbus_msg_send(b, NULL, ++cookie, 0, 0, -150, a->id, 0, NULL);
+       ASSERT_RETURN(ret == 0);
+       ret = kdbus_msg_send(b, NULL, ++cookie, 0, 0,   10, a->id, 0, NULL);
+       ASSERT_RETURN(ret == 0);
+       ret = kdbus_msg_send(b, NULL, ++cookie, 0, 0, -800, a->id, 0, NULL);
+       ASSERT_RETURN(ret == 0);
+       ret = kdbus_msg_send(b, NULL, ++cookie, 0, 0,  -10, a->id, 0, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       ASSERT_RETURN(msg_recv_prio(a, -200, -800) == 0);
+       ASSERT_RETURN(msg_recv_prio(a, -100, -800) == 0);
+       ASSERT_RETURN(msg_recv_prio(a, -400, -600) == 0);
+       ASSERT_RETURN(msg_recv_prio(a, -400, -600) == -EAGAIN);
+       ASSERT_RETURN(msg_recv_prio(a, 10, -150) == 0);
+       ASSERT_RETURN(msg_recv_prio(a, 10, -100) == 0);
+
+       kdbus_printf("--- get priority (all)\n");
+       ASSERT_RETURN(kdbus_msg_recv(a, NULL, NULL) == 0);
+
+       kdbus_conn_free(a);
+       kdbus_conn_free(b);
+
+       return TEST_OK;
+}
+
+static int kdbus_test_notify_kernel_quota(struct kdbus_test_env *env)
+{
+       int ret;
+       unsigned int i;
+       struct kdbus_conn *conn;
+       struct kdbus_conn *reader;
+       struct kdbus_msg *msg = NULL;
+       struct kdbus_cmd_recv recv = { .size = sizeof(recv) };
+
+       reader = kdbus_hello(env->buspath, 0, NULL, 0);
+       ASSERT_RETURN(reader);
+
+       conn = kdbus_hello(env->buspath, 0, NULL, 0);
+       ASSERT_RETURN(conn);
+
+       /* Register for ID signals */
+       ret = kdbus_add_match_id(reader, 0x1, KDBUS_ITEM_ID_ADD,
+                                KDBUS_MATCH_ID_ANY);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_add_match_id(reader, 0x2, KDBUS_ITEM_ID_REMOVE,
+                                KDBUS_MATCH_ID_ANY);
+       ASSERT_RETURN(ret == 0);
+
+       /* Each iteration two notifications: add and remove ID */
+       for (i = 0; i < KDBUS_CONN_MAX_MSGS / 2; i++) {
+               struct kdbus_conn *notifier;
+
+               notifier = kdbus_hello(env->buspath, 0, NULL, 0);
+               ASSERT_RETURN(notifier);
+
+               kdbus_conn_free(notifier);
+       }
+
+       /*
+        * Now the reader queue is full with kernel notfications,
+        * but as a user we still have room to push our messages.
+        */
+       ret = kdbus_msg_send(conn, NULL, 0xdeadbeef, 0, 0, 0, reader->id,
+                            0, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       /* More ID kernel notifications that will be lost */
+       kdbus_conn_free(conn);
+
+       conn = kdbus_hello(env->buspath, 0, NULL, 0);
+       ASSERT_RETURN(conn);
+
+       kdbus_conn_free(conn);
+
+       /*
+        * We lost only 3 packets since only signal msgs are
+        * accounted. The connection ID add/remove notification
+        */
+       ret = kdbus_cmd_recv(reader->fd, &recv);
+       ASSERT_RETURN(ret == 0);
+       ASSERT_RETURN(recv.return_flags & KDBUS_RECV_RETURN_DROPPED_MSGS);
+       ASSERT_RETURN(recv.dropped_msgs == 3);
+
+       msg = (struct kdbus_msg *)(reader->buf + recv.msg.offset);
+       kdbus_msg_free(msg);
+
+       /* Read our queue */
+       for (i = 0; i < KDBUS_CONN_MAX_MSGS - 1; i++) {
+               memset(&recv, 0, sizeof(recv));
+               recv.size = sizeof(recv);
+
+               ret = kdbus_cmd_recv(reader->fd, &recv);
+               ASSERT_RETURN(ret == 0);
+               ASSERT_RETURN(!(recv.return_flags &
+                               KDBUS_RECV_RETURN_DROPPED_MSGS));
+
+               msg = (struct kdbus_msg *)(reader->buf + recv.msg.offset);
+               kdbus_msg_free(msg);
+       }
+
+       ret = kdbus_msg_recv(reader, NULL, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_msg_recv(reader, NULL, NULL);
+       ASSERT_RETURN(ret == -EAGAIN);
+
+       kdbus_conn_free(reader);
+
+       return 0;
+}
+
+/* Return the number of message successfully sent */
+static int kdbus_fill_conn_queue(struct kdbus_conn *conn_src,
+                                uint64_t dst_id,
+                                unsigned int max_msgs)
+{
+       unsigned int i;
+       uint64_t cookie = 0;
+       size_t size;
+       struct kdbus_cmd_send cmd = {};
+       struct kdbus_msg *msg;
+       int ret;
+
+       size = sizeof(struct kdbus_msg);
+       msg = malloc(size);
+       ASSERT_RETURN_VAL(msg, -ENOMEM);
+
+       memset(msg, 0, size);
+       msg->size = size;
+       msg->src_id = conn_src->id;
+       msg->dst_id = dst_id;
+       msg->payload_type = KDBUS_PAYLOAD_DBUS;
+
+       cmd.size = sizeof(cmd);
+       cmd.msg_address = (uintptr_t)msg;
+
+       for (i = 0; i < max_msgs; i++) {
+               msg->cookie = cookie++;
+               ret = kdbus_cmd_send(conn_src->fd, &cmd);
+               if (ret < 0)
+                       break;
+       }
+
+       free(msg);
+
+       return i;
+}
+
+static int kdbus_test_activator_quota(struct kdbus_test_env *env)
+{
+       int ret;
+       unsigned int i;
+       unsigned int activator_msgs_count = 0;
+       uint64_t cookie = time(NULL);
+       struct kdbus_conn *conn;
+       struct kdbus_conn *sender;
+       struct kdbus_conn *activator;
+       struct kdbus_msg *msg;
+       uint64_t flags = KDBUS_NAME_REPLACE_EXISTING;
+       struct kdbus_cmd_recv recv = { .size = sizeof(recv) };
+       struct kdbus_policy_access access = {
+               .type = KDBUS_POLICY_ACCESS_USER,
+               .id = geteuid(),
+               .access = KDBUS_POLICY_OWN,
+       };
+
+       activator = kdbus_hello_activator(env->buspath, "foo.test.activator",
+                                         &access, 1);
+       ASSERT_RETURN(activator);
+
+       conn = kdbus_hello(env->buspath, 0, NULL, 0);
+       sender = kdbus_hello(env->buspath, 0, NULL, 0);
+       ASSERT_RETURN(conn || sender);
+
+       ret = kdbus_list(sender, KDBUS_LIST_NAMES |
+                                KDBUS_LIST_UNIQUE |
+                                KDBUS_LIST_ACTIVATORS |
+                                KDBUS_LIST_QUEUED);
+       ASSERT_RETURN(ret == 0);
+
+       for (i = 0; i < KDBUS_CONN_MAX_MSGS; i++) {
+               ret = kdbus_msg_send(sender, "foo.test.activator",
+                                    cookie++, 0, 0, 0,
+                                    KDBUS_DST_ID_NAME,
+                                    0, NULL);
+               if (ret < 0)
+                       break;
+               activator_msgs_count++;
+       }
+
+       /* we must have at least sent one message */
+       ASSERT_RETURN_VAL(i > 0, -errno);
+       ASSERT_RETURN(ret == -ENOBUFS);
+
+       /* Good, activator queue is full now */
+
+       /* ENXIO on direct send (activators can never be addressed by ID) */
+       ret = kdbus_msg_send(conn, NULL, cookie++, 0, 0, 0, activator->id,
+                            0, NULL);
+       ASSERT_RETURN(ret == -ENXIO);
+
+       /* can't queue more */
+       ret = kdbus_msg_send(conn, "foo.test.activator", cookie++,
+                            0, 0, 0, KDBUS_DST_ID_NAME, 0, NULL);
+       ASSERT_RETURN(ret == -ENOBUFS);
+
+       /* no match installed, so the broadcast will not inc dropped_msgs */
+       ret = kdbus_msg_send(sender, NULL, cookie++, 0, 0, 0,
+                            KDBUS_DST_ID_BROADCAST, 0, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       /* Check activator queue */
+       ret = kdbus_cmd_recv(activator->fd, &recv);
+       ASSERT_RETURN(ret == 0);
+       ASSERT_RETURN(recv.dropped_msgs == 0);
+
+       activator_msgs_count--;
+
+       msg = (struct kdbus_msg *)(activator->buf + recv.msg.offset);
+       kdbus_msg_free(msg);
+
+
+       /* Stage 1) of test check the pool memory quota */
+
+       /* Consume the connection pool memory */
+       for (i = 0; i < KDBUS_CONN_MAX_MSGS; i++) {
+               ret = kdbus_msg_send(sender, NULL,
+                                    cookie++, 0, 0, 0, conn->id, 0, NULL);
+               if (ret < 0)
+                       break;
+       }
+
+       /* consume one message, so later at least one can be moved */
+       memset(&recv, 0, sizeof(recv));
+       recv.size = sizeof(recv);
+       ret = kdbus_cmd_recv(conn->fd, &recv);
+       ASSERT_RETURN(ret == 0);
+       ASSERT_RETURN(recv.dropped_msgs == 0);
+       msg = (struct kdbus_msg *)(conn->buf + recv.msg.offset);
+       kdbus_msg_free(msg);
+
+       /* Try to acquire the name now */
+       ret = kdbus_name_acquire(conn, "foo.test.activator", &flags);
+       ASSERT_RETURN(ret == 0);
+
+       /* try to read messages and see if we have lost some */
+       memset(&recv, 0, sizeof(recv));
+       recv.size = sizeof(recv);
+       ret = kdbus_cmd_recv(conn->fd, &recv);
+       ASSERT_RETURN(ret == 0);
+       ASSERT_RETURN(recv.dropped_msgs != 0);
+
+       /* number of dropped msgs < received ones (at least one was moved) */
+       ASSERT_RETURN(recv.dropped_msgs < activator_msgs_count);
+
+       /* Deduct the number of dropped msgs from the activator msgs */
+       activator_msgs_count -= recv.dropped_msgs;
+
+       msg = (struct kdbus_msg *)(activator->buf + recv.msg.offset);
+       kdbus_msg_free(msg);
+
+       /*
+        * Release the name and hand it back to activator, now
+        * we should have 'activator_msgs_count' msgs again in
+        * the activator queue
+        */
+       ret = kdbus_name_release(conn, "foo.test.activator");
+       ASSERT_RETURN(ret == 0);
+
+       /* make sure that we got our previous activator msgs */
+       ret = kdbus_msg_recv(activator, &msg, NULL);
+       ASSERT_RETURN(ret == 0);
+       ASSERT_RETURN(msg->src_id == sender->id);
+
+       activator_msgs_count--;
+
+       kdbus_msg_free(msg);
+
+
+       /* Stage 2) of test check max message quota */
+
+       /* Empty conn queue */
+       for (i = 0; i < KDBUS_CONN_MAX_MSGS; i++) {
+               ret = kdbus_msg_recv(conn, NULL, NULL);
+               if (ret == -EAGAIN)
+                       break;
+       }
+
+       /* fill queue with max msgs quota */
+       ret = kdbus_fill_conn_queue(sender, conn->id, KDBUS_CONN_MAX_MSGS);
+       ASSERT_RETURN(ret == KDBUS_CONN_MAX_MSGS);
+
+       /* This one is lost but it is not accounted */
+       ret = kdbus_msg_send(sender, NULL,
+                            cookie++, 0, 0, 0, conn->id, 0, NULL);
+       ASSERT_RETURN(ret == -ENOBUFS);
+
+       /* Acquire the name again */
+       ret = kdbus_name_acquire(conn, "foo.test.activator", &flags);
+       ASSERT_RETURN(ret == 0);
+
+       memset(&recv, 0, sizeof(recv));
+       recv.size = sizeof(recv);
+
+       /*
+        * Try to read messages and make sure that we have lost all
+        * the activator messages due to quota checks. Our queue is
+        * already full.
+        */
+       ret = kdbus_cmd_recv(conn->fd, &recv);
+       ASSERT_RETURN(ret == 0);
+       ASSERT_RETURN(recv.dropped_msgs == activator_msgs_count);
+
+       msg = (struct kdbus_msg *)(activator->buf + recv.msg.offset);
+       kdbus_msg_free(msg);
+
+       kdbus_conn_free(sender);
+       kdbus_conn_free(conn);
+       kdbus_conn_free(activator);
+
+       return 0;
+}
+
+static int kdbus_test_expected_reply_quota(struct kdbus_test_env *env)
+{
+       int ret;
+       unsigned int i, n;
+       unsigned int count;
+       uint64_t cookie = 0x1234abcd5678eeff;
+       struct kdbus_conn *conn;
+       struct kdbus_conn *connections[9];
+
+       conn = kdbus_hello(env->buspath, 0, NULL, 0);
+       ASSERT_RETURN(conn);
+
+       for (i = 0; i < 9; i++) {
+               connections[i] = kdbus_hello(env->buspath, 0, NULL, 0);
+               ASSERT_RETURN(connections[i]);
+       }
+
+       count = 0;
+       /* Send 16 messages to 8 different connections */
+       for (i = 0; i < 8; i++) {
+               for (n = 0; n < 16; n++) {
+                       ret = kdbus_msg_send(conn, NULL, cookie++,
+                                            KDBUS_MSG_EXPECT_REPLY,
+                                            100000000ULL, 0,
+                                            connections[i]->id,
+                                            0, NULL);
+                       if (ret < 0)
+                               break;
+
+                       count++;
+               }
+       }
+
+       /*
+        * We should have queued at least
+        * KDBUS_CONN_MAX_REQUESTS_PENDING method call
+        */
+       ASSERT_RETURN(count == KDBUS_CONN_MAX_REQUESTS_PENDING);
+
+       /*
+        * Now try to send a message to the last connection,
+        * if we have reached KDBUS_CONN_MAX_REQUESTS_PENDING
+        * no further requests are allowed
+        */
+       ret = kdbus_msg_send(conn, NULL, cookie++, KDBUS_MSG_EXPECT_REPLY,
+                            1000000000ULL, 0, connections[8]->id, 0, NULL);
+       ASSERT_RETURN(ret == -EMLINK);
+
+       for (i = 0; i < 9; i++)
+               kdbus_conn_free(connections[i]);
+
+       kdbus_conn_free(conn);
+
+       return 0;
+}
+
+int kdbus_test_pool_quota(struct kdbus_test_env *env)
+{
+       struct kdbus_conn *a, *b, *c;
+       struct kdbus_cmd_send cmd = {};
+       struct kdbus_item *item;
+       struct kdbus_msg *recv_msg;
+       struct kdbus_msg *msg;
+       uint64_t cookie = time(NULL);
+       uint64_t size;
+       unsigned int i;
+       char *payload;
+       int ret;
+
+       /* just a guard */
+       if (POOL_SIZE <= KDBUS_MSG_MAX_PAYLOAD_VEC_SIZE ||
+           POOL_SIZE % KDBUS_MSG_MAX_PAYLOAD_VEC_SIZE != 0)
+               return 0;
+
+       payload = calloc(KDBUS_MSG_MAX_PAYLOAD_VEC_SIZE, sizeof(char));
+       ASSERT_RETURN_VAL(payload, -ENOMEM);
+
+       a = kdbus_hello(env->buspath, 0, NULL, 0);
+       b = kdbus_hello(env->buspath, 0, NULL, 0);
+       c = kdbus_hello(env->buspath, 0, NULL, 0);
+       ASSERT_RETURN(a && b && c);
+
+       size = sizeof(struct kdbus_msg);
+       size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_vec));
+
+       msg = malloc(size);
+       ASSERT_RETURN_VAL(msg, -ENOMEM);
+
+       memset(msg, 0, size);
+       msg->size = size;
+       msg->src_id = a->id;
+       msg->dst_id = c->id;
+       msg->payload_type = KDBUS_PAYLOAD_DBUS;
+
+       item = msg->items;
+       item->type = KDBUS_ITEM_PAYLOAD_VEC;
+       item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(struct kdbus_vec);
+       item->vec.address = (uintptr_t)payload;
+       item->vec.size = KDBUS_MSG_MAX_PAYLOAD_VEC_SIZE;
+       item = KDBUS_ITEM_NEXT(item);
+
+       cmd.size = sizeof(cmd);
+       cmd.msg_address = (uintptr_t)msg;
+
+       /*
+        * Send 2097248 bytes, a user is only allowed to get 33% of half of
+        * the free space of the pool, the already used space is
+        * accounted as free space
+        */
+       size += KDBUS_MSG_MAX_PAYLOAD_VEC_SIZE;
+       for (i = size; i < (POOL_SIZE / 2 / 3); i += size) {
+               msg->cookie = cookie++;
+
+               ret = kdbus_cmd_send(a->fd, &cmd);
+               ASSERT_RETURN_VAL(ret == 0, ret);
+       }
+
+       /* Try to get more than 33% */
+       msg->cookie = cookie++;
+       ret = kdbus_cmd_send(a->fd, &cmd);
+       ASSERT_RETURN(ret == -ENOBUFS);
+
+       /* We still can pass small messages */
+       ret = kdbus_msg_send(b, NULL, cookie++, 0, 0, 0, c->id, 0, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       for (i = size; i < (POOL_SIZE / 2 / 3); i += size) {
+               ret = kdbus_msg_recv(c, &recv_msg, NULL);
+               ASSERT_RETURN(ret == 0);
+               ASSERT_RETURN(recv_msg->src_id == a->id);
+
+               kdbus_msg_free(recv_msg);
+       }
+
+       ret = kdbus_msg_recv(c, &recv_msg, NULL);
+       ASSERT_RETURN(ret == 0);
+       ASSERT_RETURN(recv_msg->src_id == b->id);
+
+       kdbus_msg_free(recv_msg);
+
+       ret = kdbus_msg_recv(c, NULL, NULL);
+       ASSERT_RETURN(ret == -EAGAIN);
+
+       free(msg);
+       free(payload);
+
+       kdbus_conn_free(c);
+       kdbus_conn_free(b);
+       kdbus_conn_free(a);
+
+       return 0;
+}
+
+int kdbus_test_message_quota(struct kdbus_test_env *env)
+{
+       struct kdbus_conn *a, *b;
+       uint64_t cookie = 0;
+       int ret;
+       int i;
+
+       ret = kdbus_test_activator_quota(env);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_test_notify_kernel_quota(env);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_test_pool_quota(env);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_test_expected_reply_quota(env);
+       ASSERT_RETURN(ret == 0);
+
+       a = kdbus_hello(env->buspath, 0, NULL, 0);
+       b = kdbus_hello(env->buspath, 0, NULL, 0);
+
+       ret = kdbus_fill_conn_queue(b, a->id, KDBUS_CONN_MAX_MSGS);
+       ASSERT_RETURN(ret == KDBUS_CONN_MAX_MSGS);
+
+       ret = kdbus_msg_send(b, NULL, ++cookie, 0, 0, 0, a->id, 0, NULL);
+       ASSERT_RETURN(ret == -ENOBUFS);
+
+       for (i = 0; i < KDBUS_CONN_MAX_MSGS; ++i) {
+               ret = kdbus_msg_recv(a, NULL, NULL);
+               ASSERT_RETURN(ret == 0);
+       }
+
+       ret = kdbus_msg_recv(a, NULL, NULL);
+       ASSERT_RETURN(ret == -EAGAIN);
+
+       ret = kdbus_fill_conn_queue(b, a->id, KDBUS_CONN_MAX_MSGS + 1);
+       ASSERT_RETURN(ret == KDBUS_CONN_MAX_MSGS);
+
+       ret = kdbus_msg_send(b, NULL, ++cookie, 0, 0, 0, a->id, 0, NULL);
+       ASSERT_RETURN(ret == -ENOBUFS);
+
+       kdbus_conn_free(a);
+       kdbus_conn_free(b);
+
+       return TEST_OK;
+}
+
+int kdbus_test_memory_access(struct kdbus_test_env *env)
+{
+       struct kdbus_conn *a, *b;
+       struct kdbus_cmd_send cmd = {};
+       struct kdbus_item *item;
+       struct kdbus_msg *msg;
+       uint64_t test_addr = 0;
+       char line[256];
+       uint64_t size;
+       FILE *f;
+       int ret;
+
+       /*
+        * Search in /proc/kallsyms for the address of a kernel symbol that
+        * should always be there, regardless of the config. Use that address
+        * in a PAYLOAD_VEC item and make sure it's inaccessible.
+        */
+
+       f = fopen("/proc/kallsyms", "r");
+       if (!f)
+               return TEST_SKIP;
+
+       while (fgets(line, sizeof(line), f)) {
+               char *s = line;
+
+               if (!strsep(&s, " "))
+                       continue;
+
+               if (!strsep(&s, " "))
+                       continue;
+
+               if (!strncmp(s, "mutex_lock", 10)) {
+                       test_addr = strtoull(line, NULL, 16);
+                       break;
+               }
+       }
+
+       fclose(f);
+
+       if (!test_addr)
+               return TEST_SKIP;
+
+       a = kdbus_hello(env->buspath, 0, NULL, 0);
+       b = kdbus_hello(env->buspath, 0, NULL, 0);
+       ASSERT_RETURN(a && b);
+
+       size = sizeof(struct kdbus_msg);
+       size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_vec));
+
+       msg = alloca(size);
+       ASSERT_RETURN_VAL(msg, -ENOMEM);
+
+       memset(msg, 0, size);
+       msg->size = size;
+       msg->src_id = a->id;
+       msg->dst_id = b->id;
+       msg->payload_type = KDBUS_PAYLOAD_DBUS;
+
+       item = msg->items;
+       item->type = KDBUS_ITEM_PAYLOAD_VEC;
+       item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(struct kdbus_vec);
+       item->vec.address = test_addr;
+       item->vec.size = sizeof(void*);
+       item = KDBUS_ITEM_NEXT(item);
+
+       cmd.size = sizeof(cmd);
+       cmd.msg_address = (uintptr_t)msg;
+
+       ret = kdbus_cmd_send(a->fd, &cmd);
+       ASSERT_RETURN(ret == -EFAULT);
+
+       kdbus_conn_free(b);
+       kdbus_conn_free(a);
+
+       return 0;
+}
diff --git a/tools/testing/selftests/kdbus/test-metadata-ns.c b/tools/testing/selftests/kdbus/test-metadata-ns.c
new file mode 100644 (file)
index 0000000..395a3fa
--- /dev/null
@@ -0,0 +1,506 @@
+/*
+ * Test metadata in new namespaces. Even if our tests can run
+ * in a namespaced setup, this test is necessary so we can inspect
+ * metadata on the same kdbusfs but between multiple namespaces
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sched.h>
+#include <time.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <errno.h>
+#include <assert.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <sys/prctl.h>
+#include <sys/eventfd.h>
+#include <sys/syscall.h>
+#include <sys/capability.h>
+#include <linux/sched.h>
+
+#include "kdbus-test.h"
+#include "kdbus-util.h"
+#include "kdbus-enum.h"
+
+static const struct kdbus_creds privileged_creds = {};
+
+static const struct kdbus_creds unmapped_creds = {
+       .uid    = UNPRIV_UID,
+       .euid   = UNPRIV_UID,
+       .suid   = UNPRIV_UID,
+       .fsuid  = UNPRIV_UID,
+       .gid    = UNPRIV_GID,
+       .egid   = UNPRIV_GID,
+       .sgid   = UNPRIV_GID,
+       .fsgid  = UNPRIV_GID,
+};
+
+static const struct kdbus_pids unmapped_pids = {};
+
+/* Get only the first item */
+static struct kdbus_item *kdbus_get_item(struct kdbus_msg *msg,
+                                        uint64_t type)
+{
+       struct kdbus_item *item;
+
+       KDBUS_ITEM_FOREACH(item, msg, items)
+               if (item->type == type)
+                       return item;
+
+       return NULL;
+}
+
+static int kdbus_match_kdbus_creds(struct kdbus_msg *msg,
+                                  const struct kdbus_creds *expected_creds)
+{
+       struct kdbus_item *item;
+
+       item = kdbus_get_item(msg, KDBUS_ITEM_CREDS);
+       ASSERT_RETURN(item);
+
+       ASSERT_RETURN(memcmp(&item->creds, expected_creds,
+                            sizeof(struct kdbus_creds)) == 0);
+
+       return 0;
+}
+
+static int kdbus_match_kdbus_pids(struct kdbus_msg *msg,
+                                 const struct kdbus_pids *expected_pids)
+{
+       struct kdbus_item *item;
+
+       item = kdbus_get_item(msg, KDBUS_ITEM_PIDS);
+       ASSERT_RETURN(item);
+
+       ASSERT_RETURN(memcmp(&item->pids, expected_pids,
+                            sizeof(struct kdbus_pids)) == 0);
+
+       return 0;
+}
+
+static int __kdbus_clone_userns_test(const char *bus,
+                                    struct kdbus_conn *conn,
+                                    uint64_t grandpa_pid,
+                                    int signal_fd)
+{
+       int clone_ret;
+       int ret;
+       struct kdbus_msg *msg = NULL;
+       const struct kdbus_item *item;
+       uint64_t cookie = time(NULL) ^ 0xdeadbeef;
+       struct kdbus_conn *unpriv_conn = NULL;
+       struct kdbus_pids parent_pids = {
+               .pid = getppid(),
+               .tid = getppid(),
+               .ppid = grandpa_pid,
+       };
+
+       ret = drop_privileges(UNPRIV_UID, UNPRIV_GID);
+       ASSERT_EXIT(ret == 0);
+
+       unpriv_conn = kdbus_hello(bus, 0, NULL, 0);
+       ASSERT_EXIT(unpriv_conn);
+
+       ret = kdbus_add_match_empty(unpriv_conn);
+       ASSERT_EXIT(ret == 0);
+
+       /*
+        * ping privileged connection from this new unprivileged
+        * one
+        */
+
+       ret = kdbus_msg_send(unpriv_conn, NULL, cookie, 0, 0,
+                            0, conn->id, 0, NULL);
+       ASSERT_EXIT(ret == 0);
+
+       /*
+        * Since we just dropped privileges, the dumpable flag
+        * was just cleared which makes the /proc/$clone_child/uid_map
+        * to be owned by root, hence any userns uid mapping will fail
+        * with -EPERM since the mapping will be done by uid 65534.
+        *
+        * To avoid this set the dumpable flag again which makes
+        * procfs update the /proc/$clone_child/ inodes owner to 65534.
+        *
+        * Using this we will be able write to /proc/$clone_child/uid_map
+        * as uid 65534 and map the uid 65534 to 0 inside the user namespace.
+        */
+       ret = prctl(PR_SET_DUMPABLE, SUID_DUMP_USER);
+       ASSERT_EXIT(ret == 0);
+
+       /* Make child privileged in its new userns and run tests */
+
+       ret = RUN_CLONE_CHILD(&clone_ret,
+                             SIGCHLD | CLONE_NEWUSER | CLONE_NEWPID,
+       ({ 0;  /* Clone setup, nothing */ }),
+       ({
+               eventfd_t event_status = 0;
+               struct kdbus_conn *userns_conn;
+
+               /* ping connection from the new user namespace */
+               userns_conn = kdbus_hello(bus, 0, NULL, 0);
+               ASSERT_EXIT(userns_conn);
+
+               ret = kdbus_add_match_empty(userns_conn);
+               ASSERT_EXIT(ret == 0);
+
+               cookie++;
+               ret = kdbus_msg_send(userns_conn, NULL, cookie,
+                                    0, 0, 0, conn->id, 0, NULL);
+               ASSERT_EXIT(ret == 0);
+
+               /* Parent did send */
+               ret = eventfd_read(signal_fd, &event_status);
+               ASSERT_RETURN(ret >= 0 && event_status == 1);
+
+               /*
+                * Receive from privileged connection
+                */
+               kdbus_printf("Privileged â†’ unprivileged/privileged "
+                            "in its userns "
+                            "(different userns and pidns):\n");
+               ret = kdbus_msg_recv_poll(userns_conn, 300, &msg, NULL);
+               ASSERT_EXIT(ret == 0);
+               ASSERT_EXIT(msg->dst_id == userns_conn->id);
+
+               /* Different namespaces no CAPS */
+               item = kdbus_get_item(msg, KDBUS_ITEM_CAPS);
+               ASSERT_EXIT(item == NULL);
+
+               /* uid/gid not mapped, so we have unpriv cached creds */
+               ret = kdbus_match_kdbus_creds(msg, &unmapped_creds);
+               ASSERT_EXIT(ret == 0);
+
+               /*
+                * Diffent pid namepsaces. This is the child pidns
+                * so it should not see its parent kdbus_pids
+                */
+               ret = kdbus_match_kdbus_pids(msg, &unmapped_pids);
+               ASSERT_EXIT(ret == 0);
+
+               kdbus_msg_free(msg);
+
+
+               /*
+                * Receive broadcast from privileged connection
+                */
+               kdbus_printf("Privileged â†’ unprivileged/privileged "
+                            "in its userns "
+                            "(different userns and pidns):\n");
+               ret = kdbus_msg_recv_poll(userns_conn, 300, &msg, NULL);
+               ASSERT_EXIT(ret == 0);
+               ASSERT_EXIT(msg->dst_id == KDBUS_DST_ID_BROADCAST);
+
+               /* Different namespaces no CAPS */
+               item = kdbus_get_item(msg, KDBUS_ITEM_CAPS);
+               ASSERT_EXIT(item == NULL);
+
+               /* uid/gid not mapped, so we have unpriv cached creds */
+               ret = kdbus_match_kdbus_creds(msg, &unmapped_creds);
+               ASSERT_EXIT(ret == 0);
+
+               /*
+                * Diffent pid namepsaces. This is the child pidns
+                * so it should not see its parent kdbus_pids
+                */
+               ret = kdbus_match_kdbus_pids(msg, &unmapped_pids);
+               ASSERT_EXIT(ret == 0);
+
+               kdbus_msg_free(msg);
+
+               kdbus_conn_free(userns_conn);
+       }),
+       ({
+               /* Parent setup map child uid/gid */
+               ret = userns_map_uid_gid(pid, "0 65534 1", "0 65534 1");
+               ASSERT_EXIT(ret == 0);
+       }),
+       ({ 0; }));
+       /* Unprivileged was not able to create user namespace */
+       if (clone_ret == -EPERM) {
+               kdbus_printf("-- CLONE_NEWUSER TEST Failed for "
+                            "uid: %u\n -- Make sure that your kernel "
+                            "do not allow CLONE_NEWUSER for "
+                            "unprivileged users\n", UNPRIV_UID);
+               ret = 0;
+               goto out;
+       }
+
+       ASSERT_EXIT(ret == 0);
+
+
+       /*
+        * Receive from privileged connection
+        */
+       kdbus_printf("\nPrivileged â†’ unprivileged (same namespaces):\n");
+       ret = kdbus_msg_recv_poll(unpriv_conn, 300, &msg, NULL);
+
+       ASSERT_EXIT(ret == 0);
+       ASSERT_EXIT(msg->dst_id == unpriv_conn->id);
+
+       /* will get the privileged creds */
+       ret = kdbus_match_kdbus_creds(msg, &privileged_creds);
+       ASSERT_EXIT(ret == 0);
+
+       /* Same pidns so will get the kdbus_pids */
+       ret = kdbus_match_kdbus_pids(msg, &parent_pids);
+       ASSERT_RETURN(ret == 0);
+
+       kdbus_msg_free(msg);
+
+
+       /*
+        * Receive broadcast from privileged connection
+        */
+       kdbus_printf("\nPrivileged â†’ unprivileged (same namespaces):\n");
+       ret = kdbus_msg_recv_poll(unpriv_conn, 300, &msg, NULL);
+
+       ASSERT_EXIT(ret == 0);
+       ASSERT_EXIT(msg->dst_id == KDBUS_DST_ID_BROADCAST);
+
+       /* will get the privileged creds */
+       ret = kdbus_match_kdbus_creds(msg, &privileged_creds);
+       ASSERT_EXIT(ret == 0);
+
+       ret = kdbus_match_kdbus_pids(msg, &parent_pids);
+       ASSERT_RETURN(ret == 0);
+
+       kdbus_msg_free(msg);
+
+out:
+       kdbus_conn_free(unpriv_conn);
+
+       return ret;
+}
+
+static int kdbus_clone_userns_test(const char *bus,
+                                  struct kdbus_conn *conn)
+{
+       int ret;
+       int status;
+       int efd = -1;
+       pid_t pid, ppid;
+       uint64_t unpriv_conn_id = 0;
+       uint64_t userns_conn_id = 0;
+       struct kdbus_msg *msg;
+       const struct kdbus_item *item;
+       struct kdbus_pids expected_pids;
+       struct kdbus_conn *monitor = NULL;
+
+       kdbus_printf("STARTING TEST 'metadata-ns'.\n");
+
+       monitor = kdbus_hello(bus, KDBUS_HELLO_MONITOR, NULL, 0);
+       ASSERT_EXIT(monitor);
+
+       /*
+        * parent will signal to child that is in its
+        * userns to read its queue
+        */
+       efd = eventfd(0, EFD_CLOEXEC);
+       ASSERT_RETURN_VAL(efd >= 0, efd);
+
+       ppid = getppid();
+
+       pid = fork();
+       ASSERT_RETURN_VAL(pid >= 0, -errno);
+
+       if (pid == 0) {
+               ret = prctl(PR_SET_PDEATHSIG, SIGKILL);
+               ASSERT_EXIT_VAL(ret == 0, -errno);
+
+               ret = __kdbus_clone_userns_test(bus, conn, ppid, efd);
+               _exit(ret);
+       }
+
+
+       /* Phase 1) privileged receives from unprivileged */
+
+       /*
+        * Receive from the unprivileged child
+        */
+       kdbus_printf("\nUnprivileged â†’ privileged (same namespaces):\n");
+       ret = kdbus_msg_recv_poll(conn, 300, &msg, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       unpriv_conn_id = msg->src_id;
+
+       /* Unprivileged user */
+       ret = kdbus_match_kdbus_creds(msg, &unmapped_creds);
+       ASSERT_RETURN(ret == 0);
+
+       /* Set the expected creds_pids */
+       expected_pids = (struct kdbus_pids) {
+               .pid = pid,
+               .tid = pid,
+               .ppid = getpid(),
+       };
+       ret = kdbus_match_kdbus_pids(msg, &expected_pids);
+       ASSERT_RETURN(ret == 0);
+
+       kdbus_msg_free(msg);
+
+
+       /*
+        * Receive from the unprivileged that is in his own
+        * userns and pidns
+        */
+
+       kdbus_printf("\nUnprivileged/privileged in its userns â†’ privileged "
+                    "(different userns and pidns)\n");
+       ret = kdbus_msg_recv_poll(conn, 300, &msg, NULL);
+       if (ret == -ETIMEDOUT)
+               /* perhaps unprivileged userns is not allowed */
+               goto wait;
+
+       ASSERT_RETURN(ret == 0);
+
+       userns_conn_id = msg->src_id;
+
+       /* We do not share the userns, os no KDBUS_ITEM_CAPS */
+       item = kdbus_get_item(msg, KDBUS_ITEM_CAPS);
+       ASSERT_RETURN(item == NULL);
+
+       /*
+        * Compare received items, creds must be translated into
+        * the receiver user namespace, so the user is unprivileged
+        */
+       ret = kdbus_match_kdbus_creds(msg, &unmapped_creds);
+       ASSERT_RETURN(ret == 0);
+
+       /*
+        * We should have the kdbus_pids since we are the parent
+        * pidns
+        */
+       item = kdbus_get_item(msg, KDBUS_ITEM_PIDS);
+       ASSERT_RETURN(item);
+
+       ASSERT_RETURN(memcmp(&item->pids, &unmapped_pids,
+                            sizeof(struct kdbus_pids)) != 0);
+
+       /*
+        * Parent pid of the unprivileged/privileged in its userns
+        * is the unprivileged child pid that was forked here.
+        */
+       ASSERT_RETURN((uint64_t)pid == item->pids.ppid);
+
+       kdbus_msg_free(msg);
+
+
+       /* Phase 2) Privileged connection sends now 3 packets */
+
+       /*
+        * Sending to unprivileged connections a unicast
+        */
+       ret = kdbus_msg_send(conn, NULL, 0xdeadbeef, 0, 0,
+                            0, unpriv_conn_id, 0, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       /* signal to child that is in its userns */
+       ret = eventfd_write(efd, 1);
+       ASSERT_EXIT(ret == 0);
+
+       /*
+        * Sending to unprivileged/privilged in its userns
+        * connections a unicast
+        */
+       ret = kdbus_msg_send(conn, NULL, 0xdeadbeef, 0, 0,
+                            0, userns_conn_id, 0, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       /*
+        * Sending to unprivileged connections a broadcast
+        */
+       ret = kdbus_msg_send(conn, NULL, 0xdeadbeef, 0, 0,
+                            0, KDBUS_DST_ID_BROADCAST, 0, NULL);
+       ASSERT_RETURN(ret == 0);
+
+
+wait:
+       ret = waitpid(pid, &status, 0);
+       ASSERT_RETURN(ret >= 0);
+
+       ASSERT_RETURN(WIFEXITED(status))
+       ASSERT_RETURN(!WEXITSTATUS(status));
+
+       /* Dump monitor queue */
+       kdbus_printf("\n\nMonitor queue:\n");
+       for (;;) {
+               ret = kdbus_msg_recv_poll(monitor, 100, &msg, NULL);
+               if (ret < 0)
+                       break;
+
+               if (msg->payload_type == KDBUS_PAYLOAD_DBUS) {
+                       /*
+                        * Parent pidns should see all the
+                        * pids
+                        */
+                       item = kdbus_get_item(msg, KDBUS_ITEM_PIDS);
+                       ASSERT_RETURN(item);
+
+                       ASSERT_RETURN(item->pids.pid != 0 &&
+                                     item->pids.tid != 0 &&
+                                     item->pids.ppid != 0);
+               }
+
+               kdbus_msg_free(msg);
+       }
+
+       kdbus_conn_free(monitor);
+       close(efd);
+
+       return 0;
+}
+
+int kdbus_test_metadata_ns(struct kdbus_test_env *env)
+{
+       int ret;
+       struct kdbus_conn *holder, *conn;
+       struct kdbus_policy_access policy_access = {
+               /* Allow world so we can inspect metadata in namespace */
+               .type = KDBUS_POLICY_ACCESS_WORLD,
+               .id = geteuid(),
+               .access = KDBUS_POLICY_TALK,
+       };
+
+       /*
+        * We require user-namespaces and all uids/gids
+        * should be mapped (we can just require the necessary ones)
+        */
+       if (!config_user_ns_is_enabled() ||
+           !all_uids_gids_are_mapped())
+               return TEST_SKIP;
+
+       ret = test_is_capable(CAP_SETUID, CAP_SETGID, CAP_SYS_ADMIN, -1);
+       ASSERT_RETURN(ret >= 0);
+
+       /* no enough privileges, SKIP test */
+       if (!ret)
+               return TEST_SKIP;
+
+       holder = kdbus_hello_registrar(env->buspath, "com.example.metadata",
+                                      &policy_access, 1,
+                                      KDBUS_HELLO_POLICY_HOLDER);
+       ASSERT_RETURN(holder);
+
+       conn = kdbus_hello(env->buspath, 0, NULL, 0);
+       ASSERT_RETURN(conn);
+
+       ret = kdbus_add_match_empty(conn);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_name_acquire(conn, "com.example.metadata", NULL);
+       ASSERT_EXIT(ret >= 0);
+
+       ret = kdbus_clone_userns_test(env->buspath, conn);
+       ASSERT_RETURN(ret == 0);
+
+       kdbus_conn_free(holder);
+       kdbus_conn_free(conn);
+
+       return TEST_OK;
+}
diff --git a/tools/testing/selftests/kdbus/test-monitor.c b/tools/testing/selftests/kdbus/test-monitor.c
new file mode 100644 (file)
index 0000000..ae87d87
--- /dev/null
@@ -0,0 +1,177 @@
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <assert.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sys/capability.h>
+#include <sys/wait.h>
+
+#include "kdbus-util.h"
+#include "kdbus-enum.h"
+
+#include "kdbus-util.h"
+#include "kdbus-enum.h"
+#include "kdbus-test.h"
+
+int kdbus_test_monitor(struct kdbus_test_env *env)
+{
+       struct kdbus_conn *monitor, *conn;
+       unsigned int cookie = 0xdeadbeef;
+       struct kdbus_msg *msg;
+       uint64_t offset = 0;
+       int ret;
+
+       conn = kdbus_hello(env->buspath, 0, NULL, 0);
+       ASSERT_RETURN(conn);
+
+       /* add matches to make sure the monitor do not trigger an item add or
+        * remove on connect and disconnect, respectively.
+        */
+       ret = kdbus_add_match_id(conn, 0x1, KDBUS_ITEM_ID_ADD,
+                                KDBUS_MATCH_ID_ANY);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_add_match_id(conn, 0x2, KDBUS_ITEM_ID_REMOVE,
+                                KDBUS_MATCH_ID_ANY);
+       ASSERT_RETURN(ret == 0);
+
+       /* register a monitor */
+       monitor = kdbus_hello(env->buspath, KDBUS_HELLO_MONITOR, NULL, 0);
+       ASSERT_RETURN(monitor);
+
+       /* make sure we did not receive a monitor connect notification */
+       ret = kdbus_msg_recv(conn, &msg, &offset);
+       ASSERT_RETURN(ret == -EAGAIN);
+
+       /* check that a monitor cannot acquire a name */
+       ret = kdbus_name_acquire(monitor, "foo.bar.baz", NULL);
+       ASSERT_RETURN(ret == -EOPNOTSUPP);
+
+       ret = kdbus_msg_send(env->conn, NULL, cookie, 0, 0,  0, conn->id,
+                            0, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       /* the recipient should have gotten the message */
+       ret = kdbus_msg_recv(conn, &msg, &offset);
+       ASSERT_RETURN(ret == 0);
+       ASSERT_RETURN(msg->cookie == cookie);
+       kdbus_msg_free(msg);
+       kdbus_free(conn, offset);
+
+       /* and so should the monitor */
+       ret = kdbus_msg_recv(monitor, &msg, &offset);
+       ASSERT_RETURN(ret == 0);
+       ASSERT_RETURN(msg->cookie == cookie);
+
+       kdbus_msg_free(msg);
+       kdbus_free(monitor, offset);
+
+       /* Installing matches for monitors must fais must fail */
+       ret = kdbus_add_match_empty(monitor);
+       ASSERT_RETURN(ret == -EOPNOTSUPP);
+
+       cookie++;
+       ret = kdbus_msg_send(env->conn, NULL, cookie, 0, 0, 0,
+                            KDBUS_DST_ID_BROADCAST, 0, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       /* The monitor should get the message. */
+       ret = kdbus_msg_recv_poll(monitor, 100, &msg, &offset);
+       ASSERT_RETURN(ret == 0);
+       ASSERT_RETURN(msg->cookie == cookie);
+
+       kdbus_msg_free(msg);
+       kdbus_free(monitor, offset);
+
+       /*
+        * Since we are the only monitor, update the attach flags
+        * and tell we are not interessted in attach flags recv
+        */
+
+       ret = kdbus_conn_update_attach_flags(monitor,
+                                            _KDBUS_ATTACH_ALL,
+                                            0);
+       ASSERT_RETURN(ret == 0);
+
+       cookie++;
+       ret = kdbus_msg_send(env->conn, NULL, cookie, 0, 0, 0,
+                            KDBUS_DST_ID_BROADCAST, 0, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_msg_recv_poll(monitor, 100, &msg, &offset);
+       ASSERT_RETURN(ret == 0);
+       ASSERT_RETURN(msg->cookie == cookie);
+
+       ret = kdbus_item_in_message(msg, KDBUS_ITEM_TIMESTAMP);
+       ASSERT_RETURN(ret == 0);
+
+       kdbus_msg_free(msg);
+       kdbus_free(monitor, offset);
+
+       /*
+        * Now we are interested in KDBUS_ITEM_TIMESTAMP and
+        * KDBUS_ITEM_CREDS
+        */
+       ret = kdbus_conn_update_attach_flags(monitor,
+                                            _KDBUS_ATTACH_ALL,
+                                            KDBUS_ATTACH_TIMESTAMP |
+                                            KDBUS_ATTACH_CREDS);
+       ASSERT_RETURN(ret == 0);
+
+       cookie++;
+       ret = kdbus_msg_send(env->conn, NULL, cookie, 0, 0, 0,
+                            KDBUS_DST_ID_BROADCAST, 0, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_msg_recv_poll(monitor, 100, &msg, &offset);
+       ASSERT_RETURN(ret == 0);
+       ASSERT_RETURN(msg->cookie == cookie);
+
+       ret = kdbus_item_in_message(msg, KDBUS_ITEM_TIMESTAMP);
+       ASSERT_RETURN(ret == 1);
+
+       ret = kdbus_item_in_message(msg, KDBUS_ITEM_CREDS);
+       ASSERT_RETURN(ret == 1);
+
+       /* the KDBUS_ITEM_PID_COMM was not requested */
+       ret = kdbus_item_in_message(msg, KDBUS_ITEM_PID_COMM);
+       ASSERT_RETURN(ret == 0);
+
+       kdbus_msg_free(msg);
+       kdbus_free(monitor, offset);
+
+       kdbus_conn_free(monitor);
+       /* make sure we did not receive a monitor disconnect notification */
+       ret = kdbus_msg_recv(conn, &msg, &offset);
+       ASSERT_RETURN(ret == -EAGAIN);
+
+       kdbus_conn_free(conn);
+
+       /* Make sure that monitor as unprivileged is not allowed */
+       ret = test_is_capable(CAP_SETUID, CAP_SETGID, -1);
+       ASSERT_RETURN(ret >= 0);
+
+       if (ret && all_uids_gids_are_mapped()) {
+               ret = RUN_UNPRIVILEGED(UNPRIV_UID, UNPRIV_UID, ({
+                       monitor = kdbus_hello(env->buspath,
+                                             KDBUS_HELLO_MONITOR,
+                                             NULL, 0);
+                       ASSERT_EXIT(!monitor && errno == EPERM);
+
+                       _exit(EXIT_SUCCESS);
+               }),
+               ({ 0; }));
+               ASSERT_RETURN(ret == 0);
+       }
+
+       return TEST_OK;
+}
diff --git a/tools/testing/selftests/kdbus/test-names.c b/tools/testing/selftests/kdbus/test-names.c
new file mode 100644 (file)
index 0000000..66ebb47
--- /dev/null
@@ -0,0 +1,194 @@
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <errno.h>
+#include <assert.h>
+#include <limits.h>
+#include <getopt.h>
+#include <stdbool.h>
+
+#include "kdbus-api.h"
+#include "kdbus-util.h"
+#include "kdbus-enum.h"
+#include "kdbus-test.h"
+
+static int conn_is_name_owner(const struct kdbus_conn *conn,
+                             const char *needle)
+{
+       struct kdbus_cmd_list cmd_list = { .size = sizeof(cmd_list) };
+       struct kdbus_info *name, *list;
+       bool found = false;
+       int ret;
+
+       cmd_list.flags = KDBUS_LIST_NAMES;
+
+       ret = kdbus_cmd_list(conn->fd, &cmd_list);
+       ASSERT_RETURN(ret == 0);
+
+       list = (struct kdbus_info *)(conn->buf + cmd_list.offset);
+       KDBUS_FOREACH(name, list, cmd_list.list_size) {
+               struct kdbus_item *item;
+               const char *n = NULL;
+
+               KDBUS_ITEM_FOREACH(item, name, items)
+                       if (item->type == KDBUS_ITEM_OWNED_NAME)
+                               n = item->name.name;
+
+               if (name->id == conn->id &&
+                   n && strcmp(needle, n) == 0) {
+                       found = true;
+                       break;
+               }
+       }
+
+       ret = kdbus_free(conn, cmd_list.offset);
+       ASSERT_RETURN(ret == 0);
+
+       return found ? 0 : -1;
+}
+
+int kdbus_test_name_basic(struct kdbus_test_env *env)
+{
+       struct kdbus_conn *conn;
+       char *name, *dot_name, *invalid_name, *wildcard_name;
+       int ret;
+
+       name = "foo.bla.blaz";
+       dot_name = ".bla.blaz";
+       invalid_name = "foo";
+       wildcard_name = "foo.bla.bl.*";
+
+       /* create a 2nd connection */
+       conn = kdbus_hello(env->buspath, 0, NULL, 0);
+       ASSERT_RETURN(conn != NULL);
+
+       /* acquire name "foo.bar.xxx" name */
+       ret = kdbus_name_acquire(conn, "foo.bar.xxx", NULL);
+       ASSERT_RETURN(ret == 0);
+
+       /* Name is not valid, must fail */
+       ret = kdbus_name_acquire(env->conn, dot_name, NULL);
+       ASSERT_RETURN(ret == -EINVAL);
+
+       ret = kdbus_name_acquire(env->conn, invalid_name, NULL);
+       ASSERT_RETURN(ret == -EINVAL);
+
+       ret = kdbus_name_acquire(env->conn, wildcard_name, NULL);
+       ASSERT_RETURN(ret == -EINVAL);
+
+       /* check that we can acquire a name */
+       ret = kdbus_name_acquire(env->conn, name, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       ret = conn_is_name_owner(env->conn, name);
+       ASSERT_RETURN(ret == 0);
+
+       /* ... and release it again */
+       ret = kdbus_name_release(env->conn, name);
+       ASSERT_RETURN(ret == 0);
+
+       ret = conn_is_name_owner(env->conn, name);
+       ASSERT_RETURN(ret != 0);
+
+       /* check that we can't release it again */
+       ret = kdbus_name_release(env->conn, name);
+       ASSERT_RETURN(ret == -ESRCH);
+
+       /* check that we can't release a name that we don't own */
+       ret = kdbus_name_release(env->conn, "foo.bar.xxx");
+       ASSERT_RETURN(ret == -EADDRINUSE);
+
+       /* Name is not valid, must fail */
+       ret = kdbus_name_release(env->conn, dot_name);
+       ASSERT_RETURN(ret == -ESRCH);
+
+       ret = kdbus_name_release(env->conn, invalid_name);
+       ASSERT_RETURN(ret == -ESRCH);
+
+       ret = kdbus_name_release(env->conn, wildcard_name);
+       ASSERT_RETURN(ret == -ESRCH);
+
+       kdbus_conn_free(conn);
+
+       return TEST_OK;
+}
+
+int kdbus_test_name_conflict(struct kdbus_test_env *env)
+{
+       struct kdbus_conn *conn;
+       char *name;
+       int ret;
+
+       name = "foo.bla.blaz";
+
+       /* create a 2nd connection */
+       conn = kdbus_hello(env->buspath, 0, NULL, 0);
+       ASSERT_RETURN(conn != NULL);
+
+       /* allow the new connection to own the same name */
+       /* acquire name from the 1st connection */
+       ret = kdbus_name_acquire(env->conn, name, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       ret = conn_is_name_owner(env->conn, name);
+       ASSERT_RETURN(ret == 0);
+
+       /* check that we can't acquire it again from the 1st connection */
+       ret = kdbus_name_acquire(env->conn, name, NULL);
+       ASSERT_RETURN(ret == -EALREADY);
+
+       /* check that we also can't acquire it again from the 2nd connection */
+       ret = kdbus_name_acquire(conn, name, NULL);
+       ASSERT_RETURN(ret == -EEXIST);
+
+       kdbus_conn_free(conn);
+
+       return TEST_OK;
+}
+
+int kdbus_test_name_queue(struct kdbus_test_env *env)
+{
+       struct kdbus_conn *conn;
+       const char *name;
+       uint64_t flags;
+       int ret;
+
+       name = "foo.bla.blaz";
+
+       flags = KDBUS_NAME_ALLOW_REPLACEMENT;
+
+       /* create a 2nd connection */
+       conn = kdbus_hello(env->buspath, 0, NULL, 0);
+       ASSERT_RETURN(conn != NULL);
+
+       /* allow the new connection to own the same name */
+       /* acquire name from the 1st connection */
+       ret = kdbus_name_acquire(env->conn, name, &flags);
+       ASSERT_RETURN(ret == 0);
+
+       ret = conn_is_name_owner(env->conn, name);
+       ASSERT_RETURN(ret == 0);
+
+       /* queue the 2nd connection as waiting owner */
+       flags = KDBUS_NAME_QUEUE;
+       ret = kdbus_name_acquire(conn, name, &flags);
+       ASSERT_RETURN(ret == 0);
+       ASSERT_RETURN(flags & KDBUS_NAME_IN_QUEUE);
+
+       /* release name from 1st connection */
+       ret = kdbus_name_release(env->conn, name);
+       ASSERT_RETURN(ret == 0);
+
+       /* now the name should be owned by the 2nd connection */
+       ret = conn_is_name_owner(conn, name);
+       ASSERT_RETURN(ret == 0);
+
+       kdbus_conn_free(conn);
+
+       return TEST_OK;
+}
diff --git a/tools/testing/selftests/kdbus/test-policy-ns.c b/tools/testing/selftests/kdbus/test-policy-ns.c
new file mode 100644 (file)
index 0000000..2d70ee4
--- /dev/null
@@ -0,0 +1,632 @@
+/*
+ * Test metadata and policies in new namespaces. Even if our tests
+ * can run in a namespaced setup, this test is necessary so we can
+ * inspect policies on the same kdbusfs but between multiple
+ * namespaces.
+ *
+ * Copyright (C) 2014-2015 Djalal Harouni
+ *
+ * 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 <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <sched.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <sys/prctl.h>
+#include <sys/eventfd.h>
+#include <sys/syscall.h>
+#include <sys/capability.h>
+#include <linux/sched.h>
+
+#include "kdbus-test.h"
+#include "kdbus-util.h"
+#include "kdbus-enum.h"
+
+#define MAX_CONN       64
+#define POLICY_NAME    "foo.test.policy-test"
+
+#define KDBUS_CONN_MAX_MSGS_PER_USER            16
+
+/**
+ * Note: this test can be used to inspect policy_db->talk_access_hash
+ *
+ * The purpose of these tests:
+ * 1) Check KDBUS_POLICY_TALK
+ * 2) Check the cache state: kdbus_policy_db->talk_access_hash
+ * Should be extended
+ */
+
+/**
+ * Check a list of connections against conn_db[0]
+ * conn_db[0] will own the name "foo.test.policy-test" and the
+ * policy holder connection for this name will update the policy
+ * entries, so different use cases can be tested.
+ */
+static struct kdbus_conn **conn_db;
+
+static void *kdbus_recv_echo(void *ptr)
+{
+       int ret;
+       struct kdbus_conn *conn = ptr;
+
+       ret = kdbus_msg_recv_poll(conn, 200, NULL, NULL);
+
+       return (void *)(long)ret;
+}
+
+/* Trigger kdbus_policy_set() */
+static int kdbus_set_policy_talk(struct kdbus_conn *conn,
+                                const char *name,
+                                uid_t id, unsigned int type)
+{
+       int ret;
+       struct kdbus_policy_access access = {
+               .type = type,
+               .id = id,
+               .access = KDBUS_POLICY_TALK,
+       };
+
+       ret = kdbus_conn_update_policy(conn, name, &access, 1);
+       ASSERT_RETURN(ret == 0);
+
+       return TEST_OK;
+}
+
+/* return TEST_OK or TEST_ERR on failure */
+static int kdbus_register_same_activator(char *bus, const char *name,
+                                        struct kdbus_conn **c)
+{
+       int ret;
+       struct kdbus_conn *activator;
+
+       activator = kdbus_hello_activator(bus, name, NULL, 0);
+       if (activator) {
+               *c = activator;
+               fprintf(stderr, "--- error was able to register name twice '%s'.\n",
+                       name);
+               return TEST_ERR;
+       }
+
+       ret = -errno;
+       /* -EEXIST means test succeeded */
+       if (ret == -EEXIST)
+               return TEST_OK;
+
+       return TEST_ERR;
+}
+
+/* return TEST_OK or TEST_ERR on failure */
+static int kdbus_register_policy_holder(char *bus, const char *name,
+                                       struct kdbus_conn **conn)
+{
+       struct kdbus_conn *c;
+       struct kdbus_policy_access access[2];
+
+       access[0].type = KDBUS_POLICY_ACCESS_USER;
+       access[0].access = KDBUS_POLICY_OWN;
+       access[0].id = geteuid();
+
+       access[1].type = KDBUS_POLICY_ACCESS_WORLD;
+       access[1].access = KDBUS_POLICY_TALK;
+       access[1].id = geteuid();
+
+       c = kdbus_hello_registrar(bus, name, access, 2,
+                                 KDBUS_HELLO_POLICY_HOLDER);
+       ASSERT_RETURN(c);
+
+       *conn = c;
+
+       return TEST_OK;
+}
+
+/**
+ * Create new threads for receiving from multiple senders,
+ * The 'conn_db' will be populated by newly created connections.
+ * Caller should free all allocated connections.
+ *
+ * return 0 on success, negative errno on failure.
+ */
+static int kdbus_recv_in_threads(const char *bus, const char *name,
+                                struct kdbus_conn **conn_db)
+{
+       int ret;
+       bool pool_full = false;
+       unsigned int sent_packets = 0;
+       unsigned int lost_packets = 0;
+       unsigned int i, tid;
+       unsigned long dst_id;
+       unsigned long cookie = 1;
+       unsigned int thread_nr = MAX_CONN - 1;
+       pthread_t thread_id[MAX_CONN - 1] = {'\0'};
+
+       dst_id = name ? KDBUS_DST_ID_NAME : conn_db[0]->id;
+
+       for (tid = 0, i = 1; tid < thread_nr; tid++, i++) {
+               ret = pthread_create(&thread_id[tid], NULL,
+                                    kdbus_recv_echo, (void *)conn_db[0]);
+               if (ret < 0) {
+                       ret = -errno;
+                       kdbus_printf("error pthread_create: %d (%m)\n",
+                                     ret);
+                       break;
+               }
+
+               /* just free before re-using */
+               kdbus_conn_free(conn_db[i]);
+               conn_db[i] = NULL;
+
+               /* We need to create connections here */
+               conn_db[i] = kdbus_hello(bus, 0, NULL, 0);
+               if (!conn_db[i]) {
+                       ret = -errno;
+                       break;
+               }
+
+               ret = kdbus_add_match_empty(conn_db[i]);
+               if (ret < 0)
+                       break;
+
+               ret = kdbus_msg_send(conn_db[i], name, cookie++,
+                                    0, 0, 0, dst_id, 0, NULL);
+               if (ret < 0) {
+                       /*
+                        * Receivers are not reading their messages,
+                        * not scheduled ?!
+                        *
+                        * So set the pool full here, perhaps the
+                        * connection pool or queue was full, later
+                        * recheck receivers errors
+                        */
+                       if (ret == -ENOBUFS || ret == -EXFULL)
+                               pool_full = true;
+                       break;
+               }
+
+               sent_packets++;
+       }
+
+       for (tid = 0; tid < thread_nr; tid++) {
+               int thread_ret = 0;
+
+               if (thread_id[tid]) {
+                       pthread_join(thread_id[tid], (void *)&thread_ret);
+                       if (thread_ret < 0) {
+                               /* Update only if send did not fail */
+                               if (ret == 0)
+                                       ret = thread_ret;
+
+                               lost_packets++;
+                       }
+               }
+       }
+
+       /*
+        * When sending if we did fail with -ENOBUFS or -EXFULL
+        * then we should have set lost_packet and we should at
+        * least have sent_packets set to KDBUS_CONN_MAX_MSGS_PER_USER
+        */
+       if (pool_full) {
+               ASSERT_RETURN(lost_packets > 0);
+
+               /*
+                * We should at least send KDBUS_CONN_MAX_MSGS_PER_USER
+                *
+                * For every send operation we create a thread to
+                * recv the packet, so we keep the queue clean
+                */
+               ASSERT_RETURN(sent_packets >= KDBUS_CONN_MAX_MSGS_PER_USER);
+
+               /*
+                * Set ret to zero since we only failed due to
+                * the receiving threads that have not been
+                * scheduled
+                */
+               ret = 0;
+       }
+
+       return ret;
+}
+
+/* Return: TEST_OK or TEST_ERR on failure */
+static int kdbus_normal_test(const char *bus, const char *name,
+                            struct kdbus_conn **conn_db)
+{
+       int ret;
+
+       ret = kdbus_recv_in_threads(bus, name, conn_db);
+       ASSERT_RETURN(ret >= 0);
+
+       return TEST_OK;
+}
+
+static int kdbus_fork_test_by_id(const char *bus,
+                                struct kdbus_conn **conn_db,
+                                int parent_status, int child_status)
+{
+       int ret;
+       pid_t pid;
+       uint64_t cookie = 0x9876ecba;
+       struct kdbus_msg *msg = NULL;
+       uint64_t offset = 0;
+       int status = 0;
+
+       /*
+        * If the child_status is not EXIT_SUCCESS, then we expect
+        * that sending from the child will fail, thus receiving
+        * from parent must error with -ETIMEDOUT, and vice versa.
+        */
+       bool parent_timedout = !!child_status;
+       bool child_timedout = !!parent_status;
+
+       pid = fork();
+       ASSERT_RETURN_VAL(pid >= 0, pid);
+
+       if (pid == 0) {
+               struct kdbus_conn *conn_src;
+
+               ret = prctl(PR_SET_PDEATHSIG, SIGKILL);
+               ASSERT_EXIT(ret == 0);
+
+               ret = drop_privileges(65534, 65534);
+               ASSERT_EXIT(ret == 0);
+
+               conn_src = kdbus_hello(bus, 0, NULL, 0);
+               ASSERT_EXIT(conn_src);
+
+               ret = kdbus_add_match_empty(conn_src);
+               ASSERT_EXIT(ret == 0);
+
+               /*
+                * child_status is always checked against send
+                * operations, in case it fails always return
+                * EXIT_FAILURE.
+                */
+               ret = kdbus_msg_send(conn_src, NULL, cookie,
+                                    0, 0, 0, conn_db[0]->id, 0, NULL);
+               ASSERT_EXIT(ret == child_status);
+
+               ret = kdbus_msg_recv_poll(conn_src, 100, NULL, NULL);
+
+               kdbus_conn_free(conn_src);
+
+               /*
+                * Child kdbus_msg_recv_poll() should timeout since
+                * the parent_status was set to a non EXIT_SUCCESS
+                * value.
+                */
+               if (child_timedout)
+                       _exit(ret == -ETIMEDOUT ? EXIT_SUCCESS : EXIT_FAILURE);
+
+               _exit(ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
+       }
+
+       ret = kdbus_msg_recv_poll(conn_db[0], 100, &msg, &offset);
+       /*
+        * If parent_timedout is set then this should fail with
+        * -ETIMEDOUT since the child_status was set to a non
+        * EXIT_SUCCESS value. Otherwise, assume
+        * that kdbus_msg_recv_poll() has succeeded.
+        */
+       if (parent_timedout) {
+               ASSERT_RETURN_VAL(ret == -ETIMEDOUT, TEST_ERR);
+
+               /* timedout no need to continue, we don't have the
+                * child connection ID, so just terminate. */
+               goto out;
+       } else {
+               ASSERT_RETURN_VAL(ret == 0, ret);
+       }
+
+       ret = kdbus_msg_send(conn_db[0], NULL, ++cookie,
+                            0, 0, 0, msg->src_id, 0, NULL);
+       /*
+        * parent_status is checked against send operations,
+        * on failures always return TEST_ERR.
+        */
+       ASSERT_RETURN_VAL(ret == parent_status, TEST_ERR);
+
+       kdbus_msg_free(msg);
+       kdbus_free(conn_db[0], offset);
+
+out:
+       ret = waitpid(pid, &status, 0);
+       ASSERT_RETURN_VAL(ret >= 0, ret);
+
+       return (status == EXIT_SUCCESS) ? TEST_OK : TEST_ERR;
+}
+
+/*
+ * Return: TEST_OK, TEST_ERR or TEST_SKIP
+ * we return TEST_OK only if the children return with the expected
+ * 'expected_status' that is specified as an argument.
+ */
+static int kdbus_fork_test(const char *bus, const char *name,
+                          struct kdbus_conn **conn_db, int expected_status)
+{
+       pid_t pid;
+       int ret = 0;
+       int status = 0;
+
+       pid = fork();
+       ASSERT_RETURN_VAL(pid >= 0, pid);
+
+       if (pid == 0) {
+               ret = prctl(PR_SET_PDEATHSIG, SIGKILL);
+               ASSERT_EXIT(ret == 0);
+
+               ret = drop_privileges(65534, 65534);
+               ASSERT_EXIT(ret == 0);
+
+               ret = kdbus_recv_in_threads(bus, name, conn_db);
+               _exit(ret == expected_status ? EXIT_SUCCESS : EXIT_FAILURE);
+       }
+
+       ret = waitpid(pid, &status, 0);
+       ASSERT_RETURN(ret >= 0);
+
+       return (status == EXIT_SUCCESS) ? TEST_OK : TEST_ERR;
+}
+
+/* Return EXIT_SUCCESS, EXIT_FAILURE or negative errno */
+static int __kdbus_clone_userns_test(const char *bus,
+                                    const char *name,
+                                    struct kdbus_conn **conn_db,
+                                    int expected_status)
+{
+       int efd;
+       pid_t pid;
+       int ret = 0;
+       unsigned int uid = 65534;
+       int status;
+
+       ret = drop_privileges(uid, uid);
+       ASSERT_RETURN_VAL(ret == 0, ret);
+
+       /*
+        * Since we just dropped privileges, the dumpable flag was just
+        * cleared which makes the /proc/$clone_child/uid_map to be
+        * owned by root, hence any userns uid mapping will fail with
+        * -EPERM since the mapping will be done by uid 65534.
+        *
+        * To avoid this set the dumpable flag again which makes procfs
+        * update the /proc/$clone_child/ inodes owner to 65534.
+        *
+        * Using this we will be able write to /proc/$clone_child/uid_map
+        * as uid 65534 and map the uid 65534 to 0 inside the user
+        * namespace.
+        */
+       ret = prctl(PR_SET_DUMPABLE, SUID_DUMP_USER);
+       ASSERT_RETURN_VAL(ret == 0, ret);
+
+       /* sync parent/child */
+       efd = eventfd(0, EFD_CLOEXEC);
+       ASSERT_RETURN_VAL(efd >= 0, efd);
+
+       pid = syscall(__NR_clone, SIGCHLD|CLONE_NEWUSER, NULL);
+       if (pid < 0) {
+               ret = -errno;
+               kdbus_printf("error clone: %d (%m)\n", ret);
+               /*
+                * Normal user not allowed to create userns,
+                * so nothing to worry about ?
+                */
+               if (ret == -EPERM) {
+                       kdbus_printf("-- CLONE_NEWUSER TEST Failed for uid: %u\n"
+                               "-- Make sure that your kernel do not allow "
+                               "CLONE_NEWUSER for unprivileged users\n"
+                               "-- Upstream Commit: "
+                               "https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=5eaf563e\n",
+                               uid);
+                       ret = 0;
+               }
+
+               return ret;
+       }
+
+       if (pid == 0) {
+               struct kdbus_conn *conn_src;
+               eventfd_t event_status = 0;
+
+               ret = prctl(PR_SET_PDEATHSIG, SIGKILL);
+               ASSERT_EXIT(ret == 0);
+
+               ret = eventfd_read(efd, &event_status);
+               ASSERT_EXIT(ret >= 0 && event_status == 1);
+
+               /* ping connection from the new user namespace */
+               conn_src = kdbus_hello(bus, 0, NULL, 0);
+               ASSERT_EXIT(conn_src);
+
+               ret = kdbus_add_match_empty(conn_src);
+               ASSERT_EXIT(ret == 0);
+
+               ret = kdbus_msg_send(conn_src, name, 0xabcd1234,
+                                    0, 0, 0, KDBUS_DST_ID_NAME, 0, NULL);
+               kdbus_conn_free(conn_src);
+
+               _exit(ret == expected_status ? EXIT_SUCCESS : EXIT_FAILURE);
+       }
+
+       ret = userns_map_uid_gid(pid, "0 65534 1", "0 65534 1");
+       ASSERT_RETURN_VAL(ret == 0, ret);
+
+       /* Tell child we are ready */
+       ret = eventfd_write(efd, 1);
+       ASSERT_RETURN_VAL(ret == 0, ret);
+
+       ret = waitpid(pid, &status, 0);
+       ASSERT_RETURN_VAL(ret >= 0, ret);
+
+       close(efd);
+
+       return status == EXIT_SUCCESS ? TEST_OK : TEST_ERR;
+}
+
+static int kdbus_clone_userns_test(const char *bus,
+                                  const char *name,
+                                  struct kdbus_conn **conn_db,
+                                  int expected_status)
+{
+       pid_t pid;
+       int ret = 0;
+       int status;
+
+       pid = fork();
+       ASSERT_RETURN_VAL(pid >= 0, -errno);
+
+       if (pid == 0) {
+               ret = prctl(PR_SET_PDEATHSIG, SIGKILL);
+               if (ret < 0)
+                       _exit(EXIT_FAILURE);
+
+               ret = __kdbus_clone_userns_test(bus, name, conn_db,
+                                               expected_status);
+               _exit(ret);
+       }
+
+       /*
+        * Receive in the original (root privileged) user namespace,
+        * must fail with -ETIMEDOUT.
+        */
+       ret = kdbus_msg_recv_poll(conn_db[0], 100, NULL, NULL);
+       ASSERT_RETURN_VAL(ret == -ETIMEDOUT, ret);
+
+       ret = waitpid(pid, &status, 0);
+       ASSERT_RETURN_VAL(ret >= 0, ret);
+
+       return (status == EXIT_SUCCESS) ? TEST_OK : TEST_ERR;
+}
+
+int kdbus_test_policy_ns(struct kdbus_test_env *env)
+{
+       int i;
+       int ret;
+       struct kdbus_conn *activator = NULL;
+       struct kdbus_conn *policy_holder = NULL;
+       char *bus = env->buspath;
+
+       ret = test_is_capable(CAP_SETUID, CAP_SETGID, -1);
+       ASSERT_RETURN(ret >= 0);
+
+       /* no enough privileges, SKIP test */
+       if (!ret)
+               return TEST_SKIP;
+
+       /* we require user-namespaces */
+       if (access("/proc/self/uid_map", F_OK) != 0)
+               return TEST_SKIP;
+
+       /* uids/gids must be mapped */
+       if (!all_uids_gids_are_mapped())
+               return TEST_SKIP;
+
+       conn_db = calloc(MAX_CONN, sizeof(struct kdbus_conn *));
+       ASSERT_RETURN(conn_db);
+
+       memset(conn_db, 0, MAX_CONN * sizeof(struct kdbus_conn *));
+
+       conn_db[0] = kdbus_hello(bus, 0, NULL, 0);
+       ASSERT_RETURN(conn_db[0]);
+
+       ret = kdbus_add_match_empty(conn_db[0]);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_fork_test_by_id(bus, conn_db, -EPERM, -EPERM);
+       ASSERT_EXIT(ret == 0);
+
+       ret = kdbus_register_policy_holder(bus, POLICY_NAME,
+                                          &policy_holder);
+       ASSERT_RETURN(ret == 0);
+
+       /* Try to register the same name with an activator */
+       ret = kdbus_register_same_activator(bus, POLICY_NAME,
+                                           &activator);
+       ASSERT_RETURN(ret == 0);
+
+       /* Acquire POLICY_NAME */
+       ret = kdbus_name_acquire(conn_db[0], POLICY_NAME, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_normal_test(bus, POLICY_NAME, conn_db);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_list(conn_db[0], KDBUS_LIST_NAMES |
+                                    KDBUS_LIST_UNIQUE |
+                                    KDBUS_LIST_ACTIVATORS |
+                                    KDBUS_LIST_QUEUED);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_fork_test(bus, POLICY_NAME, conn_db, EXIT_SUCCESS);
+       ASSERT_RETURN(ret == 0);
+
+       /*
+        * children connections are able to talk to conn_db[0] since
+        * current POLICY_NAME TALK type is KDBUS_POLICY_ACCESS_WORLD,
+        * so expect EXIT_SUCCESS when sending from child. However,
+        * since the child's connection does not own any well-known
+        * name, The parent connection conn_db[0] should fail with
+        * -EPERM but since it is a privileged bus user the TALK is
+        *  allowed.
+        */
+       ret = kdbus_fork_test_by_id(bus, conn_db,
+                                   EXIT_SUCCESS, EXIT_SUCCESS);
+       ASSERT_EXIT(ret == 0);
+
+       /*
+        * Connections that can talk are perhaps being destroyed now.
+        * Restrict the policy and purge cache entries where the
+        * conn_db[0] is the destination.
+        *
+        * Now only connections with uid == 0 are allowed to talk.
+        */
+       ret = kdbus_set_policy_talk(policy_holder, POLICY_NAME,
+                                   geteuid(), KDBUS_POLICY_ACCESS_USER);
+       ASSERT_RETURN(ret == 0);
+
+       /*
+        * Testing connections (FORK+DROP) again:
+        * After setting the policy re-check connections
+        * we expect the children to fail with -EPERM
+        */
+       ret = kdbus_fork_test(bus, POLICY_NAME, conn_db, -EPERM);
+       ASSERT_RETURN(ret == 0);
+
+       /*
+        * Now expect that both parent and child to fail.
+        *
+        * Child should fail with -EPERM since we just restricted
+        * the POLICY_NAME TALK to uid 0 and its uid is 65534.
+        *
+        * Since the parent's connection will timeout when receiving
+        * from the child, we never continue. FWIW just put -EPERM.
+        */
+       ret = kdbus_fork_test_by_id(bus, conn_db, -EPERM, -EPERM);
+       ASSERT_EXIT(ret == 0);
+
+       /* Check if the name can be reached in a new userns */
+       ret = kdbus_clone_userns_test(bus, POLICY_NAME, conn_db, -EPERM);
+       ASSERT_RETURN(ret == 0);
+
+       for (i = 0; i < MAX_CONN; i++)
+               kdbus_conn_free(conn_db[i]);
+
+       kdbus_conn_free(activator);
+       kdbus_conn_free(policy_holder);
+
+       free(conn_db);
+
+       return ret;
+}
diff --git a/tools/testing/selftests/kdbus/test-policy-priv.c b/tools/testing/selftests/kdbus/test-policy-priv.c
new file mode 100644 (file)
index 0000000..14a09c6
--- /dev/null
@@ -0,0 +1,1275 @@
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/capability.h>
+#include <sys/eventfd.h>
+#include <sys/wait.h>
+
+#include "kdbus-test.h"
+#include "kdbus-util.h"
+#include "kdbus-enum.h"
+
+static int test_policy_priv_by_id(const char *bus,
+                                 struct kdbus_conn *conn_dst,
+                                 bool drop_second_user,
+                                 int parent_status,
+                                 int child_status)
+{
+       int ret = 0;
+       uint64_t expected_cookie = time(NULL) ^ 0xdeadbeef;
+
+       ASSERT_RETURN(conn_dst);
+
+       ret = RUN_UNPRIVILEGED_CONN(unpriv, bus, ({
+               ret = kdbus_msg_send(unpriv, NULL,
+                                    expected_cookie, 0, 0, 0,
+                                    conn_dst->id, 0, NULL);
+               ASSERT_EXIT(ret == child_status);
+       }));
+       ASSERT_RETURN(ret >= 0);
+
+       ret = kdbus_msg_recv_poll(conn_dst, 300, NULL, NULL);
+       ASSERT_RETURN(ret == parent_status);
+
+       return 0;
+}
+
+static int test_policy_priv_by_broadcast(const char *bus,
+                                        struct kdbus_conn *conn_dst,
+                                        int drop_second_user,
+                                        int parent_status,
+                                        int child_status)
+{
+       int efd;
+       int ret = 0;
+       eventfd_t event_status = 0;
+       struct kdbus_msg *msg = NULL;
+       uid_t second_uid = UNPRIV_UID;
+       gid_t second_gid = UNPRIV_GID;
+       struct kdbus_conn *child_2 = conn_dst;
+       uint64_t expected_cookie = time(NULL) ^ 0xdeadbeef;
+
+       /* Drop to another unprivileged user other than UNPRIV_UID */
+       if (drop_second_user == DROP_OTHER_UNPRIV) {
+               second_uid = UNPRIV_UID - 1;
+               second_gid = UNPRIV_GID - 1;
+       }
+
+       /* child will signal parent to send broadcast */
+       efd = eventfd(0, EFD_CLOEXEC);
+       ASSERT_RETURN_VAL(efd >= 0, efd);
+
+       ret = RUN_UNPRIVILEGED(UNPRIV_UID, UNPRIV_GID, ({
+               struct kdbus_conn *child;
+
+               child = kdbus_hello(bus, 0, NULL, 0);
+               ASSERT_EXIT(child);
+
+               ret = kdbus_add_match_empty(child);
+               ASSERT_EXIT(ret == 0);
+
+               /* signal parent */
+               ret = eventfd_write(efd, 1);
+               ASSERT_EXIT(ret == 0);
+
+               /* Use a little bit high time */
+               ret = kdbus_msg_recv_poll(child, 500, &msg, NULL);
+               ASSERT_EXIT(ret == child_status);
+
+               /*
+                * If we expect the child to get the broadcast
+                * message, then check the received cookie.
+                */
+               if (ret == 0) {
+                       ASSERT_EXIT(expected_cookie == msg->cookie);
+               }
+
+               /* Use expected_cookie since 'msg' might be NULL */
+               ret = kdbus_msg_send(child, NULL, expected_cookie + 1,
+                                    0, 0, 0, KDBUS_DST_ID_BROADCAST,
+                                    0, NULL);
+               ASSERT_EXIT(ret == 0);
+
+               kdbus_msg_free(msg);
+               kdbus_conn_free(child);
+       }),
+       ({
+               if (drop_second_user == DO_NOT_DROP) {
+                       ASSERT_RETURN(child_2);
+
+                       ret = eventfd_read(efd, &event_status);
+                       ASSERT_RETURN(ret >= 0 && event_status == 1);
+
+                       ret = kdbus_msg_send(child_2, NULL,
+                                            expected_cookie, 0, 0, 0,
+                                            KDBUS_DST_ID_BROADCAST,
+                                            0, NULL);
+                       ASSERT_RETURN(ret == 0);
+
+                       /* Use a little bit high time */
+                       ret = kdbus_msg_recv_poll(child_2, 1000,
+                                                 &msg, NULL);
+                       ASSERT_RETURN(ret == parent_status);
+
+                       /*
+                        * Check returned cookie in case we expect
+                        * success.
+                        */
+                       if (ret == 0) {
+                               ASSERT_RETURN(msg->cookie ==
+                                             expected_cookie + 1);
+                       }
+
+                       kdbus_msg_free(msg);
+               } else {
+                       /*
+                        * Two unprivileged users will try to
+                        * communicate using broadcast.
+                        */
+                       ret = RUN_UNPRIVILEGED(second_uid, second_gid, ({
+                               child_2 = kdbus_hello(bus, 0, NULL, 0);
+                               ASSERT_EXIT(child_2);
+
+                               ret = kdbus_add_match_empty(child_2);
+                               ASSERT_EXIT(ret == 0);
+
+                               ret = eventfd_read(efd, &event_status);
+                               ASSERT_EXIT(ret >= 0 && event_status == 1);
+
+                               ret = kdbus_msg_send(child_2, NULL,
+                                               expected_cookie, 0, 0, 0,
+                                               KDBUS_DST_ID_BROADCAST,
+                                               0, NULL);
+                               ASSERT_EXIT(ret == 0);
+
+                               /* Use a little bit high time */
+                               ret = kdbus_msg_recv_poll(child_2, 1000,
+                                                         &msg, NULL);
+                               ASSERT_EXIT(ret == parent_status);
+
+                               /*
+                                * Check returned cookie in case we expect
+                                * success.
+                                */
+                               if (ret == 0) {
+                                       ASSERT_EXIT(msg->cookie ==
+                                                   expected_cookie + 1);
+                               }
+
+                               kdbus_msg_free(msg);
+                               kdbus_conn_free(child_2);
+                       }),
+                       ({ 0; }));
+                       ASSERT_RETURN(ret == 0);
+               }
+       }));
+       ASSERT_RETURN(ret == 0);
+
+       close(efd);
+
+       return ret;
+}
+
+static void nosig(int sig)
+{
+}
+
+static int test_priv_before_policy_upload(struct kdbus_test_env *env)
+{
+       int ret = 0;
+       struct kdbus_conn *conn;
+
+       conn = kdbus_hello(env->buspath, 0, NULL, 0);
+       ASSERT_RETURN(conn);
+
+       /*
+        * Make sure unprivileged bus user cannot acquire names
+        * before registring any policy holder.
+        */
+
+       ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({
+               ret = kdbus_name_acquire(unpriv, "com.example.a", NULL);
+               ASSERT_EXIT(ret < 0);
+       }));
+       ASSERT_RETURN(ret == 0);
+
+       /*
+        * Make sure unprivileged bus users cannot talk by default
+        * to privileged ones, unless a policy holder that allows
+        * this was uploaded.
+        */
+
+       ret = test_policy_priv_by_id(env->buspath, conn, false,
+                                    -ETIMEDOUT, -EPERM);
+       ASSERT_RETURN(ret == 0);
+
+       /* Activate matching for a privileged connection */
+       ret = kdbus_add_match_empty(conn);
+       ASSERT_RETURN(ret == 0);
+
+       /*
+        * First make sure that BROADCAST with msg flag
+        * KDBUS_MSG_EXPECT_REPLY will fail with -ENOTUNIQ
+        */
+       ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({
+               ret = kdbus_msg_send(unpriv, NULL, 0xdeadbeef,
+                                    KDBUS_MSG_EXPECT_REPLY,
+                                    5000000000ULL, 0,
+                                    KDBUS_DST_ID_BROADCAST,
+                                    0, NULL);
+               ASSERT_EXIT(ret == -ENOTUNIQ);
+       }));
+       ASSERT_RETURN(ret == 0);
+
+       /*
+        * Test broadcast with a privileged connection.
+        *
+        * The first unprivileged receiver should not get the
+        * broadcast message sent by the privileged connection,
+        * since there is no a TALK policy that allows the
+        * unprivileged to TALK to the privileged connection. It
+        * will fail with -ETIMEDOUT
+        *
+        * Then second case:
+        * The privileged connection should get the broadcast
+        * message from the unprivileged one. Since the receiver is
+        * a privileged bus user and it has default TALK access to
+        * all connections it will receive those.
+        */
+
+       ret = test_policy_priv_by_broadcast(env->buspath, conn,
+                                           DO_NOT_DROP,
+                                           0, -ETIMEDOUT);
+       ASSERT_RETURN(ret == 0);
+
+
+       /*
+        * Test broadcast with two unprivileged connections running
+        * under the same user.
+        *
+        * Both connections should succeed.
+        */
+
+       ret = test_policy_priv_by_broadcast(env->buspath, NULL,
+                                           DROP_SAME_UNPRIV, 0, 0);
+       ASSERT_RETURN(ret == 0);
+
+       /*
+        * Test broadcast with two unprivileged connections running
+        * under different users.
+        *
+        * Both connections will fail with -ETIMEDOUT.
+        */
+
+       ret = test_policy_priv_by_broadcast(env->buspath, NULL,
+                                           DROP_OTHER_UNPRIV,
+                                           -ETIMEDOUT, -ETIMEDOUT);
+       ASSERT_RETURN(ret == 0);
+
+       kdbus_conn_free(conn);
+
+       return ret;
+}
+
+static int test_broadcast_after_policy_upload(struct kdbus_test_env *env)
+{
+       int ret;
+       int efd;
+       eventfd_t event_status = 0;
+       struct kdbus_msg *msg = NULL;
+       struct kdbus_conn *owner_a, *owner_b;
+       struct kdbus_conn *holder_a, *holder_b;
+       struct kdbus_policy_access access = {};
+       uint64_t expected_cookie = time(NULL) ^ 0xdeadbeef;
+
+       owner_a = kdbus_hello(env->buspath, 0, NULL, 0);
+       ASSERT_RETURN(owner_a);
+
+       ret = kdbus_name_acquire(owner_a, "com.example.broadcastA", NULL);
+       ASSERT_EXIT(ret >= 0);
+
+       /*
+        * Make sure unprivileged bus users cannot talk by default
+        * to privileged ones, unless a policy holder that allows
+        * this was uploaded.
+        */
+
+       ++expected_cookie;
+       ret = test_policy_priv_by_id(env->buspath, owner_a, false,
+                                    -ETIMEDOUT, -EPERM);
+       ASSERT_RETURN(ret == 0);
+
+       /*
+        * Make sure that privileged won't receive broadcasts unless
+        * it installs a match. It will fail with -ETIMEDOUT
+        *
+        * At same time check that the unprivileged connection will
+        * not receive the broadcast message from the privileged one
+        * since the privileged one owns a name with a restricted
+        * policy TALK (actually the TALK policy is still not
+        * registered so we fail by default), thus the unprivileged
+        * receiver is not able to TALK to that name.
+        */
+
+       ret = test_policy_priv_by_broadcast(env->buspath, owner_a,
+                                           DO_NOT_DROP,
+                                           -ETIMEDOUT, -ETIMEDOUT);
+       ASSERT_RETURN(ret == 0);
+
+       /* Activate matching for a privileged connection */
+       ret = kdbus_add_match_empty(owner_a);
+       ASSERT_RETURN(ret == 0);
+
+       /*
+        * Redo the previous test. The privileged conn owner_a is
+        * able to TALK to any connection so it will receive the
+        * broadcast message now.
+        */
+
+       ret = test_policy_priv_by_broadcast(env->buspath, owner_a,
+                                           DO_NOT_DROP,
+                                           0, -ETIMEDOUT);
+       ASSERT_RETURN(ret == 0);
+
+       /*
+        * Test that broadcast between two unprivileged users running
+        * under the same user still succeed.
+        */
+
+       ret = test_policy_priv_by_broadcast(env->buspath, NULL,
+                                           DROP_SAME_UNPRIV, 0, 0);
+       ASSERT_RETURN(ret == 0);
+
+       /*
+        * Test broadcast with two unprivileged connections running
+        * under different users.
+        *
+        * Both connections will fail with -ETIMEDOUT.
+        */
+
+       ret = test_policy_priv_by_broadcast(env->buspath, NULL,
+                                           DROP_OTHER_UNPRIV,
+                                           -ETIMEDOUT, -ETIMEDOUT);
+       ASSERT_RETURN(ret == 0);
+
+       access = (struct kdbus_policy_access){
+               .type = KDBUS_POLICY_ACCESS_USER,
+               .id = geteuid(),
+               .access = KDBUS_POLICY_OWN,
+       };
+
+       holder_a = kdbus_hello_registrar(env->buspath,
+                                        "com.example.broadcastA",
+                                        &access, 1,
+                                        KDBUS_HELLO_POLICY_HOLDER);
+       ASSERT_RETURN(holder_a);
+
+       holder_b = kdbus_hello_registrar(env->buspath,
+                                        "com.example.broadcastB",
+                                        &access, 1,
+                                        KDBUS_HELLO_POLICY_HOLDER);
+       ASSERT_RETURN(holder_b);
+
+       /* Free connections and their received messages and restart */
+       kdbus_conn_free(owner_a);
+
+       owner_a = kdbus_hello(env->buspath, 0, NULL, 0);
+       ASSERT_RETURN(owner_a);
+
+       /* Activate matching for a privileged connection */
+       ret = kdbus_add_match_empty(owner_a);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_name_acquire(owner_a, "com.example.broadcastA", NULL);
+       ASSERT_EXIT(ret >= 0);
+
+       owner_b = kdbus_hello(env->buspath, 0, NULL, 0);
+       ASSERT_RETURN(owner_b);
+
+       ret = kdbus_name_acquire(owner_b, "com.example.broadcastB", NULL);
+       ASSERT_EXIT(ret >= 0);
+
+       /* Activate matching for a privileged connection */
+       ret = kdbus_add_match_empty(owner_b);
+       ASSERT_RETURN(ret == 0);
+
+       /*
+        * Test that even if "com.example.broadcastA" and
+        * "com.example.broadcastB" do have a TALK access by default
+        * they are able to signal each other using broadcast due to
+        * the fact they are privileged connections, they receive
+        * all broadcasts if the match allows it.
+        */
+
+       ++expected_cookie;
+       ret = kdbus_msg_send(owner_a, NULL, expected_cookie, 0,
+                            0, 0, KDBUS_DST_ID_BROADCAST, 0, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_msg_recv_poll(owner_b, 100, &msg, NULL);
+       ASSERT_RETURN(ret == 0);
+       ASSERT_RETURN(msg->cookie == expected_cookie);
+
+       /* Check src ID */
+       ASSERT_RETURN(msg->src_id == owner_a->id);
+
+       kdbus_msg_free(msg);
+
+       /* Release name "com.example.broadcastB" */
+
+       ret = kdbus_name_release(owner_b, "com.example.broadcastB");
+       ASSERT_EXIT(ret >= 0);
+
+       /* KDBUS_POLICY_OWN for unprivileged connections */
+       access = (struct kdbus_policy_access){
+               .type = KDBUS_POLICY_ACCESS_WORLD,
+               .id = geteuid(),
+               .access = KDBUS_POLICY_OWN,
+       };
+
+       /* Update the policy so unprivileged will own the name */
+
+       ret = kdbus_conn_update_policy(holder_b,
+                                      "com.example.broadcastB",
+                                      &access, 1);
+       ASSERT_RETURN(ret == 0);
+
+       /*
+        * Send broadcasts from an unprivileged connection that
+        * owns a name "com.example.broadcastB".
+        *
+        * We'll have four destinations here:
+        *
+        * 1) destination owner_a: privileged connection that owns
+        * "com.example.broadcastA". It will receive the broadcast
+        * since it is a privileged has default TALK access to all
+        * connections, and it is subscribed to the match.
+        * Will succeed.
+        *
+        * owner_b: privileged connection (running under a different
+        * uid) that do not own names, but with an empty broadcast
+        * match, so it will receive broadcasts since it has default
+        * TALK access to all connection.
+        *
+        * unpriv_a: unpriv connection that do not own any name.
+        * It will receive the broadcast since it is running under
+        * the same user of the one broadcasting and did install
+        * matches. It should get the message.
+        *
+        * unpriv_b: unpriv connection is not interested in broadcast
+        * messages, so it did not install broadcast matches. Should
+        * fail with -ETIMEDOUT
+        */
+
+       ++expected_cookie;
+       efd = eventfd(0, EFD_CLOEXEC);
+       ASSERT_RETURN_VAL(efd >= 0, efd);
+
+       ret = RUN_UNPRIVILEGED(UNPRIV_UID, UNPRIV_UID, ({
+               struct kdbus_conn *unpriv_owner;
+               struct kdbus_conn *unpriv_a, *unpriv_b;
+
+               unpriv_owner = kdbus_hello(env->buspath, 0, NULL, 0);
+               ASSERT_EXIT(unpriv_owner);
+
+               unpriv_a = kdbus_hello(env->buspath, 0, NULL, 0);
+               ASSERT_EXIT(unpriv_a);
+
+               unpriv_b = kdbus_hello(env->buspath, 0, NULL, 0);
+               ASSERT_EXIT(unpriv_b);
+
+               ret = kdbus_name_acquire(unpriv_owner,
+                                        "com.example.broadcastB",
+                                        NULL);
+               ASSERT_EXIT(ret >= 0);
+
+               ret = kdbus_add_match_empty(unpriv_a);
+               ASSERT_EXIT(ret == 0);
+
+               /* Signal that we are doing broadcasts */
+               ret = eventfd_write(efd, 1);
+               ASSERT_EXIT(ret == 0);
+
+               /*
+                * Do broadcast from a connection that owns the
+                * names "com.example.broadcastB".
+                */
+               ret = kdbus_msg_send(unpriv_owner, NULL,
+                                    expected_cookie,
+                                    0, 0, 0,
+                                    KDBUS_DST_ID_BROADCAST,
+                                    0, NULL);
+               ASSERT_EXIT(ret == 0);
+
+               /*
+                * Unprivileged connection running under the same
+                * user. It should succeed.
+                */
+               ret = kdbus_msg_recv_poll(unpriv_a, 300, &msg, NULL);
+               ASSERT_EXIT(ret == 0 && msg->cookie == expected_cookie);
+
+               /*
+                * Did not install matches, not interested in
+                * broadcasts
+                */
+               ret = kdbus_msg_recv_poll(unpriv_b, 300, NULL, NULL);
+               ASSERT_EXIT(ret == -ETIMEDOUT);
+       }),
+       ({
+               ret = eventfd_read(efd, &event_status);
+               ASSERT_RETURN(ret >= 0 && event_status == 1);
+
+               /*
+                * owner_a must fail with -ETIMEDOUT, since it owns
+                * name "com.example.broadcastA" and its TALK
+                * access is restriced.
+                */
+               ret = kdbus_msg_recv_poll(owner_a, 300, &msg, NULL);
+               ASSERT_RETURN(ret == 0);
+
+               /* confirm the received cookie */
+               ASSERT_RETURN(msg->cookie == expected_cookie);
+
+               kdbus_msg_free(msg);
+
+               /*
+                * owner_b got the broadcast from an unprivileged
+                * connection.
+                */
+               ret = kdbus_msg_recv_poll(owner_b, 300, &msg, NULL);
+               ASSERT_RETURN(ret == 0);
+
+               /* confirm the received cookie */
+               ASSERT_RETURN(msg->cookie == expected_cookie);
+
+               kdbus_msg_free(msg);
+
+       }));
+       ASSERT_RETURN(ret == 0);
+
+       close(efd);
+
+       /*
+        * Test broadcast with two unprivileged connections running
+        * under different users.
+        *
+        * Both connections will fail with -ETIMEDOUT.
+        */
+
+       ret = test_policy_priv_by_broadcast(env->buspath, NULL,
+                                           DROP_OTHER_UNPRIV,
+                                           -ETIMEDOUT, -ETIMEDOUT);
+       ASSERT_RETURN(ret == 0);
+
+       /* Drop received broadcasts by privileged */
+       ret = kdbus_msg_recv_poll(owner_a, 100, NULL, NULL);
+       ret = kdbus_msg_recv_poll(owner_a, 100, NULL, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_msg_recv(owner_a, NULL, NULL);
+       ASSERT_RETURN(ret == -EAGAIN);
+
+       ret = kdbus_msg_recv_poll(owner_b, 100, NULL, NULL);
+       ret = kdbus_msg_recv_poll(owner_b, 100, NULL, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_msg_recv(owner_b, NULL, NULL);
+       ASSERT_RETURN(ret == -EAGAIN);
+
+       /*
+        * Perform last tests, allow others to talk to name
+        * "com.example.broadcastA". So now receiving broadcasts
+        * from it should succeed since the TALK policy allow it.
+        */
+
+       /* KDBUS_POLICY_OWN for unprivileged connections */
+       access = (struct kdbus_policy_access){
+               .type = KDBUS_POLICY_ACCESS_WORLD,
+               .id = geteuid(),
+               .access = KDBUS_POLICY_TALK,
+       };
+
+       ret = kdbus_conn_update_policy(holder_a,
+                                      "com.example.broadcastA",
+                                      &access, 1);
+       ASSERT_RETURN(ret == 0);
+
+       /*
+        * Unprivileged is able to TALK to "com.example.broadcastA"
+        * now so it will receive its broadcasts
+        */
+       ret = test_policy_priv_by_broadcast(env->buspath, owner_a,
+                                           DO_NOT_DROP, 0, 0);
+       ASSERT_RETURN(ret == 0);
+
+       ++expected_cookie;
+       ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({
+               ret = kdbus_name_acquire(unpriv, "com.example.broadcastB",
+                                        NULL);
+               ASSERT_EXIT(ret >= 0);
+               ret = kdbus_msg_send(unpriv, NULL, expected_cookie,
+                                    0, 0, 0, KDBUS_DST_ID_BROADCAST, 0, NULL);
+               ASSERT_EXIT(ret == 0);
+       }));
+       ASSERT_RETURN(ret == 0);
+
+       /* owner_a is privileged it will get the broadcast now. */
+       ret = kdbus_msg_recv_poll(owner_a, 300, &msg, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       /* confirm the received cookie */
+       ASSERT_RETURN(msg->cookie == expected_cookie);
+
+       kdbus_msg_free(msg);
+
+       /*
+        * owner_a released name "com.example.broadcastA". It should
+        * receive broadcasts since it is still privileged and has
+        * the right match.
+        *
+        * Unprivileged connection will own a name and will try to
+        * signal to the privileged connection.
+        */
+
+       ret = kdbus_name_release(owner_a, "com.example.broadcastA");
+       ASSERT_EXIT(ret >= 0);
+
+       ++expected_cookie;
+       ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({
+               ret = kdbus_name_acquire(unpriv, "com.example.broadcastB",
+                                        NULL);
+               ASSERT_EXIT(ret >= 0);
+               ret = kdbus_msg_send(unpriv, NULL, expected_cookie,
+                                    0, 0, 0, KDBUS_DST_ID_BROADCAST, 0, NULL);
+               ASSERT_EXIT(ret == 0);
+       }));
+       ASSERT_RETURN(ret == 0);
+
+       /* owner_a will get the broadcast now. */
+       ret = kdbus_msg_recv_poll(owner_a, 300, &msg, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       /* confirm the received cookie */
+       ASSERT_RETURN(msg->cookie == expected_cookie);
+
+       kdbus_msg_free(msg);
+
+       kdbus_conn_free(owner_a);
+       kdbus_conn_free(owner_b);
+       kdbus_conn_free(holder_a);
+       kdbus_conn_free(holder_b);
+
+       return 0;
+}
+
+static int test_policy_priv(struct kdbus_test_env *env)
+{
+       struct kdbus_conn *conn_a, *conn_b, *conn, *owner;
+       struct kdbus_policy_access access, *acc;
+       sigset_t sset;
+       size_t num;
+       int ret;
+
+       /*
+        * Make sure we have CAP_SETUID/SETGID so we can drop privileges
+        */
+
+       ret = test_is_capable(CAP_SETUID, CAP_SETGID, -1);
+       ASSERT_RETURN(ret >= 0);
+
+       if (!ret)
+               return TEST_SKIP;
+
+       /* make sure that uids and gids are mapped */
+       if (!all_uids_gids_are_mapped())
+               return TEST_SKIP;
+
+       /*
+        * Setup:
+        *  conn_a: policy holder for com.example.a
+        *  conn_b: name holder of com.example.b
+        */
+
+       signal(SIGUSR1, nosig);
+       sigemptyset(&sset);
+       sigaddset(&sset, SIGUSR1);
+       sigprocmask(SIG_BLOCK, &sset, NULL);
+
+       conn = kdbus_hello(env->buspath, 0, NULL, 0);
+       ASSERT_RETURN(conn);
+
+       /*
+        * Before registering any policy holder, make sure that the
+        * bus is secure by default. This test is necessary, it catches
+        * several cases where old D-Bus was vulnerable.
+        */
+
+       ret = test_priv_before_policy_upload(env);
+       ASSERT_RETURN(ret == 0);
+
+       /*
+        * Make sure unprivileged are not able to register policy
+        * holders
+        */
+
+       ret = RUN_UNPRIVILEGED(UNPRIV_UID, UNPRIV_GID, ({
+               struct kdbus_conn *holder;
+
+               holder = kdbus_hello_registrar(env->buspath,
+                                              "com.example.a", NULL, 0,
+                                              KDBUS_HELLO_POLICY_HOLDER);
+               ASSERT_EXIT(holder == NULL && errno == EPERM);
+       }),
+       ({ 0; }));
+       ASSERT_RETURN(ret == 0);
+
+
+       /* Register policy holder */
+
+       conn_a = kdbus_hello_registrar(env->buspath, "com.example.a",
+                                      NULL, 0, KDBUS_HELLO_POLICY_HOLDER);
+       ASSERT_RETURN(conn_a);
+
+       conn_b = kdbus_hello(env->buspath, 0, NULL, 0);
+       ASSERT_RETURN(conn_b);
+
+       ret = kdbus_name_acquire(conn_b, "com.example.b", NULL);
+       ASSERT_EXIT(ret >= 0);
+
+       /*
+        * Make sure bus-owners can always acquire names.
+        */
+       ret = kdbus_name_acquire(conn, "com.example.a", NULL);
+       ASSERT_EXIT(ret >= 0);
+
+       kdbus_conn_free(conn);
+
+       /*
+        * Make sure unprivileged users cannot acquire names with default
+        * policy assigned.
+        */
+
+       ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({
+               ret = kdbus_name_acquire(unpriv, "com.example.a", NULL);
+               ASSERT_EXIT(ret < 0);
+       }));
+       ASSERT_RETURN(ret >= 0);
+
+       /*
+        * Make sure unprivileged users can acquire names if we make them
+        * world-accessible.
+        */
+
+       access = (struct kdbus_policy_access){
+               .type = KDBUS_POLICY_ACCESS_WORLD,
+               .id = 0,
+               .access = KDBUS_POLICY_OWN,
+       };
+
+       /*
+        * Make sure unprivileged/normal connections are not able
+        * to update policies
+        */
+
+       ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({
+               ret = kdbus_conn_update_policy(unpriv, "com.example.a",
+                                              &access, 1);
+               ASSERT_EXIT(ret == -EOPNOTSUPP);
+       }));
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_conn_update_policy(conn_a, "com.example.a", &access, 1);
+       ASSERT_RETURN(ret == 0);
+
+       ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({
+               ret = kdbus_name_acquire(unpriv, "com.example.a", NULL);
+               ASSERT_EXIT(ret >= 0);
+       }));
+       ASSERT_RETURN(ret >= 0);
+
+       /*
+        * Make sure unprivileged users can acquire names if we make them
+        * gid-accessible. But only if the gid matches.
+        */
+
+       access = (struct kdbus_policy_access){
+               .type = KDBUS_POLICY_ACCESS_GROUP,
+               .id = UNPRIV_GID,
+               .access = KDBUS_POLICY_OWN,
+       };
+
+       ret = kdbus_conn_update_policy(conn_a, "com.example.a", &access, 1);
+       ASSERT_RETURN(ret == 0);
+
+       ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({
+               ret = kdbus_name_acquire(unpriv, "com.example.a", NULL);
+               ASSERT_EXIT(ret >= 0);
+       }));
+       ASSERT_RETURN(ret >= 0);
+
+       access = (struct kdbus_policy_access){
+               .type = KDBUS_POLICY_ACCESS_GROUP,
+               .id = 1,
+               .access = KDBUS_POLICY_OWN,
+       };
+
+       ret = kdbus_conn_update_policy(conn_a, "com.example.a", &access, 1);
+       ASSERT_RETURN(ret == 0);
+
+       ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({
+               ret = kdbus_name_acquire(unpriv, "com.example.a", NULL);
+               ASSERT_EXIT(ret < 0);
+       }));
+       ASSERT_RETURN(ret >= 0);
+
+       /*
+        * Make sure unprivileged users can acquire names if we make them
+        * uid-accessible. But only if the uid matches.
+        */
+
+       access = (struct kdbus_policy_access){
+               .type = KDBUS_POLICY_ACCESS_USER,
+               .id = UNPRIV_UID,
+               .access = KDBUS_POLICY_OWN,
+       };
+
+       ret = kdbus_conn_update_policy(conn_a, "com.example.a", &access, 1);
+       ASSERT_RETURN(ret == 0);
+
+       ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({
+               ret = kdbus_name_acquire(unpriv, "com.example.a", NULL);
+               ASSERT_EXIT(ret >= 0);
+       }));
+       ASSERT_RETURN(ret >= 0);
+
+       access = (struct kdbus_policy_access){
+               .type = KDBUS_POLICY_ACCESS_USER,
+               .id = 1,
+               .access = KDBUS_POLICY_OWN,
+       };
+
+       ret = kdbus_conn_update_policy(conn_a, "com.example.a", &access, 1);
+       ASSERT_RETURN(ret == 0);
+
+       ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({
+               ret = kdbus_name_acquire(unpriv, "com.example.a", NULL);
+               ASSERT_EXIT(ret < 0);
+       }));
+       ASSERT_RETURN(ret >= 0);
+
+       /*
+        * Make sure unprivileged users cannot acquire names if no owner-policy
+        * matches, even if SEE/TALK policies match.
+        */
+
+       num = 4;
+       acc = (struct kdbus_policy_access[]){
+               {
+                       .type = KDBUS_POLICY_ACCESS_GROUP,
+                       .id = UNPRIV_GID,
+                       .access = KDBUS_POLICY_SEE,
+               },
+               {
+                       .type = KDBUS_POLICY_ACCESS_USER,
+                       .id = UNPRIV_UID,
+                       .access = KDBUS_POLICY_TALK,
+               },
+               {
+                       .type = KDBUS_POLICY_ACCESS_WORLD,
+                       .id = 0,
+                       .access = KDBUS_POLICY_TALK,
+               },
+               {
+                       .type = KDBUS_POLICY_ACCESS_WORLD,
+                       .id = 0,
+                       .access = KDBUS_POLICY_SEE,
+               },
+       };
+
+       ret = kdbus_conn_update_policy(conn_a, "com.example.a", acc, num);
+       ASSERT_RETURN(ret == 0);
+
+       ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({
+               ret = kdbus_name_acquire(unpriv, "com.example.a", NULL);
+               ASSERT_EXIT(ret < 0);
+       }));
+       ASSERT_RETURN(ret >= 0);
+
+       /*
+        * Make sure unprivileged users can acquire names if the only matching
+        * policy is somewhere in the middle.
+        */
+
+       num = 5;
+       acc = (struct kdbus_policy_access[]){
+               {
+                       .type = KDBUS_POLICY_ACCESS_USER,
+                       .id = 1,
+                       .access = KDBUS_POLICY_OWN,
+               },
+               {
+                       .type = KDBUS_POLICY_ACCESS_USER,
+                       .id = 2,
+                       .access = KDBUS_POLICY_OWN,
+               },
+               {
+                       .type = KDBUS_POLICY_ACCESS_USER,
+                       .id = UNPRIV_UID,
+                       .access = KDBUS_POLICY_OWN,
+               },
+               {
+                       .type = KDBUS_POLICY_ACCESS_USER,
+                       .id = 3,
+                       .access = KDBUS_POLICY_OWN,
+               },
+               {
+                       .type = KDBUS_POLICY_ACCESS_USER,
+                       .id = 4,
+                       .access = KDBUS_POLICY_OWN,
+               },
+       };
+
+       ret = kdbus_conn_update_policy(conn_a, "com.example.a", acc, num);
+       ASSERT_RETURN(ret == 0);
+
+       ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({
+               ret = kdbus_name_acquire(unpriv, "com.example.a", NULL);
+               ASSERT_EXIT(ret >= 0);
+       }));
+       ASSERT_RETURN(ret >= 0);
+
+       /*
+        * Clear policies
+        */
+
+       ret = kdbus_conn_update_policy(conn_a, "com.example.a", NULL, 0);
+       ASSERT_RETURN(ret == 0);
+
+       /*
+        * Make sure privileged bus users can _always_ talk to others.
+        */
+
+       conn = kdbus_hello(env->buspath, 0, NULL, 0);
+       ASSERT_RETURN(conn);
+
+       ret = kdbus_msg_send(conn, "com.example.b", 0xdeadbeef, 0, 0, 0, 0,
+                            0, NULL);
+       ASSERT_EXIT(ret >= 0);
+
+       ret = kdbus_msg_recv_poll(conn_b, 300, NULL, NULL);
+       ASSERT_EXIT(ret >= 0);
+
+       kdbus_conn_free(conn);
+
+       /*
+        * Make sure unprivileged bus users cannot talk by default.
+        */
+
+       ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({
+               ret = kdbus_msg_send(unpriv, "com.example.b", 0xdeadbeef, 0, 0,
+                                    0, 0, 0, NULL);
+               ASSERT_EXIT(ret == -EPERM);
+       }));
+       ASSERT_RETURN(ret >= 0);
+
+       /*
+        * Make sure unprivileged bus users can talk to equals, even without
+        * policy.
+        */
+
+       access = (struct kdbus_policy_access){
+               .type = KDBUS_POLICY_ACCESS_USER,
+               .id = UNPRIV_UID,
+               .access = KDBUS_POLICY_OWN,
+       };
+
+       ret = kdbus_conn_update_policy(conn_a, "com.example.c", &access, 1);
+       ASSERT_RETURN(ret == 0);
+
+       ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({
+               struct kdbus_conn *owner;
+
+               owner = kdbus_hello(env->buspath, 0, NULL, 0);
+               ASSERT_RETURN(owner);
+
+               ret = kdbus_name_acquire(owner, "com.example.c", NULL);
+               ASSERT_EXIT(ret >= 0);
+
+               ret = kdbus_msg_send(unpriv, "com.example.c", 0xdeadbeef, 0, 0,
+                                    0, 0, 0, NULL);
+               ASSERT_EXIT(ret >= 0);
+               ret = kdbus_msg_recv_poll(owner, 100, NULL, NULL);
+               ASSERT_EXIT(ret >= 0);
+
+               kdbus_conn_free(owner);
+       }));
+       ASSERT_RETURN(ret >= 0);
+
+       /*
+        * Make sure unprivileged bus users can talk to privileged users if a
+        * suitable UID policy is set.
+        */
+
+       access = (struct kdbus_policy_access){
+               .type = KDBUS_POLICY_ACCESS_USER,
+               .id = UNPRIV_UID,
+               .access = KDBUS_POLICY_TALK,
+       };
+
+       ret = kdbus_conn_update_policy(conn_a, "com.example.b", &access, 1);
+       ASSERT_RETURN(ret == 0);
+
+       ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({
+               ret = kdbus_msg_send(unpriv, "com.example.b", 0xdeadbeef, 0, 0,
+                                    0, 0, 0, NULL);
+               ASSERT_EXIT(ret >= 0);
+       }));
+       ASSERT_RETURN(ret >= 0);
+
+       ret = kdbus_msg_recv_poll(conn_b, 100, NULL, NULL);
+       ASSERT_EXIT(ret >= 0);
+
+       /*
+        * Make sure unprivileged bus users can talk to privileged users if a
+        * suitable GID policy is set.
+        */
+
+       access = (struct kdbus_policy_access){
+               .type = KDBUS_POLICY_ACCESS_GROUP,
+               .id = UNPRIV_GID,
+               .access = KDBUS_POLICY_TALK,
+       };
+
+       ret = kdbus_conn_update_policy(conn_a, "com.example.b", &access, 1);
+       ASSERT_RETURN(ret == 0);
+
+       ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({
+               ret = kdbus_msg_send(unpriv, "com.example.b", 0xdeadbeef, 0, 0,
+                                    0, 0, 0, NULL);
+               ASSERT_EXIT(ret >= 0);
+       }));
+       ASSERT_RETURN(ret >= 0);
+
+       ret = kdbus_msg_recv_poll(conn_b, 100, NULL, NULL);
+       ASSERT_EXIT(ret >= 0);
+
+       /*
+        * Make sure unprivileged bus users can talk to privileged users if a
+        * suitable WORLD policy is set.
+        */
+
+       access = (struct kdbus_policy_access){
+               .type = KDBUS_POLICY_ACCESS_WORLD,
+               .id = 0,
+               .access = KDBUS_POLICY_TALK,
+       };
+
+       ret = kdbus_conn_update_policy(conn_a, "com.example.b", &access, 1);
+       ASSERT_RETURN(ret == 0);
+
+       ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({
+               ret = kdbus_msg_send(unpriv, "com.example.b", 0xdeadbeef, 0, 0,
+                                    0, 0, 0, NULL);
+               ASSERT_EXIT(ret >= 0);
+       }));
+       ASSERT_RETURN(ret >= 0);
+
+       ret = kdbus_msg_recv_poll(conn_b, 100, NULL, NULL);
+       ASSERT_EXIT(ret >= 0);
+
+       /*
+        * Make sure unprivileged bus users cannot talk to privileged users if
+        * no suitable policy is set.
+        */
+
+       num = 5;
+       acc = (struct kdbus_policy_access[]){
+               {
+                       .type = KDBUS_POLICY_ACCESS_USER,
+                       .id = 0,
+                       .access = KDBUS_POLICY_OWN,
+               },
+               {
+                       .type = KDBUS_POLICY_ACCESS_USER,
+                       .id = 1,
+                       .access = KDBUS_POLICY_TALK,
+               },
+               {
+                       .type = KDBUS_POLICY_ACCESS_USER,
+                       .id = UNPRIV_UID,
+                       .access = KDBUS_POLICY_SEE,
+               },
+               {
+                       .type = KDBUS_POLICY_ACCESS_USER,
+                       .id = 3,
+                       .access = KDBUS_POLICY_TALK,
+               },
+               {
+                       .type = KDBUS_POLICY_ACCESS_USER,
+                       .id = 4,
+                       .access = KDBUS_POLICY_TALK,
+               },
+       };
+
+       ret = kdbus_conn_update_policy(conn_a, "com.example.b", acc, num);
+       ASSERT_RETURN(ret == 0);
+
+       ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({
+               ret = kdbus_msg_send(unpriv, "com.example.b", 0xdeadbeef, 0, 0,
+                                    0, 0, 0, NULL);
+               ASSERT_EXIT(ret == -EPERM);
+       }));
+       ASSERT_RETURN(ret >= 0);
+
+       /*
+        * Make sure unprivileged bus users can talk to privileged users if a
+        * suitable OWN privilege overwrites TALK.
+        */
+
+       access = (struct kdbus_policy_access){
+               .type = KDBUS_POLICY_ACCESS_WORLD,
+               .id = 0,
+               .access = KDBUS_POLICY_OWN,
+       };
+
+       ret = kdbus_conn_update_policy(conn_a, "com.example.b", &access, 1);
+       ASSERT_RETURN(ret == 0);
+
+       ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({
+               ret = kdbus_msg_send(unpriv, "com.example.b", 0xdeadbeef, 0, 0,
+                                    0, 0, 0, NULL);
+               ASSERT_EXIT(ret >= 0);
+       }));
+       ASSERT_RETURN(ret >= 0);
+
+       ret = kdbus_msg_recv_poll(conn_b, 100, NULL, NULL);
+       ASSERT_EXIT(ret >= 0);
+
+       /*
+        * Make sure the TALK cache is reset correctly when policies are
+        * updated.
+        */
+
+       access = (struct kdbus_policy_access){
+               .type = KDBUS_POLICY_ACCESS_WORLD,
+               .id = 0,
+               .access = KDBUS_POLICY_TALK,
+       };
+
+       ret = kdbus_conn_update_policy(conn_a, "com.example.b", &access, 1);
+       ASSERT_RETURN(ret == 0);
+
+       ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({
+               ret = kdbus_msg_send(unpriv, "com.example.b", 0xdeadbeef, 0, 0,
+                                    0, 0, 0, NULL);
+               ASSERT_EXIT(ret >= 0);
+
+               ret = kdbus_msg_recv_poll(conn_b, 100, NULL, NULL);
+               ASSERT_EXIT(ret >= 0);
+
+               ret = kdbus_conn_update_policy(conn_a, "com.example.b",
+                                              NULL, 0);
+               ASSERT_RETURN(ret == 0);
+
+               ret = kdbus_msg_send(unpriv, "com.example.b", 0xdeadbeef, 0, 0,
+                                    0, 0, 0, NULL);
+               ASSERT_EXIT(ret == -EPERM);
+       }));
+       ASSERT_RETURN(ret >= 0);
+
+       /*
+        * Make sure the TALK cache is reset correctly when policy holders
+        * disconnect.
+        */
+
+       access = (struct kdbus_policy_access){
+               .type = KDBUS_POLICY_ACCESS_WORLD,
+               .id = 0,
+               .access = KDBUS_POLICY_OWN,
+       };
+
+       conn = kdbus_hello_registrar(env->buspath, "com.example.c",
+                                    NULL, 0, KDBUS_HELLO_POLICY_HOLDER);
+       ASSERT_RETURN(conn);
+
+       ret = kdbus_conn_update_policy(conn, "com.example.c", &access, 1);
+       ASSERT_RETURN(ret == 0);
+
+       owner = kdbus_hello(env->buspath, 0, NULL, 0);
+       ASSERT_RETURN(owner);
+
+       ret = kdbus_name_acquire(owner, "com.example.c", NULL);
+       ASSERT_RETURN(ret >= 0);
+
+       ret = RUN_UNPRIVILEGED(UNPRIV_UID, UNPRIV_GID, ({
+               struct kdbus_conn *unpriv;
+
+               /* wait for parent to be finished */
+               sigemptyset(&sset);
+               ret = sigsuspend(&sset);
+               ASSERT_RETURN(ret == -1 && errno == EINTR);
+
+               unpriv = kdbus_hello(env->buspath, 0, NULL, 0);
+               ASSERT_RETURN(unpriv);
+
+               ret = kdbus_msg_send(unpriv, "com.example.c", 0xdeadbeef, 0, 0,
+                                    0, 0, 0, NULL);
+               ASSERT_EXIT(ret >= 0);
+
+               ret = kdbus_msg_recv_poll(owner, 100, NULL, NULL);
+               ASSERT_EXIT(ret >= 0);
+
+               /* free policy holder */
+               kdbus_conn_free(conn);
+
+               ret = kdbus_msg_send(unpriv, "com.example.c", 0xdeadbeef, 0, 0,
+                                    0, 0, 0, NULL);
+               ASSERT_EXIT(ret == -EPERM);
+
+               kdbus_conn_free(unpriv);
+       }), ({
+               /* make sure policy holder is only valid in child */
+               kdbus_conn_free(conn);
+               kill(pid, SIGUSR1);
+       }));
+       ASSERT_RETURN(ret >= 0);
+
+
+       /*
+        * The following tests are necessary.
+        */
+
+       ret = test_broadcast_after_policy_upload(env);
+       ASSERT_RETURN(ret == 0);
+
+       kdbus_conn_free(owner);
+
+       /*
+        * cleanup resources
+        */
+
+       kdbus_conn_free(conn_b);
+       kdbus_conn_free(conn_a);
+
+       return TEST_OK;
+}
+
+int kdbus_test_policy_priv(struct kdbus_test_env *env)
+{
+       pid_t pid;
+       int ret;
+
+       /* make sure to exit() if a child returns from fork() */
+       pid = getpid();
+       ret = test_policy_priv(env);
+       if (pid != getpid())
+               exit(1);
+
+       return ret;
+}
diff --git a/tools/testing/selftests/kdbus/test-policy.c b/tools/testing/selftests/kdbus/test-policy.c
new file mode 100644 (file)
index 0000000..96d20d5
--- /dev/null
@@ -0,0 +1,80 @@
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <unistd.h>
+
+#include "kdbus-test.h"
+#include "kdbus-util.h"
+#include "kdbus-enum.h"
+
+int kdbus_test_policy(struct kdbus_test_env *env)
+{
+       struct kdbus_conn *conn_a, *conn_b;
+       struct kdbus_policy_access access;
+       int ret;
+
+       /* Invalid name */
+       conn_a = kdbus_hello_registrar(env->buspath, ".example.a",
+                                      NULL, 0, KDBUS_HELLO_POLICY_HOLDER);
+       ASSERT_RETURN(conn_a == NULL);
+
+       conn_a = kdbus_hello_registrar(env->buspath, "example",
+                                      NULL, 0, KDBUS_HELLO_POLICY_HOLDER);
+       ASSERT_RETURN(conn_a == NULL);
+
+       conn_a = kdbus_hello_registrar(env->buspath, "com.example.a",
+                                      NULL, 0, KDBUS_HELLO_POLICY_HOLDER);
+       ASSERT_RETURN(conn_a);
+
+       conn_b = kdbus_hello_registrar(env->buspath, "com.example.b",
+                                      NULL, 0, KDBUS_HELLO_POLICY_HOLDER);
+       ASSERT_RETURN(conn_b);
+
+       /*
+        * Verify there cannot be any duplicate entries, except for specific vs.
+        * wildcard entries.
+        */
+
+       access = (struct kdbus_policy_access){
+               .type = KDBUS_POLICY_ACCESS_USER,
+               .id = geteuid(),
+               .access = KDBUS_POLICY_SEE,
+       };
+
+       ret = kdbus_conn_update_policy(conn_a, "com.example.a", &access, 1);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_conn_update_policy(conn_b, "com.example.a", &access, 1);
+       ASSERT_RETURN(ret == -EEXIST);
+
+       ret = kdbus_conn_update_policy(conn_b, "com.example.a.*", &access, 1);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_conn_update_policy(conn_a, "com.example.a.*", &access, 1);
+       ASSERT_RETURN(ret == -EEXIST);
+
+       ret = kdbus_conn_update_policy(conn_a, "com.example.*", &access, 1);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_conn_update_policy(conn_b, "com.example.a", &access, 1);
+       ASSERT_RETURN(ret == 0);
+
+       ret = kdbus_conn_update_policy(conn_b, "com.example.*", &access, 1);
+       ASSERT_RETURN(ret == -EEXIST);
+
+       /* Invalid name */
+       ret = kdbus_conn_update_policy(conn_b, ".example.*", &access, 1);
+       ASSERT_RETURN(ret == -EINVAL);
+
+       ret = kdbus_conn_update_policy(conn_b, "example", &access, 1);
+       ASSERT_RETURN(ret == -EINVAL);
+
+       kdbus_conn_free(conn_b);
+       kdbus_conn_free(conn_a);
+
+       return TEST_OK;
+}
diff --git a/tools/testing/selftests/kdbus/test-send.c b/tools/testing/selftests/kdbus/test-send.c
new file mode 100644 (file)
index 0000000..9bd8c8f
--- /dev/null
@@ -0,0 +1,84 @@
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <fcntl.h>
+#include <stdlib.h>
+/* Use in conjunction with test-kdbus-daemon */
+
+#include <unistd.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <errno.h>
+#include <assert.h>
+#include <poll.h>
+#include <sys/ioctl.h>
+
+#include "kdbus-test.h"
+#include "kdbus-util.h"
+#include "kdbus-enum.h"
+
+int get_file(const char *fname, int flags, const char *content)
+{
+       FILE *f;
+
+       if (access(fname, F_OK) < 0) {
+               f = fopen(fname, "w");
+               if (!f)
+                       return -1;
+               fprintf(f, "%s\n", content);
+               fclose(f);
+       }
+
+       return open(fname, flags);
+}
+
+int kdbus_test_send(struct kdbus_test_env *env)
+{
+       int ret;
+       int serial = 1;
+       int fds[3];
+       size_t i;
+
+       if (!env->conn)
+               return EXIT_FAILURE;
+
+       fds[0] = get_file("/tmp/kdbus-test-send.rd", O_RDONLY, "foo");
+       fds[1] = get_file("/tmp/kdbus-test-send.wr", O_WRONLY, "bar");
+       fds[2] = get_file("/tmp/kdbus-test-send.rdwr", O_RDWR, "baz");
+
+       for (i = 0; i < ELEMENTSOF(fds); i++) {
+               if (fds[i] < 0) {
+                       fprintf(stderr, "Unable to open data/fileN file(s)\n");
+                       return EXIT_FAILURE;
+               }
+       }
+
+       ret = kdbus_msg_send(env->conn, "com.example.kdbus-test", serial++,
+                            0, 0, 0, 0, 0, NULL);
+       if (ret < 0)
+               fprintf(stderr, "error sending simple message: %d (%m)\n",
+                       ret);
+
+       ret = kdbus_msg_send(env->conn, "com.example.kdbus-test", serial++,
+                            0, 0, 0, 0, 1, fds);
+       if (ret < 0)
+               fprintf(stderr, "error sending message with 1 fd: %d (%m)\n",
+                       ret);
+
+       ret = kdbus_msg_send(env->conn, "com.example.kdbus-test", serial++,
+                            0, 0, 0, 0, 2, fds);
+       if (ret < 0)
+               fprintf(stderr, "error sending message with 2 fds: %d (%m)\n",
+                       ret);
+
+       ret = kdbus_msg_send(env->conn, "com.example.kdbus-test", serial++,
+                            0, 0, 0, 0, 3, fds);
+       if (ret < 0)
+               fprintf(stderr, "error sending message with 3 fds: %d (%m)\n",
+                       ret);
+
+       for (i = 0; i < ELEMENTSOF(fds); i++)
+               close(fds[i]);
+
+       return EXIT_SUCCESS;
+}
diff --git a/tools/testing/selftests/kdbus/test-sync.c b/tools/testing/selftests/kdbus/test-sync.c
new file mode 100644 (file)
index 0000000..77c3c41
--- /dev/null
@@ -0,0 +1,369 @@
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <errno.h>
+#include <assert.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <sys/eventfd.h>
+
+#include "kdbus-api.h"
+#include "kdbus-test.h"
+#include "kdbus-util.h"
+#include "kdbus-enum.h"
+
+static struct kdbus_conn *conn_a, *conn_b;
+static unsigned int cookie = 0xdeadbeef;
+
+static void nop_handler(int sig) {}
+
+static int interrupt_sync(struct kdbus_conn *conn_src,
+                         struct kdbus_conn *conn_dst)
+{
+       pid_t pid;
+       int ret, status;
+       struct kdbus_msg *msg = NULL;
+       struct sigaction sa = {
+               .sa_handler = nop_handler,
+               .sa_flags = SA_NOCLDSTOP|SA_RESTART,
+       };
+
+       cookie++;
+       pid = fork();
+       ASSERT_RETURN_VAL(pid >= 0, pid);
+
+       if (pid == 0) {
+               ret = sigaction(SIGINT, &sa, NULL);
+               ASSERT_EXIT(ret == 0);
+
+               ret = kdbus_msg_send_sync(conn_dst, NULL, cookie,
+                                         KDBUS_MSG_EXPECT_REPLY,
+                                         100000000ULL, 0, conn_src->id, -1);
+               ASSERT_EXIT(ret == -ETIMEDOUT);
+
+               _exit(EXIT_SUCCESS);
+       }
+
+       ret = kdbus_msg_recv_poll(conn_src, 100, &msg, NULL);
+       ASSERT_RETURN(ret == 0 && msg->cookie == cookie);
+
+       kdbus_msg_free(msg);
+
+       ret = kill(pid, SIGINT);
+       ASSERT_RETURN_VAL(ret == 0, ret);
+
+       ret = waitpid(pid, &status, 0);
+       ASSERT_RETURN_VAL(ret >= 0, ret);
+
+       if (WIFSIGNALED(status))
+               return TEST_ERR;
+
+       ret = kdbus_msg_recv_poll(conn_src, 100, NULL, NULL);
+       ASSERT_RETURN(ret == -ETIMEDOUT);
+
+       return (status == EXIT_SUCCESS) ? TEST_OK : TEST_ERR;
+}
+
+static int close_epipe_sync(const char *bus)
+{
+       pid_t pid;
+       int ret, status;
+       struct kdbus_conn *conn_src;
+       struct kdbus_conn *conn_dst;
+       struct kdbus_msg *msg = NULL;
+
+       conn_src = kdbus_hello(bus, 0, NULL, 0);
+       ASSERT_RETURN(conn_src);
+
+       ret = kdbus_add_match_empty(conn_src);
+       ASSERT_RETURN(ret == 0);
+
+       conn_dst = kdbus_hello(bus, 0, NULL, 0);
+       ASSERT_RETURN(conn_dst);
+
+       cookie++;
+       pid = fork();
+       ASSERT_RETURN_VAL(pid >= 0, pid);
+
+       if (pid == 0) {
+               uint64_t dst_id;
+
+               /* close our reference */
+               dst_id = conn_dst->id;
+               kdbus_conn_free(conn_dst);
+
+               ret = kdbus_msg_recv_poll(conn_src, 100, &msg, NULL);
+               ASSERT_EXIT(ret == 0 && msg->cookie == cookie);
+               ASSERT_EXIT(msg->src_id == dst_id);
+
+               cookie++;
+               ret = kdbus_msg_send_sync(conn_src, NULL, cookie,
+                                         KDBUS_MSG_EXPECT_REPLY,
+                                         100000000ULL, 0, dst_id, -1);
+               ASSERT_EXIT(ret == -EPIPE);
+
+               _exit(EXIT_SUCCESS);
+       }
+
+       ret = kdbus_msg_send(conn_dst, NULL, cookie, 0, 0, 0,
+                            KDBUS_DST_ID_BROADCAST, 0, NULL);
+       ASSERT_RETURN(ret == 0);
+
+       cookie++;
+       ret = kdbus_msg_recv_poll(conn_dst, 100, &msg, NULL);
+       ASSERT_RETURN(ret == 0 && msg->cookie == cookie);
+
+       kdbus_msg_free(msg);
+
+       /* destroy connection */
+       kdbus_conn_free(conn_dst);
+       kdbus_conn_free(conn_src);
+
+       ret = waitpid(pid, &status, 0);
+       ASSERT_RETURN_VAL(ret >= 0, ret);
+
+       if (!WIFEXITED(status))
+               return TEST_ERR;
+
+       return (status == EXIT_SUCCESS) ? TEST_OK : TEST_ERR;
+}
+
+static int cancel_fd_sync(struct kdbus_conn *conn_src,
+                         struct kdbus_conn *conn_dst)
+{
+       pid_t pid;
+       int cancel_fd;
+       int ret, status;
+       uint64_t counter = 1;
+       struct kdbus_msg *msg = NULL;
+
+       cancel_fd = eventfd(0, 0);
+       ASSERT_RETURN_VAL(cancel_fd >= 0, cancel_fd);
+
+       cookie++;
+       pid = fork();
+       ASSERT_RETURN_VAL(pid >= 0, pid);
+
+       if (pid == 0) {
+               ret = kdbus_msg_send_sync(conn_dst, NULL, cookie,
+                                         KDBUS_MSG_EXPECT_REPLY,
+                                         100000000ULL, 0, conn_src->id,
+                                         cancel_fd);
+               ASSERT_EXIT(ret == -ECANCELED);
+
+               _exit(EXIT_SUCCESS);
+       }
+
+       ret = kdbus_msg_recv_poll(conn_src, 100, &msg, NULL);
+       ASSERT_RETURN(ret == 0 && msg->cookie == cookie);
+
+       kdbus_msg_free(msg);
+
+       ret = write(cancel_fd, &counter, sizeof(counter));
+       ASSERT_RETURN(ret == sizeof(counter));
+
+       ret = waitpid(pid, &status, 0);
+       ASSERT_RETURN_VAL(ret >= 0, ret);
+
+       if (WIFSIGNALED(status))
+               return TEST_ERR;
+
+       return (status == EXIT_SUCCESS) ? TEST_OK : TEST_ERR;
+}
+
+static int no_cancel_sync(struct kdbus_conn *conn_src,
+                         struct kdbus_conn *conn_dst)
+{
+       pid_t pid;
+       int cancel_fd;
+       int ret, status;
+       struct kdbus_msg *msg = NULL;
+
+       /* pass eventfd, but never signal it so it shouldn't have any effect */
+
+       cancel_fd = eventfd(0, 0);
+       ASSERT_RETURN_VAL(cancel_fd >= 0, cancel_fd);
+
+       cookie++;
+       pid = fork();
+       ASSERT_RETURN_VAL(pid >= 0, pid);
+
+       if (pid == 0) {
+               ret = kdbus_msg_send_sync(conn_dst, NULL, cookie,
+                                         KDBUS_MSG_EXPECT_REPLY,
+                                         100000000ULL, 0, conn_src->id,
+                                         cancel_fd);
+               ASSERT_EXIT(ret == 0);
+
+               _exit(EXIT_SUCCESS);
+       }
+
+       ret = kdbus_msg_recv_poll(conn_src, 100, &msg, NULL);
+       ASSERT_RETURN_VAL(ret == 0 && msg->cookie == cookie, -1);
+
+       kdbus_msg_free(msg);
+
+       ret = kdbus_msg_send_reply(conn_src, cookie, conn_dst->id);
+       ASSERT_RETURN_VAL(ret >= 0, ret);
+
+       ret = waitpid(pid, &status, 0);
+       ASSERT_RETURN_VAL(ret >= 0, ret);
+
+       if (WIFSIGNALED(status))
+               return -1;
+
+       return (status == EXIT_SUCCESS) ? 0 : -1;
+}
+
+static void *run_thread_reply(void *data)
+{
+       int ret;
+       unsigned long status = TEST_OK;
+
+       ret = kdbus_msg_recv_poll(conn_a, 3000, NULL, NULL);
+       if (ret < 0)
+               goto exit_thread;
+
+       kdbus_printf("Thread received message, sending reply ...\n");
+
+       /* using an unknown cookie must fail */
+       ret = kdbus_msg_send_reply(conn_a, ~cookie, conn_b->id);
+       if (ret != -EPERM) {
+               status = TEST_ERR;
+               goto exit_thread;
+       }
+
+       ret = kdbus_msg_send_reply(conn_a, cookie, conn_b->id);
+       if (ret != 0) {
+               status = TEST_ERR;
+               goto exit_thread;
+       }
+
+exit_thread:
+       pthread_exit(NULL);
+       return (void *) status;
+}
+
+int kdbus_test_sync_reply(struct kdbus_test_env *env)
+{
+       unsigned long status;
+       pthread_t thread;
+       int ret;
+
+       conn_a = kdbus_hello(env->buspath, 0, NULL, 0);
+       conn_b = kdbus_hello(env->buspath, 0, NULL, 0);
+       ASSERT_RETURN(conn_a && conn_b);
+
+       pthread_create(&thread, NULL, run_thread_reply, NULL);
+
+       ret = kdbus_msg_send_sync(conn_b, NULL, cookie,
+                                 KDBUS_MSG_EXPECT_REPLY,
+                                 5000000000ULL, 0, conn_a->id, -1);
+
+       pthread_join(thread, (void *) &status);
+       ASSERT_RETURN(status == 0);
+       ASSERT_RETURN(ret == 0);
+
+       ret = interrupt_sync(conn_a, conn_b);
+       ASSERT_RETURN(ret == 0);
+
+       ret = close_epipe_sync(env->buspath);
+       ASSERT_RETURN(ret == 0);
+
+       ret = cancel_fd_sync(conn_a, conn_b);
+       ASSERT_RETURN(ret == 0);
+
+       ret = no_cancel_sync(conn_a, conn_b);
+       ASSERT_RETURN(ret == 0);
+
+       kdbus_printf("-- closing bus connections\n");
+
+       kdbus_conn_free(conn_a);
+       kdbus_conn_free(conn_b);
+
+       return TEST_OK;
+}
+
+#define BYEBYE_ME ((void*)0L)
+#define BYEBYE_THEM ((void*)1L)
+
+static void *run_thread_byebye(void *data)
+{
+       struct kdbus_cmd cmd_byebye = { .size = sizeof(cmd_byebye) };
+       int ret;
+
+       ret = kdbus_msg_recv_poll(conn_a, 3000, NULL, NULL);
+       if (ret == 0) {
+               kdbus_printf("Thread received message, invoking BYEBYE ...\n");
+               kdbus_msg_recv(conn_a, NULL, NULL);
+               if (data == BYEBYE_ME)
+                       kdbus_cmd_byebye(conn_b->fd, &cmd_byebye);
+               else if (data == BYEBYE_THEM)
+                       kdbus_cmd_byebye(conn_a->fd, &cmd_byebye);
+       }
+
+       pthread_exit(NULL);
+       return NULL;
+}
+
+int kdbus_test_sync_byebye(struct kdbus_test_env *env)
+{
+       pthread_t thread;
+       int ret;
+
+       /*
+        * This sends a synchronous message to a thread, which waits until it
+        * received the message and then invokes BYEBYE on the *ORIGINAL*
+        * connection. That is, on the same connection that synchronously waits
+        * for an reply.
+        * This should properly wake the connection up and cause ECONNRESET as
+        * the connection is disconnected now.
+        *
+        * The second time, we do the same but invoke BYEBYE on the *TARGET*
+        * connection. This should also wake up the synchronous sender as the
+        * reply cannot be sent by a disconnected target.
+        */
+
+       conn_a = kdbus_hello(env->buspath, 0, NULL, 0);
+       conn_b = kdbus_hello(env->buspath, 0, NULL, 0);
+       ASSERT_RETURN(conn_a && conn_b);
+
+       pthread_create(&thread, NULL, run_thread_byebye, BYEBYE_ME);
+
+       ret = kdbus_msg_send_sync(conn_b, NULL, cookie,
+                                 KDBUS_MSG_EXPECT_REPLY,
+                                 5000000000ULL, 0, conn_a->id, -1);
+
+       ASSERT_RETURN(ret == -ECONNRESET);
+
+       pthread_join(thread, NULL);
+
+       kdbus_conn_free(conn_a);
+       kdbus_conn_free(conn_b);
+
+       conn_a = kdbus_hello(env->buspath, 0, NULL, 0);
+       conn_b = kdbus_hello(env->buspath, 0, NULL, 0);
+       ASSERT_RETURN(conn_a && conn_b);
+
+       pthread_create(&thread, NULL, run_thread_byebye, BYEBYE_THEM);
+
+       ret = kdbus_msg_send_sync(conn_b, NULL, cookie,
+                                 KDBUS_MSG_EXPECT_REPLY,
+                                 5000000000ULL, 0, conn_a->id, -1);
+
+       ASSERT_RETURN(ret == -EPIPE);
+
+       pthread_join(thread, NULL);
+
+       kdbus_conn_free(conn_a);
+       kdbus_conn_free(conn_b);
+
+       return TEST_OK;
+}
diff --git a/tools/testing/selftests/kdbus/test-timeout.c b/tools/testing/selftests/kdbus/test-timeout.c
new file mode 100644 (file)
index 0000000..a5cdaf2
--- /dev/null
@@ -0,0 +1,99 @@
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <errno.h>
+#include <assert.h>
+#include <poll.h>
+#include <stdbool.h>
+
+#include "kdbus-api.h"
+#include "kdbus-test.h"
+#include "kdbus-util.h"
+#include "kdbus-enum.h"
+
+int timeout_msg_recv(struct kdbus_conn *conn, uint64_t *expected)
+{
+       struct kdbus_cmd_recv recv = { .size = sizeof(recv) };
+       struct kdbus_msg *msg;
+       int ret;
+
+       ret = kdbus_cmd_recv(conn->fd, &recv);
+       if (ret < 0) {
+               kdbus_printf("error receiving message: %d (%m)\n", ret);
+               return ret;
+       }
+
+       msg = (struct kdbus_msg *)(conn->buf + recv.msg.offset);
+
+       ASSERT_RETURN_VAL(msg->payload_type == KDBUS_PAYLOAD_KERNEL, -EINVAL);
+       ASSERT_RETURN_VAL(msg->src_id == KDBUS_SRC_ID_KERNEL, -EINVAL);
+       ASSERT_RETURN_VAL(msg->dst_id == conn->id, -EINVAL);
+
+       *expected &= ~(1ULL << msg->cookie_reply);
+       kdbus_printf("Got message timeout for cookie %llu\n",
+                    msg->cookie_reply);
+
+       ret = kdbus_free(conn, recv.msg.offset);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+int kdbus_test_timeout(struct kdbus_test_env *env)
+{
+       struct kdbus_conn *conn_a, *conn_b;
+       struct pollfd fd;
+       int ret, i, n_msgs = 4;
+       uint64_t expected = 0;
+       uint64_t cookie = 0xdeadbeef;
+
+       conn_a = kdbus_hello(env->buspath, 0, NULL, 0);
+       conn_b = kdbus_hello(env->buspath, 0, NULL, 0);
+       ASSERT_RETURN(conn_a && conn_b);
+
+       fd.fd = conn_b->fd;
+
+       /*
+        * send messages that expect a reply (within 100 msec),
+        * but never answer it.
+        */
+       for (i = 0; i < n_msgs; i++, cookie++) {
+               kdbus_printf("Sending message with cookie %llu ...\n",
+                            (unsigned long long)cookie);
+               ASSERT_RETURN(kdbus_msg_send(conn_b, NULL, cookie,
+                             KDBUS_MSG_EXPECT_REPLY,
+                             (i + 1) * 100ULL * 1000000ULL, 0,
+                             conn_a->id, 0, NULL) == 0);
+               expected |= 1ULL << cookie;
+       }
+
+       for (;;) {
+               fd.events = POLLIN | POLLPRI | POLLHUP;
+               fd.revents = 0;
+
+               ret = poll(&fd, 1, (n_msgs + 1) * 100);
+               if (ret == 0)
+                       kdbus_printf("--- timeout\n");
+               if (ret <= 0)
+                       break;
+
+               if (fd.revents & POLLIN)
+                       ASSERT_RETURN(!timeout_msg_recv(conn_b, &expected));
+
+               if (expected == 0)
+                       break;
+       }
+
+       ASSERT_RETURN(expected == 0);
+
+       kdbus_conn_free(conn_a);
+       kdbus_conn_free(conn_b);
+
+       return TEST_OK;
+}