Merge pull request #3984 from poettering/refcnt

permit bus clients to pin units to avoid automatic GC
This commit is contained in:
Evgeny Vereshchagin 2016-08-26 16:17:05 +03:00 committed by GitHub
commit 6afe14ff5b
36 changed files with 1910 additions and 395 deletions

1
.gitignore vendored
View file

@ -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

View file

@ -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 \

View file

@ -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

21
TODO
View file

@ -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

View file

@ -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>

View file

@ -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>

View file

@ -0,0 +1,261 @@
<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<!--
This file is part of systemd.
Copyright 2016 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
-->
<refentry id="sd_bus_track_add_name">
<refentryinfo>
<title>sd_bus_track_add_name</title>
<productname>systemd</productname>
<authorgroup>
<author>
<contrib>Developer</contrib>
<firstname>Lennart</firstname>
<surname>Poettering</surname>
<email>lennart@poettering.net</email>
</author>
</authorgroup>
</refentryinfo>
<refmeta>
<refentrytitle>sd_bus_track_add_name</refentrytitle>
<manvolnum>3</manvolnum>
</refmeta>
<refnamediv>
<refname>sd_bus_track_add_name</refname>
<refname>sd_bus_track_add_sender</refname>
<refname>sd_bus_track_remove_name</refname>
<refname>sd_bus_track_remove_sender</refname>
<refname>sd_bus_track_count</refname>
<refname>sd_bus_track_count_sender</refname>
<refname>sd_bus_track_count_name</refname>
<refname>sd_bus_track_contains</refname>
<refname>sd_bus_track_first</refname>
<refname>sd_bus_track_next</refname>
<refpurpose>Add, remove and retrieve bus peers tracked in a bus peer tracking object</refpurpose>
</refnamediv>
<refsynopsisdiv>
<funcsynopsis>
<funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
<funcprototype>
<funcdef>int <function>sd_bus_track_add_name</function></funcdef>
<paramdef>sd_bus_track* <parameter>t</parameter></paramdef>
<paramdef>const char* <parameter>name</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_bus_track_add_sender</function></funcdef>
<paramdef>sd_bus_track* <parameter>t</parameter></paramdef>
<paramdef>sd_bus_message* <parameter>message</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_bus_track_remove_name</function></funcdef>
<paramdef>sd_bus_track* <parameter>t</parameter></paramdef>
<paramdef>const char* <parameter>name</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_bus_track_remove_sender</function></funcdef>
<paramdef>sd_bus_track* <parameter>t</parameter></paramdef>
<paramdef>sd_bus_message* <parameter>message</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>unsigned <function>sd_bus_track_count</function></funcdef>
<paramdef>sd_bus_track* <parameter>t</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_bus_track_count_name</function></funcdef>
<paramdef>sd_bus_track* <parameter>t</parameter></paramdef>
<paramdef>const char* <parameter>name</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_bus_track_count_sender</function></funcdef>
<paramdef>sd_bus_track* <parameter>t</parameter></paramdef>
<paramdef>sd_bus_message* <parameter>message</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_bus_track_contains</function></funcdef>
<paramdef>sd_bus_track* <parameter>t</parameter></paramdef>
<paramdef>const char* <parameter>name</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>const char* <function>sd_bus_track_first</function></funcdef>
<paramdef>sd_bus_track* <parameter>t</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>const char* <function>sd_bus_track_next</function></funcdef>
<paramdef>sd_bus_track* <parameter>t</parameter></paramdef>
</funcprototype>
</funcsynopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para><function>sd_bus_track_add_name()</function> adds a peer to track to a bus peer tracking object. The first
argument should refer to a bus peer tracking object created with
<citerefentry><refentrytitle>sd_bus_track_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>, the second
name should refer to a D-Bus peer name to track, either in unique or well-known service format. If the name is not
tracked yet it will be added to the list of names to track. If it already is being tracked and non-recursive mode
is enabled, no operation is executed by this call. If recursive mode is enabled a per-name counter is increased by
one each time this call is invoked, and <function>sd_bus_track_remove_name()</function> has to be called as many
times as <function>sd_bus_track_add_name()</function> was invoked before in order to stop tracking of the name. Use
<citerefentry><refentrytitle>sd_bus_track_set_recursive</refentrytitle><manvolnum>3</manvolnum></citerefentry> to
switch from the default non-recursive mode to recursive mode, or back. Note that the specified name is tracked as
it is, well-known names are not resolved to unique names by this call. Note that multiple bus peer tracking objects
may track the same name.</para>
<para><function>sd_bus_track_remove_name()</function> undoes the effect of
<function>sd_bus_track_add_name()</function> and removes a bus peer name from the list of peers to watch. Depending
on whether non-recursive or recursive mode is enabled for the bus peer tracking object this call will either remove
the name fully from the tracking object, or will simply decrement the per-name counter by one, removing the name
only when the counter reaches zero (see above). Note that a bus peer disconnecting from the bus will implicitly
remove its names fully from the bus peer tracking object, regardless of the current per-name counter.</para>
<para><function>sd_bus_track_add_sender()</function> and <function>sd_bus_track_remove_sender()</function> are
similar to <function>sd_bus_track_add_name()</function> and <function>sd_bus_track_remove_name()</function> but
take a bus message as argument. The sender of this bus message is determined and added to/removed from the bus peer
tracking object. As messages always originate from unique names, and never from well-known names this means that
this call will effectively only add unique names to the bus peer tracking object.</para>
<para><function>sd_bus_track_count()</function> returns the number of names currently being tracked by the
specified bus peer tracking object. Note that this function always returns the actual number of names tracked, and
hence if <function>sd_bus_track_add_name()</function> has been invoked multiple times for the same name it is only
counted as one, regardless if recursive mode is used or not.</para>
<para><function>sd_bus_track_count_name()</function> returns the current per-name counter for the specified
name. If non-recursive mode is used this returns either 1 or 0, depending on whether the specified name has been
added to the tracking object before, or not. If recursive mode has been enabled, values larger than 1 may be
returned too, in case <function>sd_bus_track_add_name()</function> has been called multiple times for the same
name.</para>
<para><function>sd_bus_track_count_sender()</function> is similar to
<function>sd_bus_track_count_name()</function>, but takes a bus message object and returns the per-name counter
matching the sender of the message.</para>
<para><function>sd_bus_track_contains()</function> may be used to determine whether the specified name has been
added at least once to the specified bus peer tracking object.</para>
<para><function>sd_bus_track_first()</function> and <function>sd_bus_track_next()</function> may be used to
enumerate all names currently being tracked by the passed bus peer tracking
object. <function>sd_bus_track_first()</function> returns the first entry in the object, and resets an internally
maintained read index. Each subsequent invocation of <function>sd_bus_track_next()</function> returns the next name
contained in the bus object. If the end is reached <constant>NULL</constant> is returned. If no names have been
added to the object yet <function>sd_bus_track_first()</function> will return <constant>NULL</constant>
immediately. The order in which names are returned is undefined; in particular which name is considered the first
returned is not defined. If recursive mode is enabled and the same name has been added multiple times to the bus
peer tracking object it is only returned once by this enumeration. If new names are added to or existing names
removed from the bus peer tracking object while it is being enumerated the enumeration ends on the next invocation
of <function>sd_bus_track_next()</function> as <constant>NULL</constant> is returned.</para>
</refsect1>
<refsect1>
<title>Return Value</title>
<para>On success, <function>sd_bus_track_add_name()</function> and <function>sd_bus_track_add_sender()</function>
return 0 if the specified name has already been added to the bus peer tracking object before and positive if it
hasn't. On failure, they return a negative errno-style error code.</para>
<para><function>sd_bus_track_remove_name()</function> and <function>sd_bus_track_remove_sender()</function> return
positive if the specified name was previously tracked by the bus peer tracking object and has now been removed. In
non-recursive mode, 0 is returned if the specified name was not being tracked yet. In recursive mode
<constant>-EUNATCH</constant> is returned in this case. On failure, they return a negative errno-style error
code.</para>
<para><function>sd_bus_track_count()</function> returns the number of names currently being tracked, or 0 on
failure.</para>
<para><function>sd_bus_track_count_name()</function> and <function>sd_bus_track_count_sender()</function> return
the current per-name counter for the specified name or the sender of the specified message. Zero is returned for
names that are not being tracked yet, a positive value for names added at least once. Larger values than 1 are only
returned in recursive mode. On failure, a negative errno-style error code is returned.</para>
<para><function>sd_bus_track_contains()</function> returns the passed name if it exists in the bus peer tracking
object. On failure, and if the name has not been added yet <constant>NULL</constant> is returned.</para>
<para><function>sd_bus_track_first()</function> and <function>sd_bus_track_next()</function> return the first/next
name contained in the bus peer tracking object, and <constant>NULL</constant> if the end of the enumeration is
reached and on error.</para>
</refsect1>
<refsect1>
<title>Errors</title>
<para>Returned errors may indicate the following problems:</para>
<variablelist>
<varlistentry>
<term><constant>-EUNATCH</constant></term>
<listitem><para><function>sd_bus_track_remove_name()</function> or
<function>sd_bus_track_remove_sender()</function> have been invoked for a name not previously added to the bus
peer object.</para></listitem>
</varlistentry>
<varlistentry>
<term><constant>-EINVAL</constant></term>
<listitem><para>Specified parameter is invalid.</para></listitem>
</varlistentry>
<varlistentry>
<term><constant>-ENOMEM</constant></term>
<listitem><para>Memory allocation failed.</para></listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>Notes</title>
<para><function>sd_bus_track_add_name()</function> and the other calls described here are available as a shared library,
which can be compiled and linked to with the <constant>libsystemd</constant> <citerefentry
project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry> file.</para>
</refsect1>
<refsect1>
<title>See Also</title>
<para>
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_bus_track_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>
</para>
</refsect1>
</refentry>

