summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--DISTRO_PORTING1
-rw-r--r--Makefile-man.am91
-rw-r--r--Makefile.am19
-rw-r--r--TODO21
-rw-r--r--hwdb/60-evdev.hwdb14
-rw-r--r--man/sd-bus.xml51
-rw-r--r--man/sd_bus_creds_new_from_pid.xml4
-rw-r--r--man/sd_bus_track_add_name.xml261
-rw-r--r--man/sd_bus_track_new.xml263
-rw-r--r--man/standard-options.xml6
-rw-r--r--man/systemctl.xml12
-rw-r--r--man/systemd-journal-remote.xml4
-rw-r--r--man/systemd-mount.xml295
-rw-r--r--man/systemd-run.xml168
-rw-r--r--man/systemd.link.xml40
-rw-r--r--man/systemd.mount.xml26
-rw-r--r--man/systemd.resource-control.xml18
-rw-r--r--po/hu.po110
-rw-r--r--src/basic/cgroup-util.c131
-rw-r--r--src/basic/cgroup-util.h12
-rw-r--r--src/basic/fileio.c5
-rw-r--r--src/basic/missing.h17
-rw-r--r--src/basic/mount-util.c1
-rw-r--r--src/basic/terminal-util.c7
-rw-r--r--src/boot/bootctl.c5
-rw-r--r--src/cgls/cgls.c2
-rw-r--r--src/cgtop/cgtop.c10
-rw-r--r--src/core/cgroup.c69
-rw-r--r--src/core/cgroup.h1
-rw-r--r--src/core/dbus-cgroup.c5
-rw-r--r--src/core/dbus-manager.c57
-rw-r--r--src/core/dbus-mount.c2
-rw-r--r--src/core/dbus-unit.c155
-rw-r--r--src/core/dbus-unit.h8
-rw-r--r--src/core/dbus.c71
-rw-r--r--src/core/dbus.h5
-rw-r--r--src/core/execute.c38
-rw-r--r--src/core/job.c17
-rw-r--r--src/core/job.h4
-rw-r--r--src/core/load-fragment-gperf.gperf.m43
-rw-r--r--src/core/load-fragment.c27
-rw-r--r--src/core/main.c6
-rw-r--r--src/core/manager.c29
-rw-r--r--src/core/mount-setup.c6
-rw-r--r--src/core/mount.c14
-rw-r--r--src/core/mount.h3
-rw-r--r--src/core/org.freedesktop.systemd1.conf8
-rw-r--r--src/core/scope.c2
-rw-r--r--src/core/selinux-access.h2
-rw-r--r--src/core/service.c2
-rw-r--r--src/core/unit.c72
-rw-r--r--src/core/unit.h10
-rw-r--r--src/import/export-raw.c1
-rw-r--r--src/journal/journald-server.c20
-rw-r--r--src/libsystemd/libsystemd.sym10
-rw-r--r--src/libsystemd/sd-bus/bus-common-errors.c1
-rw-r--r--src/libsystemd/sd-bus/bus-common-errors.h1
-rw-r--r--src/libsystemd/sd-bus/bus-internal.h4
-rw-r--r--src/libsystemd/sd-bus/bus-track.c260
-rw-r--r--src/libsystemd/sd-bus/bus-track.h1
-rw-r--r--src/libsystemd/sd-bus/sd-bus.c144
-rw-r--r--src/libsystemd/sd-bus/test-bus-creds.c2
-rw-r--r--src/libsystemd/sd-bus/test-bus-track.c113
-rw-r--r--src/machine/machinectl.c129
l---------src/mount/Makefile1
-rw-r--r--src/mount/mount-tool.c1114
-rw-r--r--src/network/networkd-link.c12
-rw-r--r--src/network/networkd-link.h2
-rw-r--r--src/network/networkd-netdev.h2
-rw-r--r--src/network/networkd-route.c35
-rw-r--r--src/nspawn/nspawn-cgroup.c13
-rw-r--r--src/nspawn/nspawn-cgroup.h6
-rw-r--r--src/nspawn/nspawn-mount.c46
-rw-r--r--src/nspawn/nspawn-mount.h6
-rw-r--r--src/nspawn/nspawn-register.c9
-rw-r--r--src/nspawn/nspawn.c70
-rw-r--r--src/run/run.c270
-rw-r--r--src/shared/bus-unit-util.c15
-rw-r--r--src/shared/bus-unit-util.h1
-rw-r--r--src/shared/bus-util.c78
-rw-r--r--src/shared/install.c35
-rw-r--r--src/shared/ptyfwd.c102
-rw-r--r--src/shared/ptyfwd.h8
-rw-r--r--src/shared/seccomp-util.c11
-rw-r--r--src/shared/seccomp-util.h2
-rw-r--r--src/systemctl/systemctl.c24
-rw-r--r--src/systemd/sd-bus.h10
-rw-r--r--src/test/test-execute.c11
-rw-r--r--src/udev/net/ethtool-util.c113
-rw-r--r--src/udev/net/ethtool-util.h11
-rw-r--r--src/udev/net/link-config-gperf.gperf43
-rw-r--r--src/udev/net/link-config.c6
-rw-r--r--src/udev/net/link-config.h1
-rw-r--r--src/udev/udevd.c16
-rw-r--r--test/test-functions1
-rw-r--r--units/systemd-udevd-control.socket1
97 files changed, 4253 insertions, 720 deletions
diff --git a/.gitignore b/.gitignore
index f7db68b4a6..565a3a3839 100644
--- a/.gitignore
+++ b/.gitignore
@@ -89,6 +89,7 @@
/systemd-machine-id-setup
/systemd-machined
/systemd-modules-load
+/systemd-mount
/systemd-networkd
/systemd-networkd-wait-online
/systemd-notify
@@ -151,6 +152,7 @@
/test-bus-policy
/test-bus-server
/test-bus-signature
+/test-bus-track
/test-bus-zero-copy
/test-calendarspec
/test-cap-list
diff --git a/DISTRO_PORTING b/DISTRO_PORTING
index 07aea865be..a397d700c6 100644
--- a/DISTRO_PORTING
+++ b/DISTRO_PORTING
@@ -15,6 +15,7 @@ HOWTO:
--with-kbd-setfont=
--with-tty-gid=
--with-ntp-servers=
+ --with-support-url=
2) Try it out. Play around (as an ordinary user) with
'/usr/lib/systemd/systemd --test --system' for a test run
diff --git a/Makefile-man.am b/Makefile-man.am
index 3ac1906a4a..ef5077cc5a 100644
--- a/Makefile-man.am
+++ b/Makefile-man.am
@@ -52,6 +52,8 @@ MANPAGES += \
man/sd_bus_path_encode.3 \
man/sd_bus_process.3 \
man/sd_bus_request_name.3 \
+ man/sd_bus_track_add_name.3 \
+ man/sd_bus_track_new.3 \
man/sd_event_add_child.3 \
man/sd_event_add_defer.3 \
man/sd_event_add_io.3 \
@@ -121,6 +123,7 @@ MANPAGES += \
man/systemd-journald.service.8 \
man/systemd-machine-id-commit.service.8 \
man/systemd-machine-id-setup.1 \
+ man/systemd-mount.1 \
man/systemd-notify.1 \
man/systemd-nspawn.1 \
man/systemd-path.1 \
@@ -331,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 \
@@ -664,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
@@ -1299,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)
@@ -2558,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 \
@@ -2651,6 +2741,7 @@ EXTRA_DIST += \
man/systemd-machine-id-setup.xml \
man/systemd-machined.service.xml \
man/systemd-modules-load.service.xml \
+ man/systemd-mount.xml \
man/systemd-networkd-wait-online.service.xml \
man/systemd-networkd.service.xml \
man/systemd-notify.xml \
diff --git a/Makefile.am b/Makefile.am
index a1bb21e4fc..3a617560e0 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -384,6 +384,7 @@ bin_PROGRAMS = \
systemd-delta \
systemd-analyze \
systemd-run \
+ systemd-mount \
systemd-stdio-bridge \
systemd-path
@@ -3146,6 +3147,13 @@ systemd_run_LDADD = \
libsystemd-shared.la
# ------------------------------------------------------------------------------
+systemd_mount_SOURCES = \
+ src/mount/mount-tool.c
+
+systemd_mount_LDADD = \
+ libsystemd-shared.la
+
+# ------------------------------------------------------------------------------
systemd_stdio_bridge_SOURCES = \
src/stdio-bridge/stdio-bridge.c
@@ -3315,6 +3323,7 @@ tests += \
test-bus-error \
test-bus-creds \
test-bus-gvariant \
+ test-bus-track \
test-event \
test-netlink \
test-local-addresses \
@@ -3358,6 +3367,16 @@ test_bus_cleanup_CFLAGS = \
test_bus_cleanup_LDADD = \
libsystemd-shared.la
+test_bus_track_SOURCES = \
+ src/libsystemd/sd-bus/test-bus-track.c
+
+test_bus_track_CFLAGS = \
+ $(AM_CFLAGS) \
+ $(SECCOMP_CFLAGS)
+
+test_bus_track_LDADD = \
+ libsystemd-shared.la
+
test_bus_server_SOURCES = \
src/libsystemd/sd-bus/test-bus-server.c
diff --git a/TODO b/TODO
index 33afe39783..0ce9a1fd30 100644
--- a/TODO
+++ b/TODO
@@ -33,7 +33,13 @@ Janitorial Clean-ups:
Features:
-* RemoveIPC= in unit files for removing POSIX/SysV IPC objects
+* introduce an "invocation ID" for units, that is randomly generated, and
+ identifies each runtime-cycle of a unit. It should be set freshly each time
+ we traverse inactive → activating/active, and should be the primary key to
+ map offline data (stored in the journal) with online bus objects. Let's pass
+ this as $SYSTEMD_INVOCATION_ID to services, as well as set this as xattr on
+ the cgroup of a services. The former is accessible without privileges, the
+ latter ensures the ID cannot be faked.
* Introduce ProtectSystem=strict for making the entire OS hierarchy read-only
except for a select few
@@ -58,6 +64,8 @@ Features:
* ProtectControlGroups= which mounts all of /sys/fs/cgroup read-only
+* ProtectKernelTunables= which mounts /sys and /proc/sys read-only
+
* RemoveKeyRing= to remove all keyring entries of the specified user
* Add DataDirectory=, CacheDirectory= and LogDirectory= to match
@@ -76,6 +84,9 @@ Features:
* journalctl: make sure -f ends when the container indicated by -M terminates
+* mount: automatically search for "main" partition of an image has multiple
+ partitions
+
* expose the "privileged" flag of ExecCommand on the bus, and open it up to
transient units
@@ -86,6 +97,12 @@ Features:
* allow attaching additional journald log fields to cgroups
+* add bus API for creating unit files in /etc, reusing the code for transient units
+
+* add bus API to remove unit files from /etc
+
+* add bus API to retrieve current unit file contents (i.e. implement "systemctl cat" on the bus only)
+
* rework fopen_temporary() to make use of open_tmpfile_linkable() (problem: the
kernel doesn't support linkat() that replaces existing files, currently)
@@ -112,8 +129,6 @@ Features:
* add systemctl stop --job-mode=triggering that follows TRIGGERED_BY deps and adds them to the same transaction
-* Maybe add a way how users can "pin" units into memory, so that they are not subject to automatic GC?
-
* PID1: find a way how we can reload unit file configuration for
specific units only, without reloading the whole of systemd
diff --git a/hwdb/60-evdev.hwdb b/hwdb/60-evdev.hwdb
index e738ff7be5..4bed8d026d 100644
--- a/hwdb/60-evdev.hwdb
+++ b/hwdb/60-evdev.hwdb
@@ -45,6 +45,8 @@
# Macbook2,1 (late 2006), single-button touchpad
evdev:input:b0003v05ACp021B*
+# Macbook4,1
+evdev:input:b0003v05ACp0229*
EVDEV_ABS_00=256:1471:12
EVDEV_ABS_01=256:831:12
@@ -155,6 +157,11 @@ evdev:name:SynPS/2 Synaptics TouchPad:dmi:bvn*:bvr*:bd*:svnDellInc.:pnXPS159550*
EVDEV_ABS_35=::41
EVDEV_ABS_36=::43
+# Dell XPS M1530
+evdev:name:AlpsPS/2 ALPS GlidePoint:dmi:bvn*:bvr*:bd*:svnDellInc.:pnXPSM1530*
+ EVDEV_ABS_00=85:947:15
+ EVDEV_ABS_01=154:726:18
+
#########################################
# Google
#########################################
@@ -233,6 +240,13 @@ evdev:name:AlpsPS/2 ALPS GlidePoint:dmi:*svnLENOVO:*pvrLenovoideapadY700-14ISK*
EVDEV_ABS_35=::27
EVDEV_ABS_36=::29
+# Lenovo Ideapad 500S-13ISK
+evdev:name:AlpsPS/2 ALPS GlidePoint:dmi:*svnLENOVO:*pvrLenovoideapad500S-13ISK*
+ EVDEV_ABS_00=125:3955:37
+ EVDEV_ABS_01=104:1959:27
+ EVDEV_ABS_35=125:3954:37
+ EVDEV_ABS_36=104:1959:27
+
#########################################
# Samsung
#########################################
diff --git a/man/sd-bus.xml b/man/sd-bus.xml
index 336dd33ea0..66b1c96c15 100644
--- a/man/sd-bus.xml
+++ b/man/sd-bus.xml
@@ -44,7 +44,7 @@
<refnamediv>
<refname>sd-bus</refname>
- <refpurpose>A lightweight D-Bus and kdbus client library</refpurpose>
+ <refpurpose>A lightweight D-Bus IPC client library</refpurpose>
</refnamediv>
<refsynopsisdiv>
@@ -61,49 +61,40 @@
<refsect1>
<title>Description</title>
- <para><filename>sd-bus.h</filename> provides an implementation
- of a D-Bus client. It can interoperate both with the traditional
- <citerefentry project='man-pages'><refentrytitle>dbus-daemon</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
- and with kdbus. See
+ <para><filename>sd-bus.h</filename> provides an implementation of a D-Bus IPC client. See
<ulink url="http://www.freedesktop.org/software/dbus/" />
- for more information about the big picture.
+ for more information about D-Bus IPC.
</para>
- <important>
- <para>Interfaces described here have not been declared stable yet,
- and are not accessible from <filename>libsystemd.so</filename>.
- This documentation is provided in hope it might be useful for
- developers, without any guarantees of availability or stability.
- </para>
- </important>
-
<para>See
+ <citerefentry><refentrytitle>sd-bus-errors</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_creds_get_pid</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_creds_new_from_pid</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_bus_default</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_request_name</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_start</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_error</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_error_add_map</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_get_name_creds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_get_owner_creds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_bus_message_append</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_message_append_basic</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_bus_message_append_array</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_message_append_basic</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_bus_message_append_string_memfd</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_bus_message_append_strv</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_bus_message_can_send</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_bus_message_get_cookie</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_bus_message_get_monotonic_usec</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_negotiate_fds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_path_encode</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_request_name</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_bus_send</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_bus_set_address</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_set_allow_interactive_authorization</refentrytitle><manvolnum>3</manvolnum></citerefentry>
<citerefentry><refentrytitle>sd_bus_set_description</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_bus_set_prepare</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_creds_get_pid</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_creds_new_from_pid</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_get_name_creds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_get_owner_creds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_negotiate_fds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_path_encode</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd-bus-errors</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_error</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_error_add_map</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_bus_set_allow_interactive_authorization</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ <citerefentry><refentrytitle>sd_bus_start</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_track_add_name</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_track_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
for more information about the functions available.</para>
</refsect1>
@@ -114,9 +105,9 @@
<para>
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>busctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry project='man-pages'><refentrytitle>dbus-daemon</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
- <citerefentry project='man-pages'><refentrytitle>dbus-send</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
- <ulink url="https://developer.gnome.org/gio/stable/gdbus.html">gdbus</ulink>
+ <citerefentry project='man-pages'><refentrytitle>dbus-send</refentrytitle><manvolnum>1</manvolnum></citerefentry>
</para>
</refsect1>
diff --git a/man/sd_bus_creds_new_from_pid.xml b/man/sd_bus_creds_new_from_pid.xml
index 082f7b67db..b4d7d61d0f 100644
--- a/man/sd_bus_creds_new_from_pid.xml
+++ b/man/sd_bus_creds_new_from_pid.xml
@@ -66,12 +66,12 @@
<funcprototype>
<funcdef>uint64_t <function>sd_bus_creds_get_mask</function></funcdef>
- <paramdef>const sd_bus_creds *<parameter>c</parameter></paramdef>
+ <paramdef>sd_bus_creds *<parameter>c</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>uint64_t <function>sd_bus_creds_get_augmented_mask</function></funcdef>
- <paramdef>const sd_bus_creds *<parameter>c</parameter></paramdef>
+ <paramdef>sd_bus_creds *<parameter>c</parameter></paramdef>
</funcprototype>
<funcprototype>
diff --git a/man/sd_bus_track_add_name.xml b/man/sd_bus_track_add_name.xml
new file mode 100644
index 0000000000..6a5e344cb1
--- /dev/null
+++ b/man/sd_bus_track_add_name.xml
@@ -0,0 +1,261 @@
+<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<!--
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+-->
+
+<refentry id="sd_bus_track_add_name">
+
+ <refentryinfo>
+ <title>sd_bus_track_add_name</title>
+ <productname>systemd</productname>
+
+ <authorgroup>
+ <author>
+ <contrib>Developer</contrib>
+ <firstname>Lennart</firstname>
+ <surname>Poettering</surname>
+ <email>lennart@poettering.net</email>
+ </author>
+ </authorgroup>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>sd_bus_track_add_name</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>sd_bus_track_add_name</refname>
+ <refname>sd_bus_track_add_sender</refname>
+ <refname>sd_bus_track_remove_name</refname>
+ <refname>sd_bus_track_remove_sender</refname>
+ <refname>sd_bus_track_count</refname>
+ <refname>sd_bus_track_count_sender</refname>
+ <refname>sd_bus_track_count_name</refname>
+ <refname>sd_bus_track_contains</refname>
+ <refname>sd_bus_track_first</refname>
+ <refname>sd_bus_track_next</refname>
+
+ <refpurpose>Add, remove and retrieve bus peers tracked in a bus peer tracking object</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_track_add_name</function></funcdef>
+ <paramdef>sd_bus_track* <parameter>t</parameter></paramdef>
+ <paramdef>const char* <parameter>name</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_track_add_sender</function></funcdef>
+ <paramdef>sd_bus_track* <parameter>t</parameter></paramdef>
+ <paramdef>sd_bus_message* <parameter>message</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_track_remove_name</function></funcdef>
+ <paramdef>sd_bus_track* <parameter>t</parameter></paramdef>
+ <paramdef>const char* <parameter>name</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_track_remove_sender</function></funcdef>
+ <paramdef>sd_bus_track* <parameter>t</parameter></paramdef>
+ <paramdef>sd_bus_message* <parameter>message</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>unsigned <function>sd_bus_track_count</function></funcdef>
+ <paramdef>sd_bus_track* <parameter>t</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_track_count_name</function></funcdef>
+ <paramdef>sd_bus_track* <parameter>t</parameter></paramdef>
+ <paramdef>const char* <parameter>name</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_track_count_sender</function></funcdef>
+ <paramdef>sd_bus_track* <parameter>t</parameter></paramdef>
+ <paramdef>sd_bus_message* <parameter>message</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_track_contains</function></funcdef>
+ <paramdef>sd_bus_track* <parameter>t</parameter></paramdef>
+ <paramdef>const char* <parameter>name</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>const char* <function>sd_bus_track_first</function></funcdef>
+ <paramdef>sd_bus_track* <parameter>t</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>const char* <function>sd_bus_track_next</function></funcdef>
+ <paramdef>sd_bus_track* <parameter>t</parameter></paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><function>sd_bus_track_add_name()</function> adds a peer to track to a bus peer tracking object. The first
+ argument should refer to a bus peer tracking object created with
+ <citerefentry><refentrytitle>sd_bus_track_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>, the second
+ name should refer to a D-Bus peer name to track, either in unique or well-known service format. If the name is not
+ tracked yet it will be added to the list of names to track. If it already is being tracked and non-recursive mode
+ is enabled, no operation is executed by this call. If recursive mode is enabled a per-name counter is increased by
+ one each time this call is invoked, and <function>sd_bus_track_remove_name()</function> has to be called as many
+ times as <function>sd_bus_track_add_name()</function> was invoked before in order to stop tracking of the name. Use
+ <citerefentry><refentrytitle>sd_bus_track_set_recursive</refentrytitle><manvolnum>3</manvolnum></citerefentry> to
+ switch from the default non-recursive mode to recursive mode, or back. Note that the specified name is tracked as
+ it is, well-known names are not resolved to unique names by this call. Note that multiple bus peer tracking objects
+ may track the same name.</para>
+
+ <para><function>sd_bus_track_remove_name()</function> undoes the effect of
+ <function>sd_bus_track_add_name()</function> and removes a bus peer name from the list of peers to watch. Depending
+ on whether non-recursive or recursive mode is enabled for the bus peer tracking object this call will either remove
+ the name fully from the tracking object, or will simply decrement the per-name counter by one, removing the name
+ only when the counter reaches zero (see above). Note that a bus peer disconnecting from the bus will implicitly
+ remove its names fully from the bus peer tracking object, regardless of the current per-name counter.</para>
+
+ <para><function>sd_bus_track_add_sender()</function> and <function>sd_bus_track_remove_sender()</function> are
+ similar to <function>sd_bus_track_add_name()</function> and <function>sd_bus_track_remove_name()</function> but
+ take a bus message as argument. The sender of this bus message is determined and added to/removed from the bus peer
+ tracking object. As messages always originate from unique names, and never from well-known names this means that
+ this call will effectively only add unique names to the bus peer tracking object.</para>
+
+ <para><function>sd_bus_track_count()</function> returns the number of names currently being tracked by the
+ specified bus peer tracking object. Note that this function always returns the actual number of names tracked, and
+ hence if <function>sd_bus_track_add_name()</function> has been invoked multiple times for the same name it is only
+ counted as one, regardless if recursive mode is used or not.</para>
+
+ <para><function>sd_bus_track_count_name()</function> returns the current per-name counter for the specified
+ name. If non-recursive mode is used this returns either 1 or 0, depending on whether the specified name has been
+ added to the tracking object before, or not. If recursive mode has been enabled, values larger than 1 may be
+ returned too, in case <function>sd_bus_track_add_name()</function> has been called multiple times for the same
+ name.</para>
+
+ <para><function>sd_bus_track_count_sender()</function> is similar to
+ <function>sd_bus_track_count_name()</function>, but takes a bus message object and returns the per-name counter
+ matching the sender of the message.</para>
+
+ <para><function>sd_bus_track_contains()</function> may be used to determine whether the specified name has been
+ added at least once to the specified bus peer tracking object.</para>
+
+ <para><function>sd_bus_track_first()</function> and <function>sd_bus_track_next()</function> may be used to
+ enumerate all names currently being tracked by the passed bus peer tracking
+ object. <function>sd_bus_track_first()</function> returns the first entry in the object, and resets an internally
+ maintained read index. Each subsequent invocation of <function>sd_bus_track_next()</function> returns the next name
+ contained in the bus object. If the end is reached <constant>NULL</constant> is returned. If no names have been
+ added to the object yet <function>sd_bus_track_first()</function> will return <constant>NULL</constant>
+ immediately. The order in which names are returned is undefined; in particular which name is considered the first
+ returned is not defined. If recursive mode is enabled and the same name has been added multiple times to the bus
+ peer tracking object it is only returned once by this enumeration. If new names are added to or existing names
+ removed from the bus peer tracking object while it is being enumerated the enumeration ends on the next invocation
+ of <function>sd_bus_track_next()</function> as <constant>NULL</constant> is returned.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+
+ <para>On success, <function>sd_bus_track_add_name()</function> and <function>sd_bus_track_add_sender()</function>
+ return 0 if the specified name has already been added to the bus peer tracking object before and positive if it
+ hasn't. On failure, they return a negative errno-style error code.</para>
+
+ <para><function>sd_bus_track_remove_name()</function> and <function>sd_bus_track_remove_sender()</function> return
+ positive if the specified name was previously tracked by the bus peer tracking object and has now been removed. In
+ non-recursive mode, 0 is returned if the specified name was not being tracked yet. In recursive mode
+ <constant>-EUNATCH</constant> is returned in this case. On failure, they return a negative errno-style error
+ code.</para>
+
+ <para><function>sd_bus_track_count()</function> returns the number of names currently being tracked, or 0 on
+ failure.</para>
+
+ <para><function>sd_bus_track_count_name()</function> and <function>sd_bus_track_count_sender()</function> return
+ the current per-name counter for the specified name or the sender of the specified message. Zero is returned for
+ names that are not being tracked yet, a positive value for names added at least once. Larger values than 1 are only
+ returned in recursive mode. On failure, a negative errno-style error code is returned.</para>
+
+ <para><function>sd_bus_track_contains()</function> returns the passed name if it exists in the bus peer tracking
+ object. On failure, and if the name has not been added yet <constant>NULL</constant> is returned.</para>
+
+ <para><function>sd_bus_track_first()</function> and <function>sd_bus_track_next()</function> return the first/next
+ name contained in the bus peer tracking object, and <constant>NULL</constant> if the end of the enumeration is
+ reached and on error.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Errors</title>
+
+ <para>Returned errors may indicate the following problems:</para>
+
+ <variablelist>
+
+ <varlistentry>
+ <term><constant>-EUNATCH</constant></term>
+
+ <listitem><para><function>sd_bus_track_remove_name()</function> or
+ <function>sd_bus_track_remove_sender()</function> have been invoked for a name not previously added to the bus
+ peer object.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-EINVAL</constant></term>
+
+ <listitem><para>Specified parameter is invalid.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-ENOMEM</constant></term>
+
+ <listitem><para>Memory allocation failed.</para></listitem>
+ </varlistentry>
+
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Notes</title>
+
+ <para><function>sd_bus_track_add_name()</function> and the other calls described here are available as a shared library,
+ which can be compiled and linked to with the <constant>libsystemd</constant> <citerefentry
+ project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry> file.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_track_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
diff --git a/man/sd_bus_track_new.xml b/man/sd_bus_track_new.xml
new file mode 100644
index 0000000000..60e2e77f75
--- /dev/null
+++ b/man/sd_bus_track_new.xml
@@ -0,0 +1,263 @@
+<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<!--
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+-->
+
+<refentry id="sd_bus_track_new">
+
+ <refentryinfo>
+ <title>sd_bus_track_new</title>
+ <productname>systemd</productname>
+
+ <authorgroup>
+ <author>
+ <contrib>Developer</contrib>
+ <firstname>Lennart</firstname>
+ <surname>Poettering</surname>
+ <email>lennart@poettering.net</email>
+ </author>
+ </authorgroup>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>sd_bus_track_new</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>sd_bus_track_new</refname>
+ <refname>sd_bus_track_ref</refname>
+ <refname>sd_bus_track_unref</refname>
+ <refname>sd_bus_track_unrefp</refname>
+ <refname>sd_bus_track_set_recursive</refname>
+ <refname>sd_bus_track_get_recursive</refname>
+ <refname>sd_bus_track_get_bus</refname>
+ <refname>sd_bus_track_get_userdata</refname>
+ <refname>sd_bus_track_set_userdata</refname>
+
+ <refpurpose>Track bus peers</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_track_new</function></funcdef>
+ <paramdef>sd_bus* <parameter>bus</parameter></paramdef>
+ <paramdef>sd_bus_track** <parameter>ret</parameter></paramdef>
+ <paramdef>sd_bus_track_handler_t <parameter>handler</parameter></paramdef>
+ <paramdef>void* <parameter>userdata</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>sd_bus_track *<function>sd_bus_track_ref</function></funcdef>
+ <paramdef>sd_bus_track *<parameter>t</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>sd_bus_track *<function>sd_bus_track_unref</function></funcdef>
+ <paramdef>sd_bus_track *<parameter>t</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>sd_bus_track_unrefp</function></funcdef>
+ <paramdef>sd_bus_track **<parameter>t</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_track_get_recursive</function></funcdef>
+ <paramdef>sd_bus_track *<parameter>t</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_track_set_recursive</function></funcdef>
+ <paramdef>sd_bus_track *<parameter>t</parameter></paramdef>
+ <paramdef>int <parameter>b</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>sd_bus* <function>sd_bus_track_get_bus</function></funcdef>
+ <paramdef>sd_bus_track *<parameter>t</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void* <function>sd_bus_track_get_userdata</function></funcdef>
+ <paramdef>sd_bus_track *<parameter>t</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void* <function>sd_bus_track_set_userdata</function></funcdef>
+ <paramdef>sd_bus_track *<parameter>t</parameter></paramdef>
+ <paramdef>void *userdata</paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><function>sd_bus_track_new()</function> creates a new bus peer tracking object. The object is allocated for
+ the specified bus, and returned in the <parameter>*ret</parameter> parameter. After use, the object should be freed
+ again by dropping the acquired reference with <function>sd_bus_track_unref()</function> (see below). A bus peer
+ tracking object may be used to keep track of peers on a specific IPC bus, for cases where peers are making use of
+ one or more local objects, in order to control the lifecycle of the local objects and ensure they stay around as
+ long as the peers needing them are around, and unreferenced (and possibly destroyed) as soon as all relevant peers
+ have vanished. Each bus peer tracking object may be used to track zero, one or more peers add a time. References to
+ specific bus peers are added via
+ <citerefentry><refentrytitle>sd_bus_track_add_name</refentrytitle><manvolnum>3</manvolnum></citerefentry> or
+ <function>sd_bus_track_add_sender()</function>. They may be dropped again via
+ <function>sd_bus_track_remove_name()</function> and
+ <function>sd_bus_track_remove_sender()</function>. Alternatively, references on peers are removed automatically
+ when they disconnect from the bus. If non-NULL the <parameter>handler</parameter> may specify a function that is
+ invoked whenever the last reference is dropped, regardless whether the reference is dropped explicitly via
+ <function>sd_bus_track_remove_name()</function> or implicitly because the peer disconnected from the bus. The final
+ argument <parameter>userdata</parameter> may be used to attach a generic user data pointer to the object. This
+ pointer is passed to the handler callback when it is invoked.</para>
+
+ <para><function>sd_bus_track_ref()</function> creates a new reference to a bus peer tracking object. This object
+ will not be destroyed until <function>sd_bus_track_unref()</function> has been called as many times plus once
+ more. Once the reference count has dropped to zero, the specified object cannot be used anymore, further calls to
+ <function>sd_bus_track_ref()</function> or <function>sd_bus_track_unref()</function> on the same object are
+ illegal.</para>
+
+ <para><function>sd_bus_track_unref()</function> destroys a reference to a bus peer tracking object.</para>
+
+ <para><function>sd_bus_track_unrefp()</function> is similar to <function>sd_bus_track_unref()</function> but takes
+ a pointer to a pointer to an <type>sd_bus_track</type> object. This call is useful in conjunction with GCC's and
+ LLVM's <ulink url="https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html">Clean-up Variable
+ Attribute</ulink>. Note that this function is defined as inline function.</para>
+
+ <para><function>sd_bus_track_ref()</function>, <function>sd_bus_track_unref()</function> and
+ <function>sd_bus_track_unrefp()</function> execute no operation if the passed in bus peer tracking object is
+ <constant>NULL</constant>.</para>
+
+ <para>Bus peer tracking objects may exist in two modes: by default they operate in non-recursive mode, but may
+ optionally be switched into recursive mode. If operating in the default non-recursive mode a peer is either tracked
+ or not tracked. In this mode invoking <function>sd_bus_track_add_name()</function> multiple times in a row for the
+ same peer is fully equivalent to calling it just once, as the call adds the peer to the set of tracked peers if
+ necessary, and executes no operation if the peer is already being tracked. A single invocation of
+ <function>sd_bus_track_remove_name()</function> removes the reference on the peer again, regardless how many times
+ <function>sd_bus_track_add_name()</function> was called before. If operating in recursive mode, the number of times
+ <function>sd_bus_track_add_name()</function> is invoked for the same peer name is counted and
+ <function>sd_bus_track_remove_name()</function> must be called the same number of times before the peer is not
+ tracked anymore, with the exception when the tracked peer vanishes from the bus, in which case the count is
+ irrelevant and the tracking of the specific peer is immediately
+ removed. <function>sd_bus_track_get_recursive()</function> may be used to determine whether the bus peer tracking
+ object is operating in recursive mode. <function>sd_bus_track_set_recursive()</function> may be used to enable or
+ disable recursive mode. By default a bus peer tracking object operates in non-recursive mode, and
+ <function>sd_bus_track_get_recursive()</function> for a newly allocated object hence returns a value equal to
+ zero. Use <function>sd_bus_track_set_recursive()</function> to enable recursive mode, right after allocation. It
+ takes a boolean argument to enable or disable recursive mode. Note that tracking objects for which
+ <function>sd_bus_track_add_name()</function> was already invoked at least once (and which hence track already one
+ or more peers) may not be switched from recursive to non-recursive mode anymore.</para>
+
+ <para><function>sd_bus_track_get_bus()</function> returns the bus object the bus peer tracking object belongs
+ to. It returns the bus object initially passed to <function>sd_bus_track_new()</function> when the object was
+ allocated.</para>
+
+ <para><function>sd_bus_track_get_userdata()</function> returns the generic user data pointer set on the bus peer
+ tracking object at the time of creation using <function>sd_bus_track_new()</function> or at a later time, using
+ <function>sd_bus_track_set_userdata()</function>.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+
+ <para>On success, <function>sd_bus_track_new()</function> and <function>sd_bus_track_set_recursive()</function>
+ return 0 or a positive integer. On failure, they return a negative errno-style error code.</para>
+
+ <para><function>sd_bus_track_ref()</function> always returns the argument.</para>
+
+ <para><function>sd_bus_track_unref()</function> always returns <constant>NULL</constant>.</para>
+
+ <para><function>sd_bus_track_get_recursive()</function> returns 0 if non-recursive mode is selected (default), and
+ greater than 0 if recursive mode is selected. On failure a negative errno-style error code is returned.</para>
+
+ <para><function>sd_bus_track_get_bus()</function> returns the bus object associated to the bus peer tracking
+ object.</para>
+
+ <para><function>sd_bus_track_get_userdata()</function> returns the generic user data pointer associated with the
+ bus peer tracking object. <function>sd_bus_track_set_userdata()</function> returns the previous user data pointer
+ set.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Reference ownership</title>
+
+ <para>The <function>sd_bus_track_new()</function> function creates a new object and the caller owns the sole
+ reference. When not needed anymore, this reference should be destroyed with
+ <function>sd_bus_track_unref()</function>.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Errors</title>
+
+ <para>Returned errors may indicate the following problems:</para>
+
+ <variablelist>
+
+ <varlistentry>
+ <term><constant>-EBUSY</constant></term>
+
+ <listitem><para>Bus peers have already been added to the bus peer tracking object and
+ <function>sd_bus_track_set_recursive()</function> was called to change tracking mode.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-EINVAL</constant></term>
+
+ <listitem><para>Specified parameter is invalid
+ (<constant>NULL</constant> in case of output
+ parameters).</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-ENOMEM</constant></term>
+
+ <listitem><para>Memory allocation failed.</para></listitem>
+ </varlistentry>
+
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Notes</title>
+
+ <para><function>sd_bus_track_new()</function> and the other calls described here are available as a shared library,
+ which can be compiled and linked to with the <constant>libsystemd</constant> <citerefentry
+ project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry> file.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ <citerefentry><refentrytitle>sd_bus_track_add_name</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
diff --git a/man/standard-options.xml b/man/standard-options.xml
index f214463392..f718451a1b 100644
--- a/man/standard-options.xml
+++ b/man/standard-options.xml
@@ -28,6 +28,12 @@
</listitem>
</varlistentry>
+ <varlistentry id='no-ask-password'>
+ <term><option>--no-ask-password</option></term>
+
+ <listitem><para>Do not query the user for authentication for privileged operations.</para></listitem>
+ </varlistentry>
+
<varlistentry id='no-legend'>
<term><option>--no-legend</option></term>
diff --git a/man/systemctl.xml b/man/systemctl.xml
index fde4f4f3bb..7e0ac9613a 100644
--- a/man/systemctl.xml
+++ b/man/systemctl.xml
@@ -642,13 +642,13 @@
<term><command>list-units <optional><replaceable>PATTERN</replaceable>...</optional></command></term>
<listitem>
- <para>List units that <command>systemd</command> has loaded. This includes units that
- are either referenced directly or through a dependency, or units that were active in the
- past and have failed. By default only units which are active, have pending jobs, or have
+ <para>List units that <command>systemd</command> has loaded. This includes units that are either referenced
+ directly or through a dependency, units that are pinned by applications programmatically, or units that
+ were active in the past and have failed. By default only units which are active, have pending jobs, or have
failed are shown; this can be changed with option <option>--all</option>. If one or more
- <replaceable>PATTERN</replaceable>s are specified, only units matching one of them are
- shown. The units that are shown are additionally filtered by <option>--type=</option>
- and <option>--state=</option> if those options are specified.</para>
+ <replaceable>PATTERN</replaceable>s are specified, only units matching one of them are shown. The units
+ that are shown are additionally filtered by <option>--type=</option> and <option>--state=</option> if those
+ options are specified.</para>
<para>This is the default command.</para>
</listitem>
diff --git a/man/systemd-journal-remote.xml b/man/systemd-journal-remote.xml
index 3899f175d4..f208f8deb4 100644
--- a/man/systemd-journal-remote.xml
+++ b/man/systemd-journal-remote.xml
@@ -121,8 +121,8 @@
<replaceable>ADDRESS</replaceable>. This URL should refer to the
root of a remote
<citerefentry><refentrytitle>systemd-journal-gatewayd</refentrytitle><manvolnum>8</manvolnum></citerefentry>
- instance (e.g. <ulink>http://some.host:19531/</ulink> or
- <ulink>https://some.host:19531/</ulink>).</para></listitem>
+ instance, e.g. http://some.host:19531/ or
+ https://some.host:19531/.</para></listitem>
</varlistentry>
</variablelist>
diff --git a/man/systemd-mount.xml b/man/systemd-mount.xml
new file mode 100644
index 0000000000..e6c07863c0
--- /dev/null
+++ b/man/systemd-mount.xml
@@ -0,0 +1,295 @@
+<?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="systemd-mount"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+ <refentryinfo>
+ <title>systemd-mount</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>systemd-mount</refentrytitle>
+ <manvolnum>1</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>systemd-mount</refname>
+ <refpurpose>Establish a mount or auto-mount point transiently</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>systemd-mount</command>
+ <arg choice="opt" rep="repeat"><replaceable>OPTIONS</replaceable></arg>
+ <arg choice="plain"><replaceable>WHAT</replaceable></arg>
+ <arg choice="opt"><replaceable>WHERE</replaceable></arg>
+ </cmdsynopsis>
+ <cmdsynopsis>
+ <command>systemd-mount</command>
+ <arg choice="opt" rep="repeat"><replaceable>OPTIONS</replaceable></arg>
+ <arg choice="plain"><option>--list</option></arg>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><command>systemd-mount</command> may be used to create and start a transient <filename>.mount</filename> or
+ <filename>.automount</filename> unit of the file system <replaceable>WHAT</replaceable> on the mount point
+ <replaceable>WHERE</replaceable>.</para>
+
+ <para>In many ways, <command>systemd-mount</command> is similar to the lower-level
+ <citerefentry><refentrytitle>mount</refentrytitle><manvolnum>8</manvolnum></citerefentry> command, however instead
+ of executing the mount operation directly and immediately, <command>systemd-mount</command> schedules it through
+ the service manager job queue, so that it may pull in further dependencies (such as parent mounts, or a file system
+ checker to execute a priori), and may make use of the auto-mounting logic.</para>
+
+ <para>The command takes either one or two arguments. If only one argument is specified it should refer to a block
+ device containing a file system (e.g. <literal>/dev/sdb1</literal>), which is then probed for a label and other
+ metadata, and is mounted to a directory whose name is generated from the label. In this mode the block device must
+ exist at the time of invocation of the command, so that it may be probed. If the device is found to be a removable
+ block device (e.g. a USB stick) an automount point instead of a regular mount point is created (i.e. the
+ <option>--automount=</option> option is implied, see below).</para>
+
+ <para>If two arguments are specified the first indicates the mount source (the <replaceable>WHAT</replaceable>) and
+ the second indicates the path to mount it on (the <replaceable>WHERE</replaceable>). In this mode no probing of the
+ source is attempted, and a backing device node doesn't have to exist yet. However, if this mode is combined with
+ <option>--discover</option>, device node probing for additional metadata is enabled, and – much like in the
+ single-argument case discussed above – the specified device has to exist at the time of invocation of the
+ command.</para>
+
+ <para>Use the <option>--list</option> command to show a terse table of all local, known block devices with file
+ systems that may be mounted with this command.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Options</title>
+
+ <para>The following options are understood:</para>
+
+ <variablelist>
+
+ <varlistentry>
+ <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-mount</command> will wait until the mount or automount unit's
+ start-up is completed. By passing this argument, it is only verified and enqueued.</para>
+ </listitem>
+ </varlistentry>
+
+ <xi:include href="standard-options.xml" xpointer="no-pager"/>
+ <xi:include href="standard-options.xml" xpointer="no-ask-password"/>
+
+ <varlistentry>
+ <term><option>--quiet</option></term>
+ <term><option>-q</option></term>
+
+ <listitem><para>Suppresses additional informational output while running.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--discover</option></term>
+
+ <listitem><para>Enable probing of the mount source. This switch is implied if a single argument is specified on
+ the command line. If passed, additional metadata is read from the device to enhance the unit to create. For
+ example, a descriptive string for the transient units is generated from the file system label and device
+ model. Moreover if a removable block device (e.g. USB stick) is detected an automount unit instead of a regular
+ mount unit is created, with a short idle time-out, in order to ensure the file-system is placed in a clean
+ state quickly after each access.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--type=</option></term>
+ <term><option>-t</option></term>
+
+ <listitem><para>Specifies the file system type to mount (e.g. <literal>vfat</literal>, <literal>ext4</literal>,
+ …). If omitted (or set to <literal>auto</literal>) the file system is determined automatically.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--options=</option></term>
+ <term><option>-o</option></term>
+
+ <listitem><para>Additional mount options for the mount point.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--fsck=</option></term>
+
+ <listitem><para>Takes a boolean argument, defaults to on. Controls whether to run a file system check
+ immediately before the mount operation. In the automount case (see <option>--automount=</option> below) the
+ check will be run the moment the first access to the device is made, which might slightly delay the
+ access.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--description=</option></term>
+
+ <listitem><para>Provide a description for the mount or automount unit. See <varname>Description=</varname> in
+ <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--property=</option></term>
+ <term><option>-p</option></term>
+
+ <listitem><para>Sets a unit property for the mount unit that is created. This 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>
+
+ <varlistentry>
+ <term><option>--automount=</option></term>
+
+ <listitem><para>Takes a boolean argument. Controls whether to create an automount point or a regular mount
+ point. If true an automount point is created that is backed by the actual file system at the time of first
+ access. If false a plain mount point is created that is backed by the actual file system immediately. Automount
+ points have the benefit that the file system stays unmounted and hence in clean state until it is first
+ accessed. In automount mode the <option>--timeout-idle-sec=</option> switch (see below) may be used to ensure
+ the mount point is unmounted automatically after the last access and an idle period passed.</para>
+
+ <para>If this switch is not specified it defaults to false. If not specified and <option>--discover</option> is
+ used (or only a single argument passed, which implies <option>--discover</option>, see above), and the file
+ system block device is detected to be removable, it is set to true, in order to increase the chance that the
+ file system is in a fully clean state if the device is unplugged abruptly.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-A</option></term>
+
+ <listitem><para>Equivalent to <option>--automount=yes</option>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--timeout-idle-sec=</option></term>
+
+ <listitem><para>Takes a time value that controls the idle timeout in automount mode. If set to
+ <literal>infinity</literal> (the default) no automatic unmounts are done. Otherwise the file system backing the
+ automount point is detached after the last access and the idle timeout passed. See
+ <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry> for details on
+ the time syntax supported. This option has no effect if only a regular mount is established, and automounting
+ is not used.</para>
+
+ <para>Note that if <option>--discover</option> is used (or only a single argument passed, which implies
+ <option>--discover</option>, see above), and the file system block device is detected to be removable,
+ <option>--timeout-idle-sec=1s</option> is implied.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--automount-property=</option></term>
+
+ <listitem><para>Similar to <option>--property=</option>, but applies additional properties to the automount
+ unit created, instead of the mount unit.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--bind-device=</option></term>
+
+ <listitem><para>Takes a boolean argument, defaults to off. This option only has an effect in automount mode,
+ and controls whether the automount unit shall be bound to the backing device's lifetime. If enabled, the
+ automount point will be removed automatically when the backing device vanishes. If disabled the automount point
+ stays around, and subsequent accesses will block until backing device is replugged. This option has no effect
+ in case of non-device mounts, such as network or virtual file system mounts.</para>
+
+ <para>Note that if <option>--discover</option> is used (or only a single argument passed, which implies
+ <option>--discover</option>, see above), and the file system block device is detected to be removable, this
+ option is implied.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--list</option></term>
+
+ <listitem><para>Instead of establishing a mount or automount point, print a terse list of block devices
+ containing file systems that may be mounted with <literal>systemd-mount</literal>, along with useful metadata
+ such as labels, etc.</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" />
+ <xi:include href="user-system-options.xml" xpointer="machine" />
+
+ <xi:include href="standard-options.xml" xpointer="help" />
+ <xi:include href="standard-options.xml" xpointer="version" />
+ </variablelist>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Exit status</title>
+
+ <para>On success, 0 is returned, a non-zero failure
+ code otherwise.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>The udev Database</title>
+
+ <para>If <option>--discover</option> is used, <command>systemd-mount</command> honours a couple of additional udev
+ properties of block devices:</para>
+
+ <variablelist class='udev-directives'>
+ <varlistentry>
+ <term><varname>SYSTEMD_MOUNT_OPTIONS=</varname></term>
+
+ <listitem><para>The mount options to use, if <option>--options=</option> is not used.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>SYSTEMD_MOUNT_WHERE=</varname></term>
+
+ <listitem><para>The file system path to place the mount point at, instead of the automatically generated
+ one.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>mount</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd.mount</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd.automount</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-run</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
diff --git a/man/systemd-run.xml b/man/systemd-run.xml
index 9c1a29218e..15f9119e54 100644
--- a/man/systemd-run.xml
+++ b/man/systemd-run.xml
@@ -45,7 +45,7 @@
<refnamediv>
<refname>systemd-run</refname>
- <refpurpose>Run programs in transient scope or service or timer units</refpurpose>
+ <refpurpose>Run programs in transient scope units, service units, or timer-scheduled service units</refpurpose>
</refnamediv>
<refsynopsisdiv>
@@ -68,42 +68,30 @@
<refsect1>
<title>Description</title>
- <para><command>systemd-run</command> may be used to create and
- start a transient <filename>.service</filename> or
- <filename>.scope</filename> unit and run the specified
- <replaceable>COMMAND</replaceable> in it. It may also be used to
- create and start transient <filename>.timer</filename>
- units.</para>
-
- <para>If a command is run as transient service unit, it will be
- started and managed by the service manager like any other service,
- and thus shows up in the output of <command>systemctl
- list-units</command> like any other unit. It will run in a clean
- and detached execution environment, with the service manager as
- its parent process. In this mode, <command>systemd-run</command>
- will start the service asynchronously in the background and return
- after the command has begun execution.</para>
-
- <para>If a command is run as transient scope unit, it will be
- started by <command>systemd-run</command> itself as parent process
- and will thus inherit the execution environment of the
- caller. However, the processes of the command are managed by the
- service manager similar to normal services, and will show up in
- the output of <command>systemctl list-units</command>. Execution
- in this case is synchronous, and will return only when the command
- finishes. This mode is enabled via the <option>--scope</option>
- switch (see below). </para>
-
- <para>If a command is run with timer options such as
- <option>--on-calendar=</option> (see below), a transient timer
- unit is created alongside the service unit for the specified
- command. Only the transient timer unit is started immediately, the
- transient service unit will be started when the transient timer
- elapses. If the <option>--unit=</option> is specified, the
- <replaceable>COMMAND</replaceable> may be omitted. In this case,
- <command>systemd-run</command> only creates a
- <filename>.timer</filename> unit that invokes the specified unit
- when elapsing.</para>
+ <para><command>systemd-run</command> may be used to create and start a transient <filename>.service</filename> or
+ <filename>.scope</filename> unit and run the specified <replaceable>COMMAND</replaceable> in it. It may also be
+ used to create and start a transient <filename>.timer</filename> unit, that activates a
+ <filename>.service</filename> unit when elapsing.</para>
+
+ <para>If a command is run as transient service unit, it will be started and managed by the service manager like any
+ other service, and thus shows up in the output of <command>systemctl list-units</command> like any other unit. It
+ will run in a clean and detached execution environment, with the service manager as its parent process. In this
+ mode, <command>systemd-run</command> will start the service asynchronously in the background and return after the
+ command has begun execution (unless <option>--no-block</option> or <option>--watch</option> are specified, see
+ below).</para>
+
+ <para>If a command is run as transient scope unit, it will be executed by <command>systemd-run</command> itself as
+ parent process and will thus inherit the execution environment of the caller. However, the processes of the command
+ are managed by the service manager similar to normal services, and will show up in the output of <command>systemctl
+ list-units</command>. Execution in this case is synchronous, and will return only when the command finishes. This
+ mode is enabled via the <option>--scope</option> switch (see below). </para>
+
+ <para>If a command is run with timer options such as <option>--on-calendar=</option> (see below), a transient timer
+ unit is created alongside the service unit for the specified command. Only the transient timer unit is started
+ immediately, the transient service unit will be started when the timer elapses. If the <option>--unit=</option>
+ option is specified, the <replaceable>COMMAND</replaceable> may be omitted. In this case,
+ <command>systemd-run</command> creates only a <filename>.timer</filename> unit that invokes the specified unit when
+ elapsing.</para>
</refsect1>
<refsect1>
@@ -123,8 +111,8 @@
<term><option>--scope</option></term>
<listitem>
- <para>Create a transient <filename>.scope</filename> unit instead of
- the default transient <filename>.service</filename> unit.
+ <para>Create a transient <filename>.scope</filename> unit instead of the default transient
+ <filename>.service</filename> unit (see above).
</para>
</listitem>
</varlistentry>
@@ -140,9 +128,8 @@
<term><option>--property=</option></term>
<term><option>-p</option></term>
- <listitem><para>Sets a unit property for the scope or service
- unit that is created. This takes an assignment in the same
- format as
+ <listitem><para>Sets a property on the scope or service unit that is created. This option takes an assignment
+ in the same format as
<citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
<command>set-property</command> command.</para>
</listitem>
@@ -151,9 +138,8 @@
<varlistentry>
<term><option>--description=</option></term>
- <listitem><para>Provide a description for the service or scope
- unit. If not specified, the command itself will be used as a
- description. See <varname>Description=</varname> in
+ <listitem><para>Provide a description for the service, scope or timer unit. If not specified, the command
+ itself will be used as a description. See <varname>Description=</varname> in
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
</para></listitem>
</varlistentry>
@@ -161,19 +147,16 @@
<varlistentry>
<term><option>--slice=</option></term>
- <listitem><para>Make the new <filename>.service</filename> or
- <filename>.scope</filename> unit part of the specified slice,
- instead of the <filename>system.slice</filename>.</para>
+ <listitem><para>Make the new <filename>.service</filename> or <filename>.scope</filename> unit part of the
+ specified slice, instead of <filename>system.slice</filename>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--remain-after-exit</option></term>
- <listitem><para>After the service or scope process has
- terminated, keep the service around until it is explicitly
- stopped. This is useful to collect runtime information about
- the service after it finished running. Also see
+ <listitem><para>After the service process has terminated, keep the service around until it is explicitly
+ stopped. This is useful to collect runtime information about the service after it finished running. Also see
<varname>RemainAfterExit=</varname> in
<citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
</para>
@@ -183,10 +166,8 @@
<varlistentry>
<term><option>--send-sighup</option></term>
- <listitem><para>When terminating the scope or service unit,
- send a SIGHUP immediately after SIGTERM. This is useful to
- indicate to shells and shell-like processes that the
- connection has been severed. Also see
+ <listitem><para>When terminating the scope or service unit, send a SIGHUP immediately after SIGTERM. This is
+ useful to indicate to shells and shell-like processes that the connection has been severed. Also see
<varname>SendSIGHUP=</varname> in
<citerefentry><refentrytitle>systemd.kill</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
</para>
@@ -209,9 +190,8 @@
<term><option>--uid=</option></term>
<term><option>--gid=</option></term>
- <listitem><para>Runs the service process under the UNIX user
- and group. Also see <varname>User=</varname> and
- <varname>Group=</varname> in
+ <listitem><para>Runs the service process under the specified UNIX user and group. Also see
+ <varname>User=</varname> and <varname>Group=</varname> in
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
</listitem>
</varlistentry>
@@ -239,11 +219,9 @@
<term><option>--pty</option></term>
<term><option>-t</option></term>
- <listitem><para>When invoking a command, the service connects
- its standard input and output to the invoking tty via a
- pseudo TTY device. This allows invoking binaries as services
- that expect interactive user input, such as interactive
- command shells.</para></listitem>
+ <listitem><para>When invoking the command, the transient service connects its standard input and output to the
+ terminal <command>systemd-run</command> is invoked on, via a pseudo TTY device. This allows running binaries
+ that expect interactive user input as services, such as interactive command shells.</para></listitem>
</varlistentry>
<varlistentry>
@@ -263,44 +241,32 @@
<term><option>--on-unit-active=</option></term>
<term><option>--on-unit-inactive=</option></term>
- <listitem><para>Defines monotonic timers relative to different
- starting points. Also see <varname>OnActiveSec=</varname>,
- <varname>OnBootSec=</varname>,
- <varname>OnStartupSec=</varname>,
- <varname>OnUnitActiveSec=</varname> and
- <varname>OnUnitInactiveSec=</varname> in
- <citerefentry><refentrytitle>systemd.timer</refentrytitle><manvolnum>5</manvolnum></citerefentry>. This
- options have no effect in conjunction with
- <option>--scope</option>.</para>
+ <listitem><para>Defines a monotonic timer relative to different starting points for starting the specified
+ command. See <varname>OnActiveSec=</varname>, <varname>OnBootSec=</varname>, <varname>OnStartupSec=</varname>,
+ <varname>OnUnitActiveSec=</varname> and <varname>OnUnitInactiveSec=</varname> in
+ <citerefentry><refentrytitle>systemd.timer</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
+ details. These options may not be combined with <option>--scope</option>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--on-calendar=</option></term>
- <listitem><para>Defines realtime (i.e. wallclock) timers with
- calendar event expressions. Also see
- <varname>OnCalendar=</varname> in
- <citerefentry><refentrytitle>systemd.timer</refentrytitle><manvolnum>5</manvolnum></citerefentry>. This
- option has no effect in conjunction with
- <option>--scope</option>.</para>
+ <listitem><para>Defines a calendar timer for starting the specified command. See <varname>OnCalendar=</varname>
+ in <citerefentry><refentrytitle>systemd.timer</refentrytitle><manvolnum>5</manvolnum></citerefentry>. This
+ option may not be combined with <option>--scope</option>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--timer-property=</option></term>
- <listitem><para>Sets a timer unit property for the timer unit
- that is created. It is similar with
- <option>--property</option> but only for created timer
- unit. This option only has effect in conjunction with
- <option>--on-active=</option>, <option>--on-boot=</option>,
- <option>--on-startup=</option>,
- <option>--on-unit-active=</option>,
- <option>--on-unit-inactive=</option>,
- <option>--on-calendar=</option>. This takes an assignment in
- the same format as
- <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
+ <listitem><para>Sets a property on the timer unit that is created. This option is similar to
+ <option>--property=</option> but applies to the transient timer unit rather than the transient service unit
+ created. This option only has an effect in conjunction with <option>--on-active=</option>,
+ <option>--on-boot=</option>, <option>--on-startup=</option>, <option>--on-unit-active=</option>,
+ <option>--on-unit-inactive=</option> or <option>--on-calendar=</option>. This option takes an assignment in the
+ same format as <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
<command>set-property</command> command.</para> </listitem>
</varlistentry>
@@ -308,14 +274,25 @@
<term><option>--no-block</option></term>
<listitem>
- <para>Do not synchronously wait for the requested operation
- to finish. If this is not specified, the job will be
- verified, enqueued and <command>systemd-run</command> will
- wait until the unit's start-up is completed. By passing this
- argument, it is only verified and enqueued.</para>
+ <para>Do not synchronously wait for the unit start operation to finish. If this option is not specified, the
+ start request for the transient unit will be verified, enqueued and <command>systemd-run</command> will wait
+ until the unit's start-up is completed. By passing this argument, it is only verified and enqueued. This
+ option may not be combined with <option>--wait</option>.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--wait</option></term>
+
+ <listitem><para>Synchronously wait for the transient service to terminate. If this option is specified, the
+ start request for the transient unit is verified, enqueued, and waited for. Subsequently the invoked unit is
+ monitored, and it is waited until it is deactivated again (most likely because the specified command
+ completed). On exit, terse information about the unit's runtime is shown, including total runtime (as well as
+ CPU usage, if <option>--property=CPUAccounting=1</option> was set) and the exit code and status of the main
+ process. This output may be suppressed with <option>--quiet</option>. This option may not be combined with
+ <option>--no-block</option>, <option>--scope</option> or the various timer options.</para></listitem>
+ </varlistentry>
+
<xi:include href="user-system-options.xml" xpointer="user" />
<xi:include href="user-system-options.xml" xpointer="system" />
<xi:include href="user-system-options.xml" xpointer="host" />
@@ -452,6 +429,7 @@ There is a screen on:
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd.timer</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-mount</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
</para>
</refsect1>
diff --git a/man/systemd.link.xml b/man/systemd.link.xml
index d5b4d1038d..10fddeced0 100644
--- a/man/systemd.link.xml
+++ b/man/systemd.link.xml
@@ -387,6 +387,46 @@
</variablelist>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>TCPSegmentationOffload=</varname></term>
+ <listitem>
+ <para>The TCP Segmentation Offload (TSO) when true enables
+ TCP segmentation offload. Takes a boolean value.
+ Defaults to "unset".</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>GenericSegmentationOffload=</varname></term>
+ <listitem>
+ <para>The Generic Segmentation Offload (GSO) when true enables
+ generic segmentation offload. Takes a boolean value.
+ Defaults to "unset".</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>UDPSegmentationOffload=</varname></term>
+ <listitem>
+ <para>The UDP Segmentation Offload (USO) when true enables
+ UDP segmentation offload. Takes a boolean value.
+ Defaults to "unset".</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>GenericReceiveOffload=</varname></term>
+ <listitem>
+ <para>The Generic Receive Offload (GRO) when true enables
+ generic receive offload. Takes a boolean value.
+ Defaults to "unset".</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>LargeReceiveOffload=</varname></term>
+ <listitem>
+ <para>The Large Receive Offload (LRO) when true enables
+ large receive offload. Takes a boolean value.
+ Defaults to "unset".</para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
diff --git a/man/systemd.mount.xml b/man/systemd.mount.xml
index a38165f9b9..b0f156f6df 100644
--- a/man/systemd.mount.xml
+++ b/man/systemd.mount.xml
@@ -352,6 +352,30 @@
</varlistentry>
<varlistentry>
+ <term><varname>LazyUnmount=</varname></term>
+
+ <listitem><para>Takes a boolean argument. If true, detach the
+ filesystem from the filesystem hierarchy at time of the unmount
+ operation, and clean up all references to the filesystem as
+ soon as they are not busy anymore.
+ This corresponds with
+ <citerefentry project='man-pages'><refentrytitle>umount</refentrytitle><manvolnum>8</manvolnum></citerefentry>'s
+ <parameter>-l</parameter> switch. Defaults to
+ off.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>ForceUnmount=</varname></term>
+
+ <listitem><para>Takes a boolean argument. If true, force an
+ unmount (in case of an unreachable NFS system).
+ This corresponds with
+ <citerefentry project='man-pages'><refentrytitle>umount</refentrytitle><manvolnum>8</manvolnum></citerefentry>'s
+ <parameter>-f</parameter> switch. Defaults to
+ off.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><varname>DirectoryMode=</varname></term>
<listitem><para>Directories of mount points (and any parent
directories) are automatically created if needed. This option
@@ -373,7 +397,7 @@
Takes a unit-less value in seconds, or a time span value such
as "5min 20s". Pass 0 to disable the timeout logic. The
default value is set from the manager configuration file's
- <varname>DefaultTimeoutStart=</varname>
+ <varname>DefaultTimeoutStartSec=</varname>
variable.</para></listitem>
</varlistentry>
</variablelist>
diff --git a/man/systemd.resource-control.xml b/man/systemd.resource-control.xml
index 84dbfa2ff3..c11f420fe5 100644
--- a/man/systemd.resource-control.xml
+++ b/man/systemd.resource-control.xml
@@ -326,6 +326,24 @@
</varlistentry>
<varlistentry>
+ <term><varname>MemorySwapMax=<replaceable>bytes</replaceable></varname></term>
+
+ <listitem>
+ <para>Specify the absolute limit on swap usage of the executed processes in this unit.</para>
+
+ <para>Takes a swap size in bytes. If the value is suffixed with K, M, G or T, the specified swap size is
+ parsed as Kilobytes, Megabytes, Gigabytes, or Terabytes (with the base 1024), respectively. If assigned the
+ special value <literal>infinity</literal>, no swap limit is applied. This controls the
+ <literal>memory.swap.max</literal> control group attribute. For details about this control group attribute,
+ see <ulink url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink>.</para>
+
+ <para>Implies <literal>MemoryAccounting=true</literal>.</para>
+
+ <para>This setting is supported only if the unified control group hierarchy is used.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><varname>MemoryLimit=<replaceable>bytes</replaceable></varname></term>
<listitem>
diff --git a/po/hu.po b/po/hu.po
index 88fecbc506..b92921707f 100644
--- a/po/hu.po
+++ b/po/hu.po
@@ -3,20 +3,21 @@
# This file is distributed under the same license as the systemd package.
#
# Gabor Kelemen <kelemeng at gnome dot hu>, 2015, 2016.
+# Balázs Úr <urbalazs at gmail dot com>, 2016.
msgid ""
msgstr ""
"Project-Id-Version: systemd master\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2016-01-02 13:41+0100\n"
-"PO-Revision-Date: 2016-01-02 13:45+0100\n"
-"Last-Translator: Gabor Kelemen <kelemeng at ubuntu dot com>\n"
+"Report-Msgid-Bugs-To: https://github.com/systemd/systemd/issues\n"
+"POT-Creation-Date: 2016-04-24 12:53+0000\n"
+"PO-Revision-Date: 2016-08-23 18:03+0100\n"
+"Last-Translator: Balázs Úr <urbalazs@gmail.com>\n"
"Language-Team: Hungarian <openscope at googlegroups dot com>\n"
"Language: hu\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-"X-Generator: Lokalize 1.5\n"
+"X-Generator: Lokalize 2.0\n"
#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:1
msgid "Send passphrase back to system"
@@ -241,50 +242,60 @@ msgstr ""
"kezelésének meggátlásához."
#: ../src/login/org.freedesktop.login1.policy.in.h:19
+msgid "Allow non-logged-in user to run programs"
+msgstr "Programfuttatás engedélyezése be nem jelentkezett felhasználó számára"
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:20
+msgid "Explicit request is required to run programs as a non-logged-in user."
+msgstr ""
+"Határozott kérés szükséges a programfuttatáshoz be nem jelentkezett "
+"felhasználóként."
+
+#: ../src/login/org.freedesktop.login1.policy.in.h:21
msgid "Allow non-logged-in users to run programs"
msgstr "Programfuttatás engedélyezése be nem jelentkezett felhasználók számára"
-#: ../src/login/org.freedesktop.login1.policy.in.h:20
+#: ../src/login/org.freedesktop.login1.policy.in.h:22
msgid "Authentication is required to run programs as a non-logged-in user."
msgstr ""
"Hitelesítés szükséges a programfuttatáshoz be nem jelentkezett "
"felhasználóként."
-#: ../src/login/org.freedesktop.login1.policy.in.h:21
+#: ../src/login/org.freedesktop.login1.policy.in.h:23
msgid "Allow attaching devices to seats"
msgstr "Eszközök csatolásának engedélyezése munkaállomásokhoz"
-#: ../src/login/org.freedesktop.login1.policy.in.h:22
+#: ../src/login/org.freedesktop.login1.policy.in.h:24
msgid "Authentication is required for attaching a device to a seat."
msgstr ""
"Hitelesítés szükséges eszköz csatolásának engedélyezéséhez egy "
"munkaállomáshoz"
-#: ../src/login/org.freedesktop.login1.policy.in.h:23
+#: ../src/login/org.freedesktop.login1.policy.in.h:25
msgid "Flush device to seat attachments"
msgstr "Eszközök és munkaállomások csatolásainak törlése"
-#: ../src/login/org.freedesktop.login1.policy.in.h:24
+#: ../src/login/org.freedesktop.login1.policy.in.h:26
msgid ""
"Authentication is required for resetting how devices are attached to seats."
msgstr ""
"Hitelesítés szükséges az eszközök munkaállomásokhoz csatolásainak "
"alaphelyzetbe állításához."
-#: ../src/login/org.freedesktop.login1.policy.in.h:25
+#: ../src/login/org.freedesktop.login1.policy.in.h:27
msgid "Power off the system"
msgstr "A rendszer kikapcsolása"
-#: ../src/login/org.freedesktop.login1.policy.in.h:26
+#: ../src/login/org.freedesktop.login1.policy.in.h:28
msgid "Authentication is required for powering off the system."
msgstr "Hitelesítés szükséges a rendszer kikapcsolásához."
-#: ../src/login/org.freedesktop.login1.policy.in.h:27
+#: ../src/login/org.freedesktop.login1.policy.in.h:29
msgid "Power off the system while other users are logged in"
msgstr ""
"A rendszer kikapcsolása miközben be vannak jelentkezve más felhasználók"
-#: ../src/login/org.freedesktop.login1.policy.in.h:28
+#: ../src/login/org.freedesktop.login1.policy.in.h:30
msgid ""
"Authentication is required for powering off the system while other users are "
"logged in."
@@ -292,12 +303,12 @@ msgstr ""
"Hitelesítés szükséges a rendszer kikapcsolásához miközben be vannak "
"jelentkezve más felhasználók."
-#: ../src/login/org.freedesktop.login1.policy.in.h:29
+#: ../src/login/org.freedesktop.login1.policy.in.h:31
msgid "Power off the system while an application asked to inhibit it"
msgstr ""
"A rendszer kikapcsolása miközben egy alkalmazás ennek meggátlását kérte"
-#: ../src/login/org.freedesktop.login1.policy.in.h:30
+#: ../src/login/org.freedesktop.login1.policy.in.h:32
msgid ""
"Authentication is required for powering off the system while an application "
"asked to inhibit it."
@@ -305,19 +316,19 @@ msgstr ""
"Hitelesítés szükséges a rendszer kikapcsolásához miközben egy alkalmazás "
"ennek meggátlását kérte."
-#: ../src/login/org.freedesktop.login1.policy.in.h:31
+#: ../src/login/org.freedesktop.login1.policy.in.h:33
msgid "Reboot the system"
msgstr "A rendszer újraindítása"
-#: ../src/login/org.freedesktop.login1.policy.in.h:32
+#: ../src/login/org.freedesktop.login1.policy.in.h:34
msgid "Authentication is required for rebooting the system."
msgstr "Hitelesítés szükséges a rendszer újraindításához."
-#: ../src/login/org.freedesktop.login1.policy.in.h:33
+#: ../src/login/org.freedesktop.login1.policy.in.h:35
msgid "Reboot the system while other users are logged in"
msgstr "A rendszer újraindítása mialatt be vannak jelentkezve más felhasználók"
-#: ../src/login/org.freedesktop.login1.policy.in.h:34
+#: ../src/login/org.freedesktop.login1.policy.in.h:36
msgid ""
"Authentication is required for rebooting the system while other users are "
"logged in."
@@ -325,12 +336,12 @@ msgstr ""
"Hitelesítés szükséges a rendszer újraindításához miközben be vannak "
"jelentkezve más felhasználók."
-#: ../src/login/org.freedesktop.login1.policy.in.h:35
+#: ../src/login/org.freedesktop.login1.policy.in.h:37
msgid "Reboot the system while an application asked to inhibit it"
msgstr ""
"A rendszer újraindítása miközben egy alkalmazás ennek meggátlását kérte"
-#: ../src/login/org.freedesktop.login1.policy.in.h:36
+#: ../src/login/org.freedesktop.login1.policy.in.h:38
msgid ""
"Authentication is required for rebooting the system while an application "
"asked to inhibit it."
@@ -338,20 +349,20 @@ msgstr ""
"Hitelesítés szükséges a rendszer újraindításához miközben egy alkalmazás "
"ennek meggátlását kérte."
-#: ../src/login/org.freedesktop.login1.policy.in.h:37
+#: ../src/login/org.freedesktop.login1.policy.in.h:39
msgid "Suspend the system"
msgstr "A rendszer felfüggesztése"
-#: ../src/login/org.freedesktop.login1.policy.in.h:38
+#: ../src/login/org.freedesktop.login1.policy.in.h:40
msgid "Authentication is required for suspending the system."
msgstr "Hitelesítés szükséges a rendszer felfüggesztéséhez."
-#: ../src/login/org.freedesktop.login1.policy.in.h:39
+#: ../src/login/org.freedesktop.login1.policy.in.h:41
msgid "Suspend the system while other users are logged in"
msgstr ""
"A rendszer felfüggesztése mialatt be vannak jelentkezve más felhasználók"
-#: ../src/login/org.freedesktop.login1.policy.in.h:40
+#: ../src/login/org.freedesktop.login1.policy.in.h:42
msgid ""
"Authentication is required for suspending the system while other users are "
"logged in."
@@ -359,12 +370,12 @@ msgstr ""
"Hitelesítés szükséges a rendszer felfüggesztéséhez miközben be vannak "
"jelentkezve más felhasználók."
-#: ../src/login/org.freedesktop.login1.policy.in.h:41
+#: ../src/login/org.freedesktop.login1.policy.in.h:43
msgid "Suspend the system while an application asked to inhibit it"
msgstr ""
"A rendszer felfüggesztése miközben egy alkalmazás ennek meggátlását kérte"
-#: ../src/login/org.freedesktop.login1.policy.in.h:42
+#: ../src/login/org.freedesktop.login1.policy.in.h:44
msgid ""
"Authentication is required for suspending the system while an application "
"asked to inhibit it."
@@ -372,19 +383,19 @@ msgstr ""
"Hitelesítés szükséges a rendszer felfüggesztéséhez miközben egy alkalmazás "
"ennek meggátlását kérte."
-#: ../src/login/org.freedesktop.login1.policy.in.h:43
+#: ../src/login/org.freedesktop.login1.policy.in.h:45
msgid "Hibernate the system"
msgstr "A rendszer hibernálása"
-#: ../src/login/org.freedesktop.login1.policy.in.h:44
+#: ../src/login/org.freedesktop.login1.policy.in.h:46
msgid "Authentication is required for hibernating the system."
msgstr "Hitelesítés szükséges a rendszer hibernálásához."
-#: ../src/login/org.freedesktop.login1.policy.in.h:45
+#: ../src/login/org.freedesktop.login1.policy.in.h:47
msgid "Hibernate the system while other users are logged in"
msgstr "A rendszer hibernálása mialatt be vannak jelentkezve más felhasználók"
-#: ../src/login/org.freedesktop.login1.policy.in.h:46
+#: ../src/login/org.freedesktop.login1.policy.in.h:48
msgid ""
"Authentication is required for hibernating the system while other users are "
"logged in."
@@ -392,11 +403,11 @@ msgstr ""
"Hitelesítés szükséges a rendszer hibernálásához miközben be vannak "
"jelentkezve más felhasználók."
-#: ../src/login/org.freedesktop.login1.policy.in.h:47
+#: ../src/login/org.freedesktop.login1.policy.in.h:49
msgid "Hibernate the system while an application asked to inhibit it"
msgstr "A rendszer hibernálása miközben egy alkalmazás ennek meggátlását kérte"
-#: ../src/login/org.freedesktop.login1.policy.in.h:48
+#: ../src/login/org.freedesktop.login1.policy.in.h:50
msgid ""
"Authentication is required for hibernating the system while an application "
"asked to inhibit it."
@@ -404,31 +415,31 @@ msgstr ""
"Hitelesítés szükséges a rendszer hibernálásához miközben egy alkalmazás "
"ennek meggátlását kérte."
-#: ../src/login/org.freedesktop.login1.policy.in.h:49
+#: ../src/login/org.freedesktop.login1.policy.in.h:51
msgid "Manage active sessions, users and seats"
msgstr "Aktív munkamenetek, felhasználók és munkaállomások kezelése"
-#: ../src/login/org.freedesktop.login1.policy.in.h:50
+#: ../src/login/org.freedesktop.login1.policy.in.h:52
msgid ""
"Authentication is required for managing active sessions, users and seats."
msgstr ""
"Hitelesítés szükséges az aktív munkamenetek, felhasználók és munkaállomások "
"kezeléséhez."
-#: ../src/login/org.freedesktop.login1.policy.in.h:51
+#: ../src/login/org.freedesktop.login1.policy.in.h:53
msgid "Lock or unlock active sessions"
msgstr "Aktív munkamenetek zárolása vagy feloldása"
-#: ../src/login/org.freedesktop.login1.policy.in.h:52
+#: ../src/login/org.freedesktop.login1.policy.in.h:54
msgid "Authentication is required to lock or unlock active sessions."
msgstr ""
"Hitelesítés szükséges az aktív munkamenetek zárolásához vagy feloldásához."
-#: ../src/login/org.freedesktop.login1.policy.in.h:53
+#: ../src/login/org.freedesktop.login1.policy.in.h:55
msgid "Allow indication to the firmware to boot to setup interface"
msgstr "A firmware-nek jelezhető, hogy a beállítófelületet bootolja"
-#: ../src/login/org.freedesktop.login1.policy.in.h:54
+#: ../src/login/org.freedesktop.login1.policy.in.h:56
msgid ""
"Authentication is required to indicate to the firmware to boot to setup "
"interface."
@@ -436,11 +447,11 @@ msgstr ""
"Hitelesítés szükséges a firmware-nek jelzéshez, hogy a beállítófelületet "
"bootolja"
-#: ../src/login/org.freedesktop.login1.policy.in.h:55
+#: ../src/login/org.freedesktop.login1.policy.in.h:57
msgid "Set a wall message"
msgstr "Falüzenet beállítása"
-#: ../src/login/org.freedesktop.login1.policy.in.h:56
+#: ../src/login/org.freedesktop.login1.policy.in.h:58
msgid "Authentication is required to set a wall message"
msgstr "Hitelesítés szükséges a falüzenet beállításához"
@@ -552,33 +563,34 @@ msgid ""
"shall be enabled."
msgstr "Hitelesítés szükséges a hálózati időszinkronizáció engedélyezéséhez."
-#: ../src/core/dbus-unit.c:449
+#: ../src/core/dbus-unit.c:450
msgid "Authentication is required to start '$(unit)'."
msgstr "Hitelesítés szükséges a következő elindításához: „$(unit)”."
-#: ../src/core/dbus-unit.c:450
+#: ../src/core/dbus-unit.c:451
msgid "Authentication is required to stop '$(unit)'."
msgstr "Hitelesítés szükséges a következő leállításához: „$(unit)”."
-#: ../src/core/dbus-unit.c:451
+#: ../src/core/dbus-unit.c:452
msgid "Authentication is required to reload '$(unit)'."
msgstr "Hitelesítés szükséges a következő újratöltéséhez: „$(unit)”."
-#: ../src/core/dbus-unit.c:452 ../src/core/dbus-unit.c:453
+#: ../src/core/dbus-unit.c:453 ../src/core/dbus-unit.c:454
msgid "Authentication is required to restart '$(unit)'."
msgstr "Hitelesítés szükséges a következő újraindításához: „$(unit)”."
-#: ../src/core/dbus-unit.c:556
+#: ../src/core/dbus-unit.c:560
msgid "Authentication is required to kill '$(unit)'."
msgstr "Hitelesítés szükséges a következő kilövéséhez: „$(unit)”."
-#: ../src/core/dbus-unit.c:586
+#: ../src/core/dbus-unit.c:590
msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
msgstr ""
"Hitelesítés szükséges a következő „sikertelen” állapotának törléséhez: "
"„$(unit)”."
-#: ../src/core/dbus-unit.c:618
+#: ../src/core/dbus-unit.c:622
msgid "Authentication is required to set properties on '$(unit)'."
msgstr ""
"Hitelesítés szükséges a következő tulajdonságainak beállításához: „$(unit)”."
+
diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c
index 25ef8a5c76..9b44c5a7a5 100644
--- a/src/basic/cgroup-util.c
+++ b/src/basic/cgroup-util.c
@@ -623,7 +623,7 @@ int cg_get_path(const char *controller, const char *path, const char *suffix, ch
if (!cg_controller_is_valid(controller))
return -EINVAL;
- unified = cg_unified();
+ unified = cg_all_unified();
if (unified < 0)
return unified;
@@ -651,7 +651,7 @@ static int controller_is_accessible(const char *controller) {
if (!cg_controller_is_valid(controller))
return -EINVAL;
- unified = cg_unified();
+ unified = cg_all_unified();
if (unified < 0)
return unified;
if (unified > 0) {
@@ -869,7 +869,7 @@ int cg_set_task_access(
if (r < 0)
return r;
- unified = cg_unified();
+ unified = cg_unified(controller);
if (unified < 0)
return unified;
if (unified)
@@ -893,18 +893,17 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
assert(path);
assert(pid >= 0);
- unified = cg_unified();
+ if (controller) {
+ if (!cg_controller_is_valid(controller))
+ return -EINVAL;
+ } else
+ controller = SYSTEMD_CGROUP_CONTROLLER;
+
+ unified = cg_unified(controller);
if (unified < 0)
return unified;
- if (unified == 0) {
- if (controller) {
- if (!cg_controller_is_valid(controller))
- return -EINVAL;
- } else
- controller = SYSTEMD_CGROUP_CONTROLLER;
-
+ if (unified == 0)
cs = strlen(controller);
- }
fs = procfs_file_alloca(pid, "cgroup");
f = fopen(fs, "re");
@@ -969,7 +968,7 @@ int cg_install_release_agent(const char *controller, const char *agent) {
assert(agent);
- unified = cg_unified();
+ unified = cg_unified(controller);
if (unified < 0)
return unified;
if (unified) /* doesn't apply to unified hierarchy */
@@ -1020,7 +1019,7 @@ int cg_uninstall_release_agent(const char *controller) {
_cleanup_free_ char *fs = NULL;
int r, unified;
- unified = cg_unified();
+ unified = cg_unified(controller);
if (unified < 0)
return unified;
if (unified) /* Doesn't apply to unified hierarchy */
@@ -1076,7 +1075,7 @@ int cg_is_empty_recursive(const char *controller, const char *path) {
if (controller && (isempty(path) || path_equal(path, "/")))
return false;
- unified = cg_unified();
+ unified = cg_unified(controller);
if (unified < 0)
return unified;
@@ -1962,7 +1961,7 @@ int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path
return r;
/* If we are in the unified hierarchy, we are done now */
- unified = cg_unified();
+ unified = cg_all_unified();
if (unified < 0)
return unified;
if (unified > 0)
@@ -1992,7 +1991,7 @@ int cg_attach_everywhere(CGroupMask supported, const char *path, pid_t pid, cg_m
if (r < 0)
return r;
- unified = cg_unified();
+ unified = cg_all_unified();
if (unified < 0)
return unified;
if (unified > 0)
@@ -2044,7 +2043,7 @@ int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to
return r;
}
- unified = cg_unified();
+ unified = cg_all_unified();
if (unified < 0)
return unified;
if (unified > 0)
@@ -2077,7 +2076,7 @@ int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root)
if (r < 0)
return r;
- unified = cg_unified();
+ unified = cg_all_unified();
if (unified < 0)
return unified;
if (unified > 0)
@@ -2103,7 +2102,7 @@ int cg_mask_supported(CGroupMask *ret) {
* includes controllers we can make sense of and that are
* actually accessible. */
- unified = cg_unified();
+ unified = cg_all_unified();
if (unified < 0)
return unified;
if (unified > 0) {
@@ -2224,9 +2223,10 @@ int cg_kernel_controllers(Set *controllers) {
return 0;
}
-static thread_local int unified_cache = -1;
+static thread_local CGroupUnified unified_cache = CGROUP_UNIFIED_UNKNOWN;
+
+static int cg_update_unified(void) {
-int cg_unified(void) {
struct statfs fs;
/* Checks if we support the unified hierarchy. Returns an
@@ -2234,24 +2234,47 @@ int cg_unified(void) {
* have any other trouble determining if the unified hierarchy
* is supported. */
- if (unified_cache >= 0)
- return unified_cache;
+ if (unified_cache >= CGROUP_UNIFIED_NONE)
+ return 0;
if (statfs("/sys/fs/cgroup/", &fs) < 0)
return -errno;
if (F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC))
- unified_cache = true;
- else if (F_TYPE_EQUAL(fs.f_type, TMPFS_MAGIC))
- unified_cache = false;
- else
+ unified_cache = CGROUP_UNIFIED_ALL;
+ else if (F_TYPE_EQUAL(fs.f_type, TMPFS_MAGIC)) {
+ if (statfs("/sys/fs/cgroup/systemd/", &fs) < 0)
+ return -errno;
+
+ unified_cache = F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC) ?
+ CGROUP_UNIFIED_SYSTEMD : CGROUP_UNIFIED_NONE;
+ } else
return -ENOMEDIUM;
- return unified_cache;
+ return 0;
+}
+
+int cg_unified(const char *controller) {
+
+ int r;
+
+ r = cg_update_unified();
+ if (r < 0)
+ return r;
+
+ if (streq_ptr(controller, SYSTEMD_CGROUP_CONTROLLER))
+ return unified_cache >= CGROUP_UNIFIED_SYSTEMD;
+ else
+ return unified_cache >= CGROUP_UNIFIED_ALL;
+}
+
+int cg_all_unified(void) {
+
+ return cg_unified(NULL);
}
void cg_unified_flush(void) {
- unified_cache = -1;
+ unified_cache = CGROUP_UNIFIED_UNKNOWN;
}
int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p) {
@@ -2264,7 +2287,7 @@ int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p) {
if (supported == 0)
return 0;
- unified = cg_unified();
+ unified = cg_all_unified();
if (unified < 0)
return unified;
if (!unified) /* on the legacy hiearchy there's no joining of controllers defined */
@@ -2303,7 +2326,7 @@ bool cg_is_unified_wanted(void) {
/* If the hierarchy is already mounted, then follow whatever
* was chosen for it. */
- unified = cg_unified();
+ unified = cg_all_unified();
if (unified >= 0)
return unified;
@@ -2333,6 +2356,50 @@ bool cg_is_legacy_wanted(void) {
return !cg_is_unified_wanted();
}
+bool cg_is_unified_systemd_controller_wanted(void) {
+ static thread_local int wanted = -1;
+ int r, unified;
+
+ /* If the unified hierarchy is requested in full, no need to
+ * bother with this. */
+ if (cg_is_unified_wanted())
+ return 0;
+
+ /* If the hierarchy is already mounted, then follow whatever
+ * was chosen for it. */
+ unified = cg_unified(SYSTEMD_CGROUP_CONTROLLER);
+ if (unified >= 0)
+ return unified;
+
+ /* Otherwise, let's see what the kernel command line has to
+ * say. Since checking that is expensive, let's cache the
+ * result. */
+ if (wanted >= 0)
+ return wanted;
+
+ r = get_proc_cmdline_key("systemd.legacy_systemd_cgroup_controller", NULL);
+ if (r > 0)
+ wanted = false;
+ else {
+ _cleanup_free_ char *value = NULL;
+
+ r = get_proc_cmdline_key("systemd.legacy_systemd_cgroup_controller=", &value);
+ if (r < 0)
+ return true;
+
+ if (r == 0)
+ wanted = true;
+ else
+ wanted = parse_boolean(value) <= 0;
+ }
+
+ return wanted;
+}
+
+bool cg_is_legacy_systemd_controller_wanted(void) {
+ return cg_is_legacy_wanted() && !cg_is_unified_systemd_controller_wanted();
+}
+
int cg_weight_parse(const char *s, uint64_t *ret) {
uint64_t u;
int r;
diff --git a/src/basic/cgroup-util.h b/src/basic/cgroup-util.h
index f1617a16be..1a61c7ad22 100644
--- a/src/basic/cgroup-util.h
+++ b/src/basic/cgroup-util.h
@@ -116,6 +116,13 @@ static inline bool CGROUP_BLKIO_WEIGHT_IS_OK(uint64_t x) {
#define DEFAULT_TASKS_MAX_PERCENTAGE 15U /* 15% of PIDs, 4915 on default settings */
#define DEFAULT_USER_TASKS_MAX_PERCENTAGE 33U /* 33% of PIDs, 10813 on default settings */
+typedef enum CGroupUnified {
+ CGROUP_UNIFIED_UNKNOWN = -1,
+ CGROUP_UNIFIED_NONE = 0, /* Both systemd and controllers on legacy */
+ CGROUP_UNIFIED_SYSTEMD = 1, /* Only systemd on unified */
+ CGROUP_UNIFIED_ALL = 2, /* Both systemd and controllers on unified */
+} CGroupUnified;
+
/*
* General rules:
*
@@ -229,11 +236,14 @@ int cg_kernel_controllers(Set *controllers);
bool cg_ns_supported(void);
-int cg_unified(void);
+int cg_all_unified(void);
+int cg_unified(const char *controller);
void cg_unified_flush(void);
bool cg_is_unified_wanted(void);
bool cg_is_legacy_wanted(void);
+bool cg_is_unified_systemd_controller_wanted(void);
+bool cg_is_legacy_systemd_controller_wanted(void);
const char* cgroup_controller_to_string(CGroupController c) _const_;
CGroupController cgroup_controller_from_string(const char *s) _pure_;
diff --git a/src/basic/fileio.c b/src/basic/fileio.c
index d642f3daea..a5920e7d36 100644
--- a/src/basic/fileio.c
+++ b/src/basic/fileio.c
@@ -37,6 +37,7 @@
#include "hexdecoct.h"
#include "log.h"
#include "macro.h"
+#include "missing.h"
#include "parse-util.h"
#include "path-util.h"
#include "random-util.h"
@@ -1280,12 +1281,10 @@ int open_tmpfile_unlinkable(const char *directory, int flags) {
/* Returns an unlinked temporary file that cannot be linked into the file system anymore */
-#ifdef O_TMPFILE
/* Try O_TMPFILE first, if it is supported */
fd = open(directory, flags|O_TMPFILE|O_EXCL, S_IRUSR|S_IWUSR);
if (fd >= 0)
return fd;
-#endif
/* Fall back to unguessable name + unlinking */
p = strjoina(directory, "/systemd-tmp-XXXXXX");
@@ -1313,7 +1312,6 @@ int open_tmpfile_linkable(const char *target, int flags, char **ret_path) {
* which case "ret_path" will be returned as NULL. If not possible a the tempoary path name used is returned in
* "ret_path". Use link_tmpfile() below to rename the result after writing the file in full. */
-#ifdef O_TMPFILE
{
_cleanup_free_ char *dn = NULL;
@@ -1329,7 +1327,6 @@ int open_tmpfile_linkable(const char *target, int flags, char **ret_path) {
log_debug_errno(errno, "Failed to use O_TMPFILE on %s: %m", dn);
}
-#endif
r = tempfn_random(target, NULL, &tmp);
if (r < 0)
diff --git a/src/basic/missing.h b/src/basic/missing.h
index f8e096605e..13ff51cd35 100644
--- a/src/basic/missing.h
+++ b/src/basic/missing.h
@@ -537,12 +537,21 @@ struct btrfs_ioctl_quota_ctl_args {
# define DRM_IOCTL_DROP_MASTER _IO('d', 0x1f)
#endif
-#if defined(__i386__) || defined(__x86_64__)
-
-/* The precise definition of __O_TMPFILE is arch specific, so let's
- * just define this on x86 where we know the value. */
+/* The precise definition of __O_TMPFILE is arch specific; use the
+ * values defined by the kernel (note: some are hexa, some are octal,
+ * duplicated as-is from the kernel definitions):
+ * - alpha, parisc, sparc: each has a specific value;
+ * - others: they use the "generic" value.
+ */
#ifndef __O_TMPFILE
+#if defined(__alpha__)
+#define __O_TMPFILE 0100000000
+#elif defined(__parisc__) || defined(__hppa__)
+#define __O_TMPFILE 0400000000
+#elif defined(__sparc__) || defined(__sparc64__)
+#define __O_TMPFILE 0x2000000
+#else
#define __O_TMPFILE 020000000
#endif
diff --git a/src/basic/mount-util.c b/src/basic/mount-util.c
index 28dc778969..bfa04394fe 100644
--- a/src/basic/mount-util.c
+++ b/src/basic/mount-util.c
@@ -75,7 +75,6 @@ static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *mnt_id
return safe_atoi(p, mnt_id);
}
-
int fd_is_mount_point(int fd, const char *filename, int flags) {
union file_handle_union h = FILE_HANDLE_INIT, h_parent = FILE_HANDLE_INIT;
int mount_id = -1, mount_id_parent = -1;
diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c
index bfa936bd4e..eafdea9eb3 100644
--- a/src/basic/terminal-util.c
+++ b/src/basic/terminal-util.c
@@ -346,12 +346,7 @@ int open_terminal(const char *name, int mode) {
}
r = isatty(fd);
- if (r < 0) {
- safe_close(fd);
- return -errno;
- }
-
- if (!r) {
+ if (r == 0) {
safe_close(fd);
return -ENOTTY;
}
diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c
index a7cdf92ed2..ee6d7eb864 100644
--- a/src/boot/bootctl.c
+++ b/src/boot/bootctl.c
@@ -439,9 +439,12 @@ static int status_variables(void) {
for (j = 0; j < n_order; j++)
if (options[i] == order[j])
- continue;
+ goto next_option;
print_efi_option(options[i], false);
+
+ next_option:
+ continue;
}
return 0;
diff --git a/src/cgls/cgls.c b/src/cgls/cgls.c
index dcb5912b83..adf488e8e1 100644
--- a/src/cgls/cgls.c
+++ b/src/cgls/cgls.c
@@ -166,7 +166,7 @@ static int get_cgroup_root(char **ret) {
static void show_cg_info(const char *controller, const char *path) {
- if (cg_unified() <= 0 && controller && !streq(controller, SYSTEMD_CGROUP_CONTROLLER))
+ if (cg_all_unified() <= 0 && controller && !streq(controller, SYSTEMD_CGROUP_CONTROLLER))
printf("Controller %s; ", controller);
printf("Control group %s:\n", isempty(path) ? "/" : path);
diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c
index 6045ae0437..aba17c9829 100644
--- a/src/cgtop/cgtop.c
+++ b/src/cgtop/cgtop.c
@@ -213,7 +213,7 @@ static int process(
uint64_t new_usage;
nsec_t timestamp;
- if (cg_unified() > 0) {
+ if (cg_all_unified() > 0) {
const char *keys[] = { "usage_usec", NULL };
_cleanup_free_ char *val = NULL;
@@ -273,7 +273,7 @@ static int process(
} else if (streq(controller, "memory")) {
_cleanup_free_ char *p = NULL, *v = NULL;
- if (cg_unified() <= 0)
+ if (cg_all_unified() <= 0)
r = cg_get_path(controller, path, "memory.usage_in_bytes", &p);
else
r = cg_get_path(controller, path, "memory.current", &p);
@@ -293,11 +293,11 @@ static int process(
if (g->memory > 0)
g->memory_valid = true;
- } else if ((streq(controller, "io") && cg_unified() > 0) ||
- (streq(controller, "blkio") && cg_unified() <= 0)) {
+ } else if ((streq(controller, "io") && cg_all_unified() > 0) ||
+ (streq(controller, "blkio") && cg_all_unified() <= 0)) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *p = NULL;
- bool unified = cg_unified() > 0;
+ bool unified = cg_all_unified() > 0;
uint64_t wr = 0, rd = 0;
nsec_t timestamp;
diff --git a/src/core/cgroup.c b/src/core/cgroup.c
index ca3c3366f3..7873f88785 100644
--- a/src/core/cgroup.c
+++ b/src/core/cgroup.c
@@ -66,6 +66,7 @@ void cgroup_context_init(CGroupContext *c) {
c->memory_high = CGROUP_LIMIT_MAX;
c->memory_max = CGROUP_LIMIT_MAX;
+ c->memory_swap_max = CGROUP_LIMIT_MAX;
c->memory_limit = CGROUP_LIMIT_MAX;
@@ -173,6 +174,7 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) {
"%sMemoryLow=%" PRIu64 "\n"
"%sMemoryHigh=%" PRIu64 "\n"
"%sMemoryMax=%" PRIu64 "\n"
+ "%sMemorySwapMax=%" PRIu64 "\n"
"%sMemoryLimit=%" PRIu64 "\n"
"%sTasksMax=%" PRIu64 "\n"
"%sDevicePolicy=%s\n"
@@ -194,6 +196,7 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) {
prefix, c->memory_low,
prefix, c->memory_high,
prefix, c->memory_max,
+ prefix, c->memory_swap_max,
prefix, c->memory_limit,
prefix, c->tasks_max,
prefix, cgroup_device_policy_to_string(c->device_policy),
@@ -617,7 +620,7 @@ static unsigned cgroup_apply_blkio_device_limit(Unit *u, const char *dev_path, u
}
static bool cgroup_context_has_unified_memory_config(CGroupContext *c) {
- return c->memory_low > 0 || c->memory_high != CGROUP_LIMIT_MAX || c->memory_max != CGROUP_LIMIT_MAX;
+ return c->memory_low > 0 || c->memory_high != CGROUP_LIMIT_MAX || c->memory_max != CGROUP_LIMIT_MAX || c->memory_swap_max != CGROUP_LIMIT_MAX;
}
static void cgroup_apply_unified_memory_limit(Unit *u, const char *file, uint64_t v) {
@@ -665,7 +668,7 @@ static void cgroup_context_apply(Unit *u, CGroupMask mask, ManagerState state) {
bool has_weight = cgroup_context_has_cpu_weight(c);
bool has_shares = cgroup_context_has_cpu_shares(c);
- if (cg_unified() > 0) {
+ if (cg_all_unified() > 0) {
uint64_t weight;
if (has_weight)
@@ -846,12 +849,14 @@ static void cgroup_context_apply(Unit *u, CGroupMask mask, ManagerState state) {
}
if ((mask & CGROUP_MASK_MEMORY) && !is_root) {
- if (cg_unified() > 0) {
+ if (cg_all_unified() > 0) {
uint64_t max = c->memory_max;
+ uint64_t swap_max = c->memory_swap_max;
- if (cgroup_context_has_unified_memory_config(c))
+ if (cgroup_context_has_unified_memory_config(c)) {
max = c->memory_max;
- else {
+ swap_max = c->memory_swap_max;
+ } else {
max = c->memory_limit;
if (max != CGROUP_LIMIT_MAX)
@@ -861,6 +866,7 @@ static void cgroup_context_apply(Unit *u, CGroupMask mask, ManagerState state) {
cgroup_apply_unified_memory_limit(u, "memory.low", c->memory_low);
cgroup_apply_unified_memory_limit(u, "memory.high", c->memory_high);
cgroup_apply_unified_memory_limit(u, "memory.max", max);
+ cgroup_apply_unified_memory_limit(u, "memory.swap.max", swap_max);
} else {
char buf[DECIMAL_STR_MAX(uint64_t) + 1];
uint64_t val = c->memory_limit;
@@ -1019,7 +1025,7 @@ CGroupMask unit_get_own_mask(Unit *u) {
e = unit_get_exec_context(u);
if (!e ||
exec_context_maintains_privileges(e) ||
- cg_unified() > 0)
+ cg_all_unified() > 0)
return _CGROUP_MASK_ALL;
}
@@ -1245,7 +1251,7 @@ int unit_watch_cgroup(Unit *u) {
return 0;
/* Only applies to the unified hierarchy */
- r = cg_unified();
+ r = cg_unified(SYSTEMD_CGROUP_CONTROLLER);
if (r < 0)
return log_unit_error_errno(u, r, "Failed detect whether the unified hierarchy is used: %m");
if (r == 0)
@@ -1524,6 +1530,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);
@@ -1645,7 +1653,7 @@ int unit_watch_all_pids(Unit *u) {
if (!u->cgroup_path)
return -ENOENT;
- if (cg_unified() > 0) /* On unified we can use proper notifications */
+ if (cg_unified(SYSTEMD_CGROUP_CONTROLLER) > 0) /* On unified we can use proper notifications */
return 0;
return unit_watch_pids_in_path(u, u->cgroup_path);
@@ -1718,7 +1726,7 @@ static int on_cgroup_inotify_event(sd_event_source *s, int fd, uint32_t revents,
int manager_setup_cgroup(Manager *m) {
_cleanup_free_ char *path = NULL;
CGroupController c;
- int r, unified;
+ int r, all_unified, systemd_unified;
char *e;
assert(m);
@@ -1755,11 +1763,17 @@ int manager_setup_cgroup(Manager *m) {
if (r < 0)
return log_error_errno(r, "Cannot find cgroup mount point: %m");
- unified = cg_unified();
- if (unified < 0)
- return log_error_errno(r, "Couldn't determine if we are running in the unified hierarchy: %m");
- if (unified > 0)
+ all_unified = cg_all_unified();
+ systemd_unified = cg_unified(SYSTEMD_CGROUP_CONTROLLER);
+
+ if (all_unified < 0 || systemd_unified < 0)
+ return log_error_errno(all_unified < 0 ? all_unified : systemd_unified,
+ "Couldn't determine if we are running in the unified hierarchy: %m");
+
+ if (all_unified > 0)
log_debug("Unified cgroup hierarchy is located at %s.", path);
+ else if (systemd_unified > 0)
+ log_debug("Unified cgroup hierarchy is located at %s. Controllers are on legacy hierarchies.", path);
else
log_debug("Using cgroup controller " SYSTEMD_CGROUP_CONTROLLER ". File system hierarchy is at %s.", path);
@@ -1767,7 +1781,7 @@ int manager_setup_cgroup(Manager *m) {
const char *scope_path;
/* 3. Install agent */
- if (unified) {
+ if (systemd_unified) {
/* In the unified hierarchy we can get
* cgroup empty notifications via inotify. */
@@ -1827,7 +1841,7 @@ int manager_setup_cgroup(Manager *m) {
return log_error_errno(errno, "Failed to open pin file: %m");
/* 6. Always enable hierarchical support if it exists... */
- if (!unified)
+ if (!all_unified)
(void) cg_set_attribute("memory", "/", "memory.use_hierarchy", "1");
}
@@ -1953,7 +1967,7 @@ int unit_get_memory_current(Unit *u, uint64_t *ret) {
if ((u->cgroup_realized_mask & CGROUP_MASK_MEMORY) == 0)
return -ENODATA;
- if (cg_unified() <= 0)
+ if (cg_all_unified() <= 0)
r = cg_get_attribute("memory", u->cgroup_path, "memory.usage_in_bytes", &v);
else
r = cg_get_attribute("memory", u->cgroup_path, "memory.current", &v);
@@ -1998,7 +2012,7 @@ static int unit_get_cpu_usage_raw(Unit *u, nsec_t *ret) {
if (!u->cgroup_path)
return -ENODATA;
- if (cg_unified() > 0) {
+ if (cg_all_unified() > 0) {
const char *keys[] = { "usage_usec", NULL };
_cleanup_free_ char *val = NULL;
uint64_t us;
@@ -2038,7 +2052,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;
@@ -2047,7 +2075,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;
}
@@ -2057,6 +2088,8 @@ int unit_reset_cpu_usage(Unit *u) {
assert(u);
+ u->cpu_usage_last = NSEC_INFINITY;
+
r = unit_get_cpu_usage_raw(u, &ns);
if (r < 0) {
u->cpu_usage_base = 0;
diff --git a/src/core/cgroup.h b/src/core/cgroup.h
index 2fe9cc4039..4cd168f63e 100644
--- a/src/core/cgroup.h
+++ b/src/core/cgroup.h
@@ -101,6 +101,7 @@ struct CGroupContext {
uint64_t memory_low;
uint64_t memory_high;
uint64_t memory_max;
+ uint64_t memory_swap_max;
/* For legacy hierarchies */
uint64_t cpu_shares;
diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c
index 2ca80d2996..c4067a95bf 100644
--- a/src/core/dbus-cgroup.c
+++ b/src/core/dbus-cgroup.c
@@ -233,6 +233,7 @@ const sd_bus_vtable bus_cgroup_vtable[] = {
SD_BUS_PROPERTY("MemoryLow", "t", NULL, offsetof(CGroupContext, memory_low), 0),
SD_BUS_PROPERTY("MemoryHigh", "t", NULL, offsetof(CGroupContext, memory_high), 0),
SD_BUS_PROPERTY("MemoryMax", "t", NULL, offsetof(CGroupContext, memory_max), 0),
+ SD_BUS_PROPERTY("MemorySwapMax", "t", NULL, offsetof(CGroupContext, memory_swap_max), 0),
SD_BUS_PROPERTY("MemoryLimit", "t", NULL, offsetof(CGroupContext, memory_limit), 0),
SD_BUS_PROPERTY("DevicePolicy", "s", property_get_cgroup_device_policy, offsetof(CGroupContext, device_policy), 0),
SD_BUS_PROPERTY("DeviceAllow", "a(ss)", property_get_device_allow, 0, 0),
@@ -875,7 +876,7 @@ int bus_cgroup_set_property(
return 1;
- } else if (STR_IN_SET(name, "MemoryLow", "MemoryHigh", "MemoryMax")) {
+ } else if (STR_IN_SET(name, "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax")) {
uint64_t v;
r = sd_bus_message_read(message, "t", &v);
@@ -889,6 +890,8 @@ int bus_cgroup_set_property(
c->memory_low = v;
else if (streq(name, "MemoryHigh"))
c->memory_high = v;
+ else if (streq(name, "MemorySwapMax"))
+ c->memory_swap_max = v;
else
c->memory_max = v;
diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c
index ef05a75a8b..ea7ced2fd0 100644
--- a/src/core/dbus-manager.c
+++ b/src/core/dbus-manager.c
@@ -643,6 +643,54 @@ static int method_set_unit_properties(sd_bus_message *message, void *userdata, s
return bus_unit_method_set_properties(message, u, error);
}
+static int method_ref_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *m = userdata;
+ const char *name;
+ Unit *u;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_read(message, "s", &name);
+ if (r < 0)
+ return r;
+
+ r = manager_load_unit(m, name, NULL, error, &u);
+ if (r < 0)
+ return r;
+
+ r = bus_unit_check_load_state(u, error);
+ if (r < 0)
+ return r;
+
+ return bus_unit_method_ref(message, u, error);
+}
+
+static int method_unref_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *m = userdata;
+ const char *name;
+ Unit *u;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_read(message, "s", &name);
+ if (r < 0)
+ return r;
+
+ r = manager_load_unit(m, name, NULL, error, &u);
+ if (r < 0)
+ return r;
+
+ r = bus_unit_check_load_state(u, error);
+ if (r < 0)
+ return r;
+
+ return bus_unit_method_unref(message, u, error);
+}
+
static int reply_unit_info(sd_bus_message *reply, Unit *u) {
_cleanup_free_ char *unit_path = NULL, *job_path = NULL;
Unit *following;
@@ -781,6 +829,13 @@ static int transient_unit_from_message(
if (r < 0)
return r;
+ /* If the client asked for it, automatically add a reference to this unit. */
+ if (u->bus_track_add) {
+ r = bus_unit_track_add_sender(u, message);
+ if (r < 0)
+ return log_error_errno(r, "Failed to watch sender: %m");
+ }
+
/* Now load the missing bits of the unit we just created */
unit_add_to_load_queue(u);
manager_dispatch_load_queue(m);
@@ -2211,6 +2266,8 @@ const sd_bus_vtable bus_manager_vtable[] = {
SD_BUS_METHOD("KillUnit", "ssi", NULL, method_kill_unit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ResetFailedUnit", "s", NULL, method_reset_failed_unit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetUnitProperties", "sba(sv)", NULL, method_set_unit_properties, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("RefUnit", "s", NULL, method_ref_unit, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("UnrefUnit", "s", NULL, method_unref_unit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("StartTransientUnit", "ssa(sv)a(sa(sv))", "o", method_start_transient_unit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetUnitProcesses", "s", "a(sus)", method_get_unit_processes, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetJob", "u", "o", method_get_job, SD_BUS_VTABLE_UNPRIVILEGED),
diff --git a/src/core/dbus-mount.c b/src/core/dbus-mount.c
index 3c6bda4073..76a7a7ce97 100644
--- a/src/core/dbus-mount.c
+++ b/src/core/dbus-mount.c
@@ -116,6 +116,8 @@ const sd_bus_vtable bus_mount_vtable[] = {
SD_BUS_PROPERTY("ControlPID", "u", bus_property_get_pid, offsetof(Mount, control_pid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("DirectoryMode", "u", bus_property_get_mode, offsetof(Mount, directory_mode), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SloppyOptions", "b", bus_property_get_bool, offsetof(Mount, sloppy_options), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LazyUnmount", "b", bus_property_get_bool, offsetof(Mount, lazy_unmount), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("ForceUnmount", "b", bus_property_get_bool, offsetof(Mount, force_unmount), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Mount, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("UID", "u", NULL, offsetof(Unit, ref_uid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("GID", "u", NULL, offsetof(Unit, ref_gid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c
index 89e56a2e51..1b86bdde43 100644
--- a/src/core/dbus-unit.c
+++ b/src/core/dbus-unit.c
@@ -418,6 +418,7 @@ static int bus_verify_manage_units_async_full(
const char *verb,
int capability,
const char *polkit_message,
+ bool interactive,
sd_bus_message *call,
sd_bus_error *error) {
@@ -433,7 +434,15 @@ static int bus_verify_manage_units_async_full(
details[7] = GETTEXT_PACKAGE;
}
- return bus_verify_polkit_async(call, capability, "org.freedesktop.systemd1.manage-units", details, false, UID_INVALID, &u->manager->polkit_registry, error);
+ return bus_verify_polkit_async(
+ call,
+ capability,
+ "org.freedesktop.systemd1.manage-units",
+ details,
+ interactive,
+ UID_INVALID,
+ &u->manager->polkit_registry,
+ error);
}
int bus_unit_method_start_generic(
@@ -486,6 +495,7 @@ int bus_unit_method_start_generic(
verb,
CAP_SYS_ADMIN,
job_type < _JOB_TYPE_MAX ? polkit_message_for_job[job_type] : NULL,
+ true,
message,
error);
if (r < 0)
@@ -558,6 +568,7 @@ int bus_unit_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *
"kill",
CAP_KILL,
N_("Authentication is required to kill '$(unit)'."),
+ true,
message,
error);
if (r < 0)
@@ -588,6 +599,7 @@ int bus_unit_method_reset_failed(sd_bus_message *message, void *userdata, sd_bus
"reset-failed",
CAP_SYS_ADMIN,
N_("Authentication is required to reset the \"failed\" state of '$(unit)'."),
+ true,
message,
error);
if (r < 0)
@@ -620,6 +632,7 @@ int bus_unit_method_set_properties(sd_bus_message *message, void *userdata, sd_b
"set-property",
CAP_SYS_ADMIN,
N_("Authentication is required to set properties on '$(unit)'."),
+ true,
message,
error);
if (r < 0)
@@ -634,6 +647,53 @@ int bus_unit_method_set_properties(sd_bus_message *message, void *userdata, sd_b
return sd_bus_reply_method_return(message, NULL);
}
+int bus_unit_method_ref(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Unit *u = userdata;
+ int r;
+
+ assert(message);
+ assert(u);
+
+ r = mac_selinux_unit_access_check(u, message, "start", error);
+ if (r < 0)
+ return r;
+
+ r = bus_verify_manage_units_async_full(
+ u,
+ "ref",
+ CAP_SYS_ADMIN,
+ NULL,
+ false,
+ message,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
+
+ r = bus_unit_track_add_sender(u, message);
+ if (r < 0)
+ return r;
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
+int bus_unit_method_unref(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Unit *u = userdata;
+ int r;
+
+ assert(message);
+ assert(u);
+
+ r = bus_unit_track_remove_sender(u, message);
+ if (r == -EUNATCH)
+ return sd_bus_error_setf(error, BUS_ERROR_NOT_REFERENCED, "Unit has not been referenced yet.");
+ if (r < 0)
+ return r;
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
const sd_bus_vtable bus_unit_vtable[] = {
SD_BUS_VTABLE_START(0),
@@ -715,6 +775,8 @@ const sd_bus_vtable bus_unit_vtable[] = {
SD_BUS_METHOD("Kill", "si", NULL, bus_unit_method_kill, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ResetFailed", NULL, NULL, bus_unit_method_reset_failed, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetProperties", "ba(sv)", NULL, bus_unit_method_set_properties, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("Ref", NULL, NULL, bus_unit_method_ref, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("Unref", NULL, NULL, bus_unit_method_unref, SD_BUS_VTABLE_UNPRIVILEGED),
/* Obsolete properties or obsolete alias names */
SD_BUS_PROPERTY("RequiresOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN),
@@ -1318,6 +1380,29 @@ static int bus_unit_set_transient_property(
return r;
return 1;
+
+ } else if (streq(name, "AddRef")) {
+
+ int b;
+
+ /* Why is this called "AddRef" rather than just "Ref", or "Reference"? There's already a "Ref()" method
+ * on the Unit interface, and it's probably not a good idea to expose a property and a method on the
+ * same interface (well, strictly speaking AddRef isn't exposed as full property, we just read it for
+ * transient units, but still). And "References" and "ReferencedBy" is already used as unit reference
+ * dependency type, hence let's not confuse things with that.
+ *
+ * Note that we don't acually add the reference to the bus track. We do that only after the setup of
+ * the transient unit is complete, so that setting this property multiple times in the same transient
+ * unit creation call doesn't count as individual references. */
+
+ r = sd_bus_message_read(message, "b", &b);
+ if (r < 0)
+ return r;
+
+ if (mode != UNIT_CHECK)
+ u->bus_track_add = b;
+
+ return 1;
}
return 0;
@@ -1422,3 +1507,71 @@ int bus_unit_check_load_state(Unit *u, sd_bus_error *error) {
return sd_bus_error_set_errnof(error, u->load_error, "Unit %s is not loaded properly: %m.", u->id);
}
+
+static int bus_track_handler(sd_bus_track *t, void *userdata) {
+ Unit *u = userdata;
+
+ assert(t);
+ assert(u);
+
+ u->bus_track = sd_bus_track_unref(u->bus_track); /* make sure we aren't called again */
+
+ unit_add_to_gc_queue(u);
+ return 0;
+}
+
+static int allocate_bus_track(Unit *u) {
+ int r;
+
+ assert(u);
+
+ if (u->bus_track)
+ return 0;
+
+ r = sd_bus_track_new(u->manager->api_bus, &u->bus_track, bus_track_handler, u);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_track_set_recursive(u->bus_track, true);
+ if (r < 0) {
+ u->bus_track = sd_bus_track_unref(u->bus_track);
+ return r;
+ }
+
+ return 0;
+}
+
+int bus_unit_track_add_name(Unit *u, const char *name) {
+ int r;
+
+ assert(u);
+
+ r = allocate_bus_track(u);
+ if (r < 0)
+ return r;
+
+ return sd_bus_track_add_name(u->bus_track, name);
+}
+
+int bus_unit_track_add_sender(Unit *u, sd_bus_message *m) {
+ int r;
+
+ assert(u);
+
+ r = allocate_bus_track(u);
+ if (r < 0)
+ return r;
+
+ return sd_bus_track_add_sender(u->bus_track, m);
+}
+
+int bus_unit_track_remove_sender(Unit *u, sd_bus_message *m) {
+ assert(u);
+
+ /* If we haven't allocated the bus track object yet, then there's definitely no reference taken yet, return an
+ * error */
+ if (!u->bus_track)
+ return -EUNATCH;
+
+ return sd_bus_track_remove_sender(u->bus_track, m);
+}
diff --git a/src/core/dbus-unit.h b/src/core/dbus-unit.h
index 4db88dbebc..b280de7a1d 100644
--- a/src/core/dbus-unit.h
+++ b/src/core/dbus-unit.h
@@ -33,9 +33,15 @@ int bus_unit_method_start_generic(sd_bus_message *message, Unit *u, JobType job_
int bus_unit_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_unit_method_reset_failed(sd_bus_message *message, void *userdata, sd_bus_error *error);
-int bus_unit_queue_job(sd_bus_message *message, Unit *u, JobType type, JobMode mode, bool reload_if_possible, sd_bus_error *error);
int bus_unit_set_properties(Unit *u, sd_bus_message *message, UnitSetPropertiesMode mode, bool commit, sd_bus_error *error);
int bus_unit_method_set_properties(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_unit_method_get_processes(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_unit_method_ref(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_unit_method_unref(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_unit_queue_job(sd_bus_message *message, Unit *u, JobType type, JobMode mode, bool reload_if_possible, sd_bus_error *error);
int bus_unit_check_load_state(Unit *u, sd_bus_error *error);
+
+int bus_unit_track_add_name(Unit *u, const char *name);
+int bus_unit_track_add_sender(Unit *u, sd_bus_message *m);
+int bus_unit_track_remove_sender(Unit *u, sd_bus_message *m);
diff --git a/src/core/dbus.c b/src/core/dbus.c
index 3422a02d68..1e41a42aa6 100644
--- a/src/core/dbus.c
+++ b/src/core/dbus.c
@@ -1168,60 +1168,57 @@ int bus_foreach_bus(
return ret;
}
-void bus_track_serialize(sd_bus_track *t, FILE *f) {
+void bus_track_serialize(sd_bus_track *t, FILE *f, const char *prefix) {
const char *n;
assert(f);
+ assert(prefix);
- for (n = sd_bus_track_first(t); n; n = sd_bus_track_next(t))
- fprintf(f, "subscribed=%s\n", n);
-}
-
-int bus_track_deserialize_item(char ***l, const char *line) {
- const char *e;
- int r;
-
- assert(l);
- assert(line);
-
- e = startswith(line, "subscribed=");
- if (!e)
- return 0;
+ for (n = sd_bus_track_first(t); n; n = sd_bus_track_next(t)) {
+ int c, j;
- r = strv_extend(l, e);
- if (r < 0)
- return r;
+ c = sd_bus_track_count_name(t, n);
- return 1;
+ for (j = 0; j < c; j++) {
+ fputs(prefix, f);
+ fputc('=', f);
+ fputs(n, f);
+ fputc('\n', f);
+ }
+ }
}
-int bus_track_coldplug(Manager *m, sd_bus_track **t, char ***l) {
+int bus_track_coldplug(Manager *m, sd_bus_track **t, bool recursive, char **l) {
+ char **i;
int r = 0;
assert(m);
assert(t);
- assert(l);
-
- if (!strv_isempty(*l) && m->api_bus) {
- char **i;
- if (!*t) {
- r = sd_bus_track_new(m->api_bus, t, NULL, NULL);
- if (r < 0)
- return r;
- }
+ if (strv_isempty(l))
+ return 0;
- r = 0;
- STRV_FOREACH(i, *l) {
- int k;
+ if (!m->api_bus)
+ return 0;
- k = sd_bus_track_add_name(*t, *i);
- if (k < 0)
- r = k;
- }
+ if (!*t) {
+ r = sd_bus_track_new(m->api_bus, t, NULL, NULL);
+ if (r < 0)
+ return r;
}
- *l = strv_free(*l);
+ r = sd_bus_track_set_recursive(*t, recursive);
+ if (r < 0)
+ return r;
+
+ r = 0;
+ STRV_FOREACH(i, l) {
+ int k;
+
+ k = sd_bus_track_add_name(*t, *i);
+ if (k < 0)
+ r = k;
+ }
return r;
}
diff --git a/src/core/dbus.h b/src/core/dbus.h
index 6baaffbd75..a092ed9d76 100644
--- a/src/core/dbus.h
+++ b/src/core/dbus.h
@@ -28,9 +28,8 @@ void bus_done(Manager *m);
int bus_fdset_add_all(Manager *m, FDSet *fds);
-void bus_track_serialize(sd_bus_track *t, FILE *f);
-int bus_track_deserialize_item(char ***l, const char *line);
-int bus_track_coldplug(Manager *m, sd_bus_track **t, char ***l);
+void bus_track_serialize(sd_bus_track *t, FILE *f, const char *prefix);
+int bus_track_coldplug(Manager *m, sd_bus_track **t, bool recursive, char **l);
int manager_sync_bus_names(Manager *m, sd_bus *bus);
diff --git a/src/core/execute.c b/src/core/execute.c
index 0af8eb5a02..55f15d7e49 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -1074,7 +1074,17 @@ static void rename_process_from_path(const char *path) {
#ifdef HAVE_SECCOMP
-static int apply_seccomp(const ExecContext *c) {
+static bool skip_seccomp_unavailable(const Unit* u, const char* msg) {
+ if (!is_seccomp_available()) {
+ log_open();
+ log_unit_debug(u, "SECCOMP not detected in the kernel, skipping %s", msg);
+ log_close();
+ return true;
+ }
+ return false;
+}
+
+static int apply_seccomp(const Unit* u, const ExecContext *c) {
uint32_t negative_action, action;
scmp_filter_ctx *seccomp;
Iterator i;
@@ -1083,6 +1093,9 @@ static int apply_seccomp(const ExecContext *c) {
assert(c);
+ if (skip_seccomp_unavailable(u, "syscall filtering"))
+ return 0;
+
negative_action = c->syscall_errno == 0 ? SCMP_ACT_KILL : SCMP_ACT_ERRNO(c->syscall_errno);
seccomp = seccomp_init(c->syscall_whitelist ? negative_action : SCMP_ACT_ALLOW);
@@ -1123,13 +1136,16 @@ finish:
return r;
}
-static int apply_address_families(const ExecContext *c) {
+static int apply_address_families(const Unit* u, const ExecContext *c) {
scmp_filter_ctx *seccomp;
Iterator i;
int r;
assert(c);
+ if (skip_seccomp_unavailable(u, "RestrictAddressFamilies="))
+ return 0;
+
seccomp = seccomp_init(SCMP_ACT_ALLOW);
if (!seccomp)
return -ENOMEM;
@@ -1244,12 +1260,15 @@ finish:
return r;
}
-static int apply_memory_deny_write_execute(const ExecContext *c) {
+static int apply_memory_deny_write_execute(const Unit* u, const ExecContext *c) {
scmp_filter_ctx *seccomp;
int r;
assert(c);
+ if (skip_seccomp_unavailable(u, "MemoryDenyWriteExecute="))
+ return 0;
+
seccomp = seccomp_init(SCMP_ACT_ALLOW);
if (!seccomp)
return -ENOMEM;
@@ -1283,7 +1302,7 @@ finish:
return r;
}
-static int apply_restrict_realtime(const ExecContext *c) {
+static int apply_restrict_realtime(const Unit* u, const ExecContext *c) {
static const int permitted_policies[] = {
SCHED_OTHER,
SCHED_BATCH,
@@ -1296,6 +1315,9 @@ static int apply_restrict_realtime(const ExecContext *c) {
assert(c);
+ if (skip_seccomp_unavailable(u, "RestrictRealtime="))
+ return 0;
+
seccomp = seccomp_init(SCMP_ACT_ALLOW);
if (!seccomp)
return -ENOMEM;
@@ -2403,7 +2425,7 @@ static int exec_child(
#ifdef HAVE_SECCOMP
if (use_address_families) {
- r = apply_address_families(context);
+ r = apply_address_families(unit, context);
if (r < 0) {
*exit_status = EXIT_ADDRESS_FAMILIES;
return r;
@@ -2411,7 +2433,7 @@ static int exec_child(
}
if (context->memory_deny_write_execute) {
- r = apply_memory_deny_write_execute(context);
+ r = apply_memory_deny_write_execute(unit, context);
if (r < 0) {
*exit_status = EXIT_SECCOMP;
return r;
@@ -2419,7 +2441,7 @@ static int exec_child(
}
if (context->restrict_realtime) {
- r = apply_restrict_realtime(context);
+ r = apply_restrict_realtime(unit, context);
if (r < 0) {
*exit_status = EXIT_SECCOMP;
return r;
@@ -2427,7 +2449,7 @@ static int exec_child(
}
if (use_syscall_filter) {
- r = apply_seccomp(context);
+ r = apply_seccomp(unit, context);
if (r < 0) {
*exit_status = EXIT_SECCOMP;
return r;
diff --git a/src/core/job.c b/src/core/job.c
index 7557874d4d..7faf2ef686 100644
--- a/src/core/job.c
+++ b/src/core/job.c
@@ -997,7 +997,10 @@ char *job_dbus_path(Job *j) {
return p;
}
-int job_serialize(Job *j, FILE *f, FDSet *fds) {
+int job_serialize(Job *j, FILE *f) {
+ assert(j);
+ assert(f);
+
fprintf(f, "job-id=%u\n", j->id);
fprintf(f, "job-type=%s\n", job_type_to_string(j->type));
fprintf(f, "job-state=%s\n", job_state_to_string(j->state));
@@ -1008,15 +1011,16 @@ int job_serialize(Job *j, FILE *f, FDSet *fds) {
if (j->begin_usec > 0)
fprintf(f, "job-begin="USEC_FMT"\n", j->begin_usec);
- bus_track_serialize(j->clients, f);
+ bus_track_serialize(j->clients, f, "subscribed");
/* End marker */
fputc('\n', f);
return 0;
}
-int job_deserialize(Job *j, FILE *f, FDSet *fds) {
+int job_deserialize(Job *j, FILE *f) {
assert(j);
+ assert(f);
for (;;) {
char line[LINE_MAX], *l, *v;
@@ -1106,7 +1110,7 @@ int job_deserialize(Job *j, FILE *f, FDSet *fds) {
} else if (streq(l, "subscribed")) {
if (strv_extend(&j->deserialized_clients, v) < 0)
- return log_oom();
+ log_oom();
}
}
}
@@ -1118,9 +1122,8 @@ int job_coldplug(Job *j) {
/* After deserialization is complete and the bus connection
* set up again, let's start watching our subscribers again */
- r = bus_track_coldplug(j->manager, &j->clients, &j->deserialized_clients);
- if (r < 0)
- return r;
+ (void) bus_track_coldplug(j->manager, &j->clients, false, j->deserialized_clients);
+ j->deserialized_clients = strv_free(j->deserialized_clients);
if (j->state == JOB_WAITING)
job_add_to_run_queue(j);
diff --git a/src/core/job.h b/src/core/job.h
index d359e8bb3e..85368f0d30 100644
--- a/src/core/job.h
+++ b/src/core/job.h
@@ -177,8 +177,8 @@ Job* job_install(Job *j);
int job_install_deserialized(Job *j);
void job_uninstall(Job *j);
void job_dump(Job *j, FILE*f, const char *prefix);
-int job_serialize(Job *j, FILE *f, FDSet *fds);
-int job_deserialize(Job *j, FILE *f, FDSet *fds);
+int job_serialize(Job *j, FILE *f);
+int job_deserialize(Job *j, FILE *f);
int job_coldplug(Job *j);
JobDependency* job_dependency_new(Job *subject, Job *object, bool matters, bool conflicts);
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index 05fe0df7e3..2e6c965aec 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -131,6 +131,7 @@ $1.MemoryAccounting, config_parse_bool, 0,
$1.MemoryLow, config_parse_memory_limit, 0, offsetof($1, cgroup_context)
$1.MemoryHigh, config_parse_memory_limit, 0, offsetof($1, cgroup_context)
$1.MemoryMax, config_parse_memory_limit, 0, offsetof($1, cgroup_context)
+$1.MemorySwapMax, config_parse_memory_limit, 0, offsetof($1, cgroup_context)
$1.MemoryLimit, config_parse_memory_limit, 0, offsetof($1, cgroup_context)
$1.DeviceAllow, config_parse_device_allow, 0, offsetof($1, cgroup_context)
$1.DevicePolicy, config_parse_device_policy, 0, offsetof($1, cgroup_context.device_policy)
@@ -355,6 +356,8 @@ Mount.Type, config_parse_string, 0,
Mount.TimeoutSec, config_parse_sec, 0, offsetof(Mount, timeout_usec)
Mount.DirectoryMode, config_parse_mode, 0, offsetof(Mount, directory_mode)
Mount.SloppyOptions, config_parse_bool, 0, offsetof(Mount, sloppy_options)
+Mount.LazyUnmount, config_parse_bool, 0, offsetof(Mount, lazy_unmount)
+Mount.ForceUnmount, config_parse_bool, 0, offsetof(Mount, force_unmount)
EXEC_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl
CGROUP_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl
KILL_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index d5185cf6a0..8f067b5586 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -1338,10 +1338,13 @@ int config_parse_timer(const char *unit,
void *userdata) {
Timer *t = data;
- usec_t u = 0;
+ usec_t usec = 0;
TimerValue *v;
TimerBase b;
CalendarSpec *c = NULL;
+ Unit *u = userdata;
+ _cleanup_free_ char *k = NULL;
+ int r;
assert(filename);
assert(lvalue);
@@ -1360,14 +1363,20 @@ int config_parse_timer(const char *unit,
return 0;
}
+ r = unit_full_printf(u, rvalue, &k);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
+ return 0;
+ }
+
if (b == TIMER_CALENDAR) {
- if (calendar_spec_from_string(rvalue, &c) < 0) {
- log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse calendar specification, ignoring: %s", rvalue);
+ if (calendar_spec_from_string(k, &c) < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse calendar specification, ignoring: %s", k);
return 0;
}
} else {
- if (parse_sec(rvalue, &u) < 0) {
- log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse timer value, ignoring: %s", rvalue);
+ if (parse_sec(k, &usec) < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse timer value, ignoring: %s", k);
return 0;
}
}
@@ -1379,7 +1388,7 @@ int config_parse_timer(const char *unit,
}
v->base = b;
- v->value = u;
+ v->value = usec;
v->calendar_spec = c;
LIST_PREPEND(value, t->values, v);
@@ -2981,8 +2990,12 @@ int config_parse_memory_limit(
c->memory_high = bytes;
else if (streq(lvalue, "MemoryMax"))
c->memory_max = bytes;
- else
+ else if (streq(lvalue, "MemorySwapMax"))
+ c->memory_swap_max = bytes;
+ else if (streq(lvalue, "MemoryLimit"))
c->memory_limit = bytes;
+ else
+ return -EINVAL;
return 0;
}
diff --git a/src/core/main.c b/src/core/main.c
index 125cfb28f0..7d8322ebd8 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -72,6 +72,9 @@
#include "process-util.h"
#include "raw-clone.h"
#include "rlimit-util.h"
+#ifdef HAVE_SECCOMP
+#include "seccomp-util.h"
+#endif
#include "selinux-setup.h"
#include "selinux-util.h"
#include "signal-util.h"
@@ -1186,6 +1189,9 @@ static int enforce_syscall_archs(Set *archs) {
void *id;
int r;
+ if (!is_seccomp_available())
+ return 0;
+
seccomp = seccomp_init(SCMP_ACT_ALLOW);
if (!seccomp)
return log_oom();
diff --git a/src/core/manager.c b/src/core/manager.c
index 6f2477eef4..b58f68fa7a 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -771,7 +771,7 @@ static int manager_setup_cgroups_agent(Manager *m) {
if (!MANAGER_IS_SYSTEM(m))
return 0;
- if (cg_unified() > 0) /* We don't need this anymore on the unified hierarchy */
+ if (cg_unified(SYSTEMD_CGROUP_CONTROLLER) > 0) /* We don't need this anymore on the unified hierarchy */
return 0;
if (m->cgroups_agent_fd < 0) {
@@ -1287,10 +1287,11 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
if (q < 0 && r == 0)
r = q;
- /* We might have deserialized the kdbus control fd, but if we
- * didn't, then let's create the bus now. */
- manager_connect_bus(m, !!serialization);
- bus_track_coldplug(m, &m->subscribed, &m->deserialized_subscribed);
+ /* We might have deserialized the kdbus control fd, but if we didn't, then let's create the bus now. */
+ (void) manager_connect_bus(m, !!serialization);
+
+ (void) bus_track_coldplug(m, &m->subscribed, false, m->deserialized_subscribed);
+ m->deserialized_subscribed = strv_free(m->deserialized_subscribed);
/* Third, fire things up! */
manager_coldplug(m);
@@ -2490,7 +2491,7 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) {
fprintf(f, "kdbus-fd=%i\n", copy);
}
- bus_track_serialize(m->subscribed, f);
+ bus_track_serialize(m->subscribed, f, "subscribed");
r = dynamic_user_serialize(m, f, fds);
if (r < 0)
@@ -2693,15 +2694,13 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
manager_deserialize_uid_refs_one(m, l + 16);
else if (startswith(l, "destroy-ipc-gid="))
manager_deserialize_gid_refs_one(m, l + 16);
- else {
- int k;
-
- k = bus_track_deserialize_item(&m->deserialized_subscribed, l);
- if (k < 0)
- log_debug_errno(k, "Failed to deserialize bus tracker object: %m");
- else if (k == 0)
- log_debug("Unknown serialization item '%s'", l);
- }
+ else if (startswith(l, "subscribed=")) {
+
+ if (strv_extend(&m->deserialized_subscribed, l+11) < 0)
+ log_oom();
+
+ } else
+ log_debug("Unknown serialization item '%s'", l);
}
for (;;) {
diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c
index 5d8ab0ec70..ca63a93e8b 100644
--- a/src/core/mount-setup.c
+++ b/src/core/mount-setup.c
@@ -99,10 +99,12 @@ static const MountPoint mount_table[] = {
cg_is_unified_wanted, MNT_FATAL|MNT_IN_CONTAINER },
{ "tmpfs", "/sys/fs/cgroup", "tmpfs", "mode=755", MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME,
cg_is_legacy_wanted, MNT_FATAL|MNT_IN_CONTAINER },
+ { "cgroup", "/sys/fs/cgroup/systemd", "cgroup2", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
+ cg_is_unified_systemd_controller_wanted, MNT_IN_CONTAINER },
{ "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd,xattr", MS_NOSUID|MS_NOEXEC|MS_NODEV,
- cg_is_legacy_wanted, MNT_IN_CONTAINER },
+ cg_is_legacy_systemd_controller_wanted, MNT_IN_CONTAINER },
{ "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd", MS_NOSUID|MS_NOEXEC|MS_NODEV,
- cg_is_legacy_wanted, MNT_FATAL|MNT_IN_CONTAINER },
+ cg_is_legacy_systemd_controller_wanted, MNT_FATAL|MNT_IN_CONTAINER },
{ "pstore", "/sys/fs/pstore", "pstore", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
NULL, MNT_NONE },
#ifdef ENABLE_EFI
diff --git a/src/core/mount.c b/src/core/mount.c
index f2ac8d171f..04025b83b9 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -677,7 +677,10 @@ static void mount_dump(Unit *u, FILE *f, const char *prefix) {
"%sOptions: %s\n"
"%sFrom /proc/self/mountinfo: %s\n"
"%sFrom fragment: %s\n"
- "%sDirectoryMode: %04o\n",
+ "%sDirectoryMode: %04o\n"
+ "%sSloppyOptions: %s\n"
+ "%sLazyUnmount: %s\n"
+ "%sForceUnmount: %s\n",
prefix, mount_state_to_string(m->state),
prefix, mount_result_to_string(m->result),
prefix, m->where,
@@ -686,7 +689,10 @@ static void mount_dump(Unit *u, FILE *f, const char *prefix) {
prefix, p ? strna(p->options) : "n/a",
prefix, yes_no(m->from_proc_self_mountinfo),
prefix, yes_no(m->from_fragment),
- prefix, m->directory_mode);
+ prefix, m->directory_mode,
+ prefix, yes_no(m->sloppy_options),
+ prefix, yes_no(m->lazy_unmount),
+ prefix, yes_no(m->force_unmount));
if (m->control_pid > 0)
fprintf(f,
@@ -846,6 +852,10 @@ static void mount_enter_unmounting(Mount *m) {
m->control_command = m->exec_command + MOUNT_EXEC_UNMOUNT;
r = exec_command_set(m->control_command, UMOUNT_PATH, m->where, NULL);
+ if (r >= 0 && m->lazy_unmount)
+ r = exec_command_append(m->control_command, "-l", NULL);
+ if (r >= 0 && m->force_unmount)
+ r = exec_command_append(m->control_command, "-f", NULL);
if (r < 0)
goto fail;
diff --git a/src/core/mount.h b/src/core/mount.h
index ac27b518cc..9f7326ba6a 100644
--- a/src/core/mount.h
+++ b/src/core/mount.h
@@ -71,6 +71,9 @@ struct Mount {
bool sloppy_options;
+ bool lazy_unmount;
+ bool force_unmount;
+
MountResult result;
MountResult reload_result;
diff --git a/src/core/org.freedesktop.systemd1.conf b/src/core/org.freedesktop.systemd1.conf
index 14f6aec029..647e5f736c 100644
--- a/src/core/org.freedesktop.systemd1.conf
+++ b/src/core/org.freedesktop.systemd1.conf
@@ -184,6 +184,14 @@
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Manager"
+ send_member="RefUnit"/>
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Manager"
+ send_member="UnrefUnit"/>
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Manager"
send_member="EnableUnitFiles"/>
<allow send_destination="org.freedesktop.systemd1"
diff --git a/src/core/scope.c b/src/core/scope.c
index b278aed3d6..65fa65493b 100644
--- a/src/core/scope.c
+++ b/src/core/scope.c
@@ -441,7 +441,7 @@ static void scope_sigchld_event(Unit *u, pid_t pid, int code, int status) {
/* If the PID set is empty now, then let's finish this off
(On unified we use proper notifications) */
- if (cg_unified() <= 0 && set_isempty(u->pids))
+ if (cg_unified(SYSTEMD_CGROUP_CONTROLLER) <= 0 && set_isempty(u->pids))
scope_notify_cgroup_empty_event(u);
}
diff --git a/src/core/selinux-access.h b/src/core/selinux-access.h
index 8f1f058a32..f46370d020 100644
--- a/src/core/selinux-access.h
+++ b/src/core/selinux-access.h
@@ -33,7 +33,7 @@ int mac_selinux_generic_access_check(sd_bus_message *message, const char *path,
#define mac_selinux_unit_access_check(unit, message, permission, error) \
({ \
- Unit *_unit = (unit); \
+ const Unit *_unit = (unit); \
mac_selinux_generic_access_check((message), _unit->source_path ?: _unit->fragment_path, (permission), (error)); \
})
diff --git a/src/core/service.c b/src/core/service.c
index 1951ba9222..969c62bd83 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -2869,7 +2869,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
/* If the PID set is empty now, then let's finish this off
(On unified we use proper notifications) */
- if (cg_unified() <= 0 && set_isempty(u->pids))
+ if (cg_unified(SYSTEMD_CGROUP_CONTROLLER) <= 0 && set_isempty(u->pids))
service_notify_cgroup_empty_event(u);
}
diff --git a/src/core/unit.c b/src/core/unit.c
index 4b8d81c3f1..de22f657c6 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -102,6 +102,7 @@ Unit *unit_new(Manager *m, size_t size) {
u->job_timeout = USEC_INFINITY;
u->ref_uid = UID_INVALID;
u->ref_gid = GID_INVALID;
+ u->cpu_usage_last = NSEC_INFINITY;
RATELIMIT_INIT(u->start_limit, m->default_start_limit_interval, m->default_start_limit_burst);
RATELIMIT_INIT(u->auto_stop_ratelimit, 10 * USEC_PER_SEC, 16);
@@ -113,7 +114,7 @@ bool unit_has_name(Unit *u, const char *name) {
assert(u);
assert(name);
- return !!set_get(u->names, (char*) name);
+ return set_contains(u->names, (char*) name);
}
static void unit_init(Unit *u) {
@@ -329,6 +330,9 @@ bool unit_check_gc(Unit *u) {
if (u->refs)
return true;
+ if (sd_bus_track_count(u->bus_track) > 0)
+ return true;
+
if (UNIT_VTABLE(u)->check_gc)
if (UNIT_VTABLE(u)->check_gc(u))
return true;
@@ -509,6 +513,9 @@ void unit_free(Unit *u) {
sd_bus_slot_unref(u->match_bus_slot);
+ sd_bus_track_unref(u->bus_track);
+ u->deserialized_refs = strv_free(u->deserialized_refs);
+
unit_free_requires_mounts_for(u);
SET_FOREACH(t, u->names, i)
@@ -897,6 +904,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
Unit *following;
_cleanup_set_free_ Set *following_set = NULL;
int r;
+ const char *n;
assert(u);
assert(u->type >= 0);
@@ -1038,6 +1046,8 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
else if (u->load_state == UNIT_ERROR)
fprintf(f, "%s\tLoad Error Code: %s\n", prefix, strerror(-u->load_error));
+ for (n = sd_bus_track_first(u->bus_track); n; n = sd_bus_track_next(u->bus_track))
+ fprintf(f, "%s\tBus Ref: %s\n", prefix, n);
if (u->job)
job_dump(u->job, f, prefix2);
@@ -2611,7 +2621,10 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
unit_serialize_item(u, f, "assert-result", yes_no(u->assert_result));
unit_serialize_item(u, f, "transient", yes_no(u->transient));
+
unit_serialize_item_format(u, f, "cpu-usage-base", "%" PRIu64, u->cpu_usage_base);
+ if (u->cpu_usage_last != NSEC_INFINITY)
+ unit_serialize_item_format(u, f, "cpu-usage-last", "%" PRIu64, u->cpu_usage_last);
if (u->cgroup_path)
unit_serialize_item(u, f, "cgroup", u->cgroup_path);
@@ -2622,15 +2635,17 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
if (gid_is_valid(u->ref_gid))
unit_serialize_item_format(u, f, "ref-gid", GID_FMT, u->ref_gid);
+ bus_track_serialize(u->bus_track, f, "ref");
+
if (serialize_jobs) {
if (u->job) {
fprintf(f, "job\n");
- job_serialize(u->job, f, fds);
+ job_serialize(u->job, f);
}
if (u->nop_job) {
fprintf(f, "job\n");
- job_serialize(u->nop_job, f, fds);
+ job_serialize(u->nop_job, f);
}
}
@@ -2760,7 +2775,7 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
if (!j)
return log_oom();
- r = job_deserialize(j, f, fds);
+ r = job_deserialize(j, f);
if (r < 0) {
job_free(j);
return r;
@@ -2832,11 +2847,19 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
continue;
- } else if (streq(l, "cpu-usage-base") || streq(l, "cpuacct-usage-base")) {
+ } else if (STR_IN_SET(l, "cpu-usage-base", "cpuacct-usage-base")) {
r = safe_atou64(v, &u->cpu_usage_base);
if (r < 0)
- log_unit_debug(u, "Failed to parse CPU usage %s, ignoring.", v);
+ log_unit_debug(u, "Failed to parse CPU usage base %s, ignoring.", v);
+
+ continue;
+
+ } else if (streq(l, "cpu-usage-last")) {
+
+ r = safe_atou64(v, &u->cpu_usage_last);
+ if (r < 0)
+ log_unit_debug(u, "Failed to read CPU usage last %s, ignoring.", v);
continue;
@@ -2880,6 +2903,12 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
else
unit_ref_uid_gid(u, UID_INVALID, gid);
+ } else if (streq(l, "ref")) {
+
+ r = strv_extend(&u->deserialized_refs, v);
+ if (r < 0)
+ log_oom();
+
continue;
}
@@ -2955,7 +2984,8 @@ int unit_add_node_link(Unit *u, const char *what, bool wants, UnitDependency dep
}
int unit_coldplug(Unit *u) {
- int r = 0, q = 0;
+ int r = 0, q;
+ char **i;
assert(u);
@@ -2966,18 +2996,26 @@ int unit_coldplug(Unit *u) {
u->coldplugged = true;
- if (UNIT_VTABLE(u)->coldplug)
- r = UNIT_VTABLE(u)->coldplug(u);
+ STRV_FOREACH(i, u->deserialized_refs) {
+ q = bus_unit_track_add_name(u, *i);
+ if (q < 0 && r >= 0)
+ r = q;
+ }
+ u->deserialized_refs = strv_free(u->deserialized_refs);
- if (u->job)
- q = job_coldplug(u->job);
+ if (UNIT_VTABLE(u)->coldplug) {
+ q = UNIT_VTABLE(u)->coldplug(u);
+ if (q < 0 && r >= 0)
+ r = q;
+ }
- if (r < 0)
- return r;
- if (q < 0)
- return q;
+ if (u->job) {
+ q = job_coldplug(u->job);
+ if (q < 0 && r >= 0)
+ r = q;
+ }
- return 0;
+ return r;
}
static bool fragment_mtime_newer(const char *path, usec_t mtime) {
@@ -3726,7 +3764,7 @@ int unit_kill_context(
* there we get proper events. Hence rely on
* them.*/
- if (cg_unified() > 0 ||
+ if (cg_unified(SYSTEMD_CGROUP_CONTROLLER) > 0 ||
(detect_container() == 0 && !unit_cgroup_delegate(u)))
wait_for_exit = true;
diff --git a/src/core/unit.h b/src/core/unit.h
index 53875653d7..3584c16d8c 100644
--- a/src/core/unit.h
+++ b/src/core/unit.h
@@ -108,6 +108,10 @@ struct Unit {
/* The slot used for watching NameOwnerChanged signals */
sd_bus_slot *match_bus_slot;
+ /* References to this unit from clients */
+ sd_bus_track *bus_track;
+ char **deserialized_refs;
+
/* Job timeout and action to take */
usec_t job_timeout;
FailureAction job_timeout_action;
@@ -190,6 +194,7 @@ struct Unit {
/* Where the cpu.stat or cpuacct.usage was at the time the unit was started */
nsec_t cpu_usage_base;
+ nsec_t cpu_usage_last; /* the most recently read value */
/* Counterparts in the cgroup filesystem */
char *cgroup_path;
@@ -247,6 +252,9 @@ struct Unit {
/* Did we already invoke unit_coldplug() for this unit? */
bool coldplugged:1;
+
+ /* For transient units: whether to add a bus track reference after creating the unit */
+ bool bus_track_add:1;
};
struct UnitStatusMessageFormats {
@@ -639,7 +647,7 @@ void unit_notify_user_lookup(Unit *u, uid_t uid, gid_t gid);
#define log_unit_full(unit, level, error, ...) \
({ \
- Unit *_u = (unit); \
+ const Unit *_u = (unit); \
_u ? log_object_internal(level, error, __FILE__, __LINE__, __func__, _u->manager->unit_log_field, _u->id, ##__VA_ARGS__) : \
log_internal(level, error, __FILE__, __LINE__, __func__, ##__VA_ARGS__); \
})
diff --git a/src/import/export-raw.c b/src/import/export-raw.c
index db06e11b87..6136b677dd 100644
--- a/src/import/export-raw.c
+++ b/src/import/export-raw.c
@@ -34,6 +34,7 @@
#include "fd-util.h"
#include "fileio.h"
#include "import-common.h"
+#include "missing.h"
#include "ratelimit.h"
#include "string-util.h"
#include "util.h"
diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c
index 2a043a95b1..3507910919 100644
--- a/src/journal/journald-server.c
+++ b/src/journal/journald-server.c
@@ -267,14 +267,18 @@ static int open_journal(
return r;
}
+static bool flushed_flag_is_set(void) {
+ return (access("/run/systemd/journal/flushed", F_OK) >= 0);
+}
+
static int system_journal_open(Server *s, bool flush_requested) {
+ bool flushed = false;
const char *fn;
int r = 0;
if (!s->system_journal &&
(s->storage == STORAGE_PERSISTENT || s->storage == STORAGE_AUTO) &&
- (flush_requested
- || access("/run/systemd/journal/flushed", F_OK) >= 0)) {
+ (flush_requested || (flushed = flushed_flag_is_set()))) {
/* If in auto mode: first try to create the machine
* path, but not the prefix.
@@ -299,6 +303,16 @@ static int system_journal_open(Server *s, bool flush_requested) {
r = 0;
}
+
+ /* If the runtime journal is open, and we're post-flush, we're
+ * recovering from a failed system journal rotate (ENOSPC)
+ * for which the runtime journal was reopened.
+ *
+ * Perform an implicit flush to var, leaving the runtime
+ * journal closed, now that the system journal is back.
+ */
+ if (s->runtime_journal && flushed)
+ (void) server_flush_to_var(s);
}
if (!s->runtime_journal &&
@@ -1294,7 +1308,7 @@ static int dispatch_sigusr1(sd_event_source *es, const struct signalfd_siginfo *
log_info("Received request to flush runtime journal from PID " PID_FMT, si->ssi_pid);
- server_flush_to_var(s);
+ (void) server_flush_to_var(s);
server_sync(s);
server_vacuum(s, false, false);
diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym
index 542254295c..70ea347361 100644
--- a/src/libsystemd/libsystemd.sym
+++ b/src/libsystemd/libsystemd.sym
@@ -500,3 +500,13 @@ LIBSYSTEMD_231 {
global:
sd_event_get_iteration;
} LIBSYSTEMD_230;
+
+LIBSYSTEMD_232 {
+global:
+ sd_bus_track_set_recursive;
+ sd_bus_track_get_recursive;
+ sd_bus_track_count_name;
+ sd_bus_track_count_sender;
+ sd_bus_set_exit_on_disconnect;
+ sd_bus_get_exit_on_disconnect;
+} LIBSYSTEMD_231;
diff --git a/src/libsystemd/sd-bus/bus-common-errors.c b/src/libsystemd/sd-bus/bus-common-errors.c
index 32be3cdc38..a69193aa32 100644
--- a/src/libsystemd/sd-bus/bus-common-errors.c
+++ b/src/libsystemd/sd-bus/bus-common-errors.c
@@ -45,6 +45,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
SD_BUS_ERROR_MAP(BUS_ERROR_SHUTTING_DOWN, ECANCELED),
SD_BUS_ERROR_MAP(BUS_ERROR_SCOPE_NOT_RUNNING, EHOSTDOWN),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_DYNAMIC_USER, ESRCH),
+ SD_BUS_ERROR_MAP(BUS_ERROR_NOT_REFERENCED, EUNATCH),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_MACHINE, ENXIO),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_IMAGE, ENOENT),
diff --git a/src/libsystemd/sd-bus/bus-common-errors.h b/src/libsystemd/sd-bus/bus-common-errors.h
index befb6fbfe0..5df21c8926 100644
--- a/src/libsystemd/sd-bus/bus-common-errors.h
+++ b/src/libsystemd/sd-bus/bus-common-errors.h
@@ -41,6 +41,7 @@
#define BUS_ERROR_SHUTTING_DOWN "org.freedesktop.systemd1.ShuttingDown"
#define BUS_ERROR_SCOPE_NOT_RUNNING "org.freedesktop.systemd1.ScopeNotRunning"
#define BUS_ERROR_NO_SUCH_DYNAMIC_USER "org.freedesktop.systemd1.NoSuchDynamicUser"
+#define BUS_ERROR_NOT_REFERENCED "org.freedesktop.systemd1.NotReferenced"
#define BUS_ERROR_NO_SUCH_MACHINE "org.freedesktop.machine1.NoSuchMachine"
#define BUS_ERROR_NO_SUCH_IMAGE "org.freedesktop.machine1.NoSuchImage"
diff --git a/src/libsystemd/sd-bus/bus-internal.h b/src/libsystemd/sd-bus/bus-internal.h
index 216d9f62bc..2608f5469c 100644
--- a/src/libsystemd/sd-bus/bus-internal.h
+++ b/src/libsystemd/sd-bus/bus-internal.h
@@ -209,6 +209,9 @@ struct sd_bus {
bool is_system:1;
bool is_user:1;
bool allow_interactive_authorization:1;
+ bool exit_on_disconnect:1;
+ bool exited:1;
+ bool exit_triggered:1;
int use_memfd;
@@ -320,6 +323,7 @@ struct sd_bus {
sd_bus_track *track_queue;
LIST_HEAD(sd_bus_slot, slots);
+ LIST_HEAD(sd_bus_track, tracks);
};
#define BUS_DEFAULT_TIMEOUT ((usec_t) (25 * USEC_PER_SEC))
diff --git a/src/libsystemd/sd-bus/bus-track.c b/src/libsystemd/sd-bus/bus-track.c
index 1f436fe560..00e93a215f 100644
--- a/src/libsystemd/sd-bus/bus-track.c
+++ b/src/libsystemd/sd-bus/bus-track.c
@@ -24,16 +24,27 @@
#include "bus-track.h"
#include "bus-util.h"
+struct track_item {
+ unsigned n_ref;
+ char *name;
+ sd_bus_slot *slot;
+};
+
struct sd_bus_track {
unsigned n_ref;
+ unsigned n_adding; /* are we in the process of adding a new name? */
sd_bus *bus;
sd_bus_track_handler_t handler;
void *userdata;
Hashmap *names;
LIST_FIELDS(sd_bus_track, queue);
Iterator iterator;
- bool in_queue;
- bool modified;
+ bool in_list:1; /* In bus->tracks? */
+ bool in_queue:1; /* In bus->track_queue? */
+ bool modified:1;
+ bool recursive:1;
+
+ LIST_FIELDS(sd_bus_track, tracks);
};
#define MATCH_PREFIX \
@@ -56,15 +67,47 @@ struct sd_bus_track {
_x; \
})
+static struct track_item* track_item_free(struct track_item *i) {
+
+ if (!i)
+ return NULL;
+
+ sd_bus_slot_unref(i->slot);
+ free(i->name);
+ free(i);
+
+ return NULL;
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct track_item*, track_item_free);
+
static void bus_track_add_to_queue(sd_bus_track *track) {
assert(track);
+ /* Adds the bus track object to the queue of objects we should dispatch next, subject to a number of
+ * conditions. */
+
+ /* Already in the queue? */
if (track->in_queue)
return;
+ /* if we are currently in the process of adding a new name, then let's not enqueue this just yet, let's wait
+ * until the addition is complete. */
+ if (track->n_adding > 0)
+ return;
+
+ /* still referenced? */
+ if (hashmap_size(track->names) > 0)
+ return;
+
+ /* Nothing to call? */
if (!track->handler)
return;
+ /* Already closed? */
+ if (!track->in_list)
+ return;
+
LIST_PREPEND(queue, track->bus->track_queue, track);
track->in_queue = true;
}
@@ -79,6 +122,24 @@ static void bus_track_remove_from_queue(sd_bus_track *track) {
track->in_queue = false;
}
+static int bus_track_remove_name_fully(sd_bus_track *track, const char *name) {
+ struct track_item *i;
+
+ assert(track);
+ assert(name);
+
+ i = hashmap_remove(track->names, name);
+ if (!i)
+ return 0;
+
+ track_item_free(i);
+
+ bus_track_add_to_queue(track);
+
+ track->modified = true;
+ return 1;
+}
+
_public_ int sd_bus_track_new(
sd_bus *bus,
sd_bus_track **track,
@@ -102,6 +163,9 @@ _public_ int sd_bus_track_new(
t->userdata = userdata;
t->bus = sd_bus_ref(bus);
+ LIST_PREPEND(tracks, bus->tracks, t);
+ t->in_list = true;
+
bus_track_add_to_queue(t);
*track = t;
@@ -121,7 +185,7 @@ _public_ sd_bus_track* sd_bus_track_ref(sd_bus_track *track) {
}
_public_ sd_bus_track* sd_bus_track_unref(sd_bus_track *track) {
- const char *n;
+ struct track_item *i;
if (!track)
return NULL;
@@ -133,8 +197,11 @@ _public_ sd_bus_track* sd_bus_track_unref(sd_bus_track *track) {
return NULL;
}
- while ((n = hashmap_first_key(track->names)))
- sd_bus_track_remove_name(track, n);
+ while ((i = hashmap_steal_first(track->names)))
+ track_item_free(i);
+
+ if (track->in_list)
+ LIST_REMOVE(tracks, track->bus->tracks, track);
bus_track_remove_from_queue(track);
hashmap_free(track->names);
@@ -156,49 +223,76 @@ static int on_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus
if (r < 0)
return 0;
- sd_bus_track_remove_name(track, name);
+ bus_track_remove_name_fully(track, name);
return 0;
}
_public_ int sd_bus_track_add_name(sd_bus_track *track, const char *name) {
- _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
- _cleanup_free_ char *n = NULL;
+ _cleanup_(track_item_freep) struct track_item *n = NULL;
+ struct track_item *i;
const char *match;
int r;
assert_return(track, -EINVAL);
assert_return(service_name_is_valid(name), -EINVAL);
+ i = hashmap_get(track->names, name);
+ if (i) {
+ if (track->recursive) {
+ unsigned k = track->n_ref + 1;
+
+ if (k < track->n_ref) /* Check for overflow */
+ return -EOVERFLOW;
+
+ track->n_ref = k;
+ }
+
+ bus_track_remove_from_queue(track);
+ return 0;
+ }
+
r = hashmap_ensure_allocated(&track->names, &string_hash_ops);
if (r < 0)
return r;
- n = strdup(name);
+ n = new0(struct track_item, 1);
if (!n)
return -ENOMEM;
+ n->name = strdup(name);
+ if (!n->name)
+ return -ENOMEM;
/* First, subscribe to this name */
- match = MATCH_FOR_NAME(n);
- r = sd_bus_add_match(track->bus, &slot, match, on_name_owner_changed, track);
- if (r < 0)
+ match = MATCH_FOR_NAME(name);
+
+ bus_track_remove_from_queue(track); /* don't dispatch this while we work in it */
+
+ track->n_adding++; /* make sure we aren't dispatched while we synchronously add this match */
+ r = sd_bus_add_match(track->bus, &n->slot, match, on_name_owner_changed, track);
+ track->n_adding--;
+ if (r < 0) {
+ bus_track_add_to_queue(track);
return r;
+ }
- r = hashmap_put(track->names, n, slot);
- if (r == -EEXIST)
- return 0;
- if (r < 0)
+ r = hashmap_put(track->names, n->name, n);
+ if (r < 0) {
+ bus_track_add_to_queue(track);
return r;
+ }
- /* Second, check if it is currently existing, or maybe
- * doesn't, or maybe disappeared already. */
- r = sd_bus_get_name_creds(track->bus, n, 0, NULL);
+ /* Second, check if it is currently existing, or maybe doesn't, or maybe disappeared already. */
+ track->n_adding++; /* again, make sure this isn't dispatch while we are working in it */
+ r = sd_bus_get_name_creds(track->bus, name, 0, NULL);
+ track->n_adding--;
if (r < 0) {
- hashmap_remove(track->names, n);
+ hashmap_remove(track->names, name);
+ bus_track_add_to_queue(track);
return r;
}
+ n->n_ref = 1;
n = NULL;
- slot = NULL;
bus_track_remove_from_queue(track);
track->modified = true;
@@ -207,37 +301,48 @@ _public_ int sd_bus_track_add_name(sd_bus_track *track, const char *name) {
}
_public_ int sd_bus_track_remove_name(sd_bus_track *track, const char *name) {
- _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
- _cleanup_free_ char *n = NULL;
+ struct track_item *i;
assert_return(name, -EINVAL);
- if (!track)
+ if (!track) /* Treat a NULL track object as an empty track object */
return 0;
- slot = hashmap_remove2(track->names, (char*) name, (void**) &n);
- if (!slot)
- return 0;
+ if (!track->recursive)
+ return bus_track_remove_name_fully(track, name);
- if (hashmap_isempty(track->names))
- bus_track_add_to_queue(track);
+ i = hashmap_get(track->names, name);
+ if (!i)
+ return -EUNATCH;
+ if (i->n_ref <= 0)
+ return -EUNATCH;
- track->modified = true;
+ i->n_ref--;
+
+ if (i->n_ref <= 0)
+ return bus_track_remove_name_fully(track, name);
return 1;
}
_public_ unsigned sd_bus_track_count(sd_bus_track *track) {
- if (!track)
+
+ if (!track) /* Let's consider a NULL object equivalent to an empty object */
return 0;
+ /* This signature really should have returned an int, so that we can propagate errors. But well, ... Also, note
+ * that this returns the number of names being watched, and multiple references to the same name are not
+ * counted. */
+
return hashmap_size(track->names);
}
_public_ const char* sd_bus_track_contains(sd_bus_track *track, const char *name) {
- assert_return(track, NULL);
assert_return(name, NULL);
+ if (!track) /* Let's consider a NULL object equivalent to an empty object */
+ return NULL;
+
return hashmap_get(track->names, (void*) name) ? name : NULL;
}
@@ -273,6 +378,9 @@ _public_ int sd_bus_track_add_sender(sd_bus_track *track, sd_bus_message *m) {
assert_return(track, -EINVAL);
assert_return(m, -EINVAL);
+ if (sd_bus_message_get_bus(m) != track->bus)
+ return -EINVAL;
+
sender = sd_bus_message_get_sender(m);
if (!sender)
return -EINVAL;
@@ -283,9 +391,14 @@ _public_ int sd_bus_track_add_sender(sd_bus_track *track, sd_bus_message *m) {
_public_ int sd_bus_track_remove_sender(sd_bus_track *track, sd_bus_message *m) {
const char *sender;
- assert_return(track, -EINVAL);
assert_return(m, -EINVAL);
+ if (!track) /* Treat a NULL track object as an empty track object */
+ return 0;
+
+ if (sd_bus_message_get_bus(m) != track->bus)
+ return -EINVAL;
+
sender = sd_bus_message_get_sender(m);
if (!sender)
return -EINVAL;
@@ -303,7 +416,6 @@ void bus_track_dispatch(sd_bus_track *track) {
int r;
assert(track);
- assert(track->in_queue);
assert(track->handler);
bus_track_remove_from_queue(track);
@@ -319,6 +431,34 @@ void bus_track_dispatch(sd_bus_track *track) {
sd_bus_track_unref(track);
}
+void bus_track_close(sd_bus_track *track) {
+ struct track_item *i;
+
+ assert(track);
+
+ /* Called whenever our bus connected is closed. If so, and our track object is non-empty, dispatch it
+ * immediately, as we are closing now, but first flush out all names. */
+
+ if (!track->in_list)
+ return; /* We already closed this one, don't close it again. */
+
+ /* Remember that this one is closed now */
+ LIST_REMOVE(tracks, track->bus->tracks, track);
+ track->in_list = false;
+
+ /* If there's no name in this one anyway, we don't have to dispatch */
+ if (hashmap_isempty(track->names))
+ return;
+
+ /* Let's flush out all names */
+ while ((i = hashmap_steal_first(track->names)))
+ track_item_free(i);
+
+ /* Invoke handler */
+ if (track->handler)
+ bus_track_dispatch(track);
+}
+
_public_ void *sd_bus_track_get_userdata(sd_bus_track *track) {
assert_return(track, NULL);
@@ -335,3 +475,55 @@ _public_ void *sd_bus_track_set_userdata(sd_bus_track *track, void *userdata) {
return ret;
}
+
+_public_ int sd_bus_track_set_recursive(sd_bus_track *track, int b) {
+ assert_return(track, -EINVAL);
+
+ if (track->recursive == !!b)
+ return 0;
+
+ if (!hashmap_isempty(track->names))
+ return -EBUSY;
+
+ track->recursive = b;
+ return 0;
+}
+
+_public_ int sd_bus_track_get_recursive(sd_bus_track *track) {
+ assert_return(track, -EINVAL);
+
+ return track->recursive;
+}
+
+_public_ int sd_bus_track_count_sender(sd_bus_track *track, sd_bus_message *m) {
+ const char *sender;
+
+ assert_return(m, -EINVAL);
+
+ if (!track) /* Let's consider a NULL object equivalent to an empty object */
+ return 0;
+
+ if (sd_bus_message_get_bus(m) != track->bus)
+ return -EINVAL;
+
+ sender = sd_bus_message_get_sender(m);
+ if (!sender)
+ return -EINVAL;
+
+ return sd_bus_track_count_name(track, sender);
+}
+
+_public_ int sd_bus_track_count_name(sd_bus_track *track, const char *name) {
+ struct track_item *i;
+
+ assert_return(service_name_is_valid(name), -EINVAL);
+
+ if (!track) /* Let's consider a NULL object equivalent to an empty object */
+ return 0;
+
+ i = hashmap_get(track->names, name);
+ if (!i)
+ return 0;
+
+ return i->n_ref;
+}
diff --git a/src/libsystemd/sd-bus/bus-track.h b/src/libsystemd/sd-bus/bus-track.h
index 7d93a727d6..26bd05f5c7 100644
--- a/src/libsystemd/sd-bus/bus-track.h
+++ b/src/libsystemd/sd-bus/bus-track.h
@@ -20,3 +20,4 @@
***/
void bus_track_dispatch(sd_bus_track *track);
+void bus_track_close(sd_bus_track *track);
diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c
index ed5f94e136..d746348544 100644
--- a/src/libsystemd/sd-bus/sd-bus.c
+++ b/src/libsystemd/sd-bus/sd-bus.c
@@ -107,6 +107,7 @@ static void bus_free(sd_bus *b) {
assert(b);
assert(!b->track_queue);
+ assert(!b->tracks);
b->state = BUS_CLOSED;
@@ -2640,62 +2641,101 @@ null_message:
return r;
}
-static int process_closing(sd_bus *bus, sd_bus_message **ret) {
+static int bus_exit_now(sd_bus *bus) {
+ assert(bus);
+
+ /* Exit due to close, if this is requested. If this is bus object is attached to an event source, invokes
+ * sd_event_exit(), otherwise invokes libc exit(). */
+
+ if (bus->exited) /* did we already exit? */
+ return 0;
+ if (!bus->exit_triggered) /* was the exit condition triggered? */
+ return 0;
+ if (!bus->exit_on_disconnect) /* Shall we actually exit on disconnection? */
+ return 0;
+
+ bus->exited = true; /* never exit more than once */
+
+ log_debug("Bus connection disconnected, exiting.");
+
+ if (bus->event)
+ return sd_event_exit(bus->event, EXIT_FAILURE);
+ else
+ exit(EXIT_FAILURE);
+
+ assert_not_reached("exit() didn't exit?");
+}
+
+static int process_closing_reply_callback(sd_bus *bus, struct reply_callback *c) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
- struct reply_callback *c;
+ sd_bus_slot *slot;
int r;
assert(bus);
- assert(bus->state == BUS_CLOSING);
+ assert(c);
- c = ordered_hashmap_first(bus->reply_callbacks);
- if (c) {
- _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
- sd_bus_slot *slot;
+ r = bus_message_new_synthetic_error(
+ bus,
+ c->cookie,
+ &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NO_REPLY, "Connection terminated"),
+ &m);
+ if (r < 0)
+ return r;
- /* First, fail all outstanding method calls */
- r = bus_message_new_synthetic_error(
- bus,
- c->cookie,
- &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NO_REPLY, "Connection terminated"),
- &m);
- if (r < 0)
- return r;
+ r = bus_seal_synthetic_message(bus, m);
+ if (r < 0)
+ return r;
- r = bus_seal_synthetic_message(bus, m);
- if (r < 0)
- return r;
+ if (c->timeout != 0) {
+ prioq_remove(bus->reply_callbacks_prioq, c, &c->prioq_idx);
+ c->timeout = 0;
+ }
- if (c->timeout != 0) {
- prioq_remove(bus->reply_callbacks_prioq, c, &c->prioq_idx);
- c->timeout = 0;
- }
+ ordered_hashmap_remove(bus->reply_callbacks, &c->cookie);
+ c->cookie = 0;
- ordered_hashmap_remove(bus->reply_callbacks, &c->cookie);
- c->cookie = 0;
+ slot = container_of(c, sd_bus_slot, reply_callback);
- slot = container_of(c, sd_bus_slot, reply_callback);
+ bus->iteration_counter++;
- bus->iteration_counter++;
+ bus->current_message = m;
+ bus->current_slot = sd_bus_slot_ref(slot);
+ bus->current_handler = c->callback;
+ bus->current_userdata = slot->userdata;
+ r = c->callback(m, slot->userdata, &error_buffer);
+ bus->current_userdata = NULL;
+ bus->current_handler = NULL;
+ bus->current_slot = NULL;
+ bus->current_message = NULL;
- bus->current_message = m;
- bus->current_slot = sd_bus_slot_ref(slot);
- bus->current_handler = c->callback;
- bus->current_userdata = slot->userdata;
- r = c->callback(m, slot->userdata, &error_buffer);
- bus->current_userdata = NULL;
- bus->current_handler = NULL;
- bus->current_slot = NULL;
- bus->current_message = NULL;
+ if (slot->floating) {
+ bus_slot_disconnect(slot);
+ sd_bus_slot_unref(slot);
+ }
- if (slot->floating) {
- bus_slot_disconnect(slot);
- sd_bus_slot_unref(slot);
- }
+ sd_bus_slot_unref(slot);
- sd_bus_slot_unref(slot);
+ return bus_maybe_reply_error(m, r, &error_buffer);
+}
- return bus_maybe_reply_error(m, r, &error_buffer);
+static int process_closing(sd_bus *bus, sd_bus_message **ret) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+ struct reply_callback *c;
+ int r;
+
+ assert(bus);
+ assert(bus->state == BUS_CLOSING);
+
+ /* First, fail all outstanding method calls */
+ c = ordered_hashmap_first(bus->reply_callbacks);
+ if (c)
+ return process_closing_reply_callback(bus, c);
+
+ /* Then, fake-drop all remaining bus tracking references */
+ if (bus->tracks) {
+ bus_track_close(bus->tracks);
+ return 1;
}
/* Then, synthesize a Disconnected message */
@@ -2727,6 +2767,10 @@ static int process_closing(sd_bus *bus, sd_bus_message **ret) {
if (r != 0)
goto finish;
+ /* Nothing else to do, exit now, if the condition holds */
+ bus->exit_triggered = true;
+ (void) bus_exit_now(bus);
+
if (ret) {
*ret = m;
m = NULL;
@@ -3789,3 +3833,21 @@ _public_ void sd_bus_default_flush_close(void) {
flush_close(default_user_bus);
flush_close(default_system_bus);
}
+
+_public_ int sd_bus_set_exit_on_disconnect(sd_bus *bus, int b) {
+ assert_return(bus, -EINVAL);
+
+ /* Turns on exit-on-disconnect, and triggers it immediately if the bus connection was already
+ * disconnected. Note that this is triggered exclusively on disconnections triggered by the server side, never
+ * from the client side. */
+ bus->exit_on_disconnect = b;
+
+ /* If the exit condition was triggered already, exit immediately. */
+ return bus_exit_now(bus);
+}
+
+_public_ int sd_bus_get_exit_on_disconnect(sd_bus *bus) {
+ assert_return(bus, -EINVAL);
+
+ return bus->exit_on_disconnect;
+}
diff --git a/src/libsystemd/sd-bus/test-bus-creds.c b/src/libsystemd/sd-bus/test-bus-creds.c
index e9ef483bdd..82237af115 100644
--- a/src/libsystemd/sd-bus/test-bus-creds.c
+++ b/src/libsystemd/sd-bus/test-bus-creds.c
@@ -27,7 +27,7 @@ int main(int argc, char *argv[]) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
int r;
- if (cg_unified() == -ENOMEDIUM) {
+ if (cg_all_unified() == -ENOMEDIUM) {
puts("Skipping test: /sys/fs/cgroup/ not available");
return EXIT_TEST_SKIP;
}
diff --git a/src/libsystemd/sd-bus/test-bus-track.c b/src/libsystemd/sd-bus/test-bus-track.c
new file mode 100644
index 0000000000..4beb61f05a
--- /dev/null
+++ b/src/libsystemd/sd-bus/test-bus-track.c
@@ -0,0 +1,113 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sd-bus.h>
+
+#include "macro.h"
+
+static bool track_cb_called_x = false;
+static bool track_cb_called_y = false;
+
+static int track_cb_x(sd_bus_track *t, void *userdata) {
+
+ log_error("TRACK CB X");
+
+ assert_se(!track_cb_called_x);
+ track_cb_called_x = true;
+
+ /* This means b's name disappeared. Let's now disconnect, to make sure the track handling on disconnect works
+ * as it should. */
+
+ assert_se(shutdown(sd_bus_get_fd(sd_bus_track_get_bus(t)), SHUT_RDWR) >= 0);
+ return 1;
+}
+
+static int track_cb_y(sd_bus_track *t, void *userdata) {
+ int r;
+
+ log_error("TRACK CB Y");
+
+ assert_se(!track_cb_called_y);
+ track_cb_called_y = true;
+
+ /* We got disconnected, let's close everything */
+
+ r = sd_event_exit(sd_bus_get_event(sd_bus_track_get_bus(t)), EXIT_SUCCESS);
+ assert_se(r >= 0);
+
+ return 0;
+}
+
+int main(int argc, char *argv[]) {
+ _cleanup_(sd_event_unrefp) sd_event *event = NULL;
+ _cleanup_(sd_bus_track_unrefp) sd_bus_track *x = NULL, *y = NULL;
+ _cleanup_(sd_bus_unrefp) sd_bus *a = NULL, *b = NULL;
+ const char *unique;
+ int r;
+
+ r = sd_event_default(&event);
+ assert_se(r >= 0);
+
+ r = sd_bus_open_system(&a);
+ if (IN_SET(r, -ECONNREFUSED, -ENOENT)) {
+ log_info("Failed to connect to bus, skipping tests.");
+ return EXIT_TEST_SKIP;
+ }
+ assert_se(r >= 0);
+
+ r = sd_bus_attach_event(a, event, SD_EVENT_PRIORITY_NORMAL);
+ assert_se(r >= 0);
+
+ r = sd_bus_open_system(&b);
+ assert_se(r >= 0);
+
+ r = sd_bus_attach_event(b, event, SD_EVENT_PRIORITY_NORMAL);
+ assert_se(r >= 0);
+
+ /* Watch b's name from a */
+ r = sd_bus_track_new(a, &x, track_cb_x, NULL);
+ assert_se(r >= 0);
+
+ r = sd_bus_get_unique_name(b, &unique);
+ assert_se(r >= 0);
+
+ r = sd_bus_track_add_name(x, unique);
+ assert_se(r >= 0);
+
+ /* Watch's a's own name from a */
+ r = sd_bus_track_new(a, &y, track_cb_y, NULL);
+ assert_se(r >= 0);
+
+ r = sd_bus_get_unique_name(a, &unique);
+ assert_se(r >= 0);
+
+ r = sd_bus_track_add_name(y, unique);
+ assert_se(r >= 0);
+
+ /* Now make b's name disappear */
+ sd_bus_close(b);
+
+ r = sd_event_loop(event);
+ assert_se(r >= 0);
+
+ assert_se(track_cb_called_x);
+ assert_se(track_cb_called_y);
+
+ return 0;
+}
diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c
index c78ca7ad76..4acce4bea7 100644
--- a/src/machine/machinectl.c
+++ b/src/machine/machinectl.c
@@ -109,6 +109,7 @@ typedef struct MachineInfo {
const char *name;
const char *class;
const char *service;
+ char *os;
} MachineInfo;
static int compare_machine_info(const void *a, const void *b) {
@@ -117,12 +118,66 @@ static int compare_machine_info(const void *a, const void *b) {
return strcmp(x->name, y->name);
}
+static void clean_machine_info(MachineInfo *machines, size_t n_machines) {
+ size_t i;
+
+ if (!machines || n_machines == 0)
+ return;
+
+ for (i = 0; i < n_machines; i++)
+ free(machines[i].os);
+ free(machines);
+}
+
+static int get_os_name(sd_bus *bus, const char *name, char **out) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ const char *k, *v, *os;
+ char *str;
+ int r;
+
+ assert(bus);
+ assert(name);
+ assert(out);
+
+ r = sd_bus_call_method(bus,
+ "org.freedesktop.machine1",
+ "/org/freedesktop/machine1",
+ "org.freedesktop.machine1.Manager",
+ "GetMachineOSRelease",
+ NULL, &reply, "s", name);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_enter_container(reply, 'a', "{ss}");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ while ((r = sd_bus_message_read(reply, "{ss}", &k, &v)) > 0) {
+ if (streq(k, "PRETTY_NAME"))
+ os = v;
+ }
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_exit_container(reply);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ str = strdup(os);
+ if (!str)
+ return log_oom();
+ *out = str;
+
+ return 0;
+}
+
static int list_machines(int argc, char *argv[], void *userdata) {
- size_t max_name = strlen("MACHINE"), max_class = strlen("CLASS"), max_service = strlen("SERVICE");
+ size_t max_name = strlen("MACHINE"), max_class = strlen("CLASS"),
+ max_service = strlen("SERVICE"), max_os = strlen("OS");
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
- _cleanup_free_ MachineInfo *machines = NULL;
+ MachineInfo *machines = NULL;
const char *name, *class, *service, *object;
size_t n_machines = 0, n_allocated = 0, j;
sd_bus *bus = userdata;
@@ -148,15 +203,21 @@ static int list_machines(int argc, char *argv[], void *userdata) {
r = sd_bus_message_enter_container(reply, 'a', "(ssso)");
if (r < 0)
return bus_log_parse_error(r);
-
while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) {
size_t l;
if (name[0] == '.' && !arg_all)
continue;
- if (!GREEDY_REALLOC(machines, n_allocated, n_machines + 1))
- return log_oom();
+ if (!GREEDY_REALLOC(machines, n_allocated, n_machines + 1)) {
+ r = log_oom();
+ goto out;
+ }
+
+ machines[n_machines].os = NULL;
+ r = get_os_name(bus, name, &machines[n_machines].os);
+ if (r < 0)
+ goto out;
machines[n_machines].name = name;
machines[n_machines].class = class;
@@ -174,35 +235,47 @@ static int list_machines(int argc, char *argv[], void *userdata) {
if (l > max_service)
max_service = l;
+ l = machines[n_machines].os ? strlen(machines[n_machines].os) : 0;
+ if (l > max_os)
+ max_os = l;
+
n_machines++;
}
- if (r < 0)
- return bus_log_parse_error(r);
+ if (r < 0) {
+ r = bus_log_parse_error(r);
+ goto out;
+ }
r = sd_bus_message_exit_container(reply);
- if (r < 0)
- return bus_log_parse_error(r);
+ if (r < 0) {
+ r = bus_log_parse_error(r);
+ goto out;
+ }
qsort_safe(machines, n_machines, sizeof(MachineInfo), compare_machine_info);
if (arg_legend && n_machines > 0)
- printf("%-*s %-*s %-*s\n",
+ printf("%-*s %-*s %-*s %-*s\n",
(int) max_name, "MACHINE",
(int) max_class, "CLASS",
- (int) max_service, "SERVICE");
+ (int) max_service, "SERVICE",
+ (int) max_os, "OS");
for (j = 0; j < n_machines; j++)
- printf("%-*s %-*s %-*s\n",
+ printf("%-*s %-*s %-*s %-*s\n",
(int) max_name, machines[j].name,
(int) max_class, machines[j].class,
- (int) max_service, machines[j].service);
+ (int) max_service, machines[j].service,
+ (int) max_os, machines[j].os ? machines[j].os : "");
if (arg_legend && n_machines > 0)
printf("\n%zu machines listed.\n", n_machines);
else
printf("No machines.\n");
- return 0;
+out:
+ clean_machine_info(machines, n_machines);
+ return r;
}
typedef struct ImageInfo {
@@ -456,41 +529,17 @@ static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *p
}
static int print_os_release(sd_bus *bus, const char *name, const char *prefix) {
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
- const char *k, *v, *pretty = NULL;
+ _cleanup_free_ char *pretty = NULL;
int r;
assert(bus);
assert(name);
assert(prefix);
- r = sd_bus_call_method(bus,
- "org.freedesktop.machine1",
- "/org/freedesktop/machine1",
- "org.freedesktop.machine1.Manager",
- "GetMachineOSRelease",
- NULL,
- &reply,
- "s", name);
+ r = get_os_name(bus, name, &pretty);
if (r < 0)
return r;
- r = sd_bus_message_enter_container(reply, 'a', "{ss}");
- if (r < 0)
- return bus_log_parse_error(r);
-
- while ((r = sd_bus_message_read(reply, "{ss}", &k, &v)) > 0) {
- if (streq(k, "PRETTY_NAME"))
- pretty = v;
-
- }
- if (r < 0)
- return bus_log_parse_error(r);
-
- r = sd_bus_message_exit_container(reply);
- if (r < 0)
- return bus_log_parse_error(r);
-
if (pretty)
printf("%s%s\n", prefix, pretty);
diff --git a/src/mount/Makefile b/src/mount/Makefile
new file mode 120000
index 0000000000..d0b0e8e008
--- /dev/null
+++ b/src/mount/Makefile
@@ -0,0 +1 @@
+../Makefile \ No newline at end of file
diff --git a/src/mount/mount-tool.c b/src/mount/mount-tool.c
new file mode 100644
index 0000000000..80bba086e4
--- /dev/null
+++ b/src/mount/mount-tool.c
@@ -0,0 +1,1114 @@
+/***
+ 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 <getopt.h>
+
+#include "libudev.h"
+#include "sd-bus.h"
+
+#include "bus-error.h"
+#include "bus-unit-util.h"
+#include "bus-util.h"
+#include "escape.h"
+#include "fstab-util.h"
+#include "pager.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "spawn-polkit-agent.h"
+#include "strv.h"
+#include "udev-util.h"
+#include "unit-name.h"
+#include "terminal-util.h"
+
+enum {
+ ACTION_DEFAULT,
+ ACTION_MOUNT,
+ ACTION_AUTOMOUNT,
+ ACTION_LIST,
+} arg_action = ACTION_DEFAULT;
+
+static bool arg_no_block = false;
+static bool arg_no_pager = false;
+static bool arg_ask_password = true;
+static bool arg_quiet = false;
+static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
+static bool arg_user = false;
+static const char *arg_host = NULL;
+static bool arg_discover = false;
+static char *arg_mount_what = NULL;
+static char *arg_mount_where = NULL;
+static char *arg_mount_type = NULL;
+static char *arg_mount_options = NULL;
+static char *arg_description = NULL;
+static char **arg_property = NULL;
+static usec_t arg_timeout_idle = USEC_INFINITY;
+static bool arg_timeout_idle_set = false;
+static char **arg_automount_property = NULL;
+static int arg_bind_device = -1;
+static bool arg_fsck = true;
+
+static void polkit_agent_open_if_enabled(void) {
+
+ /* Open the polkit agent as a child process if necessary */
+ if (!arg_ask_password)
+ return;
+
+ if (arg_transport != BUS_TRANSPORT_LOCAL)
+ return;
+
+ polkit_agent_open();
+}
+
+static void help(void) {
+ printf("%s [OPTIONS...] WHAT [WHERE]\n\n"
+ "Establish a mount or auto-mount point transiently.\n\n"
+ " -h --help Show this help\n"
+ " --version Show package version\n"
+ " --no-block Do not wait until operation finished\n"
+ " --no-pager Do not pipe output into a pager\n"
+ " --no-ask-password Do not prompt for password\n"
+ " -q --quiet Suppress information messages during runtime\n"
+ " --user Run as user unit\n"
+ " -H --host=[USER@]HOST Operate on remote host\n"
+ " -M --machine=CONTAINER Operate on local container\n"
+ " --discover Discover mount device metadata\n"
+ " -t --type=TYPE File system type\n"
+ " -o --options=OPTIONS Mount options\n"
+ " --fsck=no Don't run file system check before mount\n"
+ " --description=TEXT Description for unit\n"
+ " -p --property=NAME=VALUE Set mount unit property\n"
+ " -A --automount=BOOL Create an auto-mount point\n"
+ " --timeout-idle-sec=SEC Specify automount idle timeout\n"
+ " --automount-property=NAME=VALUE\n"
+ " Set automount unit property\n"
+ " --bind-device Bind automount unit to device\n"
+ " --list List mountable block devices\n"
+ , program_invocation_short_name);
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+ enum {
+ ARG_VERSION = 0x100,
+ ARG_NO_BLOCK,
+ ARG_NO_PAGER,
+ ARG_NO_ASK_PASSWORD,
+ ARG_USER,
+ ARG_SYSTEM,
+ ARG_DISCOVER,
+ ARG_MOUNT_TYPE,
+ ARG_MOUNT_OPTIONS,
+ ARG_FSCK,
+ ARG_DESCRIPTION,
+ ARG_TIMEOUT_IDLE,
+ ARG_AUTOMOUNT,
+ ARG_AUTOMOUNT_PROPERTY,
+ ARG_BIND_DEVICE,
+ ARG_LIST,
+ };
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "no-block", no_argument, NULL, ARG_NO_BLOCK },
+ { "no-pager", no_argument, NULL, ARG_NO_PAGER },
+ { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
+ { "quiet", no_argument, NULL, 'q' },
+ { "user", no_argument, NULL, ARG_USER },
+ { "system", no_argument, NULL, ARG_SYSTEM },
+ { "host", required_argument, NULL, 'H' },
+ { "machine", required_argument, NULL, 'M' },
+ { "discover", no_argument, NULL, ARG_DISCOVER },
+ { "type", required_argument, NULL, 't' },
+ { "options", required_argument, NULL, 'o' },
+ { "description", required_argument, NULL, ARG_DESCRIPTION },
+ { "property", required_argument, NULL, 'p' },
+ { "automount", required_argument, NULL, ARG_AUTOMOUNT },
+ { "timeout-idle-sec", required_argument, NULL, ARG_TIMEOUT_IDLE },
+ { "automount-property", required_argument, NULL, ARG_AUTOMOUNT_PROPERTY },
+ { "bind-device", no_argument, NULL, ARG_BIND_DEVICE },
+ { "list", no_argument, NULL, ARG_LIST },
+ {},
+ };
+
+ int r, c;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ while ((c = getopt_long(argc, argv, "hqH:M:t:o:p:A", options, NULL)) >= 0)
+
+ switch (c) {
+
+ case 'h':
+ help();
+ return 0;
+
+ case ARG_VERSION:
+ return version();
+
+ case ARG_NO_BLOCK:
+ arg_no_block = true;
+ break;
+
+ case ARG_NO_PAGER:
+ arg_no_pager = true;
+ break;
+
+ case ARG_NO_ASK_PASSWORD:
+ arg_ask_password = false;
+ break;
+
+ case 'q':
+ arg_quiet = true;
+ break;
+
+ case ARG_USER:
+ arg_user = true;
+ break;
+
+ case ARG_SYSTEM:
+ arg_user = false;
+ break;
+
+ case 'H':
+ arg_transport = BUS_TRANSPORT_REMOTE;
+ arg_host = optarg;
+ break;
+
+ case 'M':
+ arg_transport = BUS_TRANSPORT_MACHINE;
+ arg_host = optarg;
+ break;
+
+ case ARG_DISCOVER:
+ arg_discover = true;
+ break;
+
+ case 't':
+ if (free_and_strdup(&arg_mount_type, optarg) < 0)
+ return log_oom();
+ break;
+
+ case 'o':
+ if (free_and_strdup(&arg_mount_options, optarg) < 0)
+ return log_oom();
+ break;
+
+ case ARG_FSCK:
+ r = parse_boolean(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse --fsck= argument: %s", optarg);
+
+ arg_fsck = r;
+ break;
+
+ case ARG_DESCRIPTION:
+ if (free_and_strdup(&arg_description, optarg) < 0)
+ return log_oom();
+ break;
+
+ case 'p':
+ if (strv_extend(&arg_property, optarg) < 0)
+ return log_oom();
+
+ break;
+
+ case 'A':
+ arg_action = ACTION_AUTOMOUNT;
+ break;
+
+ case ARG_AUTOMOUNT:
+ r = parse_boolean(optarg);
+ if (r < 0)
+ return log_error_errno(r, "--automount= expects a valid boolean parameter: %s", optarg);
+
+ arg_action = r ? ACTION_AUTOMOUNT : ACTION_MOUNT;
+ break;
+
+ case ARG_TIMEOUT_IDLE:
+ r = parse_sec(optarg, &arg_timeout_idle);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse timeout: %s", optarg);
+
+ break;
+
+ case ARG_AUTOMOUNT_PROPERTY:
+ if (strv_extend(&arg_automount_property, optarg) < 0)
+ return log_oom();
+
+ break;
+
+ case ARG_BIND_DEVICE:
+ arg_bind_device = true;
+ break;
+
+ case ARG_LIST:
+ arg_action = ACTION_LIST;
+ break;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ assert_not_reached("Unhandled option");
+ }
+
+ if (arg_user && arg_transport != BUS_TRANSPORT_LOCAL) {
+ log_error("Execution in user context is not supported on non-local systems.");
+ return -EINVAL;
+ }
+
+ if (arg_action == ACTION_LIST) {
+ if (optind < argc) {
+ log_error("Too many arguments.");
+ return -EINVAL;
+ }
+
+ if (arg_transport != BUS_TRANSPORT_LOCAL) {
+ log_error("Listing devices only supported locally.");
+ return -EOPNOTSUPP;
+ }
+ } else {
+ if (optind >= argc) {
+ log_error("At least one argument required.");
+ return -EINVAL;
+ }
+
+ if (argc > optind+2) {
+ log_error("At most two arguments required.");
+ return -EINVAL;
+ }
+
+ arg_mount_what = fstab_node_to_udev_node(argv[optind]);
+ if (!arg_mount_what)
+ return log_oom();
+
+ if (argc > optind+1) {
+ r = path_make_absolute_cwd(argv[optind+1], &arg_mount_where);
+ if (r < 0)
+ return log_error_errno(r, "Failed to make path absolute: %m");
+ } else
+ arg_discover = true;
+
+ if (arg_discover && arg_transport != BUS_TRANSPORT_LOCAL) {
+ log_error("Automatic mount location discovery is only supported locally.");
+ return -EOPNOTSUPP;
+ }
+ }
+
+ return 1;
+}
+
+static int transient_unit_set_properties(sd_bus_message *m, char **properties) {
+ int r;
+
+ if (!isempty(arg_description)) {
+ r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description);
+ if (r < 0)
+ return r;
+ }
+
+ if (arg_bind_device && is_device_path(arg_mount_what)) {
+ _cleanup_free_ char *device_unit = NULL;
+
+ r = unit_name_from_path(arg_mount_what, ".device", &device_unit);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(m, "(sv)(sv)",
+ "After", "as", 1, device_unit,
+ "BindsTo", "as", 1, device_unit);
+ if (r < 0)
+ return r;
+ }
+
+ r = bus_append_unit_property_assignment_many(m, properties);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+static int transient_mount_set_properties(sd_bus_message *m) {
+ int r;
+
+ assert(m);
+
+ r = transient_unit_set_properties(m, arg_property);
+ if (r < 0)
+ return r;
+
+ if (arg_mount_what) {
+ r = sd_bus_message_append(m, "(sv)", "What", "s", arg_mount_what);
+ if (r < 0)
+ return r;
+ }
+
+ if (arg_mount_type) {
+ r = sd_bus_message_append(m, "(sv)", "Type", "s", arg_mount_type);
+ if (r < 0)
+ return r;
+ }
+
+ if (arg_mount_options) {
+ r = sd_bus_message_append(m, "(sv)", "Options", "s", arg_mount_options);
+ if (r < 0)
+ return r;
+ }
+
+ if (arg_fsck) {
+ _cleanup_free_ char *fsck = NULL;
+
+ r = unit_name_from_path_instance("systemd-fsck", arg_mount_what, ".service", &fsck);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(m,
+ "(sv)(sv)",
+ "Requires", "as", 1, fsck,
+ "After", "as", 1, fsck);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int transient_automount_set_properties(sd_bus_message *m) {
+ int r;
+
+ assert(m);
+
+ r = transient_unit_set_properties(m, arg_automount_property);
+ if (r < 0)
+ return r;
+
+ if (arg_timeout_idle != USEC_INFINITY) {
+ r = sd_bus_message_append(m, "(sv)", "TimeoutIdleUSec", "t", arg_timeout_idle);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int start_transient_mount(
+ sd_bus *bus,
+ char **argv) {
+
+ _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;
+ _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
+ _cleanup_free_ char *mount_unit = NULL;
+ int r;
+
+ if (!arg_no_block) {
+ r = bus_wait_for_jobs_new(bus, &w);
+ if (r < 0)
+ return log_error_errno(r, "Could not watch jobs: %m");
+ }
+
+ r = unit_name_from_path(arg_mount_where, ".mount", &mount_unit);
+ if (r < 0)
+ return log_error_errno(r, "Failed to make mount unit name: %m");
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ &m,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "StartTransientUnit");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ /* Name and mode */
+ r = sd_bus_message_append(m, "ss", mount_unit, "fail");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ /* Properties */
+ r = sd_bus_message_open_container(m, 'a', "(sv)");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = transient_mount_set_properties(m);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ /* Auxiliary units */
+ r = sd_bus_message_append(m, "a(sa(sv))", 0);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ polkit_agent_open_if_enabled();
+
+ r = sd_bus_call(bus, m, 0, &error, &reply);
+ if (r < 0)
+ return log_error_errno(r, "Failed to start transient mount unit: %s", bus_error_message(&error, r));
+
+ if (w) {
+ const char *object;
+
+ r = sd_bus_message_read(reply, "o", &object);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = bus_wait_for_jobs_one(w, object, arg_quiet);
+ if (r < 0)
+ return r;
+ }
+
+ if (!arg_quiet)
+ log_info("Started unit %s%s%s for mount point: %s%s%s",
+ ansi_highlight(), mount_unit, ansi_normal(),
+ ansi_highlight(), arg_mount_where, ansi_normal());
+
+ return 0;
+}
+
+static int start_transient_automount(
+ sd_bus *bus,
+ char **argv) {
+
+ _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;
+ _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
+ _cleanup_free_ char *automount_unit = NULL, *mount_unit = NULL;
+ int r;
+
+ if (!arg_no_block) {
+ r = bus_wait_for_jobs_new(bus, &w);
+ if (r < 0)
+ return log_error_errno(r, "Could not watch jobs: %m");
+ }
+
+ r = unit_name_from_path(arg_mount_where, ".automount", &automount_unit);
+ if (r < 0)
+ return log_error_errno(r, "Failed to make automount unit name: %m");
+
+ r = unit_name_from_path(arg_mount_where, ".mount", &mount_unit);
+ if (r < 0)
+ return log_error_errno(r, "Failed to make mount unit name: %m");
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ &m,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "StartTransientUnit");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ /* Name and mode */
+ r = sd_bus_message_append(m, "ss", automount_unit, "fail");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ /* Properties */
+ r = sd_bus_message_open_container(m, 'a', "(sv)");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = transient_automount_set_properties(m);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ /* Auxiliary units */
+ r = sd_bus_message_open_container(m, 'a', "(sa(sv))");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_open_container(m, 'r', "sa(sv)");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(m, "s", mount_unit);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_open_container(m, 'a', "(sv)");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = transient_mount_set_properties(m);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ polkit_agent_open_if_enabled();
+
+ r = sd_bus_call(bus, m, 0, &error, &reply);
+ if (r < 0)
+ return log_error_errno(r, "Failed to start transient automount unit: %s", bus_error_message(&error, r));
+
+ if (w) {
+ const char *object;
+
+ r = sd_bus_message_read(reply, "o", &object);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = bus_wait_for_jobs_one(w, object, arg_quiet);
+ if (r < 0)
+ return r;
+ }
+
+ if (!arg_quiet)
+ log_info("Started unit %s%s%s for mount point: %s%s%s",
+ ansi_highlight(), automount_unit, ansi_normal(),
+ ansi_highlight(), arg_mount_where, ansi_normal());
+
+ return 0;
+}
+
+static int acquire_mount_type(struct udev_device *d) {
+ const char *v;
+
+ assert(d);
+
+ if (arg_mount_type)
+ return 0;
+
+ v = udev_device_get_property_value(d, "ID_FS_TYPE");
+ if (isempty(v))
+ return 0;
+
+ arg_mount_type = strdup(v);
+ if (!arg_mount_type)
+ return log_oom();
+
+ log_debug("Discovered type=%s", arg_mount_type);
+ return 1;
+}
+
+static int acquire_mount_options(struct udev_device *d) {
+ const char *v;
+
+ if (arg_mount_options)
+ return 0;
+
+ v = udev_device_get_property_value(d, "SYSTEMD_MOUNT_OPTIONS");
+ if (isempty(v))
+ return 0;
+
+ arg_mount_options = strdup(v);
+ if (!arg_mount_options)
+ return log_oom();
+
+ log_debug("Discovered options=%s", arg_mount_options);
+ return 1;
+}
+
+static const char *get_model(struct udev_device *d) {
+ const char *model;
+
+ assert(d);
+
+ model = udev_device_get_property_value(d, "ID_MODEL_FROM_DATABASE");
+ if (model)
+ return model;
+
+ return udev_device_get_property_value(d, "ID_MODEL");
+}
+
+static const char* get_label(struct udev_device *d) {
+ const char *label;
+
+ assert(d);
+
+ label = udev_device_get_property_value(d, "ID_FS_LABEL");
+ if (label)
+ return label;
+
+ return udev_device_get_property_value(d, "ID_PART_ENTRY_NAME");
+}
+
+static int acquire_mount_where(struct udev_device *d) {
+ const char *v;
+
+ if (arg_mount_where)
+ return 0;
+
+ v = udev_device_get_property_value(d, "SYSTEMD_MOUNT_WHERE");
+ if (isempty(v)) {
+ _cleanup_free_ char *escaped = NULL;
+ const char *name;
+
+ name = get_label(d);
+ if (!name)
+ name = get_model(d);
+ if (!name) {
+ const char *dn;
+
+ dn = udev_device_get_devnode(d);
+ if (!dn)
+ return 0;
+
+ name = basename(dn);
+ }
+
+ escaped = xescape(name, "\\");
+ if (!filename_is_valid(escaped))
+ return 0;
+
+ arg_mount_where = strjoin("/run/media/system/", escaped, NULL);
+ } else
+ arg_mount_where = strdup(v);
+
+ if (!arg_mount_where)
+ return log_oom();
+
+ log_debug("Discovered where=%s", arg_mount_where);
+ return 1;
+}
+
+static int acquire_description(struct udev_device *d) {
+ const char *model, *label;
+
+ if (arg_description)
+ return 0;
+
+ model = get_model(d);
+
+ label = get_label(d);
+ if (!label)
+ label = udev_device_get_property_value(d, "ID_PART_ENTRY_NUMBER");
+
+ if (model && label)
+ arg_description = strjoin(model, " ", label, NULL);
+ else if (label)
+ arg_description = strdup(label);
+ else if (model)
+ arg_description = strdup(model);
+ else
+ return 0;
+
+ if (!arg_description)
+ return log_oom();
+
+ log_debug("Discovered description=%s", arg_description);
+ return 1;
+}
+
+static int acquire_removable(struct udev_device *d) {
+ const char *v;
+
+ /* Shortcut this if there's no reason to check it */
+ if (arg_action != ACTION_DEFAULT && arg_timeout_idle_set && arg_bind_device >= 0)
+ return 0;
+
+ for (;;) {
+ v = udev_device_get_sysattr_value(d, "removable");
+ if (v)
+ break;
+
+ d = udev_device_get_parent(d);
+ if (!d)
+ return 0;
+
+ if (!streq_ptr(udev_device_get_subsystem(d), "block"))
+ return 0;
+ }
+
+ if (parse_boolean(v) <= 0)
+ return 0;
+
+ log_debug("Discovered removable device.");
+
+ if (arg_action == ACTION_DEFAULT) {
+ log_debug("Automatically turning on automount.");
+ arg_action = ACTION_AUTOMOUNT;
+ }
+
+ if (!arg_timeout_idle_set) {
+ log_debug("Setting idle timeout to 1s.");
+ arg_timeout_idle = USEC_PER_SEC;
+ }
+
+ if (arg_bind_device < 0) {
+ log_debug("Binding automount unit to device.");
+ arg_bind_device = true;
+ }
+
+ return 1;
+}
+
+static int discover_device(void) {
+ _cleanup_udev_device_unref_ struct udev_device *d = NULL;
+ _cleanup_udev_unref_ struct udev *udev = NULL;
+ struct stat st;
+ const char *v;
+ int r;
+
+ if (!arg_discover)
+ return 0;
+
+ if (!is_device_path(arg_mount_what)) {
+ log_error("Discovery only supported for block devices, don't know what to do.");
+ return -EINVAL;
+ }
+
+ if (stat(arg_mount_what, &st) < 0)
+ return log_error_errno(errno, "Can't stat %s: %m", arg_mount_what);
+
+ if (!S_ISBLK(st.st_mode)) {
+ log_error("Path %s is not a block device, don't know what to do.", arg_mount_what);
+ return -ENOTBLK;
+ }
+
+ udev = udev_new();
+ if (!udev)
+ return log_oom();
+
+ d = udev_device_new_from_devnum(udev, 'b', st.st_rdev);
+ if (!d)
+ return log_oom();
+
+ v = udev_device_get_property_value(d, "ID_FS_USAGE");
+ if (!streq_ptr(v, "filesystem")) {
+ log_error("%s does not contain a file system.", arg_mount_what);
+ return -EINVAL;
+ }
+
+ r = acquire_mount_type(d);
+ if (r < 0)
+ return r;
+
+ r = acquire_mount_options(d);
+ if (r < 0)
+ return r;
+
+ r = acquire_mount_where(d);
+ if (r < 0)
+ return r;
+
+ r = acquire_description(d);
+ if (r < 0)
+ return r;
+
+ r = acquire_removable(d);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+enum {
+ COLUMN_NODE,
+ COLUMN_PATH,
+ COLUMN_MODEL,
+ COLUMN_WWN,
+ COLUMN_FSTYPE,
+ COLUMN_LABEL,
+ COLUMN_UUID,
+ _COLUMN_MAX,
+};
+
+struct item {
+ char* columns[_COLUMN_MAX];
+};
+
+static int compare_item(const void *a, const void *b) {
+ const struct item *x = a, *y = b;
+
+ if (x->columns[COLUMN_NODE] == y->columns[COLUMN_NODE])
+ return 0;
+ if (!x->columns[COLUMN_NODE])
+ return 1;
+ if (!y->columns[COLUMN_NODE])
+ return -1;
+
+ return path_compare(x->columns[COLUMN_NODE], y->columns[COLUMN_NODE]);
+}
+
+static int list_devices(void) {
+
+ static const char * const titles[_COLUMN_MAX] = {
+ [COLUMN_NODE] = "NODE",
+ [COLUMN_PATH] = "PATH",
+ [COLUMN_MODEL] = "MODEL",
+ [COLUMN_WWN] = "WWN",
+ [COLUMN_FSTYPE] = "TYPE",
+ [COLUMN_LABEL] = "LABEL",
+ [COLUMN_UUID] = "UUID"
+ };
+
+ _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
+ _cleanup_udev_unref_ struct udev *udev = NULL;
+ struct udev_list_entry *item = NULL, *first = NULL;
+ size_t n_allocated = 0, n = 0, i;
+ size_t column_width[_COLUMN_MAX];
+ struct item *items = NULL;
+ unsigned c;
+ int r;
+
+ for (c = 0; c < _COLUMN_MAX; c++)
+ column_width[c] = strlen(titles[c]);
+
+ udev = udev_new();
+ if (!udev)
+ return log_oom();
+
+ e = udev_enumerate_new(udev);
+ if (!e)
+ return log_oom();
+
+ r = udev_enumerate_add_match_subsystem(e, "block");
+ if (r < 0)
+ return log_error_errno(r, "Failed to add block match: %m");
+
+ r = udev_enumerate_add_match_property(e, "ID_FS_USAGE", "filesystem");
+ if (r < 0)
+ return log_error_errno(r, "Failed to add property match: %m");
+
+ r = udev_enumerate_scan_devices(e);
+ if (r < 0)
+ return log_error_errno(r, "Failed to scan devices: %m");
+
+ first = udev_enumerate_get_list_entry(e);
+ udev_list_entry_foreach(item, first) {
+ _cleanup_udev_device_unref_ struct udev_device *d;
+ struct item *j;
+
+ d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
+ if (!d) {
+ r = log_oom();
+ goto finish;
+ }
+
+ if (!GREEDY_REALLOC0(items, n_allocated, n+1)) {
+ r = log_oom();
+ goto finish;
+ }
+
+ j = items + n++;
+
+ for (c = 0; c < _COLUMN_MAX; c++) {
+ const char *x;
+ size_t k;
+
+ switch (c) {
+
+ case COLUMN_NODE:
+ x = udev_device_get_devnode(d);
+ break;
+
+ case COLUMN_PATH:
+ x = udev_device_get_property_value(d, "ID_PATH");
+ break;
+
+ case COLUMN_MODEL:
+ x = get_model(d);
+ break;
+
+ case COLUMN_WWN:
+ x = udev_device_get_property_value(d, "ID_WWN");
+ break;
+
+ case COLUMN_FSTYPE:
+ x = udev_device_get_property_value(d, "ID_FS_TYPE");
+ break;
+
+ case COLUMN_LABEL:
+ x = get_label(d);
+ break;
+
+ case COLUMN_UUID:
+ x = udev_device_get_property_value(d, "ID_FS_UUID");
+ break;
+ }
+
+ if (isempty(x))
+ continue;
+
+ j->columns[c] = strdup(x);
+ if (!j->columns[c]) {
+ r = log_oom();
+ goto finish;
+ }
+
+ k = strlen(x);
+ if (k > column_width[c])
+ column_width[c] = k;
+ }
+ }
+
+ if (n == 0) {
+ log_info("No devices found.");
+ goto finish;
+ }
+
+ qsort_safe(items, n, sizeof(struct item), compare_item);
+
+ pager_open(arg_no_pager, false);
+
+ fputs(ansi_underline(), stdout);
+ for (c = 0; c < _COLUMN_MAX; c++) {
+ if (c > 0)
+ fputc(' ', stdout);
+
+ printf("%-*s", (int) column_width[c], titles[c]);
+ }
+ fputs(ansi_normal(), stdout);
+ fputc('\n', stdout);
+
+ for (i = 0; i < n; i++) {
+ for (c = 0; c < _COLUMN_MAX; c++) {
+ if (c > 0)
+ fputc(' ', stdout);
+
+ printf("%-*s", (int) column_width[c], strna(items[i].columns[c]));
+ }
+ fputc('\n', stdout);
+ }
+
+ r = 0;
+
+finish:
+ for (i = 0; i < n; i++)
+ for (c = 0; c < _COLUMN_MAX; c++)
+ free(items[i].columns[c]);
+
+ free(items);
+ return r;
+}
+
+int main(int argc, char* argv[]) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ int r;
+
+ log_parse_environment();
+ log_open();
+
+ r = parse_argv(argc, argv);
+ if (r <= 0)
+ goto finish;
+
+ if (arg_action == ACTION_LIST) {
+ r = list_devices();
+ goto finish;
+ }
+
+ r = discover_device();
+ if (r < 0)
+ goto finish;
+ if (!arg_mount_where) {
+ log_error("Can't figure out where to mount %s.", arg_mount_what);
+ r = -EINVAL;
+ goto finish;
+ }
+
+ path_kill_slashes(arg_mount_where);
+
+ if (path_equal(arg_mount_where, "/")) {
+ log_error("Refusing to operate on root directory.");
+ r = -EINVAL;
+ goto finish;
+ }
+
+ if (!path_is_safe(arg_mount_where)) {
+ log_error("Path is contains unsafe components.");
+ r = -EINVAL;
+ goto finish;
+ }
+
+ if (streq_ptr(arg_mount_type, "auto"))
+ arg_mount_type = mfree(arg_mount_type);
+ if (streq_ptr(arg_mount_options, "defaults"))
+ arg_mount_options = mfree(arg_mount_options);
+
+ if (!is_device_path(arg_mount_what))
+ arg_fsck = false;
+
+ if (arg_fsck && arg_mount_type && arg_transport == BUS_TRANSPORT_LOCAL) {
+ r = fsck_exists(arg_mount_type);
+ if (r < 0)
+ log_warning_errno(r, "Couldn't determine whether fsck for %s exists, proceeding anyway.", arg_mount_type);
+ else if (r == 0) {
+ log_debug("Disabling file system check as fsck for %s doesn't exist.", arg_mount_type);
+ arg_fsck = false; /* fsck doesn't exist, let's not attempt it */
+ }
+ }
+
+ 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;
+ }
+
+ switch (arg_action) {
+
+ case ACTION_MOUNT:
+ case ACTION_DEFAULT:
+ r = start_transient_mount(bus, argv + optind);
+ break;
+
+ case ACTION_AUTOMOUNT:
+ r = start_transient_automount(bus, argv + optind);
+ break;
+
+ default:
+ assert_not_reached("Unexpected action.");
+ }
+
+finish:
+ bus = sd_bus_flush_close_unref(bus);
+
+ pager_close();
+
+ free(arg_mount_what);
+ free(arg_mount_where);
+ free(arg_mount_type);
+ free(arg_mount_options);
+ free(arg_description);
+ strv_free(arg_property);
+ strv_free(arg_automount_property);
+
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 69ee7424ce..71484e3288 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -2805,17 +2805,17 @@ network_file_fail:
if (dhcp4_address) {
r = in_addr_from_string(AF_INET, dhcp4_address, &address);
if (r < 0) {
- log_link_debug_errno(link, r, "Falied to parse DHCPv4 address %s: %m", dhcp4_address);
+ log_link_debug_errno(link, r, "Failed to parse DHCPv4 address %s: %m", dhcp4_address);
goto dhcp4_address_fail;
}
r = sd_dhcp_client_new(&link->dhcp_client);
if (r < 0)
- return log_link_error_errno(link, r, "Falied to create DHCPv4 client: %m");
+ return log_link_error_errno(link, r, "Failed to create DHCPv4 client: %m");
r = sd_dhcp_client_set_request_address(link->dhcp_client, &address.in);
if (r < 0)
- return log_link_error_errno(link, r, "Falied to set initial DHCPv4 address %s: %m", dhcp4_address);
+ return log_link_error_errno(link, r, "Failed to set initial DHCPv4 address %s: %m", dhcp4_address);
}
dhcp4_address_fail:
@@ -2823,17 +2823,17 @@ dhcp4_address_fail:
if (ipv4ll_address) {
r = in_addr_from_string(AF_INET, ipv4ll_address, &address);
if (r < 0) {
- log_link_debug_errno(link, r, "Falied to parse IPv4LL address %s: %m", ipv4ll_address);
+ log_link_debug_errno(link, r, "Failed to parse IPv4LL address %s: %m", ipv4ll_address);
goto ipv4ll_address_fail;
}
r = sd_ipv4ll_new(&link->ipv4ll);
if (r < 0)
- return log_link_error_errno(link, r, "Falied to create IPv4LL client: %m");
+ return log_link_error_errno(link, r, "Failed to create IPv4LL client: %m");
r = sd_ipv4ll_set_address(link->ipv4ll, &address.in);
if (r < 0)
- return log_link_error_errno(link, r, "Falied to set initial IPv4LL address %s: %m", ipv4ll_address);
+ return log_link_error_errno(link, r, "Failed to set initial IPv4LL address %s: %m", ipv4ll_address);
}
ipv4ll_address_fail:
diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h
index 2809b1fe0b..05b2a2b323 100644
--- a/src/network/networkd-link.h
+++ b/src/network/networkd-link.h
@@ -186,7 +186,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Link*, link_unref);
#define log_link_full(link, level, error, ...) \
({ \
- Link *_l = (link); \
+ const Link *_l = (link); \
_l ? log_object_internal(level, error, __FILE__, __LINE__, __func__, "INTERFACE=", _l->ifname, ##__VA_ARGS__) : \
log_internal(level, error, __FILE__, __LINE__, __func__, ##__VA_ARGS__); \
}) \
diff --git a/src/network/networkd-netdev.h b/src/network/networkd-netdev.h
index b92a973b85..09863e72b4 100644
--- a/src/network/networkd-netdev.h
+++ b/src/network/networkd-netdev.h
@@ -180,7 +180,7 @@ const struct ConfigPerfItem* network_netdev_gperf_lookup(const char *key, unsign
#define log_netdev_full(netdev, level, error, ...) \
({ \
- NetDev *_n = (netdev); \
+ const NetDev *_n = (netdev); \
_n ? log_object_internal(level, error, __FILE__, __LINE__, __func__, "INTERFACE=", _n->ifname, ##__VA_ARGS__) : \
log_internal(level, error, __FILE__, __LINE__, __func__, ##__VA_ARGS__); \
})
diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c
index b197a5da6b..6f60ee5e31 100644
--- a/src/network/networkd-route.c
+++ b/src/network/networkd-route.c
@@ -26,10 +26,37 @@
#include "parse-util.h"
#include "set.h"
#include "string-util.h"
+#include "sysctl-util.h"
#include "util.h"
-#define ROUTES_PER_LINK_MAX 2048U
-#define STATIC_ROUTES_PER_NETWORK_MAX 1024U
+#define ROUTES_DEFAULT_MAX_PER_FAMILY 4096U
+
+static unsigned routes_max(void) {
+ static thread_local unsigned cached = 0;
+
+ _cleanup_free_ char *s4 = NULL, *s6 = NULL;
+ unsigned val4 = ROUTES_DEFAULT_MAX_PER_FAMILY, val6 = ROUTES_DEFAULT_MAX_PER_FAMILY;
+
+ if (cached > 0)
+ return cached;
+
+ if (sysctl_read("net/ipv4/route/max_size", &s4) >= 0) {
+ truncate_nl(s4);
+ if (safe_atou(s4, &val4) >= 0 &&
+ val4 == 2147483647U)
+ /* This is the default "no limit" value in the kernel */
+ val4 = ROUTES_DEFAULT_MAX_PER_FAMILY;
+ }
+
+ if (sysctl_read("net/ipv6/route/max_size", &s6) >= 0) {
+ truncate_nl(s6);
+ (void) safe_atou(s6, &val6);
+ }
+
+ cached = MAX(ROUTES_DEFAULT_MAX_PER_FAMILY, val4) +
+ MAX(ROUTES_DEFAULT_MAX_PER_FAMILY, val6);
+ return cached;
+}
int route_new(Route **ret) {
_cleanup_route_free_ Route *route = NULL;
@@ -67,7 +94,7 @@ int route_new_static(Network *network, unsigned section, Route **ret) {
}
}
- if (network->n_static_routes >= STATIC_ROUTES_PER_NETWORK_MAX)
+ if (network->n_static_routes >= routes_max())
return -E2BIG;
r = route_new(&route);
@@ -484,7 +511,7 @@ int route_configure(
assert(route->family == AF_INET || route->family == AF_INET6);
if (route_get(link, route->family, &route->dst, route->dst_prefixlen, route->tos, route->priority, route->table, NULL) <= 0 &&
- set_size(link->routes) >= ROUTES_PER_LINK_MAX)
+ set_size(link->routes) >= routes_max())
return -E2BIG;
r = sd_rtnl_message_new_route(link->manager->rtnl, &req,
diff --git a/src/nspawn/nspawn-cgroup.c b/src/nspawn/nspawn-cgroup.c
index b1580236c9..aa0da04955 100644
--- a/src/nspawn/nspawn-cgroup.c
+++ b/src/nspawn/nspawn-cgroup.c
@@ -20,7 +20,6 @@
#include <sys/mount.h>
#include "alloc-util.h"
-#include "cgroup-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "mkdir.h"
@@ -63,18 +62,18 @@ int chown_cgroup(pid_t pid, uid_t uid_shift) {
return 0;
}
-int sync_cgroup(pid_t pid, bool unified_requested) {
+int sync_cgroup(pid_t pid, CGroupUnified unified_requested) {
_cleanup_free_ char *cgroup = NULL;
char tree[] = "/tmp/unifiedXXXXXX", pid_string[DECIMAL_STR_MAX(pid) + 1];
bool undo_mount = false;
const char *fn;
int unified, r;
- unified = cg_unified();
+ unified = cg_unified(SYSTEMD_CGROUP_CONTROLLER);
if (unified < 0)
return log_error_errno(unified, "Failed to determine whether the unified hierarchy is used: %m");
- if ((unified > 0) == unified_requested)
+ if ((unified > 0) == (unified_requested >= CGROUP_UNIFIED_SYSTEMD))
return 0;
/* When the host uses the legacy cgroup setup, but the
@@ -117,7 +116,7 @@ finish:
return r;
}
-int create_subcgroup(pid_t pid, bool unified_requested) {
+int create_subcgroup(pid_t pid, CGroupUnified unified_requested) {
_cleanup_free_ char *cgroup = NULL;
const char *child;
int unified, r;
@@ -129,10 +128,10 @@ int create_subcgroup(pid_t pid, bool unified_requested) {
* did not create a scope unit for the container move us and
* the container into two separate subcgroups. */
- if (!unified_requested)
+ if (unified_requested == CGROUP_UNIFIED_NONE)
return 0;
- unified = cg_unified();
+ unified = cg_unified(SYSTEMD_CGROUP_CONTROLLER);
if (unified < 0)
return log_error_errno(unified, "Failed to determine whether the unified hierarchy is used: %m");
if (unified == 0)
diff --git a/src/nspawn/nspawn-cgroup.h b/src/nspawn/nspawn-cgroup.h
index 1ff35a299a..dc33da8abe 100644
--- a/src/nspawn/nspawn-cgroup.h
+++ b/src/nspawn/nspawn-cgroup.h
@@ -22,6 +22,8 @@
#include <stdbool.h>
#include <sys/types.h>
+#include "cgroup-util.h"
+
int chown_cgroup(pid_t pid, uid_t uid_shift);
-int sync_cgroup(pid_t pid, bool unified_requested);
-int create_subcgroup(pid_t pid, bool unified_requested);
+int sync_cgroup(pid_t pid, CGroupUnified unified_requested);
+int create_subcgroup(pid_t pid, CGroupUnified unified_requested);
diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c
index 803caef3dd..295b75341f 100644
--- a/src/nspawn/nspawn-mount.c
+++ b/src/nspawn/nspawn-mount.c
@@ -21,7 +21,6 @@
#include <linux/magic.h>
#include "alloc-util.h"
-#include "cgroup-util.h"
#include "escape.h"
#include "fd-util.h"
#include "fileio.h"
@@ -661,7 +660,8 @@ static int get_controllers(Set *subsystems) {
return 0;
}
-static int mount_legacy_cgroup_hierarchy(const char *dest, const char *controller, const char *hierarchy, bool read_only) {
+static int mount_legacy_cgroup_hierarchy(const char *dest, const char *controller, const char *hierarchy,
+ CGroupUnified unified_requested, bool read_only) {
char *to;
int r;
@@ -677,7 +677,15 @@ static int mount_legacy_cgroup_hierarchy(const char *dest, const char *controlle
/* The superblock mount options of the mount point need to be
* identical to the hosts', and hence writable... */
- if (mount("cgroup", to, "cgroup", MS_NOSUID|MS_NOEXEC|MS_NODEV, controller) < 0)
+ if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
+ if (unified_requested >= CGROUP_UNIFIED_SYSTEMD)
+ r = mount("cgroup", to, "cgroup2", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL);
+ else
+ r = mount("cgroup", to, "cgroup", MS_NOSUID|MS_NOEXEC|MS_NODEV, "none,name=systemd,xattr");
+ } else
+ r = mount("cgroup", to, "cgroup", MS_NOSUID|MS_NOEXEC|MS_NODEV, controller);
+
+ if (r < 0)
return log_error_errno(errno, "Failed to mount to %s: %m", to);
/* ... hence let's only make the bind mount read-only, not the
@@ -691,8 +699,8 @@ static int mount_legacy_cgroup_hierarchy(const char *dest, const char *controlle
/* Mount a legacy cgroup hierarchy when cgroup namespaces are supported. */
static int mount_legacy_cgns_supported(
- bool userns, uid_t uid_shift, uid_t uid_range,
- const char *selinux_apifs_context) {
+ CGroupUnified unified_requested, bool userns, uid_t uid_shift,
+ uid_t uid_range, const char *selinux_apifs_context) {
_cleanup_set_free_free_ Set *controllers = NULL;
const char *cgroup_root = "/sys/fs/cgroup", *c;
int r;
@@ -721,7 +729,7 @@ static int mount_legacy_cgns_supported(
return log_error_errno(errno, "Failed to mount /sys/fs/cgroup: %m");
}
- if (cg_unified() > 0)
+ if (cg_all_unified() > 0)
goto skip_controllers;
controllers = set_new(&string_hash_ops);
@@ -739,7 +747,7 @@ static int mount_legacy_cgns_supported(
if (!controller)
break;
- r = mount_legacy_cgroup_hierarchy("", controller, controller, !userns);
+ r = mount_legacy_cgroup_hierarchy("", controller, controller, unified_requested, !userns);
if (r < 0)
return r;
@@ -773,7 +781,7 @@ static int mount_legacy_cgns_supported(
}
skip_controllers:
- r = mount_legacy_cgroup_hierarchy("", "none,name=systemd,xattr", "systemd", false);
+ r = mount_legacy_cgroup_hierarchy("", SYSTEMD_CGROUP_CONTROLLER, "systemd", unified_requested, false);
if (r < 0)
return r;
@@ -788,7 +796,7 @@ skip_controllers:
/* Mount legacy cgroup hierarchy when cgroup namespaces are unsupported. */
static int mount_legacy_cgns_unsupported(
const char *dest,
- bool userns, uid_t uid_shift, uid_t uid_range,
+ CGroupUnified unified_requested, bool userns, uid_t uid_shift, uid_t uid_range,
const char *selinux_apifs_context) {
_cleanup_set_free_free_ Set *controllers = NULL;
const char *cgroup_root;
@@ -813,7 +821,7 @@ static int mount_legacy_cgns_unsupported(
return log_error_errno(errno, "Failed to mount /sys/fs/cgroup: %m");
}
- if (cg_unified() > 0)
+ if (cg_all_unified() > 0)
goto skip_controllers;
controllers = set_new(&string_hash_ops);
@@ -839,7 +847,7 @@ static int mount_legacy_cgns_unsupported(
if (r == -EINVAL) {
/* Not a symbolic link, but directly a single cgroup hierarchy */
- r = mount_legacy_cgroup_hierarchy(dest, controller, controller, true);
+ r = mount_legacy_cgroup_hierarchy(dest, controller, controller, unified_requested, true);
if (r < 0)
return r;
@@ -859,7 +867,7 @@ static int mount_legacy_cgns_unsupported(
continue;
}
- r = mount_legacy_cgroup_hierarchy(dest, combined, combined, true);
+ r = mount_legacy_cgroup_hierarchy(dest, combined, combined, unified_requested, true);
if (r < 0)
return r;
@@ -872,7 +880,7 @@ static int mount_legacy_cgns_unsupported(
}
skip_controllers:
- r = mount_legacy_cgroup_hierarchy(dest, "none,name=systemd,xattr", "systemd", false);
+ r = mount_legacy_cgroup_hierarchy(dest, SYSTEMD_CGROUP_CONTROLLER, "systemd", unified_requested, false);
if (r < 0)
return r;
@@ -914,22 +922,22 @@ static int mount_unified_cgroups(const char *dest) {
int mount_cgroups(
const char *dest,
- bool unified_requested,
+ CGroupUnified unified_requested,
bool userns, uid_t uid_shift, uid_t uid_range,
const char *selinux_apifs_context,
bool use_cgns) {
- if (unified_requested)
+ if (unified_requested >= CGROUP_UNIFIED_ALL)
return mount_unified_cgroups(dest);
else if (use_cgns && cg_ns_supported())
- return mount_legacy_cgns_supported(userns, uid_shift, uid_range, selinux_apifs_context);
+ return mount_legacy_cgns_supported(unified_requested, userns, uid_shift, uid_range, selinux_apifs_context);
- return mount_legacy_cgns_unsupported(dest, userns, uid_shift, uid_range, selinux_apifs_context);
+ return mount_legacy_cgns_unsupported(dest, unified_requested, userns, uid_shift, uid_range, selinux_apifs_context);
}
int mount_systemd_cgroup_writable(
const char *dest,
- bool unified_requested) {
+ CGroupUnified unified_requested) {
_cleanup_free_ char *own_cgroup_path = NULL;
const char *systemd_root, *systemd_own;
@@ -945,7 +953,7 @@ int mount_systemd_cgroup_writable(
if (path_equal(own_cgroup_path, "/"))
return 0;
- if (unified_requested) {
+ if (unified_requested >= CGROUP_UNIFIED_ALL) {
systemd_own = strjoina(dest, "/sys/fs/cgroup", own_cgroup_path);
systemd_root = prefix_roota(dest, "/sys/fs/cgroup");
} else {
diff --git a/src/nspawn/nspawn-mount.h b/src/nspawn/nspawn-mount.h
index 0eff8e1006..7307a838a5 100644
--- a/src/nspawn/nspawn-mount.h
+++ b/src/nspawn/nspawn-mount.h
@@ -21,6 +21,8 @@
#include <stdbool.h>
+#include "cgroup-util.h"
+
typedef enum VolatileMode {
VOLATILE_NO,
VOLATILE_YES,
@@ -58,8 +60,8 @@ int custom_mount_compare(const void *a, const void *b);
int mount_all(const char *dest, bool use_userns, bool in_userns, bool use_netns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context);
int mount_sysfs(const char *dest);
-int mount_cgroups(const char *dest, bool unified_requested, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context, bool use_cgns);
-int mount_systemd_cgroup_writable(const char *dest, bool unified_requested);
+int mount_cgroups(const char *dest, CGroupUnified unified_requested, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context, bool use_cgns);
+int mount_systemd_cgroup_writable(const char *dest, CGroupUnified unified_requested);
int mount_custom(const char *dest, CustomMount *mounts, unsigned n, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context);
diff --git a/src/nspawn/nspawn-register.c b/src/nspawn/nspawn-register.c
index e5b76a0c5d..06c56d9ec8 100644
--- a/src/nspawn/nspawn-register.c
+++ b/src/nspawn/nspawn-register.c
@@ -68,7 +68,6 @@ int register_machine(
local_ifindex > 0 ? 1 : 0, local_ifindex);
} else {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
- char **i;
unsigned j;
r = sd_bus_message_new_method_call(
@@ -157,11 +156,9 @@ int register_machine(
return bus_log_create_error(r);
}
- STRV_FOREACH(i, properties) {
- r = bus_append_unit_property_assignment(m, *i);
- if (r < 0)
- return r;
- }
+ r = bus_append_unit_property_assignment_many(m, properties);
+ if (r < 0)
+ return r;
r = sd_bus_message_close_container(m);
if (r < 0)
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index fcf14bba4c..6d0420965a 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -169,7 +169,6 @@ static CustomMount *arg_custom_mounts = NULL;
static unsigned arg_n_custom_mounts = 0;
static char **arg_setenv = NULL;
static bool arg_quiet = false;
-static bool arg_share_system = false;
static bool arg_register = true;
static bool arg_keep_unit = false;
static char **arg_network_interfaces = NULL;
@@ -188,13 +187,14 @@ static UserNamespaceMode arg_userns_mode = USER_NAMESPACE_NO;
static uid_t arg_uid_shift = UID_INVALID, arg_uid_range = 0x10000U;
static bool arg_userns_chown = false;
static int arg_kill_signal = 0;
-static bool arg_unified_cgroup_hierarchy = false;
+static CGroupUnified arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_UNKNOWN;
static SettingsMask arg_settings_mask = 0;
static int arg_settings_trusted = -1;
static char **arg_parameters = NULL;
static const char *arg_container_service_name = "systemd-nspawn";
static bool arg_notify_ready = false;
static bool arg_use_cgns = true;
+static unsigned long arg_clone_ns_flags = CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS;
static void help(void) {
printf("%s [OPTIONS...] [PATH] [ARGUMENTS...]\n\n"
@@ -318,7 +318,14 @@ static int custom_mounts_prepare(void) {
static int detect_unified_cgroup_hierarchy(void) {
const char *e;
- int r;
+ int r, all_unified, systemd_unified;
+
+ all_unified = cg_all_unified();
+ systemd_unified = cg_unified(SYSTEMD_CGROUP_CONTROLLER);
+
+ if (all_unified < 0 || systemd_unified < 0)
+ return log_error_errno(all_unified < 0 ? all_unified : systemd_unified,
+ "Failed to determine whether the unified cgroups hierarchy is used: %m");
/* Allow the user to control whether the unified hierarchy is used */
e = getenv("UNIFIED_CGROUP_HIERARCHY");
@@ -326,20 +333,36 @@ static int detect_unified_cgroup_hierarchy(void) {
r = parse_boolean(e);
if (r < 0)
return log_error_errno(r, "Failed to parse $UNIFIED_CGROUP_HIERARCHY.");
+ if (r > 0)
+ arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_ALL;
+ else
+ arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_NONE;
- arg_unified_cgroup_hierarchy = r;
return 0;
}
/* Otherwise inherit the default from the host system */
- r = cg_unified();
- if (r < 0)
- return log_error_errno(r, "Failed to determine whether the unified cgroups hierarchy is used: %m");
+ if (all_unified > 0)
+ arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_ALL;
+ else if (systemd_unified > 0)
+ arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_SYSTEMD;
+ else
+ arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_NONE;
- arg_unified_cgroup_hierarchy = r;
return 0;
}
+static void parse_share_ns_env(const char *name, unsigned long ns_flag) {
+ int r;
+
+ r = getenv_bool(name);
+ if (r == -ENXIO)
+ return;
+ if (r < 0)
+ log_warning_errno(r, "Failed to parse %s from environment, defaulting to false.", name);
+ arg_clone_ns_flags = (arg_clone_ns_flags & ~ns_flag) | (r > 0 ? 0 : ns_flag);
+}
+
static int parse_argv(int argc, char *argv[]) {
enum {
@@ -812,8 +835,8 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_SHARE_SYSTEM:
/* We don't officially support this anymore, except for compat reasons. People should use the
- * $SYSTEMD_NSPAWN_SHARE_SYSTEM environment variable instead. */
- arg_share_system = true;
+ * $SYSTEMD_NSPAWN_SHARE_* environment variables instead. */
+ arg_clone_ns_flags = 0;
break;
case ARG_REGISTER:
@@ -1017,20 +1040,22 @@ static int parse_argv(int argc, char *argv[]) {
assert_not_reached("Unhandled option");
}
- if (getenv_bool("SYSTEMD_NSPAWN_SHARE_SYSTEM") > 0)
- arg_share_system = true;
+ parse_share_ns_env("SYSTEMD_NSPAWN_SHARE_NS_IPC", CLONE_NEWIPC);
+ parse_share_ns_env("SYSTEMD_NSPAWN_SHARE_NS_PID", CLONE_NEWPID);
+ parse_share_ns_env("SYSTEMD_NSPAWN_SHARE_NS_UTS", CLONE_NEWUTS);
+ parse_share_ns_env("SYSTEMD_NSPAWN_SHARE_SYSTEM", CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS);
- if (arg_share_system)
+ if (arg_clone_ns_flags != (CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS)) {
arg_register = false;
+ if (arg_start_mode != START_PID1) {
+ log_error("--boot cannot be used without namespacing.");
+ return -EINVAL;
+ }
+ }
if (arg_userns_mode == USER_NAMESPACE_PICK)
arg_userns_chown = true;
- if (arg_start_mode != START_PID1 && arg_share_system) {
- log_error("--boot and SYSTEMD_NSPAWN_SHARE_SYSTEM=1 may not be combined.");
- return -EINVAL;
- }
-
if (arg_keep_unit && cg_pid_get_owner_uid(0, NULL) >= 0) {
log_error("--keep-unit may not be used when invoked from a user session.");
return -EINVAL;
@@ -1298,9 +1323,6 @@ static int setup_boot_id(const char *dest) {
const char *from, *to;
int r;
- if (arg_share_system)
- return 0;
-
/* Generate a new randomized boot ID, so that each boot-up of
* the container gets a new one */
@@ -1518,7 +1540,7 @@ static int on_address_change(sd_netlink *rtnl, sd_netlink_message *m, void *user
static int setup_hostname(void) {
- if (arg_share_system)
+ if ((arg_clone_ns_flags & CLONE_NEWUTS) == 0)
return 0;
if (sethostname_idempotent(arg_machine) < 0)
@@ -1669,7 +1691,7 @@ static int reset_audit_loginuid(void) {
_cleanup_free_ char *p = NULL;
int r;
- if (arg_share_system)
+ if ((arg_clone_ns_flags & CLONE_NEWPID) == 0)
return 0;
r = read_one_line_file("/proc/self/loginuid", &p);
@@ -3076,7 +3098,7 @@ static int outer_child(
return fd;
pid = raw_clone(SIGCHLD|CLONE_NEWNS|
- (arg_share_system ? 0 : CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS) |
+ arg_clone_ns_flags |
(arg_private_network ? CLONE_NEWNET : 0) |
(arg_userns_mode != USER_NAMESPACE_NO ? CLONE_NEWUSER : 0));
if (pid < 0)
diff --git a/src/run/run.c b/src/run/run.c
index 1917ffd857..2dd229868c 100644
--- a/src/run/run.c
+++ b/src/run/run.c
@@ -33,6 +33,7 @@
#include "formats-util.h"
#include "parse-util.h"
#include "path-util.h"
+#include "process-util.h"
#include "ptyfwd.h"
#include "signal-util.h"
#include "spawn-polkit-agent.h"
@@ -45,6 +46,7 @@ static bool arg_ask_password = true;
static bool arg_scope = false;
static bool arg_remain_after_exit = false;
static bool arg_no_block = false;
+static bool arg_wait = false;
static const char *arg_unit = NULL;
static const char *arg_description = NULL;
static const char *arg_slice = NULL;
@@ -83,9 +85,7 @@ static void polkit_agent_open_if_enabled(void) {
static void help(void) {
printf("%s [OPTIONS...] {COMMAND} [ARGS...]\n\n"
- "Run the specified command in a transient scope or service or timer\n"
- "unit. If a timer option is specified and the unit specified with\n"
- "the --unit option exists, the command can be omitted.\n\n"
+ "Run the specified command in a transient scope or service.\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
" --no-ask-password Do not prompt for password\n"
@@ -94,11 +94,12 @@ static void help(void) {
" -M --machine=CONTAINER Operate on local container\n"
" --scope Run this as scope rather than service\n"
" --unit=UNIT Run under the specified unit name\n"
- " -p --property=NAME=VALUE Set unit property\n"
+ " -p --property=NAME=VALUE Set service or scope unit property\n"
" --description=TEXT Description for unit\n"
" --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"
@@ -107,15 +108,15 @@ static void help(void) {
" -E --setenv=NAME=VALUE Set environment\n"
" -t --pty Run service on pseudo tty\n"
" -q --quiet Suppress information messages during runtime\n\n"
- "Timer options:\n\n"
+ "Timer options:\n"
" --on-active=SECONDS Run after SECONDS delay\n"
" --on-boot=SECONDS Run SECONDS after machine was booted up\n"
" --on-startup=SECONDS Run SECONDS after systemd activation\n"
" --on-unit-active=SECONDS Run SECONDS after the last activation\n"
" --on-unit-inactive=SECONDS Run SECONDS after the last deactivation\n"
" --on-calendar=SPEC Realtime timer\n"
- " --timer-property=NAME=VALUE Set timer unit property\n",
- program_invocation_short_name);
+ " --timer-property=NAME=VALUE Set timer unit property\n"
+ , program_invocation_short_name);
}
static bool with_timer(void) {
@@ -146,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[] = {
@@ -162,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 },
@@ -178,7 +181,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "on-calendar", required_argument, NULL, ARG_ON_CALENDAR },
{ "timer-property", required_argument, NULL, ARG_TIMER_PROPERTY },
{ "no-block", no_argument, NULL, ARG_NO_BLOCK },
- { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
+ { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
{},
};
@@ -195,13 +198,13 @@ static int parse_argv(int argc, char *argv[]) {
help();
return 0;
+ case ARG_VERSION:
+ return version();
+
case ARG_NO_ASK_PASSWORD:
arg_ask_password = false;
break;
- case ARG_VERSION:
- return version();
-
case ARG_USER:
arg_user = true;
break;
@@ -359,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;
@@ -406,22 +413,36 @@ 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;
}
static int transient_unit_set_properties(sd_bus_message *m, char **properties) {
- char **i;
int r;
r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description);
if (r < 0)
return r;
- STRV_FOREACH(i, properties) {
- r = bus_append_unit_property_assignment(m, *i);
- if (r < 0)
- return r;
- }
+ r = bus_append_unit_property_assignment_many(m, properties);
+ if (r < 0)
+ return r;
return 0;
}
@@ -471,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)
@@ -728,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;
@@ -741,6 +856,7 @@ static int start_transient_service(
assert(bus);
assert(argv);
+ assert(retval);
if (arg_pty) {
@@ -864,40 +980,95 @@ static int start_transient_service(
return r;
}
- if (master >= 0) {
- _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
- _cleanup_(sd_event_unrefp) sd_event *event = NULL;
- char last_char = 0;
+ if (!arg_quiet)
+ log_info("Running as unit: %s", service);
+
+ if (arg_wait || master >= 0) {
+ _cleanup_(run_context_free) RunContext c = {};
+
+ c.bus = sd_bus_ref(bus);
- r = sd_event_default(&event);
+ 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;
}
@@ -1144,7 +1315,7 @@ static int start_transient_timer(
if (r < 0)
return bus_log_create_error(r);
- if (argv[0]) {
+ if (!strv_isempty(argv)) {
r = sd_bus_message_open_container(m, 'r', "sa(sv)");
if (r < 0)
return bus_log_create_error(r);
@@ -1190,9 +1361,11 @@ static int start_transient_timer(
if (r < 0)
return r;
- log_info("Running timer as unit: %s", timer);
- if (argv[0])
- log_info("Will run service as unit: %s", service);
+ if (!arg_quiet) {
+ log_info("Running timer as unit: %s", timer);
+ if (argv[0])
+ log_info("Will run service as unit: %s", service);
+ }
return 0;
}
@@ -1200,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();
@@ -1237,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;
@@ -1248,12 +1426,12 @@ int main(int argc, char* argv[]) {
else if (with_timer())
r = start_transient_timer(bus, argv + optind);
else
- r = start_transient_service(bus, argv + optind);
+ r = start_transient_service(bus, argv + optind, &retval);
finish:
strv_free(arg_environment);
strv_free(arg_property);
strv_free(arg_timer_property);
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return r < 0 ? EXIT_FAILURE : retval;
}
diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c
index ab30afb527..feb4a06737 100644
--- a/src/shared/bus-unit-util.c
+++ b/src/shared/bus-unit-util.c
@@ -568,6 +568,21 @@ finish:
return 0;
}
+int bus_append_unit_property_assignment_many(sd_bus_message *m, char **l) {
+ char **i;
+ int r;
+
+ assert(m);
+
+ STRV_FOREACH(i, l) {
+ r = bus_append_unit_property_assignment(m, *i);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
typedef struct BusWaitForJobs {
sd_bus *bus;
Set *jobs;
diff --git a/src/shared/bus-unit-util.h b/src/shared/bus-unit-util.h
index c0c172f336..d102ea180e 100644
--- a/src/shared/bus-unit-util.h
+++ b/src/shared/bus-unit-util.h
@@ -41,6 +41,7 @@ typedef struct UnitInfo {
int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u);
int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment);
+int bus_append_unit_property_assignment_many(sd_bus_message *m, char **l);
typedef struct BusWaitForJobs BusWaitForJobs;
diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c
index 52410999cf..e2a216a5cc 100644
--- a/src/shared/bus-util.c
+++ b/src/shared/bus-util.c
@@ -1016,19 +1016,19 @@ static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_
return r;
switch (type) {
+
case SD_BUS_TYPE_STRING: {
- const char *s;
char **p = userdata;
+ const char *s;
r = sd_bus_message_read_basic(m, type, &s);
if (r < 0)
- break;
+ return r;
if (isempty(s))
- break;
+ s = NULL;
- r = free_and_strdup(p, s);
- break;
+ return free_and_strdup(p, s);
}
case SD_BUS_TYPE_ARRAY: {
@@ -1037,13 +1037,12 @@ static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_
r = bus_message_read_strv_extend(m, &l);
if (r < 0)
- break;
+ return r;
strv_free(*p);
*p = l;
l = NULL;
-
- break;
+ return 0;
}
case SD_BUS_TYPE_BOOLEAN: {
@@ -1052,57 +1051,48 @@ static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_
r = sd_bus_message_read_basic(m, type, &b);
if (r < 0)
- break;
+ return r;
*p = b;
-
- break;
+ return 0;
}
+ case SD_BUS_TYPE_INT32:
case SD_BUS_TYPE_UINT32: {
- uint32_t u;
- uint32_t *p = userdata;
+ uint32_t u, *p = userdata;
r = sd_bus_message_read_basic(m, type, &u);
if (r < 0)
- break;
+ return r;
*p = u;
-
- break;
+ return 0;
}
+ case SD_BUS_TYPE_INT64:
case SD_BUS_TYPE_UINT64: {
- uint64_t t;
- uint64_t *p = userdata;
+ uint64_t t, *p = userdata;
r = sd_bus_message_read_basic(m, type, &t);
if (r < 0)
- break;
+ return r;
*p = t;
-
- break;
+ return 0;
}
case SD_BUS_TYPE_DOUBLE: {
- double d;
- double *p = userdata;
+ double d, *p = userdata;
r = sd_bus_message_read_basic(m, type, &d);
if (r < 0)
- break;
+ return r;
*p = d;
+ return 0;
+ }}
- break;
- }
-
- default:
- break;
- }
-
- return r;
+ return -EOPNOTSUPP;
}
int bus_message_map_all_properties(
@@ -1240,12 +1230,13 @@ int bus_map_all_properties(
return bus_message_map_all_properties(m, map, userdata);
}
-int bus_connect_transport(BusTransport transport, const char *host, bool user, sd_bus **bus) {
+int bus_connect_transport(BusTransport transport, const char *host, bool user, sd_bus **ret) {
+ _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
int r;
assert(transport >= 0);
assert(transport < _BUS_TRANSPORT_MAX);
- assert(bus);
+ assert(ret);
assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL);
assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -EOPNOTSUPP);
@@ -1254,25 +1245,34 @@ int bus_connect_transport(BusTransport transport, const char *host, bool user, s
case BUS_TRANSPORT_LOCAL:
if (user)
- r = sd_bus_default_user(bus);
+ r = sd_bus_default_user(&bus);
else
- r = sd_bus_default_system(bus);
+ r = sd_bus_default_system(&bus);
break;
case BUS_TRANSPORT_REMOTE:
- r = sd_bus_open_system_remote(bus, host);
+ r = sd_bus_open_system_remote(&bus, host);
break;
case BUS_TRANSPORT_MACHINE:
- r = sd_bus_open_system_machine(bus, host);
+ r = sd_bus_open_system_machine(&bus, host);
break;
default:
assert_not_reached("Hmm, unknown transport type.");
}
+ if (r < 0)
+ return r;
- return r;
+ r = sd_bus_set_exit_on_disconnect(bus, true);
+ if (r < 0)
+ return r;
+
+ *ret = bus;
+ bus = NULL;
+
+ return 0;
}
int bus_connect_transport_systemd(BusTransport transport, const char *host, bool user, sd_bus **bus) {
diff --git a/src/shared/install.c b/src/shared/install.c
index 6a16f8985b..6b82ad05a7 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -912,8 +912,8 @@ static int install_info_may_process(
assert(i);
assert(paths);
- /* Checks whether the loaded unit file is one we should process, or is masked, transient or generated and thus
- * not subject to enable/disable operations. */
+ /* Checks whether the loaded unit file is one we should process, or is masked,
+ * transient or generated and thus not subject to enable/disable operations. */
if (i->type == UNIT_FILE_TYPE_MASKED) {
unit_file_changes_add(changes, n_changes, -ERFKILL, i->path, NULL);
@@ -1134,7 +1134,6 @@ static int unit_file_load(
struct stat st;
int r;
- assert(c);
assert(info);
assert(path);
@@ -1163,6 +1162,9 @@ static int unit_file_load(
return 0;
}
+ /* c is only needed if we actually load the file */
+ assert(c);
+
fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
if (fd < 0)
return -errno;
@@ -1275,7 +1277,6 @@ static int unit_file_search(
char **p;
int r;
- assert(c);
assert(info);
assert(paths);
@@ -1546,7 +1547,14 @@ static int install_info_symlink_wants(
assert(paths);
assert(config_path);
+ if (strv_isempty(list))
+ return 0;
+
if (unit_name_is_valid(i->name, UNIT_NAME_TEMPLATE)) {
+ UnitFileInstallInfo instance = {
+ .type = _UNIT_FILE_TYPE_INVALID,
+ };
+ _cleanup_free_ char *path = NULL;
/* Don't install any symlink if there's no default
* instance configured */
@@ -1558,6 +1566,19 @@ static int install_info_symlink_wants(
if (r < 0)
return r;
+ instance.name = buf;
+ r = unit_file_search(NULL, &instance, paths, SEARCH_FOLLOW_CONFIG_SYMLINKS);
+ if (r < 0)
+ return r;
+
+ path = instance.path;
+ instance.path = NULL;
+
+ if (instance.type == UNIT_FILE_TYPE_MASKED) {
+ unit_file_changes_add(changes, n_changes, -ERFKILL, path, NULL);
+ return -ERFKILL;
+ }
+
n = buf;
} else
n = i->name;
@@ -1687,12 +1708,12 @@ static int install_context_apply(
return r;
/* We can attempt to process a masked unit when a different unit
- * that we were processing specifies it in DefaultInstance= or Also=. */
+ * that we were processing specifies it in Also=. */
if (i->type == UNIT_FILE_TYPE_MASKED) {
unit_file_changes_add(changes, n_changes, UNIT_FILE_IS_MASKED, i->path, NULL);
if (r >= 0)
- /* Assume that some *could* have been enabled here, avoid
- * "empty [Install] section" warning. */
+ /* Assume that something *could* have been enabled here,
+ * avoid "empty [Install] section" warning. */
r += 1;
continue;
}
diff --git a/src/shared/ptyfwd.c b/src/shared/ptyfwd.c
index 02c03b98d8..24055e772b 100644
--- a/src/shared/ptyfwd.c
+++ b/src/shared/ptyfwd.c
@@ -68,6 +68,8 @@ struct PTYForward {
bool read_from_master:1;
+ bool done:1;
+
bool last_char_set:1;
char last_char;
@@ -76,10 +78,54 @@ struct PTYForward {
usec_t escape_timestamp;
unsigned escape_counter;
+
+ PTYForwardHandler handler;
+ void *userdata;
};
#define ESCAPE_USEC (1*USEC_PER_SEC)
+static void pty_forward_disconnect(PTYForward *f) {
+
+ if (f) {
+ f->stdin_event_source = sd_event_source_unref(f->stdin_event_source);
+ f->stdout_event_source = sd_event_source_unref(f->stdout_event_source);
+
+ f->master_event_source = sd_event_source_unref(f->master_event_source);
+ f->sigwinch_event_source = sd_event_source_unref(f->sigwinch_event_source);
+ f->event = sd_event_unref(f->event);
+
+ if (f->saved_stdout)
+ tcsetattr(STDOUT_FILENO, TCSANOW, &f->saved_stdout_attr);
+ if (f->saved_stdin)
+ tcsetattr(STDIN_FILENO, TCSANOW, &f->saved_stdin_attr);
+
+ f->saved_stdout = f->saved_stdin = false;
+ }
+
+ /* STDIN/STDOUT should not be nonblocking normally, so let's unconditionally reset it */
+ fd_nonblock(STDIN_FILENO, false);
+ fd_nonblock(STDOUT_FILENO, false);
+}
+
+static int pty_forward_done(PTYForward *f, int rcode) {
+ _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+ assert(f);
+
+ if (f->done)
+ return 0;
+
+ e = sd_event_ref(f->event);
+
+ f->done = true;
+ pty_forward_disconnect(f);
+
+ if (f->handler)
+ return f->handler(f, rcode, f->userdata);
+ else
+ return sd_event_exit(e, rcode < 0 ? EXIT_FAILURE : rcode);
+}
+
static bool look_for_escape(PTYForward *f, const char *buffer, size_t n) {
const char *p;
@@ -147,7 +193,7 @@ static int shovel(PTYForward *f) {
f->stdin_event_source = sd_event_source_unref(f->stdin_event_source);
} else {
log_error_errno(errno, "read(): %m");
- return sd_event_exit(f->event, EXIT_FAILURE);
+ return pty_forward_done(f, -errno);
}
} else if (k == 0) {
/* EOF on stdin */
@@ -156,12 +202,10 @@ static int shovel(PTYForward *f) {
f->stdin_event_source = sd_event_source_unref(f->stdin_event_source);
} else {
- /* Check if ^] has been
- * pressed three times within
- * one second. If we get this
- * we quite immediately. */
+ /* Check if ^] has been pressed three times within one second. If we get this we quite
+ * immediately. */
if (look_for_escape(f, f->in_buffer + f->in_buffer_full, k))
- return sd_event_exit(f->event, EXIT_FAILURE);
+ return pty_forward_done(f, -ECANCELED);
f->in_buffer_full += (size_t) k;
}
@@ -181,7 +225,7 @@ static int shovel(PTYForward *f) {
f->master_event_source = sd_event_source_unref(f->master_event_source);
} else {
log_error_errno(errno, "write(): %m");
- return sd_event_exit(f->event, EXIT_FAILURE);
+ return pty_forward_done(f, -errno);
}
} else {
assert(f->in_buffer_full >= (size_t) k);
@@ -211,7 +255,7 @@ static int shovel(PTYForward *f) {
f->master_event_source = sd_event_source_unref(f->master_event_source);
} else {
log_error_errno(errno, "read(): %m");
- return sd_event_exit(f->event, EXIT_FAILURE);
+ return pty_forward_done(f, -errno);
}
} else {
f->read_from_master = true;
@@ -232,7 +276,7 @@ static int shovel(PTYForward *f) {
f->stdout_event_source = sd_event_source_unref(f->stdout_event_source);
} else {
log_error_errno(errno, "write(): %m");
- return sd_event_exit(f->event, EXIT_FAILURE);
+ return pty_forward_done(f, -errno);
}
} else {
@@ -255,7 +299,7 @@ static int shovel(PTYForward *f) {
if ((f->out_buffer_full <= 0 || f->stdout_hangup) &&
(f->in_buffer_full <= 0 || f->master_hangup))
- return sd_event_exit(f->event, EXIT_SUCCESS);
+ return pty_forward_done(f, 0);
}
return 0;
@@ -418,27 +462,8 @@ int pty_forward_new(
}
PTYForward *pty_forward_free(PTYForward *f) {
-
- if (f) {
- sd_event_source_unref(f->stdin_event_source);
- sd_event_source_unref(f->stdout_event_source);
- sd_event_source_unref(f->master_event_source);
- sd_event_source_unref(f->sigwinch_event_source);
- sd_event_unref(f->event);
-
- if (f->saved_stdout)
- tcsetattr(STDOUT_FILENO, TCSANOW, &f->saved_stdout_attr);
- if (f->saved_stdin)
- tcsetattr(STDIN_FILENO, TCSANOW, &f->saved_stdin_attr);
-
- free(f);
- }
-
- /* STDIN/STDOUT should not be nonblocking normally, so let's
- * unconditionally reset it */
- fd_nonblock(STDIN_FILENO, false);
- fd_nonblock(STDOUT_FILENO, false);
-
+ pty_forward_disconnect(f);
+ free(f);
return NULL;
}
@@ -477,8 +502,21 @@ int pty_forward_set_ignore_vhangup(PTYForward *f, bool b) {
return 0;
}
-int pty_forward_get_ignore_vhangup(PTYForward *f) {
+bool pty_forward_get_ignore_vhangup(PTYForward *f) {
assert(f);
return !!(f->flags & PTY_FORWARD_IGNORE_VHANGUP);
}
+
+bool pty_forward_is_done(PTYForward *f) {
+ assert(f);
+
+ return f->done;
+}
+
+void pty_forward_set_handler(PTYForward *f, PTYForwardHandler cb, void *userdata) {
+ assert(f);
+
+ f->handler = cb;
+ f->userdata = userdata;
+}
diff --git a/src/shared/ptyfwd.h b/src/shared/ptyfwd.h
index a046eb4e5e..bd5d5fec0d 100644
--- a/src/shared/ptyfwd.h
+++ b/src/shared/ptyfwd.h
@@ -37,12 +37,18 @@ typedef enum PTYForwardFlags {
PTY_FORWARD_IGNORE_INITIAL_VHANGUP = 4,
} PTYForwardFlags;
+typedef int (*PTYForwardHandler)(PTYForward *f, int rcode, void*userdata);
+
int pty_forward_new(sd_event *event, int master, PTYForwardFlags flags, PTYForward **f);
PTYForward *pty_forward_free(PTYForward *f);
int pty_forward_get_last_char(PTYForward *f, char *ch);
int pty_forward_set_ignore_vhangup(PTYForward *f, bool ignore_vhangup);
-int pty_forward_get_ignore_vhangup(PTYForward *f);
+bool pty_forward_get_ignore_vhangup(PTYForward *f);
+
+bool pty_forward_is_done(PTYForward *f);
+
+void pty_forward_set_handler(PTYForward *f, PTYForwardHandler handler, void *userdata);
DEFINE_TRIVIAL_CLEANUP_FUNC(PTYForward*, pty_forward_free);
diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c
index 8656d112b8..6c489284d1 100644
--- a/src/shared/seccomp-util.c
+++ b/src/shared/seccomp-util.c
@@ -21,6 +21,8 @@
#include <seccomp.h>
#include <stddef.h>
+#include "alloc-util.h"
+#include "fileio.h"
#include "macro.h"
#include "seccomp-util.h"
#include "string-util.h"
@@ -89,6 +91,14 @@ int seccomp_add_secondary_archs(scmp_filter_ctx *c) {
}
+bool is_seccomp_available(void) {
+ _cleanup_free_ char* field = NULL;
+ static int cached_enabled = -1;
+ if (cached_enabled < 0)
+ cached_enabled = get_proc_field("/proc/self/status", "Seccomp", "\n", &field) == 0;
+ return cached_enabled;
+}
+
const SystemCallFilterSet syscall_filter_sets[] = {
{
/* Clock */
@@ -127,6 +137,7 @@ const SystemCallFilterSet syscall_filter_sets[] = {
"execve\0"
"exit\0"
"exit_group\0"
+ "getrlimit\0" /* make sure processes can query stack size and such */
"rt_sigreturn\0"
"sigreturn\0"
}, {
diff --git a/src/shared/seccomp-util.h b/src/shared/seccomp-util.h
index be33eecb85..cca7c17912 100644
--- a/src/shared/seccomp-util.h
+++ b/src/shared/seccomp-util.h
@@ -27,6 +27,8 @@ int seccomp_arch_from_string(const char *n, uint32_t *ret);
int seccomp_add_secondary_archs(scmp_filter_ctx *c);
+bool is_seccomp_available(void);
+
typedef struct SystemCallFilterSet {
const char *set_name;
const char *value;
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index b4ce6fba5a..682805045d 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -3384,9 +3384,9 @@ static int kill_unit(int argc, char *argv[], void *userdata) {
"KillUnit",
&error,
NULL,
- "ssi", *names, kill_who ? kill_who : arg_kill_who, arg_signal);
+ "ssi", *name, kill_who ? kill_who : arg_kill_who, arg_signal);
if (q < 0) {
- log_error_errno(q, "Failed to kill unit %s: %s", *names, bus_error_message(&error, q));
+ log_error_errno(q, "Failed to kill unit %s: %s", *name, bus_error_message(&error, q));
if (r == 0)
r = q;
}
@@ -3571,6 +3571,7 @@ typedef struct UnitStatusInfo {
uint64_t memory_low;
uint64_t memory_high;
uint64_t memory_max;
+ uint64_t memory_swap_max;
uint64_t memory_limit;
uint64_t cpu_usage_nsec;
uint64_t tasks_current;
@@ -3883,7 +3884,8 @@ static void print_status_info(
printf(" Memory: %s", format_bytes(buf, sizeof(buf), i->memory_current));
- if (i->memory_low > 0 || i->memory_high != CGROUP_LIMIT_MAX || i->memory_max != CGROUP_LIMIT_MAX ||
+ if (i->memory_low > 0 || i->memory_high != CGROUP_LIMIT_MAX ||
+ i->memory_max != CGROUP_LIMIT_MAX || i->memory_swap_max != CGROUP_LIMIT_MAX ||
i->memory_limit != CGROUP_LIMIT_MAX) {
const char *prefix = "";
@@ -3900,6 +3902,10 @@ static void print_status_info(
printf("%smax: %s", prefix, format_bytes(buf, sizeof(buf), i->memory_max));
prefix = " ";
}
+ if (i->memory_swap_max != CGROUP_LIMIT_MAX) {
+ printf("%sswap max: %s", prefix, format_bytes(buf, sizeof(buf), i->memory_swap_max));
+ prefix = " ";
+ }
if (i->memory_limit != CGROUP_LIMIT_MAX) {
printf("%slimit: %s", prefix, format_bytes(buf, sizeof(buf), i->memory_limit));
prefix = " ";
@@ -4140,6 +4146,8 @@ static int status_property(const char *name, sd_bus_message *m, UnitStatusInfo *
i->memory_high = u;
else if (streq(name, "MemoryMax"))
i->memory_max = u;
+ else if (streq(name, "MemorySwapMax"))
+ i->memory_swap_max = u;
else if (streq(name, "MemoryLimit"))
i->memory_limit = u;
else if (streq(name, "TasksCurrent"))
@@ -4655,6 +4663,7 @@ static int show_one(
.memory_current = (uint64_t) -1,
.memory_high = CGROUP_LIMIT_MAX,
.memory_max = CGROUP_LIMIT_MAX,
+ .memory_swap_max = CGROUP_LIMIT_MAX,
.memory_limit = (uint64_t) -1,
.cpu_usage_nsec = (uint64_t) -1,
.tasks_current = (uint64_t) -1,
@@ -5093,7 +5102,6 @@ static int set_property(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_free_ char *n = NULL;
sd_bus *bus;
- char **i;
int r;
r = acquire_bus(BUS_MANAGER, &bus);
@@ -5124,11 +5132,9 @@ static int set_property(int argc, char *argv[], void *userdata) {
if (r < 0)
return bus_log_create_error(r);
- STRV_FOREACH(i, strv_skip(argv, 2)) {
- r = bus_append_unit_property_assignment(m, *i);
- if (r < 0)
- return r;
- }
+ r = bus_append_unit_property_assignment_many(m, strv_skip(argv, 2));
+ if (r < 0)
+ return r;
r = sd_bus_message_close_container(m);
if (r < 0)
diff --git a/src/systemd/sd-bus.h b/src/systemd/sd-bus.h
index 295989cd69..c47459c9ad 100644
--- a/src/systemd/sd-bus.h
+++ b/src/systemd/sd-bus.h
@@ -147,6 +147,8 @@ int sd_bus_can_send(sd_bus *bus, char type);
int sd_bus_get_creds_mask(sd_bus *bus, uint64_t *creds_mask);
int sd_bus_set_allow_interactive_authorization(sd_bus *bus, int b);
int sd_bus_get_allow_interactive_authorization(sd_bus *bus);
+int sd_bus_set_exit_on_disconnect(sd_bus *bus, int b);
+int sd_bus_get_exit_on_disconnect(sd_bus *bus);
int sd_bus_start(sd_bus *ret);
@@ -438,8 +440,14 @@ int sd_bus_track_remove_sender(sd_bus_track *track, sd_bus_message *m);
int sd_bus_track_add_name(sd_bus_track *track, const char *name);
int sd_bus_track_remove_name(sd_bus_track *track, const char *name);
+int sd_bus_track_set_recursive(sd_bus_track *track, int b);
+int sd_bus_track_get_recursive(sd_bus_track *track);
+
unsigned sd_bus_track_count(sd_bus_track *track);
-const char* sd_bus_track_contains(sd_bus_track *track, const char *names);
+int sd_bus_track_count_sender(sd_bus_track *track, sd_bus_message *m);
+int sd_bus_track_count_name(sd_bus_track *track, const char *name);
+
+const char* sd_bus_track_contains(sd_bus_track *track, const char *name);
const char* sd_bus_track_first(sd_bus_track *track);
const char* sd_bus_track_next(sd_bus_track *track);
diff --git a/src/test/test-execute.c b/src/test/test-execute.c
index 1d24115b5c..05ec1d2eb1 100644
--- a/src/test/test-execute.c
+++ b/src/test/test-execute.c
@@ -30,6 +30,9 @@
#include "mkdir.h"
#include "path-util.h"
#include "rm-rf.h"
+#ifdef HAVE_SECCOMP
+#include "seccomp-util.h"
+#endif
#include "test-helper.h"
#include "unit.h"
#include "util.h"
@@ -132,21 +135,27 @@ static void test_exec_privatedevices(Manager *m) {
static void test_exec_systemcallfilter(Manager *m) {
#ifdef HAVE_SECCOMP
+ if (!is_seccomp_available())
+ return;
test(m, "exec-systemcallfilter-not-failing.service", 0, CLD_EXITED);
test(m, "exec-systemcallfilter-not-failing2.service", 0, CLD_EXITED);
test(m, "exec-systemcallfilter-failing.service", SIGSYS, CLD_KILLED);
test(m, "exec-systemcallfilter-failing2.service", SIGSYS, CLD_KILLED);
+
#endif
}
static void test_exec_systemcallerrornumber(Manager *m) {
#ifdef HAVE_SECCOMP
- test(m, "exec-systemcallerrornumber.service", 1, CLD_EXITED);
+ if (is_seccomp_available())
+ test(m, "exec-systemcallerrornumber.service", 1, CLD_EXITED);
#endif
}
static void test_exec_systemcall_system_mode_with_user(Manager *m) {
#ifdef HAVE_SECCOMP
+ if (!is_seccomp_available())
+ return;
if (getpwnam("nobody"))
test(m, "exec-systemcallfilter-system-user.service", 0, CLD_EXITED);
else if (getpwnam("nfsnobody"))
diff --git a/src/udev/net/ethtool-util.c b/src/udev/net/ethtool-util.c
index c00ff79123..19c69a98b1 100644
--- a/src/udev/net/ethtool-util.c
+++ b/src/udev/net/ethtool-util.c
@@ -46,6 +46,14 @@ static const char* const wol_table[_WOL_MAX] = {
DEFINE_STRING_TABLE_LOOKUP(wol, WakeOnLan);
DEFINE_CONFIG_PARSE_ENUM(config_parse_wol, wol, WakeOnLan, "Failed to parse WakeOnLan setting");
+static const char* const netdev_feature_table[_NET_DEV_FEAT_MAX] = {
+ [NET_DEV_FEAT_GSO] = "tx-generic-segmentation",
+ [NET_DEV_FEAT_GRO] = "rx-gro",
+ [NET_DEV_FEAT_LRO] = "rx-lro",
+ [NET_DEV_FEAT_TSO] = "tx-tcp-segmentation",
+ [NET_DEV_FEAT_UFO] = "tx-udp-fragmentation",
+};
+
int ethtool_connect(int *ret) {
int fd;
@@ -206,3 +214,108 @@ int ethtool_set_wol(int *fd, const char *ifname, WakeOnLan wol) {
return 0;
}
+
+static int ethtool_get_stringset(int *fd, struct ifreq *ifr, int stringset_id, struct ethtool_gstrings **gstrings) {
+ _cleanup_free_ struct ethtool_gstrings *strings = NULL;
+ struct ethtool_sset_info info = {
+ .cmd = ETHTOOL_GSSET_INFO,
+ .reserved = 0,
+ .sset_mask = 1ULL << stringset_id,
+ };
+ unsigned len;
+ int r;
+
+ ifr->ifr_data = (void *) &info;
+
+ r = ioctl(*fd, SIOCETHTOOL, ifr);
+ if (r < 0)
+ return -errno;
+
+ if (!info.sset_mask)
+ return -EINVAL;
+
+ len = info.data[0];
+
+ strings = malloc0(sizeof(struct ethtool_gstrings) + len * ETH_GSTRING_LEN);
+ if (!strings)
+ return -ENOMEM;
+
+ strings->cmd = ETHTOOL_GSTRINGS;
+ strings->string_set = stringset_id;
+ strings->len = len;
+
+ ifr->ifr_data = (void *) strings;
+
+ r = ioctl(*fd, SIOCETHTOOL, ifr);
+ if (r < 0)
+ return -errno;
+
+ *gstrings = strings;
+ strings = NULL;
+
+ return 0;
+}
+
+static int find_feature_index(struct ethtool_gstrings *strings, const char *feature) {
+ unsigned i;
+
+ for (i = 0; i < strings->len; i++) {
+ if (streq((char *) &strings->data[i * ETH_GSTRING_LEN], feature))
+ return i;
+ }
+
+ return -1;
+}
+
+int ethtool_set_features(int *fd, const char *ifname, NetDevFeature *features) {
+ _cleanup_free_ struct ethtool_gstrings *strings = NULL;
+ struct ethtool_sfeatures *sfeatures;
+ int block, bit, i, r;
+ struct ifreq ifr;
+
+ if (*fd < 0) {
+ r = ethtool_connect(fd);
+ if (r < 0)
+ return log_warning_errno(r, "link_config: could not connect to ethtool: %m");
+ }
+
+ strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
+
+ r = ethtool_get_stringset(fd, &ifr, ETH_SS_FEATURES, &strings);
+ if (r < 0)
+ return log_warning_errno(r, "link_config: could not get ethtool features for %s", ifname);
+
+ sfeatures = alloca0(sizeof(struct ethtool_gstrings) + DIV_ROUND_UP(strings->len, 32U) * sizeof(sfeatures->features[0]));
+ sfeatures->cmd = ETHTOOL_SFEATURES;
+ sfeatures->size = DIV_ROUND_UP(strings->len, 32U);
+
+ for (i = 0; i < _NET_DEV_FEAT_MAX; i++) {
+
+ if (features[i] != -1) {
+
+ r = find_feature_index(strings, netdev_feature_table[i]);
+ if (r < 0) {
+ log_warning_errno(r, "link_config: could not find feature: %s", netdev_feature_table[i]);
+ continue;
+ }
+
+ block = r / 32;
+ bit = r % 32;
+
+ sfeatures->features[block].valid |= 1 << bit;
+
+ if (features[i])
+ sfeatures->features[block].requested |= 1 << bit;
+ else
+ sfeatures->features[block].requested &= ~(1 << bit);
+ }
+ }
+
+ ifr.ifr_data = (void *) sfeatures;
+
+ r = ioctl(*fd, SIOCETHTOOL, &ifr);
+ if (r < 0)
+ return log_warning_errno(r, "link_config: could not set ethtool features for %s", ifname);
+
+ return 0;
+}
diff --git a/src/udev/net/ethtool-util.h b/src/udev/net/ethtool-util.h
index 7716516e76..0744164653 100644
--- a/src/udev/net/ethtool-util.h
+++ b/src/udev/net/ethtool-util.h
@@ -38,11 +38,22 @@ typedef enum WakeOnLan {
_WOL_INVALID = -1
} WakeOnLan;
+typedef enum NetDevFeature {
+ NET_DEV_FEAT_GSO,
+ NET_DEV_FEAT_GRO,
+ NET_DEV_FEAT_LRO,
+ NET_DEV_FEAT_TSO,
+ NET_DEV_FEAT_UFO,
+ _NET_DEV_FEAT_MAX,
+ _NET_DEV_FEAT_INVALID = -1
+} NetDevFeature;
+
int ethtool_connect(int *ret);
int ethtool_get_driver(int *fd, const char *ifname, char **ret);
int ethtool_set_speed(int *fd, const char *ifname, unsigned int speed, Duplex duplex);
int ethtool_set_wol(int *fd, const char *ifname, WakeOnLan wol);
+int ethtool_set_features(int *fd, const char *ifname, NetDevFeature *features);
const char *duplex_to_string(Duplex d) _const_;
Duplex duplex_from_string(const char *d) _pure_;
diff --git a/src/udev/net/link-config-gperf.gperf b/src/udev/net/link-config-gperf.gperf
index b25e4b3344..f8b85cbd13 100644
--- a/src/udev/net/link-config-gperf.gperf
+++ b/src/udev/net/link-config-gperf.gperf
@@ -16,22 +16,27 @@ struct ConfigPerfItem;
%struct-type
%includes
%%
-Match.MACAddress, config_parse_hwaddr, 0, offsetof(link_config, match_mac)
-Match.OriginalName, config_parse_ifnames, 0, offsetof(link_config, match_name)
-Match.Path, config_parse_strv, 0, offsetof(link_config, match_path)
-Match.Driver, config_parse_strv, 0, offsetof(link_config, match_driver)
-Match.Type, config_parse_strv, 0, offsetof(link_config, match_type)
-Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(link_config, match_host)
-Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(link_config, match_virt)
-Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(link_config, match_kernel)
-Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(link_config, match_arch)
-Link.Description, config_parse_string, 0, offsetof(link_config, description)
-Link.MACAddressPolicy, config_parse_mac_policy, 0, offsetof(link_config, mac_policy)
-Link.MACAddress, config_parse_hwaddr, 0, offsetof(link_config, mac)
-Link.NamePolicy, config_parse_name_policy, 0, offsetof(link_config, name_policy)
-Link.Name, config_parse_ifname, 0, offsetof(link_config, name)
-Link.Alias, config_parse_ifalias, 0, offsetof(link_config, alias)
-Link.MTUBytes, config_parse_iec_size, 0, offsetof(link_config, mtu)
-Link.BitsPerSecond, config_parse_si_size, 0, offsetof(link_config, speed)
-Link.Duplex, config_parse_duplex, 0, offsetof(link_config, duplex)
-Link.WakeOnLan, config_parse_wol, 0, offsetof(link_config, wol)
+Match.MACAddress, config_parse_hwaddr, 0, offsetof(link_config, match_mac)
+Match.OriginalName, config_parse_ifnames, 0, offsetof(link_config, match_name)
+Match.Path, config_parse_strv, 0, offsetof(link_config, match_path)
+Match.Driver, config_parse_strv, 0, offsetof(link_config, match_driver)
+Match.Type, config_parse_strv, 0, offsetof(link_config, match_type)
+Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(link_config, match_host)
+Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(link_config, match_virt)
+Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(link_config, match_kernel)
+Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(link_config, match_arch)
+Link.Description, config_parse_string, 0, offsetof(link_config, description)
+Link.MACAddressPolicy, config_parse_mac_policy, 0, offsetof(link_config, mac_policy)
+Link.MACAddress, config_parse_hwaddr, 0, offsetof(link_config, mac)
+Link.NamePolicy, config_parse_name_policy, 0, offsetof(link_config, name_policy)
+Link.Name, config_parse_ifname, 0, offsetof(link_config, name)
+Link.Alias, config_parse_ifalias, 0, offsetof(link_config, alias)
+Link.MTUBytes, config_parse_iec_size, 0, offsetof(link_config, mtu)
+Link.BitsPerSecond, config_parse_si_size, 0, offsetof(link_config, speed)
+Link.Duplex, config_parse_duplex, 0, offsetof(link_config, duplex)
+Link.WakeOnLan, config_parse_wol, 0, offsetof(link_config, wol)
+Link.GenericSegmentationOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_GSO])
+Link.TCPSegmentationOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_TSO])
+Link.UDPSegmentationOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_UFO])
+Link.GenericReceiveOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_GRO])
+Link.LargeReceiveOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_LRO])
diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c
index c66504102f..eedd94e777 100644
--- a/src/udev/net/link-config.c
+++ b/src/udev/net/link-config.c
@@ -168,6 +168,8 @@ static int load_link(link_config_ctx *ctx, const char *filename) {
link->wol = _WOL_INVALID;
link->duplex = _DUP_INVALID;
+ memset(&link->features, -1, _NET_DEV_FEAT_MAX);
+
r = config_parse(NULL, filename, file,
"Match\0Link\0Ethernet\0",
config_item_perf_lookup, link_config_gperf_lookup,
@@ -397,6 +399,10 @@ int link_config_apply(link_config_ctx *ctx, link_config *config,
log_warning_errno(r, "Could not set WakeOnLan of %s to %s: %m",
old_name, wol_to_string(config->wol));
+ r = ethtool_set_features(&ctx->ethtool_fd, old_name, config->features);
+ if (r < 0)
+ log_warning_errno(r, "Could not set offload features of %s: %m", old_name);
+
ifindex = udev_device_get_ifindex(device);
if (ifindex <= 0) {
log_warning("Could not find ifindex");
diff --git a/src/udev/net/link-config.h b/src/udev/net/link-config.h
index 9df5529d05..91cc0357c4 100644
--- a/src/udev/net/link-config.h
+++ b/src/udev/net/link-config.h
@@ -70,6 +70,7 @@ struct link_config {
size_t speed;
Duplex duplex;
WakeOnLan wol;
+ NetDevFeature features[_NET_DEV_FEAT_MAX];
LIST_FIELDS(link_config, links);
};
diff --git a/src/udev/udevd.c b/src/udev/udevd.c
index a893a2b3d9..19f1c29198 100644
--- a/src/udev/udevd.c
+++ b/src/udev/udevd.c
@@ -776,9 +776,9 @@ static void manager_reload(Manager *manager) {
manager->rules = udev_rules_unref(manager->rules);
udev_builtin_exit(manager->udev);
- sd_notify(false,
- "READY=1\n"
- "STATUS=Processing...");
+ sd_notifyf(false,
+ "READY=1\n"
+ "STATUS=Processing with %u children at max", arg_children_max);
}
static void event_queue_start(Manager *manager) {
@@ -1000,6 +1000,10 @@ static int on_ctrl_msg(sd_event_source *s, int fd, uint32_t revents, void *userd
if (i >= 0) {
log_debug("udevd message (SET_MAX_CHILDREN) received, children_max=%i", i);
arg_children_max = i;
+
+ (void) sd_notifyf(false,
+ "READY=1\n"
+ "STATUS=Processing with %u children at max", arg_children_max);
}
if (udev_ctrl_get_ping(ctrl_msg) > 0)
@@ -1627,9 +1631,9 @@ static int run(int fd_ctrl, int fd_uevent, const char *cgroup) {
if (r < 0)
log_error_errno(r, "failed to apply permissions on static device nodes: %m");
- (void) sd_notify(false,
- "READY=1\n"
- "STATUS=Processing...");
+ (void) sd_notifyf(false,
+ "READY=1\n"
+ "STATUS=Processing with %u children at max", arg_children_max);
r = sd_event_loop(manager->event);
if (r < 0) {
diff --git a/test/test-functions b/test/test-functions
index 567a000b8d..2a21a64c5c 100644
--- a/test/test-functions
+++ b/test/test-functions
@@ -225,6 +225,7 @@ create_valgrind_wrapper() {
cat >$_valgrind_wrapper <<EOF
#!/bin/bash
+mount -t proc proc /proc
exec valgrind --leak-check=full --log-file=/valgrind.out $ROOTLIBDIR/systemd "\$@"
EOF
chmod 0755 $_valgrind_wrapper
diff --git a/units/systemd-udevd-control.socket b/units/systemd-udevd-control.socket
index 8330a1c035..46f704ed79 100644
--- a/units/systemd-udevd-control.socket
+++ b/units/systemd-udevd-control.socket
@@ -17,3 +17,4 @@ Service=systemd-udevd.service
ListenSequentialPacket=/run/udev/control
SocketMode=0600
PassCredentials=yes
+RemoveOnStop=yes