43c4e9c549
Yes, this is a big uber-patch. But with the git-am apply method, it still splits them out in the prepped tree. There's really no advantage to listing 78 patches in the spec at this point as kdbus is likely to go all-or-nothing if/when it is merged.
40482 lines
1.2 MiB
40482 lines
1.2 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
|