263
man/sd_bus_track_new.xml Normal file
View file

@ -0,0 +1,263 @@
<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<!--
This file is part of systemd.
Copyright 2016 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
-->
<refentry id="sd_bus_track_new">
<refentryinfo>
<title>sd_bus_track_new</title>
<productname>systemd</productname>
<authorgroup>
<author>
<contrib>Developer</contrib>
<firstname>Lennart</firstname>
<surname>Poettering</surname>
<email>lennart@poettering.net</email>
</author>
</authorgroup>
</refentryinfo>
<refmeta>
<refentrytitle>sd_bus_track_new</refentrytitle>
<manvolnum>3</manvolnum>
</refmeta>
<refnamediv>
<refname>sd_bus_track_new</refname>
<refname>sd_bus_track_ref</refname>
<refname>sd_bus_track_unref</refname>
<refname>sd_bus_track_unrefp</refname>
<refname>sd_bus_track_set_recursive</refname>
<refname>sd_bus_track_get_recursive</refname>
<refname>sd_bus_track_get_bus</refname>
<refname>sd_bus_track_get_userdata</refname>
<refname>sd_bus_track_set_userdata</refname>
<refpurpose>Track bus peers</refpurpose>
</refnamediv>
<refsynopsisdiv>
<funcsynopsis>
<funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
<funcprototype>
<funcdef>int <function>sd_bus_track_new</function></funcdef>
<paramdef>sd_bus* <parameter>bus</parameter></paramdef>
<paramdef>sd_bus_track** <parameter>ret</parameter></paramdef>
<paramdef>sd_bus_track_handler_t <parameter>handler</parameter></paramdef>
<paramdef>void* <parameter>userdata</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>sd_bus_track *<function>sd_bus_track_ref</function></funcdef>
<paramdef>sd_bus_track *<parameter>t</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>sd_bus_track *<function>sd_bus_track_unref</function></funcdef>
<paramdef>sd_bus_track *<parameter>t</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>void <function>sd_bus_track_unrefp</function></funcdef>
<paramdef>sd_bus_track **<parameter>t</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_bus_track_get_recursive</function></funcdef>
<paramdef>sd_bus_track *<parameter>t</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_bus_track_set_recursive</function></funcdef>
<paramdef>sd_bus_track *<parameter>t</parameter></paramdef>
<paramdef>int <parameter>b</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>sd_bus* <function>sd_bus_track_get_bus</function></funcdef>
<paramdef>sd_bus_track *<parameter>t</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>void* <function>sd_bus_track_get_userdata</function></funcdef>
<paramdef>sd_bus_track *<parameter>t</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>void* <function>sd_bus_track_set_userdata</function></funcdef>
<paramdef>sd_bus_track *<parameter>t</parameter></paramdef>
<paramdef>void *userdata</paramdef>
</funcprototype>
</funcsynopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para><function>sd_bus_track_new()</function> creates a new bus peer tracking object. The object is allocated for
the specified bus, and returned in the <parameter>*ret</parameter> parameter. After use, the object should be freed
again by dropping the acquired reference with <function>sd_bus_track_unref()</function> (see below). A bus peer
tracking object may be used to keep track of peers on a specific IPC bus, for cases where peers are making use of
one or more local objects, in order to control the lifecycle of the local objects and ensure they stay around as
long as the peers needing them are around, and unreferenced (and possibly destroyed) as soon as all relevant peers
have vanished. Each bus peer tracking object may be used to track zero, one or more peers add a time. References to
specific bus peers are added via
<citerefentry><refentrytitle>sd_bus_track_add_name</refentrytitle><manvolnum>3</manvolnum></citerefentry> or
<function>sd_bus_track_add_sender()</function>. They may be dropped again via
<function>sd_bus_track_remove_name()</function> and
<function>sd_bus_track_remove_sender()</function>. Alternatively, references on peers are removed automatically
when they disconnect from the bus. If non-NULL the <parameter>handler</parameter> may specify a function that is
invoked whenever the last reference is dropped, regardless whether the reference is dropped explicitly via
<function>sd_bus_track_remove_name()</function> or implicitly because the peer disconnected from the bus. The final
argument <parameter>userdata</parameter> may be used to attach a generic user data pointer to the object. This
pointer is passed to the handler callback when it is invoked.</para>
<para><function>sd_bus_track_ref()</function> creates a new reference to a bus peer tracking object. This object
will not be destroyed until <function>sd_bus_track_unref()</function> has been called as many times plus once
more. Once the reference count has dropped to zero, the specified object cannot be used anymore, further calls to
<function>sd_bus_track_ref()</function> or <function>sd_bus_track_unref()</function> on the same object are
illegal.</para>
<para><function>sd_bus_track_unref()</function> destroys a reference to a bus peer tracking object.</para>
<para><function>sd_bus_track_unrefp()</function> is similar to <function>sd_bus_track_unref()</function> but takes
a pointer to a pointer to an <type>sd_bus_track</type> object. This call is useful in conjunction with GCC's and
LLVM's <ulink url="https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html">Clean-up Variable
Attribute</ulink>. Note that this function is defined as inline function.</para>
<para><function>sd_bus_track_ref()</function>, <function>sd_bus_track_unref()</function> and
<function>sd_bus_track_unrefp()</function> execute no operation if the passed in bus peer tracking object is
<constant>NULL</constant>.</para>
<para>Bus peer tracking objects may exist in two modes: by default they operate in non-recursive mode, but may
optionally be switched into recursive mode. If operating in the default non-recursive mode a peer is either tracked
or not tracked. In this mode invoking <function>sd_bus_track_add_name()</function> multiple times in a row for the
same peer is fully equivalent to calling it just once, as the call adds the peer to the set of tracked peers if
necessary, and executes no operation if the peer is already being tracked. A single invocation of
<function>sd_bus_track_remove_name()</function> removes the reference on the peer again, regardless how many times
<function>sd_bus_track_add_name()</function> was called before. If operating in recursive mode, the number of times
<function>sd_bus_track_add_name()</function> is invoked for the same peer name is counted and
<function>sd_bus_track_remove_name()</function> must be called the same number of times before the peer is not
tracked anymore, with the exception when the tracked peer vanishes from the bus, in which case the count is
irrelevant and the tracking of the specific peer is immediately
removed. <function>sd_bus_track_get_recursive()</function> may be used to determine whether the bus peer tracking
object is operating in recursive mode. <function>sd_bus_track_set_recursive()</function> may be used to enable or
disable recursive mode. By default a bus peer tracking object operates in non-recursive mode, and
<function>sd_bus_track_get_recursive()</function> for a newly allocated object hence returns a value equal to
zero. Use <function>sd_bus_track_set_recursive()</function> to enable recursive mode, right after allocation. It
takes a boolean argument to enable or disable recursive mode. Note that tracking objects for which
<function>sd_bus_track_add_name()</function> was already invoked at least once (and which hence track already one
or more peers) may not be switched from recursive to non-recursive mode anymore.</para>
<para><function>sd_bus_track_get_bus()</function> returns the bus object the bus peer tracking object belongs
to. It returns the bus object initially passed to <function>sd_bus_track_new()</function> when the object was
allocated.</para>
<para><function>sd_bus_track_get_userdata()</function> returns the generic user data pointer set on the bus peer
tracking object at the time of creation using <function>sd_bus_track_new()</function> or at a later time, using
<function>sd_bus_track_set_userdata()</function>.</para>
</refsect1>
<refsect1>
<title>Return Value</title>
<para>On success, <function>sd_bus_track_new()</function> and <function>sd_bus_track_set_recursive()</function>
return 0 or a positive integer. On failure, they return a negative errno-style error code.</para>
<para><function>sd_bus_track_ref()</function> always returns the argument.</para>
<para><function>sd_bus_track_unref()</function> always returns <constant>NULL</constant>.</para>
<para><function>sd_bus_track_get_recursive()</function> returns 0 if non-recursive mode is selected (default), and
greater than 0 if recursive mode is selected. On failure a negative errno-style error code is returned.</para>
<para><function>sd_bus_track_get_bus()</function> returns the bus object associated to the bus peer tracking
object.</para>
<para><function>sd_bus_track_get_userdata()</function> returns the generic user data pointer associated with the
bus peer tracking object. <function>sd_bus_track_set_userdata()</function> returns the previous user data pointer
set.</para>
</refsect1>
<refsect1>
<title>Reference ownership</title>
<para>The <function>sd_bus_track_new()</function> function creates a new object and the caller owns the sole
reference. When not needed anymore, this reference should be destroyed with
<function>sd_bus_track_unref()</function>.
</para>
</refsect1>
<refsect1>
<title>Errors</title>
<para>Returned errors may indicate the following problems:</para>
<variablelist>
<varlistentry>
<term><constant>-EBUSY</constant></term>
<listitem><para>Bus peers have already been added to the bus peer tracking object and
<function>sd_bus_track_set_recursive()</function> was called to change tracking mode.</para></listitem>
</varlistentry>
<varlistentry>
<term><constant>-EINVAL</constant></term>
<listitem><para>Specified parameter is invalid
(<constant>NULL</constant> in case of output
parameters).</para></listitem>
</varlistentry>
<varlistentry>
<term><constant>-ENOMEM</constant></term>
<listitem><para>Memory allocation failed.</para></listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>Notes</title>
<para><function>sd_bus_track_new()</function> and the other calls described here are available as a shared library,
which can be compiled and linked to with the <constant>libsystemd</constant> <citerefentry
project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry> file.</para>
</refsect1>
<refsect1>
<title>See Also</title>
<para>
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>
<citerefentry><refentrytitle>sd_bus_track_add_name</refentrytitle><manvolnum>3</manvolnum></citerefentry>
</para>
</refsect1>
</refentry>

