diff options
author | Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> | 2018-01-05 19:46:38 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-01-05 19:46:38 +0100 |
commit | 2df46112052ee9752ce2d2920ad6184b304c77ed (patch) | |
tree | b89e539530c0485e172417c2d1b4050de2ca4bb8 | |
parent | 65d36b49508a53e56bae9609ff00fdc3de340608 (diff) | |
parent | f3c33b234d9f0256805722f02c7b4c4b59fd6de6 (diff) | |
download | systemd-2df46112052ee9752ce2d2920ad6184b304c77ed.tar.gz |
Merge pull request #7714 from poettering/sd-bus-watch-connect
many sd-bus improvements
70 files changed, 3115 insertions, 1053 deletions
@@ -35,6 +35,9 @@ Features: * add bpf-based implementation of devices cgroup controller logic for compat with cgroupsv2 as supported by newest kernel +* sd-bus: add vtable flag, that may be used to request client creds implicitly + and asynchronously before dispatching the operation + * implement transient socket unit. * make systemd-run create transient path and socket unit. @@ -56,8 +59,6 @@ Features: the runtime dir as we maintain for the fdstore: i.e. keep it around as long as the unit is running or has a job queued. -* add async version of sd_bus_add_match and make use of that - * support projid-based quota in machinectl for containers, and then drop implicit btrfs loopback magic in machined @@ -495,14 +496,12 @@ Features: - see if we can introduce a new sd_bus_get_owner_machine_id() call to retrieve the machine ID of the machine of the bus itself - see if we can drop more message validation on the sending side - add API to clone sd_bus_message objects - - make AddMatch calls on dbus1 transports async? - longer term: priority inheritance - dbus spec updates: - NameLost/NameAcquired obsolete - GVariant - path escaping - update systemd.special(7) to mention that dbus.socket is only about the compatibility socket now - - test bloom filter generation indexes * sd-event - allow multiple signal handlers per signal? diff --git a/man/busctl.xml b/man/busctl.xml index 0c0d28b5d3..2320cb8ed3 100644 --- a/man/busctl.xml +++ b/man/busctl.xml @@ -241,6 +241,16 @@ </listitem> </varlistentry> + <varlistentry> + <term><option>--watch-bind=</option><replaceable>BOOL</replaceable></term> + + <listitem> + <para>Controls whether to wait for the specified <constant>AF_UNIX</constant> bus socket to appear in the + file system before connecting to it. Defaults to off. When enabled, the tool will watch the file system until + the socket is created and then connect to it.</para> + </listitem> + </varlistentry> + <xi:include href="user-system-options.xml" xpointer="user" /> <xi:include href="user-system-options.xml" xpointer="system" /> <xi:include href="user-system-options.xml" xpointer="host" /> diff --git a/man/rules/meson.build b/man/rules/meson.build index 54c5a9d923..bfc267b544 100644 --- a/man/rules/meson.build +++ b/man/rules/meson.build @@ -105,7 +105,12 @@ manpages = [ ['sd-journal', '3', [], ''], ['sd-login', '3', [], 'HAVE_PAM'], ['sd_booted', '3', [], ''], - ['sd_bus_add_match', '3', [], ''], + ['sd_bus_add_match', + '3', + ['sd_bus_add_match_async', + 'sd_bus_match_signal', + 'sd_bus_match_signal_async'], + ''], ['sd_bus_creds_get_pid', '3', ['sd_bus_creds_get_audit_login_uid', @@ -181,6 +186,7 @@ manpages = [ ['SD_BUS_ERROR_END', 'SD_BUS_ERROR_MAP', 'sd_bus_error_map'], ''], ['sd_bus_get_fd', '3', [], ''], + ['sd_bus_is_open', '3', ['sd_bus_is_ready'], ''], ['sd_bus_message_append', '3', ['sd_bus_message_appendv'], ''], ['sd_bus_message_append_array', '3', @@ -200,6 +206,7 @@ manpages = [ ['sd_bus_message_get_realtime_usec', 'sd_bus_message_get_seqnum'], ''], ['sd_bus_message_read_basic', '3', [], ''], + ['sd_bus_message_set_destination', '3', ['sd_bus_message_set_sender'], ''], ['sd_bus_negotiate_fds', '3', ['sd_bus_negotiate_creds', 'sd_bus_negotiate_timestamp'], @@ -210,7 +217,15 @@ manpages = [ ['sd_bus_path_decode', 'sd_bus_path_decode_many', 'sd_bus_path_encode_many'], ''], ['sd_bus_process', '3', [], ''], - ['sd_bus_request_name', '3', ['sd_bus_release_name'], ''], + ['sd_bus_request_name', + '3', + ['sd_bus_release_name', + 'sd_bus_release_name_async', + 'sd_bus_request_name_async'], + ''], + ['sd_bus_set_connected_signal', '3', ['sd_bus_get_connected_signal'], ''], + ['sd_bus_set_sender', '3', ['sd_bus_get_sender'], ''], + ['sd_bus_set_watch_bind', '3', ['sd_bus_get_watch_bind'], ''], ['sd_bus_track_add_name', '3', ['sd_bus_track_add_sender', @@ -655,6 +670,7 @@ manpages = [ ['systemd', '1', ['init'], ''], ['systemd.automount', '5', [], ''], ['systemd.device', '5', [], ''], + ['systemd.dnssd', '5', [], 'ENABLE_RESOLVE'], ['systemd.environment-generator', '7', [], 'ENABLE_ENVIRONMENT_D'], ['systemd.exec', '5', [], ''], ['systemd.generator', '7', [], ''], @@ -663,7 +679,6 @@ manpages = [ ['systemd.link', '5', [], ''], ['systemd.mount', '5', [], ''], ['systemd.netdev', '5', [], 'ENABLE_NETWORKD'], - ['systemd.dnssd', '5', [], 'ENABLE_RESOLVE'], ['systemd.network', '5', [], 'ENABLE_NETWORKD'], ['systemd.nspawn', '5', [], ''], ['systemd.offline-updates', '7', [], ''], diff --git a/man/runlevel.xml b/man/runlevel.xml index b3d90d8ff4..596307af9f 100644 --- a/man/runlevel.xml +++ b/man/runlevel.xml @@ -173,10 +173,9 @@ <variablelist> <varlistentry> - <term><filename>/var/run/utmp</filename></term> + <term><filename>/run/utmp</filename></term> - <listitem><para>The utmp database <command>runlevel</command> - reads the previous and current runlevel + <listitem><para>The utmp database <command>runlevel</command> reads the previous and current runlevel from.</para></listitem> </varlistentry> </variablelist> diff --git a/man/sd_bus_add_match.xml b/man/sd_bus_add_match.xml index 4772c738ac..f11d078284 100644 --- a/man/sd_bus_add_match.xml +++ b/man/sd_bus_add_match.xml @@ -45,8 +45,11 @@ <refnamediv> <refname>sd_bus_add_match</refname> + <refname>sd_bus_add_match_async</refname> + <refname>sd_bus_match_signal</refname> + <refname>sd_bus_match_signal_async</refname> - <refpurpose>Add a match rule for message dispatching</refpurpose> + <refpurpose>Add a match rule for incoming message dispatching</refpurpose> </refnamediv> <refsynopsisdiv> @@ -54,6 +57,13 @@ <funcsynopsisinfo>#include <systemd/sd-bus.h></funcsynopsisinfo> <funcprototype> + <funcdef>typedef int (*<function>sd_bus_message_handler_t</function>)</funcdef> + <paramdef>sd_bus_message *<parameter>m</parameter></paramdef> + <paramdef>void *<parameter>userdata</parameter></paramdef> + <paramdef>sd_bus_error *<parameter>ret_error</parameter></paramdef> + </funcprototype> + + <funcprototype> <funcdef>int <function>sd_bus_add_match</function></funcdef> <paramdef>sd_bus *<parameter>bus</parameter></paramdef> <paramdef>sd_bus_slot **<parameter>slot</parameter></paramdef> @@ -63,57 +73,111 @@ </funcprototype> <funcprototype> - <funcdef>typedef int (*<function>sd_bus_message_handler_t</function>)</funcdef> - <paramdef>sd_bus_message *<parameter>m</parameter></paramdef> + <funcdef>int <function>sd_bus_add_match_async</function></funcdef> + <paramdef>sd_bus *<parameter>bus</parameter></paramdef> + <paramdef>sd_bus_slot **<parameter>slot</parameter></paramdef> + <paramdef>const char *<parameter>match</parameter></paramdef> + <paramdef>sd_bus_message_handler_t <parameter>callback</parameter></paramdef> + <paramdef>sd_bus_message_handler_t <parameter>install_callback</parameter></paramdef> + <paramdef>void *<parameter>userdata</parameter></paramdef> + </funcprototype> + + <funcprototype> + <funcdef>int <function>sd_bus_match_signal</function></funcdef> + <paramdef>sd_bus *<parameter>bus</parameter></paramdef> + <paramdef>sd_bus_slot **<parameter>slot</parameter></paramdef> + <paramdef>const char *<parameter>sender</parameter></paramdef> + <paramdef>const char *<parameter>path</parameter></paramdef> + <paramdef>const char *<parameter>interface</parameter></paramdef> + <paramdef>const char *<parameter>member</parameter></paramdef> + <paramdef>sd_bus_message_handler_t <parameter>callback</parameter></paramdef> + <paramdef>void *<parameter>userdata</parameter></paramdef> + </funcprototype> + + <funcprototype> + <funcdef>int <function>sd_bus_match_signal_async</function></funcdef> + <paramdef>sd_bus *<parameter>bus</parameter></paramdef> + <paramdef>sd_bus_slot **<parameter>slot</parameter></paramdef> + <paramdef>const char *<parameter>sender</parameter></paramdef> + <paramdef>const char *<parameter>path</parameter></paramdef> + <paramdef>const char *<parameter>interface</parameter></paramdef> + <paramdef>const char *<parameter>member</parameter></paramdef> + <paramdef>sd_bus_message_handler_t <parameter>callback</parameter></paramdef> + <paramdef>sd_bus_message_handler_t <parameter>install_callback</parameter></paramdef> <paramdef>void *<parameter>userdata</parameter></paramdef> - <paramdef>sd_bus_error *<parameter>ret_error</parameter></paramdef> </funcprototype> + </funcsynopsis> </refsynopsisdiv> <refsect1> <title>Description</title> - <para> - <function>sd_bus_add_match()</function> installs a match rule for incoming messages received on the specified bus - connection object <parameter>bus</parameter>. The syntax of the match rule expression passed in - <parameter>match</parameter> is described in the <ulink - url="https://dbus.freedesktop.org/doc/dbus-specification.html">D-Bus Specification</ulink>. The specified handler - function <parameter>callback</parameter> is called for eaching incoming message matching the specified - expression, the <parameter>userdata</parameter> parameter is passed as-is to the callback function. + <para><function>sd_bus_add_match()</function> installs a match rule for messages received on the specified bus + connection object <parameter>bus</parameter>. The syntax of the match rule expression passed in + <parameter>match</parameter> is described in the <ulink + url="https://dbus.freedesktop.org/doc/dbus-specification.html">D-Bus Specification</ulink>. The specified handler + function <parameter>callback</parameter> is called for eaching incoming message matching the specified expression, + the <parameter>userdata</parameter> parameter is passed as-is to the callback function. The match is installed + synchronously when connected to a bus broker, i.e. the call sends a control message requested the match to be added + to the broker and waits until the broker confirms the match has been installed successfully.</para> + + <para><function>sd_bus_add_match_async()</function> operates very similar to + <function>sd_bus_match_signal()</function>, however it installs the match asynchronously, in a non-blocking + fashion: a request is sent to the broker, but the call does not wait for a response. The + <parameter>install_callback</parameter> function is called when the response is later received, with the response + message from the broker as parameter. If this function is specified as <constant>NULL</constant> a default + implementation is used that terminates the bus connection should installing the match fail.</para> + + <para><function>sd_bus_match_signal()</function> is very similar to <function>sd_bus_add_match()</function>, but + only matches signals, and instead of a match expression accepts four parameters: <parameter>sender</parameter> (the + service name of the sender), <parameter>path</parameter> (the object path of the emitting object), + <parameter>interface</parameter> (the interface the signal belongs to), <parameter>member</parameter> (the signal + name), from which the match string is internally generated. Optionally, these parameters may be specified as + <constant>NULL</constant> in which case the relevant field of incoming signals is not tested.</para> + + <para><function>sd_bus_match_signal_async()</function> is combines the signal matching logic of + <function>sd_bus_match_signal()</function> with the asynchronous behaviour of + <function>sd_bus_add_match_async()</function>.</para> + + <para>On success, and if non-<constant>NULL</constant>, the <parameter>slot</parameter> return parameter will be + set to a slot object that may be used as a reference to the installed match, and may be utilized to remove it again + at a later time with + <citerefentry><refentrytitle>sd_bus_slot_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>. If specified + as <constant>NULL</constant> the lifetime of the match is bound to the lifetime of the bus object itself, and the + match cannot be removed independently.</para> + + <para>The message <parameter>m</parameter> passed to the callback is only borrowed, that is, the callback should + not call <citerefentry><refentrytitle>sd_bus_message_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry> + on it. If the callback wants to hold on to the message beyond the lifetime of the callback, it needs to call + <citerefentry><refentrytitle>sd_bus_message_ref</refentrytitle><manvolnum>3</manvolnum></citerefentry> to create a + new reference.</para> + + <para>If an error occurs during the callback invocation, the callback should return a negative error number + (optionally, a more precise error may be returned in <parameter>ret_error</parameter>, as well). If it wants other + callbacks that match the same rule to be called, it should return 0. Otherwise it should return a positive integer. </para> - <para> - On success, and if non-<constant>NULL</constant>, the <parameter>slot</parameter> return parameter will be set to - a slot object that may be used as a reference to the installed match, and may be utilized to remove it again at a - later time with - <citerefentry><refentrytitle>sd_bus_slot_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>. If - specified as <constant>NULL</constant> the lifetime of the match is bound to the lifetime of the bus object itself, and the match - cannot be removed independently. - </para> + <para>If the <parameter>bus</parameter> refers to a direct connection (i.e. not a bus connection, as set with + <citerefentry><refentrytitle>sd_bus_set_bus_client</refentrytitle><manvolnum>3</manvolnum></citerefentry>) the + match is only installed on the client side, and the synchronous and asynchronous functions operate the same.</para> + </refsect1> - <para> - The message <parameter>m</parameter> passed to the callback is only borrowed, that is, the callback should not - call <citerefentry><refentrytitle>sd_bus_message_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry> on - it. If the callback wants to hold on to the message beyond the lifetime of the callback, it needs to call - <citerefentry><refentrytitle>sd_bus_message_ref</refentrytitle><manvolnum>3</manvolnum></citerefentry> to create - a new reference. - </para> + <refsect1> + <title>Return Value</title> <para> - If an error occurs during the callback invocation, the callback should return a negative error number. If it - wants other callbacks that match the same rule to be called, it should return 0. Otherwise it should return a - positive integer. + On success, <function>sd_bus_add_match()</function> and the other calls return 0 or a positive integer. On + failure, they return a negative errno-style error code. </para> </refsect1> <refsect1> - <title>Return Value</title> + <title>Notes</title> - <para> - On success, <function>sd_bus_add_match()</function> returns 0 or a positive integer. On failure, it returns a - negative errno-style error code. - </para> + <para><function>sd_bus_add_match()</function> and the other functions described here are available as a shared + library, which can be compiled and linked to with the <constant>libsystemd</constant> <citerefentry + project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry> file.</para> </refsect1> <refsect1> @@ -121,7 +185,10 @@ <para> <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>, - <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry> + <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd_bus_slot_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd_bus_message_ref</refentrytitle><manvolnum>3</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd_bus_set_bus_client</refentrytitle><manvolnum>3</manvolnum></citerefentry> </para> </refsect1> diff --git a/man/sd_bus_is_open.xml b/man/sd_bus_is_open.xml new file mode 100644 index 0000000000..9bc671fc37 --- /dev/null +++ b/man/sd_bus_is_open.xml @@ -0,0 +1,137 @@ +<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*--> +<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" +"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> + +<!-- + SPDX-License-Identifier: LGPL-2.1+ + + This file is part of systemd. + + Copyright 2017 Lennart Poettering + + systemd 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. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +--> + +<refentry id="sd_bus_is_open"> + + <refentryinfo> + <title>sd_bus_is_open</title> + <productname>systemd</productname> + + <authorgroup> + <author> + <contrib>Developer</contrib> + <firstname>Lennart</firstname> + <surname>Poettering</surname> + <email>lennart@poettering.net</email> + </author> + </authorgroup> + </refentryinfo> + + <refmeta> + <refentrytitle>sd_bus_is_open</refentrytitle> + <manvolnum>3</manvolnum> + </refmeta> + + <refnamediv> + <refname>sd_bus_is_open</refname> + <refname>sd_bus_is_ready</refname> + + <refpurpose>Check whether the a bus connection is open or ready.</refpurpose> + </refnamediv> + + <refsynopsisdiv> + <funcsynopsis> + <funcsynopsisinfo>#include <systemd/sd-bus.h></funcsynopsisinfo> + + <funcprototype> + <funcdef>int <function>sd_bus_is_open</function></funcdef> + <paramdef>sd_bus *<parameter>bus</parameter></paramdef> + </funcprototype> + + <funcprototype> + <funcdef>int <function>sd_bus_is_ready</function></funcdef> + <paramdef>sd_bus *<parameter>bus</parameter></paramdef> + </funcprototype> + + </funcsynopsis> + </refsynopsisdiv> + + <refsect1> + <title>Description</title> + + <para><function>sd_bus_is_open()</function> checks whether the specified bus connection is open, i.e. in the + process of being established, already established or in the process of being torn down. It returns zero when the + connection has not been started yet + (i.e. <citerefentry><refentrytitle>sd_bus_start</refentrytitle><manvolnum>3</manvolnum></citerefentry> or some + equivalent call has not been invoked yet), or is fully terminated again (for example after + <citerefentry><refentrytitle>sd_bus_close</refentrytitle><manvolnum>3</manvolnum></citerefentry>), it returns + positive otherwise.</para> + + <para><function>sd_bus_is_ready()</function> checks whether the specified connection is fully established, + i.e. completed the connection and authentication phases of the protocol and received the + <function>Hello()</function> method call response, and is not in the process of being torn down again. It returns + zero outside of this state, and positive otherwise. Effectively, this function returns positive while regular + messages can be sent or received on the connection.</para> + + <para>To be notified when the connection is fully established, use + <citerefentry><refentrytitle>sd_bus_set_connected_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry> and + install a match for the <function>Connected()</function> signal on the + <literal>org.freedesktop.DBus.Local</literal> interface. To be notified when the connection is torn down again, + install a match for the <function>Disconnected()</function> signal on the + <literal>org.freedesktop.DBus.Local</literal> interface.</para> + </refsect1> + + <refsect1> + <title>Return Value</title> + + <para>On success, these functions return 0 or a positive integer. On failure, they return a negative errno-style + error code.</para> + </refsect1> + + <refsect1> + <title>Errors</title> + + <para>Returned errors may indicate the following problems:</para> + + <variablelist> + <varlistentry> + <term><constant>-ECHILD</constant></term> + + <listitem><para>The bus connection has been created in a different process.</para></listitem> + </varlistentry> + </variablelist> + </refsect1> + + <refsect1> + <title>Notes</title> + + <para><function>sd_bus_is_open()</function> and <function>sd_bus_is_ready()</function> are available as + a shared library, which can be compiled and linked to with the <constant>libsystemd</constant> <citerefentry + project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry> file.</para> + </refsect1> + + <refsect1> + <title>See Also</title> + + <para> + <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd_bus_start</refentrytitle><manvolnum>3</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd_bus_close</refentrytitle><manvolnum>3</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd_bus_set_connected_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry> + </para> + </refsect1> + +</refentry> diff --git a/man/sd_bus_message_set_destination.xml b/man/sd_bus_message_set_destination.xml new file mode 100644 index 0000000000..ff69a23152 --- /dev/null +++ b/man/sd_bus_message_set_destination.xml @@ -0,0 +1,137 @@ +<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" + "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> + +<!-- + SPDX-License-Identifier: LGPL-2.1+ + + This file is part of systemd. + + Copyright 2017 Lennart Poettering + + systemd 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. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +--> + +<refentry id="sd_bus_message_set_destination"> + + <refentryinfo> + <title>sd_bus_message_set_destination</title> + <productname>systemd</productname> + + <authorgroup> + <author> + <contrib>Developer</contrib> + <firstname>Lennart</firstname> + <surname>Poettering</surname> + <email>lennart@poettering.net</email> + </author> + </authorgroup> + </refentryinfo> + + <refmeta> + <refentrytitle>sd_bus_message_set_destination</refentrytitle> + <manvolnum>3</manvolnum> + </refmeta> + + <refnamediv> + <refname>sd_bus_message_set_destination</refname> + <refname>sd_bus_message_set_sender</refname> + <refpurpose>Set the destination or sender service name of a bus message</refpurpose> + </refnamediv> + + <refsynopsisdiv> + <funcsynopsis> + <funcsynopsisinfo>#include <systemd/sd-bus.h></funcsynopsisinfo> + + <funcprototype> + <funcdef>int <function>sd_bus_message_set_destination</function></funcdef> + <paramdef>sd_bus_message *<parameter>message</parameter></paramdef> + <paramdef>const char *<parameter>destination</parameter></paramdef> + </funcprototype> + + <funcprototype> + <funcdef>int <function>sd_bus_message_set_sender</function></funcdef> + <paramdef>sd_bus_message *<parameter>message</parameter></paramdef> + <paramdef>const char *<parameter>sender</parameter></paramdef> + </funcprototype> + </funcsynopsis> + </refsynopsisdiv> + + <refsect1> + <title>Description</title> + + <para><function>sd_bus_message_set_destination()</function> sets the destination service name for the specified bus + message object. The specified name must be a valid unique or well-known service name.</para> + + <para><function>sd_bus_message_set_sender()</function> sets the sender service name for the specified bus message + object. The specified name must be a valid unique or well-known service name. This function is useful only for + messages to send on direct connections as for connections to bus brokers the broker will fill in the destination + field anyway, and the sender field set by original sender is ignored.</para> + </refsect1> + + <refsect1> + <title>Return Value</title> + + <para>On success, these calls return 0 or a positive integer. On failure, these calls return a negative errno-style + error code.</para> + </refsect1> + + <refsect1> + <title>Errors</title> + + <para>Returned errors may indicate the following problems:</para> + + <variablelist> + <varlistentry> + <term><constant>-EINVAL</constant></term> + + <listitem><para>A specified parameter is invalid.</para></listitem> + </varlistentry> + + <varlistentry> + <term><constant>-EPERM</constant></term> + + <listitem><para>The message is already sealed.</para></listitem> + </varlistentry> + + <varlistentry> + <term><constant>-EEXIST</constant></term> + + <listitem><para>The message already has a destination or sender field set.</para></listitem> + </varlistentry> + </variablelist> + </refsect1> + + <refsect1> + <title>Notes</title> + + <para>The <function>sd_bus_message_set_destination()</function> and + <function>sd_bus_message_set_sender()</function> interfaces + are available as a shared library, which can be compiled and + linked to with the + <constant>libsystemd</constant> <citerefentry project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry> + file.</para> + </refsect1> + + <refsect1> + <title>See Also</title> + + <para> + <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd_bus_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd_bus_set_sender</refentrytitle><manvolnum>3</manvolnum></citerefentry> + </para> + </refsect1> + +</refentry> diff --git a/man/sd_bus_request_name.xml b/man/sd_bus_request_name.xml index f44cfdb1ac..9e668f0b02 100644 --- a/man/sd_bus_request_name.xml +++ b/man/sd_bus_request_name.xml @@ -46,7 +46,9 @@ <refnamediv> <refname>sd_bus_request_name</refname> + <refname>sd_bus_request_name_async</refname> <refname>sd_bus_release_name</refname> + <refname>sd_bus_release_name_async</refname> <refpurpose>Request or release a well-known service name on a bus</refpurpose> </refnamediv> @@ -62,70 +64,102 @@ </funcprototype> <funcprototype> + <funcdef>int <function>sd_bus_request_name_async</function></funcdef> + <paramdef>sd_bus *<parameter>bus</parameter></paramdef> + <paramdef>sd_bus_slot **<parameter>slot</parameter></paramdef> + <paramdef>const char *<parameter>name</parameter></paramdef> + <paramdef>uint64_t <parameter>flags</parameter></paramdef> + <paramdef>sd_bus_message_handler_t <parameter>callback</parameter></paramdef> + <paramdef>void *<parameter>userdata</parameter></paramdef> + </funcprototype> + + <funcprototype> <funcdef>int <function>sd_bus_release_name</function></funcdef> <paramdef>sd_bus *<parameter>bus</parameter></paramdef> <paramdef>const char *<parameter>name</parameter></paramdef> </funcprototype> + + <funcprototype> + <funcdef>int <function>sd_bus_release_name_async</function></funcdef> + <paramdef>sd_bus *<parameter>bus</parameter></paramdef> + <paramdef>sd_bus_slot **<parameter>slot</parameter></paramdef> + <paramdef>const char *<parameter>name</parameter></paramdef> + <paramdef>sd_bus_message_handler_t <parameter>callback</parameter></paramdef> + <paramdef>void *<parameter>userdata</parameter></paramdef> + </funcprototype> </funcsynopsis> </refsynopsisdiv> <refsect1> <title>Description</title> - <para><function>sd_bus_request_name()</function> requests a - well-known service name on a bus. It takes a bus connection, a - valid bus name and a flags parameter. The flags parameter is a - combination of the following flags:</para> + <para><function>sd_bus_request_name()</function> requests a well-known service name on a bus. It takes a bus + connection, a valid bus name and a flags parameter. The flags parameter is a combination of the following + flags:</para> <variablelist> <varlistentry> <term><varname>SD_BUS_NAME_ALLOW_REPLACEMENT</varname></term> - <listitem><para>After acquiring the name successfully, permit - other peers to take over the name when they try to acquire it - with the <varname>SD_BUS_NAME_REPLACE_EXISTING</varname> flag - set. If <varname>SD_BUS_NAME_ALLOW_REPLACEMENT</varname> is - not set on the original request, such a request by other peers - will be denied.</para></listitem> + <listitem><para>After acquiring the name successfully, permit other peers to take over the name when they try + to acquire it with the <varname>SD_BUS_NAME_REPLACE_EXISTING</varname> flag set. If + <varname>SD_BUS_NAME_ALLOW_REPLACEMENT</varname> is not set on the original request, such a request by other + peers will be denied.</para></listitem> </varlistentry> <varlistentry> <term><varname>SD_BUS_NAME_REPLACE_EXISTING</varname></term> - <listitem><para>Take over the name if it is already acquired - by another peer, and that other peer has permitted takeover by - setting <varname>SD_BUS_NAME_ALLOW_REPLACEMENT</varname> while - acquiring it.</para></listitem> + <listitem><para>Take over the name if it is already acquired by another peer, and that other peer has permitted + takeover by setting <varname>SD_BUS_NAME_ALLOW_REPLACEMENT</varname> while acquiring it.</para></listitem> </varlistentry> <varlistentry> <term><varname>SD_BUS_NAME_QUEUE</varname></term> - <listitem><para>Queue the acquisition of the name when the - name is already taken.</para></listitem> + <listitem><para>Queue the acquisition of the name when the name is already taken.</para></listitem> </varlistentry> </variablelist> - <para><function>sd_bus_release_name()</function> releases an - acquired well-known name. It takes a bus connection and a valid - bus name as parameters.</para> + <para><function>sd_bus_request_name()</function> operates in a synchronous fashion: a message requesting the name + is sent to the bus broker, and the call waits until the broker responds.</para> + + <para><function>sd_bus_request_name_async()</function> is an asynchronous version of of + <function>sd_bus_release_name()</function>. Instead of waiting for the request to complete, the request message is + enqueued. The specified <parameter>callback</parameter> will be called when the broker's response is received. If + the parameter is specified as <constant>NULL</constant> a default implementation is used instead which will + terminate the connection when the name cannot be acquired. The function returns a slot object in its + <parameter>slot</parameter> parameter — if it is passed as non-<constant>NULL</constant> — which may be used as a + reference to the name request operation. Use + <citerefentry><refentrytitle>sd_bus_slot_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry> to destroy + this reference. Note that destroying the reference will not unregister the name, but simply ensure the specified + callback is no longer called.</para> + + <para><function>sd_bus_release_name()</function> releases an acquired well-known name. It takes a bus connection + and a valid bus name as parameters. This function operates synchronously, sending a release request message to the + bus broker and waiting for it to reply.</para> + + <para><function>sd_bus_release_name_async()</function> is an asynchronous version of + <function>sd_bus_release_name()</function>. The specified <parameter>callback</parameter> function is called when + the name has been released successfully. If specified as <constant>NULL</constant> a generic implementation is used + that ignores the result of the operation. As above, the <parameter>slot</parameter> (if + non-<constant>NULL</constant>) is set to an object that may be used to reference the operation.</para> + + <para>These functions are supported only on bus connections, i.e. connections to a bus broker and not on direct + connections.</para> </refsect1> <refsect1> <title>Return Value</title> - <para>On success, these calls return 0 or a positive integer. On - failure, these calls return a negative errno-style error - code.</para> - - <para>If <varname>SD_BUS_NAME_QUEUE</varname> is specified, - <function>sd_bus_request_name()</function> will return 0 when the - name is already taken by another peer and the client has been - added to the queue for the name. In that case, the caller can - subscribe to <literal>NameOwnerChanged</literal> signals to be - notified when the name is successfully acquired. - <function>sd_bus_request_name()</function> returns > 0 when the - name has immediately been acquired successfully.</para> + <para>On success, these calls return 0 or a positive integer. On failure, these calls return a negative errno-style + error code.</para> + + <para>If <varname>SD_BUS_NAME_QUEUE</varname> is specified, <function>sd_bus_request_name()</function> will return + 0 when the name is already taken by another peer and the client has been added to the queue for the name. In that + case, the caller can subscribe to <literal>NameOwnerChanged</literal> signals to be notified when the name is + successfully acquired. <function>sd_bus_request_name()</function> returns > 0 when the name has immediately + been acquired successfully.</para> </refsect1> <refsect1> @@ -137,56 +171,50 @@ <varlistentry> <term><constant>-EALREADY</constant></term> - <listitem><para>The caller already is the owner of the - specified name.</para></listitem> + <listitem><para>The caller already is the owner of the specified name.</para></listitem> </varlistentry> <varlistentry> <term><constant>-EEXIST</constant></term> - <listitem><para>The name has already been acquired by a - different peer, and SD_BUS_NAME_REPLACE_EXISTING was not - specified or the other peer did not specify - SD_BUS_NAME_ALLOW_REPLACEMENT while acquiring the + <listitem><para>The name has already been acquired by a different peer, and SD_BUS_NAME_REPLACE_EXISTING was + not specified or the other peer did not specify SD_BUS_NAME_ALLOW_REPLACEMENT while acquiring the name.</para></listitem> </varlistentry> <varlistentry> <term><constant>-ESRCH</constant></term> - <listitem><para>It was attempted to release a name that is - currently not registered on the bus.</para></listitem> + <listitem><para>It was attempted to release a name that is currently not registered on the + bus.</para></listitem> </varlistentry> <varlistentry> <term><constant>-EADDRINUSE</constant></term> - <listitem><para>It was attempted to release a name that is - owned by a different peer on the bus.</para></listitem> + <listitem><para>It was attempted to release a name that is owned by a different peer on the + bus.</para></listitem> </varlistentry> <varlistentry> <term><constant>-EINVAL</constant></term> - <listitem><para>A specified parameter is invalid. This is also - generated when the requested name is a special service name - reserved by the D-Bus specification, or when the operation is - requested on a connection that does not refer to a - bus.</para></listitem> + <listitem><para>A specified parameter is invalid. This is also generated when the requested name is a special + service name reserved by the D-Bus specification, or when the operation is requested on a connection that does + not refer to a bus.</para></listitem> </varlistentry> <varlistentry> <term><constant>-ENOTCONN</constant></term> - <listitem><para>The bus connection has been - disconnected.</para></listitem> + <listitem><para>The bus connection has been disconnected.</para></listitem> </varlistentry> <varlistentry> <term><constant>-ECHILD</constant></term> - <listitem><para>The bus connection has been created in a - different process than the current one.</para></listitem> + <listitem><para>The bus connection has been created in a different process than the current + one.</para></listitem> </varlistentry> </variablelist> </refsect1> @@ -194,12 +222,9 @@ <refsect1> <title>Notes</title> - <para>The <function>sd_bus_acquire_name()</function> and - <function>sd_bus_release_name()</function> interfaces are - available as a shared library, which can be compiled and linked to - with the - <constant>libsystemd</constant> <citerefentry project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry> - file.</para> + <para>The <function>sd_bus_acquire_name()</function> and the other interfaces described here are available as a + shared library, which can be compiled and linked to with the <constant>libsystemd</constant> <citerefentry + project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry> file.</para> </refsect1> <refsect1> @@ -208,7 +233,8 @@ <para> <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>, <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>, - <citerefentry><refentrytitle>sd_bus_new</refentrytitle><manvolnum>3</manvolnum></citerefentry> + <citerefentry><refentrytitle>sd_bus_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd_bus_slot_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry> </para> </refsect1> diff --git a/man/sd_bus_set_connected_signal.xml b/man/sd_bus_set_connected_signal.xml new file mode 100644 index 0000000000..9374dac3a5 --- /dev/null +++ b/man/sd_bus_set_connected_signal.xml @@ -0,0 +1,143 @@ +<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*--> +<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" +"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> + +<!-- + SPDX-License-Identifier: LGPL-2.1+ + + This file is part of systemd. + + Copyright 2017 Lennart Poettering + + systemd 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. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +--> + +<refentry id="sd_bus_set_connected_signal"> + + <refentryinfo> + <title>sd_bus_set_connected_signal</title> + <productname>systemd</productname> + + <authorgroup> + <author> + <contrib>Developer</contrib> + <firstname>Lennart</firstname> + <surname>Poettering</surname> + <email>lennart@poettering.net</email> + </author> + </authorgroup> + </refentryinfo> + + <refmeta> + <refentrytitle>sd_bus_set_connected_signal</refentrytitle> + <manvolnum>3</manvolnum> + </refmeta> + + <refnamediv> + <refname>sd_bus_set_connected_signal</refname> + <refname>sd_bus_get_connected_signal</refname> + + <refpurpose>Control emmission of local connection establishment signal on bus connections</refpurpose> + </refnamediv> + + <refsynopsisdiv> + <funcsynopsis> + <funcsynopsisinfo>#include <systemd/sd-bus.h></funcsynopsisinfo> + + <funcprototype> + <funcdef>int <function>sd_bus_set_connected_signal</function></funcdef> + <paramdef>sd_bus *<parameter>bus</parameter></paramdef> + <paramdef>int <parameter>b</parameter></paramdef> + </funcprototype> + + <funcprototype> + <funcdef>int <function>sd_bus_get_connected_signal</function></funcdef> + <paramdef>sd_bus *<parameter>bus</parameter></paramdef> + </funcprototype> + + </funcsynopsis> + </refsynopsisdiv> + + <refsect1> + <title>Description</title> + + <para><function>sd_bus_set_connected_signal()</function> may be used to control whether a local, synthetic + <function>Connected()</function> signal message shall be generated and enqueued for dispatching when the connection + is fully established. If the <parameter>b</parameter> parameter is zero the message is not generated (the default), + otherwise it is generated.</para> + + <para><function>sd_bus_get_connected_signal()</function> may be used to query whether this feature is enabled. It + returns zero if not, positive otherwise.</para> + + <para>The <function>Connected()</function> signal message is generated from the + <literal>org.freedesktop.DBus.Local</literal> service and interface, and + <literal>/org/freedesktop/DBus/Local</literal> object path. Use + <citerefentry><refentrytitle>sd_bus_match_signal_async</refentrytitle><manvolnum>3</manvolnum></citerefentry> to + match on this signal.</para> + + <para>This message is particularly useful on slow transports where connections take a long time to be + established. This is especially the case when + <citerefentry><refentrytitle>sd_bus_set_watch_bind</refentrytitle><manvolnum>3</manvolnum></citerefentry> is + used. The signal is generated when the + <citerefentry><refentrytitle>sd_bus_is_ready</refentrytitle><manvolnum>3</manvolnum></citerefentry> returns + positive for the first time.</para> + + <para>The <function>Connected()</function> signal corresponds with the <function>Disconnected()</function> signal + that is synthesized locally when the connection is terminated. The latter is generated unconditionally however, + unlike the former which needs to be enabled explicitly before it is generated, with + <function>sd_bus_set_connected_signal()</function>.</para> + </refsect1> + + <refsect1> + <title>Return Value</title> + + <para>On success, these functions return 0 or a positive integer. On failure, they return a negative errno-style + error code.</para> + </refsect1> + + <refsect1> + <title>Errors</title> + + <para>Returned errors may indicate the following problems:</para> + + <variablelist> + <varlistentry> + <term><constant>-ECHILD</constant></term> + + <listitem><para>The bus connection has been created in a different process.</para></listitem> + </varlistentry> + </variablelist> + </refsect1> + + <refsect1> + <title>Notes</title> + + <para><function>sd_bus_set_connected_signal()</function> and <function>sd_bus_get_connected_signal()</function> are available as + a shared library, which can be compiled and linked to with the <constant>libsystemd</constant> <citerefentry + project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry> file.</para> + </refsect1> + + <refsect1> + <title>See Also</title> + + <para> + <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd_bus_match_signal_async</refentrytitle><manvolnum>3</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd_bus_set_watch_bind</refentrytitle><manvolnum>3</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd_bus_is_ready</refentrytitle><manvolnum>3</manvolnum></citerefentry> + </para> + </refsect1> + +</refentry> diff --git a/man/sd_bus_set_sender.xml b/man/sd_bus_set_sender.xml new file mode 100644 index 0000000000..38efda7286 --- /dev/null +++ b/man/sd_bus_set_sender.xml @@ -0,0 +1,136 @@ +<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*--> +<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" +"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> + +<!-- + SPDX-License-Identifier: LGPL-2.1+ + + This file is part of systemd. + + Copyright 2017 Lennart Poettering + + systemd 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. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +--> + +<refentry id="sd_bus_set_sender"> + + <refentryinfo> + <title>sd_bus_set_sender</title> + <productname>systemd</productname> + + <authorgroup> + <author> + <contrib>Developer</contrib> + <firstname>Lennart</firstname> + <surname>Poettering</surname> + <email>lennart@poettering.net</email> + </author> + </authorgroup> + </refentryinfo> + + <refmeta> + <refentrytitle>sd_bus_set_sender</refentrytitle> + <manvolnum>3</manvolnum> + </refmeta> + + <refnamediv> + <refname>sd_bus_set_sender</refname> + <refname>sd_bus_get_sender</refname> + + <refpurpose>Configure default sender for outgoing messages</refpurpose> + </refnamediv> + + <refsynopsisdiv> + <funcsynopsis> + <funcsynopsisinfo>#include <systemd/sd-bus.h></funcsynopsisinfo> + + <funcprototype> + <funcdef>int <function>sd_bus_set_sender</function></funcdef> + <paramdef>sd_bus *<parameter>bus</parameter></paramdef> + <paramdef>const char* <parameter>name</parameter></paramdef> + </funcprototype> + + <funcprototype> + <funcdef>int <function>sd_bus_get_sender</function></funcdef> + <paramdef>sd_bus *<parameter>bus</parameter></paramdef> + <paramdef>const char** <parameter>name</parameter></paramdef> + </funcprototype> + + </funcsynopsis> + </refsynopsisdiv> + + <refsect1> + <title>Description</title> + + <para><function>sd_bus_set_sender()</function> configures the default sender service name to use for outgoing + messages. The service name specified in the <parameter>name</parameter> parameter is set on all outgoing messages + that are sent on the connection and have no sender set yet, for example through + <citerefentry><refentrytitle>sd_bus_message_set_sender</refentrytitle><manvolnum>3</manvolnum></citerefentry>. Note + that this function is only supported on direct connections, i.e. not on connections to a bus broker as the broker + will fill in the sender service name automatically anyway. By default no sender name is configured, and hence + messages are sent without sender field set. If the <parameter>name</parameter> parameter is specified as + <constant>NULL</constant> the default sender service name is cleared, returning to the default state if a default + sender service name was set before. If passed as non-<constant>NULL</constant> the specified name must be a valid + unique or well-known service name.</para> + + <para><function>sd_bus_get_sender()</function> may be used to query the current default service name for outgoing + messages.</para> + </refsect1> + + <refsect1> + <title>Return Value</title> + + <para>On success, these functions return 0 or a positive integer. On failure, they return a negative errno-style + error code.</para> + </refsect1> + + <refsect1> + <title>Errors</title> + + <para>Returned errors may indicate the following problems:</para> + + <variablelist> + <varlistentry> + <term><constant>-ECHILD</constant></term> + + <listitem><para>The bus connection has been created in a different process.</para></listitem> + </varlistentry> + + <varlistentry> + <term><constant>-EPERM</constant></term> + + <listitem><para>The specified bus connection object is a not a direct but a brokered connection.</para></listitem> + </varlistentry> + </variablelist> + </refsect1> + + <refsect1> + <title>Notes</title> + + <para><function>sd_bus_set_sender()</function> and <function>sd_bus_get_sender()</function> are available as + a shared library, which can be compiled and linked to with the <constant>libsystemd</constant> <citerefentry + project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry> file.</para> + </refsect1> + + <refsect1> + <title>See Also</title> + + <para> + <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd_bus_message_set_sender</refentrytitle><manvolnum>3</manvolnum></citerefentry> + </para> + </refsect1> + +</refentry> diff --git a/man/sd_bus_set_watch_bind.xml b/man/sd_bus_set_watch_bind.xml new file mode 100644 index 0000000000..5e6a6fa588 --- /dev/null +++ b/man/sd_bus_set_watch_bind.xml @@ -0,0 +1,152 @@ +<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*--> +<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" +"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> + +<!-- + SPDX-License-Identifier: LGPL-2.1+ + + This file is part of systemd. + + Copyright 2017 Lennart Poettering + + systemd 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. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +--> + +<refentry id="sd_bus_set_watch_bind"> + + <refentryinfo> + <title>sd_bus_set_watch_bind</title> + <productname>systemd</productname> + + <authorgroup> + <author> + <contrib>Developer</contrib> + <firstname>Lennart</firstname> + <surname>Poettering</surname> + <email>lennart@poettering.net</email> + </author> + </authorgroup> + </refentryinfo> + + <refmeta> + <refentrytitle>sd_bus_set_watch_bind</refentrytitle> + <manvolnum>3</manvolnum> + </refmeta> + + <refnamediv> + <refname>sd_bus_set_watch_bind</refname> + <refname>sd_bus_get_watch_bind</refname> + + <refpurpose>Control socket binding watching on bus connections</refpurpose> + </refnamediv> + + <refsynopsisdiv> + <funcsynopsis> + <funcsynopsisinfo>#include <systemd/sd-bus.h></funcsynopsisinfo> + + <funcprototype> + <funcdef>int <function>sd_bus_set_watch_bind</function></funcdef> + <paramdef>sd_bus *<parameter>bus</parameter></paramdef> + <paramdef>int <parameter>b</parameter></paramdef> + </funcprototype> + + <funcprototype> + <funcdef>int <function>sd_bus_get_watch_bind</function></funcdef> + <paramdef>sd_bus *<parameter>bus</parameter></paramdef> + </funcprototype> + + </funcsynopsis> + </refsynopsisdiv> + + <refsect1> + <title>Description</title> + + <para><function>sd_bus_set_watch_bind()</function> may be used to enable or disable client-side watching of server + socket binding for a bus connection object. If the <parameter>b</parameter> is true, the feature is enabled, + otherwise disabled (which is the default). When enabled, and the selected bus address refers to an + <filename>AF_UNIX</filename> socket in the file system which does not exist while the connection attempt is made an + <citerefentry><refentrytitle>inotify</refentrytitle><manvolnum>7</manvolnum></citerefentry> watch is installed on + it, waiting for the socket to appear. As soon as the socket appears the connection is made. This functionality is + useful in particular in early-boot programs that need to run before the system bus is available, but want to + connect to it the instant it may be connected to.</para> + + <para><function>sd_bus_get_watch_bind()</function> may be used to query the current setting of this feature. It + returns zero when the feature is disabled, and positive if enabled.</para> + + <para>Note that no timeout is applied while it is waited for the socket to appear. This means that any synchronous + remote operation (such as + <citerefentry><refentrytitle>sd_bus_call</refentrytitle><manvolnum>3</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd_bus_add_match</refentrytitle><manvolnum>3</manvolnum></citerefentry> or + <citerefentry><refentrytitle>sd_bus_request_name</refentrytitle><manvolnum>3</manvolnum></citerefentry>), that is + used on a connection with this feature enabled that is not established yet might block unbounded if the socket is + never created. However, asynchronous remote operations (such as + <citerefentry><refentrytitle>sd_bus_send</refentrytitle><manvolnum>3</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd_bus_add_match_async</refentrytitle><manvolnum>3</manvolnum></citerefentry> or + <citerefentry><refentrytitle>sd_bus_request_match_async</refentrytitle><manvolnum>3</manvolnum></citerefentry>) do + not block in this case, and safely enqueue the requested operations to be dispatched the instant the connection is + set up.</para> + + <para>Use <citerefentry><refentrytitle>sd_bus_is_ready</refentrytitle><manvolnum>3</manvolnum></citerefentry> to + determine whether the connection is fully established, i.e. whether the peer socket has been bound, connected to + and authenticated. Use + <citerefentry><refentrytitle>sd_bus_set_connected_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry> to + be notified when the connection is fully established.</para> + + </refsect1> + + <refsect1> + <title>Return Value</title> + + <para>On success, these functions return 0 or a positive integer. On failure, they return a negative errno-style + error code.</para> + </refsect1> + + <refsect1> + <title>Errors</title> + + <para>Returned errors may indicate the following problems:</para> + + <variablelist> + <varlistentry> + <term><constant>-ECHILD</constant></term> + + <listitem><para>The bus connection has been created in a different process.</para></listitem> + </varlistentry> + </variablelist> + </refsect1> + + <refsect1> + <title>Notes</title> + + <para><function>sd_bus_set_watch_bind()</function> and <function>sd_bus_get_watch_bind()</function> are available as + a shared library, which can be compiled and linked to with the <constant>libsystemd</constant> <citerefentry + project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry> file.</para> + </refsect1> + + <refsect1> + <title>See Also</title> + + <para> + <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>, + <citerefentry><refentrytitle>inotify</refentrytitle><manvolnum>7</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd_bus_call</refentrytitle><manvolnum>3</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd_bus_add_match</refentrytitle><manvolnum>3</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd_bus_request_name</refentrytitle><manvolnum>3</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd_bus_is_ready</refentrytitle><manvolnum>3</manvolnum></citerefentry>, + <citerefentry><refentrytitle>sd_bus_set_connected_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry> + </para> + </refsect1> + +</refentry> diff --git a/meson.build b/meson.build index f95feee99d..924274de63 100644 --- a/meson.build +++ b/meson.build @@ -2556,6 +2556,14 @@ endif ############################################################ +meson_check_api_docs_sh = find_program('tools/meson-check-api-docs.sh') +run_target( + 'check-api-docs', + depends : [man, libsystemd, libudev], + command : [meson_check_api_docs_sh, libsystemd.full_path(), libudev.full_path()]) + +############################################################ + status = [ '@0@ @1@'.format(meson.project_name(), meson.project_version()), diff --git a/src/basic/def.h b/src/basic/def.h index 77ab735aed..43e7e17008 100644 --- a/src/basic/def.h +++ b/src/basic/def.h @@ -53,9 +53,10 @@ "/usr/lib/kbd/keymaps/\0" #endif -#define UNIX_SYSTEM_BUS_ADDRESS "unix:path=/var/run/dbus/system_bus_socket" -#define DEFAULT_SYSTEM_BUS_ADDRESS UNIX_SYSTEM_BUS_ADDRESS -#define UNIX_USER_BUS_ADDRESS_FMT "unix:path=%s/bus" +/* Note that we use the new /run prefix here (instead of /var/run) since we require them to be aliases and that way we + * become independent of /var being mounted */ +#define DEFAULT_SYSTEM_BUS_ADDRESS "unix:path=/run/dbus/system_bus_socket" +#define DEFAULT_USER_BUS_ADDRESS_FMT "unix:path=%s/bus" #define PLYMOUTH_SOCKET { \ .un.sun_family = AF_UNIX, \ diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index 4ca36faf09..16f97893c9 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -315,43 +315,60 @@ int fd_warn_permissions(const char *path, int fd) { } int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) { - _cleanup_close_ int fd; - int r; + char fdpath[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; + _cleanup_close_ int fd = -1; + int r, ret = 0; assert(path); - if (parents) - mkdir_parents(path, 0755); - - fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, - IN_SET(mode, 0, MODE_INVALID) ? 0644 : mode); - if (fd < 0) - return -errno; + /* Note that touch_file() does not follow symlinks: if invoked on an existing symlink, then it is the symlink + * itself which is updated, not its target + * + * Returns the first error we encounter, but tries to apply as much as possible. */ - if (mode != MODE_INVALID) { - r = fchmod(fd, mode); - if (r < 0) + if (parents) + (void) mkdir_parents(path, 0755); + + /* Initially, we try to open the node with O_PATH, so that we get a reference to the node. This is useful in + * case the path refers to an existing device or socket node, as we can open it successfully in all cases, and + * won't trigger any driver magic or so. */ + fd = open(path, O_PATH|O_CLOEXEC|O_NOFOLLOW); + if (fd < 0) { + if (errno != ENOENT) return -errno; - } - if (uid != UID_INVALID || gid != GID_INVALID) { - r = fchown(fd, uid, gid); - if (r < 0) + /* if the node doesn't exist yet, we create it, but with O_EXCL, so that we only create a regular file + * here, and nothing else */ + fd = open(path, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, IN_SET(mode, 0, MODE_INVALID) ? 0644 : mode); + if (fd < 0) return -errno; } + /* Let's make a path from the fd, and operate on that. With this logic, we can adjust the access mode, + * ownership and time of the file node in all cases, even if the fd refers to an O_PATH object — which is + * something fchown(), fchmod(), futimensat() don't allow. */ + xsprintf(fdpath, "/proc/self/fd/%i", fd); + + if (mode != MODE_INVALID) + if (chmod(fdpath, mode) < 0) + ret = -errno; + + if (uid_is_valid(uid) || gid_is_valid(gid)) + if (chown(fdpath, uid, gid) < 0 && ret >= 0) + ret = -errno; + if (stamp != USEC_INFINITY) { struct timespec ts[2]; timespec_store(&ts[0], stamp); ts[1] = ts[0]; - r = futimens(fd, ts); + r = utimensat(AT_FDCWD, fdpath, ts, 0); } else - r = futimens(fd, NULL); - if (r < 0) + r = utimensat(AT_FDCWD, fdpath, NULL, 0); + if (r < 0 && ret >= 0) return -errno; - return 0; + return ret; } int touch(const char *path) { diff --git a/src/basic/io-util.c b/src/basic/io-util.c index 77c9bdc739..08ad42ed95 100644 --- a/src/basic/io-util.c +++ b/src/basic/io-util.c @@ -33,6 +33,7 @@ int flush_fd(int fd) { .fd = fd, .events = POLLIN, }; + int count = 0; /* Read from the specified file descriptor, until POLLIN is not set anymore, throwing away everything * read. Note that some file descriptors (notable IP sockets) will trigger POLLIN even when no data can be read @@ -52,7 +53,7 @@ int flush_fd(int fd) { return -errno; } else if (r == 0) - return 0; + return count; l = read(fd, buf, sizeof(buf)); if (l < 0) { @@ -61,11 +62,13 @@ int flush_fd(int fd) { continue; if (errno == EAGAIN) - return 0; + return count; return -errno; } else if (l == 0) - return 0; + return count; + + count += (int) l; } } diff --git a/src/basic/socket-label.c b/src/basic/socket-label.c index 20be406371..97f3ebe2af 100644 --- a/src/basic/socket-label.c +++ b/src/basic/socket-label.c @@ -29,6 +29,7 @@ #include "alloc-util.h" #include "fd-util.h" +#include "fs-util.h" #include "log.h" #include "macro.h" #include "missing.h" @@ -51,6 +52,7 @@ int socket_address_listen( const char *label) { _cleanup_close_ int fd = -1; + const char *p; int r, one; assert(a); @@ -112,19 +114,23 @@ int socket_address_listen( if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0) return -errno; - if (socket_address_family(a) == AF_UNIX && a->sockaddr.un.sun_path[0] != 0) { + p = socket_address_get_path(a); + if (p) { /* Create parents */ - (void) mkdir_parents_label(a->sockaddr.un.sun_path, directory_mode); + (void) mkdir_parents_label(p, directory_mode); /* Enforce the right access mode for the socket */ RUN_WITH_UMASK(~socket_mode) { r = mac_selinux_bind(fd, &a->sockaddr.sa, a->size); if (r == -EADDRINUSE) { /* Unlink and try again */ - unlink(a->sockaddr.un.sun_path); - if (bind(fd, &a->sockaddr.sa, a->size) < 0) - return -errno; - } else if (r < 0) + + if (unlink(p) < 0) + return r; /* didn't work, return original error */ + + r = mac_selinux_bind(fd, &a->sockaddr.sa, a->size); + } + if (r < 0) return r; } } else { @@ -136,6 +142,11 @@ int socket_address_listen( if (listen(fd, backlog) < 0) return -errno; + /* Let's trigger an inotify event on the socket node, so that anyone waiting for this socket to be connectable + * gets notified */ + if (p) + (void) touch(p); + r = fd; fd = -1; diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c index cb10a1dd07..d6371f833d 100644 --- a/src/basic/socket-util.c +++ b/src/basic/socket-util.c @@ -68,7 +68,6 @@ DEFINE_STRING_TABLE_LOOKUP(socket_address_type, int); int socket_address_parse(SocketAddress *a, const char *s) { char *e, *n; - unsigned u; int r; assert(a); @@ -78,6 +77,8 @@ int socket_address_parse(SocketAddress *a, const char *s) { a->type = SOCK_STREAM; if (*s == '[') { + uint16_t port; + /* IPv6 in [x:.....:z]:p notation */ e = strchr(s+1, ']'); @@ -95,15 +96,12 @@ int socket_address_parse(SocketAddress *a, const char *s) { return -EINVAL; e++; - r = safe_atou(e, &u); + r = parse_ip_port(e, &port); if (r < 0) return r; - if (u <= 0 || u > 0xFFFF) - return -EINVAL; - a->sockaddr.in6.sin6_family = AF_INET6; - a->sockaddr.in6.sin6_port = htobe16((uint16_t)u); + a->sockaddr.in6.sin6_port = htobe16(port); a->size = sizeof(struct sockaddr_in6); } else if (*s == '/') { @@ -134,12 +132,13 @@ int socket_address_parse(SocketAddress *a, const char *s) { } else if (startswith(s, "vsock:")) { /* AF_VSOCK socket in vsock:cid:port notation */ const char *cid_start = s + STRLEN("vsock:"); + unsigned port; e = strchr(cid_start, ':'); if (!e) return -EINVAL; - r = safe_atou(e+1, &u); + r = safe_atou(e+1, &port); if (r < 0) return r; @@ -152,19 +151,18 @@ int socket_address_parse(SocketAddress *a, const char *s) { a->sockaddr.vm.svm_cid = VMADDR_CID_ANY; a->sockaddr.vm.svm_family = AF_VSOCK; - a->sockaddr.vm.svm_port = u; + a->sockaddr.vm.svm_port = port; a->size = sizeof(struct sockaddr_vm); } else { + uint16_t port; + e = strchr(s, ':'); if (e) { - r = safe_atou(e+1, &u); + r = parse_ip_port(e + 1, &port); if (r < 0) return r; - if (u <= 0 || u > 0xFFFF) - return -EINVAL; - n = strndupa(s, e-s); /* IPv4 in w.x.y.z:p notation? */ @@ -175,7 +173,7 @@ int socket_address_parse(SocketAddress *a, const char *s) { if (r > 0) { /* Gotcha, it's a traditional IPv4 address */ a->sockaddr.in.sin_family = AF_INET; - a->sockaddr.in.sin_port = htobe16((uint16_t)u); + a->sockaddr.in.sin_port = htobe16(port); a->size = sizeof(struct sockaddr_in); } else { unsigned idx; @@ -189,7 +187,7 @@ int socket_address_parse(SocketAddress *a, const char *s) { return -EINVAL; a->sockaddr.in6.sin6_family = AF_INET6; - a->sockaddr.in6.sin6_port = htobe16((uint16_t)u); + a->sockaddr.in6.sin6_port = htobe16(port); a->sockaddr.in6.sin6_scope_id = idx; a->sockaddr.in6.sin6_addr = in6addr_any; a->size = sizeof(struct sockaddr_in6); @@ -197,21 +195,18 @@ int socket_address_parse(SocketAddress *a, const char *s) { } else { /* Just a port */ - r = safe_atou(s, &u); + r = parse_ip_port(s, &port); if (r < 0) return r; - if (u <= 0 || u > 0xFFFF) - return -EINVAL; - if (socket_ipv6_is_supported()) { a->sockaddr.in6.sin6_family = AF_INET6; - a->sockaddr.in6.sin6_port = htobe16((uint16_t)u); + a->sockaddr.in6.sin6_port = htobe16(port); a->sockaddr.in6.sin6_addr = in6addr_any; a->size = sizeof(struct sockaddr_in6); } else { a->sockaddr.in.sin_family = AF_INET; - a->sockaddr.in.sin_port = htobe16((uint16_t)u); + a->sockaddr.in.sin_port = htobe16(port); a->sockaddr.in.sin_addr.s_addr = INADDR_ANY; a->size = sizeof(struct sockaddr_in); } diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h index 83af91dbef..49c937aef5 100644 --- a/src/basic/socket-util.h +++ b/src/basic/socket-util.h @@ -36,16 +36,26 @@ #include "util.h" union sockaddr_union { + /* The minimal, abstract version */ struct sockaddr sa; + + /* The libc provided version that allocates "enough room" for every protocol */ + struct sockaddr_storage storage; + + /* Protoctol-specific implementations */ struct sockaddr_in in; struct sockaddr_in6 in6; struct sockaddr_un un; struct sockaddr_nl nl; - struct sockaddr_storage storage; struct sockaddr_ll ll; struct sockaddr_vm vm; + /* Ensure there is enough space to store Infiniband addresses */ uint8_t ll_buffer[offsetof(struct sockaddr_ll, sll_addr) + CONST_MAX(ETH_ALEN, INFINIBAND_ALEN)]; + + /* Ensure there is enough space after the AF_UNIX sun_path for one more NUL byte, just to be sure that the path + * component is always followed by at least one NUL byte. */ + uint8_t un_buffer[sizeof(struct sockaddr_un) + 1]; }; typedef struct SocketAddress { diff --git a/src/basic/verbs.c b/src/basic/verbs.c index d8ebf89d03..556acdcaa3 100644 --- a/src/basic/verbs.c +++ b/src/basic/verbs.c @@ -30,45 +30,36 @@ #include "verbs.h" #include "virt.h" -/* Wraps running_in_chroot() which is used in various places, - * but also adds an environment variable check so external processes - * can reliably force this on. +/* Wraps running_in_chroot() which is used in various places, but also adds an environment variable check so external + * processes can reliably force this on. */ bool running_in_chroot_or_offline(void) { int r; - /* Added to support use cases like rpm-ostree, where from %post - * scripts we only want to execute "preset", but not "start"/"restart" - * for example. + /* Added to support use cases like rpm-ostree, where from %post scripts we only want to execute "preset", but + * not "start"/"restart" for example. * * See ENVIRONMENT.md for docs. */ r = getenv_bool("SYSTEMD_OFFLINE"); - if (r < 0) - log_debug_errno(r, "Parsing SYSTEMD_OFFLINE: %m"); - else if (r == 0) - return false; - else - return true; - - /* We've had this condition check for a long time which basically - * checks for legacy chroot case like Fedora's - * "mock", which is used for package builds. We don't want - * to try to start systemd services there, since without --new-chroot - * we don't even have systemd running, and even if we did, adding - * a concept of background daemons to builds would be an enormous change, - * requiring considering things like how the journal output is handled, etc. - * And there's really not a use case today for a build talking to a service. + if (r < 0 && r != -ENXIO) + log_debug_errno(r, "Failed to parse $SYSTEMD_OFFLINE: %m"); + else if (r >= 0) + return r > 0; + + /* We've had this condition check for a long time which basically checks for legacy chroot case like Fedora's + * "mock", which is used for package builds. We don't want to try to start systemd services there, since + * without --new-chroot we don't even have systemd running, and even if we did, adding a concept of background + * daemons to builds would be an enormous change, requiring considering things like how the journal output is + * handled, etc. And there's really not a use case today for a build talking to a service. * * Note this call itself also looks for a different variable SYSTEMD_IGNORE_CHROOT=1. */ r = running_in_chroot(); if (r < 0) log_debug_errno(r, "running_in_chroot(): %m"); - else if (r > 0) - return true; - return false; + return r > 0; } int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) { diff --git a/src/busctl/busctl.c b/src/busctl/busctl.c index 7a8d6ba5ac..5623bb2ffa 100644 --- a/src/busctl/busctl.c +++ b/src/busctl/busctl.c @@ -62,6 +62,7 @@ static bool arg_expect_reply = true; static bool arg_auto_start = true; static bool arg_allow_interactive_authorization = true; static bool arg_augment_creds = true; +static bool arg_watch_bind = false; static usec_t arg_timeout = 0; #define NAME_IS_ACQUIRED INT_TO_PTR(1) @@ -1735,7 +1736,9 @@ static int help(void) { " --allow-interactive-authorization=BOOL\n" " Allow interactive authorization for operation\n" " --timeout=SECS Maximum time to wait for method call completion\n" - " --augment-creds=BOOL Extend credential data with data read from /proc/$PID\n\n" + " --augment-creds=BOOL Extend credential data with data read from /proc/$PID\n" + " --watch-bind=BOOL Wait for bus AF_UNIX socket to be bound in the file\n" + " system\n\n" "Commands:\n" " list List bus names\n" " status [SERVICE] Show bus service, process or bus owner credentials\n" @@ -1777,6 +1780,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_ALLOW_INTERACTIVE_AUTHORIZATION, ARG_TIMEOUT, ARG_AUGMENT_CREDS, + ARG_WATCH_BIND, }; static const struct option options[] = { @@ -1803,6 +1807,7 @@ static int parse_argv(int argc, char *argv[]) { { "allow-interactive-authorization", required_argument, NULL, ARG_ALLOW_INTERACTIVE_AUTHORIZATION }, { "timeout", required_argument, NULL, ARG_TIMEOUT }, { "augment-creds",required_argument, NULL, ARG_AUGMENT_CREDS}, + { "watch-bind", required_argument, NULL, ARG_WATCH_BIND }, {}, }; @@ -1953,6 +1958,16 @@ static int parse_argv(int argc, char *argv[]) { arg_augment_creds = !!r; break; + case ARG_WATCH_BIND: + r = parse_boolean(optarg); + if (r < 0) { + log_error("Failed to parse --watch-bind= parameter."); + return r; + } + + arg_watch_bind = !!r; + break; + case '?': return -EINVAL; @@ -2051,6 +2066,12 @@ int main(int argc, char *argv[]) { goto finish; } + r = sd_bus_set_watch_bind(bus, arg_watch_bind); + if (r < 0) { + log_error_errno(r, "Failed to set watch-bind setting to '%s': %m", yes_no(arg_watch_bind)); + goto finish; + } + if (arg_address) r = sd_bus_set_address(bus, arg_address); else { diff --git a/src/core/dbus.c b/src/core/dbus.c index b7d8af9396..5e8a299983 100644 --- a/src/core/dbus.c +++ b/src/core/dbus.c @@ -37,6 +37,7 @@ #include "dbus-unit.h" #include "dbus.h" #include "fd-util.h" +#include "fs-util.h" #include "log.h" #include "missing.h" #include "mkdir.h" @@ -603,18 +604,16 @@ static int bus_setup_disconnected_match(Manager *m, sd_bus *bus) { assert(m); assert(bus); - r = sd_bus_add_match( + r = sd_bus_match_signal_async( bus, NULL, - "sender='org.freedesktop.DBus.Local'," - "type='signal'," - "path='/org/freedesktop/DBus/Local'," - "interface='org.freedesktop.DBus.Local'," - "member='Disconnected'", - signal_disconnected, m); - + "org.freedesktop.DBus.Local", + "/org/freedesktop/DBus/Local", + "org.freedesktop.DBus.Local", + "Disconnected", + signal_disconnected, NULL, m); if (r < 0) - return log_error_errno(r, "Failed to register match for Disconnected message: %m"); + return log_error_errno(r, "Failed to request match for Disconnected message: %m"); return 0; } @@ -683,6 +682,12 @@ static int bus_on_connection(sd_event_source *s, int fd, uint32_t revents, void return 0; } + r = sd_bus_set_sender(bus, "org.freedesktop.systemd1"); + if (r < 0) { + log_warning_errno(r, "Failed to set direct connection sender: %m"); + return 0; + } + r = sd_bus_start(bus); if (r < 0) { log_warning_errno(r, "Failed to start new connection bus: %m"); @@ -813,26 +818,23 @@ static int bus_setup_api(Manager *m, sd_bus *bus) { log_error_errno(r, "Failed to subscribe to NameOwnerChanged signal for '%s': %m", name); } - r = sd_bus_add_match( + r = sd_bus_match_signal_async( bus, NULL, - "type='signal'," - "sender='org.freedesktop.DBus'," - "path='/org/freedesktop/DBus'," - "interface='org.freedesktop.systemd1.Activator'," - "member='ActivationRequest'", - signal_activation_request, m); + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.systemd1.Activator", + "ActivationRequest", + signal_activation_request, NULL, m); if (r < 0) log_warning_errno(r, "Failed to subscribe to activation signal: %m"); - /* Allow replacing of our name, to ease implementation of - * reexecution, where we keep the old connection open until - * after the new connection is set up and the name installed - * to allow clients to synchronously wait for reexecution to - * finish */ - r = sd_bus_request_name(bus,"org.freedesktop.systemd1", SD_BUS_NAME_REPLACE_EXISTING|SD_BUS_NAME_ALLOW_REPLACEMENT); + /* Allow replacing of our name, to ease implementation of reexecution, where we keep the old connection open + * until after the new connection is set up and the name installed to allow clients to synchronously wait for + * reexecution to finish */ + r = sd_bus_request_name_async(bus, NULL, "org.freedesktop.systemd1", SD_BUS_NAME_REPLACE_EXISTING|SD_BUS_NAME_ALLOW_REPLACEMENT, NULL, NULL); if (r < 0) - return log_error_errno(r, "Failed to register name: %m"); + return log_error_errno(r, "Failed to request name: %m"); r = manager_sync_bus_names(m, bus); if (r < 0) @@ -857,7 +859,6 @@ static int bus_init_api(Manager *m) { r = sd_bus_open_system(&bus); else r = sd_bus_open_user(&bus); - if (r < 0) { log_debug("Failed to connect to API bus, retrying later..."); return 0; @@ -894,16 +895,16 @@ static int bus_setup_system(Manager *m, sd_bus *bus) { /* if we are a user instance we get the Released message via the system bus */ if (MANAGER_IS_USER(m)) { - r = sd_bus_add_match( + r = sd_bus_match_signal_async( bus, NULL, - "type='signal'," - "interface='org.freedesktop.systemd1.Agent'," - "member='Released'," - "path='/org/freedesktop/systemd1/agent'", - signal_agent_released, m); + NULL, + "/org/freedesktop/systemd1/agent", + "org.freedesktop.systemd1.Agent", + "Released", + signal_agent_released, NULL, m); if (r < 0) - log_warning_errno(r, "Failed to register Released match on system bus: %m"); + log_warning_errno(r, "Failed to request Released match on system bus: %m"); } log_debug("Successfully connected to system bus."); @@ -1005,6 +1006,9 @@ static int bus_init_private(Manager *m) { if (r < 0) return log_error_errno(errno, "Failed to make private socket listening: %m"); + /* Generate an inotify event in case somebody waits for this socket to appear using inotify() */ + (void) touch(sa.un.sun_path); + r = sd_event_add_io(m->event, &s, fd, EPOLLIN, bus_on_connection, m); if (r < 0) return log_error_errno(r, "Failed to allocate event source: %m"); diff --git a/src/core/manager.c b/src/core/manager.c index 15720ada24..860d58617f 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -263,7 +263,7 @@ static int manager_dispatch_ask_password_fd(sd_event_source *source, assert(m); - flush_fd(fd); + (void) flush_fd(fd); m->have_ask_password = have_ask_password(); if (m->have_ask_password < 0) diff --git a/src/core/unit.c b/src/core/unit.c index 652587e6ad..d736d274de 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -3042,7 +3042,7 @@ int unit_install_bus_match(Unit *u, sd_bus *bus, const char *name) { "member='NameOwnerChanged'," "arg0='", name, "'"); - return sd_bus_add_match(bus, &u->match_bus_slot, match, signal_name_owner_changed, u); + return sd_bus_add_match_async(bus, &u->match_bus_slot, match, signal_name_owner_changed, NULL, u); } int unit_watch_bus_name(Unit *u, const char *name) { diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c index 5feaa60c99..1c8c76934c 100644 --- a/src/hostname/hostnamed.c +++ b/src/hostname/hostnamed.c @@ -680,9 +680,9 @@ static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) { if (r < 0) return log_error_errno(r, "Failed to register object: %m"); - r = sd_bus_request_name(bus, "org.freedesktop.hostname1", 0); + r = sd_bus_request_name_async(bus, NULL, "org.freedesktop.hostname1", 0, NULL, NULL); if (r < 0) - return log_error_errno(r, "Failed to register name: %m"); + return log_error_errno(r, "Failed to request name: %m"); r = sd_bus_attach_event(bus, event, 0); if (r < 0) diff --git a/src/import/importd.c b/src/import/importd.c index 21af09fc45..98ee1a2fab 100644 --- a/src/import/importd.c +++ b/src/import/importd.c @@ -1149,9 +1149,9 @@ static int manager_add_bus_objects(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to add transfer enumerator: %m"); - r = sd_bus_request_name(m->bus, "org.freedesktop.import1", 0); + r = sd_bus_request_name_async(m->bus, NULL, "org.freedesktop.import1", 0, NULL, NULL); if (r < 0) - return log_error_errno(r, "Failed to register name: %m"); + return log_error_errno(r, "Failed to request name: %m"); r = sd_bus_attach_event(m->bus, m->event, 0); if (r < 0) diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym index 1a29b03e85..5a07344175 100644 --- a/src/libsystemd/libsystemd.sym +++ b/src/libsystemd/libsystemd.sym @@ -530,3 +530,20 @@ global: sd_bus_message_new; sd_bus_message_seal; } LIBSYSTEMD_234; + +LIBSYSTEMD_237 { +global: + sd_bus_set_watch_bind; + sd_bus_get_watch_bind; + sd_bus_request_name_async; + sd_bus_release_name_async; + sd_bus_add_match_async; + sd_bus_match_signal; + sd_bus_match_signal_async; + sd_bus_is_ready; + sd_bus_set_connected_signal; + sd_bus_get_connected_signal; + sd_bus_set_sender; + sd_bus_get_sender; + sd_bus_message_set_sender; +} LIBSYSTEMD_236; diff --git a/src/libsystemd/sd-bus/bus-control.c b/src/libsystemd/sd-bus/bus-control.c index 0b39115d16..4adc996a86 100644 --- a/src/libsystemd/sd-bus/bus-control.c +++ b/src/libsystemd/sd-bus/bus-control.c @@ -57,13 +57,31 @@ _public_ int sd_bus_get_unique_name(sd_bus *bus, const char **unique) { return 0; } -static int bus_request_name_dbus1(sd_bus *bus, const char *name, uint64_t flags) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - uint32_t ret, param = 0; - int r; +static int validate_request_name_parameters( + sd_bus *bus, + const char *name, + uint64_t flags, + uint32_t *ret_param) { + + uint32_t param = 0; assert(bus); assert(name); + assert(ret_param); + + assert_return(!(flags & ~(SD_BUS_NAME_ALLOW_REPLACEMENT|SD_BUS_NAME_REPLACE_EXISTING|SD_BUS_NAME_QUEUE)), -EINVAL); + assert_return(service_name_is_valid(name), -EINVAL); + assert_return(name[0] != ':', -EINVAL); + + if (!bus->bus_client) + return -EINVAL; + + /* Don't allow requesting the special driver and local names */ + if (STR_IN_SET(name, "org.freedesktop.DBus", "org.freedesktop.DBus.Local")) + return -EINVAL; + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; if (flags & SD_BUS_NAME_ALLOW_REPLACEMENT) param |= BUS_NAME_ALLOW_REPLACEMENT; @@ -72,6 +90,28 @@ static int bus_request_name_dbus1(sd_bus *bus, const char *name, uint64_t flags) if (!(flags & SD_BUS_NAME_QUEUE)) param |= BUS_NAME_DO_NOT_QUEUE; + *ret_param = param; + + return 0; +} + +_public_ int sd_bus_request_name( + sd_bus *bus, + const char *name, + uint64_t flags) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + uint32_t ret, param = 0; + int r; + + assert_return(bus, -EINVAL); + assert_return(name, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + r = validate_request_name_parameters(bus, name, flags, ¶m); + if (r < 0) + return r; + r = sd_bus_call_method( bus, "org.freedesktop.DBus", @@ -90,46 +130,143 @@ static int bus_request_name_dbus1(sd_bus *bus, const char *name, uint64_t flags) if (r < 0) return r; - if (ret == BUS_NAME_ALREADY_OWNER) + switch (ret) { + + case BUS_NAME_ALREADY_OWNER: return -EALREADY; - else if (ret == BUS_NAME_EXISTS) + + case BUS_NAME_EXISTS: return -EEXIST; - else if (ret == BUS_NAME_IN_QUEUE) + + case BUS_NAME_IN_QUEUE: return 0; - else if (ret == BUS_NAME_PRIMARY_OWNER) + + case BUS_NAME_PRIMARY_OWNER: return 1; + } return -EIO; } -_public_ int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags) { +static int default_request_name_handler( + sd_bus_message *m, + void *userdata, + sd_bus_error *ret_error) { + + uint32_t ret; + int r; + + assert(m); + + if (sd_bus_message_is_method_error(m, NULL)) { + log_debug_errno(sd_bus_message_get_errno(m), + "Unable to request name, failing connection: %s", + sd_bus_message_get_error(m)->message); + + bus_enter_closing(sd_bus_message_get_bus(m)); + return 1; + } + + r = sd_bus_message_read(m, "u", &ret); + if (r < 0) + return r; + + switch (ret) { + + case BUS_NAME_ALREADY_OWNER: + log_debug("Already owner of requested service name, ignoring."); + return 1; + + case BUS_NAME_IN_QUEUE: + log_debug("In queue for requested service name."); + return 1; + + case BUS_NAME_PRIMARY_OWNER: + log_debug("Successfully acquired requested service name."); + return 1; + + case BUS_NAME_EXISTS: + log_debug("Requested service name already owned, failing connection."); + bus_enter_closing(sd_bus_message_get_bus(m)); + return 1; + } + + log_debug("Unexpected response from RequestName(), failing connection."); + bus_enter_closing(sd_bus_message_get_bus(m)); + return 1; +} + +_public_ int sd_bus_request_name_async( + sd_bus *bus, + sd_bus_slot **ret_slot, + const char *name, + uint64_t flags, + sd_bus_message_handler_t callback, + void *userdata) { + + uint32_t param = 0; + int r; + assert_return(bus, -EINVAL); assert_return(name, -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); - assert_return(!(flags & ~(SD_BUS_NAME_ALLOW_REPLACEMENT|SD_BUS_NAME_REPLACE_EXISTING|SD_BUS_NAME_QUEUE)), -EINVAL); + + r = validate_request_name_parameters(bus, name, flags, ¶m); + if (r < 0) + return r; + + return sd_bus_call_method_async( + bus, + ret_slot, + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + "RequestName", + callback ?: default_request_name_handler, + userdata, + "su", + name, + param); +} + +static int validate_release_name_parameters( + sd_bus *bus, + const char *name) { + + assert(bus); + assert(name); + assert_return(service_name_is_valid(name), -EINVAL); assert_return(name[0] != ':', -EINVAL); if (!bus->bus_client) return -EINVAL; - /* Don't allow requesting the special driver and local names */ + /* Don't allow releasing the special driver and local names */ if (STR_IN_SET(name, "org.freedesktop.DBus", "org.freedesktop.DBus.Local")) return -EINVAL; if (!BUS_IS_OPEN(bus->state)) return -ENOTCONN; - return bus_request_name_dbus1(bus, name, flags); + return 0; } -static int bus_release_name_dbus1(sd_bus *bus, const char *name) { +_public_ int sd_bus_release_name( + sd_bus *bus, + const char *name) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; uint32_t ret; int r; - assert(bus); - assert(name); + assert_return(bus, -EINVAL); + assert_return(name, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + r = validate_release_name_parameters(bus, name); + if (r < 0) + return r; r = sd_bus_call_method( bus, @@ -147,41 +284,110 @@ static int bus_release_name_dbus1(sd_bus *bus, const char *name) { r = sd_bus_message_read(reply, "u", &ret); if (r < 0) return r; - if (ret == BUS_NAME_NON_EXISTENT) + + switch (ret) { + + case BUS_NAME_NON_EXISTENT: return -ESRCH; - if (ret == BUS_NAME_NOT_OWNER) + + case BUS_NAME_NOT_OWNER: return -EADDRINUSE; - if (ret == BUS_NAME_RELEASED) + + case BUS_NAME_RELEASED: return 0; + } - return -EINVAL; + return -EIO; } -_public_ int sd_bus_release_name(sd_bus *bus, const char *name) { +static int default_release_name_handler( + sd_bus_message *m, + void *userdata, + sd_bus_error *ret_error) { + + uint32_t ret; + int r; + + assert(m); + + if (sd_bus_message_is_method_error(m, NULL)) { + log_debug_errno(sd_bus_message_get_errno(m), + "Unable to release name, failing connection: %s", + sd_bus_message_get_error(m)->message); + + bus_enter_closing(sd_bus_message_get_bus(m)); + return 1; + } + + r = sd_bus_message_read(m, "u", &ret); + if (r < 0) + return r; + + switch (ret) { + + case BUS_NAME_NON_EXISTENT: + log_debug("Name asked to release is not taken currently, ignoring."); + return 1; + + case BUS_NAME_NOT_OWNER: + log_debug("Name asked to release is owned by somebody else, ignoring."); + return 1; + + case BUS_NAME_RELEASED: + log_debug("Name successfully released."); + return 1; + } + + log_debug("Unexpected response from ReleaseName(), failing connection."); + bus_enter_closing(sd_bus_message_get_bus(m)); + return 1; +} + +_public_ int sd_bus_release_name_async( + sd_bus *bus, + sd_bus_slot **ret_slot, + const char *name, + sd_bus_message_handler_t callback, + void *userdata) { + + int r; + assert_return(bus, -EINVAL); assert_return(name, -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); - assert_return(service_name_is_valid(name), -EINVAL); - assert_return(name[0] != ':', -EINVAL); - - if (!bus->bus_client) - return -EINVAL; - - /* Don't allow releasing the special driver and local names */ - if (STR_IN_SET(name, "org.freedesktop.DBus", "org.freedesktop.DBus.Local")) - return -EINVAL; - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; + r = validate_release_name_parameters(bus, name); + if (r < 0) + return r; - return bus_release_name_dbus1(bus, name); + return sd_bus_call_method_async( + bus, + ret_slot, + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + "ReleaseName", + callback ?: default_release_name_handler, + userdata, + "s", + name); } -static int bus_list_names_dbus1(sd_bus *bus, char ***acquired, char ***activatable) { +_public_ int sd_bus_list_names(sd_bus *bus, char ***acquired, char ***activatable) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_strv_free_ char **x = NULL, **y = NULL; int r; + assert_return(bus, -EINVAL); + assert_return(acquired || activatable, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + if (!bus->bus_client) + return -EINVAL; + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + if (acquired) { r = sd_bus_call_method( bus, @@ -231,21 +437,7 @@ static int bus_list_names_dbus1(sd_bus *bus, char ***acquired, char ***activatab return 0; } -_public_ int sd_bus_list_names(sd_bus *bus, char ***acquired, char ***activatable) { - assert_return(bus, -EINVAL); - assert_return(acquired || activatable, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - if (!bus->bus_client) - return -EINVAL; - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - return bus_list_names_dbus1(bus, acquired, activatable); -} - -static int bus_get_name_creds_dbus1( +_public_ int sd_bus_get_name_creds( sd_bus *bus, const char *name, uint64_t mask, @@ -257,6 +449,30 @@ static int bus_get_name_creds_dbus1( pid_t pid = 0; int r; + assert_return(bus, -EINVAL); + assert_return(name, -EINVAL); + assert_return((mask & ~SD_BUS_CREDS_AUGMENT) <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP); + assert_return(mask == 0 || creds, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + assert_return(service_name_is_valid(name), -EINVAL); + + if (!bus->bus_client) + return -EINVAL; + + /* Turn off augmenting if this isn't a local connection. If the connection is not local, then /proc is not + * going to match. */ + if (!bus->is_local) + mask &= ~SD_BUS_CREDS_AUGMENT; + + if (streq(name, "org.freedesktop.DBus.Local")) + return -EINVAL; + + if (streq(name, "org.freedesktop.DBus")) + return sd_bus_get_owner_creds(bus, mask, creds); + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + /* Only query the owner if the caller wants to know it or if * the caller just wants to check whether a name exists */ if ((mask & SD_BUS_CREDS_UNIQUE_NAME) || mask == 0) { @@ -519,46 +735,22 @@ static int bus_get_name_creds_dbus1( return 0; } -_public_ int sd_bus_get_name_creds( - sd_bus *bus, - const char *name, - uint64_t mask, - sd_bus_creds **creds) { +_public_ int sd_bus_get_owner_creds(sd_bus *bus, uint64_t mask, sd_bus_creds **ret) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL; + bool do_label, do_groups; + pid_t pid = 0; + int r; assert_return(bus, -EINVAL); - assert_return(name, -EINVAL); assert_return((mask & ~SD_BUS_CREDS_AUGMENT) <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP); - assert_return(mask == 0 || creds, -EINVAL); + assert_return(ret, -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); - assert_return(service_name_is_valid(name), -EINVAL); - - if (!bus->bus_client) - return -EINVAL; - - /* Turn off augmenting if this isn't a local connection. If the connection is not local, then /proc is not - * going to match. */ - if (!bus->is_local) - mask &= ~SD_BUS_CREDS_AUGMENT; - - if (streq(name, "org.freedesktop.DBus.Local")) - return -EINVAL; - - if (streq(name, "org.freedesktop.DBus")) - return sd_bus_get_owner_creds(bus, mask, creds); if (!BUS_IS_OPEN(bus->state)) return -ENOTCONN; - return bus_get_name_creds_dbus1(bus, name, mask, creds); -} - -static int bus_get_owner_creds_dbus1(sd_bus *bus, uint64_t mask, sd_bus_creds **ret) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL; - pid_t pid = 0; - bool do_label, do_groups; - int r; - - assert(bus); + if (!bus->is_local) + mask &= ~SD_BUS_CREDS_AUGMENT; do_label = bus->label && (mask & SD_BUS_CREDS_SELINUX_CONTEXT); do_groups = bus->n_groups != (size_t) -1 && (mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS); @@ -615,36 +807,23 @@ static int bus_get_owner_creds_dbus1(sd_bus *bus, uint64_t mask, sd_bus_creds ** return 0; } -_public_ int sd_bus_get_owner_creds(sd_bus *bus, uint64_t mask, sd_bus_creds **ret) { - assert_return(bus, -EINVAL); - assert_return((mask & ~SD_BUS_CREDS_AUGMENT) <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP); - assert_return(ret, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - if (!bus->is_local) - mask &= ~SD_BUS_CREDS_AUGMENT; - - return bus_get_owner_creds_dbus1(bus, mask, ret); -} - -#define internal_match(bus, m) \ - ((bus)->hello_flags & KDBUS_HELLO_MONITOR \ +#define append_eavesdrop(bus, m) \ + ((bus)->is_monitor \ ? (isempty(m) ? "eavesdrop='true'" : strjoina((m), ",eavesdrop='true'")) \ : (m)) -static int bus_add_match_internal_dbus1( +int bus_add_match_internal( sd_bus *bus, const char *match) { const char *e; assert(bus); - assert(match); - e = internal_match(bus, match); + if (!bus->bus_client) + return -EINVAL; + + e = append_eavesdrop(bus, match); return sd_bus_call_method( bus, @@ -657,22 +836,36 @@ static int bus_add_match_internal_dbus1( "s", e); } - -int bus_add_match_internal( +int bus_add_match_internal_async( sd_bus *bus, + sd_bus_slot **ret_slot, const char *match, - struct bus_match_component *components, - unsigned n_components) { + sd_bus_message_handler_t callback, + void *userdata) { + + const char *e; assert(bus); if (!bus->bus_client) return -EINVAL; - return bus_add_match_internal_dbus1(bus, match); + e = append_eavesdrop(bus, match); + + return sd_bus_call_method_async( + bus, + ret_slot, + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + "AddMatch", + callback, + userdata, + "s", + e); } -static int bus_remove_match_internal_dbus1( +int bus_remove_match_internal( sd_bus *bus, const char *match) { @@ -681,10 +874,16 @@ static int bus_remove_match_internal_dbus1( assert(bus); assert(match); - e = internal_match(bus, match); + if (!bus->bus_client) + return -EINVAL; - return sd_bus_call_method( + e = append_eavesdrop(bus, match); + + /* Fire and forget */ + + return sd_bus_call_method_async( bus, + NULL, "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", @@ -695,18 +894,6 @@ static int bus_remove_match_internal_dbus1( e); } -int bus_remove_match_internal( - sd_bus *bus, - const char *match) { - - assert(bus); - - if (!bus->bus_client) - return -EINVAL; - - return bus_remove_match_internal_dbus1(bus, match); -} - _public_ int sd_bus_get_name_machine_id(sd_bus *bus, const char *name, sd_id128_t *machine) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *m = NULL; const char *mid; diff --git a/src/libsystemd/sd-bus/bus-control.h b/src/libsystemd/sd-bus/bus-control.h index c9d434c607..3d9acebaf6 100644 --- a/src/libsystemd/sd-bus/bus-control.h +++ b/src/libsystemd/sd-bus/bus-control.h @@ -22,7 +22,7 @@ #include "sd-bus.h" -#include "bus-match.h" +int bus_add_match_internal(sd_bus *bus, const char *match); +int bus_add_match_internal_async(sd_bus *bus, sd_bus_slot **ret, const char *match, sd_bus_message_handler_t callback, void *userdata); -int bus_add_match_internal(sd_bus *bus, const char *match, struct bus_match_component *components, unsigned n_components); int bus_remove_match_internal(sd_bus *bus, const char *match); diff --git a/src/libsystemd/sd-bus/bus-convenience.c b/src/libsystemd/sd-bus/bus-convenience.c index 9d3b596429..7d702e6376 100644 --- a/src/libsystemd/sd-bus/bus-convenience.c +++ b/src/libsystemd/sd-bus/bus-convenience.c @@ -615,3 +615,69 @@ _public_ int sd_bus_query_sender_privilege(sd_bus_message *call, int capability) return 0; } + +#define make_expression(sender, path, interface, member) \ + strjoina( \ + "type='signal'", \ + sender ? ",sender='" : "", \ + sender ?: "", \ + sender ? "'" : "", \ + path ? ",path='" : "", \ + path ?: "", \ + path ? "'" : "", \ + interface ? ",interface='" : "", \ + interface ?: "", \ + interface ? "'" : "", \ + member ? ",member='" : "", \ + member ?: "", \ + member ? "'" : "" \ + ) + +_public_ int sd_bus_match_signal( + sd_bus *bus, + sd_bus_slot **ret, + const char *sender, + const char *path, + const char *interface, + const char *member, + sd_bus_message_handler_t callback, + void *userdata) { + + const char *expression; + + assert_return(bus, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + assert_return(!sender || service_name_is_valid(sender), -EINVAL); + assert_return(!path || object_path_is_valid(path), -EINVAL); + assert_return(!interface || interface_name_is_valid(interface), -EINVAL); + assert_return(!member || member_name_is_valid(member), -EINVAL); + + expression = make_expression(sender, path, interface, member); + + return sd_bus_add_match(bus, ret, expression, callback, userdata); +} + +_public_ int sd_bus_match_signal_async( + sd_bus *bus, + sd_bus_slot **ret, + const char *sender, + const char *path, + const char *interface, + const char *member, + sd_bus_message_handler_t callback, + sd_bus_message_handler_t install_callback, + void *userdata) { + + const char *expression; + + assert_return(bus, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + assert_return(!sender || service_name_is_valid(sender), -EINVAL); + assert_return(!path || object_path_is_valid(path), -EINVAL); + assert_return(!interface || interface_name_is_valid(interface), -EINVAL); + assert_return(!member || member_name_is_valid(member), -EINVAL); + + expression = make_expression(sender, path, interface, member); + + return sd_bus_add_match_async(bus, ret, expression, callback, install_callback, userdata); +} diff --git a/src/libsystemd/sd-bus/bus-internal.c b/src/libsystemd/sd-bus/bus-internal.c index 3c381b0ffe..05a022fbf3 100644 --- a/src/libsystemd/sd-bus/bus-internal.c +++ b/src/libsystemd/sd-bus/bus-internal.c @@ -362,13 +362,18 @@ int bus_maybe_reply_error(sd_bus_message *m, int r, sd_bus_error *error) { } else return r; - log_debug("Failed to process message [type=%s sender=%s path=%s interface=%s member=%s signature=%s]: %s", + log_debug("Failed to process message type=%s sender=%s destination=%s path=%s interface=%s member=%s cookie=%" PRIu64 " reply_cookie=%" PRIu64 " signature=%s error-name=%s error-message=%s: %s", bus_message_type_to_string(m->header->type), - strna(m->sender), - strna(m->path), - strna(m->interface), - strna(m->member), + strna(sd_bus_message_get_sender(m)), + strna(sd_bus_message_get_destination(m)), + strna(sd_bus_message_get_path(m)), + strna(sd_bus_message_get_interface(m)), + strna(sd_bus_message_get_member(m)), + BUS_MESSAGE_COOKIE(m), + m->reply_cookie, strna(m->root_container.signature), + strna(m->error.name), + strna(m->error.message), bus_error_message(error, r)); return 1; diff --git a/src/libsystemd/sd-bus/bus-internal.h b/src/libsystemd/sd-bus/bus-internal.h index 4175ca3efe..a28f5f06f4 100644 --- a/src/libsystemd/sd-bus/bus-internal.h +++ b/src/libsystemd/sd-bus/bus-internal.h @@ -38,7 +38,7 @@ struct reply_callback { sd_bus_message_handler_t callback; - usec_t timeout; + usec_t timeout_usec; /* this is a relative timeout until we reach the BUS_HELLO state, and an absolute one right after */ uint64_t cookie; unsigned prioq_idx; }; @@ -53,6 +53,9 @@ struct filter_callback { struct match_callback { sd_bus_message_handler_t callback; + sd_bus_message_handler_t install_callback; + + sd_bus_slot *install_slot; /* The AddMatch() call */ unsigned last_iteration; @@ -157,12 +160,14 @@ struct sd_bus_slot { enum bus_state { BUS_UNSET, - BUS_OPENING, - BUS_AUTHENTICATING, - BUS_HELLO, + BUS_WATCH_BIND, /* waiting for the socket to appear via inotify */ + BUS_OPENING, /* the kernel's connect() is still not ready */ + BUS_AUTHENTICATING, /* we are currently in the "SASL" authorization phase of dbus */ + BUS_HELLO, /* we are waiting for the Hello() response */ BUS_RUNNING, BUS_CLOSING, - BUS_CLOSED + BUS_CLOSED, + _BUS_STATE_MAX, }; static inline bool BUS_IS_OPEN(enum bus_state state) { @@ -188,6 +193,7 @@ struct sd_bus { enum bus_state state; int input_fd, output_fd; + int inotify_fd; int message_version; int message_endian; @@ -210,6 +216,11 @@ struct sd_bus { bool exited:1; bool exit_triggered:1; bool is_local:1; + bool watch_bind:1; + bool is_monitor:1; + bool accept_fd:1; + bool attach_timestamp:1; + bool connected_signal:1; int use_memfd; @@ -286,13 +297,11 @@ struct sd_bus { pid_t original_pid; - uint64_t hello_flags; - uint64_t attach_flags; - sd_event_source *input_io_event_source; sd_event_source *output_io_event_source; sd_event_source *time_event_source; sd_event_source *quit_event_source; + sd_event_source *inotify_event_source; sd_event *event; int event_priority; @@ -307,11 +316,15 @@ struct sd_bus { char *cgroup_root; char *description; + char *patch_sender; sd_bus_track *track_queue; LIST_HEAD(sd_bus_slot, slots); LIST_HEAD(sd_bus_track, tracks); + + int *inotify_watches; + size_t n_inotify_watches; }; /* For method calls we time-out at 25s, like in the D-Bus reference implementation */ @@ -367,6 +380,12 @@ bool bus_pid_changed(sd_bus *bus); char *bus_address_escape(const char *v); +int bus_attach_io_events(sd_bus *b); +int bus_attach_inotify_event(sd_bus *b); + +void bus_close_inotify_fd(sd_bus *b); +void bus_close_io_fds(sd_bus *b); + #define OBJECT_PATH_FOREACH_PREFIX(prefix, path) \ for (char *_slash = ({ strcpy((prefix), (path)); streq((prefix), "/") ? NULL : strrchr((prefix), '/'); }) ; \ _slash && !(_slash[(_slash) == (prefix)] = 0); \ @@ -383,8 +402,6 @@ int bus_set_address_user(sd_bus *bus); int bus_set_address_system_remote(sd_bus *b, const char *host); int bus_set_address_system_machine(sd_bus *b, const char *machine); -int bus_remove_match_by_string(sd_bus *bus, const char *match, sd_bus_message_handler_t callback, void *userdata); - int bus_get_root_path(sd_bus *bus); int bus_maybe_reply_error(sd_bus_message *m, int r, sd_bus_error *error); @@ -395,64 +412,6 @@ int bus_maybe_reply_error(sd_bus_message *m, int r, sd_bus_error *error); return sd_bus_error_set_errno(error, r); \ } while (false) -/** - * 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 -}; +void bus_enter_closing(sd_bus *bus); -/** - * 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, -}; +void bus_set_state(sd_bus *bus, enum bus_state state); diff --git a/src/libsystemd/sd-bus/bus-kernel.c b/src/libsystemd/sd-bus/bus-kernel.c index c6179b4d95..b27b9d7d86 100644 --- a/src/libsystemd/sd-bus/bus-kernel.c +++ b/src/libsystemd/sd-bus/bus-kernel.c @@ -66,49 +66,3 @@ void bus_flush_memfd(sd_bus *b) { for (i = 0; i < b->n_memfd_cache; i++) close_and_munmap(b->memfd_cache[i].fd, b->memfd_cache[i].address, b->memfd_cache[i].mapped); } - -uint64_t attach_flags_to_kdbus(uint64_t mask) { - uint64_t m = 0; - - if (mask & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID| - SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID)) - m |= KDBUS_ATTACH_CREDS; - - if (mask & (SD_BUS_CREDS_PID|SD_BUS_CREDS_TID|SD_BUS_CREDS_PPID)) - m |= KDBUS_ATTACH_PIDS; - - if (mask & SD_BUS_CREDS_COMM) - m |= KDBUS_ATTACH_PID_COMM; - - if (mask & SD_BUS_CREDS_TID_COMM) - m |= KDBUS_ATTACH_TID_COMM; - - if (mask & SD_BUS_CREDS_EXE) - m |= KDBUS_ATTACH_EXE; - - if (mask & SD_BUS_CREDS_CMDLINE) - m |= KDBUS_ATTACH_CMDLINE; - - if (mask & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID)) - m |= KDBUS_ATTACH_CGROUP; - - if (mask & (SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS)) - m |= KDBUS_ATTACH_CAPS; - - if (mask & SD_BUS_CREDS_SELINUX_CONTEXT) - m |= KDBUS_ATTACH_SECLABEL; - - if (mask & (SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID)) - m |= KDBUS_ATTACH_AUDIT; - - if (mask & SD_BUS_CREDS_WELL_KNOWN_NAMES) - m |= KDBUS_ATTACH_NAMES; - - if (mask & SD_BUS_CREDS_DESCRIPTION) - m |= KDBUS_ATTACH_CONN_DESCRIPTION; - - if (mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS) - m |= KDBUS_ATTACH_AUXGROUPS; - - return m; -} diff --git a/src/libsystemd/sd-bus/bus-kernel.h b/src/libsystemd/sd-bus/bus-kernel.h index d9f80935fe..fa78e5c80d 100644 --- a/src/libsystemd/sd-bus/bus-kernel.h +++ b/src/libsystemd/sd-bus/bus-kernel.h @@ -41,5 +41,3 @@ struct memfd_cache { void close_and_munmap(int fd, void *address, size_t size); void bus_flush_memfd(sd_bus *bus); - -uint64_t attach_flags_to_kdbus(uint64_t sd_bus_flags); diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c index 219cff1f6e..3fbb6dc306 100644 --- a/src/libsystemd/sd-bus/bus-message.c +++ b/src/libsystemd/sd-bus/bus-message.c @@ -127,7 +127,6 @@ static void message_free(sd_bus_message *m) { if (m->iovec != m->iovec_fixed) free(m->iovec); - m->destination_ptr = mfree(m->destination_ptr); message_reset_containers(m); free(m->root_container.signature); free(m->root_container.offsets); @@ -5488,6 +5487,15 @@ _public_ int sd_bus_message_set_destination(sd_bus_message *m, const char *desti return message_append_field_string(m, BUS_MESSAGE_HEADER_DESTINATION, SD_BUS_TYPE_STRING, destination, &m->destination); } +_public_ int sd_bus_message_set_sender(sd_bus_message *m, const char *sender) { + assert_return(m, -EINVAL); + assert_return(sender, -EINVAL); + assert_return(!m->sealed, -EPERM); + assert_return(!m->sender, -EEXIST); + + return message_append_field_string(m, BUS_MESSAGE_HEADER_SENDER, SD_BUS_TYPE_STRING, sender, &m->sender); +} + int bus_message_get_blob(sd_bus_message *m, void **buffer, size_t *sz) { size_t total; void *p, *e; diff --git a/src/libsystemd/sd-bus/bus-message.h b/src/libsystemd/sd-bus/bus-message.h index 1e4b20926d..88998700d6 100644 --- a/src/libsystemd/sd-bus/bus-message.h +++ b/src/libsystemd/sd-bus/bus-message.h @@ -136,10 +136,6 @@ struct sd_bus_message { usec_t timeout; - char sender_buffer[3 + DECIMAL_STR_MAX(uint64_t) + 1]; - char destination_buffer[3 + DECIMAL_STR_MAX(uint64_t) + 1]; - char *destination_ptr; - size_t header_offsets[_BUS_MESSAGE_HEADER_MAX]; unsigned n_header_offsets; }; diff --git a/src/libsystemd/sd-bus/bus-objects.c b/src/libsystemd/sd-bus/bus-objects.c index 121197bbcb..1237819b49 100644 --- a/src/libsystemd/sd-bus/bus-objects.c +++ b/src/libsystemd/sd-bus/bus-objects.c @@ -1369,7 +1369,7 @@ int bus_process_object(sd_bus *bus, sd_bus_message *m) { assert(bus); assert(m); - if (bus->hello_flags & KDBUS_HELLO_MONITOR) + if (bus->is_monitor) return 0; if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL) diff --git a/src/libsystemd/sd-bus/bus-slot.c b/src/libsystemd/sd-bus/bus-slot.c index 756761c3ed..9a56371715 100644 --- a/src/libsystemd/sd-bus/bus-slot.c +++ b/src/libsystemd/sd-bus/bus-slot.c @@ -81,7 +81,7 @@ void bus_slot_disconnect(sd_bus_slot *slot) { if (slot->reply_callback.cookie != 0) ordered_hashmap_remove(slot->bus->reply_callbacks, &slot->reply_callback.cookie); - if (slot->reply_callback.timeout != 0) + if (slot->reply_callback.timeout_usec != 0) prioq_remove(slot->bus->reply_callbacks_prioq, &slot->reply_callback, &slot->reply_callback.prioq_idx); break; @@ -94,12 +94,17 @@ void bus_slot_disconnect(sd_bus_slot *slot) { case BUS_MATCH_CALLBACK: if (slot->match_added) - bus_remove_match_internal(slot->bus, slot->match_callback.match_string); + (void) bus_remove_match_internal(slot->bus, slot->match_callback.match_string); + + if (slot->match_callback.install_slot) { + bus_slot_disconnect(slot->match_callback.install_slot); + slot->match_callback.install_slot = sd_bus_slot_unref(slot->match_callback.install_slot); + } slot->bus->match_callbacks_modified = true; bus_match_remove(&slot->bus->match_callbacks, &slot->match_callback); - free(slot->match_callback.match_string); + slot->match_callback.match_string = mfree(slot->match_callback.match_string); break; @@ -174,7 +179,7 @@ void bus_slot_disconnect(sd_bus_slot *slot) { } } - free(slot->node_vtable.interface); + slot->node_vtable.interface = mfree(slot->node_vtable.interface); if (slot->node_vtable.node) { LIST_REMOVE(vtables, slot->node_vtable.node->vtables, &slot->node_vtable); diff --git a/src/libsystemd/sd-bus/bus-socket.c b/src/libsystemd/sd-bus/bus-socket.c index 0a1d36a9d3..61539313bc 100644 --- a/src/libsystemd/sd-bus/bus-socket.c +++ b/src/libsystemd/sd-bus/bus-socket.c @@ -32,9 +32,12 @@ #include "bus-socket.h" #include "fd-util.h" #include "format-util.h" +#include "fs-util.h" #include "hexdecoct.h" +#include "io-util.h" #include "macro.h" #include "missing.h" +#include "path-util.h" #include "selinux-util.h" #include "signal-util.h" #include "stdio-util.h" @@ -188,7 +191,7 @@ static int bus_socket_auth_verify_client(sd_bus *b) { if (!e) return 0; - if (b->hello_flags & KDBUS_HELLO_ACCEPT_FD) { + if (b->accept_fd) { f = memmem(e + 2, b->rbuffer_size - (e - (char*) b->rbuffer) - 2, "\r\n", 2); if (!f) return 0; @@ -475,7 +478,7 @@ static int bus_socket_auth_verify_server(sd_bus *b) { r = bus_socket_auth_write_ok(b); } } else if (line_equals(line, l, "NEGOTIATE_UNIX_FD")) { - if (b->auth == _BUS_AUTH_INVALID || !(b->hello_flags & KDBUS_HELLO_ACCEPT_FD)) + if (b->auth == _BUS_AUTH_INVALID || !b->accept_fd) r = bus_socket_auth_write(b, "ERROR\r\n"); else { b->can_fds = true; @@ -592,8 +595,8 @@ void bus_socket_setup(sd_bus *b) { assert(b); /* Increase the buffers to 8 MB */ - fd_inc_rcvbuf(b->input_fd, SNDBUF_SIZE); - fd_inc_sndbuf(b->output_fd, SNDBUF_SIZE); + (void) fd_inc_rcvbuf(b->input_fd, SNDBUF_SIZE); + (void) fd_inc_sndbuf(b->output_fd, SNDBUF_SIZE); b->message_version = 1; b->message_endian = 0; @@ -652,7 +655,7 @@ static int bus_socket_start_auth_client(sd_bus *b) { if (!b->auth_buffer) return -ENOMEM; - if (b->hello_flags & KDBUS_HELLO_ACCEPT_FD) + if (b->accept_fd) auth_suffix = "\r\nNEGOTIATE_UNIX_FD\r\nBEGIN\r\n"; else auth_suffix = "\r\nBEGIN\r\n"; @@ -672,15 +675,15 @@ int bus_socket_start_auth(sd_bus *b) { bus_get_peercred(b); - b->state = BUS_AUTHENTICATING; + bus_set_state(b, BUS_AUTHENTICATING); b->auth_timeout = now(CLOCK_MONOTONIC) + BUS_AUTH_TIMEOUT; if (sd_is_socket(b->input_fd, AF_UNIX, 0, 0) <= 0) - b->hello_flags &= ~KDBUS_HELLO_ACCEPT_FD; + b->accept_fd = false; if (b->output_fd != b->input_fd) if (sd_is_socket(b->output_fd, AF_UNIX, 0, 0) <= 0) - b->hello_flags &= ~KDBUS_HELLO_ACCEPT_FD; + b->accept_fd = false; if (b->is_server) return bus_socket_read_auth(b); @@ -688,30 +691,249 @@ int bus_socket_start_auth(sd_bus *b) { return bus_socket_start_auth_client(b); } +static int bus_socket_inotify_setup(sd_bus *b) { + _cleanup_free_ int *new_watches = NULL; + _cleanup_free_ char *absolute = NULL; + size_t n_allocated = 0, n = 0, done = 0, i; + unsigned max_follow = 32; + const char *p; + int wd, r; + + assert(b); + assert(b->watch_bind); + assert(b->sockaddr.sa.sa_family == AF_UNIX); + assert(b->sockaddr.un.sun_path[0] != 0); + + /* Sets up an inotify fd in case watch_bind is enabled: wait until the configured AF_UNIX file system socket + * appears before connecting to it. The implemented is pretty simplistic: we just subscribe to relevant changes + * to all prefix components of the path, and every time we get an event for that we try to reconnect again, + * without actually caring what precisely the event we got told us. If we still can't connect we re-subscribe + * to all relevant changes of anything in the path, so that our watches include any possibly newly created path + * components. */ + + if (b->inotify_fd < 0) { + b->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); + if (b->inotify_fd < 0) + return -errno; + } + + /* Make sure the path is NUL terminated */ + p = strndupa(b->sockaddr.un.sun_path, sizeof(b->sockaddr.un.sun_path)); + + /* Make sure the path is absolute */ + r = path_make_absolute_cwd(p, &absolute); + if (r < 0) + goto fail; + + /* Watch all parent directories, and don't mind any prefix that doesn't exist yet. For the innermost directory + * that exists we want to know when files are created or moved into it. For all parents of it we just care if + * they are removed or renamed. */ + + if (!GREEDY_REALLOC(new_watches, n_allocated, n + 1)) { + r = -ENOMEM; + goto fail; + } + + /* Start with the top-level directory, which is a bit simpler than the rest, since it can't be a symlink, and + * always exists */ + wd = inotify_add_watch(b->inotify_fd, "/", IN_CREATE|IN_MOVED_TO); + if (wd < 0) { + r = log_debug_errno(errno, "Failed to add inotify watch on /: %m"); + goto fail; + } else + new_watches[n++] = wd; + + for (;;) { + _cleanup_free_ char *component = NULL, *prefix = NULL, *destination = NULL; + size_t n_slashes, n_component; + char *c = NULL; + + n_slashes = strspn(absolute + done, "/"); + n_component = n_slashes + strcspn(absolute + done + n_slashes, "/"); + + if (n_component == 0) /* The end */ + break; + + component = strndup(absolute + done, n_component); + if (!component) { + r = -ENOMEM; + goto fail; + } + + /* A trailing slash? That's a directory, and not a socket then */ + if (path_equal(component, "/")) { + r = -EISDIR; + goto fail; + } + + /* A single dot? Let's eat this up */ + if (path_equal(component, "/.")) { + done += n_component; + continue; + } + + prefix = strndup(absolute, done + n_component); + if (!prefix) { + r = -ENOMEM; + goto fail; + } + + if (!GREEDY_REALLOC(new_watches, n_allocated, n + 1)) { + r = -ENOMEM; + goto fail; + } + + wd = inotify_add_watch(b->inotify_fd, prefix, IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CREATE|IN_MOVED_TO|IN_DONT_FOLLOW); + log_debug("Added inotify watch for %s on bus %s: %i", prefix, strna(b->description), wd); + + if (wd < 0) { + if (IN_SET(errno, ENOENT, ELOOP)) + break; /* This component doesn't exist yet, or the path contains a cyclic symlink right now */ + + r = log_debug_errno(errno, "Failed to add inotify watch on %s: %m", isempty(prefix) ? "/" : prefix); + goto fail; + } else + new_watches[n++] = wd; + + /* Check if this is possibly a symlink. If so, let's follow it and watch it too. */ + r = readlink_malloc(prefix, &destination); + if (r == -EINVAL) { /* not a symlink */ + done += n_component; + continue; + } + if (r < 0) + goto fail; + + if (isempty(destination)) { /* Empty symlink target? Yuck! */ + r = -EINVAL; + goto fail; + } + + if (max_follow <= 0) { /* Let's make sure we don't follow symlinks forever */ + r = -ELOOP; + goto fail; + } + + if (path_is_absolute(destination)) { + /* For absolute symlinks we build the new path and start anew */ + c = strjoin(destination, absolute + done + n_component); + done = 0; + } else { + _cleanup_free_ char *t = NULL; + + /* For relative symlinks we replace the last component, and try again */ + t = strndup(absolute, done); + if (!t) + return -ENOMEM; + + c = strjoin(t, "/", destination, absolute + done + n_component); + } + if (!c) { + r = -ENOMEM; + goto fail; + } + + free(absolute); + absolute = c; + + max_follow--; + } + + /* And now, let's remove all watches from the previous iteration we don't need anymore */ + for (i = 0; i < b->n_inotify_watches; i++) { + bool found = false; + size_t j; + + for (j = 0; j < n; j++) + if (new_watches[j] == b->inotify_watches[i]) { + found = true; + break; + } + + if (found) + continue; + + (void) inotify_rm_watch(b->inotify_fd, b->inotify_watches[i]); + } + + free_and_replace(b->inotify_watches, new_watches); + b->n_inotify_watches = n; + + return 0; + +fail: + bus_close_inotify_fd(b); + return r; +} + int bus_socket_connect(sd_bus *b) { + bool inotify_done = false; int r; assert(b); - assert(b->input_fd < 0); - assert(b->output_fd < 0); - assert(b->sockaddr.sa.sa_family != AF_UNSPEC); - b->input_fd = socket(b->sockaddr.sa.sa_family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (b->input_fd < 0) - return -errno; + for (;;) { + assert(b->input_fd < 0); + assert(b->output_fd < 0); + assert(b->sockaddr.sa.sa_family != AF_UNSPEC); - b->output_fd = b->input_fd; + b->input_fd = socket(b->sockaddr.sa.sa_family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (b->input_fd < 0) + return -errno; - bus_socket_setup(b); + b->output_fd = b->input_fd; + bus_socket_setup(b); - r = connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size); - if (r < 0) { - if (errno == EINPROGRESS) - return 1; + if (connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size) < 0) { + if (errno == EINPROGRESS) { - return -errno; + /* If we have any inotify watches open, close them now, we don't need them anymore, as + * we have successfully initiated a connection */ + bus_close_inotify_fd(b); + + /* Note that very likely we are already in BUS_OPENING state here, as we enter it when + * we start parsing the address string. The only reason we set the state explicitly + * here, is to undo BUS_WATCH_BIND, in case we did the inotify magic. */ + bus_set_state(b, BUS_OPENING); + return 1; + } + + if (IN_SET(errno, ENOENT, ECONNREFUSED) && /* ENOENT → unix socket doesn't exist at all; ECONNREFUSED → unix socket stale */ + b->watch_bind && + b->sockaddr.sa.sa_family == AF_UNIX && + b->sockaddr.un.sun_path[0] != 0) { + + /* This connection attempt failed, let's release the socket for now, and start with a + * fresh one when reconnecting. */ + bus_close_io_fds(b); + + if (inotify_done) { + /* inotify set up already, don't do it again, just return now, and remember + * that we are waiting for inotify events now. */ + bus_set_state(b, BUS_WATCH_BIND); + return 1; + } + + /* This is a file system socket, and the inotify logic is enabled. Let's create the necessary inotify fd. */ + r = bus_socket_inotify_setup(b); + if (r < 0) + return r; + + /* Let's now try to connect a second time, because in theory there's otherwise a race + * here: the socket might have been created in the time between our first connect() and + * the time we set up the inotify logic. But let's remember that we set up inotify now, + * so that we don't do the connect() more than twice. */ + inotify_done = true; + + } else + return -errno; + } else + break; } + /* Yay, established, we don't need no inotify anymore! */ + bus_close_inotify_fd(b); + return bus_socket_start_auth(b); } @@ -742,10 +964,10 @@ int bus_socket_exec(sd_bus *b) { if (!IN_SET(s[1], STDIN_FILENO, STDOUT_FILENO)) safe_close(s[1]); - fd_cloexec(STDIN_FILENO, false); - fd_cloexec(STDOUT_FILENO, false); - fd_nonblock(STDIN_FILENO, false); - fd_nonblock(STDOUT_FILENO, false); + (void) fd_cloexec(STDIN_FILENO, false); + (void) fd_cloexec(STDOUT_FILENO, false); + (void) fd_nonblock(STDIN_FILENO, false); + (void) fd_nonblock(STDOUT_FILENO, false); if (b->exec_argv) execvp(b->exec_path, b->exec_argv); @@ -1069,3 +1291,34 @@ int bus_socket_process_authenticating(sd_bus *b) { return bus_socket_read_auth(b); } + +int bus_socket_process_watch_bind(sd_bus *b) { + int r, q; + + assert(b); + assert(b->state == BUS_WATCH_BIND); + assert(b->inotify_fd >= 0); + + r = flush_fd(b->inotify_fd); + if (r <= 0) + return r; + + log_debug("Got inotify event on bus %s.", strna(b->description)); + + /* We flushed events out of the inotify fd. In that case, maybe the socket is valid now? Let's try to connect + * to it again */ + + r = bus_socket_connect(b); + if (r < 0) + return r; + + q = bus_attach_io_events(b); + if (q < 0) + return q; + + q = bus_attach_inotify_event(b); + if (q < 0) + return q; + + return r; +} diff --git a/src/libsystemd/sd-bus/bus-socket.h b/src/libsystemd/sd-bus/bus-socket.h index 915a283f5a..c180562f98 100644 --- a/src/libsystemd/sd-bus/bus-socket.h +++ b/src/libsystemd/sd-bus/bus-socket.h @@ -34,5 +34,6 @@ int bus_socket_read_message(sd_bus *bus); int bus_socket_process_opening(sd_bus *b); int bus_socket_process_authenticating(sd_bus *b); +int bus_socket_process_watch_bind(sd_bus *b); bool bus_socket_auth_needs_write(sd_bus *b); diff --git a/src/libsystemd/sd-bus/bus-track.c b/src/libsystemd/sd-bus/bus-track.c index ab22d6e4de..0dd06e9b99 100644 --- a/src/libsystemd/sd-bus/bus-track.c +++ b/src/libsystemd/sd-bus/bus-track.c @@ -48,25 +48,13 @@ struct sd_bus_track { LIST_FIELDS(sd_bus_track, tracks); }; -#define MATCH_PREFIX \ - "type='signal'," \ - "sender='org.freedesktop.DBus'," \ - "path='/org/freedesktop/DBus'," \ - "interface='org.freedesktop.DBus'," \ - "member='NameOwnerChanged'," \ - "arg0='" - -#define MATCH_SUFFIX \ - "'" - -#define MATCH_FOR_NAME(name) \ - ({ \ - char *_x; \ - size_t _l = strlen(name); \ - _x = alloca(STRLEN(MATCH_PREFIX)+_l+STRLEN(MATCH_SUFFIX)+1); \ - strcpy(stpcpy(stpcpy(_x, MATCH_PREFIX), name), MATCH_SUFFIX); \ - _x; \ - }) +#define MATCH_FOR_NAME(name) \ + strjoina("type='signal'," \ + "sender='org.freedesktop.DBus'," \ + "path='/org/freedesktop/DBus'," \ + "interface='org.freedesktop.DBus'," \ + "member='NameOwnerChanged'," \ + "arg0='", name, "'") static struct track_item* track_item_free(struct track_item *i) { @@ -259,9 +247,7 @@ _public_ int sd_bus_track_add_name(sd_bus_track *track, const char *name) { bus_track_remove_from_queue(track); /* don't dispatch this while we work in it */ - track->n_adding++; /* make sure we aren't dispatched while we synchronously add this match */ - r = sd_bus_add_match(track->bus, &n->slot, match, on_name_owner_changed, track); - track->n_adding--; + r = sd_bus_add_match_async(track->bus, &n->slot, match, on_name_owner_changed, NULL, track); if (r < 0) { bus_track_add_to_queue(track); return r; diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c index 3cfc60fe86..8c8853c7ee 100644 --- a/src/libsystemd/sd-bus/sd-bus.c +++ b/src/libsystemd/sd-bus/sd-bus.c @@ -57,7 +57,7 @@ #define log_debug_bus_message(m) \ do { \ sd_bus_message *_mm = (m); \ - log_debug("Got message type=%s sender=%s destination=%s object=%s interface=%s member=%s cookie=%" PRIu64 " reply_cookie=%" PRIu64 " error-name=%s error-message=%s", \ + log_debug("Got message type=%s sender=%s destination=%s path=%s interface=%s member=%s cookie=%" PRIu64 " reply_cookie=%" PRIu64 " signature=%s error-name=%s error-message=%s", \ bus_message_type_to_string(_mm->header->type), \ strna(sd_bus_message_get_sender(_mm)), \ strna(sd_bus_message_get_destination(_mm)), \ @@ -66,28 +66,39 @@ strna(sd_bus_message_get_member(_mm)), \ BUS_MESSAGE_COOKIE(_mm), \ _mm->reply_cookie, \ + strna(_mm->root_container.signature), \ strna(_mm->error.name), \ strna(_mm->error.message)); \ } while (false) static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec); -static int attach_io_events(sd_bus *b); -static void detach_io_events(sd_bus *b); +static void bus_detach_io_events(sd_bus *b); +static void bus_detach_inotify_event(sd_bus *b); static thread_local sd_bus *default_system_bus = NULL; static thread_local sd_bus *default_user_bus = NULL; static thread_local sd_bus *default_starter_bus = NULL; -static void bus_close_fds(sd_bus *b) { +void bus_close_io_fds(sd_bus *b) { assert(b); - detach_io_events(b); + bus_detach_io_events(b); if (b->input_fd != b->output_fd) safe_close(b->output_fd); b->output_fd = b->input_fd = safe_close(b->input_fd); } +void bus_close_inotify_fd(sd_bus *b) { + assert(b); + + bus_detach_inotify_event(b); + + b->inotify_fd = safe_close(b->inotify_fd); + b->inotify_watches = mfree(b->inotify_watches); + b->n_inotify_watches = 0; +} + static void bus_reset_queues(sd_bus *b) { assert(b); @@ -131,7 +142,8 @@ static void bus_free(sd_bus *b) { if (b->default_bus_ptr) *b->default_bus_ptr = NULL; - bus_close_fds(b); + bus_close_io_fds(b); + bus_close_inotify_fd(b); free(b->label); free(b->groups); @@ -142,6 +154,7 @@ static void bus_free(sd_bus *b) { free(b->machine); free(b->cgroup_root); free(b->description); + free(b->patch_sender); free(b->exec_path); strv_free(b->exec_argv); @@ -181,10 +194,10 @@ _public_ int sd_bus_new(sd_bus **ret) { r->n_ref = REFCNT_INIT; r->input_fd = r->output_fd = -1; + r->inotify_fd = -1; r->message_version = 1; r->creds_mask |= SD_BUS_CREDS_WELL_KNOWN_NAMES|SD_BUS_CREDS_UNIQUE_NAME; - r->hello_flags |= KDBUS_HELLO_ACCEPT_FD; - r->attach_flags |= KDBUS_ATTACH_NAMES; + r->accept_fd = true; r->original_pid = getpid_cached(); r->n_groups = (size_t) -1; @@ -262,6 +275,7 @@ _public_ int sd_bus_set_exec(sd_bus *bus, const char *path, char *const argv[]) _public_ int sd_bus_set_bus_client(sd_bus *bus, int b) { assert_return(bus, -EINVAL); assert_return(bus->state == BUS_UNSET, -EPERM); + assert_return(!bus->patch_sender, -EPERM); assert_return(!bus_pid_changed(bus), -ECHILD); bus->bus_client = !!b; @@ -273,7 +287,7 @@ _public_ int sd_bus_set_monitor(sd_bus *bus, int b) { assert_return(bus->state == BUS_UNSET, -EPERM); assert_return(!bus_pid_changed(bus), -ECHILD); - SET_FLAG(bus->hello_flags, KDBUS_HELLO_MONITOR, b); + bus->is_monitor = b; return 0; } @@ -282,30 +296,23 @@ _public_ int sd_bus_negotiate_fds(sd_bus *bus, int b) { assert_return(bus->state == BUS_UNSET, -EPERM); assert_return(!bus_pid_changed(bus), -ECHILD); - SET_FLAG(bus->hello_flags, KDBUS_HELLO_ACCEPT_FD, b); + bus->accept_fd = b; return 0; } _public_ int sd_bus_negotiate_timestamp(sd_bus *bus, int b) { - uint64_t new_flags; assert_return(bus, -EINVAL); assert_return(!IN_SET(bus->state, BUS_CLOSING, BUS_CLOSED), -EPERM); assert_return(!bus_pid_changed(bus), -ECHILD); - new_flags = bus->attach_flags; - SET_FLAG(new_flags, KDBUS_ATTACH_TIMESTAMP, b); - - if (bus->attach_flags == new_flags) - return 0; - - bus->attach_flags = new_flags; + /* This is not actually supported by any of our transports these days, but we do honour it for synthetic + * replies, and maybe one day classic D-Bus learns this too */ + bus->attach_timestamp = b; return 0; } _public_ int sd_bus_negotiate_creds(sd_bus *bus, int b, uint64_t mask) { - uint64_t new_flags; - assert_return(bus, -EINVAL); assert_return(mask <= _SD_BUS_CREDS_ALL, -EINVAL); assert_return(!IN_SET(bus->state, BUS_CLOSING, BUS_CLOSED), -EPERM); @@ -316,13 +323,6 @@ _public_ int sd_bus_negotiate_creds(sd_bus *bus, int b, uint64_t mask) { /* The well knowns we need unconditionally, so that matches can work */ bus->creds_mask |= SD_BUS_CREDS_WELL_KNOWN_NAMES|SD_BUS_CREDS_UNIQUE_NAME; - /* Make sure we don't lose the timestamp flag */ - new_flags = (bus->attach_flags & KDBUS_ATTACH_TIMESTAMP) | attach_flags_to_kdbus(bus->creds_mask); - if (bus->attach_flags == new_flags) - return 0; - - bus->attach_flags = new_flags; - return 0; } @@ -378,6 +378,105 @@ _public_ int sd_bus_get_allow_interactive_authorization(sd_bus *bus) { return bus->allow_interactive_authorization; } +_public_ int sd_bus_set_watch_bind(sd_bus *bus, int b) { + assert_return(bus, -EINVAL); + assert_return(bus->state == BUS_UNSET, -EPERM); + assert_return(!bus_pid_changed(bus), -ECHILD); + + bus->watch_bind = b; + return 0; +} + +_public_ int sd_bus_get_watch_bind(sd_bus *bus) { + assert_return(bus, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + return bus->watch_bind; +} + +_public_ int sd_bus_set_connected_signal(sd_bus *bus, int b) { + assert_return(bus, -EINVAL); + assert_return(bus->state == BUS_UNSET, -EPERM); + assert_return(!bus_pid_changed(bus), -ECHILD); + + bus->connected_signal = b; + return 0; +} + +_public_ int sd_bus_get_connected_signal(sd_bus *bus) { + assert_return(bus, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + return bus->connected_signal; +} + +static int synthesize_connected_signal(sd_bus *bus) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + int r; + + assert(bus); + + /* If enabled, synthesizes a local "Connected" signal mirroring the local "Disconnected" signal. This is called + * whenever we fully established a connection, i.e. after the authorization phase, and after receiving the + * Hello() reply. Or in other words, whenver we enter BUS_RUNNING state. + * + * This is useful so that clients can start doing stuff whenver the connection is fully established in a way + * that works independently from whether we connected to a full bus or just a direct connection. */ + + if (!bus->connected_signal) + return 0; + + r = sd_bus_message_new_signal( + bus, + &m, + "/org/freedesktop/DBus/Local", + "org.freedesktop.DBus.Local", + "Connected"); + if (r < 0) + return r; + + bus_message_set_sender_local(bus, m); + + r = bus_seal_synthetic_message(bus, m); + if (r < 0) + return r; + + r = bus_rqueue_make_room(bus); + if (r < 0) + return r; + + /* Insert at the very front */ + memmove(bus->rqueue + 1, bus->rqueue, sizeof(sd_bus_message*) * bus->rqueue_size); + bus->rqueue[0] = m; + m = NULL; + bus->rqueue_size++; + + return 0; +} + +void bus_set_state(sd_bus *bus, enum bus_state state) { + + static const char * const table[_BUS_STATE_MAX] = { + [BUS_UNSET] = "UNSET", + [BUS_WATCH_BIND] = "WATCH_BIND", + [BUS_OPENING] = "OPENING", + [BUS_AUTHENTICATING] = "AUTHENTICATING", + [BUS_HELLO] = "HELLO", + [BUS_RUNNING] = "RUNNING", + [BUS_CLOSING] = "CLOSING", + [BUS_CLOSED] = "CLOSED", + }; + + assert(bus); + assert(state < _BUS_STATE_MAX); + + if (state == bus->state) + return; + + log_debug("Bus %s: changing state %s → %s", strna(bus->description), table[bus->state], table[state]); + bus->state = state; +} + static int hello_callback(sd_bus_message *reply, void *userdata, sd_bus_error *error) { const char *s; sd_bus *bus; @@ -403,8 +502,13 @@ static int hello_callback(sd_bus_message *reply, void *userdata, sd_bus_error *e if (!bus->unique_name) return -ENOMEM; - if (bus->state == BUS_HELLO) - bus->state = BUS_RUNNING; + if (bus->state == BUS_HELLO) { + bus_set_state(bus, BUS_RUNNING); + + r = synthesize_connected_signal(bus); + if (r < 0) + return r; + } return 1; } @@ -432,14 +536,37 @@ static int bus_send_hello(sd_bus *bus) { } int bus_start_running(sd_bus *bus) { + struct reply_callback *c; + Iterator i; + usec_t n; + int r; + assert(bus); + assert(bus->state < BUS_HELLO); + + /* We start all method call timeouts when we enter BUS_HELLO or BUS_RUNNING mode. At this point let's convert + * all relative to absolute timestamps. Note that we do not reshuffle the reply callback priority queue since + * adding a fixed value to all entries should not alter the internal order. */ + + n = now(CLOCK_MONOTONIC); + ORDERED_HASHMAP_FOREACH(c, bus->reply_callbacks, i) { + if (c->timeout_usec == 0) + continue; + + c->timeout_usec = usec_add(n, c->timeout_usec); + } if (bus->bus_client) { - bus->state = BUS_HELLO; + bus_set_state(bus, BUS_HELLO); return 1; } - bus->state = BUS_RUNNING; + bus_set_state(bus, BUS_RUNNING); + + r = synthesize_connected_signal(bus); + if (r < 0) + return r; + return 1; } @@ -801,6 +928,7 @@ static int parse_container_unix_address(sd_bus *b, const char **p, char **guid) b->nspid = 0; b->sockaddr.un.sun_family = AF_UNIX; + /* Note that we use the old /var/run prefix here, to increase compatibility with really old containers */ strncpy(b->sockaddr.un.sun_path, "/var/run/dbus/system_bus_socket", sizeof(b->sockaddr.un.sun_path)); b->sockaddr_size = SOCKADDR_UN_LEN(b->sockaddr.un); b->is_local = false; @@ -900,7 +1028,8 @@ static int bus_start_address(sd_bus *b) { assert(b); for (;;) { - bus_close_fds(b); + bus_close_io_fds(b); + bus_close_inotify_fd(b); /* If you provide multiple different bus-addresses, we * try all of them in order and use the first one that @@ -908,20 +1037,25 @@ static int bus_start_address(sd_bus *b) { if (b->exec_path) r = bus_socket_exec(b); - else if ((b->nspid > 0 || b->machine) && b->sockaddr.sa.sa_family != AF_UNSPEC) r = bus_container_connect_socket(b); - else if (b->sockaddr.sa.sa_family != AF_UNSPEC) r = bus_socket_connect(b); - else goto next; if (r >= 0) { - r = attach_io_events(b); - if (r >= 0) - return r; + int q; + + q = bus_attach_io_events(b); + if (q < 0) + return q; + + q = bus_attach_inotify_event(b); + if (q < 0) + return q; + + return r; } b->last_connect_error = -r; @@ -981,7 +1115,7 @@ _public_ int sd_bus_start(sd_bus *bus) { assert_return(bus->state == BUS_UNSET, -EPERM); assert_return(!bus_pid_changed(bus), -ECHILD); - bus->state = BUS_OPENING; + bus_set_state(bus, BUS_OPENING); if (bus->is_server && bus->bus_client) return -EINVAL; @@ -1042,7 +1176,6 @@ _public_ int sd_bus_open(sd_bus **ret) { * be safe, and authenticate everything */ b->trusted = false; b->is_local = false; - b->attach_flags |= KDBUS_ATTACH_CAPS | KDBUS_ATTACH_CREDS; b->creds_mask |= SD_BUS_CREDS_UID | SD_BUS_CREDS_EUID | SD_BUS_CREDS_EFFECTIVE_CAPS; r = sd_bus_start(b); @@ -1088,7 +1221,6 @@ _public_ int sd_bus_open_system(sd_bus **ret) { /* Let's do per-method access control on the system bus. We * need the caller's UID and capability set for that. */ b->trusted = false; - b->attach_flags |= KDBUS_ATTACH_CAPS | KDBUS_ATTACH_CREDS; b->creds_mask |= SD_BUS_CREDS_UID | SD_BUS_CREDS_EUID | SD_BUS_CREDS_EFFECTIVE_CAPS; b->is_local = true; @@ -1122,7 +1254,7 @@ int bus_set_address_user(sd_bus *b) { if (!ee) return -ENOMEM; - if (asprintf(&s, UNIX_USER_BUS_ADDRESS_FMT, ee) < 0) + if (asprintf(&s, DEFAULT_USER_BUS_ADDRESS_FMT, ee) < 0) return -ENOMEM; b->address = s; @@ -1296,7 +1428,7 @@ _public_ void sd_bus_close(sd_bus *bus) { if (bus_pid_changed(bus)) return; - bus->state = BUS_CLOSED; + bus_set_state(bus, BUS_CLOSED); sd_bus_detach_event(bus); @@ -1304,7 +1436,8 @@ _public_ void sd_bus_close(sd_bus *bus) { * the bus object and the bus may be freed */ bus_reset_queues(bus); - bus_close_fds(bus); + bus_close_io_fds(bus); + bus_close_inotify_fd(bus); } _public_ sd_bus* sd_bus_flush_close_unref(sd_bus *bus) { @@ -1318,13 +1451,13 @@ _public_ sd_bus* sd_bus_flush_close_unref(sd_bus *bus) { return sd_bus_unref(bus); } -static void bus_enter_closing(sd_bus *bus) { +void bus_enter_closing(sd_bus *bus) { assert(bus); - if (!IN_SET(bus->state, BUS_OPENING, BUS_AUTHENTICATING, BUS_HELLO, BUS_RUNNING)) + if (!IN_SET(bus->state, BUS_WATCH_BIND, BUS_OPENING, BUS_AUTHENTICATING, BUS_HELLO, BUS_RUNNING)) return; - bus->state = BUS_CLOSING; + bus_set_state(bus, BUS_CLOSING); } _public_ sd_bus *sd_bus_ref(sd_bus *bus) { @@ -1359,6 +1492,13 @@ _public_ int sd_bus_is_open(sd_bus *bus) { return BUS_IS_OPEN(bus->state); } +_public_ int sd_bus_is_ready(sd_bus *bus) { + assert_return(bus, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + return bus->state == BUS_RUNNING; +} + _public_ int sd_bus_can_send(sd_bus *bus, char type) { int r; @@ -1366,11 +1506,11 @@ _public_ int sd_bus_can_send(sd_bus *bus, char type) { assert_return(bus->state != BUS_UNSET, -ENOTCONN); assert_return(!bus_pid_changed(bus), -ECHILD); - if (bus->hello_flags & KDBUS_HELLO_MONITOR) + if (bus->is_monitor) return 0; if (type == SD_BUS_TYPE_UNIX_FD) { - if (!(bus->hello_flags & KDBUS_HELLO_ACCEPT_FD)) + if (!bus->accept_fd) return 0; r = bus_ensure_running(bus); @@ -1399,6 +1539,8 @@ _public_ int sd_bus_get_bus_id(sd_bus *bus, sd_id128_t *id) { } static int bus_seal_message(sd_bus *b, sd_bus_message *m, usec_t timeout) { + int r; + assert(b); assert(m); @@ -1413,6 +1555,12 @@ static int bus_seal_message(sd_bus *b, sd_bus_message *m, usec_t timeout) { if (timeout == 0) timeout = BUS_DEFAULT_TIMEOUT; + if (!m->sender && b->patch_sender) { + r = sd_bus_message_set_sender(m, b->patch_sender); + if (r < 0) + return r; + } + return sd_bus_message_seal(m, ++b->cookie, timeout); } @@ -1438,7 +1586,7 @@ int bus_seal_synthetic_message(sd_bus *b, sd_bus_message *m) { /* Fake some timestamps, if they were requested, and not * already initialized */ - if (b->attach_flags & KDBUS_ATTACH_TIMESTAMP) { + if (b->attach_timestamp) { if (m->realtime <= 0) m->realtime = now(CLOCK_REALTIME); @@ -1456,7 +1604,7 @@ int bus_seal_synthetic_message(sd_bus *b, sd_bus_message *m) { return sd_bus_message_seal(m, 0xFFFFFFFFULL, 0); } -static int bus_write_message(sd_bus *bus, sd_bus_message *m, bool hint_sync_call, size_t *idx) { +static int bus_write_message(sd_bus *bus, sd_bus_message *m, size_t *idx) { int r; assert(bus); @@ -1467,7 +1615,7 @@ static int bus_write_message(sd_bus *bus, sd_bus_message *m, bool hint_sync_call return r; if (*idx >= BUS_MESSAGE_SIZE(m)) - log_debug("Sent message type=%s sender=%s destination=%s object=%s interface=%s member=%s cookie=%" PRIu64 " reply_cookie=%" PRIu64 " error-name=%s error-message=%s", + log_debug("Sent message type=%s sender=%s destination=%s path=%s interface=%s member=%s cookie=%" PRIu64 " reply_cookie=%" PRIu64 " signature=%s error-name=%s error-message=%s", bus_message_type_to_string(m->header->type), strna(sd_bus_message_get_sender(m)), strna(sd_bus_message_get_destination(m)), @@ -1476,6 +1624,7 @@ static int bus_write_message(sd_bus *bus, sd_bus_message *m, bool hint_sync_call strna(sd_bus_message_get_member(m)), BUS_MESSAGE_COOKIE(m), m->reply_cookie, + strna(m->root_container.signature), strna(m->error.name), strna(m->error.message)); @@ -1490,7 +1639,7 @@ static int dispatch_wqueue(sd_bus *bus) { while (bus->wqueue_size > 0) { - r = bus_write_message(bus, bus->wqueue[0], false, &bus->windex); + r = bus_write_message(bus, bus->wqueue[0], &bus->windex); if (r < 0) return r; else if (r == 0) @@ -1569,7 +1718,7 @@ static int dispatch_rqueue(sd_bus *bus, bool hint_priority, int64_t priority, sd } } -static int bus_send_internal(sd_bus *bus, sd_bus_message *_m, uint64_t *cookie, bool hint_sync_call) { +_public_ int sd_bus_send(sd_bus *bus, sd_bus_message *_m, uint64_t *cookie) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = sd_bus_message_ref(_m); int r; @@ -1614,7 +1763,7 @@ static int bus_send_internal(sd_bus *bus, sd_bus_message *_m, uint64_t *cookie, if (IN_SET(bus->state, BUS_RUNNING, BUS_HELLO) && bus->wqueue_size <= 0) { size_t idx = 0; - r = bus_write_message(bus, m, hint_sync_call, &idx); + r = bus_write_message(bus, m, &idx); if (r < 0) { if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { bus_enter_closing(bus); @@ -1654,10 +1803,6 @@ finish: return 1; } -_public_ int sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *cookie) { - return bus_send_internal(bus, m, cookie, false); -} - _public_ int sd_bus_send_to(sd_bus *bus, sd_bus_message *m, const char *destination, uint64_t *cookie) { int r; @@ -1684,26 +1829,35 @@ _public_ int sd_bus_send_to(sd_bus *bus, sd_bus_message *m, const char *destinat return sd_bus_send(bus, m, cookie); } -static usec_t calc_elapse(uint64_t usec) { +static usec_t calc_elapse(sd_bus *bus, uint64_t usec) { + assert(bus); + if (usec == (uint64_t) -1) return 0; - return now(CLOCK_MONOTONIC) + usec; + /* We start all timeouts the instant we enter BUS_HELLO/BUS_RUNNING state, so that the don't run in parallel + * with any connection setup states. Hence, if a method callback is started earlier than that we just store the + * relative timestamp, and afterwards the absolute one. */ + + if (IN_SET(bus->state, BUS_WATCH_BIND, BUS_OPENING, BUS_AUTHENTICATING)) + return usec; + else + return now(CLOCK_MONOTONIC) + usec; } static int timeout_compare(const void *a, const void *b) { const struct reply_callback *x = a, *y = b; - if (x->timeout != 0 && y->timeout == 0) + if (x->timeout_usec != 0 && y->timeout_usec == 0) return -1; - if (x->timeout == 0 && y->timeout != 0) + if (x->timeout_usec == 0 && y->timeout_usec != 0) return 1; - if (x->timeout < y->timeout) + if (x->timeout_usec < y->timeout_usec) return -1; - if (x->timeout > y->timeout) + if (x->timeout_usec > y->timeout_usec) return 1; return 0; @@ -1723,8 +1877,7 @@ _public_ int sd_bus_call_async( assert_return(m, -EINVAL); assert_return(m->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL); - assert_return(!(m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED), -EINVAL); - assert_return(callback, -EINVAL); + assert_return(!m->sealed || (!!callback == !(m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)), -EINVAL); if (!bus) bus = m->bus; @@ -1734,6 +1887,10 @@ _public_ int sd_bus_call_async( if (!BUS_IS_OPEN(bus->state)) return -ENOTCONN; + /* If no callback is specified and there's no interest in a slot, then there's no reason to ask for a reply */ + if (!callback && !slot && !m->sealed) + m->header->flags |= BUS_MESSAGE_NO_REPLY_EXPECTED; + r = ordered_hashmap_ensure_allocated(&bus->reply_callbacks, &uint64_hash_ops); if (r < 0) return r; @@ -1750,29 +1907,31 @@ _public_ int sd_bus_call_async( if (r < 0) return r; - s = bus_slot_allocate(bus, !slot, BUS_REPLY_CALLBACK, sizeof(struct reply_callback), userdata); - if (!s) - return -ENOMEM; - - s->reply_callback.callback = callback; + if (slot || callback) { + s = bus_slot_allocate(bus, !slot, BUS_REPLY_CALLBACK, sizeof(struct reply_callback), userdata); + if (!s) + return -ENOMEM; - s->reply_callback.cookie = BUS_MESSAGE_COOKIE(m); - r = ordered_hashmap_put(bus->reply_callbacks, &s->reply_callback.cookie, &s->reply_callback); - if (r < 0) { - s->reply_callback.cookie = 0; - return r; - } + s->reply_callback.callback = callback; - s->reply_callback.timeout = calc_elapse(m->timeout); - if (s->reply_callback.timeout != 0) { - r = prioq_put(bus->reply_callbacks_prioq, &s->reply_callback, &s->reply_callback.prioq_idx); + s->reply_callback.cookie = BUS_MESSAGE_COOKIE(m); + r = ordered_hashmap_put(bus->reply_callbacks, &s->reply_callback.cookie, &s->reply_callback); if (r < 0) { - s->reply_callback.timeout = 0; + s->reply_callback.cookie = 0; return r; } + + s->reply_callback.timeout_usec = calc_elapse(bus, m->timeout); + if (s->reply_callback.timeout_usec != 0) { + r = prioq_put(bus->reply_callbacks_prioq, &s->reply_callback, &s->reply_callback.prioq_idx); + if (r < 0) { + s->reply_callback.timeout_usec = 0; + return r; + } + } } - r = sd_bus_send(bus, m, &s->reply_callback.cookie); + r = sd_bus_send(bus, m, s ? &s->reply_callback.cookie : NULL); if (r < 0) return r; @@ -1850,11 +2009,11 @@ _public_ int sd_bus_call( if (r < 0) goto fail; - r = bus_send_internal(bus, m, &cookie, true); + r = sd_bus_send(bus, m, &cookie); if (r < 0) goto fail; - timeout = calc_elapse(m->timeout); + timeout = calc_elapse(bus, m->timeout); for (;;) { usec_t left; @@ -1873,7 +2032,7 @@ _public_ int sd_bus_call( if (incoming->header->type == SD_BUS_MESSAGE_METHOD_RETURN) { - if (incoming->n_fds <= 0 || (bus->hello_flags & KDBUS_HELLO_ACCEPT_FD)) { + if (incoming->n_fds <= 0 || bus->accept_fd) { if (reply) *reply = incoming; else @@ -1971,7 +2130,16 @@ _public_ int sd_bus_get_fd(sd_bus *bus) { assert_return(bus->input_fd == bus->output_fd, -EPERM); assert_return(!bus_pid_changed(bus), -ECHILD); - return bus->input_fd; + if (bus->state == BUS_CLOSED) + return -ENOTCONN; + + if (bus->inotify_fd >= 0) + return bus->inotify_fd; + + if (bus->input_fd >= 0) + return bus->input_fd; + + return -ENOTCONN; } _public_ int sd_bus_get_events(sd_bus *bus) { @@ -1980,23 +2148,40 @@ _public_ int sd_bus_get_events(sd_bus *bus) { assert_return(bus, -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); - if (!BUS_IS_OPEN(bus->state) && bus->state != BUS_CLOSING) + switch (bus->state) { + + case BUS_UNSET: + case BUS_CLOSED: return -ENOTCONN; - if (bus->state == BUS_OPENING) + case BUS_WATCH_BIND: + flags |= POLLIN; + break; + + case BUS_OPENING: flags |= POLLOUT; - else if (bus->state == BUS_AUTHENTICATING) { + break; + case BUS_AUTHENTICATING: if (bus_socket_auth_needs_write(bus)) flags |= POLLOUT; flags |= POLLIN; + break; - } else if (IN_SET(bus->state, BUS_RUNNING, BUS_HELLO)) { + case BUS_RUNNING: + case BUS_HELLO: if (bus->rqueue_size <= 0) flags |= POLLIN; if (bus->wqueue_size > 0) flags |= POLLOUT; + break; + + case BUS_CLOSING: + break; + + default: + assert_not_reached("Unknown state"); } return flags; @@ -2017,39 +2202,45 @@ _public_ int sd_bus_get_timeout(sd_bus *bus, uint64_t *timeout_usec) { return 1; } - if (bus->state == BUS_CLOSING) { - *timeout_usec = 0; - return 1; - } + switch (bus->state) { - if (bus->state == BUS_AUTHENTICATING) { + case BUS_AUTHENTICATING: *timeout_usec = bus->auth_timeout; return 1; - } - if (!IN_SET(bus->state, BUS_RUNNING, BUS_HELLO)) { - *timeout_usec = (uint64_t) -1; - return 0; - } + case BUS_RUNNING: + case BUS_HELLO: + if (bus->rqueue_size > 0) { + *timeout_usec = 0; + return 1; + } + + c = prioq_peek(bus->reply_callbacks_prioq); + if (!c) { + *timeout_usec = (uint64_t) -1; + return 0; + } + + if (c->timeout_usec == 0) { + *timeout_usec = (uint64_t) -1; + return 0; + } - if (bus->rqueue_size > 0) { + *timeout_usec = c->timeout_usec; + return 1; + + case BUS_CLOSING: *timeout_usec = 0; return 1; - } - c = prioq_peek(bus->reply_callbacks_prioq); - if (!c) { + case BUS_WATCH_BIND: + case BUS_OPENING: *timeout_usec = (uint64_t) -1; return 0; - } - if (c->timeout == 0) { - *timeout_usec = (uint64_t) -1; - return 0; + default: + assert_not_reached("Unknown or unexpected stat"); } - - *timeout_usec = c->timeout; - return 1; } static int process_timeout(sd_bus *bus) { @@ -2057,17 +2248,19 @@ static int process_timeout(sd_bus *bus) { _cleanup_(sd_bus_message_unrefp) sd_bus_message* m = NULL; struct reply_callback *c; sd_bus_slot *slot; + bool is_hello; usec_t n; int r; assert(bus); + assert(IN_SET(bus->state, BUS_RUNNING, BUS_HELLO)); c = prioq_peek(bus->reply_callbacks_prioq); if (!c) return 0; n = now(CLOCK_MONOTONIC); - if (c->timeout > n) + if (c->timeout_usec > n) return 0; r = bus_message_new_synthetic_error( @@ -2083,7 +2276,7 @@ static int process_timeout(sd_bus *bus) { return r; assert_se(prioq_pop(bus->reply_callbacks_prioq) == c); - c->timeout = 0; + c->timeout_usec = 0; ordered_hashmap_remove(bus->reply_callbacks, &c->cookie); c->cookie = 0; @@ -2092,6 +2285,8 @@ static int process_timeout(sd_bus *bus) { bus->iteration_counter++; + is_hello = bus->state == BUS_HELLO && c->callback == hello_callback; + bus->current_message = m; bus->current_slot = sd_bus_slot_ref(slot); bus->current_handler = c->callback; @@ -2109,6 +2304,11 @@ static int process_timeout(sd_bus *bus) { sd_bus_slot_unref(slot); + /* When this is the hello message and it timed out, then make sure to propagate the error up, don't just log + * and ignore the callback handler's return value. */ + if (is_hello) + return r; + return bus_maybe_reply_error(m, r, &error_buffer); } @@ -2138,6 +2338,7 @@ static int process_reply(sd_bus *bus, sd_bus_message *m) { _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL; struct reply_callback *c; sd_bus_slot *slot; + bool is_hello; int r; assert(bus); @@ -2157,7 +2358,7 @@ static int process_reply(sd_bus *bus, sd_bus_message *m) { slot = container_of(c, sd_bus_slot, reply_callback); - if (m->n_fds > 0 && !(bus->hello_flags & KDBUS_HELLO_ACCEPT_FD)) { + if (m->n_fds > 0 && !bus->accept_fd) { /* If the reply contained a file descriptor which we * didn't want we pass an error instead. */ @@ -2186,11 +2387,13 @@ static int process_reply(sd_bus *bus, sd_bus_message *m) { return r; } - if (c->timeout != 0) { + if (c->timeout_usec != 0) { prioq_remove(bus->reply_callbacks_prioq, c, &c->prioq_idx); - c->timeout = 0; + c->timeout_usec = 0; } + is_hello = bus->state == BUS_HELLO && c->callback == hello_callback; + bus->current_slot = sd_bus_slot_ref(slot); bus->current_handler = c->callback; bus->current_userdata = slot->userdata; @@ -2206,6 +2409,11 @@ static int process_reply(sd_bus *bus, sd_bus_message *m) { sd_bus_slot_unref(slot); + /* When this is the hello message and it failed, then make sure to propagate the error up, don't just log and + * ignore the callback handler's return value. */ + if (is_hello) + return r; + return bus_maybe_reply_error(m, r, &error_buffer); } @@ -2282,7 +2490,7 @@ static int process_builtin(sd_bus *bus, sd_bus_message *m) { assert(bus); assert(m); - if (bus->hello_flags & KDBUS_HELLO_MONITOR) + if (bus->is_monitor) return 0; if (bus->manual_peer_interface) @@ -2340,13 +2548,13 @@ static int process_fd_check(sd_bus *bus, sd_bus_message *m) { * delivered to us later even though we ourselves did not * negotiate it. */ - if (bus->hello_flags & KDBUS_HELLO_MONITOR) + if (bus->is_monitor) return 0; if (m->n_fds <= 0) return 0; - if (bus->hello_flags & KDBUS_HELLO_ACCEPT_FD) + if (bus->accept_fd) return 0; if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL) @@ -2517,9 +2725,9 @@ static int process_closing_reply_callback(sd_bus *bus, struct reply_callback *c) if (r < 0) return r; - if (c->timeout != 0) { + if (c->timeout_usec != 0) { prioq_remove(bus->reply_callbacks_prioq, c, &c->prioq_idx); - c->timeout = 0; + c->timeout_usec = 0; } ordered_hashmap_remove(bus->reply_callbacks, &c->cookie); @@ -2638,48 +2846,44 @@ static int bus_process_internal(sd_bus *bus, bool hint_priority, int64_t priorit case BUS_CLOSED: return -ECONNRESET; + case BUS_WATCH_BIND: + r = bus_socket_process_watch_bind(bus); + break; + case BUS_OPENING: r = bus_socket_process_opening(bus); - if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { - bus_enter_closing(bus); - r = 1; - } else if (r < 0) - return r; - if (ret) - *ret = NULL; - return r; + break; case BUS_AUTHENTICATING: r = bus_socket_process_authenticating(bus); - if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { - bus_enter_closing(bus); - r = 1; - } else if (r < 0) - return r; - - if (ret) - *ret = NULL; - - return r; + break; case BUS_RUNNING: case BUS_HELLO: r = process_running(bus, hint_priority, priority, ret); - if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { - bus_enter_closing(bus); - r = 1; - - if (ret) - *ret = NULL; - } + if (r >= 0) + return r; - return r; + /* This branch initializes *ret, hence we don't use the generic error checking below */ + break; case BUS_CLOSING: return process_closing(bus, ret); + + default: + assert_not_reached("Unknown state"); } - assert_not_reached("Unknown state"); + if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { + bus_enter_closing(bus); + r = 1; + } else if (r < 0) + return r; + + if (ret) + *ret = NULL; + + return r; } _public_ int sd_bus_process(sd_bus *bus, sd_bus_message **ret) { @@ -2692,7 +2896,7 @@ _public_ int sd_bus_process_priority(sd_bus *bus, int64_t priority, sd_bus_messa static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec) { struct pollfd p[2] = {}; - int r, e, n; + int r, n; struct timespec ts; usec_t m = USEC_INFINITY; @@ -2704,45 +2908,52 @@ static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec) { if (!BUS_IS_OPEN(bus->state)) return -ENOTCONN; - e = sd_bus_get_events(bus); - if (e < 0) - return e; - - if (need_more) - /* The caller really needs some more data, he doesn't - * care about what's already read, or any timeouts - * except its own. */ - e |= POLLIN; - else { - usec_t until; - /* The caller wants to process if there's something to - * process, but doesn't care otherwise */ - - r = sd_bus_get_timeout(bus, &until); - if (r < 0) - return r; - if (r > 0) { - usec_t nw; - nw = now(CLOCK_MONOTONIC); - m = until > nw ? until - nw : 0; - } - } - - if (timeout_usec != (uint64_t) -1 && (m == (uint64_t) -1 || timeout_usec < m)) - m = timeout_usec; + if (bus->state == BUS_WATCH_BIND) { + assert(bus->inotify_fd >= 0); - p[0].fd = bus->input_fd; - if (bus->output_fd == bus->input_fd) { - p[0].events = e; + p[0].events = POLLIN; + p[0].fd = bus->inotify_fd; n = 1; } else { - p[0].events = e & POLLIN; - p[1].fd = bus->output_fd; - p[1].events = e & POLLOUT; - n = 2; + int e; + + e = sd_bus_get_events(bus); + if (e < 0) + return e; + + if (need_more) + /* The caller really needs some more data, he doesn't + * care about what's already read, or any timeouts + * except its own. */ + e |= POLLIN; + else { + usec_t until; + /* The caller wants to process if there's something to + * process, but doesn't care otherwise */ + + r = sd_bus_get_timeout(bus, &until); + if (r < 0) + return r; + if (r > 0) + m = usec_sub_unsigned(until, now(CLOCK_MONOTONIC)); + } + + p[0].fd = bus->input_fd; + if (bus->output_fd == bus->input_fd) { + p[0].events = e; + n = 1; + } else { + p[0].events = e & POLLIN; + p[1].fd = bus->output_fd; + p[1].events = e & POLLOUT; + n = 2; + } } - r = ppoll(p, n, m == (uint64_t) -1 ? NULL : timespec_store(&ts, m), NULL); + if (timeout_usec != (uint64_t) -1 && (m == USEC_INFINITY || timeout_usec < m)) + m = timeout_usec; + + r = ppoll(p, n, m == USEC_INFINITY ? NULL : timespec_store(&ts, m), NULL); if (r < 0) return -errno; @@ -2778,6 +2989,10 @@ _public_ int sd_bus_flush(sd_bus *bus) { if (!BUS_IS_OPEN(bus->state)) return -ENOTCONN; + /* We never were connected? Don't hang in inotify for good, as there's no timeout set for it */ + if (bus->state == BUS_WATCH_BIND) + return -EUNATCH; + r = bus_ensure_running(bus); if (r < 0) return r; @@ -2832,11 +3047,78 @@ _public_ int sd_bus_add_filter( return 0; } -_public_ int sd_bus_add_match( +static int add_match_callback( + sd_bus_message *m, + void *userdata, + sd_bus_error *ret_error) { + + sd_bus_slot *match_slot = userdata; + bool failed = false; + int r; + + assert(m); + assert(match_slot); + + sd_bus_slot_ref(match_slot); + + if (sd_bus_message_is_method_error(m, NULL)) { + log_debug_errno(sd_bus_message_get_errno(m), + "Unable to add match %s, failing connection: %s", + match_slot->match_callback.match_string, + sd_bus_message_get_error(m)->message); + + failed = true; + } else + log_debug("Match %s successfully installed.", match_slot->match_callback.match_string); + + if (match_slot->match_callback.install_callback) { + sd_bus *bus; + + bus = sd_bus_message_get_bus(m); + + /* This function has been called as slot handler, and we want to call another slot handler. Let's + * update the slot callback metadata temporarily with our own data, and then revert back to the old + * values. */ + + assert(bus->current_slot == match_slot->match_callback.install_slot); + assert(bus->current_handler == add_match_callback); + assert(bus->current_userdata == userdata); + + bus->current_slot = match_slot; + bus->current_handler = match_slot->match_callback.install_callback; + bus->current_userdata = match_slot->userdata; + + r = match_slot->match_callback.install_callback(m, match_slot->userdata, ret_error); + + bus->current_slot = match_slot->match_callback.install_slot; + bus->current_handler = add_match_callback; + bus->current_userdata = userdata; + + match_slot->match_callback.install_slot = sd_bus_slot_unref(match_slot->match_callback.install_slot); + } else { + if (failed) /* Generic failure handling: destroy the connection */ + bus_enter_closing(sd_bus_message_get_bus(m)); + + r = 1; + } + + if (failed && match_slot->floating) { + bus_slot_disconnect(match_slot); + sd_bus_slot_unref(match_slot); + } + + sd_bus_slot_unref(match_slot); + + return r; +} + +static int bus_add_match_full( sd_bus *bus, sd_bus_slot **slot, + bool asynchronous, const char *match, sd_bus_message_handler_t callback, + sd_bus_message_handler_t install_callback, void *userdata) { struct bus_match_component *components = NULL; @@ -2859,18 +3141,17 @@ _public_ int sd_bus_add_match( } s->match_callback.callback = callback; + s->match_callback.install_callback = install_callback; if (bus->bus_client) { enum bus_match_scope scope; scope = bus_match_get_scope(components, n_components); - /* Do not install server-side matches for matches - * against the local service, interface or bus path. */ + /* Do not install server-side matches for matches against the local service, interface or bus path. */ if (scope != BUS_MATCH_LOCAL) { - /* We store the original match string, so that - * we can use it to remove the match again. */ + /* We store the original match string, so that we can use it to remove the match again. */ s->match_callback.match_string = strdup(match); if (!s->match_callback.match_string) { @@ -2878,7 +3159,14 @@ _public_ int sd_bus_add_match( goto finish; } - r = bus_add_match_internal(bus, s->match_callback.match_string, components, n_components); + if (asynchronous) + r = bus_add_match_internal_async(bus, + &s->match_callback.install_slot, + s->match_callback.match_string, + add_match_callback, + s); + else + r = bus_add_match_internal(bus, s->match_callback.match_string); if (r < 0) goto finish; @@ -2902,35 +3190,25 @@ finish: return r; } -int bus_remove_match_by_string( +_public_ int sd_bus_add_match( sd_bus *bus, + sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata) { - struct bus_match_component *components = NULL; - unsigned n_components = 0; - struct match_callback *c; - int r = 0; - - assert_return(bus, -EINVAL); - assert_return(match, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - r = bus_match_parse(match, &components, &n_components); - if (r < 0) - goto finish; - - r = bus_match_find(&bus->match_callbacks, components, n_components, NULL, NULL, &c); - if (r <= 0) - goto finish; - - sd_bus_slot_unref(container_of(c, sd_bus_slot, match_callback)); + return bus_add_match_full(bus, slot, false, match, callback, NULL, userdata); +} -finish: - bus_match_parse_free(components, n_components); +_public_ int sd_bus_add_match_async( + sd_bus *bus, + sd_bus_slot **slot, + const char *match, + sd_bus_message_handler_t callback, + sd_bus_message_handler_t install_callback, + void *userdata) { - return r; + return bus_add_match_full(bus, slot, true, match, callback, install_callback, userdata); } bool bus_pid_changed(sd_bus *bus) { @@ -2948,9 +3226,13 @@ static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userd assert(bus); + /* Note that this is called both on input_fd, output_fd as well as inotify_fd events */ + r = sd_bus_process(bus, NULL); - if (r < 0) - return r; + if (r < 0) { + log_debug_errno(r, "Processing of bus failed, closing down: %m"); + bus_enter_closing(bus); + } return 1; } @@ -2962,8 +3244,10 @@ static int time_callback(sd_event_source *s, uint64_t usec, void *userdata) { assert(bus); r = sd_bus_process(bus, NULL); - if (r < 0) - return r; + if (r < 0) { + log_debug_errno(r, "Processing of bus failed, closing down: %m"); + bus_enter_closing(bus); + } return 1; } @@ -2977,38 +3261,45 @@ static int prepare_callback(sd_event_source *s, void *userdata) { assert(bus); e = sd_bus_get_events(bus); - if (e < 0) - return e; + if (e < 0) { + r = e; + goto fail; + } if (bus->output_fd != bus->input_fd) { r = sd_event_source_set_io_events(bus->input_io_event_source, e & POLLIN); if (r < 0) - return r; + goto fail; r = sd_event_source_set_io_events(bus->output_io_event_source, e & POLLOUT); - if (r < 0) - return r; - } else { + } else r = sd_event_source_set_io_events(bus->input_io_event_source, e); - if (r < 0) - return r; - } + if (r < 0) + goto fail; r = sd_bus_get_timeout(bus, &until); if (r < 0) - return r; + goto fail; if (r > 0) { int j; j = sd_event_source_set_time(bus->time_event_source, until); - if (j < 0) - return j; + if (j < 0) { + r = j; + goto fail; + } } r = sd_event_source_set_enabled(bus->time_event_source, r > 0); if (r < 0) - return r; + goto fail; + + return 1; + +fail: + log_debug_errno(r, "Preparing of bus events failed, closing down: %m"); + bus_enter_closing(bus); return 1; } @@ -3024,7 +3315,7 @@ static int quit_callback(sd_event_source *event, void *userdata) { return 1; } -static int attach_io_events(sd_bus *bus) { +int bus_attach_io_events(sd_bus *bus) { int r; assert(bus); @@ -3078,7 +3369,7 @@ static int attach_io_events(sd_bus *bus) { return 0; } -static void detach_io_events(sd_bus *bus) { +static void bus_detach_io_events(sd_bus *bus) { assert(bus); if (bus->input_io_event_source) { @@ -3092,6 +3383,44 @@ static void detach_io_events(sd_bus *bus) { } } +int bus_attach_inotify_event(sd_bus *bus) { + int r; + + assert(bus); + + if (bus->inotify_fd < 0) + return 0; + + if (!bus->event) + return 0; + + if (!bus->inotify_event_source) { + r = sd_event_add_io(bus->event, &bus->inotify_event_source, bus->inotify_fd, EPOLLIN, io_callback, bus); + if (r < 0) + return r; + + r = sd_event_source_set_priority(bus->inotify_event_source, bus->event_priority); + if (r < 0) + return r; + + r = sd_event_source_set_description(bus->inotify_event_source, "bus-inotify"); + } else + r = sd_event_source_set_io_fd(bus->inotify_event_source, bus->inotify_fd); + if (r < 0) + return r; + + return 0; +} + +static void bus_detach_inotify_event(sd_bus *bus) { + assert(bus); + + if (bus->inotify_event_source) { + sd_event_source_set_enabled(bus->inotify_event_source, SD_EVENT_OFF); + bus->inotify_event_source = sd_event_source_unref(bus->inotify_event_source); + } +} + _public_ int sd_bus_attach_event(sd_bus *bus, sd_event *event, int priority) { int r; @@ -3132,7 +3461,11 @@ _public_ int sd_bus_attach_event(sd_bus *bus, sd_event *event, int priority) { if (r < 0) goto fail; - r = attach_io_events(bus); + r = bus_attach_io_events(bus); + if (r < 0) + goto fail; + + r = bus_attach_inotify_event(bus); if (r < 0) goto fail; @@ -3149,7 +3482,8 @@ _public_ int sd_bus_detach_event(sd_bus *bus) { if (!bus->event) return 0; - detach_io_events(bus); + bus_detach_io_events(bus); + bus_detach_inotify_event(bus); if (bus->time_event_source) { sd_event_source_set_enabled(bus->time_event_source, SD_EVENT_OFF); @@ -3251,12 +3585,9 @@ _public_ int sd_bus_default(sd_bus **ret) { /* No type is specified, so we have not other option than to * use the starter address if it is set. */ - e = secure_getenv("DBUS_STARTER_ADDRESS"); - if (e) { - + if (e) return bus_default(sd_bus_open, &default_starter_bus, ret); - } /* Finally, if nothing is set use the cached connection for * the right scope */ @@ -3597,7 +3928,7 @@ _public_ int sd_bus_is_monitor(sd_bus *bus) { assert_return(bus, -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); - return !!(bus->hello_flags & KDBUS_HELLO_MONITOR); + return bus->is_monitor; } static void flush_close(sd_bus *bus) { @@ -3634,3 +3965,22 @@ _public_ int sd_bus_get_exit_on_disconnect(sd_bus *bus) { return bus->exit_on_disconnect; } + +_public_ int sd_bus_set_sender(sd_bus *bus, const char *sender) { + assert_return(bus, -EINVAL); + assert_return(!bus->bus_client, -EPERM); + assert_return(!sender || service_name_is_valid(sender), -EINVAL); + + return free_and_strdup(&bus->patch_sender, sender); +} + +_public_ int sd_bus_get_sender(sd_bus *bus, const char **ret) { + assert_return(bus, -EINVAL); + assert_return(ret, -EINVAL); + + if (!bus->patch_sender) + return -ENODATA; + + *ret = bus->patch_sender; + return 0; +} diff --git a/src/libsystemd/sd-bus/test-bus-chat.c b/src/libsystemd/sd-bus/test-bus-chat.c index 1b2efb9bb4..bd6721946a 100644 --- a/src/libsystemd/sd-bus/test-bus-chat.c +++ b/src/libsystemd/sd-bus/test-bus-chat.c @@ -102,9 +102,9 @@ static int server_init(sd_bus **_bus) { goto fail; } - r = sd_bus_add_match(bus, NULL, "type='signal',interface='foo.bar',member='Notify'", match_callback, NULL); + r = sd_bus_match_signal(bus, NULL, NULL, NULL, "foo.bar", "Notify", match_callback, NULL); if (r < 0) { - log_error_errno(r, "Failed to add match: %m"); + log_error_errno(r, "Failed to request match: %m"); goto fail; } diff --git a/src/libsystemd/sd-bus/test-bus-watch-bind.c b/src/libsystemd/sd-bus/test-bus-watch-bind.c new file mode 100644 index 0000000000..aef5ba9486 --- /dev/null +++ b/src/libsystemd/sd-bus/test-bus-watch-bind.c @@ -0,0 +1,239 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/*** + This file is part of systemd. + + Copyright 2017 Lennart Poettering + + systemd 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. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <pthread.h> + +#include "sd-bus.h" +#include "sd-event.h" +#include "sd-id128.h" + +#include "alloc-util.h" +#include "fd-util.h" +#include "fileio.h" +#include "fs-util.h" +#include "mkdir.h" +#include "path-util.h" +#include "random-util.h" +#include "rm-rf.h" +#include "socket-util.h" +#include "string-util.h" + +static int method_foobar(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { + log_info("Got Foobar() call."); + + assert_se(sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), 0) >= 0); + return sd_bus_reply_method_return(m, NULL); +} + +static int method_exit(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { + log_info("Got Exit() call"); + assert_se(sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), 1) >= 0); + return sd_bus_reply_method_return(m, NULL); +} + +static const sd_bus_vtable vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_METHOD("Foobar", NULL, NULL, method_foobar, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Exit", NULL, NULL, method_exit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_VTABLE_END, +}; + +static void* thread_server(void *p) { + _cleanup_free_ char *suffixed = NULL, *suffixed2 = NULL, *d = NULL; + _cleanup_close_ int fd = -1; + union sockaddr_union u = { + .un.sun_family = AF_UNIX, + }; + const char *path = p; + + log_debug("Initializing server"); + + /* Let's play some games, by slowly creating the socket directory, and renaming it in the middle */ + (void) usleep(100 * USEC_PER_MSEC); + + assert_se(mkdir_parents(path, 0755) >= 0); + (void) usleep(100 * USEC_PER_MSEC); + + d = dirname_malloc(path); + assert_se(d); + assert_se(asprintf(&suffixed, "%s.%" PRIx64, d, random_u64()) >= 0); + assert_se(rename(d, suffixed) >= 0); + (void) usleep(100 * USEC_PER_MSEC); + + assert_se(asprintf(&suffixed2, "%s.%" PRIx64, d, random_u64()) >= 0); + assert_se(symlink(suffixed2, d) >= 0); + (void) usleep(100 * USEC_PER_MSEC); + + assert_se(symlink(basename(suffixed), suffixed2) >= 0); + (void) usleep(100 * USEC_PER_MSEC); + + strncpy(u.un.sun_path, path, sizeof(u.un.sun_path)); + + fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0); + assert_se(fd >= 0); + + assert_se(bind(fd, &u.sa, SOCKADDR_UN_LEN(u.un)) >= 0); + usleep(100 * USEC_PER_MSEC); + + assert_se(listen(fd, SOMAXCONN) >= 0); + usleep(100 * USEC_PER_MSEC); + + assert_se(touch(path) >= 0); + usleep(100 * USEC_PER_MSEC); + + log_debug("Initialized server"); + + for (;;) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + _cleanup_(sd_event_unrefp) sd_event *event = NULL; + sd_id128_t id; + int bus_fd, code; + + assert_se(sd_id128_randomize(&id) >= 0); + + assert_se(sd_event_new(&event) >= 0); + + bus_fd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC); + assert_se(bus_fd >= 0); + + log_debug("Accepted server connection"); + + assert_se(sd_bus_new(&bus) >= 0); + assert_se(sd_bus_set_description(bus, "server") >= 0); + assert_se(sd_bus_set_fd(bus, bus_fd, bus_fd) >= 0); + assert_se(sd_bus_set_server(bus, true, id) >= 0); + /* assert_se(sd_bus_set_anonymous(bus, true) >= 0); */ + + assert_se(sd_bus_attach_event(bus, event, 0) >= 0); + + assert_se(sd_bus_add_object_vtable(bus, NULL, "/foo", "foo.TestInterface", vtable, NULL) >= 0); + + assert_se(sd_bus_start(bus) >= 0); + + assert_se(sd_event_loop(event) >= 0); + + assert_se(sd_event_get_exit_code(event, &code) >= 0); + + if (code > 0) + break; + } + + log_debug("Server done"); + + return NULL; +} + +static void* thread_client1(void *p) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + const char *path = p, *t; + int r; + + log_debug("Initializing client1"); + + assert_se(sd_bus_new(&bus) >= 0); + assert_se(sd_bus_set_description(bus, "client1") >= 0); + + t = strjoina("unix:path=", path); + assert_se(sd_bus_set_address(bus, t) >= 0); + assert_se(sd_bus_set_watch_bind(bus, true) >= 0); + assert_se(sd_bus_start(bus) >= 0); + + r = sd_bus_call_method(bus, "foo.bar", "/foo", "foo.TestInterface", "Foobar", &error, NULL, NULL); + assert_se(r >= 0); + + log_debug("Client1 done"); + + return NULL; +} + +static int client2_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { + assert_se(sd_bus_message_is_method_error(m, NULL) == 0); + assert_se(sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), 0) >= 0); + return 0; +} + +static void* thread_client2(void *p) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + _cleanup_(sd_event_unrefp) sd_event *event = NULL; + const char *path = p, *t; + + log_debug("Initializing client2"); + + assert_se(sd_event_new(&event) >= 0); + assert_se(sd_bus_new(&bus) >= 0); + assert_se(sd_bus_set_description(bus, "client2") >= 0); + + t = strjoina("unix:path=", path); + assert_se(sd_bus_set_address(bus, t) >= 0); + assert_se(sd_bus_set_watch_bind(bus, true) >= 0); + assert_se(sd_bus_attach_event(bus, event, 0) >= 0); + assert_se(sd_bus_start(bus) >= 0); + + assert_se(sd_bus_call_method_async(bus, NULL, "foo.bar", "/foo", "foo.TestInterface", "Foobar", client2_callback, NULL, NULL) >= 0); + + assert_se(sd_event_loop(event) >= 0); + + log_debug("Client2 done"); + + return NULL; +} + +static void request_exit(const char *path) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + const char *t; + + assert_se(sd_bus_new(&bus) >= 0); + + t = strjoina("unix:path=", path); + assert_se(sd_bus_set_address(bus, t) >= 0); + assert_se(sd_bus_set_watch_bind(bus, true) >= 0); + assert_se(sd_bus_set_description(bus, "request-exit") >= 0); + assert_se(sd_bus_start(bus) >= 0); + + assert_se(sd_bus_call_method(bus, "foo.bar", "/foo", "foo.TestInterface", "Exit", NULL, NULL, NULL) >= 0); +} + +int main(int argc, char *argv[]) { + _cleanup_(rm_rf_physical_and_freep) char *d = NULL; + pthread_t server, client1, client2; + char *path; + + log_set_max_level(LOG_DEBUG); + + /* We use /dev/shm here rather than /tmp, since some weird distros might set up /tmp as some weird fs that + * doesn't support inotify properly. */ + assert_se(mkdtemp_malloc("/dev/shm/systemd-watch-bind-XXXXXX", &d) >= 0); + + path = strjoina(d, "/this/is/a/socket"); + + assert_se(pthread_create(&server, NULL, thread_server, path) == 0); + assert_se(pthread_create(&client1, NULL, thread_client1, path) == 0); + assert_se(pthread_create(&client2, NULL, thread_client2, path) == 0); + + assert_se(pthread_join(client1, NULL) == 0); + assert_se(pthread_join(client2, NULL) == 0); + + request_exit(path); + + assert_se(pthread_join(server, NULL) == 0); + + return 0; +} diff --git a/src/libsystemd/sd-login/sd-login.c b/src/libsystemd/sd-login/sd-login.c index e8adaa6823..69572d1a51 100644 --- a/src/libsystemd/sd-login/sd-login.c +++ b/src/libsystemd/sd-login/sd-login.c @@ -1061,10 +1061,15 @@ _public_ sd_login_monitor* sd_login_monitor_unref(sd_login_monitor *m) { } _public_ int sd_login_monitor_flush(sd_login_monitor *m) { + int r; assert_return(m, -EINVAL); - return flush_fd(MONITOR_TO_FD(m)); + r = flush_fd(MONITOR_TO_FD(m)); + if (r < 0) + return r; + + return 0; } _public_ int sd_login_monitor_get_fd(sd_login_monitor *m) { diff --git a/src/libudev/libudev-queue.c b/src/libudev/libudev-queue.c index b941afb773..85ceb263a3 100644 --- a/src/libudev/libudev-queue.c +++ b/src/libudev/libudev-queue.c @@ -268,8 +268,16 @@ _public_ int udev_queue_get_fd(struct udev_queue *udev_queue) { * Returns: the result of clearing the watch for queue changes. */ _public_ int udev_queue_flush(struct udev_queue *udev_queue) { + int r; + + assert(udev_queue); + if (udev_queue->fd < 0) return -EINVAL; - return flush_fd(udev_queue->fd); + r = flush_fd(udev_queue->fd); + if (r < 0) + return r; + + return 0; } diff --git a/src/locale/localed.c b/src/locale/localed.c index 3e3f03e046..02f5e8c656 100644 --- a/src/locale/localed.c +++ b/src/locale/localed.c @@ -652,9 +652,9 @@ static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) { if (r < 0) return log_error_errno(r, "Failed to register object: %m"); - r = sd_bus_request_name(bus, "org.freedesktop.locale1", 0); + r = sd_bus_request_name_async(bus, NULL, "org.freedesktop.locale1", 0, NULL, NULL); if (r < 0) - return log_error_errno(r, "Failed to register name: %m"); + return log_error_errno(r, "Failed to request name: %m"); r = sd_bus_attach_event(bus, event, 0); if (r < 0) diff --git a/src/login/logind-user-dbus.c b/src/login/logind-user-dbus.c index 9fca5ce0cd..d5d086cfe0 100644 --- a/src/login/logind-user-dbus.c +++ b/src/login/logind-user-dbus.c @@ -288,13 +288,13 @@ int user_object_find(sd_bus *bus, const char *path, const char *interface, void return 0; r = parse_uid(p, &uid); - } - if (r < 0) - return 0; + if (r < 0) + return 0; - user = hashmap_get(m->users, UID_TO_PTR(uid)); - if (!user) - return 0; + user = hashmap_get(m->users, UID_TO_PTR(uid)); + if (!user) + return 0; + } *found = user; return 1; diff --git a/src/login/logind.c b/src/login/logind.c index 49ca367e18..cbaaa48e5a 100644 --- a/src/login/logind.c +++ b/src/login/logind.c @@ -658,7 +658,6 @@ static int manager_reserve_vt(Manager *m) { } static int manager_connect_bus(Manager *m) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; int r; assert(m); @@ -696,65 +695,65 @@ static int manager_connect_bus(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to add user enumerator: %m"); - r = sd_bus_add_match(m->bus, - NULL, - "type='signal'," - "sender='org.freedesktop.systemd1'," - "interface='org.freedesktop.systemd1.Manager'," - "member='JobRemoved'," - "path='/org/freedesktop/systemd1'", - match_job_removed, m); + r = sd_bus_match_signal_async( + m->bus, + NULL, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "JobRemoved", + match_job_removed, NULL, m); if (r < 0) - return log_error_errno(r, "Failed to add match for JobRemoved: %m"); - - r = sd_bus_add_match(m->bus, - NULL, - "type='signal'," - "sender='org.freedesktop.systemd1'," - "interface='org.freedesktop.systemd1.Manager'," - "member='UnitRemoved'," - "path='/org/freedesktop/systemd1'", - match_unit_removed, m); + return log_error_errno(r, "Failed to request match for JobRemoved: %m"); + + r = sd_bus_match_signal_async( + m->bus, + NULL, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "UnitRemoved", + match_unit_removed, NULL, m); if (r < 0) - return log_error_errno(r, "Failed to add match for UnitRemoved: %m"); - - r = sd_bus_add_match(m->bus, - NULL, - "type='signal'," - "sender='org.freedesktop.systemd1'," - "interface='org.freedesktop.DBus.Properties'," - "member='PropertiesChanged'", - match_properties_changed, m); + return log_error_errno(r, "Failed to request match for UnitRemoved: %m"); + + r = sd_bus_match_signal_async( + m->bus, + NULL, + "org.freedesktop.systemd1", + NULL, + "org.freedesktop.DBus.Properties", + "PropertiesChanged", + match_properties_changed, NULL, m); if (r < 0) - return log_error_errno(r, "Failed to add match for PropertiesChanged: %m"); - - r = sd_bus_add_match(m->bus, - NULL, - "type='signal'," - "sender='org.freedesktop.systemd1'," - "interface='org.freedesktop.systemd1.Manager'," - "member='Reloading'," - "path='/org/freedesktop/systemd1'", - match_reloading, m); + return log_error_errno(r, "Failed to request match for PropertiesChanged: %m"); + + r = sd_bus_match_signal_async( + m->bus, + NULL, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "Reloading", + match_reloading, NULL, m); if (r < 0) - return log_error_errno(r, "Failed to add match for Reloading: %m"); + return log_error_errno(r, "Failed to request match for Reloading: %m"); - r = sd_bus_call_method( + r = sd_bus_call_method_async( m->bus, + NULL, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "Subscribe", - &error, - NULL, NULL); - if (r < 0) { - log_error("Failed to enable subscription: %s", bus_error_message(&error, r)); - return r; - } + NULL, NULL, + NULL); + if (r < 0) + return log_error_errno(r, "Failed to enable subscription: %m"); - r = sd_bus_request_name(m->bus, "org.freedesktop.login1", 0); + r = sd_bus_request_name_async(m->bus, NULL, "org.freedesktop.login1", 0, NULL, NULL); if (r < 0) - return log_error_errno(r, "Failed to register name: %m"); + return log_error_errno(r, "Failed to request name: %m"); r = sd_bus_attach_event(m->bus, m->event, SD_EVENT_PRIORITY_NORMAL); if (r < 0) diff --git a/src/login/pam_systemd.c b/src/login/pam_systemd.c index 246bbddeee..1c3ba33e23 100644 --- a/src/login/pam_systemd.c +++ b/src/login/pam_systemd.c @@ -197,7 +197,7 @@ static int export_legacy_dbus_address( return PAM_SUCCESS; s = mfree(s); - if (asprintf(&s, UNIX_USER_BUS_ADDRESS_FMT, runtime) < 0) + if (asprintf(&s, DEFAULT_USER_BUS_ADDRESS_FMT, runtime) < 0) goto error; r = pam_misc_setenv(handle, "DBUS_SESSION_BUS_ADDRESS", s, 0); diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index c435bb9b5a..615db5afe8 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -1568,9 +1568,9 @@ static int login_machine(int argc, char *argv[], void *userdata) { "member='MachineRemoved'," "arg0='", machine, "'"); - r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward); + r = sd_bus_add_match_async(bus, &slot, match, on_machine_removed, NULL, &forward); if (r < 0) - return log_error_errno(r, "Failed to add machine removal match: %m"); + return log_error_errno(r, "Failed to request machine removal match: %m"); r = sd_bus_call_method( bus, @@ -1643,9 +1643,9 @@ static int shell_machine(int argc, char *argv[], void *userdata) { "member='MachineRemoved'," "arg0='", machine, "'"); - r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward); + r = sd_bus_add_match_async(bus, &slot, match, on_machine_removed, NULL, &forward); if (r < 0) - return log_error_errno(r, "Failed to add machine removal match: %m"); + return log_error_errno(r, "Failed to request machine removal match: %m"); r = sd_bus_message_new_method_call( bus, @@ -2087,28 +2087,27 @@ static int transfer_image_common(sd_bus *bus, sd_bus_message *m) { if (r < 0) return log_error_errno(r, "Failed to attach bus to event loop: %m"); - r = sd_bus_add_match( + r = sd_bus_match_signal_async( bus, &slot_job_removed, - "type='signal'," - "sender='org.freedesktop.import1'," - "interface='org.freedesktop.import1.Manager'," - "member='TransferRemoved'," - "path='/org/freedesktop/import1'", - match_transfer_removed, &path); + "org.freedesktop.import1", + "/org/freedesktop/import1", + "org.freedesktop.import1.Manager", + "TransferRemoved", + match_transfer_removed, NULL, &path); if (r < 0) - return log_error_errno(r, "Failed to install match: %m"); + return log_error_errno(r, "Failed to request match: %m"); - r = sd_bus_add_match( + r = sd_bus_match_signal_async( bus, &slot_log_message, - "type='signal'," - "sender='org.freedesktop.import1'," - "interface='org.freedesktop.import1.Transfer'," - "member='LogMessage'", - match_log_message, &path); + "org.freedesktop.import1", + NULL, + "org.freedesktop.import1.Transfer", + "LogMessage", + match_log_message, NULL, &path); if (r < 0) - return log_error_errno(r, "Failed to install match: %m"); + return log_error_errno(r, "Failed to request match: %m"); r = sd_bus_call(bus, m, 0, &error, &reply); if (r < 0) { diff --git a/src/machine/machined.c b/src/machine/machined.c index b5cb863e5d..34b2024043 100644 --- a/src/machine/machined.c +++ b/src/machine/machined.c @@ -186,7 +186,6 @@ int manager_enumerate_machines(Manager *m) { } static int manager_connect_bus(Manager *m) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; int r; assert(m); @@ -216,70 +215,65 @@ static int manager_connect_bus(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to add image enumerator: %m"); - r = sd_bus_add_match(m->bus, - NULL, - "type='signal'," - "sender='org.freedesktop.systemd1'," - "interface='org.freedesktop.systemd1.Manager'," - "member='JobRemoved'," - "path='/org/freedesktop/systemd1'", - match_job_removed, - m); + r = sd_bus_match_signal_async( + m->bus, + NULL, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "JobRemoved", + match_job_removed, NULL, m); if (r < 0) return log_error_errno(r, "Failed to add match for JobRemoved: %m"); - r = sd_bus_add_match(m->bus, - NULL, - "type='signal'," - "sender='org.freedesktop.systemd1'," - "interface='org.freedesktop.systemd1.Manager'," - "member='UnitRemoved'," - "path='/org/freedesktop/systemd1'", - match_unit_removed, - m); + r = sd_bus_match_signal_async( + m->bus, + NULL, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "UnitRemoved", + match_unit_removed, NULL, m); if (r < 0) - return log_error_errno(r, "Failed to add match for UnitRemoved: %m"); - - r = sd_bus_add_match(m->bus, - NULL, - "type='signal'," - "sender='org.freedesktop.systemd1'," - "interface='org.freedesktop.DBus.Properties'," - "member='PropertiesChanged'," - "arg0='org.freedesktop.systemd1.Unit'", - match_properties_changed, - m); + return log_error_errno(r, "Failed to request match for UnitRemoved: %m"); + + r = sd_bus_match_signal_async( + m->bus, + NULL, + "org.freedesktop.systemd1", + NULL, + "org.freedesktop.DBus.Properties", + "PropertiesChanged", + match_properties_changed, NULL, m); if (r < 0) - return log_error_errno(r, "Failed to add match for PropertiesChanged: %m"); - - r = sd_bus_add_match(m->bus, - NULL, - "type='signal'," - "sender='org.freedesktop.systemd1'," - "interface='org.freedesktop.systemd1.Manager'," - "member='Reloading'," - "path='/org/freedesktop/systemd1'", - match_reloading, - m); + return log_error_errno(r, "Failed to request match for PropertiesChanged: %m"); + + r = sd_bus_match_signal_async( + m->bus, + NULL, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "Reloading", + match_reloading, NULL, m); if (r < 0) - return log_error_errno(r, "Failed to add match for Reloading: %m"); + return log_error_errno(r, "Failed to request match for Reloading: %m"); - r = sd_bus_call_method( + r = sd_bus_call_method_async( m->bus, + NULL, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "Subscribe", - &error, - NULL, NULL); - if (r < 0) { - log_error("Failed to enable subscription: %s", bus_error_message(&error, r)); - return r; - } + NULL, NULL, + NULL); + if (r < 0) + return log_error_errno(r, "Failed to enable subscription: %m"); - r = sd_bus_request_name(m->bus, "org.freedesktop.machine1", 0); + r = sd_bus_request_name_async(m->bus, NULL, "org.freedesktop.machine1", 0, NULL, NULL); if (r < 0) - return log_error_errno(r, "Failed to register name: %m"); + return log_error_errno(r, "Failed to request name: %m"); r = sd_bus_attach_event(m->bus, m->event, 0); if (r < 0) diff --git a/src/network/netdev/netdev.c b/src/network/netdev/netdev.c index 7cf110672f..0bfe49006b 100644 --- a/src/network/netdev/netdev.c +++ b/src/network/netdev/netdev.c @@ -153,7 +153,15 @@ static void netdev_free(NetDev *netdev) { condition_free_list(netdev->match_kernel_version); condition_free_list(netdev->match_arch); - if (NETDEV_VTABLE(netdev) && + /* Invoke the per-kind done() destructor, but only if the state field is initialized. We conditionalize that + * because we parse .netdev files twice: once to determine the kind (with a short, minimal NetDev structure + * allocation, with no room for per-kind fields), and once to read the kind's properties (with a full, + * comprehensive NetDev structure allocation with enough space for whatever the specific kind needs). Now, in + * the first case we shouldn't try to destruct the per-kind NetDev fields on destruction, in the second case we + * should. We use the state field to discern the two cases: it's _NETDEV_STATE_INVALID on the first "raw" + * call. */ + if (netdev->state != _NETDEV_STATE_INVALID && + NETDEV_VTABLE(netdev) && NETDEV_VTABLE(netdev)->done) NETDEV_VTABLE(netdev)->done(netdev); @@ -615,8 +623,8 @@ static int netdev_load_one(Manager *manager, const char *filename) { if (!file) { if (errno == ENOENT) return 0; - else - return -errno; + + return -errno; } if (null_or_empty_fd(fileno(file))) { @@ -630,7 +638,7 @@ static int netdev_load_one(Manager *manager, const char *filename) { netdev_raw->n_ref = 1; netdev_raw->kind = _NETDEV_KIND_INVALID; - netdev_raw->state = _NETDEV_STATE_INVALID; + netdev_raw->state = _NETDEV_STATE_INVALID; /* an invalid state means done() of the implementation won't be called on destruction */ dropin_dirname = strjoina(basename(filename), ".d"); r = config_parse_many(filename, network_dirs, dropin_dirname, @@ -669,7 +677,7 @@ static int netdev_load_one(Manager *manager, const char *filename) { netdev->n_ref = 1; netdev->manager = manager; netdev->kind = netdev_raw->kind; - netdev->state = _NETDEV_STATE_INVALID; + netdev->state = NETDEV_STATE_LOADING; /* we initialize the state here for the first time, so that done() will be called on destruction */ if (NETDEV_VTABLE(netdev)->init) NETDEV_VTABLE(netdev)->init(netdev); diff --git a/src/network/netdev/netdev.h b/src/network/netdev/netdev.h index 507b86a078..7ae064ba8f 100644 --- a/src/network/netdev/netdev.h +++ b/src/network/netdev/netdev.h @@ -65,6 +65,7 @@ typedef enum NetDevKind { } NetDevKind; typedef enum NetDevState { + NETDEV_STATE_LOADING, NETDEV_STATE_FAILED, NETDEV_STATE_CREATING, NETDEV_STATE_READY, @@ -149,7 +150,9 @@ extern const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX]; /* For casting a netdev into the various netdev kinds */ #define DEFINE_NETDEV_CAST(UPPERCASE, MixedCase) \ static inline MixedCase* UPPERCASE(NetDev *n) { \ - if (_unlikely_(!n || n->kind != NETDEV_KIND_##UPPERCASE)) \ + if (_unlikely_(!n || \ + n->kind != NETDEV_KIND_##UPPERCASE) || \ + n->state == _NETDEV_STATE_INVALID) \ return NULL; \ \ return (MixedCase*) n; \ diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index e3c514c804..4733dd5177 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -82,19 +82,6 @@ static int setup_default_address_pool(Manager *m) { return 0; } -static int on_bus_retry(sd_event_source *s, usec_t usec, void *userdata) { - Manager *m = userdata; - - assert(s); - assert(m); - - m->bus_retry_event_source = sd_event_source_unref(m->bus_retry_event_source); - - manager_connect_bus(m); - - return 0; -} - static int manager_reset_all(Manager *m) { Link *link; Iterator i; @@ -116,6 +103,7 @@ static int match_prepare_for_sleep(sd_bus_message *message, void *userdata, sd_b int b, r; assert(message); + assert(m); r = sd_bus_message_read(message, "b", &b); if (r < 0) { @@ -133,35 +121,32 @@ static int match_prepare_for_sleep(sd_bus_message *message, void *userdata, sd_b return 0; } -int manager_connect_bus(Manager *m) { - int r; +static int on_connected(sd_bus_message *message, void *userdata, sd_bus_error *ret_error) { + Manager *m = userdata; + assert(message); assert(m); - r = sd_bus_default_system(&m->bus); - if (r < 0) { - /* We failed to connect? Yuck, we must be in early - * boot. Let's try in 5s again. */ + /* Did we get a timezone or transient hostname from DHCP while D-Bus wasn't up yet? */ + if (m->dynamic_hostname) + (void) manager_set_hostname(m, m->dynamic_hostname); + if (m->dynamic_timezone) + (void) manager_set_timezone(m, m->dynamic_timezone); - log_debug_errno(r, "Failed to connect to bus, trying again in 5s: %m"); + return 0; +} - r = sd_event_add_time(m->event, &m->bus_retry_event_source, CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + 5*USEC_PER_SEC, 0, on_bus_retry, m); - if (r < 0) - return log_error_errno(r, "Failed to install bus reconnect time event: %m"); +int manager_connect_bus(Manager *m) { + int r; + assert(m); + + if (m->bus) return 0; - } - r = sd_bus_add_match(m->bus, &m->prepare_for_sleep_slot, - "type='signal'," - "sender='org.freedesktop.login1'," - "interface='org.freedesktop.login1.Manager'," - "member='PrepareForSleep'," - "path='/org/freedesktop/login1'", - match_prepare_for_sleep, - m); + r = bus_open_system_watch_bind(&m->bus); if (r < 0) - return log_error_errno(r, "Failed to add match for PrepareForSleep: %m"); + return log_error_errno(r, "Failed to connect to bus: %m"); r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/network1", "org.freedesktop.network1.Manager", manager_vtable, m); if (r < 0) @@ -183,25 +168,35 @@ int manager_connect_bus(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to add network enumerator: %m"); - r = sd_bus_request_name(m->bus, "org.freedesktop.network1", 0); + r = sd_bus_request_name_async(m->bus, NULL, "org.freedesktop.network1", 0, NULL, NULL); if (r < 0) - return log_error_errno(r, "Failed to register name: %m"); + return log_error_errno(r, "Failed to request name: %m"); r = sd_bus_attach_event(m->bus, m->event, 0); if (r < 0) return log_error_errno(r, "Failed to attach bus to event loop: %m"); - /* Did we get a timezone or transient hostname from DHCP while D-Bus wasn't up yet? */ - if (m->dynamic_hostname) { - r = manager_set_hostname(m, m->dynamic_hostname); - if (r < 0) - return r; - } - if (m->dynamic_timezone) { - r = manager_set_timezone(m, m->dynamic_timezone); - if (r < 0) - return r; - } + r = sd_bus_match_signal_async( + m->bus, + &m->connected_slot, + "org.freedesktop.DBus.Local", + NULL, + "org.freedesktop.DBus.Local", + "Connected", + on_connected, NULL, m); + if (r < 0) + return log_error_errno(r, "Failed to request match on Connected signal: %m"); + + r = sd_bus_match_signal_async( + m->bus, + &m->prepare_for_sleep_slot, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "PrepareForSleep", + match_prepare_for_sleep, NULL, m); + if (r < 0) + log_warning_errno(r, "Failed to request match for PrepareForSleep, ignoring: %m"); return 0; } @@ -1321,6 +1316,7 @@ void manager_free(Manager *m) { sd_bus_unref(m->bus); sd_bus_slot_unref(m->prepare_for_sleep_slot); + sd_bus_slot_unref(m->connected_slot); sd_event_source_unref(m->bus_retry_event_source); free(m->dynamic_timezone); @@ -1595,12 +1591,12 @@ int manager_set_hostname(Manager *m, const char *hostname) { int r; log_debug("Setting transient hostname: '%s'", strna(hostname)); + if (free_and_strdup(&m->dynamic_hostname, hostname) < 0) return log_oom(); - if (!m->bus) { - /* TODO: replace by assert when we can rely on kdbus */ - log_info("Not connected to system bus, ignoring transient hostname."); + if (!m->bus || sd_bus_is_ready(m->bus) <= 0) { + log_info("Not connected to system bus, not setting hostname."); return 0; } @@ -1647,8 +1643,8 @@ int manager_set_timezone(Manager *m, const char *tz) { if (free_and_strdup(&m->dynamic_timezone, tz) < 0) return log_oom(); - if (!m->bus) { - log_info("Not connected to system bus, ignoring timezone."); + if (!m->bus || sd_bus_is_ready(m->bus) <= 0) { + log_info("Not connected to system bus, not setting hostname."); return 0; } diff --git a/src/network/networkd-manager.h b/src/network/networkd-manager.h index 186cb41891..1f7f306af9 100644 --- a/src/network/networkd-manager.h +++ b/src/network/networkd-manager.h @@ -43,6 +43,7 @@ struct Manager { sd_event_source *bus_retry_event_source; sd_bus *bus; sd_bus_slot *prepare_for_sleep_slot; + sd_bus_slot *connected_slot; struct udev *udev; struct udev_monitor *udev_monitor; sd_event_source *udev_event_source; diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 383047acc7..f580b46f86 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -3622,14 +3622,16 @@ static int run(int master, * case PID 1 will send us a friendly RequestStop signal, when it is asked to terminate the * scope. Let's hook into that, and cleanly shut down the container, and print a friendly message. */ - r = sd_bus_add_match(bus, NULL, - "type='signal'," - "sender='org.freedesktop.systemd1'," - "interface='org.freedesktop.systemd1.Scope'," - "member='RequestStop'", - on_request_stop, PID_TO_PTR(*pid)); + r = sd_bus_match_signal_async( + bus, + NULL, + "org.freedesktop.systemd1", + NULL, + "org.freedesktop.systemd1.Scope", + "RequestStop", + on_request_stop, NULL, PID_TO_PTR(*pid)); if (r < 0) - return log_error_errno(r, "Failed to install request stop match: %m"); + return log_error_errno(r, "Failed to request RequestStop match: %m"); } if (arg_register) { diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c index 9157d9ea68..ffd7c4824e 100644 --- a/src/resolve/resolved-bus.c +++ b/src/resolve/resolved-bus.c @@ -1844,18 +1844,6 @@ static const sd_bus_vtable resolve_vtable[] = { SD_BUS_VTABLE_END, }; -static int on_bus_retry(sd_event_source *s, usec_t usec, void *userdata) { - Manager *m = userdata; - - assert(s); - assert(m); - - m->bus_retry_event_source = sd_event_source_unref(m->bus_retry_event_source); - - manager_connect_bus(m); - return 0; -} - static int match_prepare_for_sleep(sd_bus_message *message, void *userdata, sd_bus_error *ret_error) { Manager *m = userdata; int b, r; @@ -1886,20 +1874,9 @@ int manager_connect_bus(Manager *m) { if (m->bus) return 0; - r = sd_bus_default_system(&m->bus); - if (r < 0) { - /* We failed to connect? Yuck, we must be in early - * boot. Let's try in 5s again. */ - - log_debug_errno(r, "Failed to connect to bus, trying again in 5s: %m"); - - r = sd_event_add_time(m->event, &m->bus_retry_event_source, CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + 5*USEC_PER_SEC, 0, on_bus_retry, m); - if (r < 0) - return log_error_errno(r, "Failed to install bus reconnect time event: %m"); - - (void) sd_event_source_set_description(m->bus_retry_event_source, "bus-retry"); - return 0; - } + r = bus_open_system_watch_bind(&m->bus); + if (r < 0) + return log_error_errno(r, "Failed to connect to system bus: %m"); r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/resolve1", "org.freedesktop.resolve1.Manager", resolve_vtable, m); if (r < 0) @@ -1921,24 +1898,26 @@ int manager_connect_bus(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to register dnssd enumerator: %m"); - r = sd_bus_request_name(m->bus, "org.freedesktop.resolve1", 0); + r = sd_bus_request_name_async(m->bus, NULL, "org.freedesktop.resolve1", 0, NULL, NULL); if (r < 0) - return log_error_errno(r, "Failed to register name: %m"); + return log_error_errno(r, "Failed to request name: %m"); r = sd_bus_attach_event(m->bus, m->event, 0); if (r < 0) return log_error_errno(r, "Failed to attach bus to event loop: %m"); - r = sd_bus_add_match(m->bus, &m->prepare_for_sleep_slot, - "type='signal'," - "sender='org.freedesktop.login1'," - "interface='org.freedesktop.login1.Manager'," - "member='PrepareForSleep'," - "path='/org/freedesktop/login1'", - match_prepare_for_sleep, - m); - if (r < 0) - log_error_errno(r, "Failed to add match for PrepareForSleep: %m"); + r = sd_bus_match_signal_async( + m->bus, + &m->prepare_for_sleep_slot, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "PrepareForSleep", + match_prepare_for_sleep, + NULL, + m); + if (r < 0) + log_warning_errno(r, "Failed to request match for PrepareForSleep, ignoring: %m"); return 0; } diff --git a/src/run/run.c b/src/run/run.c index 0ff8db6b3e..a30501169c 100644 --- a/src/run/run.c +++ b/src/run/run.c @@ -1059,7 +1059,6 @@ static int start_transient_service( .inactive_enter_usec = USEC_INFINITY, }; _cleanup_free_ char *path = NULL; - const char *mt; c.bus = sd_bus_ref(bus); @@ -1089,18 +1088,20 @@ static int start_transient_service( if (!path) return log_oom(); - mt = strjoina("type='signal'," - "sender='org.freedesktop.systemd1'," - "path='", path, "'," - "interface='org.freedesktop.DBus.Properties'," - "member='PropertiesChanged'"); - r = sd_bus_add_match(bus, &c.match, mt, on_properties_changed, &c); + r = sd_bus_match_signal_async( + bus, + &c.match, + "org.freedesktop.systemd1", + path, + "org.freedesktop.DBus.Properties", + "PropertiesChanged", + on_properties_changed, NULL, &c); if (r < 0) - return log_error_errno(r, "Failed to add properties changed signal."); + return log_error_errno(r, "Failed to request properties changed signal match: %m"); r = sd_bus_attach_event(bus, c.event, SD_EVENT_PRIORITY_NORMAL); if (r < 0) - return log_error_errno(r, "Failed to attach bus to event loop."); + return log_error_errno(r, "Failed to attach bus to event loop: %m"); r = run_context_update(&c, path); if (r < 0) diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c index 346d8c9d24..99d6a9b143 100644 --- a/src/shared/ask-password-api.c +++ b/src/shared/ask-password-api.c @@ -316,7 +316,7 @@ int ask_password_tty( } if (notify >= 0 && pollfd[POLL_INOTIFY].revents != 0) - flush_fd(notify); + (void) flush_fd(notify); if (pollfd[POLL_TTY].revents == 0) continue; diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index a46fa62c2c..94ee71697e 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -1730,31 +1730,25 @@ int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret) { /* When we are a bus client we match by sender. Direct * connections OTOH have no initialized sender field, and * hence we ignore the sender then */ - r = sd_bus_add_match( + r = sd_bus_match_signal_async( bus, &d->slot_job_removed, - bus->bus_client ? - "type='signal'," - "sender='org.freedesktop.systemd1'," - "interface='org.freedesktop.systemd1.Manager'," - "member='JobRemoved'," - "path='/org/freedesktop/systemd1'" : - "type='signal'," - "interface='org.freedesktop.systemd1.Manager'," - "member='JobRemoved'," - "path='/org/freedesktop/systemd1'", - match_job_removed, d); + bus->bus_client ? "org.freedesktop.systemd1" : NULL, + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "JobRemoved", + match_job_removed, NULL, d); if (r < 0) return r; - r = sd_bus_add_match( + r = sd_bus_match_signal_async( bus, &d->slot_disconnected, - "type='signal'," - "sender='org.freedesktop.DBus.Local'," - "interface='org.freedesktop.DBus.Local'," - "member='Disconnected'", - match_disconnected, d); + "org.freedesktop.DBus.Local", + NULL, + "org.freedesktop.DBus.Local", + "Disconnected", + match_disconnected, NULL, d); if (r < 0) return r; diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c index 50731fa5ff..b6677e27f6 100644 --- a/src/shared/bus-util.c +++ b/src/shared/bus-util.c @@ -68,7 +68,7 @@ static int name_owner_change_callback(sd_bus_message *m, void *userdata, sd_bus_ } int bus_async_unregister_and_exit(sd_event *e, sd_bus *bus, const char *name) { - _cleanup_free_ char *match = NULL; + const char *match; const char *unique; int r; @@ -85,23 +85,21 @@ int bus_async_unregister_and_exit(sd_event *e, sd_bus *bus, const char *name) { if (r < 0) return r; - r = asprintf(&match, - "sender='org.freedesktop.DBus'," - "type='signal'," - "interface='org.freedesktop.DBus'," - "member='NameOwnerChanged'," - "path='/org/freedesktop/DBus'," - "arg0='%s'," - "arg1='%s'," - "arg2=''", name, unique); - if (r < 0) - return -ENOMEM; - - r = sd_bus_add_match(bus, NULL, match, name_owner_change_callback, e); + match = strjoina( + "sender='org.freedesktop.DBus'," + "type='signal'," + "interface='org.freedesktop.DBus'," + "member='NameOwnerChanged'," + "path='/org/freedesktop/DBus'," + "arg0='", name, "',", + "arg1='", unique, "',", + "arg2=''"); + + r = sd_bus_add_match_async(bus, NULL, match, name_owner_change_callback, NULL, e); if (r < 0) return r; - r = sd_bus_release_name(bus, name); + r = sd_bus_release_name_async(bus, NULL, name, NULL, NULL); if (r < 0) return r; @@ -1600,3 +1598,54 @@ int bus_track_add_name_many(sd_bus_track *t, char **l) { return r; } + +int bus_open_system_watch_bind(sd_bus **ret) { + _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL; + const char *e; + int r; + + assert(ret); + + /* Match like sd_bus_open_system(), but with the "watch_bind" feature and the Connected() signal turned on. */ + + r = sd_bus_new(&bus); + if (r < 0) + return r; + + e = secure_getenv("DBUS_SYSTEM_BUS_ADDRESS"); + if (!e) + e = DEFAULT_SYSTEM_BUS_ADDRESS; + + r = sd_bus_set_address(bus, e); + if (r < 0) + return r; + + r = sd_bus_set_bus_client(bus, true); + if (r < 0) + return r; + + r = sd_bus_set_trusted(bus, true); + if (r < 0) + return r; + + r = sd_bus_negotiate_creds(bus, true, SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EFFECTIVE_CAPS); + if (r < 0) + return r; + + r = sd_bus_set_watch_bind(bus, true); + if (r < 0) + return r; + + r = sd_bus_set_connected_signal(bus, true); + if (r < 0) + return r; + + r = sd_bus_start(bus); + if (r < 0) + return r; + + *ret = bus; + bus = NULL; + + return 0; +} diff --git a/src/shared/bus-util.h b/src/shared/bus-util.h index a9f4969d7d..cbd22a6cd6 100644 --- a/src/shared/bus-util.h +++ b/src/shared/bus-util.h @@ -162,3 +162,5 @@ int bus_path_decode_unique(const char *path, const char *prefix, char **ret_send int bus_property_get_rlimit(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error); int bus_track_add_name_many(sd_bus_track *t, char **l); + +int bus_open_system_watch_bind(sd_bus **ret); diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 1376497f8d..906eb1879a 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -2905,7 +2905,6 @@ static int start_unit_one( if (wait_context) { _cleanup_free_ char *unit_path = NULL; - const char* mt; log_debug("Watching for property changes of %s", name); r = sd_bus_call_method( @@ -2928,13 +2927,15 @@ static int start_unit_one( if (r < 0) return log_error_errno(r, "Failed to add unit path %s to set: %m", unit_path); - mt = strjoina("type='signal'," - "interface='org.freedesktop.DBus.Properties'," - "path='", unit_path, "'," - "member='PropertiesChanged'"); - r = sd_bus_add_match(bus, &wait_context->match, mt, on_properties_changed, wait_context); + r = sd_bus_match_signal_async(bus, + &wait_context->match, + NULL, + unit_path, + "org.freedesktop.DBus.Properties", + "PropertiesChanged", + on_properties_changed, NULL, wait_context); if (r < 0) - return log_error_errno(r, "Failed to add match for PropertiesChanged signal: %m"); + return log_error_errno(r, "Failed to request match for PropertiesChanged signal: %m"); } log_debug("%s manager for %s on %s, %s", @@ -3150,22 +3151,21 @@ static int start_unit(int argc, char *argv[], void *userdata) { } if (arg_wait) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - wait_context.unit_paths = set_new(&string_hash_ops); if (!wait_context.unit_paths) return log_oom(); - r = sd_bus_call_method( + r = sd_bus_call_method_async( bus, + NULL, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "Subscribe", - &error, - NULL, NULL); + NULL, NULL, + NULL); if (r < 0) - return log_error_errno(r, "Failed to enable subscription: %s", bus_error_message(&error, r)); + return log_error_errno(r, "Failed to enable subscription: %m"); r = sd_event_default(&wait_context.event); if (r < 0) return log_error_errno(r, "Failed to allocate event loop: %m"); diff --git a/src/systemd/sd-bus.h b/src/systemd/sd-bus.h index c5c7096d55..c30ac9e6ec 100644 --- a/src/systemd/sd-bus.h +++ b/src/systemd/sd-bus.h @@ -150,8 +150,14 @@ int sd_bus_set_allow_interactive_authorization(sd_bus *bus, int b); int sd_bus_get_allow_interactive_authorization(sd_bus *bus); int sd_bus_set_exit_on_disconnect(sd_bus *bus, int b); int sd_bus_get_exit_on_disconnect(sd_bus *bus); +int sd_bus_set_watch_bind(sd_bus *bus, int b); +int sd_bus_get_watch_bind(sd_bus *bus); +int sd_bus_set_connected_signal(sd_bus *bus, int b); +int sd_bus_get_connected_signal(sd_bus *bus); +int sd_bus_set_sender(sd_bus *bus, const char *sender); +int sd_bus_get_sender(sd_bus *bus, const char **ret); -int sd_bus_start(sd_bus *ret); +int sd_bus_start(sd_bus *bus); int sd_bus_try_close(sd_bus *bus); void sd_bus_close(sd_bus *bus); @@ -163,6 +169,7 @@ sd_bus *sd_bus_flush_close_unref(sd_bus *bus); void sd_bus_default_flush_close(void); int sd_bus_is_open(sd_bus *bus); +int sd_bus_is_ready(sd_bus *bus); int sd_bus_get_bus_id(sd_bus *bus, sd_id128_t *id); int sd_bus_get_scope(sd_bus *bus, const char **scope); @@ -193,6 +200,7 @@ sd_event *sd_bus_get_event(sd_bus *bus); int sd_bus_add_filter(sd_bus *bus, sd_bus_slot **slot, sd_bus_message_handler_t callback, void *userdata); int sd_bus_add_match(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata); +int sd_bus_add_match_async(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, sd_bus_message_handler_t install_callback, void *userdata); int sd_bus_add_object(sd_bus *bus, sd_bus_slot **slot, const char *path, sd_bus_message_handler_t callback, void *userdata); int sd_bus_add_fallback(sd_bus *bus, sd_bus_slot **slot, const char *prefix, sd_bus_message_handler_t callback, void *userdata); int sd_bus_add_object_vtable(sd_bus *bus, sd_bus_slot **slot, const char *path, const char *interface, const sd_bus_vtable *vtable, void *userdata); @@ -267,6 +275,7 @@ int sd_bus_message_set_auto_start(sd_bus_message *m, int b); int sd_bus_message_set_allow_interactive_authorization(sd_bus_message *m, int b); int sd_bus_message_set_destination(sd_bus_message *m, const char *destination); +int sd_bus_message_set_sender(sd_bus_message *m, const char *sender); int sd_bus_message_set_priority(sd_bus_message *m, int64_t priority); int sd_bus_message_append(sd_bus_message *m, const char *types, ...); @@ -300,7 +309,9 @@ int sd_bus_message_rewind(sd_bus_message *m, int complete); int sd_bus_get_unique_name(sd_bus *bus, const char **unique); int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags); +int sd_bus_request_name_async(sd_bus *bus, sd_bus_slot **ret_slot, const char *name, uint64_t flags, sd_bus_message_handler_t callback, void *userdata); int sd_bus_release_name(sd_bus *bus, const char *name); +int sd_bus_release_name_async(sd_bus *bus, sd_bus_slot **ret_slot, const char *name, sd_bus_message_handler_t callback, void *userdata); int sd_bus_list_names(sd_bus *bus, char ***acquired, char ***activatable); /* free the results */ int sd_bus_get_name_creds(sd_bus *bus, const char *name, uint64_t mask, sd_bus_creds **creds); /* unref the result! */ int sd_bus_get_name_machine_id(sd_bus *bus, const char *name, sd_id128_t *machine); @@ -336,6 +347,9 @@ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *in int sd_bus_query_sender_creds(sd_bus_message *call, uint64_t mask, sd_bus_creds **creds); int sd_bus_query_sender_privilege(sd_bus_message *call, int capability); +int sd_bus_match_signal(sd_bus *bus, sd_bus_slot **ret, const char *sender, const char *path, const char *interface, const char *member, sd_bus_message_handler_t callback, void *userdata); +int sd_bus_match_signal_async(sd_bus *bus, sd_bus_slot **ret, const char *sender, const char *path, const char *interface, const char *member, sd_bus_message_handler_t match_callback, sd_bus_message_handler_t add_callback, void *userdata); + /* Credential handling */ int sd_bus_creds_new_from_pid(sd_bus_creds **ret, pid_t pid, uint64_t creds_mask); diff --git a/src/test/meson.build b/src/test/meson.build index 71b440637d..18e957ddc8 100644 --- a/src/test/meson.build +++ b/src/test/meson.build @@ -762,6 +762,10 @@ tests += [ [], [threads]], + [['src/libsystemd/sd-bus/test-bus-watch-bind.c'], + [], + [threads], '', 'timeout=120'], + [['src/libsystemd/sd-bus/test-bus-chat.c'], [], [threads]], diff --git a/src/test/test-condition.c b/src/test/test-condition.c index 8323a66ad3..ad64a2bb36 100644 --- a/src/test/test-condition.c +++ b/src/test/test-condition.c @@ -29,8 +29,8 @@ #include "apparmor-util.h" #include "architecture.h" #include "audit-util.h" -#include "condition.h" #include "cgroup-util.h" +#include "condition.h" #include "hostname-util.h" #include "id128-util.h" #include "ima-util.h" @@ -187,12 +187,12 @@ static int test_condition_test_control_group_controller(void) { /* Multiple valid controllers at the same time */ assert_se(cg_mask_to_string(system_mask, &controller_name) >= 0); - condition = condition_new(CONDITION_CONTROL_GROUP_CONTROLLER, controller_name, false, false); + condition = condition_new(CONDITION_CONTROL_GROUP_CONTROLLER, strempty(controller_name), false, false); assert_se(condition); assert_se(condition_test(condition)); condition_free(condition); - condition = condition_new(CONDITION_CONTROL_GROUP_CONTROLLER, controller_name, false, true); + condition = condition_new(CONDITION_CONTROL_GROUP_CONTROLLER, strempty(controller_name), false, true); assert_se(condition); assert_se(!condition_test(condition)); condition_free(condition); diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c index 86d963c4c7..2ad9e71856 100644 --- a/src/test/test-fs-util.c +++ b/src/test/test-fs-util.c @@ -388,6 +388,92 @@ static void test_access_fd(void) { } } +static void test_touch_file(void) { + uid_t test_uid, test_gid; + _cleanup_(rm_rf_physical_and_freep) char *p = NULL; + struct stat st; + const char *a; + usec_t test_mtime; + + test_uid = geteuid() == 0 ? 65534 : getuid(); + test_gid = geteuid() == 0 ? 65534 : getgid(); + + test_mtime = usec_sub_unsigned(now(CLOCK_REALTIME), USEC_PER_WEEK); + + assert_se(mkdtemp_malloc("/dev/shm/touch-file-XXXXXX", &p) >= 0); + + a = strjoina(p, "/regular"); + assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0); + assert_se(lstat(a, &st) >= 0); + assert_se(st.st_uid == test_uid); + assert_se(st.st_gid == test_gid); + assert_se(S_ISREG(st.st_mode)); + assert_se((st.st_mode & 0777) == 0640); + assert_se(timespec_load(&st.st_mtim) == test_mtime); + + a = strjoina(p, "/dir"); + assert_se(mkdir(a, 0775) >= 0); + assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0); + assert_se(lstat(a, &st) >= 0); + assert_se(st.st_uid == test_uid); + assert_se(st.st_gid == test_gid); + assert_se(S_ISDIR(st.st_mode)); + assert_se((st.st_mode & 0777) == 0640); + assert_se(timespec_load(&st.st_mtim) == test_mtime); + + a = strjoina(p, "/fifo"); + assert_se(mkfifo(a, 0775) >= 0); + assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0); + assert_se(lstat(a, &st) >= 0); + assert_se(st.st_uid == test_uid); + assert_se(st.st_gid == test_gid); + assert_se(S_ISFIFO(st.st_mode)); + assert_se((st.st_mode & 0777) == 0640); + assert_se(timespec_load(&st.st_mtim) == test_mtime); + + a = strjoina(p, "/sock"); + assert_se(mknod(a, 0775 | S_IFSOCK, 0) >= 0); + assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0); + assert_se(lstat(a, &st) >= 0); + assert_se(st.st_uid == test_uid); + assert_se(st.st_gid == test_gid); + assert_se(S_ISSOCK(st.st_mode)); + assert_se((st.st_mode & 0777) == 0640); + assert_se(timespec_load(&st.st_mtim) == test_mtime); + + if (geteuid() == 0) { + a = strjoina(p, "/cdev"); + assert_se(mknod(a, 0775 | S_IFCHR, makedev(0, 0)) >= 0); + assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0); + assert_se(lstat(a, &st) >= 0); + assert_se(st.st_uid == test_uid); + assert_se(st.st_gid == test_gid); + assert_se(S_ISCHR(st.st_mode)); + assert_se((st.st_mode & 0777) == 0640); + assert_se(timespec_load(&st.st_mtim) == test_mtime); + + a = strjoina(p, "/bdev"); + assert_se(mknod(a, 0775 | S_IFBLK, makedev(0, 0)) >= 0); + assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0); + assert_se(lstat(a, &st) >= 0); + assert_se(st.st_uid == test_uid); + assert_se(st.st_gid == test_gid); + assert_se(S_ISBLK(st.st_mode)); + assert_se((st.st_mode & 0777) == 0640); + assert_se(timespec_load(&st.st_mtim) == test_mtime); + } + + a = strjoina(p, "/lnk"); + assert_se(symlink("target", a) >= 0); + assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0); + assert_se(lstat(a, &st) >= 0); + assert_se(st.st_uid == test_uid); + assert_se(st.st_gid == test_gid); + assert_se(S_ISLNK(st.st_mode)); + assert_se((st.st_mode & 0777) == 0640); + assert_se(timespec_load(&st.st_mtim) == test_mtime); +} + int main(int argc, char *argv[]) { test_unlink_noerrno(); test_get_files_in_directory(); @@ -396,6 +482,7 @@ int main(int argc, char *argv[]) { test_chase_symlinks(); test_dot_or_dot_dot(); test_access_fd(); + test_touch_file(); return 0; } diff --git a/src/timedate/timedated.c b/src/timedate/timedated.c index a55a929a49..822835cce9 100644 --- a/src/timedate/timedated.c +++ b/src/timedate/timedated.c @@ -675,9 +675,9 @@ static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) { if (r < 0) return log_error_errno(r, "Failed to register object: %m"); - r = sd_bus_request_name(bus, "org.freedesktop.timedate1", 0); + r = sd_bus_request_name_async(bus, NULL, "org.freedesktop.timedate1", 0, NULL, NULL); if (r < 0) - return log_error_errno(r, "Failed to register name: %m"); + return log_error_errno(r, "Failed to request name: %m"); r = sd_bus_attach_event(bus, event, 0); if (r < 0) diff --git a/src/tty-ask-password-agent/tty-ask-password-agent.c b/src/tty-ask-password-agent/tty-ask-password-agent.c index 6e9c10aeb0..d2648ead93 100644 --- a/src/tty-ask-password-agent/tty-ask-password-agent.c +++ b/src/tty-ask-password-agent/tty-ask-password-agent.c @@ -159,7 +159,7 @@ static int ask_password_plymouth( } if (notify >= 0 && pollfd[POLL_INOTIFY].revents != 0) - flush_fd(notify); + (void) flush_fd(notify); if (pollfd[POLL_SOCKET].revents == 0) continue; diff --git a/tools/meson-check-api-docs.sh b/tools/meson-check-api-docs.sh new file mode 100644 index 0000000000..5bc808c1e4 --- /dev/null +++ b/tools/meson-check-api-docs.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +set -eu + +for symbol in `nm -g --defined-only "$@" | grep " T " | cut -d" " -f3 | sort -u` ; do + if test -f ${MESON_BUILD_ROOT}/man/$symbol.3 ; then + echo "✓ Symbol $symbol() is documented." + else + printf " \x1b[1;31mSymbol $symbol() lacks documentation.\x1b[0m\n" + fi +done |