summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEvgeny Vereshchagin <evvers@ya.ru>2016-08-26 16:17:05 +0300
committerGitHub <noreply@github.com>2016-08-26 16:17:05 +0300
commit6afe14ff5b91e62072005dd8eb75315a16989874 (patch)
tree279f9cde40fe2d0ce2ebb85abe961147b2cc6837
parent0c582db0c6ec3330953270cf0324038cb6615617 (diff)
parent383034987de71927683dd34896ca7f3c9c259f7c (diff)
Merge pull request #3984 from poettering/refcnt
permit bus clients to pin units to avoid automatic GC
-rw-r--r--.gitignore1
-rw-r--r--Makefile-man.am89
-rw-r--r--Makefile.am11
-rw-r--r--TODO21
-rw-r--r--man/sd-bus.xml51
-rw-r--r--man/sd_bus_creds_new_from_pid.xml4
-rw-r--r--man/sd_bus_track_add_name.xml261
-rw-r--r--man/sd_bus_track_new.xml263
-rw-r--r--man/systemctl.xml12
-rw-r--r--man/systemd-run.xml165
-rw-r--r--src/core/cgroup.c23
-rw-r--r--src/core/dbus-manager.c57
-rw-r--r--src/core/dbus-unit.c155
-rw-r--r--src/core/dbus-unit.h8
-rw-r--r--src/core/dbus.c71
-rw-r--r--src/core/dbus.h5
-rw-r--r--src/core/job.c17
-rw-r--r--src/core/job.h4
-rw-r--r--src/core/manager.c27
-rw-r--r--src/core/org.freedesktop.systemd1.conf8
-rw-r--r--src/core/unit.c70
-rw-r--r--src/core/unit.h8
-rw-r--r--src/libsystemd/libsystemd.sym10
-rw-r--r--src/libsystemd/sd-bus/bus-common-errors.c1
-rw-r--r--src/libsystemd/sd-bus/bus-common-errors.h1
-rw-r--r--src/libsystemd/sd-bus/bus-internal.h4
-rw-r--r--src/libsystemd/sd-bus/bus-track.c260
-rw-r--r--src/libsystemd/sd-bus/bus-track.h1
-rw-r--r--src/libsystemd/sd-bus/sd-bus.c144
-rw-r--r--src/libsystemd/sd-bus/test-bus-track.c113
-rw-r--r--src/run/run.c231
-rw-r--r--src/shared/bus-util.c78
-rw-r--r--src/shared/ptyfwd.c102
-rw-r--r--src/shared/ptyfwd.h8
-rw-r--r--src/shared/seccomp-util.c1
-rw-r--r--src/systemd/sd-bus.h10
36 files changed, 1905 insertions, 390 deletions
diff --git a/.gitignore b/.gitignore
index 6caa8218bd..565a3a3839 100644
--- a/.gitignore
+++ b/.gitignore
@@ -152,6 +152,7 @@
/test-bus-policy
/test-bus-server
/test-bus-signature
+/test-bus-track
/test-bus-zero-copy
/test-calendarspec
/test-cap-list
diff --git a/Makefile-man.am b/Makefile-man.am
index bf8de1e58f..ef5077cc5a 100644
--- a/Makefile-man.am
+++ b/Makefile-man.am
@@ -52,6 +52,8 @@ MANPAGES += \
man/sd_bus_path_encode.3 \
man/sd_bus_process.3 \
man/sd_bus_request_name.3 \
+ man/sd_bus_track_add_name.3 \
+ man/sd_bus_track_new.3 \
man/sd_event_add_child.3 \
man/sd_event_add_defer.3 \
man/sd_event_add_io.3 \
@@ -332,6 +334,23 @@ MANPAGES_ALIAS += \
man/sd_bus_path_encode_many.3 \
man/sd_bus_ref.3 \
man/sd_bus_release_name.3 \
+ man/sd_bus_track_add_sender.3 \
+ man/sd_bus_track_contains.3 \
+ man/sd_bus_track_count.3 \
+ man/sd_bus_track_count_name.3 \
+ man/sd_bus_track_count_sender.3 \
+ man/sd_bus_track_first.3 \
+ man/sd_bus_track_get_bus.3 \
+ man/sd_bus_track_get_recursive.3 \
+ man/sd_bus_track_get_userdata.3 \
+ man/sd_bus_track_next.3 \
+ man/sd_bus_track_ref.3 \
+ man/sd_bus_track_remove_name.3 \
+ man/sd_bus_track_remove_sender.3 \
+ man/sd_bus_track_set_recursive.3 \
+ man/sd_bus_track_set_userdata.3 \
+ man/sd_bus_track_unref.3 \
+ man/sd_bus_track_unrefp.3 \
man/sd_bus_unref.3 \
man/sd_bus_unrefp.3 \
man/sd_event.3 \
@@ -665,6 +684,23 @@ man/sd_bus_path_decode_many.3: man/sd_bus_path_encode.3
man/sd_bus_path_encode_many.3: man/sd_bus_path_encode.3
man/sd_bus_ref.3: man/sd_bus_new.3
man/sd_bus_release_name.3: man/sd_bus_request_name.3
+man/sd_bus_track_add_sender.3: man/sd_bus_track_add_name.3
+man/sd_bus_track_contains.3: man/sd_bus_track_add_name.3
+man/sd_bus_track_count.3: man/sd_bus_track_add_name.3
+man/sd_bus_track_count_name.3: man/sd_bus_track_add_name.3
+man/sd_bus_track_count_sender.3: man/sd_bus_track_add_name.3
+man/sd_bus_track_first.3: man/sd_bus_track_add_name.3
+man/sd_bus_track_get_bus.3: man/sd_bus_track_new.3
+man/sd_bus_track_get_recursive.3: man/sd_bus_track_new.3
+man/sd_bus_track_get_userdata.3: man/sd_bus_track_new.3
+man/sd_bus_track_next.3: man/sd_bus_track_add_name.3
+man/sd_bus_track_ref.3: man/sd_bus_track_new.3
+man/sd_bus_track_remove_name.3: man/sd_bus_track_add_name.3
+man/sd_bus_track_remove_sender.3: man/sd_bus_track_add_name.3
+man/sd_bus_track_set_recursive.3: man/sd_bus_track_new.3
+man/sd_bus_track_set_userdata.3: man/sd_bus_track_new.3
+man/sd_bus_track_unref.3: man/sd_bus_track_new.3
+man/sd_bus_track_unrefp.3: man/sd_bus_track_new.3
man/sd_bus_unref.3: man/sd_bus_new.3
man/sd_bus_unrefp.3: man/sd_bus_new.3
man/sd_event.3: man/sd_event_new.3
@@ -1300,6 +1336,57 @@ man/sd_bus_ref.html: man/sd_bus_new.html
man/sd_bus_release_name.html: man/sd_bus_request_name.html
$(html-alias)
+man/sd_bus_track_add_sender.html: man/sd_bus_track_add_name.html
+ $(html-alias)
+
+man/sd_bus_track_contains.html: man/sd_bus_track_add_name.html
+ $(html-alias)
+
+man/sd_bus_track_count.html: man/sd_bus_track_add_name.html
+ $(html-alias)
+
+man/sd_bus_track_count_name.html: man/sd_bus_track_add_name.html
+ $(html-alias)
+
+man/sd_bus_track_count_sender.html: man/sd_bus_track_add_name.html
+ $(html-alias)
+
+man/sd_bus_track_first.html: man/sd_bus_track_add_name.html
+ $(html-alias)
+
+man/sd_bus_track_get_bus.html: man/sd_bus_track_new.html
+ $(html-alias)
+
+man/sd_bus_track_get_recursive.html: man/sd_bus_track_new.html
+ $(html-alias)
+
+man/sd_bus_track_get_userdata.html: man/sd_bus_track_new.html
+ $(html-alias)
+
+man/sd_bus_track_next.html: man/sd_bus_track_add_name.html
+ $(html-alias)
+
+man/sd_bus_track_ref.html: man/sd_bus_track_new.html
+ $(html-alias)
+
+man/sd_bus_track_remove_name.html: man/sd_bus_track_add_name.html
+ $(html-alias)
+
+man/sd_bus_track_remove_sender.html: man/sd_bus_track_add_name.html
+ $(html-alias)
+
+man/sd_bus_track_set_recursive.html: man/sd_bus_track_new.html
+ $(html-alias)
+
+man/sd_bus_track_set_userdata.html: man/sd_bus_track_new.html
+ $(html-alias)
+
+man/sd_bus_track_unref.html: man/sd_bus_track_new.html
+ $(html-alias)
+
+man/sd_bus_track_unrefp.html: man/sd_bus_track_new.html
+ $(html-alias)
+
man/sd_bus_unref.html: man/sd_bus_new.html
$(html-alias)
@@ -2559,6 +2646,8 @@ EXTRA_DIST += \
man/sd_bus_path_encode.xml \
man/sd_bus_process.xml \
man/sd_bus_request_name.xml \
+ man/sd_bus_track_add_name.xml \
+ man/sd_bus_track_new.xml \
man/sd_event_add_child.xml \
man/sd_event_add_defer.xml \
man/sd_event_add_io.xml \
diff --git a/Makefile.am b/Makefile.am
index 431975de8b..3a617560e0 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -3323,6 +3323,7 @@ tests += \
test-bus-error \
test-bus-creds \
test-bus-gvariant \
+ test-bus-track \
test-event \
test-netlink \
test-local-addresses \
@@ -3366,6 +3367,16 @@ test_bus_cleanup_CFLAGS = \
test_bus_cleanup_LDADD = \
libsystemd-shared.la
+test_bus_track_SOURCES = \
+ src/libsystemd/sd-bus/test-bus-track.c
+
+test_bus_track_CFLAGS = \
+ $(AM_CFLAGS) \
+ $(SECCOMP_CFLAGS)
+
+test_bus_track_LDADD = \
+ libsystemd-shared.la
+
test_bus_server_SOURCES = \
src/libsystemd/sd-bus/test-bus-server.c
diff --git a/TODO b/TODO
index 33afe39783..0ce9a1fd30 100644
--- a/TODO
+++ b/TODO
@@ -33,7 +33,13 @@ Janitorial Clean-ups:
Features:
-* RemoveIPC= in unit files for removing POSIX/SysV IPC objects
+* introduce an "invocation ID" for units, that is randomly generated, and
+ identifies each runtime-cycle of a unit. It should be set freshly each time
+ we traverse inactive → activating/active, and should be the primary key to
+ map offline data (stored in the journal) with online bus objects. Let's pass
+ this as $SYSTEMD_INVOCATION_ID to services, as well as set this as xattr on
+ the cgroup of a services. The former is accessible without privileges, the
+ latter ensures the ID cannot be faked.
* Introduce ProtectSystem=strict for making the entire OS hierarchy read-only
except for a select few
@@ -58,6 +64,8 @@ Features:
* ProtectControlGroups= which mounts all of /sys/fs/cgroup read-only
+* ProtectKernelTunables= which mounts /sys and /proc/sys read-only
+
* RemoveKeyRing= to remove all keyring entries of the specified user
* Add DataDirectory=, CacheDirectory= and LogDirectory= to match
@@ -76,6 +84,9 @@ Features:
* journalctl: make sure -f ends when the container indicated by -M terminates
+* mount: automatically search for "main" partition of an image has multiple
+ partitions
+
* expose the "privileged" flag of ExecCommand on the bus, and open it up to
transient units
@@ -86,6 +97,12 @@ Features:
* allow attaching additional journald log fields to cgroups
+* add bus API for creating unit files in /etc, reusing the code for transient units
+
+* add bus API to remove unit files from /etc
+
+* add bus API to retrieve current unit file contents (i.e. implement "systemctl cat" on the bus only)
+
* rework fopen_temporary() to make use of open_tmpfile_linkable() (problem: the
kernel doesn't support linkat() that replaces existing files, currently)
@@ -112,8 +129,6 @@ Features:
* add systemctl stop --job-mode=triggering that follows TRIGGERED_BY deps and adds them to the same transaction
-* Maybe add a way how users can "pin" units into memory, so that they are not subject to automatic GC?
-
* PID1: find a way how we can reload unit file configuration for
specific units only, without reloading the whole of systemd
diff --git a/man/sd-bus.xml b/man/sd-bus.xml
index 336dd33ea0..66b1c96c15 100644
--- a/man/sd-bus.xml
+++ b/man/sd-bus.xml
@@ -44,7 +44,7 @@
<refnamediv>
<refname>sd-bus</refname>
- <refpurpose>A lightweight D-Bus and kdbus client library</refpurpose>
+ <refpurpose>A lightweight D-Bus IPC client library</refpurpose>
</refnamediv>
<refsynopsisdiv>
@@ -61,49 +61,40 @@
<refsect1>
<title>Description</title>
- <para><filename>sd-bus.h</filename> provides an implementation
- of a D-Bus client. It can interoperate both with the traditional
- <citerefentry project='man-pages'><refentrytitle>dbus-daemon</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
- and with kdbus. See
+ <para><filename>sd-bus.h</filename> provides an implementation of a D-Bus IPC client. See
<ulink url="http://www.freedesktop.org/software/dbus/" />
- for more information about the big picture.
+ for more information about D-Bus IPC.
</para>
- <important>
- <para>Interfaces described here have not been declared stable yet,
- and are not accessible from <filename>libsystemd.so</filename>.
- This documentation is provided in hope it might be useful for
- developers, without any guarantees of availability or stability.
- </para>
- </important>
-
<para>See
+ <citerefentry><refentrytitle>sd-bus-errors</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_creds_get_pid</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_creds_new_from_pid</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_bus_default</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_request_name</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_start</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_error</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_error_add_map</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_get_name_creds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_get_owner_creds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_bus_message_append</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_message_append_basic</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_bus_message_append_array</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_message_append_basic</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_bus_message_append_string_memfd</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_bus_message_append_strv</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_bus_message_can_send</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_bus_message_get_cookie</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_bus_message_get_monotonic_usec</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_negotiate_fds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_path_encode</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_request_name</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_bus_send</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_bus_set_address</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_set_allow_interactive_authorization</refentrytitle><manvolnum>3</manvolnum></citerefentry>
<citerefentry><refentrytitle>sd_bus_set_description</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_bus_set_prepare</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_creds_get_pid</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_creds_new_from_pid</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_get_name_creds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_get_owner_creds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_negotiate_fds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_path_encode</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd-bus-errors</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_error</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_error_add_map</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_set_allow_interactive_authorization</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ <citerefentry><refentrytitle>sd_bus_start</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_track_add_name</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_track_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
for more information about the functions available.</para>
</refsect1>
@@ -114,9 +105,9 @@
<para>
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>busctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry project='man-pages'><refentrytitle>dbus-daemon</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
- <citerefentry project='man-pages'><refentrytitle>dbus-send</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
- <ulink url="https://developer.gnome.org/gio/stable/gdbus.html">gdbus</ulink>
+ <citerefentry project='man-pages'><refentrytitle>dbus-send</refentrytitle><manvolnum>1</manvolnum></citerefentry>
</para>
</refsect1>
diff --git a/man/sd_bus_creds_new_from_pid.xml b/man/sd_bus_creds_new_from_pid.xml
index 082f7b67db..b4d7d61d0f 100644
--- a/man/sd_bus_creds_new_from_pid.xml
+++ b/man/sd_bus_creds_new_from_pid.xml
@@ -66,12 +66,12 @@
<funcprototype>
<funcdef>uint64_t <function>sd_bus_creds_get_mask</function></funcdef>
- <paramdef>const sd_bus_creds *<parameter>c</parameter></paramdef>
+ <paramdef>sd_bus_creds *<parameter>c</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>uint64_t <function>sd_bus_creds_get_augmented_mask</function></funcdef>
- <paramdef>const sd_bus_creds *<parameter>c</parameter></paramdef>
+ <paramdef>sd_bus_creds *<parameter>c</parameter></paramdef>
</funcprototype>
<funcprototype>
diff --git a/man/sd_bus_track_add_name.xml b/man/sd_bus_track_add_name.xml
new file mode 100644
index 0000000000..6a5e344cb1
--- /dev/null
+++ b/man/sd_bus_track_add_name.xml
@@ -0,0 +1,261 @@
+<?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">
+
+<!--
+ This file is part of systemd.
+
+ Copyright 2016 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_track_add_name">
+
+ <refentryinfo>
+ <title>sd_bus_track_add_name</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_track_add_name</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>sd_bus_track_add_name</refname>
+ <refname>sd_bus_track_add_sender</refname>
+ <refname>sd_bus_track_remove_name</refname>
+ <refname>sd_bus_track_remove_sender</refname>
+ <refname>sd_bus_track_count</refname>
+ <refname>sd_bus_track_count_sender</refname>
+ <refname>sd_bus_track_count_name</refname>
+ <refname>sd_bus_track_contains</refname>
+ <refname>sd_bus_track_first</refname>
+ <refname>sd_bus_track_next</refname>
+
+ <refpurpose>Add, remove and retrieve bus peers tracked in a bus peer tracking object</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_track_add_name</function></funcdef>
+ <paramdef>sd_bus_track* <parameter>t</parameter></paramdef>
+ <paramdef>const char* <parameter>name</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_track_add_sender</function></funcdef>
+ <paramdef>sd_bus_track* <parameter>t</parameter></paramdef>
+ <paramdef>sd_bus_message* <parameter>message</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_track_remove_name</function></funcdef>
+ <paramdef>sd_bus_track* <parameter>t</parameter></paramdef>
+ <paramdef>const char* <parameter>name</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_track_remove_sender</function></funcdef>
+ <paramdef>sd_bus_track* <parameter>t</parameter></paramdef>
+ <paramdef>sd_bus_message* <parameter>message</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>unsigned <function>sd_bus_track_count</function></funcdef>
+ <paramdef>sd_bus_track* <parameter>t</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_track_count_name</function></funcdef>
+ <paramdef>sd_bus_track* <parameter>t</parameter></paramdef>
+ <paramdef>const char* <parameter>name</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_track_count_sender</function></funcdef>
+ <paramdef>sd_bus_track* <parameter>t</parameter></paramdef>
+ <paramdef>sd_bus_message* <parameter>message</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_track_contains</function></funcdef>
+ <paramdef>sd_bus_track* <parameter>t</parameter></paramdef>
+ <paramdef>const char* <parameter>name</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>const char* <function>sd_bus_track_first</function></funcdef>
+ <paramdef>sd_bus_track* <parameter>t</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>const char* <function>sd_bus_track_next</function></funcdef>
+ <paramdef>sd_bus_track* <parameter>t</parameter></paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><function>sd_bus_track_add_name()</function> adds a peer to track to a bus peer tracking object. The first
+ argument should refer to a bus peer tracking object created with
+ <citerefentry><refentrytitle>sd_bus_track_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>, the second
+ name should refer to a D-Bus peer name to track, either in unique or well-known service format. If the name is not
+ tracked yet it will be added to the list of names to track. If it already is being tracked and non-recursive mode
+ is enabled, no operation is executed by this call. If recursive mode is enabled a per-name counter is increased by
+ one each time this call is invoked, and <function>sd_bus_track_remove_name()</function> has to be called as many
+ times as <function>sd_bus_track_add_name()</function> was invoked before in order to stop tracking of the name. Use
+ <citerefentry><refentrytitle>sd_bus_track_set_recursive</refentrytitle><manvolnum>3</manvolnum></citerefentry> to
+ switch from the default non-recursive mode to recursive mode, or back. Note that the specified name is tracked as
+ it is, well-known names are not resolved to unique names by this call. Note that multiple bus peer tracking objects
+ may track the same name.</para>
+
+ <para><function>sd_bus_track_remove_name()</function> undoes the effect of
+ <function>sd_bus_track_add_name()</function> and removes a bus peer name from the list of peers to watch. Depending
+ on whether non-recursive or recursive mode is enabled for the bus peer tracking object this call will either remove
+ the name fully from the tracking object, or will simply decrement the per-name counter by one, removing the name
+ only when the counter reaches zero (see above). Note that a bus peer disconnecting from the bus will implicitly
+ remove its names fully from the bus peer tracking object, regardless of the current per-name counter.</para>
+
+ <para><function>sd_bus_track_add_sender()</function> and <function>sd_bus_track_remove_sender()</function> are
+ similar to <function>sd_bus_track_add_name()</function> and <function>sd_bus_track_remove_name()</function> but
+ take a bus message as argument. The sender of this bus message is determined and added to/removed from the bus peer
+ tracking object. As messages always originate from unique names, and never from well-known names this means that
+ this call will effectively only add unique names to the bus peer tracking object.</para>
+
+ <para><function>sd_bus_track_count()</function> returns the number of names currently being tracked by the
+ specified bus peer tracking object. Note that this function always returns the actual number of names tracked, and
+ hence if <function>sd_bus_track_add_name()</function> has been invoked multiple times for the same name it is only
+ counted as one, regardless if recursive mode is used or not.</para>
+
+ <para><function>sd_bus_track_count_name()</function> returns the current per-name counter for the specified
+ name. If non-recursive mode is used this returns either 1 or 0, depending on whether the specified name has been
+ added to the tracking object before, or not. If recursive mode has been enabled, values larger than 1 may be
+ returned too, in case <function>sd_bus_track_add_name()</function> has been called multiple times for the same
+ name.</para>
+
+ <para><function>sd_bus_track_count_sender()</function> is similar to
+ <function>sd_bus_track_count_name()</function>, but takes a bus message object and returns the per-name counter
+ matching the sender of the message.</para>
+
+ <para><function>sd_bus_track_contains()</function> may be used to determine whether the specified name has been
+ added at least once to the specified bus peer tracking object.</para>
+
+ <para><function>sd_bus_track_first()</function> and <function>sd_bus_track_next()</function> may be used to
+ enumerate all names currently being tracked by the passed bus peer tracking
+ object. <function>sd_bus_track_first()</function> returns the first entry in the object, and resets an internally
+ maintained read index. Each subsequent invocation of <function>sd_bus_track_next()</function> returns the next name
+ contained in the bus object. If the end is reached <constant>NULL</constant> is returned. If no names have been
+ added to the object yet <function>sd_bus_track_first()</function> will return <constant>NULL</constant>
+ immediately. The order in which names are returned is undefined; in particular which name is considered the first
+ returned is not defined. If recursive mode is enabled and the same name has been added multiple times to the bus
+ peer tracking object it is only returned once by this enumeration. If new names are added to or existing names
+ removed from the bus peer tracking object while it is being enumerated the enumeration ends on the next invocation
+ of <function>sd_bus_track_next()</function> as <constant>NULL</constant> is returned.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+
+ <para>On success, <function>sd_bus_track_add_name()</function> and <function>sd_bus_track_add_sender()</function>
+ return 0 if the specified name has already been added to the bus peer tracking object before and positive if it
+ hasn't. On failure, they return a negative errno-style error code.</para>
+
+ <para><function>sd_bus_track_remove_name()</function> and <function>sd_bus_track_remove_sender()</function> return
+ positive if the specified name was previously tracked by the bus peer tracking object and has now been removed. In
+ non-recursive mode, 0 is returned if the specified name was not being tracked yet. In recursive mode
+ <constant>-EUNATCH</constant> is returned in this case. On failure, they return a negative errno-style error
+ code.</para>
+
+ <para><function>sd_bus_track_count()</function> returns the number of names currently being tracked, or 0 on
+ failure.</para>
+
+ <para><function>sd_bus_track_count_name()</function> and <function>sd_bus_track_count_sender()</function> return
+ the current per-name counter for the specified name or the sender of the specified message. Zero is returned for
+ names that are not being tracked yet, a positive value for names added at least once. Larger values than 1 are only
+ returned in recursive mode. On failure, a negative errno-style error code is returned.</para>
+
+ <para><function>sd_bus_track_contains()</function> returns the passed name if it exists in the bus peer tracking
+ object. On failure, and if the name has not been added yet <constant>NULL</constant> is returned.</para>
+
+ <para><function>sd_bus_track_first()</function> and <function>sd_bus_track_next()</function> return the first/next
+ name contained in the bus peer tracking object, and <constant>NULL</constant> if the end of the enumeration is
+ reached and on error.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Errors</title>
+
+ <para>Returned errors may indicate the following problems:</para>
+
+ <variablelist>
+
+ <varlistentry>
+ <term><constant>-EUNATCH</constant></term>
+
+ <listitem><para><function>sd_bus_track_remove_name()</function> or
+ <function>sd_bus_track_remove_sender()</function> have been invoked for a name not previously added to the bus
+ peer object.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-EINVAL</constant></term>
+
+ <listitem><para>Specified parameter is invalid.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-ENOMEM</constant></term>
+
+ <listitem><para>Memory allocation failed.</para></listitem>
+ </varlistentry>
+
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Notes</title>
+
+ <para><function>sd_bus_track_add_name()</function> and the other calls 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>
+ <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_track_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
diff --git a/man/sd_bus_track_new.xml b/man/sd_bus_track_new.xml
new file mode 100644
index 0000000000..60e2e77f75
--- /dev/null
+++ b/man/sd_bus_track_new.xml
@@ -0,0 +1,263 @@
+<?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">
+
+<!--
+ This file is part of systemd.
+
+ Copyright 2016 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_track_new">
+
+ <refentryinfo>
+ <title>sd_bus_track_new</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_track_new</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>sd_bus_track_new</refname>
+ <refname>sd_bus_track_ref</refname>
+ <refname>sd_bus_track_unref</refname>
+ <refname>sd_bus_track_unrefp</refname>
+ <refname>sd_bus_track_set_recursive</refname>
+ <refname>sd_bus_track_get_recursive</refname>
+ <refname>sd_bus_track_get_bus</refname>
+ <refname>sd_bus_track_get_userdata</refname>
+ <refname>sd_bus_track_set_userdata</refname>
+
+ <refpurpose>Track bus peers</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_track_new</function></funcdef>
+ <paramdef>sd_bus* <parameter>bus</parameter></paramdef>
+ <paramdef>sd_bus_track** <parameter>ret</parameter></paramdef>
+ <paramdef>sd_bus_track_handler_t <parameter>handler</parameter></paramdef>
+ <paramdef>void* <parameter>userdata</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>sd_bus_track *<function>sd_bus_track_ref</function></funcdef>
+ <paramdef>sd_bus_track *<parameter>t</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>sd_bus_track *<function>sd_bus_track_unref</function></funcdef>
+ <paramdef>sd_bus_track *<parameter>t</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>sd_bus_track_unrefp</function></funcdef>
+ <paramdef>sd_bus_track **<parameter>t</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_track_get_recursive</function></funcdef>
+ <paramdef>sd_bus_track *<parameter>t</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_track_set_recursive</function></funcdef>
+ <paramdef>sd_bus_track *<parameter>t</parameter></paramdef>
+ <paramdef>int <parameter>b</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>sd_bus* <function>sd_bus_track_get_bus</function></funcdef>
+ <paramdef>sd_bus_track *<parameter>t</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void* <function>sd_bus_track_get_userdata</function></funcdef>
+ <paramdef>sd_bus_track *<parameter>t</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void* <function>sd_bus_track_set_userdata</function></funcdef>
+ <paramdef>sd_bus_track *<parameter>t</parameter></paramdef>
+ <paramdef>void *userdata</paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><function>sd_bus_track_new()</function> creates a new bus peer tracking object. The object is allocated for
+ the specified bus, and returned in the <parameter>*ret</parameter> parameter. After use, the object should be freed
+ again by dropping the acquired reference with <function>sd_bus_track_unref()</function> (see below). A bus peer
+ tracking object may be used to keep track of peers on a specific IPC bus, for cases where peers are making use of
+ one or more local objects, in order to control the lifecycle of the local objects and ensure they stay around as
+ long as the peers needing them are around, and unreferenced (and possibly destroyed) as soon as all relevant peers
+ have vanished. Each bus peer tracking object may be used to track zero, one or more peers add a time. References to
+ specific bus peers are added via
+ <citerefentry><refentrytitle>sd_bus_track_add_name</refentrytitle><manvolnum>3</manvolnum></citerefentry> or
+ <function>sd_bus_track_add_sender()</function>. They may be dropped again via
+ <function>sd_bus_track_remove_name()</function> and
+ <function>sd_bus_track_remove_sender()</function>. Alternatively, references on peers are removed automatically
+ when they disconnect from the bus. If non-NULL the <parameter>handler</parameter> may specify a function that is
+ invoked whenever the last reference is dropped, regardless whether the reference is dropped explicitly via
+ <function>sd_bus_track_remove_name()</function> or implicitly because the peer disconnected from the bus. The final
+ argument <parameter>userdata</parameter> may be used to attach a generic user data pointer to the object. This
+ pointer is passed to the handler callback when it is invoked.</para>
+
+ <para><function>sd_bus_track_ref()</function> creates a new reference to a bus peer tracking object. This object
+ will not be destroyed until <function>sd_bus_track_unref()</function> has been called as many times plus once
+ more. Once the reference count has dropped to zero, the specified object cannot be used anymore, further calls to
+ <function>sd_bus_track_ref()</function> or <function>sd_bus_track_unref()</function> on the same object are
+ illegal.</para>
+
+ <para><function>sd_bus_track_unref()</function> destroys a reference to a bus peer tracking object.</para>
+
+ <para><function>sd_bus_track_unrefp()</function> is similar to <function>sd_bus_track_unref()</function> but takes
+ a pointer to a pointer to an <type>sd_bus_track</type> object. This call is useful in conjunction with GCC's and
+ LLVM's <ulink url="https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html">Clean-up Variable
+ Attribute</ulink>. Note that this function is defined as inline function.</para>
+
+ <para><function>sd_bus_track_ref()</function>, <function>sd_bus_track_unref()</function> and
+ <function>sd_bus_track_unrefp()</function> execute no operation if the passed in bus peer tracking object is
+ <constant>NULL</constant>.</para>
+
+ <para>Bus peer tracking objects may exist in two modes: by default they operate in non-recursive mode, but may
+ optionally be switched into recursive mode. If operating in the default non-recursive mode a peer is either tracked
+ or not tracked. In this mode invoking <function>sd_bus_track_add_name()</function> multiple times in a row for the
+ same peer is fully equivalent to calling it just once, as the call adds the peer to the set of tracked peers if
+ necessary, and executes no operation if the peer is already being tracked. A single invocation of
+ <function>sd_bus_track_remove_name()</function> removes the reference on the peer again, regardless how many times
+ <function>sd_bus_track_add_name()</function> was called before. If operating in recursive mode, the number of times
+ <function>sd_bus_track_add_name()</function> is invoked for the same peer name is counted and
+ <function>sd_bus_track_remove_name()</function> must be called the same number of times before the peer is not
+ tracked anymore, with the exception when the tracked peer vanishes from the bus, in which case the count is
+ irrelevant and the tracking of the specific peer is immediately
+ removed. <function>sd_bus_track_get_recursive()</function> may be used to determine whether the bus peer tracking
+ object is operating in recursive mode. <function>sd_bus_track_set_recursive()</function> may be used to enable or
+ disable recursive mode. By default a bus peer tracking object operates in non-recursive mode, and
+ <function>sd_bus_track_get_recursive()</function> for a newly allocated object hence returns a value equal to
+ zero. Use <function>sd_bus_track_set_recursive()</function> to enable recursive mode, right after allocation. It
+ takes a boolean argument to enable or disable recursive mode. Note that tracking objects for which
+ <function>sd_bus_track_add_name()</function> was already invoked at least once (and which hence track already one
+ or more peers) may not be switched from recursive to non-recursive mode anymore.</para>
+
+ <para><function>sd_bus_track_get_bus()</function> returns the bus object the bus peer tracking object belongs
+ to. It returns the bus object initially passed to <function>sd_bus_track_new()</function> when the object was
+ allocated.</para>
+
+ <para><function>sd_bus_track_get_userdata()</function> returns the generic user data pointer set on the bus peer
+ tracking object at the time of creation using <function>sd_bus_track_new()</function> or at a later time, using
+ <function>sd_bus_track_set_userdata()</function>.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+
+ <para>On success, <function>sd_bus_track_new()</function> and <function>sd_bus_track_set_recursive()</function>
+ return 0 or a positive integer. On failure, they return a negative errno-style error code.</para>
+
+ <para><function>sd_bus_track_ref()</function> always returns the argument.</para>
+
+ <para><function>sd_bus_track_unref()</function> always returns <constant>NULL</constant>.</para>
+
+ <para><function>sd_bus_track_get_recursive()</function> returns 0 if non-recursive mode is selected (default), and
+ greater than 0 if recursive mode is selected. On failure a negative errno-style error code is returned.</para>
+
+ <para><function>sd_bus_track_get_bus()</function> returns the bus object associated to the bus peer tracking
+ object.</para>
+
+ <para><function>sd_bus_track_get_userdata()</function> returns the generic user data pointer associated with the
+ bus peer tracking object. <function>sd_bus_track_set_userdata()</function> returns the previous user data pointer
+ set.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Reference ownership</title>
+
+ <para>The <function>sd_bus_track_new()</function> function creates a new object and the caller owns the sole
+ reference. When not needed anymore, this reference should be destroyed with
+ <function>sd_bus_track_unref()</function>.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Errors</title>
+
+ <para>Returned errors may indicate the following problems:</para>
+
+ <variablelist>
+
+ <varlistentry>
+ <term><constant>-EBUSY</constant></term>
+
+ <listitem><para>Bus peers have already been added to the bus peer tracking object and
+ <function>sd_bus_track_set_recursive()</function> was called to change tracking mode.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-EINVAL</constant></term>
+
+ <listitem><para>Specified parameter is invalid
+ (<constant>NULL</constant> in case of output
+ parameters).</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-ENOMEM</constant></term>
+
+ <listitem><para>Memory allocation failed.</para></listitem>
+ </varlistentry>
+
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Notes</title>
+
+ <para><function>sd_bus_track_new()</function> and the other calls 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>
+ <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_track_add_name</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
diff --git a/man/systemctl.xml b/man/systemctl.xml
index fde4f4f3bb..7e0ac9613a 100644
--- a/man/systemctl.xml
+++ b/man/systemctl.xml
@@ -642,13 +642,13 @@
<term><command>list-units <optional><replaceable>PATTERN</replaceable>...</optional></command></term>
<listitem>
- <para>List units that <command>systemd</command> has loaded. This includes units that
- are either referenced directly or through a dependency, or units that were active in the
- past and have failed. By default only units which are active, have pending jobs, or have
+ <para>List units that <command>systemd</command> has loaded. This includes units that are either referenced
+ directly or through a dependency, units that are pinned by applications programmatically, or units that
+ were active in the past and have failed. By default only units which are active, have pending jobs, or have
failed are shown; this can be changed with option <option>--all</option>. If one or more
- <replaceable>PATTERN</replaceable>s are specified, only units matching one of them are
- shown. The units that are shown are additionally filtered by <option>--type=</option>
- and <option>--state=</option> if those options are specified.</para>
+ <replaceable>PATTERN</replaceable>s are specified, only units matching one of them are shown. The units
+ that are shown are additionally filtered by <option>--type=</option> and <option>--state=</option> if those
+ options are specified.</para>
<para>This is the default command.</para>
</listitem>
diff --git a/man/systemd-run.xml b/man/systemd-run.xml
index 56d585cdf9..15f9119e54 100644
--- a/man/systemd-run.xml
+++ b/man/systemd-run.xml
@@ -68,42 +68,30 @@
<refsect1>
<title>Description</title>
- <para><command>systemd-run</command> may be used to create and
- start a transient <filename>.service</filename> or
- <filename>.scope</filename> unit and run the specified
- <replaceable>COMMAND</replaceable> in it. It may also be used to
- create and start transient <filename>.timer</filename>
- units.</para>
-
- <para>If a command is run as transient service unit, it will be
- started and managed by the service manager like any other service,
- and thus shows up in the output of <command>systemctl
- list-units</command> like any other unit. It will run in a clean
- and detached execution environment, with the service manager as
- its parent process. In this mode, <command>systemd-run</command>
- will start the service asynchronously in the background and return
- after the command has begun execution.</para>
-
- <para>If a command is run as transient scope unit, it will be
- started by <command>systemd-run</command> itself as parent process
- and will thus inherit the execution environment of the
- caller. However, the processes of the command are managed by the
- service manager similar to normal services, and will show up in
- the output of <command>systemctl list-units</command>. Execution
- in this case is synchronous, and will return only when the command
- finishes. This mode is enabled via the <option>--scope</option>
- switch (see below). </para>
-
- <para>If a command is run with timer options such as
- <option>--on-calendar=</option> (see below), a transient timer
- unit is created alongside the service unit for the specified
- command. Only the transient timer unit is started immediately, the
- transient service unit will be started when the transient timer
- elapses. If the <option>--unit=</option> is specified, the
- <replaceable>COMMAND</replaceable> may be omitted. In this case,
- <command>systemd-run</command> only creates a
- <filename>.timer</filename> unit that invokes the specified unit
- when elapsing.</para>
+ <para><command>systemd-run</command> may be used to create and start a transient <filename>.service</filename> or
+ <filename>.scope</filename> unit and run the specified <replaceable>COMMAND</replaceable> in it. It may also be
+ used to create and start a transient <filename>.timer</filename> unit, that activates a
+ <filename>.service</filename> unit when elapsing.</para>
+
+ <para>If a command is run as transient service unit, it will be started and managed by the service manager like any
+ other service, and thus shows up in the output of <command>systemctl list-units</command> like any other unit. It
+ will run in a clean and detached execution environment, with the service manager as its parent process. In this
+ mode, <command>systemd-run</command> will start the service asynchronously in the background and return after the
+ command has begun execution (unless <option>--no-block</option> or <option>--watch</option> are specified, see
+ below).</para>
+
+ <para>If a command is run as transient scope unit, it will be executed by <command>systemd-run</command> itself as
+ parent process and will thus inherit the execution environment of the caller. However, the processes of the command
+ are managed by the service manager similar to normal services, and will show up in the output of <command>systemctl
+ list-units</command>. Execution in this case is synchronous, and will return only when the command finishes. This
+ mode is enabled via the <option>--scope</option> switch (see below). </para>
+
+ <para>If a command is run with timer options such as <option>--on-calendar=</option> (see below), a transient timer
+ unit is created alongside the service unit for the specified command. Only the transient timer unit is started
+ immediately, the transient service unit will be started when the timer elapses. If the <option>--unit=</option>
+ option is specified, the <replaceable>COMMAND</replaceable> may be omitted. In this case,
+ <command>systemd-run</command> creates only a <filename>.timer</filename> unit that invokes the specified unit when
+ elapsing.</para>
</refsect1>
<refsect1>
@@ -123,8 +111,8 @@
<term><option>--scope</option></term>
<listitem>
- <para>Create a transient <filename>.scope</filename> unit instead of
- the default transient <filename>.service</filename> unit.
+ <para>Create a transient <filename>.scope</filename> unit instead of the default transient
+ <filename>.service</filename> unit (see above).
</para>
</listitem>
</varlistentry>
@@ -140,9 +128,8 @@
<term><option>--property=</option></term>
<term><option>-p</option></term>
- <listitem><para>Sets a unit property for the scope or service
- unit that is created. This takes an assignment in the same
- format as
+ <listitem><para>Sets a property on the scope or service unit that is created. This option takes an assignment
+ in the same format as
<citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
<command>set-property</command> command.</para>
</listitem>
@@ -151,9 +138,8 @@
<varlistentry>
<term><option>--description=</option></term>
- <listitem><para>Provide a description for the service or scope
- unit. If not specified, the command itself will be used as a
- description. See <varname>Description=</varname> in
+ <listitem><para>Provide a description for the service, scope or timer unit. If not specified, the command
+ itself will be used as a description. See <varname>Description=</varname> in
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
</para></listitem>
</varlistentry>
@@ -161,19 +147,16 @@
<varlistentry>
<term><option>--slice=</option></term>
- <listitem><para>Make the new <filename>.service</filename> or
- <filename>.scope</filename> unit part of the specified slice,
- instead of the <filename>system.slice</filename>.</para>
+ <listitem><para>Make the new <filename>.service</filename> or <filename>.scope</filename> unit part of the
+ specified slice, instead of <filename>system.slice</filename>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--remain-after-exit</option></term>
- <listitem><para>After the service or scope process has
- terminated, keep the service around until it is explicitly
- stopped. This is useful to collect runtime information about
- the service after it finished running. Also see
+ <listitem><para>After the service process has terminated, keep the service around until it is explicitly
+ stopped. This is useful to collect runtime information about the service after it finished running. Also see
<varname>RemainAfterExit=</varname> in
<citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
</para>
@@ -183,10 +166,8 @@
<varlistentry>
<term><option>--send-sighup</option></term>
- <listitem><para>When terminating the scope or service unit,
- send a SIGHUP immediately after SIGTERM. This is useful to
- indicate to shells and shell-like processes that the
- connection has been severed. Also see
+ <listitem><para>When terminating the scope or service unit, send a SIGHUP immediately after SIGTERM. This is
+ useful to indicate to shells and shell-like processes that the connection has been severed. Also see
<varname>SendSIGHUP=</varname> in
<citerefentry><refentrytitle>systemd.kill</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
</para>
@@ -209,9 +190,8 @@
<term><option>--uid=</option></term>
<term><option>--gid=</option></term>
- <listitem><para>Runs the service process under the UNIX user
- and group. Also see <varname>User=</varname> and
- <varname>Group=</varname> in
+ <listitem><para>Runs the service process under the specified UNIX user and group. Also see
+ <varname>User=</varname> and <varname>Group=</varname> in
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
</listitem>
</varlistentry>
@@ -239,11 +219,9 @@
<term><option>--pty</option></term>
<term><option>-t</option></term>
- <listitem><para>When invoking a command, the service connects
- its standard input and output to the invoking tty via a
- pseudo TTY device. This allows invoking binaries as services
- that expect interactive user input, such as interactive
- command shells.</para></listitem>
+ <listitem><para>When invoking the command, the transient service connects its standard input and output to the
+ terminal <command>systemd-run</command> is invoked on, via a pseudo TTY device. This allows running binaries
+ that expect interactive user input as services, such as interactive command shells.</para></listitem>
</varlistentry>
<varlistentry>
@@ -263,44 +241,32 @@
<term><option>--on-unit-active=</option></term>
<term><option>--on-unit-inactive=</option></term>
- <listitem><para>Defines monotonic timers relative to different
- starting points. Also see <varname>OnActiveSec=</varname>,
- <varname>OnBootSec=</varname>,
- <varname>OnStartupSec=</varname>,
- <varname>OnUnitActiveSec=</varname> and
- <varname>OnUnitInactiveSec=</varname> in
- <citerefentry><refentrytitle>systemd.timer</refentrytitle><manvolnum>5</manvolnum></citerefentry>. This
- options have no effect in conjunction with
- <option>--scope</option>.</para>
+ <listitem><para>Defines a monotonic timer relative to different starting points for starting the specified
+ command. See <varname>OnActiveSec=</varname>, <varname>OnBootSec=</varname>, <varname>OnStartupSec=</varname>,
+ <varname>OnUnitActiveSec=</varname> and <varname>OnUnitInactiveSec=</varname> in
+ <citerefentry><refentrytitle>systemd.timer</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
+ details. These options may not be combined with <option>--scope</option>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--on-calendar=</option></term>
- <listitem><para>Defines realtime (i.e. wallclock) timers with
- calendar event expressions. Also see
- <varname>OnCalendar=</varname> in
- <citerefentry><refentrytitle>systemd.timer</refentrytitle><manvolnum>5</manvolnum></citerefentry>. This
- option has no effect in conjunction with
- <option>--scope</option>.</para>
+ <listitem><para>Defines a calendar timer for starting the specified command. See <varname>OnCalendar=</varname>
+ in <citerefentry><refentrytitle>systemd.timer</refentrytitle><manvolnum>5</manvolnum></citerefentry>. This
+ option may not be combined with <option>--scope</option>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--timer-property=</option></term>
- <listitem><para>Sets a timer unit property for the timer unit
- that is created. It is similar with
- <option>--property</option> but only for created timer
- unit. This option only has effect in conjunction with
- <option>--on-active=</option>, <option>--on-boot=</option>,
- <option>--on-startup=</option>,
- <option>--on-unit-active=</option>,
- <option>--on-unit-inactive=</option>,
- <option>--on-calendar=</option>. This takes an assignment in
- the same format as
- <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
+ <listitem><para>Sets a property on the timer unit that is created. This option is similar to
+ <option>--property=</option> but applies to the transient timer unit rather than the transient service unit
+ created. This option only has an effect in conjunction with <option>--on-active=</option>,
+ <option>--on-boot=</option>, <option>--on-startup=</option>, <option>--on-unit-active=</option>,
+ <option>--on-unit-inactive=</option> or <option>--on-calendar=</option>. This option takes an assignment in the
+ same format as <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
<command>set-property</command> command.</para> </listitem>
</varlistentry>
@@ -308,14 +274,25 @@
<term><option>--no-block</option></term>
<listitem>
- <para>Do not synchronously wait for the requested operation
- to finish. If this is not specified, the job will be
- verified, enqueued and <command>systemd-run</command> will
- wait until the unit's start-up is completed. By passing this
- argument, it is only verified and enqueued.</para>
+ <para>Do not synchronously wait for the unit start operation to finish. If this option is not specified, the
+ start request for the transient unit will be verified, enqueued and <command>systemd-run</command> will wait
+ until the unit's start-up is completed. By passing this argument, it is only verified and enqueued. This
+ option may not be combined with <option>--wait</option>.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--wait</option></term>
+
+ <listitem><para>Synchronously wait for the transient service to terminate. If this option is specified, the
+ start request for the transient unit is verified, enqueued, and waited for. Subsequently the invoked unit is
+ monitored, and it is waited until it is deactivated again (most likely because the specified command
+ completed). On exit, terse information about the unit's runtime is shown, including total runtime (as well as
+ CPU usage, if <option>--property=CPUAccounting=1</option> was set) and the exit code and status of the main
+ process. This output may be suppressed with <option>--quiet</option>. This option may not be combined with
+ <option>--no-block</option>, <option>--scope</option> or the various timer options.</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/src/core/cgroup.c b/src/core/cgroup.c
index 086cd7a789..20f9f9fc7e 100644
--- a/src/core/cgroup.c
+++ b/src/core/cgroup.c
@@ -1524,6 +1524,8 @@ void unit_prune_cgroup(Unit *u) {
if (!u->cgroup_path)
return;
+ (void) unit_get_cpu_usage(u, NULL); /* Cache the last CPU usage value before we destroy the cgroup */
+
is_root_slice = unit_has_name(u, SPECIAL_ROOT_SLICE);
r = cg_trim_everywhere(u->manager->cgroup_supported, u->cgroup_path, !is_root_slice);
@@ -2044,7 +2046,21 @@ int unit_get_cpu_usage(Unit *u, nsec_t *ret) {
nsec_t ns;
int r;
+ assert(u);
+
+ /* Retrieve the current CPU usage counter. This will subtract the CPU counter taken when the unit was
+ * started. If the cgroup has been removed already, returns the last cached value. To cache the value, simply
+ * call this function with a NULL return value. */
+
r = unit_get_cpu_usage_raw(u, &ns);
+ if (r == -ENODATA && u->cpu_usage_last != NSEC_INFINITY) {
+ /* If we can't get the CPU usage anymore (because the cgroup was already removed, for example), use our
+ * cached value. */
+
+ if (ret)
+ *ret = u->cpu_usage_last;
+ return 0;
+ }
if (r < 0)
return r;
@@ -2053,7 +2069,10 @@ int unit_get_cpu_usage(Unit *u, nsec_t *ret) {
else
ns = 0;
- *ret = ns;
+ u->cpu_usage_last = ns;
+ if (ret)
+ *ret = ns;
+
return 0;
}
@@ -2063,6 +2082,8 @@ int unit_reset_cpu_usage(Unit *u) {
assert(u);
+ u->cpu_usage_last = NSEC_INFINITY;
+
r = unit_get_cpu_usage_raw(u, &ns);
if (r < 0) {
u->cpu_usage_base = 0;
diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c
index ef05a75a8b..ea7ced2fd0 100644
--- a/src/core/dbus-manager.c
+++ b/src/core/dbus-manager.c
@@ -643,6 +643,54 @@ static int method_set_unit_properties(sd_bus_message *message, void *userdata, s
return bus_unit_method_set_properties(message, u, error);
}
+static int method_ref_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *m = userdata;
+ const char *name;
+ Unit *u;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_read(message, "s", &name);
+ if (r < 0)
+ return r;
+
+ r = manager_load_unit(m, name, NULL, error, &u);
+ if (r < 0)
+ return r;
+
+ r = bus_unit_check_load_state(u, error);
+ if (r < 0)
+ return r;
+
+ return bus_unit_method_ref(message, u, error);
+}
+
+static int method_unref_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *m = userdata;
+ const char *name;
+ Unit *u;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_read(message, "s", &name);
+ if (r < 0)
+ return r;
+
+ r = manager_load_unit(m, name, NULL, error, &u);
+ if (r < 0)
+ return r;
+
+ r = bus_unit_check_load_state(u, error);
+ if (r < 0)
+ return r;
+
+ return bus_unit_method_unref(message, u, error);
+}
+
static int reply_unit_info(sd_bus_message *reply, Unit *u) {
_cleanup_free_ char *unit_path = NULL, *job_path = NULL;
Unit *following;
@@ -781,6 +829,13 @@ static int transient_unit_from_message(
if (r < 0)
return r;
+ /* If the client asked for it, automatically add a reference to this unit. */
+ if (u->bus_track_add) {
+ r = bus_unit_track_add_sender(u, message);
+ if (r < 0)
+ return log_error_errno(r, "Failed to watch sender: %m");
+ }
+
/* Now load the missing bits of the unit we just created */
unit_add_to_load_queue(u);
manager_dispatch_load_queue(m);
@@ -2211,6 +2266,8 @@ const sd_bus_vtable bus_manager_vtable[] = {
SD_BUS_METHOD("KillUnit", "ssi", NULL, method_kill_unit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ResetFailedUnit", "s", NULL, method_reset_failed_unit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetUnitProperties", "sba(sv)", NULL, method_set_unit_properties, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("RefUnit", "s", NULL, method_ref_unit, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("UnrefUnit", "s", NULL, method_unref_unit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("StartTransientUnit", "ssa(sv)a(sa(sv))", "o", method_start_transient_unit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetUnitProcesses", "s", "a(sus)", method_get_unit_processes, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetJob", "u", "o", method_get_job, SD_BUS_VTABLE_UNPRIVILEGED),
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c
index 89e56a2e51..1b86bdde43 100644
--- a/src/core/dbus-unit.c
+++ b/src/core/dbus-unit.c
@@ -418,6 +418,7 @@ static int bus_verify_manage_units_async_full(
const char *verb,
int capability,
const char *polkit_message,
+ bool interactive,
sd_bus_message *call,
sd_bus_error *error) {
@@ -433,7 +434,15 @@ static int bus_verify_manage_units_async_full(
details[7] = GETTEXT_PACKAGE;
}
- return bus_verify_polkit_async(call, capability, "org.freedesktop.systemd1.manage-units", details, false, UID_INVALID, &u->manager->polkit_registry, error);
+ return bus_verify_polkit_async(
+ call,
+ capability,
+ "org.freedesktop.systemd1.manage-units",
+ details,
+ interactive,
+ UID_INVALID,
+ &u->manager->polkit_registry,
+ error);
}
int bus_unit_method_start_generic(
@@ -486,6 +495,7 @@ int bus_unit_method_start_generic(
verb,
CAP_SYS_ADMIN,
job_type < _JOB_TYPE_MAX ? polkit_message_for_job[job_type] : NULL,
+ true,
message,
error);
if (r < 0)
@@ -558,6 +568,7 @@ int bus_unit_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *
"kill",
CAP_KILL,
N_("Authentication is required to kill '$(unit)'."),
+ true,
message,
error);
if (r < 0)
@@ -588,6 +599,7 @@ int bus_unit_method_reset_failed(sd_bus_message *message, void *userdata, sd_bus
"reset-failed",
CAP_SYS_ADMIN,
N_("Authentication is required to reset the \"failed\" state of '$(unit)'."),
+ true,
message,
error);
if (r < 0)
@@ -620,6 +632,7 @@ int bus_unit_method_set_properties(sd_bus_message *message, void *userdata, sd_b
"set-property",
CAP_SYS_ADMIN,
N_("Authentication is required to set properties on '$(unit)'."),
+ true,
message,
error);
if (r < 0)
@@ -634,6 +647,53 @@ int bus_unit_method_set_properties(sd_bus_message *message, void *userdata, sd_b
return sd_bus_reply_method_return(message, NULL);
}
+int bus_unit_method_ref(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Unit *u = userdata;
+ int r;
+
+ assert(message);
+ assert(u);
+
+ r = mac_selinux_unit_access_check(u, message, "start", error);
+ if (r < 0)
+ return r;
+
+ r = bus_verify_manage_units_async_full(
+ u,
+ "ref",
+ CAP_SYS_ADMIN,
+ NULL,
+ false,
+ message,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
+
+ r = bus_unit_track_add_sender(u, message);
+ if (r < 0)
+ return r;
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
+int bus_unit_method_unref(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Unit *u = userdata;
+ int r;
+
+ assert(message);
+ assert(u);
+
+ r = bus_unit_track_remove_sender(u, message);
+ if (r == -EUNATCH)
+ return sd_bus_error_setf(error, BUS_ERROR_NOT_REFERENCED, "Unit has not been referenced yet.");
+ if (r < 0)
+ return r;
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
const sd_bus_vtable bus_unit_vtable[] = {
SD_BUS_VTABLE_START(0),
@@ -715,6 +775,8 @@ const sd_bus_vtable bus_unit_vtable[] = {
SD_BUS_METHOD("Kill", "si", NULL, bus_unit_method_kill, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ResetFailed", NULL, NULL, bus_unit_method_reset_failed, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetProperties", "ba(sv)", NULL, bus_unit_method_set_properties, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("Ref", NULL, NULL, bus_unit_method_ref, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("Unref", NULL, NULL, bus_unit_method_unref, SD_BUS_VTABLE_UNPRIVILEGED),
/* Obsolete properties or obsolete alias names */
SD_BUS_PROPERTY("RequiresOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN),
@@ -1318,6 +1380,29 @@ static int bus_unit_set_transient_property(
return r;
return 1;
+
+ } else if (streq(name, "AddRef")) {
+
+ int b;
+
+ /* Why is this called "AddRef" rather than just "Ref", or "Reference"? There's already a "Ref()" method
+ * on the Unit interface, and it's probably not a good idea to expose a property and a method on the
+ * same interface (well, strictly speaking AddRef isn't exposed as full property, we just read it for
+ * transient units, but still). And "References" and "ReferencedBy" is already used as unit reference
+ * dependency type, hence let's not confuse things with that.
+ *
+ * Note that we don't acually add the reference to the bus track. We do that only after the setup of
+ * the transient unit is complete, so that setting this property multiple times in the same transient
+ * unit creation call doesn't count as individual references. */
+
+ r = sd_bus_message_read(message, "b", &b);
+ if (r < 0)
+ return r;
+
+ if (mode != UNIT_CHECK)
+ u->bus_track_add = b;
+
+ return 1;
}
return 0;
@@ -1422,3 +1507,71 @@ int bus_unit_check_load_state(Unit *u, sd_bus_error *error) {
return sd_bus_error_set_errnof(error, u->load_error, "Unit %s is not loaded properly: %m.", u->id);
}
+
+static int bus_track_handler(sd_bus_track *t, void *userdata) {
+ Unit *u = userdata;
+
+ assert(t);
+ assert(u);
+
+ u->bus_track = sd_bus_track_unref(u->bus_track); /* make sure we aren't called again */
+
+ unit_add_to_gc_queue(u);
+ return 0;
+}
+
+static int allocate_bus_track(Unit *u) {
+ int r;
+
+ assert(u);
+
+ if (u->bus_track)
+ return 0;
+
+ r = sd_bus_track_new(u->manager->api_bus, &u->bus_track, bus_track_handler, u);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_track_set_recursive(u->bus_track, true);
+ if (r < 0) {
+ u->bus_track = sd_bus_track_unref(u->bus_track);
+ return r;
+ }
+
+ return 0;
+}
+
+int bus_unit_track_add_name(Unit *u, const char *name) {
+ int r;
+
+ assert(u);
+
+ r = allocate_bus_track(u);
+ if (r < 0)
+ return r;
+
+ return sd_bus_track_add_name(u->bus_track, name);
+}
+
+int bus_unit_track_add_sender(Unit *u, sd_bus_message *m) {
+ int r;
+
+ assert(u);
+
+ r = allocate_bus_track(u);
+ if (r < 0)
+ return r;
+
+ return sd_bus_track_add_sender(u->bus_track, m);
+}
+
+int bus_unit_track_remove_sender(Unit *u, sd_bus_message *m) {
+ assert(u);
+
+ /* If we haven't allocated the bus track object yet, then there's definitely no reference taken yet, return an
+ * error */
+ if (!u->bus_track)
+ return -EUNATCH;
+
+ return sd_bus_track_remove_sender(u->bus_track, m);
+}
diff --git a/src/core/dbus-unit.h b/src/core/dbus-unit.h
index 4db88dbebc..b280de7a1d 100644
--- a/src/core/dbus-unit.h
+++ b/src/core/dbus-unit.h
@@ -33,9 +33,15 @@ int bus_unit_method_start_generic(sd_bus_message *message, Unit *u, JobType job_
int bus_unit_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_unit_method_reset_failed(sd_bus_message *message, void *userdata, sd_bus_error *error);
-int bus_unit_queue_job(sd_bus_message *message, Unit *u, JobType type, JobMode mode, bool reload_if_possible, sd_bus_error *error);
int bus_unit_set_properties(Unit *u, sd_bus_message *message, UnitSetPropertiesMode mode, bool commit, sd_bus_error *error);
int bus_unit_method_set_properties(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_unit_method_get_processes(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_unit_method_ref(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_unit_method_unref(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_unit_queue_job(sd_bus_message *message, Unit *u, JobType type, JobMode mode, bool reload_if_possible, sd_bus_error *error);
int bus_unit_check_load_state(Unit *u, sd_bus_error *error);
+
+int bus_unit_track_add_name(Unit *u, const char *name);
+int bus_unit_track_add_sender(Unit *u, sd_bus_message *m);
+int bus_unit_track_remove_sender(Unit *u, sd_bus_message *m);
diff --git a/src/core/dbus.c b/src/core/dbus.c
index 3422a02d68..1e41a42aa6 100644
--- a/src/core/dbus.c
+++ b/src/core/dbus.c
@@ -1168,60 +1168,57 @@ int bus_foreach_bus(
return ret;
}
-void bus_track_serialize(sd_bus_track *t, FILE *f) {
+void bus_track_serialize(sd_bus_track *t, FILE *f, const char *prefix) {
const char *n;
assert(f);
+ assert(prefix);
- for (n = sd_bus_track_first(t); n; n = sd_bus_track_next(t))
- fprintf(f, "subscribed=%s\n", n);
-}
-
-int bus_track_deserialize_item(char ***l, const char *line) {
- const char *e;
- int r;
-
- assert(l);
- assert(line);
-
- e = startswith(line, "subscribed=");
- if (!e)
- return 0;
+ for (n = sd_bus_track_first(t); n; n = sd_bus_track_next(t)) {
+ int c, j;
- r = strv_extend(l, e);
- if (r < 0)
- return r;
+ c = sd_bus_track_count_name(t, n);
- return 1;
+ for (j = 0; j < c; j++) {
+ fputs(prefix, f);
+ fputc('=', f);
+ fputs(n, f);
+ fputc('\n', f);
+ }
+ }
}
-int bus_track_coldplug(Manager *m, sd_bus_track **t, char ***l) {
+int bus_track_coldplug(Manager *m, sd_bus_track **t, bool recursive, char **l) {
+ char **i;
int r = 0;
assert(m);
assert(t);
- assert(l);
-
- if (!strv_isempty(*l) && m->api_bus) {
- char **i;
- if (!*t) {
- r = sd_bus_track_new(m->api_bus, t, NULL, NULL);
- if (r < 0)
- return r;
- }
+ if (strv_isempty(l))
+ return 0;
- r = 0;
- STRV_FOREACH(i, *l) {
- int k;
+ if (!m->api_bus)
+ return 0;
- k = sd_bus_track_add_name(*t, *i);
- if (k < 0)
- r = k;
- }
+ if (!*t) {
+ r = sd_bus_track_new(m->api_bus, t, NULL, NULL);
+ if (r < 0)
+ return r;
}
- *l = strv_free(*l);
+ r = sd_bus_track_set_recursive(*t, recursive);
+ if (r < 0)
+ return r;
+
+ r = 0;
+ STRV_FOREACH(i, l) {
+ int k;
+
+ k = sd_bus_track_add_name(*t, *i);
+ if (k < 0)
+ r = k;
+ }
return r;
}
diff --git a/src/core/dbus.h b/src/core/dbus.h
index 6baaffbd75..a092ed9d76 100644
--- a/src/core/dbus.h
+++ b/src/core/dbus.h
@@ -28,9 +28,8 @@ void bus_done(Manager *m);
int bus_fdset_add_all(Manager *m, FDSet *fds);
-void bus_track_serialize(sd_bus_track *t, FILE *f);
-int bus_track_deserialize_item(char ***l, const char *line);
-int bus_track_coldplug(Manager *m, sd_bus_track **t, char ***l);
+void bus_track_serialize(sd_bus_track *t, FILE *f, const char *prefix);
+int bus_track_coldplug(Manager *m, sd_bus_track **t, bool recursive, char **l);
int manager_sync_bus_names(Manager *m, sd_bus *bus);
diff --git a/src/core/job.c b/src/core/job.c
index 7557874d4d..7faf2ef686 100644
--- a/src/core/job.c
+++ b/src/core/job.c
@@ -997,7 +997,10 @@ char *job_dbus_path(Job *j) {
return p;
}
-int job_serialize(Job *j, FILE *f, FDSet *fds) {
+int job_serialize(Job *j, FILE *f) {
+ assert(j);
+ assert(f);
+
fprintf(f, "job-id=%u\n", j->id);
fprintf(f, "job-type=%s\n", job_type_to_string(j->type));
fprintf(f, "job-state=%s\n", job_state_to_string(j->state));
@@ -1008,15 +1011,16 @@ int job_serialize(Job *j, FILE *f, FDSet *fds) {
if (j->begin_usec > 0)
fprintf(f, "job-begin="USEC_FMT"\n", j->begin_usec);
- bus_track_serialize(j->clients, f);
+ bus_track_serialize(j->clients, f, "subscribed");
/* End marker */
fputc('\n', f);
return 0;
}
-int job_deserialize(Job *j, FILE *f, FDSet *fds) {
+int job_deserialize(Job *j, FILE *f) {
assert(j);
+ assert(f);
for (;;) {
char line[LINE_MAX], *l, *v;
@@ -1106,7 +1110,7 @@ int job_deserialize(Job *j, FILE *f, FDSet *fds) {
} else if (streq(l, "subscribed")) {
if (strv_extend(&j->deserialized_clients, v) < 0)
- return log_oom();
+ log_oom();
}
}
}
@@ -1118,9 +1122,8 @@ int job_coldplug(Job *j) {
/* After deserialization is complete and the bus connection
* set up again, let's start watching our subscribers again */
- r = bus_track_coldplug(j->manager, &j->clients, &j->deserialized_clients);
- if (r < 0)
- return r;
+ (void) bus_track_coldplug(j->manager, &j->clients, false, j->deserialized_clients);
+ j->deserialized_clients = strv_free(j->deserialized_clients);
if (j->state == JOB_WAITING)
job_add_to_run_queue(j);
diff --git a/src/core/job.h b/src/core/job.h
index d359e8bb3e..85368f0d30 100644
--- a/src/core/job.h
+++ b/src/core/job.h
@@ -177,8 +177,8 @@ Job* job_install(Job *j);
int job_install_deserialized(Job *j);
void job_uninstall(Job *j);
void job_dump(Job *j, FILE*f, const char *prefix);
-int job_serialize(Job *j, FILE *f, FDSet *fds);
-int job_deserialize(Job *j, FILE *f, FDSet *fds);
+int job_serialize(Job *j, FILE *f);
+int job_deserialize(Job *j, FILE *f);
int job_coldplug(Job *j);
JobDependency* job_dependency_new(Job *subject, Job *object, bool matters, bool conflicts);
diff --git a/src/core/manager.c b/src/core/manager.c
index 7576d038a2..b58f68fa7a 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -1287,10 +1287,11 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
if (q < 0 && r == 0)
r = q;
- /* We might have deserialized the kdbus control fd, but if we
- * didn't, then let's create the bus now. */
- manager_connect_bus(m, !!serialization);
- bus_track_coldplug(m, &m->subscribed, &m->deserialized_subscribed);
+ /* We might have deserialized the kdbus control fd, but if we didn't, then let's create the bus now. */
+ (void) manager_connect_bus(m, !!serialization);
+
+ (void) bus_track_coldplug(m, &m->subscribed, false, m->deserialized_subscribed);
+ m->deserialized_subscribed = strv_free(m->deserialized_subscribed);
/* Third, fire things up! */
manager_coldplug(m);
@@ -2490,7 +2491,7 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) {
fprintf(f, "kdbus-fd=%i\n", copy);
}
- bus_track_serialize(m->subscribed, f);
+ bus_track_serialize(m->subscribed, f, "subscribed");
r = dynamic_user_serialize(m, f, fds);
if (r < 0)
@@ -2693,15 +2694,13 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
manager_deserialize_uid_refs_one(m, l + 16);
else if (startswith(l, "destroy-ipc-gid="))
manager_deserialize_gid_refs_one(m, l + 16);
- else {
- int k;
-
- k = bus_track_deserialize_item(&m->deserialized_subscribed, l);
- if (k < 0)
- log_debug_errno(k, "Failed to deserialize bus tracker object: %m");
- else if (k == 0)
- log_debug("Unknown serialization item '%s'", l);
- }
+ else if (startswith(l, "subscribed=")) {
+
+ if (strv_extend(&m->deserialized_subscribed, l+11) < 0)
+ log_oom();
+
+ } else
+ log_debug("Unknown serialization item '%s'", l);
}
for (;;) {
diff --git a/src/core/org.freedesktop.systemd1.conf b/src/core/org.freedesktop.systemd1.conf
index 14f6aec029..647e5f736c 100644
--- a/src/core/org.freedesktop.systemd1.conf
+++ b/src/core/org.freedesktop.systemd1.conf
@@ -184,6 +184,14 @@
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Manager"
+ send_member="RefUnit"/>
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Manager"
+ send_member="UnrefUnit"/>
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Manager"
send_member="EnableUnitFiles"/>
<allow send_destination="org.freedesktop.systemd1"
diff --git a/src/core/unit.c b/src/core/unit.c
index c58c501bc3..de22f657c6 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -102,6 +102,7 @@ Unit *unit_new(Manager *m, size_t size) {
u->job_timeout = USEC_INFINITY;
u->ref_uid = UID_INVALID;
u->ref_gid = GID_INVALID;
+ u->cpu_usage_last = NSEC_INFINITY;
RATELIMIT_INIT(u->start_limit, m->default_start_limit_interval, m->default_start_limit_burst);
RATELIMIT_INIT(u->auto_stop_ratelimit, 10 * USEC_PER_SEC, 16);
@@ -113,7 +114,7 @@ bool unit_has_name(Unit *u, const char *name) {
assert(u);
assert(name);
- return !!set_get(u->names, (char*) name);
+ return set_contains(u->names, (char*) name);
}
static void unit_init(Unit *u) {
@@ -329,6 +330,9 @@ bool unit_check_gc(Unit *u) {
if (u->refs)
return true;
+ if (sd_bus_track_count(u->bus_track) > 0)
+ return true;
+
if (UNIT_VTABLE(u)->check_gc)
if (UNIT_VTABLE(u)->check_gc(u))
return true;
@@ -509,6 +513,9 @@ void unit_free(Unit *u) {
sd_bus_slot_unref(u->match_bus_slot);
+ sd_bus_track_unref(u->bus_track);
+ u->deserialized_refs = strv_free(u->deserialized_refs);
+
unit_free_requires_mounts_for(u);
SET_FOREACH(t, u->names, i)
@@ -897,6 +904,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
Unit *following;
_cleanup_set_free_ Set *following_set = NULL;
int r;
+ const char *n;
assert(u);
assert(u->type >= 0);
@@ -1038,6 +1046,8 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
else if (u->load_state == UNIT_ERROR)
fprintf(f, "%s\tLoad Error Code: %s\n", prefix, strerror(-u->load_error));
+ for (n = sd_bus_track_first(u->bus_track); n; n = sd_bus_track_next(u->bus_track))
+ fprintf(f, "%s\tBus Ref: %s\n", prefix, n);
if (u->job)
job_dump(u->job, f, prefix2);
@@ -2611,7 +2621,10 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
unit_serialize_item(u, f, "assert-result", yes_no(u->assert_result));
unit_serialize_item(u, f, "transient", yes_no(u->transient));
+
unit_serialize_item_format(u, f, "cpu-usage-base", "%" PRIu64, u->cpu_usage_base);
+ if (u->cpu_usage_last != NSEC_INFINITY)
+ unit_serialize_item_format(u, f, "cpu-usage-last", "%" PRIu64, u->cpu_usage_last);
if (u->cgroup_path)
unit_serialize_item(u, f, "cgroup", u->cgroup_path);
@@ -2622,15 +2635,17 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
if (gid_is_valid(u->ref_gid))
unit_serialize_item_format(u, f, "ref-gid", GID_FMT, u->ref_gid);
+ bus_track_serialize(u->bus_track, f, "ref");
+
if (serialize_jobs) {
if (u->job) {
fprintf(f, "job\n");
- job_serialize(u->job, f, fds);
+ job_serialize(u->job, f);
}
if (u->nop_job) {
fprintf(f, "job\n");
- job_serialize(u->nop_job, f, fds);
+ job_serialize(u->nop_job, f);
}
}
@@ -2760,7 +2775,7 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
if (!j)
return log_oom();
- r = job_deserialize(j, f, fds);
+ r = job_deserialize(j, f);
if (r < 0) {
job_free(j);
return r;
@@ -2832,11 +2847,19 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
continue;
- } else if (streq(l, "cpu-usage-base") || streq(l, "cpuacct-usage-base")) {
+ } else if (STR_IN_SET(l, "cpu-usage-base", "cpuacct-usage-base")) {
r = safe_atou64(v, &u->cpu_usage_base);
if (r < 0)
- log_unit_debug(u, "Failed to parse CPU usage %s, ignoring.", v);
+ log_unit_debug(u, "Failed to parse CPU usage base %s, ignoring.", v);
+
+ continue;
+
+ } else if (streq(l, "cpu-usage-last")) {
+
+ r = safe_atou64(v, &u->cpu_usage_last);
+ if (r < 0)
+ log_unit_debug(u, "Failed to read CPU usage last %s, ignoring.", v);
continue;
@@ -2880,6 +2903,12 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
else
unit_ref_uid_gid(u, UID_INVALID, gid);
+ } else if (streq(l, "ref")) {
+
+ r = strv_extend(&u->deserialized_refs, v);
+ if (r < 0)
+ log_oom();
+
continue;
}
@@ -2955,7 +2984,8 @@ int unit_add_node_link(Unit *u, const char *what, bool wants, UnitDependency dep
}
int unit_coldplug(Unit *u) {
- int r = 0, q = 0;
+ int r = 0, q;
+ char **i;
assert(u);
@@ -2966,18 +2996,26 @@ int unit_coldplug(Unit *u) {
u->coldplugged = true;
- if (UNIT_VTABLE(u)->coldplug)
- r = UNIT_VTABLE(u)->coldplug(u);
+ STRV_FOREACH(i, u->deserialized_refs) {
+ q = bus_unit_track_add_name(u, *i);
+ if (q < 0 && r >= 0)
+ r = q;
+ }
+ u->deserialized_refs = strv_free(u->deserialized_refs);
- if (u->job)
- q = job_coldplug(u->job);
+ if (UNIT_VTABLE(u)->coldplug) {
+ q = UNIT_VTABLE(u)->coldplug(u);
+ if (q < 0 && r >= 0)
+ r = q;
+ }
- if (r < 0)
- return r;
- if (q < 0)
- return q;
+ if (u->job) {
+ q = job_coldplug(u->job);
+ if (q < 0 && r >= 0)
+ r = q;
+ }
- return 0;
+ return r;
}
static bool fragment_mtime_newer(const char *path, usec_t mtime) {
diff --git a/src/core/unit.h b/src/core/unit.h
index f7a65782f1..3584c16d8c 100644
--- a/src/core/unit.h
+++ b/src/core/unit.h
@@ -108,6 +108,10 @@ struct Unit {
/* The slot used for watching NameOwnerChanged signals */
sd_bus_slot *match_bus_slot;
+ /* References to this unit from clients */
+ sd_bus_track *bus_track;
+ char **deserialized_refs;
+
/* Job timeout and action to take */
usec_t job_timeout;
FailureAction job_timeout_action;
@@ -190,6 +194,7 @@ struct Unit {
/* Where the cpu.stat or cpuacct.usage was at the time the unit was started */
nsec_t cpu_usage_base;
+ nsec_t cpu_usage_last; /* the most recently read value */
/* Counterparts in the cgroup filesystem */
char *cgroup_path;
@@ -247,6 +252,9 @@ struct Unit {
/* Did we already invoke unit_coldplug() for this unit? */
bool coldplugged:1;
+
+ /* For transient units: whether to add a bus track reference after creating the unit */
+ bool bus_track_add:1;
};
struct UnitStatusMessageFormats {
diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym
index 542254295c..70ea347361 100644
--- a/src/libsystemd/libsystemd.sym
+++ b/src/libsystemd/libsystemd.sym
@@ -500,3 +500,13 @@ LIBSYSTEMD_231 {
global:
sd_event_get_iteration;
} LIBSYSTEMD_230;
+
+LIBSYSTEMD_232 {
+global:
+ sd_bus_track_set_recursive;
+ sd_bus_track_get_recursive;
+ sd_bus_track_count_name;
+ sd_bus_track_count_sender;
+ sd_bus_set_exit_on_disconnect;
+ sd_bus_get_exit_on_disconnect;
+} LIBSYSTEMD_231;
diff --git a/src/libsystemd/sd-bus/bus-common-errors.c b/src/libsystemd/sd-bus/bus-common-errors.c
index 32be3cdc38..a69193aa32 100644
--- a/src/libsystemd/sd-bus/bus-common-errors.c
+++ b/src/libsystemd/sd-bus/bus-common-errors.c
@@ -45,6 +45,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
SD_BUS_ERROR_MAP(BUS_ERROR_SHUTTING_DOWN, ECANCELED),
SD_BUS_ERROR_MAP(BUS_ERROR_SCOPE_NOT_RUNNING, EHOSTDOWN),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_DYNAMIC_USER, ESRCH),
+ SD_BUS_ERROR_MAP(BUS_ERROR_NOT_REFERENCED, EUNATCH),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_MACHINE, ENXIO),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_IMAGE, ENOENT),
diff --git a/src/libsystemd/sd-bus/bus-common-errors.h b/src/libsystemd/sd-bus/bus-common-errors.h
index befb6fbfe0..5df21c8926 100644
--- a/src/libsystemd/sd-bus/bus-common-errors.h
+++ b/src/libsystemd/sd-bus/bus-common-errors.h
@@ -41,6 +41,7 @@
#define BUS_ERROR_SHUTTING_DOWN "org.freedesktop.systemd1.ShuttingDown"
#define BUS_ERROR_SCOPE_NOT_RUNNING "org.freedesktop.systemd1.ScopeNotRunning"
#define BUS_ERROR_NO_SUCH_DYNAMIC_USER "org.freedesktop.systemd1.NoSuchDynamicUser"
+#define BUS_ERROR_NOT_REFERENCED "org.freedesktop.systemd1.NotReferenced"
#define BUS_ERROR_NO_SUCH_MACHINE "org.freedesktop.machine1.NoSuchMachine"
#define BUS_ERROR_NO_SUCH_IMAGE "org.freedesktop.machine1.NoSuchImage"
diff --git a/src/libsystemd/sd-bus/bus-internal.h b/src/libsystemd/sd-bus/bus-internal.h
index 216d9f62bc..2608f5469c 100644
--- a/src/libsystemd/sd-bus/bus-internal.h
+++ b/src/libsystemd/sd-bus/bus-internal.h
@@ -209,6 +209,9 @@ struct sd_bus {
bool is_system:1;
bool is_user:1;
bool allow_interactive_authorization:1;
+ bool exit_on_disconnect:1;
+ bool exited:1;
+ bool exit_triggered:1;
int use_memfd;
@@ -320,6 +323,7 @@ struct sd_bus {
sd_bus_track *track_queue;
LIST_HEAD(sd_bus_slot, slots);
+ LIST_HEAD(sd_bus_track, tracks);
};
#define BUS_DEFAULT_TIMEOUT ((usec_t) (25 * USEC_PER_SEC))
diff --git a/src/libsystemd/sd-bus/bus-track.c b/src/libsystemd/sd-bus/bus-track.c
index 1f436fe560..00e93a215f 100644
--- a/src/libsystemd/sd-bus/bus-track.c
+++ b/src/libsystemd/sd-bus/bus-track.c
@@ -24,16 +24,27 @@
#include "bus-track.h"
#include "bus-util.h"
+struct track_item {
+ unsigned n_ref;
+ char *name;
+ sd_bus_slot *slot;
+};
+
struct sd_bus_track {
unsigned n_ref;
+ unsigned n_adding; /* are we in the process of adding a new name? */
sd_bus *bus;
sd_bus_track_handler_t handler;
void *userdata;
Hashmap *names;
LIST_FIELDS(sd_bus_track, queue);
Iterator iterator;
- bool in_queue;
- bool modified;
+ bool in_list:1; /* In bus->tracks? */
+ bool in_queue:1; /* In bus->track_queue? */
+ bool modified:1;
+ bool recursive:1;
+
+ LIST_FIELDS(sd_bus_track, tracks);
};
#define MATCH_PREFIX \
@@ -56,15 +67,47 @@ struct sd_bus_track {
_x; \
})
+static struct track_item* track_item_free(struct track_item *i) {
+
+ if (!i)
+ return NULL;
+
+ sd_bus_slot_unref(i->slot);
+ free(i->name);
+ free(i);
+
+ return NULL;
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct track_item*, track_item_free);
+
static void bus_track_add_to_queue(sd_bus_track *track) {
assert(track);
+ /* Adds the bus track object to the queue of objects we should dispatch next, subject to a number of
+ * conditions. */
+
+ /* Already in the queue? */
if (track->in_queue)
return;
+ /* if we are currently in the process of adding a new name, then let's not enqueue this just yet, let's wait
+ * until the addition is complete. */
+ if (track->n_adding > 0)
+ return;
+
+ /* still referenced? */
+ if (hashmap_size(track->names) > 0)
+ return;
+
+ /* Nothing to call? */
if (!track->handler)
return;
+ /* Already closed? */
+ if (!track->in_list)
+ return;
+
LIST_PREPEND(queue, track->bus->track_queue, track);
track->in_queue = true;
}
@@ -79,6 +122,24 @@ static void bus_track_remove_from_queue(sd_bus_track *track) {
track->in_queue = false;
}
+static int bus_track_remove_name_fully(sd_bus_track *track, const char *name) {
+ struct track_item *i;
+
+ assert(track);
+ assert(name);
+
+ i = hashmap_remove(track->names, name);
+ if (!i)
+ return 0;
+
+ track_item_free(i);
+
+ bus_track_add_to_queue(track);
+
+ track->modified = true;
+ return 1;
+}
+
_public_ int sd_bus_track_new(
sd_bus *bus,
sd_bus_track **track,
@@ -102,6 +163,9 @@ _public_ int sd_bus_track_new(
t->userdata = userdata;
t->bus = sd_bus_ref(bus);
+ LIST_PREPEND(tracks, bus->tracks, t);
+ t->in_list = true;
+
bus_track_add_to_queue(t);
*track = t;
@@ -121,7 +185,7 @@ _public_ sd_bus_track* sd_bus_track_ref(sd_bus_track *track) {
}
_public_ sd_bus_track* sd_bus_track_unref(sd_bus_track *track) {
- const char *n;
+ struct track_item *i;
if (!track)
return NULL;
@@ -133,8 +197,11 @@ _public_ sd_bus_track* sd_bus_track_unref(sd_bus_track *track) {
return NULL;
}
- while ((n = hashmap_first_key(track->names)))
- sd_bus_track_remove_name(track, n);
+ while ((i = hashmap_steal_first(track->names)))
+ track_item_free(i);
+
+ if (track->in_list)
+ LIST_REMOVE(tracks, track->bus->tracks, track);
bus_track_remove_from_queue(track);
hashmap_free(track->names);
@@ -156,49 +223,76 @@ static int on_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus
if (r < 0)
return 0;
- sd_bus_track_remove_name(track, name);
+ bus_track_remove_name_fully(track, name);
return 0;
}
_public_ int sd_bus_track_add_name(sd_bus_track *track, const char *name) {
- _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
- _cleanup_free_ char *n = NULL;
+ _cleanup_(track_item_freep) struct track_item *n = NULL;
+ struct track_item *i;
const char *match;
int r;
assert_return(track, -EINVAL);
assert_return(service_name_is_valid(name), -EINVAL);
+ i = hashmap_get(track->names, name);
+ if (i) {
+ if (track->recursive) {
+ unsigned k = track->n_ref + 1;
+
+ if (k < track->n_ref) /* Check for overflow */
+ return -EOVERFLOW;
+
+ track->n_ref = k;
+ }
+
+ bus_track_remove_from_queue(track);
+ return 0;
+ }
+
r = hashmap_ensure_allocated(&track->names, &string_hash_ops);
if (r < 0)
return r;
- n = strdup(name);
+ n = new0(struct track_item, 1);
if (!n)
return -ENOMEM;
+ n->name = strdup(name);
+ if (!n->name)
+ return -ENOMEM;
/* First, subscribe to this name */
- match = MATCH_FOR_NAME(n);
- r = sd_bus_add_match(track->bus, &slot, match, on_name_owner_changed, track);
- if (r < 0)
+ match = MATCH_FOR_NAME(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--;
+ if (r < 0) {
+ bus_track_add_to_queue(track);
return r;
+ }
- r = hashmap_put(track->names, n, slot);
- if (r == -EEXIST)
- return 0;
- if (r < 0)
+ r = hashmap_put(track->names, n->name, n);
+ if (r < 0) {
+ bus_track_add_to_queue(track);
return r;
+ }
- /* Second, check if it is currently existing, or maybe
- * doesn't, or maybe disappeared already. */
- r = sd_bus_get_name_creds(track->bus, n, 0, NULL);
+ /* Second, check if it is currently existing, or maybe doesn't, or maybe disappeared already. */
+ track->n_adding++; /* again, make sure this isn't dispatch while we are working in it */
+ r = sd_bus_get_name_creds(track->bus, name, 0, NULL);
+ track->n_adding--;
if (r < 0) {
- hashmap_remove(track->names, n);
+ hashmap_remove(track->names, name);
+ bus_track_add_to_queue(track);
return r;
}
+ n->n_ref = 1;
n = NULL;
- slot = NULL;
bus_track_remove_from_queue(track);
track->modified = true;
@@ -207,37 +301,48 @@ _public_ int sd_bus_track_add_name(sd_bus_track *track, const char *name) {
}
_public_ int sd_bus_track_remove_name(sd_bus_track *track, const char *name) {
- _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
- _cleanup_free_ char *n = NULL;
+ struct track_item *i;
assert_return(name, -EINVAL);
- if (!track)
+ if (!track) /* Treat a NULL track object as an empty track object */
return 0;
- slot = hashmap_remove2(track->names, (char*) name, (void**) &n);
- if (!slot)
- return 0;
+ if (!track->recursive)
+ return bus_track_remove_name_fully(track, name);
- if (hashmap_isempty(track->names))
- bus_track_add_to_queue(track);
+ i = hashmap_get(track->names, name);
+ if (!i)
+ return -EUNATCH;
+ if (i->n_ref <= 0)
+ return -EUNATCH;
- track->modified = true;
+ i->n_ref--;
+
+ if (i->n_ref <= 0)
+ return bus_track_remove_name_fully(track, name);
return 1;
}
_public_ unsigned sd_bus_track_count(sd_bus_track *track) {
- if (!track)
+
+ if (!track) /* Let's consider a NULL object equivalent to an empty object */
return 0;
+ /* This signature really should have returned an int, so that we can propagate errors. But well, ... Also, note
+ * that this returns the number of names being watched, and multiple references to the same name are not
+ * counted. */
+
return hashmap_size(track->names);
}
_public_ const char* sd_bus_track_contains(sd_bus_track *track, const char *name) {
- assert_return(track, NULL);
assert_return(name, NULL);
+ if (!track) /* Let's consider a NULL object equivalent to an empty object */
+ return NULL;
+
return hashmap_get(track->names, (void*) name) ? name : NULL;
}
@@ -273,6 +378,9 @@ _public_ int sd_bus_track_add_sender(sd_bus_track *track, sd_bus_message *m) {
assert_return(track, -EINVAL);
assert_return(m, -EINVAL);
+ if (sd_bus_message_get_bus(m) != track->bus)
+ return -EINVAL;
+
sender = sd_bus_message_get_sender(m);
if (!sender)
return -EINVAL;
@@ -283,9 +391,14 @@ _public_ int sd_bus_track_add_sender(sd_bus_track *track, sd_bus_message *m) {
_public_ int sd_bus_track_remove_sender(sd_bus_track *track, sd_bus_message *m) {
const char *sender;
- assert_return(track, -EINVAL);
assert_return(m, -EINVAL);
+ if (!track) /* Treat a NULL track object as an empty track object */
+ return 0;
+
+ if (sd_bus_message_get_bus(m) != track->bus)
+ return -EINVAL;
+
sender = sd_bus_message_get_sender(m);
if (!sender)
return -EINVAL;
@@ -303,7 +416,6 @@ void bus_track_dispatch(sd_bus_track *track) {
int r;
assert(track);
- assert(track->in_queue);
assert(track->handler);
bus_track_remove_from_queue(track);
@@ -319,6 +431,34 @@ void bus_track_dispatch(sd_bus_track *track) {
sd_bus_track_unref(track);
}
+void bus_track_close(sd_bus_track *track) {
+ struct track_item *i;
+
+ assert(track);
+
+ /* Called whenever our bus connected is closed. If so, and our track object is non-empty, dispatch it
+ * immediately, as we are closing now, but first flush out all names. */
+
+ if (!track->in_list)
+ return; /* We already closed this one, don't close it again. */
+
+ /* Remember that this one is closed now */
+ LIST_REMOVE(tracks, track->bus->tracks, track);
+ track->in_list = false;
+
+ /* If there's no name in this one anyway, we don't have to dispatch */
+ if (hashmap_isempty(track->names))
+ return;
+
+ /* Let's flush out all names */
+ while ((i = hashmap_steal_first(track->names)))
+ track_item_free(i);
+
+ /* Invoke handler */
+ if (track->handler)
+ bus_track_dispatch(track);
+}
+
_public_ void *sd_bus_track_get_userdata(sd_bus_track *track) {
assert_return(track, NULL);
@@ -335,3 +475,55 @@ _public_ void *sd_bus_track_set_userdata(sd_bus_track *track, void *userdata) {
return ret;
}
+
+_public_ int sd_bus_track_set_recursive(sd_bus_track *track, int b) {
+ assert_return(track, -EINVAL);
+
+ if (track->recursive == !!b)
+ return 0;
+
+ if (!hashmap_isempty(track->names))
+ return -EBUSY;
+
+ track->recursive = b;
+ return 0;
+}
+
+_public_ int sd_bus_track_get_recursive(sd_bus_track *track) {
+ assert_return(track, -EINVAL);
+
+ return track->recursive;
+}
+
+_public_ int sd_bus_track_count_sender(sd_bus_track *track, sd_bus_message *m) {
+ const char *sender;
+
+ assert_return(m, -EINVAL);
+
+ if (!track) /* Let's consider a NULL object equivalent to an empty object */
+ return 0;
+
+ if (sd_bus_message_get_bus(m) != track->bus)
+ return -EINVAL;
+
+ sender = sd_bus_message_get_sender(m);
+ if (!sender)
+ return -EINVAL;
+
+ return sd_bus_track_count_name(track, sender);
+}
+
+_public_ int sd_bus_track_count_name(sd_bus_track *track, const char *name) {
+ struct track_item *i;
+
+ assert_return(service_name_is_valid(name), -EINVAL);
+
+ if (!track) /* Let's consider a NULL object equivalent to an empty object */
+ return 0;
+
+ i = hashmap_get(track->names, name);
+ if (!i)
+ return 0;
+
+ return i->n_ref;
+}
diff --git a/src/libsystemd/sd-bus/bus-track.h b/src/libsystemd/sd-bus/bus-track.h
index 7d93a727d6..26bd05f5c7 100644
--- a/src/libsystemd/sd-bus/bus-track.h
+++ b/src/libsystemd/sd-bus/bus-track.h
@@ -20,3 +20,4 @@
***/
void bus_track_dispatch(sd_bus_track *track);
+void bus_track_close(sd_bus_track *track);
diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c
index ed5f94e136..d746348544 100644
--- a/src/libsystemd/sd-bus/sd-bus.c
+++ b/src/libsystemd/sd-bus/sd-bus.c
@@ -107,6 +107,7 @@ static void bus_free(sd_bus *b) {
assert(b);
assert(!b->track_queue);
+ assert(!b->tracks);
b->state = BUS_CLOSED;
@@ -2640,62 +2641,101 @@ null_message:
return r;
}
-static int process_closing(sd_bus *bus, sd_bus_message **ret) {
+static int bus_exit_now(sd_bus *bus) {
+ assert(bus);
+
+ /* Exit due to close, if this is requested. If this is bus object is attached to an event source, invokes
+ * sd_event_exit(), otherwise invokes libc exit(). */
+
+ if (bus->exited) /* did we already exit? */
+ return 0;
+ if (!bus->exit_triggered) /* was the exit condition triggered? */
+ return 0;
+ if (!bus->exit_on_disconnect) /* Shall we actually exit on disconnection? */
+ return 0;
+
+ bus->exited = true; /* never exit more than once */
+
+ log_debug("Bus connection disconnected, exiting.");
+
+ if (bus->event)
+ return sd_event_exit(bus->event, EXIT_FAILURE);
+ else
+ exit(EXIT_FAILURE);
+
+ assert_not_reached("exit() didn't exit?");
+}
+
+static int process_closing_reply_callback(sd_bus *bus, struct reply_callback *c) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
- struct reply_callback *c;
+ sd_bus_slot *slot;
int r;
assert(bus);
- assert(bus->state == BUS_CLOSING);
+ assert(c);
- c = ordered_hashmap_first(bus->reply_callbacks);
- if (c) {
- _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
- sd_bus_slot *slot;
+ r = bus_message_new_synthetic_error(
+ bus,
+ c->cookie,
+ &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NO_REPLY, "Connection terminated"),
+ &m);
+ if (r < 0)
+ return r;
- /* First, fail all outstanding method calls */
- r = bus_message_new_synthetic_error(
- bus,
- c->cookie,
- &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NO_REPLY, "Connection terminated"),
- &m);
- if (r < 0)
- return r;
+ r = bus_seal_synthetic_message(bus, m);
+ if (r < 0)
+ return r;
- r = bus_seal_synthetic_message(bus, m);
- if (r < 0)
- return r;
+ if (c->timeout != 0) {
+ prioq_remove(bus->reply_callbacks_prioq, c, &c->prioq_idx);
+ c->timeout = 0;
+ }
- if (c->timeout != 0) {
- prioq_remove(bus->reply_callbacks_prioq, c, &c->prioq_idx);
- c->timeout = 0;
- }
+ ordered_hashmap_remove(bus->reply_callbacks, &c->cookie);
+ c->cookie = 0;
- ordered_hashmap_remove(bus->reply_callbacks, &c->cookie);
- c->cookie = 0;
+ slot = container_of(c, sd_bus_slot, reply_callback);
- slot = container_of(c, sd_bus_slot, reply_callback);
+ bus->iteration_counter++;
- bus->iteration_counter++;
+ bus->current_message = m;
+ bus->current_slot = sd_bus_slot_ref(slot);
+ bus->current_handler = c->callback;
+ bus->current_userdata = slot->userdata;
+ r = c->callback(m, slot->userdata, &error_buffer);
+ bus->current_userdata = NULL;
+ bus->current_handler = NULL;
+ bus->current_slot = NULL;
+ bus->current_message = NULL;
- bus->current_message = m;
- bus->current_slot = sd_bus_slot_ref(slot);
- bus->current_handler = c->callback;
- bus->current_userdata = slot->userdata;
- r = c->callback(m, slot->userdata, &error_buffer);
- bus->current_userdata = NULL;
- bus->current_handler = NULL;
- bus->current_slot = NULL;
- bus->current_message = NULL;
+ if (slot->floating) {
+ bus_slot_disconnect(slot);
+ sd_bus_slot_unref(slot);
+ }
- if (slot->floating) {
- bus_slot_disconnect(slot);
- sd_bus_slot_unref(slot);
- }
+ sd_bus_slot_unref(slot);
- sd_bus_slot_unref(slot);
+ return bus_maybe_reply_error(m, r, &error_buffer);
+}
- return bus_maybe_reply_error(m, r, &error_buffer);
+static int process_closing(sd_bus *bus, sd_bus_message **ret) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+ struct reply_callback *c;
+ int r;
+
+ assert(bus);
+ assert(bus->state == BUS_CLOSING);
+
+ /* First, fail all outstanding method calls */
+ c = ordered_hashmap_first(bus->reply_callbacks);
+ if (c)
+ return process_closing_reply_callback(bus, c);
+
+ /* Then, fake-drop all remaining bus tracking references */
+ if (bus->tracks) {
+ bus_track_close(bus->tracks);
+ return 1;
}
/* Then, synthesize a Disconnected message */
@@ -2727,6 +2767,10 @@ static int process_closing(sd_bus *bus, sd_bus_message **ret) {
if (r != 0)
goto finish;
+ /* Nothing else to do, exit now, if the condition holds */
+ bus->exit_triggered = true;
+ (void) bus_exit_now(bus);
+
if (ret) {
*ret = m;
m = NULL;
@@ -3789,3 +3833,21 @@ _public_ void sd_bus_default_flush_close(void) {
flush_close(default_user_bus);
flush_close(default_system_bus);
}
+
+_public_ int sd_bus_set_exit_on_disconnect(sd_bus *bus, int b) {
+ assert_return(bus, -EINVAL);
+
+ /* Turns on exit-on-disconnect, and triggers it immediately if the bus connection was already
+ * disconnected. Note that this is triggered exclusively on disconnections triggered by the server side, never
+ * from the client side. */
+ bus->exit_on_disconnect = b;
+
+ /* If the exit condition was triggered already, exit immediately. */
+ return bus_exit_now(bus);
+}
+
+_public_ int sd_bus_get_exit_on_disconnect(sd_bus *bus) {
+ assert_return(bus, -EINVAL);
+
+ return bus->exit_on_disconnect;
+}
diff --git a/src/libsystemd/sd-bus/test-bus-track.c b/src/libsystemd/sd-bus/test-bus-track.c
new file mode 100644
index 0000000000..4beb61f05a
--- /dev/null
+++ b/src/libsystemd/sd-bus/test-bus-track.c
@@ -0,0 +1,113 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2016 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 <sd-bus.h>
+
+#include "macro.h"
+
+static bool track_cb_called_x = false;
+static bool track_cb_called_y = false;
+
+static int track_cb_x(sd_bus_track *t, void *userdata) {
+
+ log_error("TRACK CB X");
+
+ assert_se(!track_cb_called_x);
+ track_cb_called_x = true;
+
+ /* This means b's name disappeared. Let's now disconnect, to make sure the track handling on disconnect works
+ * as it should. */
+
+ assert_se(shutdown(sd_bus_get_fd(sd_bus_track_get_bus(t)), SHUT_RDWR) >= 0);
+ return 1;
+}
+
+static int track_cb_y(sd_bus_track *t, void *userdata) {
+ int r;
+
+ log_error("TRACK CB Y");
+
+ assert_se(!track_cb_called_y);
+ track_cb_called_y = true;
+
+ /* We got disconnected, let's close everything */
+
+ r = sd_event_exit(sd_bus_get_event(sd_bus_track_get_bus(t)), EXIT_SUCCESS);
+ assert_se(r >= 0);
+
+ return 0;
+}
+
+int main(int argc, char *argv[]) {
+ _cleanup_(sd_event_unrefp) sd_event *event = NULL;
+ _cleanup_(sd_bus_track_unrefp) sd_bus_track *x = NULL, *y = NULL;
+ _cleanup_(sd_bus_unrefp) sd_bus *a = NULL, *b = NULL;
+ const char *unique;
+ int r;
+
+ r = sd_event_default(&event);
+ assert_se(r >= 0);
+
+ r = sd_bus_open_system(&a);
+ if (IN_SET(r, -ECONNREFUSED, -ENOENT)) {
+ log_info("Failed to connect to bus, skipping tests.");
+ return EXIT_TEST_SKIP;
+ }
+ assert_se(r >= 0);
+
+ r = sd_bus_attach_event(a, event, SD_EVENT_PRIORITY_NORMAL);
+ assert_se(r >= 0);
+
+ r = sd_bus_open_system(&b);
+ assert_se(r >= 0);
+
+ r = sd_bus_attach_event(b, event, SD_EVENT_PRIORITY_NORMAL);
+ assert_se(r >= 0);
+
+ /* Watch b's name from a */
+ r = sd_bus_track_new(a, &x, track_cb_x, NULL);
+ assert_se(r >= 0);
+
+ r = sd_bus_get_unique_name(b, &unique);
+ assert_se(r >= 0);
+
+ r = sd_bus_track_add_name(x, unique);
+ assert_se(r >= 0);
+
+ /* Watch's a's own name from a */
+ r = sd_bus_track_new(a, &y, track_cb_y, NULL);
+ assert_se(r >= 0);
+
+ r = sd_bus_get_unique_name(a, &unique);
+ assert_se(r >= 0);
+
+ r = sd_bus_track_add_name(y, unique);
+ assert_se(r >= 0);
+
+ /* Now make b's name disappear */
+ sd_bus_close(b);
+
+ r = sd_event_loop(event);
+ assert_se(r >= 0);
+
+ assert_se(track_cb_called_x);
+ assert_se(track_cb_called_y);
+
+ return 0;
+}
diff --git a/src/run/run.c b/src/run/run.c
index e8586ef61a..2dd229868c 100644
--- a/src/run/run.c
+++ b/src/run/run.c
@@ -33,6 +33,7 @@
#include "formats-util.h"
#include "parse-util.h"
#include "path-util.h"
+#include "process-util.h"
#include "ptyfwd.h"
#include "signal-util.h"
#include "spawn-polkit-agent.h"
@@ -45,6 +46,7 @@ static bool arg_ask_password = true;
static bool arg_scope = false;
static bool arg_remain_after_exit = false;
static bool arg_no_block = false;
+static bool arg_wait = false;
static const char *arg_unit = NULL;
static const char *arg_description = NULL;
static const char *arg_slice = NULL;
@@ -97,6 +99,7 @@ static void help(void) {
" --slice=SLICE Run in the specified slice\n"
" --no-block Do not wait until operation finished\n"
" -r --remain-after-exit Leave service around until explicitly stopped\n"
+ " --wait Wait until service stopped again\n"
" --send-sighup Send SIGHUP when terminating\n"
" --service-type=TYPE Service type\n"
" --uid=USER Run as system user\n"
@@ -144,6 +147,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_TIMER_PROPERTY,
ARG_NO_BLOCK,
ARG_NO_ASK_PASSWORD,
+ ARG_WAIT,
};
static const struct option options[] = {
@@ -160,6 +164,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "host", required_argument, NULL, 'H' },
{ "machine", required_argument, NULL, 'M' },
{ "service-type", required_argument, NULL, ARG_SERVICE_TYPE },
+ { "wait", no_argument, NULL, ARG_WAIT },
{ "uid", required_argument, NULL, ARG_EXEC_USER },
{ "gid", required_argument, NULL, ARG_EXEC_GROUP },
{ "nice", required_argument, NULL, ARG_NICE },
@@ -357,6 +362,10 @@ static int parse_argv(int argc, char *argv[]) {
arg_no_block = true;
break;
+ case ARG_WAIT:
+ arg_wait = true;
+ break;
+
case '?':
return -EINVAL;
@@ -404,6 +413,23 @@ static int parse_argv(int argc, char *argv[]) {
return -EINVAL;
}
+ if (arg_wait) {
+ if (arg_no_block) {
+ log_error("--wait may not be combined with --no-block.");
+ return -EINVAL;
+ }
+
+ if (with_timer()) {
+ log_error("--wait may not be combined with timer operations.");
+ return -EINVAL;
+ }
+
+ if (arg_scope) {
+ log_error("--wait may not be combined with --scope.");
+ return -EINVAL;
+ }
+ }
+
return 1;
}
@@ -466,6 +492,12 @@ static int transient_service_set_properties(sd_bus_message *m, char **argv, cons
if (r < 0)
return r;
+ if (arg_wait) {
+ r = sd_bus_message_append(m, "(sv)", "AddRef", "b", 1);
+ if (r < 0)
+ return r;
+ }
+
if (arg_remain_after_exit) {
r = sd_bus_message_append(m, "(sv)", "RemainAfterExit", "b", arg_remain_after_exit);
if (r < 0)
@@ -723,9 +755,97 @@ static int make_unit_name(sd_bus *bus, UnitType t, char **ret) {
return 0;
}
+typedef struct RunContext {
+ sd_bus *bus;
+ sd_event *event;
+ PTYForward *forward;
+ sd_bus_slot *match;
+
+ /* The exit data of the unit */
+ char *active_state;
+ uint64_t inactive_exit_usec;
+ uint64_t inactive_enter_usec;
+ char *result;
+ uint64_t cpu_usage_nsec;
+ uint32_t exit_code;
+ uint32_t exit_status;
+} RunContext;
+
+static void run_context_free(RunContext *c) {
+ assert(c);
+
+ c->forward = pty_forward_free(c->forward);
+ c->match = sd_bus_slot_unref(c->match);
+ c->bus = sd_bus_unref(c->bus);
+ c->event = sd_event_unref(c->event);
+
+ free(c->active_state);
+ free(c->result);
+}
+
+static void run_context_check_done(RunContext *c) {
+ bool done = true;
+
+ assert(c);
+
+ if (c->match)
+ done = done && (c->active_state && STR_IN_SET(c->active_state, "inactive", "failed"));
+
+ if (c->forward)
+ done = done && pty_forward_is_done(c->forward);
+
+ if (done)
+ sd_event_exit(c->event, EXIT_SUCCESS);
+}
+
+static int on_properties_changed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+
+ static const struct bus_properties_map map[] = {
+ { "ActiveState", "s", NULL, offsetof(RunContext, active_state) },
+ { "InactiveExitTimestampMonotonic", "t", NULL, offsetof(RunContext, inactive_exit_usec) },
+ { "InactiveEnterTimestampMonotonic", "t", NULL, offsetof(RunContext, inactive_enter_usec) },
+ { "Result", "s", NULL, offsetof(RunContext, result) },
+ { "ExecMainCode", "i", NULL, offsetof(RunContext, exit_code) },
+ { "ExecMainStatus", "i", NULL, offsetof(RunContext, exit_status) },
+ { "CPUUsageNSec", "t", NULL, offsetof(RunContext, cpu_usage_nsec) },
+ {}
+ };
+
+ RunContext *c = userdata;
+ int r;
+
+ r = bus_map_all_properties(c->bus,
+ "org.freedesktop.systemd1",
+ sd_bus_message_get_path(m),
+ map,
+ c);
+ if (r < 0) {
+ sd_event_exit(c->event, EXIT_FAILURE);
+ return log_error_errno(r, "Failed to query unit state: %m");
+ }
+
+ run_context_check_done(c);
+ return 0;
+}
+
+static int pty_forward_handler(PTYForward *f, int rcode, void *userdata) {
+ RunContext *c = userdata;
+
+ assert(f);
+
+ if (rcode < 0) {
+ sd_event_exit(c->event, EXIT_FAILURE);
+ return log_error_errno(rcode, "Error on PTY forwarding logic: %m");
+ }
+
+ run_context_check_done(c);
+ return 0;
+}
+
static int start_transient_service(
sd_bus *bus,
- char **argv) {
+ char **argv,
+ int *retval) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
@@ -736,6 +856,7 @@ static int start_transient_service(
assert(bus);
assert(argv);
+ assert(retval);
if (arg_pty) {
@@ -859,40 +980,95 @@ static int start_transient_service(
return r;
}
- if (master >= 0) {
- _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
- _cleanup_(sd_event_unrefp) sd_event *event = NULL;
- char last_char = 0;
+ if (!arg_quiet)
+ log_info("Running as unit: %s", service);
+
+ if (arg_wait || master >= 0) {
+ _cleanup_(run_context_free) RunContext c = {};
- r = sd_event_default(&event);
+ c.bus = sd_bus_ref(bus);
+
+ r = sd_event_default(&c.event);
if (r < 0)
return log_error_errno(r, "Failed to get event loop: %m");
- assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGWINCH, SIGTERM, SIGINT, -1) >= 0);
+ if (master >= 0) {
+ assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGWINCH, SIGTERM, SIGINT, -1) >= 0);
+ (void) sd_event_add_signal(c.event, NULL, SIGINT, NULL, NULL);
+ (void) sd_event_add_signal(c.event, NULL, SIGTERM, NULL, NULL);
+
+ if (!arg_quiet)
+ log_info("Press ^] three times within 1s to disconnect TTY.");
- (void) sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
- (void) sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
+ r = pty_forward_new(c.event, master, PTY_FORWARD_IGNORE_INITIAL_VHANGUP, &c.forward);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create PTY forwarder: %m");
+
+ pty_forward_set_handler(c.forward, pty_forward_handler, &c);
+ }
- if (!arg_quiet)
- log_info("Running as unit: %s\nPress ^] three times within 1s to disconnect TTY.", service);
+ if (arg_wait) {
+ _cleanup_free_ char *path = NULL;
+ const char *mt;
- r = pty_forward_new(event, master, PTY_FORWARD_IGNORE_INITIAL_VHANGUP, &forward);
- if (r < 0)
- return log_error_errno(r, "Failed to create PTY forwarder: %m");
+ path = unit_dbus_path_from_name(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);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add properties changed signal.");
- r = sd_event_loop(event);
+ r = sd_bus_attach_event(bus, c.event, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to attach bus to event loop.");
+ }
+
+ r = sd_event_loop(c.event);
if (r < 0)
return log_error_errno(r, "Failed to run event loop: %m");
- pty_forward_get_last_char(forward, &last_char);
+ if (c.forward) {
+ char last_char = 0;
- forward = pty_forward_free(forward);
+ r = pty_forward_get_last_char(c.forward, &last_char);
+ if (r >= 0 && !arg_quiet && last_char != '\n')
+ fputc('\n', stdout);
+ }
- if (!arg_quiet && last_char != '\n')
- fputc('\n', stdout);
+ if (!arg_quiet) {
+ if (!isempty(c.result))
+ log_info("Finished with result: %s", strna(c.result));
- } else if (!arg_quiet)
- log_info("Running as unit: %s", service);
+ if (c.exit_code == CLD_EXITED)
+ log_info("Main processes terminated with: code=%s/status=%i", sigchld_code_to_string(c.exit_code), c.exit_status);
+ else if (c.exit_code > 0)
+ log_info("Main processes terminated with: code=%s/status=%s", sigchld_code_to_string(c.exit_code), signal_to_string(c.exit_status));
+
+ if (c.inactive_enter_usec > 0 && c.inactive_enter_usec != USEC_INFINITY &&
+ c.inactive_exit_usec > 0 && c.inactive_exit_usec != USEC_INFINITY &&
+ c.inactive_enter_usec > c.inactive_exit_usec) {
+ char ts[FORMAT_TIMESPAN_MAX];
+ log_info("Service runtime: %s", format_timespan(ts, sizeof(ts), c.inactive_enter_usec - c.inactive_exit_usec, USEC_PER_MSEC));
+ }
+
+ if (c.cpu_usage_nsec > 0 && c.cpu_usage_nsec != NSEC_INFINITY) {
+ char ts[FORMAT_TIMESPAN_MAX];
+ log_info("CPU time consumed: %s", format_timespan(ts, sizeof(ts), (c.cpu_usage_nsec + NSEC_PER_USEC - 1) / NSEC_PER_USEC, USEC_PER_MSEC));
+ }
+ }
+
+ /* Try to propagate the service's return value */
+ if (c.result && STR_IN_SET(c.result, "success", "exit-code") && c.exit_code == CLD_EXITED)
+ *retval = c.exit_status;
+ else
+ *retval = EXIT_FAILURE;
+ }
return 0;
}
@@ -1197,7 +1373,7 @@ static int start_transient_timer(
int main(int argc, char* argv[]) {
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
_cleanup_free_ char *description = NULL, *command = NULL;
- int r;
+ int r, retval = EXIT_SUCCESS;
log_parse_environment();
log_open();
@@ -1234,7 +1410,12 @@ int main(int argc, char* argv[]) {
arg_description = description;
}
- r = bus_connect_transport_systemd(arg_transport, arg_host, arg_user, &bus);
+ /* If --wait is used connect via the bus, unconditionally, as ref/unref is not supported via the limited direct
+ * connection */
+ if (arg_wait)
+ r = bus_connect_transport(arg_transport, arg_host, arg_user, &bus);
+ else
+ r = bus_connect_transport_systemd(arg_transport, arg_host, arg_user, &bus);
if (r < 0) {
log_error_errno(r, "Failed to create bus connection: %m");
goto finish;
@@ -1245,12 +1426,12 @@ int main(int argc, char* argv[]) {
else if (with_timer())
r = start_transient_timer(bus, argv + optind);
else
- r = start_transient_service(bus, argv + optind);
+ r = start_transient_service(bus, argv + optind, &retval);
finish:
strv_free(arg_environment);
strv_free(arg_property);
strv_free(arg_timer_property);
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return r < 0 ? EXIT_FAILURE : retval;
}
diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c
index 52410999cf..e2a216a5cc 100644
--- a/src/shared/bus-util.c
+++ b/src/shared/bus-util.c
@@ -1016,19 +1016,19 @@ static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_
return r;
switch (type) {
+
case SD_BUS_TYPE_STRING: {
- const char *s;
char **p = userdata;
+ const char *s;
r = sd_bus_message_read_basic(m, type, &s);
if (r < 0)
- break;
+ return r;
if (isempty(s))
- break;
+ s = NULL;
- r = free_and_strdup(p, s);
- break;
+ return free_and_strdup(p, s);
}
case SD_BUS_TYPE_ARRAY: {
@@ -1037,13 +1037,12 @@ static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_
r = bus_message_read_strv_extend(m, &l);
if (r < 0)
- break;
+ return r;
strv_free(*p);
*p = l;
l = NULL;
-
- break;
+ return 0;
}
case SD_BUS_TYPE_BOOLEAN: {
@@ -1052,57 +1051,48 @@ static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_
r = sd_bus_message_read_basic(m, type, &b);
if (r < 0)
- break;
+ return r;
*p = b;
-
- break;
+ return 0;
}
+ case SD_BUS_TYPE_INT32:
case SD_BUS_TYPE_UINT32: {
- uint32_t u;
- uint32_t *p = userdata;
+ uint32_t u, *p = userdata;
r = sd_bus_message_read_basic(m, type, &u);
if (r < 0)
- break;
+ return r;
*p = u;
-
- break;
+ return 0;
}
+ case SD_BUS_TYPE_INT64:
case SD_BUS_TYPE_UINT64: {
- uint64_t t;
- uint64_t *p = userdata;
+ uint64_t t, *p = userdata;
r = sd_bus_message_read_basic(m, type, &t);
if (r < 0)
- break;
+ return r;
*p = t;
-
- break;
+ return 0;
}
case SD_BUS_TYPE_DOUBLE: {
- double d;
- double *p = userdata;
+ double d, *p = userdata;
r = sd_bus_message_read_basic(m, type, &d);
if (r < 0)
- break;
+ return r;
*p = d;
+ return 0;
+ }}
- break;
- }
-
- default:
- break;
- }
-
- return r;
+ return -EOPNOTSUPP;
}
int bus_message_map_all_properties(
@@ -1240,12 +1230,13 @@ int bus_map_all_properties(
return bus_message_map_all_properties(m, map, userdata);
}
-int bus_connect_transport(BusTransport transport, const char *host, bool user, sd_bus **bus) {
+int bus_connect_transport(BusTransport transport, const char *host, bool user, sd_bus **ret) {
+ _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
int r;
assert(transport >= 0);
assert(transport < _BUS_TRANSPORT_MAX);
- assert(bus);
+ assert(ret);
assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL);
assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -EOPNOTSUPP);
@@ -1254,25 +1245,34 @@ int bus_connect_transport(BusTransport transport, const char *host, bool user, s
case BUS_TRANSPORT_LOCAL:
if (user)
- r = sd_bus_default_user(bus);
+ r = sd_bus_default_user(&bus);
else
- r = sd_bus_default_system(bus);
+ r = sd_bus_default_system(&bus);
break;
case BUS_TRANSPORT_REMOTE:
- r = sd_bus_open_system_remote(bus, host);
+ r = sd_bus_open_system_remote(&bus, host);
break;
case BUS_TRANSPORT_MACHINE:
- r = sd_bus_open_system_machine(bus, host);
+ r = sd_bus_open_system_machine(&bus, host);
break;
default:
assert_not_reached("Hmm, unknown transport type.");
}
+ if (r < 0)
+ return r;
- return r;
+ r = sd_bus_set_exit_on_disconnect(bus, true);
+ if (r < 0)
+ return r;
+
+ *ret = bus;
+ bus = NULL;
+
+ return 0;
}
int bus_connect_transport_systemd(BusTransport transport, const char *host, bool user, sd_bus **bus) {
diff --git a/src/shared/ptyfwd.c b/src/shared/ptyfwd.c
index 02c03b98d8..24055e772b 100644
--- a/src/shared/ptyfwd.c
+++ b/src/shared/ptyfwd.c
@@ -68,6 +68,8 @@ struct PTYForward {
bool read_from_master:1;
+ bool done:1;
+
bool last_char_set:1;
char last_char;
@@ -76,10 +78,54 @@ struct PTYForward {
usec_t escape_timestamp;
unsigned escape_counter;
+
+ PTYForwardHandler handler;
+ void *userdata;
};
#define ESCAPE_USEC (1*USEC_PER_SEC)
+static void pty_forward_disconnect(PTYForward *f) {
+
+ if (f) {
+ f->stdin_event_source = sd_event_source_unref(f->stdin_event_source);
+ f->stdout_event_source = sd_event_source_unref(f->stdout_event_source);
+
+ f->master_event_source = sd_event_source_unref(f->master_event_source);
+ f->sigwinch_event_source = sd_event_source_unref(f->sigwinch_event_source);
+ f->event = sd_event_unref(f->event);
+
+ if (f->saved_stdout)
+ tcsetattr(STDOUT_FILENO, TCSANOW, &f->saved_stdout_attr);
+ if (f->saved_stdin)
+ tcsetattr(STDIN_FILENO, TCSANOW, &f->saved_stdin_attr);
+
+ f->saved_stdout = f->saved_stdin = false;
+ }
+
+ /* STDIN/STDOUT should not be nonblocking normally, so let's unconditionally reset it */
+ fd_nonblock(STDIN_FILENO, false);
+ fd_nonblock(STDOUT_FILENO, false);
+}
+
+static int pty_forward_done(PTYForward *f, int rcode) {
+ _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+ assert(f);
+
+ if (f->done)
+ return 0;
+
+ e = sd_event_ref(f->event);
+
+ f->done = true;
+ pty_forward_disconnect(f);
+
+ if (f->handler)
+ return f->handler(f, rcode, f->userdata);
+ else
+ return sd_event_exit(e, rcode < 0 ? EXIT_FAILURE : rcode);
+}
+
static bool look_for_escape(PTYForward *f, const char *buffer, size_t n) {
const char *p;
@@ -147,7 +193,7 @@ static int shovel(PTYForward *f) {
f->stdin_event_source = sd_event_source_unref(f->stdin_event_source);
} else {
log_error_errno(errno, "read(): %m");
- return sd_event_exit(f->event, EXIT_FAILURE);
+ return pty_forward_done(f, -errno);
}
} else if (k == 0) {
/* EOF on stdin */
@@ -156,12 +202,10 @@ static int shovel(PTYForward *f) {
f->stdin_event_source = sd_event_source_unref(f->stdin_event_source);
} else {
- /* Check if ^] has been
- * pressed three times within
- * one second. If we get this
- * we quite immediately. */
+ /* Check if ^] has been pressed three times within one second. If we get this we quite
+ * immediately. */
if (look_for_escape(f, f->in_buffer + f->in_buffer_full, k))
- return sd_event_exit(f->event, EXIT_FAILURE);
+ return pty_forward_done(f, -ECANCELED);
f->in_buffer_full += (size_t) k;
}
@@ -181,7 +225,7 @@ static int shovel(PTYForward *f) {
f->master_event_source = sd_event_source_unref(f->master_event_source);
} else {
log_error_errno(errno, "write(): %m");
- return sd_event_exit(f->event, EXIT_FAILURE);
+ return pty_forward_done(f, -errno);
}
} else {
assert(f->in_buffer_full >= (size_t) k);
@@ -211,7 +255,7 @@ static int shovel(PTYForward *f) {
f->master_event_source = sd_event_source_unref(f->master_event_source);
} else {
log_error_errno(errno, "read(): %m");
- return sd_event_exit(f->event, EXIT_FAILURE);
+ return pty_forward_done(f, -errno);
}
} else {
f->read_from_master = true;
@@ -232,7 +276,7 @@ static int shovel(PTYForward *f) {
f->stdout_event_source = sd_event_source_unref(f->stdout_event_source);
} else {
log_error_errno(errno, "write(): %m");
- return sd_event_exit(f->event, EXIT_FAILURE);
+ return pty_forward_done(f, -errno);
}
} else {
@@ -255,7 +299,7 @@ static int shovel(PTYForward *f) {
if ((f->out_buffer_full <= 0 || f->stdout_hangup) &&
(f->in_buffer_full <= 0 || f->master_hangup))
- return sd_event_exit(f->event, EXIT_SUCCESS);
+ return pty_forward_done(f, 0);
}
return 0;
@@ -418,27 +462,8 @@ int pty_forward_new(
}
PTYForward *pty_forward_free(PTYForward *f) {
-
- if (f) {
- sd_event_source_unref(f->stdin_event_source);
- sd_event_source_unref(f->stdout_event_source);
- sd_event_source_unref(f->master_event_source);
- sd_event_source_unref(f->sigwinch_event_source);
- sd_event_unref(f->event);
-
- if (f->saved_stdout)
- tcsetattr(STDOUT_FILENO, TCSANOW, &f->saved_stdout_attr);
- if (f->saved_stdin)
- tcsetattr(STDIN_FILENO, TCSANOW, &f->saved_stdin_attr);
-
- free(f);
- }
-
- /* STDIN/STDOUT should not be nonblocking normally, so let's
- * unconditionally reset it */
- fd_nonblock(STDIN_FILENO, false);
- fd_nonblock(STDOUT_FILENO, false);
-
+ pty_forward_disconnect(f);
+ free(f);
return NULL;
}
@@ -477,8 +502,21 @@ int pty_forward_set_ignore_vhangup(PTYForward *f, bool b) {
return 0;
}
-int pty_forward_get_ignore_vhangup(PTYForward *f) {
+bool pty_forward_get_ignore_vhangup(PTYForward *f) {
assert(f);
return !!(f->flags & PTY_FORWARD_IGNORE_VHANGUP);
}
+
+bool pty_forward_is_done(PTYForward *f) {
+ assert(f);
+
+ return f->done;
+}
+
+void pty_forward_set_handler(PTYForward *f, PTYForwardHandler cb, void *userdata) {
+ assert(f);
+
+ f->handler = cb;
+ f->userdata = userdata;
+}
diff --git a/src/shared/ptyfwd.h b/src/shared/ptyfwd.h
index a046eb4e5e..bd5d5fec0d 100644
--- a/src/shared/ptyfwd.h
+++ b/src/shared/ptyfwd.h
@@ -37,12 +37,18 @@ typedef enum PTYForwardFlags {
PTY_FORWARD_IGNORE_INITIAL_VHANGUP = 4,
} PTYForwardFlags;
+typedef int (*PTYForwardHandler)(PTYForward *f, int rcode, void*userdata);
+
int pty_forward_new(sd_event *event, int master, PTYForwardFlags flags, PTYForward **f);
PTYForward *pty_forward_free(PTYForward *f);
int pty_forward_get_last_char(PTYForward *f, char *ch);
int pty_forward_set_ignore_vhangup(PTYForward *f, bool ignore_vhangup);
-int pty_forward_get_ignore_vhangup(PTYForward *f);
+bool pty_forward_get_ignore_vhangup(PTYForward *f);
+
+bool pty_forward_is_done(PTYForward *f);
+
+void pty_forward_set_handler(PTYForward *f, PTYForwardHandler handler, void *userdata);
DEFINE_TRIVIAL_CLEANUP_FUNC(PTYForward*, pty_forward_free);
diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c
index 4667f508c7..6c489284d1 100644
--- a/src/shared/seccomp-util.c
+++ b/src/shared/seccomp-util.c
@@ -137,6 +137,7 @@ const SystemCallFilterSet syscall_filter_sets[] = {
"execve\0"
"exit\0"
"exit_group\0"
+ "getrlimit\0" /* make sure processes can query stack size and such */
"rt_sigreturn\0"
"sigreturn\0"
}, {
diff --git a/src/systemd/sd-bus.h b/src/systemd/sd-bus.h
index 295989cd69..c47459c9ad 100644
--- a/src/systemd/sd-bus.h
+++ b/src/systemd/sd-bus.h
@@ -147,6 +147,8 @@ int sd_bus_can_send(sd_bus *bus, char type);
int sd_bus_get_creds_mask(sd_bus *bus, uint64_t *creds_mask);
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_start(sd_bus *ret);
@@ -438,8 +440,14 @@ int sd_bus_track_remove_sender(sd_bus_track *track, sd_bus_message *m);
int sd_bus_track_add_name(sd_bus_track *track, const char *name);
int sd_bus_track_remove_name(sd_bus_track *track, const char *name);
+int sd_bus_track_set_recursive(sd_bus_track *track, int b);
+int sd_bus_track_get_recursive(sd_bus_track *track);
+
unsigned sd_bus_track_count(sd_bus_track *track);
-const char* sd_bus_track_contains(sd_bus_track *track, const char *names);
+int sd_bus_track_count_sender(sd_bus_track *track, sd_bus_message *m);
+int sd_bus_track_count_name(sd_bus_track *track, const char *name);
+
+const char* sd_bus_track_contains(sd_bus_track *track, const char *name);
const char* sd_bus_track_first(sd_bus_track *track);
const char* sd_bus_track_next(sd_bus_track *track);