View file

@ -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>

View file

@ -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><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.</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
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 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 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>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" />

View file

@ -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;

View file

@ -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),

View file

@ -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);
}

View file

@ -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);

View file

@ -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);
for (n = sd_bus_track_first(t); n; n = sd_bus_track_next(t)) {
int c, j;
c = sd_bus_track_count_name(t, n);
for (j = 0; j < c; j++) {
fputs(prefix, f);
fputc('=', f);
fputs(n, f);
fputc('\n', f);
}
}
}
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;
r = strv_extend(l, e);
if (r < 0)
return r;
return 1;
}
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 (strv_isempty(l))
return 0;
if (!*t) {
r = sd_bus_track_new(m->api_bus, t, NULL, NULL);
if (r < 0)
return r;
}
if (!m->api_bus)
return 0;
r = 0;
STRV_FOREACH(i, *l) {
int k;
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;
}

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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;
else if (startswith(l, "subscribed=")) {
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);
}
if (strv_extend(&m->deserialized_subscribed, l+11) < 0)
log_oom();
} else
log_debug("Unknown serialization item '%s'", l);
}
for (;;) {

View file

@ -182,6 +182,14 @@
send_interface="org.freedesktop.systemd1.Manager"
send_member="Reexecute"/>
<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"/>

View file

@ -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)
if (UNIT_VTABLE(u)->coldplug) {
q = UNIT_VTABLE(u)->coldplug(u);
if (q < 0 && r >= 0)
r = q;
}
if (u->job) {
q = job_coldplug(u->job);
if (q < 0 && r >= 0)
r = q;
}
if (r < 0)
return r;
if (q < 0)
return q;
return 0;
return r;
}
static bool fragment_mtime_newer(const char *path, usec_t mtime) {

View file

@ -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 {

View file

@ -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;

View file

@ -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),

View file

@ -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"

View file

@ -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))

