diff options
author | Evgeny Vereshchagin <evvers@ya.ru> | 2016-08-26 16:17:05 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-08-26 16:17:05 +0300 |
commit | 6afe14ff5b91e62072005dd8eb75315a16989874 (patch) | |
tree | 279f9cde40fe2d0ce2ebb85abe961147b2cc6837 | |
parent | 0c582db0c6ec3330953270cf0324038cb6615617 (diff) | |
parent | 383034987de71927683dd34896ca7f3c9c259f7c (diff) |
Merge pull request #3984 from poettering/refcnt
permit bus clients to pin units to avoid automatic GC
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 @@ -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 <systemd/sd-bus.h></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 <systemd/sd-bus.h></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); |