11713ce88b
Latest upstream commit 748cf9547ea30f5a4dfee5e97d1d2ff4b307c392
49102 lines
1.4 MiB
49102 lines
1.4 MiB
From 668f12a28f79be67c7227829d06f9f1070450a1d Mon Sep 17 00:00:00 2001
|
|
From: Daniel Mack <daniel@zonque.org>
|
|
Date: Thu, 11 Sep 2014 21:50:47 +0200
|
|
Subject: [PATCH 01/78] kdbus: add documentation
|
|
|
|
kdbus is a system for low-latency, low-overhead, easy to use
|
|
interprocess communication (IPC).
|
|
|
|
The interface to all functions in this driver is implemented via ioctls
|
|
on files exposed through a filesystem called 'kdbusfs'. The default
|
|
mount point of kdbusfs is /sys/fs/kdbus. This patch adds detailed
|
|
documentation about the kernel level API design.
|
|
|
|
This patch adds a set of comprehensive set of DocBook files which
|
|
can be turned into man-pages using 'make mandocs', or into HTML
|
|
files with 'make htmldocs'.
|
|
|
|
Signed-off-by: Daniel Mack <daniel@zonque.org>
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Djalal Harouni <tixxdz@opendz.org>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
Documentation/Makefile | 2 +-
|
|
Documentation/kdbus/Makefile | 30 +
|
|
Documentation/kdbus/kdbus.bus.xml | 360 +++++++++
|
|
Documentation/kdbus/kdbus.connection.xml | 1252 +++++++++++++++++++++++++++++
|
|
Documentation/kdbus/kdbus.endpoint.xml | 436 ++++++++++
|
|
Documentation/kdbus/kdbus.fs.xml | 124 +++
|
|
Documentation/kdbus/kdbus.item.xml | 840 ++++++++++++++++++++
|
|
Documentation/kdbus/kdbus.match.xml | 553 +++++++++++++
|
|
Documentation/kdbus/kdbus.message.xml | 1277 ++++++++++++++++++++++++++++++
|
|
Documentation/kdbus/kdbus.name.xml | 711 +++++++++++++++++
|
|
Documentation/kdbus/kdbus.policy.xml | 406 ++++++++++
|
|
Documentation/kdbus/kdbus.pool.xml | 320 ++++++++
|
|
Documentation/kdbus/kdbus.xml | 1012 +++++++++++++++++++++++
|
|
Documentation/kdbus/stylesheet.xsl | 16 +
|
|
Makefile | 1 +
|
|
15 files changed, 7339 insertions(+), 1 deletion(-)
|
|
create mode 100644 Documentation/kdbus/Makefile
|
|
create mode 100644 Documentation/kdbus/kdbus.bus.xml
|
|
create mode 100644 Documentation/kdbus/kdbus.connection.xml
|
|
create mode 100644 Documentation/kdbus/kdbus.endpoint.xml
|
|
create mode 100644 Documentation/kdbus/kdbus.fs.xml
|
|
create mode 100644 Documentation/kdbus/kdbus.item.xml
|
|
create mode 100644 Documentation/kdbus/kdbus.match.xml
|
|
create mode 100644 Documentation/kdbus/kdbus.message.xml
|
|
create mode 100644 Documentation/kdbus/kdbus.name.xml
|
|
create mode 100644 Documentation/kdbus/kdbus.policy.xml
|
|
create mode 100644 Documentation/kdbus/kdbus.pool.xml
|
|
create mode 100644 Documentation/kdbus/kdbus.xml
|
|
create mode 100644 Documentation/kdbus/stylesheet.xsl
|
|
|
|
diff --git a/Documentation/Makefile b/Documentation/Makefile
|
|
index bc05482..e2127a7 100644
|
|
--- a/Documentation/Makefile
|
|
+++ b/Documentation/Makefile
|
|
@@ -1,4 +1,4 @@
|
|
subdir-y := accounting auxdisplay blackfin connector \
|
|
- filesystems filesystems ia64 laptops mic misc-devices \
|
|
+ filesystems filesystems ia64 kdbus laptops mic misc-devices \
|
|
networking pcmcia prctl ptp spi timers vDSO video4linux \
|
|
watchdog
|
|
diff --git a/Documentation/kdbus/Makefile b/Documentation/kdbus/Makefile
|
|
new file mode 100644
|
|
index 0000000..cd6b48e
|
|
--- /dev/null
|
|
+++ b/Documentation/kdbus/Makefile
|
|
@@ -0,0 +1,30 @@
|
|
+DOCS := \
|
|
+ kdbus.xml \
|
|
+ kdbus.bus.xml \
|
|
+ kdbus.connection.xml \
|
|
+ kdbus.endpoint.xml \
|
|
+ kdbus.fs.xml \
|
|
+ kdbus.item.xml \
|
|
+ kdbus.match.xml \
|
|
+ kdbus.message.xml \
|
|
+ kdbus.name.xml \
|
|
+ kdbus.policy.xml \
|
|
+ kdbus.pool.xml
|
|
+
|
|
+XMLFILES := $(addprefix $(obj)/,$(DOCS))
|
|
+MANFILES := $(patsubst %.xml, %.7, $(XMLFILES))
|
|
+HTMLFILES := $(patsubst %.xml, %.html, $(XMLFILES))
|
|
+
|
|
+XMLTO_ARGS := -m $(obj)/stylesheet.xsl
|
|
+
|
|
+%.7: %.xml
|
|
+ xmlto man $(XMLTO_ARGS) -o . $<
|
|
+
|
|
+%.html: %.xml
|
|
+ xmlto html-nochunks $(XMLTO_ARGS) -o . $<
|
|
+
|
|
+mandocs: $(MANFILES)
|
|
+
|
|
+htmldocs: $(HTMLFILES)
|
|
+
|
|
+clean-files := $(MANFILES) $(HTMLFILES)
|
|
diff --git a/Documentation/kdbus/kdbus.bus.xml b/Documentation/kdbus/kdbus.bus.xml
|
|
new file mode 100644
|
|
index 0000000..4d875e5
|
|
--- /dev/null
|
|
+++ b/Documentation/kdbus/kdbus.bus.xml
|
|
@@ -0,0 +1,360 @@
|
|
+<?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/Documentation/kdbus/kdbus.connection.xml b/Documentation/kdbus/kdbus.connection.xml
|
|
new file mode 100644
|
|
index 0000000..0985212
|
|
--- /dev/null
|
|
+++ b/Documentation/kdbus/kdbus.connection.xml
|
|
@@ -0,0 +1,1252 @@
|
|
+<?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.<ID>"</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>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>
|
|
+ Items to describe the connection details to be updated. 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/Documentation/kdbus/kdbus.endpoint.xml b/Documentation/kdbus/kdbus.endpoint.xml
|
|
new file mode 100644
|
|
index 0000000..76e325d
|
|
--- /dev/null
|
|
+++ b/Documentation/kdbus/kdbus.endpoint.xml
|
|
@@ -0,0 +1,436 @@
|
|
+<?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 update, 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>
|
|
+
|
|
+ <varlistentry>
|
|
+ <term><constant>EEXIST</constant></term>
|
|
+ <listitem><para>
|
|
+ An endpoint of that name already exists.
|
|
+ </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/Documentation/kdbus/kdbus.fs.xml b/Documentation/kdbus/kdbus.fs.xml
|
|
new file mode 100644
|
|
index 0000000..8c2a90e
|
|
--- /dev/null
|
|
+++ b/Documentation/kdbus/kdbus.fs.xml
|
|
@@ -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/Documentation/kdbus/kdbus.item.xml b/Documentation/kdbus/kdbus.item.xml
|
|
new file mode 100644
|
|
index 0000000..bfe4736
|
|
--- /dev/null
|
|
+++ b/Documentation/kdbus/kdbus.item.xml
|
|
@@ -0,0 +1,840 @@
|
|
+<?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 items.
|
|
+ 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.
|
|
+ file descriptor.
|
|
+ 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 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 is 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 is 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/Documentation/kdbus/kdbus.match.xml b/Documentation/kdbus/kdbus.match.xml
|
|
new file mode 100644
|
|
index 0000000..ef77b64
|
|
--- /dev/null
|
|
+++ b/Documentation/kdbus/kdbus.match.xml
|
|
@@ -0,0 +1,553 @@
|
|
+<?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
|
|
+ connections bloom mask (simply by &-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 are as large as
|
|
+ the bloom 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 struct of the struct 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 section below 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/Documentation/kdbus/kdbus.message.xml b/Documentation/kdbus/kdbus.message.xml
|
|
new file mode 100644
|
|
index 0000000..c25000d
|
|
--- /dev/null
|
|
+++ b/Documentation/kdbus/kdbus.message.xml
|
|
@@ -0,0 +1,1277 @@
|
|
+<?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 in <varname>offset_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 fields in this struct are described below.
|
|
+ The message referenced 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.reply_cookie</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
|
|
+ "Passing of Payload Data".
|
|
+ </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/Documentation/kdbus/kdbus.name.xml b/Documentation/kdbus/kdbus.name.xml
|
|
new file mode 100644
|
|
index 0000000..3f5f6a6
|
|
--- /dev/null
|
|
+++ b/Documentation/kdbus/kdbus.name.xml
|
|
@@ -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/Documentation/kdbus/kdbus.policy.xml b/Documentation/kdbus/kdbus.policy.xml
|
|
new file mode 100644
|
|
index 0000000..6732416
|
|
--- /dev/null
|
|
+++ b/Documentation/kdbus/kdbus.policy.xml
|
|
@@ -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/Documentation/kdbus/kdbus.pool.xml b/Documentation/kdbus/kdbus.pool.xml
|
|
new file mode 100644
|
|
index 0000000..05fd019
|
|
--- /dev/null
|
|
+++ b/Documentation/kdbus/kdbus.pool.xml
|
|
@@ -0,0 +1,320 @@
|
|
+<?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>
|
|
+ </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/Documentation/kdbus/kdbus.xml b/Documentation/kdbus/kdbus.xml
|
|
new file mode 100644
|
|
index 0000000..194abd2
|
|
--- /dev/null
|
|
+++ b/Documentation/kdbus/kdbus.xml
|
|
@@ -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/<bus name>/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/<bus>/<endpoint>
|
|
+ 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 are
|
|
+ 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 & b & c</constant>.
|
|
+ </para>
|
|
+ </listitem>
|
|
+
|
|
+ <listitem>
|
|
+ <para>
|
|
+ [2] When connecting to a bus (<constant>KDBUS_CMD_HELLO</constant>),
|
|
+ and <constant>~b & 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 & b & f</constant>.
|
|
+ </para>
|
|
+ </listitem>
|
|
+
|
|
+ <listitem>
|
|
+ <para>
|
|
+ [4] When querying creds of a bus owner, the creds returned are
|
|
+ <constant>a & e & 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 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/Documentation/kdbus/stylesheet.xsl b/Documentation/kdbus/stylesheet.xsl
|
|
new file mode 100644
|
|
index 0000000..52565ea
|
|
--- /dev/null
|
|
+++ b/Documentation/kdbus/stylesheet.xsl
|
|
@@ -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/Makefile b/Makefile
|
|
index 3cc6f44..e030a43 100644
|
|
--- a/Makefile
|
|
+++ b/Makefile
|
|
@@ -1344,6 +1344,7 @@ $(help-board-dirs): help-%:
|
|
%docs: scripts_basic FORCE
|
|
$(Q)$(MAKE) $(build)=scripts build_docproc
|
|
$(Q)$(MAKE) $(build)=Documentation/DocBook $@
|
|
+ $(Q)$(MAKE) $(build)=Documentation/kdbus $@
|
|
|
|
else # KBUILD_EXTMOD
|
|
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From a2193d4819fd8b7d159638985425a00efa8f96a9 Mon Sep 17 00:00:00 2001
|
|
From: Daniel Mack <daniel@zonque.org>
|
|
Date: Thu, 11 Sep 2014 18:38:06 +0200
|
|
Subject: [PATCH 02/78] kdbus: add uapi header file
|
|
|
|
This patch adds the header file which describes the low-level
|
|
transport protocol used by various ioctls. The header file is located
|
|
in include/uapi/linux/ as it is shared between kernel and userspace,
|
|
and it only contains data structure definitions, enums and defines
|
|
for constants.
|
|
|
|
The low-level kernel API of kdbus is exposed through ioctls, employed
|
|
on nodes exposed by kdbusfs. We've chosen a ioctl-based implementation
|
|
over syscalls for various reaons:
|
|
|
|
* The ioctls kdbus offers are completely specific to nodes exposed by
|
|
kdbusfs and can not be applied to any other file descriptor in a
|
|
system.
|
|
|
|
* The file descriptors derived from opening nodes in kdbusfs can only be
|
|
used for poll(), close() and the ioctls described in kdbus.h.
|
|
|
|
* Not all systems will make use of kdbus eventually, and we want to
|
|
make as many parts of the kernel optional at build time.
|
|
|
|
* We want to build the kdbus code as module, which is impossible to
|
|
do when implemented with syscalls.
|
|
|
|
* The ioctl dispatching logic does not show up in our performance
|
|
graphs; its overhead is negligible.
|
|
|
|
* For development, being able to build, load and unload a separate
|
|
module with a versioned name suffix is essential.
|
|
|
|
Signed-off-by: Daniel Mack <daniel@zonque.org>
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Djalal Harouni <tixxdz@opendz.org>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
include/uapi/linux/Kbuild | 1 +
|
|
include/uapi/linux/kdbus.h | 979 +++++++++++++++++++++++++++++++++++++++++++++
|
|
2 files changed, 980 insertions(+)
|
|
create mode 100644 include/uapi/linux/kdbus.h
|
|
|
|
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
|
|
index 1ff9942..67a4c60 100644
|
|
--- a/include/uapi/linux/Kbuild
|
|
+++ b/include/uapi/linux/Kbuild
|
|
@@ -216,6 +216,7 @@ header-y += ixjuser.h
|
|
header-y += jffs2.h
|
|
header-y += joystick.h
|
|
header-y += kcmp.h
|
|
+header-y += kdbus.h
|
|
header-y += kdev_t.h
|
|
header-y += kd.h
|
|
header-y += kernelcapi.h
|
|
diff --git a/include/uapi/linux/kdbus.h b/include/uapi/linux/kdbus.h
|
|
new file mode 100644
|
|
index 0000000..fc1d77d
|
|
--- /dev/null
|
|
+++ b/include/uapi/linux/kdbus.h
|
|
@@ -0,0 +1,979 @@
|
|
+/*
|
|
+ * 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_UAPI_H_
|
|
+#define _KDBUS_UAPI_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,
|
|
+
|
|
+ /* 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
|
|
+ * offset_reply in struct kdbus_msg 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
|
|
+ */
|
|
+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,
|
|
+};
|
|
+
|
|
+/**
|
|
+ * 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 /* _KDBUS_UAPI_H_ */
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From c5401facb4743fda472e6a583b6cf2712cf557f4 Mon Sep 17 00:00:00 2001
|
|
From: Daniel Mack <daniel@zonque.org>
|
|
Date: Thu, 11 Sep 2014 18:52:52 +0200
|
|
Subject: [PATCH 03/78] kdbus: add driver skeleton, ioctl entry points and
|
|
utility functions
|
|
|
|
Add the basic driver structure.
|
|
|
|
handle.c is the main ioctl command dispatcher that calls into other parts
|
|
of the driver.
|
|
|
|
main.c contains the code that creates the initial domain at startup, and
|
|
util.c has utility functions such as item iterators that are shared with
|
|
other files.
|
|
|
|
limits.h describes limits on things like maximum data structure sizes,
|
|
number of messages per users and suchlike. Some of the numbers currently
|
|
picked are rough ideas of what what might be sufficient and are probably
|
|
rather conservative.
|
|
|
|
Signed-off-by: Daniel Mack <daniel@zonque.org>
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Djalal Harouni <tixxdz@opendz.org>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
Documentation/ioctl/ioctl-number.txt | 1 +
|
|
ipc/kdbus/handle.c | 617 +++++++++++++++++++++++++++++++++++
|
|
ipc/kdbus/handle.h | 85 +++++
|
|
ipc/kdbus/limits.h | 64 ++++
|
|
ipc/kdbus/main.c | 125 +++++++
|
|
ipc/kdbus/util.c | 201 ++++++++++++
|
|
ipc/kdbus/util.h | 74 +++++
|
|
7 files changed, 1167 insertions(+)
|
|
create mode 100644 ipc/kdbus/handle.c
|
|
create mode 100644 ipc/kdbus/handle.h
|
|
create mode 100644 ipc/kdbus/limits.h
|
|
create mode 100644 ipc/kdbus/main.c
|
|
create mode 100644 ipc/kdbus/util.c
|
|
create mode 100644 ipc/kdbus/util.h
|
|
|
|
diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
|
|
index 611c522..1e166ad 100644
|
|
--- a/Documentation/ioctl/ioctl-number.txt
|
|
+++ b/Documentation/ioctl/ioctl-number.txt
|
|
@@ -292,6 +292,7 @@ Code Seq#(hex) Include File Comments
|
|
0x92 00-0F drivers/usb/mon/mon_bin.c
|
|
0x93 60-7F linux/auto_fs.h
|
|
0x94 all fs/btrfs/ioctl.h
|
|
+0x95 all uapi/linux/kdbus.h kdbus IPC driver
|
|
0x97 00-7F fs/ceph/ioctl.h Ceph file system
|
|
0x99 00-0F 537-Addinboard driver
|
|
<mailto:buk@buks.ipn.de>
|
|
diff --git a/ipc/kdbus/handle.c b/ipc/kdbus/handle.c
|
|
new file mode 100644
|
|
index 0000000..f72dbe5
|
|
--- /dev/null
|
|
+++ b/ipc/kdbus/handle.c
|
|
@@ -0,0 +1,617 @@
|
|
+/*
|
|
+ * 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/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;
|
|
+
|
|
+ for (i = 0; i < args->argc; ++i)
|
|
+ if (args->argv[i].mandatory && !args->argv[i].item)
|
|
+ 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
|
|
+ * @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.
|
|
+ *
|
|
+ * 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, void __user *argp,
|
|
+ size_t type_size, size_t items_offset, void **out)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ args->cmd = kdbus_memdup_user(argp, type_size, KDBUS_CMD_MAX_SIZE);
|
|
+ if (IS_ERR(args->cmd))
|
|
+ return PTR_ERR(args->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;
|
|
+
|
|
+ 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;
|
|
+
|
|
+ *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 (put_user(args->cmd->return_flags,
|
|
+ &args->user->return_flags))
|
|
+ ret = -EFAULT;
|
|
+ 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
|
|
+ * @rwlock: 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
|
|
+ * @privileged: Flag to mark a handle as privileged
|
|
+ */
|
|
+struct kdbus_handle {
|
|
+ struct rw_semaphore rwlock;
|
|
+
|
|
+ enum kdbus_handle_type type;
|
|
+ union {
|
|
+ struct kdbus_bus *bus_owner;
|
|
+ struct kdbus_ep *ep_owner;
|
|
+ struct kdbus_conn *conn;
|
|
+ };
|
|
+
|
|
+ bool privileged:1;
|
|
+};
|
|
+
|
|
+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;
|
|
+ }
|
|
+
|
|
+ init_rwsem(&handle->rwlock);
|
|
+ handle->type = KDBUS_HANDLE_NONE;
|
|
+
|
|
+ if (node->type == KDBUS_NODE_ENDPOINT) {
|
|
+ struct kdbus_ep *ep = kdbus_ep_from_node(node);
|
|
+ struct kdbus_bus *bus = ep->bus;
|
|
+
|
|
+ /*
|
|
+ * A connection is privileged if it is opened on an endpoint
|
|
+ * without custom policy and either:
|
|
+ * * the user has CAP_IPC_OWNER in the domain user namespace
|
|
+ * or
|
|
+ * * the callers euid matches the uid of the bus creator
|
|
+ */
|
|
+ if (!ep->user &&
|
|
+ (ns_capable(bus->domain->user_namespace, CAP_IPC_OWNER) ||
|
|
+ uid_eq(file->f_cred->euid, bus->node.uid)))
|
|
+ handle->privileged = true;
|
|
+ }
|
|
+
|
|
+ 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->type = KDBUS_HANDLE_BUS_OWNER;
|
|
+ handle->bus_owner = bus;
|
|
+ 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_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 (!handle->privileged) {
|
|
+ ret = -EPERM;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ ep = kdbus_cmd_ep_make(file_ep->bus, buf);
|
|
+ if (IS_ERR_OR_NULL(ep)) {
|
|
+ ret = PTR_ERR_OR_ZERO(ep);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ handle->type = KDBUS_HANDLE_EP_OWNER;
|
|
+ handle->ep_owner = ep;
|
|
+ break;
|
|
+
|
|
+ case KDBUS_CMD_HELLO:
|
|
+ conn = kdbus_cmd_hello(file_ep, handle->privileged, buf);
|
|
+ if (IS_ERR_OR_NULL(conn)) {
|
|
+ ret = PTR_ERR_OR_ZERO(conn);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ handle->type = KDBUS_HANDLE_CONNECTED;
|
|
+ handle->conn = conn;
|
|
+ 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:
|
|
+ /* bail out early if already typed */
|
|
+ if (handle->type != KDBUS_HANDLE_NONE)
|
|
+ break;
|
|
+
|
|
+ down_write(&handle->rwlock);
|
|
+ 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);
|
|
+ }
|
|
+ up_write(&handle->rwlock);
|
|
+ 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:
|
|
+ down_read(&handle->rwlock);
|
|
+ if (handle->type == KDBUS_HANDLE_EP_OWNER)
|
|
+ ret = kdbus_handle_ioctl_ep_owner(file, cmd, argp);
|
|
+ else if (handle->type == KDBUS_HANDLE_CONNECTED)
|
|
+ ret = kdbus_handle_ioctl_connected(file, cmd, argp);
|
|
+ up_read(&handle->rwlock);
|
|
+ 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;
|
|
+ unsigned int mask = POLLOUT | POLLWRNORM;
|
|
+ int ret;
|
|
+
|
|
+ /* Only a connected endpoint can read/write data */
|
|
+ down_read(&handle->rwlock);
|
|
+ if (handle->type != KDBUS_HANDLE_CONNECTED) {
|
|
+ up_read(&handle->rwlock);
|
|
+ return POLLERR | POLLHUP;
|
|
+ }
|
|
+ up_read(&handle->rwlock);
|
|
+
|
|
+ ret = kdbus_conn_acquire(handle->conn);
|
|
+ if (ret < 0)
|
|
+ return POLLERR | POLLHUP;
|
|
+
|
|
+ poll_wait(file, &handle->conn->wait, wait);
|
|
+
|
|
+ if (!list_empty(&handle->conn->queue.msg_list) ||
|
|
+ atomic_read(&handle->conn->lost_count) > 0)
|
|
+ mask |= POLLIN | POLLRDNORM;
|
|
+
|
|
+ kdbus_conn_release(handle->conn);
|
|
+
|
|
+ return mask;
|
|
+}
|
|
+
|
|
+static int kdbus_handle_mmap(struct file *file, struct vm_area_struct *vma)
|
|
+{
|
|
+ struct kdbus_handle *handle = file->private_data;
|
|
+ int ret = -EBADFD;
|
|
+
|
|
+ if (down_read_trylock(&handle->rwlock)) {
|
|
+ if (handle->type == KDBUS_HANDLE_CONNECTED)
|
|
+ ret = kdbus_pool_mmap(handle->conn->pool, vma);
|
|
+ up_read(&handle->rwlock);
|
|
+ }
|
|
+ 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
|
|
index 0000000..93a372d
|
|
--- /dev/null
|
|
+++ b/ipc/kdbus/handle.h
|
|
@@ -0,0 +1,85 @@
|
|
+/*
|
|
+ * 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
|
|
+ * @items: points to item array in @cmd
|
|
+ * @items_size: size of @items in bytes
|
|
+ *
|
|
+ * 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.
|
|
+ */
|
|
+struct kdbus_args {
|
|
+ u64 allowed_flags;
|
|
+ size_t argc;
|
|
+ struct kdbus_arg *argv;
|
|
+
|
|
+ struct kdbus_cmd __user *user;
|
|
+ struct kdbus_cmd *cmd;
|
|
+
|
|
+ struct kdbus_item *items;
|
|
+ size_t items_size;
|
|
+};
|
|
+
|
|
+int __kdbus_args_parse(struct kdbus_args *args, 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), (_argp), sizeof(**(_v)), \
|
|
+ offsetof(typeof(**(_v)), items), \
|
|
+ (void **)(_v)); \
|
|
+ })
|
|
+
|
|
+#endif
|
|
diff --git a/ipc/kdbus/limits.h b/ipc/kdbus/limits.h
|
|
new file mode 100644
|
|
index 0000000..6450f58
|
|
--- /dev/null
|
|
+++ b/ipc/kdbus/limits.h
|
|
@@ -0,0 +1,64 @@
|
|
+/*
|
|
+ * 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 message items */
|
|
+#define KDBUS_MSG_MAX_ITEMS 128
|
|
+
|
|
+/* 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 128
|
|
+
|
|
+/* 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
|
|
index 0000000..785f529
|
|
--- /dev/null
|
|
+++ b/ipc/kdbus/main.c
|
|
@@ -0,0 +1,125 @@
|
|
+/*
|
|
+ * 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 <linux/moduleparam.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;
|
|
+
|
|
+/* global module option to apply a mask to exported metadata */
|
|
+unsigned long long kdbus_meta_attach_mask = KDBUS_ATTACH_TIMESTAMP |
|
|
+ KDBUS_ATTACH_CREDS |
|
|
+ KDBUS_ATTACH_PIDS |
|
|
+ KDBUS_ATTACH_AUXGROUPS |
|
|
+ KDBUS_ATTACH_NAMES |
|
|
+ KDBUS_ATTACH_SECLABEL |
|
|
+ KDBUS_ATTACH_CONN_DESCRIPTION;
|
|
+MODULE_PARM_DESC(attach_flags_mask, "Attach-flags mask for exported metadata");
|
|
+module_param_named(attach_flags_mask, kdbus_meta_attach_mask, ullong, 0644);
|
|
+
|
|
+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);
|
|
+}
|
|
+
|
|
+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/util.c b/ipc/kdbus/util.c
|
|
new file mode 100644
|
|
index 0000000..eaa806a
|
|
--- /dev/null
|
|
+++ b/ipc/kdbus/util.c
|
|
@@ -0,0 +1,201 @@
|
|
+/*
|
|
+ * 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_memdup_user() - copy dynamically sized object from user-space
|
|
+ * @user_ptr: user-provided source buffer
|
|
+ * @sz_min: minimum object size
|
|
+ * @sz_max: maximum object size
|
|
+ *
|
|
+ * This copies a dynamically sized object from user-space into kernel-space. We
|
|
+ * require the object to have a 64bit size field at offset 0. We read it out
|
|
+ * first, allocate a suitably sized buffer and then copy all data.
|
|
+ *
|
|
+ * The @sz_min and @sz_max parameters define possible min and max object sizes
|
|
+ * so user-space cannot trigger un-bound kernel-space allocations.
|
|
+ *
|
|
+ * The same alignment-restrictions as described in kdbus_copy_from_user() apply.
|
|
+ *
|
|
+ * Return: pointer to dynamically allocated copy, or ERR_PTR() on failure.
|
|
+ */
|
|
+void *kdbus_memdup_user(void __user *user_ptr, size_t sz_min, size_t sz_max)
|
|
+{
|
|
+ void *ptr;
|
|
+ u64 size;
|
|
+ int ret;
|
|
+
|
|
+ ret = kdbus_copy_from_user(&size, user_ptr, sizeof(size));
|
|
+ if (ret < 0)
|
|
+ return ERR_PTR(ret);
|
|
+
|
|
+ if (size < sz_min)
|
|
+ return ERR_PTR(-EINVAL);
|
|
+
|
|
+ if (size > sz_max)
|
|
+ return ERR_PTR(-EMSGSIZE);
|
|
+
|
|
+ ptr = memdup_user(user_ptr, size);
|
|
+ if (IS_ERR(ptr))
|
|
+ return ptr;
|
|
+
|
|
+ if (*(u64 *)ptr != size) {
|
|
+ kfree(ptr);
|
|
+ return ERR_PTR(-EINVAL);
|
|
+ }
|
|
+
|
|
+ return ptr;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * 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
|
|
index 0000000..9caadb3
|
|
--- /dev/null
|
|
+++ b/ipc/kdbus/util.h
|
|
@@ -0,0 +1,74 @@
|
|
+/*
|
|
+ * 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 "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, sizeof(((_t *)0)->_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);
|
|
+void *kdbus_memdup_user(void __user *user_ptr, size_t sz_min, size_t sz_max);
|
|
+
|
|
+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
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 50f8453db4717c18a49841b630d35a7e6b9446d2 Mon Sep 17 00:00:00 2001
|
|
From: Daniel Mack <daniel@zonque.org>
|
|
Date: Thu, 11 Sep 2014 18:56:41 +0200
|
|
Subject: [PATCH 04/78] kdbus: add connection pool implementation
|
|
|
|
A pool for data received from the kernel is installed for every
|
|
connection of the bus, and it is used to copy data from the kernel to
|
|
userspace clients, for messages and other information.
|
|
|
|
It is accessed when one of the following ioctls is issued:
|
|
|
|
* KDBUS_CMD_MSG_RECV, to receive a message
|
|
* KDBUS_CMD_NAME_LIST, to dump the name registry
|
|
* KDBUS_CMD_CONN_INFO, to retrieve information on a connection
|
|
|
|
The offsets returned by either one of the aforementioned ioctls
|
|
describe offsets inside the pool. Internally, the pool is organized in
|
|
slices, that are dynamically allocated on demand. The overall size of
|
|
the pool is chosen by the connection when it connects to the bus with
|
|
KDBUS_CMD_HELLO.
|
|
|
|
In order to make the slice available for subsequent calls,
|
|
KDBUS_CMD_FREE has to be called on the offset.
|
|
|
|
To access the memory, the caller is expected to mmap() it to its task.
|
|
|
|
Signed-off-by: Daniel Mack <daniel@zonque.org>
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Djalal Harouni <tixxdz@opendz.org>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
ipc/kdbus/pool.c | 728 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
ipc/kdbus/pool.h | 46 ++++
|
|
2 files changed, 774 insertions(+)
|
|
create mode 100644 ipc/kdbus/pool.c
|
|
create mode 100644 ipc/kdbus/pool.h
|
|
|
|
diff --git a/ipc/kdbus/pool.c b/ipc/kdbus/pool.c
|
|
new file mode 100644
|
|
index 0000000..139bb77
|
|
--- /dev/null
|
|
+++ b/ipc/kdbus/pool.c
|
|
@@ -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 direcly 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 = f_src->f_op->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
|
|
index 0000000..a903821
|
|
--- /dev/null
|
|
+++ b/ipc/kdbus/pool.h
|
|
@@ -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
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From e3c51efcfdb0fdc8563ce32889939ca9e987c299 Mon Sep 17 00:00:00 2001
|
|
From: Daniel Mack <daniel@zonque.org>
|
|
Date: Thu, 11 Sep 2014 18:57:24 +0200
|
|
Subject: [PATCH 05/78] kdbus: add connection, queue handling and message
|
|
validation code
|
|
|
|
This patch adds code to create and destroy connections, to validate
|
|
incoming messages and to maintain the queue of messages that are
|
|
associated with a connection.
|
|
|
|
Note that connection and queue have a 1:1 relation, the code is only
|
|
split in two parts for cleaner separation and better readability.
|
|
|
|
Signed-off-by: Daniel Mack <daniel@zonque.org>
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Djalal Harouni <tixxdz@opendz.org>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
ipc/kdbus/connection.c | 2215 ++++++++++++++++++++++++++++++++++++++++++++++++
|
|
ipc/kdbus/connection.h | 257 ++++++
|
|
ipc/kdbus/item.c | 339 ++++++++
|
|
ipc/kdbus/item.h | 64 ++
|
|
ipc/kdbus/message.c | 616 ++++++++++++++
|
|
ipc/kdbus/message.h | 133 +++
|
|
ipc/kdbus/queue.c | 678 +++++++++++++++
|
|
ipc/kdbus/queue.h | 92 ++
|
|
ipc/kdbus/reply.c | 259 ++++++
|
|
ipc/kdbus/reply.h | 68 ++
|
|
ipc/kdbus/util.h | 2 +-
|
|
11 files changed, 4722 insertions(+), 1 deletion(-)
|
|
create mode 100644 ipc/kdbus/connection.c
|
|
create mode 100644 ipc/kdbus/connection.h
|
|
create mode 100644 ipc/kdbus/item.c
|
|
create mode 100644 ipc/kdbus/item.h
|
|
create mode 100644 ipc/kdbus/message.c
|
|
create mode 100644 ipc/kdbus/message.h
|
|
create mode 100644 ipc/kdbus/queue.c
|
|
create mode 100644 ipc/kdbus/queue.h
|
|
create mode 100644 ipc/kdbus/reply.c
|
|
create mode 100644 ipc/kdbus/reply.h
|
|
|
|
diff --git a/ipc/kdbus/connection.c b/ipc/kdbus/connection.c
|
|
new file mode 100644
|
|
index 0000000..e554f1a
|
|
--- /dev/null
|
|
+++ b/ipc/kdbus/connection.c
|
|
@@ -0,0 +1,2215 @@
|
|
+/*
|
|
+ * 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)
|
|
+
|
|
+static struct kdbus_conn *kdbus_conn_new(struct kdbus_ep *ep, bool privileged,
|
|
+ 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;
|
|
+ struct kvec kvec;
|
|
+ int ret;
|
|
+
|
|
+ struct {
|
|
+ u64 size;
|
|
+ u64 type;
|
|
+ struct kdbus_bloom_parameter bloom;
|
|
+ } bloom_item;
|
|
+
|
|
+ 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 (!privileged && (is_activator || is_policy_holder || is_monitor))
|
|
+ return ERR_PTR(-EPERM);
|
|
+ if ((creds || pids || seclabel) && !privileged)
|
|
+ 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);
|
|
+
|
|
+ /* The attach flags must always satisfy the bus requirements. */
|
|
+ if (bus->attach_flags_req & ~attach_flags_send)
|
|
+ return ERR_PTR(-ECONNREFUSED);
|
|
+
|
|
+ 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->names_queue_list);
|
|
+ INIT_LIST_HEAD(&conn->reply_list);
|
|
+ atomic_set(&conn->name_count, 0);
|
|
+ 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_current_cred();
|
|
+ init_waitqueue_head(&conn->wait);
|
|
+ kdbus_queue_init(&conn->queue);
|
|
+ conn->privileged = privileged;
|
|
+ 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));
|
|
+
|
|
+ conn->meta = kdbus_meta_proc_new();
|
|
+ if (IS_ERR(conn->meta)) {
|
|
+ ret = PTR_ERR(conn->meta);
|
|
+ conn->meta = NULL;
|
|
+ goto exit_unref;
|
|
+ }
|
|
+
|
|
+ /* privileged processes can impersonate somebody else */
|
|
+ if (creds || pids || seclabel) {
|
|
+ ret = kdbus_meta_proc_fake(conn->meta, creds, pids, seclabel);
|
|
+ if (ret < 0)
|
|
+ goto exit_unref;
|
|
+
|
|
+ conn->faked_meta = true;
|
|
+ } else {
|
|
+ ret = kdbus_meta_proc_collect(conn->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;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * 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).
|
|
+ */
|
|
+ if (ep->user) {
|
|
+ conn->user = kdbus_user_ref(ep->user);
|
|
+ } else {
|
|
+ conn->user = kdbus_user_lookup(ep->bus->domain, current_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->names_queue_list));
|
|
+ WARN_ON(!list_empty(&conn->reply_list));
|
|
+
|
|
+ if (conn->user) {
|
|
+ atomic_dec(&conn->user->connections);
|
|
+ kdbus_user_unref(conn->user);
|
|
+ }
|
|
+
|
|
+ kdbus_meta_proc_unref(conn->meta);
|
|
+ kdbus_match_db_free(conn->match_db);
|
|
+ kdbus_pool_free(conn->pool);
|
|
+ kdbus_ep_unref(conn->ep);
|
|
+ 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(conn->ep->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) {
|
|
+ if (r->sync) {
|
|
+ kdbus_sync_reply_wakeup(r, -EPIPE);
|
|
+ kdbus_reply_unlink(r);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ /* 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_entry *e;
|
|
+
|
|
+ lockdep_assert_held(&conn->ep->bus->name_registry->rwlock);
|
|
+
|
|
+ list_for_each_entry(e, &conn->names_list, conn_entry)
|
|
+ if (strcmp(e->name, name) == 0)
|
|
+ return true;
|
|
+
|
|
+ return false;
|
|
+}
|
|
+
|
|
+struct kdbus_quota {
|
|
+ uint32_t memory;
|
|
+ uint16_t msgs;
|
|
+ uint8_t 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 allocations
|
|
+ * 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 slice 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 there 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_dst,
|
|
+ const struct kdbus_kmsg *kmsg,
|
|
+ struct kdbus_user *user)
|
|
+{
|
|
+ struct kdbus_queue_entry *entry;
|
|
+
|
|
+ /* 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) &&
|
|
+ kmsg->res && kmsg->res->fds_count > 0)
|
|
+ return ERR_PTR(-ECOMM);
|
|
+
|
|
+ entry = kdbus_queue_entry_new(conn_dst, kmsg, user);
|
|
+ if (IS_ERR(entry))
|
|
+ return entry;
|
|
+
|
|
+ return entry;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * 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,
|
|
+ const struct kdbus_kmsg *kmsg,
|
|
+ struct kdbus_reply *reply_wake)
|
|
+{
|
|
+ struct kdbus_queue_entry *entry;
|
|
+ int remote_ret;
|
|
+ int 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(conn_dst, kmsg,
|
|
+ reply_wake->reply_src->user);
|
|
+ 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);
|
|
+ kdbus_reply_unlink(reply_wake);
|
|
+ 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
|
|
+ * @kmsg: The kmsg to queue
|
|
+ * @reply: The reply tracker to attach to the queue entry
|
|
+ *
|
|
+ * Return: 0 on success. negative error otherwise.
|
|
+ */
|
|
+int kdbus_conn_entry_insert(struct kdbus_conn *conn_src,
|
|
+ struct kdbus_conn *conn_dst,
|
|
+ const struct kdbus_kmsg *kmsg,
|
|
+ struct kdbus_reply *reply)
|
|
+{
|
|
+ struct kdbus_queue_entry *entry;
|
|
+ int ret;
|
|
+
|
|
+ kdbus_conn_lock2(conn_src, conn_dst);
|
|
+
|
|
+ entry = kdbus_conn_entry_make(conn_dst, kmsg,
|
|
+ conn_src ? conn_src->user : NULL);
|
|
+ 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);
|
|
+ }
|
|
+
|
|
+ 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_kmsg *kmsg,
|
|
+ struct kdbus_name_entry **out_name,
|
|
+ struct kdbus_conn **out_dst)
|
|
+{
|
|
+ struct kdbus_msg_resources *res = kmsg->res;
|
|
+ struct kdbus_name_entry *name = NULL;
|
|
+ struct kdbus_conn *dst = NULL;
|
|
+ struct kdbus_msg *msg = &kmsg->msg;
|
|
+ int ret;
|
|
+
|
|
+ if (WARN_ON(!res))
|
|
+ return -EINVAL;
|
|
+
|
|
+ lockdep_assert_held(&bus->name_registry->rwlock);
|
|
+
|
|
+ if (!res->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,
|
|
+ res->dst_name);
|
|
+ if (!name)
|
|
+ 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 != name->conn->id)
|
|
+ return -EREMCHG;
|
|
+
|
|
+ if (!name->conn && name->activator)
|
|
+ dst = kdbus_conn_ref(name->activator);
|
|
+ else
|
|
+ dst = kdbus_conn_ref(name->conn);
|
|
+
|
|
+ if ((msg->flags & KDBUS_MSG_NO_AUTO_START) &&
|
|
+ kdbus_conn_is_activator(dst)) {
|
|
+ ret = -EADDRNOTAVAIL;
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Record the sequence number of the registered name; it will
|
|
+ * be passed on to the queue, in case messages addressed to a
|
|
+ * name need to be moved from or to an activator.
|
|
+ */
|
|
+ kmsg->dst_name_id = name->name_id;
|
|
+ }
|
|
+
|
|
+ *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_kmsg *kmsg)
|
|
+{
|
|
+ struct kdbus_name_entry *name = NULL;
|
|
+ struct kdbus_reply *reply, *wake = NULL;
|
|
+ struct kdbus_conn *dst = NULL;
|
|
+ struct kdbus_bus *bus = src->ep->bus;
|
|
+ u64 attach;
|
|
+ int ret;
|
|
+
|
|
+ if (WARN_ON(kmsg->msg.dst_id == KDBUS_DST_ID_BROADCAST) ||
|
|
+ WARN_ON(kmsg->msg.flags & KDBUS_MSG_EXPECT_REPLY) ||
|
|
+ WARN_ON(kmsg->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, kmsg, &name, &dst);
|
|
+ if (ret < 0)
|
|
+ goto exit;
|
|
+
|
|
+ mutex_lock(&dst->lock);
|
|
+ reply = kdbus_reply_find(src, dst, kmsg->msg.cookie_reply);
|
|
+ if (reply) {
|
|
+ if (reply->sync)
|
|
+ wake = kdbus_reply_ref(reply);
|
|
+ kdbus_reply_unlink(reply);
|
|
+ }
|
|
+ mutex_unlock(&dst->lock);
|
|
+
|
|
+ if (!reply) {
|
|
+ ret = -EPERM;
|
|
+ goto exit;
|
|
+ }
|
|
+
|
|
+ /* attach metadata */
|
|
+
|
|
+ attach = kdbus_meta_calc_attach_flags(src, dst);
|
|
+
|
|
+ if (!src->faked_meta) {
|
|
+ ret = kdbus_meta_proc_collect(kmsg->proc_meta, attach);
|
|
+ if (ret < 0)
|
|
+ goto exit;
|
|
+ }
|
|
+
|
|
+ ret = kdbus_meta_conn_collect(kmsg->conn_meta, kmsg, src, attach);
|
|
+ if (ret < 0)
|
|
+ goto exit;
|
|
+
|
|
+ /* send message */
|
|
+
|
|
+ kdbus_bus_eavesdrop(bus, src, kmsg);
|
|
+
|
|
+ if (wake)
|
|
+ ret = kdbus_conn_entry_sync_attach(dst, kmsg, wake);
|
|
+ else
|
|
+ ret = kdbus_conn_entry_insert(src, dst, kmsg, NULL);
|
|
+
|
|
+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_kmsg *kmsg,
|
|
+ ktime_t exp)
|
|
+{
|
|
+ struct kdbus_name_entry *name = NULL;
|
|
+ struct kdbus_reply *wait = NULL;
|
|
+ struct kdbus_conn *dst = NULL;
|
|
+ struct kdbus_bus *bus = src->ep->bus;
|
|
+ u64 attach;
|
|
+ int ret;
|
|
+
|
|
+ if (WARN_ON(kmsg->msg.dst_id == KDBUS_DST_ID_BROADCAST) ||
|
|
+ WARN_ON(kmsg->msg.flags & KDBUS_MSG_SIGNAL) ||
|
|
+ WARN_ON(!(kmsg->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, kmsg->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, kmsg, &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, &kmsg->msg, name, true);
|
|
+ if (IS_ERR(wait)) {
|
|
+ ret = PTR_ERR(wait);
|
|
+ wait = NULL;
|
|
+ goto exit;
|
|
+ }
|
|
+
|
|
+ /* attach metadata */
|
|
+
|
|
+ attach = kdbus_meta_calc_attach_flags(src, dst);
|
|
+
|
|
+ if (!src->faked_meta) {
|
|
+ ret = kdbus_meta_proc_collect(kmsg->proc_meta, attach);
|
|
+ if (ret < 0)
|
|
+ goto exit;
|
|
+ }
|
|
+
|
|
+ ret = kdbus_meta_conn_collect(kmsg->conn_meta, kmsg, src, attach);
|
|
+ if (ret < 0)
|
|
+ goto exit;
|
|
+
|
|
+ /* send message */
|
|
+
|
|
+ kdbus_bus_eavesdrop(bus, src, kmsg);
|
|
+
|
|
+ ret = kdbus_conn_entry_insert(src, dst, kmsg, wait);
|
|
+ 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_kmsg *kmsg)
|
|
+{
|
|
+ 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 = (kmsg->msg.flags & KDBUS_MSG_SIGNAL);
|
|
+ u64 attach;
|
|
+ int ret = 0;
|
|
+
|
|
+ if (WARN_ON(kmsg->msg.dst_id == KDBUS_DST_ID_BROADCAST) ||
|
|
+ WARN_ON(!(kmsg->msg.flags & KDBUS_MSG_EXPECT_REPLY) &&
|
|
+ kmsg->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, kmsg, &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, kmsg);
|
|
+
|
|
+ /* drop silently if peer is not interested or not privileged */
|
|
+ if (!kdbus_match_db_match_kmsg(dst->match_db, src, kmsg) ||
|
|
+ !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 (kmsg->msg.flags & KDBUS_MSG_EXPECT_REPLY) {
|
|
+ wait = kdbus_reply_new(dst, src, &kmsg->msg, name, false);
|
|
+ if (IS_ERR(wait)) {
|
|
+ ret = PTR_ERR(wait);
|
|
+ wait = NULL;
|
|
+ goto exit;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* attach metadata */
|
|
+
|
|
+ attach = kdbus_meta_calc_attach_flags(src, dst);
|
|
+
|
|
+ if (!src->faked_meta) {
|
|
+ ret = kdbus_meta_proc_collect(kmsg->proc_meta, attach);
|
|
+ if (ret < 0 && !is_signal)
|
|
+ goto exit;
|
|
+ }
|
|
+
|
|
+ ret = kdbus_meta_conn_collect(kmsg->conn_meta, kmsg, src, attach);
|
|
+ if (ret < 0 && !is_signal)
|
|
+ goto exit;
|
|
+
|
|
+ /* send message */
|
|
+
|
|
+ if (!is_signal)
|
|
+ kdbus_bus_eavesdrop(bus, src, kmsg);
|
|
+
|
|
+ ret = kdbus_conn_entry_insert(src, dst, kmsg, wait);
|
|
+ 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->msg_res && e->msg_res->fds_count > 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_entry *ne;
|
|
+ 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(ne, &whom->names_list, conn_entry) {
|
|
+ res = kdbus_policy_query_unlocked(db, conn_creds ? : conn->cred,
|
|
+ ne->name,
|
|
+ kdbus_strhash(ne->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;
|
|
+
|
|
+ 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->privileged)
|
|
+ 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)
|
|
+{
|
|
+ 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->privileged)
|
|
+ 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;
|
|
+
|
|
+ /*
|
|
+ * 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)
|
|
+{
|
|
+ /*
|
|
+ * 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
|
|
+ * @kmsg: The message carrying the notification
|
|
+ *
|
|
+ * This checks whether @conn is allowed to see the kernel notification @kmsg.
|
|
+ *
|
|
+ * 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_kmsg *kmsg)
|
|
+{
|
|
+ if (WARN_ON(kmsg->msg.src_id != KDBUS_SRC_ID_KERNEL))
|
|
+ return false;
|
|
+
|
|
+ /*
|
|
+ * 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}: As new peers cannot have names, and all
|
|
+ * names are dropped before a peer is removed, those notifications
|
|
+ * cannot be seen on custom endpoints. Thus, we only pass them
|
|
+ * through on default endpoints.
|
|
+ */
|
|
+
|
|
+ switch (kmsg->notify_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,
|
|
+ kmsg->notify_name);
|
|
+
|
|
+ case KDBUS_ITEM_ID_ADD:
|
|
+ case KDBUS_ITEM_ID_REMOVE:
|
|
+ return !conn->ep->user;
|
|
+
|
|
+ default:
|
|
+ WARN(1, "Invalid type for notification broadcast: %llu\n",
|
|
+ (unsigned long long)kmsg->notify_type);
|
|
+ return false;
|
|
+ }
|
|
+}
|
|
+
|
|
+/**
|
|
+ * kdbus_cmd_hello() - handle KDBUS_CMD_HELLO
|
|
+ * @ep: Endpoint to operate on
|
|
+ * @privileged: Whether the caller is privileged
|
|
+ * @argp: Command payload
|
|
+ *
|
|
+ * Return: Newly created connection on success, ERR_PTR on failure.
|
|
+ */
|
|
+struct kdbus_conn *kdbus_cmd_hello(struct kdbus_ep *ep, bool privileged,
|
|
+ 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, privileged, 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_conn *owner_conn = NULL;
|
|
+ struct kdbus_info info = {};
|
|
+ struct kdbus_cmd_info *cmd;
|
|
+ struct kdbus_bus *bus = conn->ep->bus;
|
|
+ struct kvec kvec;
|
|
+ size_t meta_size;
|
|
+ const char *name;
|
|
+ u64 attach_flags;
|
|
+ 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 || !entry->conn ||
|
|
+ !kdbus_conn_policy_see_name(conn, current_cred(), name) ||
|
|
+ (cmd->id != 0 && entry->conn->id != cmd->id)) {
|
|
+ /* pretend a name doesn't exist if you cannot see it */
|
|
+ ret = -ESRCH;
|
|
+ goto exit;
|
|
+ }
|
|
+
|
|
+ owner_conn = kdbus_conn_ref(entry->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;
|
|
+ }
|
|
+
|
|
+ info.id = owner_conn->id;
|
|
+ info.flags = owner_conn->flags;
|
|
+ kdbus_kvec_set(&kvec, &info, sizeof(info), &info.size);
|
|
+
|
|
+ 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, NULL, owner_conn,
|
|
+ attach_flags);
|
|
+ if (ret < 0)
|
|
+ goto exit;
|
|
+
|
|
+ ret = kdbus_meta_export_prepare(owner_conn->meta, conn_meta,
|
|
+ &attach_flags, &meta_size);
|
|
+ if (ret < 0)
|
|
+ goto exit;
|
|
+
|
|
+ slice = kdbus_pool_slice_alloc(conn->pool,
|
|
+ info.size + meta_size, false);
|
|
+ if (IS_ERR(slice)) {
|
|
+ ret = PTR_ERR(slice);
|
|
+ slice = NULL;
|
|
+ goto exit;
|
|
+ }
|
|
+
|
|
+ ret = kdbus_meta_export(owner_conn->meta, conn_meta, attach_flags,
|
|
+ slice, sizeof(info), &meta_size);
|
|
+ if (ret < 0)
|
|
+ goto exit;
|
|
+
|
|
+ info.size += meta_size;
|
|
+
|
|
+ ret = kdbus_pool_slice_copy_kvec(slice, 0, &kvec, 1, sizeof(info));
|
|
+ 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);
|
|
+ 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_bus *bus = conn->ep->bus;
|
|
+ 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 (bus->attach_flags_req & ~attach_send) {
|
|
+ ret = -EINVAL;
|
|
+ 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_kmsg *kmsg = NULL;
|
|
+ struct file *cancel_fd = NULL;
|
|
+ int ret;
|
|
+
|
|
+ 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),
|
|
+ };
|
|
+
|
|
+ if (!kdbus_conn_is_ordinary(conn))
|
|
+ return -EOPNOTSUPP;
|
|
+
|
|
+ ret = kdbus_args_parse(&args, argp, &cmd);
|
|
+ if (ret != 0)
|
|
+ return ret;
|
|
+
|
|
+ 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 (IS_ERR(cancel_fd)) {
|
|
+ ret = PTR_ERR(cancel_fd);
|
|
+ cancel_fd = NULL;
|
|
+ goto exit;
|
|
+ }
|
|
+
|
|
+ if (!cancel_fd->f_op->poll) {
|
|
+ ret = -EINVAL;
|
|
+ goto exit;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ kmsg = kdbus_kmsg_new_from_cmd(conn, cmd);
|
|
+ if (IS_ERR(kmsg)) {
|
|
+ ret = PTR_ERR(kmsg);
|
|
+ kmsg = NULL;
|
|
+ goto exit;
|
|
+ }
|
|
+
|
|
+ if (kmsg->msg.dst_id == KDBUS_DST_ID_BROADCAST) {
|
|
+ down_read(&conn->ep->bus->name_registry->rwlock);
|
|
+ kdbus_bus_broadcast(conn->ep->bus, conn, kmsg);
|
|
+ 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(kmsg->msg.timeout_ns);
|
|
+ r = kdbus_conn_call(conn, kmsg, 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 ((kmsg->msg.flags & KDBUS_MSG_EXPECT_REPLY) ||
|
|
+ kmsg->msg.cookie_reply == 0) {
|
|
+ ret = kdbus_conn_unicast(conn, kmsg);
|
|
+ if (ret < 0)
|
|
+ goto exit;
|
|
+ } else {
|
|
+ ret = kdbus_conn_reply(conn, kmsg);
|
|
+ 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_kmsg_free(kmsg);
|
|
+ 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
|
|
index 0000000..d1ffe90
|
|
--- /dev/null
|
|
+++ b/ipc/kdbus/connection.h
|
|
@@ -0,0 +1,257 @@
|
|
+/*
|
|
+ * 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_quota;
|
|
+struct kdbus_kmsg;
|
|
+
|
|
+/**
|
|
+ * 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: Active connection creator's metadata/credentials,
|
|
+ * either from the handle or from HELLO
|
|
+ * @pool: The user's buffer to receive messages
|
|
+ * @user: Owner of the connection
|
|
+ * @cred: The credentials of the connection at creation time
|
|
+ * @name_count: Number of owned well-known names
|
|
+ * @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
|
|
+ * @activator_of: Well-known name entry this connection acts as an
|
|
+ * @names_list: List of well-known names
|
|
+ * @names_queue_list: Well-known names this connection waits for
|
|
+ * @privileged: Whether this connection is privileged on the bus
|
|
+ * @faked_meta: Whether the metadata was faked on HELLO
|
|
+ */
|
|
+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;
|
|
+ struct kdbus_pool *pool;
|
|
+ struct kdbus_user *user;
|
|
+ const struct cred *cred;
|
|
+ atomic_t name_count;
|
|
+ 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 kdbus_name_entry *activator_of;
|
|
+ struct list_head names_list;
|
|
+ struct list_head names_queue_list;
|
|
+
|
|
+ bool privileged:1;
|
|
+ bool faked_meta: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,
|
|
+ const struct kdbus_kmsg *kmsg,
|
|
+ struct kdbus_reply *reply);
|
|
+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_kmsg *kmsg);
|
|
+
|
|
+/* command dispatcher */
|
|
+struct kdbus_conn *kdbus_cmd_hello(struct kdbus_ep *ep, bool privileged,
|
|
+ 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/item.c b/ipc/kdbus/item.c
|
|
new file mode 100644
|
|
index 0000000..745ad54
|
|
--- /dev/null
|
|
+++ b/ipc/kdbus/item.c
|
|
@@ -0,0 +1,339 @@
|
|
+/*
|
|
+ * 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:
|
|
+ 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_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:
|
|
+ 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;
|
|
+}
|
|
+
|
|
+static struct kdbus_item *kdbus_items_get(const struct kdbus_item *items,
|
|
+ size_t items_size,
|
|
+ unsigned int item_type)
|
|
+{
|
|
+ const struct kdbus_item *iter, *found = NULL;
|
|
+
|
|
+ KDBUS_ITEMS_FOREACH(iter, items, items_size) {
|
|
+ if (iter->type == item_type) {
|
|
+ if (found)
|
|
+ return ERR_PTR(-EEXIST);
|
|
+ found = iter;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return (struct kdbus_item *)found ? : ERR_PTR(-EBADMSG);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * kdbus_items_get_str() - get string from a list of items
|
|
+ * @items: The items to walk
|
|
+ * @items_size: The size of all items
|
|
+ * @item_type: The item type to look for
|
|
+ *
|
|
+ * This function walks a list of items and searches for items of type
|
|
+ * @item_type. If it finds exactly one such item, @str_ret will be set to
|
|
+ * the .str member of the item.
|
|
+ *
|
|
+ * Return: the string, if the item was found exactly once, ERR_PTR(-EEXIST)
|
|
+ * if the item was found more than once, and ERR_PTR(-EBADMSG) if there was
|
|
+ * no item of the given type.
|
|
+ */
|
|
+const char *kdbus_items_get_str(const struct kdbus_item *items,
|
|
+ size_t items_size,
|
|
+ unsigned int item_type)
|
|
+{
|
|
+ const struct kdbus_item *item;
|
|
+
|
|
+ item = kdbus_items_get(items, items_size, item_type);
|
|
+ return IS_ERR(item) ? ERR_CAST(item) : item->str;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * 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
|
|
index 0000000..eeefd8b
|
|
--- /dev/null
|
|
+++ b/ipc/kdbus/item.h
|
|
@@ -0,0 +1,64 @@
|
|
+/*
|
|
+ * 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);
|
|
+const char *kdbus_items_get_str(const struct kdbus_item *items,
|
|
+ size_t items_size,
|
|
+ unsigned int item_type);
|
|
+struct kdbus_item *kdbus_item_set(struct kdbus_item *item, u64 type,
|
|
+ const void *data, size_t len);
|
|
+
|
|
+#endif
|
|
diff --git a/ipc/kdbus/message.c b/ipc/kdbus/message.c
|
|
new file mode 100644
|
|
index 0000000..8096075
|
|
--- /dev/null
|
|
+++ b/ipc/kdbus/message.c
|
|
@@ -0,0 +1,616 @@
|
|
+/*
|
|
+ * 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"
|
|
+
|
|
+#define KDBUS_KMSG_HEADER_SIZE offsetof(struct kdbus_kmsg, msg)
|
|
+
|
|
+static struct kdbus_msg_resources *kdbus_msg_resources_new(void)
|
|
+{
|
|
+ struct kdbus_msg_resources *r;
|
|
+
|
|
+ r = kzalloc(sizeof(*r), GFP_KERNEL);
|
|
+ if (!r)
|
|
+ return ERR_PTR(-ENOMEM);
|
|
+
|
|
+ kref_init(&r->kref);
|
|
+
|
|
+ return r;
|
|
+}
|
|
+
|
|
+static void __kdbus_msg_resources_free(struct kref *kref)
|
|
+{
|
|
+ struct kdbus_msg_resources *r =
|
|
+ container_of(kref, struct kdbus_msg_resources, kref);
|
|
+ size_t i;
|
|
+
|
|
+ for (i = 0; i < r->data_count; ++i) {
|
|
+ switch (r->data[i].type) {
|
|
+ case KDBUS_MSG_DATA_VEC:
|
|
+ /* nothing to do */
|
|
+ break;
|
|
+ case KDBUS_MSG_DATA_MEMFD:
|
|
+ if (r->data[i].memfd.file)
|
|
+ fput(r->data[i].memfd.file);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < r->fds_count; i++)
|
|
+ if (r->fds[i])
|
|
+ fput(r->fds[i]);
|
|
+
|
|
+ kfree(r->dst_name);
|
|
+ kfree(r->data);
|
|
+ kfree(r->fds);
|
|
+ kfree(r);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * kdbus_msg_resources_ref() - Acquire reference to msg resources
|
|
+ * @r: resources to acquire ref to
|
|
+ *
|
|
+ * Return: The acquired resource
|
|
+ */
|
|
+struct kdbus_msg_resources *
|
|
+kdbus_msg_resources_ref(struct kdbus_msg_resources *r)
|
|
+{
|
|
+ if (r)
|
|
+ kref_get(&r->kref);
|
|
+ return r;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * kdbus_msg_resources_unref() - Drop reference to msg resources
|
|
+ * @r: resources to drop reference of
|
|
+ *
|
|
+ * Return: NULL
|
|
+ */
|
|
+struct kdbus_msg_resources *
|
|
+kdbus_msg_resources_unref(struct kdbus_msg_resources *r)
|
|
+{
|
|
+ if (r)
|
|
+ kref_put(&r->kref, __kdbus_msg_resources_free);
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * kdbus_kmsg_free() - free allocated message
|
|
+ * @kmsg: Message
|
|
+ */
|
|
+void kdbus_kmsg_free(struct kdbus_kmsg *kmsg)
|
|
+{
|
|
+ if (!kmsg)
|
|
+ return;
|
|
+
|
|
+ kdbus_msg_resources_unref(kmsg->res);
|
|
+ kdbus_meta_conn_unref(kmsg->conn_meta);
|
|
+ kdbus_meta_proc_unref(kmsg->proc_meta);
|
|
+ kfree(kmsg->iov);
|
|
+ kfree(kmsg);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * kdbus_kmsg_new() - allocate message
|
|
+ * @bus: Bus this message is allocated on
|
|
+ * @extra_size: Additional size to reserve for data
|
|
+ *
|
|
+ * Return: new kdbus_kmsg on success, ERR_PTR on failure.
|
|
+ */
|
|
+struct kdbus_kmsg *kdbus_kmsg_new(struct kdbus_bus *bus, size_t extra_size)
|
|
+{
|
|
+ struct kdbus_kmsg *m;
|
|
+ size_t size;
|
|
+ int ret;
|
|
+
|
|
+ size = sizeof(struct kdbus_kmsg) + KDBUS_ITEM_SIZE(extra_size);
|
|
+ m = kzalloc(size, GFP_KERNEL);
|
|
+ if (!m)
|
|
+ return ERR_PTR(-ENOMEM);
|
|
+
|
|
+ m->seq = atomic64_inc_return(&bus->domain->last_id);
|
|
+ m->msg.size = size - KDBUS_KMSG_HEADER_SIZE;
|
|
+ m->msg.items[0].size = KDBUS_ITEM_SIZE(extra_size);
|
|
+
|
|
+ m->proc_meta = kdbus_meta_proc_new();
|
|
+ if (IS_ERR(m->proc_meta)) {
|
|
+ ret = PTR_ERR(m->proc_meta);
|
|
+ m->proc_meta = NULL;
|
|
+ goto exit;
|
|
+ }
|
|
+
|
|
+ m->conn_meta = kdbus_meta_conn_new();
|
|
+ if (IS_ERR(m->conn_meta)) {
|
|
+ ret = PTR_ERR(m->conn_meta);
|
|
+ m->conn_meta = NULL;
|
|
+ goto exit;
|
|
+ }
|
|
+
|
|
+ return m;
|
|
+
|
|
+exit:
|
|
+ kdbus_kmsg_free(m);
|
|
+ return ERR_PTR(ret);
|
|
+}
|
|
+
|
|
+static int kdbus_handle_check_file(struct file *file)
|
|
+{
|
|
+ struct inode *inode = file_inode(file);
|
|
+ struct socket *sock;
|
|
+
|
|
+ /*
|
|
+ * Don't allow file descriptors in the transport that themselves allow
|
|
+ * file descriptor queueing. This will eventually be allowed once both
|
|
+ * unix domain sockets and kdbus share a generic garbage collector.
|
|
+ */
|
|
+
|
|
+ if (file->f_op == &kdbus_handle_ops)
|
|
+ return -EOPNOTSUPP;
|
|
+
|
|
+ if (!S_ISSOCK(inode->i_mode))
|
|
+ return 0;
|
|
+
|
|
+ if (file->f_mode & FMODE_PATH)
|
|
+ return 0;
|
|
+
|
|
+ sock = SOCKET_I(inode);
|
|
+ if (sock->sk && sock->ops && sock->ops->family == PF_UNIX)
|
|
+ return -EOPNOTSUPP;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const char * const zeros = "\0\0\0\0\0\0\0";
|
|
+
|
|
+/*
|
|
+ * kdbus_msg_scan_items() - validate incoming data and prepare parsing
|
|
+ * @kmsg: Message
|
|
+ * @bus: Bus the message is sent over
|
|
+ *
|
|
+ * Return: 0 on success, negative errno on failure.
|
|
+ *
|
|
+ * Files references in MEMFD or FDS items are pinned.
|
|
+ *
|
|
+ * On errors, the caller should drop any taken reference with
|
|
+ * kdbus_kmsg_free()
|
|
+ */
|
|
+static int kdbus_msg_scan_items(struct kdbus_kmsg *kmsg,
|
|
+ struct kdbus_bus *bus)
|
|
+{
|
|
+ struct kdbus_msg_resources *res = kmsg->res;
|
|
+ const struct kdbus_msg *msg = &kmsg->msg;
|
|
+ const struct kdbus_item *item;
|
|
+ size_t n, n_vecs, n_memfds;
|
|
+ bool has_bloom = false;
|
|
+ bool has_name = false;
|
|
+ bool has_fds = false;
|
|
+ bool is_broadcast;
|
|
+ bool is_signal;
|
|
+ u64 vec_size;
|
|
+
|
|
+ is_broadcast = (msg->dst_id == KDBUS_DST_ID_BROADCAST);
|
|
+ is_signal = !!(msg->flags & KDBUS_MSG_SIGNAL);
|
|
+
|
|
+ /* count data payloads */
|
|
+ n_vecs = 0;
|
|
+ n_memfds = 0;
|
|
+ KDBUS_ITEMS_FOREACH(item, msg->items, KDBUS_ITEMS_SIZE(msg, items)) {
|
|
+ switch (item->type) {
|
|
+ case KDBUS_ITEM_PAYLOAD_VEC:
|
|
+ ++n_vecs;
|
|
+ break;
|
|
+ case KDBUS_ITEM_PAYLOAD_MEMFD:
|
|
+ ++n_memfds;
|
|
+ if (item->memfd.size % 8)
|
|
+ ++n_vecs;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ n = n_vecs + n_memfds;
|
|
+ if (n > 0) {
|
|
+ res->data = kcalloc(n, sizeof(*res->data), GFP_KERNEL);
|
|
+ if (!res->data)
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ if (n_vecs > 0) {
|
|
+ kmsg->iov = kcalloc(n_vecs, sizeof(*kmsg->iov), GFP_KERNEL);
|
|
+ if (!kmsg->iov)
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ /* import data payloads */
|
|
+ n = 0;
|
|
+ vec_size = 0;
|
|
+ KDBUS_ITEMS_FOREACH(item, msg->items, KDBUS_ITEMS_SIZE(msg, items)) {
|
|
+ size_t payload_size = KDBUS_ITEM_PAYLOAD_SIZE(item);
|
|
+ struct iovec *iov = kmsg->iov + kmsg->iov_count;
|
|
+
|
|
+ if (++n > KDBUS_MSG_MAX_ITEMS)
|
|
+ return -E2BIG;
|
|
+
|
|
+ switch (item->type) {
|
|
+ case KDBUS_ITEM_PAYLOAD_VEC: {
|
|
+ struct kdbus_msg_data *d = res->data + res->data_count;
|
|
+ void __force __user *ptr = KDBUS_PTR(item->vec.address);
|
|
+ size_t size = item->vec.size;
|
|
+
|
|
+ if (vec_size + size < vec_size)
|
|
+ return -EMSGSIZE;
|
|
+ if (vec_size + size > KDBUS_MSG_MAX_PAYLOAD_VEC_SIZE)
|
|
+ return -EMSGSIZE;
|
|
+
|
|
+ d->type = KDBUS_MSG_DATA_VEC;
|
|
+ d->size = size;
|
|
+
|
|
+ if (ptr) {
|
|
+ if (unlikely(!access_ok(VERIFY_READ, ptr,
|
|
+ size)))
|
|
+ return -EFAULT;
|
|
+
|
|
+ d->vec.off = kmsg->pool_size;
|
|
+ iov->iov_base = ptr;
|
|
+ iov->iov_len = size;
|
|
+ } else {
|
|
+ d->vec.off = ~0ULL;
|
|
+ iov->iov_base = (char __user *)zeros;
|
|
+ iov->iov_len = size % 8;
|
|
+ }
|
|
+
|
|
+ if (kmsg->pool_size + iov->iov_len < kmsg->pool_size)
|
|
+ return -EMSGSIZE;
|
|
+
|
|
+ kmsg->pool_size += iov->iov_len;
|
|
+ ++kmsg->iov_count;
|
|
+ ++res->vec_count;
|
|
+ ++res->data_count;
|
|
+ vec_size += size;
|
|
+
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ case KDBUS_ITEM_PAYLOAD_MEMFD: {
|
|
+ struct kdbus_msg_data *d = res->data + res->data_count;
|
|
+ u64 start = item->memfd.start;
|
|
+ u64 size = item->memfd.size;
|
|
+ size_t pad = size % 8;
|
|
+ int seals, mask;
|
|
+ struct file *f;
|
|
+
|
|
+ if (kmsg->pool_size + size % 8 < kmsg->pool_size)
|
|
+ return -EMSGSIZE;
|
|
+ if (start + size < start)
|
|
+ return -EMSGSIZE;
|
|
+
|
|
+ if (item->memfd.fd < 0)
|
|
+ return -EBADF;
|
|
+
|
|
+ if (res->memfd_count >= KDBUS_MSG_MAX_MEMFD_ITEMS)
|
|
+ return -E2BIG;
|
|
+
|
|
+ f = fget(item->memfd.fd);
|
|
+ if (!f)
|
|
+ return -EBADF;
|
|
+
|
|
+ if (pad) {
|
|
+ iov->iov_base = (char __user *)zeros;
|
|
+ iov->iov_len = pad;
|
|
+
|
|
+ kmsg->pool_size += pad;
|
|
+ ++kmsg->iov_count;
|
|
+ }
|
|
+
|
|
+ ++res->data_count;
|
|
+ ++res->memfd_count;
|
|
+
|
|
+ d->type = KDBUS_MSG_DATA_MEMFD;
|
|
+ d->size = size;
|
|
+ d->memfd.start = start;
|
|
+ d->memfd.file = f;
|
|
+
|
|
+ /*
|
|
+ * We only accept a sealed memfd file whose content
|
|
+ * cannot be altered by the sender or anybody else
|
|
+ * while it is shared or in-flight. Other files need
|
|
+ * to be passed with KDBUS_MSG_FDS.
|
|
+ */
|
|
+ seals = shmem_get_seals(f);
|
|
+ if (seals < 0)
|
|
+ return -EMEDIUMTYPE;
|
|
+
|
|
+ mask = F_SEAL_SHRINK | F_SEAL_GROW |
|
|
+ F_SEAL_WRITE | F_SEAL_SEAL;
|
|
+ if ((seals & mask) != mask)
|
|
+ return -ETXTBSY;
|
|
+
|
|
+ if (start + size > (u64)i_size_read(file_inode(f)))
|
|
+ return -EBADF;
|
|
+
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ case KDBUS_ITEM_FDS: {
|
|
+ unsigned int i;
|
|
+ unsigned int fds_count = payload_size / sizeof(int);
|
|
+
|
|
+ /* do not allow multiple fd arrays */
|
|
+ if (has_fds)
|
|
+ return -EEXIST;
|
|
+ has_fds = true;
|
|
+
|
|
+ /* Do not allow to broadcast file descriptors */
|
|
+ if (is_broadcast)
|
|
+ return -ENOTUNIQ;
|
|
+
|
|
+ if (fds_count > KDBUS_CONN_MAX_FDS_PER_USER)
|
|
+ return -EMFILE;
|
|
+
|
|
+ res->fds = kcalloc(fds_count, sizeof(struct file *),
|
|
+ GFP_KERNEL);
|
|
+ if (!res->fds)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ for (i = 0; i < fds_count; i++) {
|
|
+ int fd = item->fds[i];
|
|
+ int ret;
|
|
+
|
|
+ /*
|
|
+ * Verify the fd and increment the usage count.
|
|
+ * Use fget_raw() to allow passing O_PATH fds.
|
|
+ */
|
|
+ if (fd < 0)
|
|
+ return -EBADF;
|
|
+
|
|
+ res->fds[i] = fget_raw(fd);
|
|
+ if (!res->fds[i])
|
|
+ return -EBADF;
|
|
+
|
|
+ res->fds_count++;
|
|
+
|
|
+ ret = kdbus_handle_check_file(res->fds[i]);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ case KDBUS_ITEM_BLOOM_FILTER: {
|
|
+ u64 bloom_size;
|
|
+
|
|
+ /* do not allow multiple bloom filters */
|
|
+ if (has_bloom)
|
|
+ return -EEXIST;
|
|
+ has_bloom = true;
|
|
+
|
|
+ bloom_size = payload_size -
|
|
+ offsetof(struct kdbus_bloom_filter, data);
|
|
+
|
|
+ /*
|
|
+ * Allow only bloom filter sizes of a multiple of 64bit.
|
|
+ */
|
|
+ if (!KDBUS_IS_ALIGNED8(bloom_size))
|
|
+ return -EFAULT;
|
|
+
|
|
+ /* do not allow mismatching bloom filter sizes */
|
|
+ if (bloom_size != bus->bloom.size)
|
|
+ return -EDOM;
|
|
+
|
|
+ kmsg->bloom_filter = &item->bloom_filter;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ case KDBUS_ITEM_DST_NAME:
|
|
+ /* do not allow multiple names */
|
|
+ if (has_name)
|
|
+ return -EEXIST;
|
|
+ has_name = true;
|
|
+
|
|
+ if (!kdbus_name_is_valid(item->str, false))
|
|
+ return -EINVAL;
|
|
+
|
|
+ res->dst_name = kstrdup(item->str, GFP_KERNEL);
|
|
+ if (!res->dst_name)
|
|
+ return -ENOMEM;
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* name is needed if no ID is given */
|
|
+ if (msg->dst_id == KDBUS_DST_ID_NAME && !has_name)
|
|
+ return -EDESTADDRREQ;
|
|
+
|
|
+ if (is_broadcast) {
|
|
+ /* Broadcasts can't take names */
|
|
+ if (has_name)
|
|
+ return -EBADMSG;
|
|
+
|
|
+ /* All broadcasts have to be signals */
|
|
+ if (!is_signal)
|
|
+ return -EBADMSG;
|
|
+
|
|
+ /* Timeouts are not allowed for broadcasts */
|
|
+ if (msg->timeout_ns > 0)
|
|
+ return -ENOTUNIQ;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Signal messages require a bloom filter, and bloom filters are
|
|
+ * only valid with signals.
|
|
+ */
|
|
+ if (is_signal ^ has_bloom)
|
|
+ return -EBADMSG;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * kdbus_kmsg_new_from_cmd() - create kernel message from send payload
|
|
+ * @conn: Connection
|
|
+ * @cmd_send: Payload of KDBUS_CMD_SEND
|
|
+ *
|
|
+ * Return: a new kdbus_kmsg on success, ERR_PTR on failure.
|
|
+ */
|
|
+struct kdbus_kmsg *kdbus_kmsg_new_from_cmd(struct kdbus_conn *conn,
|
|
+ struct kdbus_cmd_send *cmd_send)
|
|
+{
|
|
+ struct kdbus_kmsg *m;
|
|
+ u64 size;
|
|
+ int ret;
|
|
+
|
|
+ ret = kdbus_copy_from_user(&size, KDBUS_PTR(cmd_send->msg_address),
|
|
+ sizeof(size));
|
|
+ if (ret < 0)
|
|
+ return ERR_PTR(ret);
|
|
+
|
|
+ if (size < sizeof(struct kdbus_msg) || size > KDBUS_MSG_MAX_SIZE)
|
|
+ return ERR_PTR(-EINVAL);
|
|
+
|
|
+ m = kmalloc(size + KDBUS_KMSG_HEADER_SIZE, GFP_KERNEL);
|
|
+ if (!m)
|
|
+ return ERR_PTR(-ENOMEM);
|
|
+
|
|
+ memset(m, 0, KDBUS_KMSG_HEADER_SIZE);
|
|
+ m->seq = atomic64_inc_return(&conn->ep->bus->domain->last_id);
|
|
+
|
|
+ m->proc_meta = kdbus_meta_proc_new();
|
|
+ if (IS_ERR(m->proc_meta)) {
|
|
+ ret = PTR_ERR(m->proc_meta);
|
|
+ m->proc_meta = NULL;
|
|
+ goto exit_free;
|
|
+ }
|
|
+
|
|
+ m->conn_meta = kdbus_meta_conn_new();
|
|
+ if (IS_ERR(m->conn_meta)) {
|
|
+ ret = PTR_ERR(m->conn_meta);
|
|
+ m->conn_meta = NULL;
|
|
+ goto exit_free;
|
|
+ }
|
|
+
|
|
+ if (copy_from_user(&m->msg, KDBUS_PTR(cmd_send->msg_address), size)) {
|
|
+ ret = -EFAULT;
|
|
+ goto exit_free;
|
|
+ }
|
|
+
|
|
+ if (m->msg.size != size) {
|
|
+ ret = -EINVAL;
|
|
+ goto exit_free;
|
|
+ }
|
|
+
|
|
+ if (m->msg.flags & ~(KDBUS_MSG_EXPECT_REPLY |
|
|
+ KDBUS_MSG_NO_AUTO_START |
|
|
+ KDBUS_MSG_SIGNAL)) {
|
|
+ ret = -EINVAL;
|
|
+ goto exit_free;
|
|
+ }
|
|
+
|
|
+ ret = kdbus_items_validate(m->msg.items,
|
|
+ KDBUS_ITEMS_SIZE(&m->msg, items));
|
|
+ if (ret < 0)
|
|
+ goto exit_free;
|
|
+
|
|
+ m->res = kdbus_msg_resources_new();
|
|
+ if (IS_ERR(m->res)) {
|
|
+ ret = PTR_ERR(m->res);
|
|
+ m->res = NULL;
|
|
+ goto exit_free;
|
|
+ }
|
|
+
|
|
+ /* do not accept kernel-generated messages */
|
|
+ if (m->msg.payload_type == KDBUS_PAYLOAD_KERNEL) {
|
|
+ ret = -EINVAL;
|
|
+ goto exit_free;
|
|
+ }
|
|
+
|
|
+ if (m->msg.flags & KDBUS_MSG_EXPECT_REPLY) {
|
|
+ /* requests for replies need timeout and cookie */
|
|
+ if (m->msg.timeout_ns == 0 || m->msg.cookie == 0) {
|
|
+ ret = -EINVAL;
|
|
+ goto exit_free;
|
|
+ }
|
|
+
|
|
+ /* replies may not be expected for broadcasts */
|
|
+ if (m->msg.dst_id == KDBUS_DST_ID_BROADCAST) {
|
|
+ ret = -ENOTUNIQ;
|
|
+ goto exit_free;
|
|
+ }
|
|
+
|
|
+ /* replies may not be expected for signals */
|
|
+ if (m->msg.flags & KDBUS_MSG_SIGNAL) {
|
|
+ ret = -EINVAL;
|
|
+ goto exit_free;
|
|
+ }
|
|
+ } else {
|
|
+ /*
|
|
+ * KDBUS_SEND_SYNC_REPLY is only valid together with
|
|
+ * KDBUS_MSG_EXPECT_REPLY
|
|
+ */
|
|
+ if (cmd_send->flags & KDBUS_SEND_SYNC_REPLY) {
|
|
+ ret = -EINVAL;
|
|
+ goto exit_free;
|
|
+ }
|
|
+
|
|
+ /* replies cannot be signals */
|
|
+ if (m->msg.cookie_reply && (m->msg.flags & KDBUS_MSG_SIGNAL)) {
|
|
+ ret = -EINVAL;
|
|
+ goto exit_free;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ ret = kdbus_msg_scan_items(m, conn->ep->bus);
|
|
+ if (ret < 0)
|
|
+ goto exit_free;
|
|
+
|
|
+ /* patch-in the source of this message */
|
|
+ if (m->msg.src_id > 0 && m->msg.src_id != conn->id) {
|
|
+ ret = -EINVAL;
|
|
+ goto exit_free;
|
|
+ }
|
|
+ m->msg.src_id = conn->id;
|
|
+
|
|
+ return m;
|
|
+
|
|
+exit_free:
|
|
+ kdbus_kmsg_free(m);
|
|
+ return ERR_PTR(ret);
|
|
+}
|
|
diff --git a/ipc/kdbus/message.h b/ipc/kdbus/message.h
|
|
new file mode 100644
|
|
index 0000000..af47758
|
|
--- /dev/null
|
|
+++ b/ipc/kdbus/message.h
|
|
@@ -0,0 +1,133 @@
|
|
+/*
|
|
+ * 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 "util.h"
|
|
+#include "metadata.h"
|
|
+
|
|
+/**
|
|
+ * enum kdbus_msg_data_type - Type of kdbus_msg_data payloads
|
|
+ * @KDBUS_MSG_DATA_VEC: Data vector provided by user-space
|
|
+ * @KDBUS_MSG_DATA_MEMFD: Memfd payload
|
|
+ */
|
|
+enum kdbus_msg_data_type {
|
|
+ KDBUS_MSG_DATA_VEC,
|
|
+ KDBUS_MSG_DATA_MEMFD,
|
|
+};
|
|
+
|
|
+/**
|
|
+ * struct kdbus_msg_data - Data payload as stored by messages
|
|
+ * @type: Type of payload (KDBUS_MSG_DATA_*)
|
|
+ * @size: Size of the described payload
|
|
+ * @off: The offset, relative to the vec slice
|
|
+ * @start: Offset inside the memfd
|
|
+ * @file: Backing file referenced by the memfd
|
|
+ */
|
|
+struct kdbus_msg_data {
|
|
+ unsigned int type;
|
|
+ u64 size;
|
|
+
|
|
+ union {
|
|
+ struct {
|
|
+ u64 off;
|
|
+ } vec;
|
|
+ struct {
|
|
+ u64 start;
|
|
+ struct file *file;
|
|
+ } memfd;
|
|
+ };
|
|
+};
|
|
+
|
|
+/**
|
|
+ * struct kdbus_kmsg_resources - resources of a message
|
|
+ * @kref: Reference counter
|
|
+ * @dst_name: Short-cut to msg for faster lookup
|
|
+ * @fds: Array of file descriptors to pass
|
|
+ * @fds_count: Number of file descriptors to pass
|
|
+ * @data: Array of data payloads
|
|
+ * @vec_count: Number of VEC entries
|
|
+ * @memfd_count: Number of MEMFD entries in @data
|
|
+ * @data_count: Sum of @vec_count + @memfd_count
|
|
+ */
|
|
+struct kdbus_msg_resources {
|
|
+ struct kref kref;
|
|
+ const char *dst_name;
|
|
+
|
|
+ struct file **fds;
|
|
+ unsigned int fds_count;
|
|
+
|
|
+ struct kdbus_msg_data *data;
|
|
+ size_t vec_count;
|
|
+ size_t memfd_count;
|
|
+ size_t data_count;
|
|
+};
|
|
+
|
|
+struct kdbus_msg_resources *
|
|
+kdbus_msg_resources_ref(struct kdbus_msg_resources *r);
|
|
+struct kdbus_msg_resources *
|
|
+kdbus_msg_resources_unref(struct kdbus_msg_resources *r);
|
|
+
|
|
+/**
|
|
+ * struct kdbus_kmsg - internal message handling data
|
|
+ * @seq: Domain-global message sequence number
|
|
+ * @notify_type: Short-cut for faster lookup
|
|
+ * @notify_old_id: Short-cut for faster lookup
|
|
+ * @notify_new_id: Short-cut for faster lookup
|
|
+ * @notify_name: Short-cut for faster lookup
|
|
+ * @dst_name_id: Short-cut to msg for faster lookup
|
|
+ * @bloom_filter: Bloom filter to match message properties
|
|
+ * @bloom_generation: Generation of bloom element set
|
|
+ * @notify_entry: List of kernel-generated notifications
|
|
+ * @iov: Array of iovec, describing the payload to copy
|
|
+ * @iov_count: Number of array members in @iov
|
|
+ * @pool_size: Overall size of inlined data referenced by @iov
|
|
+ * @proc_meta: Appended SCM-like metadata of the sending process
|
|
+ * @conn_meta: Appended SCM-like metadata of the sending connection
|
|
+ * @res: Message resources
|
|
+ * @msg: Message from or to userspace
|
|
+ */
|
|
+struct kdbus_kmsg {
|
|
+ u64 seq;
|
|
+ u64 notify_type;
|
|
+ u64 notify_old_id;
|
|
+ u64 notify_new_id;
|
|
+ const char *notify_name;
|
|
+
|
|
+ u64 dst_name_id;
|
|
+ const struct kdbus_bloom_filter *bloom_filter;
|
|
+ u64 bloom_generation;
|
|
+ struct list_head notify_entry;
|
|
+
|
|
+ struct iovec *iov;
|
|
+ size_t iov_count;
|
|
+ u64 pool_size;
|
|
+
|
|
+ struct kdbus_meta_proc *proc_meta;
|
|
+ struct kdbus_meta_conn *conn_meta;
|
|
+ struct kdbus_msg_resources *res;
|
|
+
|
|
+ /* variable size, must be the last member */
|
|
+ struct kdbus_msg msg;
|
|
+};
|
|
+
|
|
+struct kdbus_bus;
|
|
+struct kdbus_conn;
|
|
+
|
|
+struct kdbus_kmsg *kdbus_kmsg_new(struct kdbus_bus *bus, size_t extra_size);
|
|
+struct kdbus_kmsg *kdbus_kmsg_new_from_cmd(struct kdbus_conn *conn,
|
|
+ struct kdbus_cmd_send *cmd_send);
|
|
+void kdbus_kmsg_free(struct kdbus_kmsg *kmsg);
|
|
+
|
|
+#endif
|
|
diff --git a/ipc/kdbus/queue.c b/ipc/kdbus/queue.c
|
|
new file mode 100644
|
|
index 0000000..a449464
|
|
--- /dev/null
|
|
+++ b/ipc/kdbus/queue.c
|
|
@@ -0,0 +1,678 @@
|
|
+/*
|
|
+ * 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
|
|
+ * @conn_dst: destination connection
|
|
+ * @kmsg: kmsg object the queue entry should track
|
|
+ * @user: user to account message on (or NULL for kernel messages)
|
|
+ *
|
|
+ * Allocates a queue entry based on a given kmsg 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 *conn_dst,
|
|
+ const struct kdbus_kmsg *kmsg,
|
|
+ struct kdbus_user *user)
|
|
+{
|
|
+ struct kdbus_msg_resources *res = kmsg->res;
|
|
+ const struct kdbus_msg *msg = &kmsg->msg;
|
|
+ struct kdbus_queue_entry *entry;
|
|
+ size_t memfd_cnt = 0;
|
|
+ struct kvec kvec[2];
|
|
+ size_t meta_size;
|
|
+ size_t msg_size;
|
|
+ u64 payload_off;
|
|
+ u64 size = 0;
|
|
+ int ret = 0;
|
|
+
|
|
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
|
|
+ if (!entry)
|
|
+ return ERR_PTR(-ENOMEM);
|
|
+
|
|
+ INIT_LIST_HEAD(&entry->entry);
|
|
+ entry->priority = msg->priority;
|
|
+ entry->dst_name_id = kmsg->dst_name_id;
|
|
+ entry->msg_res = kdbus_msg_resources_ref(res);
|
|
+ entry->proc_meta = kdbus_meta_proc_ref(kmsg->proc_meta);
|
|
+ entry->conn_meta = kdbus_meta_conn_ref(kmsg->conn_meta);
|
|
+ entry->conn = kdbus_conn_ref(conn_dst);
|
|
+
|
|
+ if (kmsg->msg.src_id == KDBUS_SRC_ID_KERNEL)
|
|
+ msg_size = msg->size;
|
|
+ else
|
|
+ msg_size = offsetof(struct kdbus_msg, items);
|
|
+
|
|
+ /* sum up the size of the needed slice */
|
|
+ size = msg_size;
|
|
+
|
|
+ if (res) {
|
|
+ size += res->vec_count *
|
|
+ KDBUS_ITEM_SIZE(sizeof(struct kdbus_vec));
|
|
+
|
|
+ if (res->memfd_count) {
|
|
+ entry->memfd_offset =
|
|
+ kcalloc(res->memfd_count, sizeof(size_t),
|
|
+ GFP_KERNEL);
|
|
+ if (!entry->memfd_offset) {
|
|
+ ret = -ENOMEM;
|
|
+ goto exit_free_entry;
|
|
+ }
|
|
+
|
|
+ size += res->memfd_count *
|
|
+ KDBUS_ITEM_SIZE(sizeof(struct kdbus_memfd));
|
|
+ }
|
|
+
|
|
+ if (res->fds_count)
|
|
+ size += KDBUS_ITEM_SIZE(sizeof(int) * res->fds_count);
|
|
+
|
|
+ if (res->dst_name)
|
|
+ size += KDBUS_ITEM_SIZE(strlen(res->dst_name) + 1);
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Remember the offset of the metadata part, so we can override
|
|
+ * this part later during kdbus_queue_entry_install().
|
|
+ */
|
|
+ entry->meta_offset = size;
|
|
+
|
|
+ if (entry->proc_meta || entry->conn_meta) {
|
|
+ entry->attach_flags =
|
|
+ atomic64_read(&conn_dst->attach_flags_recv);
|
|
+
|
|
+ ret = kdbus_meta_export_prepare(entry->proc_meta,
|
|
+ entry->conn_meta,
|
|
+ &entry->attach_flags,
|
|
+ &meta_size);
|
|
+ if (ret < 0)
|
|
+ goto exit_free_entry;
|
|
+
|
|
+ size += meta_size;
|
|
+ }
|
|
+
|
|
+ payload_off = size;
|
|
+ size += kmsg->pool_size;
|
|
+ size = KDBUS_ALIGN8(size);
|
|
+
|
|
+ ret = kdbus_conn_quota_inc(conn_dst, user, size,
|
|
+ res ? res->fds_count : 0);
|
|
+ if (ret < 0)
|
|
+ goto exit_free_entry;
|
|
+
|
|
+ entry->slice = kdbus_pool_slice_alloc(conn_dst->pool, size, true);
|
|
+ if (IS_ERR(entry->slice)) {
|
|
+ ret = PTR_ERR(entry->slice);
|
|
+ entry->slice = NULL;
|
|
+ kdbus_conn_quota_dec(conn_dst, user, size,
|
|
+ res ? res->fds_count : 0);
|
|
+ goto exit_free_entry;
|
|
+ }
|
|
+
|
|
+ /* we accounted for exactly 'size' bytes, make sure it didn't grow */
|
|
+ WARN_ON(kdbus_pool_slice_size(entry->slice) != size);
|
|
+ entry->user = kdbus_user_ref(user);
|
|
+
|
|
+ /* copy message header */
|
|
+ kvec[0].iov_base = (char *)msg;
|
|
+ kvec[0].iov_len = msg_size;
|
|
+
|
|
+ ret = kdbus_pool_slice_copy_kvec(entry->slice, 0, kvec, 1, msg_size);
|
|
+ if (ret < 0)
|
|
+ goto exit_free_entry;
|
|
+
|
|
+ /* 'size' will now track the write position */
|
|
+ size = msg_size;
|
|
+
|
|
+ /* create message payload items */
|
|
+ if (res) {
|
|
+ size_t dst_name_len = 0;
|
|
+ unsigned int i;
|
|
+ size_t sz = 0;
|
|
+
|
|
+ if (res->dst_name) {
|
|
+ dst_name_len = strlen(res->dst_name) + 1;
|
|
+ sz += KDBUS_ITEM_SIZE(dst_name_len);
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < res->data_count; ++i) {
|
|
+ struct kdbus_vec v;
|
|
+ struct kdbus_memfd m;
|
|
+
|
|
+ switch (res->data[i].type) {
|
|
+ case KDBUS_MSG_DATA_VEC:
|
|
+ sz += KDBUS_ITEM_SIZE(sizeof(v));
|
|
+ break;
|
|
+
|
|
+ case KDBUS_MSG_DATA_MEMFD:
|
|
+ sz += KDBUS_ITEM_SIZE(sizeof(m));
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (sz) {
|
|
+ struct kdbus_item *items, *item;
|
|
+
|
|
+ items = kmalloc(sz, GFP_KERNEL);
|
|
+ if (!items) {
|
|
+ ret = -ENOMEM;
|
|
+ goto exit_free_entry;
|
|
+ }
|
|
+
|
|
+ item = items;
|
|
+
|
|
+ if (res->dst_name)
|
|
+ item = kdbus_item_set(item, KDBUS_ITEM_DST_NAME,
|
|
+ res->dst_name,
|
|
+ dst_name_len);
|
|
+
|
|
+ for (i = 0; i < res->data_count; ++i) {
|
|
+ struct kdbus_msg_data *d = res->data + i;
|
|
+ struct kdbus_memfd m = {};
|
|
+ struct kdbus_vec v = {};
|
|
+
|
|
+ switch (d->type) {
|
|
+ case KDBUS_MSG_DATA_VEC:
|
|
+ v.size = d->size;
|
|
+ v.offset = d->vec.off;
|
|
+ if (v.offset != ~0ULL)
|
|
+ v.offset += payload_off;
|
|
+
|
|
+ item = kdbus_item_set(item,
|
|
+ KDBUS_ITEM_PAYLOAD_OFF,
|
|
+ &v, sizeof(v));
|
|
+ break;
|
|
+
|
|
+ case KDBUS_MSG_DATA_MEMFD:
|
|
+ /*
|
|
+ * Remember the location of memfds, so
|
|
+ * we can override the content from
|
|
+ * kdbus_queue_entry_install().
|
|
+ */
|
|
+ entry->memfd_offset[memfd_cnt++] =
|
|
+ msg_size +
|
|
+ (char *)item - (char *)items +
|
|
+ offsetof(struct kdbus_item,
|
|
+ memfd);
|
|
+
|
|
+ item = kdbus_item_set(item,
|
|
+ KDBUS_ITEM_PAYLOAD_MEMFD,
|
|
+ &m, sizeof(m));
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ kvec[0].iov_base = items;
|
|
+ kvec[0].iov_len = sz;
|
|
+
|
|
+ ret = kdbus_pool_slice_copy_kvec(entry->slice, size,
|
|
+ kvec, 1, sz);
|
|
+ kfree(items);
|
|
+
|
|
+ if (ret < 0)
|
|
+ goto exit_free_entry;
|
|
+
|
|
+ size += sz;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Remember the location of the FD part, so we can override the
|
|
+ * content in kdbus_queue_entry_install().
|
|
+ */
|
|
+ if (res->fds_count) {
|
|
+ entry->fds_offset = size;
|
|
+ size += KDBUS_ITEM_SIZE(sizeof(int) * res->fds_count);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* finally, copy over the actual message payload */
|
|
+ if (kmsg->iov_count) {
|
|
+ ret = kdbus_pool_slice_copy_iovec(entry->slice, payload_off,
|
|
+ kmsg->iov,
|
|
+ kmsg->iov_count,
|
|
+ kmsg->pool_size);
|
|
+ if (ret < 0)
|
|
+ goto exit_free_entry;
|
|
+ }
|
|
+
|
|
+ return entry;
|
|
+
|
|
+exit_free_entry:
|
|
+ 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->msg_res ?
|
|
+ entry->msg_res->fds_count : 0);
|
|
+ kdbus_pool_slice_release(entry->slice);
|
|
+ kdbus_user_unref(entry->user);
|
|
+ }
|
|
+
|
|
+ kdbus_msg_resources_unref(entry->msg_res);
|
|
+ kdbus_meta_conn_unref(entry->conn_meta);
|
|
+ kdbus_meta_proc_unref(entry->proc_meta);
|
|
+ kdbus_conn_unref(entry->conn);
|
|
+ kfree(entry->memfd_offset);
|
|
+ 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
|
|
+ *
|
|
+ * This function will create a slice to transport the message header, the
|
|
+ * metadata items and other items for information stored in @entry, and
|
|
+ * store it as entry->slice.
|
|
+ *
|
|
+ * If @install_fds is %true, file descriptors will as well be installed.
|
|
+ * This function must always be called from the task context of the receiver.
|
|
+ *
|
|
+ * Return: 0 on success.
|
|
+ */
|
|
+int kdbus_queue_entry_install(struct kdbus_queue_entry *entry,
|
|
+ u64 *return_flags, bool install_fds)
|
|
+{
|
|
+ u64 msg_size = entry->meta_offset;
|
|
+ struct kdbus_conn *conn_dst = entry->conn;
|
|
+ struct kdbus_msg_resources *res;
|
|
+ bool incomplete_fds = false;
|
|
+ struct kvec kvec[2];
|
|
+ size_t memfds = 0;
|
|
+ int i, ret;
|
|
+
|
|
+ lockdep_assert_held(&conn_dst->lock);
|
|
+
|
|
+ if (entry->proc_meta || entry->conn_meta) {
|
|
+ size_t meta_size;
|
|
+
|
|
+ ret = kdbus_meta_export(entry->proc_meta,
|
|
+ entry->conn_meta,
|
|
+ entry->attach_flags,
|
|
+ entry->slice,
|
|
+ entry->meta_offset,
|
|
+ &meta_size);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ msg_size += meta_size;
|
|
+ }
|
|
+
|
|
+ /* Update message size at offset 0 */
|
|
+ kvec[0].iov_base = &msg_size;
|
|
+ kvec[0].iov_len = sizeof(msg_size);
|
|
+
|
|
+ ret = kdbus_pool_slice_copy_kvec(entry->slice, 0, kvec, 1,
|
|
+ sizeof(msg_size));
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ res = entry->msg_res;
|
|
+
|
|
+ if (!res)
|
|
+ return 0;
|
|
+
|
|
+ if (res->fds_count) {
|
|
+ struct kdbus_item_header hdr;
|
|
+ size_t off;
|
|
+ int *fds;
|
|
+
|
|
+ fds = kmalloc_array(res->fds_count, sizeof(int), GFP_KERNEL);
|
|
+ if (!fds)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ for (i = 0; i < res->fds_count; i++) {
|
|
+ if (install_fds) {
|
|
+ fds[i] = get_unused_fd_flags(O_CLOEXEC);
|
|
+ if (fds[i] >= 0)
|
|
+ fd_install(fds[i],
|
|
+ get_file(res->fds[i]));
|
|
+ else
|
|
+ incomplete_fds = true;
|
|
+ } else {
|
|
+ fds[i] = -1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ off = entry->fds_offset;
|
|
+
|
|
+ hdr.type = KDBUS_ITEM_FDS;
|
|
+ hdr.size = KDBUS_ITEM_HEADER_SIZE +
|
|
+ sizeof(int) * res->fds_count;
|
|
+
|
|
+ kvec[0].iov_base = &hdr;
|
|
+ kvec[0].iov_len = sizeof(hdr);
|
|
+
|
|
+ kvec[1].iov_base = fds;
|
|
+ kvec[1].iov_len = sizeof(int) * res->fds_count;
|
|
+
|
|
+ ret = kdbus_pool_slice_copy_kvec(entry->slice, off,
|
|
+ kvec, 2, hdr.size);
|
|
+ kfree(fds);
|
|
+
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < res->data_count; ++i) {
|
|
+ struct kdbus_msg_data *d = res->data + i;
|
|
+ struct kdbus_memfd m;
|
|
+
|
|
+ if (d->type != KDBUS_MSG_DATA_MEMFD)
|
|
+ continue;
|
|
+
|
|
+ m.start = d->memfd.start;
|
|
+ m.size = d->size;
|
|
+ m.fd = -1;
|
|
+
|
|
+ if (install_fds) {
|
|
+ m.fd = get_unused_fd_flags(O_CLOEXEC);
|
|
+ if (m.fd < 0) {
|
|
+ m.fd = -1;
|
|
+ incomplete_fds = true;
|
|
+ } else {
|
|
+ fd_install(m.fd,
|
|
+ get_file(d->memfd.file));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ kvec[0].iov_base = &m;
|
|
+ kvec[0].iov_len = sizeof(m);
|
|
+
|
|
+ ret = kdbus_pool_slice_copy_kvec(entry->slice,
|
|
+ entry->memfd_offset[memfds++],
|
|
+ kvec, 1, sizeof(m));
|
|
+ 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(IS_ERR(e->user)) || WARN_ON(list_empty(&e->entry)))
|
|
+ return -EINVAL;
|
|
+ if (src == dst)
|
|
+ return 0;
|
|
+
|
|
+ size = kdbus_pool_slice_size(e->slice);
|
|
+ fds = e->msg_res ? e->msg_res->fds_count : 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
|
|
index 0000000..7f2db96
|
|
--- /dev/null
|
|
+++ b/ipc/kdbus/queue.h
|
|
@@ -0,0 +1,92 @@
|
|
+/*
|
|
+ * 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
|
|
+
|
|
+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
|
|
+ * @slice: Slice in the receiver's pool for the message
|
|
+ * @attach_flags: Attach flags used during slice allocation
|
|
+ * @meta_offset: Offset of first metadata item in slice
|
|
+ * @fds_offset: Offset of FD item in slice
|
|
+ * @memfd_offset: Array of slice-offsets for all memfd items
|
|
+ * @priority: Message priority
|
|
+ * @dst_name_id: The sequence number of the name this message is
|
|
+ * addressed to, 0 for messages sent to an ID
|
|
+ * @msg_res: Message resources
|
|
+ * @proc_meta: Process metadata, captured at message arrival
|
|
+ * @conn_meta: Connection metadata, captured at message arrival
|
|
+ * @reply: The reply block if a reply to this message is expected
|
|
+ * @user: User used for accounting
|
|
+ */
|
|
+struct kdbus_queue_entry {
|
|
+ struct list_head entry;
|
|
+ struct rb_node prio_node;
|
|
+ struct list_head prio_entry;
|
|
+
|
|
+ struct kdbus_pool_slice *slice;
|
|
+
|
|
+ u64 attach_flags;
|
|
+ size_t meta_offset;
|
|
+ size_t fds_offset;
|
|
+ size_t *memfd_offset;
|
|
+
|
|
+ s64 priority;
|
|
+ u64 dst_name_id;
|
|
+
|
|
+ struct kdbus_msg_resources *msg_res;
|
|
+ struct kdbus_meta_proc *proc_meta;
|
|
+ struct kdbus_meta_conn *conn_meta;
|
|
+ struct kdbus_reply *reply;
|
|
+ struct kdbus_conn *conn;
|
|
+ struct kdbus_user *user;
|
|
+};
|
|
+
|
|
+struct kdbus_kmsg;
|
|
+
|
|
+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 *conn_dst,
|
|
+ const struct kdbus_kmsg *kmsg,
|
|
+ struct kdbus_user *user);
|
|
+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
|
|
index 0000000..6b3bd81
|
|
--- /dev/null
|
|
+++ b/ipc/kdbus/reply.c
|
|
@@ -0,0 +1,259 @@
|
|
+#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 = 0;
|
|
+
|
|
+ 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;
|
|
+ }
|
|
+
|
|
+exit_dec_request_count:
|
|
+ if (ret < 0) {
|
|
+ atomic_dec(&reply_dst->request_count);
|
|
+ return ERR_PTR(ret);
|
|
+ }
|
|
+
|
|
+ return r;
|
|
+}
|
|
+
|
|
+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(&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
|
|
+ *
|
|
+ * Remove the synchronous reply object from its connection reply_list, and
|
|
+ * 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, *reply = NULL;
|
|
+
|
|
+ list_for_each_entry(r, &reply_dst->reply_list, entry) {
|
|
+ if (r->cookie == cookie &&
|
|
+ (!replying || r->reply_src == replying)) {
|
|
+ reply = r;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return reply;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * 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;
|
|
+ struct timespec64 ts;
|
|
+ u64 now;
|
|
+
|
|
+ ktime_get_ts64(&ts);
|
|
+ now = timespec64_to_ns(&ts);
|
|
+
|
|
+ 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
|
|
index 0000000..68d5232
|
|
--- /dev/null
|
|
+++ b/ipc/kdbus/reply.h
|
|
@@ -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.h b/ipc/kdbus/util.h
|
|
index 9caadb3..740b198 100644
|
|
--- a/ipc/kdbus/util.h
|
|
+++ b/ipc/kdbus/util.h
|
|
@@ -18,7 +18,7 @@
|
|
#include <linux/dcache.h>
|
|
#include <linux/ioctl.h>
|
|
|
|
-#include "kdbus.h"
|
|
+#include <uapi/linux/kdbus.h>
|
|
|
|
/* all exported addresses are 64 bit */
|
|
#define KDBUS_PTR(addr) ((void __user *)(uintptr_t)(addr))
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 9aef18e03b60ff39bbf94044285c62356a614dc3 Mon Sep 17 00:00:00 2001
|
|
From: Daniel Mack <daniel@zonque.org>
|
|
Date: Fri, 14 Nov 2014 09:59:08 +0100
|
|
Subject: [PATCH 06/78] kdbus: add node and filesystem implementation
|
|
|
|
kdbusfs is a filesystem that will expose a fresh kdbus domain context
|
|
each time it is mounted. Per mount point, there will be a 'control'
|
|
node, which can be used to create buses. fs.c contains the
|
|
implementation of that pseudo-fs. Exported inodes of 'file' type have
|
|
their i_fop set to either kdbus_handle_control_ops or
|
|
kdbus_handle_ep_ops, depending on their type. The actual dispatching
|
|
of file operations is done from handle.c
|
|
|
|
node.c is an implementation of a kdbus object that has an id and
|
|
children, organized in an R/B tree. The tree is used by the filesystem
|
|
code for lookup and iterator functions, and to deactivate children
|
|
once the parent is deactivated. Every inode exported by kdbusfs is
|
|
backed by a kdbus_node, hence it is embedded in struct kdbus_ep,
|
|
struct kdbus_bus and struct kdbus_domain.
|
|
|
|
Signed-off-by: Daniel Mack <daniel@zonque.org>
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Djalal Harouni <tixxdz@opendz.org>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
include/uapi/linux/magic.h | 2 +
|
|
ipc/kdbus/fs.c | 510 +++++++++++++++++++++++++
|
|
ipc/kdbus/fs.h | 28 ++
|
|
ipc/kdbus/node.c | 910 +++++++++++++++++++++++++++++++++++++++++++++
|
|
ipc/kdbus/node.h | 84 +++++
|
|
5 files changed, 1534 insertions(+)
|
|
create mode 100644 ipc/kdbus/fs.c
|
|
create mode 100644 ipc/kdbus/fs.h
|
|
create mode 100644 ipc/kdbus/node.c
|
|
create mode 100644 ipc/kdbus/node.h
|
|
|
|
diff --git a/include/uapi/linux/magic.h b/include/uapi/linux/magic.h
|
|
index 7b1425a..ce2ac5a 100644
|
|
--- a/include/uapi/linux/magic.h
|
|
+++ b/include/uapi/linux/magic.h
|
|
@@ -76,4 +76,6 @@
|
|
#define BTRFS_TEST_MAGIC 0x73727279
|
|
#define NSFS_MAGIC 0x6e736673
|
|
|
|
+#define KDBUS_SUPER_MAGIC 0x44427573
|
|
+
|
|
#endif /* __LINUX_MAGIC_H__ */
|
|
diff --git a/ipc/kdbus/fs.c b/ipc/kdbus/fs.c
|
|
new file mode 100644
|
|
index 0000000..d01f33b
|
|
--- /dev/null
|
|
+++ b/ipc/kdbus/fs.c
|
|
@@ -0,0 +1,510 @@
|
|
+/*
|
|
+ * 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 neccessarily 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);
|
|
+
|
|
+ if (domain)
|
|
+ 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
|
|
index 0000000..62f7d6a
|
|
--- /dev/null
|
|
+++ b/ipc/kdbus/fs.h
|
|
@@ -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/node.c b/ipc/kdbus/node.c
|
|
new file mode 100644
|
|
index 0000000..520df00
|
|
--- /dev/null
|
|
+++ b/ipc/kdbus/node.c
|
|
@@ -0,0 +1,910 @@
|
|
+/*
|
|
+ * 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 deactvating 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 IDR. 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 */
|
|
+static DEFINE_IDR(kdbus_node_idr);
|
|
+static DECLARE_RWSEM(kdbus_node_idr_lock);
|
|
+
|
|
+/**
|
|
+ * 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);
|
|
+ }
|
|
+
|
|
+ down_write(&kdbus_node_idr_lock);
|
|
+ ret = idr_alloc(&kdbus_node_idr, node, 1, 0, GFP_KERNEL);
|
|
+ if (ret >= 0)
|
|
+ node->id = ret;
|
|
+ up_write(&kdbus_node_idr_lock);
|
|
+
|
|
+ if (ret < 0)
|
|
+ return 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);
|
|
+
|
|
+ down_write(&kdbus_node_idr_lock);
|
|
+ if (safe.id > 0)
|
|
+ idr_remove(&kdbus_node_idr, safe.id);
|
|
+ /* drop caches after last node to not leak memory on unload */
|
|
+ if (idr_is_empty(&kdbus_node_idr)) {
|
|
+ idr_destroy(&kdbus_node_idr);
|
|
+ idr_init(&kdbus_node_idr);
|
|
+ }
|
|
+ up_write(&kdbus_node_idr_lock);
|
|
+
|
|
+ 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 somone 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
|
|
index 0000000..be125ce
|
|
--- /dev/null
|
|
+++ b/ipc/kdbus/node.h
|
|
@@ -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
|
|
+ *
|
|
+ * 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)
|
|
+
|
|
+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
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 367632aaf80758c61568f19cd30a86635141cca5 Mon Sep 17 00:00:00 2001
|
|
From: Daniel Mack <daniel@zonque.org>
|
|
Date: Thu, 11 Sep 2014 18:58:45 +0200
|
|
Subject: [PATCH 07/78] kdbus: add code to gather metadata
|
|
|
|
A connection chooses which metadata it wants to have attached to each
|
|
message it receives with kdbus_cmd_hello.attach_flags. The metadata
|
|
will be attached as items to the messages. All metadata refers to
|
|
information about the sending task at sending time, unless otherwise
|
|
stated. Also, the metadata is copied, not referenced, so even if the
|
|
sending task doesn't exist anymore at the time the message is received,
|
|
the information is still preserved.
|
|
|
|
In traditional D-Bus, userspace tasks like polkit or journald make a
|
|
live lookup in procfs and sysfs to gain information about a sending
|
|
task. This is racy, of course, as in a a connection-less system like
|
|
D-Bus, the originating peer can go away immediately after sending the
|
|
message. As we're moving D-Bus prmitives into the kernel, we have to
|
|
provide the same semantics here, and inform the receiving peer on the
|
|
live credentials of the sending peer.
|
|
|
|
Metadata is collected at the following times.
|
|
|
|
* When a bus is created (KDBUS_CMD_MAKE), information about the
|
|
calling task is collected. This data is returned by the kernel
|
|
via the KDBUS_CMD_BUS_CREATOR_INFO call.
|
|
|
|
* When a connection is created (KDBUS_CMD_HELLO), 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
|
|
(KDBUS_CMD_CONN_INFO). Only metadata that a connection allowed to
|
|
be sent (by setting its bit in attach_flags_send) will be exported
|
|
in this way.
|
|
|
|
* When a message is sent (KDBUS_CMD_SEND), information about the
|
|
sending task and the sending connection are 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 kdbus.connection(7)), the message will not
|
|
be augmented by any information about the currently sending task.
|
|
|
|
Which metadata items are actually delivered depends on the following
|
|
sets and masks:
|
|
|
|
(a) the system-wide kmod creds mask
|
|
(module parameter 'attach_flags_mask')
|
|
|
|
(b) the per-connection send creds mask, set by the connecting client
|
|
|
|
(c) the per-connection receive creds mask, set by the connecting client
|
|
|
|
(d) the per-bus minimal creds mask, set by the bus creator
|
|
|
|
(e) the per-bus owner creds mask, set by the bus creator
|
|
|
|
(f) the mask specified when querying creds of a bus peer
|
|
|
|
(g) the mask specified when querying creds of a bus owner
|
|
|
|
With the following rules:
|
|
|
|
[1] The creds attached to messages are determined as a & b & c.
|
|
|
|
[2] When connecting to a bus (KDBUS_CMD_HELLO), and ~b & d != 0,
|
|
the call will fail with, -1, and errno is set to ECONNREFUSED.
|
|
|
|
[3] When querying creds of a bus peer, the creds returned
|
|
are a & b & f.
|
|
|
|
[4] When querying creds of a bus owner, the creds returned
|
|
are a & e & g.
|
|
|
|
See kdbus.metadata(7) and kdbus.item(7) for more details on which
|
|
metadata can currently be attached to messages.
|
|
|
|
Signed-off-by: Daniel Mack <daniel@zonque.org>
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Djalal Harouni <tixxdz@opendz.org>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
ipc/kdbus/metadata.c | 1164 ++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
ipc/kdbus/metadata.h | 57 +++
|
|
2 files changed, 1221 insertions(+)
|
|
create mode 100644 ipc/kdbus/metadata.c
|
|
create mode 100644 ipc/kdbus/metadata.h
|
|
|
|
diff --git a/ipc/kdbus/metadata.c b/ipc/kdbus/metadata.c
|
|
new file mode 100644
|
|
index 0000000..06e0a54
|
|
--- /dev/null
|
|
+++ b/ipc/kdbus/metadata.c
|
|
@@ -0,0 +1,1164 @@
|
|
+/*
|
|
+ * 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 <linux/version.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
|
|
+ * @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
|
|
+ * @auxgrps: Auxiliary groups
|
|
+ * @n_auxgrps: Number of items in @auxgrps
|
|
+ * @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
|
|
+ * @caps: Capabilities
|
|
+ * @caps_namespace: User-namespace of @caps
|
|
+ * @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 */
|
|
+ kuid_t uid, euid, suid, fsuid;
|
|
+ kgid_t gid, egid, sgid, fsgid;
|
|
+
|
|
+ /* KDBUS_ITEM_PIDS */
|
|
+ struct pid *pid;
|
|
+ struct pid *tgid;
|
|
+ struct pid *ppid;
|
|
+
|
|
+ /* KDBUS_ITEM_AUXGROUPS */
|
|
+ kgid_t *auxgrps;
|
|
+ size_t n_auxgrps;
|
|
+
|
|
+ /* 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_CAPS */
|
|
+ struct caps {
|
|
+ /* binary compatible to kdbus_caps */
|
|
+ u32 last_cap;
|
|
+ struct {
|
|
+ u32 caps[_KERNEL_CAPABILITY_U32S];
|
|
+ } set[4];
|
|
+ } caps;
|
|
+ struct user_namespace *caps_namespace;
|
|
+
|
|
+ /* 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;
|
|
+};
|
|
+
|
|
+/**
|
|
+ * 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);
|
|
+ put_user_ns(mp->caps_namespace);
|
|
+ put_pid(mp->ppid);
|
|
+ put_pid(mp->tgid);
|
|
+ put_pid(mp->pid);
|
|
+
|
|
+ kfree(mp->seclabel);
|
|
+ kfree(mp->auxgrps);
|
|
+ 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_creds(struct kdbus_meta_proc *mp)
|
|
+{
|
|
+ mp->uid = current_uid();
|
|
+ mp->euid = current_euid();
|
|
+ mp->suid = current_suid();
|
|
+ mp->fsuid = current_fsuid();
|
|
+
|
|
+ mp->gid = current_gid();
|
|
+ mp->egid = current_egid();
|
|
+ mp->sgid = current_sgid();
|
|
+ mp->fsgid = current_fsgid();
|
|
+
|
|
+ mp->valid |= KDBUS_ATTACH_CREDS;
|
|
+}
|
|
+
|
|
+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 int kdbus_meta_proc_collect_auxgroups(struct kdbus_meta_proc *mp)
|
|
+{
|
|
+ struct group_info *info;
|
|
+ size_t i;
|
|
+
|
|
+ info = get_current_groups();
|
|
+
|
|
+ if (info->ngroups > 0) {
|
|
+ mp->auxgrps = kmalloc_array(info->ngroups, sizeof(kgid_t),
|
|
+ GFP_KERNEL);
|
|
+ if (!mp->auxgrps) {
|
|
+ put_group_info(info);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < info->ngroups; i++)
|
|
+ mp->auxgrps[i] = GROUP_AT(info, i);
|
|
+ }
|
|
+
|
|
+ mp->n_auxgrps = info->ngroups;
|
|
+ put_group_info(info);
|
|
+ mp->valid |= KDBUS_ATTACH_AUXGROUPS;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+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 mm_struct *mm;
|
|
+
|
|
+ mm = get_task_mm(current);
|
|
+ if (!mm)
|
|
+ return;
|
|
+
|
|
+ down_read(&mm->mmap_sem);
|
|
+ if (mm->exe_file) {
|
|
+ mp->exe_path = mm->exe_file->f_path;
|
|
+ path_get(&mp->exe_path);
|
|
+ get_fs_root(current->fs, &mp->root_path);
|
|
+ mp->valid |= KDBUS_ATTACH_EXE;
|
|
+ }
|
|
+ up_read(&mm->mmap_sem);
|
|
+
|
|
+ mmput(mm);
|
|
+}
|
|
+
|
|
+static int kdbus_meta_proc_collect_cmdline(struct kdbus_meta_proc *mp)
|
|
+{
|
|
+ struct mm_struct *mm;
|
|
+ char *cmdline;
|
|
+
|
|
+ mm = get_task_mm(current);
|
|
+ if (!mm)
|
|
+ return 0;
|
|
+
|
|
+ if (!mm->arg_end) {
|
|
+ mmput(mm);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ cmdline = strndup_user((const char __user *)mm->arg_start,
|
|
+ mm->arg_end - mm->arg_start);
|
|
+ mmput(mm);
|
|
+
|
|
+ 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 void kdbus_meta_proc_collect_caps(struct kdbus_meta_proc *mp)
|
|
+{
|
|
+ const struct cred *c = current_cred();
|
|
+ int i;
|
|
+
|
|
+ /* ABI: "last_cap" equals /proc/sys/kernel/cap_last_cap */
|
|
+ mp->caps.last_cap = CAP_LAST_CAP;
|
|
+ mp->caps_namespace = get_user_ns(current_user_ns());
|
|
+
|
|
+ CAP_FOR_EACH_U32(i) {
|
|
+ mp->caps.set[0].caps[i] = c->cap_inheritable.cap[i];
|
|
+ mp->caps.set[1].caps[i] = c->cap_permitted.cap[i];
|
|
+ mp->caps.set[2].caps[i] = c->cap_effective.cap[i];
|
|
+ mp->caps.set[3].caps[i] = c->cap_bset.cap[i];
|
|
+ }
|
|
+
|
|
+ /* clear unused bits */
|
|
+ for (i = 0; i < 4; i++)
|
|
+ mp->caps.set[i].caps[CAP_TO_INDEX(CAP_LAST_CAP)] &=
|
|
+ CAP_LAST_U32_VALID_MASK;
|
|
+
|
|
+ mp->valid |= KDBUS_ATTACH_CAPS;
|
|
+}
|
|
+
|
|
+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);
|
|
+
|
|
+ if ((what & KDBUS_ATTACH_CREDS) &&
|
|
+ !(mp->collected & KDBUS_ATTACH_CREDS)) {
|
|
+ kdbus_meta_proc_collect_creds(mp);
|
|
+ mp->collected |= KDBUS_ATTACH_CREDS;
|
|
+ }
|
|
+
|
|
+ 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_AUXGROUPS) &&
|
|
+ !(mp->collected & KDBUS_ATTACH_AUXGROUPS)) {
|
|
+ ret = kdbus_meta_proc_collect_auxgroups(mp);
|
|
+ if (ret < 0)
|
|
+ goto exit_unlock;
|
|
+ mp->collected |= KDBUS_ATTACH_AUXGROUPS;
|
|
+ }
|
|
+
|
|
+ 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_CAPS) &&
|
|
+ !(mp->collected & KDBUS_ATTACH_CAPS)) {
|
|
+ kdbus_meta_proc_collect_caps(mp);
|
|
+ mp->collected |= KDBUS_ATTACH_CAPS;
|
|
+ }
|
|
+
|
|
+ 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_proc_fake() - Fill process metadata from faked credentials
|
|
+ * @mp: Metadata
|
|
+ * @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. A call to this function
|
|
+ * is considered an alternative to calling kdbus_meta_add_current(), which
|
|
+ * derives the same information from the 'current' task.
|
|
+ *
|
|
+ * This call uses the current task's namespaces to resolve the given
|
|
+ * information.
|
|
+ *
|
|
+ * Return: 0 on success, negative error number otherwise.
|
|
+ */
|
|
+int kdbus_meta_proc_fake(struct kdbus_meta_proc *mp,
|
|
+ const struct kdbus_creds *creds,
|
|
+ const struct kdbus_pids *pids,
|
|
+ const char *seclabel)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ if (!mp)
|
|
+ return 0;
|
|
+
|
|
+ mutex_lock(&mp->lock);
|
|
+
|
|
+ if (creds && !(mp->collected & KDBUS_ATTACH_CREDS)) {
|
|
+ struct user_namespace *ns = current_user_ns();
|
|
+
|
|
+ mp->uid = make_kuid(ns, creds->uid);
|
|
+ mp->euid = make_kuid(ns, creds->euid);
|
|
+ mp->suid = make_kuid(ns, creds->suid);
|
|
+ mp->fsuid = make_kuid(ns, creds->fsuid);
|
|
+
|
|
+ mp->gid = make_kgid(ns, creds->gid);
|
|
+ mp->egid = make_kgid(ns, creds->egid);
|
|
+ mp->sgid = make_kgid(ns, creds->sgid);
|
|
+ mp->fsgid = make_kgid(ns, creds->fsgid);
|
|
+
|
|
+ if ((creds->uid != (uid_t)-1 && !uid_valid(mp->uid)) ||
|
|
+ (creds->euid != (uid_t)-1 && !uid_valid(mp->euid)) ||
|
|
+ (creds->suid != (uid_t)-1 && !uid_valid(mp->suid)) ||
|
|
+ (creds->fsuid != (uid_t)-1 && !uid_valid(mp->fsuid)) ||
|
|
+ (creds->gid != (gid_t)-1 && !gid_valid(mp->gid)) ||
|
|
+ (creds->egid != (gid_t)-1 && !gid_valid(mp->egid)) ||
|
|
+ (creds->sgid != (gid_t)-1 && !gid_valid(mp->sgid)) ||
|
|
+ (creds->fsgid != (gid_t)-1 && !gid_valid(mp->fsgid))) {
|
|
+ ret = -EINVAL;
|
|
+ goto exit_unlock;
|
|
+ }
|
|
+
|
|
+ mp->valid |= KDBUS_ATTACH_CREDS;
|
|
+ mp->collected |= KDBUS_ATTACH_CREDS;
|
|
+ }
|
|
+
|
|
+ if (pids && !(mp->collected & KDBUS_ATTACH_PIDS)) {
|
|
+ mp->pid = get_pid(find_vpid(pids->tid));
|
|
+ mp->tgid = get_pid(find_vpid(pids->pid));
|
|
+ mp->ppid = get_pid(find_vpid(pids->ppid));
|
|
+
|
|
+ if ((pids->tid != 0 && !mp->pid) ||
|
|
+ (pids->pid != 0 && !mp->tgid) ||
|
|
+ (pids->ppid != 0 && !mp->ppid)) {
|
|
+ put_pid(mp->pid);
|
|
+ put_pid(mp->tgid);
|
|
+ put_pid(mp->ppid);
|
|
+ mp->pid = NULL;
|
|
+ mp->tgid = NULL;
|
|
+ mp->ppid = NULL;
|
|
+ ret = -EINVAL;
|
|
+ goto exit_unlock;
|
|
+ }
|
|
+
|
|
+ mp->valid |= KDBUS_ATTACH_PIDS;
|
|
+ mp->collected |= KDBUS_ATTACH_PIDS;
|
|
+ }
|
|
+
|
|
+ if (seclabel && !(mp->collected & KDBUS_ATTACH_SECLABEL)) {
|
|
+ mp->seclabel = kstrdup(seclabel, GFP_KERNEL);
|
|
+ if (!mp->seclabel) {
|
|
+ ret = -ENOMEM;
|
|
+ goto exit_unlock;
|
|
+ }
|
|
+
|
|
+ mp->valid |= KDBUS_ATTACH_SECLABEL;
|
|
+ mp->collected |= KDBUS_ATTACH_SECLABEL;
|
|
+ }
|
|
+
|
|
+ ret = 0;
|
|
+
|
|
+exit_unlock:
|
|
+ mutex_unlock(&mp->lock);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * 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,
|
|
+ struct kdbus_kmsg *kmsg)
|
|
+{
|
|
+ struct timespec ts;
|
|
+
|
|
+ ktime_get_ts(&ts);
|
|
+ mc->ts.monotonic_ns = timespec_to_ns(&ts);
|
|
+
|
|
+ ktime_get_real_ts(&ts);
|
|
+ mc->ts.realtime_ns = timespec_to_ns(&ts);
|
|
+
|
|
+ if (kmsg)
|
|
+ mc->ts.seqnum = kmsg->seq;
|
|
+
|
|
+ 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_entry *e;
|
|
+ struct kdbus_item *item;
|
|
+ size_t slen, size;
|
|
+
|
|
+ lockdep_assert_held(&conn->ep->bus->name_registry->rwlock);
|
|
+
|
|
+ size = 0;
|
|
+ list_for_each_entry(e, &conn->names_list, conn_entry)
|
|
+ size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_name) +
|
|
+ strlen(e->name) + 1);
|
|
+
|
|
+ if (!size)
|
|
+ return 0;
|
|
+
|
|
+ item = kmalloc(size, GFP_KERNEL);
|
|
+ if (!item)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ mc->owned_names_items = item;
|
|
+ mc->owned_names_size = size;
|
|
+
|
|
+ list_for_each_entry(e, &conn->names_list, conn_entry) {
|
|
+ slen = strlen(e->name) + 1;
|
|
+ kdbus_item_set(item, KDBUS_ITEM_OWNED_NAME, NULL,
|
|
+ sizeof(struct kdbus_name) + slen);
|
|
+ item->name.flags = e->flags;
|
|
+ memcpy(item->name.name, e->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 + 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
|
|
+ * @kmsg: Kmsg to collect data from
|
|
+ * @conn: Connection to collect data from
|
|
+ * @what: Attach flags to collect
|
|
+ *
|
|
+ * This collects connection metadata from @kmsg 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_kmsg *kmsg,
|
|
+ struct kdbus_conn *conn,
|
|
+ u64 what)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ if (!mc || !(what & (KDBUS_ATTACH_TIMESTAMP |
|
|
+ KDBUS_ATTACH_NAMES |
|
|
+ KDBUS_ATTACH_CONN_DESCRIPTION)))
|
|
+ return 0;
|
|
+
|
|
+ mutex_lock(&mc->lock);
|
|
+
|
|
+ if (kmsg && (what & KDBUS_ATTACH_TIMESTAMP) &&
|
|
+ !(mc->collected & KDBUS_ATTACH_TIMESTAMP)) {
|
|
+ kdbus_meta_conn_collect_timestamp(mc, kmsg);
|
|
+ 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;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * kdbus_meta_export_prepare() - Prepare metadata for export
|
|
+ * @mp: Process metadata, or NULL
|
|
+ * @mc: Connection metadata, or NULL
|
|
+ * @mask: Pointer to mask of KDBUS_ATTACH_* flags to export
|
|
+ * @sz: Pointer to return the size needed by the metadata
|
|
+ *
|
|
+ * Does a conservative calculation of how much space metadata information
|
|
+ * will take up during export. It is 'conservative' because for string
|
|
+ * translations in namespaces, it will use the kernel namespaces, which is
|
|
+ * the longest possible version.
|
|
+ *
|
|
+ * The actual size consumed by kdbus_meta_export() may hence vary from the
|
|
+ * one reported here, but it is guaranteed never to be greater.
|
|
+ *
|
|
+ * Return: 0 on success, negative error number otherwise.
|
|
+ */
|
|
+int kdbus_meta_export_prepare(struct kdbus_meta_proc *mp,
|
|
+ struct kdbus_meta_conn *mc,
|
|
+ u64 *mask, size_t *sz)
|
|
+{
|
|
+ char *exe_pathname = NULL;
|
|
+ void *exe_page = NULL;
|
|
+ size_t size = 0;
|
|
+ u64 valid = 0;
|
|
+ int ret = 0;
|
|
+
|
|
+ if (mp) {
|
|
+ mutex_lock(&mp->lock);
|
|
+ valid |= mp->valid;
|
|
+ mutex_unlock(&mp->lock);
|
|
+ }
|
|
+
|
|
+ if (mc) {
|
|
+ mutex_lock(&mc->lock);
|
|
+ valid |= mc->valid;
|
|
+ mutex_unlock(&mc->lock);
|
|
+ }
|
|
+
|
|
+ *mask &= valid;
|
|
+ *mask &= kdbus_meta_attach_mask;
|
|
+
|
|
+ if (!*mask)
|
|
+ goto exit;
|
|
+
|
|
+ /* process metadata */
|
|
+
|
|
+ if (mp && (*mask & KDBUS_ATTACH_CREDS))
|
|
+ size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_creds));
|
|
+
|
|
+ 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->n_auxgrps * 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 (mp && (*mask & KDBUS_ATTACH_EXE)) {
|
|
+ exe_page = (void *)__get_free_page(GFP_TEMPORARY);
|
|
+ if (!exe_page) {
|
|
+ ret = -ENOMEM;
|
|
+ goto exit;
|
|
+ }
|
|
+
|
|
+ exe_pathname = d_path(&mp->exe_path, exe_page, PAGE_SIZE);
|
|
+ if (IS_ERR(exe_pathname)) {
|
|
+ ret = PTR_ERR(exe_pathname);
|
|
+ goto exit;
|
|
+ }
|
|
+
|
|
+ size += KDBUS_ITEM_SIZE(strlen(exe_pathname) + 1);
|
|
+ free_page((unsigned long)exe_page);
|
|
+ }
|
|
+
|
|
+ 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(mp->caps));
|
|
+
|
|
+ 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 += 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));
|
|
+
|
|
+exit:
|
|
+ *sz = size;
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int kdbus_meta_push_kvec(struct kvec *kvec,
|
|
+ struct kdbus_item_header *hdr,
|
|
+ u64 type, void *payload,
|
|
+ size_t payload_size, u64 *size)
|
|
+{
|
|
+ hdr->type = type;
|
|
+ hdr->size = KDBUS_ITEM_HEADER_SIZE + payload_size;
|
|
+ kdbus_kvec_set(kvec++, hdr, sizeof(*hdr), size);
|
|
+ kdbus_kvec_set(kvec++, payload, payload_size, size);
|
|
+ return 2 + !!kdbus_kvec_pad(kvec++, size);
|
|
+}
|
|
+
|
|
+/* This is equivalent to from_kuid_munged(), but maps INVALID_UID to itself */
|
|
+static uid_t kdbus_from_kuid_keep(kuid_t uid)
|
|
+{
|
|
+ return uid_valid(uid) ?
|
|
+ from_kuid_munged(current_user_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(kgid_t gid)
|
|
+{
|
|
+ return gid_valid(gid) ?
|
|
+ from_kgid_munged(current_user_ns(), gid) : ((gid_t)-1);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * kdbus_meta_export() - export information from metadata into a slice
|
|
+ * @mp: Process metadata, or NULL
|
|
+ * @mc: Connection metadata, or NULL
|
|
+ * @mask: Mask of KDBUS_ATTACH_* flags to export
|
|
+ * @slice: The slice to export to
|
|
+ * @offset: The offset inside @slice to write to
|
|
+ * @real_size: The real size the metadata consumed
|
|
+ *
|
|
+ * This function exports information from metadata into @slice at offset
|
|
+ * @offset inside that slice. Only information that is requested in @mask
|
|
+ * and that has been collected before is exported.
|
|
+ *
|
|
+ * In order to make sure not to write out of bounds, @mask must be the same
|
|
+ * value that was previously returned from kdbus_meta_export_prepare(). The
|
|
+ * function will, however, not necessarily write as many bytes as returned by
|
|
+ * kdbus_meta_export_prepare(); depending on the namespaces in question, it
|
|
+ * might use up less than that.
|
|
+ *
|
|
+ * All information will be translated using the current namespaces.
|
|
+ *
|
|
+ * Return: 0 on success, negative error number otherwise.
|
|
+ */
|
|
+int kdbus_meta_export(struct kdbus_meta_proc *mp,
|
|
+ struct kdbus_meta_conn *mc,
|
|
+ u64 mask,
|
|
+ struct kdbus_pool_slice *slice,
|
|
+ off_t offset,
|
|
+ size_t *real_size)
|
|
+{
|
|
+ struct user_namespace *user_ns = current_user_ns();
|
|
+ struct kdbus_item_header item_hdr[13], *hdr;
|
|
+ char *exe_pathname = NULL;
|
|
+ struct kdbus_creds creds;
|
|
+ struct kdbus_pids pids;
|
|
+ void *exe_page = NULL;
|
|
+ struct kvec kvec[40];
|
|
+ u64 *auxgrps = NULL;
|
|
+ size_t cnt = 0;
|
|
+ u64 size = 0;
|
|
+ int ret = 0;
|
|
+
|
|
+ hdr = &item_hdr[0];
|
|
+
|
|
+ /*
|
|
+ * TODO: We currently have no sane way of translating a set of caps
|
|
+ * between different user namespaces. Until that changes, we have
|
|
+ * to drop such items.
|
|
+ */
|
|
+ if (mp && mp->caps_namespace != user_ns)
|
|
+ mask &= ~KDBUS_ATTACH_CAPS;
|
|
+
|
|
+ if (mask == 0) {
|
|
+ *real_size = 0;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ /* process metadata */
|
|
+
|
|
+ if (mp && (mask & KDBUS_ATTACH_CREDS)) {
|
|
+ creds.uid = kdbus_from_kuid_keep(mp->uid);
|
|
+ creds.euid = kdbus_from_kuid_keep(mp->euid);
|
|
+ creds.suid = kdbus_from_kuid_keep(mp->suid);
|
|
+ creds.fsuid = kdbus_from_kuid_keep(mp->fsuid);
|
|
+ creds.gid = kdbus_from_kgid_keep(mp->gid);
|
|
+ creds.egid = kdbus_from_kgid_keep(mp->egid);
|
|
+ creds.sgid = kdbus_from_kgid_keep(mp->sgid);
|
|
+ creds.fsgid = kdbus_from_kgid_keep(mp->fsgid);
|
|
+
|
|
+ cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++, KDBUS_ITEM_CREDS,
|
|
+ &creds, sizeof(creds), &size);
|
|
+ }
|
|
+
|
|
+ if (mp && (mask & KDBUS_ATTACH_PIDS)) {
|
|
+ pids.pid = pid_vnr(mp->tgid);
|
|
+ pids.tid = pid_vnr(mp->pid);
|
|
+ pids.ppid = pid_vnr(mp->ppid);
|
|
+
|
|
+ cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++, KDBUS_ITEM_PIDS,
|
|
+ &pids, sizeof(pids), &size);
|
|
+ }
|
|
+
|
|
+ if (mp && (mask & KDBUS_ATTACH_AUXGROUPS)) {
|
|
+ size_t payload_size = mp->n_auxgrps * sizeof(u64);
|
|
+ int i;
|
|
+
|
|
+ auxgrps = kmalloc(payload_size, GFP_KERNEL);
|
|
+ if (!auxgrps) {
|
|
+ ret = -ENOMEM;
|
|
+ goto exit;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < mp->n_auxgrps; i++)
|
|
+ auxgrps[i] = from_kgid_munged(user_ns, mp->auxgrps[i]);
|
|
+
|
|
+ cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
|
|
+ KDBUS_ITEM_AUXGROUPS,
|
|
+ auxgrps, payload_size, &size);
|
|
+ }
|
|
+
|
|
+ if (mp && (mask & KDBUS_ATTACH_TID_COMM))
|
|
+ cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
|
|
+ KDBUS_ITEM_TID_COMM, mp->tid_comm,
|
|
+ strlen(mp->tid_comm) + 1, &size);
|
|
+
|
|
+ if (mp && (mask & KDBUS_ATTACH_PID_COMM))
|
|
+ cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
|
|
+ KDBUS_ITEM_PID_COMM, mp->pid_comm,
|
|
+ strlen(mp->pid_comm) + 1, &size);
|
|
+
|
|
+ if (mp && (mask & KDBUS_ATTACH_EXE)) {
|
|
+ struct path p;
|
|
+
|
|
+ /*
|
|
+ * TODO: 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, &mp->root_path)) {
|
|
+ exe_page = (void *)__get_free_page(GFP_TEMPORARY);
|
|
+ if (!exe_page) {
|
|
+ path_put(&p);
|
|
+ ret = -ENOMEM;
|
|
+ goto exit;
|
|
+ }
|
|
+
|
|
+ exe_pathname = d_path(&mp->exe_path, exe_page,
|
|
+ PAGE_SIZE);
|
|
+ if (IS_ERR(exe_pathname)) {
|
|
+ path_put(&p);
|
|
+ ret = PTR_ERR(exe_pathname);
|
|
+ goto exit;
|
|
+ }
|
|
+
|
|
+ cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
|
|
+ KDBUS_ITEM_EXE,
|
|
+ exe_pathname,
|
|
+ strlen(exe_pathname) + 1,
|
|
+ &size);
|
|
+ }
|
|
+ path_put(&p);
|
|
+ }
|
|
+
|
|
+ if (mp && (mask & KDBUS_ATTACH_CMDLINE))
|
|
+ cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
|
|
+ KDBUS_ITEM_CMDLINE, mp->cmdline,
|
|
+ strlen(mp->cmdline) + 1, &size);
|
|
+
|
|
+ if (mp && (mask & KDBUS_ATTACH_CGROUP))
|
|
+ cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
|
|
+ KDBUS_ITEM_CGROUP, mp->cgroup,
|
|
+ strlen(mp->cgroup) + 1, &size);
|
|
+
|
|
+ if (mp && (mask & KDBUS_ATTACH_CAPS))
|
|
+ cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
|
|
+ KDBUS_ITEM_CAPS, &mp->caps,
|
|
+ sizeof(mp->caps), &size);
|
|
+
|
|
+ if (mp && (mask & KDBUS_ATTACH_SECLABEL))
|
|
+ cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
|
|
+ KDBUS_ITEM_SECLABEL, mp->seclabel,
|
|
+ strlen(mp->seclabel) + 1, &size);
|
|
+
|
|
+ if (mp && (mask & KDBUS_ATTACH_AUDIT)) {
|
|
+ struct kdbus_audit a = {
|
|
+ .loginuid = from_kuid(user_ns, mp->audit_loginuid),
|
|
+ .sessionid = mp->audit_sessionid,
|
|
+ };
|
|
+
|
|
+ cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++, KDBUS_ITEM_AUDIT,
|
|
+ &a, sizeof(a), &size);
|
|
+ }
|
|
+
|
|
+ /* connection metadata */
|
|
+
|
|
+ if (mc && (mask & KDBUS_ATTACH_NAMES))
|
|
+ kdbus_kvec_set(&kvec[cnt++], mc->owned_names_items,
|
|
+ mc->owned_names_size, &size);
|
|
+
|
|
+ if (mc && (mask & KDBUS_ATTACH_CONN_DESCRIPTION))
|
|
+ cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
|
|
+ KDBUS_ITEM_CONN_DESCRIPTION,
|
|
+ mc->conn_description,
|
|
+ strlen(mc->conn_description) + 1,
|
|
+ &size);
|
|
+
|
|
+ if (mc && (mask & KDBUS_ATTACH_TIMESTAMP))
|
|
+ cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
|
|
+ KDBUS_ITEM_TIMESTAMP, &mc->ts,
|
|
+ sizeof(mc->ts), &size);
|
|
+
|
|
+ ret = kdbus_pool_slice_copy_kvec(slice, offset, kvec, cnt, size);
|
|
+ *real_size = size;
|
|
+
|
|
+exit:
|
|
+ kfree(auxgrps);
|
|
+
|
|
+ if (exe_page)
|
|
+ free_page((unsigned long)exe_page);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * kdbus_meta_calc_attach_flags() - calculate attach flags for a sender
|
|
+ * and a receiver
|
|
+ * @sender: Sending connection
|
|
+ * @receiver: Receiving connection
|
|
+ *
|
|
+ * Return: the attach flags both the sender and the receiver have opted-in
|
|
+ * for.
|
|
+ */
|
|
+u64 kdbus_meta_calc_attach_flags(const struct kdbus_conn *sender,
|
|
+ const struct kdbus_conn *receiver)
|
|
+{
|
|
+ return atomic64_read(&sender->attach_flags_send) &
|
|
+ atomic64_read(&receiver->attach_flags_recv);
|
|
+}
|
|
diff --git a/ipc/kdbus/metadata.h b/ipc/kdbus/metadata.h
|
|
new file mode 100644
|
|
index 0000000..42c942b
|
|
--- /dev/null
|
|
+++ b/ipc/kdbus/metadata.h
|
|
@@ -0,0 +1,57 @@
|
|
+/*
|
|
+ * 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_kmsg;
|
|
+struct kdbus_pool_slice;
|
|
+
|
|
+struct kdbus_meta_proc;
|
|
+struct kdbus_meta_conn;
|
|
+
|
|
+extern unsigned long long kdbus_meta_attach_mask;
|
|
+
|
|
+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);
|
|
+int kdbus_meta_proc_fake(struct kdbus_meta_proc *mp,
|
|
+ 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_kmsg *kmsg,
|
|
+ struct kdbus_conn *conn,
|
|
+ u64 what);
|
|
+
|
|
+int kdbus_meta_export_prepare(struct kdbus_meta_proc *mp,
|
|
+ struct kdbus_meta_conn *mc,
|
|
+ u64 *mask, size_t *sz);
|
|
+int kdbus_meta_export(struct kdbus_meta_proc *mp,
|
|
+ struct kdbus_meta_conn *mc,
|
|
+ u64 mask,
|
|
+ struct kdbus_pool_slice *slice,
|
|
+ off_t offset, size_t *real_size);
|
|
+u64 kdbus_meta_calc_attach_flags(const struct kdbus_conn *sender,
|
|
+ const struct kdbus_conn *receiver);
|
|
+
|
|
+#endif
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 10c565df756c1a1fea438670313b8ee64b7efb8a Mon Sep 17 00:00:00 2001
|
|
From: Daniel Mack <daniel@zonque.org>
|
|
Date: Thu, 11 Sep 2014 18:59:16 +0200
|
|
Subject: [PATCH 08/78] kdbus: add code for notifications and matches
|
|
|
|
This patch adds code for matches and notifications.
|
|
|
|
Notifications are broadcast messages generated by the kernel, which
|
|
notify subscribes when connections are created or destroyed, when
|
|
well-known-names have been claimed, released or changed ownership,
|
|
or when reply messages have timed out.
|
|
|
|
Matches are used to tell the kernel driver which broadcast messages
|
|
a connection is interested in. Matches can either be specific on one
|
|
of the kernel-generated notification types, or carry a bloom filter
|
|
mask to match against a message from userspace. The latter is a way
|
|
to pre-filter messages from other connections in order to mitigate
|
|
unnecessary wakeups.
|
|
|
|
Signed-off-by: Daniel Mack <daniel@zonque.org>
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Djalal Harouni <tixxdz@opendz.org>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
ipc/kdbus/match.c | 559 +++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
ipc/kdbus/match.h | 35 ++++
|
|
ipc/kdbus/notify.c | 248 ++++++++++++++++++++++++
|
|
ipc/kdbus/notify.h | 30 +++
|
|
4 files changed, 872 insertions(+)
|
|
create mode 100644 ipc/kdbus/match.c
|
|
create mode 100644 ipc/kdbus/match.h
|
|
create mode 100644 ipc/kdbus/notify.c
|
|
create mode 100644 ipc/kdbus/notify.h
|
|
|
|
diff --git a/ipc/kdbus/match.c b/ipc/kdbus/match.c
|
|
new file mode 100644
|
|
index 0000000..30cec1c
|
|
--- /dev/null
|
|
+++ b/ipc/kdbus/match.c
|
|
@@ -0,0 +1,559 @@
|
|
+/*
|
|
+ * 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 agains
|
|
+ * @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
|
|
+ * @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;
|
|
+ };
|
|
+ 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_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_rules(const struct kdbus_match_entry *entry,
|
|
+ struct kdbus_conn *conn_src,
|
|
+ struct kdbus_kmsg *kmsg)
|
|
+{
|
|
+ struct kdbus_match_rule *r;
|
|
+
|
|
+ if (conn_src)
|
|
+ lockdep_assert_held(&conn_src->ep->bus->name_registry->rwlock);
|
|
+
|
|
+ /*
|
|
+ * Walk all the rules and bail out immediately
|
|
+ * if any of them is unsatisfied.
|
|
+ */
|
|
+
|
|
+ list_for_each_entry(r, &entry->rules_list, rules_entry) {
|
|
+ if (conn_src) {
|
|
+ /* messages from userspace */
|
|
+
|
|
+ switch (r->type) {
|
|
+ case KDBUS_ITEM_BLOOM_MASK:
|
|
+ if (!kdbus_match_bloom(kmsg->bloom_filter,
|
|
+ &r->bloom_mask,
|
|
+ conn_src))
|
|
+ return false;
|
|
+ break;
|
|
+
|
|
+ case KDBUS_ITEM_ID:
|
|
+ if (r->src_id != conn_src->id &&
|
|
+ r->src_id != KDBUS_MATCH_ID_ANY)
|
|
+ return false;
|
|
+
|
|
+ break;
|
|
+
|
|
+ case KDBUS_ITEM_NAME:
|
|
+ if (!kdbus_conn_has_name(conn_src, r->name))
|
|
+ return false;
|
|
+
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ return false;
|
|
+ }
|
|
+ } else {
|
|
+ /* kernel notifications */
|
|
+
|
|
+ if (kmsg->notify_type != r->type)
|
|
+ return false;
|
|
+
|
|
+ switch (r->type) {
|
|
+ case KDBUS_ITEM_ID_ADD:
|
|
+ if (r->new_id != KDBUS_MATCH_ID_ANY &&
|
|
+ r->new_id != kmsg->notify_new_id)
|
|
+ return false;
|
|
+
|
|
+ break;
|
|
+
|
|
+ case KDBUS_ITEM_ID_REMOVE:
|
|
+ if (r->old_id != KDBUS_MATCH_ID_ANY &&
|
|
+ r->old_id != kmsg->notify_old_id)
|
|
+ return false;
|
|
+
|
|
+ break;
|
|
+
|
|
+ case KDBUS_ITEM_NAME_ADD:
|
|
+ case KDBUS_ITEM_NAME_CHANGE:
|
|
+ case KDBUS_ITEM_NAME_REMOVE:
|
|
+ if ((r->old_id != KDBUS_MATCH_ID_ANY &&
|
|
+ r->old_id != kmsg->notify_old_id) ||
|
|
+ (r->new_id != KDBUS_MATCH_ID_ANY &&
|
|
+ r->new_id != kmsg->notify_new_id) ||
|
|
+ (r->name && kmsg->notify_name &&
|
|
+ strcmp(r->name, kmsg->notify_name) != 0))
|
|
+ return false;
|
|
+
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * kdbus_match_db_match_kmsg() - match a kmsg object agains the database entries
|
|
+ * @mdb: The match database
|
|
+ * @conn_src: The connection object originating the message
|
|
+ * @kmsg: The kmsg to perform the match on
|
|
+ *
|
|
+ * 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_kmsg(struct kdbus_match_db *mdb,
|
|
+ struct kdbus_conn *conn_src,
|
|
+ struct kdbus_kmsg *kmsg)
|
|
+{
|
|
+ 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, kmsg);
|
|
+ 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_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 and KDBUS_ITEM_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_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_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
|
|
index 0000000..ea42929
|
|
--- /dev/null
|
|
+++ b/ipc/kdbus/match.h
|
|
@@ -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_kmsg;
|
|
+struct kdbus_match_db;
|
|
+
|
|
+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_kmsg(struct kdbus_match_db *db,
|
|
+ struct kdbus_conn *conn_src,
|
|
+ struct kdbus_kmsg *kmsg);
|
|
+
|
|
+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/notify.c b/ipc/kdbus/notify.c
|
|
new file mode 100644
|
|
index 0000000..e4a4542
|
|
--- /dev/null
|
|
+++ b/ipc/kdbus/notify.c
|
|
@@ -0,0 +1,248 @@
|
|
+/*
|
|
+ * 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_kmsg *kmsg,
|
|
+ struct kdbus_bus *bus)
|
|
+{
|
|
+ spin_lock(&bus->notify_lock);
|
|
+ list_add_tail(&kmsg->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_kmsg *kmsg = NULL;
|
|
+
|
|
+ WARN_ON(id == 0);
|
|
+
|
|
+ kmsg = kdbus_kmsg_new(bus, 0);
|
|
+ if (IS_ERR(kmsg))
|
|
+ return PTR_ERR(kmsg);
|
|
+
|
|
+ /*
|
|
+ * a kernel-generated notification can only contain one
|
|
+ * struct kdbus_item, so make a shortcut here for
|
|
+ * faster lookup in the match db.
|
|
+ */
|
|
+ kmsg->notify_type = msg_type;
|
|
+ kmsg->msg.flags = KDBUS_MSG_SIGNAL;
|
|
+ kmsg->msg.dst_id = id;
|
|
+ kmsg->msg.src_id = KDBUS_SRC_ID_KERNEL;
|
|
+ kmsg->msg.payload_type = KDBUS_PAYLOAD_KERNEL;
|
|
+ kmsg->msg.cookie_reply = cookie;
|
|
+ kmsg->msg.items[0].type = msg_type;
|
|
+
|
|
+ kdbus_notify_add_tail(kmsg, 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)
|
|
+{
|
|
+ struct kdbus_kmsg *kmsg = NULL;
|
|
+ size_t name_len, extra_size;
|
|
+
|
|
+ name_len = strlen(name) + 1;
|
|
+ extra_size = sizeof(struct kdbus_notify_name_change) + name_len;
|
|
+ kmsg = kdbus_kmsg_new(bus, extra_size);
|
|
+ if (IS_ERR(kmsg))
|
|
+ return PTR_ERR(kmsg);
|
|
+
|
|
+ kmsg->msg.flags = KDBUS_MSG_SIGNAL;
|
|
+ kmsg->msg.dst_id = KDBUS_DST_ID_BROADCAST;
|
|
+ kmsg->msg.src_id = KDBUS_SRC_ID_KERNEL;
|
|
+ kmsg->msg.payload_type = KDBUS_PAYLOAD_KERNEL;
|
|
+ kmsg->notify_type = type;
|
|
+ kmsg->notify_old_id = old_id;
|
|
+ kmsg->notify_new_id = new_id;
|
|
+ kmsg->msg.items[0].type = type;
|
|
+ kmsg->msg.items[0].name_change.old_id.id = old_id;
|
|
+ kmsg->msg.items[0].name_change.old_id.flags = old_flags;
|
|
+ kmsg->msg.items[0].name_change.new_id.id = new_id;
|
|
+ kmsg->msg.items[0].name_change.new_id.flags = new_flags;
|
|
+ memcpy(kmsg->msg.items[0].name_change.name, name, name_len);
|
|
+ kmsg->notify_name = kmsg->msg.items[0].name_change.name;
|
|
+
|
|
+ kdbus_notify_add_tail(kmsg, 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_kmsg *kmsg = NULL;
|
|
+
|
|
+ kmsg = kdbus_kmsg_new(bus, sizeof(struct kdbus_notify_id_change));
|
|
+ if (IS_ERR(kmsg))
|
|
+ return PTR_ERR(kmsg);
|
|
+
|
|
+ kmsg->msg.flags = KDBUS_MSG_SIGNAL;
|
|
+ kmsg->msg.dst_id = KDBUS_DST_ID_BROADCAST;
|
|
+ kmsg->msg.src_id = KDBUS_SRC_ID_KERNEL;
|
|
+ kmsg->msg.payload_type = KDBUS_PAYLOAD_KERNEL;
|
|
+ kmsg->notify_type = type;
|
|
+
|
|
+ switch (type) {
|
|
+ case KDBUS_ITEM_ID_ADD:
|
|
+ kmsg->notify_new_id = id;
|
|
+ break;
|
|
+
|
|
+ case KDBUS_ITEM_ID_REMOVE:
|
|
+ kmsg->notify_old_id = id;
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ BUG();
|
|
+ }
|
|
+
|
|
+ kmsg->msg.items[0].type = type;
|
|
+ kmsg->msg.items[0].id_change.id = id;
|
|
+ kmsg->msg.items[0].id_change.flags = flags;
|
|
+
|
|
+ kdbus_notify_add_tail(kmsg, 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_kmsg *kmsg, *tmp;
|
|
+
|
|
+ mutex_lock(&bus->notify_flush_lock);
|
|
+ down_read(&bus->name_registry->rwlock);
|
|
+
|
|
+ spin_lock(&bus->notify_lock);
|
|
+ list_splice_init(&bus->notify_list, ¬ify_list);
|
|
+ spin_unlock(&bus->notify_lock);
|
|
+
|
|
+ list_for_each_entry_safe(kmsg, tmp, ¬ify_list, notify_entry) {
|
|
+ kdbus_meta_conn_collect(kmsg->conn_meta, kmsg, NULL,
|
|
+ KDBUS_ATTACH_TIMESTAMP);
|
|
+
|
|
+ if (kmsg->msg.dst_id != KDBUS_DST_ID_BROADCAST) {
|
|
+ struct kdbus_conn *conn;
|
|
+
|
|
+ conn = kdbus_bus_find_conn_by_id(bus, kmsg->msg.dst_id);
|
|
+ if (conn) {
|
|
+ kdbus_bus_eavesdrop(bus, NULL, kmsg);
|
|
+ kdbus_conn_entry_insert(NULL, conn, kmsg, NULL);
|
|
+ kdbus_conn_unref(conn);
|
|
+ }
|
|
+ } else {
|
|
+ kdbus_bus_broadcast(bus, NULL, kmsg);
|
|
+ }
|
|
+
|
|
+ list_del(&kmsg->notify_entry);
|
|
+ kdbus_kmsg_free(kmsg);
|
|
+ }
|
|
+
|
|
+ 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_kmsg *kmsg, *tmp;
|
|
+
|
|
+ list_for_each_entry_safe(kmsg, tmp, &bus->notify_list, notify_entry) {
|
|
+ list_del(&kmsg->notify_entry);
|
|
+ kdbus_kmsg_free(kmsg);
|
|
+ }
|
|
+}
|
|
diff --git a/ipc/kdbus/notify.h b/ipc/kdbus/notify.h
|
|
new file mode 100644
|
|
index 0000000..03df464
|
|
--- /dev/null
|
|
+++ b/ipc/kdbus/notify.h
|
|
@@ -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
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 091ae03577b2029e0b041231565e251b1e5e1c05 Mon Sep 17 00:00:00 2001
|
|
From: Daniel Mack <daniel@zonque.org>
|
|
Date: Thu, 11 Sep 2014 18:59:39 +0200
|
|
Subject: [PATCH 09/78] kdbus: add code for buses, domains and endpoints
|
|
|
|
Add the logic to handle the following entities:
|
|
|
|
Domain:
|
|
A domain is an unamed object containing a number of buses. A
|
|
domain is automatically created when an instance of kdbusfs
|
|
is mounted, and destroyed when it is unmounted.
|
|
Every domain offers its own 'control' device node to create
|
|
buses. Domains are isolated from each other.
|
|
|
|
Bus:
|
|
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
|
|
entry point to a bus, where clients establish the connection to, is
|
|
the "bus" device node /sys/fs/kdbus/<bus name>/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 named buses.
|
|
|
|
Endpoint:
|
|
An endpoint provides the device node to talk to a bus. Opening an
|
|
endpoint creates a new connection to the bus to which the endpoint
|
|
belongs. Every bus has a default endpoint called "bus". A bus can
|
|
optionally offer additional endpoints with custom names to provide
|
|
a restricted access to the same bus. Custom endpoints carry
|
|
additional policy which can be used to give sandboxed processes
|
|
only a locked-down, limited, filtered access to the same bus.
|
|
|
|
See kdbus(7), kdbus.bus(7), kdbus.endpoint(7) and kdbus.fs(7)
|
|
for more details.
|
|
|
|
Signed-off-by: Daniel Mack <daniel@zonque.org>
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Djalal Harouni <tixxdz@opendz.org>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
ipc/kdbus/bus.c | 560 +++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
ipc/kdbus/bus.h | 101 ++++++++++
|
|
ipc/kdbus/domain.c | 296 +++++++++++++++++++++++++++
|
|
ipc/kdbus/domain.h | 77 +++++++
|
|
ipc/kdbus/endpoint.c | 275 +++++++++++++++++++++++++
|
|
ipc/kdbus/endpoint.h | 67 ++++++
|
|
6 files changed, 1376 insertions(+)
|
|
create mode 100644 ipc/kdbus/bus.c
|
|
create mode 100644 ipc/kdbus/bus.h
|
|
create mode 100644 ipc/kdbus/domain.c
|
|
create mode 100644 ipc/kdbus/domain.h
|
|
create mode 100644 ipc/kdbus/endpoint.c
|
|
create mode 100644 ipc/kdbus/endpoint.h
|
|
|
|
diff --git a/ipc/kdbus/bus.c b/ipc/kdbus/bus.c
|
|
new file mode 100644
|
|
index 0000000..9d0679e
|
|
--- /dev/null
|
|
+++ b/ipc/kdbus/bus.c
|
|
@@ -0,0 +1,560 @@
|
|
+/*
|
|
+ * 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,
|
|
+ const u64 *pattach_recv,
|
|
+ u64 flags, kuid_t uid, kgid_t gid)
|
|
+{
|
|
+ struct kdbus_bus *b;
|
|
+ u64 attach_owner;
|
|
+ u64 attach_recv;
|
|
+ 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_recv ? *pattach_recv : 0,
|
|
+ &attach_recv);
|
|
+ if (ret < 0)
|
|
+ return ERR_PTR(ret);
|
|
+
|
|
+ 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_req = attach_recv;
|
|
+ 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
|
|
+ * @kmsg: The message to send.
|
|
+ *
|
|
+ * Send @kmsg 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_kmsg *kmsg)
|
|
+{
|
|
+ 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, kmsg);
|
|
+
|
|
+ down_read(&bus->conn_rwlock);
|
|
+ hash_for_each(bus->conn_hash, i, conn_dst, hentry) {
|
|
+ if (conn_dst->id == kmsg->msg.src_id)
|
|
+ continue;
|
|
+ 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_kmsg(conn_dst->match_db, conn_src,
|
|
+ kmsg))
|
|
+ continue;
|
|
+
|
|
+ if (conn_src) {
|
|
+ u64 attach_flags;
|
|
+
|
|
+ /*
|
|
+ * 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;
|
|
+
|
|
+ attach_flags = kdbus_meta_calc_attach_flags(conn_src,
|
|
+ conn_dst);
|
|
+
|
|
+ /*
|
|
+ * Keep sending messages even if we cannot acquire the
|
|
+ * requested metadata. It's up to the receiver to drop
|
|
+ * messages that lack expected metadata.
|
|
+ */
|
|
+ if (!conn_src->faked_meta)
|
|
+ kdbus_meta_proc_collect(kmsg->proc_meta,
|
|
+ attach_flags);
|
|
+ kdbus_meta_conn_collect(kmsg->conn_meta, kmsg, conn_src,
|
|
+ attach_flags);
|
|
+ } 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,
|
|
+ kmsg))
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ ret = kdbus_conn_entry_insert(conn_src, conn_dst, kmsg, 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
|
|
+ * @kmsg: The message to send.
|
|
+ *
|
|
+ * Send @kmsg 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_kmsg *kmsg)
|
|
+{
|
|
+ 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) {
|
|
+ /*
|
|
+ * Collect metadata requested by the destination connection.
|
|
+ * Ignore errors, as receivers need to check metadata
|
|
+ * availability, anyway. So it's still better to send messages
|
|
+ * that lack data, than to skip it entirely.
|
|
+ */
|
|
+ if (conn_src) {
|
|
+ u64 attach_flags;
|
|
+
|
|
+ attach_flags = kdbus_meta_calc_attach_flags(conn_src,
|
|
+ conn_dst);
|
|
+ if (!conn_src->faked_meta)
|
|
+ kdbus_meta_proc_collect(kmsg->proc_meta,
|
|
+ attach_flags);
|
|
+ kdbus_meta_conn_collect(kmsg->conn_meta, kmsg, conn_src,
|
|
+ attach_flags);
|
|
+ }
|
|
+
|
|
+ ret = kdbus_conn_entry_insert(conn_src, conn_dst, kmsg, 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: 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 },
|
|
+ { .type = KDBUS_ITEM_ATTACH_FLAGS_RECV },
|
|
+ };
|
|
+ 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,
|
|
+ argv[4].item ? argv[4].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_header item_hdr;
|
|
+ struct kdbus_info info = {};
|
|
+ size_t meta_size, name_len;
|
|
+ struct kvec kvec[5];
|
|
+ u64 hdr_size = 0;
|
|
+ u64 attach_flags;
|
|
+ size_t cnt = 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_export_prepare(bus->creator_meta, NULL,
|
|
+ &attach_flags, &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), &hdr_size);
|
|
+ kdbus_kvec_set(&kvec[cnt++], &item_hdr, sizeof(item_hdr), &hdr_size);
|
|
+ kdbus_kvec_set(&kvec[cnt++], bus->node.name, name_len, &hdr_size);
|
|
+ cnt += !!kdbus_kvec_pad(&kvec[cnt], &hdr_size);
|
|
+
|
|
+ slice = kdbus_pool_slice_alloc(conn->pool, hdr_size + meta_size, false);
|
|
+ if (IS_ERR(slice)) {
|
|
+ ret = PTR_ERR(slice);
|
|
+ slice = NULL;
|
|
+ goto exit;
|
|
+ }
|
|
+
|
|
+ ret = kdbus_meta_export(bus->creator_meta, NULL, attach_flags,
|
|
+ slice, hdr_size, &meta_size);
|
|
+ if (ret < 0)
|
|
+ goto exit;
|
|
+
|
|
+ info.size = hdr_size + meta_size;
|
|
+
|
|
+ ret = kdbus_pool_slice_copy_kvec(slice, 0, kvec, cnt, hdr_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);
|
|
+
|
|
+ return kdbus_args_clear(&args, ret);
|
|
+}
|
|
diff --git a/ipc/kdbus/bus.h b/ipc/kdbus/bus.h
|
|
new file mode 100644
|
|
index 0000000..5bea5ef
|
|
--- /dev/null
|
|
+++ b/ipc/kdbus/bus.h
|
|
@@ -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_kmsg;
|
|
+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_req: KDBUS_ATTACH_* flags required by connecting peers
|
|
+ * @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
|
|
+ * @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_req;
|
|
+ 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 */
|
|
+ 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_kmsg *kmsg);
|
|
+void kdbus_bus_eavesdrop(struct kdbus_bus *bus,
|
|
+ struct kdbus_conn *conn_src,
|
|
+ struct kdbus_kmsg *kmsg);
|
|
+
|
|
+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/domain.c b/ipc/kdbus/domain.c
|
|
new file mode 100644
|
|
index 0000000..ac9f760
|
|
--- /dev/null
|
|
+++ b/ipc/kdbus/domain.c
|
|
@@ -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
|
|
index 0000000..447a2bd
|
|
--- /dev/null
|
|
+++ b/ipc/kdbus/domain.h
|
|
@@ -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
|
|
index 0000000..174d274
|
|
--- /dev/null
|
|
+++ b/ipc/kdbus/endpoint.c
|
|
@@ -0,0 +1,275 @@
|
|
+/*
|
|
+ * 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 enpoint 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_cmd_ep_make() - handle KDBUS_CMD_ENDPOINT_MAKE
|
|
+ * @bus: bus to operate on
|
|
+ * @argp: command payload
|
|
+ *
|
|
+ * Return: 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: Newly created endpoint on success, ERR_PTR 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
|
|
index 0000000..d31954b
|
|
--- /dev/null
|
|
+++ b/ipc/kdbus/endpoint.h
|
|
@@ -0,0 +1,67 @@
|
|
+/*
|
|
+ * 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 - enpoint 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 enpoint 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);
|
|
+
|
|
+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
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From a7c83836bfe591f0025bfc81eaffed57f0be76a1 Mon Sep 17 00:00:00 2001
|
|
From: Daniel Mack <daniel@zonque.org>
|
|
Date: Thu, 11 Sep 2014 19:00:00 +0200
|
|
Subject: [PATCH 10/78] kdbus: add name registry implementation
|
|
|
|
This patch adds the name registry implementation.
|
|
|
|
Each bus instantiates a name registry to resolve well-known names
|
|
into unique connection IDs for message delivery. The registry will
|
|
be queried when a message is sent with kdbus_msg.dst_id set to
|
|
KDBUS_DST_ID_NAME, or when a registry dump is requested.
|
|
|
|
It's important to have this registry implemented in the kernel to
|
|
implement lookups and take-overs in a race-free way.
|
|
|
|
Signed-off-by: Daniel Mack <daniel@zonque.org>
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Djalal Harouni <tixxdz@opendz.org>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
ipc/kdbus/names.c | 772 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
ipc/kdbus/names.h | 74 ++++++
|
|
2 files changed, 846 insertions(+)
|
|
create mode 100644 ipc/kdbus/names.c
|
|
create mode 100644 ipc/kdbus/names.h
|
|
|
|
diff --git a/ipc/kdbus/names.c b/ipc/kdbus/names.c
|
|
new file mode 100644
|
|
index 0000000..657008e
|
|
--- /dev/null
|
|
+++ b/ipc/kdbus/names.c
|
|
@@ -0,0 +1,772 @@
|
|
+/*
|
|
+ * 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"
|
|
+
|
|
+struct kdbus_name_pending {
|
|
+ u64 flags;
|
|
+ struct kdbus_conn *conn;
|
|
+ struct kdbus_name_entry *name;
|
|
+ struct list_head conn_entry;
|
|
+ struct list_head name_entry;
|
|
+};
|
|
+
|
|
+static int kdbus_name_pending_new(struct kdbus_name_entry *e,
|
|
+ struct kdbus_conn *conn, u64 flags)
|
|
+{
|
|
+ struct kdbus_name_pending *p;
|
|
+
|
|
+ kdbus_conn_assert_active(conn);
|
|
+
|
|
+ p = kmalloc(sizeof(*p), GFP_KERNEL);
|
|
+ if (!p)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ p->flags = flags;
|
|
+ p->conn = conn;
|
|
+ p->name = e;
|
|
+ list_add_tail(&p->conn_entry, &conn->names_queue_list);
|
|
+ list_add_tail(&p->name_entry, &e->queue);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void kdbus_name_pending_free(struct kdbus_name_pending *p)
|
|
+{
|
|
+ if (!p)
|
|
+ return;
|
|
+
|
|
+ list_del(&p->name_entry);
|
|
+ list_del(&p->conn_entry);
|
|
+ kfree(p);
|
|
+}
|
|
+
|
|
+static struct kdbus_name_entry *
|
|
+kdbus_name_entry_new(struct kdbus_name_registry *r, u32 hash, const char *name)
|
|
+{
|
|
+ struct kdbus_name_entry *e;
|
|
+ size_t namelen;
|
|
+
|
|
+ namelen = strlen(name);
|
|
+
|
|
+ e = kmalloc(sizeof(*e) + namelen + 1, GFP_KERNEL);
|
|
+ if (!e)
|
|
+ return ERR_PTR(-ENOMEM);
|
|
+
|
|
+ e->name_id = ++r->name_seq_last;
|
|
+ e->flags = 0;
|
|
+ e->conn = NULL;
|
|
+ e->activator = NULL;
|
|
+ INIT_LIST_HEAD(&e->queue);
|
|
+ INIT_LIST_HEAD(&e->conn_entry);
|
|
+ hash_add(r->entries_hash, &e->hentry, hash);
|
|
+ memcpy(e->name, name, namelen + 1);
|
|
+
|
|
+ return e;
|
|
+}
|
|
+
|
|
+static void kdbus_name_entry_free(struct kdbus_name_entry *e)
|
|
+{
|
|
+ if (!e)
|
|
+ return;
|
|
+
|
|
+ WARN_ON(!list_empty(&e->conn_entry));
|
|
+ WARN_ON(!list_empty(&e->queue));
|
|
+ WARN_ON(e->activator);
|
|
+ WARN_ON(e->conn);
|
|
+
|
|
+ hash_del(&e->hentry);
|
|
+ kfree(e);
|
|
+}
|
|
+
|
|
+static void kdbus_name_entry_set_owner(struct kdbus_name_entry *e,
|
|
+ struct kdbus_conn *conn, u64 flags)
|
|
+{
|
|
+ WARN_ON(e->conn);
|
|
+
|
|
+ e->conn = kdbus_conn_ref(conn);
|
|
+ e->flags = flags;
|
|
+ atomic_inc(&conn->name_count);
|
|
+ list_add_tail(&e->conn_entry, &e->conn->names_list);
|
|
+}
|
|
+
|
|
+static void kdbus_name_entry_remove_owner(struct kdbus_name_entry *e)
|
|
+{
|
|
+ WARN_ON(!e->conn);
|
|
+
|
|
+ list_del_init(&e->conn_entry);
|
|
+ atomic_dec(&e->conn->name_count);
|
|
+ e->flags = 0;
|
|
+ e->conn = kdbus_conn_unref(e->conn);
|
|
+}
|
|
+
|
|
+static void kdbus_name_entry_replace_owner(struct kdbus_name_entry *e,
|
|
+ struct kdbus_conn *conn, u64 flags)
|
|
+{
|
|
+ if (WARN_ON(!e->conn) || WARN_ON(conn == e->conn))
|
|
+ return;
|
|
+
|
|
+ kdbus_notify_name_change(conn->ep->bus, KDBUS_ITEM_NAME_CHANGE,
|
|
+ e->conn->id, conn->id,
|
|
+ e->flags, flags, e->name);
|
|
+ kdbus_name_entry_remove_owner(e);
|
|
+ kdbus_name_entry_set_owner(e, conn, flags);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * kdbus_name_is_valid() - check if a name is valid
|
|
+ * @p: The name to check
|
|
+ * @allow_wildcard: Whether or not to allow a wildcard name
|
|
+ *
|
|
+ * A name is valid if all of the following criterias are met:
|
|
+ *
|
|
+ * - The name has two or more elements separated by a period ('.') character.
|
|
+ * - All elements must contain at least one character.
|
|
+ * - Each element must only contain the ASCII characters "[A-Z][a-z][0-9]_-"
|
|
+ * and must not begin with a digit.
|
|
+ * - The name must not exceed KDBUS_NAME_MAX_LEN.
|
|
+ * - If @allow_wildcard is true, the name may end on '.*'
|
|
+ */
|
|
+bool kdbus_name_is_valid(const char *p, bool allow_wildcard)
|
|
+{
|
|
+ bool dot, found_dot = false;
|
|
+ const char *q;
|
|
+
|
|
+ for (dot = true, q = p; *q; q++) {
|
|
+ if (*q == '.') {
|
|
+ if (dot)
|
|
+ return false;
|
|
+
|
|
+ found_dot = true;
|
|
+ dot = true;
|
|
+ } else {
|
|
+ bool good;
|
|
+
|
|
+ good = isalpha(*q) || (!dot && isdigit(*q)) ||
|
|
+ *q == '_' || *q == '-' ||
|
|
+ (allow_wildcard && dot &&
|
|
+ *q == '*' && *(q + 1) == '\0');
|
|
+
|
|
+ if (!good)
|
|
+ return false;
|
|
+
|
|
+ dot = false;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (q - p > KDBUS_NAME_MAX_LEN)
|
|
+ return false;
|
|
+
|
|
+ if (dot)
|
|
+ return false;
|
|
+
|
|
+ if (!found_dot)
|
|
+ return false;
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * kdbus_name_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() - drop a name reg's reference
|
|
+ * @reg: The name registry, may be %NULL
|
|
+ *
|
|
+ * Cleanup the name registry's internal structures.
|
|
+ */
|
|
+void kdbus_name_registry_free(struct kdbus_name_registry *reg)
|
|
+{
|
|
+ if (!reg)
|
|
+ return;
|
|
+
|
|
+ WARN_ON(!hash_empty(reg->entries_hash));
|
|
+ kfree(reg);
|
|
+}
|
|
+
|
|
+static struct kdbus_name_entry *
|
|
+kdbus_name_find(struct kdbus_name_registry *reg, u32 hash, const char *name)
|
|
+{
|
|
+ struct kdbus_name_entry *e;
|
|
+
|
|
+ lockdep_assert_held(®->rwlock);
|
|
+
|
|
+ hash_for_each_possible(reg->entries_hash, e, hentry, hash)
|
|
+ if (strcmp(e->name, name) == 0)
|
|
+ return e;
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * 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_find(reg, kdbus_strhash(name), name);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * kdbus_name_acquire() - acquire a name
|
|
+ * @reg: The name registry
|
|
+ * @conn: The connection to pin this entry to
|
|
+ * @name: The name to acquire
|
|
+ * @flags: Acquisition flags (KDBUS_NAME_*)
|
|
+ * @return_flags: Pointer to return flags for the acquired name
|
|
+ * (KDBUS_NAME_*), may be %NULL
|
|
+ *
|
|
+ * Callers must ensure that @conn is either a privileged bus user or has
|
|
+ * sufficient privileges in the policy-db to own the well-known name @name.
|
|
+ *
|
|
+ * Return: 0 success, negative error number on failure.
|
|
+ */
|
|
+int kdbus_name_acquire(struct kdbus_name_registry *reg,
|
|
+ struct kdbus_conn *conn, const char *name,
|
|
+ u64 flags, u64 *return_flags)
|
|
+{
|
|
+ struct kdbus_name_entry *e;
|
|
+ u64 rflags = 0;
|
|
+ int ret = 0;
|
|
+ u32 hash;
|
|
+
|
|
+ kdbus_conn_assert_active(conn);
|
|
+
|
|
+ down_write(®->rwlock);
|
|
+
|
|
+ if (!kdbus_conn_policy_own_name(conn, current_cred(), name)) {
|
|
+ ret = -EPERM;
|
|
+ goto exit_unlock;
|
|
+ }
|
|
+
|
|
+ hash = kdbus_strhash(name);
|
|
+ e = kdbus_name_find(reg, hash, name);
|
|
+ if (!e) {
|
|
+ /* claim new name */
|
|
+
|
|
+ if (conn->activator_of) {
|
|
+ ret = -EINVAL;
|
|
+ goto exit_unlock;
|
|
+ }
|
|
+
|
|
+ e = kdbus_name_entry_new(reg, hash, name);
|
|
+ if (IS_ERR(e)) {
|
|
+ ret = PTR_ERR(e);
|
|
+ goto exit_unlock;
|
|
+ }
|
|
+
|
|
+ if (kdbus_conn_is_activator(conn)) {
|
|
+ e->activator = kdbus_conn_ref(conn);
|
|
+ conn->activator_of = e;
|
|
+ }
|
|
+
|
|
+ kdbus_name_entry_set_owner(e, conn, flags);
|
|
+ kdbus_notify_name_change(e->conn->ep->bus, KDBUS_ITEM_NAME_ADD,
|
|
+ 0, e->conn->id, 0, e->flags, e->name);
|
|
+ } else if (e->conn == conn || e == conn->activator_of) {
|
|
+ /* connection already owns that name */
|
|
+ ret = -EALREADY;
|
|
+ } else if (kdbus_conn_is_activator(conn)) {
|
|
+ /* activator claims existing name */
|
|
+
|
|
+ if (conn->activator_of) {
|
|
+ ret = -EINVAL; /* multiple names not allowed */
|
|
+ } else if (e->activator) {
|
|
+ ret = -EEXIST; /* only one activator per name */
|
|
+ } else {
|
|
+ e->activator = kdbus_conn_ref(conn);
|
|
+ conn->activator_of = e;
|
|
+ }
|
|
+ } else if (e->flags & KDBUS_NAME_ACTIVATOR) {
|
|
+ /* claim name of an activator */
|
|
+
|
|
+ kdbus_conn_move_messages(conn, e->activator, 0);
|
|
+ kdbus_name_entry_replace_owner(e, conn, flags);
|
|
+ } else if ((flags & KDBUS_NAME_REPLACE_EXISTING) &&
|
|
+ (e->flags & KDBUS_NAME_ALLOW_REPLACEMENT)) {
|
|
+ /* claim name of a previous owner */
|
|
+
|
|
+ if (e->flags & KDBUS_NAME_QUEUE) {
|
|
+ /* move owner back to queue if they asked for it */
|
|
+ ret = kdbus_name_pending_new(e, e->conn, e->flags);
|
|
+ if (ret < 0)
|
|
+ goto exit_unlock;
|
|
+ }
|
|
+
|
|
+ kdbus_name_entry_replace_owner(e, conn, flags);
|
|
+ } else if (flags & KDBUS_NAME_QUEUE) {
|
|
+ /* add to waiting-queue of the name */
|
|
+
|
|
+ ret = kdbus_name_pending_new(e, conn, flags);
|
|
+ if (ret >= 0)
|
|
+ /* tell the caller that we queued it */
|
|
+ rflags |= KDBUS_NAME_IN_QUEUE;
|
|
+ } else {
|
|
+ /* the name is busy, return a failure */
|
|
+ ret = -EEXIST;
|
|
+ }
|
|
+
|
|
+ if (ret == 0 && return_flags)
|
|
+ *return_flags = rflags;
|
|
+
|
|
+exit_unlock:
|
|
+ up_write(®->rwlock);
|
|
+ kdbus_notify_flush(conn->ep->bus);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void kdbus_name_release_unlocked(struct kdbus_name_registry *reg,
|
|
+ struct kdbus_name_entry *e)
|
|
+{
|
|
+ struct kdbus_name_pending *p;
|
|
+
|
|
+ lockdep_assert_held(®->rwlock);
|
|
+
|
|
+ p = list_first_entry_or_null(&e->queue, struct kdbus_name_pending,
|
|
+ name_entry);
|
|
+
|
|
+ if (p) {
|
|
+ /* give it to first active waiter in the queue */
|
|
+ kdbus_name_entry_replace_owner(e, p->conn, p->flags);
|
|
+ kdbus_name_pending_free(p);
|
|
+ } else if (e->activator && e->activator != e->conn) {
|
|
+ /* hand it back to an active activator connection */
|
|
+ kdbus_conn_move_messages(e->activator, e->conn, e->name_id);
|
|
+ kdbus_name_entry_replace_owner(e, e->activator,
|
|
+ KDBUS_NAME_ACTIVATOR);
|
|
+ } else {
|
|
+ /* release the name */
|
|
+ kdbus_notify_name_change(e->conn->ep->bus,
|
|
+ KDBUS_ITEM_NAME_REMOVE,
|
|
+ e->conn->id, 0, e->flags, 0, e->name);
|
|
+ kdbus_name_entry_remove_owner(e);
|
|
+ kdbus_name_entry_free(e);
|
|
+ }
|
|
+}
|
|
+
|
|
+static int kdbus_name_release(struct kdbus_name_registry *reg,
|
|
+ struct kdbus_conn *conn,
|
|
+ const char *name)
|
|
+{
|
|
+ struct kdbus_name_pending *p;
|
|
+ struct kdbus_name_entry *e;
|
|
+ int ret = 0;
|
|
+
|
|
+ down_write(®->rwlock);
|
|
+ e = kdbus_name_find(reg, kdbus_strhash(name), name);
|
|
+ if (!e) {
|
|
+ ret = -ESRCH;
|
|
+ } else if (e->conn == conn) {
|
|
+ kdbus_name_release_unlocked(reg, e);
|
|
+ } else {
|
|
+ ret = -EADDRINUSE;
|
|
+ list_for_each_entry(p, &e->queue, name_entry) {
|
|
+ if (p->conn == conn) {
|
|
+ kdbus_name_pending_free(p);
|
|
+ ret = 0;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ up_write(®->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_pending *p;
|
|
+ struct kdbus_conn *activator = NULL;
|
|
+ struct kdbus_name_entry *e;
|
|
+
|
|
+ down_write(®->rwlock);
|
|
+
|
|
+ if (kdbus_conn_is_activator(conn)) {
|
|
+ activator = conn->activator_of->activator;
|
|
+ conn->activator_of->activator = NULL;
|
|
+ }
|
|
+
|
|
+ while ((p = list_first_entry_or_null(&conn->names_queue_list,
|
|
+ struct kdbus_name_pending,
|
|
+ conn_entry)))
|
|
+ kdbus_name_pending_free(p);
|
|
+ while ((e = list_first_entry_or_null(&conn->names_list,
|
|
+ struct kdbus_name_entry,
|
|
+ conn_entry)))
|
|
+ kdbus_name_release_unlocked(reg, e);
|
|
+
|
|
+ up_write(®->rwlock);
|
|
+
|
|
+ kdbus_conn_unref(activator);
|
|
+ kdbus_notify_flush(conn->ep->bus);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * 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;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Do atomic_inc_return here to reserve our slot, then decrement
|
|
+ * it before returning.
|
|
+ */
|
|
+ if (atomic_inc_return(&conn->name_count) > KDBUS_CONN_MAX_NAMES) {
|
|
+ ret = -E2BIG;
|
|
+ goto exit_dec;
|
|
+ }
|
|
+
|
|
+ ret = kdbus_name_acquire(conn->ep->bus->name_registry, conn, item_name,
|
|
+ cmd->flags, &cmd->return_flags);
|
|
+ if (ret < 0)
|
|
+ goto exit_dec;
|
|
+
|
|
+exit_dec:
|
|
+ atomic_dec(&conn->name_count);
|
|
+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_entry *e,
|
|
+ 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 (e && !kdbus_conn_policy_see_name_unlocked(conn, current_cred(),
|
|
+ e->name))
|
|
+ return 0;
|
|
+
|
|
+ kdbus_kvec_set(&kvec[cnt++], &info, sizeof(info), &info.size);
|
|
+
|
|
+ /* append name */
|
|
+ if (e) {
|
|
+ size_t slen = strlen(e->name) + 1;
|
|
+
|
|
+ h.size = offsetof(struct kdbus_item, name.name) + slen;
|
|
+ h.type = KDBUS_ITEM_OWNED_NAME;
|
|
+ h.flags = e->flags;
|
|
+
|
|
+ kdbus_kvec_set(&kvec[cnt++], &h, sizeof(h), &info.size);
|
|
+ kdbus_kvec_set(&kvec[cnt++], e->name, slen, &info.size);
|
|
+ cnt += !!kdbus_kvec_pad(&kvec[cnt], &info.size);
|
|
+ }
|
|
+
|
|
+ if (write) {
|
|
+ ret = kdbus_pool_slice_copy_kvec(slice, *pos, kvec,
|
|
+ cnt, info.size);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ *pos += info.size;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int kdbus_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;
|
|
+
|
|
+ /* skip activators */
|
|
+ if (!(flags & KDBUS_LIST_ACTIVATORS) &&
|
|
+ kdbus_conn_is_activator(c))
|
|
+ continue;
|
|
+
|
|
+ /* all names the connection owns */
|
|
+ if (flags & (KDBUS_LIST_NAMES | KDBUS_LIST_ACTIVATORS)) {
|
|
+ struct kdbus_name_entry *e;
|
|
+
|
|
+ list_for_each_entry(e, &c->names_list, conn_entry) {
|
|
+ struct kdbus_conn *a = e->activator;
|
|
+
|
|
+ if ((flags & KDBUS_LIST_ACTIVATORS) &&
|
|
+ a && a != c) {
|
|
+ ret = kdbus_list_write(conn, a, slice,
|
|
+ &p, e, write);
|
|
+ if (ret < 0) {
|
|
+ mutex_unlock(&c->lock);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ added = true;
|
|
+ }
|
|
+
|
|
+ if (flags & KDBUS_LIST_NAMES ||
|
|
+ kdbus_conn_is_activator(c)) {
|
|
+ ret = kdbus_list_write(conn, c, slice,
|
|
+ &p, e, write);
|
|
+ if (ret < 0) {
|
|
+ mutex_unlock(&c->lock);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ added = true;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* queue of names the connection is currently waiting for */
|
|
+ if (flags & KDBUS_LIST_QUEUED) {
|
|
+ struct kdbus_name_pending *q;
|
|
+
|
|
+ list_for_each_entry(q, &c->names_queue_list,
|
|
+ conn_entry) {
|
|
+ ret = kdbus_list_write(conn, c, slice, &p,
|
|
+ q->name, 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(®->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(®->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
|
|
index 0000000..3dd2589
|
|
--- /dev/null
|
|
+++ b/ipc/kdbus/names.h
|
|
@@ -0,0 +1,74 @@
|
|
+/*
|
|
+ * 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_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
|
|
+ * @flags: KDBUS_NAME_* flags
|
|
+ * @conn: Connection owning the name
|
|
+ * @activator: Connection of the activator queuing incoming messages
|
|
+ * @queue: List of queued connections
|
|
+ * @conn_entry: Entry in connection
|
|
+ * @hentry: Entry in registry map
|
|
+ * @name: The well-known name
|
|
+ */
|
|
+struct kdbus_name_entry {
|
|
+ u64 name_id;
|
|
+ u64 flags;
|
|
+ struct kdbus_conn *conn;
|
|
+ struct kdbus_conn *activator;
|
|
+ struct list_head queue;
|
|
+ struct list_head conn_entry;
|
|
+ struct hlist_node hentry;
|
|
+ char name[];
|
|
+};
|
|
+
|
|
+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);
|
|
+
|
|
+#endif
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From d42b90be029eb05159f9eb39e4c90574ea701229 Mon Sep 17 00:00:00 2001
|
|
From: Daniel Mack <daniel@zonque.org>
|
|
Date: Thu, 11 Sep 2014 19:00:43 +0200
|
|
Subject: [PATCH 11/78] kdbus: add policy database implementation
|
|
|
|
This patch adds the policy database implementation.
|
|
|
|
A policy database restricts the possibilities of connections to own,
|
|
see and talk to well-known names. It can be associated with a bus
|
|
(through a policy holder connection) or a custom endpoint.
|
|
|
|
By default, buses have an empty policy database that is augmented on
|
|
demand when a policy holder connection is instantiated.
|
|
|
|
Policies are set through KDBUS_CMD_HELLO (when creating a policy
|
|
holder connection), KDBUS_CMD_CONN_UPDATE (when updating a policy
|
|
holder connection), KDBUS_CMD_EP_MAKE (creating a custom endpoint)
|
|
or KDBUS_CMD_EP_UPDATE (updating a custom endpoint). In all cases,
|
|
the name and policy access information is stored in items of type
|
|
KDBUS_ITEM_NAME and KDBUS_ITEM_POLICY_ACCESS.
|
|
|
|
See kdbus.policy(7) for more details.
|
|
|
|
Signed-off-by: Daniel Mack <daniel@zonque.org>
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Djalal Harouni <tixxdz@opendz.org>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
ipc/kdbus/policy.c | 489 +++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
ipc/kdbus/policy.h | 51 ++++++
|
|
2 files changed, 540 insertions(+)
|
|
create mode 100644 ipc/kdbus/policy.c
|
|
create mode 100644 ipc/kdbus/policy.h
|
|
|
|
diff --git a/ipc/kdbus/policy.c b/ipc/kdbus/policy.c
|
|
new file mode 100644
|
|
index 0000000..dd7fffa
|
|
--- /dev/null
|
|
+++ b/ipc/kdbus/policy.c
|
|
@@ -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 sur 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
|
|
index 0000000..15dd7bc
|
|
--- /dev/null
|
|
+++ b/ipc/kdbus/policy.h
|
|
@@ -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
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 54706574d4054a4ef63c90d0cad1060a146b9de0 Mon Sep 17 00:00:00 2001
|
|
From: Daniel Mack <daniel@zonque.org>
|
|
Date: Thu, 11 Sep 2014 18:48:06 +0200
|
|
Subject: [PATCH 12/78] kdbus: add Makefile, Kconfig and MAINTAINERS entry
|
|
|
|
This patch hooks up the build system to actually compile the files
|
|
added by previous patches. It also adds an entry to MAINTAINERS to
|
|
direct people to Greg KH, David Herrmann, Djalal Harouni and me for
|
|
questions and patches.
|
|
|
|
Signed-off-by: Daniel Mack <daniel@zonque.org>
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Djalal Harouni <tixxdz@opendz.org>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
MAINTAINERS | 13 +++++++++++++
|
|
init/Kconfig | 12 ++++++++++++
|
|
ipc/Makefile | 2 +-
|
|
ipc/kdbus/Makefile | 22 ++++++++++++++++++++++
|
|
4 files changed, 48 insertions(+), 1 deletion(-)
|
|
create mode 100644 ipc/kdbus/Makefile
|
|
|
|
diff --git a/MAINTAINERS b/MAINTAINERS
|
|
index fd60784..4c2b69d 100644
|
|
--- a/MAINTAINERS
|
|
+++ b/MAINTAINERS
|
|
@@ -5793,6 +5793,19 @@ S: Maintained
|
|
F: Documentation/kbuild/kconfig-language.txt
|
|
F: scripts/kconfig/
|
|
|
|
+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: Vivek Goyal <vgoyal@redhat.com>
|
|
M: Haren Myneni <hbabu@us.ibm.com>
|
|
diff --git a/init/Kconfig b/init/Kconfig
|
|
index 860ca23..02735f9 100644
|
|
--- a/init/Kconfig
|
|
+++ b/init/Kconfig
|
|
@@ -261,6 +261,18 @@ 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 Documentation/kdbus.txt
|
|
+
|
|
+ To compile this driver as a module, choose M here: the
|
|
+ module will be called kdbus.
|
|
+
|
|
config CROSS_MEMORY_ATTACH
|
|
bool "Enable process_vm_readv/writev syscalls"
|
|
depends on MMU
|
|
diff --git a/ipc/Makefile b/ipc/Makefile
|
|
index 86c7300..68ec416 100644
|
|
--- a/ipc/Makefile
|
|
+++ b/ipc/Makefile
|
|
@@ -9,4 +9,4 @@ obj_mq-$(CONFIG_COMPAT) += compat_mq.o
|
|
obj-$(CONFIG_POSIX_MQUEUE) += mqueue.o msgutil.o $(obj_mq-y)
|
|
obj-$(CONFIG_IPC_NS) += namespace.o
|
|
obj-$(CONFIG_POSIX_MQUEUE_SYSCTL) += mq_sysctl.o
|
|
-
|
|
+obj-$(CONFIG_KDBUS) += kdbus/
|
|
diff --git a/ipc/kdbus/Makefile b/ipc/kdbus/Makefile
|
|
new file mode 100644
|
|
index 0000000..7ee9271
|
|
--- /dev/null
|
|
+++ b/ipc/kdbus/Makefile
|
|
@@ -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
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From a00dd9314033d14f100cff18d3dca91c5948ca9a Mon Sep 17 00:00:00 2001
|
|
From: Daniel Mack <daniel@zonque.org>
|
|
Date: Thu, 26 Feb 2015 21:06:38 +0100
|
|
Subject: [PATCH 13/78] kdbus: add walk-through user space example
|
|
|
|
Provide a walk-through example that explains how to use the low-level
|
|
ioctl API that kdbus offers. This example is meant to be useful for
|
|
developers who want to gain a in-depth understanding of how the kdbus
|
|
API works by reading a well-documented real-world example.
|
|
|
|
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.
|
|
|
|
Signed-off-by: Daniel Mack <daniel@zonque.org>
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Djalal Harouni <tixxdz@opendz.org>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
samples/Makefile | 3 +-
|
|
samples/kdbus/.gitignore | 1 +
|
|
samples/kdbus/Makefile | 10 +
|
|
samples/kdbus/kdbus-api.h | 114 ++++
|
|
samples/kdbus/kdbus-workers.c | 1326 +++++++++++++++++++++++++++++++++++++++++
|
|
5 files changed, 1453 insertions(+), 1 deletion(-)
|
|
create mode 100644 samples/kdbus/.gitignore
|
|
create mode 100644 samples/kdbus/Makefile
|
|
create mode 100644 samples/kdbus/kdbus-api.h
|
|
create mode 100644 samples/kdbus/kdbus-workers.c
|
|
|
|
diff --git a/samples/Makefile b/samples/Makefile
|
|
index f00257b..f0ad51e 100644
|
|
--- a/samples/Makefile
|
|
+++ b/samples/Makefile
|
|
@@ -1,4 +1,5 @@
|
|
# Makefile for Linux samples code
|
|
|
|
obj-$(CONFIG_SAMPLES) += kobject/ kprobes/ trace_events/ livepatch/ \
|
|
- hw_breakpoint/ kfifo/ kdb/ hidraw/ rpmsg/ seccomp/
|
|
+ hw_breakpoint/ kfifo/ kdb/ kdbus/ hidraw/ rpmsg/ \
|
|
+ seccomp/
|
|
diff --git a/samples/kdbus/.gitignore b/samples/kdbus/.gitignore
|
|
new file mode 100644
|
|
index 0000000..ee07d98
|
|
--- /dev/null
|
|
+++ b/samples/kdbus/.gitignore
|
|
@@ -0,0 +1 @@
|
|
+kdbus-workers
|
|
diff --git a/samples/kdbus/Makefile b/samples/kdbus/Makefile
|
|
new file mode 100644
|
|
index 0000000..d009025
|
|
--- /dev/null
|
|
+++ b/samples/kdbus/Makefile
|
|
@@ -0,0 +1,10 @@
|
|
+# kbuild trick to avoid linker error. Can be omitted if a module is built.
|
|
+obj- := dummy.o
|
|
+
|
|
+hostprogs-y += kdbus-workers
|
|
+
|
|
+always := $(hostprogs-y)
|
|
+
|
|
+HOSTCFLAGS_kdbus-workers.o += \
|
|
+ -I$(objtree)/usr/include/ \
|
|
+ -I$(objtree)/include/uapi/
|
|
diff --git a/samples/kdbus/kdbus-api.h b/samples/kdbus/kdbus-api.h
|
|
new file mode 100644
|
|
index 0000000..5ed5907
|
|
--- /dev/null
|
|
+++ b/samples/kdbus/kdbus-api.h
|
|
@@ -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
|
|
index 0000000..d1d8f7a
|
|
--- /dev/null
|
|
+++ b/samples/kdbus/kdbus-workers.c
|
|
@@ -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 receveiver 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;
|
|
+}
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From f6fb872e6b1ccc554317184096cf194b5c5a8758 Mon Sep 17 00:00:00 2001
|
|
From: Daniel Mack <daniel@zonque.org>
|
|
Date: Sat, 13 Sep 2014 23:15:02 +0200
|
|
Subject: [PATCH 14/78] kdbus: add selftests
|
|
|
|
This patch adds an extensive test suite for kdbus that checks the most
|
|
important code paths in the driver. The idea is to extend the test
|
|
suite over time.
|
|
|
|
Also, this code can serve as another example for how to use the kernel
|
|
API from userspace.
|
|
|
|
The code in the kdbus test suite makes use of the ioctl wrappers
|
|
provided by samples/kdbus/kdbus-api.h.
|
|
|
|
Signed-off-by: Daniel Mack <daniel@zonque.org>
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Djalal Harouni <tixxdz@opendz.org>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
tools/testing/selftests/Makefile | 1 +
|
|
tools/testing/selftests/kdbus/.gitignore | 3 +
|
|
tools/testing/selftests/kdbus/Makefile | 46 +
|
|
tools/testing/selftests/kdbus/kdbus-enum.c | 94 ++
|
|
tools/testing/selftests/kdbus/kdbus-enum.h | 14 +
|
|
tools/testing/selftests/kdbus/kdbus-test.c | 923 ++++++++++++
|
|
tools/testing/selftests/kdbus/kdbus-test.h | 85 ++
|
|
tools/testing/selftests/kdbus/kdbus-util.c | 1615 +++++++++++++++++++++
|
|
tools/testing/selftests/kdbus/kdbus-util.h | 222 +++
|
|
tools/testing/selftests/kdbus/test-activator.c | 318 ++++
|
|
tools/testing/selftests/kdbus/test-attach-flags.c | 750 ++++++++++
|
|
tools/testing/selftests/kdbus/test-benchmark.c | 451 ++++++
|
|
tools/testing/selftests/kdbus/test-bus.c | 175 +++
|
|
tools/testing/selftests/kdbus/test-chat.c | 122 ++
|
|
tools/testing/selftests/kdbus/test-connection.c | 616 ++++++++
|
|
tools/testing/selftests/kdbus/test-daemon.c | 65 +
|
|
tools/testing/selftests/kdbus/test-endpoint.c | 341 +++++
|
|
tools/testing/selftests/kdbus/test-fd.c | 789 ++++++++++
|
|
tools/testing/selftests/kdbus/test-free.c | 64 +
|
|
tools/testing/selftests/kdbus/test-match.c | 441 ++++++
|
|
tools/testing/selftests/kdbus/test-message.c | 731 ++++++++++
|
|
tools/testing/selftests/kdbus/test-metadata-ns.c | 506 +++++++
|
|
tools/testing/selftests/kdbus/test-monitor.c | 176 +++
|
|
tools/testing/selftests/kdbus/test-names.c | 194 +++
|
|
tools/testing/selftests/kdbus/test-policy-ns.c | 632 ++++++++
|
|
tools/testing/selftests/kdbus/test-policy-priv.c | 1269 ++++++++++++++++
|
|
tools/testing/selftests/kdbus/test-policy.c | 80 +
|
|
tools/testing/selftests/kdbus/test-sync.c | 369 +++++
|
|
tools/testing/selftests/kdbus/test-timeout.c | 99 ++
|
|
29 files changed, 11191 insertions(+)
|
|
create mode 100644 tools/testing/selftests/kdbus/.gitignore
|
|
create mode 100644 tools/testing/selftests/kdbus/Makefile
|
|
create mode 100644 tools/testing/selftests/kdbus/kdbus-enum.c
|
|
create mode 100644 tools/testing/selftests/kdbus/kdbus-enum.h
|
|
create mode 100644 tools/testing/selftests/kdbus/kdbus-test.c
|
|
create mode 100644 tools/testing/selftests/kdbus/kdbus-test.h
|
|
create mode 100644 tools/testing/selftests/kdbus/kdbus-util.c
|
|
create mode 100644 tools/testing/selftests/kdbus/kdbus-util.h
|
|
create mode 100644 tools/testing/selftests/kdbus/test-activator.c
|
|
create mode 100644 tools/testing/selftests/kdbus/test-attach-flags.c
|
|
create mode 100644 tools/testing/selftests/kdbus/test-benchmark.c
|
|
create mode 100644 tools/testing/selftests/kdbus/test-bus.c
|
|
create mode 100644 tools/testing/selftests/kdbus/test-chat.c
|
|
create mode 100644 tools/testing/selftests/kdbus/test-connection.c
|
|
create mode 100644 tools/testing/selftests/kdbus/test-daemon.c
|
|
create mode 100644 tools/testing/selftests/kdbus/test-endpoint.c
|
|
create mode 100644 tools/testing/selftests/kdbus/test-fd.c
|
|
create mode 100644 tools/testing/selftests/kdbus/test-free.c
|
|
create mode 100644 tools/testing/selftests/kdbus/test-match.c
|
|
create mode 100644 tools/testing/selftests/kdbus/test-message.c
|
|
create mode 100644 tools/testing/selftests/kdbus/test-metadata-ns.c
|
|
create mode 100644 tools/testing/selftests/kdbus/test-monitor.c
|
|
create mode 100644 tools/testing/selftests/kdbus/test-names.c
|
|
create mode 100644 tools/testing/selftests/kdbus/test-policy-ns.c
|
|
create mode 100644 tools/testing/selftests/kdbus/test-policy-priv.c
|
|
create mode 100644 tools/testing/selftests/kdbus/test-policy.c
|
|
create mode 100644 tools/testing/selftests/kdbus/test-sync.c
|
|
create mode 100644 tools/testing/selftests/kdbus/test-timeout.c
|
|
|
|
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
|
|
index 24ae9e8..3af31af 100644
|
|
--- a/tools/testing/selftests/Makefile
|
|
+++ b/tools/testing/selftests/Makefile
|
|
@@ -6,6 +6,7 @@ TARGETS += firmware
|
|
TARGETS += ftrace
|
|
TARGETS += futex
|
|
TARGETS += kcmp
|
|
+TARGETS += kdbus
|
|
TARGETS += memfd
|
|
TARGETS += memory-hotplug
|
|
TARGETS += mount
|
|
diff --git a/tools/testing/selftests/kdbus/.gitignore b/tools/testing/selftests/kdbus/.gitignore
|
|
new file mode 100644
|
|
index 0000000..7b421f7
|
|
--- /dev/null
|
|
+++ b/tools/testing/selftests/kdbus/.gitignore
|
|
@@ -0,0 +1,3 @@
|
|
+*.7
|
|
+manpage.*
|
|
+*.proc
|
|
diff --git a/tools/testing/selftests/kdbus/Makefile b/tools/testing/selftests/kdbus/Makefile
|
|
new file mode 100644
|
|
index 0000000..f6cfab2
|
|
--- /dev/null
|
|
+++ b/tools/testing/selftests/kdbus/Makefile
|
|
@@ -0,0 +1,46 @@
|
|
+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-sync.o \
|
|
+ test-timeout.o
|
|
+
|
|
+all: kdbus-test
|
|
+
|
|
+%.o: %.c
|
|
+ gcc $(CFLAGS) -c $< -o $@
|
|
+
|
|
+kdbus-test: $(OBJS)
|
|
+ gcc $(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
|
|
index 0000000..4f1e579
|
|
--- /dev/null
|
|
+++ b/tools/testing/selftests/kdbus/kdbus-enum.c
|
|
@@ -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
|
|
index 0000000..a67cec3
|
|
--- /dev/null
|
|
+++ b/tools/testing/selftests/kdbus/kdbus-enum.h
|
|
@@ -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
|
|
index 0000000..a43674c
|
|
--- /dev/null
|
|
+++ b/tools/testing/selftests/kdbus/kdbus-test.c
|
|
@@ -0,0 +1,923 @@
|
|
+#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,
|
|
+};
|
|
+
|
|
+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 = "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);
|
|
+
|
|
+ asprintf(&env->buspath, "%s/%s/bus", args->root, s);
|
|
+ free(s);
|
|
+ }
|
|
+
|
|
+ if (t->flags & TEST_CREATE_CONN) {
|
|
+ env->conn = kdbus_hello(env->buspath, 0, NULL, 0);
|
|
+ ASSERT_RETURN(env->conn);
|
|
+ }
|
|
+
|
|
+ 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,
|
|
+ };
|
|
+
|
|
+ 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));
|
|
+
|
|
+ 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
|
|
index 0000000..6473318
|
|
--- /dev/null
|
|
+++ b/tools/testing/selftests/kdbus/kdbus-test.h
|
|
@@ -0,0 +1,85 @@
|
|
+#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_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
|
|
index 0000000..4b376ec
|
|
--- /dev/null
|
|
+++ b/tools/testing/selftests/kdbus/kdbus-util.c
|
|
@@ -0,0 +1,1615 @@
|
|
+/*
|
|
+ * 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;
|
|
+
|
|
+ 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;
|
|
+
|
|
+ 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)
|
|
+{
|
|
+ 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));
|
|
+
|
|
+ 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);
|
|
+
|
|
+ size = sizeof(*cmd);
|
|
+ if (cancel_fd != -1)
|
|
+ size += KDBUS_ITEM_SIZE(sizeof(cancel_fd));
|
|
+
|
|
+ cmd = malloc(size);
|
|
+ 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)
|
|
+{
|
|
+ return __kdbus_msg_send(conn, name, cookie, flags, timeout, priority,
|
|
+ dst_id, 0, -1);
|
|
+}
|
|
+
|
|
+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);
|
|
+}
|
|
+
|
|
+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
|
|
index 0000000..50ff071
|
|
--- /dev/null
|
|
+++ b/tools/testing/selftests/kdbus/kdbus-util.h
|
|
@@ -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 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
|
|
index 0000000..3d1b763
|
|
--- /dev/null
|
|
+++ b/tools/testing/selftests/kdbus/test-activator.c
|
|
@@ -0,0 +1,318 @@
|
|
+#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);
|
|
+ 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);
|
|
+ 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);
|
|
+ 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);
|
|
+ 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);
|
|
+ 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);
|
|
+ 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);
|
|
+ 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);
|
|
+ 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);
|
|
+ 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
|
|
index 0000000..deee7c3
|
|
--- /dev/null
|
|
+++ b/tools/testing/selftests/kdbus/test-attach-flags.c
|
|
@@ -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
|
|
index 0000000..8a9744b
|
|
--- /dev/null
|
|
+++ b/tools/testing/selftests/kdbus/test-benchmark.c
|
|
@@ -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
|
|
index 0000000..762fb30
|
|
--- /dev/null
|
|
+++ b/tools/testing/selftests/kdbus/test-bus.c
|
|
@@ -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
|
|
index 0000000..71a92d8
|
|
--- /dev/null
|
|
+++ b/tools/testing/selftests/kdbus/test-chat.c
|
|
@@ -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);
|
|
+ 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);
|
|
+ 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);
|
|
+ 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
|
|
index 0000000..5c2bf35
|
|
--- /dev/null
|
|
+++ b/tools/testing/selftests/kdbus/test-connection.c
|
|
@@ -0,0 +1,616 @@
|
|
+#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);
|
|
+ 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);
|
|
+ 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);
|
|
+ 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
|
|
index 0000000..8bc2386
|
|
--- /dev/null
|
|
+++ b/tools/testing/selftests/kdbus/test-daemon.c
|
|
@@ -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
|
|
index 0000000..dcc6ab9
|
|
--- /dev/null
|
|
+++ b/tools/testing/selftests/kdbus/test-endpoint.c
|
|
@@ -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
|
|
index 0000000..2ae0f5a
|
|
--- /dev/null
|
|
+++ b/tools/testing/selftests/kdbus/test-fd.c
|
|
@@ -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);
|
|
+ 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
|
|
index 0000000..f666da3
|
|
--- /dev/null
|
|
+++ b/tools/testing/selftests/kdbus/test-free.c
|
|
@@ -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
|
|
index 0000000..2360dc1
|
|
--- /dev/null
|
|
+++ b/tools/testing/selftests/kdbus/test-match.c
|
|
@@ -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
|
|
index 0000000..f1615da
|
|
--- /dev/null
|
|
+++ b/tools/testing/selftests/kdbus/test-message.c
|
|
@@ -0,0 +1,731 @@
|
|
+#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);
|
|
+ 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);
|
|
+ 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;
|
|
+
|
|
+ a = kdbus_hello(env->buspath, 0, NULL, 0);
|
|
+ b = kdbus_hello(env->buspath, 0, NULL, 0);
|
|
+ ASSERT_RETURN(a && b);
|
|
+
|
|
+ ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, 25, a->id) == 0);
|
|
+ ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, -600, a->id) == 0);
|
|
+ ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, 10, a->id) == 0);
|
|
+ ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, -35, a->id) == 0);
|
|
+ ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, -100, a->id) == 0);
|
|
+ ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, 20, a->id) == 0);
|
|
+ ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, -15, a->id) == 0);
|
|
+ ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, -800, a->id) == 0);
|
|
+ ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, -150, a->id) == 0);
|
|
+ ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, 10, a->id) == 0);
|
|
+ ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, -800, a->id) == 0);
|
|
+ ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, -10, a->id) == 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);
|
|
+ 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);
|
|
+ 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);
|
|
+ ASSERT_RETURN(ret == -ENXIO);
|
|
+
|
|
+ /* can't queue more */
|
|
+ ret = kdbus_msg_send(conn, "foo.test.activator", cookie++,
|
|
+ 0, 0, 0, KDBUS_DST_ID_NAME);
|
|
+ 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);
|
|
+ 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);
|
|
+ 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);
|
|
+ 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);
|
|
+ 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);
|
|
+ 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);
|
|
+ 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);
|
|
+ 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);
|
|
+ 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
|
|
index 0000000..2cb1d4d
|
|
--- /dev/null
|
|
+++ b/tools/testing/selftests/kdbus/test-metadata-ns.c
|
|
@@ -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);
|
|
+ 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);
|
|
+ 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);
|
|
+ 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);
|
|
+ ASSERT_RETURN(ret == 0);
|
|
+
|
|
+ /*
|
|
+ * Sending to unprivileged connections a broadcast
|
|
+ */
|
|
+ ret = kdbus_msg_send(conn, NULL, 0xdeadbeef, 0, 0,
|
|
+ 0, KDBUS_DST_ID_BROADCAST);
|
|
+ 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
|
|
index 0000000..e00d738
|
|
--- /dev/null
|
|
+++ b/tools/testing/selftests/kdbus/test-monitor.c
|
|
@@ -0,0 +1,176 @@
|
|
+#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);
|
|
+ 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);
|
|
+ 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);
|
|
+ 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);
|
|
+ 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
|
|
index 0000000..66ebb47
|
|
--- /dev/null
|
|
+++ b/tools/testing/selftests/kdbus/test-names.c
|
|
@@ -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
|
|
index 0000000..3437012
|
|
--- /dev/null
|
|
+++ b/tools/testing/selftests/kdbus/test-policy-ns.c
|
|
@@ -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);
|
|
+ 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);
|
|
+ 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);
|
|
+ /*
|
|
+ * 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);
|
|
+ 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
|
|
index 0000000..a318ccc
|
|
--- /dev/null
|
|
+++ b/tools/testing/selftests/kdbus/test-policy-priv.c
|
|
@@ -0,0 +1,1269 @@
|
|
+#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);
|
|
+ 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);
|
|
+ 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);
|
|
+ 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);
|
|
+ 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);
|
|
+ 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);
|
|
+ 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);
|
|
+ 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);
|
|
+ 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);
|
|
+ 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);
|
|
+ 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);
|
|
+ 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);
|
|
+ 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);
|
|
+ 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);
|
|
+ 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);
|
|
+ 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);
|
|
+ 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);
|
|
+ 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);
|
|
+ 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);
|
|
+ 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);
|
|
+ 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);
|
|
+ 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
|
|
index 0000000..96d20d5
|
|
--- /dev/null
|
|
+++ b/tools/testing/selftests/kdbus/test-policy.c
|
|
@@ -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-sync.c b/tools/testing/selftests/kdbus/test-sync.c
|
|
new file mode 100644
|
|
index 0000000..e2be910
|
|
--- /dev/null
|
|
+++ b/tools/testing/selftests/kdbus/test-sync.c
|
|
@@ -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);
|
|
+ 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
|
|
index 0000000..cfd1930
|
|
--- /dev/null
|
|
+++ b/tools/testing/selftests/kdbus/test-timeout.c
|
|
@@ -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);
|
|
+ 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;
|
|
+}
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From f51a2d6013766b626f9c53d19a7e88ce6174709d Mon Sep 17 00:00:00 2001
|
|
From: Daniel Mack <daniel@zonque.org>
|
|
Date: Mon, 9 Mar 2015 18:00:46 +0100
|
|
Subject: [PATCH 15/78] Documentation: kdbus: fix location for generated files
|
|
|
|
The generated files should reside in Documentation/kdbus, not in the
|
|
top-level of the source tree. Also add a .gitignore file and ignore
|
|
everything that was built from the XML files.
|
|
|
|
Signed-off-by: Daniel Mack <daniel@zonque.org>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
Documentation/kdbus/.gitignore | 2 ++
|
|
Documentation/kdbus/Makefile | 4 ++--
|
|
2 files changed, 4 insertions(+), 2 deletions(-)
|
|
create mode 100644 Documentation/kdbus/.gitignore
|
|
|
|
diff --git a/Documentation/kdbus/.gitignore b/Documentation/kdbus/.gitignore
|
|
new file mode 100644
|
|
index 0000000..b4a77cc
|
|
--- /dev/null
|
|
+++ b/Documentation/kdbus/.gitignore
|
|
@@ -0,0 +1,2 @@
|
|
+*.7
|
|
+*.html
|
|
diff --git a/Documentation/kdbus/Makefile b/Documentation/kdbus/Makefile
|
|
index cd6b48e..f6d4912 100644
|
|
--- a/Documentation/kdbus/Makefile
|
|
+++ b/Documentation/kdbus/Makefile
|
|
@@ -18,10 +18,10 @@ HTMLFILES := $(patsubst %.xml, %.html, $(XMLFILES))
|
|
XMLTO_ARGS := -m $(obj)/stylesheet.xsl
|
|
|
|
%.7: %.xml
|
|
- xmlto man $(XMLTO_ARGS) -o . $<
|
|
+ xmlto man $(XMLTO_ARGS) -o $(obj) $<
|
|
|
|
%.html: %.xml
|
|
- xmlto html-nochunks $(XMLTO_ARGS) -o . $<
|
|
+ xmlto html-nochunks $(XMLTO_ARGS) -o $(obj) $<
|
|
|
|
mandocs: $(MANFILES)
|
|
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From defeb64aad9b94d34955b6f8e8ede5760f5ec1c7 Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Thu, 12 Mar 2015 17:27:31 +0100
|
|
Subject: [PATCH 16/78] kdbus: samples/kdbus: add -lrt
|
|
|
|
On older systems -lrt is needed for clock_gettime(). Add it to
|
|
HOSTLOADLIBES of kdbus-workers so it builds fine on those systems.
|
|
|
|
Reported-by: Sasha Levin <sasha.levin@oracle.com>
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
samples/kdbus/Makefile | 1 +
|
|
1 file changed, 1 insertion(+)
|
|
|
|
diff --git a/samples/kdbus/Makefile b/samples/kdbus/Makefile
|
|
index d009025..eee9b9a 100644
|
|
--- a/samples/kdbus/Makefile
|
|
+++ b/samples/kdbus/Makefile
|
|
@@ -8,3 +8,4 @@ always := $(hostprogs-y)
|
|
HOSTCFLAGS_kdbus-workers.o += \
|
|
-I$(objtree)/usr/include/ \
|
|
-I$(objtree)/include/uapi/
|
|
+HOSTLOADLIBES_kdbus-workers := -lrt
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From d7c2ada643379dc2b623c3ee8c03142f73e438dd Mon Sep 17 00:00:00 2001
|
|
From: Nicolas Iooss <nicolas.iooss_linux@m4x.org>
|
|
Date: Sun, 15 Mar 2015 13:13:08 +0800
|
|
Subject: [PATCH 17/78] kdbus: fix minor typo in the walk-through example
|
|
|
|
s/receveiver/receiver/
|
|
|
|
Signed-off-by: Nicolas Iooss <nicolas.iooss_linux@m4x.org>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
samples/kdbus/kdbus-workers.c | 4 ++--
|
|
1 file changed, 2 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/samples/kdbus/kdbus-workers.c b/samples/kdbus/kdbus-workers.c
|
|
index d1d8f7a..d331e01 100644
|
|
--- a/samples/kdbus/kdbus-workers.c
|
|
+++ b/samples/kdbus/kdbus-workers.c
|
|
@@ -787,8 +787,8 @@ static int child_run(struct child *c)
|
|
* 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 receveiver connection, and does not need to be
|
|
- * valid after the command is employed.
|
|
+ * 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;
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 39f7f18ba37922ad84a9f1239d56a594e6f817e6 Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Mon, 16 Mar 2015 10:17:10 +0100
|
|
Subject: [PATCH 18/78] samples/kdbus: drop wrong include
|
|
|
|
There is no reason to use ./include/uapi/ directly from samples. If your
|
|
system headers are not up-to-date, you _need_ to run "make
|
|
headers-install" (which will install them to ./usr/ in your kernel tree)
|
|
before building the examples. Otherwise, you will get warnings and build
|
|
failures.
|
|
|
|
Once ./usr/ is updated with the correct headers, it contains everything we
|
|
need, so drop -Iinclude/uapi from the kdbus-workers CFLAGS.
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
samples/kdbus/Makefile | 4 +---
|
|
1 file changed, 1 insertion(+), 3 deletions(-)
|
|
|
|
diff --git a/samples/kdbus/Makefile b/samples/kdbus/Makefile
|
|
index eee9b9a..e714602 100644
|
|
--- a/samples/kdbus/Makefile
|
|
+++ b/samples/kdbus/Makefile
|
|
@@ -5,7 +5,5 @@ hostprogs-y += kdbus-workers
|
|
|
|
always := $(hostprogs-y)
|
|
|
|
-HOSTCFLAGS_kdbus-workers.o += \
|
|
- -I$(objtree)/usr/include/ \
|
|
- -I$(objtree)/include/uapi/
|
|
+HOSTCFLAGS_kdbus-workers.o += -I$(objtree)/usr/include
|
|
HOSTLOADLIBES_kdbus-workers := -lrt
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From df830fa8077244e2e62cc319774e1cd43953a8c0 Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Mon, 16 Mar 2015 10:17:11 +0100
|
|
Subject: [PATCH 19/78] Documentation/kdbus: fix out-of-tree builds
|
|
|
|
Don't use $(obj) to access source files, but use $(srctree)/$(src)/
|
|
instead. This fixes build issues if you use O= with a directory other than
|
|
the source directory.
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
Documentation/kdbus/Makefile | 2 +-
|
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
|
|
|
diff --git a/Documentation/kdbus/Makefile b/Documentation/kdbus/Makefile
|
|
index f6d4912..d8e6bf3 100644
|
|
--- a/Documentation/kdbus/Makefile
|
|
+++ b/Documentation/kdbus/Makefile
|
|
@@ -15,7 +15,7 @@ XMLFILES := $(addprefix $(obj)/,$(DOCS))
|
|
MANFILES := $(patsubst %.xml, %.7, $(XMLFILES))
|
|
HTMLFILES := $(patsubst %.xml, %.html, $(XMLFILES))
|
|
|
|
-XMLTO_ARGS := -m $(obj)/stylesheet.xsl
|
|
+XMLTO_ARGS := -m $(srctree)/$(src)/stylesheet.xsl
|
|
|
|
%.7: %.xml
|
|
xmlto man $(XMLTO_ARGS) -o $(obj) $<
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From c91bcd99699e0ecfb3e1592234a53cdfcc0b25d8 Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Mon, 16 Mar 2015 10:17:12 +0100
|
|
Subject: [PATCH 20/78] Documentation/kdbus: support quiet builds
|
|
|
|
Add support for quiet builds, just like Documentation/DocBook/Makefile
|
|
supports.
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
Documentation/kdbus/Makefile | 16 +++++++++++++---
|
|
1 file changed, 13 insertions(+), 3 deletions(-)
|
|
|
|
diff --git a/Documentation/kdbus/Makefile b/Documentation/kdbus/Makefile
|
|
index d8e6bf3..af87641 100644
|
|
--- a/Documentation/kdbus/Makefile
|
|
+++ b/Documentation/kdbus/Makefile
|
|
@@ -15,13 +15,23 @@ XMLFILES := $(addprefix $(obj)/,$(DOCS))
|
|
MANFILES := $(patsubst %.xml, %.7, $(XMLFILES))
|
|
HTMLFILES := $(patsubst %.xml, %.html, $(XMLFILES))
|
|
|
|
-XMLTO_ARGS := -m $(srctree)/$(src)/stylesheet.xsl
|
|
+XMLTO_ARGS := -m $(srctree)/$(src)/stylesheet.xsl --skip-validation
|
|
|
|
+quiet_cmd_db2man = MAN $@
|
|
+ cmd_db2man = xmlto man $(XMLTO_ARGS) -o $(obj) $<
|
|
%.7: %.xml
|
|
- xmlto man $(XMLTO_ARGS) -o $(obj) $<
|
|
+ @(which xmlto > /dev/null 2>&1) || \
|
|
+ (echo "*** You need to install xmlto ***"; \
|
|
+ exit 1)
|
|
+ $(call cmd,db2man)
|
|
|
|
+quiet_cmd_db2html = HTML $@
|
|
+ cmd_db2html = xmlto html-nochunks $(XMLTO_ARGS) -o $(obj) $<
|
|
%.html: %.xml
|
|
- xmlto html-nochunks $(XMLTO_ARGS) -o $(obj) $<
|
|
+ @(which xmlto > /dev/null 2>&1) || \
|
|
+ (echo "*** You need to install xmlto ***"; \
|
|
+ exit 1)
|
|
+ $(call cmd,db2html)
|
|
|
|
mandocs: $(MANFILES)
|
|
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 5d73d44f1ea83b16e4f9be197df1f4f10b1baea1 Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Mon, 16 Mar 2015 10:17:13 +0100
|
|
Subject: [PATCH 21/78] selftests/kdbus: fix gitignore
|
|
|
|
Drop unused elements from .gitignore (which are leftovers when
|
|
documentation was placed in the same directory).
|
|
Add "kdbus-test" to .gitignore, which is the test binary of all kdbus
|
|
selftests.
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
tools/testing/selftests/kdbus/.gitignore | 4 +---
|
|
1 file changed, 1 insertion(+), 3 deletions(-)
|
|
|
|
diff --git a/tools/testing/selftests/kdbus/.gitignore b/tools/testing/selftests/kdbus/.gitignore
|
|
index 7b421f7..d3ef42f 100644
|
|
--- a/tools/testing/selftests/kdbus/.gitignore
|
|
+++ b/tools/testing/selftests/kdbus/.gitignore
|
|
@@ -1,3 +1 @@
|
|
-*.7
|
|
-manpage.*
|
|
-*.proc
|
|
+kdbus-test
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From bc9dfadc2bdc956d1c7aeac9c37d00a7eff1b4f8 Mon Sep 17 00:00:00 2001
|
|
From: Lukasz Skalski <l.skalski@samsung.com>
|
|
Date: Mon, 16 Mar 2015 10:35:08 +0100
|
|
Subject: [PATCH 22/78] Documentation/kdbus: replace 'reply_cookie' with
|
|
'cookie_reply'
|
|
|
|
The member field is called 'cookie_reply', fix the documentation which
|
|
incorrectly used 'reply_cookie'.
|
|
|
|
Signed-off-by: Lukasz Skalski <l.skalski@samsung.com>
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
Documentation/kdbus/kdbus.message.xml | 2 +-
|
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
|
|
|
diff --git a/Documentation/kdbus/kdbus.message.xml b/Documentation/kdbus/kdbus.message.xml
|
|
index c25000d..5e7c7a3 100644
|
|
--- a/Documentation/kdbus/kdbus.message.xml
|
|
+++ b/Documentation/kdbus/kdbus.message.xml
|
|
@@ -393,7 +393,7 @@ struct kdbus_msg {
|
|
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.reply_cookie</varname> must match the
|
|
+ <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
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From a3dfca6ee5b5817f58f4972a7f94d3bd3b0dea62 Mon Sep 17 00:00:00 2001
|
|
From: Lucas De Marchi <lucas.demarchi@intel.com>
|
|
Date: Tue, 17 Mar 2015 09:21:42 -0300
|
|
Subject: [PATCH 23/78] kdbus: fix header guard name
|
|
|
|
UAPI headers have a _UAPI_ as prefix, which is removed during
|
|
headers_install. If it's put as a suffix it will not be removed and will
|
|
be the only header with UAPI in the header guard macro.
|
|
|
|
Signed-off-by: Lucas De Marchi <lucas.demarchi@intel.com>
|
|
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
include/uapi/linux/kdbus.h | 6 +++---
|
|
1 file changed, 3 insertions(+), 3 deletions(-)
|
|
|
|
diff --git a/include/uapi/linux/kdbus.h b/include/uapi/linux/kdbus.h
|
|
index fc1d77d..2fe0a1c 100644
|
|
--- a/include/uapi/linux/kdbus.h
|
|
+++ b/include/uapi/linux/kdbus.h
|
|
@@ -5,8 +5,8 @@
|
|
* your option) any later version.
|
|
*/
|
|
|
|
-#ifndef _KDBUS_UAPI_H_
|
|
-#define _KDBUS_UAPI_H_
|
|
+#ifndef _UAPI_KDBUS_H_
|
|
+#define _UAPI_KDBUS_H_
|
|
|
|
#include <linux/ioctl.h>
|
|
#include <linux/types.h>
|
|
@@ -976,4 +976,4 @@ enum kdbus_ioctl_type {
|
|
struct kdbus_cmd_match),
|
|
};
|
|
|
|
-#endif /* _KDBUS_UAPI_H_ */
|
|
+#endif /* _UAPI_KDBUS_H_ */
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From d9a4dd0692f35e2d5621e3358d4f9144dc46d41b Mon Sep 17 00:00:00 2001
|
|
From: Daniel Mack <daniel@zonque.org>
|
|
Date: Tue, 17 Mar 2015 19:48:24 +0100
|
|
Subject: [PATCH 24/78] kdbus: connection: fix handling of failed fget()
|
|
|
|
The patch 5fc8dd5c84fc: "kdbus: add connection, queue handling and
|
|
message validation code" from Sep 11, 2014, leads to the following
|
|
static checker warning:
|
|
|
|
ipc/kdbus/connection.c:2000 kdbus_cmd_send()
|
|
warn: 'cancel_fd' isn't an ERR_PTR
|
|
|
|
Fix this by checking for NULL pointers returned from fget().
|
|
|
|
Reported-by: Dan Carpenter <dan.carpenter@oracle.com>
|
|
Signed-off-by: Daniel Mack <daniel@zonque.org>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
ipc/kdbus/connection.c | 5 ++---
|
|
1 file changed, 2 insertions(+), 3 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/connection.c b/ipc/kdbus/connection.c
|
|
index e554f1a..ab476fa 100644
|
|
--- a/ipc/kdbus/connection.c
|
|
+++ b/ipc/kdbus/connection.c
|
|
@@ -1997,9 +1997,8 @@ int kdbus_cmd_send(struct kdbus_conn *conn, struct file *f, void __user *argp)
|
|
|
|
if (argv[1].item) {
|
|
cancel_fd = fget(argv[1].item->fds[0]);
|
|
- if (IS_ERR(cancel_fd)) {
|
|
- ret = PTR_ERR(cancel_fd);
|
|
- cancel_fd = NULL;
|
|
+ if (!cancel_fd) {
|
|
+ ret = -EBADF;
|
|
goto exit;
|
|
}
|
|
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 11711d0bff395fc8a003409b9b15409caf6c2472 Mon Sep 17 00:00:00 2001
|
|
From: Daniel Mack <daniel@zonque.org>
|
|
Date: Tue, 24 Mar 2015 19:51:55 +0100
|
|
Subject: [PATCH 25/78] kdbus: Fix CONFIG_KDBUS help text
|
|
|
|
Drop a left-over from the times when documentation lived in a
|
|
simple text file, which is no longer the case. Mention the
|
|
auto-generated man-pages and HTML files instead.
|
|
|
|
Reported-by: Jiri Slaby <jslaby@suse.cz>
|
|
Signed-off-by: Daniel Mack <daniel@zonque.org>
|
|
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
init/Kconfig | 7 ++++---
|
|
1 file changed, 4 insertions(+), 3 deletions(-)
|
|
|
|
diff --git a/init/Kconfig b/init/Kconfig
|
|
index 02735f9..c407507 100644
|
|
--- a/init/Kconfig
|
|
+++ b/init/Kconfig
|
|
@@ -268,10 +268,11 @@ config KDBUS
|
|
D-Bus is a system for low-latency, low-overhead, easy to use
|
|
interprocess communication (IPC).
|
|
|
|
- See Documentation/kdbus.txt
|
|
+ See the man-pages and HTML files in Documentation/kdbus/
|
|
+ that are generated by 'make mandocs' and 'make htmldocs'.
|
|
|
|
- To compile this driver as a module, choose M here: the
|
|
- module will be called kdbus.
|
|
+ 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"
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 4992ce01746fcc0a19dc08a0c5fafb7168db9656 Mon Sep 17 00:00:00 2001
|
|
From: Daniel Mack <daniel@zonque.org>
|
|
Date: Tue, 31 Mar 2015 15:11:34 +0200
|
|
Subject: [PATCH 26/78] samples: kdbus: build kdbus-workers conditionally
|
|
|
|
Give the kdbus sample its own config switch and only build it if it's
|
|
explicitly switched on.
|
|
|
|
Signed-off-by: Daniel Mack <daniel@zonque.org>
|
|
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Reported-by: Jiri Slaby <jslaby@suse.cz>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
samples/Kconfig | 7 +++++++
|
|
samples/kdbus/Makefile | 2 +-
|
|
2 files changed, 8 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/samples/Kconfig b/samples/Kconfig
|
|
index 224ebb4..a4c6b2f 100644
|
|
--- a/samples/Kconfig
|
|
+++ b/samples/Kconfig
|
|
@@ -55,6 +55,13 @@ config SAMPLE_KDB
|
|
Build an example of how to dynamically add the hello
|
|
command to the kdb shell.
|
|
|
|
+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/Makefile b/samples/kdbus/Makefile
|
|
index e714602..137f842 100644
|
|
--- a/samples/kdbus/Makefile
|
|
+++ b/samples/kdbus/Makefile
|
|
@@ -1,7 +1,7 @@
|
|
# kbuild trick to avoid linker error. Can be omitted if a module is built.
|
|
obj- := dummy.o
|
|
|
|
-hostprogs-y += kdbus-workers
|
|
+hostprogs-$(CONFIG_SAMPLE_KDBUS) += kdbus-workers
|
|
|
|
always := $(hostprogs-y)
|
|
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From aa92dbdf038c70943882edcaada5f77ec2212122 Mon Sep 17 00:00:00 2001
|
|
From: Tyler Baker <tyler.baker@linaro.org>
|
|
Date: Wed, 1 Apr 2015 16:20:16 -0700
|
|
Subject: [PATCH 27/78] selftest/kdbus: enable cross compilation
|
|
|
|
Use the CC variable instead of hard coding gcc and include lib.mk.
|
|
|
|
Signed-off-by: Tyler Baker <tyler.baker@linaro.org>
|
|
Acked-by: Shuah Khan <shuahkh@osg.samsung.com>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
tools/testing/selftests/kdbus/Makefile | 6 ++++--
|
|
1 file changed, 4 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/tools/testing/selftests/kdbus/Makefile b/tools/testing/selftests/kdbus/Makefile
|
|
index f6cfab2..de8242f 100644
|
|
--- a/tools/testing/selftests/kdbus/Makefile
|
|
+++ b/tools/testing/selftests/kdbus/Makefile
|
|
@@ -33,11 +33,13 @@ OBJS= \
|
|
|
|
all: kdbus-test
|
|
|
|
+include ../lib.mk
|
|
+
|
|
%.o: %.c
|
|
- gcc $(CFLAGS) -c $< -o $@
|
|
+ $(CC) $(CFLAGS) -c $< -o $@
|
|
|
|
kdbus-test: $(OBJS)
|
|
- gcc $(CFLAGS) $^ $(LDLIBS) -o $@
|
|
+ $(CC) $(CFLAGS) $^ $(LDLIBS) -o $@
|
|
|
|
run_tests:
|
|
./kdbus-test --tap
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 0922de265ffa1edc164beeae4ad30b79cdbd12b7 Mon Sep 17 00:00:00 2001
|
|
From: Sergei Zviagintsev <sergei@s15v.net>
|
|
Date: Thu, 9 Apr 2015 13:11:01 +0300
|
|
Subject: [PATCH 28/78] kdbus: uapi: Fix kernel-doc for enum kdbus_send_flags
|
|
|
|
Signed-off-by: Sergei Zviagintsev <sergei@s15v.net>
|
|
Acked-by: Daniel Mack <daniel@zonque.org>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
include/uapi/linux/kdbus.h | 2 +-
|
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
|
|
|
diff --git a/include/uapi/linux/kdbus.h b/include/uapi/linux/kdbus.h
|
|
index 2fe0a1c..00a6e14 100644
|
|
--- a/include/uapi/linux/kdbus.h
|
|
+++ b/include/uapi/linux/kdbus.h
|
|
@@ -544,7 +544,7 @@ struct kdbus_msg_info {
|
|
* reply to this message. The
|
|
* KDBUS_CMD_SEND ioctl() will block
|
|
* until the reply is received, and
|
|
- * offset_reply in struct kdbus_msg will
|
|
+ * 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
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 9ee67a43f2e428896b1d3b99d73e34fadb9ebc93 Mon Sep 17 00:00:00 2001
|
|
From: Sergei Zviagintsev <sergei@s15v.net>
|
|
Date: Thu, 9 Apr 2015 13:08:07 +0300
|
|
Subject: [PATCH 29/78] Documentation: kdbus: Fix list of
|
|
KDBUS_CMD_ENDPOINT_UPDATE errors
|
|
|
|
Remove EEXIST.
|
|
|
|
Signed-off-by: Sergei Zviagintsev <sergei@s15v.net>
|
|
Acked-by: Daniel Mack <daniel@zonque.org>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
Documentation/kdbus/kdbus.endpoint.xml | 7 -------
|
|
1 file changed, 7 deletions(-)
|
|
|
|
diff --git a/Documentation/kdbus/kdbus.endpoint.xml b/Documentation/kdbus/kdbus.endpoint.xml
|
|
index 76e325d..c36aa97 100644
|
|
--- a/Documentation/kdbus/kdbus.endpoint.xml
|
|
+++ b/Documentation/kdbus/kdbus.endpoint.xml
|
|
@@ -369,13 +369,6 @@ struct kdbus_cmd {
|
|
<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>
|
|
</variablelist>
|
|
</refsect2>
|
|
</refsect1>
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 40f1c051ba263096cceea9c4170384bca182dc13 Mon Sep 17 00:00:00 2001
|
|
From: Sergei Zviagintsev <sergei@s15v.net>
|
|
Date: Thu, 9 Apr 2015 13:08:06 +0300
|
|
Subject: [PATCH 30/78] Documentation: kdbus: Update list of ioctls which cause
|
|
writing to receiver's pool
|
|
|
|
Add KDBUS_CMD_BUS_CREATOR_INFO.
|
|
|
|
Signed-off-by: Sergei Zviagintsev <sergei@s15v.net>
|
|
Acked-by: Daniel Mack <daniel@zonque.org>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
Documentation/kdbus/kdbus.pool.xml | 6 ++++++
|
|
1 file changed, 6 insertions(+)
|
|
|
|
diff --git a/Documentation/kdbus/kdbus.pool.xml b/Documentation/kdbus/kdbus.pool.xml
|
|
index 05fd019..a9e16f1 100644
|
|
--- a/Documentation/kdbus/kdbus.pool.xml
|
|
+++ b/Documentation/kdbus/kdbus.pool.xml
|
|
@@ -66,6 +66,12 @@
|
|
... 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>
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From f70343af27bd783ee7c36d3ab27d7af0d963f2ef Mon Sep 17 00:00:00 2001
|
|
From: Sergei Zviagintsev <sergei@s15v.net>
|
|
Date: Thu, 9 Apr 2015 13:08:05 +0300
|
|
Subject: [PATCH 31/78] Documentation: kdbus: Fix description of
|
|
KDBUS_SEND_SYNC_REPLY flag
|
|
|
|
Signed-off-by: Sergei Zviagintsev <sergei@s15v.net>
|
|
Acked-by: Daniel Mack <daniel@zonque.org>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
Documentation/kdbus/kdbus.message.xml | 4 ++--
|
|
1 file changed, 2 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/Documentation/kdbus/kdbus.message.xml b/Documentation/kdbus/kdbus.message.xml
|
|
index 5e7c7a3..90f6596 100644
|
|
--- a/Documentation/kdbus/kdbus.message.xml
|
|
+++ b/Documentation/kdbus/kdbus.message.xml
|
|
@@ -242,8 +242,8 @@ struct kdbus_cmd_send {
|
|
</citerefentry>.
|
|
|
|
The offset of the reply message in the sender's pool is stored
|
|
- in in <varname>offset_reply</varname> when the ioctl has
|
|
- returned without error. Hence, there is no need for another
|
|
+ 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>
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From e738dc38b1f35330b313137aea8103563aef571f Mon Sep 17 00:00:00 2001
|
|
From: Sergei Zviagintsev <sergei@s15v.net>
|
|
Date: Thu, 9 Apr 2015 13:08:04 +0300
|
|
Subject: [PATCH 32/78] Documentation: kdbus: Fix typos
|
|
|
|
Signed-off-by: Sergei Zviagintsev <sergei@s15v.net>
|
|
Acked-by: Daniel Mack <daniel@zonque.org>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
Documentation/kdbus/kdbus.bus.xml | 9 ++++-----
|
|
Documentation/kdbus/kdbus.connection.xml | 10 ++++------
|
|
Documentation/kdbus/kdbus.endpoint.xml | 2 +-
|
|
Documentation/kdbus/kdbus.item.xml | 9 ++++-----
|
|
Documentation/kdbus/kdbus.match.xml | 14 ++++++++------
|
|
Documentation/kdbus/kdbus.message.xml | 11 +++++------
|
|
Documentation/kdbus/kdbus.xml | 6 +++---
|
|
7 files changed, 29 insertions(+), 32 deletions(-)
|
|
|
|
diff --git a/Documentation/kdbus/kdbus.bus.xml b/Documentation/kdbus/kdbus.bus.xml
|
|
index 4d875e5..4b9a0ac 100644
|
|
--- a/Documentation/kdbus/kdbus.bus.xml
|
|
+++ b/Documentation/kdbus/kdbus.bus.xml
|
|
@@ -28,8 +28,7 @@
|
|
<citerefentry>
|
|
<refentrytitle>kdbus.message</refentrytitle>
|
|
<manvolnum>7</manvolnum>
|
|
- </citerefentry>
|
|
- ).
|
|
+ </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
|
|
@@ -42,7 +41,7 @@
|
|
<refentrytitle>kdbus.fs</refentrytitle>
|
|
<manvolnum>7</manvolnum>
|
|
</citerefentry>
|
|
- , a bus is presented as a directory. No operations can be performed on
|
|
+ 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
|
|
@@ -165,8 +164,8 @@ struct kdbus_cmd {
|
|
<citerefentry>
|
|
<refentrytitle>kdbus.item</refentrytitle>
|
|
<manvolnum>7</manvolnum>
|
|
- </citerefentry>
|
|
- ) are expected for <constant>KDBUS_CMD_BUS_MAKE</constant>.
|
|
+ </citerefentry>)
|
|
+ are expected for <constant>KDBUS_CMD_BUS_MAKE</constant>.
|
|
</para>
|
|
<variablelist>
|
|
<varlistentry>
|
|
diff --git a/Documentation/kdbus/kdbus.connection.xml b/Documentation/kdbus/kdbus.connection.xml
|
|
index 0985212..cefb419 100644
|
|
--- a/Documentation/kdbus/kdbus.connection.xml
|
|
+++ b/Documentation/kdbus/kdbus.connection.xml
|
|
@@ -50,8 +50,7 @@
|
|
<citerefentry>
|
|
<refentrytitle>kdbus.match</refentrytitle>
|
|
<manvolnum>7</manvolnum>
|
|
- </citerefentry>
|
|
- ).
|
|
+ </citerefentry>).
|
|
</para>
|
|
<para>
|
|
Messages synthesized and sent directly by the kernel will carry the
|
|
@@ -595,13 +594,13 @@ struct kdbus_cmd_info {
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
- <term><varname>flags</varname></term>
|
|
+ <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>
|
|
+ </citerefentry>.
|
|
</para></listitem>
|
|
</varlistentry>
|
|
|
|
@@ -986,8 +985,7 @@ struct kdbus_cmd {
|
|
<term><varname>items</varname></term>
|
|
<listitem>
|
|
<para>
|
|
- Items to describe the connection details to be updated. The
|
|
- following item types are supported.
|
|
+ The following item types are supported.
|
|
</para>
|
|
<variablelist>
|
|
<varlistentry>
|
|
diff --git a/Documentation/kdbus/kdbus.endpoint.xml b/Documentation/kdbus/kdbus.endpoint.xml
|
|
index c36aa97..6632485 100644
|
|
--- a/Documentation/kdbus/kdbus.endpoint.xml
|
|
+++ b/Documentation/kdbus/kdbus.endpoint.xml
|
|
@@ -201,7 +201,7 @@ struct kdbus_cmd {
|
|
<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 update, using
|
|
+ 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>
|
|
diff --git a/Documentation/kdbus/kdbus.item.xml b/Documentation/kdbus/kdbus.item.xml
|
|
index bfe4736..09f8b90 100644
|
|
--- a/Documentation/kdbus/kdbus.item.xml
|
|
+++ b/Documentation/kdbus/kdbus.item.xml
|
|
@@ -139,7 +139,7 @@ struct kdbus_item {
|
|
<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 items.
|
|
+ <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
|
|
@@ -232,7 +232,6 @@ struct kdbus_memfd {
|
|
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.
|
|
- file descriptor.
|
|
In either case, the number of entries in the array is derived from
|
|
the item's total size. See
|
|
<citerefentry>
|
|
@@ -487,7 +486,7 @@ struct kdbus_pids {
|
|
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 and dividing the
|
|
+ size, subtracting the size of the header and dividing the
|
|
remainder by <constant>sizeof(uint32_t)</constant>.
|
|
</para></listitem>
|
|
</varlistentry>
|
|
@@ -748,7 +747,7 @@ struct kdbus_notify_name_change {
|
|
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 is stored in the message
|
|
+ The remote peer ID and the message cookie are stored in the message
|
|
header. See
|
|
<citerefentry>
|
|
<refentrytitle>kdbus.message</refentrytitle>
|
|
@@ -765,7 +764,7 @@ struct kdbus_notify_name_change {
|
|
<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 is stored in the message header. See
|
|
+ cookie are stored in the message header. See
|
|
<citerefentry>
|
|
<refentrytitle>kdbus.message</refentrytitle>
|
|
<manvolnum>7</manvolnum>
|
|
diff --git a/Documentation/kdbus/kdbus.match.xml b/Documentation/kdbus/kdbus.match.xml
|
|
index ef77b64..ae38e04 100644
|
|
--- a/Documentation/kdbus/kdbus.match.xml
|
|
+++ b/Documentation/kdbus/kdbus.match.xml
|
|
@@ -55,7 +55,7 @@
|
|
possibly along with some other rules to further limit the match.
|
|
|
|
The kernel will match the signal message's bloom filter against the
|
|
- connections bloom mask (simply by &-ing it), and will decide whether
|
|
+ connection's bloom mask (simply by &-ing it), and will decide whether
|
|
the message should be delivered to a connection.
|
|
</para>
|
|
<para>
|
|
@@ -138,9 +138,9 @@
|
|
<title>Generations</title>
|
|
|
|
<para>
|
|
- Uploaded matches may contain multiple masks, which have are as large as
|
|
- the bloom size defined by the bus. Each block of a mask is called a
|
|
- <emphasis>generation</emphasis>, starting at index 0.
|
|
+ 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
|
|
@@ -171,7 +171,8 @@
|
|
<title>Adding a match</title>
|
|
<para>
|
|
To add a match, the <constant>KDBUS_CMD_MATCH_ADD</constant> ioctl is
|
|
- used, which takes a struct of the struct described below.
|
|
+ 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,
|
|
@@ -266,7 +267,8 @@ struct kdbus_cmd_match {
|
|
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 section below for more information on bloom filters.
|
|
+ See the "Bloom filters" section above for more information on
|
|
+ bloom filters.
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
diff --git a/Documentation/kdbus/kdbus.message.xml b/Documentation/kdbus/kdbus.message.xml
|
|
index 90f6596..0115d9d 100644
|
|
--- a/Documentation/kdbus/kdbus.message.xml
|
|
+++ b/Documentation/kdbus/kdbus.message.xml
|
|
@@ -344,8 +344,7 @@ struct kdbus_cmd_send {
|
|
</variablelist>
|
|
|
|
<para>
|
|
- The fields in this struct are described below.
|
|
- The message referenced the <varname>msg_address</varname> above has
|
|
+ The message referenced by the <varname>msg_address</varname> above has
|
|
the following layout.
|
|
</para>
|
|
|
|
@@ -528,7 +527,7 @@ struct kdbus_msg {
|
|
<listitem>
|
|
<para>
|
|
Actual data records containing the payload. See section
|
|
- "Passing of Payload Data".
|
|
+ "Message payload".
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
@@ -707,7 +706,7 @@ struct kdbus_cmd_recv {
|
|
<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..)
|
|
+ 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
|
|
@@ -963,7 +962,7 @@ struct kdbus_msg_info {
|
|
<varlistentry>
|
|
<term><constant>E2BIG</constant></term>
|
|
<listitem><para>
|
|
- Too many items
|
|
+ Too many items.
|
|
</para></listitem>
|
|
</varlistentry>
|
|
|
|
@@ -1172,7 +1171,7 @@ struct kdbus_msg_info {
|
|
<varlistentry>
|
|
<term><constant>EAGAIN</constant></term>
|
|
<listitem><para>
|
|
- No message found in the queue
|
|
+ No message found in the queue.
|
|
</para></listitem>
|
|
</varlistentry>
|
|
</variablelist>
|
|
diff --git a/Documentation/kdbus/kdbus.xml b/Documentation/kdbus/kdbus.xml
|
|
index 194abd2..d8e7400 100644
|
|
--- a/Documentation/kdbus/kdbus.xml
|
|
+++ b/Documentation/kdbus/kdbus.xml
|
|
@@ -379,7 +379,7 @@
|
|
<listitem>
|
|
<para>
|
|
When a message is sent (<constant>KDBUS_CMD_SEND</constant>),
|
|
- information about the sending task and the sending connection are
|
|
+ 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
|
|
@@ -514,7 +514,7 @@
|
|
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 the <varname>attach_flags_recv</varname> and
|
|
+ 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
|
|
@@ -924,7 +924,7 @@
|
|
|
|
<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.
|
|
+ detail in the other documents linked to in the "See Also" section below.
|
|
</para>
|
|
</refsect1>
|
|
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 71c3c5db874e5d0bacdc50c98e856f67497ce5bd Mon Sep 17 00:00:00 2001
|
|
From: Arnd Bergmann <arnd@arndb.de>
|
|
Date: Fri, 10 Apr 2015 13:43:37 +0200
|
|
Subject: [PATCH 33/78] kdbus: avoid the use of struct timespec
|
|
|
|
I did a routine check for new users of 'timespec', which we are trying to remove
|
|
from the kernel in order to survive y2038. kdbus came up and looks particularly
|
|
trivial to clean up.
|
|
|
|
This changes the three ktime_get_ts() variants used in kdbus to ktime_get_ns(),
|
|
which aside from removing timespec also simplifies the code and makes it
|
|
slightly more efficient by avoiding a two-way conversion.
|
|
|
|
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
ipc/kdbus/metadata.c | 9 ++-------
|
|
ipc/kdbus/reply.c | 4 +---
|
|
2 files changed, 3 insertions(+), 10 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/metadata.c b/ipc/kdbus/metadata.c
|
|
index 06e0a54..3adc6c2 100644
|
|
--- a/ipc/kdbus/metadata.c
|
|
+++ b/ipc/kdbus/metadata.c
|
|
@@ -678,13 +678,8 @@ struct kdbus_meta_conn *kdbus_meta_conn_unref(struct kdbus_meta_conn *mc)
|
|
static void kdbus_meta_conn_collect_timestamp(struct kdbus_meta_conn *mc,
|
|
struct kdbus_kmsg *kmsg)
|
|
{
|
|
- struct timespec ts;
|
|
-
|
|
- ktime_get_ts(&ts);
|
|
- mc->ts.monotonic_ns = timespec_to_ns(&ts);
|
|
-
|
|
- ktime_get_real_ts(&ts);
|
|
- mc->ts.realtime_ns = timespec_to_ns(&ts);
|
|
+ mc->ts.monotonic_ns = ktime_get_ns();
|
|
+ mc->ts.realtime_ns = ktime_get_real_ns();
|
|
|
|
if (kmsg)
|
|
mc->ts.seqnum = kmsg->seq;
|
|
diff --git a/ipc/kdbus/reply.c b/ipc/kdbus/reply.c
|
|
index 6b3bd81..008dca8 100644
|
|
--- a/ipc/kdbus/reply.c
|
|
+++ b/ipc/kdbus/reply.c
|
|
@@ -204,11 +204,9 @@ void kdbus_reply_list_scan_work(struct work_struct *work)
|
|
container_of(work, struct kdbus_conn, work.work);
|
|
struct kdbus_reply *reply, *reply_tmp;
|
|
u64 deadline = ~0ULL;
|
|
- struct timespec64 ts;
|
|
u64 now;
|
|
|
|
- ktime_get_ts64(&ts);
|
|
- now = timespec64_to_ns(&ts);
|
|
+ now = ktime_get_ns();
|
|
|
|
mutex_lock(&conn->lock);
|
|
if (!kdbus_conn_active(conn)) {
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 38b6cece4eb9e79d33dea512349edf5317e56868 Mon Sep 17 00:00:00 2001
|
|
From: Sergei Zviagintsev <sergei@s15v.net>
|
|
Date: Tue, 21 Apr 2015 02:12:18 +0300
|
|
Subject: [PATCH 34/78] kdbus: pool: use __vfs_read()
|
|
|
|
After commit 5d5d56897530 ("make new_sync_{read,write}() static")
|
|
->read() cannot be called directly.
|
|
|
|
kdbus_pool_slice_copy() leads to oops, which can be reproduced by
|
|
launching tools/testing/selftests/kdbus/kdbus-test -t message-quota:
|
|
|
|
[ 1167.146793] BUG: unable to handle kernel NULL pointer dereference at (null)
|
|
[ 1167.147554] IP: [< (null)>] (null)
|
|
[ 1167.148670] PGD 3a9dd067 PUD 3a841067 PMD 0
|
|
[ 1167.149611] Oops: 0010 [#1] SMP
|
|
[ 1167.150088] Modules linked in: nfsv3 nfs kdbus lockd grace sunrpc
|
|
[ 1167.150771] CPU: 0 PID: 518 Comm: kdbus-test Not tainted 4.0.0-next-20150420-kdbus #62
|
|
[ 1167.150771] Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011
|
|
[ 1167.150771] task: ffff88003daed120 ti: ffff88003a800000 task.ti: ffff88003a800000
|
|
[ 1167.150771] RIP: 0010:[<0000000000000000>] [< (null)>] (null)
|
|
[ 1167.150771] RSP: 0018:ffff88003a803bc0 EFLAGS: 00010286
|
|
[ 1167.150771] RAX: ffff8800377fb000 RBX: 00000000000201e8 RCX: ffff88003a803c00
|
|
[ 1167.150771] RDX: 0000000000000b40 RSI: ffff8800377fb4c0 RDI: ffff88003d815700
|
|
[ 1167.150771] RBP: ffff88003a803c48 R08: ffffffff8139e380 R09: ffff880039d80490
|
|
[ 1167.150771] R10: ffff88003a803a90 R11: 00000000000004c0 R12: 00000000002a24c0
|
|
[ 1167.150771] R13: 0000000000000b40 R14: ffff88003d815700 R15: ffffffff8139e460
|
|
[ 1167.150771] FS: 00007f41dccd4740(0000) GS:ffff88003fc00000(0000) knlGS:0000000000000000
|
|
[ 1167.150771] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
|
|
[ 1167.150771] CR2: 0000000000000000 CR3: 000000003ccdf000 CR4: 00000000000007b0
|
|
[ 1167.150771] Stack:
|
|
[ 1167.150771] ffffffffa0065497 ffff88003a803c10 00007ffffffff000 ffff88003aaa67c0
|
|
[ 1167.150771] 00000000000004c0 ffff88003aaa6870 ffff88003ca83300 ffffffffa006537d
|
|
[ 1167.150771] 00000000000201e8 ffffea0000ddfec0 ffff88003a803c20 0000000000000018
|
|
[ 1167.150771] Call Trace:
|
|
[ 1167.150771] [<ffffffffa0065497>] ? kdbus_pool_slice_copy+0x127/0x200 [kdbus]
|
|
[ 1167.150771] [<ffffffffa006537d>] ? kdbus_pool_slice_copy+0xd/0x200 [kdbus]
|
|
[ 1167.150771] [<ffffffffa006670a>] kdbus_queue_entry_move+0xaa/0x180 [kdbus]
|
|
[ 1167.150771] [<ffffffffa0059e64>] kdbus_conn_move_messages+0x1e4/0x2c0 [kdbus]
|
|
[ 1167.150771] [<ffffffffa006234e>] kdbus_name_acquire+0x31e/0x390 [kdbus]
|
|
[ 1167.150771] [<ffffffffa00625c5>] kdbus_cmd_name_acquire+0x125/0x130 [kdbus]
|
|
[ 1167.150771] [<ffffffffa005db5d>] kdbus_handle_ioctl+0x4ed/0x610 [kdbus]
|
|
[ 1167.150771] [<ffffffff811040e0>] do_vfs_ioctl+0x2e0/0x4e0
|
|
[ 1167.150771] [<ffffffff81389750>] ? preempt_schedule_common+0x1f/0x3f
|
|
[ 1167.150771] [<ffffffff8110431c>] SyS_ioctl+0x3c/0x80
|
|
[ 1167.150771] [<ffffffff8138c36e>] system_call_fastpath+0x12/0x71
|
|
[ 1167.150771] Code: Bad RIP value.
|
|
[ 1167.150771] RIP [< (null)>] (null)
|
|
[ 1167.150771] RSP <ffff88003a803bc0>
|
|
[ 1167.150771] CR2: 0000000000000000
|
|
[ 1167.168756] ---[ end trace a676bcfa75db5a96 ]---
|
|
|
|
Use __vfs_read() instead.
|
|
|
|
Signed-off-by: Sergei Zviagintsev <sergei@s15v.net>
|
|
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
ipc/kdbus/pool.c | 2 +-
|
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
|
|
|
diff --git a/ipc/kdbus/pool.c b/ipc/kdbus/pool.c
|
|
index 139bb77..45dcdea 100644
|
|
--- a/ipc/kdbus/pool.c
|
|
+++ b/ipc/kdbus/pool.c
|
|
@@ -675,7 +675,7 @@ int kdbus_pool_slice_copy(const struct kdbus_pool_slice *slice_dst,
|
|
}
|
|
|
|
kaddr = (char __force __user *)kmap(page) + page_off;
|
|
- n_read = f_src->f_op->read(f_src, kaddr, copy_len, &off_src);
|
|
+ n_read = __vfs_read(f_src, kaddr, copy_len, &off_src);
|
|
kunmap(page);
|
|
mark_page_accessed(page);
|
|
flush_dcache_page(page);
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 4e77bfa320b38c2a4497c3d3cc091f79e46c4be3 Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Thu, 21 May 2015 20:03:29 +0200
|
|
Subject: [PATCH 35/78] kdbus: skip mandatory items on negotiation
|
|
|
|
The kdbus negotiation is used to figure out what items and flags an ioctl
|
|
supports. It is highly impractical to pass in mandatory items when all we
|
|
do is negotiation. Therefore, allow user-space to skip mandatory items if
|
|
KDBUS_FLAG_NEGOTIATE is passed.
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Acked-by: Daniel Mack <daniel@zonque.org>
|
|
---
|
|
ipc/kdbus/handle.c | 15 ++++++++++-----
|
|
1 file changed, 10 insertions(+), 5 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/handle.c b/ipc/kdbus/handle.c
|
|
index f72dbe5..3f5d808 100644
|
|
--- a/ipc/kdbus/handle.c
|
|
+++ b/ipc/kdbus/handle.c
|
|
@@ -71,10 +71,6 @@ static int kdbus_args_verify(struct kdbus_args *args)
|
|
if (!KDBUS_ITEMS_END(item, args->items, args->items_size))
|
|
return -EINVAL;
|
|
|
|
- for (i = 0; i < args->argc; ++i)
|
|
- if (args->argv[i].mandatory && !args->argv[i].item)
|
|
- return -EINVAL;
|
|
-
|
|
return 0;
|
|
}
|
|
|
|
@@ -149,7 +145,7 @@ static int kdbus_args_negotiate(struct kdbus_args *args)
|
|
int __kdbus_args_parse(struct kdbus_args *args, void __user *argp,
|
|
size_t type_size, size_t items_offset, void **out)
|
|
{
|
|
- int ret;
|
|
+ int ret, i;
|
|
|
|
args->cmd = kdbus_memdup_user(argp, type_size, KDBUS_CMD_MAX_SIZE);
|
|
if (IS_ERR(args->cmd))
|
|
@@ -173,6 +169,15 @@ int __kdbus_args_parse(struct kdbus_args *args, void __user *argp,
|
|
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);
|
|
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From c02da9e058fb09c20fb9002666748aa76a9089d5 Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Sat, 18 Apr 2015 12:00:33 +0200
|
|
Subject: [PATCH 36/78] kdbus: turn kdbus_node_idr into an ida
|
|
|
|
We no longer use the node-idr for lookups. We're only interested in unique
|
|
ID allocation. Hence, turn the kdbus_node_idr into an ida and drop the now
|
|
redundant locking. This is also what kernfs does for ino allocations.
|
|
|
|
Reported-by: Al Viro <viro@zeniv.linux.org.uk>
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Acked-by: Daniel Mack <daniel@zonque.org>
|
|
---
|
|
ipc/kdbus/main.c | 1 +
|
|
ipc/kdbus/node.c | 23 +++++------------------
|
|
ipc/kdbus/node.h | 2 ++
|
|
3 files changed, 8 insertions(+), 18 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/main.c b/ipc/kdbus/main.c
|
|
index 785f529..f8eac78 100644
|
|
--- a/ipc/kdbus/main.c
|
|
+++ b/ipc/kdbus/main.c
|
|
@@ -116,6 +116,7 @@ static void __exit kdbus_exit(void)
|
|
{
|
|
kdbus_fs_exit();
|
|
kobject_put(kdbus_dir);
|
|
+ ida_destroy(&kdbus_node_ida);
|
|
}
|
|
|
|
module_init(kdbus_init);
|
|
diff --git a/ipc/kdbus/node.c b/ipc/kdbus/node.c
|
|
index 520df00..0d65c65 100644
|
|
--- a/ipc/kdbus/node.c
|
|
+++ b/ipc/kdbus/node.c
|
|
@@ -178,7 +178,7 @@
|
|
* accessed by other callers to properly initialize
|
|
* filesystem nodes.
|
|
*
|
|
- * * node->id: This is an unsigned 32bit integer allocated by an IDR. It is
|
|
+ * * 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.
|
|
@@ -233,8 +233,7 @@
|
|
#define KDBUS_NODE_NEW (KDBUS_NODE_BIAS - 4)
|
|
|
|
/* global unique ID mapping for kdbus nodes */
|
|
-static DEFINE_IDR(kdbus_node_idr);
|
|
-static DECLARE_RWSEM(kdbus_node_idr_lock);
|
|
+DEFINE_IDA(kdbus_node_ida);
|
|
|
|
/**
|
|
* kdbus_node_name_hash() - hash a name
|
|
@@ -337,15 +336,11 @@ int kdbus_node_link(struct kdbus_node *node, struct kdbus_node *parent,
|
|
node->hash = kdbus_node_name_hash(name);
|
|
}
|
|
|
|
- down_write(&kdbus_node_idr_lock);
|
|
- ret = idr_alloc(&kdbus_node_idr, node, 1, 0, GFP_KERNEL);
|
|
- if (ret >= 0)
|
|
- node->id = ret;
|
|
- up_write(&kdbus_node_idr_lock);
|
|
-
|
|
+ ret = ida_simple_get(&kdbus_node_ida, 1, 0, GFP_KERNEL);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
+ node->id = ret;
|
|
ret = 0;
|
|
|
|
if (parent) {
|
|
@@ -440,16 +435,8 @@ struct kdbus_node *kdbus_node_unref(struct kdbus_node *node)
|
|
|
|
if (node->free_cb)
|
|
node->free_cb(node);
|
|
-
|
|
- down_write(&kdbus_node_idr_lock);
|
|
if (safe.id > 0)
|
|
- idr_remove(&kdbus_node_idr, safe.id);
|
|
- /* drop caches after last node to not leak memory on unload */
|
|
- if (idr_is_empty(&kdbus_node_idr)) {
|
|
- idr_destroy(&kdbus_node_idr);
|
|
- idr_init(&kdbus_node_idr);
|
|
- }
|
|
- up_write(&kdbus_node_idr_lock);
|
|
+ ida_simple_remove(&kdbus_node_ida, safe.id);
|
|
|
|
kfree(safe.name);
|
|
|
|
diff --git a/ipc/kdbus/node.h b/ipc/kdbus/node.h
|
|
index be125ce..970e02b 100644
|
|
--- a/ipc/kdbus/node.h
|
|
+++ b/ipc/kdbus/node.h
|
|
@@ -58,6 +58,8 @@ struct kdbus_node {
|
|
|
|
#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,
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From c875f422fa4d6cc6074be422bf17d91b951c010c Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Sat, 18 Apr 2015 12:39:51 +0200
|
|
Subject: [PATCH 37/78] kdbus: reduce scope of handle locking
|
|
|
|
A kdbus handle is used to create objects in the kdbus hierarchy. During
|
|
open(), we do not have enough information to know how to setup the object.
|
|
Therefore, we provide setup ioctls, which allow user-space to pass in
|
|
parameters and options how the to-be-created object should behave. Once
|
|
setup is done, we allow user-space to use ioctls to operate on that newly
|
|
created object.
|
|
|
|
It is important to notice:
|
|
1) Only one setup ioctl can ever be called on a handle. You cannot call
|
|
multiple, different setup ioctls on the same handle.
|
|
2) A setup ioctl can only be called once, if it succeeded. If it failed,
|
|
it must not modify the handle in any way. If it succeeded, no further
|
|
setup ioctl can be issued.
|
|
3) After a setup ioctl is done, the handle is constant and must not be
|
|
modified in any way.
|
|
|
|
So far, we used a write-lock around all setup ioctls, and a read-lock
|
|
around everything else. The handle setup-indicator (the type field) can
|
|
only be set under the write-lock. Whenever you access the handle under a
|
|
read-lock, you must verify it was set before, otherwise, you must bail out
|
|
as the handle was not initialized, yet.
|
|
|
|
This has the downside that we need a read-lock on all operations on the
|
|
handle. For performance reasons, we should avoid that. This patch turns
|
|
the rwlock into a mutex and removes the read-side lock from all paths. It
|
|
relies on the 3 behaviors described above.
|
|
|
|
With this patch, the mutex is only taken around setup ioctls. Furthermore,
|
|
the setup-indicator (the type field) is only ever set if the mutex is
|
|
held. The mutex guarantees that multiple setup ioctls cannot race, and
|
|
also, that only one setup ioctl will ever succeed. If a setup ioctl is
|
|
called after setup was already finished, we do not touch the handle at all
|
|
and immediately fail.
|
|
|
|
Furthermore, all other operations (non-setup operations) can only be
|
|
called once setup is done. Therefore, we must synchronize them with any
|
|
racing setup, otherwise, they might access the handle which is currently
|
|
modified by setup.
|
|
We protect from this race by setting the setup-indicator (the type field)
|
|
_last_, and issue a write-barrier before setting it. Once it is set, we
|
|
never modify the handle ever again; it is constant from now on until
|
|
file-release.
|
|
Hence, on the read-side we simply read the type field and issue a
|
|
read-barrier afterwards. _Iff_ the type field was not set, yet, we must
|
|
not access the handle in any way, but bail out immediately. Setup was not
|
|
done, yet. But if the type field was set, the read-barrier pairs with the
|
|
write-barrier during setup. All member fields of the handle object are
|
|
guaranteed to be accessible by us, as the type-field is always the last
|
|
field that is written.
|
|
|
|
With this in place, we reduce the locking-overhead of all non-setup ioctls
|
|
to a read-barrier, instead of a read-side lock. And in combination with
|
|
the follow-up that removes the active-refs from kdbus_handle_poll(), we're
|
|
now lock-free in ->poll and ->mmap callbacks.
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Acked-by: Daniel Mack <daniel@zonque.org>
|
|
---
|
|
ipc/kdbus/handle.c | 110 ++++++++++++++++++++++++++++++++++++++++-------------
|
|
1 file changed, 83 insertions(+), 27 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/handle.c b/ipc/kdbus/handle.c
|
|
index 3f5d808..a3e0138 100644
|
|
--- a/ipc/kdbus/handle.c
|
|
+++ b/ipc/kdbus/handle.c
|
|
@@ -18,6 +18,7 @@
|
|
#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>
|
|
@@ -229,7 +230,7 @@ enum kdbus_handle_type {
|
|
|
|
/**
|
|
* struct kdbus_handle - handle to the kdbus system
|
|
- * @rwlock: handle lock
|
|
+ * @lock: handle lock
|
|
* @type: type of this handle (KDBUS_HANDLE_*)
|
|
* @bus_owner: bus this handle owns
|
|
* @ep_owner: endpoint this handle owns
|
|
@@ -237,7 +238,7 @@ enum kdbus_handle_type {
|
|
* @privileged: Flag to mark a handle as privileged
|
|
*/
|
|
struct kdbus_handle {
|
|
- struct rw_semaphore rwlock;
|
|
+ struct mutex lock;
|
|
|
|
enum kdbus_handle_type type;
|
|
union {
|
|
@@ -265,7 +266,7 @@ static int kdbus_handle_open(struct inode *inode, struct file *file)
|
|
goto exit;
|
|
}
|
|
|
|
- init_rwsem(&handle->rwlock);
|
|
+ mutex_init(&handle->lock);
|
|
handle->type = KDBUS_HANDLE_NONE;
|
|
|
|
if (node->type == KDBUS_NODE_ENDPOINT) {
|
|
@@ -355,8 +356,8 @@ static long kdbus_handle_ioctl_control(struct file *file, unsigned int cmd,
|
|
break;
|
|
}
|
|
|
|
- handle->type = KDBUS_HANDLE_BUS_OWNER;
|
|
handle->bus_owner = bus;
|
|
+ ret = KDBUS_HANDLE_BUS_OWNER;
|
|
break;
|
|
}
|
|
|
|
@@ -396,8 +397,8 @@ static long kdbus_handle_ioctl_ep(struct file *file, unsigned int cmd,
|
|
break;
|
|
}
|
|
|
|
- handle->type = KDBUS_HANDLE_EP_OWNER;
|
|
handle->ep_owner = ep;
|
|
+ ret = KDBUS_HANDLE_EP_OWNER;
|
|
break;
|
|
|
|
case KDBUS_CMD_HELLO:
|
|
@@ -407,8 +408,8 @@ static long kdbus_handle_ioctl_ep(struct file *file, unsigned int cmd,
|
|
break;
|
|
}
|
|
|
|
- handle->type = KDBUS_HANDLE_CONNECTED;
|
|
handle->conn = conn;
|
|
+ ret = KDBUS_HANDLE_CONNECTED;
|
|
break;
|
|
|
|
default:
|
|
@@ -522,19 +523,41 @@ static long kdbus_handle_ioctl(struct file *file, unsigned int cmd,
|
|
case KDBUS_CMD_BUS_MAKE:
|
|
case KDBUS_CMD_ENDPOINT_MAKE:
|
|
case KDBUS_CMD_HELLO:
|
|
- /* bail out early if already typed */
|
|
- if (handle->type != KDBUS_HANDLE_NONE)
|
|
- break;
|
|
-
|
|
- down_write(&handle->rwlock);
|
|
+ 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;
|
|
+ }
|
|
}
|
|
- up_write(&handle->rwlock);
|
|
+ mutex_unlock(&handle->lock);
|
|
break;
|
|
|
|
case KDBUS_CMD_ENDPOINT_UPDATE:
|
|
@@ -549,14 +572,30 @@ static long kdbus_handle_ioctl(struct file *file, unsigned int cmd,
|
|
case KDBUS_CMD_MATCH_REMOVE:
|
|
case KDBUS_CMD_SEND:
|
|
case KDBUS_CMD_RECV:
|
|
- case KDBUS_CMD_FREE:
|
|
- down_read(&handle->rwlock);
|
|
- if (handle->type == KDBUS_HANDLE_EP_OWNER)
|
|
+ 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 (handle->type == KDBUS_HANDLE_CONNECTED)
|
|
+ else if (type == KDBUS_HANDLE_CONNECTED)
|
|
ret = kdbus_handle_ioctl_connected(file, cmd, argp);
|
|
- up_read(&handle->rwlock);
|
|
+
|
|
break;
|
|
+ }
|
|
default:
|
|
ret = -ENOTTY;
|
|
break;
|
|
@@ -569,16 +608,23 @@ 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;
|
|
int ret;
|
|
|
|
+ /*
|
|
+ * 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 */
|
|
- down_read(&handle->rwlock);
|
|
- if (handle->type != KDBUS_HANDLE_CONNECTED) {
|
|
- up_read(&handle->rwlock);
|
|
+ if (type != KDBUS_HANDLE_CONNECTED)
|
|
return POLLERR | POLLHUP;
|
|
- }
|
|
- up_read(&handle->rwlock);
|
|
|
|
ret = kdbus_conn_acquire(handle->conn);
|
|
if (ret < 0)
|
|
@@ -598,13 +644,23 @@ static unsigned int kdbus_handle_poll(struct file *file,
|
|
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;
|
|
|
|
- if (down_read_trylock(&handle->rwlock)) {
|
|
- if (handle->type == KDBUS_HANDLE_CONNECTED)
|
|
- ret = kdbus_pool_mmap(handle->conn->pool, vma);
|
|
- up_read(&handle->rwlock);
|
|
- }
|
|
+ /*
|
|
+ * 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;
|
|
}
|
|
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 76b3a09768081c963ed3cbafbab3ed2541d947e7 Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Sat, 18 Apr 2015 13:04:42 +0200
|
|
Subject: [PATCH 38/78] kdbus: skip acquiring an active reference in poll()
|
|
|
|
During poll(), we currently acquire an active reference to the connection
|
|
in question to verify it's still active. If it's not active, anymore, we
|
|
return POLLHUP.
|
|
|
|
This works fine, but requires an atomic_inc() to acquire the active
|
|
reference. However, all we need is a guarantee that the connection is
|
|
active right now, and a guarantee we're called again once this changes.
|
|
This is as simple as adding the waitqueue first, then checking the
|
|
active-state afterwards. kdbus_conn_disconnect() guarantees to wake us up
|
|
_after_ deactivating the connection, thus providing the required barrier
|
|
implicitly (in case someone is actually polling / waiting on the
|
|
connection).
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Acked-by: Daniel Mack <daniel@zonque.org>
|
|
---
|
|
ipc/kdbus/handle.c | 16 +++++++++-------
|
|
1 file changed, 9 insertions(+), 7 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/handle.c b/ipc/kdbus/handle.c
|
|
index a3e0138..6230c7e 100644
|
|
--- a/ipc/kdbus/handle.c
|
|
+++ b/ipc/kdbus/handle.c
|
|
@@ -610,7 +610,6 @@ static unsigned int kdbus_handle_poll(struct file *file,
|
|
struct kdbus_handle *handle = file->private_data;
|
|
enum kdbus_handle_type type;
|
|
unsigned int mask = POLLOUT | POLLWRNORM;
|
|
- int ret;
|
|
|
|
/*
|
|
* This pairs with smp_wmb() during handle setup. It guarantees that
|
|
@@ -626,18 +625,21 @@ static unsigned int kdbus_handle_poll(struct file *file,
|
|
if (type != KDBUS_HANDLE_CONNECTED)
|
|
return POLLERR | POLLHUP;
|
|
|
|
- ret = kdbus_conn_acquire(handle->conn);
|
|
- if (ret < 0)
|
|
- 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;
|
|
|
|
- kdbus_conn_release(handle->conn);
|
|
-
|
|
return mask;
|
|
}
|
|
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 554971a083221051bfc9390d354da8286feed799 Mon Sep 17 00:00:00 2001
|
|
From: Wei Yongjun <yongjun_wei@trendmicro.com.cn>
|
|
Date: Thu, 16 Apr 2015 21:07:18 +0800
|
|
Subject: [PATCH 39/78] kdbus: remove unused linux/version.h include
|
|
|
|
Remove <linux/version.h> include, it's not needed.
|
|
|
|
Signed-off-by: Wei Yongjun <yongjun_wei@trendmicro.com.cn>
|
|
Acked-by: Daniel Mack <daniel@zonque.org>
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
---
|
|
ipc/kdbus/metadata.c | 1 -
|
|
1 file changed, 1 deletion(-)
|
|
|
|
diff --git a/ipc/kdbus/metadata.c b/ipc/kdbus/metadata.c
|
|
index 3adc6c2..eeebfef 100644
|
|
--- a/ipc/kdbus/metadata.c
|
|
+++ b/ipc/kdbus/metadata.c
|
|
@@ -29,7 +29,6 @@
|
|
#include <linux/uidgid.h>
|
|
#include <linux/uio.h>
|
|
#include <linux/user_namespace.h>
|
|
-#include <linux/version.h>
|
|
|
|
#include "bus.h"
|
|
#include "connection.h"
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 44ecbe6df982e6c9b0dd6bde4d0e61a6065238a9 Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Mon, 20 Apr 2015 10:53:35 +0200
|
|
Subject: [PATCH 40/78] kdbus: optimize auxgroup collector
|
|
|
|
current->creds can only be changed by 'current'. That is, as long as we
|
|
only access our own credentials, we can be sure it does not change. Hence,
|
|
there is no need to ref cred->group_info if all we do is copy its content.
|
|
|
|
This avoids touching shared cachelines when collecting auxgroups.
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Acked-by: Daniel Mack <daniel@zonque.org>
|
|
---
|
|
ipc/kdbus/metadata.c | 10 ++++------
|
|
1 file changed, 4 insertions(+), 6 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/metadata.c b/ipc/kdbus/metadata.c
|
|
index eeebfef..174436f 100644
|
|
--- a/ipc/kdbus/metadata.c
|
|
+++ b/ipc/kdbus/metadata.c
|
|
@@ -245,25 +245,23 @@ static void kdbus_meta_proc_collect_pids(struct kdbus_meta_proc *mp)
|
|
|
|
static int kdbus_meta_proc_collect_auxgroups(struct kdbus_meta_proc *mp)
|
|
{
|
|
- struct group_info *info;
|
|
+ const struct group_info *info;
|
|
size_t i;
|
|
|
|
- info = get_current_groups();
|
|
+ /* no need to lock/ref, current creds cannot change */
|
|
+ info = current_cred()->group_info;
|
|
|
|
if (info->ngroups > 0) {
|
|
mp->auxgrps = kmalloc_array(info->ngroups, sizeof(kgid_t),
|
|
GFP_KERNEL);
|
|
- if (!mp->auxgrps) {
|
|
- put_group_info(info);
|
|
+ if (!mp->auxgrps)
|
|
return -ENOMEM;
|
|
- }
|
|
|
|
for (i = 0; i < info->ngroups; i++)
|
|
mp->auxgrps[i] = GROUP_AT(info, i);
|
|
}
|
|
|
|
mp->n_auxgrps = info->ngroups;
|
|
- put_group_info(info);
|
|
mp->valid |= KDBUS_ATTACH_AUXGROUPS;
|
|
|
|
return 0;
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 6f29598d86284a9a06fb7218d0604baf2bd3c126 Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Wed, 22 Apr 2015 19:31:50 +0200
|
|
Subject: [PATCH 41/78] kdbus: drop obsolete WARN_ON
|
|
|
|
entry->user is never set to an error-code. Drop the obsolete WARN_ON which
|
|
is a leftover from before the quota rework.
|
|
|
|
Reported-by: Dan Carpenter <dan.carpenter@oracle.com>
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Acked-by: Daniel Mack <daniel@zonque.org>
|
|
---
|
|
ipc/kdbus/queue.c | 2 +-
|
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
|
|
|
diff --git a/ipc/kdbus/queue.c b/ipc/kdbus/queue.c
|
|
index a449464..25bb3ad 100644
|
|
--- a/ipc/kdbus/queue.c
|
|
+++ b/ipc/kdbus/queue.c
|
|
@@ -637,7 +637,7 @@ int kdbus_queue_entry_move(struct kdbus_queue_entry *e,
|
|
lockdep_assert_held(&src->lock);
|
|
lockdep_assert_held(&dst->lock);
|
|
|
|
- if (WARN_ON(IS_ERR(e->user)) || WARN_ON(list_empty(&e->entry)))
|
|
+ if (WARN_ON(list_empty(&e->entry)))
|
|
return -EINVAL;
|
|
if (src == dst)
|
|
return 0;
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From aa8e894a0e6b9b31af24f8c6f9b831c3c56117f5 Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Mon, 20 Apr 2015 16:20:59 +0200
|
|
Subject: [PATCH 42/78] kdbus: copy small ioctl payloads to stack
|
|
|
|
Right now, we use memdup_user() on all ioctl payloads. However, most of
|
|
the time an ioctl payload is pretty small. 512 bytes on stack seem
|
|
reasonable (similar to what poll() does) to speed up small ioctl payloads.
|
|
Add a command-buffer to kdbus_args and use it instead of kmalloc() for
|
|
reasonably small payloads.
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Acked-by: Daniel Mack <daniel@zonque.org>
|
|
---
|
|
ipc/kdbus/handle.c | 30 ++++++++++++++++++++++++++----
|
|
ipc/kdbus/handle.h | 5 +++++
|
|
ipc/kdbus/util.c | 45 ---------------------------------------------
|
|
ipc/kdbus/util.h | 1 -
|
|
4 files changed, 31 insertions(+), 50 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/handle.c b/ipc/kdbus/handle.c
|
|
index 6230c7e..0752799 100644
|
|
--- a/ipc/kdbus/handle.c
|
|
+++ b/ipc/kdbus/handle.c
|
|
@@ -146,11 +146,32 @@ static int kdbus_args_negotiate(struct kdbus_args *args)
|
|
int __kdbus_args_parse(struct kdbus_args *args, void __user *argp,
|
|
size_t type_size, size_t items_offset, void **out)
|
|
{
|
|
+ u64 user_size;
|
|
int ret, i;
|
|
|
|
- args->cmd = kdbus_memdup_user(argp, type_size, KDBUS_CMD_MAX_SIZE);
|
|
- if (IS_ERR(args->cmd))
|
|
- return PTR_ERR(args->cmd);
|
|
+ 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;
|
|
+ }
|
|
|
|
args->cmd->return_flags = 0;
|
|
args->user = argp;
|
|
@@ -207,7 +228,8 @@ int kdbus_args_clear(struct kdbus_args *args, int ret)
|
|
if (put_user(args->cmd->return_flags,
|
|
&args->user->return_flags))
|
|
ret = -EFAULT;
|
|
- kfree(args->cmd);
|
|
+ if (args->cmd != (void*)args->cmd_buf)
|
|
+ kfree(args->cmd);
|
|
args->cmd = NULL;
|
|
}
|
|
|
|
diff --git a/ipc/kdbus/handle.h b/ipc/kdbus/handle.h
|
|
index 93a372d..13c59d9 100644
|
|
--- a/ipc/kdbus/handle.h
|
|
+++ b/ipc/kdbus/handle.h
|
|
@@ -45,6 +45,7 @@ struct kdbus_arg {
|
|
* @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: 512 bytes inline buf to avoid kmalloc() on small cmds
|
|
* @items: points to item array in @cmd
|
|
* @items_size: size of @items in bytes
|
|
*
|
|
@@ -52,6 +53,9 @@ struct kdbus_arg {
|
|
* 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 512 bytes buffer for small command payloads, to be allocated on
|
|
+ * stack on syscall entrance.
|
|
*/
|
|
struct kdbus_args {
|
|
u64 allowed_flags;
|
|
@@ -60,6 +64,7 @@ struct kdbus_args {
|
|
|
|
struct kdbus_cmd __user *user;
|
|
struct kdbus_cmd *cmd;
|
|
+ u8 cmd_buf[512];
|
|
|
|
struct kdbus_item *items;
|
|
size_t items_size;
|
|
diff --git a/ipc/kdbus/util.c b/ipc/kdbus/util.c
|
|
index eaa806a..72b1883 100644
|
|
--- a/ipc/kdbus/util.c
|
|
+++ b/ipc/kdbus/util.c
|
|
@@ -50,51 +50,6 @@ int kdbus_copy_from_user(void *dest, void __user *user_ptr, size_t size)
|
|
}
|
|
|
|
/**
|
|
- * kdbus_memdup_user() - copy dynamically sized object from user-space
|
|
- * @user_ptr: user-provided source buffer
|
|
- * @sz_min: minimum object size
|
|
- * @sz_max: maximum object size
|
|
- *
|
|
- * This copies a dynamically sized object from user-space into kernel-space. We
|
|
- * require the object to have a 64bit size field at offset 0. We read it out
|
|
- * first, allocate a suitably sized buffer and then copy all data.
|
|
- *
|
|
- * The @sz_min and @sz_max parameters define possible min and max object sizes
|
|
- * so user-space cannot trigger un-bound kernel-space allocations.
|
|
- *
|
|
- * The same alignment-restrictions as described in kdbus_copy_from_user() apply.
|
|
- *
|
|
- * Return: pointer to dynamically allocated copy, or ERR_PTR() on failure.
|
|
- */
|
|
-void *kdbus_memdup_user(void __user *user_ptr, size_t sz_min, size_t sz_max)
|
|
-{
|
|
- void *ptr;
|
|
- u64 size;
|
|
- int ret;
|
|
-
|
|
- ret = kdbus_copy_from_user(&size, user_ptr, sizeof(size));
|
|
- if (ret < 0)
|
|
- return ERR_PTR(ret);
|
|
-
|
|
- if (size < sz_min)
|
|
- return ERR_PTR(-EINVAL);
|
|
-
|
|
- if (size > sz_max)
|
|
- return ERR_PTR(-EMSGSIZE);
|
|
-
|
|
- ptr = memdup_user(user_ptr, size);
|
|
- if (IS_ERR(ptr))
|
|
- return ptr;
|
|
-
|
|
- if (*(u64 *)ptr != size) {
|
|
- kfree(ptr);
|
|
- return ERR_PTR(-EINVAL);
|
|
- }
|
|
-
|
|
- return ptr;
|
|
-}
|
|
-
|
|
-/**
|
|
* 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
|
|
diff --git a/ipc/kdbus/util.h b/ipc/kdbus/util.h
|
|
index 740b198..9fedf8a 100644
|
|
--- a/ipc/kdbus/util.h
|
|
+++ b/ipc/kdbus/util.h
|
|
@@ -64,7 +64,6 @@ int kdbus_verify_uid_prefix(const char *name, struct user_namespace *user_ns,
|
|
int kdbus_sanitize_attach_flags(u64 flags, u64 *attach_flags);
|
|
|
|
int kdbus_copy_from_user(void *dest, void __user *user_ptr, size_t size);
|
|
-void *kdbus_memdup_user(void __user *user_ptr, size_t sz_min, size_t sz_max);
|
|
|
|
struct kvec;
|
|
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 574de6bf6778689b0bf06b9ce272e8acb481e5f0 Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Fri, 22 May 2015 10:25:08 +0200
|
|
Subject: [PATCH 43/78] kdbus: drop kdbus_meta_attach_mask modparam
|
|
|
|
This parameter was introduced to mask out experimental metadata items. As
|
|
the discussion on metadata items has shifted, plans changed: Enable all
|
|
metadata items and drop the controversial items entirely. Lets not work
|
|
around differences of opinions, but figure them out.
|
|
|
|
Also drop all the related kselftests infrastructure to make sure the tests
|
|
still run fine.
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Acked-by: Daniel Mack <daniel@zonque.org>
|
|
---
|
|
ipc/kdbus/main.c | 12 -
|
|
ipc/kdbus/metadata.c | 1 -
|
|
ipc/kdbus/metadata.h | 2 -
|
|
tools/testing/selftests/kdbus/Makefile | 1 -
|
|
tools/testing/selftests/kdbus/kdbus-test.c | 25 +-
|
|
tools/testing/selftests/kdbus/kdbus-test.h | 2 -
|
|
tools/testing/selftests/kdbus/test-attach-flags.c | 750 ----------------------
|
|
tools/testing/selftests/kdbus/test-connection.c | 34 +-
|
|
8 files changed, 13 insertions(+), 814 deletions(-)
|
|
delete mode 100644 tools/testing/selftests/kdbus/test-attach-flags.c
|
|
|
|
diff --git a/ipc/kdbus/main.c b/ipc/kdbus/main.c
|
|
index f8eac78..1ad4dc8 100644
|
|
--- a/ipc/kdbus/main.c
|
|
+++ b/ipc/kdbus/main.c
|
|
@@ -15,7 +15,6 @@
|
|
#include <linux/fs.h>
|
|
#include <linux/init.h>
|
|
#include <linux/module.h>
|
|
-#include <linux/moduleparam.h>
|
|
|
|
#include "util.h"
|
|
#include "fs.h"
|
|
@@ -79,17 +78,6 @@
|
|
/* kdbus mount-point /sys/fs/kdbus */
|
|
static struct kobject *kdbus_dir;
|
|
|
|
-/* global module option to apply a mask to exported metadata */
|
|
-unsigned long long kdbus_meta_attach_mask = KDBUS_ATTACH_TIMESTAMP |
|
|
- KDBUS_ATTACH_CREDS |
|
|
- KDBUS_ATTACH_PIDS |
|
|
- KDBUS_ATTACH_AUXGROUPS |
|
|
- KDBUS_ATTACH_NAMES |
|
|
- KDBUS_ATTACH_SECLABEL |
|
|
- KDBUS_ATTACH_CONN_DESCRIPTION;
|
|
-MODULE_PARM_DESC(attach_flags_mask, "Attach-flags mask for exported metadata");
|
|
-module_param_named(attach_flags_mask, kdbus_meta_attach_mask, ullong, 0644);
|
|
-
|
|
static int __init kdbus_init(void)
|
|
{
|
|
int ret;
|
|
diff --git a/ipc/kdbus/metadata.c b/ipc/kdbus/metadata.c
|
|
index 174436f..b908b63 100644
|
|
--- a/ipc/kdbus/metadata.c
|
|
+++ b/ipc/kdbus/metadata.c
|
|
@@ -835,7 +835,6 @@ int kdbus_meta_export_prepare(struct kdbus_meta_proc *mp,
|
|
}
|
|
|
|
*mask &= valid;
|
|
- *mask &= kdbus_meta_attach_mask;
|
|
|
|
if (!*mask)
|
|
goto exit;
|
|
diff --git a/ipc/kdbus/metadata.h b/ipc/kdbus/metadata.h
|
|
index 42c942b..79b6ac3 100644
|
|
--- a/ipc/kdbus/metadata.h
|
|
+++ b/ipc/kdbus/metadata.h
|
|
@@ -24,8 +24,6 @@ struct kdbus_pool_slice;
|
|
struct kdbus_meta_proc;
|
|
struct kdbus_meta_conn;
|
|
|
|
-extern unsigned long long kdbus_meta_attach_mask;
|
|
-
|
|
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);
|
|
diff --git a/tools/testing/selftests/kdbus/Makefile b/tools/testing/selftests/kdbus/Makefile
|
|
index de8242f..076f9f4 100644
|
|
--- a/tools/testing/selftests/kdbus/Makefile
|
|
+++ b/tools/testing/selftests/kdbus/Makefile
|
|
@@ -11,7 +11,6 @@ OBJS= \
|
|
kdbus-test.o \
|
|
kdbus-test.o \
|
|
test-activator.o \
|
|
- test-attach-flags.o \
|
|
test-benchmark.o \
|
|
test-bus.o \
|
|
test-chat.o \
|
|
diff --git a/tools/testing/selftests/kdbus/kdbus-test.c b/tools/testing/selftests/kdbus/kdbus-test.c
|
|
index a43674c..294e82a 100644
|
|
--- a/tools/testing/selftests/kdbus/kdbus-test.c
|
|
+++ b/tools/testing/selftests/kdbus/kdbus-test.c
|
|
@@ -48,7 +48,6 @@ struct kdbus_test_args {
|
|
char *root;
|
|
char *test;
|
|
char *busname;
|
|
- char *mask_param_path;
|
|
};
|
|
|
|
static const struct kdbus_test tests[] = {
|
|
@@ -274,13 +273,6 @@ static const struct kdbus_test tests[] = {
|
|
.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])))
|
|
@@ -323,7 +315,6 @@ static int test_prepare_env(const struct kdbus_test *t,
|
|
|
|
env->root = args->root;
|
|
env->module = args->module;
|
|
- env->mask_param_path = args->mask_param_path;
|
|
|
|
return 0;
|
|
}
|
|
@@ -754,8 +745,7 @@ int start_tests(struct kdbus_test_args *kdbus_args)
|
|
{
|
|
int ret;
|
|
bool namespaces;
|
|
- uint64_t kdbus_param_mask;
|
|
- static char fspath[4096], parampath[4096];
|
|
+ static char fspath[4096];
|
|
|
|
namespaces = (kdbus_args->mntns || kdbus_args->pidns ||
|
|
kdbus_args->userns);
|
|
@@ -791,19 +781,6 @@ int start_tests(struct kdbus_test_args *kdbus_args)
|
|
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);
|
|
diff --git a/tools/testing/selftests/kdbus/kdbus-test.h b/tools/testing/selftests/kdbus/kdbus-test.h
|
|
index 6473318..a5c6ae8 100644
|
|
--- a/tools/testing/selftests/kdbus/kdbus-test.h
|
|
+++ b/tools/testing/selftests/kdbus/kdbus-test.h
|
|
@@ -5,7 +5,6 @@ struct kdbus_test_env {
|
|
char *buspath;
|
|
const char *root;
|
|
const char *module;
|
|
- const char *mask_param_path;
|
|
int control_fd;
|
|
struct kdbus_conn *conn;
|
|
};
|
|
@@ -44,7 +43,6 @@ enum {
|
|
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);
|
|
diff --git a/tools/testing/selftests/kdbus/test-attach-flags.c b/tools/testing/selftests/kdbus/test-attach-flags.c
|
|
deleted file mode 100644
|
|
index deee7c3..0000000
|
|
--- a/tools/testing/selftests/kdbus/test-attach-flags.c
|
|
+++ /dev/null
|
|
@@ -1,750 +0,0 @@
|
|
-#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-connection.c b/tools/testing/selftests/kdbus/test-connection.c
|
|
index 5c2bf35..e7c4866 100644
|
|
--- a/tools/testing/selftests/kdbus/test-connection.c
|
|
+++ b/tools/testing/selftests/kdbus/test-connection.c
|
|
@@ -185,13 +185,10 @@ 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 |
|
|
@@ -227,13 +224,6 @@ static int kdbus_fuzz_conn_info(struct kdbus_test_env *env, int capable)
|
|
.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);
|
|
@@ -246,7 +236,7 @@ static int kdbus_fuzz_conn_info(struct kdbus_test_env *env, int capable)
|
|
ASSERT_RETURN(item == NULL);
|
|
|
|
item = kdbus_get_item(info, KDBUS_ITEM_CONN_DESCRIPTION);
|
|
- if (valid_flags_set & KDBUS_ATTACH_CONN_DESCRIPTION) {
|
|
+ if (valid_flags & KDBUS_ATTACH_CONN_DESCRIPTION) {
|
|
ASSERT_RETURN(item);
|
|
} else {
|
|
ASSERT_RETURN(item == NULL);
|
|
@@ -271,7 +261,7 @@ static int kdbus_fuzz_conn_info(struct kdbus_test_env *env, int capable)
|
|
ASSERT_RETURN(item == NULL);
|
|
|
|
cnt = kdbus_count_item(info, KDBUS_ITEM_CREDS);
|
|
- if (valid_flags_set & KDBUS_ATTACH_CREDS) {
|
|
+ if (valid_flags & KDBUS_ATTACH_CREDS) {
|
|
ASSERT_RETURN(cnt == 1);
|
|
|
|
item = kdbus_get_item(info, KDBUS_ITEM_CREDS);
|
|
@@ -285,7 +275,7 @@ static int kdbus_fuzz_conn_info(struct kdbus_test_env *env, int capable)
|
|
}
|
|
|
|
item = kdbus_get_item(info, KDBUS_ITEM_PIDS);
|
|
- if (valid_flags_set & KDBUS_ATTACH_PIDS) {
|
|
+ if (valid_flags & KDBUS_ATTACH_PIDS) {
|
|
ASSERT_RETURN(item);
|
|
|
|
/* Compare item->pids with cached PIDs */
|
|
@@ -312,7 +302,7 @@ static int kdbus_fuzz_conn_info(struct kdbus_test_env *env, int capable)
|
|
ASSERT_RETURN(info->id == conn->id);
|
|
|
|
item = kdbus_get_item(info, KDBUS_ITEM_OWNED_NAME);
|
|
- if (valid_flags_set & KDBUS_ATTACH_NAMES) {
|
|
+ if (valid_flags & KDBUS_ATTACH_NAMES) {
|
|
ASSERT_RETURN(item && !strcmp(item->name.name, "com.example.a"));
|
|
} else {
|
|
ASSERT_RETURN(item == NULL);
|
|
@@ -340,14 +330,14 @@ static int kdbus_fuzz_conn_info(struct kdbus_test_env *env, int capable)
|
|
info = (struct kdbus_info *)(conn->buf + offset);
|
|
ASSERT_EXIT(info->id == conn->id);
|
|
|
|
- if (valid_flags_set & KDBUS_ATTACH_NAMES) {
|
|
+ if (valid_flags & 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) {
|
|
+ if (valid_flags & KDBUS_ATTACH_CREDS) {
|
|
item = kdbus_get_item(info, KDBUS_ITEM_CREDS);
|
|
ASSERT_EXIT(item);
|
|
|
|
@@ -356,7 +346,7 @@ static int kdbus_fuzz_conn_info(struct kdbus_test_env *env, int capable)
|
|
sizeof(struct kdbus_creds)) == 0);
|
|
}
|
|
|
|
- if (valid_flags_set & KDBUS_ATTACH_PIDS) {
|
|
+ if (valid_flags & KDBUS_ATTACH_PIDS) {
|
|
item = kdbus_get_item(info, KDBUS_ITEM_PIDS);
|
|
ASSERT_EXIT(item);
|
|
|
|
@@ -385,7 +375,7 @@ static int kdbus_fuzz_conn_info(struct kdbus_test_env *env, int capable)
|
|
* it points to the cached creds.
|
|
*/
|
|
cnt = kdbus_count_item(info, KDBUS_ITEM_CREDS);
|
|
- if (invalid_flags_set & KDBUS_ATTACH_CREDS) {
|
|
+ if (invalid_flags & KDBUS_ATTACH_CREDS) {
|
|
ASSERT_EXIT(cnt == 1);
|
|
|
|
item = kdbus_get_item(info, KDBUS_ITEM_CREDS);
|
|
@@ -398,7 +388,7 @@ static int kdbus_fuzz_conn_info(struct kdbus_test_env *env, int capable)
|
|
ASSERT_EXIT(cnt == 0);
|
|
}
|
|
|
|
- if (invalid_flags_set & KDBUS_ATTACH_PIDS) {
|
|
+ if (invalid_flags & KDBUS_ATTACH_PIDS) {
|
|
cnt = kdbus_count_item(info, KDBUS_ITEM_PIDS);
|
|
ASSERT_EXIT(cnt == 1);
|
|
|
|
@@ -411,14 +401,14 @@ static int kdbus_fuzz_conn_info(struct kdbus_test_env *env, int capable)
|
|
}
|
|
|
|
cnt = kdbus_count_item(info, KDBUS_ITEM_CGROUP);
|
|
- if (invalid_flags_set & KDBUS_ATTACH_CGROUP) {
|
|
+ if (invalid_flags & 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) {
|
|
+ if (invalid_flags & KDBUS_ATTACH_CAPS) {
|
|
ASSERT_EXIT(cnt == 1);
|
|
} else {
|
|
ASSERT_EXIT(cnt == 0);
|
|
@@ -442,7 +432,7 @@ continue_test:
|
|
ASSERT_RETURN(info->id == conn->id);
|
|
|
|
cnt = kdbus_count_item(info, KDBUS_ITEM_OWNED_NAME);
|
|
- if (valid_flags_set & KDBUS_ATTACH_NAMES) {
|
|
+ if (valid_flags & KDBUS_ATTACH_NAMES) {
|
|
ASSERT_RETURN(cnt == 2);
|
|
} else {
|
|
ASSERT_RETURN(cnt == 0);
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 9d231d6f69d93299829a9f865897ffebf2f214df Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Tue, 26 May 2015 09:29:52 +0200
|
|
Subject: [PATCH 44/78] kdbus: fix typo
|
|
|
|
Fix "there" -> "their" typo.
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Acked-by: Daniel Mack <daniel@zonque.org>
|
|
---
|
|
ipc/kdbus/connection.c | 2 +-
|
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
|
|
|
diff --git a/ipc/kdbus/connection.c b/ipc/kdbus/connection.c
|
|
index ab476fa..fb2c6c6 100644
|
|
--- a/ipc/kdbus/connection.c
|
|
+++ b/ipc/kdbus/connection.c
|
|
@@ -753,7 +753,7 @@ void kdbus_conn_quota_dec(struct kdbus_conn *c, struct kdbus_user *u,
|
|
*
|
|
* 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 there unicast message
|
|
+ * 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
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From f7f2bcc877d1e8b167d1d0f2a8851e45d397737f Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Tue, 26 May 2015 09:30:14 +0200
|
|
Subject: [PATCH 45/78] kdbus: forward ID notifications to everyone
|
|
|
|
Even if you cannot SEE another peer (eg., if you're behind a private
|
|
endpoint), the other peer might be able to TALK to you. Therefore, you
|
|
might get messages from them. This works mostly fine, with one major
|
|
exception, that you cannot track the remote peer. You will not receive ID
|
|
notifications for it, thus, you don't get notified when they disconnect.
|
|
This is unforunate and breaks sandboxes kdbus peers.
|
|
|
|
Fix this by forwarding ID notifications to everyone. Note that those
|
|
notifications don't carry _any_ useful information, besides the peer ID.
|
|
Therefore, even if you should not able to SEE a peer, you will now still
|
|
get ID notifications. This does not reveal any additional information on
|
|
the remote peer, besides its lifetime. Hence, it should be fine.
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Acked-by: Daniel Mack <daniel@zonque.org>
|
|
---
|
|
ipc/kdbus/connection.c | 8 +++-----
|
|
tools/testing/selftests/kdbus/test-endpoint.c | 13 ++++++++++++-
|
|
2 files changed, 15 insertions(+), 6 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/connection.c b/ipc/kdbus/connection.c
|
|
index fb2c6c6..272b991 100644
|
|
--- a/ipc/kdbus/connection.c
|
|
+++ b/ipc/kdbus/connection.c
|
|
@@ -1588,10 +1588,8 @@ bool kdbus_conn_policy_see_notification(struct kdbus_conn *conn,
|
|
* to a peer if, and only if, that peer can see the name this
|
|
* notification is for.
|
|
*
|
|
- * KDBUS_ITEM_ID_{ADD,REMOVE}: As new peers cannot have names, and all
|
|
- * names are dropped before a peer is removed, those notifications
|
|
- * cannot be seen on custom endpoints. Thus, we only pass them
|
|
- * through on default endpoints.
|
|
+ * KDBUS_ITEM_ID_{ADD,REMOVE}: Notifications for ID changes are
|
|
+ * broadcast to everyone, to allow tracking peers.
|
|
*/
|
|
|
|
switch (kmsg->notify_type) {
|
|
@@ -1603,7 +1601,7 @@ bool kdbus_conn_policy_see_notification(struct kdbus_conn *conn,
|
|
|
|
case KDBUS_ITEM_ID_ADD:
|
|
case KDBUS_ITEM_ID_REMOVE:
|
|
- return !conn->ep->user;
|
|
+ return true;
|
|
|
|
default:
|
|
WARN(1, "Invalid type for notification broadcast: %llu\n",
|
|
diff --git a/tools/testing/selftests/kdbus/test-endpoint.c b/tools/testing/selftests/kdbus/test-endpoint.c
|
|
index dcc6ab9..34a7be4 100644
|
|
--- a/tools/testing/selftests/kdbus/test-endpoint.c
|
|
+++ b/tools/testing/selftests/kdbus/test-endpoint.c
|
|
@@ -255,6 +255,13 @@ int kdbus_test_custom_endpoint(struct kdbus_test_env *env)
|
|
ep_conn = kdbus_hello(ep, 0, NULL, 0);
|
|
ASSERT_RETURN(ep_conn);
|
|
|
|
+ /* Check that the reader got the IdAdd notification */
|
|
+ ret = kdbus_msg_recv(reader, &msg, NULL);
|
|
+ ASSERT_RETURN(ret == 0);
|
|
+ ASSERT_RETURN(msg->items[0].type == KDBUS_ITEM_ID_ADD);
|
|
+ ASSERT_RETURN(msg->items[0].id_change.id == ep_conn->id);
|
|
+ kdbus_msg_free(msg);
|
|
+
|
|
/*
|
|
* Add a name add match on the endpoint connection, acquire name from
|
|
* the unfiltered connection, and make sure the filtered connection
|
|
@@ -283,7 +290,7 @@ int kdbus_test_custom_endpoint(struct kdbus_test_env *env)
|
|
ret = kdbus_conn_info(ep_conn, 0x0fffffffffffffffULL, NULL, 0, NULL);
|
|
ASSERT_RETURN(ret == -ENXIO);
|
|
|
|
- /* Check that the reader did not receive anything */
|
|
+ /* Check that the reader did not receive the name notification */
|
|
ret = kdbus_msg_recv(reader, NULL, NULL);
|
|
ASSERT_RETURN(ret == -EAGAIN);
|
|
|
|
@@ -295,6 +302,10 @@ int kdbus_test_custom_endpoint(struct kdbus_test_env *env)
|
|
ret = kdbus_name_release(env->conn, name);
|
|
ASSERT_RETURN(ret == 0);
|
|
|
|
+ /* Check that the reader did not receive the name notification */
|
|
+ ret = kdbus_msg_recv(reader, NULL, NULL);
|
|
+ ASSERT_RETURN(ret == -EAGAIN);
|
|
+
|
|
ret = update_endpoint(ep_fd, name);
|
|
ASSERT_RETURN(ret == 0);
|
|
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From e20750572987e35c011369a594a8e8ab8b1e73bf Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Tue, 26 May 2015 09:59:02 +0200
|
|
Subject: [PATCH 46/78] kdbus: provide helper to collect metadata
|
|
|
|
Provide a new helper kdbus_kmsg_collect_metadata() which implements the
|
|
common task of collecting proc- and conn-metadata on a kmsg.
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Acked-by: Daniel Mack <daniel@zonque.org>
|
|
---
|
|
ipc/kdbus/bus.c | 24 +++---------------------
|
|
ipc/kdbus/connection.c | 35 ++++-------------------------------
|
|
ipc/kdbus/message.c | 24 ++++++++++++++++++++++++
|
|
ipc/kdbus/message.h | 2 ++
|
|
4 files changed, 33 insertions(+), 52 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/bus.c b/ipc/kdbus/bus.c
|
|
index 9d0679e..9a0ecbc 100644
|
|
--- a/ipc/kdbus/bus.c
|
|
+++ b/ipc/kdbus/bus.c
|
|
@@ -285,8 +285,6 @@ void kdbus_bus_broadcast(struct kdbus_bus *bus,
|
|
continue;
|
|
|
|
if (conn_src) {
|
|
- u64 attach_flags;
|
|
-
|
|
/*
|
|
* Anyone can send broadcasts, as they have no
|
|
* destination. But a receiver needs TALK access to
|
|
@@ -295,19 +293,12 @@ void kdbus_bus_broadcast(struct kdbus_bus *bus,
|
|
if (!kdbus_conn_policy_talk(conn_dst, NULL, conn_src))
|
|
continue;
|
|
|
|
- attach_flags = kdbus_meta_calc_attach_flags(conn_src,
|
|
- conn_dst);
|
|
-
|
|
/*
|
|
* Keep sending messages even if we cannot acquire the
|
|
* requested metadata. It's up to the receiver to drop
|
|
* messages that lack expected metadata.
|
|
*/
|
|
- if (!conn_src->faked_meta)
|
|
- kdbus_meta_proc_collect(kmsg->proc_meta,
|
|
- attach_flags);
|
|
- kdbus_meta_conn_collect(kmsg->conn_meta, kmsg, conn_src,
|
|
- attach_flags);
|
|
+ kdbus_kmsg_collect_metadata(kmsg, conn_src, conn_dst);
|
|
} else {
|
|
/*
|
|
* Check if there is a policy db that prevents the
|
|
@@ -359,17 +350,8 @@ void kdbus_bus_eavesdrop(struct kdbus_bus *bus,
|
|
* availability, anyway. So it's still better to send messages
|
|
* that lack data, than to skip it entirely.
|
|
*/
|
|
- if (conn_src) {
|
|
- u64 attach_flags;
|
|
-
|
|
- attach_flags = kdbus_meta_calc_attach_flags(conn_src,
|
|
- conn_dst);
|
|
- if (!conn_src->faked_meta)
|
|
- kdbus_meta_proc_collect(kmsg->proc_meta,
|
|
- attach_flags);
|
|
- kdbus_meta_conn_collect(kmsg->conn_meta, kmsg, conn_src,
|
|
- attach_flags);
|
|
- }
|
|
+ if (conn_src)
|
|
+ kdbus_kmsg_collect_metadata(kmsg, conn_src, conn_dst);
|
|
|
|
ret = kdbus_conn_entry_insert(conn_src, conn_dst, kmsg, NULL);
|
|
if (ret < 0)
|
|
diff --git a/ipc/kdbus/connection.c b/ipc/kdbus/connection.c
|
|
index 272b991..cbfbf38 100644
|
|
--- a/ipc/kdbus/connection.c
|
|
+++ b/ipc/kdbus/connection.c
|
|
@@ -1098,7 +1098,6 @@ static int kdbus_conn_reply(struct kdbus_conn *src, struct kdbus_kmsg *kmsg)
|
|
struct kdbus_reply *reply, *wake = NULL;
|
|
struct kdbus_conn *dst = NULL;
|
|
struct kdbus_bus *bus = src->ep->bus;
|
|
- u64 attach;
|
|
int ret;
|
|
|
|
if (WARN_ON(kmsg->msg.dst_id == KDBUS_DST_ID_BROADCAST) ||
|
|
@@ -1131,15 +1130,7 @@ static int kdbus_conn_reply(struct kdbus_conn *src, struct kdbus_kmsg *kmsg)
|
|
|
|
/* attach metadata */
|
|
|
|
- attach = kdbus_meta_calc_attach_flags(src, dst);
|
|
-
|
|
- if (!src->faked_meta) {
|
|
- ret = kdbus_meta_proc_collect(kmsg->proc_meta, attach);
|
|
- if (ret < 0)
|
|
- goto exit;
|
|
- }
|
|
-
|
|
- ret = kdbus_meta_conn_collect(kmsg->conn_meta, kmsg, src, attach);
|
|
+ ret = kdbus_kmsg_collect_metadata(kmsg, src, dst);
|
|
if (ret < 0)
|
|
goto exit;
|
|
|
|
@@ -1167,7 +1158,6 @@ static struct kdbus_reply *kdbus_conn_call(struct kdbus_conn *src,
|
|
struct kdbus_reply *wait = NULL;
|
|
struct kdbus_conn *dst = NULL;
|
|
struct kdbus_bus *bus = src->ep->bus;
|
|
- u64 attach;
|
|
int ret;
|
|
|
|
if (WARN_ON(kmsg->msg.dst_id == KDBUS_DST_ID_BROADCAST) ||
|
|
@@ -1218,15 +1208,7 @@ static struct kdbus_reply *kdbus_conn_call(struct kdbus_conn *src,
|
|
|
|
/* attach metadata */
|
|
|
|
- attach = kdbus_meta_calc_attach_flags(src, dst);
|
|
-
|
|
- if (!src->faked_meta) {
|
|
- ret = kdbus_meta_proc_collect(kmsg->proc_meta, attach);
|
|
- if (ret < 0)
|
|
- goto exit;
|
|
- }
|
|
-
|
|
- ret = kdbus_meta_conn_collect(kmsg->conn_meta, kmsg, src, attach);
|
|
+ ret = kdbus_kmsg_collect_metadata(kmsg, src, dst);
|
|
if (ret < 0)
|
|
goto exit;
|
|
|
|
@@ -1257,7 +1239,6 @@ static int kdbus_conn_unicast(struct kdbus_conn *src, struct kdbus_kmsg *kmsg)
|
|
struct kdbus_conn *dst = NULL;
|
|
struct kdbus_bus *bus = src->ep->bus;
|
|
bool is_signal = (kmsg->msg.flags & KDBUS_MSG_SIGNAL);
|
|
- u64 attach;
|
|
int ret = 0;
|
|
|
|
if (WARN_ON(kmsg->msg.dst_id == KDBUS_DST_ID_BROADCAST) ||
|
|
@@ -1296,16 +1277,8 @@ static int kdbus_conn_unicast(struct kdbus_conn *src, struct kdbus_kmsg *kmsg)
|
|
|
|
/* attach metadata */
|
|
|
|
- attach = kdbus_meta_calc_attach_flags(src, dst);
|
|
-
|
|
- if (!src->faked_meta) {
|
|
- ret = kdbus_meta_proc_collect(kmsg->proc_meta, attach);
|
|
- if (ret < 0 && !is_signal)
|
|
- goto exit;
|
|
- }
|
|
-
|
|
- ret = kdbus_meta_conn_collect(kmsg->conn_meta, kmsg, src, attach);
|
|
- if (ret < 0 && !is_signal)
|
|
+ ret = kdbus_kmsg_collect_metadata(kmsg, src, dst);
|
|
+ if (ret < 0)
|
|
goto exit;
|
|
|
|
/* send message */
|
|
diff --git a/ipc/kdbus/message.c b/ipc/kdbus/message.c
|
|
index 8096075..066e816 100644
|
|
--- a/ipc/kdbus/message.c
|
|
+++ b/ipc/kdbus/message.c
|
|
@@ -614,3 +614,27 @@ exit_free:
|
|
kdbus_kmsg_free(m);
|
|
return ERR_PTR(ret);
|
|
}
|
|
+
|
|
+/**
|
|
+ * kdbus_kmsg_collect_metadata() - collect metadata
|
|
+ * @kmsg: message to collect metadata on
|
|
+ * @src: source connection of message
|
|
+ * @dst: destination connection of message
|
|
+ *
|
|
+ * Return: 0 on success, negative error code on failure.
|
|
+ */
|
|
+int kdbus_kmsg_collect_metadata(struct kdbus_kmsg *kmsg, struct kdbus_conn *src,
|
|
+ struct kdbus_conn *dst)
|
|
+{
|
|
+ u64 attach;
|
|
+ int ret;
|
|
+
|
|
+ attach = kdbus_meta_calc_attach_flags(src, dst);
|
|
+ if (!src->faked_meta) {
|
|
+ ret = kdbus_meta_proc_collect(kmsg->proc_meta, attach);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ return kdbus_meta_conn_collect(kmsg->conn_meta, kmsg, src, attach);
|
|
+}
|
|
diff --git a/ipc/kdbus/message.h b/ipc/kdbus/message.h
|
|
index af47758..cdaa65c 100644
|
|
--- a/ipc/kdbus/message.h
|
|
+++ b/ipc/kdbus/message.h
|
|
@@ -129,5 +129,7 @@ struct kdbus_kmsg *kdbus_kmsg_new(struct kdbus_bus *bus, size_t extra_size);
|
|
struct kdbus_kmsg *kdbus_kmsg_new_from_cmd(struct kdbus_conn *conn,
|
|
struct kdbus_cmd_send *cmd_send);
|
|
void kdbus_kmsg_free(struct kdbus_kmsg *kmsg);
|
|
+int kdbus_kmsg_collect_metadata(struct kdbus_kmsg *kmsg, struct kdbus_conn *src,
|
|
+ struct kdbus_conn *dst);
|
|
|
|
#endif
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From a605e4ed7211a5df1abd4faa0dfb277edde3b363 Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Tue, 26 May 2015 10:01:37 +0200
|
|
Subject: [PATCH 47/78] kdbus: make metadata on broadcasts reliable
|
|
|
|
If we cannot collect metadata, this is a serious error. Don't try to
|
|
continue sending a message, but immediately bail out and tell the receiver
|
|
that we dropped it. Otherwise, the receiver cannot rely on metadata to be
|
|
present and might assume it's a faked connection.
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Acked-by: Daniel Mack <daniel@zonque.org>
|
|
---
|
|
ipc/kdbus/bus.c | 28 ++++++++++++++--------------
|
|
1 file changed, 14 insertions(+), 14 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/bus.c b/ipc/kdbus/bus.c
|
|
index 9a0ecbc..d547596 100644
|
|
--- a/ipc/kdbus/bus.c
|
|
+++ b/ipc/kdbus/bus.c
|
|
@@ -293,12 +293,12 @@ void kdbus_bus_broadcast(struct kdbus_bus *bus,
|
|
if (!kdbus_conn_policy_talk(conn_dst, NULL, conn_src))
|
|
continue;
|
|
|
|
- /*
|
|
- * Keep sending messages even if we cannot acquire the
|
|
- * requested metadata. It's up to the receiver to drop
|
|
- * messages that lack expected metadata.
|
|
- */
|
|
- kdbus_kmsg_collect_metadata(kmsg, conn_src, conn_dst);
|
|
+ ret = kdbus_kmsg_collect_metadata(kmsg, conn_src,
|
|
+ conn_dst);
|
|
+ if (ret < 0) {
|
|
+ kdbus_conn_lost_message(conn_dst);
|
|
+ continue;
|
|
+ }
|
|
} else {
|
|
/*
|
|
* Check if there is a policy db that prevents the
|
|
@@ -344,14 +344,14 @@ void kdbus_bus_eavesdrop(struct kdbus_bus *bus,
|
|
|
|
down_read(&bus->conn_rwlock);
|
|
list_for_each_entry(conn_dst, &bus->monitors_list, monitor_entry) {
|
|
- /*
|
|
- * Collect metadata requested by the destination connection.
|
|
- * Ignore errors, as receivers need to check metadata
|
|
- * availability, anyway. So it's still better to send messages
|
|
- * that lack data, than to skip it entirely.
|
|
- */
|
|
- if (conn_src)
|
|
- kdbus_kmsg_collect_metadata(kmsg, conn_src, conn_dst);
|
|
+ if (conn_src) {
|
|
+ ret = kdbus_kmsg_collect_metadata(kmsg, conn_src,
|
|
+ conn_dst);
|
|
+ if (ret < 0) {
|
|
+ kdbus_conn_lost_message(conn_dst);
|
|
+ continue;
|
|
+ }
|
|
+ }
|
|
|
|
ret = kdbus_conn_entry_insert(conn_src, conn_dst, kmsg, NULL);
|
|
if (ret < 0)
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 17458efe3cda10ceb9193adf0167af0fbd2fb067 Mon Sep 17 00:00:00 2001
|
|
From: Daniel Mack <daniel@zonque.org>
|
|
Date: Fri, 3 Apr 2015 12:41:52 +0200
|
|
Subject: [PATCH 48/78] samples/kdbus: stub out code for glibc < 2.7
|
|
|
|
Andrew Morton reports the following build error in samples/kdbus on Fedora
|
|
Core 6:
|
|
|
|
samples/kdbus/kdbus-workers.c:73:26: error: sys/signalfd.h: No such file or directory
|
|
samples/kdbus/kdbus-workers.c: In function 'master_new':
|
|
samples/kdbus/kdbus-workers.c:231: warning: implicit declaration of function 'signalfd'
|
|
samples/kdbus/kdbus-workers.c:231: error: 'SFD_CLOEXEC' undeclared (first use in this function)
|
|
samples/kdbus/kdbus-workers.c:231: error: (Each undeclared identifier is reported only once
|
|
samples/kdbus/kdbus-workers.c:231: error: for each function it appears in.)
|
|
samples/kdbus/kdbus-workers.c: In function 'master_handle_signal':
|
|
samples/kdbus/kdbus-workers.c:406: error: storage size of 'val' isn't known
|
|
samples/kdbus/kdbus-workers.c:406: warning: unused variable 'val'
|
|
samples/kdbus/kdbus-workers.c: In function 'child_run':
|
|
samples/kdbus/kdbus-workers.c:773: error: 'CLOCK_MONOTONIC_COARSE' undeclared (first use in this function)
|
|
samples/kdbus/kdbus-workers.c: In function 'bus_open_connection':
|
|
samples/kdbus/kdbus-workers.c:1038: error: 'O_CLOEXEC' undeclared (first use in this function)
|
|
samples/kdbus/kdbus-workers.c: In function 'bus_make':
|
|
samples/kdbus/kdbus-workers.c:1275: error: 'O_CLOEXEC' undeclared (first use in this function)
|
|
|
|
Fedora Core 6 was released in 2006, which predates the introduction of
|
|
signalfds in the kernel (v2.6.22, 2007).
|
|
|
|
The example cannot be built without signalfds, and kbuild cannot depend on
|
|
specific features of the local libc when building userspace executables, so
|
|
we have to work around the issue by checking for specific glibc versions at
|
|
compile time and stub the entire thing if it can't be compiled.
|
|
|
|
Reported-by: Andrew Morton <akpm@linux-foundation.org>
|
|
Signed-off-by: Daniel Mack <daniel@zonque.org>
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
---
|
|
samples/kdbus/kdbus-workers.c | 23 +++++++++++++++++++++--
|
|
1 file changed, 21 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/samples/kdbus/kdbus-workers.c b/samples/kdbus/kdbus-workers.c
|
|
index d331e01..c3ba958 100644
|
|
--- a/samples/kdbus/kdbus-workers.c
|
|
+++ b/samples/kdbus/kdbus-workers.c
|
|
@@ -57,6 +57,12 @@
|
|
* top-down, but requires some forward-declarations. Just ignore those.
|
|
*/
|
|
|
|
+#include <stdio.h>
|
|
+#include <stdlib.h>
|
|
+
|
|
+/* glibc < 2.7 does not ship sys/signalfd.h */
|
|
+#if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 7
|
|
+
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
@@ -65,8 +71,6 @@
|
|
#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>
|
|
@@ -1324,3 +1328,18 @@ static int bus_make(uid_t uid, const char *name)
|
|
|
|
return fd;
|
|
}
|
|
+
|
|
+#else
|
|
+
|
|
+#warning "Skipping compilation due to unsupported libc version"
|
|
+
|
|
+int main(int argc, char **argv)
|
|
+{
|
|
+ fprintf(stderr,
|
|
+ "Compilation of %s was skipped due to unsupported libc.\n",
|
|
+ argv[0]);
|
|
+
|
|
+ return EXIT_FAILURE;
|
|
+}
|
|
+
|
|
+#endif /* libc sanity check */
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 96008ee02a576a60afb0475ecb497385c360fa77 Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Thu, 23 Apr 2015 10:23:38 +0200
|
|
Subject: [PATCH 49/78] kdbus: fix up documentation of ioctl handlers
|
|
|
|
We support feature negotiation on ioctls. As this is not necessarily fully
|
|
generic, we indicate this by returning >0 from kdbus_args_parse().
|
|
Therefore, all ioctl handlers that forward the return value of
|
|
kdbus_args_parse() might also return >0 on negotiation. Which is totally
|
|
fine and handled in kdbus_handle_ioctl(). However, the documentation of
|
|
the ioctl handlers doesn't reflect that behavior. Fix those up!
|
|
|
|
Reported-by: Dan Carpenter <dan.carpenter@oracle.com>
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
---
|
|
ipc/kdbus/bus.c | 4 ++--
|
|
ipc/kdbus/connection.c | 14 +++++++-------
|
|
ipc/kdbus/endpoint.c | 4 ++--
|
|
ipc/kdbus/match.c | 4 ++--
|
|
ipc/kdbus/names.c | 6 +++---
|
|
5 files changed, 16 insertions(+), 16 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/bus.c b/ipc/kdbus/bus.c
|
|
index d547596..bbdf0f2 100644
|
|
--- a/ipc/kdbus/bus.c
|
|
+++ b/ipc/kdbus/bus.c
|
|
@@ -365,7 +365,7 @@ void kdbus_bus_eavesdrop(struct kdbus_bus *bus,
|
|
* @domain: domain to operate on
|
|
* @argp: command payload
|
|
*
|
|
- * Return: Newly created bus on success, ERR_PTR on failure.
|
|
+ * 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)
|
|
@@ -459,7 +459,7 @@ exit:
|
|
* @conn: connection to operate on
|
|
* @argp: command payload
|
|
*
|
|
- * Return: 0 on success, negative error code on failure.
|
|
+ * Return: >=0 on success, negative error code on failure.
|
|
*/
|
|
int kdbus_cmd_bus_creator_info(struct kdbus_conn *conn, void __user *argp)
|
|
{
|
|
diff --git a/ipc/kdbus/connection.c b/ipc/kdbus/connection.c
|
|
index cbfbf38..8ee62fc 100644
|
|
--- a/ipc/kdbus/connection.c
|
|
+++ b/ipc/kdbus/connection.c
|
|
@@ -1589,7 +1589,7 @@ bool kdbus_conn_policy_see_notification(struct kdbus_conn *conn,
|
|
* @privileged: Whether the caller is privileged
|
|
* @argp: Command payload
|
|
*
|
|
- * Return: Newly created connection on success, ERR_PTR on failure.
|
|
+ * Return: NULL or newly created connection on success, ERR_PTR on failure.
|
|
*/
|
|
struct kdbus_conn *kdbus_cmd_hello(struct kdbus_ep *ep, bool privileged,
|
|
void __user *argp)
|
|
@@ -1676,7 +1676,7 @@ exit:
|
|
*
|
|
* The caller must not hold any active reference to @conn or this will deadlock.
|
|
*
|
|
- * Return: 0 on success, negative error code on failure.
|
|
+ * Return: >=0 on success, negative error code on failure.
|
|
*/
|
|
int kdbus_cmd_byebye_unlocked(struct kdbus_conn *conn, void __user *argp)
|
|
{
|
|
@@ -1708,7 +1708,7 @@ int kdbus_cmd_byebye_unlocked(struct kdbus_conn *conn, void __user *argp)
|
|
* @conn: connection to operate on
|
|
* @argp: command payload
|
|
*
|
|
- * Return: 0 on success, negative error code on failure.
|
|
+ * Return: >=0 on success, negative error code on failure.
|
|
*/
|
|
int kdbus_cmd_conn_info(struct kdbus_conn *conn, void __user *argp)
|
|
{
|
|
@@ -1838,7 +1838,7 @@ exit:
|
|
* @conn: connection to operate on
|
|
* @argp: command payload
|
|
*
|
|
- * Return: 0 on success, negative error code on failure.
|
|
+ * Return: >=0 on success, negative error code on failure.
|
|
*/
|
|
int kdbus_cmd_update(struct kdbus_conn *conn, void __user *argp)
|
|
{
|
|
@@ -1935,7 +1935,7 @@ exit:
|
|
* @f: file this command was called on
|
|
* @argp: command payload
|
|
*
|
|
- * Return: 0 on success, negative error code on failure.
|
|
+ * Return: >=0 on success, negative error code on failure.
|
|
*/
|
|
int kdbus_cmd_send(struct kdbus_conn *conn, struct file *f, void __user *argp)
|
|
{
|
|
@@ -2031,7 +2031,7 @@ exit:
|
|
* @conn: connection to operate on
|
|
* @argp: command payload
|
|
*
|
|
- * Return: 0 on success, negative error code on failure.
|
|
+ * Return: >=0 on success, negative error code on failure.
|
|
*/
|
|
int kdbus_cmd_recv(struct kdbus_conn *conn, void __user *argp)
|
|
{
|
|
@@ -2154,7 +2154,7 @@ exit:
|
|
* @conn: connection to operate on
|
|
* @argp: command payload
|
|
*
|
|
- * Return: 0 on success, negative error code on failure.
|
|
+ * Return: >=0 on success, negative error code on failure.
|
|
*/
|
|
int kdbus_cmd_free(struct kdbus_conn *conn, void __user *argp)
|
|
{
|
|
diff --git a/ipc/kdbus/endpoint.c b/ipc/kdbus/endpoint.c
|
|
index 174d274..9a95a5e 100644
|
|
--- a/ipc/kdbus/endpoint.c
|
|
+++ b/ipc/kdbus/endpoint.c
|
|
@@ -188,7 +188,7 @@ struct kdbus_ep *kdbus_ep_unref(struct kdbus_ep *ep)
|
|
* @bus: bus to operate on
|
|
* @argp: command payload
|
|
*
|
|
- * Return: Newly created endpoint on success, ERR_PTR on failure.
|
|
+ * 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)
|
|
{
|
|
@@ -247,7 +247,7 @@ exit:
|
|
* @ep: endpoint to operate on
|
|
* @argp: command payload
|
|
*
|
|
- * Return: Newly created endpoint on success, ERR_PTR on failure.
|
|
+ * Return: >=0 on success, negative error code on failure.
|
|
*/
|
|
int kdbus_cmd_ep_update(struct kdbus_ep *ep, void __user *argp)
|
|
{
|
|
diff --git a/ipc/kdbus/match.c b/ipc/kdbus/match.c
|
|
index 30cec1c..cc083b4 100644
|
|
--- a/ipc/kdbus/match.c
|
|
+++ b/ipc/kdbus/match.c
|
|
@@ -368,7 +368,7 @@ static int kdbus_match_db_remove_unlocked(struct kdbus_match_db *mdb,
|
|
* are used to match messages from userspace, while the others apply to
|
|
* kernel-generated notifications.
|
|
*
|
|
- * Return: 0 on success, negative error code on failure.
|
|
+ * Return: >=0 on success, negative error code on failure.
|
|
*/
|
|
int kdbus_cmd_match_add(struct kdbus_conn *conn, void __user *argp)
|
|
{
|
|
@@ -528,7 +528,7 @@ exit:
|
|
* @conn: connection to operate on
|
|
* @argp: command payload
|
|
*
|
|
- * Return: 0 on success, negative error code on failure.
|
|
+ * Return: >=0 on success, negative error code on failure.
|
|
*/
|
|
int kdbus_cmd_match_remove(struct kdbus_conn *conn, void __user *argp)
|
|
{
|
|
diff --git a/ipc/kdbus/names.c b/ipc/kdbus/names.c
|
|
index 657008e..5f5d84e 100644
|
|
--- a/ipc/kdbus/names.c
|
|
+++ b/ipc/kdbus/names.c
|
|
@@ -469,7 +469,7 @@ void kdbus_name_release_all(struct kdbus_name_registry *reg,
|
|
* @conn: connection to operate on
|
|
* @argp: command payload
|
|
*
|
|
- * Return: 0 on success, negative error code on failure.
|
|
+ * Return: >=0 on success, negative error code on failure.
|
|
*/
|
|
int kdbus_cmd_name_acquire(struct kdbus_conn *conn, void __user *argp)
|
|
{
|
|
@@ -528,7 +528,7 @@ exit:
|
|
* @conn: connection to operate on
|
|
* @argp: command payload
|
|
*
|
|
- * Return: 0 on success, negative error code on failure.
|
|
+ * Return: >=0 on success, negative error code on failure.
|
|
*/
|
|
int kdbus_cmd_name_release(struct kdbus_conn *conn, void __user *argp)
|
|
{
|
|
@@ -699,7 +699,7 @@ static int kdbus_list_all(struct kdbus_conn *conn, u64 flags,
|
|
* @conn: connection to operate on
|
|
* @argp: command payload
|
|
*
|
|
- * Return: 0 on success, negative error code on failure.
|
|
+ * Return: >=0 on success, negative error code on failure.
|
|
*/
|
|
int kdbus_cmd_list(struct kdbus_conn *conn, void __user *argp)
|
|
{
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 75f0cba4aceeeecfdb3ab4b4778460a7967f6702 Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Wed, 22 Apr 2015 13:14:24 +0200
|
|
Subject: [PATCH 50/78] kdbus: translate capabilities between namespaces
|
|
|
|
Right now, we always drop capability-items if we cross user-namespaces.
|
|
However, the kernel _does_ support capability translation, as defined in
|
|
./security/commoncap.c cap_capable().
|
|
|
|
This patch adds capability translation support just like cap_capable()
|
|
does. This way, a message sent from a task into a child user-namespace of
|
|
its own, will retain the capability-item and thus keep the parent
|
|
privileged inside of the user-namespace of its children.
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
---
|
|
ipc/kdbus/metadata.c | 126 ++++++++++++++++++++++++++++++++++-----------------
|
|
1 file changed, 84 insertions(+), 42 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/metadata.c b/ipc/kdbus/metadata.c
|
|
index b908b63..7949c8d 100644
|
|
--- a/ipc/kdbus/metadata.c
|
|
+++ b/ipc/kdbus/metadata.c
|
|
@@ -63,8 +63,7 @@
|
|
* @root_path: Root-FS path
|
|
* @cmdline: Command-line
|
|
* @cgroup: Full cgroup path
|
|
- * @caps: Capabilities
|
|
- * @caps_namespace: User-namespace of @caps
|
|
+ * @cred: Credentials
|
|
* @seclabel: Seclabel
|
|
* @audit_loginuid: Audit login-UID
|
|
* @audit_sessionid: Audit session-ID
|
|
@@ -104,14 +103,7 @@ struct kdbus_meta_proc {
|
|
char *cgroup;
|
|
|
|
/* KDBUS_ITEM_CAPS */
|
|
- struct caps {
|
|
- /* binary compatible to kdbus_caps */
|
|
- u32 last_cap;
|
|
- struct {
|
|
- u32 caps[_KERNEL_CAPABILITY_U32S];
|
|
- } set[4];
|
|
- } caps;
|
|
- struct user_namespace *caps_namespace;
|
|
+ const struct cred *cred;
|
|
|
|
/* KDBUS_ITEM_SECLABEL */
|
|
char *seclabel;
|
|
@@ -149,6 +141,14 @@ struct kdbus_meta_conn {
|
|
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
|
|
*
|
|
@@ -175,7 +175,8 @@ static void kdbus_meta_proc_free(struct kref *kref)
|
|
|
|
path_put(&mp->exe_path);
|
|
path_put(&mp->root_path);
|
|
- put_user_ns(mp->caps_namespace);
|
|
+ if (mp->cred)
|
|
+ put_cred(mp->cred);
|
|
put_pid(mp->ppid);
|
|
put_pid(mp->tgid);
|
|
put_pid(mp->pid);
|
|
@@ -354,25 +355,7 @@ static int kdbus_meta_proc_collect_cgroup(struct kdbus_meta_proc *mp)
|
|
|
|
static void kdbus_meta_proc_collect_caps(struct kdbus_meta_proc *mp)
|
|
{
|
|
- const struct cred *c = current_cred();
|
|
- int i;
|
|
-
|
|
- /* ABI: "last_cap" equals /proc/sys/kernel/cap_last_cap */
|
|
- mp->caps.last_cap = CAP_LAST_CAP;
|
|
- mp->caps_namespace = get_user_ns(current_user_ns());
|
|
-
|
|
- CAP_FOR_EACH_U32(i) {
|
|
- mp->caps.set[0].caps[i] = c->cap_inheritable.cap[i];
|
|
- mp->caps.set[1].caps[i] = c->cap_permitted.cap[i];
|
|
- mp->caps.set[2].caps[i] = c->cap_effective.cap[i];
|
|
- mp->caps.set[3].caps[i] = c->cap_bset.cap[i];
|
|
- }
|
|
-
|
|
- /* clear unused bits */
|
|
- for (i = 0; i < 4; i++)
|
|
- mp->caps.set[i].caps[CAP_TO_INDEX(CAP_LAST_CAP)] &=
|
|
- CAP_LAST_U32_VALID_MASK;
|
|
-
|
|
+ mp->cred = get_current_cred();
|
|
mp->valid |= KDBUS_ATTACH_CAPS;
|
|
}
|
|
|
|
@@ -880,7 +863,7 @@ int kdbus_meta_export_prepare(struct kdbus_meta_proc *mp,
|
|
size += KDBUS_ITEM_SIZE(strlen(mp->cgroup) + 1);
|
|
|
|
if (mp && (*mask & KDBUS_ATTACH_CAPS))
|
|
- size += KDBUS_ITEM_SIZE(sizeof(mp->caps));
|
|
+ size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_meta_caps));
|
|
|
|
if (mp && (*mask & KDBUS_ATTACH_SECLABEL))
|
|
size += KDBUS_ITEM_SIZE(strlen(mp->seclabel) + 1);
|
|
@@ -917,6 +900,69 @@ static int kdbus_meta_push_kvec(struct kvec *kvec,
|
|
return 2 + !!kdbus_kvec_pad(kvec++, size);
|
|
}
|
|
|
|
+static void kdbus_meta_export_caps(struct kdbus_meta_caps *out,
|
|
+ struct kdbus_meta_proc *mp)
|
|
+{
|
|
+ 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 current
|
|
+ * user-namespace. If the current 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 current 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 = current_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(kuid_t uid)
|
|
{
|
|
@@ -975,14 +1021,6 @@ int kdbus_meta_export(struct kdbus_meta_proc *mp,
|
|
|
|
hdr = &item_hdr[0];
|
|
|
|
- /*
|
|
- * TODO: We currently have no sane way of translating a set of caps
|
|
- * between different user namespaces. Until that changes, we have
|
|
- * to drop such items.
|
|
- */
|
|
- if (mp && mp->caps_namespace != user_ns)
|
|
- mask &= ~KDBUS_ATTACH_CAPS;
|
|
-
|
|
if (mask == 0) {
|
|
*real_size = 0;
|
|
return 0;
|
|
@@ -1088,10 +1126,14 @@ int kdbus_meta_export(struct kdbus_meta_proc *mp,
|
|
KDBUS_ITEM_CGROUP, mp->cgroup,
|
|
strlen(mp->cgroup) + 1, &size);
|
|
|
|
- if (mp && (mask & KDBUS_ATTACH_CAPS))
|
|
+ if (mp && (mask & KDBUS_ATTACH_CAPS)) {
|
|
+ struct kdbus_meta_caps caps = {};
|
|
+
|
|
+ kdbus_meta_export_caps(&caps, mp);
|
|
cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
|
|
- KDBUS_ITEM_CAPS, &mp->caps,
|
|
- sizeof(mp->caps), &size);
|
|
+ KDBUS_ITEM_CAPS, &caps,
|
|
+ sizeof(caps), &size);
|
|
+ }
|
|
|
|
if (mp && (mask & KDBUS_ATTACH_SECLABEL))
|
|
cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 96cf8b5dbab0155121acadcab875ce591b6b62d2 Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Wed, 3 Jun 2015 17:53:29 +0200
|
|
Subject: [PATCH 51/78] kdbus/selftests: add build-dependencies on headers
|
|
|
|
Make sure the selftests are re-built if one of the local headers changes.
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
---
|
|
tools/testing/selftests/kdbus/Makefile | 2 +-
|
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
|
|
|
diff --git a/tools/testing/selftests/kdbus/Makefile b/tools/testing/selftests/kdbus/Makefile
|
|
index 076f9f4..7ad587b 100644
|
|
--- a/tools/testing/selftests/kdbus/Makefile
|
|
+++ b/tools/testing/selftests/kdbus/Makefile
|
|
@@ -34,7 +34,7 @@ all: kdbus-test
|
|
|
|
include ../lib.mk
|
|
|
|
-%.o: %.c
|
|
+%.o: %.c kdbus-enum.h kdbus-test.h kdbus-util.h
|
|
$(CC) $(CFLAGS) -c $< -o $@
|
|
|
|
kdbus-test: $(OBJS)
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 14cd8a1e61cc5ebc75c6c0e6aea8decc6fdf95c4 Mon Sep 17 00:00:00 2001
|
|
From: Daniel Mack <daniel@zonque.org>
|
|
Date: Sat, 18 Apr 2015 12:04:36 +0200
|
|
Subject: [PATCH 52/78] kdbus: use rcu to access exe file in metadata
|
|
|
|
Commit 90f31d0ea888 ("mm: rcu-protected get_mm_exe_file()") removed
|
|
mm->mmap_sem from mm->exe_file read side. Follow that change in the
|
|
kdbus metadata code.
|
|
|
|
Signed-off-by: Daniel Mack <daniel@zonque.org>
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
---
|
|
ipc/kdbus/metadata.c | 10 ++++++----
|
|
1 file changed, 6 insertions(+), 4 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/metadata.c b/ipc/kdbus/metadata.c
|
|
index 7949c8d..a85eac3 100644
|
|
--- a/ipc/kdbus/metadata.c
|
|
+++ b/ipc/kdbus/metadata.c
|
|
@@ -283,19 +283,21 @@ static void kdbus_meta_proc_collect_pid_comm(struct kdbus_meta_proc *mp)
|
|
static void kdbus_meta_proc_collect_exe(struct kdbus_meta_proc *mp)
|
|
{
|
|
struct mm_struct *mm;
|
|
+ struct file *exe_file;
|
|
|
|
mm = get_task_mm(current);
|
|
if (!mm)
|
|
return;
|
|
|
|
- down_read(&mm->mmap_sem);
|
|
- if (mm->exe_file) {
|
|
- mp->exe_path = mm->exe_file->f_path;
|
|
+ rcu_read_lock();
|
|
+ exe_file = rcu_dereference(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;
|
|
}
|
|
- up_read(&mm->mmap_sem);
|
|
+ rcu_read_unlock();
|
|
|
|
mmput(mm);
|
|
}
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 1fb9c8482dfe0ffba65003cfaa2f5fc20df999d6 Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Mon, 20 Apr 2015 11:13:54 +0200
|
|
Subject: [PATCH 53/78] kdbus: no need to ref current->mm
|
|
|
|
If we access current->mm temporarily, there is no need to ref it. It can
|
|
only be changed by us, so no-one can race with us.
|
|
|
|
Avoid ref'ing and unref'ing it just to access some of its fields, similar
|
|
to what syscalls in mm/ do.
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
---
|
|
ipc/kdbus/metadata.c | 21 +++------------------
|
|
1 file changed, 3 insertions(+), 18 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/metadata.c b/ipc/kdbus/metadata.c
|
|
index a85eac3..c36b9cc 100644
|
|
--- a/ipc/kdbus/metadata.c
|
|
+++ b/ipc/kdbus/metadata.c
|
|
@@ -282,15 +282,10 @@ static void kdbus_meta_proc_collect_pid_comm(struct kdbus_meta_proc *mp)
|
|
|
|
static void kdbus_meta_proc_collect_exe(struct kdbus_meta_proc *mp)
|
|
{
|
|
- struct mm_struct *mm;
|
|
struct file *exe_file;
|
|
|
|
- mm = get_task_mm(current);
|
|
- if (!mm)
|
|
- return;
|
|
-
|
|
rcu_read_lock();
|
|
- exe_file = rcu_dereference(mm->exe_file);
|
|
+ exe_file = rcu_dereference(current->mm->exe_file);
|
|
if (exe_file) {
|
|
mp->exe_path = exe_file->f_path;
|
|
path_get(&mp->exe_path);
|
|
@@ -298,28 +293,18 @@ static void kdbus_meta_proc_collect_exe(struct kdbus_meta_proc *mp)
|
|
mp->valid |= KDBUS_ATTACH_EXE;
|
|
}
|
|
rcu_read_unlock();
|
|
-
|
|
- mmput(mm);
|
|
}
|
|
|
|
static int kdbus_meta_proc_collect_cmdline(struct kdbus_meta_proc *mp)
|
|
{
|
|
- struct mm_struct *mm;
|
|
+ struct mm_struct *mm = current->mm;
|
|
char *cmdline;
|
|
|
|
- mm = get_task_mm(current);
|
|
- if (!mm)
|
|
- return 0;
|
|
-
|
|
- if (!mm->arg_end) {
|
|
- mmput(mm);
|
|
+ if (!mm->arg_end)
|
|
return 0;
|
|
- }
|
|
|
|
cmdline = strndup_user((const char __user *)mm->arg_start,
|
|
mm->arg_end - mm->arg_start);
|
|
- mmput(mm);
|
|
-
|
|
if (IS_ERR(cmdline))
|
|
return PTR_ERR(cmdline);
|
|
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 96a38d3ebefbfa542c3350b6b9f6bee02010db9f Mon Sep 17 00:00:00 2001
|
|
From: Tyler Baker <tyler.baker@linaro.org>
|
|
Date: Tue, 21 Apr 2015 15:50:51 -0700
|
|
Subject: [PATCH 54/78] selftests/kdbus: install kdbus-test
|
|
|
|
Set TEST_PROGS so that kdbus-test is installed.
|
|
|
|
Acked-by: Michael Ellerman <mpe@ellerman.id.au>
|
|
Signed-off-by: Tyler Baker <tyler.baker@linaro.org>
|
|
Cc: Shuah Khan <shuahkh@osg.samsung.com>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
tools/testing/selftests/kdbus/Makefile | 2 ++
|
|
1 file changed, 2 insertions(+)
|
|
|
|
diff --git a/tools/testing/selftests/kdbus/Makefile b/tools/testing/selftests/kdbus/Makefile
|
|
index 7ad587b..8f36cb5 100644
|
|
--- a/tools/testing/selftests/kdbus/Makefile
|
|
+++ b/tools/testing/selftests/kdbus/Makefile
|
|
@@ -40,6 +40,8 @@ include ../lib.mk
|
|
kdbus-test: $(OBJS)
|
|
$(CC) $(CFLAGS) $^ $(LDLIBS) -o $@
|
|
|
|
+TEST_PROGS := kdbus-test
|
|
+
|
|
run_tests:
|
|
./kdbus-test --tap
|
|
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 9c9ea894c5fa1cb619982cf842e0238b44b6b76a Mon Sep 17 00:00:00 2001
|
|
From: Sergei Zviagintsev <sergei@s15v.net>
|
|
Date: Tue, 2 Jun 2015 18:48:47 +0300
|
|
Subject: [PATCH 55/78] kdbus: update kernel-doc for kdbus_sync_reply_wakeup()
|
|
|
|
kdbus_sync_reply_wakeup() doesn't remove reply object from connection
|
|
reply_list. Update function description.
|
|
|
|
Signed-off-by: Sergei Zviagintsev <sergei@s15v.net>
|
|
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
ipc/kdbus/reply.c | 3 +--
|
|
1 file changed, 1 insertion(+), 2 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/reply.c b/ipc/kdbus/reply.c
|
|
index 008dca8..89d355b 100644
|
|
--- a/ipc/kdbus/reply.c
|
|
+++ b/ipc/kdbus/reply.c
|
|
@@ -140,8 +140,7 @@ void kdbus_reply_unlink(struct kdbus_reply *r)
|
|
* @reply: The reply object
|
|
* @err: Error code to set on the remote side
|
|
*
|
|
- * Remove the synchronous reply object from its connection reply_list, and
|
|
- * wake up remote peer (method origin) with the appropriate synchronous reply
|
|
+ * Wake up remote peer (method origin) with the appropriate synchronous reply
|
|
* code.
|
|
*/
|
|
void kdbus_sync_reply_wakeup(struct kdbus_reply *reply, int err)
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 108c5dd68bb73e60b2e30b583263f63392eb2804 Mon Sep 17 00:00:00 2001
|
|
From: Sergei Zviagintsev <sergei@s15v.net>
|
|
Date: Tue, 2 Jun 2015 18:48:48 +0300
|
|
Subject: [PATCH 56/78] kdbus: remove redundant code from
|
|
kdbus_conn_entry_make()
|
|
|
|
We don't need to check `entry' for error, as in either case it is
|
|
returned as is. Return result of kdbus_queue_entry_new() directly.
|
|
|
|
Signed-off-by: Sergei Zviagintsev <sergei@s15v.net>
|
|
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
ipc/kdbus/connection.c | 8 +-------
|
|
1 file changed, 1 insertion(+), 7 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/connection.c b/ipc/kdbus/connection.c
|
|
index 8ee62fc..1bd7bb9 100644
|
|
--- a/ipc/kdbus/connection.c
|
|
+++ b/ipc/kdbus/connection.c
|
|
@@ -775,8 +775,6 @@ kdbus_conn_entry_make(struct kdbus_conn *conn_dst,
|
|
const struct kdbus_kmsg *kmsg,
|
|
struct kdbus_user *user)
|
|
{
|
|
- struct kdbus_queue_entry *entry;
|
|
-
|
|
/* The remote connection was disconnected */
|
|
if (!kdbus_conn_active(conn_dst))
|
|
return ERR_PTR(-ECONNRESET);
|
|
@@ -793,11 +791,7 @@ kdbus_conn_entry_make(struct kdbus_conn *conn_dst,
|
|
kmsg->res && kmsg->res->fds_count > 0)
|
|
return ERR_PTR(-ECOMM);
|
|
|
|
- entry = kdbus_queue_entry_new(conn_dst, kmsg, user);
|
|
- if (IS_ERR(entry))
|
|
- return entry;
|
|
-
|
|
- return entry;
|
|
+ return kdbus_queue_entry_new(conn_dst, kmsg, user);
|
|
}
|
|
|
|
/*
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From ee0440dfd55a6a30fb56703cbd1c465b529c19c2 Mon Sep 17 00:00:00 2001
|
|
From: Sergei Zviagintsev <sergei@s15v.net>
|
|
Date: Tue, 2 Jun 2015 18:48:49 +0300
|
|
Subject: [PATCH 57/78] kdbus: kdbus_item_validate(): remove duplicated code
|
|
|
|
KDBUS_ITEM_PAYLOAD_VEC and KDBUS_ITEM_PAYLOAD_OFF cases use literally
|
|
the same code, so merge them.
|
|
|
|
Signed-off-by: Sergei Zviagintsev <sergei@s15v.net>
|
|
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
ipc/kdbus/item.c | 6 ------
|
|
1 file changed, 6 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/item.c b/ipc/kdbus/item.c
|
|
index 745ad54..1ee72c2 100644
|
|
--- a/ipc/kdbus/item.c
|
|
+++ b/ipc/kdbus/item.c
|
|
@@ -96,12 +96,6 @@ int kdbus_item_validate(const struct kdbus_item *item)
|
|
break;
|
|
|
|
case KDBUS_ITEM_PAYLOAD_VEC:
|
|
- 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_OFF:
|
|
if (payload_size != sizeof(struct kdbus_vec))
|
|
return -EINVAL;
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 81d2a5a70f62ac04b7074d7aec86cf43dea3ca2f Mon Sep 17 00:00:00 2001
|
|
From: Sergei Zviagintsev <sergei@s15v.net>
|
|
Date: Tue, 2 Jun 2015 18:48:50 +0300
|
|
Subject: [PATCH 58/78] kdbus: kdbus_conn_connect(): use `bus' instead of
|
|
`conn->ep->bus'
|
|
|
|
Local `bus' is already set to `conn->ep->bus'. Use it.
|
|
|
|
Signed-off-by: Sergei Zviagintsev <sergei@s15v.net>
|
|
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
ipc/kdbus/connection.c | 2 +-
|
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
|
|
|
diff --git a/ipc/kdbus/connection.c b/ipc/kdbus/connection.c
|
|
index 1bd7bb9..707be05 100644
|
|
--- a/ipc/kdbus/connection.c
|
|
+++ b/ipc/kdbus/connection.c
|
|
@@ -432,7 +432,7 @@ static int kdbus_conn_connect(struct kdbus_conn *conn, const char *name)
|
|
* directly, and won't cause any notifications.
|
|
*/
|
|
if (!kdbus_conn_is_monitor(conn)) {
|
|
- ret = kdbus_notify_id_change(conn->ep->bus, KDBUS_ITEM_ID_ADD,
|
|
+ ret = kdbus_notify_id_change(bus, KDBUS_ITEM_ID_ADD,
|
|
conn->id, conn->flags);
|
|
if (ret < 0)
|
|
goto exit_disconnect;
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From db0a8149ddfb5f23eca9a535d024da4350c721a5 Mon Sep 17 00:00:00 2001
|
|
From: Sergei Zviagintsev <sergei@s15v.net>
|
|
Date: Thu, 4 Jun 2015 13:39:30 +0300
|
|
Subject: [PATCH 59/78] kdbus: use FIELD_SIZEOF in kdbus_member_set_user macro
|
|
|
|
sizeof(((_t *)0)->_m) -> FIELD_SIZEOF(_t, _m)
|
|
|
|
Use conventional macro according to chapter 17 of
|
|
Documentation/CodingStyle.
|
|
|
|
Signed-off-by: Sergei Zviagintsev <sergei@s15v.net>
|
|
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
ipc/kdbus/util.h | 2 +-
|
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
|
|
|
diff --git a/ipc/kdbus/util.h b/ipc/kdbus/util.h
|
|
index 9fedf8a..5297166 100644
|
|
--- a/ipc/kdbus/util.h
|
|
+++ b/ipc/kdbus/util.h
|
|
@@ -40,7 +40,7 @@
|
|
({ \
|
|
u64 __user *_sz = \
|
|
(void __user *)((u8 __user *)(_b) + offsetof(_t, _m)); \
|
|
- copy_to_user(_sz, _s, sizeof(((_t *)0)->_m)); \
|
|
+ copy_to_user(_sz, _s, FIELD_SIZEOF(_t, _m)); \
|
|
})
|
|
|
|
/**
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From b17d8bbc5b4ad33c9efce52de87ee4ac1015ab84 Mon Sep 17 00:00:00 2001
|
|
From: Sergei Zviagintsev <sergei@s15v.net>
|
|
Date: Wed, 17 Jun 2015 19:33:24 +0300
|
|
Subject: [PATCH 60/78] selftests/kdbus: handle cap_get_proc() error properly
|
|
|
|
Fix typo in checking error value of cap_get_proc(): cap -> caps
|
|
|
|
Signed-off-by: Sergei Zviagintsev <sergei@s15v.net>
|
|
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
tools/testing/selftests/kdbus/kdbus-util.c | 2 +-
|
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
|
|
|
diff --git a/tools/testing/selftests/kdbus/kdbus-util.c b/tools/testing/selftests/kdbus/kdbus-util.c
|
|
index 4b376ec..6909fb9 100644
|
|
--- a/tools/testing/selftests/kdbus/kdbus-util.c
|
|
+++ b/tools/testing/selftests/kdbus/kdbus-util.c
|
|
@@ -1547,7 +1547,7 @@ int test_is_capable(int cap, ...)
|
|
cap_t caps;
|
|
|
|
caps = cap_get_proc();
|
|
- if (!cap) {
|
|
+ if (!caps) {
|
|
ret = -errno;
|
|
kdbus_printf("error cap_get_proc(): %d (%m)\n", ret);
|
|
return ret;
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From a3bcbc3b9786b9b031bcf8324ac7ba7ca906d7e4 Mon Sep 17 00:00:00 2001
|
|
From: Sergei Zviagintsev <sergei@s15v.net>
|
|
Date: Wed, 17 Jun 2015 19:33:25 +0300
|
|
Subject: [PATCH 61/78] selftests/kdbus: drop useless assignment
|
|
|
|
Assign returned file descriptor directly to `fd', without intermediate
|
|
`ret' variable.
|
|
|
|
Signed-off-by: Sergei Zviagintsev <sergei@s15v.net>
|
|
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
tools/testing/selftests/kdbus/kdbus-util.c | 8 +++-----
|
|
1 file changed, 3 insertions(+), 5 deletions(-)
|
|
|
|
diff --git a/tools/testing/selftests/kdbus/kdbus-util.c b/tools/testing/selftests/kdbus/kdbus-util.c
|
|
index 6909fb9..5b92453 100644
|
|
--- a/tools/testing/selftests/kdbus/kdbus-util.c
|
|
+++ b/tools/testing/selftests/kdbus/kdbus-util.c
|
|
@@ -408,11 +408,9 @@ 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;
|
|
+ fd = syscall(__NR_memfd_create, name, MFD_ALLOW_SEALING);
|
|
+ if (fd < 0)
|
|
+ return fd;
|
|
|
|
ret = ftruncate(fd, size);
|
|
if (ret < 0) {
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 684c669c95184cf446f309556a634bd91175b5a8 Mon Sep 17 00:00:00 2001
|
|
From: Sergei Zviagintsev <sergei@s15v.net>
|
|
Date: Wed, 17 Jun 2015 19:33:26 +0300
|
|
Subject: [PATCH 62/78] selftests/kdbus: remove useless initializations from
|
|
kdbus_clone_userns_test()
|
|
|
|
Do not initialize efd, unpriv_conn_id, userns_conn_id and monitor. These
|
|
vars are assigned to values later in code while initial values are never
|
|
used.
|
|
|
|
Signed-off-by: Sergei Zviagintsev <sergei@s15v.net>
|
|
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
tools/testing/selftests/kdbus/test-metadata-ns.c | 9 +++------
|
|
1 file changed, 3 insertions(+), 6 deletions(-)
|
|
|
|
diff --git a/tools/testing/selftests/kdbus/test-metadata-ns.c b/tools/testing/selftests/kdbus/test-metadata-ns.c
|
|
index 2cb1d4d..ccdfae0 100644
|
|
--- a/tools/testing/selftests/kdbus/test-metadata-ns.c
|
|
+++ b/tools/testing/selftests/kdbus/test-metadata-ns.c
|
|
@@ -281,16 +281,13 @@ out:
|
|
static int kdbus_clone_userns_test(const char *bus,
|
|
struct kdbus_conn *conn)
|
|
{
|
|
- int ret;
|
|
- int status;
|
|
- int efd = -1;
|
|
+ int ret, status, efd;
|
|
pid_t pid, ppid;
|
|
- uint64_t unpriv_conn_id = 0;
|
|
- uint64_t userns_conn_id = 0;
|
|
+ uint64_t unpriv_conn_id, userns_conn_id;
|
|
struct kdbus_msg *msg;
|
|
const struct kdbus_item *item;
|
|
struct kdbus_pids expected_pids;
|
|
- struct kdbus_conn *monitor = NULL;
|
|
+ struct kdbus_conn *monitor;
|
|
|
|
kdbus_printf("STARTING TEST 'metadata-ns'.\n");
|
|
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 3c468e27e8523be85e7754b29c923914483c52f4 Mon Sep 17 00:00:00 2001
|
|
From: Sergei Zviagintsev <sergei@s15v.net>
|
|
Date: Wed, 17 Jun 2015 19:33:27 +0300
|
|
Subject: [PATCH 63/78] selftests/kdbus: drop duplicated code from
|
|
__kdbus_msg_send()
|
|
|
|
Set value of `size' in one step instead of four.
|
|
|
|
Signed-off-by: Sergei Zviagintsev <sergei@s15v.net>
|
|
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
tools/testing/selftests/kdbus/kdbus-util.c | 5 +----
|
|
1 file changed, 1 insertion(+), 4 deletions(-)
|
|
|
|
diff --git a/tools/testing/selftests/kdbus/kdbus-util.c b/tools/testing/selftests/kdbus/kdbus-util.c
|
|
index 5b92453..d35ec89 100644
|
|
--- a/tools/testing/selftests/kdbus/kdbus-util.c
|
|
+++ b/tools/testing/selftests/kdbus/kdbus-util.c
|
|
@@ -462,10 +462,7 @@ static int __kdbus_msg_send(const struct kdbus_conn *conn,
|
|
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 = sizeof(*msg) + 3 * KDBUS_ITEM_SIZE(sizeof(struct kdbus_vec));
|
|
|
|
if (dst_id == KDBUS_DST_ID_BROADCAST)
|
|
size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_bloom_filter)) + 64;
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 33e67d8577b6c9297257a48338a2f62193d7bce8 Mon Sep 17 00:00:00 2001
|
|
From: Sergei Zviagintsev <sergei@s15v.net>
|
|
Date: Wed, 17 Jun 2015 19:33:28 +0300
|
|
Subject: [PATCH 64/78] selftests/kdbus: fix error paths in __kdbus_msg_send()
|
|
|
|
Handle errors properly, free allocated resources.
|
|
|
|
Signed-off-by: Sergei Zviagintsev <sergei@s15v.net>
|
|
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
tools/testing/selftests/kdbus/kdbus-util.c | 31 ++++++++++++++++++------------
|
|
1 file changed, 19 insertions(+), 12 deletions(-)
|
|
|
|
diff --git a/tools/testing/selftests/kdbus/kdbus-util.c b/tools/testing/selftests/kdbus/kdbus-util.c
|
|
index d35ec89..9fac4b3 100644
|
|
--- a/tools/testing/selftests/kdbus/kdbus-util.c
|
|
+++ b/tools/testing/selftests/kdbus/kdbus-util.c
|
|
@@ -452,8 +452,8 @@ static int __kdbus_msg_send(const struct kdbus_conn *conn,
|
|
uint64_t cmd_flags,
|
|
int cancel_fd)
|
|
{
|
|
- struct kdbus_cmd_send *cmd;
|
|
- struct kdbus_msg *msg;
|
|
+ struct kdbus_cmd_send *cmd = NULL;
|
|
+ struct kdbus_msg *msg = NULL;
|
|
const char ref1[1024 * 128 + 3] = "0123456789_0";
|
|
const char ref2[] = "0123456789_1";
|
|
struct kdbus_item *item;
|
|
@@ -476,14 +476,14 @@ static int __kdbus_msg_send(const struct kdbus_conn *conn,
|
|
if (write(memfd, "kdbus memfd 1234567", 19) != 19) {
|
|
ret = -errno;
|
|
kdbus_printf("writing to memfd failed: %m\n");
|
|
- return ret;
|
|
+ goto out;
|
|
}
|
|
|
|
ret = sys_memfd_seal_set(memfd);
|
|
if (ret < 0) {
|
|
ret = -errno;
|
|
kdbus_printf("memfd sealing failed: %m\n");
|
|
- return ret;
|
|
+ goto out;
|
|
}
|
|
|
|
size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_memfd));
|
|
@@ -496,7 +496,7 @@ static int __kdbus_msg_send(const struct kdbus_conn *conn,
|
|
if (!msg) {
|
|
ret = -errno;
|
|
kdbus_printf("unable to malloc()!?\n");
|
|
- return ret;
|
|
+ goto out;
|
|
}
|
|
|
|
if (dst_id == KDBUS_DST_ID_BROADCAST)
|
|
@@ -514,7 +514,7 @@ static int __kdbus_msg_send(const struct kdbus_conn *conn,
|
|
if (timeout) {
|
|
ret = clock_gettime(CLOCK_MONOTONIC_COARSE, &now);
|
|
if (ret < 0)
|
|
- return ret;
|
|
+ goto out;
|
|
|
|
msg->timeout_ns = now.tv_sec * 1000000000ULL +
|
|
now.tv_nsec + timeout;
|
|
@@ -565,6 +565,12 @@ static int __kdbus_msg_send(const struct kdbus_conn *conn,
|
|
size += KDBUS_ITEM_SIZE(sizeof(cancel_fd));
|
|
|
|
cmd = malloc(size);
|
|
+ if (!cmd) {
|
|
+ ret = -errno;
|
|
+ kdbus_printf("unable to malloc()!?\n");
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
cmd->size = size;
|
|
cmd->flags = cmd_flags;
|
|
cmd->msg_address = (uintptr_t)msg;
|
|
@@ -579,12 +585,9 @@ static int __kdbus_msg_send(const struct kdbus_conn *conn,
|
|
}
|
|
|
|
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;
|
|
+ goto out;
|
|
}
|
|
|
|
if (cmd_flags & KDBUS_SEND_SYNC_REPLY) {
|
|
@@ -598,13 +601,17 @@ static int __kdbus_msg_send(const struct kdbus_conn *conn,
|
|
|
|
ret = kdbus_free(conn, cmd->reply.offset);
|
|
if (ret < 0)
|
|
- return ret;
|
|
+ goto out;
|
|
}
|
|
|
|
+out:
|
|
free(msg);
|
|
free(cmd);
|
|
|
|
- return 0;
|
|
+ if (memfd >= 0)
|
|
+ close(memfd);
|
|
+
|
|
+ return ret < 0 ? ret : 0;
|
|
}
|
|
|
|
int kdbus_msg_send(const struct kdbus_conn *conn, const char *name,
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 559b841d9d8365120d0a5e8ef94a4daf64f265fb Mon Sep 17 00:00:00 2001
|
|
From: Marc-Antoine Perennou <Marc-Antoine@Perennou.com>
|
|
Date: Fri, 5 Jun 2015 14:37:34 +0200
|
|
Subject: [PATCH 65/78] kdbus: drop useless goto
|
|
|
|
Signed-off-by: Marc-Antoine Perennou <Marc-Antoine@Perennou.com>
|
|
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
ipc/kdbus/names.c | 2 --
|
|
1 file changed, 2 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/names.c b/ipc/kdbus/names.c
|
|
index 5f5d84e..d77ee08 100644
|
|
--- a/ipc/kdbus/names.c
|
|
+++ b/ipc/kdbus/names.c
|
|
@@ -514,8 +514,6 @@ int kdbus_cmd_name_acquire(struct kdbus_conn *conn, void __user *argp)
|
|
|
|
ret = kdbus_name_acquire(conn->ep->bus->name_registry, conn, item_name,
|
|
cmd->flags, &cmd->return_flags);
|
|
- if (ret < 0)
|
|
- goto exit_dec;
|
|
|
|
exit_dec:
|
|
atomic_dec(&conn->name_count);
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 8b256c603f372d0d36c0d0c76e7df52ef79a1ed3 Mon Sep 17 00:00:00 2001
|
|
From: Sergei Zviagintsev <sergei@s15v.net>
|
|
Date: Tue, 9 Jun 2015 23:59:59 +0300
|
|
Subject: [PATCH 66/78] kdbus: fix operator precedence issues in item macros
|
|
|
|
`_i' argument in KDBUS_ITEM_NEXT and KDBUS_ITEMS_END macros is not
|
|
enclosed into parentheses when the cast operator is applied, which
|
|
leads to improper type conversion if `_i' is supplied as a complex
|
|
expression, e.g.
|
|
|
|
KDBUS_ITEM_NEXT(condition ? a : b)
|
|
|
|
KDBUS_ITEMS_SIZE macro has similar issue, missing parentheses around
|
|
`_h' when using indirection operator.
|
|
|
|
Use parentheses properly to guarantee right precedence.
|
|
|
|
Signed-off-by: Sergei Zviagintsev <sergei@s15v.net>
|
|
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
ipc/kdbus/item.h | 6 +++---
|
|
1 file changed, 3 insertions(+), 3 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/item.h b/ipc/kdbus/item.h
|
|
index eeefd8b..32909e2 100644
|
|
--- a/ipc/kdbus/item.h
|
|
+++ b/ipc/kdbus/item.h
|
|
@@ -21,8 +21,8 @@
|
|
#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_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)
|
|
@@ -40,7 +40,7 @@
|
|
(u8 *)(_i) >= (u8 *)(_is))
|
|
|
|
#define KDBUS_ITEMS_END(_i, _is, _s) \
|
|
- ((u8 *)_i == ((u8 *)(_is) + KDBUS_ALIGN8(_s)))
|
|
+ ((u8 *)(_i) == ((u8 *)(_is) + KDBUS_ALIGN8(_s)))
|
|
|
|
/**
|
|
* struct kdbus_item_header - Describes the fix part of an item
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From fc10040157a349e9437e1d13421fcaf5d923b0b3 Mon Sep 17 00:00:00 2001
|
|
From: Sergei Zviagintsev <sergei@s15v.net>
|
|
Date: Wed, 10 Jun 2015 00:00:00 +0300
|
|
Subject: [PATCH 67/78] kdbus: use parentheses uniformly in KDBUS_ITEMS_FOREACH
|
|
macro
|
|
|
|
Enclose all arguments into parentheses to stay consistent across the
|
|
whole macro.
|
|
|
|
Signed-off-by: Sergei Zviagintsev <sergei@s15v.net>
|
|
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
ipc/kdbus/item.h | 4 ++--
|
|
1 file changed, 2 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/item.h b/ipc/kdbus/item.h
|
|
index 32909e2..bca63b4 100644
|
|
--- a/ipc/kdbus/item.h
|
|
+++ b/ipc/kdbus/item.h
|
|
@@ -28,10 +28,10 @@
|
|
#define KDBUS_ITEM_PAYLOAD_SIZE(_i) ((_i)->size - KDBUS_ITEM_HEADER_SIZE)
|
|
|
|
#define KDBUS_ITEMS_FOREACH(_i, _is, _s) \
|
|
- for (_i = _is; \
|
|
+ for ((_i) = (_is); \
|
|
((u8 *)(_i) < (u8 *)(_is) + (_s)) && \
|
|
((u8 *)(_i) >= (u8 *)(_is)); \
|
|
- _i = KDBUS_ITEM_NEXT(_i))
|
|
+ (_i) = KDBUS_ITEM_NEXT(_i))
|
|
|
|
#define KDBUS_ITEM_VALID(_i, _is, _s) \
|
|
((_i)->size >= KDBUS_ITEM_HEADER_SIZE && \
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 441274901febbc76f66d73250cf0f2b5f6af305a Mon Sep 17 00:00:00 2001
|
|
From: Sergei Zviagintsev <sergei@s15v.net>
|
|
Date: Wed, 10 Jun 2015 00:00:01 +0300
|
|
Subject: [PATCH 68/78] Documentation/kdbus: fix operator precedence issue in
|
|
KDBUS_ITEM_NEXT macro
|
|
|
|
`item' argument in KDBUS_ITEM_NEXT macro example is not enclosed into
|
|
parentheses when the cast operator is applied, which leads to improper
|
|
type conversion if `item' is supplied as a complex expression, e.g.
|
|
|
|
KDBUS_ITEM_NEXT(condition ? a : b)
|
|
|
|
Use parentheses properly to guarantee right precedence.
|
|
|
|
Signed-off-by: Sergei Zviagintsev <sergei@s15v.net>
|
|
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
Documentation/kdbus/kdbus.item.xml | 2 +-
|
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
|
|
|
diff --git a/Documentation/kdbus/kdbus.item.xml b/Documentation/kdbus/kdbus.item.xml
|
|
index 09f8b90..b0eeeef 100644
|
|
--- a/Documentation/kdbus/kdbus.item.xml
|
|
+++ b/Documentation/kdbus/kdbus.item.xml
|
|
@@ -69,7 +69,7 @@
|
|
#define KDBUS_ALIGN8(val) (((val) + 7) & ~7)
|
|
|
|
#define KDBUS_ITEM_NEXT(item) \
|
|
- (typeof(item))(((uint8_t *)item) + KDBUS_ALIGN8((item)->size))
|
|
+ (typeof(item))((uint8_t *)(item) + KDBUS_ALIGN8((item)->size))
|
|
|
|
#define KDBUS_ITEM_FOREACH(item, head, first) \
|
|
for (item = (head)->first; \
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From bf6cd22a8845e1f7e62d55673527fe599b92a863 Mon Sep 17 00:00:00 2001
|
|
From: Sergei Zviagintsev <sergei@s15v.net>
|
|
Date: Wed, 10 Jun 2015 00:00:02 +0300
|
|
Subject: [PATCH 69/78] Documentation/kdbus: use parentheses uniformly in
|
|
KDBUS_ITEM_FOREACH macro
|
|
|
|
Enclose all arguments into parentheses to stay consistent across the
|
|
whole macro.
|
|
|
|
Signed-off-by: Sergei Zviagintsev <sergei@s15v.net>
|
|
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
Documentation/kdbus/kdbus.item.xml | 4 ++--
|
|
1 file changed, 2 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/Documentation/kdbus/kdbus.item.xml b/Documentation/kdbus/kdbus.item.xml
|
|
index b0eeeef..ee09dfa 100644
|
|
--- a/Documentation/kdbus/kdbus.item.xml
|
|
+++ b/Documentation/kdbus/kdbus.item.xml
|
|
@@ -72,10 +72,10 @@
|
|
(typeof(item))((uint8_t *)(item) + KDBUS_ALIGN8((item)->size))
|
|
|
|
#define KDBUS_ITEM_FOREACH(item, head, first) \
|
|
- for (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))
|
|
+ (item) = KDBUS_ITEM_NEXT(item))
|
|
]]></programlisting>
|
|
</refsect2>
|
|
</refsect1>
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 4853c1358c6d3feeabb6a95f5b5151bc892e9a35 Mon Sep 17 00:00:00 2001
|
|
From: Sergei Zviagintsev <sergei@s15v.net>
|
|
Date: Wed, 10 Jun 2015 00:00:03 +0300
|
|
Subject: [PATCH 70/78] selftests/kdbus: fix trivial style issues
|
|
|
|
Signed-off-by: Sergei Zviagintsev <sergei@s15v.net>
|
|
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
tools/testing/selftests/kdbus/kdbus-enum.h | 1 +
|
|
tools/testing/selftests/kdbus/kdbus-util.c | 2 +-
|
|
tools/testing/selftests/kdbus/kdbus-util.h | 21 +++++++++------------
|
|
3 files changed, 11 insertions(+), 13 deletions(-)
|
|
|
|
diff --git a/tools/testing/selftests/kdbus/kdbus-enum.h b/tools/testing/selftests/kdbus/kdbus-enum.h
|
|
index a67cec3..ed28cca 100644
|
|
--- a/tools/testing/selftests/kdbus/kdbus-enum.h
|
|
+++ b/tools/testing/selftests/kdbus/kdbus-enum.h
|
|
@@ -6,6 +6,7 @@
|
|
* 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);
|
|
diff --git a/tools/testing/selftests/kdbus/kdbus-util.c b/tools/testing/selftests/kdbus/kdbus-util.c
|
|
index 9fac4b3..29a0cb1 100644
|
|
--- a/tools/testing/selftests/kdbus/kdbus-util.c
|
|
+++ b/tools/testing/selftests/kdbus/kdbus-util.c
|
|
@@ -1355,7 +1355,7 @@ static int all_ids_are_mapped(const char *path)
|
|
return 0;
|
|
}
|
|
|
|
-int all_uids_gids_are_mapped()
|
|
+int all_uids_gids_are_mapped(void)
|
|
{
|
|
int ret;
|
|
|
|
diff --git a/tools/testing/selftests/kdbus/kdbus-util.h b/tools/testing/selftests/kdbus/kdbus-util.h
|
|
index 50ff071..b53b03f 100644
|
|
--- a/tools/testing/selftests/kdbus/kdbus-util.h
|
|
+++ b/tools/testing/selftests/kdbus/kdbus-util.h
|
|
@@ -7,6 +7,7 @@
|
|
* Free Software Foundation; either version 2.1 of the License, or (at
|
|
* your option) any later version.
|
|
*/
|
|
+
|
|
#pragma once
|
|
|
|
#define BIT(X) (1 << (X))
|
|
@@ -30,24 +31,22 @@
|
|
#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)); \
|
|
+ ((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)))
|
|
-
|
|
+ iter = (void *)(((uint8_t *)iter) + KDBUS_ALIGN8((iter)->size)))
|
|
|
|
-#define _KDBUS_ATTACH_BITS_SET_NR (__builtin_popcountll(_KDBUS_ATTACH_ALL))
|
|
+#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 ) + \
|
|
+#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
|
|
@@ -207,14 +206,12 @@ 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 all_uids_gids_are_mapped(void);
|
|
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 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);
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From a0ced7b35a9c4e211f0a81ed5a09a75cb16be194 Mon Sep 17 00:00:00 2001
|
|
From: Sergei Zviagintsev <sergei@s15v.net>
|
|
Date: Wed, 10 Jun 2015 00:00:04 +0300
|
|
Subject: [PATCH 71/78] selftests/kdbus: fix precedence issues in macros
|
|
|
|
`item' argument in KDBUS_ITEM_NEXT macro is not enclosed into
|
|
parentheses when the cast operator is applied, which leads to improper
|
|
type conversion if `item' is supplied as a complex expression, e.g.
|
|
|
|
KDBUS_ITEM_NEXT(condition ? a : b)
|
|
|
|
RUN_CLONE_CHILD macro has similar issue, missing parentheses around
|
|
`clone_ret' when using indirection operator.
|
|
|
|
Use parentheses properly to guarantee right precedence.
|
|
|
|
Signed-off-by: Sergei Zviagintsev <sergei@s15v.net>
|
|
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
tools/testing/selftests/kdbus/kdbus-util.h | 6 +++---
|
|
1 file changed, 3 insertions(+), 3 deletions(-)
|
|
|
|
diff --git a/tools/testing/selftests/kdbus/kdbus-util.h b/tools/testing/selftests/kdbus/kdbus-util.h
|
|
index b53b03f..df5721e 100644
|
|
--- a/tools/testing/selftests/kdbus/kdbus-util.h
|
|
+++ b/tools/testing/selftests/kdbus/kdbus-util.h
|
|
@@ -27,7 +27,7 @@
|
|
#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))
|
|
+ (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) && \
|
|
@@ -104,7 +104,7 @@ extern int kdbus_util_verbose;
|
|
_setup_; \
|
|
efd = eventfd(0, EFD_CLOEXEC); \
|
|
ASSERT_RETURN(efd >= 0); \
|
|
- *clone_ret = 0; \
|
|
+ *(clone_ret) = 0; \
|
|
pid = syscall(__NR_clone, flags, NULL); \
|
|
if (pid == 0) { \
|
|
eventfd_t event_status = 0; \
|
|
@@ -129,7 +129,7 @@ extern int kdbus_util_verbose;
|
|
ret = TEST_OK; \
|
|
} else { \
|
|
ret = -errno; \
|
|
- *clone_ret = -errno; \
|
|
+ *(clone_ret) = -errno; \
|
|
} \
|
|
close(efd); \
|
|
ret; \
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 4f64786094f0b697d6445bd00bb8c2500893f3f8 Mon Sep 17 00:00:00 2001
|
|
From: Sergei Zviagintsev <sergei@s15v.net>
|
|
Date: Wed, 10 Jun 2015 00:00:05 +0300
|
|
Subject: [PATCH 72/78] selftests/kdbus: use parentheses in iteration macros
|
|
uniformly
|
|
|
|
Enclose all arguments into parentheses in KDBUS_ITEM_FOREACH and
|
|
KDBUS_FOREACH macros to stay consistent across the whole macro.
|
|
|
|
Signed-off-by: Sergei Zviagintsev <sergei@s15v.net>
|
|
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
tools/testing/selftests/kdbus/kdbus-util.h | 8 ++++----
|
|
1 file changed, 4 insertions(+), 4 deletions(-)
|
|
|
|
diff --git a/tools/testing/selftests/kdbus/kdbus-util.h b/tools/testing/selftests/kdbus/kdbus-util.h
|
|
index df5721e..d1a0f1b 100644
|
|
--- a/tools/testing/selftests/kdbus/kdbus-util.h
|
|
+++ b/tools/testing/selftests/kdbus/kdbus-util.h
|
|
@@ -29,15 +29,15 @@
|
|
#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; \
|
|
+ for ((item) = (head)->first; \
|
|
((uint8_t *)(item) < (uint8_t *)(head) + (head)->size) && \
|
|
((uint8_t *)(item) >= (uint8_t *)(head)); \
|
|
- item = KDBUS_ITEM_NEXT(item))
|
|
+ (item) = KDBUS_ITEM_NEXT(item))
|
|
#define KDBUS_FOREACH(iter, first, _size) \
|
|
- for (iter = (first); \
|
|
+ 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)))
|
|
+ (iter) = (void *)((uint8_t *)(iter) + KDBUS_ALIGN8((iter)->size)))
|
|
|
|
#define _KDBUS_ATTACH_BITS_SET_NR (__builtin_popcountll(_KDBUS_ATTACH_ALL))
|
|
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From ef4fe0e145267838df2b2eff6541ae8ded6510d6 Mon Sep 17 00:00:00 2001
|
|
From: Sergei Zviagintsev <sergei@s15v.net>
|
|
Date: Wed, 10 Jun 2015 00:00:06 +0300
|
|
Subject: [PATCH 73/78] samples/kdbus: add whitespace
|
|
|
|
Signed-off-by: Sergei Zviagintsev <sergei@s15v.net>
|
|
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
samples/kdbus/kdbus-api.h | 2 +-
|
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
|
|
|
diff --git a/samples/kdbus/kdbus-api.h b/samples/kdbus/kdbus-api.h
|
|
index 5ed5907..2de4d6a 100644
|
|
--- a/samples/kdbus/kdbus-api.h
|
|
+++ b/samples/kdbus/kdbus-api.h
|
|
@@ -13,7 +13,7 @@
|
|
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)))
|
|
+ iter = (void *)(((uint8_t *)iter) + KDBUS_ALIGN8((iter)->size)))
|
|
|
|
static inline int kdbus_cmd_bus_make(int control_fd, struct kdbus_cmd *cmd)
|
|
{
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 138ef61b6db6de87c013b2f9da4fbcb3d7ac52e3 Mon Sep 17 00:00:00 2001
|
|
From: Sergei Zviagintsev <sergei@s15v.net>
|
|
Date: Wed, 10 Jun 2015 00:00:07 +0300
|
|
Subject: [PATCH 74/78] samples/kdbus: fix operator precedence issue in
|
|
KDBUS_ITEM_NEXT macro
|
|
|
|
`item' argument in KDBUS_ITEM_NEXT macro is not enclosed into
|
|
parentheses when the cast operator is applied, which leads to improper
|
|
type conversion if `item' is supplied as a complex expression, e.g.
|
|
|
|
KDBUS_ITEM_NEXT(condition ? a : b)
|
|
|
|
Use parentheses properly to guarantee right precedence.
|
|
|
|
Signed-off-by: Sergei Zviagintsev <sergei@s15v.net>
|
|
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
samples/kdbus/kdbus-api.h | 2 +-
|
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
|
|
|
diff --git a/samples/kdbus/kdbus-api.h b/samples/kdbus/kdbus-api.h
|
|
index 2de4d6a..fab873b 100644
|
|
--- a/samples/kdbus/kdbus-api.h
|
|
+++ b/samples/kdbus/kdbus-api.h
|
|
@@ -8,7 +8,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))
|
|
+ (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)) && \
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From ff544d55a78cf60b30b91936de23cce5426ba3a7 Mon Sep 17 00:00:00 2001
|
|
From: Sergei Zviagintsev <sergei@s15v.net>
|
|
Date: Wed, 10 Jun 2015 00:00:08 +0300
|
|
Subject: [PATCH 75/78] samples/kdbus: use parentheses uniformly in
|
|
KDBUS_FOREACH macro
|
|
|
|
Enclose all arguments into parentheses to stay consistent across the
|
|
whole macro.
|
|
|
|
Signed-off-by: Sergei Zviagintsev <sergei@s15v.net>
|
|
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
samples/kdbus/kdbus-api.h | 4 ++--
|
|
1 file changed, 2 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/samples/kdbus/kdbus-api.h b/samples/kdbus/kdbus-api.h
|
|
index fab873b..7f3abae 100644
|
|
--- a/samples/kdbus/kdbus-api.h
|
|
+++ b/samples/kdbus/kdbus-api.h
|
|
@@ -10,10 +10,10 @@
|
|
#define KDBUS_ITEM_NEXT(item) \
|
|
(typeof(item))((uint8_t *)(item) + KDBUS_ALIGN8((item)->size))
|
|
#define KDBUS_FOREACH(iter, first, _size) \
|
|
- for (iter = (first); \
|
|
+ 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)))
|
|
+ (iter) = (void *)((uint8_t *)(iter) + KDBUS_ALIGN8((iter)->size)))
|
|
|
|
static inline int kdbus_cmd_bus_make(int control_fd, struct kdbus_cmd *cmd)
|
|
{
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 5435ab0c3309c1f454df25761e1ca97924e3a809 Mon Sep 17 00:00:00 2001
|
|
From: Sergei Zviagintsev <sergei@s15v.net>
|
|
Date: Wed, 17 Jun 2015 20:14:56 +0300
|
|
Subject: [PATCH 76/78] kdbus: kdbus_reply_find(): return on found entry
|
|
|
|
Return found entry immediately instead of assigning it to additional
|
|
variable and breaking the loop. It's simpler to read, the same way is
|
|
used in kdbus_conn_has_name(), for example.
|
|
|
|
Signed-off-by: Sergei Zviagintsev <sergei@s15v.net>
|
|
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Reviewed-by: Djalal Harouni <tixxdz@opendz.org>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
ipc/kdbus/reply.c | 10 ++++------
|
|
1 file changed, 4 insertions(+), 6 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/reply.c b/ipc/kdbus/reply.c
|
|
index 89d355b..9d823eb 100644
|
|
--- a/ipc/kdbus/reply.c
|
|
+++ b/ipc/kdbus/reply.c
|
|
@@ -171,17 +171,15 @@ struct kdbus_reply *kdbus_reply_find(struct kdbus_conn *replying,
|
|
struct kdbus_conn *reply_dst,
|
|
u64 cookie)
|
|
{
|
|
- struct kdbus_reply *r, *reply = NULL;
|
|
+ struct kdbus_reply *r;
|
|
|
|
list_for_each_entry(r, &reply_dst->reply_list, entry) {
|
|
if (r->cookie == cookie &&
|
|
- (!replying || r->reply_src == replying)) {
|
|
- reply = r;
|
|
- break;
|
|
- }
|
|
+ (!replying || r->reply_src == replying))
|
|
+ return r;
|
|
}
|
|
|
|
- return reply;
|
|
+ return NULL;
|
|
}
|
|
|
|
/**
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 70da92e8b042e22142f58d62ff26113642fe8358 Mon Sep 17 00:00:00 2001
|
|
From: Sergei Zviagintsev <sergei@s15v.net>
|
|
Date: Wed, 17 Jun 2015 20:14:57 +0300
|
|
Subject: [PATCH 77/78] kdbus: optimize error path in kdbus_reply_new()
|
|
|
|
Move cleanup code to separate location as it never executes on normal
|
|
flow. This removes extra if-block and the need to initialize `ret'.
|
|
|
|
Signed-off-by: Sergei Zviagintsev <sergei@s15v.net>
|
|
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Reviewed-by: Djalal Harouni <tixxdz@opendz.org>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
ipc/kdbus/reply.c | 12 +++++-------
|
|
1 file changed, 5 insertions(+), 7 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/reply.c b/ipc/kdbus/reply.c
|
|
index 9d823eb..e6791d8 100644
|
|
--- a/ipc/kdbus/reply.c
|
|
+++ b/ipc/kdbus/reply.c
|
|
@@ -37,7 +37,7 @@ struct kdbus_reply *kdbus_reply_new(struct kdbus_conn *reply_src,
|
|
bool sync)
|
|
{
|
|
struct kdbus_reply *r;
|
|
- int ret = 0;
|
|
+ int ret;
|
|
|
|
if (atomic_inc_return(&reply_dst->request_count) >
|
|
KDBUS_CONN_MAX_REQUESTS_PENDING) {
|
|
@@ -64,13 +64,11 @@ struct kdbus_reply *kdbus_reply_new(struct kdbus_conn *reply_src,
|
|
r->waiting = true;
|
|
}
|
|
|
|
-exit_dec_request_count:
|
|
- if (ret < 0) {
|
|
- atomic_dec(&reply_dst->request_count);
|
|
- return ERR_PTR(ret);
|
|
- }
|
|
-
|
|
return r;
|
|
+
|
|
+exit_dec_request_count:
|
|
+ atomic_dec(&reply_dst->request_count);
|
|
+ return ERR_PTR(ret);
|
|
}
|
|
|
|
static void __kdbus_reply_free(struct kref *kref)
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From b77698964037d78e47a278ba33d8a7983c267fc6 Mon Sep 17 00:00:00 2001
|
|
From: Sergei Zviagintsev <sergei@s15v.net>
|
|
Date: Wed, 17 Jun 2015 20:14:58 +0300
|
|
Subject: [PATCH 78/78] kdbus: optimize if statements in
|
|
kdbus_conn_disconnect()
|
|
|
|
if (r->sync) branch and code after it both call kdbus_reply_unlink().
|
|
Rewrite them as if-else to eliminate code duplication and make algorithm
|
|
more obvious.
|
|
|
|
Convert outer if statement to use continue in order to reduce
|
|
indentation and make things easier to read.
|
|
|
|
Signed-off-by: Sergei Zviagintsev <sergei@s15v.net>
|
|
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Reviewed-by: Djalal Harouni <tixxdz@opendz.org>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
ipc/kdbus/connection.c | 15 +++++++--------
|
|
1 file changed, 7 insertions(+), 8 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/connection.c b/ipc/kdbus/connection.c
|
|
index 707be05..9993753 100644
|
|
--- a/ipc/kdbus/connection.c
|
|
+++ b/ipc/kdbus/connection.c
|
|
@@ -559,17 +559,16 @@ int kdbus_conn_disconnect(struct kdbus_conn *conn, bool ensure_queue_empty)
|
|
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) {
|
|
- if (r->sync) {
|
|
- kdbus_sync_reply_wakeup(r, -EPIPE);
|
|
- kdbus_reply_unlink(r);
|
|
- continue;
|
|
- }
|
|
+ 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);
|
|
- }
|
|
+
|
|
+ kdbus_reply_unlink(r);
|
|
}
|
|
mutex_unlock(&c->lock);
|
|
}
|
|
--
|
|
2.4.3
|
|
From 7d894da303164a38522eaac6e95ee558423b9272 Mon Sep 17 00:00:00 2001
|
|
From: Markus Elfring <elfring@users.sourceforge.net>
|
|
Date: Wed, 24 Jun 2015 14:30:17 +0200
|
|
Subject: [PATCH 01/34] kdbus: delete unnecessary check before
|
|
kdbus_domain_unref
|
|
|
|
The kdbus_domain_unref() function tests whether its argument is NULL
|
|
and then returns immediately. Thus the test around the call is not needed.
|
|
|
|
This issue was detected by using the Coccinelle software.
|
|
|
|
Signed-off-by: Markus Elfring <elfring@users.sourceforge.net>
|
|
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
|
|
(shorten commit-message-head slightly)
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
---
|
|
ipc/kdbus/fs.c | 4 +---
|
|
1 file changed, 1 insertion(+), 3 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/fs.c b/ipc/kdbus/fs.c
|
|
index d01f33baaa0d..205a3adaa2ab 100644
|
|
--- a/ipc/kdbus/fs.c
|
|
+++ b/ipc/kdbus/fs.c
|
|
@@ -325,9 +325,7 @@ static void fs_super_kill(struct super_block *sb)
|
|
}
|
|
|
|
kill_anon_super(sb);
|
|
-
|
|
- if (domain)
|
|
- kdbus_domain_unref(domain);
|
|
+ kdbus_domain_unref(domain);
|
|
}
|
|
|
|
static int fs_super_set(struct super_block *sb, void *data)
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From b57a271a7a2c5bac0fdfa848481bca55dc1d1336 Mon Sep 17 00:00:00 2001
|
|
From: Sergei Zviagintsev <sergei@s15v.net>
|
|
Date: Sun, 28 Jun 2015 16:17:30 +0300
|
|
Subject: [PATCH 02/34] kdbus: fix typos in kdbus_conn_quota_inc()
|
|
|
|
Signed-off-by: Sergei Zviagintsev <sergei@s15v.net>
|
|
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
---
|
|
ipc/kdbus/connection.c | 4 ++--
|
|
1 file changed, 2 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/connection.c b/ipc/kdbus/connection.c
|
|
index 9993753d11de..df072487e23c 100644
|
|
--- a/ipc/kdbus/connection.c
|
|
+++ b/ipc/kdbus/connection.c
|
|
@@ -646,7 +646,7 @@ int kdbus_conn_quota_inc(struct kdbus_conn *c, struct kdbus_user *u,
|
|
* 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 allocations
|
|
+ * 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
|
|
@@ -688,7 +688,7 @@ int kdbus_conn_quota_inc(struct kdbus_conn *c, struct kdbus_user *u,
|
|
|
|
/*
|
|
* Pool owner slices are un-accounted slices; they can claim more
|
|
- * than 50% of the queue. However, the slice we're dealing with here
|
|
+ * 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.
|
|
*/
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From c1bddc44862c977384e66aea1d2be7ded9fb0f0e Mon Sep 17 00:00:00 2001
|
|
From: Sergei Zviagintsev <sergei@s15v.net>
|
|
Date: Sun, 28 Jun 2015 16:17:31 +0300
|
|
Subject: [PATCH 03/34] kdbus: use standard kernel types in struct kdbus_quota
|
|
|
|
uint{8,16,32}_t -> u{8,16,32}
|
|
|
|
Signed-off-by: Sergei Zviagintsev <sergei@s15v.net>
|
|
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
---
|
|
ipc/kdbus/connection.c | 6 +++---
|
|
1 file changed, 3 insertions(+), 3 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/connection.c b/ipc/kdbus/connection.c
|
|
index df072487e23c..af044f93c14f 100644
|
|
--- a/ipc/kdbus/connection.c
|
|
+++ b/ipc/kdbus/connection.c
|
|
@@ -606,9 +606,9 @@ bool kdbus_conn_has_name(struct kdbus_conn *conn, const char *name)
|
|
}
|
|
|
|
struct kdbus_quota {
|
|
- uint32_t memory;
|
|
- uint16_t msgs;
|
|
- uint8_t fds;
|
|
+ u32 memory;
|
|
+ u16 msgs;
|
|
+ u8 fds;
|
|
};
|
|
|
|
/**
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From eb526cf227f121c8320275dde32d18495631a622 Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Tue, 9 Jun 2015 09:41:40 +0200
|
|
Subject: [PATCH 04/34] kdbus/selftests: fix CAP translation tests
|
|
|
|
We now support CAP translations. Make sure our tests reflect that. So far
|
|
they made sure we drop CAPS on namespace borders. This is wrong, though.
|
|
We really need to just make sure that no _or_ the correctly translated
|
|
caps are returned. Fix this.
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
---
|
|
tools/testing/selftests/kdbus/test-metadata-ns.c | 9 +++------
|
|
1 file changed, 3 insertions(+), 6 deletions(-)
|
|
|
|
diff --git a/tools/testing/selftests/kdbus/test-metadata-ns.c b/tools/testing/selftests/kdbus/test-metadata-ns.c
|
|
index ccdfae06922b..1f6edc09008a 100644
|
|
--- a/tools/testing/selftests/kdbus/test-metadata-ns.c
|
|
+++ b/tools/testing/selftests/kdbus/test-metadata-ns.c
|
|
@@ -168,9 +168,8 @@ static int __kdbus_clone_userns_test(const char *bus,
|
|
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);
|
|
+ ASSERT_EXIT(item);
|
|
|
|
/* uid/gid not mapped, so we have unpriv cached creds */
|
|
ret = kdbus_match_kdbus_creds(msg, &unmapped_creds);
|
|
@@ -196,9 +195,8 @@ static int __kdbus_clone_userns_test(const char *bus,
|
|
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);
|
|
+ ASSERT_EXIT(item);
|
|
|
|
/* uid/gid not mapped, so we have unpriv cached creds */
|
|
ret = kdbus_match_kdbus_creds(msg, &unmapped_creds);
|
|
@@ -358,9 +356,8 @@ static int kdbus_clone_userns_test(const char *bus,
|
|
|
|
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);
|
|
+ ASSERT_RETURN(item);
|
|
|
|
/*
|
|
* Compare received items, creds must be translated into
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 42c6f60d15f3a359e0d779f7af4a8ee51144e16d Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Mon, 15 Jun 2015 12:20:28 +0200
|
|
Subject: [PATCH 05/34] kdbus: drop redundant KDBUS_MSG_MAX_ITEMS
|
|
|
|
We already limit the size of the message object, there's no reason to add
|
|
an arbitrary additional limit on the number of items. We don't do this for
|
|
other item-arrays, so lets stop restricting the messages in this way.
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
---
|
|
ipc/kdbus/limits.h | 3 ---
|
|
ipc/kdbus/message.c | 12 ++++--------
|
|
2 files changed, 4 insertions(+), 11 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/limits.h b/ipc/kdbus/limits.h
|
|
index 6450f58cffcf..c54925a25971 100644
|
|
--- a/ipc/kdbus/limits.h
|
|
+++ b/ipc/kdbus/limits.h
|
|
@@ -19,9 +19,6 @@
|
|
/* maximum size of message header and items */
|
|
#define KDBUS_MSG_MAX_SIZE SZ_8K
|
|
|
|
-/* maximum number of message items */
|
|
-#define KDBUS_MSG_MAX_ITEMS 128
|
|
-
|
|
/* maximum number of memfd items per message */
|
|
#define KDBUS_MSG_MAX_MEMFD_ITEMS 16
|
|
|
|
diff --git a/ipc/kdbus/message.c b/ipc/kdbus/message.c
|
|
index 066e816dfdea..e9da67229eb1 100644
|
|
--- a/ipc/kdbus/message.c
|
|
+++ b/ipc/kdbus/message.c
|
|
@@ -214,7 +214,7 @@ static int kdbus_msg_scan_items(struct kdbus_kmsg *kmsg,
|
|
struct kdbus_msg_resources *res = kmsg->res;
|
|
const struct kdbus_msg *msg = &kmsg->msg;
|
|
const struct kdbus_item *item;
|
|
- size_t n, n_vecs, n_memfds;
|
|
+ size_t n_res, n_vecs, n_memfds;
|
|
bool has_bloom = false;
|
|
bool has_name = false;
|
|
bool has_fds = false;
|
|
@@ -243,9 +243,9 @@ static int kdbus_msg_scan_items(struct kdbus_kmsg *kmsg,
|
|
}
|
|
}
|
|
|
|
- n = n_vecs + n_memfds;
|
|
- if (n > 0) {
|
|
- res->data = kcalloc(n, sizeof(*res->data), GFP_KERNEL);
|
|
+ n_res = n_vecs + n_memfds;
|
|
+ if (n_res > 0) {
|
|
+ res->data = kcalloc(n_res, sizeof(*res->data), GFP_KERNEL);
|
|
if (!res->data)
|
|
return -ENOMEM;
|
|
}
|
|
@@ -257,15 +257,11 @@ static int kdbus_msg_scan_items(struct kdbus_kmsg *kmsg,
|
|
}
|
|
|
|
/* import data payloads */
|
|
- n = 0;
|
|
vec_size = 0;
|
|
KDBUS_ITEMS_FOREACH(item, msg->items, KDBUS_ITEMS_SIZE(msg, items)) {
|
|
size_t payload_size = KDBUS_ITEM_PAYLOAD_SIZE(item);
|
|
struct iovec *iov = kmsg->iov + kmsg->iov_count;
|
|
|
|
- if (++n > KDBUS_MSG_MAX_ITEMS)
|
|
- return -E2BIG;
|
|
-
|
|
switch (item->type) {
|
|
case KDBUS_ITEM_PAYLOAD_VEC: {
|
|
struct kdbus_msg_data *d = res->data + res->data_count;
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 80c15dbf8b5eff74a41ceae7d83e9fcf8abf9df9 Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Fri, 19 Jun 2015 17:08:08 +0200
|
|
Subject: [PATCH 06/34] kdbus: drop unused 'bloom_generation' field
|
|
|
|
This field is never used, drop it.
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
---
|
|
ipc/kdbus/message.h | 2 --
|
|
1 file changed, 2 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/message.h b/ipc/kdbus/message.h
|
|
index cdaa65c4e6ae..3b733474f335 100644
|
|
--- a/ipc/kdbus/message.h
|
|
+++ b/ipc/kdbus/message.h
|
|
@@ -88,7 +88,6 @@ kdbus_msg_resources_unref(struct kdbus_msg_resources *r);
|
|
* @notify_name: Short-cut for faster lookup
|
|
* @dst_name_id: Short-cut to msg for faster lookup
|
|
* @bloom_filter: Bloom filter to match message properties
|
|
- * @bloom_generation: Generation of bloom element set
|
|
* @notify_entry: List of kernel-generated notifications
|
|
* @iov: Array of iovec, describing the payload to copy
|
|
* @iov_count: Number of array members in @iov
|
|
@@ -107,7 +106,6 @@ struct kdbus_kmsg {
|
|
|
|
u64 dst_name_id;
|
|
const struct kdbus_bloom_filter *bloom_filter;
|
|
- u64 bloom_generation;
|
|
struct list_head notify_entry;
|
|
|
|
struct iovec *iov;
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From fb1fa875dc1df36250e92085b914d9d5c5e952d3 Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Fri, 5 Jun 2015 15:30:40 +0200
|
|
Subject: [PATCH 07/34] kdbus: drop support for required attach-flags on buses
|
|
|
|
This drops the KDBUS_ITEM_ATTACH_FLAGS_RECV item from KDBUS_CMD_BUS_MAKE.
|
|
This item was used to provide an attach-flags mask which defines metadata
|
|
items that all connections must have in their send-mask. Hence,
|
|
effectively forcing the transmission of such items in case the receiver
|
|
wants them.
|
|
|
|
This was never used by any code and is of questionable use. With our new
|
|
effort to make sure metadata items are only transmitted if the receiver
|
|
has actual access to the same data via /proc, this is no longer needed.
|
|
Drop support for this item now.
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
---
|
|
Documentation/kdbus/kdbus.bus.xml | 15 ---------------
|
|
Documentation/kdbus/kdbus.connection.xml | 8 +-------
|
|
ipc/kdbus/bus.c | 10 ----------
|
|
ipc/kdbus/bus.h | 2 --
|
|
ipc/kdbus/connection.c | 10 ----------
|
|
tools/testing/selftests/kdbus/kdbus-test.c | 1 -
|
|
tools/testing/selftests/kdbus/kdbus-util.c | 20 +++++++-------------
|
|
tools/testing/selftests/kdbus/kdbus-util.h | 3 +--
|
|
tools/testing/selftests/kdbus/test-connection.c | 9 ---------
|
|
9 files changed, 9 insertions(+), 69 deletions(-)
|
|
|
|
diff --git a/Documentation/kdbus/kdbus.bus.xml b/Documentation/kdbus/kdbus.bus.xml
|
|
index 4b9a0ac1b351..83f1198bc6a1 100644
|
|
--- a/Documentation/kdbus/kdbus.bus.xml
|
|
+++ b/Documentation/kdbus/kdbus.bus.xml
|
|
@@ -198,21 +198,6 @@ struct kdbus_cmd {
|
|
</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>
|
|
diff --git a/Documentation/kdbus/kdbus.connection.xml b/Documentation/kdbus/kdbus.connection.xml
|
|
index cefb419f1093..4bb5f30f379a 100644
|
|
--- a/Documentation/kdbus/kdbus.connection.xml
|
|
+++ b/Documentation/kdbus/kdbus.connection.xml
|
|
@@ -355,13 +355,7 @@ struct kdbus_cmd_hello {
|
|
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.
|
|
+ to the message.
|
|
</para></listitem>
|
|
</varlistentry>
|
|
|
|
diff --git a/ipc/kdbus/bus.c b/ipc/kdbus/bus.c
|
|
index bbdf0f2f391e..7d2c336213ad 100644
|
|
--- a/ipc/kdbus/bus.c
|
|
+++ b/ipc/kdbus/bus.c
|
|
@@ -66,23 +66,16 @@ static struct kdbus_bus *kdbus_bus_new(struct kdbus_domain *domain,
|
|
const char *name,
|
|
struct kdbus_bloom_parameter *bloom,
|
|
const u64 *pattach_owner,
|
|
- const u64 *pattach_recv,
|
|
u64 flags, kuid_t uid, kgid_t gid)
|
|
{
|
|
struct kdbus_bus *b;
|
|
u64 attach_owner;
|
|
- u64 attach_recv;
|
|
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_recv ? *pattach_recv : 0,
|
|
- &attach_recv);
|
|
- if (ret < 0)
|
|
- return ERR_PTR(ret);
|
|
-
|
|
ret = kdbus_sanitize_attach_flags(pattach_owner ? *pattach_owner : 0,
|
|
&attach_owner);
|
|
if (ret < 0)
|
|
@@ -111,7 +104,6 @@ static struct kdbus_bus *kdbus_bus_new(struct kdbus_domain *domain,
|
|
|
|
b->id = atomic64_inc_return(&domain->last_id);
|
|
b->bus_flags = flags;
|
|
- b->attach_flags_req = attach_recv;
|
|
b->attach_flags_owner = attach_owner;
|
|
generate_random_uuid(b->id128);
|
|
b->bloom = *bloom;
|
|
@@ -380,7 +372,6 @@ struct kdbus_bus *kdbus_cmd_bus_make(struct kdbus_domain *domain,
|
|
{ .type = KDBUS_ITEM_MAKE_NAME, .mandatory = true },
|
|
{ .type = KDBUS_ITEM_BLOOM_PARAMETER, .mandatory = true },
|
|
{ .type = KDBUS_ITEM_ATTACH_FLAGS_SEND },
|
|
- { .type = KDBUS_ITEM_ATTACH_FLAGS_RECV },
|
|
};
|
|
struct kdbus_args args = {
|
|
.allowed_flags = KDBUS_FLAG_NEGOTIATE |
|
|
@@ -399,7 +390,6 @@ struct kdbus_bus *kdbus_cmd_bus_make(struct kdbus_domain *domain,
|
|
bus = kdbus_bus_new(domain,
|
|
argv[1].item->str, &argv[2].item->bloom_parameter,
|
|
argv[3].item ? argv[3].item->data64 : NULL,
|
|
- argv[4].item ? argv[4].item->data64 : NULL,
|
|
cmd->flags, current_euid(), current_egid());
|
|
if (IS_ERR(bus)) {
|
|
ret = PTR_ERR(bus);
|
|
diff --git a/ipc/kdbus/bus.h b/ipc/kdbus/bus.h
|
|
index 5bea5ef768f1..e019ef34534c 100644
|
|
--- a/ipc/kdbus/bus.h
|
|
+++ b/ipc/kdbus/bus.h
|
|
@@ -37,7 +37,6 @@ struct kdbus_user;
|
|
* @node: kdbus_node
|
|
* @id: ID of this bus in the domain
|
|
* @bus_flags: Simple pass-through flags from userspace to userspace
|
|
- * @attach_flags_req: KDBUS_ATTACH_* flags required by connecting peers
|
|
* @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
|
|
@@ -60,7 +59,6 @@ struct kdbus_bus {
|
|
/* static */
|
|
u64 id;
|
|
u64 bus_flags;
|
|
- u64 attach_flags_req;
|
|
u64 attach_flags_owner;
|
|
u8 id128[16];
|
|
struct kdbus_bloom_parameter bloom;
|
|
diff --git a/ipc/kdbus/connection.c b/ipc/kdbus/connection.c
|
|
index af044f93c14f..02e3ce735034 100644
|
|
--- a/ipc/kdbus/connection.c
|
|
+++ b/ipc/kdbus/connection.c
|
|
@@ -112,10 +112,6 @@ static struct kdbus_conn *kdbus_conn_new(struct kdbus_ep *ep, bool privileged,
|
|
if (ret < 0)
|
|
return ERR_PTR(ret);
|
|
|
|
- /* The attach flags must always satisfy the bus requirements. */
|
|
- if (bus->attach_flags_req & ~attach_flags_send)
|
|
- return ERR_PTR(-ECONNREFUSED);
|
|
-
|
|
conn = kzalloc(sizeof(*conn), GFP_KERNEL);
|
|
if (!conn)
|
|
return ERR_PTR(-ENOMEM);
|
|
@@ -1835,7 +1831,6 @@ exit:
|
|
*/
|
|
int kdbus_cmd_update(struct kdbus_conn *conn, void __user *argp)
|
|
{
|
|
- struct kdbus_bus *bus = conn->ep->bus;
|
|
struct kdbus_item *item_policy;
|
|
u64 *item_attach_send = NULL;
|
|
u64 *item_attach_recv = NULL;
|
|
@@ -1876,11 +1871,6 @@ int kdbus_cmd_update(struct kdbus_conn *conn, void __user *argp)
|
|
&attach_send);
|
|
if (ret < 0)
|
|
goto exit;
|
|
-
|
|
- if (bus->attach_flags_req & ~attach_send) {
|
|
- ret = -EINVAL;
|
|
- goto exit;
|
|
- }
|
|
}
|
|
|
|
if (item_attach_recv) {
|
|
diff --git a/tools/testing/selftests/kdbus/kdbus-test.c b/tools/testing/selftests/kdbus/kdbus-test.c
|
|
index 294e82a83ab6..db732e59650a 100644
|
|
--- a/tools/testing/selftests/kdbus/kdbus-test.c
|
|
+++ b/tools/testing/selftests/kdbus/kdbus-test.c
|
|
@@ -299,7 +299,6 @@ static int test_prepare_env(const struct kdbus_test *t,
|
|
|
|
ret = kdbus_create_bus(env->control_fd,
|
|
args->busname ?: n,
|
|
- _KDBUS_ATTACH_ALL,
|
|
_KDBUS_ATTACH_ALL, &s);
|
|
free(n);
|
|
ASSERT_RETURN(ret == 0);
|
|
diff --git a/tools/testing/selftests/kdbus/kdbus-util.c b/tools/testing/selftests/kdbus/kdbus-util.c
|
|
index 29a0cb1aace2..a5e54ca3a492 100644
|
|
--- a/tools/testing/selftests/kdbus/kdbus-util.c
|
|
+++ b/tools/testing/selftests/kdbus/kdbus-util.c
|
|
@@ -114,8 +114,7 @@ int kdbus_sysfs_set_parameter_mask(const char *path, uint64_t mask)
|
|
}
|
|
|
|
int kdbus_create_bus(int control_fd, const char *name,
|
|
- uint64_t req_meta, uint64_t owner_meta,
|
|
- char **path)
|
|
+ uint64_t owner_meta, char **path)
|
|
{
|
|
struct {
|
|
struct kdbus_cmd cmd;
|
|
@@ -127,12 +126,12 @@ int kdbus_create_bus(int control_fd, const char *name,
|
|
struct kdbus_bloom_parameter bloom;
|
|
} bp;
|
|
|
|
- /* required and owner metadata items */
|
|
+ /* owner metadata items */
|
|
struct {
|
|
uint64_t size;
|
|
uint64_t type;
|
|
uint64_t flags;
|
|
- } attach[2];
|
|
+ } attach;
|
|
|
|
/* name item */
|
|
struct {
|
|
@@ -152,13 +151,9 @@ int kdbus_create_bus(int control_fd, const char *name,
|
|
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.attach.type = KDBUS_ITEM_ATTACH_FLAGS_SEND;
|
|
+ bus_make.attach.size = sizeof(bus_make.attach);
|
|
+ bus_make.attach.flags = owner_meta;
|
|
|
|
bus_make.name.type = KDBUS_ITEM_MAKE_NAME;
|
|
bus_make.name.size = KDBUS_ITEM_HEADER_SIZE +
|
|
@@ -167,8 +162,7 @@ int kdbus_create_bus(int control_fd, const char *name,
|
|
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.attach.size +
|
|
bus_make.name.size;
|
|
|
|
kdbus_printf("Creating bus with name >%s< on control fd %d ...\n",
|
|
diff --git a/tools/testing/selftests/kdbus/kdbus-util.h b/tools/testing/selftests/kdbus/kdbus-util.h
|
|
index d1a0f1b4d0eb..e1e18b92f425 100644
|
|
--- a/tools/testing/selftests/kdbus/kdbus-util.h
|
|
+++ b/tools/testing/selftests/kdbus/kdbus-util.h
|
|
@@ -168,8 +168,7 @@ 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);
|
|
+ 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);
|
|
diff --git a/tools/testing/selftests/kdbus/test-connection.c b/tools/testing/selftests/kdbus/test-connection.c
|
|
index e7c486621b04..4688ce8ec40b 100644
|
|
--- a/tools/testing/selftests/kdbus/test-connection.c
|
|
+++ b/tools/testing/selftests/kdbus/test-connection.c
|
|
@@ -70,15 +70,6 @@ int kdbus_test_hello(struct kdbus_test_env *env)
|
|
|
|
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;
|
|
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From c2bc012ac7da42acdeb079cc1ef4ea25b67d0b74 Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Tue, 9 Jun 2015 09:43:47 +0200
|
|
Subject: [PATCH 08/34] kdbus: pin namespaces on HELLO
|
|
|
|
Whenever we send messages to a target connection, all we know about the
|
|
target is the 'struct file' associated with the kdbus connection. Hence,
|
|
we cannot know which namespaces a receiving process will be in when it
|
|
calls KDBUS_CMD_RECV on the message. So far, we pinned all metadata we
|
|
wanna send and translate it on RECV-time, since we then know the exact
|
|
namespaces to translate into.
|
|
|
|
This has several drawbacks:
|
|
- Depending on the process calling RECV, the behavior is different (as
|
|
multiple processes might be in different namespaces but share the same
|
|
fd). This is unwanted behavior, as described by Eric here:
|
|
http://www.spinics.net/lists/netdev/msg329322.html
|
|
- We need to pin metadata with a message instead of translating it right
|
|
away.
|
|
- We cannot prep a message at SEND time as we don't know the size of the
|
|
translated metadata. Hence, we need to do all that at RECV time.
|
|
|
|
This patch changes the namespace behavior. Instead of using the namespaces
|
|
at RECV time, we now pin the namespaces at HELLO (i.e., open()). So
|
|
regardless who calls RECV on this file-descriptor, the same namespaces
|
|
will be used.
|
|
This gives us the advantage that we now always know the target namespaces
|
|
for a message. Hence, we can now properly prep a message at SEND time and
|
|
never have to carry any metadata pins around.
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
---
|
|
ipc/kdbus/bus.c | 2 +-
|
|
ipc/kdbus/connection.c | 8 +++++++-
|
|
ipc/kdbus/connection.h | 6 ++++++
|
|
ipc/kdbus/metadata.c | 54 ++++++++++++++++++++++++++------------------------
|
|
ipc/kdbus/metadata.h | 1 +
|
|
ipc/kdbus/queue.c | 1 +
|
|
6 files changed, 44 insertions(+), 28 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/bus.c b/ipc/kdbus/bus.c
|
|
index 7d2c336213ad..8fffc2f594a8 100644
|
|
--- a/ipc/kdbus/bus.c
|
|
+++ b/ipc/kdbus/bus.c
|
|
@@ -507,7 +507,7 @@ int kdbus_cmd_bus_creator_info(struct kdbus_conn *conn, void __user *argp)
|
|
goto exit;
|
|
}
|
|
|
|
- ret = kdbus_meta_export(bus->creator_meta, NULL, attach_flags,
|
|
+ ret = kdbus_meta_export(bus->creator_meta, NULL, conn, attach_flags,
|
|
slice, hdr_size, &meta_size);
|
|
if (ret < 0)
|
|
goto exit;
|
|
diff --git a/ipc/kdbus/connection.c b/ipc/kdbus/connection.c
|
|
index 02e3ce735034..ca241fcf19f7 100644
|
|
--- a/ipc/kdbus/connection.c
|
|
+++ b/ipc/kdbus/connection.c
|
|
@@ -130,6 +130,9 @@ static struct kdbus_conn *kdbus_conn_new(struct kdbus_ep *ep, bool privileged,
|
|
atomic_set(&conn->lost_count, 0);
|
|
INIT_DELAYED_WORK(&conn->work, kdbus_reply_list_scan_work);
|
|
conn->cred = get_current_cred();
|
|
+ conn->user_ns = get_user_ns(current_user_ns());
|
|
+ conn->pid_ns = get_pid_ns(task_active_pid_ns(current));
|
|
+ get_fs_root(current->fs, &conn->root_path);
|
|
init_waitqueue_head(&conn->wait);
|
|
kdbus_queue_init(&conn->queue);
|
|
conn->privileged = privileged;
|
|
@@ -271,6 +274,9 @@ static void __kdbus_conn_free(struct kref *kref)
|
|
kdbus_match_db_free(conn->match_db);
|
|
kdbus_pool_free(conn->pool);
|
|
kdbus_ep_unref(conn->ep);
|
|
+ path_put(&conn->root_path);
|
|
+ put_pid_ns(conn->pid_ns);
|
|
+ put_user_ns(conn->user_ns);
|
|
put_cred(conn->cred);
|
|
kfree(conn->description);
|
|
kfree(conn->quota);
|
|
@@ -1792,7 +1798,7 @@ int kdbus_cmd_conn_info(struct kdbus_conn *conn, void __user *argp)
|
|
goto exit;
|
|
}
|
|
|
|
- ret = kdbus_meta_export(owner_conn->meta, conn_meta, attach_flags,
|
|
+ ret = kdbus_meta_export(owner_conn->meta, conn_meta, conn, attach_flags,
|
|
slice, sizeof(info), &meta_size);
|
|
if (ret < 0)
|
|
goto exit;
|
|
diff --git a/ipc/kdbus/connection.h b/ipc/kdbus/connection.h
|
|
index d1ffe909cb31..226f3ff60e31 100644
|
|
--- a/ipc/kdbus/connection.h
|
|
+++ b/ipc/kdbus/connection.h
|
|
@@ -59,6 +59,9 @@ struct kdbus_kmsg;
|
|
* @pool: The user's buffer to receive messages
|
|
* @user: Owner of the connection
|
|
* @cred: The credentials of the connection at creation time
|
|
+ * @user_ns: User namespace at creation time
|
|
+ * @pid_ns: Pid namespace at creation time
|
|
+ * @root_path: Root path at creation time
|
|
* @name_count: Number of owned well-known names
|
|
* @request_count: Number of pending requests issued by this
|
|
* connection that are waiting for replies from
|
|
@@ -97,6 +100,9 @@ struct kdbus_conn {
|
|
struct kdbus_pool *pool;
|
|
struct kdbus_user *user;
|
|
const struct cred *cred;
|
|
+ struct user_namespace *user_ns;
|
|
+ struct pid_namespace *pid_ns;
|
|
+ struct path root_path;
|
|
atomic_t name_count;
|
|
atomic_t request_count;
|
|
atomic_t lost_count;
|
|
diff --git a/ipc/kdbus/metadata.c b/ipc/kdbus/metadata.c
|
|
index c36b9cc67637..79f0e8c7f4a3 100644
|
|
--- a/ipc/kdbus/metadata.c
|
|
+++ b/ipc/kdbus/metadata.c
|
|
@@ -888,7 +888,8 @@ static int kdbus_meta_push_kvec(struct kvec *kvec,
|
|
}
|
|
|
|
static void kdbus_meta_export_caps(struct kdbus_meta_caps *out,
|
|
- struct kdbus_meta_proc *mp)
|
|
+ struct kdbus_meta_proc *mp,
|
|
+ struct user_namespace *user_ns)
|
|
{
|
|
struct user_namespace *iter;
|
|
const struct cred *cred = mp->cred;
|
|
@@ -896,18 +897,18 @@ static void kdbus_meta_export_caps(struct kdbus_meta_caps *out,
|
|
int i;
|
|
|
|
/*
|
|
- * This translates the effective capabilities of 'cred' into the current
|
|
- * user-namespace. If the current user-namespace is a child-namespace of
|
|
+ * 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 current user-namespace and the 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 = current_user_ns(); iter; iter = iter->parent) {
|
|
+ for (iter = user_ns; iter; iter = iter->parent) {
|
|
if (iter == cred->user_ns) {
|
|
parent = true;
|
|
break;
|
|
@@ -951,23 +952,22 @@ static void kdbus_meta_export_caps(struct kdbus_meta_caps *out,
|
|
}
|
|
|
|
/* This is equivalent to from_kuid_munged(), but maps INVALID_UID to itself */
|
|
-static uid_t kdbus_from_kuid_keep(kuid_t uid)
|
|
+static uid_t kdbus_from_kuid_keep(struct user_namespace *ns, kuid_t uid)
|
|
{
|
|
- return uid_valid(uid) ?
|
|
- from_kuid_munged(current_user_ns(), uid) : ((uid_t)-1);
|
|
+ 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(kgid_t gid)
|
|
+static gid_t kdbus_from_kgid_keep(struct user_namespace *ns, kgid_t gid)
|
|
{
|
|
- return gid_valid(gid) ?
|
|
- from_kgid_munged(current_user_ns(), gid) : ((gid_t)-1);
|
|
+ return gid_valid(gid) ? from_kgid_munged(ns, gid) : ((gid_t)-1);
|
|
}
|
|
|
|
/**
|
|
* kdbus_meta_export() - export information from metadata into a slice
|
|
* @mp: Process metadata, or NULL
|
|
* @mc: Connection metadata, or NULL
|
|
+ * @conn: Target connection to translate metadata into
|
|
* @mask: Mask of KDBUS_ATTACH_* flags to export
|
|
* @slice: The slice to export to
|
|
* @offset: The offset inside @slice to write to
|
|
@@ -983,18 +983,19 @@ static gid_t kdbus_from_kgid_keep(kgid_t gid)
|
|
* kdbus_meta_export_prepare(); depending on the namespaces in question, it
|
|
* might use up less than that.
|
|
*
|
|
- * All information will be translated using the current namespaces.
|
|
+ * All information will be translated using the namespaces of @conn.
|
|
*
|
|
* Return: 0 on success, negative error number otherwise.
|
|
*/
|
|
int kdbus_meta_export(struct kdbus_meta_proc *mp,
|
|
struct kdbus_meta_conn *mc,
|
|
+ struct kdbus_conn *conn,
|
|
u64 mask,
|
|
struct kdbus_pool_slice *slice,
|
|
off_t offset,
|
|
size_t *real_size)
|
|
{
|
|
- struct user_namespace *user_ns = current_user_ns();
|
|
+ struct user_namespace *user_ns = conn->user_ns;
|
|
struct kdbus_item_header item_hdr[13], *hdr;
|
|
char *exe_pathname = NULL;
|
|
struct kdbus_creds creds;
|
|
@@ -1016,23 +1017,23 @@ int kdbus_meta_export(struct kdbus_meta_proc *mp,
|
|
/* process metadata */
|
|
|
|
if (mp && (mask & KDBUS_ATTACH_CREDS)) {
|
|
- creds.uid = kdbus_from_kuid_keep(mp->uid);
|
|
- creds.euid = kdbus_from_kuid_keep(mp->euid);
|
|
- creds.suid = kdbus_from_kuid_keep(mp->suid);
|
|
- creds.fsuid = kdbus_from_kuid_keep(mp->fsuid);
|
|
- creds.gid = kdbus_from_kgid_keep(mp->gid);
|
|
- creds.egid = kdbus_from_kgid_keep(mp->egid);
|
|
- creds.sgid = kdbus_from_kgid_keep(mp->sgid);
|
|
- creds.fsgid = kdbus_from_kgid_keep(mp->fsgid);
|
|
+ creds.uid = kdbus_from_kuid_keep(user_ns, mp->uid);
|
|
+ creds.euid = kdbus_from_kuid_keep(user_ns, mp->euid);
|
|
+ creds.suid = kdbus_from_kuid_keep(user_ns, mp->suid);
|
|
+ creds.fsuid = kdbus_from_kuid_keep(user_ns, mp->fsuid);
|
|
+ creds.gid = kdbus_from_kgid_keep(user_ns, mp->gid);
|
|
+ creds.egid = kdbus_from_kgid_keep(user_ns, mp->egid);
|
|
+ creds.sgid = kdbus_from_kgid_keep(user_ns, mp->sgid);
|
|
+ creds.fsgid = kdbus_from_kgid_keep(user_ns, mp->fsgid);
|
|
|
|
cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++, KDBUS_ITEM_CREDS,
|
|
&creds, sizeof(creds), &size);
|
|
}
|
|
|
|
if (mp && (mask & KDBUS_ATTACH_PIDS)) {
|
|
- pids.pid = pid_vnr(mp->tgid);
|
|
- pids.tid = pid_vnr(mp->pid);
|
|
- pids.ppid = pid_vnr(mp->ppid);
|
|
+ pids.pid = pid_nr_ns(mp->tgid, conn->pid_ns);
|
|
+ pids.tid = pid_nr_ns(mp->pid, conn->pid_ns);
|
|
+ pids.ppid = pid_nr_ns(mp->ppid, conn->pid_ns);
|
|
|
|
cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++, KDBUS_ITEM_PIDS,
|
|
&pids, sizeof(pids), &size);
|
|
@@ -1078,7 +1079,8 @@ int kdbus_meta_export(struct kdbus_meta_proc *mp,
|
|
*/
|
|
|
|
get_fs_root(current->fs, &p);
|
|
- if (path_equal(&p, &mp->root_path)) {
|
|
+ if (path_equal(&p, &mp->root_path) &&
|
|
+ path_equal(&p, &conn->root_path)) {
|
|
exe_page = (void *)__get_free_page(GFP_TEMPORARY);
|
|
if (!exe_page) {
|
|
path_put(&p);
|
|
@@ -1116,7 +1118,7 @@ int kdbus_meta_export(struct kdbus_meta_proc *mp,
|
|
if (mp && (mask & KDBUS_ATTACH_CAPS)) {
|
|
struct kdbus_meta_caps caps = {};
|
|
|
|
- kdbus_meta_export_caps(&caps, mp);
|
|
+ kdbus_meta_export_caps(&caps, mp, user_ns);
|
|
cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
|
|
KDBUS_ITEM_CAPS, &caps,
|
|
sizeof(caps), &size);
|
|
diff --git a/ipc/kdbus/metadata.h b/ipc/kdbus/metadata.h
|
|
index 79b6ac31c8ad..2dbbb3d78d97 100644
|
|
--- a/ipc/kdbus/metadata.h
|
|
+++ b/ipc/kdbus/metadata.h
|
|
@@ -46,6 +46,7 @@ int kdbus_meta_export_prepare(struct kdbus_meta_proc *mp,
|
|
u64 *mask, size_t *sz);
|
|
int kdbus_meta_export(struct kdbus_meta_proc *mp,
|
|
struct kdbus_meta_conn *mc,
|
|
+ struct kdbus_conn *conn,
|
|
u64 mask,
|
|
struct kdbus_pool_slice *slice,
|
|
off_t offset, size_t *real_size);
|
|
diff --git a/ipc/kdbus/queue.c b/ipc/kdbus/queue.c
|
|
index 25bb3ad66b98..6650b7804d87 100644
|
|
--- a/ipc/kdbus/queue.c
|
|
+++ b/ipc/kdbus/queue.c
|
|
@@ -479,6 +479,7 @@ int kdbus_queue_entry_install(struct kdbus_queue_entry *entry,
|
|
|
|
ret = kdbus_meta_export(entry->proc_meta,
|
|
entry->conn_meta,
|
|
+ conn_dst,
|
|
entry->attach_flags,
|
|
entry->slice,
|
|
entry->meta_offset,
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 539d66c50c22a40a29c97c8f1e497769de75c078 Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Wed, 1 Jul 2015 17:15:14 +0200
|
|
Subject: [PATCH 09/34] kdbus: fix NULL-deref in activator cleanup
|
|
|
|
Right now, we always assume an activator has a valid name and
|
|
conn->activator_of is set. However, this assumption is not true if the
|
|
setup of the activator fails. In those cases, the ->flags field indicates
|
|
an activator, but the name might not have been claimed, yet.
|
|
|
|
Fix the destructor of connections to not assume all activators have
|
|
claimed names.
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
---
|
|
ipc/kdbus/names.c | 2 +-
|
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
|
|
|
diff --git a/ipc/kdbus/names.c b/ipc/kdbus/names.c
|
|
index d77ee08afeda..057f8061c20f 100644
|
|
--- a/ipc/kdbus/names.c
|
|
+++ b/ipc/kdbus/names.c
|
|
@@ -444,7 +444,7 @@ void kdbus_name_release_all(struct kdbus_name_registry *reg,
|
|
|
|
down_write(®->rwlock);
|
|
|
|
- if (kdbus_conn_is_activator(conn)) {
|
|
+ if (conn->activator_of) {
|
|
activator = conn->activator_of->activator;
|
|
conn->activator_of->activator = NULL;
|
|
}
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 3c18b8a477e0eda28b3390cf9168dbdb3c7fdd56 Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Fri, 3 Jul 2015 11:10:22 +0200
|
|
Subject: [PATCH 10/34] kdbus: allow building development modules
|
|
|
|
If you run your system on kdbus.ko, but you want to run tests on your
|
|
development version of kdbus, you will have a hard-time debugging as you
|
|
need to boot your machine with it. You either have to boot with legacy
|
|
dbus-daemon or boot without dbus entirely (eg., /bin/sh).
|
|
|
|
The kdbus module always used KBUILD_MODNAME as base for all exported
|
|
objects (like kdbusfs and file-system paths). Hence, we can easily build
|
|
kdbus2.ko and get an independent module of the first one. This patch makes
|
|
the kdbus Makefile read KDBUS_EXT= and appends it to the module name.
|
|
|
|
Now you can get an independent kdbus2.ko, by simply running:
|
|
make KDBUS_EXT=2 M=ipc/kdbus
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
---
|
|
ipc/kdbus/Makefile | 15 +++++++++++++--
|
|
1 file changed, 13 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/Makefile b/ipc/kdbus/Makefile
|
|
index 7ee9271e1449..66663a124104 100644
|
|
--- a/ipc/kdbus/Makefile
|
|
+++ b/ipc/kdbus/Makefile
|
|
@@ -1,4 +1,15 @@
|
|
-kdbus-y := \
|
|
+#
|
|
+# By setting KDBUS_EXT=2, the kdbus module will be built as kdbus2.ko, and
|
|
+# KBUILD_MODNAME=kdbus2. This has the effect that all exported objects have
|
|
+# different names than usually (kdbus2fs, /sys/fs/kdbus2/) and you can run
|
|
+# your test-infrastructure against the kdbus2.ko, while running your system
|
|
+# on kdbus.ko.
|
|
+#
|
|
+# To just build the module, use:
|
|
+# make KDBUS_EXT=2 M=ipc/kdbus
|
|
+#
|
|
+
|
|
+kdbus$(KDBUS_EXT)-y := \
|
|
bus.o \
|
|
connection.o \
|
|
endpoint.o \
|
|
@@ -19,4 +30,4 @@ kdbus-y := \
|
|
queue.o \
|
|
util.o
|
|
|
|
-obj-$(CONFIG_KDBUS) += kdbus.o
|
|
+obj-$(CONFIG_KDBUS) += kdbus$(KDBUS_EXT).o
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From f8253aa89c645d1d979aa4743ebabe005f54f7df Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Mon, 15 Jun 2015 20:49:21 +0200
|
|
Subject: [PATCH 11/34] kdbus: split off faked metadata into separate object
|
|
|
|
Right now we treat faked metadata the same as process metadata. This has
|
|
the downside, that we cannot use "struct cred" to pin normal process
|
|
credentials, as we don't want to fake such structure for faked
|
|
credentials.
|
|
|
|
Splitt off handling of faked metadata into a separate kdbus_meta_fake
|
|
object. As a side effect, faked metadata is now handled explicitly instead
|
|
of hidden in the process metadata.
|
|
|
|
In follow-up patches, the kdbus_meta_proc state-tracking can be reduced
|
|
significantly by pinning "struct cred" instead of copying each field
|
|
individually.
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
---
|
|
ipc/kdbus/bus.c | 6 +-
|
|
ipc/kdbus/connection.c | 37 +++++----
|
|
ipc/kdbus/connection.h | 9 +--
|
|
ipc/kdbus/message.c | 2 +-
|
|
ipc/kdbus/metadata.c | 216 ++++++++++++++++++++++++++++++-------------------
|
|
ipc/kdbus/metadata.h | 43 +++++++++-
|
|
ipc/kdbus/queue.c | 2 +
|
|
7 files changed, 206 insertions(+), 109 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/bus.c b/ipc/kdbus/bus.c
|
|
index 8fffc2f594a8..dd1e600b8f01 100644
|
|
--- a/ipc/kdbus/bus.c
|
|
+++ b/ipc/kdbus/bus.c
|
|
@@ -484,7 +484,7 @@ int kdbus_cmd_bus_creator_info(struct kdbus_conn *conn, void __user *argp)
|
|
|
|
attach_flags &= bus->attach_flags_owner;
|
|
|
|
- ret = kdbus_meta_export_prepare(bus->creator_meta, NULL,
|
|
+ ret = kdbus_meta_export_prepare(bus->creator_meta, NULL, NULL,
|
|
&attach_flags, &meta_size);
|
|
if (ret < 0)
|
|
goto exit;
|
|
@@ -507,8 +507,8 @@ int kdbus_cmd_bus_creator_info(struct kdbus_conn *conn, void __user *argp)
|
|
goto exit;
|
|
}
|
|
|
|
- ret = kdbus_meta_export(bus->creator_meta, NULL, conn, attach_flags,
|
|
- slice, hdr_size, &meta_size);
|
|
+ ret = kdbus_meta_export(bus->creator_meta, NULL, NULL, conn,
|
|
+ attach_flags, slice, hdr_size, &meta_size);
|
|
if (ret < 0)
|
|
goto exit;
|
|
|
|
diff --git a/ipc/kdbus/connection.c b/ipc/kdbus/connection.c
|
|
index ca241fcf19f7..8976bb32ab69 100644
|
|
--- a/ipc/kdbus/connection.c
|
|
+++ b/ipc/kdbus/connection.c
|
|
@@ -172,22 +172,28 @@ static struct kdbus_conn *kdbus_conn_new(struct kdbus_ep *ep, bool privileged,
|
|
BUILD_BUG_ON(sizeof(bus->id128) != sizeof(hello->id128));
|
|
memcpy(hello->id128, bus->id128, sizeof(hello->id128));
|
|
|
|
- conn->meta = kdbus_meta_proc_new();
|
|
- if (IS_ERR(conn->meta)) {
|
|
- ret = PTR_ERR(conn->meta);
|
|
- conn->meta = NULL;
|
|
- goto exit_unref;
|
|
- }
|
|
-
|
|
/* privileged processes can impersonate somebody else */
|
|
if (creds || pids || seclabel) {
|
|
- ret = kdbus_meta_proc_fake(conn->meta, creds, pids, seclabel);
|
|
- if (ret < 0)
|
|
+ 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;
|
|
+ }
|
|
|
|
- conn->faked_meta = true;
|
|
+ ret = kdbus_meta_fake_collect(conn->meta_fake,
|
|
+ creds, pids, seclabel);
|
|
+ if (ret < 0)
|
|
+ goto exit_unref;
|
|
} else {
|
|
- ret = kdbus_meta_proc_collect(conn->meta,
|
|
+ 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 |
|
|
@@ -270,7 +276,8 @@ static void __kdbus_conn_free(struct kref *kref)
|
|
kdbus_user_unref(conn->user);
|
|
}
|
|
|
|
- kdbus_meta_proc_unref(conn->meta);
|
|
+ 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);
|
|
@@ -1785,7 +1792,8 @@ int kdbus_cmd_conn_info(struct kdbus_conn *conn, void __user *argp)
|
|
if (ret < 0)
|
|
goto exit;
|
|
|
|
- ret = kdbus_meta_export_prepare(owner_conn->meta, conn_meta,
|
|
+ ret = kdbus_meta_export_prepare(owner_conn->meta_proc,
|
|
+ owner_conn->meta_fake, conn_meta,
|
|
&attach_flags, &meta_size);
|
|
if (ret < 0)
|
|
goto exit;
|
|
@@ -1798,7 +1806,8 @@ int kdbus_cmd_conn_info(struct kdbus_conn *conn, void __user *argp)
|
|
goto exit;
|
|
}
|
|
|
|
- ret = kdbus_meta_export(owner_conn->meta, conn_meta, conn, attach_flags,
|
|
+ ret = kdbus_meta_export(owner_conn->meta_proc, owner_conn->meta_fake,
|
|
+ conn_meta, conn, attach_flags,
|
|
slice, sizeof(info), &meta_size);
|
|
if (ret < 0)
|
|
goto exit;
|
|
diff --git a/ipc/kdbus/connection.h b/ipc/kdbus/connection.h
|
|
index 226f3ff60e31..0eb3d2e70a5e 100644
|
|
--- a/ipc/kdbus/connection.h
|
|
+++ b/ipc/kdbus/connection.h
|
|
@@ -54,8 +54,8 @@ struct kdbus_kmsg;
|
|
* @work: Delayed work to handle timeouts
|
|
* activator for
|
|
* @match_db: Subscription filter to broadcast messages
|
|
- * @meta: Active connection creator's metadata/credentials,
|
|
- * either from the handle or from HELLO
|
|
+ * @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
|
|
@@ -75,7 +75,6 @@ struct kdbus_kmsg;
|
|
* @names_list: List of well-known names
|
|
* @names_queue_list: Well-known names this connection waits for
|
|
* @privileged: Whether this connection is privileged on the bus
|
|
- * @faked_meta: Whether the metadata was faked on HELLO
|
|
*/
|
|
struct kdbus_conn {
|
|
struct kref kref;
|
|
@@ -96,7 +95,8 @@ struct kdbus_conn {
|
|
struct list_head reply_list;
|
|
struct delayed_work work;
|
|
struct kdbus_match_db *match_db;
|
|
- struct kdbus_meta_proc *meta;
|
|
+ struct kdbus_meta_proc *meta_proc;
|
|
+ struct kdbus_meta_fake *meta_fake;
|
|
struct kdbus_pool *pool;
|
|
struct kdbus_user *user;
|
|
const struct cred *cred;
|
|
@@ -118,7 +118,6 @@ struct kdbus_conn {
|
|
struct list_head names_queue_list;
|
|
|
|
bool privileged:1;
|
|
- bool faked_meta:1;
|
|
};
|
|
|
|
struct kdbus_conn *kdbus_conn_ref(struct kdbus_conn *conn);
|
|
diff --git a/ipc/kdbus/message.c b/ipc/kdbus/message.c
|
|
index e9da67229eb1..55e432397b93 100644
|
|
--- a/ipc/kdbus/message.c
|
|
+++ b/ipc/kdbus/message.c
|
|
@@ -626,7 +626,7 @@ int kdbus_kmsg_collect_metadata(struct kdbus_kmsg *kmsg, struct kdbus_conn *src,
|
|
int ret;
|
|
|
|
attach = kdbus_meta_calc_attach_flags(src, dst);
|
|
- if (!src->faked_meta) {
|
|
+ if (!src->meta_fake) {
|
|
ret = kdbus_meta_proc_collect(kmsg->proc_meta, attach);
|
|
if (ret < 0)
|
|
return ret;
|
|
diff --git a/ipc/kdbus/metadata.c b/ipc/kdbus/metadata.c
|
|
index 79f0e8c7f4a3..ac4135c507c4 100644
|
|
--- a/ipc/kdbus/metadata.c
|
|
+++ b/ipc/kdbus/metadata.c
|
|
@@ -494,101 +494,116 @@ exit_unlock:
|
|
}
|
|
|
|
/**
|
|
- * kdbus_meta_proc_fake() - Fill process metadata from faked credentials
|
|
- * @mp: Metadata
|
|
+ * 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. A call to this function
|
|
- * is considered an alternative to calling kdbus_meta_add_current(), which
|
|
- * derives the same information from the 'current' task.
|
|
- *
|
|
- * This call uses the current task's namespaces to resolve the given
|
|
- * information.
|
|
+ * 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 number otherwise.
|
|
+ * Return: 0 on success, negative error code on failure.
|
|
*/
|
|
-int kdbus_meta_proc_fake(struct kdbus_meta_proc *mp,
|
|
- const struct kdbus_creds *creds,
|
|
- const struct kdbus_pids *pids,
|
|
- const char *seclabel)
|
|
+int kdbus_meta_fake_collect(struct kdbus_meta_fake *mf,
|
|
+ const struct kdbus_creds *creds,
|
|
+ const struct kdbus_pids *pids,
|
|
+ const char *seclabel)
|
|
{
|
|
- int ret;
|
|
-
|
|
- if (!mp)
|
|
- return 0;
|
|
-
|
|
- mutex_lock(&mp->lock);
|
|
+ if (mf->valid)
|
|
+ return -EALREADY;
|
|
|
|
- if (creds && !(mp->collected & KDBUS_ATTACH_CREDS)) {
|
|
+ if (creds) {
|
|
struct user_namespace *ns = current_user_ns();
|
|
|
|
- mp->uid = make_kuid(ns, creds->uid);
|
|
- mp->euid = make_kuid(ns, creds->euid);
|
|
- mp->suid = make_kuid(ns, creds->suid);
|
|
- mp->fsuid = make_kuid(ns, creds->fsuid);
|
|
-
|
|
- mp->gid = make_kgid(ns, creds->gid);
|
|
- mp->egid = make_kgid(ns, creds->egid);
|
|
- mp->sgid = make_kgid(ns, creds->sgid);
|
|
- mp->fsgid = make_kgid(ns, creds->fsgid);
|
|
-
|
|
- if ((creds->uid != (uid_t)-1 && !uid_valid(mp->uid)) ||
|
|
- (creds->euid != (uid_t)-1 && !uid_valid(mp->euid)) ||
|
|
- (creds->suid != (uid_t)-1 && !uid_valid(mp->suid)) ||
|
|
- (creds->fsuid != (uid_t)-1 && !uid_valid(mp->fsuid)) ||
|
|
- (creds->gid != (gid_t)-1 && !gid_valid(mp->gid)) ||
|
|
- (creds->egid != (gid_t)-1 && !gid_valid(mp->egid)) ||
|
|
- (creds->sgid != (gid_t)-1 && !gid_valid(mp->sgid)) ||
|
|
- (creds->fsgid != (gid_t)-1 && !gid_valid(mp->fsgid))) {
|
|
- ret = -EINVAL;
|
|
- goto exit_unlock;
|
|
- }
|
|
-
|
|
- mp->valid |= KDBUS_ATTACH_CREDS;
|
|
- mp->collected |= KDBUS_ATTACH_CREDS;
|
|
+ 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 && !(mp->collected & KDBUS_ATTACH_PIDS)) {
|
|
- mp->pid = get_pid(find_vpid(pids->tid));
|
|
- mp->tgid = get_pid(find_vpid(pids->pid));
|
|
- mp->ppid = get_pid(find_vpid(pids->ppid));
|
|
-
|
|
- if ((pids->tid != 0 && !mp->pid) ||
|
|
- (pids->pid != 0 && !mp->tgid) ||
|
|
- (pids->ppid != 0 && !mp->ppid)) {
|
|
- put_pid(mp->pid);
|
|
- put_pid(mp->tgid);
|
|
- put_pid(mp->ppid);
|
|
- mp->pid = NULL;
|
|
- mp->tgid = NULL;
|
|
- mp->ppid = NULL;
|
|
- ret = -EINVAL;
|
|
- goto exit_unlock;
|
|
+ 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;
|
|
}
|
|
|
|
- mp->valid |= KDBUS_ATTACH_PIDS;
|
|
- mp->collected |= KDBUS_ATTACH_PIDS;
|
|
+ mf->valid |= KDBUS_ATTACH_PIDS;
|
|
}
|
|
|
|
- if (seclabel && !(mp->collected & KDBUS_ATTACH_SECLABEL)) {
|
|
- mp->seclabel = kstrdup(seclabel, GFP_KERNEL);
|
|
- if (!mp->seclabel) {
|
|
- ret = -ENOMEM;
|
|
- goto exit_unlock;
|
|
- }
|
|
+ if (seclabel) {
|
|
+ mf->seclabel = kstrdup(seclabel, GFP_KERNEL);
|
|
+ if (!mf->seclabel)
|
|
+ return -ENOMEM;
|
|
|
|
- mp->valid |= KDBUS_ATTACH_SECLABEL;
|
|
- mp->collected |= KDBUS_ATTACH_SECLABEL;
|
|
+ mf->valid |= KDBUS_ATTACH_SECLABEL;
|
|
}
|
|
|
|
- ret = 0;
|
|
-
|
|
-exit_unlock:
|
|
- mutex_unlock(&mp->lock);
|
|
- return ret;
|
|
+ return 0;
|
|
}
|
|
|
|
/**
|
|
@@ -768,6 +783,7 @@ exit_unlock:
|
|
/*
|
|
* kdbus_meta_export_prepare() - Prepare metadata for export
|
|
* @mp: Process metadata, or NULL
|
|
+ * @mf: Fake metadata, or NULL
|
|
* @mc: Connection metadata, or NULL
|
|
* @mask: Pointer to mask of KDBUS_ATTACH_* flags to export
|
|
* @sz: Pointer to return the size needed by the metadata
|
|
@@ -783,6 +799,7 @@ exit_unlock:
|
|
* Return: 0 on success, negative error number otherwise.
|
|
*/
|
|
int kdbus_meta_export_prepare(struct kdbus_meta_proc *mp,
|
|
+ struct kdbus_meta_fake *mf,
|
|
struct kdbus_meta_conn *mc,
|
|
u64 *mask, size_t *sz)
|
|
{
|
|
@@ -792,6 +809,12 @@ int kdbus_meta_export_prepare(struct kdbus_meta_proc *mp,
|
|
u64 valid = 0;
|
|
int ret = 0;
|
|
|
|
+ if (WARN_ON(mf && mp))
|
|
+ mp = NULL;
|
|
+
|
|
+ if (mf)
|
|
+ valid |= mf->valid;
|
|
+
|
|
if (mp) {
|
|
mutex_lock(&mp->lock);
|
|
valid |= mp->valid;
|
|
@@ -811,10 +834,10 @@ int kdbus_meta_export_prepare(struct kdbus_meta_proc *mp,
|
|
|
|
/* process metadata */
|
|
|
|
- if (mp && (*mask & KDBUS_ATTACH_CREDS))
|
|
+ if ((mp || mf) && (*mask & KDBUS_ATTACH_CREDS))
|
|
size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_creds));
|
|
|
|
- if (mp && (*mask & KDBUS_ATTACH_PIDS))
|
|
+ if ((mp || mf) && (*mask & KDBUS_ATTACH_PIDS))
|
|
size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_pids));
|
|
|
|
if (mp && (*mask & KDBUS_ATTACH_AUXGROUPS))
|
|
@@ -852,8 +875,9 @@ int kdbus_meta_export_prepare(struct kdbus_meta_proc *mp,
|
|
if (mp && (*mask & KDBUS_ATTACH_CAPS))
|
|
size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_meta_caps));
|
|
|
|
- if (mp && (*mask & KDBUS_ATTACH_SECLABEL))
|
|
- size += KDBUS_ITEM_SIZE(strlen(mp->seclabel) + 1);
|
|
+ if ((mp || mf) && (*mask & KDBUS_ATTACH_SECLABEL))
|
|
+ size += KDBUS_ITEM_SIZE(strlen(mp ? mp->seclabel
|
|
+ : mf->seclabel) + 1);
|
|
|
|
if (mp && (*mask & KDBUS_ATTACH_AUDIT))
|
|
size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_audit));
|
|
@@ -966,6 +990,7 @@ static gid_t kdbus_from_kgid_keep(struct user_namespace *ns, kgid_t gid)
|
|
/**
|
|
* kdbus_meta_export() - export information from metadata into a slice
|
|
* @mp: Process metadata, or NULL
|
|
+ * @mf: Fake metadata, or NULL
|
|
* @mc: Connection metadata, or NULL
|
|
* @conn: Target connection to translate metadata into
|
|
* @mask: Mask of KDBUS_ATTACH_* flags to export
|
|
@@ -988,6 +1013,7 @@ static gid_t kdbus_from_kgid_keep(struct user_namespace *ns, kgid_t gid)
|
|
* Return: 0 on success, negative error number otherwise.
|
|
*/
|
|
int kdbus_meta_export(struct kdbus_meta_proc *mp,
|
|
+ struct kdbus_meta_fake *mf,
|
|
struct kdbus_meta_conn *mc,
|
|
struct kdbus_conn *conn,
|
|
u64 mask,
|
|
@@ -1007,6 +1033,9 @@ int kdbus_meta_export(struct kdbus_meta_proc *mp,
|
|
u64 size = 0;
|
|
int ret = 0;
|
|
|
|
+ if (WARN_ON(mf && mp))
|
|
+ mp = NULL;
|
|
+
|
|
hdr = &item_hdr[0];
|
|
|
|
if (mask == 0) {
|
|
@@ -1016,7 +1045,19 @@ int kdbus_meta_export(struct kdbus_meta_proc *mp,
|
|
|
|
/* process metadata */
|
|
|
|
- if (mp && (mask & KDBUS_ATTACH_CREDS)) {
|
|
+ if (mf && (mask & KDBUS_ATTACH_CREDS)) {
|
|
+ creds.uid = kdbus_from_kuid_keep(user_ns, mf->uid);
|
|
+ creds.euid = kdbus_from_kuid_keep(user_ns, mf->euid);
|
|
+ creds.suid = kdbus_from_kuid_keep(user_ns, mf->suid);
|
|
+ creds.fsuid = kdbus_from_kuid_keep(user_ns, mf->fsuid);
|
|
+ creds.gid = kdbus_from_kgid_keep(user_ns, mf->gid);
|
|
+ creds.egid = kdbus_from_kgid_keep(user_ns, mf->egid);
|
|
+ creds.sgid = kdbus_from_kgid_keep(user_ns, mf->sgid);
|
|
+ creds.fsgid = kdbus_from_kgid_keep(user_ns, mf->fsgid);
|
|
+
|
|
+ cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++, KDBUS_ITEM_CREDS,
|
|
+ &creds, sizeof(creds), &size);
|
|
+ } else if (mp && (mask & KDBUS_ATTACH_CREDS)) {
|
|
creds.uid = kdbus_from_kuid_keep(user_ns, mp->uid);
|
|
creds.euid = kdbus_from_kuid_keep(user_ns, mp->euid);
|
|
creds.suid = kdbus_from_kuid_keep(user_ns, mp->suid);
|
|
@@ -1030,7 +1071,14 @@ int kdbus_meta_export(struct kdbus_meta_proc *mp,
|
|
&creds, sizeof(creds), &size);
|
|
}
|
|
|
|
- if (mp && (mask & KDBUS_ATTACH_PIDS)) {
|
|
+ if (mf && (mask & KDBUS_ATTACH_PIDS)) {
|
|
+ pids.pid = pid_nr_ns(mf->tgid, conn->pid_ns);
|
|
+ pids.tid = pid_nr_ns(mf->pid, conn->pid_ns);
|
|
+ pids.ppid = pid_nr_ns(mf->ppid, conn->pid_ns);
|
|
+
|
|
+ cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++, KDBUS_ITEM_PIDS,
|
|
+ &pids, sizeof(pids), &size);
|
|
+ } else if (mp && (mask & KDBUS_ATTACH_PIDS)) {
|
|
pids.pid = pid_nr_ns(mp->tgid, conn->pid_ns);
|
|
pids.tid = pid_nr_ns(mp->pid, conn->pid_ns);
|
|
pids.ppid = pid_nr_ns(mp->ppid, conn->pid_ns);
|
|
@@ -1124,7 +1172,11 @@ int kdbus_meta_export(struct kdbus_meta_proc *mp,
|
|
sizeof(caps), &size);
|
|
}
|
|
|
|
- if (mp && (mask & KDBUS_ATTACH_SECLABEL))
|
|
+ if (mf && (mask & KDBUS_ATTACH_SECLABEL))
|
|
+ cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
|
|
+ KDBUS_ITEM_SECLABEL, mf->seclabel,
|
|
+ strlen(mf->seclabel) + 1, &size);
|
|
+ else if (mp && (mask & KDBUS_ATTACH_SECLABEL))
|
|
cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
|
|
KDBUS_ITEM_SECLABEL, mp->seclabel,
|
|
strlen(mp->seclabel) + 1, &size);
|
|
diff --git a/ipc/kdbus/metadata.h b/ipc/kdbus/metadata.h
|
|
index 2dbbb3d78d97..2fb04dad38d4 100644
|
|
--- a/ipc/kdbus/metadata.h
|
|
+++ b/ipc/kdbus/metadata.h
|
|
@@ -24,14 +24,47 @@ 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);
|
|
-int kdbus_meta_proc_fake(struct kdbus_meta_proc *mp,
|
|
- const struct kdbus_creds *creds,
|
|
- const struct kdbus_pids *pids,
|
|
- const char *seclabel);
|
|
+
|
|
+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);
|
|
@@ -42,9 +75,11 @@ int kdbus_meta_conn_collect(struct kdbus_meta_conn *mc,
|
|
u64 what);
|
|
|
|
int kdbus_meta_export_prepare(struct kdbus_meta_proc *mp,
|
|
+ struct kdbus_meta_fake *mf,
|
|
struct kdbus_meta_conn *mc,
|
|
u64 *mask, size_t *sz);
|
|
int kdbus_meta_export(struct kdbus_meta_proc *mp,
|
|
+ struct kdbus_meta_fake *mf,
|
|
struct kdbus_meta_conn *mc,
|
|
struct kdbus_conn *conn,
|
|
u64 mask,
|
|
diff --git a/ipc/kdbus/queue.c b/ipc/kdbus/queue.c
|
|
index 6650b7804d87..1e7916155036 100644
|
|
--- a/ipc/kdbus/queue.c
|
|
+++ b/ipc/kdbus/queue.c
|
|
@@ -251,6 +251,7 @@ struct kdbus_queue_entry *kdbus_queue_entry_new(struct kdbus_conn *conn_dst,
|
|
atomic64_read(&conn_dst->attach_flags_recv);
|
|
|
|
ret = kdbus_meta_export_prepare(entry->proc_meta,
|
|
+ NULL,
|
|
entry->conn_meta,
|
|
&entry->attach_flags,
|
|
&meta_size);
|
|
@@ -478,6 +479,7 @@ int kdbus_queue_entry_install(struct kdbus_queue_entry *entry,
|
|
size_t meta_size;
|
|
|
|
ret = kdbus_meta_export(entry->proc_meta,
|
|
+ NULL,
|
|
entry->conn_meta,
|
|
conn_dst,
|
|
entry->attach_flags,
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 2bec5609205bc69f57c7bd27834b2637aa656f6d Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Mon, 15 Jun 2015 21:25:39 +0200
|
|
Subject: [PATCH 12/34] kdbus: pin 'struct cred' in process metadata
|
|
|
|
Instead of copying over all data from 'struct cred', pin it directly. This
|
|
reduces the memory footprint of kdbus_meta_proc considerably and speeds
|
|
up the fast-path.
|
|
|
|
There's no change in behavior. We just change the level we pin data at.
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
---
|
|
ipc/kdbus/metadata.c | 131 +++++++++++++--------------------------------------
|
|
1 file changed, 34 insertions(+), 97 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/metadata.c b/ipc/kdbus/metadata.c
|
|
index ac4135c507c4..1bac0dda72e0 100644
|
|
--- a/ipc/kdbus/metadata.c
|
|
+++ b/ipc/kdbus/metadata.c
|
|
@@ -44,26 +44,16 @@
|
|
* @lock: Object lock
|
|
* @collected: Bitmask of collected items
|
|
* @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
|
|
+ * @cred: Credentials
|
|
* @pid: PID of process
|
|
* @tgid: TGID of process
|
|
* @ppid: PPID of process
|
|
- * @auxgrps: Auxiliary groups
|
|
- * @n_auxgrps: Number of items in @auxgrps
|
|
* @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
|
|
- * @cred: Credentials
|
|
* @seclabel: Seclabel
|
|
* @audit_loginuid: Audit login-UID
|
|
* @audit_sessionid: Audit session-ID
|
|
@@ -75,18 +65,15 @@ struct kdbus_meta_proc {
|
|
u64 valid;
|
|
|
|
/* KDBUS_ITEM_CREDS */
|
|
- kuid_t uid, euid, suid, fsuid;
|
|
- kgid_t gid, egid, sgid, fsgid;
|
|
+ /* KDBUS_ITEM_AUXGROUPS */
|
|
+ /* KDBUS_ITEM_CAPS */
|
|
+ const struct cred *cred;
|
|
|
|
/* KDBUS_ITEM_PIDS */
|
|
struct pid *pid;
|
|
struct pid *tgid;
|
|
struct pid *ppid;
|
|
|
|
- /* KDBUS_ITEM_AUXGROUPS */
|
|
- kgid_t *auxgrps;
|
|
- size_t n_auxgrps;
|
|
-
|
|
/* KDBUS_ITEM_TID_COMM */
|
|
char tid_comm[TASK_COMM_LEN];
|
|
/* KDBUS_ITEM_PID_COMM */
|
|
@@ -102,9 +89,6 @@ struct kdbus_meta_proc {
|
|
/* KDBUS_ITEM_CGROUP */
|
|
char *cgroup;
|
|
|
|
- /* KDBUS_ITEM_CAPS */
|
|
- const struct cred *cred;
|
|
-
|
|
/* KDBUS_ITEM_SECLABEL */
|
|
char *seclabel;
|
|
|
|
@@ -182,7 +166,6 @@ static void kdbus_meta_proc_free(struct kref *kref)
|
|
put_pid(mp->pid);
|
|
|
|
kfree(mp->seclabel);
|
|
- kfree(mp->auxgrps);
|
|
kfree(mp->cmdline);
|
|
kfree(mp->cgroup);
|
|
kfree(mp);
|
|
@@ -214,21 +197,6 @@ struct kdbus_meta_proc *kdbus_meta_proc_unref(struct kdbus_meta_proc *mp)
|
|
return NULL;
|
|
}
|
|
|
|
-static void kdbus_meta_proc_collect_creds(struct kdbus_meta_proc *mp)
|
|
-{
|
|
- mp->uid = current_uid();
|
|
- mp->euid = current_euid();
|
|
- mp->suid = current_suid();
|
|
- mp->fsuid = current_fsuid();
|
|
-
|
|
- mp->gid = current_gid();
|
|
- mp->egid = current_egid();
|
|
- mp->sgid = current_sgid();
|
|
- mp->fsgid = current_fsgid();
|
|
-
|
|
- mp->valid |= KDBUS_ATTACH_CREDS;
|
|
-}
|
|
-
|
|
static void kdbus_meta_proc_collect_pids(struct kdbus_meta_proc *mp)
|
|
{
|
|
struct task_struct *parent;
|
|
@@ -244,30 +212,6 @@ static void kdbus_meta_proc_collect_pids(struct kdbus_meta_proc *mp)
|
|
mp->valid |= KDBUS_ATTACH_PIDS;
|
|
}
|
|
|
|
-static int kdbus_meta_proc_collect_auxgroups(struct kdbus_meta_proc *mp)
|
|
-{
|
|
- const struct group_info *info;
|
|
- size_t i;
|
|
-
|
|
- /* no need to lock/ref, current creds cannot change */
|
|
- info = current_cred()->group_info;
|
|
-
|
|
- if (info->ngroups > 0) {
|
|
- mp->auxgrps = kmalloc_array(info->ngroups, sizeof(kgid_t),
|
|
- GFP_KERNEL);
|
|
- if (!mp->auxgrps)
|
|
- return -ENOMEM;
|
|
-
|
|
- for (i = 0; i < info->ngroups; i++)
|
|
- mp->auxgrps[i] = GROUP_AT(info, i);
|
|
- }
|
|
-
|
|
- mp->n_auxgrps = info->ngroups;
|
|
- mp->valid |= KDBUS_ATTACH_AUXGROUPS;
|
|
-
|
|
- return 0;
|
|
-}
|
|
-
|
|
static void kdbus_meta_proc_collect_tid_comm(struct kdbus_meta_proc *mp)
|
|
{
|
|
get_task_comm(mp->tid_comm, current);
|
|
@@ -340,12 +284,6 @@ static int kdbus_meta_proc_collect_cgroup(struct kdbus_meta_proc *mp)
|
|
return 0;
|
|
}
|
|
|
|
-static void kdbus_meta_proc_collect_caps(struct kdbus_meta_proc *mp)
|
|
-{
|
|
- mp->cred = get_current_cred();
|
|
- mp->valid |= KDBUS_ATTACH_CAPS;
|
|
-}
|
|
-
|
|
static int kdbus_meta_proc_collect_seclabel(struct kdbus_meta_proc *mp)
|
|
{
|
|
#ifdef CONFIG_SECURITY
|
|
@@ -412,10 +350,17 @@ int kdbus_meta_proc_collect(struct kdbus_meta_proc *mp, u64 what)
|
|
|
|
mutex_lock(&mp->lock);
|
|
|
|
- if ((what & KDBUS_ATTACH_CREDS) &&
|
|
- !(mp->collected & KDBUS_ATTACH_CREDS)) {
|
|
- kdbus_meta_proc_collect_creds(mp);
|
|
- mp->collected |= KDBUS_ATTACH_CREDS;
|
|
+ /* 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) &&
|
|
@@ -424,14 +369,6 @@ int kdbus_meta_proc_collect(struct kdbus_meta_proc *mp, u64 what)
|
|
mp->collected |= KDBUS_ATTACH_PIDS;
|
|
}
|
|
|
|
- if ((what & KDBUS_ATTACH_AUXGROUPS) &&
|
|
- !(mp->collected & KDBUS_ATTACH_AUXGROUPS)) {
|
|
- ret = kdbus_meta_proc_collect_auxgroups(mp);
|
|
- if (ret < 0)
|
|
- goto exit_unlock;
|
|
- mp->collected |= KDBUS_ATTACH_AUXGROUPS;
|
|
- }
|
|
-
|
|
if ((what & KDBUS_ATTACH_TID_COMM) &&
|
|
!(mp->collected & KDBUS_ATTACH_TID_COMM)) {
|
|
kdbus_meta_proc_collect_tid_comm(mp);
|
|
@@ -466,12 +403,6 @@ int kdbus_meta_proc_collect(struct kdbus_meta_proc *mp, u64 what)
|
|
mp->collected |= KDBUS_ATTACH_CGROUP;
|
|
}
|
|
|
|
- if ((what & KDBUS_ATTACH_CAPS) &&
|
|
- !(mp->collected & KDBUS_ATTACH_CAPS)) {
|
|
- kdbus_meta_proc_collect_caps(mp);
|
|
- mp->collected |= KDBUS_ATTACH_CAPS;
|
|
- }
|
|
-
|
|
if ((what & KDBUS_ATTACH_SECLABEL) &&
|
|
!(mp->collected & KDBUS_ATTACH_SECLABEL)) {
|
|
ret = kdbus_meta_proc_collect_seclabel(mp);
|
|
@@ -841,7 +772,8 @@ int kdbus_meta_export_prepare(struct kdbus_meta_proc *mp,
|
|
size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_pids));
|
|
|
|
if (mp && (*mask & KDBUS_ATTACH_AUXGROUPS))
|
|
- size += KDBUS_ITEM_SIZE(mp->n_auxgrps * sizeof(u64));
|
|
+ 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);
|
|
@@ -1058,14 +990,16 @@ int kdbus_meta_export(struct kdbus_meta_proc *mp,
|
|
cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++, KDBUS_ITEM_CREDS,
|
|
&creds, sizeof(creds), &size);
|
|
} else if (mp && (mask & KDBUS_ATTACH_CREDS)) {
|
|
- creds.uid = kdbus_from_kuid_keep(user_ns, mp->uid);
|
|
- creds.euid = kdbus_from_kuid_keep(user_ns, mp->euid);
|
|
- creds.suid = kdbus_from_kuid_keep(user_ns, mp->suid);
|
|
- creds.fsuid = kdbus_from_kuid_keep(user_ns, mp->fsuid);
|
|
- creds.gid = kdbus_from_kgid_keep(user_ns, mp->gid);
|
|
- creds.egid = kdbus_from_kgid_keep(user_ns, mp->egid);
|
|
- creds.sgid = kdbus_from_kgid_keep(user_ns, mp->sgid);
|
|
- creds.fsgid = kdbus_from_kgid_keep(user_ns, mp->fsgid);
|
|
+ const struct cred *c = mp->cred;
|
|
+
|
|
+ creds.uid = kdbus_from_kuid_keep(user_ns, c->uid);
|
|
+ creds.euid = kdbus_from_kuid_keep(user_ns, c->euid);
|
|
+ creds.suid = kdbus_from_kuid_keep(user_ns, c->suid);
|
|
+ creds.fsuid = kdbus_from_kuid_keep(user_ns, c->fsuid);
|
|
+ creds.gid = kdbus_from_kgid_keep(user_ns, c->gid);
|
|
+ creds.egid = kdbus_from_kgid_keep(user_ns, c->egid);
|
|
+ creds.sgid = kdbus_from_kgid_keep(user_ns, c->sgid);
|
|
+ creds.fsgid = kdbus_from_kgid_keep(user_ns, c->fsgid);
|
|
|
|
cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++, KDBUS_ITEM_CREDS,
|
|
&creds, sizeof(creds), &size);
|
|
@@ -1088,17 +1022,20 @@ int kdbus_meta_export(struct kdbus_meta_proc *mp,
|
|
}
|
|
|
|
if (mp && (mask & KDBUS_ATTACH_AUXGROUPS)) {
|
|
- size_t payload_size = mp->n_auxgrps * sizeof(u64);
|
|
- int i;
|
|
+ const struct group_info *info = mp->cred->group_info;
|
|
+ size_t i, n, payload_size;
|
|
|
|
+ n = info->ngroups;
|
|
+ payload_size = n * sizeof(u64);
|
|
auxgrps = kmalloc(payload_size, GFP_KERNEL);
|
|
if (!auxgrps) {
|
|
ret = -ENOMEM;
|
|
goto exit;
|
|
}
|
|
|
|
- for (i = 0; i < mp->n_auxgrps; i++)
|
|
- auxgrps[i] = from_kgid_munged(user_ns, mp->auxgrps[i]);
|
|
+ for (i = 0; i < n; ++i)
|
|
+ auxgrps[i] = from_kgid_munged(user_ns,
|
|
+ GROUP_AT(info, i));
|
|
|
|
cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
|
|
KDBUS_ITEM_AUXGROUPS,
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 46a0b5df61b398fc58897aaeb3c1e57f210057a7 Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Fri, 19 Jun 2015 15:33:34 +0200
|
|
Subject: [PATCH 13/34] kdbus: pass msg-seqnum directly into meta-conn
|
|
|
|
Instead of passing the kmsg object into kdbus_meta_conn_collect(), pass
|
|
the message sequence number directly. It is the only field that is used
|
|
there, so avoid introducing a "kdbus_kmsg" dependency from metadata.h.
|
|
|
|
This is also a preparation for an upcoming change to drop 'kdbus_kmsg'.
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
---
|
|
ipc/kdbus/connection.c | 3 +--
|
|
ipc/kdbus/message.c | 2 +-
|
|
ipc/kdbus/metadata.c | 18 +++++++++---------
|
|
ipc/kdbus/metadata.h | 4 +---
|
|
ipc/kdbus/notify.c | 2 +-
|
|
5 files changed, 13 insertions(+), 16 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/connection.c b/ipc/kdbus/connection.c
|
|
index 8976bb32ab69..8fcff17ae2b6 100644
|
|
--- a/ipc/kdbus/connection.c
|
|
+++ b/ipc/kdbus/connection.c
|
|
@@ -1787,8 +1787,7 @@ int kdbus_cmd_conn_info(struct kdbus_conn *conn, void __user *argp)
|
|
goto exit;
|
|
}
|
|
|
|
- ret = kdbus_meta_conn_collect(conn_meta, NULL, owner_conn,
|
|
- attach_flags);
|
|
+ ret = kdbus_meta_conn_collect(conn_meta, owner_conn, 0, attach_flags);
|
|
if (ret < 0)
|
|
goto exit;
|
|
|
|
diff --git a/ipc/kdbus/message.c b/ipc/kdbus/message.c
|
|
index 55e432397b93..974e8c4f6039 100644
|
|
--- a/ipc/kdbus/message.c
|
|
+++ b/ipc/kdbus/message.c
|
|
@@ -632,5 +632,5 @@ int kdbus_kmsg_collect_metadata(struct kdbus_kmsg *kmsg, struct kdbus_conn *src,
|
|
return ret;
|
|
}
|
|
|
|
- return kdbus_meta_conn_collect(kmsg->conn_meta, kmsg, src, attach);
|
|
+ return kdbus_meta_conn_collect(kmsg->conn_meta, src, kmsg->seq, attach);
|
|
}
|
|
diff --git a/ipc/kdbus/metadata.c b/ipc/kdbus/metadata.c
|
|
index 1bac0dda72e0..dcb603798aba 100644
|
|
--- a/ipc/kdbus/metadata.c
|
|
+++ b/ipc/kdbus/metadata.c
|
|
@@ -589,13 +589,13 @@ struct kdbus_meta_conn *kdbus_meta_conn_unref(struct kdbus_meta_conn *mc)
|
|
}
|
|
|
|
static void kdbus_meta_conn_collect_timestamp(struct kdbus_meta_conn *mc,
|
|
- struct kdbus_kmsg *kmsg)
|
|
+ u64 msg_seqnum)
|
|
{
|
|
mc->ts.monotonic_ns = ktime_get_ns();
|
|
mc->ts.realtime_ns = ktime_get_real_ns();
|
|
|
|
- if (kmsg)
|
|
- mc->ts.seqnum = kmsg->seq;
|
|
+ if (msg_seqnum)
|
|
+ mc->ts.seqnum = msg_seqnum;
|
|
|
|
mc->valid |= KDBUS_ATTACH_TIMESTAMP;
|
|
}
|
|
@@ -657,11 +657,12 @@ static int kdbus_meta_conn_collect_description(struct kdbus_meta_conn *mc,
|
|
/**
|
|
* kdbus_meta_conn_collect() - Collect connection metadata
|
|
* @mc: Message metadata object
|
|
- * @kmsg: Kmsg to collect data from
|
|
* @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 @kmsg and @conn and saves it in @mc.
|
|
+ * 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.
|
|
@@ -669,9 +670,8 @@ static int kdbus_meta_conn_collect_description(struct kdbus_meta_conn *mc,
|
|
* Return: 0 on success, negative error code on failure.
|
|
*/
|
|
int kdbus_meta_conn_collect(struct kdbus_meta_conn *mc,
|
|
- struct kdbus_kmsg *kmsg,
|
|
struct kdbus_conn *conn,
|
|
- u64 what)
|
|
+ u64 msg_seqnum, u64 what)
|
|
{
|
|
int ret;
|
|
|
|
@@ -682,9 +682,9 @@ int kdbus_meta_conn_collect(struct kdbus_meta_conn *mc,
|
|
|
|
mutex_lock(&mc->lock);
|
|
|
|
- if (kmsg && (what & KDBUS_ATTACH_TIMESTAMP) &&
|
|
+ if (msg_seqnum && (what & KDBUS_ATTACH_TIMESTAMP) &&
|
|
!(mc->collected & KDBUS_ATTACH_TIMESTAMP)) {
|
|
- kdbus_meta_conn_collect_timestamp(mc, kmsg);
|
|
+ kdbus_meta_conn_collect_timestamp(mc, msg_seqnum);
|
|
mc->collected |= KDBUS_ATTACH_TIMESTAMP;
|
|
}
|
|
|
|
diff --git a/ipc/kdbus/metadata.h b/ipc/kdbus/metadata.h
|
|
index 2fb04dad38d4..c33315d0b82b 100644
|
|
--- a/ipc/kdbus/metadata.h
|
|
+++ b/ipc/kdbus/metadata.h
|
|
@@ -18,7 +18,6 @@
|
|
#include <linux/kernel.h>
|
|
|
|
struct kdbus_conn;
|
|
-struct kdbus_kmsg;
|
|
struct kdbus_pool_slice;
|
|
|
|
struct kdbus_meta_proc;
|
|
@@ -70,9 +69,8 @@ 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_kmsg *kmsg,
|
|
struct kdbus_conn *conn,
|
|
- u64 what);
|
|
+ u64 msg_seqnum, u64 what);
|
|
|
|
int kdbus_meta_export_prepare(struct kdbus_meta_proc *mp,
|
|
struct kdbus_meta_fake *mf,
|
|
diff --git a/ipc/kdbus/notify.c b/ipc/kdbus/notify.c
|
|
index e4a454222f09..216720fc26ff 100644
|
|
--- a/ipc/kdbus/notify.c
|
|
+++ b/ipc/kdbus/notify.c
|
|
@@ -209,7 +209,7 @@ void kdbus_notify_flush(struct kdbus_bus *bus)
|
|
spin_unlock(&bus->notify_lock);
|
|
|
|
list_for_each_entry_safe(kmsg, tmp, ¬ify_list, notify_entry) {
|
|
- kdbus_meta_conn_collect(kmsg->conn_meta, kmsg, NULL,
|
|
+ kdbus_meta_conn_collect(kmsg->conn_meta, NULL, kmsg->seq,
|
|
KDBUS_ATTACH_TIMESTAMP);
|
|
|
|
if (kmsg->msg.dst_id != KDBUS_DST_ID_BROADCAST) {
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 9b7d557456bf4528daaad686b545cd6285602d69 Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Fri, 19 Jun 2015 15:37:03 +0200
|
|
Subject: [PATCH 14/34] kdbus: don't include padding in mc->owned_names_size
|
|
|
|
Whenever we deal with kdbus-items, we have to distinguish the size of the
|
|
items (as written in item->size) and the offset of the next item (which
|
|
includes any padding). The KDBUS_ITEM_SIZE() macro always computes the
|
|
size *plus* padding, which is a bit misleading. In most cases it's what
|
|
you want, but in case of mc->owned_names_size it is not.
|
|
|
|
Right now, mc->owned_names_size includes the padding after the last item,
|
|
even though that is not strictly part of the items. Furthermore, since we
|
|
merge all the OWNED_NAME items into an array, it is not possible to
|
|
compute the actual size (minus padding) in O(1). Therefore, change the
|
|
collector to *NOT* include the final padding.
|
|
|
|
The reason we need this is to make sure msg->size does never include the
|
|
padding after the last item. Right now it does, which will be fixed in
|
|
follow-up patches. This patch just makes sure we actually know the real
|
|
size of the owned_names_items array.
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
---
|
|
ipc/kdbus/metadata.c | 15 +++++++++------
|
|
1 file changed, 9 insertions(+), 6 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/metadata.c b/ipc/kdbus/metadata.c
|
|
index dcb603798aba..beb7dbb73d08 100644
|
|
--- a/ipc/kdbus/metadata.c
|
|
+++ b/ipc/kdbus/metadata.c
|
|
@@ -610,14 +610,16 @@ static int kdbus_meta_conn_collect_names(struct kdbus_meta_conn *mc,
|
|
lockdep_assert_held(&conn->ep->bus->name_registry->rwlock);
|
|
|
|
size = 0;
|
|
+ /* open-code length calculation to avoid final padding */
|
|
list_for_each_entry(e, &conn->names_list, conn_entry)
|
|
- size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_name) +
|
|
- strlen(e->name) + 1);
|
|
+ size = KDBUS_ALIGN8(size) + KDBUS_ITEM_HEADER_SIZE +
|
|
+ sizeof(struct kdbus_name) + strlen(e->name) + 1;
|
|
|
|
if (!size)
|
|
return 0;
|
|
|
|
- item = kmalloc(size, GFP_KERNEL);
|
|
+ /* make sure we include zeroed padding for convenience helpers */
|
|
+ item = kmalloc(KDBUS_ALIGN8(size), GFP_KERNEL);
|
|
if (!item)
|
|
return -ENOMEM;
|
|
|
|
@@ -634,7 +636,8 @@ static int kdbus_meta_conn_collect_names(struct kdbus_meta_conn *mc,
|
|
}
|
|
|
|
/* sanity check: the buffer should be completely written now */
|
|
- WARN_ON((u8 *)item != (u8 *)mc->owned_names_items + size);
|
|
+ WARN_ON((u8 *)item !=
|
|
+ (u8 *)mc->owned_names_items + KDBUS_ALIGN8(size));
|
|
|
|
mc->valid |= KDBUS_ATTACH_NAMES;
|
|
return 0;
|
|
@@ -817,7 +820,7 @@ int kdbus_meta_export_prepare(struct kdbus_meta_proc *mp,
|
|
/* connection metadata */
|
|
|
|
if (mc && (*mask & KDBUS_ATTACH_NAMES))
|
|
- size += mc->owned_names_size;
|
|
+ size += KDBUS_ALIGN8(mc->owned_names_size);
|
|
|
|
if (mc && (*mask & KDBUS_ATTACH_CONN_DESCRIPTION))
|
|
size += KDBUS_ITEM_SIZE(strlen(mc->conn_description) + 1);
|
|
@@ -1132,7 +1135,7 @@ int kdbus_meta_export(struct kdbus_meta_proc *mp,
|
|
|
|
if (mc && (mask & KDBUS_ATTACH_NAMES))
|
|
kdbus_kvec_set(&kvec[cnt++], mc->owned_names_items,
|
|
- mc->owned_names_size, &size);
|
|
+ KDBUS_ALIGN8(mc->owned_names_size), &size);
|
|
|
|
if (mc && (mask & KDBUS_ATTACH_CONN_DESCRIPTION))
|
|
cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 8c9a45e352f84382e773cec71e8ce5e936f01db5 Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Fri, 19 Jun 2015 16:16:30 +0200
|
|
Subject: [PATCH 15/34] kdbus: re-introduce metadata buffers
|
|
|
|
This re-introduces the old way of managing metadata. The new
|
|
kdbus_meta_emit() function takes metadata sources from the caller and
|
|
emits an item buffer of the requested metadata items. The buffer is
|
|
allocated dynamically and can be directly used to copy the metadata items
|
|
into the pool slice.
|
|
|
|
Unlike kdbus_meta_export(), this new function avoids calling into
|
|
kdbus_pool_slice and as such the metadata items can be integrated into the
|
|
copy-operation of the surrounding objects. We trade one slice-copy for one
|
|
kmalloc(), which should speed up most use-cases we care about
|
|
significantly.
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
---
|
|
ipc/kdbus/metadata.c | 368 ++++++++++++++++++++++++++++++++++++++++++++++++++-
|
|
ipc/kdbus/metadata.h | 7 +
|
|
2 files changed, 374 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/ipc/kdbus/metadata.c b/ipc/kdbus/metadata.c
|
|
index beb7dbb73d08..70ce5d15fc98 100644
|
|
--- a/ipc/kdbus/metadata.c
|
|
+++ b/ipc/kdbus/metadata.c
|
|
@@ -847,7 +847,7 @@ static int kdbus_meta_push_kvec(struct kvec *kvec,
|
|
}
|
|
|
|
static void kdbus_meta_export_caps(struct kdbus_meta_caps *out,
|
|
- struct kdbus_meta_proc *mp,
|
|
+ const struct kdbus_meta_proc *mp,
|
|
struct user_namespace *user_ns)
|
|
{
|
|
struct user_namespace *iter;
|
|
@@ -1161,6 +1161,372 @@ exit:
|
|
return ret;
|
|
}
|
|
|
|
+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->user_ns;
|
|
+ struct pid_namespace *pid_ns = staging->conn->pid_ns;
|
|
+ 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;
|
|
+}
|
|
+
|
|
/**
|
|
* kdbus_meta_calc_attach_flags() - calculate attach flags for a sender
|
|
* and a receiver
|
|
diff --git a/ipc/kdbus/metadata.h b/ipc/kdbus/metadata.h
|
|
index c33315d0b82b..a30b0ad26af2 100644
|
|
--- a/ipc/kdbus/metadata.h
|
|
+++ b/ipc/kdbus/metadata.h
|
|
@@ -83,6 +83,13 @@ int kdbus_meta_export(struct kdbus_meta_proc *mp,
|
|
u64 mask,
|
|
struct kdbus_pool_slice *slice,
|
|
off_t offset, size_t *real_size);
|
|
+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_calc_attach_flags(const struct kdbus_conn *sender,
|
|
const struct kdbus_conn *receiver);
|
|
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From fd856bf056f3737c73aefe34f55799b34919699b Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Fri, 19 Jun 2015 16:46:30 +0200
|
|
Subject: [PATCH 16/34] kdbus: speed up KDBUS_CMD_*_INFO by using meta_emit()
|
|
|
|
Avoid using kdbus_meta_export*() and switch to kdbus_meta_emit(). This
|
|
adds one kmalloc(), but on the other hand drops a call to
|
|
kdbus_pool_slice_copy_kvec(). As the latter is way more expensive, this
|
|
speeds up KDBUS_CMD_*_INFO ioctls by approx. 10%.
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
---
|
|
ipc/kdbus/bus.c | 40 +++++++++++++++++++---------------------
|
|
ipc/kdbus/connection.c | 42 +++++++++++++++++++++---------------------
|
|
2 files changed, 40 insertions(+), 42 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/bus.c b/ipc/kdbus/bus.c
|
|
index dd1e600b8f01..82b7171f1fdb 100644
|
|
--- a/ipc/kdbus/bus.c
|
|
+++ b/ipc/kdbus/bus.c
|
|
@@ -456,13 +456,12 @@ 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;
|
|
- struct kvec kvec[5];
|
|
- u64 hdr_size = 0;
|
|
- u64 attach_flags;
|
|
- size_t cnt = 0;
|
|
+ size_t meta_size, name_len, cnt = 0;
|
|
+ struct kvec kvec[6];
|
|
+ u64 attach_flags, size = 0;
|
|
int ret;
|
|
|
|
struct kdbus_arg argv[] = {
|
|
@@ -484,8 +483,8 @@ int kdbus_cmd_bus_creator_info(struct kdbus_conn *conn, void __user *argp)
|
|
|
|
attach_flags &= bus->attach_flags_owner;
|
|
|
|
- ret = kdbus_meta_export_prepare(bus->creator_meta, NULL, NULL,
|
|
- &attach_flags, &meta_size);
|
|
+ ret = kdbus_meta_emit(bus->creator_meta, NULL, NULL, conn,
|
|
+ attach_flags, &meta_items, &meta_size);
|
|
if (ret < 0)
|
|
goto exit;
|
|
|
|
@@ -495,26 +494,25 @@ int kdbus_cmd_bus_creator_info(struct kdbus_conn *conn, void __user *argp)
|
|
item_hdr.type = KDBUS_ITEM_MAKE_NAME;
|
|
item_hdr.size = KDBUS_ITEM_HEADER_SIZE + name_len;
|
|
|
|
- kdbus_kvec_set(&kvec[cnt++], &info, sizeof(info), &hdr_size);
|
|
- kdbus_kvec_set(&kvec[cnt++], &item_hdr, sizeof(item_hdr), &hdr_size);
|
|
- kdbus_kvec_set(&kvec[cnt++], bus->node.name, name_len, &hdr_size);
|
|
- cnt += !!kdbus_kvec_pad(&kvec[cnt], &hdr_size);
|
|
+ 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, hdr_size + meta_size, false);
|
|
+ slice = kdbus_pool_slice_alloc(conn->pool, size, false);
|
|
if (IS_ERR(slice)) {
|
|
ret = PTR_ERR(slice);
|
|
slice = NULL;
|
|
goto exit;
|
|
}
|
|
|
|
- ret = kdbus_meta_export(bus->creator_meta, NULL, NULL, conn,
|
|
- attach_flags, slice, hdr_size, &meta_size);
|
|
- if (ret < 0)
|
|
- goto exit;
|
|
-
|
|
- info.size = hdr_size + meta_size;
|
|
-
|
|
- ret = kdbus_pool_slice_copy_kvec(slice, 0, kvec, cnt, hdr_size);
|
|
+ ret = kdbus_pool_slice_copy_kvec(slice, 0, kvec, cnt, size);
|
|
if (ret < 0)
|
|
goto exit;
|
|
|
|
@@ -527,6 +525,6 @@ int kdbus_cmd_bus_creator_info(struct kdbus_conn *conn, void __user *argp)
|
|
|
|
exit:
|
|
kdbus_pool_slice_release(slice);
|
|
-
|
|
+ kfree(meta_items);
|
|
return kdbus_args_clear(&args, ret);
|
|
}
|
|
diff --git a/ipc/kdbus/connection.c b/ipc/kdbus/connection.c
|
|
index 8fcff17ae2b6..7b800b299caf 100644
|
|
--- a/ipc/kdbus/connection.c
|
|
+++ b/ipc/kdbus/connection.c
|
|
@@ -1718,13 +1718,14 @@ int kdbus_cmd_conn_info(struct kdbus_conn *conn, void __user *argp)
|
|
struct kdbus_pool_slice *slice = NULL;
|
|
struct kdbus_name_entry *entry = 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;
|
|
- size_t meta_size;
|
|
+ struct kvec kvec[3];
|
|
+ size_t meta_size, cnt = 0;
|
|
const char *name;
|
|
- u64 attach_flags;
|
|
+ u64 attach_flags, size = 0;
|
|
int ret;
|
|
|
|
struct kdbus_arg argv[] = {
|
|
@@ -1774,10 +1775,6 @@ int kdbus_cmd_conn_info(struct kdbus_conn *conn, void __user *argp)
|
|
goto exit;
|
|
}
|
|
|
|
- info.id = owner_conn->id;
|
|
- info.flags = owner_conn->flags;
|
|
- kdbus_kvec_set(&kvec, &info, sizeof(info), &info.size);
|
|
-
|
|
attach_flags &= atomic64_read(&owner_conn->attach_flags_send);
|
|
|
|
conn_meta = kdbus_meta_conn_new();
|
|
@@ -1791,29 +1788,31 @@ int kdbus_cmd_conn_info(struct kdbus_conn *conn, void __user *argp)
|
|
if (ret < 0)
|
|
goto exit;
|
|
|
|
- ret = kdbus_meta_export_prepare(owner_conn->meta_proc,
|
|
- owner_conn->meta_fake, conn_meta,
|
|
- &attach_flags, &meta_size);
|
|
+ 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;
|
|
|
|
- slice = kdbus_pool_slice_alloc(conn->pool,
|
|
- info.size + meta_size, false);
|
|
+ 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_meta_export(owner_conn->meta_proc, owner_conn->meta_fake,
|
|
- conn_meta, conn, attach_flags,
|
|
- slice, sizeof(info), &meta_size);
|
|
- if (ret < 0)
|
|
- goto exit;
|
|
-
|
|
- info.size += meta_size;
|
|
-
|
|
- ret = kdbus_pool_slice_copy_kvec(slice, 0, &kvec, 1, sizeof(info));
|
|
+ ret = kdbus_pool_slice_copy_kvec(slice, 0, kvec, cnt, size);
|
|
if (ret < 0)
|
|
goto exit;
|
|
|
|
@@ -1831,6 +1830,7 @@ int kdbus_cmd_conn_info(struct kdbus_conn *conn, void __user *argp)
|
|
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);
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From b410b7c6a9e90977dd14d6da72f8e5fd39f04e0b Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Fri, 19 Jun 2015 21:34:08 +0200
|
|
Subject: [PATCH 17/34] kdbus: simplify kdbus_match_rules()
|
|
|
|
Split up the huge kdbus_match_rules() helper into 3 helper functions. This
|
|
reduces the code size almost by half, as we no longer have to maintain 4
|
|
levels of indentation.
|
|
|
|
No functional change.
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
---
|
|
ipc/kdbus/match.c | 123 +++++++++++++++++++++---------------------------------
|
|
1 file changed, 48 insertions(+), 75 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/match.c b/ipc/kdbus/match.c
|
|
index cc083b4211de..869a9fbce37d 100644
|
|
--- a/ipc/kdbus/match.c
|
|
+++ b/ipc/kdbus/match.c
|
|
@@ -204,87 +204,60 @@ static bool kdbus_match_bloom(const struct kdbus_bloom_filter *filter,
|
|
return true;
|
|
}
|
|
|
|
-static bool kdbus_match_rules(const struct kdbus_match_entry *entry,
|
|
- struct kdbus_conn *conn_src,
|
|
- struct kdbus_kmsg *kmsg)
|
|
+static bool kdbus_match_rule_conn(const struct kdbus_match_rule *r,
|
|
+ struct kdbus_conn *c,
|
|
+ const struct kdbus_kmsg *kmsg)
|
|
{
|
|
- struct kdbus_match_rule *r;
|
|
-
|
|
- if (conn_src)
|
|
- lockdep_assert_held(&conn_src->ep->bus->name_registry->rwlock);
|
|
-
|
|
- /*
|
|
- * Walk all the rules and bail out immediately
|
|
- * if any of them is unsatisfied.
|
|
- */
|
|
-
|
|
- list_for_each_entry(r, &entry->rules_list, rules_entry) {
|
|
- if (conn_src) {
|
|
- /* messages from userspace */
|
|
-
|
|
- switch (r->type) {
|
|
- case KDBUS_ITEM_BLOOM_MASK:
|
|
- if (!kdbus_match_bloom(kmsg->bloom_filter,
|
|
- &r->bloom_mask,
|
|
- conn_src))
|
|
- return false;
|
|
- break;
|
|
-
|
|
- case KDBUS_ITEM_ID:
|
|
- if (r->src_id != conn_src->id &&
|
|
- r->src_id != KDBUS_MATCH_ID_ANY)
|
|
- return false;
|
|
-
|
|
- break;
|
|
+ lockdep_assert_held(&c->ep->bus->name_registry->rwlock);
|
|
|
|
- case KDBUS_ITEM_NAME:
|
|
- if (!kdbus_conn_has_name(conn_src, r->name))
|
|
- return false;
|
|
-
|
|
- break;
|
|
-
|
|
- default:
|
|
- return false;
|
|
- }
|
|
- } else {
|
|
- /* kernel notifications */
|
|
-
|
|
- if (kmsg->notify_type != r->type)
|
|
- return false;
|
|
-
|
|
- switch (r->type) {
|
|
- case KDBUS_ITEM_ID_ADD:
|
|
- if (r->new_id != KDBUS_MATCH_ID_ANY &&
|
|
- r->new_id != kmsg->notify_new_id)
|
|
- return false;
|
|
-
|
|
- break;
|
|
-
|
|
- case KDBUS_ITEM_ID_REMOVE:
|
|
- if (r->old_id != KDBUS_MATCH_ID_ANY &&
|
|
- r->old_id != kmsg->notify_old_id)
|
|
- return false;
|
|
+ switch (r->type) {
|
|
+ case KDBUS_ITEM_BLOOM_MASK:
|
|
+ return kdbus_match_bloom(kmsg->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_NAME:
|
|
+ return kdbus_conn_has_name(c, r->name);
|
|
+ default:
|
|
+ return false;
|
|
+ }
|
|
+}
|
|
|
|
- break;
|
|
+static bool kdbus_match_rule_kernel(const struct kdbus_match_rule *r,
|
|
+ const struct kdbus_kmsg *kmsg)
|
|
+{
|
|
+ if (kmsg->notify_type != r->type)
|
|
+ return false;
|
|
|
|
- case KDBUS_ITEM_NAME_ADD:
|
|
- case KDBUS_ITEM_NAME_CHANGE:
|
|
- case KDBUS_ITEM_NAME_REMOVE:
|
|
- if ((r->old_id != KDBUS_MATCH_ID_ANY &&
|
|
- r->old_id != kmsg->notify_old_id) ||
|
|
- (r->new_id != KDBUS_MATCH_ID_ANY &&
|
|
- r->new_id != kmsg->notify_new_id) ||
|
|
- (r->name && kmsg->notify_name &&
|
|
- strcmp(r->name, kmsg->notify_name) != 0))
|
|
- return false;
|
|
+ switch (r->type) {
|
|
+ case KDBUS_ITEM_ID_ADD:
|
|
+ return r->new_id == KDBUS_MATCH_ID_ANY ||
|
|
+ r->new_id == kmsg->notify_new_id;
|
|
+ case KDBUS_ITEM_ID_REMOVE:
|
|
+ return r->old_id == KDBUS_MATCH_ID_ANY ||
|
|
+ r->old_id == kmsg->notify_old_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 == kmsg->notify_old_id) &&
|
|
+ (r->new_id == KDBUS_MATCH_ID_ANY ||
|
|
+ r->new_id == kmsg->notify_new_id) &&
|
|
+ (!r->name || !strcmp(r->name, kmsg->notify_name));
|
|
+ default:
|
|
+ return false;
|
|
+ }
|
|
+}
|
|
|
|
- break;
|
|
+static bool kdbus_match_rules(const struct kdbus_match_entry *entry,
|
|
+ struct kdbus_conn *c,
|
|
+ const struct kdbus_kmsg *kmsg)
|
|
+{
|
|
+ struct kdbus_match_rule *r;
|
|
|
|
- default:
|
|
- return false;
|
|
- }
|
|
- }
|
|
- }
|
|
+ list_for_each_entry(r, &entry->rules_list, rules_entry)
|
|
+ if ((c && !kdbus_match_rule_conn(r, c, kmsg)) ||
|
|
+ (!c && !kdbus_match_rule_kernel(r, kmsg)))
|
|
+ return false;
|
|
|
|
return true;
|
|
}
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From c4146fd608177530fb3f6c6ad6e395d3fb0eee4e Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Fri, 19 Jun 2015 21:39:25 +0200
|
|
Subject: [PATCH 18/34] kdbus: pass notification details directly into policy
|
|
wrapper
|
|
|
|
Avoid passing the kmsg into the policy wrapper, but pass the notification
|
|
details directly. This reduces the scope of 'struct kdbus_kmsg' and avoids
|
|
accessing its members directly. This will ease the introduction of the
|
|
kmsg rework later on.
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
---
|
|
ipc/kdbus/bus.c | 3 ++-
|
|
ipc/kdbus/connection.c | 18 ++++++++----------
|
|
ipc/kdbus/connection.h | 2 +-
|
|
3 files changed, 11 insertions(+), 12 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/bus.c b/ipc/kdbus/bus.c
|
|
index 82b7171f1fdb..3c33339cf332 100644
|
|
--- a/ipc/kdbus/bus.c
|
|
+++ b/ipc/kdbus/bus.c
|
|
@@ -298,7 +298,8 @@ void kdbus_bus_broadcast(struct kdbus_bus *bus,
|
|
* notification
|
|
*/
|
|
if (!kdbus_conn_policy_see_notification(conn_dst, NULL,
|
|
- kmsg))
|
|
+ kmsg->notify_type,
|
|
+ kmsg->notify_name))
|
|
continue;
|
|
}
|
|
|
|
diff --git a/ipc/kdbus/connection.c b/ipc/kdbus/connection.c
|
|
index 7b800b299caf..73cad58d2acd 100644
|
|
--- a/ipc/kdbus/connection.c
|
|
+++ b/ipc/kdbus/connection.c
|
|
@@ -1542,19 +1542,18 @@ static bool kdbus_conn_policy_see(struct kdbus_conn *conn,
|
|
* receive a given kernel notification
|
|
* @conn: Connection
|
|
* @conn_creds: Credentials of @conn to use for policy check
|
|
- * @kmsg: The message carrying the notification
|
|
+ * @type: Type of notification
|
|
+ * @name: Well-known name this notification is about, or NULL
|
|
*
|
|
- * This checks whether @conn is allowed to see the kernel notification @kmsg.
|
|
+ * This checks whether @conn is allowed to see the kernel notification of type
|
|
+ * @type for well-known name @name.
|
|
*
|
|
* 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_kmsg *kmsg)
|
|
+ u64 type, const char *name)
|
|
{
|
|
- if (WARN_ON(kmsg->msg.src_id != KDBUS_SRC_ID_KERNEL))
|
|
- return false;
|
|
-
|
|
/*
|
|
* Depending on the notification type, broadcasted kernel notifications
|
|
* have to be filtered:
|
|
@@ -1567,12 +1566,11 @@ bool kdbus_conn_policy_see_notification(struct kdbus_conn *conn,
|
|
* broadcast to everyone, to allow tracking peers.
|
|
*/
|
|
|
|
- switch (kmsg->notify_type) {
|
|
+ switch (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,
|
|
- kmsg->notify_name);
|
|
+ return kdbus_conn_policy_see_name(conn, conn_creds, name);
|
|
|
|
case KDBUS_ITEM_ID_ADD:
|
|
case KDBUS_ITEM_ID_REMOVE:
|
|
@@ -1580,7 +1578,7 @@ bool kdbus_conn_policy_see_notification(struct kdbus_conn *conn,
|
|
|
|
default:
|
|
WARN(1, "Invalid type for notification broadcast: %llu\n",
|
|
- (unsigned long long)kmsg->notify_type);
|
|
+ (unsigned long long)type);
|
|
return false;
|
|
}
|
|
}
|
|
diff --git a/ipc/kdbus/connection.h b/ipc/kdbus/connection.h
|
|
index 0eb3d2e70a5e..aa4234a4e3e4 100644
|
|
--- a/ipc/kdbus/connection.h
|
|
+++ b/ipc/kdbus/connection.h
|
|
@@ -152,7 +152,7 @@ bool kdbus_conn_policy_see_name_unlocked(struct kdbus_conn *conn,
|
|
const char *name);
|
|
bool kdbus_conn_policy_see_notification(struct kdbus_conn *conn,
|
|
const struct cred *curr_creds,
|
|
- const struct kdbus_kmsg *kmsg);
|
|
+ u64 type, const char *name);
|
|
|
|
/* command dispatcher */
|
|
struct kdbus_conn *kdbus_cmd_hello(struct kdbus_ep *ep, bool privileged,
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From bdac38baecf3b1af14b647f791a795a91ef7f6c6 Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Fri, 19 Jun 2015 21:46:39 +0200
|
|
Subject: [PATCH 19/34] kdbus: pass source connection to queue handling
|
|
|
|
Make sure we always pass the source-connection of a message along. Right
|
|
now we drop it for queue allocation, which prevents us from accessing any
|
|
source details in the queue helpers. We work around this, by passing
|
|
everything manually, but that just makes things more complex.
|
|
|
|
Pass the source connection down to the queue helpers to allow further
|
|
cleanups in follow up patches.
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
---
|
|
ipc/kdbus/connection.c | 15 +++++++--------
|
|
ipc/kdbus/queue.c | 9 +++++----
|
|
ipc/kdbus/queue.h | 6 +++---
|
|
3 files changed, 15 insertions(+), 15 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/connection.c b/ipc/kdbus/connection.c
|
|
index 73cad58d2acd..f1667df70b7f 100644
|
|
--- a/ipc/kdbus/connection.c
|
|
+++ b/ipc/kdbus/connection.c
|
|
@@ -779,9 +779,9 @@ void kdbus_conn_lost_message(struct kdbus_conn *c)
|
|
|
|
/* Callers should take the conn_dst lock */
|
|
static struct kdbus_queue_entry *
|
|
-kdbus_conn_entry_make(struct kdbus_conn *conn_dst,
|
|
- const struct kdbus_kmsg *kmsg,
|
|
- struct kdbus_user *user)
|
|
+kdbus_conn_entry_make(struct kdbus_conn *conn_src,
|
|
+ struct kdbus_conn *conn_dst,
|
|
+ const struct kdbus_kmsg *kmsg)
|
|
{
|
|
/* The remote connection was disconnected */
|
|
if (!kdbus_conn_active(conn_dst))
|
|
@@ -799,7 +799,7 @@ kdbus_conn_entry_make(struct kdbus_conn *conn_dst,
|
|
kmsg->res && kmsg->res->fds_count > 0)
|
|
return ERR_PTR(-ECOMM);
|
|
|
|
- return kdbus_queue_entry_new(conn_dst, kmsg, user);
|
|
+ return kdbus_queue_entry_new(conn_src, conn_dst, kmsg);
|
|
}
|
|
|
|
/*
|
|
@@ -822,8 +822,8 @@ static int kdbus_conn_entry_sync_attach(struct kdbus_conn *conn_dst,
|
|
* entry and attach it to the reply object
|
|
*/
|
|
if (reply_wake->waiting) {
|
|
- entry = kdbus_conn_entry_make(conn_dst, kmsg,
|
|
- reply_wake->reply_src->user);
|
|
+ entry = kdbus_conn_entry_make(reply_wake->reply_src, conn_dst,
|
|
+ kmsg);
|
|
if (IS_ERR(entry))
|
|
ret = PTR_ERR(entry);
|
|
else
|
|
@@ -879,8 +879,7 @@ int kdbus_conn_entry_insert(struct kdbus_conn *conn_src,
|
|
|
|
kdbus_conn_lock2(conn_src, conn_dst);
|
|
|
|
- entry = kdbus_conn_entry_make(conn_dst, kmsg,
|
|
- conn_src ? conn_src->user : NULL);
|
|
+ entry = kdbus_conn_entry_make(conn_src, conn_dst, kmsg);
|
|
if (IS_ERR(entry)) {
|
|
ret = PTR_ERR(entry);
|
|
goto exit_unlock;
|
|
diff --git a/ipc/kdbus/queue.c b/ipc/kdbus/queue.c
|
|
index 1e7916155036..43824c93fb03 100644
|
|
--- a/ipc/kdbus/queue.c
|
|
+++ b/ipc/kdbus/queue.c
|
|
@@ -171,9 +171,9 @@ static void kdbus_queue_entry_unlink(struct kdbus_queue_entry *entry)
|
|
|
|
/**
|
|
* kdbus_queue_entry_new() - allocate a queue entry
|
|
+ * @conn_src: source connection
|
|
* @conn_dst: destination connection
|
|
* @kmsg: kmsg object the queue entry should track
|
|
- * @user: user to account message on (or NULL for kernel messages)
|
|
*
|
|
* Allocates a queue entry based on a given kmsg and allocate space for
|
|
* the message payload and the requested metadata in the connection's pool.
|
|
@@ -181,10 +181,11 @@ static void kdbus_queue_entry_unlink(struct kdbus_queue_entry *entry)
|
|
*
|
|
* Return: the allocated entry on success, or an ERR_PTR on failures.
|
|
*/
|
|
-struct kdbus_queue_entry *kdbus_queue_entry_new(struct kdbus_conn *conn_dst,
|
|
- const struct kdbus_kmsg *kmsg,
|
|
- struct kdbus_user *user)
|
|
+struct kdbus_queue_entry *kdbus_queue_entry_new(struct kdbus_conn *conn_src,
|
|
+ struct kdbus_conn *conn_dst,
|
|
+ const struct kdbus_kmsg *kmsg)
|
|
{
|
|
+ struct kdbus_user *user = conn_src ? conn_src->user : NULL;
|
|
struct kdbus_msg_resources *res = kmsg->res;
|
|
const struct kdbus_msg *msg = &kmsg->msg;
|
|
struct kdbus_queue_entry *entry;
|
|
diff --git a/ipc/kdbus/queue.h b/ipc/kdbus/queue.h
|
|
index 7f2db96fe308..ac471d0c809d 100644
|
|
--- a/ipc/kdbus/queue.h
|
|
+++ b/ipc/kdbus/queue.h
|
|
@@ -78,9 +78,9 @@ 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 *conn_dst,
|
|
- const struct kdbus_kmsg *kmsg,
|
|
- struct kdbus_user *user);
|
|
+struct kdbus_queue_entry *kdbus_queue_entry_new(struct kdbus_conn *conn_src,
|
|
+ struct kdbus_conn *conn_dst,
|
|
+ const struct kdbus_kmsg *kmsg);
|
|
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);
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 5347bdd081c75f847c9739fc50e43f71440687bb Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Fri, 19 Jun 2015 21:56:25 +0200
|
|
Subject: [PATCH 20/34] kdbus: remove redundant kmsg->dst_name_id
|
|
|
|
Right now we store the dst_name_id in "struct kdbus_kmsg" just to make
|
|
sure it's available when we allocate the "struct kdbus_queue_entry". This
|
|
is really not needed and we can easily get rid of this field by passing
|
|
the kdbus_name_entry to the queue allocator.
|
|
|
|
No change in behavior.
|
|
|
|
This change allows us to make "kmsg" constant in kdbus_pin_dst() and avoid
|
|
touching it ever outside of kmsg-allocation.
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
---
|
|
ipc/kdbus/bus.c | 6 ++++--
|
|
ipc/kdbus/connection.c | 29 ++++++++++++++++-------------
|
|
ipc/kdbus/connection.h | 3 ++-
|
|
ipc/kdbus/message.h | 2 --
|
|
ipc/kdbus/notify.c | 3 ++-
|
|
ipc/kdbus/queue.c | 1 -
|
|
6 files changed, 24 insertions(+), 20 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/bus.c b/ipc/kdbus/bus.c
|
|
index 3c33339cf332..16f69acfec8a 100644
|
|
--- a/ipc/kdbus/bus.c
|
|
+++ b/ipc/kdbus/bus.c
|
|
@@ -303,7 +303,8 @@ void kdbus_bus_broadcast(struct kdbus_bus *bus,
|
|
continue;
|
|
}
|
|
|
|
- ret = kdbus_conn_entry_insert(conn_src, conn_dst, kmsg, NULL);
|
|
+ ret = kdbus_conn_entry_insert(conn_src, conn_dst, kmsg, NULL,
|
|
+ NULL);
|
|
if (ret < 0)
|
|
kdbus_conn_lost_message(conn_dst);
|
|
}
|
|
@@ -346,7 +347,8 @@ void kdbus_bus_eavesdrop(struct kdbus_bus *bus,
|
|
}
|
|
}
|
|
|
|
- ret = kdbus_conn_entry_insert(conn_src, conn_dst, kmsg, NULL);
|
|
+ ret = kdbus_conn_entry_insert(conn_src, conn_dst, kmsg, NULL,
|
|
+ NULL);
|
|
if (ret < 0)
|
|
kdbus_conn_lost_message(conn_dst);
|
|
}
|
|
diff --git a/ipc/kdbus/connection.c b/ipc/kdbus/connection.c
|
|
index f1667df70b7f..2ce63ccca3b4 100644
|
|
--- a/ipc/kdbus/connection.c
|
|
+++ b/ipc/kdbus/connection.c
|
|
@@ -866,13 +866,15 @@ static int kdbus_conn_entry_sync_attach(struct kdbus_conn *conn_dst,
|
|
* @conn_dst: The connection to queue into
|
|
* @kmsg: The kmsg to queue
|
|
* @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,
|
|
const struct kdbus_kmsg *kmsg,
|
|
- struct kdbus_reply *reply)
|
|
+ struct kdbus_reply *reply,
|
|
+ const struct kdbus_name_entry *name)
|
|
{
|
|
struct kdbus_queue_entry *entry;
|
|
int ret;
|
|
@@ -891,6 +893,14 @@ int kdbus_conn_entry_insert(struct kdbus_conn *conn_src,
|
|
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);
|
|
|
|
@@ -1023,14 +1033,14 @@ static int kdbus_conn_wait_reply(struct kdbus_conn *conn_src,
|
|
}
|
|
|
|
static int kdbus_pin_dst(struct kdbus_bus *bus,
|
|
- struct kdbus_kmsg *kmsg,
|
|
+ const struct kdbus_kmsg *kmsg,
|
|
struct kdbus_name_entry **out_name,
|
|
struct kdbus_conn **out_dst)
|
|
{
|
|
struct kdbus_msg_resources *res = kmsg->res;
|
|
+ const struct kdbus_msg *msg = &kmsg->msg;
|
|
struct kdbus_name_entry *name = NULL;
|
|
struct kdbus_conn *dst = NULL;
|
|
- struct kdbus_msg *msg = &kmsg->msg;
|
|
int ret;
|
|
|
|
if (WARN_ON(!res))
|
|
@@ -1075,13 +1085,6 @@ static int kdbus_pin_dst(struct kdbus_bus *bus,
|
|
ret = -EADDRNOTAVAIL;
|
|
goto error;
|
|
}
|
|
-
|
|
- /*
|
|
- * Record the sequence number of the registered name; it will
|
|
- * be passed on to the queue, in case messages addressed to a
|
|
- * name need to be moved from or to an activator.
|
|
- */
|
|
- kmsg->dst_name_id = name->name_id;
|
|
}
|
|
|
|
*out_name = name;
|
|
@@ -1142,7 +1145,7 @@ static int kdbus_conn_reply(struct kdbus_conn *src, struct kdbus_kmsg *kmsg)
|
|
if (wake)
|
|
ret = kdbus_conn_entry_sync_attach(dst, kmsg, wake);
|
|
else
|
|
- ret = kdbus_conn_entry_insert(src, dst, kmsg, NULL);
|
|
+ ret = kdbus_conn_entry_insert(src, dst, kmsg, NULL, name);
|
|
|
|
exit:
|
|
up_read(&bus->name_registry->rwlock);
|
|
@@ -1217,7 +1220,7 @@ static struct kdbus_reply *kdbus_conn_call(struct kdbus_conn *src,
|
|
|
|
kdbus_bus_eavesdrop(bus, src, kmsg);
|
|
|
|
- ret = kdbus_conn_entry_insert(src, dst, kmsg, wait);
|
|
+ ret = kdbus_conn_entry_insert(src, dst, kmsg, wait, name);
|
|
if (ret < 0)
|
|
goto exit;
|
|
|
|
@@ -1287,7 +1290,7 @@ static int kdbus_conn_unicast(struct kdbus_conn *src, struct kdbus_kmsg *kmsg)
|
|
if (!is_signal)
|
|
kdbus_bus_eavesdrop(bus, src, kmsg);
|
|
|
|
- ret = kdbus_conn_entry_insert(src, dst, kmsg, wait);
|
|
+ ret = kdbus_conn_entry_insert(src, dst, kmsg, wait, name);
|
|
if (ret < 0 && !is_signal)
|
|
goto exit;
|
|
|
|
diff --git a/ipc/kdbus/connection.h b/ipc/kdbus/connection.h
|
|
index aa4234a4e3e4..8cffba94a564 100644
|
|
--- a/ipc/kdbus/connection.h
|
|
+++ b/ipc/kdbus/connection.h
|
|
@@ -135,7 +135,8 @@ void kdbus_conn_lost_message(struct kdbus_conn *c);
|
|
int kdbus_conn_entry_insert(struct kdbus_conn *conn_src,
|
|
struct kdbus_conn *conn_dst,
|
|
const struct kdbus_kmsg *kmsg,
|
|
- struct kdbus_reply *reply);
|
|
+ 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);
|
|
diff --git a/ipc/kdbus/message.h b/ipc/kdbus/message.h
|
|
index 3b733474f335..88e2366e49b6 100644
|
|
--- a/ipc/kdbus/message.h
|
|
+++ b/ipc/kdbus/message.h
|
|
@@ -86,7 +86,6 @@ kdbus_msg_resources_unref(struct kdbus_msg_resources *r);
|
|
* @notify_old_id: Short-cut for faster lookup
|
|
* @notify_new_id: Short-cut for faster lookup
|
|
* @notify_name: Short-cut for faster lookup
|
|
- * @dst_name_id: Short-cut to msg for faster lookup
|
|
* @bloom_filter: Bloom filter to match message properties
|
|
* @notify_entry: List of kernel-generated notifications
|
|
* @iov: Array of iovec, describing the payload to copy
|
|
@@ -104,7 +103,6 @@ struct kdbus_kmsg {
|
|
u64 notify_new_id;
|
|
const char *notify_name;
|
|
|
|
- u64 dst_name_id;
|
|
const struct kdbus_bloom_filter *bloom_filter;
|
|
struct list_head notify_entry;
|
|
|
|
diff --git a/ipc/kdbus/notify.c b/ipc/kdbus/notify.c
|
|
index 216720fc26ff..bcf7555e386a 100644
|
|
--- a/ipc/kdbus/notify.c
|
|
+++ b/ipc/kdbus/notify.c
|
|
@@ -218,7 +218,8 @@ void kdbus_notify_flush(struct kdbus_bus *bus)
|
|
conn = kdbus_bus_find_conn_by_id(bus, kmsg->msg.dst_id);
|
|
if (conn) {
|
|
kdbus_bus_eavesdrop(bus, NULL, kmsg);
|
|
- kdbus_conn_entry_insert(NULL, conn, kmsg, NULL);
|
|
+ kdbus_conn_entry_insert(NULL, conn, kmsg, NULL,
|
|
+ NULL);
|
|
kdbus_conn_unref(conn);
|
|
}
|
|
} else {
|
|
diff --git a/ipc/kdbus/queue.c b/ipc/kdbus/queue.c
|
|
index 43824c93fb03..4749b23a4f14 100644
|
|
--- a/ipc/kdbus/queue.c
|
|
+++ b/ipc/kdbus/queue.c
|
|
@@ -203,7 +203,6 @@ struct kdbus_queue_entry *kdbus_queue_entry_new(struct kdbus_conn *conn_src,
|
|
|
|
INIT_LIST_HEAD(&entry->entry);
|
|
entry->priority = msg->priority;
|
|
- entry->dst_name_id = kmsg->dst_name_id;
|
|
entry->msg_res = kdbus_msg_resources_ref(res);
|
|
entry->proc_meta = kdbus_meta_proc_ref(kmsg->proc_meta);
|
|
entry->conn_meta = kdbus_meta_conn_ref(kmsg->conn_meta);
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 462d196d27840d84e1ff686c481fbbd99509864e Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Fri, 19 Jun 2015 23:04:49 +0200
|
|
Subject: [PATCH 21/34] kdbus: simplify collecting metadata on messages
|
|
|
|
Instead of collecting metadata on the call-side for each message origin,
|
|
move it to the core: Handle it directly before allocating the message
|
|
slice.
|
|
|
|
The number of times we call into the helper stays exactly the same, but
|
|
the code paths are reduced significantly.
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
---
|
|
ipc/kdbus/bus.c | 16 ----------------
|
|
ipc/kdbus/connection.c | 31 ++++++++++++-------------------
|
|
ipc/kdbus/message.c | 4 ++--
|
|
ipc/kdbus/message.h | 4 ++--
|
|
4 files changed, 16 insertions(+), 39 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/bus.c b/ipc/kdbus/bus.c
|
|
index 16f69acfec8a..bc3c3c0383cb 100644
|
|
--- a/ipc/kdbus/bus.c
|
|
+++ b/ipc/kdbus/bus.c
|
|
@@ -284,13 +284,6 @@ void kdbus_bus_broadcast(struct kdbus_bus *bus,
|
|
*/
|
|
if (!kdbus_conn_policy_talk(conn_dst, NULL, conn_src))
|
|
continue;
|
|
-
|
|
- ret = kdbus_kmsg_collect_metadata(kmsg, conn_src,
|
|
- conn_dst);
|
|
- if (ret < 0) {
|
|
- kdbus_conn_lost_message(conn_dst);
|
|
- continue;
|
|
- }
|
|
} else {
|
|
/*
|
|
* Check if there is a policy db that prevents the
|
|
@@ -338,15 +331,6 @@ void kdbus_bus_eavesdrop(struct kdbus_bus *bus,
|
|
|
|
down_read(&bus->conn_rwlock);
|
|
list_for_each_entry(conn_dst, &bus->monitors_list, monitor_entry) {
|
|
- if (conn_src) {
|
|
- ret = kdbus_kmsg_collect_metadata(kmsg, conn_src,
|
|
- conn_dst);
|
|
- if (ret < 0) {
|
|
- kdbus_conn_lost_message(conn_dst);
|
|
- continue;
|
|
- }
|
|
- }
|
|
-
|
|
ret = kdbus_conn_entry_insert(conn_src, conn_dst, kmsg, NULL,
|
|
NULL);
|
|
if (ret < 0)
|
|
diff --git a/ipc/kdbus/connection.c b/ipc/kdbus/connection.c
|
|
index 2ce63ccca3b4..25e49455b24b 100644
|
|
--- a/ipc/kdbus/connection.c
|
|
+++ b/ipc/kdbus/connection.c
|
|
@@ -813,7 +813,12 @@ static int kdbus_conn_entry_sync_attach(struct kdbus_conn *conn_dst,
|
|
{
|
|
struct kdbus_queue_entry *entry;
|
|
int remote_ret;
|
|
- int ret = 0;
|
|
+ int ret;
|
|
+
|
|
+ ret = kdbus_kmsg_collect_metadata(kmsg, reply_wake->reply_src,
|
|
+ conn_dst);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
|
|
mutex_lock(&reply_wake->reply_dst->lock);
|
|
|
|
@@ -879,6 +884,12 @@ int kdbus_conn_entry_insert(struct kdbus_conn *conn_src,
|
|
struct kdbus_queue_entry *entry;
|
|
int ret;
|
|
|
|
+ if (conn_src) {
|
|
+ ret = kdbus_kmsg_collect_metadata(kmsg, conn_src, conn_dst);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
kdbus_conn_lock2(conn_src, conn_dst);
|
|
|
|
entry = kdbus_conn_entry_make(conn_src, conn_dst, kmsg);
|
|
@@ -1132,12 +1143,6 @@ static int kdbus_conn_reply(struct kdbus_conn *src, struct kdbus_kmsg *kmsg)
|
|
goto exit;
|
|
}
|
|
|
|
- /* attach metadata */
|
|
-
|
|
- ret = kdbus_kmsg_collect_metadata(kmsg, src, dst);
|
|
- if (ret < 0)
|
|
- goto exit;
|
|
-
|
|
/* send message */
|
|
|
|
kdbus_bus_eavesdrop(bus, src, kmsg);
|
|
@@ -1210,12 +1215,6 @@ static struct kdbus_reply *kdbus_conn_call(struct kdbus_conn *src,
|
|
goto exit;
|
|
}
|
|
|
|
- /* attach metadata */
|
|
-
|
|
- ret = kdbus_kmsg_collect_metadata(kmsg, src, dst);
|
|
- if (ret < 0)
|
|
- goto exit;
|
|
-
|
|
/* send message */
|
|
|
|
kdbus_bus_eavesdrop(bus, src, kmsg);
|
|
@@ -1279,12 +1278,6 @@ static int kdbus_conn_unicast(struct kdbus_conn *src, struct kdbus_kmsg *kmsg)
|
|
}
|
|
}
|
|
|
|
- /* attach metadata */
|
|
-
|
|
- ret = kdbus_kmsg_collect_metadata(kmsg, src, dst);
|
|
- if (ret < 0)
|
|
- goto exit;
|
|
-
|
|
/* send message */
|
|
|
|
if (!is_signal)
|
|
diff --git a/ipc/kdbus/message.c b/ipc/kdbus/message.c
|
|
index 974e8c4f6039..9346a04e10ef 100644
|
|
--- a/ipc/kdbus/message.c
|
|
+++ b/ipc/kdbus/message.c
|
|
@@ -619,8 +619,8 @@ exit_free:
|
|
*
|
|
* Return: 0 on success, negative error code on failure.
|
|
*/
|
|
-int kdbus_kmsg_collect_metadata(struct kdbus_kmsg *kmsg, struct kdbus_conn *src,
|
|
- struct kdbus_conn *dst)
|
|
+int kdbus_kmsg_collect_metadata(const struct kdbus_kmsg *kmsg,
|
|
+ struct kdbus_conn *src, struct kdbus_conn *dst)
|
|
{
|
|
u64 attach;
|
|
int ret;
|
|
diff --git a/ipc/kdbus/message.h b/ipc/kdbus/message.h
|
|
index 88e2366e49b6..9a11389d9f27 100644
|
|
--- a/ipc/kdbus/message.h
|
|
+++ b/ipc/kdbus/message.h
|
|
@@ -125,7 +125,7 @@ struct kdbus_kmsg *kdbus_kmsg_new(struct kdbus_bus *bus, size_t extra_size);
|
|
struct kdbus_kmsg *kdbus_kmsg_new_from_cmd(struct kdbus_conn *conn,
|
|
struct kdbus_cmd_send *cmd_send);
|
|
void kdbus_kmsg_free(struct kdbus_kmsg *kmsg);
|
|
-int kdbus_kmsg_collect_metadata(struct kdbus_kmsg *kmsg, struct kdbus_conn *src,
|
|
- struct kdbus_conn *dst);
|
|
+int kdbus_kmsg_collect_metadata(const struct kdbus_kmsg *kmsg,
|
|
+ struct kdbus_conn *src, struct kdbus_conn *dst);
|
|
|
|
#endif
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 6d75879b543c4eeb9544547e87174bba1be3a173 Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Fri, 19 Jun 2015 23:38:19 +0200
|
|
Subject: [PATCH 22/34] kdbus: simplify notification shortcuts
|
|
|
|
Right now, we have a bunch of shortcuts in "struct kdbus_kmsg" that cache
|
|
the values of the first kdbus-item, _iff_ the message is a kernel
|
|
notification. These are then used during match-db lookups and policy
|
|
lookups.
|
|
|
|
These shortcuts make the actually more complex and in no way faster. Drop
|
|
them in favor of a simple "struct kdbus_item *notify" shortcut which
|
|
points to the notification item (to distinguish it from normal
|
|
user-supplied messages). This way, we save a bunch of code and can even
|
|
simplify the notification message setup (as it is now fully generic).
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
---
|
|
ipc/kdbus/bus.c | 3 +-
|
|
ipc/kdbus/connection.c | 15 +++++-----
|
|
ipc/kdbus/connection.h | 2 +-
|
|
ipc/kdbus/match.c | 14 +++++----
|
|
ipc/kdbus/message.c | 54 ++++++++++++++++++----------------
|
|
ipc/kdbus/message.h | 14 ++++-----
|
|
ipc/kdbus/notify.c | 78 ++++++++++++--------------------------------------
|
|
7 files changed, 69 insertions(+), 111 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/bus.c b/ipc/kdbus/bus.c
|
|
index bc3c3c0383cb..4f51723f8780 100644
|
|
--- a/ipc/kdbus/bus.c
|
|
+++ b/ipc/kdbus/bus.c
|
|
@@ -291,8 +291,7 @@ void kdbus_bus_broadcast(struct kdbus_bus *bus,
|
|
* notification
|
|
*/
|
|
if (!kdbus_conn_policy_see_notification(conn_dst, NULL,
|
|
- kmsg->notify_type,
|
|
- kmsg->notify_name))
|
|
+ &kmsg->msg))
|
|
continue;
|
|
}
|
|
|
|
diff --git a/ipc/kdbus/connection.c b/ipc/kdbus/connection.c
|
|
index 25e49455b24b..29b99c023bfe 100644
|
|
--- a/ipc/kdbus/connection.c
|
|
+++ b/ipc/kdbus/connection.c
|
|
@@ -1537,17 +1537,15 @@ static bool kdbus_conn_policy_see(struct kdbus_conn *conn,
|
|
* receive a given kernel notification
|
|
* @conn: Connection
|
|
* @conn_creds: Credentials of @conn to use for policy check
|
|
- * @type: Type of notification
|
|
- * @name: Well-known name this notification is about, or NULL
|
|
+ * @msg: Notification message
|
|
*
|
|
- * This checks whether @conn is allowed to see the kernel notification of type
|
|
- * @type for well-known name @name.
|
|
+ * 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,
|
|
- u64 type, const char *name)
|
|
+ const struct kdbus_msg *msg)
|
|
{
|
|
/*
|
|
* Depending on the notification type, broadcasted kernel notifications
|
|
@@ -1561,11 +1559,12 @@ bool kdbus_conn_policy_see_notification(struct kdbus_conn *conn,
|
|
* broadcast to everyone, to allow tracking peers.
|
|
*/
|
|
|
|
- switch (type) {
|
|
+ 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, name);
|
|
+ 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:
|
|
@@ -1573,7 +1572,7 @@ bool kdbus_conn_policy_see_notification(struct kdbus_conn *conn,
|
|
|
|
default:
|
|
WARN(1, "Invalid type for notification broadcast: %llu\n",
|
|
- (unsigned long long)type);
|
|
+ (unsigned long long)msg->items[0].type);
|
|
return false;
|
|
}
|
|
}
|
|
diff --git a/ipc/kdbus/connection.h b/ipc/kdbus/connection.h
|
|
index 8cffba94a564..90c1bcc91fe6 100644
|
|
--- a/ipc/kdbus/connection.h
|
|
+++ b/ipc/kdbus/connection.h
|
|
@@ -153,7 +153,7 @@ bool kdbus_conn_policy_see_name_unlocked(struct kdbus_conn *conn,
|
|
const char *name);
|
|
bool kdbus_conn_policy_see_notification(struct kdbus_conn *conn,
|
|
const struct cred *curr_creds,
|
|
- u64 type, const char *name);
|
|
+ const struct kdbus_msg *msg);
|
|
|
|
/* command dispatcher */
|
|
struct kdbus_conn *kdbus_cmd_hello(struct kdbus_ep *ep, bool privileged,
|
|
diff --git a/ipc/kdbus/match.c b/ipc/kdbus/match.c
|
|
index 869a9fbce37d..3f86e4c81468 100644
|
|
--- a/ipc/kdbus/match.c
|
|
+++ b/ipc/kdbus/match.c
|
|
@@ -225,24 +225,26 @@ static bool kdbus_match_rule_conn(const struct kdbus_match_rule *r,
|
|
static bool kdbus_match_rule_kernel(const struct kdbus_match_rule *r,
|
|
const struct kdbus_kmsg *kmsg)
|
|
{
|
|
- if (kmsg->notify_type != r->type)
|
|
+ struct kdbus_item *n = kmsg->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 == kmsg->notify_new_id;
|
|
+ r->new_id == n->id_change.id;
|
|
case KDBUS_ITEM_ID_REMOVE:
|
|
return r->old_id == KDBUS_MATCH_ID_ANY ||
|
|
- r->old_id == kmsg->notify_old_id;
|
|
+ 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 == kmsg->notify_old_id) &&
|
|
+ r->old_id == n->name_change.old_id.id) &&
|
|
(r->new_id == KDBUS_MATCH_ID_ANY ||
|
|
- r->new_id == kmsg->notify_new_id) &&
|
|
- (!r->name || !strcmp(r->name, kmsg->notify_name));
|
|
+ r->new_id == n->name_change.new_id.id) &&
|
|
+ (!r->name || !strcmp(r->name, n->name_change.name));
|
|
default:
|
|
return false;
|
|
}
|
|
diff --git a/ipc/kdbus/message.c b/ipc/kdbus/message.c
|
|
index 9346a04e10ef..7f23e5c857dd 100644
|
|
--- a/ipc/kdbus/message.c
|
|
+++ b/ipc/kdbus/message.c
|
|
@@ -124,46 +124,50 @@ void kdbus_kmsg_free(struct kdbus_kmsg *kmsg)
|
|
kfree(kmsg);
|
|
}
|
|
|
|
-/**
|
|
- * kdbus_kmsg_new() - allocate message
|
|
- * @bus: Bus this message is allocated on
|
|
- * @extra_size: Additional size to reserve for data
|
|
- *
|
|
- * Return: new kdbus_kmsg on success, ERR_PTR on failure.
|
|
- */
|
|
-struct kdbus_kmsg *kdbus_kmsg_new(struct kdbus_bus *bus, size_t extra_size)
|
|
+struct kdbus_kmsg *kdbus_kmsg_new_kernel(struct kdbus_bus *bus,
|
|
+ u64 dst, u64 cookie_timeout,
|
|
+ size_t it_size, size_t it_type)
|
|
{
|
|
- struct kdbus_kmsg *m;
|
|
+ struct kdbus_kmsg *kmsg;
|
|
size_t size;
|
|
int ret;
|
|
|
|
- size = sizeof(struct kdbus_kmsg) + KDBUS_ITEM_SIZE(extra_size);
|
|
- m = kzalloc(size, GFP_KERNEL);
|
|
- if (!m)
|
|
+ size = sizeof(struct kdbus_kmsg) + KDBUS_ITEM_SIZE(it_size);
|
|
+ kmsg = kzalloc(size, GFP_KERNEL);
|
|
+ if (!kmsg)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
- m->seq = atomic64_inc_return(&bus->domain->last_id);
|
|
- m->msg.size = size - KDBUS_KMSG_HEADER_SIZE;
|
|
- m->msg.items[0].size = KDBUS_ITEM_SIZE(extra_size);
|
|
+ kmsg->seq = atomic64_inc_return(&bus->domain->last_id);
|
|
|
|
- m->proc_meta = kdbus_meta_proc_new();
|
|
- if (IS_ERR(m->proc_meta)) {
|
|
- ret = PTR_ERR(m->proc_meta);
|
|
- m->proc_meta = NULL;
|
|
+ kmsg->proc_meta = kdbus_meta_proc_new();
|
|
+ if (IS_ERR(kmsg->proc_meta)) {
|
|
+ ret = PTR_ERR(kmsg->proc_meta);
|
|
+ kmsg->proc_meta = NULL;
|
|
goto exit;
|
|
}
|
|
|
|
- m->conn_meta = kdbus_meta_conn_new();
|
|
- if (IS_ERR(m->conn_meta)) {
|
|
- ret = PTR_ERR(m->conn_meta);
|
|
- m->conn_meta = NULL;
|
|
+ kmsg->conn_meta = kdbus_meta_conn_new();
|
|
+ if (IS_ERR(kmsg->conn_meta)) {
|
|
+ ret = PTR_ERR(kmsg->conn_meta);
|
|
+ kmsg->conn_meta = NULL;
|
|
goto exit;
|
|
}
|
|
|
|
- return m;
|
|
+ kmsg->msg.size = size - KDBUS_KMSG_HEADER_SIZE;
|
|
+ kmsg->msg.flags = (dst == KDBUS_DST_ID_BROADCAST) ?
|
|
+ KDBUS_MSG_SIGNAL : 0;
|
|
+ kmsg->msg.dst_id = dst;
|
|
+ kmsg->msg.src_id = KDBUS_SRC_ID_KERNEL;
|
|
+ kmsg->msg.payload_type = KDBUS_PAYLOAD_KERNEL;
|
|
+ kmsg->msg.cookie_reply = cookie_timeout;
|
|
+ kmsg->notify = kmsg->msg.items;
|
|
+ kmsg->notify->size = KDBUS_ITEM_HEADER_SIZE + it_size;
|
|
+ kmsg->notify->type = it_type;
|
|
+
|
|
+ return kmsg;
|
|
|
|
exit:
|
|
- kdbus_kmsg_free(m);
|
|
+ kdbus_kmsg_free(kmsg);
|
|
return ERR_PTR(ret);
|
|
}
|
|
|
|
diff --git a/ipc/kdbus/message.h b/ipc/kdbus/message.h
|
|
index 9a11389d9f27..5755ad4c9fd9 100644
|
|
--- a/ipc/kdbus/message.h
|
|
+++ b/ipc/kdbus/message.h
|
|
@@ -82,10 +82,7 @@ kdbus_msg_resources_unref(struct kdbus_msg_resources *r);
|
|
/**
|
|
* struct kdbus_kmsg - internal message handling data
|
|
* @seq: Domain-global message sequence number
|
|
- * @notify_type: Short-cut for faster lookup
|
|
- * @notify_old_id: Short-cut for faster lookup
|
|
- * @notify_new_id: Short-cut for faster lookup
|
|
- * @notify_name: Short-cut for faster lookup
|
|
+ * @notify: Short-cut to notify-item for kernel notifications
|
|
* @bloom_filter: Bloom filter to match message properties
|
|
* @notify_entry: List of kernel-generated notifications
|
|
* @iov: Array of iovec, describing the payload to copy
|
|
@@ -98,10 +95,7 @@ kdbus_msg_resources_unref(struct kdbus_msg_resources *r);
|
|
*/
|
|
struct kdbus_kmsg {
|
|
u64 seq;
|
|
- u64 notify_type;
|
|
- u64 notify_old_id;
|
|
- u64 notify_new_id;
|
|
- const char *notify_name;
|
|
+ struct kdbus_item *notify;
|
|
|
|
const struct kdbus_bloom_filter *bloom_filter;
|
|
struct list_head notify_entry;
|
|
@@ -121,7 +115,9 @@ struct kdbus_kmsg {
|
|
struct kdbus_bus;
|
|
struct kdbus_conn;
|
|
|
|
-struct kdbus_kmsg *kdbus_kmsg_new(struct kdbus_bus *bus, size_t extra_size);
|
|
+struct kdbus_kmsg *kdbus_kmsg_new_kernel(struct kdbus_bus *bus,
|
|
+ u64 dst, u64 cookie_timeout,
|
|
+ size_t it_size, size_t it_type);
|
|
struct kdbus_kmsg *kdbus_kmsg_new_from_cmd(struct kdbus_conn *conn,
|
|
struct kdbus_cmd_send *cmd_send);
|
|
void kdbus_kmsg_free(struct kdbus_kmsg *kmsg);
|
|
diff --git a/ipc/kdbus/notify.c b/ipc/kdbus/notify.c
|
|
index bcf7555e386a..a07d242d0470 100644
|
|
--- a/ipc/kdbus/notify.c
|
|
+++ b/ipc/kdbus/notify.c
|
|
@@ -39,29 +39,13 @@ static inline void kdbus_notify_add_tail(struct kdbus_kmsg *kmsg,
|
|
static int kdbus_notify_reply(struct kdbus_bus *bus, u64 id,
|
|
u64 cookie, u64 msg_type)
|
|
{
|
|
- struct kdbus_kmsg *kmsg = NULL;
|
|
+ struct kdbus_kmsg *kmsg;
|
|
|
|
- WARN_ON(id == 0);
|
|
-
|
|
- kmsg = kdbus_kmsg_new(bus, 0);
|
|
+ kmsg = kdbus_kmsg_new_kernel(bus, id, cookie, 0, msg_type);
|
|
if (IS_ERR(kmsg))
|
|
return PTR_ERR(kmsg);
|
|
|
|
- /*
|
|
- * a kernel-generated notification can only contain one
|
|
- * struct kdbus_item, so make a shortcut here for
|
|
- * faster lookup in the match db.
|
|
- */
|
|
- kmsg->notify_type = msg_type;
|
|
- kmsg->msg.flags = KDBUS_MSG_SIGNAL;
|
|
- kmsg->msg.dst_id = id;
|
|
- kmsg->msg.src_id = KDBUS_SRC_ID_KERNEL;
|
|
- kmsg->msg.payload_type = KDBUS_PAYLOAD_KERNEL;
|
|
- kmsg->msg.cookie_reply = cookie;
|
|
- kmsg->msg.items[0].type = msg_type;
|
|
-
|
|
kdbus_notify_add_tail(kmsg, bus);
|
|
-
|
|
return 0;
|
|
}
|
|
|
|
@@ -115,32 +99,24 @@ int kdbus_notify_name_change(struct kdbus_bus *bus, u64 type,
|
|
u64 old_flags, u64 new_flags,
|
|
const char *name)
|
|
{
|
|
- struct kdbus_kmsg *kmsg = NULL;
|
|
size_t name_len, extra_size;
|
|
+ struct kdbus_kmsg *kmsg;
|
|
|
|
name_len = strlen(name) + 1;
|
|
extra_size = sizeof(struct kdbus_notify_name_change) + name_len;
|
|
- kmsg = kdbus_kmsg_new(bus, extra_size);
|
|
+
|
|
+ kmsg = kdbus_kmsg_new_kernel(bus, KDBUS_DST_ID_BROADCAST, 0,
|
|
+ extra_size, type);
|
|
if (IS_ERR(kmsg))
|
|
return PTR_ERR(kmsg);
|
|
|
|
- kmsg->msg.flags = KDBUS_MSG_SIGNAL;
|
|
- kmsg->msg.dst_id = KDBUS_DST_ID_BROADCAST;
|
|
- kmsg->msg.src_id = KDBUS_SRC_ID_KERNEL;
|
|
- kmsg->msg.payload_type = KDBUS_PAYLOAD_KERNEL;
|
|
- kmsg->notify_type = type;
|
|
- kmsg->notify_old_id = old_id;
|
|
- kmsg->notify_new_id = new_id;
|
|
- kmsg->msg.items[0].type = type;
|
|
- kmsg->msg.items[0].name_change.old_id.id = old_id;
|
|
- kmsg->msg.items[0].name_change.old_id.flags = old_flags;
|
|
- kmsg->msg.items[0].name_change.new_id.id = new_id;
|
|
- kmsg->msg.items[0].name_change.new_id.flags = new_flags;
|
|
- memcpy(kmsg->msg.items[0].name_change.name, name, name_len);
|
|
- kmsg->notify_name = kmsg->msg.items[0].name_change.name;
|
|
+ kmsg->notify->name_change.old_id.id = old_id;
|
|
+ kmsg->notify->name_change.old_id.flags = old_flags;
|
|
+ kmsg->notify->name_change.new_id.id = new_id;
|
|
+ kmsg->notify->name_change.new_id.flags = new_flags;
|
|
+ memcpy(kmsg->notify->name_change.name, name, name_len);
|
|
|
|
kdbus_notify_add_tail(kmsg, bus);
|
|
-
|
|
return 0;
|
|
}
|
|
|
|
@@ -156,37 +132,19 @@ int kdbus_notify_name_change(struct kdbus_bus *bus, u64 type,
|
|
*/
|
|
int kdbus_notify_id_change(struct kdbus_bus *bus, u64 type, u64 id, u64 flags)
|
|
{
|
|
- struct kdbus_kmsg *kmsg = NULL;
|
|
+ struct kdbus_kmsg *kmsg;
|
|
+ size_t extra_size;
|
|
|
|
- kmsg = kdbus_kmsg_new(bus, sizeof(struct kdbus_notify_id_change));
|
|
+ extra_size = sizeof(struct kdbus_notify_id_change);
|
|
+ kmsg = kdbus_kmsg_new_kernel(bus, KDBUS_DST_ID_BROADCAST, 0,
|
|
+ extra_size, type);
|
|
if (IS_ERR(kmsg))
|
|
return PTR_ERR(kmsg);
|
|
|
|
- kmsg->msg.flags = KDBUS_MSG_SIGNAL;
|
|
- kmsg->msg.dst_id = KDBUS_DST_ID_BROADCAST;
|
|
- kmsg->msg.src_id = KDBUS_SRC_ID_KERNEL;
|
|
- kmsg->msg.payload_type = KDBUS_PAYLOAD_KERNEL;
|
|
- kmsg->notify_type = type;
|
|
-
|
|
- switch (type) {
|
|
- case KDBUS_ITEM_ID_ADD:
|
|
- kmsg->notify_new_id = id;
|
|
- break;
|
|
-
|
|
- case KDBUS_ITEM_ID_REMOVE:
|
|
- kmsg->notify_old_id = id;
|
|
- break;
|
|
-
|
|
- default:
|
|
- BUG();
|
|
- }
|
|
-
|
|
- kmsg->msg.items[0].type = type;
|
|
- kmsg->msg.items[0].id_change.id = id;
|
|
- kmsg->msg.items[0].id_change.flags = flags;
|
|
+ kmsg->notify->id_change.id = id;
|
|
+ kmsg->notify->id_change.flags = flags;
|
|
|
|
kdbus_notify_add_tail(kmsg, bus);
|
|
-
|
|
return 0;
|
|
}
|
|
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 2e3c15be5c1f48a9082b47a5cf825dc7c9592b12 Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Fri, 19 Jun 2015 23:56:19 +0200
|
|
Subject: [PATCH 23/34] kdbus: rewrite message importer
|
|
|
|
This is a complete rewrite of the message importer (currently known as
|
|
'kmsg'). I did some benchmarks on the old code, and our many calls into
|
|
kdbus_pool_slice_copy_iovec/kvec() are by far the biggest CPU consumers.
|
|
As it turns out, we can reduce the number of calls into that function to
|
|
*one* (and one additional call per FD-set transmitted). Compared to our
|
|
10x (?) calls we do right now, this reduces the time spent on a message
|
|
transaction significantly.
|
|
|
|
The new message importer uses a new structure called "struct
|
|
kdbus_staging". This structure is solely used to import the "kdbus_msg"
|
|
object into the kernel, parse it, create some helper data and then use it
|
|
to allocate messages in each receivers pool. The staging object thus is
|
|
only used at SEND time and its sole purpose is to prepare for the real
|
|
message we allocate (as part of the queue_entry). Hence, the rename to
|
|
kdbus_staging, instead of kdbus_kmsg (which was really misleading).
|
|
|
|
Furthermore, this patch replaces "struct kdbus_msg_resources". The old
|
|
resources contained far more data than was really necessary. There is no
|
|
reason to pin VECs until RECV time. This can all be dropped. All that
|
|
needs to be pinned is memfds and fds.
|
|
|
|
This patch only introduces the new infrastructure. It is not hooked up,
|
|
yet. Follow-up patches will make sure it's actually used.
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
---
|
|
ipc/kdbus/message.c | 990 ++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
ipc/kdbus/message.h | 93 +++++
|
|
2 files changed, 1083 insertions(+)
|
|
|
|
diff --git a/ipc/kdbus/message.c b/ipc/kdbus/message.c
|
|
index 7f23e5c857dd..f1ec69f3697f 100644
|
|
--- a/ipc/kdbus/message.c
|
|
+++ b/ipc/kdbus/message.c
|
|
@@ -638,3 +638,993 @@ int kdbus_kmsg_collect_metadata(const struct kdbus_kmsg *kmsg,
|
|
|
|
return kdbus_meta_conn_collect(kmsg->conn_meta, src, kmsg->seq, attach);
|
|
}
|
|
+
|
|
+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;
|
|
+ struct inode *inode;
|
|
+ struct socket *sock;
|
|
+
|
|
+ if (fd < 0)
|
|
+ return ERR_PTR(-EBADF);
|
|
+
|
|
+ f = fget_raw(fd);
|
|
+ if (!f)
|
|
+ return ERR_PTR(-EBADF);
|
|
+
|
|
+ inode = file_inode(f);
|
|
+ sock = S_ISSOCK(inode->i_mode) ? SOCKET_I(inode) : NULL;
|
|
+
|
|
+ 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 if (sock && sock->sk && sock->ops && sock->ops->family == PF_UNIX)
|
|
+ ret = ERR_PTR(-EOPNOTSUPP); /* disallow UDS 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->domain->last_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_calc_attach_flags(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, msg_size, meta_size;
|
|
+ struct iovec *v;
|
|
+ u64 attach;
|
|
+ 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 = &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 = 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;
|
|
+ }
|
|
+
|
|
+ /* ... 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
|
|
index 5755ad4c9fd9..8fe49a961834 100644
|
|
--- a/ipc/kdbus/message.h
|
|
+++ b/ipc/kdbus/message.h
|
|
@@ -124,4 +124,97 @@ void kdbus_kmsg_free(struct kdbus_kmsg *kmsg);
|
|
int kdbus_kmsg_collect_metadata(const struct kdbus_kmsg *kmsg,
|
|
struct kdbus_conn *src, struct kdbus_conn *dst);
|
|
|
|
+/**
|
|
+ * 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
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 5eb3ea76d55f52ff6f5abdb88f925e2a695c2732 Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Sat, 20 Jun 2015 00:53:58 +0200
|
|
Subject: [PATCH 24/34] kdbus: remove unused code
|
|
|
|
kdbus_item_get() and kdbus_item_get_str() are not used anywhere. Drop
|
|
them. They probably got obsoleted during the introduction of kdbus_args.
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
---
|
|
ipc/kdbus/item.c | 41 -----------------------------------------
|
|
ipc/kdbus/item.h | 3 ---
|
|
2 files changed, 44 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/item.c b/ipc/kdbus/item.c
|
|
index 1ee72c2ad7c3..e763083cc16c 100644
|
|
--- a/ipc/kdbus/item.c
|
|
+++ b/ipc/kdbus/item.c
|
|
@@ -260,47 +260,6 @@ int kdbus_items_validate(const struct kdbus_item *items, size_t items_size)
|
|
return 0;
|
|
}
|
|
|
|
-static struct kdbus_item *kdbus_items_get(const struct kdbus_item *items,
|
|
- size_t items_size,
|
|
- unsigned int item_type)
|
|
-{
|
|
- const struct kdbus_item *iter, *found = NULL;
|
|
-
|
|
- KDBUS_ITEMS_FOREACH(iter, items, items_size) {
|
|
- if (iter->type == item_type) {
|
|
- if (found)
|
|
- return ERR_PTR(-EEXIST);
|
|
- found = iter;
|
|
- }
|
|
- }
|
|
-
|
|
- return (struct kdbus_item *)found ? : ERR_PTR(-EBADMSG);
|
|
-}
|
|
-
|
|
-/**
|
|
- * kdbus_items_get_str() - get string from a list of items
|
|
- * @items: The items to walk
|
|
- * @items_size: The size of all items
|
|
- * @item_type: The item type to look for
|
|
- *
|
|
- * This function walks a list of items and searches for items of type
|
|
- * @item_type. If it finds exactly one such item, @str_ret will be set to
|
|
- * the .str member of the item.
|
|
- *
|
|
- * Return: the string, if the item was found exactly once, ERR_PTR(-EEXIST)
|
|
- * if the item was found more than once, and ERR_PTR(-EBADMSG) if there was
|
|
- * no item of the given type.
|
|
- */
|
|
-const char *kdbus_items_get_str(const struct kdbus_item *items,
|
|
- size_t items_size,
|
|
- unsigned int item_type)
|
|
-{
|
|
- const struct kdbus_item *item;
|
|
-
|
|
- item = kdbus_items_get(items, items_size, item_type);
|
|
- return IS_ERR(item) ? ERR_CAST(item) : item->str;
|
|
-}
|
|
-
|
|
/**
|
|
* kdbus_item_set() - Set item content
|
|
* @item: The item to modify
|
|
diff --git a/ipc/kdbus/item.h b/ipc/kdbus/item.h
|
|
index bca63b4e6e80..3a7e6ccc253c 100644
|
|
--- a/ipc/kdbus/item.h
|
|
+++ b/ipc/kdbus/item.h
|
|
@@ -55,9 +55,6 @@ struct kdbus_item_header {
|
|
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);
|
|
-const char *kdbus_items_get_str(const struct kdbus_item *items,
|
|
- size_t items_size,
|
|
- unsigned int item_type);
|
|
struct kdbus_item *kdbus_item_set(struct kdbus_item *item, u64 type,
|
|
const void *data, size_t len);
|
|
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 992775c9c377ca14c6dcd2890283602a3fb36213 Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Sat, 20 Jun 2015 11:37:58 +0200
|
|
Subject: [PATCH 25/34] kdbus: import messages via kdbus_args
|
|
|
|
The kdbus_args helpers take care of copying command payloads into
|
|
kernel-space, verifying item validity and implementing negotiation. So
|
|
far, we use it for all user-space interaction but messages. This patch
|
|
adds support for kdbus-messages via kdbus_args so all user-space
|
|
interaction will now use kdbus_args.
|
|
|
|
This has the benefit that we now support negotiation for messages,
|
|
stack-placement in case the messages are small enough, and unify the item
|
|
handling across all callers.
|
|
|
|
This patch does *not* remove the old message importer. Instead, follow-up
|
|
patches will slowly perform the transition to the new one. It will be
|
|
removed once the transition is done.
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
---
|
|
ipc/kdbus/connection.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++---
|
|
ipc/kdbus/handle.c | 15 +++++++++----
|
|
ipc/kdbus/handle.h | 17 ++++++++++++--
|
|
3 files changed, 84 insertions(+), 9 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/connection.c b/ipc/kdbus/connection.c
|
|
index 29b99c023bfe..20bb083aa2b4 100644
|
|
--- a/ipc/kdbus/connection.c
|
|
+++ b/ipc/kdbus/connection.c
|
|
@@ -1929,10 +1929,13 @@ exit:
|
|
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_kmsg *kmsg = NULL;
|
|
+ struct kdbus_msg *msg = NULL;
|
|
struct file *cancel_fd = NULL;
|
|
- int ret;
|
|
+ int ret, ret2;
|
|
|
|
+ /* command arguments */
|
|
struct kdbus_arg argv[] = {
|
|
{ .type = KDBUS_ITEM_NEGOTIATE },
|
|
{ .type = KDBUS_ITEM_CANCEL_FD },
|
|
@@ -1944,12 +1947,48 @@ int kdbus_cmd_send(struct kdbus_conn *conn, struct file *f, void __user *argp)
|
|
.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)
|
|
- return ret;
|
|
+ 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,
|
|
@@ -1968,6 +2007,20 @@ int kdbus_cmd_send(struct kdbus_conn *conn, struct file *f, void __user *argp)
|
|
}
|
|
}
|
|
|
|
+ /* 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;
|
|
+ }
|
|
+
|
|
kmsg = kdbus_kmsg_new_from_cmd(conn, cmd);
|
|
if (IS_ERR(kmsg)) {
|
|
ret = PTR_ERR(kmsg);
|
|
@@ -2012,6 +2065,8 @@ exit:
|
|
if (cancel_fd)
|
|
fput(cancel_fd);
|
|
kdbus_kmsg_free(kmsg);
|
|
+ kdbus_staging_free(staging);
|
|
+ ret = kdbus_args_clear(&msg_args, ret);
|
|
return kdbus_args_clear(&args, ret);
|
|
}
|
|
|
|
diff --git a/ipc/kdbus/handle.c b/ipc/kdbus/handle.c
|
|
index 07527990a051..e0e06b0e1114 100644
|
|
--- a/ipc/kdbus/handle.c
|
|
+++ b/ipc/kdbus/handle.c
|
|
@@ -126,6 +126,7 @@ static int kdbus_args_negotiate(struct kdbus_args *args)
|
|
/**
|
|
* __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
|
|
@@ -140,10 +141,14 @@ static int kdbus_args_negotiate(struct kdbus_args *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, void __user *argp,
|
|
+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;
|
|
@@ -173,10 +178,12 @@ int __kdbus_args_parse(struct kdbus_args *args, void __user *argp,
|
|
goto error;
|
|
}
|
|
|
|
- args->cmd->return_flags = 0;
|
|
+ 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;
|
|
@@ -225,8 +232,8 @@ int kdbus_args_clear(struct kdbus_args *args, int ret)
|
|
return ret;
|
|
|
|
if (!IS_ERR_OR_NULL(args->cmd)) {
|
|
- if (put_user(args->cmd->return_flags,
|
|
- &args->user->return_flags))
|
|
+ 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);
|
|
diff --git a/ipc/kdbus/handle.h b/ipc/kdbus/handle.h
|
|
index 13c59d975728..8a36c0595091 100644
|
|
--- a/ipc/kdbus/handle.h
|
|
+++ b/ipc/kdbus/handle.h
|
|
@@ -48,6 +48,7 @@ struct kdbus_arg {
|
|
* @cmd_buf: 512 bytes 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
|
|
@@ -68,9 +69,10 @@ struct kdbus_args {
|
|
|
|
struct kdbus_item *items;
|
|
size_t items_size;
|
|
+ bool is_cmd : 1;
|
|
};
|
|
|
|
-int __kdbus_args_parse(struct kdbus_args *args, void __user *argp,
|
|
+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);
|
|
|
|
@@ -82,7 +84,18 @@ int kdbus_args_clear(struct kdbus_args *args, int ret);
|
|
offsetof(struct kdbus_cmd, flags)); \
|
|
BUILD_BUG_ON(offsetof(typeof(**(_v)), return_flags) != \
|
|
offsetof(struct kdbus_cmd, return_flags)); \
|
|
- __kdbus_args_parse((_args), (_argp), sizeof(**(_v)), \
|
|
+ __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)); \
|
|
})
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From c7bd78731b3a709a6d7d83f4851e30dc1ebf5cca Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Wed, 24 Jun 2015 15:27:03 +0200
|
|
Subject: [PATCH 26/34] kdbus: switch to kdbus_staging
|
|
|
|
This switches the existing infrastructure to use "kdbus_staging" in favor
|
|
of "kdbus_kmsg" and "kdbus_msg_resources". As described during the
|
|
introduction of kdbus_staging, it reduces the number of calls into shmem
|
|
during each message, and also minimizes the resources we pin until
|
|
RECV-time.
|
|
|
|
This patch improves the pure message transaction speed by ~30% for empty
|
|
payloads.
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
---
|
|
ipc/kdbus/bus.c | 30 ++--
|
|
ipc/kdbus/bus.h | 6 +-
|
|
ipc/kdbus/connection.c | 134 ++++++++----------
|
|
ipc/kdbus/connection.h | 4 +-
|
|
ipc/kdbus/match.c | 26 ++--
|
|
ipc/kdbus/match.h | 8 +-
|
|
ipc/kdbus/message.h | 6 +
|
|
ipc/kdbus/notify.c | 77 +++++------
|
|
ipc/kdbus/queue.c | 362 +++----------------------------------------------
|
|
ipc/kdbus/queue.h | 42 +++---
|
|
10 files changed, 176 insertions(+), 519 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/bus.c b/ipc/kdbus/bus.c
|
|
index 4f51723f8780..e7e17a7f7edd 100644
|
|
--- a/ipc/kdbus/bus.c
|
|
+++ b/ipc/kdbus/bus.c
|
|
@@ -232,9 +232,9 @@ struct kdbus_conn *kdbus_bus_find_conn_by_id(struct kdbus_bus *bus, u64 id)
|
|
* 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
|
|
- * @kmsg: The message to send.
|
|
+ * @staging: Staging object containing the message to send
|
|
*
|
|
- * Send @kmsg to all connections that are currently active on the bus.
|
|
+ * 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.
|
|
*
|
|
@@ -242,7 +242,7 @@ 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_kmsg *kmsg)
|
|
+ struct kdbus_staging *staging)
|
|
{
|
|
struct kdbus_conn *conn_dst;
|
|
unsigned int i;
|
|
@@ -259,11 +259,11 @@ void kdbus_bus_broadcast(struct kdbus_bus *bus,
|
|
* 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, kmsg);
|
|
+ kdbus_bus_eavesdrop(bus, conn_src, staging);
|
|
|
|
down_read(&bus->conn_rwlock);
|
|
hash_for_each(bus->conn_hash, i, conn_dst, hentry) {
|
|
- if (conn_dst->id == kmsg->msg.src_id)
|
|
+ if (conn_dst->id == staging->msg->src_id)
|
|
continue;
|
|
if (!kdbus_conn_is_ordinary(conn_dst))
|
|
continue;
|
|
@@ -272,8 +272,8 @@ void kdbus_bus_broadcast(struct kdbus_bus *bus,
|
|
* Check if there is a match for the kmsg object in
|
|
* the destination connection match db
|
|
*/
|
|
- if (!kdbus_match_db_match_kmsg(conn_dst->match_db, conn_src,
|
|
- kmsg))
|
|
+ if (!kdbus_match_db_match_msg(conn_dst->match_db, conn_src,
|
|
+ staging))
|
|
continue;
|
|
|
|
if (conn_src) {
|
|
@@ -291,12 +291,12 @@ void kdbus_bus_broadcast(struct kdbus_bus *bus,
|
|
* notification
|
|
*/
|
|
if (!kdbus_conn_policy_see_notification(conn_dst, NULL,
|
|
- &kmsg->msg))
|
|
+ staging->msg))
|
|
continue;
|
|
}
|
|
|
|
- ret = kdbus_conn_entry_insert(conn_src, conn_dst, kmsg, NULL,
|
|
- NULL);
|
|
+ ret = kdbus_conn_entry_insert(conn_src, conn_dst, staging,
|
|
+ NULL, NULL);
|
|
if (ret < 0)
|
|
kdbus_conn_lost_message(conn_dst);
|
|
}
|
|
@@ -307,16 +307,16 @@ void kdbus_bus_broadcast(struct kdbus_bus *bus,
|
|
* 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
|
|
- * @kmsg: The message to send.
|
|
+ * @staging: Staging object containing the message to send
|
|
*
|
|
- * Send @kmsg to all monitors that are currently active on the bus. Monitors
|
|
+ * 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_kmsg *kmsg)
|
|
+ struct kdbus_staging *staging)
|
|
{
|
|
struct kdbus_conn *conn_dst;
|
|
int ret;
|
|
@@ -330,8 +330,8 @@ void kdbus_bus_eavesdrop(struct kdbus_bus *bus,
|
|
|
|
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, kmsg, NULL,
|
|
- NULL);
|
|
+ ret = kdbus_conn_entry_insert(conn_src, conn_dst, staging,
|
|
+ NULL, NULL);
|
|
if (ret < 0)
|
|
kdbus_conn_lost_message(conn_dst);
|
|
}
|
|
diff --git a/ipc/kdbus/bus.h b/ipc/kdbus/bus.h
|
|
index e019ef34534c..238986eff92f 100644
|
|
--- a/ipc/kdbus/bus.h
|
|
+++ b/ipc/kdbus/bus.h
|
|
@@ -29,7 +29,7 @@
|
|
|
|
struct kdbus_conn;
|
|
struct kdbus_domain;
|
|
-struct kdbus_kmsg;
|
|
+struct kdbus_staging;
|
|
struct kdbus_user;
|
|
|
|
/**
|
|
@@ -87,10 +87,10 @@ 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_kmsg *kmsg);
|
|
+ struct kdbus_staging *staging);
|
|
void kdbus_bus_eavesdrop(struct kdbus_bus *bus,
|
|
struct kdbus_conn *conn_src,
|
|
- struct kdbus_kmsg *kmsg);
|
|
+ struct kdbus_staging *staging);
|
|
|
|
struct kdbus_bus *kdbus_cmd_bus_make(struct kdbus_domain *domain,
|
|
void __user *argp);
|
|
diff --git a/ipc/kdbus/connection.c b/ipc/kdbus/connection.c
|
|
index 20bb083aa2b4..2b334b04dd9d 100644
|
|
--- a/ipc/kdbus/connection.c
|
|
+++ b/ipc/kdbus/connection.c
|
|
@@ -781,7 +781,7 @@ void kdbus_conn_lost_message(struct kdbus_conn *c)
|
|
static struct kdbus_queue_entry *
|
|
kdbus_conn_entry_make(struct kdbus_conn *conn_src,
|
|
struct kdbus_conn *conn_dst,
|
|
- const struct kdbus_kmsg *kmsg)
|
|
+ struct kdbus_staging *staging)
|
|
{
|
|
/* The remote connection was disconnected */
|
|
if (!kdbus_conn_active(conn_dst))
|
|
@@ -796,10 +796,10 @@ kdbus_conn_entry_make(struct kdbus_conn *conn_src,
|
|
*/
|
|
if (!kdbus_conn_is_monitor(conn_dst) &&
|
|
!(conn_dst->flags & KDBUS_HELLO_ACCEPT_FD) &&
|
|
- kmsg->res && kmsg->res->fds_count > 0)
|
|
+ staging->gaps && staging->gaps->n_fds > 0)
|
|
return ERR_PTR(-ECOMM);
|
|
|
|
- return kdbus_queue_entry_new(conn_src, conn_dst, kmsg);
|
|
+ return kdbus_queue_entry_new(conn_src, conn_dst, staging);
|
|
}
|
|
|
|
/*
|
|
@@ -808,17 +808,11 @@ kdbus_conn_entry_make(struct kdbus_conn *conn_src,
|
|
* The connection's queue will never get to see it.
|
|
*/
|
|
static int kdbus_conn_entry_sync_attach(struct kdbus_conn *conn_dst,
|
|
- const struct kdbus_kmsg *kmsg,
|
|
+ struct kdbus_staging *staging,
|
|
struct kdbus_reply *reply_wake)
|
|
{
|
|
struct kdbus_queue_entry *entry;
|
|
- int remote_ret;
|
|
- int ret;
|
|
-
|
|
- ret = kdbus_kmsg_collect_metadata(kmsg, reply_wake->reply_src,
|
|
- conn_dst);
|
|
- if (ret < 0)
|
|
- return ret;
|
|
+ int remote_ret, ret = 0;
|
|
|
|
mutex_lock(&reply_wake->reply_dst->lock);
|
|
|
|
@@ -828,7 +822,7 @@ static int kdbus_conn_entry_sync_attach(struct kdbus_conn *conn_dst,
|
|
*/
|
|
if (reply_wake->waiting) {
|
|
entry = kdbus_conn_entry_make(reply_wake->reply_src, conn_dst,
|
|
- kmsg);
|
|
+ staging);
|
|
if (IS_ERR(entry))
|
|
ret = PTR_ERR(entry);
|
|
else
|
|
@@ -869,7 +863,7 @@ static int kdbus_conn_entry_sync_attach(struct kdbus_conn *conn_dst,
|
|
* kdbus_conn_entry_insert() - enqueue a message into the receiver's pool
|
|
* @conn_src: The sending connection
|
|
* @conn_dst: The connection to queue into
|
|
- * @kmsg: The kmsg to queue
|
|
+ * @staging: Message to send
|
|
* @reply: The reply tracker to attach to the queue entry
|
|
* @name: Destination name this msg is sent to, or NULL
|
|
*
|
|
@@ -877,22 +871,16 @@ static int kdbus_conn_entry_sync_attach(struct kdbus_conn *conn_dst,
|
|
*/
|
|
int kdbus_conn_entry_insert(struct kdbus_conn *conn_src,
|
|
struct kdbus_conn *conn_dst,
|
|
- const struct kdbus_kmsg *kmsg,
|
|
+ struct kdbus_staging *staging,
|
|
struct kdbus_reply *reply,
|
|
const struct kdbus_name_entry *name)
|
|
{
|
|
struct kdbus_queue_entry *entry;
|
|
int ret;
|
|
|
|
- if (conn_src) {
|
|
- ret = kdbus_kmsg_collect_metadata(kmsg, conn_src, conn_dst);
|
|
- if (ret < 0)
|
|
- return ret;
|
|
- }
|
|
-
|
|
kdbus_conn_lock2(conn_src, conn_dst);
|
|
|
|
- entry = kdbus_conn_entry_make(conn_src, conn_dst, kmsg);
|
|
+ entry = kdbus_conn_entry_make(conn_src, conn_dst, staging);
|
|
if (IS_ERR(entry)) {
|
|
ret = PTR_ERR(entry);
|
|
goto exit_unlock;
|
|
@@ -1044,22 +1032,18 @@ static int kdbus_conn_wait_reply(struct kdbus_conn *conn_src,
|
|
}
|
|
|
|
static int kdbus_pin_dst(struct kdbus_bus *bus,
|
|
- const struct kdbus_kmsg *kmsg,
|
|
+ struct kdbus_staging *staging,
|
|
struct kdbus_name_entry **out_name,
|
|
struct kdbus_conn **out_dst)
|
|
{
|
|
- struct kdbus_msg_resources *res = kmsg->res;
|
|
- const struct kdbus_msg *msg = &kmsg->msg;
|
|
+ const struct kdbus_msg *msg = staging->msg;
|
|
struct kdbus_name_entry *name = NULL;
|
|
struct kdbus_conn *dst = NULL;
|
|
int ret;
|
|
|
|
- if (WARN_ON(!res))
|
|
- return -EINVAL;
|
|
-
|
|
lockdep_assert_held(&bus->name_registry->rwlock);
|
|
|
|
- if (!res->dst_name) {
|
|
+ if (!staging->dst_name) {
|
|
dst = kdbus_bus_find_conn_by_id(bus, msg->dst_id);
|
|
if (!dst)
|
|
return -ENXIO;
|
|
@@ -1070,7 +1054,7 @@ static int kdbus_pin_dst(struct kdbus_bus *bus,
|
|
}
|
|
} else {
|
|
name = kdbus_name_lookup_unlocked(bus->name_registry,
|
|
- res->dst_name);
|
|
+ staging->dst_name);
|
|
if (!name)
|
|
return -ESRCH;
|
|
|
|
@@ -1107,17 +1091,19 @@ error:
|
|
return ret;
|
|
}
|
|
|
|
-static int kdbus_conn_reply(struct kdbus_conn *src, struct kdbus_kmsg *kmsg)
|
|
+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(kmsg->msg.dst_id == KDBUS_DST_ID_BROADCAST) ||
|
|
- WARN_ON(kmsg->msg.flags & KDBUS_MSG_EXPECT_REPLY) ||
|
|
- WARN_ON(kmsg->msg.flags & KDBUS_MSG_SIGNAL))
|
|
+ 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 */
|
|
@@ -1125,12 +1111,12 @@ static int kdbus_conn_reply(struct kdbus_conn *src, struct kdbus_kmsg *kmsg)
|
|
|
|
/* find and pin destination */
|
|
|
|
- ret = kdbus_pin_dst(bus, kmsg, &name, &dst);
|
|
+ ret = kdbus_pin_dst(bus, staging, &name, &dst);
|
|
if (ret < 0)
|
|
goto exit;
|
|
|
|
mutex_lock(&dst->lock);
|
|
- reply = kdbus_reply_find(src, dst, kmsg->msg.cookie_reply);
|
|
+ reply = kdbus_reply_find(src, dst, msg->cookie_reply);
|
|
if (reply) {
|
|
if (reply->sync)
|
|
wake = kdbus_reply_ref(reply);
|
|
@@ -1145,12 +1131,12 @@ static int kdbus_conn_reply(struct kdbus_conn *src, struct kdbus_kmsg *kmsg)
|
|
|
|
/* send message */
|
|
|
|
- kdbus_bus_eavesdrop(bus, src, kmsg);
|
|
+ kdbus_bus_eavesdrop(bus, src, staging);
|
|
|
|
if (wake)
|
|
- ret = kdbus_conn_entry_sync_attach(dst, kmsg, wake);
|
|
+ ret = kdbus_conn_entry_sync_attach(dst, staging, wake);
|
|
else
|
|
- ret = kdbus_conn_entry_insert(src, dst, kmsg, NULL, name);
|
|
+ ret = kdbus_conn_entry_insert(src, dst, staging, NULL, name);
|
|
|
|
exit:
|
|
up_read(&bus->name_registry->rwlock);
|
|
@@ -1160,24 +1146,25 @@ exit:
|
|
}
|
|
|
|
static struct kdbus_reply *kdbus_conn_call(struct kdbus_conn *src,
|
|
- struct kdbus_kmsg *kmsg,
|
|
+ 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(kmsg->msg.dst_id == KDBUS_DST_ID_BROADCAST) ||
|
|
- WARN_ON(kmsg->msg.flags & KDBUS_MSG_SIGNAL) ||
|
|
- WARN_ON(!(kmsg->msg.flags & KDBUS_MSG_EXPECT_REPLY)))
|
|
+ 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, kmsg->msg.cookie);
|
|
+ wait = kdbus_reply_find(NULL, src, msg->cookie);
|
|
if (wait) {
|
|
if (wait->interrupted) {
|
|
kdbus_reply_ref(wait);
|
|
@@ -1199,7 +1186,7 @@ static struct kdbus_reply *kdbus_conn_call(struct kdbus_conn *src,
|
|
|
|
/* find and pin destination */
|
|
|
|
- ret = kdbus_pin_dst(bus, kmsg, &name, &dst);
|
|
+ ret = kdbus_pin_dst(bus, staging, &name, &dst);
|
|
if (ret < 0)
|
|
goto exit;
|
|
|
|
@@ -1208,7 +1195,7 @@ static struct kdbus_reply *kdbus_conn_call(struct kdbus_conn *src,
|
|
goto exit;
|
|
}
|
|
|
|
- wait = kdbus_reply_new(dst, src, &kmsg->msg, name, true);
|
|
+ wait = kdbus_reply_new(dst, src, msg, name, true);
|
|
if (IS_ERR(wait)) {
|
|
ret = PTR_ERR(wait);
|
|
wait = NULL;
|
|
@@ -1217,9 +1204,9 @@ static struct kdbus_reply *kdbus_conn_call(struct kdbus_conn *src,
|
|
|
|
/* send message */
|
|
|
|
- kdbus_bus_eavesdrop(bus, src, kmsg);
|
|
+ kdbus_bus_eavesdrop(bus, src, staging);
|
|
|
|
- ret = kdbus_conn_entry_insert(src, dst, kmsg, wait, name);
|
|
+ ret = kdbus_conn_entry_insert(src, dst, staging, wait, name);
|
|
if (ret < 0)
|
|
goto exit;
|
|
|
|
@@ -1235,18 +1222,20 @@ exit:
|
|
return wait;
|
|
}
|
|
|
|
-static int kdbus_conn_unicast(struct kdbus_conn *src, struct kdbus_kmsg *kmsg)
|
|
+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 = (kmsg->msg.flags & KDBUS_MSG_SIGNAL);
|
|
+ bool is_signal = (msg->flags & KDBUS_MSG_SIGNAL);
|
|
int ret = 0;
|
|
|
|
- if (WARN_ON(kmsg->msg.dst_id == KDBUS_DST_ID_BROADCAST) ||
|
|
- WARN_ON(!(kmsg->msg.flags & KDBUS_MSG_EXPECT_REPLY) &&
|
|
- kmsg->msg.cookie_reply != 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 */
|
|
@@ -1254,23 +1243,23 @@ static int kdbus_conn_unicast(struct kdbus_conn *src, struct kdbus_kmsg *kmsg)
|
|
|
|
/* find and pin destination */
|
|
|
|
- ret = kdbus_pin_dst(bus, kmsg, &name, &dst);
|
|
+ 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, kmsg);
|
|
+ kdbus_bus_eavesdrop(bus, src, staging);
|
|
|
|
/* drop silently if peer is not interested or not privileged */
|
|
- if (!kdbus_match_db_match_kmsg(dst->match_db, src, kmsg) ||
|
|
+ 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 (kmsg->msg.flags & KDBUS_MSG_EXPECT_REPLY) {
|
|
- wait = kdbus_reply_new(dst, src, &kmsg->msg, name, false);
|
|
+ } 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;
|
|
@@ -1281,9 +1270,9 @@ static int kdbus_conn_unicast(struct kdbus_conn *src, struct kdbus_kmsg *kmsg)
|
|
/* send message */
|
|
|
|
if (!is_signal)
|
|
- kdbus_bus_eavesdrop(bus, src, kmsg);
|
|
+ kdbus_bus_eavesdrop(bus, src, staging);
|
|
|
|
- ret = kdbus_conn_entry_insert(src, dst, kmsg, wait, name);
|
|
+ ret = kdbus_conn_entry_insert(src, dst, staging, wait, name);
|
|
if (ret < 0 && !is_signal)
|
|
goto exit;
|
|
|
|
@@ -1353,7 +1342,7 @@ void kdbus_conn_move_messages(struct kdbus_conn *conn_dst,
|
|
continue;
|
|
|
|
if (!(conn_dst->flags & KDBUS_HELLO_ACCEPT_FD) &&
|
|
- e->msg_res && e->msg_res->fds_count > 0) {
|
|
+ e->gaps && e->gaps->n_fds > 0) {
|
|
kdbus_conn_lost_message(conn_dst);
|
|
kdbus_queue_entry_free(e);
|
|
continue;
|
|
@@ -1930,7 +1919,6 @@ 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_kmsg *kmsg = NULL;
|
|
struct kdbus_msg *msg = NULL;
|
|
struct file *cancel_fd = NULL;
|
|
int ret, ret2;
|
|
@@ -2021,23 +2009,16 @@ int kdbus_cmd_send(struct kdbus_conn *conn, struct file *f, void __user *argp)
|
|
goto exit;
|
|
}
|
|
|
|
- kmsg = kdbus_kmsg_new_from_cmd(conn, cmd);
|
|
- if (IS_ERR(kmsg)) {
|
|
- ret = PTR_ERR(kmsg);
|
|
- kmsg = NULL;
|
|
- goto exit;
|
|
- }
|
|
-
|
|
- if (kmsg->msg.dst_id == KDBUS_DST_ID_BROADCAST) {
|
|
+ if (msg->dst_id == KDBUS_DST_ID_BROADCAST) {
|
|
down_read(&conn->ep->bus->name_registry->rwlock);
|
|
- kdbus_bus_broadcast(conn->ep->bus, conn, kmsg);
|
|
+ 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(kmsg->msg.timeout_ns);
|
|
- r = kdbus_conn_call(conn, kmsg, 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;
|
|
@@ -2047,13 +2028,13 @@ int kdbus_cmd_send(struct kdbus_conn *conn, struct file *f, void __user *argp)
|
|
kdbus_reply_unref(r);
|
|
if (ret < 0)
|
|
goto exit;
|
|
- } else if ((kmsg->msg.flags & KDBUS_MSG_EXPECT_REPLY) ||
|
|
- kmsg->msg.cookie_reply == 0) {
|
|
- ret = kdbus_conn_unicast(conn, kmsg);
|
|
+ } 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, kmsg);
|
|
+ ret = kdbus_conn_reply(conn, staging);
|
|
if (ret < 0)
|
|
goto exit;
|
|
}
|
|
@@ -2064,7 +2045,6 @@ int kdbus_cmd_send(struct kdbus_conn *conn, struct file *f, void __user *argp)
|
|
exit:
|
|
if (cancel_fd)
|
|
fput(cancel_fd);
|
|
- kdbus_kmsg_free(kmsg);
|
|
kdbus_staging_free(staging);
|
|
ret = kdbus_args_clear(&msg_args, ret);
|
|
return kdbus_args_clear(&args, ret);
|
|
diff --git a/ipc/kdbus/connection.h b/ipc/kdbus/connection.h
|
|
index 90c1bcc91fe6..b1769b877504 100644
|
|
--- a/ipc/kdbus/connection.h
|
|
+++ b/ipc/kdbus/connection.h
|
|
@@ -31,7 +31,7 @@
|
|
KDBUS_HELLO_MONITOR)
|
|
|
|
struct kdbus_quota;
|
|
-struct kdbus_kmsg;
|
|
+struct kdbus_staging;
|
|
|
|
/**
|
|
* struct kdbus_conn - connection to a bus
|
|
@@ -134,7 +134,7 @@ void kdbus_conn_quota_dec(struct kdbus_conn *c, struct kdbus_user *u,
|
|
void kdbus_conn_lost_message(struct kdbus_conn *c);
|
|
int kdbus_conn_entry_insert(struct kdbus_conn *conn_src,
|
|
struct kdbus_conn *conn_dst,
|
|
- const struct kdbus_kmsg *kmsg,
|
|
+ struct kdbus_staging *staging,
|
|
struct kdbus_reply *reply,
|
|
const struct kdbus_name_entry *name);
|
|
void kdbus_conn_move_messages(struct kdbus_conn *conn_dst,
|
|
diff --git a/ipc/kdbus/match.c b/ipc/kdbus/match.c
|
|
index 3f86e4c81468..3ae224ff9f3f 100644
|
|
--- a/ipc/kdbus/match.c
|
|
+++ b/ipc/kdbus/match.c
|
|
@@ -206,13 +206,13 @@ static bool kdbus_match_bloom(const struct kdbus_bloom_filter *filter,
|
|
|
|
static bool kdbus_match_rule_conn(const struct kdbus_match_rule *r,
|
|
struct kdbus_conn *c,
|
|
- const struct kdbus_kmsg *kmsg)
|
|
+ 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(kmsg->bloom_filter, &r->bloom_mask, c);
|
|
+ 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_NAME:
|
|
@@ -223,9 +223,9 @@ static bool kdbus_match_rule_conn(const struct kdbus_match_rule *r,
|
|
}
|
|
|
|
static bool kdbus_match_rule_kernel(const struct kdbus_match_rule *r,
|
|
- const struct kdbus_kmsg *kmsg)
|
|
+ const struct kdbus_staging *s)
|
|
{
|
|
- struct kdbus_item *n = kmsg->notify;
|
|
+ struct kdbus_item *n = s->notify;
|
|
|
|
if (WARN_ON(!n) || n->type != r->type)
|
|
return false;
|
|
@@ -252,23 +252,23 @@ static bool kdbus_match_rule_kernel(const struct kdbus_match_rule *r,
|
|
|
|
static bool kdbus_match_rules(const struct kdbus_match_entry *entry,
|
|
struct kdbus_conn *c,
|
|
- const struct kdbus_kmsg *kmsg)
|
|
+ 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, kmsg)) ||
|
|
- (!c && !kdbus_match_rule_kernel(r, kmsg)))
|
|
+ if ((c && !kdbus_match_rule_conn(r, c, s)) ||
|
|
+ (!c && !kdbus_match_rule_kernel(r, s)))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
- * kdbus_match_db_match_kmsg() - match a kmsg object agains the database entries
|
|
+ * 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
|
|
- * @kmsg: The kmsg to perform the match on
|
|
+ * @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
|
|
@@ -279,16 +279,16 @@ static bool kdbus_match_rules(const struct kdbus_match_entry *entry,
|
|
*
|
|
* Return: true if there was a matching database entry, false otherwise.
|
|
*/
|
|
-bool kdbus_match_db_match_kmsg(struct kdbus_match_db *mdb,
|
|
- struct kdbus_conn *conn_src,
|
|
- struct kdbus_kmsg *kmsg)
|
|
+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, kmsg);
|
|
+ matched = kdbus_match_rules(entry, conn_src, staging);
|
|
if (matched)
|
|
break;
|
|
}
|
|
diff --git a/ipc/kdbus/match.h b/ipc/kdbus/match.h
|
|
index ea4292938deb..ceb492f8e51c 100644
|
|
--- a/ipc/kdbus/match.h
|
|
+++ b/ipc/kdbus/match.h
|
|
@@ -16,8 +16,8 @@
|
|
#define __KDBUS_MATCH_H
|
|
|
|
struct kdbus_conn;
|
|
-struct kdbus_kmsg;
|
|
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);
|
|
@@ -25,9 +25,9 @@ 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_kmsg(struct kdbus_match_db *db,
|
|
- struct kdbus_conn *conn_src,
|
|
- struct kdbus_kmsg *kmsg);
|
|
+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);
|
|
diff --git a/ipc/kdbus/message.h b/ipc/kdbus/message.h
|
|
index 8fe49a961834..e0c4f2e324b3 100644
|
|
--- a/ipc/kdbus/message.h
|
|
+++ b/ipc/kdbus/message.h
|
|
@@ -14,6 +14,9 @@
|
|
#ifndef __KDBUS_MESSAGE_H
|
|
#define __KDBUS_MESSAGE_H
|
|
|
|
+#include <linux/fs.h>
|
|
+#include <linux/kref.h>
|
|
+#include <uapi/linux/kdbus.h>
|
|
#include "util.h"
|
|
#include "metadata.h"
|
|
|
|
@@ -114,6 +117,9 @@ struct kdbus_kmsg {
|
|
|
|
struct kdbus_bus;
|
|
struct kdbus_conn;
|
|
+struct kdbus_meta_conn;
|
|
+struct kdbus_meta_proc;
|
|
+struct kdbus_pool_slice;
|
|
|
|
struct kdbus_kmsg *kdbus_kmsg_new_kernel(struct kdbus_bus *bus,
|
|
u64 dst, u64 cookie_timeout,
|
|
diff --git a/ipc/kdbus/notify.c b/ipc/kdbus/notify.c
|
|
index a07d242d0470..375758c4896a 100644
|
|
--- a/ipc/kdbus/notify.c
|
|
+++ b/ipc/kdbus/notify.c
|
|
@@ -28,24 +28,24 @@
|
|
#include "message.h"
|
|
#include "notify.h"
|
|
|
|
-static inline void kdbus_notify_add_tail(struct kdbus_kmsg *kmsg,
|
|
+static inline void kdbus_notify_add_tail(struct kdbus_staging *staging,
|
|
struct kdbus_bus *bus)
|
|
{
|
|
spin_lock(&bus->notify_lock);
|
|
- list_add_tail(&kmsg->notify_entry, &bus->notify_list);
|
|
+ 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_kmsg *kmsg;
|
|
+ struct kdbus_staging *s;
|
|
|
|
- kmsg = kdbus_kmsg_new_kernel(bus, id, cookie, 0, msg_type);
|
|
- if (IS_ERR(kmsg))
|
|
- return PTR_ERR(kmsg);
|
|
+ s = kdbus_staging_new_kernel(bus, id, cookie, 0, msg_type);
|
|
+ if (IS_ERR(s))
|
|
+ return PTR_ERR(s);
|
|
|
|
- kdbus_notify_add_tail(kmsg, bus);
|
|
+ kdbus_notify_add_tail(s, bus);
|
|
return 0;
|
|
}
|
|
|
|
@@ -100,23 +100,23 @@ int kdbus_notify_name_change(struct kdbus_bus *bus, u64 type,
|
|
const char *name)
|
|
{
|
|
size_t name_len, extra_size;
|
|
- struct kdbus_kmsg *kmsg;
|
|
+ struct kdbus_staging *s;
|
|
|
|
name_len = strlen(name) + 1;
|
|
extra_size = sizeof(struct kdbus_notify_name_change) + name_len;
|
|
|
|
- kmsg = kdbus_kmsg_new_kernel(bus, KDBUS_DST_ID_BROADCAST, 0,
|
|
+ s = kdbus_staging_new_kernel(bus, KDBUS_DST_ID_BROADCAST, 0,
|
|
extra_size, type);
|
|
- if (IS_ERR(kmsg))
|
|
- return PTR_ERR(kmsg);
|
|
+ if (IS_ERR(s))
|
|
+ return PTR_ERR(s);
|
|
|
|
- kmsg->notify->name_change.old_id.id = old_id;
|
|
- kmsg->notify->name_change.old_id.flags = old_flags;
|
|
- kmsg->notify->name_change.new_id.id = new_id;
|
|
- kmsg->notify->name_change.new_id.flags = new_flags;
|
|
- memcpy(kmsg->notify->name_change.name, name, name_len);
|
|
+ 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(kmsg, bus);
|
|
+ kdbus_notify_add_tail(s, bus);
|
|
return 0;
|
|
}
|
|
|
|
@@ -132,19 +132,19 @@ int kdbus_notify_name_change(struct kdbus_bus *bus, u64 type,
|
|
*/
|
|
int kdbus_notify_id_change(struct kdbus_bus *bus, u64 type, u64 id, u64 flags)
|
|
{
|
|
- struct kdbus_kmsg *kmsg;
|
|
+ struct kdbus_staging *s;
|
|
size_t extra_size;
|
|
|
|
extra_size = sizeof(struct kdbus_notify_id_change);
|
|
- kmsg = kdbus_kmsg_new_kernel(bus, KDBUS_DST_ID_BROADCAST, 0,
|
|
+ s = kdbus_staging_new_kernel(bus, KDBUS_DST_ID_BROADCAST, 0,
|
|
extra_size, type);
|
|
- if (IS_ERR(kmsg))
|
|
- return PTR_ERR(kmsg);
|
|
+ if (IS_ERR(s))
|
|
+ return PTR_ERR(s);
|
|
|
|
- kmsg->notify->id_change.id = id;
|
|
- kmsg->notify->id_change.flags = flags;
|
|
+ s->notify->id_change.id = id;
|
|
+ s->notify->id_change.flags = flags;
|
|
|
|
- kdbus_notify_add_tail(kmsg, bus);
|
|
+ kdbus_notify_add_tail(s, bus);
|
|
return 0;
|
|
}
|
|
|
|
@@ -157,7 +157,7 @@ int kdbus_notify_id_change(struct kdbus_bus *bus, u64 type, u64 id, u64 flags)
|
|
void kdbus_notify_flush(struct kdbus_bus *bus)
|
|
{
|
|
LIST_HEAD(notify_list);
|
|
- struct kdbus_kmsg *kmsg, *tmp;
|
|
+ struct kdbus_staging *s, *tmp;
|
|
|
|
mutex_lock(&bus->notify_flush_lock);
|
|
down_read(&bus->name_registry->rwlock);
|
|
@@ -166,26 +166,23 @@ void kdbus_notify_flush(struct kdbus_bus *bus)
|
|
list_splice_init(&bus->notify_list, ¬ify_list);
|
|
spin_unlock(&bus->notify_lock);
|
|
|
|
- list_for_each_entry_safe(kmsg, tmp, ¬ify_list, notify_entry) {
|
|
- kdbus_meta_conn_collect(kmsg->conn_meta, NULL, kmsg->seq,
|
|
- KDBUS_ATTACH_TIMESTAMP);
|
|
-
|
|
- if (kmsg->msg.dst_id != KDBUS_DST_ID_BROADCAST) {
|
|
+ list_for_each_entry_safe(s, tmp, ¬ify_list, notify_entry) {
|
|
+ if (s->msg->dst_id != KDBUS_DST_ID_BROADCAST) {
|
|
struct kdbus_conn *conn;
|
|
|
|
- conn = kdbus_bus_find_conn_by_id(bus, kmsg->msg.dst_id);
|
|
+ conn = kdbus_bus_find_conn_by_id(bus, s->msg->dst_id);
|
|
if (conn) {
|
|
- kdbus_bus_eavesdrop(bus, NULL, kmsg);
|
|
- kdbus_conn_entry_insert(NULL, conn, kmsg, NULL,
|
|
+ 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, kmsg);
|
|
+ kdbus_bus_broadcast(bus, NULL, s);
|
|
}
|
|
|
|
- list_del(&kmsg->notify_entry);
|
|
- kdbus_kmsg_free(kmsg);
|
|
+ list_del(&s->notify_entry);
|
|
+ kdbus_staging_free(s);
|
|
}
|
|
|
|
up_read(&bus->name_registry->rwlock);
|
|
@@ -198,10 +195,10 @@ void kdbus_notify_flush(struct kdbus_bus *bus)
|
|
*/
|
|
void kdbus_notify_free(struct kdbus_bus *bus)
|
|
{
|
|
- struct kdbus_kmsg *kmsg, *tmp;
|
|
+ struct kdbus_staging *s, *tmp;
|
|
|
|
- list_for_each_entry_safe(kmsg, tmp, &bus->notify_list, notify_entry) {
|
|
- list_del(&kmsg->notify_entry);
|
|
- kdbus_kmsg_free(kmsg);
|
|
+ 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/queue.c b/ipc/kdbus/queue.c
|
|
index 4749b23a4f14..f9c44d7bae6d 100644
|
|
--- a/ipc/kdbus/queue.c
|
|
+++ b/ipc/kdbus/queue.c
|
|
@@ -171,243 +171,43 @@ static void kdbus_queue_entry_unlink(struct kdbus_queue_entry *entry)
|
|
|
|
/**
|
|
* kdbus_queue_entry_new() - allocate a queue entry
|
|
- * @conn_src: source connection
|
|
- * @conn_dst: destination connection
|
|
- * @kmsg: kmsg object the queue entry should track
|
|
+ * @src: source connection, or NULL
|
|
+ * @dst: destination connection
|
|
+ * @s: staging object carrying the message
|
|
*
|
|
- * Allocates a queue entry based on a given kmsg and allocate space for
|
|
+ * 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 *conn_src,
|
|
- struct kdbus_conn *conn_dst,
|
|
- const struct kdbus_kmsg *kmsg)
|
|
+struct kdbus_queue_entry *kdbus_queue_entry_new(struct kdbus_conn *src,
|
|
+ struct kdbus_conn *dst,
|
|
+ struct kdbus_staging *s)
|
|
{
|
|
- struct kdbus_user *user = conn_src ? conn_src->user : NULL;
|
|
- struct kdbus_msg_resources *res = kmsg->res;
|
|
- const struct kdbus_msg *msg = &kmsg->msg;
|
|
struct kdbus_queue_entry *entry;
|
|
- size_t memfd_cnt = 0;
|
|
- struct kvec kvec[2];
|
|
- size_t meta_size;
|
|
- size_t msg_size;
|
|
- u64 payload_off;
|
|
- u64 size = 0;
|
|
- int ret = 0;
|
|
+ int ret;
|
|
|
|
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
|
|
if (!entry)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
INIT_LIST_HEAD(&entry->entry);
|
|
- entry->priority = msg->priority;
|
|
- entry->msg_res = kdbus_msg_resources_ref(res);
|
|
- entry->proc_meta = kdbus_meta_proc_ref(kmsg->proc_meta);
|
|
- entry->conn_meta = kdbus_meta_conn_ref(kmsg->conn_meta);
|
|
- entry->conn = kdbus_conn_ref(conn_dst);
|
|
-
|
|
- if (kmsg->msg.src_id == KDBUS_SRC_ID_KERNEL)
|
|
- msg_size = msg->size;
|
|
- else
|
|
- msg_size = offsetof(struct kdbus_msg, items);
|
|
-
|
|
- /* sum up the size of the needed slice */
|
|
- size = msg_size;
|
|
-
|
|
- if (res) {
|
|
- size += res->vec_count *
|
|
- KDBUS_ITEM_SIZE(sizeof(struct kdbus_vec));
|
|
-
|
|
- if (res->memfd_count) {
|
|
- entry->memfd_offset =
|
|
- kcalloc(res->memfd_count, sizeof(size_t),
|
|
- GFP_KERNEL);
|
|
- if (!entry->memfd_offset) {
|
|
- ret = -ENOMEM;
|
|
- goto exit_free_entry;
|
|
- }
|
|
-
|
|
- size += res->memfd_count *
|
|
- KDBUS_ITEM_SIZE(sizeof(struct kdbus_memfd));
|
|
- }
|
|
-
|
|
- if (res->fds_count)
|
|
- size += KDBUS_ITEM_SIZE(sizeof(int) * res->fds_count);
|
|
-
|
|
- if (res->dst_name)
|
|
- size += KDBUS_ITEM_SIZE(strlen(res->dst_name) + 1);
|
|
- }
|
|
-
|
|
- /*
|
|
- * Remember the offset of the metadata part, so we can override
|
|
- * this part later during kdbus_queue_entry_install().
|
|
- */
|
|
- entry->meta_offset = size;
|
|
-
|
|
- if (entry->proc_meta || entry->conn_meta) {
|
|
- entry->attach_flags =
|
|
- atomic64_read(&conn_dst->attach_flags_recv);
|
|
-
|
|
- ret = kdbus_meta_export_prepare(entry->proc_meta,
|
|
- NULL,
|
|
- entry->conn_meta,
|
|
- &entry->attach_flags,
|
|
- &meta_size);
|
|
- if (ret < 0)
|
|
- goto exit_free_entry;
|
|
-
|
|
- size += meta_size;
|
|
- }
|
|
+ entry->priority = s->msg->priority;
|
|
+ entry->conn = kdbus_conn_ref(dst);
|
|
+ entry->gaps = kdbus_gaps_ref(s->gaps);
|
|
|
|
- payload_off = size;
|
|
- size += kmsg->pool_size;
|
|
- size = KDBUS_ALIGN8(size);
|
|
-
|
|
- ret = kdbus_conn_quota_inc(conn_dst, user, size,
|
|
- res ? res->fds_count : 0);
|
|
- if (ret < 0)
|
|
- goto exit_free_entry;
|
|
-
|
|
- entry->slice = kdbus_pool_slice_alloc(conn_dst->pool, size, true);
|
|
+ entry->slice = kdbus_staging_emit(s, src, dst);
|
|
if (IS_ERR(entry->slice)) {
|
|
ret = PTR_ERR(entry->slice);
|
|
entry->slice = NULL;
|
|
- kdbus_conn_quota_dec(conn_dst, user, size,
|
|
- res ? res->fds_count : 0);
|
|
- goto exit_free_entry;
|
|
- }
|
|
-
|
|
- /* we accounted for exactly 'size' bytes, make sure it didn't grow */
|
|
- WARN_ON(kdbus_pool_slice_size(entry->slice) != size);
|
|
- entry->user = kdbus_user_ref(user);
|
|
-
|
|
- /* copy message header */
|
|
- kvec[0].iov_base = (char *)msg;
|
|
- kvec[0].iov_len = msg_size;
|
|
-
|
|
- ret = kdbus_pool_slice_copy_kvec(entry->slice, 0, kvec, 1, msg_size);
|
|
- if (ret < 0)
|
|
- goto exit_free_entry;
|
|
-
|
|
- /* 'size' will now track the write position */
|
|
- size = msg_size;
|
|
-
|
|
- /* create message payload items */
|
|
- if (res) {
|
|
- size_t dst_name_len = 0;
|
|
- unsigned int i;
|
|
- size_t sz = 0;
|
|
-
|
|
- if (res->dst_name) {
|
|
- dst_name_len = strlen(res->dst_name) + 1;
|
|
- sz += KDBUS_ITEM_SIZE(dst_name_len);
|
|
- }
|
|
-
|
|
- for (i = 0; i < res->data_count; ++i) {
|
|
- struct kdbus_vec v;
|
|
- struct kdbus_memfd m;
|
|
-
|
|
- switch (res->data[i].type) {
|
|
- case KDBUS_MSG_DATA_VEC:
|
|
- sz += KDBUS_ITEM_SIZE(sizeof(v));
|
|
- break;
|
|
-
|
|
- case KDBUS_MSG_DATA_MEMFD:
|
|
- sz += KDBUS_ITEM_SIZE(sizeof(m));
|
|
- break;
|
|
- }
|
|
- }
|
|
-
|
|
- if (sz) {
|
|
- struct kdbus_item *items, *item;
|
|
-
|
|
- items = kmalloc(sz, GFP_KERNEL);
|
|
- if (!items) {
|
|
- ret = -ENOMEM;
|
|
- goto exit_free_entry;
|
|
- }
|
|
-
|
|
- item = items;
|
|
-
|
|
- if (res->dst_name)
|
|
- item = kdbus_item_set(item, KDBUS_ITEM_DST_NAME,
|
|
- res->dst_name,
|
|
- dst_name_len);
|
|
-
|
|
- for (i = 0; i < res->data_count; ++i) {
|
|
- struct kdbus_msg_data *d = res->data + i;
|
|
- struct kdbus_memfd m = {};
|
|
- struct kdbus_vec v = {};
|
|
-
|
|
- switch (d->type) {
|
|
- case KDBUS_MSG_DATA_VEC:
|
|
- v.size = d->size;
|
|
- v.offset = d->vec.off;
|
|
- if (v.offset != ~0ULL)
|
|
- v.offset += payload_off;
|
|
-
|
|
- item = kdbus_item_set(item,
|
|
- KDBUS_ITEM_PAYLOAD_OFF,
|
|
- &v, sizeof(v));
|
|
- break;
|
|
-
|
|
- case KDBUS_MSG_DATA_MEMFD:
|
|
- /*
|
|
- * Remember the location of memfds, so
|
|
- * we can override the content from
|
|
- * kdbus_queue_entry_install().
|
|
- */
|
|
- entry->memfd_offset[memfd_cnt++] =
|
|
- msg_size +
|
|
- (char *)item - (char *)items +
|
|
- offsetof(struct kdbus_item,
|
|
- memfd);
|
|
-
|
|
- item = kdbus_item_set(item,
|
|
- KDBUS_ITEM_PAYLOAD_MEMFD,
|
|
- &m, sizeof(m));
|
|
- break;
|
|
- }
|
|
- }
|
|
-
|
|
- kvec[0].iov_base = items;
|
|
- kvec[0].iov_len = sz;
|
|
-
|
|
- ret = kdbus_pool_slice_copy_kvec(entry->slice, size,
|
|
- kvec, 1, sz);
|
|
- kfree(items);
|
|
-
|
|
- if (ret < 0)
|
|
- goto exit_free_entry;
|
|
-
|
|
- size += sz;
|
|
- }
|
|
-
|
|
- /*
|
|
- * Remember the location of the FD part, so we can override the
|
|
- * content in kdbus_queue_entry_install().
|
|
- */
|
|
- if (res->fds_count) {
|
|
- entry->fds_offset = size;
|
|
- size += KDBUS_ITEM_SIZE(sizeof(int) * res->fds_count);
|
|
- }
|
|
- }
|
|
-
|
|
- /* finally, copy over the actual message payload */
|
|
- if (kmsg->iov_count) {
|
|
- ret = kdbus_pool_slice_copy_iovec(entry->slice, payload_off,
|
|
- kmsg->iov,
|
|
- kmsg->iov_count,
|
|
- kmsg->pool_size);
|
|
- if (ret < 0)
|
|
- goto exit_free_entry;
|
|
+ goto error;
|
|
}
|
|
|
|
+ entry->user = src ? kdbus_user_ref(src->user) : NULL;
|
|
return entry;
|
|
|
|
-exit_free_entry:
|
|
+error:
|
|
kdbus_queue_entry_free(entry);
|
|
return ERR_PTR(ret);
|
|
}
|
|
@@ -432,17 +232,13 @@ void kdbus_queue_entry_free(struct kdbus_queue_entry *entry)
|
|
if (entry->slice) {
|
|
kdbus_conn_quota_dec(entry->conn, entry->user,
|
|
kdbus_pool_slice_size(entry->slice),
|
|
- entry->msg_res ?
|
|
- entry->msg_res->fds_count : 0);
|
|
+ entry->gaps ? entry->gaps->n_fds : 0);
|
|
kdbus_pool_slice_release(entry->slice);
|
|
- kdbus_user_unref(entry->user);
|
|
}
|
|
|
|
- kdbus_msg_resources_unref(entry->msg_res);
|
|
- kdbus_meta_conn_unref(entry->conn_meta);
|
|
- kdbus_meta_proc_unref(entry->proc_meta);
|
|
+ kdbus_user_unref(entry->user);
|
|
+ kdbus_gaps_unref(entry->gaps);
|
|
kdbus_conn_unref(entry->conn);
|
|
- kfree(entry->memfd_offset);
|
|
kfree(entry);
|
|
}
|
|
|
|
@@ -453,136 +249,22 @@ void kdbus_queue_entry_free(struct kdbus_queue_entry *entry)
|
|
* @return_flags: Pointer to store the return flags for userspace
|
|
* @install_fds: Whether or not to install associated file descriptors
|
|
*
|
|
- * This function will create a slice to transport the message header, the
|
|
- * metadata items and other items for information stored in @entry, and
|
|
- * store it as entry->slice.
|
|
- *
|
|
- * If @install_fds is %true, file descriptors will as well be installed.
|
|
- * This function must always be called from the task context of the receiver.
|
|
- *
|
|
* Return: 0 on success.
|
|
*/
|
|
int kdbus_queue_entry_install(struct kdbus_queue_entry *entry,
|
|
u64 *return_flags, bool install_fds)
|
|
{
|
|
- u64 msg_size = entry->meta_offset;
|
|
- struct kdbus_conn *conn_dst = entry->conn;
|
|
- struct kdbus_msg_resources *res;
|
|
bool incomplete_fds = false;
|
|
- struct kvec kvec[2];
|
|
- size_t memfds = 0;
|
|
- int i, ret;
|
|
-
|
|
- lockdep_assert_held(&conn_dst->lock);
|
|
-
|
|
- if (entry->proc_meta || entry->conn_meta) {
|
|
- size_t meta_size;
|
|
-
|
|
- ret = kdbus_meta_export(entry->proc_meta,
|
|
- NULL,
|
|
- entry->conn_meta,
|
|
- conn_dst,
|
|
- entry->attach_flags,
|
|
- entry->slice,
|
|
- entry->meta_offset,
|
|
- &meta_size);
|
|
- if (ret < 0)
|
|
- return ret;
|
|
-
|
|
- msg_size += meta_size;
|
|
- }
|
|
+ int ret;
|
|
|
|
- /* Update message size at offset 0 */
|
|
- kvec[0].iov_base = &msg_size;
|
|
- kvec[0].iov_len = sizeof(msg_size);
|
|
+ lockdep_assert_held(&entry->conn->lock);
|
|
|
|
- ret = kdbus_pool_slice_copy_kvec(entry->slice, 0, kvec, 1,
|
|
- sizeof(msg_size));
|
|
+ ret = kdbus_gaps_install(entry->gaps, entry->slice, &incomplete_fds);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
- res = entry->msg_res;
|
|
-
|
|
- if (!res)
|
|
- return 0;
|
|
-
|
|
- if (res->fds_count) {
|
|
- struct kdbus_item_header hdr;
|
|
- size_t off;
|
|
- int *fds;
|
|
-
|
|
- fds = kmalloc_array(res->fds_count, sizeof(int), GFP_KERNEL);
|
|
- if (!fds)
|
|
- return -ENOMEM;
|
|
-
|
|
- for (i = 0; i < res->fds_count; i++) {
|
|
- if (install_fds) {
|
|
- fds[i] = get_unused_fd_flags(O_CLOEXEC);
|
|
- if (fds[i] >= 0)
|
|
- fd_install(fds[i],
|
|
- get_file(res->fds[i]));
|
|
- else
|
|
- incomplete_fds = true;
|
|
- } else {
|
|
- fds[i] = -1;
|
|
- }
|
|
- }
|
|
-
|
|
- off = entry->fds_offset;
|
|
-
|
|
- hdr.type = KDBUS_ITEM_FDS;
|
|
- hdr.size = KDBUS_ITEM_HEADER_SIZE +
|
|
- sizeof(int) * res->fds_count;
|
|
-
|
|
- kvec[0].iov_base = &hdr;
|
|
- kvec[0].iov_len = sizeof(hdr);
|
|
-
|
|
- kvec[1].iov_base = fds;
|
|
- kvec[1].iov_len = sizeof(int) * res->fds_count;
|
|
-
|
|
- ret = kdbus_pool_slice_copy_kvec(entry->slice, off,
|
|
- kvec, 2, hdr.size);
|
|
- kfree(fds);
|
|
-
|
|
- if (ret < 0)
|
|
- return ret;
|
|
- }
|
|
-
|
|
- for (i = 0; i < res->data_count; ++i) {
|
|
- struct kdbus_msg_data *d = res->data + i;
|
|
- struct kdbus_memfd m;
|
|
-
|
|
- if (d->type != KDBUS_MSG_DATA_MEMFD)
|
|
- continue;
|
|
-
|
|
- m.start = d->memfd.start;
|
|
- m.size = d->size;
|
|
- m.fd = -1;
|
|
-
|
|
- if (install_fds) {
|
|
- m.fd = get_unused_fd_flags(O_CLOEXEC);
|
|
- if (m.fd < 0) {
|
|
- m.fd = -1;
|
|
- incomplete_fds = true;
|
|
- } else {
|
|
- fd_install(m.fd,
|
|
- get_file(d->memfd.file));
|
|
- }
|
|
- }
|
|
-
|
|
- kvec[0].iov_base = &m;
|
|
- kvec[0].iov_len = sizeof(m);
|
|
-
|
|
- ret = kdbus_pool_slice_copy_kvec(entry->slice,
|
|
- entry->memfd_offset[memfds++],
|
|
- kvec, 1, sizeof(m));
|
|
- if (ret < 0)
|
|
- return ret;
|
|
- }
|
|
-
|
|
if (incomplete_fds)
|
|
*return_flags |= KDBUS_RECV_RETURN_INCOMPLETE_FDS;
|
|
-
|
|
return 0;
|
|
}
|
|
|
|
@@ -646,7 +328,7 @@ int kdbus_queue_entry_move(struct kdbus_queue_entry *e,
|
|
return 0;
|
|
|
|
size = kdbus_pool_slice_size(e->slice);
|
|
- fds = e->msg_res ? e->msg_res->fds_count : 0;
|
|
+ fds = e->gaps ? e->gaps->n_fds : 0;
|
|
|
|
ret = kdbus_conn_quota_inc(dst, e->user, size, fds);
|
|
if (ret < 0)
|
|
diff --git a/ipc/kdbus/queue.h b/ipc/kdbus/queue.h
|
|
index ac471d0c809d..bf686d182ce1 100644
|
|
--- a/ipc/kdbus/queue.h
|
|
+++ b/ipc/kdbus/queue.h
|
|
@@ -15,6 +15,13 @@
|
|
#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;
|
|
|
|
/**
|
|
@@ -35,52 +42,37 @@ struct kdbus_queue {
|
|
* @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
|
|
- * @slice: Slice in the receiver's pool for the message
|
|
- * @attach_flags: Attach flags used during slice allocation
|
|
- * @meta_offset: Offset of first metadata item in slice
|
|
- * @fds_offset: Offset of FD item in slice
|
|
- * @memfd_offset: Array of slice-offsets for all memfd items
|
|
* @priority: Message priority
|
|
* @dst_name_id: The sequence number of the name this message is
|
|
* addressed to, 0 for messages sent to an ID
|
|
- * @msg_res: Message resources
|
|
- * @proc_meta: Process metadata, captured at message arrival
|
|
- * @conn_meta: Connection metadata, captured at message arrival
|
|
- * @reply: The reply block if a reply to this message is expected
|
|
+ * @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;
|
|
|
|
- struct kdbus_pool_slice *slice;
|
|
-
|
|
- u64 attach_flags;
|
|
- size_t meta_offset;
|
|
- size_t fds_offset;
|
|
- size_t *memfd_offset;
|
|
-
|
|
s64 priority;
|
|
u64 dst_name_id;
|
|
|
|
- struct kdbus_msg_resources *msg_res;
|
|
- struct kdbus_meta_proc *proc_meta;
|
|
- struct kdbus_meta_conn *conn_meta;
|
|
- struct kdbus_reply *reply;
|
|
struct kdbus_conn *conn;
|
|
+ struct kdbus_gaps *gaps;
|
|
struct kdbus_user *user;
|
|
+ struct kdbus_pool_slice *slice;
|
|
+ struct kdbus_reply *reply;
|
|
};
|
|
|
|
-struct kdbus_kmsg;
|
|
-
|
|
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 *conn_src,
|
|
- struct kdbus_conn *conn_dst,
|
|
- const struct kdbus_kmsg *kmsg);
|
|
+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);
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 149fd9e2deb3fc394cf37f6f848ccb1ea34358d2 Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Wed, 24 Jun 2015 15:29:53 +0200
|
|
Subject: [PATCH 27/34] kdbus: drop unused metadata code
|
|
|
|
Now that we switched to the new kdbus_staging infrastructure, the old
|
|
RECV-time metadata helpers are no longer needed. Drop them.
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
---
|
|
ipc/kdbus/metadata.c | 371 ---------------------------------------------------
|
|
ipc/kdbus/metadata.h | 11 --
|
|
2 files changed, 382 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/metadata.c b/ipc/kdbus/metadata.c
|
|
index 70ce5d15fc98..43e5f4564925 100644
|
|
--- a/ipc/kdbus/metadata.c
|
|
+++ b/ipc/kdbus/metadata.c
|
|
@@ -714,138 +714,6 @@ exit_unlock:
|
|
return ret;
|
|
}
|
|
|
|
-/*
|
|
- * kdbus_meta_export_prepare() - Prepare metadata for export
|
|
- * @mp: Process metadata, or NULL
|
|
- * @mf: Fake metadata, or NULL
|
|
- * @mc: Connection metadata, or NULL
|
|
- * @mask: Pointer to mask of KDBUS_ATTACH_* flags to export
|
|
- * @sz: Pointer to return the size needed by the metadata
|
|
- *
|
|
- * Does a conservative calculation of how much space metadata information
|
|
- * will take up during export. It is 'conservative' because for string
|
|
- * translations in namespaces, it will use the kernel namespaces, which is
|
|
- * the longest possible version.
|
|
- *
|
|
- * The actual size consumed by kdbus_meta_export() may hence vary from the
|
|
- * one reported here, but it is guaranteed never to be greater.
|
|
- *
|
|
- * Return: 0 on success, negative error number otherwise.
|
|
- */
|
|
-int kdbus_meta_export_prepare(struct kdbus_meta_proc *mp,
|
|
- struct kdbus_meta_fake *mf,
|
|
- struct kdbus_meta_conn *mc,
|
|
- u64 *mask, size_t *sz)
|
|
-{
|
|
- char *exe_pathname = NULL;
|
|
- void *exe_page = NULL;
|
|
- size_t size = 0;
|
|
- u64 valid = 0;
|
|
- int ret = 0;
|
|
-
|
|
- if (WARN_ON(mf && mp))
|
|
- mp = NULL;
|
|
-
|
|
- if (mf)
|
|
- valid |= mf->valid;
|
|
-
|
|
- if (mp) {
|
|
- mutex_lock(&mp->lock);
|
|
- valid |= mp->valid;
|
|
- mutex_unlock(&mp->lock);
|
|
- }
|
|
-
|
|
- if (mc) {
|
|
- mutex_lock(&mc->lock);
|
|
- valid |= mc->valid;
|
|
- mutex_unlock(&mc->lock);
|
|
- }
|
|
-
|
|
- *mask &= valid;
|
|
-
|
|
- if (!*mask)
|
|
- goto exit;
|
|
-
|
|
- /* process metadata */
|
|
-
|
|
- if ((mp || mf) && (*mask & KDBUS_ATTACH_CREDS))
|
|
- size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_creds));
|
|
-
|
|
- if ((mp || mf) && (*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 (mp && (*mask & KDBUS_ATTACH_EXE)) {
|
|
- exe_page = (void *)__get_free_page(GFP_TEMPORARY);
|
|
- if (!exe_page) {
|
|
- ret = -ENOMEM;
|
|
- goto exit;
|
|
- }
|
|
-
|
|
- exe_pathname = d_path(&mp->exe_path, exe_page, PAGE_SIZE);
|
|
- if (IS_ERR(exe_pathname)) {
|
|
- ret = PTR_ERR(exe_pathname);
|
|
- goto exit;
|
|
- }
|
|
-
|
|
- size += KDBUS_ITEM_SIZE(strlen(exe_pathname) + 1);
|
|
- free_page((unsigned long)exe_page);
|
|
- }
|
|
-
|
|
- 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 ((mp || mf) && (*mask & KDBUS_ATTACH_SECLABEL))
|
|
- size += KDBUS_ITEM_SIZE(strlen(mp ? mp->seclabel
|
|
- : mf->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));
|
|
-
|
|
-exit:
|
|
- *sz = size;
|
|
-
|
|
- return ret;
|
|
-}
|
|
-
|
|
-static int kdbus_meta_push_kvec(struct kvec *kvec,
|
|
- struct kdbus_item_header *hdr,
|
|
- u64 type, void *payload,
|
|
- size_t payload_size, u64 *size)
|
|
-{
|
|
- hdr->type = type;
|
|
- hdr->size = KDBUS_ITEM_HEADER_SIZE + payload_size;
|
|
- kdbus_kvec_set(kvec++, hdr, sizeof(*hdr), size);
|
|
- kdbus_kvec_set(kvec++, payload, payload_size, size);
|
|
- return 2 + !!kdbus_kvec_pad(kvec++, size);
|
|
-}
|
|
-
|
|
static void kdbus_meta_export_caps(struct kdbus_meta_caps *out,
|
|
const struct kdbus_meta_proc *mp,
|
|
struct user_namespace *user_ns)
|
|
@@ -922,245 +790,6 @@ 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);
|
|
}
|
|
|
|
-/**
|
|
- * kdbus_meta_export() - export information from metadata into a slice
|
|
- * @mp: Process metadata, or NULL
|
|
- * @mf: Fake metadata, or NULL
|
|
- * @mc: Connection metadata, or NULL
|
|
- * @conn: Target connection to translate metadata into
|
|
- * @mask: Mask of KDBUS_ATTACH_* flags to export
|
|
- * @slice: The slice to export to
|
|
- * @offset: The offset inside @slice to write to
|
|
- * @real_size: The real size the metadata consumed
|
|
- *
|
|
- * This function exports information from metadata into @slice at offset
|
|
- * @offset inside that slice. Only information that is requested in @mask
|
|
- * and that has been collected before is exported.
|
|
- *
|
|
- * In order to make sure not to write out of bounds, @mask must be the same
|
|
- * value that was previously returned from kdbus_meta_export_prepare(). The
|
|
- * function will, however, not necessarily write as many bytes as returned by
|
|
- * kdbus_meta_export_prepare(); depending on the namespaces in question, it
|
|
- * might use up less than that.
|
|
- *
|
|
- * All information will be translated using the namespaces of @conn.
|
|
- *
|
|
- * Return: 0 on success, negative error number otherwise.
|
|
- */
|
|
-int kdbus_meta_export(struct kdbus_meta_proc *mp,
|
|
- struct kdbus_meta_fake *mf,
|
|
- struct kdbus_meta_conn *mc,
|
|
- struct kdbus_conn *conn,
|
|
- u64 mask,
|
|
- struct kdbus_pool_slice *slice,
|
|
- off_t offset,
|
|
- size_t *real_size)
|
|
-{
|
|
- struct user_namespace *user_ns = conn->user_ns;
|
|
- struct kdbus_item_header item_hdr[13], *hdr;
|
|
- char *exe_pathname = NULL;
|
|
- struct kdbus_creds creds;
|
|
- struct kdbus_pids pids;
|
|
- void *exe_page = NULL;
|
|
- struct kvec kvec[40];
|
|
- u64 *auxgrps = NULL;
|
|
- size_t cnt = 0;
|
|
- u64 size = 0;
|
|
- int ret = 0;
|
|
-
|
|
- if (WARN_ON(mf && mp))
|
|
- mp = NULL;
|
|
-
|
|
- hdr = &item_hdr[0];
|
|
-
|
|
- if (mask == 0) {
|
|
- *real_size = 0;
|
|
- return 0;
|
|
- }
|
|
-
|
|
- /* process metadata */
|
|
-
|
|
- if (mf && (mask & KDBUS_ATTACH_CREDS)) {
|
|
- creds.uid = kdbus_from_kuid_keep(user_ns, mf->uid);
|
|
- creds.euid = kdbus_from_kuid_keep(user_ns, mf->euid);
|
|
- creds.suid = kdbus_from_kuid_keep(user_ns, mf->suid);
|
|
- creds.fsuid = kdbus_from_kuid_keep(user_ns, mf->fsuid);
|
|
- creds.gid = kdbus_from_kgid_keep(user_ns, mf->gid);
|
|
- creds.egid = kdbus_from_kgid_keep(user_ns, mf->egid);
|
|
- creds.sgid = kdbus_from_kgid_keep(user_ns, mf->sgid);
|
|
- creds.fsgid = kdbus_from_kgid_keep(user_ns, mf->fsgid);
|
|
-
|
|
- cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++, KDBUS_ITEM_CREDS,
|
|
- &creds, sizeof(creds), &size);
|
|
- } else if (mp && (mask & KDBUS_ATTACH_CREDS)) {
|
|
- const struct cred *c = mp->cred;
|
|
-
|
|
- creds.uid = kdbus_from_kuid_keep(user_ns, c->uid);
|
|
- creds.euid = kdbus_from_kuid_keep(user_ns, c->euid);
|
|
- creds.suid = kdbus_from_kuid_keep(user_ns, c->suid);
|
|
- creds.fsuid = kdbus_from_kuid_keep(user_ns, c->fsuid);
|
|
- creds.gid = kdbus_from_kgid_keep(user_ns, c->gid);
|
|
- creds.egid = kdbus_from_kgid_keep(user_ns, c->egid);
|
|
- creds.sgid = kdbus_from_kgid_keep(user_ns, c->sgid);
|
|
- creds.fsgid = kdbus_from_kgid_keep(user_ns, c->fsgid);
|
|
-
|
|
- cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++, KDBUS_ITEM_CREDS,
|
|
- &creds, sizeof(creds), &size);
|
|
- }
|
|
-
|
|
- if (mf && (mask & KDBUS_ATTACH_PIDS)) {
|
|
- pids.pid = pid_nr_ns(mf->tgid, conn->pid_ns);
|
|
- pids.tid = pid_nr_ns(mf->pid, conn->pid_ns);
|
|
- pids.ppid = pid_nr_ns(mf->ppid, conn->pid_ns);
|
|
-
|
|
- cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++, KDBUS_ITEM_PIDS,
|
|
- &pids, sizeof(pids), &size);
|
|
- } else if (mp && (mask & KDBUS_ATTACH_PIDS)) {
|
|
- pids.pid = pid_nr_ns(mp->tgid, conn->pid_ns);
|
|
- pids.tid = pid_nr_ns(mp->pid, conn->pid_ns);
|
|
- pids.ppid = pid_nr_ns(mp->ppid, conn->pid_ns);
|
|
-
|
|
- cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++, KDBUS_ITEM_PIDS,
|
|
- &pids, sizeof(pids), &size);
|
|
- }
|
|
-
|
|
- if (mp && (mask & KDBUS_ATTACH_AUXGROUPS)) {
|
|
- const struct group_info *info = mp->cred->group_info;
|
|
- size_t i, n, payload_size;
|
|
-
|
|
- n = info->ngroups;
|
|
- payload_size = n * sizeof(u64);
|
|
- auxgrps = kmalloc(payload_size, GFP_KERNEL);
|
|
- if (!auxgrps) {
|
|
- ret = -ENOMEM;
|
|
- goto exit;
|
|
- }
|
|
-
|
|
- for (i = 0; i < n; ++i)
|
|
- auxgrps[i] = from_kgid_munged(user_ns,
|
|
- GROUP_AT(info, i));
|
|
-
|
|
- cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
|
|
- KDBUS_ITEM_AUXGROUPS,
|
|
- auxgrps, payload_size, &size);
|
|
- }
|
|
-
|
|
- if (mp && (mask & KDBUS_ATTACH_TID_COMM))
|
|
- cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
|
|
- KDBUS_ITEM_TID_COMM, mp->tid_comm,
|
|
- strlen(mp->tid_comm) + 1, &size);
|
|
-
|
|
- if (mp && (mask & KDBUS_ATTACH_PID_COMM))
|
|
- cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
|
|
- KDBUS_ITEM_PID_COMM, mp->pid_comm,
|
|
- strlen(mp->pid_comm) + 1, &size);
|
|
-
|
|
- if (mp && (mask & KDBUS_ATTACH_EXE)) {
|
|
- struct path p;
|
|
-
|
|
- /*
|
|
- * TODO: 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, &mp->root_path) &&
|
|
- path_equal(&p, &conn->root_path)) {
|
|
- exe_page = (void *)__get_free_page(GFP_TEMPORARY);
|
|
- if (!exe_page) {
|
|
- path_put(&p);
|
|
- ret = -ENOMEM;
|
|
- goto exit;
|
|
- }
|
|
-
|
|
- exe_pathname = d_path(&mp->exe_path, exe_page,
|
|
- PAGE_SIZE);
|
|
- if (IS_ERR(exe_pathname)) {
|
|
- path_put(&p);
|
|
- ret = PTR_ERR(exe_pathname);
|
|
- goto exit;
|
|
- }
|
|
-
|
|
- cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
|
|
- KDBUS_ITEM_EXE,
|
|
- exe_pathname,
|
|
- strlen(exe_pathname) + 1,
|
|
- &size);
|
|
- }
|
|
- path_put(&p);
|
|
- }
|
|
-
|
|
- if (mp && (mask & KDBUS_ATTACH_CMDLINE))
|
|
- cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
|
|
- KDBUS_ITEM_CMDLINE, mp->cmdline,
|
|
- strlen(mp->cmdline) + 1, &size);
|
|
-
|
|
- if (mp && (mask & KDBUS_ATTACH_CGROUP))
|
|
- cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
|
|
- KDBUS_ITEM_CGROUP, mp->cgroup,
|
|
- strlen(mp->cgroup) + 1, &size);
|
|
-
|
|
- if (mp && (mask & KDBUS_ATTACH_CAPS)) {
|
|
- struct kdbus_meta_caps caps = {};
|
|
-
|
|
- kdbus_meta_export_caps(&caps, mp, user_ns);
|
|
- cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
|
|
- KDBUS_ITEM_CAPS, &caps,
|
|
- sizeof(caps), &size);
|
|
- }
|
|
-
|
|
- if (mf && (mask & KDBUS_ATTACH_SECLABEL))
|
|
- cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
|
|
- KDBUS_ITEM_SECLABEL, mf->seclabel,
|
|
- strlen(mf->seclabel) + 1, &size);
|
|
- else if (mp && (mask & KDBUS_ATTACH_SECLABEL))
|
|
- cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
|
|
- KDBUS_ITEM_SECLABEL, mp->seclabel,
|
|
- strlen(mp->seclabel) + 1, &size);
|
|
-
|
|
- if (mp && (mask & KDBUS_ATTACH_AUDIT)) {
|
|
- struct kdbus_audit a = {
|
|
- .loginuid = from_kuid(user_ns, mp->audit_loginuid),
|
|
- .sessionid = mp->audit_sessionid,
|
|
- };
|
|
-
|
|
- cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++, KDBUS_ITEM_AUDIT,
|
|
- &a, sizeof(a), &size);
|
|
- }
|
|
-
|
|
- /* connection metadata */
|
|
-
|
|
- if (mc && (mask & KDBUS_ATTACH_NAMES))
|
|
- kdbus_kvec_set(&kvec[cnt++], mc->owned_names_items,
|
|
- KDBUS_ALIGN8(mc->owned_names_size), &size);
|
|
-
|
|
- if (mc && (mask & KDBUS_ATTACH_CONN_DESCRIPTION))
|
|
- cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
|
|
- KDBUS_ITEM_CONN_DESCRIPTION,
|
|
- mc->conn_description,
|
|
- strlen(mc->conn_description) + 1,
|
|
- &size);
|
|
-
|
|
- if (mc && (mask & KDBUS_ATTACH_TIMESTAMP))
|
|
- cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
|
|
- KDBUS_ITEM_TIMESTAMP, &mc->ts,
|
|
- sizeof(mc->ts), &size);
|
|
-
|
|
- ret = kdbus_pool_slice_copy_kvec(slice, offset, kvec, cnt, size);
|
|
- *real_size = size;
|
|
-
|
|
-exit:
|
|
- kfree(auxgrps);
|
|
-
|
|
- if (exe_page)
|
|
- free_page((unsigned long)exe_page);
|
|
-
|
|
- return ret;
|
|
-}
|
|
-
|
|
struct kdbus_meta_staging {
|
|
const struct kdbus_meta_proc *mp;
|
|
const struct kdbus_meta_fake *mf;
|
|
diff --git a/ipc/kdbus/metadata.h b/ipc/kdbus/metadata.h
|
|
index a30b0ad26af2..8fb2a9f7f82e 100644
|
|
--- a/ipc/kdbus/metadata.h
|
|
+++ b/ipc/kdbus/metadata.h
|
|
@@ -72,17 +72,6 @@ int kdbus_meta_conn_collect(struct kdbus_meta_conn *mc,
|
|
struct kdbus_conn *conn,
|
|
u64 msg_seqnum, u64 what);
|
|
|
|
-int kdbus_meta_export_prepare(struct kdbus_meta_proc *mp,
|
|
- struct kdbus_meta_fake *mf,
|
|
- struct kdbus_meta_conn *mc,
|
|
- u64 *mask, size_t *sz);
|
|
-int kdbus_meta_export(struct kdbus_meta_proc *mp,
|
|
- struct kdbus_meta_fake *mf,
|
|
- struct kdbus_meta_conn *mc,
|
|
- struct kdbus_conn *conn,
|
|
- u64 mask,
|
|
- struct kdbus_pool_slice *slice,
|
|
- off_t offset, size_t *real_size);
|
|
int kdbus_meta_emit(struct kdbus_meta_proc *mp,
|
|
struct kdbus_meta_fake *mf,
|
|
struct kdbus_meta_conn *mc,
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 5c9d1e5ee20da087dee20f20d5fa312249a163e4 Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Wed, 24 Jun 2015 15:31:10 +0200
|
|
Subject: [PATCH 28/34] kdbus: drop unused message handling
|
|
|
|
Now that we switched to kdbus_staging, the old message importer and
|
|
handling is no longer needed. Drop it.
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
---
|
|
ipc/kdbus/message.c | 600 ----------------------------------------------------
|
|
ipc/kdbus/message.h | 106 ----------
|
|
2 files changed, 706 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/message.c b/ipc/kdbus/message.c
|
|
index f1ec69f3697f..72fb5908c927 100644
|
|
--- a/ipc/kdbus/message.c
|
|
+++ b/ipc/kdbus/message.c
|
|
@@ -37,608 +37,8 @@
|
|
#include "names.h"
|
|
#include "policy.h"
|
|
|
|
-#define KDBUS_KMSG_HEADER_SIZE offsetof(struct kdbus_kmsg, msg)
|
|
-
|
|
-static struct kdbus_msg_resources *kdbus_msg_resources_new(void)
|
|
-{
|
|
- struct kdbus_msg_resources *r;
|
|
-
|
|
- r = kzalloc(sizeof(*r), GFP_KERNEL);
|
|
- if (!r)
|
|
- return ERR_PTR(-ENOMEM);
|
|
-
|
|
- kref_init(&r->kref);
|
|
-
|
|
- return r;
|
|
-}
|
|
-
|
|
-static void __kdbus_msg_resources_free(struct kref *kref)
|
|
-{
|
|
- struct kdbus_msg_resources *r =
|
|
- container_of(kref, struct kdbus_msg_resources, kref);
|
|
- size_t i;
|
|
-
|
|
- for (i = 0; i < r->data_count; ++i) {
|
|
- switch (r->data[i].type) {
|
|
- case KDBUS_MSG_DATA_VEC:
|
|
- /* nothing to do */
|
|
- break;
|
|
- case KDBUS_MSG_DATA_MEMFD:
|
|
- if (r->data[i].memfd.file)
|
|
- fput(r->data[i].memfd.file);
|
|
- break;
|
|
- }
|
|
- }
|
|
-
|
|
- for (i = 0; i < r->fds_count; i++)
|
|
- if (r->fds[i])
|
|
- fput(r->fds[i]);
|
|
-
|
|
- kfree(r->dst_name);
|
|
- kfree(r->data);
|
|
- kfree(r->fds);
|
|
- kfree(r);
|
|
-}
|
|
-
|
|
-/**
|
|
- * kdbus_msg_resources_ref() - Acquire reference to msg resources
|
|
- * @r: resources to acquire ref to
|
|
- *
|
|
- * Return: The acquired resource
|
|
- */
|
|
-struct kdbus_msg_resources *
|
|
-kdbus_msg_resources_ref(struct kdbus_msg_resources *r)
|
|
-{
|
|
- if (r)
|
|
- kref_get(&r->kref);
|
|
- return r;
|
|
-}
|
|
-
|
|
-/**
|
|
- * kdbus_msg_resources_unref() - Drop reference to msg resources
|
|
- * @r: resources to drop reference of
|
|
- *
|
|
- * Return: NULL
|
|
- */
|
|
-struct kdbus_msg_resources *
|
|
-kdbus_msg_resources_unref(struct kdbus_msg_resources *r)
|
|
-{
|
|
- if (r)
|
|
- kref_put(&r->kref, __kdbus_msg_resources_free);
|
|
- return NULL;
|
|
-}
|
|
-
|
|
-/**
|
|
- * kdbus_kmsg_free() - free allocated message
|
|
- * @kmsg: Message
|
|
- */
|
|
-void kdbus_kmsg_free(struct kdbus_kmsg *kmsg)
|
|
-{
|
|
- if (!kmsg)
|
|
- return;
|
|
-
|
|
- kdbus_msg_resources_unref(kmsg->res);
|
|
- kdbus_meta_conn_unref(kmsg->conn_meta);
|
|
- kdbus_meta_proc_unref(kmsg->proc_meta);
|
|
- kfree(kmsg->iov);
|
|
- kfree(kmsg);
|
|
-}
|
|
-
|
|
-struct kdbus_kmsg *kdbus_kmsg_new_kernel(struct kdbus_bus *bus,
|
|
- u64 dst, u64 cookie_timeout,
|
|
- size_t it_size, size_t it_type)
|
|
-{
|
|
- struct kdbus_kmsg *kmsg;
|
|
- size_t size;
|
|
- int ret;
|
|
-
|
|
- size = sizeof(struct kdbus_kmsg) + KDBUS_ITEM_SIZE(it_size);
|
|
- kmsg = kzalloc(size, GFP_KERNEL);
|
|
- if (!kmsg)
|
|
- return ERR_PTR(-ENOMEM);
|
|
-
|
|
- kmsg->seq = atomic64_inc_return(&bus->domain->last_id);
|
|
-
|
|
- kmsg->proc_meta = kdbus_meta_proc_new();
|
|
- if (IS_ERR(kmsg->proc_meta)) {
|
|
- ret = PTR_ERR(kmsg->proc_meta);
|
|
- kmsg->proc_meta = NULL;
|
|
- goto exit;
|
|
- }
|
|
-
|
|
- kmsg->conn_meta = kdbus_meta_conn_new();
|
|
- if (IS_ERR(kmsg->conn_meta)) {
|
|
- ret = PTR_ERR(kmsg->conn_meta);
|
|
- kmsg->conn_meta = NULL;
|
|
- goto exit;
|
|
- }
|
|
-
|
|
- kmsg->msg.size = size - KDBUS_KMSG_HEADER_SIZE;
|
|
- kmsg->msg.flags = (dst == KDBUS_DST_ID_BROADCAST) ?
|
|
- KDBUS_MSG_SIGNAL : 0;
|
|
- kmsg->msg.dst_id = dst;
|
|
- kmsg->msg.src_id = KDBUS_SRC_ID_KERNEL;
|
|
- kmsg->msg.payload_type = KDBUS_PAYLOAD_KERNEL;
|
|
- kmsg->msg.cookie_reply = cookie_timeout;
|
|
- kmsg->notify = kmsg->msg.items;
|
|
- kmsg->notify->size = KDBUS_ITEM_HEADER_SIZE + it_size;
|
|
- kmsg->notify->type = it_type;
|
|
-
|
|
- return kmsg;
|
|
-
|
|
-exit:
|
|
- kdbus_kmsg_free(kmsg);
|
|
- return ERR_PTR(ret);
|
|
-}
|
|
-
|
|
-static int kdbus_handle_check_file(struct file *file)
|
|
-{
|
|
- struct inode *inode = file_inode(file);
|
|
- struct socket *sock;
|
|
-
|
|
- /*
|
|
- * Don't allow file descriptors in the transport that themselves allow
|
|
- * file descriptor queueing. This will eventually be allowed once both
|
|
- * unix domain sockets and kdbus share a generic garbage collector.
|
|
- */
|
|
-
|
|
- if (file->f_op == &kdbus_handle_ops)
|
|
- return -EOPNOTSUPP;
|
|
-
|
|
- if (!S_ISSOCK(inode->i_mode))
|
|
- return 0;
|
|
-
|
|
- if (file->f_mode & FMODE_PATH)
|
|
- return 0;
|
|
-
|
|
- sock = SOCKET_I(inode);
|
|
- if (sock->sk && sock->ops && sock->ops->family == PF_UNIX)
|
|
- return -EOPNOTSUPP;
|
|
-
|
|
- return 0;
|
|
-}
|
|
-
|
|
static const char * const zeros = "\0\0\0\0\0\0\0";
|
|
|
|
-/*
|
|
- * kdbus_msg_scan_items() - validate incoming data and prepare parsing
|
|
- * @kmsg: Message
|
|
- * @bus: Bus the message is sent over
|
|
- *
|
|
- * Return: 0 on success, negative errno on failure.
|
|
- *
|
|
- * Files references in MEMFD or FDS items are pinned.
|
|
- *
|
|
- * On errors, the caller should drop any taken reference with
|
|
- * kdbus_kmsg_free()
|
|
- */
|
|
-static int kdbus_msg_scan_items(struct kdbus_kmsg *kmsg,
|
|
- struct kdbus_bus *bus)
|
|
-{
|
|
- struct kdbus_msg_resources *res = kmsg->res;
|
|
- const struct kdbus_msg *msg = &kmsg->msg;
|
|
- const struct kdbus_item *item;
|
|
- size_t n_res, n_vecs, n_memfds;
|
|
- bool has_bloom = false;
|
|
- bool has_name = false;
|
|
- bool has_fds = false;
|
|
- bool is_broadcast;
|
|
- bool is_signal;
|
|
- u64 vec_size;
|
|
-
|
|
- is_broadcast = (msg->dst_id == KDBUS_DST_ID_BROADCAST);
|
|
- is_signal = !!(msg->flags & KDBUS_MSG_SIGNAL);
|
|
-
|
|
- /* count data payloads */
|
|
- n_vecs = 0;
|
|
- n_memfds = 0;
|
|
- KDBUS_ITEMS_FOREACH(item, msg->items, KDBUS_ITEMS_SIZE(msg, items)) {
|
|
- switch (item->type) {
|
|
- case KDBUS_ITEM_PAYLOAD_VEC:
|
|
- ++n_vecs;
|
|
- break;
|
|
- case KDBUS_ITEM_PAYLOAD_MEMFD:
|
|
- ++n_memfds;
|
|
- if (item->memfd.size % 8)
|
|
- ++n_vecs;
|
|
- break;
|
|
- default:
|
|
- break;
|
|
- }
|
|
- }
|
|
-
|
|
- n_res = n_vecs + n_memfds;
|
|
- if (n_res > 0) {
|
|
- res->data = kcalloc(n_res, sizeof(*res->data), GFP_KERNEL);
|
|
- if (!res->data)
|
|
- return -ENOMEM;
|
|
- }
|
|
-
|
|
- if (n_vecs > 0) {
|
|
- kmsg->iov = kcalloc(n_vecs, sizeof(*kmsg->iov), GFP_KERNEL);
|
|
- if (!kmsg->iov)
|
|
- return -ENOMEM;
|
|
- }
|
|
-
|
|
- /* import data payloads */
|
|
- vec_size = 0;
|
|
- KDBUS_ITEMS_FOREACH(item, msg->items, KDBUS_ITEMS_SIZE(msg, items)) {
|
|
- size_t payload_size = KDBUS_ITEM_PAYLOAD_SIZE(item);
|
|
- struct iovec *iov = kmsg->iov + kmsg->iov_count;
|
|
-
|
|
- switch (item->type) {
|
|
- case KDBUS_ITEM_PAYLOAD_VEC: {
|
|
- struct kdbus_msg_data *d = res->data + res->data_count;
|
|
- void __force __user *ptr = KDBUS_PTR(item->vec.address);
|
|
- size_t size = item->vec.size;
|
|
-
|
|
- if (vec_size + size < vec_size)
|
|
- return -EMSGSIZE;
|
|
- if (vec_size + size > KDBUS_MSG_MAX_PAYLOAD_VEC_SIZE)
|
|
- return -EMSGSIZE;
|
|
-
|
|
- d->type = KDBUS_MSG_DATA_VEC;
|
|
- d->size = size;
|
|
-
|
|
- if (ptr) {
|
|
- if (unlikely(!access_ok(VERIFY_READ, ptr,
|
|
- size)))
|
|
- return -EFAULT;
|
|
-
|
|
- d->vec.off = kmsg->pool_size;
|
|
- iov->iov_base = ptr;
|
|
- iov->iov_len = size;
|
|
- } else {
|
|
- d->vec.off = ~0ULL;
|
|
- iov->iov_base = (char __user *)zeros;
|
|
- iov->iov_len = size % 8;
|
|
- }
|
|
-
|
|
- if (kmsg->pool_size + iov->iov_len < kmsg->pool_size)
|
|
- return -EMSGSIZE;
|
|
-
|
|
- kmsg->pool_size += iov->iov_len;
|
|
- ++kmsg->iov_count;
|
|
- ++res->vec_count;
|
|
- ++res->data_count;
|
|
- vec_size += size;
|
|
-
|
|
- break;
|
|
- }
|
|
-
|
|
- case KDBUS_ITEM_PAYLOAD_MEMFD: {
|
|
- struct kdbus_msg_data *d = res->data + res->data_count;
|
|
- u64 start = item->memfd.start;
|
|
- u64 size = item->memfd.size;
|
|
- size_t pad = size % 8;
|
|
- int seals, mask;
|
|
- struct file *f;
|
|
-
|
|
- if (kmsg->pool_size + size % 8 < kmsg->pool_size)
|
|
- return -EMSGSIZE;
|
|
- if (start + size < start)
|
|
- return -EMSGSIZE;
|
|
-
|
|
- if (item->memfd.fd < 0)
|
|
- return -EBADF;
|
|
-
|
|
- if (res->memfd_count >= KDBUS_MSG_MAX_MEMFD_ITEMS)
|
|
- return -E2BIG;
|
|
-
|
|
- f = fget(item->memfd.fd);
|
|
- if (!f)
|
|
- return -EBADF;
|
|
-
|
|
- if (pad) {
|
|
- iov->iov_base = (char __user *)zeros;
|
|
- iov->iov_len = pad;
|
|
-
|
|
- kmsg->pool_size += pad;
|
|
- ++kmsg->iov_count;
|
|
- }
|
|
-
|
|
- ++res->data_count;
|
|
- ++res->memfd_count;
|
|
-
|
|
- d->type = KDBUS_MSG_DATA_MEMFD;
|
|
- d->size = size;
|
|
- d->memfd.start = start;
|
|
- d->memfd.file = f;
|
|
-
|
|
- /*
|
|
- * We only accept a sealed memfd file whose content
|
|
- * cannot be altered by the sender or anybody else
|
|
- * while it is shared or in-flight. Other files need
|
|
- * to be passed with KDBUS_MSG_FDS.
|
|
- */
|
|
- seals = shmem_get_seals(f);
|
|
- if (seals < 0)
|
|
- return -EMEDIUMTYPE;
|
|
-
|
|
- mask = F_SEAL_SHRINK | F_SEAL_GROW |
|
|
- F_SEAL_WRITE | F_SEAL_SEAL;
|
|
- if ((seals & mask) != mask)
|
|
- return -ETXTBSY;
|
|
-
|
|
- if (start + size > (u64)i_size_read(file_inode(f)))
|
|
- return -EBADF;
|
|
-
|
|
- break;
|
|
- }
|
|
-
|
|
- case KDBUS_ITEM_FDS: {
|
|
- unsigned int i;
|
|
- unsigned int fds_count = payload_size / sizeof(int);
|
|
-
|
|
- /* do not allow multiple fd arrays */
|
|
- if (has_fds)
|
|
- return -EEXIST;
|
|
- has_fds = true;
|
|
-
|
|
- /* Do not allow to broadcast file descriptors */
|
|
- if (is_broadcast)
|
|
- return -ENOTUNIQ;
|
|
-
|
|
- if (fds_count > KDBUS_CONN_MAX_FDS_PER_USER)
|
|
- return -EMFILE;
|
|
-
|
|
- res->fds = kcalloc(fds_count, sizeof(struct file *),
|
|
- GFP_KERNEL);
|
|
- if (!res->fds)
|
|
- return -ENOMEM;
|
|
-
|
|
- for (i = 0; i < fds_count; i++) {
|
|
- int fd = item->fds[i];
|
|
- int ret;
|
|
-
|
|
- /*
|
|
- * Verify the fd and increment the usage count.
|
|
- * Use fget_raw() to allow passing O_PATH fds.
|
|
- */
|
|
- if (fd < 0)
|
|
- return -EBADF;
|
|
-
|
|
- res->fds[i] = fget_raw(fd);
|
|
- if (!res->fds[i])
|
|
- return -EBADF;
|
|
-
|
|
- res->fds_count++;
|
|
-
|
|
- ret = kdbus_handle_check_file(res->fds[i]);
|
|
- if (ret < 0)
|
|
- return ret;
|
|
- }
|
|
-
|
|
- break;
|
|
- }
|
|
-
|
|
- case KDBUS_ITEM_BLOOM_FILTER: {
|
|
- u64 bloom_size;
|
|
-
|
|
- /* do not allow multiple bloom filters */
|
|
- if (has_bloom)
|
|
- return -EEXIST;
|
|
- has_bloom = true;
|
|
-
|
|
- bloom_size = payload_size -
|
|
- offsetof(struct kdbus_bloom_filter, data);
|
|
-
|
|
- /*
|
|
- * Allow only bloom filter sizes of a multiple of 64bit.
|
|
- */
|
|
- if (!KDBUS_IS_ALIGNED8(bloom_size))
|
|
- return -EFAULT;
|
|
-
|
|
- /* do not allow mismatching bloom filter sizes */
|
|
- if (bloom_size != bus->bloom.size)
|
|
- return -EDOM;
|
|
-
|
|
- kmsg->bloom_filter = &item->bloom_filter;
|
|
- break;
|
|
- }
|
|
-
|
|
- case KDBUS_ITEM_DST_NAME:
|
|
- /* do not allow multiple names */
|
|
- if (has_name)
|
|
- return -EEXIST;
|
|
- has_name = true;
|
|
-
|
|
- if (!kdbus_name_is_valid(item->str, false))
|
|
- return -EINVAL;
|
|
-
|
|
- res->dst_name = kstrdup(item->str, GFP_KERNEL);
|
|
- if (!res->dst_name)
|
|
- return -ENOMEM;
|
|
- break;
|
|
-
|
|
- default:
|
|
- return -EINVAL;
|
|
- }
|
|
- }
|
|
-
|
|
- /* name is needed if no ID is given */
|
|
- if (msg->dst_id == KDBUS_DST_ID_NAME && !has_name)
|
|
- return -EDESTADDRREQ;
|
|
-
|
|
- if (is_broadcast) {
|
|
- /* Broadcasts can't take names */
|
|
- if (has_name)
|
|
- return -EBADMSG;
|
|
-
|
|
- /* All broadcasts have to be signals */
|
|
- if (!is_signal)
|
|
- return -EBADMSG;
|
|
-
|
|
- /* Timeouts are not allowed for broadcasts */
|
|
- if (msg->timeout_ns > 0)
|
|
- return -ENOTUNIQ;
|
|
- }
|
|
-
|
|
- /*
|
|
- * Signal messages require a bloom filter, and bloom filters are
|
|
- * only valid with signals.
|
|
- */
|
|
- if (is_signal ^ has_bloom)
|
|
- return -EBADMSG;
|
|
-
|
|
- return 0;
|
|
-}
|
|
-
|
|
-/**
|
|
- * kdbus_kmsg_new_from_cmd() - create kernel message from send payload
|
|
- * @conn: Connection
|
|
- * @cmd_send: Payload of KDBUS_CMD_SEND
|
|
- *
|
|
- * Return: a new kdbus_kmsg on success, ERR_PTR on failure.
|
|
- */
|
|
-struct kdbus_kmsg *kdbus_kmsg_new_from_cmd(struct kdbus_conn *conn,
|
|
- struct kdbus_cmd_send *cmd_send)
|
|
-{
|
|
- struct kdbus_kmsg *m;
|
|
- u64 size;
|
|
- int ret;
|
|
-
|
|
- ret = kdbus_copy_from_user(&size, KDBUS_PTR(cmd_send->msg_address),
|
|
- sizeof(size));
|
|
- if (ret < 0)
|
|
- return ERR_PTR(ret);
|
|
-
|
|
- if (size < sizeof(struct kdbus_msg) || size > KDBUS_MSG_MAX_SIZE)
|
|
- return ERR_PTR(-EINVAL);
|
|
-
|
|
- m = kmalloc(size + KDBUS_KMSG_HEADER_SIZE, GFP_KERNEL);
|
|
- if (!m)
|
|
- return ERR_PTR(-ENOMEM);
|
|
-
|
|
- memset(m, 0, KDBUS_KMSG_HEADER_SIZE);
|
|
- m->seq = atomic64_inc_return(&conn->ep->bus->domain->last_id);
|
|
-
|
|
- m->proc_meta = kdbus_meta_proc_new();
|
|
- if (IS_ERR(m->proc_meta)) {
|
|
- ret = PTR_ERR(m->proc_meta);
|
|
- m->proc_meta = NULL;
|
|
- goto exit_free;
|
|
- }
|
|
-
|
|
- m->conn_meta = kdbus_meta_conn_new();
|
|
- if (IS_ERR(m->conn_meta)) {
|
|
- ret = PTR_ERR(m->conn_meta);
|
|
- m->conn_meta = NULL;
|
|
- goto exit_free;
|
|
- }
|
|
-
|
|
- if (copy_from_user(&m->msg, KDBUS_PTR(cmd_send->msg_address), size)) {
|
|
- ret = -EFAULT;
|
|
- goto exit_free;
|
|
- }
|
|
-
|
|
- if (m->msg.size != size) {
|
|
- ret = -EINVAL;
|
|
- goto exit_free;
|
|
- }
|
|
-
|
|
- if (m->msg.flags & ~(KDBUS_MSG_EXPECT_REPLY |
|
|
- KDBUS_MSG_NO_AUTO_START |
|
|
- KDBUS_MSG_SIGNAL)) {
|
|
- ret = -EINVAL;
|
|
- goto exit_free;
|
|
- }
|
|
-
|
|
- ret = kdbus_items_validate(m->msg.items,
|
|
- KDBUS_ITEMS_SIZE(&m->msg, items));
|
|
- if (ret < 0)
|
|
- goto exit_free;
|
|
-
|
|
- m->res = kdbus_msg_resources_new();
|
|
- if (IS_ERR(m->res)) {
|
|
- ret = PTR_ERR(m->res);
|
|
- m->res = NULL;
|
|
- goto exit_free;
|
|
- }
|
|
-
|
|
- /* do not accept kernel-generated messages */
|
|
- if (m->msg.payload_type == KDBUS_PAYLOAD_KERNEL) {
|
|
- ret = -EINVAL;
|
|
- goto exit_free;
|
|
- }
|
|
-
|
|
- if (m->msg.flags & KDBUS_MSG_EXPECT_REPLY) {
|
|
- /* requests for replies need timeout and cookie */
|
|
- if (m->msg.timeout_ns == 0 || m->msg.cookie == 0) {
|
|
- ret = -EINVAL;
|
|
- goto exit_free;
|
|
- }
|
|
-
|
|
- /* replies may not be expected for broadcasts */
|
|
- if (m->msg.dst_id == KDBUS_DST_ID_BROADCAST) {
|
|
- ret = -ENOTUNIQ;
|
|
- goto exit_free;
|
|
- }
|
|
-
|
|
- /* replies may not be expected for signals */
|
|
- if (m->msg.flags & KDBUS_MSG_SIGNAL) {
|
|
- ret = -EINVAL;
|
|
- goto exit_free;
|
|
- }
|
|
- } else {
|
|
- /*
|
|
- * KDBUS_SEND_SYNC_REPLY is only valid together with
|
|
- * KDBUS_MSG_EXPECT_REPLY
|
|
- */
|
|
- if (cmd_send->flags & KDBUS_SEND_SYNC_REPLY) {
|
|
- ret = -EINVAL;
|
|
- goto exit_free;
|
|
- }
|
|
-
|
|
- /* replies cannot be signals */
|
|
- if (m->msg.cookie_reply && (m->msg.flags & KDBUS_MSG_SIGNAL)) {
|
|
- ret = -EINVAL;
|
|
- goto exit_free;
|
|
- }
|
|
- }
|
|
-
|
|
- ret = kdbus_msg_scan_items(m, conn->ep->bus);
|
|
- if (ret < 0)
|
|
- goto exit_free;
|
|
-
|
|
- /* patch-in the source of this message */
|
|
- if (m->msg.src_id > 0 && m->msg.src_id != conn->id) {
|
|
- ret = -EINVAL;
|
|
- goto exit_free;
|
|
- }
|
|
- m->msg.src_id = conn->id;
|
|
-
|
|
- return m;
|
|
-
|
|
-exit_free:
|
|
- kdbus_kmsg_free(m);
|
|
- return ERR_PTR(ret);
|
|
-}
|
|
-
|
|
-/**
|
|
- * kdbus_kmsg_collect_metadata() - collect metadata
|
|
- * @kmsg: message to collect metadata on
|
|
- * @src: source connection of message
|
|
- * @dst: destination connection of message
|
|
- *
|
|
- * Return: 0 on success, negative error code on failure.
|
|
- */
|
|
-int kdbus_kmsg_collect_metadata(const struct kdbus_kmsg *kmsg,
|
|
- struct kdbus_conn *src, struct kdbus_conn *dst)
|
|
-{
|
|
- u64 attach;
|
|
- int ret;
|
|
-
|
|
- attach = kdbus_meta_calc_attach_flags(src, dst);
|
|
- if (!src->meta_fake) {
|
|
- ret = kdbus_meta_proc_collect(kmsg->proc_meta, attach);
|
|
- if (ret < 0)
|
|
- return ret;
|
|
- }
|
|
-
|
|
- return kdbus_meta_conn_collect(kmsg->conn_meta, src, kmsg->seq, attach);
|
|
-}
|
|
-
|
|
static struct kdbus_gaps *kdbus_gaps_new(size_t n_memfds, size_t n_fds)
|
|
{
|
|
size_t size_offsets, size_memfds, size_fds, size;
|
|
diff --git a/ipc/kdbus/message.h b/ipc/kdbus/message.h
|
|
index e0c4f2e324b3..298f9c99dfcf 100644
|
|
--- a/ipc/kdbus/message.h
|
|
+++ b/ipc/kdbus/message.h
|
|
@@ -17,103 +17,6 @@
|
|
#include <linux/fs.h>
|
|
#include <linux/kref.h>
|
|
#include <uapi/linux/kdbus.h>
|
|
-#include "util.h"
|
|
-#include "metadata.h"
|
|
-
|
|
-/**
|
|
- * enum kdbus_msg_data_type - Type of kdbus_msg_data payloads
|
|
- * @KDBUS_MSG_DATA_VEC: Data vector provided by user-space
|
|
- * @KDBUS_MSG_DATA_MEMFD: Memfd payload
|
|
- */
|
|
-enum kdbus_msg_data_type {
|
|
- KDBUS_MSG_DATA_VEC,
|
|
- KDBUS_MSG_DATA_MEMFD,
|
|
-};
|
|
-
|
|
-/**
|
|
- * struct kdbus_msg_data - Data payload as stored by messages
|
|
- * @type: Type of payload (KDBUS_MSG_DATA_*)
|
|
- * @size: Size of the described payload
|
|
- * @off: The offset, relative to the vec slice
|
|
- * @start: Offset inside the memfd
|
|
- * @file: Backing file referenced by the memfd
|
|
- */
|
|
-struct kdbus_msg_data {
|
|
- unsigned int type;
|
|
- u64 size;
|
|
-
|
|
- union {
|
|
- struct {
|
|
- u64 off;
|
|
- } vec;
|
|
- struct {
|
|
- u64 start;
|
|
- struct file *file;
|
|
- } memfd;
|
|
- };
|
|
-};
|
|
-
|
|
-/**
|
|
- * struct kdbus_kmsg_resources - resources of a message
|
|
- * @kref: Reference counter
|
|
- * @dst_name: Short-cut to msg for faster lookup
|
|
- * @fds: Array of file descriptors to pass
|
|
- * @fds_count: Number of file descriptors to pass
|
|
- * @data: Array of data payloads
|
|
- * @vec_count: Number of VEC entries
|
|
- * @memfd_count: Number of MEMFD entries in @data
|
|
- * @data_count: Sum of @vec_count + @memfd_count
|
|
- */
|
|
-struct kdbus_msg_resources {
|
|
- struct kref kref;
|
|
- const char *dst_name;
|
|
-
|
|
- struct file **fds;
|
|
- unsigned int fds_count;
|
|
-
|
|
- struct kdbus_msg_data *data;
|
|
- size_t vec_count;
|
|
- size_t memfd_count;
|
|
- size_t data_count;
|
|
-};
|
|
-
|
|
-struct kdbus_msg_resources *
|
|
-kdbus_msg_resources_ref(struct kdbus_msg_resources *r);
|
|
-struct kdbus_msg_resources *
|
|
-kdbus_msg_resources_unref(struct kdbus_msg_resources *r);
|
|
-
|
|
-/**
|
|
- * struct kdbus_kmsg - internal message handling data
|
|
- * @seq: Domain-global message sequence number
|
|
- * @notify: Short-cut to notify-item for kernel notifications
|
|
- * @bloom_filter: Bloom filter to match message properties
|
|
- * @notify_entry: List of kernel-generated notifications
|
|
- * @iov: Array of iovec, describing the payload to copy
|
|
- * @iov_count: Number of array members in @iov
|
|
- * @pool_size: Overall size of inlined data referenced by @iov
|
|
- * @proc_meta: Appended SCM-like metadata of the sending process
|
|
- * @conn_meta: Appended SCM-like metadata of the sending connection
|
|
- * @res: Message resources
|
|
- * @msg: Message from or to userspace
|
|
- */
|
|
-struct kdbus_kmsg {
|
|
- u64 seq;
|
|
- struct kdbus_item *notify;
|
|
-
|
|
- const struct kdbus_bloom_filter *bloom_filter;
|
|
- struct list_head notify_entry;
|
|
-
|
|
- struct iovec *iov;
|
|
- size_t iov_count;
|
|
- u64 pool_size;
|
|
-
|
|
- struct kdbus_meta_proc *proc_meta;
|
|
- struct kdbus_meta_conn *conn_meta;
|
|
- struct kdbus_msg_resources *res;
|
|
-
|
|
- /* variable size, must be the last member */
|
|
- struct kdbus_msg msg;
|
|
-};
|
|
|
|
struct kdbus_bus;
|
|
struct kdbus_conn;
|
|
@@ -121,15 +24,6 @@ struct kdbus_meta_conn;
|
|
struct kdbus_meta_proc;
|
|
struct kdbus_pool_slice;
|
|
|
|
-struct kdbus_kmsg *kdbus_kmsg_new_kernel(struct kdbus_bus *bus,
|
|
- u64 dst, u64 cookie_timeout,
|
|
- size_t it_size, size_t it_type);
|
|
-struct kdbus_kmsg *kdbus_kmsg_new_from_cmd(struct kdbus_conn *conn,
|
|
- struct kdbus_cmd_send *cmd_send);
|
|
-void kdbus_kmsg_free(struct kdbus_kmsg *kmsg);
|
|
-int kdbus_kmsg_collect_metadata(const struct kdbus_kmsg *kmsg,
|
|
- struct kdbus_conn *src, struct kdbus_conn *dst);
|
|
-
|
|
/**
|
|
* struct kdbus_gaps - gaps in message to be filled later
|
|
* @kref: Reference counter
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 5b2ae5600065167040dd6d61466cce345568c68e Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Mon, 6 Jul 2015 11:29:05 +0200
|
|
Subject: [PATCH 29/34] kdbus: allow senders to receive own broadcasts
|
|
|
|
The dbus1 spec does not place a restriction on who can receive broadcasts.
|
|
As long as the sender has a MATCH-rule on itself, it can as well receive
|
|
its own broadcasts.
|
|
|
|
As it turns out, user-space currently relies on this feature. So make sure
|
|
to allow this just like dbus1. If we find some client that does not work
|
|
with this, we will have to turn it into a HELLO-flag. Until then, just try
|
|
to adjust the default behavior.
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
---
|
|
ipc/kdbus/bus.c | 2 --
|
|
tools/testing/selftests/kdbus/test-message.c | 9 +++++---
|
|
tools/testing/selftests/kdbus/test-policy-priv.c | 26 +++++++++++++++++++-----
|
|
3 files changed, 27 insertions(+), 10 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/bus.c b/ipc/kdbus/bus.c
|
|
index e7e17a7f7edd..a67f825bdeaf 100644
|
|
--- a/ipc/kdbus/bus.c
|
|
+++ b/ipc/kdbus/bus.c
|
|
@@ -263,8 +263,6 @@ void kdbus_bus_broadcast(struct kdbus_bus *bus,
|
|
|
|
down_read(&bus->conn_rwlock);
|
|
hash_for_each(bus->conn_hash, i, conn_dst, hentry) {
|
|
- if (conn_dst->id == staging->msg->src_id)
|
|
- continue;
|
|
if (!kdbus_conn_is_ordinary(conn_dst))
|
|
continue;
|
|
|
|
diff --git a/tools/testing/selftests/kdbus/test-message.c b/tools/testing/selftests/kdbus/test-message.c
|
|
index f1615dafb7f1..ddc1e0af877b 100644
|
|
--- a/tools/testing/selftests/kdbus/test-message.c
|
|
+++ b/tools/testing/selftests/kdbus/test-message.c
|
|
@@ -54,9 +54,12 @@ int kdbus_test_message_basic(struct kdbus_test_env *env)
|
|
KDBUS_DST_ID_BROADCAST);
|
|
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);
|
|
+ /* Make sure that we do get our own broadcasts */
|
|
+ ret = kdbus_msg_recv(sender, &msg, &offset);
|
|
+ ASSERT_RETURN(ret == 0);
|
|
+ ASSERT_RETURN(msg->cookie == cookie);
|
|
+
|
|
+ kdbus_msg_free(msg);
|
|
|
|
/* ... and receive on the 2nd */
|
|
ret = kdbus_msg_recv_poll(conn, 100, &msg, &offset);
|
|
diff --git a/tools/testing/selftests/kdbus/test-policy-priv.c b/tools/testing/selftests/kdbus/test-policy-priv.c
|
|
index a318cccad0d5..0208638a7245 100644
|
|
--- a/tools/testing/selftests/kdbus/test-policy-priv.c
|
|
+++ b/tools/testing/selftests/kdbus/test-policy-priv.c
|
|
@@ -110,6 +110,12 @@ static int test_policy_priv_by_broadcast(const char *bus,
|
|
KDBUS_DST_ID_BROADCAST);
|
|
ASSERT_RETURN(ret == 0);
|
|
|
|
+ /* drop own broadcast */
|
|
+ ret = kdbus_msg_recv(child_2, &msg, NULL);
|
|
+ ASSERT_RETURN(ret == 0);
|
|
+ ASSERT_RETURN(msg->src_id == child_2->id);
|
|
+ kdbus_msg_free(msg);
|
|
+
|
|
/* Use a little bit high time */
|
|
ret = kdbus_msg_recv_poll(child_2, 1000,
|
|
&msg, NULL);
|
|
@@ -145,6 +151,12 @@ static int test_policy_priv_by_broadcast(const char *bus,
|
|
KDBUS_DST_ID_BROADCAST);
|
|
ASSERT_EXIT(ret == 0);
|
|
|
|
+ /* drop own broadcast */
|
|
+ ret = kdbus_msg_recv(child_2, &msg, NULL);
|
|
+ ASSERT_RETURN(ret == 0);
|
|
+ ASSERT_RETURN(msg->src_id == child_2->id);
|
|
+ kdbus_msg_free(msg);
|
|
+
|
|
/* Use a little bit high time */
|
|
ret = kdbus_msg_recv_poll(child_2, 1000,
|
|
&msg, NULL);
|
|
@@ -313,11 +325,6 @@ static int test_broadcast_after_policy_upload(struct kdbus_test_env *env)
|
|
* 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);
|
|
@@ -408,6 +415,15 @@ static int test_broadcast_after_policy_upload(struct kdbus_test_env *env)
|
|
0, 0, KDBUS_DST_ID_BROADCAST);
|
|
ASSERT_RETURN(ret == 0);
|
|
|
|
+ ret = kdbus_msg_recv_poll(owner_a, 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);
|
|
+
|
|
ret = kdbus_msg_recv_poll(owner_b, 100, &msg, NULL);
|
|
ASSERT_RETURN(ret == 0);
|
|
ASSERT_RETURN(msg->cookie == expected_cookie);
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From ebca68e897b944559d77d7165bf3d1b173257e72 Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Sat, 4 Jul 2015 19:41:28 +0200
|
|
Subject: [PATCH 30/34] kdbus: pass metadata which a receiver could read via
|
|
/proc
|
|
|
|
Current kdbus behavior is to only pass metadata to a receiver if the
|
|
sender actively agreed on sending the metadata (via send-attach-flags).
|
|
We now extend this logic in 2 ways:
|
|
|
|
- Implicit metadata is always sent, if requested by the receiver. This
|
|
includes basic process credentials, pids and connection metadata. The
|
|
latter is kdbus-specific information and always public (as it can be
|
|
retrieved via other means), and basic process credentials and pids are
|
|
required for compatibility to UDS.
|
|
|
|
- Any extended process credentials were only passed if told by the
|
|
sender. We now extend this logic to pass them also if the receiver
|
|
could have read that information via /proc. We verify that the sending
|
|
process has a pid in the pid-namespace of the receiver (i.e., it shows
|
|
up in /proc mounted in the receiver namespace) and verify the hide_pid
|
|
restrictions.
|
|
|
|
With this change, we make kdbus in-line with /proc access checks. We
|
|
basically say that we assume anyone with access to kdbusfs also has access
|
|
to /proc of its *own* pid-namespace (which should be given in any system
|
|
setup). We then automatically pass all information *as if* the receiver
|
|
would read them immediately on message-retrieval via /proc. But kdbus now
|
|
fixes the race-gap between message transmittal and /proc access.
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
---
|
|
ipc/kdbus/connection.c | 2 +
|
|
ipc/kdbus/connection.h | 2 +
|
|
ipc/kdbus/message.c | 2 +-
|
|
ipc/kdbus/metadata.c | 189 ++++++++++++++++++++++++++++++++++++++++++++++---
|
|
ipc/kdbus/metadata.h | 5 +-
|
|
5 files changed, 187 insertions(+), 13 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/connection.c b/ipc/kdbus/connection.c
|
|
index 2b334b04dd9d..461b8d66b2b9 100644
|
|
--- a/ipc/kdbus/connection.c
|
|
+++ b/ipc/kdbus/connection.c
|
|
@@ -132,6 +132,7 @@ static struct kdbus_conn *kdbus_conn_new(struct kdbus_ep *ep, bool privileged,
|
|
conn->cred = get_current_cred();
|
|
conn->user_ns = get_user_ns(current_user_ns());
|
|
conn->pid_ns = get_pid_ns(task_active_pid_ns(current));
|
|
+ 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);
|
|
@@ -282,6 +283,7 @@ static void __kdbus_conn_free(struct kref *kref)
|
|
kdbus_pool_free(conn->pool);
|
|
kdbus_ep_unref(conn->ep);
|
|
path_put(&conn->root_path);
|
|
+ put_pid(conn->pid);
|
|
put_pid_ns(conn->pid_ns);
|
|
put_user_ns(conn->user_ns);
|
|
put_cred(conn->cred);
|
|
diff --git a/ipc/kdbus/connection.h b/ipc/kdbus/connection.h
|
|
index b1769b877504..9a1b13d69085 100644
|
|
--- a/ipc/kdbus/connection.h
|
|
+++ b/ipc/kdbus/connection.h
|
|
@@ -61,6 +61,7 @@ struct kdbus_staging;
|
|
* @cred: The credentials of the connection at creation time
|
|
* @user_ns: User namespace at creation time
|
|
* @pid_ns: Pid namespace at creation time
|
|
+ * @pid: Pid at creation time
|
|
* @root_path: Root path at creation time
|
|
* @name_count: Number of owned well-known names
|
|
* @request_count: Number of pending requests issued by this
|
|
@@ -102,6 +103,7 @@ struct kdbus_conn {
|
|
const struct cred *cred;
|
|
struct user_namespace *user_ns;
|
|
struct pid_namespace *pid_ns;
|
|
+ struct pid *pid;
|
|
struct path root_path;
|
|
atomic_t name_count;
|
|
atomic_t request_count;
|
|
diff --git a/ipc/kdbus/message.c b/ipc/kdbus/message.c
|
|
index 72fb5908c927..64763ead693c 100644
|
|
--- a/ipc/kdbus/message.c
|
|
+++ b/ipc/kdbus/message.c
|
|
@@ -849,7 +849,7 @@ static int kdbus_staging_collect_metadata(struct kdbus_staging *staging,
|
|
int ret;
|
|
|
|
if (src)
|
|
- attach = kdbus_meta_calc_attach_flags(src, dst);
|
|
+ attach = kdbus_meta_msg_mask(src, dst);
|
|
else
|
|
attach = KDBUS_ATTACH_TIMESTAMP; /* metadata for kernel msgs */
|
|
|
|
diff --git a/ipc/kdbus/metadata.c b/ipc/kdbus/metadata.c
|
|
index 43e5f4564925..5150932cb257 100644
|
|
--- a/ipc/kdbus/metadata.c
|
|
+++ b/ipc/kdbus/metadata.c
|
|
@@ -1156,18 +1156,187 @@ exit:
|
|
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_calc_attach_flags() - calculate attach flags for a sender
|
|
- * and a receiver
|
|
- * @sender: Sending connection
|
|
- * @receiver: Receiving connection
|
|
+ * 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
|
|
*
|
|
- * Return: the attach flags both the sender and the receiver have opted-in
|
|
- * for.
|
|
+ * 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_calc_attach_flags(const struct kdbus_conn *sender,
|
|
- const struct kdbus_conn *receiver)
|
|
+u64 kdbus_meta_msg_mask(const struct kdbus_conn *snd,
|
|
+ const struct kdbus_conn *rcv)
|
|
{
|
|
- return atomic64_read(&sender->attach_flags_send) &
|
|
- atomic64_read(&receiver->attach_flags_recv);
|
|
+ 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
|
|
index 8fb2a9f7f82e..dba7cc7fdbcb 100644
|
|
--- a/ipc/kdbus/metadata.h
|
|
+++ b/ipc/kdbus/metadata.h
|
|
@@ -79,7 +79,8 @@ int kdbus_meta_emit(struct kdbus_meta_proc *mp,
|
|
u64 mask,
|
|
struct kdbus_item **out_items,
|
|
size_t *out_size);
|
|
-u64 kdbus_meta_calc_attach_flags(const struct kdbus_conn *sender,
|
|
- const struct kdbus_conn *receiver);
|
|
+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
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From 087d17ac489a3c8f7c35bb5faf4c1bc858fc0978 Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Mon, 6 Jul 2015 16:52:44 +0200
|
|
Subject: [PATCH 31/34] kdbus: drop redundant ns pointers
|
|
|
|
We already pin 'cred' and 'pid', so they provide the same information as
|
|
we store in 'user_ns' and 'pid_ns'. Drop the redundant information to
|
|
reduce memory consumption of kdbus_conn.
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
---
|
|
ipc/kdbus/connection.c | 4 ----
|
|
ipc/kdbus/connection.h | 4 ----
|
|
ipc/kdbus/metadata.c | 4 ++--
|
|
3 files changed, 2 insertions(+), 10 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/connection.c b/ipc/kdbus/connection.c
|
|
index 461b8d66b2b9..d94b417e0f32 100644
|
|
--- a/ipc/kdbus/connection.c
|
|
+++ b/ipc/kdbus/connection.c
|
|
@@ -130,8 +130,6 @@ static struct kdbus_conn *kdbus_conn_new(struct kdbus_ep *ep, bool privileged,
|
|
atomic_set(&conn->lost_count, 0);
|
|
INIT_DELAYED_WORK(&conn->work, kdbus_reply_list_scan_work);
|
|
conn->cred = get_current_cred();
|
|
- conn->user_ns = get_user_ns(current_user_ns());
|
|
- conn->pid_ns = get_pid_ns(task_active_pid_ns(current));
|
|
conn->pid = get_pid(task_pid(current));
|
|
get_fs_root(current->fs, &conn->root_path);
|
|
init_waitqueue_head(&conn->wait);
|
|
@@ -284,8 +282,6 @@ static void __kdbus_conn_free(struct kref *kref)
|
|
kdbus_ep_unref(conn->ep);
|
|
path_put(&conn->root_path);
|
|
put_pid(conn->pid);
|
|
- put_pid_ns(conn->pid_ns);
|
|
- put_user_ns(conn->user_ns);
|
|
put_cred(conn->cred);
|
|
kfree(conn->description);
|
|
kfree(conn->quota);
|
|
diff --git a/ipc/kdbus/connection.h b/ipc/kdbus/connection.h
|
|
index 9a1b13d69085..5ee864eb0e41 100644
|
|
--- a/ipc/kdbus/connection.h
|
|
+++ b/ipc/kdbus/connection.h
|
|
@@ -59,8 +59,6 @@ struct kdbus_staging;
|
|
* @pool: The user's buffer to receive messages
|
|
* @user: Owner of the connection
|
|
* @cred: The credentials of the connection at creation time
|
|
- * @user_ns: User namespace at creation time
|
|
- * @pid_ns: Pid namespace at creation time
|
|
* @pid: Pid at creation time
|
|
* @root_path: Root path at creation time
|
|
* @name_count: Number of owned well-known names
|
|
@@ -101,8 +99,6 @@ struct kdbus_conn {
|
|
struct kdbus_pool *pool;
|
|
struct kdbus_user *user;
|
|
const struct cred *cred;
|
|
- struct user_namespace *user_ns;
|
|
- struct pid_namespace *pid_ns;
|
|
struct pid *pid;
|
|
struct path root_path;
|
|
atomic_t name_count;
|
|
diff --git a/ipc/kdbus/metadata.c b/ipc/kdbus/metadata.c
|
|
index 5150932cb257..d4973a90a81e 100644
|
|
--- a/ipc/kdbus/metadata.c
|
|
+++ b/ipc/kdbus/metadata.c
|
|
@@ -896,8 +896,8 @@ static struct kdbus_item *kdbus_write_full(struct kdbus_item **iter,
|
|
static size_t kdbus_meta_write(struct kdbus_meta_staging *staging, void *mem,
|
|
size_t size)
|
|
{
|
|
- struct user_namespace *user_ns = staging->conn->user_ns;
|
|
- struct pid_namespace *pid_ns = staging->conn->pid_ns;
|
|
+ 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;
|
|
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From c61382ef278eb8569c87e4be3077f3f9f1f2fc49 Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Mon, 6 Jul 2015 16:54:36 +0200
|
|
Subject: [PATCH 32/34] kdbus: allow match rules on msg->dst_id
|
|
|
|
This patch extends kdbus MATCH rules to allow matching on the destination
|
|
of a message. We add a KDBUS_ITEM_DST_ID type which takes a u64 and
|
|
matches on msg->dst_id.
|
|
|
|
This is required for dbus1 compatibility, as dbus1 does not apply MATCH
|
|
rules to unicast signals. Therefore, this field allows to match on
|
|
unicasts directed to the owning connection.
|
|
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
---
|
|
include/uapi/linux/kdbus.h | 1 +
|
|
ipc/kdbus/item.c | 1 +
|
|
ipc/kdbus/match.c | 18 +++++++++++++++---
|
|
3 files changed, 17 insertions(+), 3 deletions(-)
|
|
|
|
diff --git a/include/uapi/linux/kdbus.h b/include/uapi/linux/kdbus.h
|
|
index 00a6e142c977..ecffc6b13c3e 100644
|
|
--- a/include/uapi/linux/kdbus.h
|
|
+++ b/include/uapi/linux/kdbus.h
|
|
@@ -374,6 +374,7 @@ enum kdbus_item_type {
|
|
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,
|
|
diff --git a/ipc/kdbus/item.c b/ipc/kdbus/item.c
|
|
index e763083cc16c..ce78dba03426 100644
|
|
--- a/ipc/kdbus/item.c
|
|
+++ b/ipc/kdbus/item.c
|
|
@@ -147,6 +147,7 @@ int kdbus_item_validate(const struct kdbus_item *item)
|
|
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;
|
|
diff --git a/ipc/kdbus/match.c b/ipc/kdbus/match.c
|
|
index 3ae224ff9f3f..d1b300769014 100644
|
|
--- a/ipc/kdbus/match.c
|
|
+++ b/ipc/kdbus/match.c
|
|
@@ -78,6 +78,7 @@ struct kdbus_bloom_mask {
|
|
* 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 {
|
|
@@ -90,6 +91,7 @@ struct kdbus_match_rule {
|
|
u64 new_id;
|
|
};
|
|
u64 src_id;
|
|
+ u64 dst_id;
|
|
};
|
|
struct list_head rules_entry;
|
|
};
|
|
@@ -112,6 +114,7 @@ static void kdbus_match_rule_free(struct kdbus_match_rule *rule)
|
|
break;
|
|
|
|
case KDBUS_ITEM_ID:
|
|
+ case KDBUS_ITEM_DST_ID:
|
|
case KDBUS_ITEM_ID_ADD:
|
|
case KDBUS_ITEM_ID_REMOVE:
|
|
break;
|
|
@@ -215,6 +218,9 @@ static bool kdbus_match_rule_conn(const struct kdbus_match_rule *r,
|
|
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:
|
|
@@ -328,6 +334,7 @@ static int kdbus_match_db_remove_unlocked(struct kdbus_match_db *mdb,
|
|
* 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
|
|
@@ -339,9 +346,9 @@ static int kdbus_match_db_remove_unlocked(struct kdbus_match_db *mdb,
|
|
* 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 and KDBUS_ITEM_ID
|
|
- * are used to match messages from userspace, while the others apply to
|
|
- * kernel-generated notifications.
|
|
+ * 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.
|
|
*/
|
|
@@ -358,6 +365,7 @@ int kdbus_cmd_match_add(struct kdbus_conn *conn, void __user *argp)
|
|
{ .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 },
|
|
@@ -440,6 +448,10 @@ int kdbus_cmd_match_add(struct kdbus_conn *conn, void __user *argp)
|
|
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:
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From cf23304185c5787d875971cc730e29637d3f4d7e Mon Sep 17 00:00:00 2001
|
|
From: Masanari Iida <standby24x7@gmail.com>
|
|
Date: Fri, 10 Jul 2015 21:55:33 +0900
|
|
Subject: [PATCH 33/34] kdbus: Fix typo in ipc/kdbus
|
|
|
|
This patch fix spelling typos found in ipc/kdbus
|
|
|
|
Signed-off-by: Masanari Iida <standby24x7@gmail.com>
|
|
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
ipc/kdbus/endpoint.c | 2 +-
|
|
ipc/kdbus/endpoint.h | 4 ++--
|
|
ipc/kdbus/fs.c | 2 +-
|
|
ipc/kdbus/match.c | 2 +-
|
|
ipc/kdbus/node.c | 4 ++--
|
|
ipc/kdbus/policy.c | 2 +-
|
|
ipc/kdbus/pool.c | 2 +-
|
|
7 files changed, 9 insertions(+), 9 deletions(-)
|
|
|
|
diff --git a/ipc/kdbus/endpoint.c b/ipc/kdbus/endpoint.c
|
|
index 9a95a5ea84d7..977964dbb44a 100644
|
|
--- a/ipc/kdbus/endpoint.c
|
|
+++ b/ipc/kdbus/endpoint.c
|
|
@@ -78,7 +78,7 @@ static void kdbus_ep_release(struct kdbus_node *node, bool was_active)
|
|
* @gid: The gid of the node
|
|
* @is_custom: Whether this is a custom endpoint
|
|
*
|
|
- * This function will create a new enpoint with the given
|
|
+ * 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.
|
|
diff --git a/ipc/kdbus/endpoint.h b/ipc/kdbus/endpoint.h
|
|
index d31954bfba2c..bc1b94a70f93 100644
|
|
--- a/ipc/kdbus/endpoint.h
|
|
+++ b/ipc/kdbus/endpoint.h
|
|
@@ -25,7 +25,7 @@ struct kdbus_bus;
|
|
struct kdbus_user;
|
|
|
|
/**
|
|
- * struct kdbus_ep - enpoint to access a bus
|
|
+ * struct kdbus_ep - endpoint to access a bus
|
|
* @node: The kdbus node
|
|
* @lock: Endpoint data lock
|
|
* @bus: Bus behind this endpoint
|
|
@@ -33,7 +33,7 @@ struct kdbus_user;
|
|
* @policy_db: Uploaded policy
|
|
* @conn_list: Connections of this endpoint
|
|
*
|
|
- * An enpoint offers access to a bus; the default endpoint node name is "bus".
|
|
+ * 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.
|
|
*/
|
|
diff --git a/ipc/kdbus/fs.c b/ipc/kdbus/fs.c
|
|
index 205a3adaa2ab..09c480924b9e 100644
|
|
--- a/ipc/kdbus/fs.c
|
|
+++ b/ipc/kdbus/fs.c
|
|
@@ -74,7 +74,7 @@ static int fs_dir_fop_iterate(struct file *file, struct dir_context *ctx)
|
|
* 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 neccessarily unique. Therefore, llseek
|
|
+ * 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
|
|
diff --git a/ipc/kdbus/match.c b/ipc/kdbus/match.c
|
|
index d1b300769014..4ee6a1f2e1bc 100644
|
|
--- a/ipc/kdbus/match.c
|
|
+++ b/ipc/kdbus/match.c
|
|
@@ -66,7 +66,7 @@ struct kdbus_bloom_mask {
|
|
|
|
/**
|
|
* struct kdbus_match_rule - a rule appended to a match entry
|
|
- * @type: An item type to match agains
|
|
+ * @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,
|
|
diff --git a/ipc/kdbus/node.c b/ipc/kdbus/node.c
|
|
index 0d65c65d2bde..89f58bc85433 100644
|
|
--- a/ipc/kdbus/node.c
|
|
+++ b/ipc/kdbus/node.c
|
|
@@ -120,7 +120,7 @@
|
|
* 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 deactvating our node. That is, once all children are entirely
|
|
+ * 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
|
|
@@ -637,7 +637,7 @@ void kdbus_node_deactivate(struct kdbus_node *node)
|
|
kdbus_fs_flush(pos);
|
|
|
|
/*
|
|
- * If the node was activated and somone subtracted BIAS
|
|
+ * 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().
|
|
diff --git a/ipc/kdbus/policy.c b/ipc/kdbus/policy.c
|
|
index dd7fffaafa84..f2618e15e78d 100644
|
|
--- a/ipc/kdbus/policy.c
|
|
+++ b/ipc/kdbus/policy.c
|
|
@@ -344,7 +344,7 @@ err:
|
|
* 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 sur that the owner is a custom
|
|
+ * 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.
|
|
*
|
|
diff --git a/ipc/kdbus/pool.c b/ipc/kdbus/pool.c
|
|
index 45dcdea505f4..63ccd55713c7 100644
|
|
--- a/ipc/kdbus/pool.c
|
|
+++ b/ipc/kdbus/pool.c
|
|
@@ -44,7 +44,7 @@
|
|
* 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 direcly by the
|
|
+ * 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
|
|
--
|
|
2.4.3
|
|
|
|
|
|
From eee8b5b3ceaeca1b1b998552cd6c9f9dea8938e5 Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Tue, 14 Jul 2015 20:12:28 +0200
|
|
Subject: [PATCH 34/34] kdbus: properly reset metadata iovecs on multicasts
|
|
|
|
If a message is sent to multiple destinations, each destination might
|
|
request a different set of metadata. Hence, we cannot rely on each one
|
|
requesting at least a single bit.
|
|
|
|
Fix the message exporter to properly reset the metadata iovec on each
|
|
emit-iteration. Otherwise, we might end up scanning random heap memory if
|
|
a following destination does not request metadata.
|
|
|
|
Reported-by: Jan Alexander Steffens <jan.steffens@gmail.com>
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
ipc/kdbus/message.c | 10 ++++++++++
|
|
1 file changed, 10 insertions(+)
|
|
|
|
diff --git a/ipc/kdbus/message.c b/ipc/kdbus/message.c
|
|
index 64763ead693c..3520f45875df 100644
|
|
--- a/ipc/kdbus/message.c
|
|
+++ b/ipc/kdbus/message.c
|
|
@@ -946,6 +946,16 @@ struct kdbus_pool_slice *kdbus_staging_emit(struct kdbus_staging *staging,
|
|
++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 ... */
|
|
--
|
|
2.4.3
|
|
|
|
From 748cf9547ea30f5a4dfee5e97d1d2ff4b307c392 Mon Sep 17 00:00:00 2001
|
|
From: David Herrmann <dh.herrmann@gmail.com>
|
|
Date: Thu, 16 Jul 2015 17:37:29 +0200
|
|
Subject: [PATCH] kdbus: fix unused %docs make-targets
|
|
|
|
The top-level Makefile re-routes all %docs targets to the Documentation
|
|
makefiles. The DocBook makefile supports a lot more targets than the kdbus
|
|
makefile, and it seems unreasonable to expect that both will always be in
|
|
sync.
|
|
|
|
Therefore, add a fallback no-op target %docs: which is used for all
|
|
unspecified %docs targets.
|
|
|
|
We will, from time to time, add further %docs targets that make sense
|
|
(like installdocs). But there is definitely no time pressure on those.
|
|
However, we really should add this fallback now, as otherwise "make" will
|
|
print errors due to unknown targets.
|
|
|
|
Reported-by: Ulf Magnusson <ulfalizer.lkml@gmail.com>
|
|
Tested-by: Jim Davis <jim.epost@gmail.com>
|
|
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
|
|
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
---
|
|
Documentation/kdbus/Makefile | 4 ++++
|
|
1 file changed, 4 insertions(+)
|
|
|
|
diff --git a/Documentation/kdbus/Makefile b/Documentation/kdbus/Makefile
|
|
index af87641db416..8caffe565750 100644
|
|
--- a/Documentation/kdbus/Makefile
|
|
+++ b/Documentation/kdbus/Makefile
|
|
@@ -38,3 +38,7 @@ mandocs: $(MANFILES)
|
|
htmldocs: $(HTMLFILES)
|
|
|
|
clean-files := $(MANFILES) $(HTMLFILES)
|
|
+
|
|
+# we don't support other %docs targets right now
|
|
+%docs:
|
|
+ @true
|
|
--
|
|
2.4.3
|
|
|