View file

@ -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)
return r;
match = MATCH_FOR_NAME(name);
r = hashmap_put(track->names, n, slot);
if (r == -EEXIST)
return 0;
if (r < 0)
return r;
bus_track_remove_from_queue(track); /* don't dispatch this while we work in it */
/* 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);
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) {
hashmap_remove(track->names, n);
bus_track_add_to_queue(track);
return r;
}
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. */
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, 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;
}

View file

@ -20,3 +20,4 @@
***/
void bus_track_dispatch(sd_bus_track *track);
void bus_track_close(sd_bus_track *track);

View file

@ -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,6 +2641,84 @@ null_message:
return r;
}
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;
sd_bus_slot *slot;
int r;
assert(bus);
assert(c);
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;
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;
slot = container_of(c, sd_bus_slot, reply_callback);
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;
if (slot->floating) {
bus_slot_disconnect(slot);
sd_bus_slot_unref(slot);
}
sd_bus_slot_unref(slot);
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;
@ -2648,54 +2727,15 @@ static int process_closing(sd_bus *bus, sd_bus_message **ret) {
assert(bus);
assert(bus->state == BUS_CLOSING);
/* First, fail all outstanding method calls */
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;
if (c)
return process_closing_reply_callback(bus, c);
/* 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;
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;
slot = container_of(c, sd_bus_slot, reply_callback);
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;
if (slot->floating) {
bus_slot_disconnect(slot);
sd_bus_slot_unref(slot);
}
sd_bus_slot_unref(slot);
return bus_maybe_reply_error(m, r, &error_buffer);
/* 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;
}

View file

@ -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;
}

View file

@ -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);
r = sd_event_default(&event);
if (arg_wait || master >= 0) {
_cleanup_(run_context_free) RunContext c = {};
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);
(void) sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
(void) sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
if (!arg_quiet)
log_info("Press ^] three times within 1s to disconnect TTY.");
if (!arg_quiet)
log_info("Running as unit: %s\nPress ^] three times within 1s to disconnect TTY.", service);
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");
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");
pty_forward_set_handler(c.forward, pty_forward_handler, &c);
}
r = sd_event_loop(event);
if (arg_wait) {
_cleanup_free_ char *path = NULL;
const char *mt;
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_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;
}

View file

@ -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) {

View file

@ -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;
}

View file

@ -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);

View file

@ -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"
}, {

View file

@ -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);