summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Makefile-man.am2
-rw-r--r--Makefile.am36
-rw-r--r--configure.ac4
-rw-r--r--hwdb/60-evdev.hwdb7
-rw-r--r--hwdb/70-mouse.hwdb14
-rwxr-xr-xhwdb/parse_hwdb.py1
-rw-r--r--man/hostnamectl.xml43
-rw-r--r--man/journalctl.xml6
-rw-r--r--man/logind.conf.xml47
-rw-r--r--man/sd_journal_open.xml11
-rw-r--r--man/standard-options.xml6
-rw-r--r--man/systemctl.xml26
-rw-r--r--man/systemd-inhibit.xml2
-rw-r--r--man/systemd-logind.service.xml2
-rw-r--r--man/systemd-mount.xml295
-rw-r--r--man/systemd-run.xml3
-rw-r--r--man/systemd.exec.xml60
-rw-r--r--man/systemd.preset.xml4
-rw-r--r--man/systemd.unit.xml36
-rw-r--r--man/systemd.xml10
-rw-r--r--rules/60-persistent-storage.rules6
-rw-r--r--shell-completion/zsh/_journalctl25
-rw-r--r--src/basic/cgroup-util.c4
-rw-r--r--src/basic/cgroup-util.h4
-rw-r--r--src/basic/hostname-util.c1
-rw-r--r--src/basic/mount-util.c1
-rw-r--r--src/basic/terminal-util.c33
-rw-r--r--src/basic/user-util.h16
-rw-r--r--src/core/cgroup.c2
-rw-r--r--src/core/dbus-execute.c5
-rw-r--r--src/core/dbus-mount.c2
-rw-r--r--src/core/dbus-service.c14
-rw-r--r--src/core/dbus-socket.c2
-rw-r--r--src/core/dbus-swap.c2
-rw-r--r--src/core/dbus-unit.c11
-rw-r--r--src/core/dynamic-user.c71
-rw-r--r--src/core/execute.c63
-rw-r--r--src/core/execute.h1
-rw-r--r--src/core/load-fragment.c20
-rw-r--r--src/core/main.c11
-rw-r--r--src/core/manager.c411
-rw-r--r--src/core/manager.h23
-rw-r--r--src/core/mount.c2
-rw-r--r--src/core/service.c3
-rw-r--r--src/core/socket.c2
-rw-r--r--src/core/swap.c2
-rw-r--r--src/core/unit.c174
-rw-r--r--src/core/unit.h20
-rw-r--r--src/hostname/hostnamectl.c36
-rw-r--r--src/journal/journalctl.c10
-rw-r--r--src/journal/journald-server.c166
-rw-r--r--src/journal/sd-journal.c40
-rw-r--r--src/libudev/libudev-device.c2
-rw-r--r--src/login/logind-action.c2
-rw-r--r--src/login/logind-user.c24
-rw-r--r--src/login/logind.c3
l---------src/mount/Makefile1
-rw-r--r--src/mount/mount-tool.c1112
-rw-r--r--src/network/networkd-route.c20
-rw-r--r--src/nspawn/nspawn-register.c9
-rw-r--r--src/nss-mymachines/nss-mymachines.c16
-rw-r--r--src/nss-systemd/nss-systemd.c262
-rw-r--r--src/run/run.c31
-rw-r--r--src/shared/bus-unit-util.c17
-rw-r--r--src/shared/bus-unit-util.h1
-rw-r--r--src/shared/clean-ipc.c106
-rw-r--r--src/shared/clean-ipc.h4
-rw-r--r--src/shared/install.c100
-rw-r--r--src/systemctl/systemctl.c9
-rw-r--r--src/sysv-generator/sysv-generator.c17
-rw-r--r--src/test/test-condition.c41
-rw-r--r--src/test/test-hostname-util.c1
-rw-r--r--src/test/test-ipcrm.c2
-rw-r--r--system-preset/90-systemd.preset7
-rwxr-xr-xtest/udev-test.pl4
l---------units/user/bluetooth.target1
l---------units/user/busnames.target1
l---------units/user/paths.target1
l---------units/user/printer.target1
l---------units/user/shutdown.target1
l---------units/user/smartcard.target1
l---------units/user/sockets.target1
l---------units/user/sound.target1
l---------units/user/timers.target1
85 files changed, 3047 insertions, 552 deletions
diff --git a/.gitignore b/.gitignore
index f7db68b4a6..6caa8218bd 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
diff --git a/Makefile-man.am b/Makefile-man.am
index 3ac1906a4a..bf8de1e58f 100644
--- a/Makefile-man.am
+++ b/Makefile-man.am
@@ -121,6 +121,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 \
@@ -2651,6 +2652,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 d4e3e9a013..431975de8b 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
@@ -575,7 +576,16 @@ dist_userunit_DATA = \
units/user/default.target \
units/user/exit.target \
units/user/graphical-session.target \
- units/user/graphical-session-pre.target
+ units/user/graphical-session-pre.target \
+ units/user/bluetooth.target \
+ units/user/busnames.target \
+ units/user/paths.target \
+ units/user/printer.target \
+ units/user/shutdown.target \
+ units/user/smartcard.target \
+ units/user/sockets.target \
+ units/user/sound.target \
+ units/user/timers.target
nodist_userunit_DATA = \
units/user/systemd-exit.service
@@ -634,6 +644,10 @@ EXTRA_DIST += \
units/rc-local.service.in \
units/halt-local.service.in
+GENERAL_ALIASES += \
+ $(systemunitdir)/reboot.target $(pkgsysconfdir)/system/ctrl-alt-del.target \
+ $(systemunitdir)/machines.target $(pkgsysconfdir)/system/multi-user.target.wants/machines.target
+
# automake is broken and can't handle files with a dash in front
# http://debbugs.gnu.org/cgi/bugreport.cgi?bug=14728#8
units-install-hook:
@@ -3133,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
@@ -6292,19 +6313,6 @@ SYSTEM_UNIT_ALIASES += \
reboot.target ctrl-alt-del.target \
getty@.service autovt@.service
-USER_UNIT_ALIASES += \
- $(systemunitdir)/shutdown.target shutdown.target \
- $(systemunitdir)/sockets.target sockets.target \
- $(systemunitdir)/timers.target timers.target \
- $(systemunitdir)/paths.target paths.target \
- $(systemunitdir)/bluetooth.target bluetooth.target \
- $(systemunitdir)/printer.target printer.target \
- $(systemunitdir)/sound.target sound.target \
- $(systemunitdir)/smartcard.target smartcard.target
-
-USER_UNIT_ALIASES += \
- $(systemunitdir)/busnames.target busnames.target
-
GENERAL_ALIASES += \
$(systemunitdir)/remote-fs.target $(pkgsysconfdir)/system/multi-user.target.wants/remote-fs.target \
$(systemunitdir)/getty@.service $(pkgsysconfdir)/system/getty.target.wants/getty@tty1.service \
diff --git a/configure.ac b/configure.ac
index 4d1c96606f..36061c0ba9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1220,9 +1220,9 @@ AM_CONDITIONAL(ENABLE_NETWORKD, [test "x$have_networkd" = "xyes"])
# ------------------------------------------------------------------------------
have_efi=no
-AC_ARG_ENABLE(efi, AS_HELP_STRING([--disable-efi], [disable EFI support]))
+AC_ARG_ENABLE(efi, AS_HELP_STRING([--disable-efi], [disable systemd-boot and bootctl (EFI support)]))
if test "x$enable_efi" != "xno"; then
- AC_DEFINE(ENABLE_EFI, 1, [Define if EFI support is to be enabled])
+ AC_DEFINE(ENABLE_EFI, 1, [Define if systemd-boot and bootctl are to be enabled])
have_efi=yes
fi
AM_CONDITIONAL(ENABLE_EFI, [test "x$have_efi" = "xyes"])
diff --git a/hwdb/60-evdev.hwdb b/hwdb/60-evdev.hwdb
index 4d14a6a2f4..e738ff7be5 100644
--- a/hwdb/60-evdev.hwdb
+++ b/hwdb/60-evdev.hwdb
@@ -134,6 +134,13 @@ evdev:name:AlpsPS/2 ALPS DualPoint TouchPad:dmi:bvn*:bvr*:bd*:svnDellInc.:pnLati
EVDEV_ABS_35=76:1815:22
EVDEV_ABS_36=131:1330:30
+# Dell Precision 5510
+evdev:name:SynPS/2 Synaptics TouchPad:dmi:bvn*:bvr*:bd*:svnDellInc.:pnPrecision5510*
+ EVDEV_ABS_00=::42
+ EVDEV_ABS_01=::43
+ EVDEV_ABS_35=::42
+ EVDEV_ABS_36=::43
+
# Dell Precision M4700
evdev:name:AlpsPS/2 ALPS DualPoint TouchPad:dmi:*svnDellInc.:pnPrecisionM4700*
EVDEV_ABS_00=0:1960:24
diff --git a/hwdb/70-mouse.hwdb b/hwdb/70-mouse.hwdb
index d8215a6179..56e36af0e5 100644
--- a/hwdb/70-mouse.hwdb
+++ b/hwdb/70-mouse.hwdb
@@ -47,6 +47,7 @@
# ID_INPUT_TRACKBALL
# MOUSE_DPI
# MOUSE_WHEEL_CLICK_ANGLE
+# MOUSE_WHEEL_CLICK_ANGLE_HORIZONTAL
#
#########################################
# ID_INPUT_TRACKBALL #
@@ -104,6 +105,14 @@
#
# Most mice have a 15 degree click stop (24 clicks per full rotation).
#
+#########################################
+# MOUSE_WHEEL_CLICK_ANGLE_HORIZONTAL #
+#########################################
+#
+# Identical to MOUSE_WHEEL_CLICK_ANGLE but for the horizontal scroll wheel.
+# This property may only be specified if the angle for the horizontal
+# scroll wheel differs from the vertical wheel. If so, *both* click angles
+# must be specified.
#
# Sort by brand, type (usb, bluetooth), DPI, frequency.
@@ -344,9 +353,14 @@ mouse:usb:v046dpc52b:name:Logitech Unifying Device. Wireless PID:402d:
# Logitech Performance MX
mouse:usb:v046dp101a:name:Logitech Performance MX:
+ MOUSE_DPI=1000@166
+
# Logitech MX Master
+# Horiz wheel has 14 stops, angle is rounded up
mouse:usb:v046dp4041:name:Logitech MX Master:
MOUSE_DPI=1000@166
+ MOUSE_WHEEL_CLICK_ANGLE=15
+ MOUSE_WHEEL_CLICK_ANGLE_HORIZONTAL=26
# Logitech MK260 Wireless Combo Receiver aka M-R0011
mouse:usb:v046dpc52e:name:Logitech USB Receiver:
diff --git a/hwdb/parse_hwdb.py b/hwdb/parse_hwdb.py
index 99d034b4e0..f55562250d 100755
--- a/hwdb/parse_hwdb.py
+++ b/hwdb/parse_hwdb.py
@@ -82,6 +82,7 @@ def property_grammar():
setting = Optional('*')('DEFAULT') + INTEGER('DPI') + Suppress('@') + INTEGER('HZ')
props = (('MOUSE_DPI', Group(OneOrMore(setting('SETTINGS*')))),
('MOUSE_WHEEL_CLICK_ANGLE', INTEGER),
+ ('MOUSE_WHEEL_CLICK_ANGLE_HORIZONTAL', INTEGER),
('ID_INPUT_TRACKBALL', Literal('1')),
('POINTINGSTICK_SENSITIVITY', INTEGER),
('POINTINGSTICK_CONST_ACCEL', REAL),
diff --git a/man/hostnamectl.xml b/man/hostnamectl.xml
index 60004e9d04..9e1b593e6d 100644
--- a/man/hostnamectl.xml
+++ b/man/hostnamectl.xml
@@ -71,10 +71,9 @@
set, and is valid (something other than localhost), then the
transient hostname is not used.</para>
- <para>Note that the pretty hostname has little restrictions on the
- characters used, while the static and transient hostnames are
- limited to the usually accepted characters of Internet domain
- names.</para>
+ <para>Note that the pretty hostname has little restrictions on the characters and length used, while the static and
+ transient hostnames are limited to the usually accepted characters of Internet domain names, and 64 characters at
+ maximum (the latter being a Linux limitation).</para>
<para>The static hostname is stored in
<filename>/etc/hostname</filename>, see
@@ -107,15 +106,11 @@
<term><option>--transient</option></term>
<term><option>--pretty</option></term>
- <listitem><para>If <command>status</command> is used (or no
- explicit command is given) and one of those fields is given,
- <command>hostnamectl</command> will print out just this
- selected hostname.</para>
+ <listitem><para>If <command>status</command> is invoked (or no explicit command is given) and one of these
+ switches is specified, <command>hostnamectl</command> will print out just this selected hostname.</para>
- <para>If used with <command>set-hostname</command>, only the
- selected hostname(s) will be updated. When more than one of
- those options is used, all the specified hostnames will be
- updated. </para></listitem>
+ <para>If used with <command>set-hostname</command>, only the selected hostname(s) will be updated. When more
+ than one of these switches are specified, all the specified hostnames will be updated. </para></listitem>
</varlistentry>
<xi:include href="user-system-options.xml" xpointer="host" />
@@ -139,22 +134,14 @@
<varlistentry>
<term><command>set-hostname <replaceable>NAME</replaceable></command></term>
- <listitem><para>Set the system hostname to
- <replaceable>NAME</replaceable>. By default, this will alter
- the pretty, the static, and the transient hostname alike;
- however, if one or more of <option>--static</option>,
- <option>--transient</option>, <option>--pretty</option> are
- used, only the selected hostnames are changed. If the pretty
- hostname is being set, and static or transient are being set
- as well, the specified hostname will be simplified in regards
- to the character set used before the latter are updated. This
- is done by replacing spaces with <literal>-</literal> and
- removing special characters. This ensures that the pretty and
- the static hostname are always closely related while still
- following the validity rules of the specific name. This
- simplification of the hostname string is not done if only the
- transient and/or static host names are set, and the pretty
- host name is left untouched.</para>
+ <listitem><para>Set the system hostname to <replaceable>NAME</replaceable>. By default, this will alter the
+ pretty, the static, and the transient hostname alike; however, if one or more of <option>--static</option>,
+ <option>--transient</option>, <option>--pretty</option> are used, only the selected hostnames are changed. If
+ the pretty hostname is being set, and static or transient are being set as well, the specified hostname will be
+ simplified in regards to the character set used before the latter are updated. This is done by removing special
+ characters and spaces. This ensures that the pretty and the static hostname are always closely related while
+ still following the validity rules of the specific name. This simplification of the hostname string is not done
+ if only the transient and/or static host names are set, and the pretty host name is left untouched.</para>
<para>Pass the empty string <literal></literal> as the
hostname to reset the selected hostnames to their default
diff --git a/man/journalctl.xml b/man/journalctl.xml
index c448a29a51..63b4a267b8 100644
--- a/man/journalctl.xml
+++ b/man/journalctl.xml
@@ -659,10 +659,12 @@
<term><option>--root=<replaceable>ROOT</replaceable></option></term>
<listitem><para>Takes a directory path as an argument. If
- specified, journalctl will operate on catalog file hierarchy
+ specified, journalctl will operate on journal directories and catalog file hierarchy
underneath the specified directory instead of the root
directory (e.g. <option>--update-catalog</option> will create
- <filename><replaceable>ROOT</replaceable>/var/lib/systemd/catalog/database</filename>).
+ <filename><replaceable>ROOT</replaceable>/var/lib/systemd/catalog/database</filename>,
+ and journal files under <filename><replaceable>ROOT</replaceable>/run/journal</filename>
+ or <filename><replaceable>ROOT</replaceable>/var/log/journal</filename> will be displayed).
</para></listitem>
</varlistentry>
diff --git a/man/logind.conf.xml b/man/logind.conf.xml
index adba5a4131..9b0e181849 100644
--- a/man/logind.conf.xml
+++ b/man/logind.conf.xml
@@ -211,7 +211,7 @@
<term><varname>HandleLidSwitch=</varname></term>
<term><varname>HandleLidSwitchDocked=</varname></term>
- <listitem><para>Controls whether logind shall handle the
+ <listitem><para>Controls how logind shall handle the
system power and sleep keys and the lid switch to trigger
actions such as system power-off or suspend. Can be one of
<literal>ignore</literal>,
@@ -240,7 +240,16 @@
docking station, or if more than one display is connected, the
action specified by <varname>HandleLidSwitchDocked=</varname>
occurs; otherwise the <varname>HandleLidSwitch=</varname>
- action occurs.</para></listitem>
+ action occurs.</para>
+
+ <para>A different application may disable logind's handling of system power and
+ sleep keys and the lid switch by taking a low-level inhibitor lock
+ ("handle-power-key", "handle-suspend-key", "handle-hibernate-key",
+ "handle-lid-switch"). This is most commonly used by graphical desktop environments
+ to take over suspend and hibernation handling, and to use their own configuration
+ mechanisms. If a low-level inhibitor lock is taken, logind will not take any
+ action when that key or switch is triggered and the <varname>Handle*=</varname>
+ settings are irrelevant.</para></listitem>
</varlistentry>
<varlistentry>
@@ -249,21 +258,22 @@
<term><varname>HibernateKeyIgnoreInhibited=</varname></term>
<term><varname>LidSwitchIgnoreInhibited=</varname></term>
- <listitem><para>Controls whether actions triggered by the
- power and sleep keys and the lid switch are subject to
- inhibitor locks. These settings take boolean arguments. If
- <literal>no</literal>, the inhibitor locks taken by
- applications in order to block the requested operation are
- respected. If <literal>yes</literal>, the requested operation
- is executed in any case.
+ <listitem><para>Controls whether actions that <command>systemd-logind</command>
+ takes when the power and sleep keys and the lid switch are triggered are subject
+ to high-level inhibitor locks ("shutdown", "sleep", "idle"). Low level inhibitor
+ locks ("handle-*-key"), are always honoured, irrespective of this setting.</para>
+
+ <para>These settings take boolean arguments. If <literal>no</literal>, the
+ inhibitor locks taken by applications are respected. If <literal>yes</literal>,
+ "shutdown", "sleep", and "idle" inhibitor locks are ignored.
<varname>PowerKeyIgnoreInhibited=</varname>,
- <varname>SuspendKeyIgnoreInhibited=</varname> and
- <varname>HibernateKeyIgnoreInhibited=</varname> default to
- <literal>no</literal>.
- <varname>LidSwitchIgnoreInhibited=</varname> defaults to
- <literal>yes</literal>. This means that the lid switch does
- not respect suspend blockers by default, but the power and
- sleep keys do. </para></listitem>
+ <varname>SuspendKeyIgnoreInhibited=</varname>, and
+ <varname>HibernateKeyIgnoreInhibited=</varname> default to <literal>no</literal>.
+ <varname>LidSwitchIgnoreInhibited=</varname> defaults to <literal>yes</literal>.
+ This means that when <command>systemd-logind</command> is handling events by
+ itself (no low level inhibitor locks are taken by another application), the lid
+ switch does not respect suspend blockers by default, but the power and sleep keys
+ do.</para></listitem>
</varlistentry>
<varlistentry>
@@ -318,8 +328,9 @@
<listitem><para>Sets the maximum number of OS tasks each user may run concurrently. This controls the
<varname>TasksMax=</varname> setting of the per-user slice unit, see
<citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>
- for details. Defaults to 33%, which equals 10813 with the kernel's defaults on the host, but might be smaller
- in OS containers.</para></listitem>
+ for details. If assigned the special value <literal>infinity</literal>, no tasks limit is applied.
+ Defaults to 33%, which equals 10813 with the kernel's defaults on the host, but might be smaller in
+ OS containers.</para></listitem>
</varlistentry>
<varlistentry>
diff --git a/man/sd_journal_open.xml b/man/sd_journal_open.xml
index 153af2387f..74e67023b5 100644
--- a/man/sd_journal_open.xml
+++ b/man/sd_journal_open.xml
@@ -129,10 +129,13 @@
<para><function>sd_journal_open_directory()</function> is similar to <function>sd_journal_open()</function> but
takes an absolute directory path as argument. All journal files in this directory will be opened and interleaved
- automatically. This call also takes a flags argument. The only flags parameter accepted by this call is
- <constant>SD_JOURNAL_OS_ROOT</constant>. If specified, the journal files are searched below the usual
- <filename>/var/log/journal</filename> and <filename>/run/log/journal</filename> relative to the specified path,
- instead of directly beneath it.</para>
+ automatically. This call also takes a flags argument. The flags parameters accepted by this call are
+ <constant>SD_JOURNAL_OS_ROOT</constant>, <constant>SD_JOURNAL_SYSTEM</constant>, and
+ <constant>SD_JOURNAL_CURRENT_USER</constant>. If <constant>SD_JOURNAL_OS_ROOT</constant> is specified, journal
+ files are searched for below the usual <filename>/var/log/journal</filename> and
+ <filename>/run/log/journal</filename> relative to the specified path, instead of directly beneath it.
+ The other two flags limit which files are opened, the same as for <function>sd_journal_open()</function>.
+ </para>
<para><function>sd_journal_open_directory_fd()</function> is similar to
<function>sd_journal_open_directory()</function>, but takes a file descriptor referencing a directory in the file
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 0ad0ad6d7e..fde4f4f3bb 100644
--- a/man/systemctl.xml
+++ b/man/systemctl.xml
@@ -1092,7 +1092,8 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
enabled and disabled, or only enabled, or only disabled.</para>
<para>If the unit carries no install information, it will be silently ignored
- by this command.</para>
+ by this command. <replaceable>NAME</replaceable> must be the real unit name,
+ any alias names are ignored silently.</para>
<para>For more information on the preset policy format, see
<citerefentry><refentrytitle>systemd.preset</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
@@ -1680,20 +1681,15 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
<term><command>switch-root <replaceable>ROOT</replaceable> <optional><replaceable>INIT</replaceable></optional></command></term>
<listitem>
- <para>Switches to a different root directory and executes a
- new system manager process below it. This is intended for
- usage in initial RAM disks ("initrd"), and will transition
- from the initrd's system manager process (a.k.a. "init"
- process) to the main system manager process. This call takes two
- arguments: the directory that is to become the new root directory, and
- the path to the new system manager binary below it to
- execute as PID 1. If the latter is omitted or the empty
- string, a systemd binary will automatically be searched for
- and used as init. If the system manager path is omitted or
- equal to the empty string, the state of the initrd's system
- manager process is passed to the main system manager, which
- allows later introspection of the state of the services
- involved in the initrd boot.</para>
+ <para>Switches to a different root directory and executes a new system manager process below it. This is
+ intended for usage in initial RAM disks ("initrd"), and will transition from the initrd's system manager
+ process (a.k.a. "init" process) to the main system manager process which is loaded from the actual host
+ volume. This call takes two arguments: the directory that is to become the new root directory, and the path
+ to the new system manager binary below it to execute as PID 1. If the latter is omitted or the empty
+ string, a systemd binary will automatically be searched for and used as init. If the system manager path is
+ omitted, equal to the empty string or identical to the path to the systemd binary, the state of the
+ initrd's system manager process is passed to the main system manager, which allows later introspection of
+ the state of the services involved in the initrd boot phase.</para>
</listitem>
</varlistentry>
diff --git a/man/systemd-inhibit.xml b/man/systemd-inhibit.xml
index 9d85908f97..ce169960d8 100644
--- a/man/systemd-inhibit.xml
+++ b/man/systemd-inhibit.xml
@@ -61,7 +61,7 @@
<title>Description</title>
<para><command>systemd-inhibit</command> may be used to execute a
- program with a shutdown, sleep or idle inhibitor lock taken. The
+ program with a shutdown, sleep, or idle inhibitor lock taken. The
lock will be acquired before the specified command line is
executed and released afterwards.</para>
diff --git a/man/systemd-logind.service.xml b/man/systemd-logind.service.xml
index 5733e42cd1..f0bdb1c756 100644
--- a/man/systemd-logind.service.xml
+++ b/man/systemd-logind.service.xml
@@ -84,7 +84,7 @@
management</para></listitem>
</itemizedlist>
- <para>User sessions are registered in logind via the
+ <para>User sessions are registered with logind via the
<citerefentry><refentrytitle>pam_systemd</refentrytitle><manvolnum>8</manvolnum></citerefentry>
PAM module.</para>
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..56d585cdf9 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>
@@ -452,6 +452,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.exec.xml b/man/systemd.exec.xml
index bf82326096..bcedebd5bb 100644
--- a/man/systemd.exec.xml
+++ b/man/systemd.exec.xml
@@ -160,14 +160,14 @@
use. However, UID/GIDs are recycled after a unit is terminated. Care should be taken that any processes running
as part of a unit for which dynamic users/groups are enabled do not leave files or directories owned by these
users/groups around, as a different unit might get the same UID/GID assigned later on, and thus gain access to
- these files or directories. If <varname>DynamicUser=</varname> is enabled, <varname>PrivateTmp=</varname> is
- implied. This ensures that the lifetime of temporary files created by the executed processes is bound to the
- runtime of the service, and hence the lifetime of the dynamic user/group. Since <filename>/tmp</filename> and
- <filename>/var/tmp</filename> are usually the only world-writable directories on a system this ensures that a
- unit making use of dynamic user/group allocation cannot leave files around after unit termination. Use
- <varname>RuntimeDirectory=</varname> (see below) in order to assign a writable runtime directory to a service,
- owned by the dynamic user/group and removed automatically when the unit is terminated. Defaults to
- off.</para></listitem>
+ these files or directories. If <varname>DynamicUser=</varname> is enabled, <varname>RemoveIPC=</varname> and
+ <varname>PrivateTmp=</varname> are implied. This ensures that the lifetime of IPC objects and temporary files
+ created by the executed processes is bound to the runtime of the service, and hence the lifetime of the dynamic
+ user/group. Since <filename>/tmp</filename> and <filename>/var/tmp</filename> are usually the only
+ world-writable directories on a system this ensures that a unit making use of dynamic user/group allocation
+ cannot leave files around after unit termination. Use <varname>RuntimeDirectory=</varname> (see below) in order
+ to assign a writable runtime directory to a service, owned by the dynamic user/group and removed automatically
+ when the unit is terminated. Defaults to off.</para></listitem>
</varlistentry>
<varlistentry>
@@ -186,6 +186,18 @@
</varlistentry>
<varlistentry>
+ <term><varname>RemoveIPC=</varname></term>
+
+ <listitem><para>Takes a boolean parameter. If set, all System V and POSIX IPC objects owned by the user and
+ group the processes of this unit are run as are removed when the unit is stopped. This setting only has an
+ effect if at least one of <varname>User=</varname>, <varname>Group=</varname> and
+ <varname>DynamicUser=</varname> are used. It has no effect on IPC objects owned by the root user. Specifically,
+ this removes System V semaphores, as well as System V and POSIX shared memory segments and message queues. If
+ multiple units use the same user or group the IPC objects are removed when the last of these units is
+ stopped. This setting is implied if <varname>DynamicUser=</varname> is set.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><varname>Nice=</varname></term>
<listitem><para>Sets the default nice level (scheduling
@@ -920,27 +932,19 @@
<varlistentry>
<term><varname>PrivateTmp=</varname></term>
- <listitem><para>Takes a boolean argument. If true, sets up a
- new file system namespace for the executed processes and
- mounts private <filename>/tmp</filename> and
- <filename>/var/tmp</filename> directories inside it that is
- not shared by processes outside of the namespace. This is
- useful to secure access to temporary files of the process, but
- makes sharing between processes via <filename>/tmp</filename>
- or <filename>/var/tmp</filename> impossible. If this is
- enabled, all temporary files created by a service in these
- directories will be removed after the service is stopped.
- Defaults to false. It is possible to run two or more units
- within the same private <filename>/tmp</filename> and
- <filename>/var/tmp</filename> namespace by using the
+ <listitem><para>Takes a boolean argument. If true, sets up a new file system namespace for the executed
+ processes and mounts private <filename>/tmp</filename> and <filename>/var/tmp</filename> directories inside it
+ that is not shared by processes outside of the namespace. This is useful to secure access to temporary files of
+ the process, but makes sharing between processes via <filename>/tmp</filename> or <filename>/var/tmp</filename>
+ impossible. If this is enabled, all temporary files created by a service in these directories will be removed
+ after the service is stopped. Defaults to false. It is possible to run two or more units within the same
+ private <filename>/tmp</filename> and <filename>/var/tmp</filename> namespace by using the
<varname>JoinsNamespaceOf=</varname> directive, see
- <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
- for details. Note that using this setting will disconnect
- propagation of mounts from the service to the host
- (propagation in the opposite direction continues to work).
- This means that this setting may not be used for services
- which shall be able to install mount points in the main mount
- namespace.</para></listitem>
+ <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
+ details. Note that using this setting will disconnect propagation of mounts from the service to the host
+ (propagation in the opposite direction continues to work). This means that this setting may not be used for
+ services which shall be able to install mount points in the main mount namespace. This setting is implied if
+ <varname>DynamicUser=</varname> is set.</para></listitem>
</varlistentry>
<varlistentry>
diff --git a/man/systemd.preset.xml b/man/systemd.preset.xml
index b7164014f0..d09167baaf 100644
--- a/man/systemd.preset.xml
+++ b/man/systemd.preset.xml
@@ -98,6 +98,10 @@
Empty lines and lines whose first non-whitespace character is # or
; are ignored.</para>
+ <para>Presets must refer to the "real" unit file, and not to any aliases. See
+ <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ for a description of unit aliasing.</para>
+
<para>Two different directives are understood:
<literal>enable</literal> may be used to enable units by default,
<literal>disable</literal> to disable units by default.</para>
diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml
index 85a7b12d76..f818e772a9 100644
--- a/man/systemd.unit.xml
+++ b/man/systemd.unit.xml
@@ -144,21 +144,31 @@
<option>false</option> and <option>off</option> are
equivalent.</para>
- <para>Time span values encoded in unit files can be written in
- various formats. A stand-alone number specifies a time in seconds.
- If suffixed with a time unit, the unit is honored. A concatenation
- of multiple values with units is supported, in which case the
- values are added up. Example: "50" refers to 50 seconds; "2min
- 200ms" refers to 2 minutes plus 200 milliseconds, i.e. 120200ms.
- The following time units are understood: s, min, h, d, w, ms, us.
- For details see
+ <para>Time span values encoded in unit files can be written in various formats. A stand-alone number specifies a
+ time in seconds. If suffixed with a time unit, the unit is honored. A concatenation of multiple values with units
+ is supported, in which case the values are added up. Example: <literal>50</literal> refers to 50 seconds;
+ <literal>2min 200ms</literal> refers to 2 minutes and 200 milliseconds, i.e. 120200 ms. The following time units
+ are understood: <literal>s</literal>, <literal>min</literal>, <literal>h</literal>, <literal>d</literal>,
+ <literal>w</literal>, <literal>ms</literal>, <literal>us</literal>. For details see
<citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
- <para>Empty lines and lines starting with # or ; are
- ignored. This may be used for commenting. Lines ending
- in a backslash are concatenated with the following
- line while reading and the backslash is replaced by a
- space character. This may be used to wrap long lines.</para>
+ <para>Empty lines and lines starting with <literal>#</literal> or <literal>;</literal> are ignored. This may be
+ used for commenting. Lines ending in a backslash are concatenated with the following line while reading and the
+ backslash is replaced by a space character. This may be used to wrap long lines.</para>
+
+ <para>Units can be aliased (have an alternative name), by creating a symlink from the new name to the existing name
+ in one of the unit search paths. For example, <filename>systemd-networkd.service</filename> has the alias
+ <filename>dbus-org.freedesktop.network1.service</filename>, created during installation as the symlink
+ <filename>/usr/lib/systemd/system/dbus-org.freedesktop.network1.service</filename>. In addition, unit files may
+ specify aliases through the <varname>Alias=</varname> directive in the [Install] section; those aliases are only
+ effective when the unit is enabled. When the unit is enabled, symlinks will be created for those names, and removed
+ when the unit is disabled. For example, <filename>reboot.target</filename> specifies
+ <varname>Alias=ctrl-alt-del.target</varname>, so when enabled it will be invoked whenever CTRL+ALT+DEL is
+ pressed. Alias names may be used in commands like <command>enable</command>, <command>disable</command>,
+ <command>start</command>, <command>stop</command>, <command>status</command>, …, and in unit dependency directives
+ <varname>Wants=</varname>, <varname>Requires=</varname>, <varname>Before=</varname>, <varname>After=</varname>, …,
+ with the limitation that aliases specified through <varname>Alias=</varname> are only effective when the unit is
+ enabled. Aliases cannot be used with the <command>preset</command> command.</para>
<para>Along with a unit file <filename>foo.service</filename>, the
directory <filename>foo.service.wants/</filename> may exist. All
diff --git a/man/systemd.xml b/man/systemd.xml
index 65f55199e2..e30333e209 100644
--- a/man/systemd.xml
+++ b/man/systemd.xml
@@ -837,8 +837,10 @@
<varlistentry>
<term><varname>$SYSTEMD_COLORS</varname></term>
- <listitem><para>Controls whether colorized output should be generated.
- </para></listitem>
+ <listitem><para>The value must be a boolean. Controls whether colorized output should be
+ generated. This can be specified to override the decision that <command>systemd</command>
+ makes based on <varname>$TERM</varname> and what the console is connected to.</para>
+ </listitem>
</varlistentry>
<varlistentry>
@@ -849,7 +851,7 @@
<listitem><para>Set by systemd for supervised processes during
socket-based activation. See
<citerefentry><refentrytitle>sd_listen_fds</refentrytitle><manvolnum>3</manvolnum></citerefentry>
- for more information. </para></listitem>
+ for more information.</para></listitem>
</varlistentry>
<varlistentry>
@@ -858,7 +860,7 @@
<listitem><para>Set by systemd for supervised processes for
status and start-up completion notification. See
<citerefentry><refentrytitle>sd_notify</refentrytitle><manvolnum>3</manvolnum></citerefentry>
- for more information. </para></listitem>
+ for more information.</para></listitem>
</varlistentry>
</variablelist>
</refsect1>
diff --git a/rules/60-persistent-storage.rules b/rules/60-persistent-storage.rules
index d7bbbf9866..c13d05cdb1 100644
--- a/rules/60-persistent-storage.rules
+++ b/rules/60-persistent-storage.rules
@@ -19,6 +19,12 @@ ENV{DEVTYPE}=="partition", IMPORT{parent}="ID_*"
KERNEL=="nvme*[0-9]n*[0-9]", ATTR{wwid}=="?*", SYMLINK+="disk/by-id/nvme-$attr{wwid}"
KERNEL=="nvme*[0-9]n*[0-9]p*[0-9]", ENV{DEVTYPE}=="partition", ATTRS{wwid}=="?*", SYMLINK+="disk/by-id/nvme-$attr{wwid}-part%n"
+KERNEL=="nvme*[0-9]n*[0-9]", ENV{DEVTYPE}=="disk", ATTRS{serial}=="?*", ENV{ID_SERIAL_SHORT}="$attr{serial}"
+KERNEL=="nvme*[0-9]n*[0-9]", ENV{DEVTYPE}=="disk", ATTRS{model}=="?*", ENV{ID_SERIAL_SHORT}=="?*", ENV{ID_SERIAL}="$attr{model}_$env{ID_SERIAL_SHORT}", SYMLINK+="disk/by-id/nvme-$env{ID_SERIAL}"
+
+KERNEL=="nvme*[0-9]n*[0-9]p*[0-9]", ENV{DEVTYPE}=="partition", ATTRS{serial}=="?*", ENV{ID_SERIAL_SHORT}="$attr{serial}"
+KERNEL=="nvme*[0-9]n*[0-9]p*[0-9]", ENV{DEVTYPE}=="partition", ATTRS{model}=="?*", ENV{ID_SERIAL_SHORT}=="?*", ENV{ID_SERIAL}="$attr{model}_$env{ID_SERIAL_SHORT}", SYMLINK+="disk/by-id/nvme-$env{ID_SERIAL}-part%n"
+
# virtio-blk
KERNEL=="vd*[!0-9]", ATTRS{serial}=="?*", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/virtio-$env{ID_SERIAL}"
KERNEL=="vd*[0-9]", ATTRS{serial}=="?*", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/virtio-$env{ID_SERIAL}-part%n"
diff --git a/shell-completion/zsh/_journalctl b/shell-completion/zsh/_journalctl
index e6549e863f..ef67fcf2a0 100644
--- a/shell-completion/zsh/_journalctl
+++ b/shell-completion/zsh/_journalctl
@@ -54,16 +54,25 @@ _journal_boots() {
# Build arguments for "journalctl" to be used in completion.
# Use both --user and --system modes, they are not exclusive.
local -a _modes; _modes=(--user --system)
-local -a _modes_with_arg; _modes_with_arg=(--directory -D --file -M --machine)
+local -a _modes_with_arg; _modes_with_arg=(--directory -D --file -M --machine --root)
typeset -a _sys_service_mgr
-local w i=0 n=$#words
+local w k v i=0 n=$#words
while (( i++ < n )); do
w=$words[$i]
if (( $_modes[(I)$w] )); then
_sys_service_mgr+=($w)
else
- if (( ${_modes_with_arg[(I)$w]} )); then
- _sys_service_mgr+=($w ${words[((++i))]})
+ # Handle options with arguments. "--key=value" and "--key value".
+ k=${w%%=*}
+ if (( ${_modes_with_arg[(I)$k]} )); then
+ v=${w#*=}
+ if [[ "$k" != "$w" ]]; then
+ # "--key=value" style.
+ _sys_service_mgr+=($w)
+ else
+ # "--key value" style.
+ _sys_service_mgr+=($w ${words[((++i))]})
+ fi
fi
fi
done
@@ -96,10 +105,10 @@ _arguments -s \
{-F,--field=}'[List all values a certain field takes]:Fields:_list_fields' \
'--system[Show system and kernel messages]' \
'--user[Show messages from user services]' \
- {-M+,--machine=}'[Operate on local container]:machines:_sd_machines' \
- {-D+,--directory=}'[Show journal files from directory]:directories:_directories' \
- '--file=[Operate on specified journal files]:file:_files' \
- '--root=[Operate on catalog hierarchy under specified directory]:directories:_directories' \
+ '(--directory -D -M --machine --root --file)'{-M+,--machine=}'[Operate on local container]:machines:_sd_machines' \
+ '(--directory -D -M --machine --root --file)'{-D+,--directory=}'[Show journal files from directory]:directories:_directories' \
+ '(--directory -D -M --machine --root --file)''--root=[Operate on catalog hierarchy under specified directory]:directories:_directories' \
+ '(--directory -D -M --machine --root)--file=[Operate on specified journal files]:file:_files' \
'--new-id128[Generate a new 128 Bit ID]' \
'--header[Show journal header information]' \
'--disk-usage[Show total disk usage]' \
diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c
index 1ef1de0604..9b44c5a7a5 100644
--- a/src/basic/cgroup-util.c
+++ b/src/basic/cgroup-util.c
@@ -2378,9 +2378,9 @@ bool cg_is_unified_systemd_controller_wanted(void) {
return wanted;
r = get_proc_cmdline_key("systemd.legacy_systemd_cgroup_controller", NULL);
- if (r > 0) {
+ if (r > 0)
wanted = false;
- } else {
+ else {
_cleanup_free_ char *value = NULL;
r = get_proc_cmdline_key("systemd.legacy_systemd_cgroup_controller=", &value);
diff --git a/src/basic/cgroup-util.h b/src/basic/cgroup-util.h
index 5d9bee50f5..1a61c7ad22 100644
--- a/src/basic/cgroup-util.h
+++ b/src/basic/cgroup-util.h
@@ -112,6 +112,10 @@ static inline bool CGROUP_BLKIO_WEIGHT_IS_OK(uint64_t x) {
(x >= CGROUP_BLKIO_WEIGHT_MIN && x <= CGROUP_BLKIO_WEIGHT_MAX);
}
+/* Default resource limits */
+#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 */
diff --git a/src/basic/hostname-util.c b/src/basic/hostname-util.c
index 13c3bb6446..e44a357287 100644
--- a/src/basic/hostname-util.c
+++ b/src/basic/hostname-util.c
@@ -163,7 +163,6 @@ char* hostname_cleanup(char *s) {
*(d++) = *p;
dot = false;
}
-
}
if (dot && d > s)
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 f0a46c48cf..eafdea9eb3 100644
--- a/src/basic/terminal-util.c
+++ b/src/basic/terminal-util.c
@@ -39,6 +39,7 @@
#include <unistd.h>
#include "alloc-util.h"
+#include "env-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
@@ -345,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;
}
@@ -1191,12 +1187,9 @@ int open_terminal_in_namespace(pid_t pid, const char *name, int mode) {
return receive_one_fd(pair[0], 0);
}
-bool terminal_is_dumb(void) {
+static bool getenv_terminal_is_dumb(void) {
const char *e;
- if (!on_tty())
- return true;
-
e = getenv("TERM");
if (!e)
return true;
@@ -1204,15 +1197,25 @@ bool terminal_is_dumb(void) {
return streq(e, "dumb");
}
+bool terminal_is_dumb(void) {
+ if (!on_tty())
+ return true;
+
+ return getenv_terminal_is_dumb();
+}
+
bool colors_enabled(void) {
static int enabled = -1;
if (_unlikely_(enabled < 0)) {
- const char *colors;
-
- colors = getenv("SYSTEMD_COLORS");
- if (colors)
- enabled = parse_boolean(colors) != 0;
+ int val;
+
+ val = getenv_bool("SYSTEMD_COLORS");
+ if (val >= 0)
+ enabled = val;
+ else if (getpid() == 1)
+ /* PID1 outputs to the console without holding it open all the time */
+ enabled = !getenv_terminal_is_dumb();
else
enabled = !terminal_is_dumb();
}
diff --git a/src/basic/user-util.h b/src/basic/user-util.h
index 36f71fb004..f569363811 100644
--- a/src/basic/user-util.h
+++ b/src/basic/user-util.h
@@ -20,6 +20,7 @@
***/
#include <stdbool.h>
+#include <stdint.h>
#include <sys/types.h>
#include <unistd.h>
@@ -57,8 +58,19 @@ int take_etc_passwd_lock(const char *root);
#define UID_INVALID ((uid_t) -1)
#define GID_INVALID ((gid_t) -1)
-/* The following macros add 1 when converting things, since UID 0 is a
- * valid UID, while the pointer NULL is special */
+/* Let's pick a UIDs within the 16bit range, so that we are compatible with containers using 16bit
+ * user namespacing. At least on Fedora normal users are allocated until UID 60000, hence do not
+ * allocate from below this. Also stay away from the upper end of the range as that is often used
+ * for overflow/nobody users. */
+#define DYNAMIC_UID_MIN ((uid_t) UINT32_C(0x0000EF00))
+#define DYNAMIC_UID_MAX ((uid_t) UINT32_C(0x0000FFEF))
+
+static inline bool uid_is_dynamic(uid_t uid) {
+ return DYNAMIC_UID_MIN <= uid && uid <= DYNAMIC_UID_MAX;
+}
+
+/* The following macros add 1 when converting things, since UID 0 is a valid UID, while the pointer
+ * NULL is special */
#define PTR_TO_UID(p) ((uid_t) (((uintptr_t) (p))-1))
#define UID_TO_PTR(u) ((void*) (((uintptr_t) (u))+1))
diff --git a/src/core/cgroup.c b/src/core/cgroup.c
index 4b73a9ac13..086cd7a789 100644
--- a/src/core/cgroup.c
+++ b/src/core/cgroup.c
@@ -952,7 +952,7 @@ static void cgroup_context_apply(Unit *u, CGroupMask mask, ManagerState state) {
if ((mask & CGROUP_MASK_PIDS) && !is_root) {
- if (c->tasks_max != (uint64_t) -1) {
+ if (c->tasks_max != CGROUP_LIMIT_MAX) {
char buf[DECIMAL_STR_MAX(uint64_t) + 2];
sprintf(buf, "%" PRIu64 "\n", c->tasks_max);
diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c
index e35d3ccd2e..7e33a2d201 100644
--- a/src/core/dbus-execute.c
+++ b/src/core/dbus-execute.c
@@ -695,6 +695,7 @@ const sd_bus_vtable bus_exec_vtable[] = {
SD_BUS_PROPERTY("User", "s", NULL, offsetof(ExecContext, user), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Group", "s", NULL, offsetof(ExecContext, group), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DynamicUser", "b", bus_property_get_bool, offsetof(ExecContext, dynamic_user), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("RemoveIPC", "b", bus_property_get_bool, offsetof(ExecContext, remove_ipc), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SupplementaryGroups", "as", NULL, offsetof(ExecContext, supplementary_groups), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("PAMName", "s", NULL, offsetof(ExecContext, pam_name), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ReadWriteDirectories", "as", NULL, offsetof(ExecContext, read_write_paths), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
@@ -1071,7 +1072,7 @@ int bus_exec_context_set_transient_property(
"IgnoreSIGPIPE", "TTYVHangup", "TTYReset",
"PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers",
"NoNewPrivileges", "SyslogLevelPrefix", "MemoryDenyWriteExecute",
- "RestrictRealtime", "DynamicUser")) {
+ "RestrictRealtime", "DynamicUser", "RemoveIPC")) {
int b;
r = sd_bus_message_read(message, "b", &b);
@@ -1103,6 +1104,8 @@ int bus_exec_context_set_transient_property(
c->restrict_realtime = b;
else if (streq(name, "DynamicUser"))
c->dynamic_user = b;
+ else if (streq(name, "RemoveIPC"))
+ c->remove_ipc = b;
unit_write_drop_in_private_format(u, mode, name, "%s=%s", name, yes_no(b));
}
diff --git a/src/core/dbus-mount.c b/src/core/dbus-mount.c
index b4bbee0648..3c6bda4073 100644
--- a/src/core/dbus-mount.c
+++ b/src/core/dbus-mount.c
@@ -117,6 +117,8 @@ const sd_bus_vtable bus_mount_vtable[] = {
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("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),
BUS_EXEC_COMMAND_VTABLE("ExecMount", offsetof(Mount, exec_command[MOUNT_EXEC_MOUNT]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
BUS_EXEC_COMMAND_VTABLE("ExecUnmount", offsetof(Mount, exec_command[MOUNT_EXEC_UNMOUNT]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
BUS_EXEC_COMMAND_VTABLE("ExecRemount", offsetof(Mount, exec_command[MOUNT_EXEC_REMOUNT]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c
index fab3677a01..3c55e0f7fe 100644
--- a/src/core/dbus-service.c
+++ b/src/core/dbus-service.c
@@ -50,11 +50,6 @@ const sd_bus_vtable bus_service_vtable[] = {
SD_BUS_PROPERTY("RuntimeMaxUSec", "t", bus_property_get_usec, offsetof(Service, runtime_max_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("WatchdogUSec", "t", bus_property_get_usec, offsetof(Service, watchdog_usec), SD_BUS_VTABLE_PROPERTY_CONST),
BUS_PROPERTY_DUAL_TIMESTAMP("WatchdogTimestamp", offsetof(Service, watchdog_timestamp), 0),
- /* The following four are obsolete, and thus marked hidden here. They moved into the Unit interface */
- SD_BUS_PROPERTY("StartLimitInterval", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
- SD_BUS_PROPERTY("StartLimitBurst", "u", bus_property_get_unsigned, offsetof(Unit, start_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
- SD_BUS_PROPERTY("StartLimitAction", "s", property_get_failure_action, offsetof(Unit, start_limit_action), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
- SD_BUS_PROPERTY("RebootArgument", "s", NULL, offsetof(Unit, reboot_arg), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
SD_BUS_PROPERTY("FailureAction", "s", property_get_failure_action, offsetof(Service, failure_action), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("PermissionsStartOnly", "b", bus_property_get_bool, offsetof(Service, permissions_start_only), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RootDirectoryStartOnly", "b", bus_property_get_bool, offsetof(Service, root_directory_start_only), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -70,6 +65,9 @@ const sd_bus_vtable bus_service_vtable[] = {
SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Service, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("USBFunctionDescriptors", "s", NULL, offsetof(Service, usb_function_descriptors), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("USBFunctionStrings", "s", NULL, offsetof(Service, usb_function_strings), 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),
+
BUS_EXEC_STATUS_VTABLE("ExecMain", offsetof(Service, main_exec_status), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPre", offsetof(Service, exec_command[SERVICE_EXEC_START_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStart", offsetof(Service, exec_command[SERVICE_EXEC_START]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
@@ -77,6 +75,12 @@ const sd_bus_vtable bus_service_vtable[] = {
BUS_EXEC_COMMAND_LIST_VTABLE("ExecReload", offsetof(Service, exec_command[SERVICE_EXEC_RELOAD]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStop", offsetof(Service, exec_command[SERVICE_EXEC_STOP]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStopPost", offsetof(Service, exec_command[SERVICE_EXEC_STOP_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
+
+ /* The following four are obsolete, and thus marked hidden here. They moved into the Unit interface */
+ SD_BUS_PROPERTY("StartLimitInterval", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
+ SD_BUS_PROPERTY("StartLimitBurst", "u", bus_property_get_unsigned, offsetof(Unit, start_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
+ SD_BUS_PROPERTY("StartLimitAction", "s", property_get_failure_action, offsetof(Unit, start_limit_action), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
+ SD_BUS_PROPERTY("RebootArgument", "s", NULL, offsetof(Unit, reboot_arg), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
SD_BUS_VTABLE_END
};
diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c
index 9a071a1355..21adb64e15 100644
--- a/src/core/dbus-socket.c
+++ b/src/core/dbus-socket.c
@@ -152,6 +152,8 @@ const sd_bus_vtable bus_socket_vtable[] = {
SD_BUS_PROPERTY("SocketProtocol", "i", bus_property_get_int, offsetof(Socket, socket_protocol), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("TriggerLimitIntervalUSec", "t", bus_property_get_usec, offsetof(Socket, trigger_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("TriggerLimitBurst", "u", bus_property_get_unsigned, offsetof(Socket, trigger_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST),
+ 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),
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPre", offsetof(Socket, exec_command[SOCKET_EXEC_START_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPost", offsetof(Socket, exec_command[SOCKET_EXEC_START_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStopPre", offsetof(Socket, exec_command[SOCKET_EXEC_STOP_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
diff --git a/src/core/dbus-swap.c b/src/core/dbus-swap.c
index 292f8738c6..85a2c26b98 100644
--- a/src/core/dbus-swap.c
+++ b/src/core/dbus-swap.c
@@ -84,6 +84,8 @@ const sd_bus_vtable bus_swap_vtable[] = {
SD_BUS_PROPERTY("TimeoutUSec", "t", bus_property_get_usec, offsetof(Swap, timeout_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ControlPID", "u", bus_property_get_pid, offsetof(Swap, control_pid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Swap, 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),
BUS_EXEC_COMMAND_VTABLE("ExecActivate", offsetof(Swap, exec_command[SWAP_EXEC_ACTIVATE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
BUS_EXEC_COMMAND_VTABLE("ExecDeactivate", offsetof(Swap, exec_command[SWAP_EXEC_DEACTIVATE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
SD_BUS_VTABLE_END
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c
index b55d2cf735..89e56a2e51 100644
--- a/src/core/dbus-unit.c
+++ b/src/core/dbus-unit.c
@@ -660,10 +660,6 @@ const sd_bus_vtable bus_unit_vtable[] = {
SD_BUS_PROPERTY("PropagatesReloadTo", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_PROPAGATES_RELOAD_TO]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ReloadPropagatedFrom", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_RELOAD_PROPAGATED_FROM]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("JoinsNamespaceOf", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_JOINS_NAMESPACE_OF]), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("RequiresOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN),
- SD_BUS_PROPERTY("RequisiteOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN),
- SD_BUS_PROPERTY("RequiredByOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN),
- SD_BUS_PROPERTY("RequisiteOfOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN),
SD_BUS_PROPERTY("RequiresMountsFor", "as", NULL, offsetof(Unit, requires_mounts_for), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Documentation", "as", NULL, offsetof(Unit, documentation), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Description", "s", property_get_description, 0, SD_BUS_VTABLE_PROPERTY_CONST),
@@ -705,7 +701,6 @@ const sd_bus_vtable bus_unit_vtable[] = {
SD_BUS_PROPERTY("LoadError", "(ss)", property_get_load_error, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Transient", "b", bus_property_get_bool, offsetof(Unit, transient), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("StartLimitIntervalSec", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("StartLimitInterval", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), /* obsolete alias name */
SD_BUS_PROPERTY("StartLimitBurst", "u", bus_property_get_unsigned, offsetof(Unit, start_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("StartLimitAction", "s", property_get_failure_action, offsetof(Unit, start_limit_action), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RebootArgument", "s", NULL, offsetof(Unit, reboot_arg), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -721,6 +716,12 @@ const sd_bus_vtable bus_unit_vtable[] = {
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),
+ /* Obsolete properties or obsolete alias names */
+ SD_BUS_PROPERTY("RequiresOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN),
+ SD_BUS_PROPERTY("RequisiteOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN),
+ SD_BUS_PROPERTY("RequiredByOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN),
+ SD_BUS_PROPERTY("RequisiteOfOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN),
+ SD_BUS_PROPERTY("StartLimitInterval", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
SD_BUS_VTABLE_END
};
diff --git a/src/core/dynamic-user.c b/src/core/dynamic-user.c
index 8035bee231..310aaa94e1 100644
--- a/src/core/dynamic-user.c
+++ b/src/core/dynamic-user.c
@@ -31,14 +31,8 @@
#include "user-util.h"
#include "fileio.h"
-/* Let's pick a UIDs within the 16bit range, so that we are compatible with containers using 16bit user namespacing. At
- * least on Fedora normal users are allocated until UID 60000, hence do not allocate from below this. Also stay away
- * from the upper end of the range as that is often used for overflow/nobody users. */
-#define UID_PICK_MIN ((uid_t) UINT32_C(0x0000EF00))
-#define UID_PICK_MAX ((uid_t) UINT32_C(0x0000FFEF))
-
/* Takes a value generated randomly or by hashing and turns it into a UID in the right range */
-#define UID_CLAMP_INTO_RANGE(rnd) (((uid_t) (rnd) % (UID_PICK_MAX - UID_PICK_MIN + 1)) + UID_PICK_MIN)
+#define UID_CLAMP_INTO_RANGE(rnd) (((uid_t) (rnd) % (DYNAMIC_UID_MAX - DYNAMIC_UID_MIN + 1)) + DYNAMIC_UID_MIN)
static DynamicUser* dynamic_user_free(DynamicUser *d) {
if (!d)
@@ -150,6 +144,45 @@ int dynamic_user_acquire(Manager *m, const char *name, DynamicUser** ret) {
return 1;
}
+static int make_uid_symlinks(uid_t uid, const char *name, bool b) {
+
+ char path1[strlen("/run/systemd/dynamic-uid/direct:") + DECIMAL_STR_MAX(uid_t) + 1];
+ const char *path2;
+ int r = 0, k;
+
+ /* Add direct additional symlinks for direct lookups of dynamic UIDs and their names by userspace code. The
+ * only reason we have this is because dbus-daemon cannot use D-Bus for resolving users and groups (since it
+ * would be its own client then). We hence keep these world-readable symlinks in place, so that the
+ * unprivileged dbus user can read the mappings when it needs them via these symlinks instead of having to go
+ * via the bus. Ideally, we'd use the lock files we keep for this anyway, but we can't since we use BSD locks
+ * on them and as those may be taken by any user with read access we can't make them world-readable. */
+
+ xsprintf(path1, "/run/systemd/dynamic-uid/direct:" UID_FMT, uid);
+ if (unlink(path1) < 0 && errno != ENOENT)
+ r = -errno;
+
+ if (b && symlink(name, path1) < 0) {
+ k = log_warning_errno(errno, "Failed to symlink \"%s\": %m", path1);
+ if (r == 0)
+ r = k;
+ }
+
+ path2 = strjoina("/run/systemd/dynamic-uid/direct:", name);
+ if (unlink(path2) < 0 && errno != ENOENT) {
+ k = -errno;
+ if (r == 0)
+ r = k;
+ }
+
+ if (b && symlink(path1 + strlen("/run/systemd/dynamic-uid/direct:"), path2) < 0) {
+ k = log_warning_errno(errno, "Failed to symlink \"%s\": %m", path2);
+ if (r == 0)
+ r = k;
+ }
+
+ return r;
+}
+
static int pick_uid(const char *name, uid_t *ret_uid) {
static const uint8_t hash_key[] = {
@@ -175,7 +208,7 @@ static int pick_uid(const char *name, uid_t *ret_uid) {
if (--n_tries <= 0) /* Give up retrying eventually */
return -EBUSY;
- if (candidate < UID_PICK_MIN || candidate > UID_PICK_MAX)
+ if (!uid_is_dynamic(candidate))
goto next;
xsprintf(lock_path, "/run/systemd/dynamic-uid/" UID_FMT, candidate);
@@ -223,6 +256,7 @@ static int pick_uid(const char *name, uid_t *ret_uid) {
}
(void) ftruncate(lock_fd, l);
+ (void) make_uid_symlinks(candidate, name, true); /* also add direct lookup symlinks */
*ret_uid = candidate;
r = lock_fd;
@@ -324,14 +358,16 @@ static int dynamic_user_push(DynamicUser *d, uid_t uid, int lock_fd) {
return 0;
}
-static void unlink_uid_lock(int lock_fd, uid_t uid) {
+static void unlink_uid_lock(int lock_fd, uid_t uid, const char *name) {
char lock_path[strlen("/run/systemd/dynamic-uid/") + DECIMAL_STR_MAX(uid_t) + 1];
if (lock_fd < 0)
return;
xsprintf(lock_path, "/run/systemd/dynamic-uid/" UID_FMT, uid);
- (void) unlink_noerrno(lock_path);
+ (void) unlink(lock_path);
+
+ (void) make_uid_symlinks(uid, name, false); /* remove direct lookup symlinks */
}
int dynamic_user_realize(DynamicUser *d, uid_t *ret) {
@@ -399,7 +435,7 @@ int dynamic_user_realize(DynamicUser *d, uid_t *ret) {
/* So, we found a working UID/lock combination. Let's see if we actually still need it. */
if (lockf(d->storage_socket[0], F_LOCK, 0) < 0) {
- unlink_uid_lock(uid_lock_fd, uid);
+ unlink_uid_lock(uid_lock_fd, uid, d->name);
return -errno;
}
@@ -407,7 +443,7 @@ int dynamic_user_realize(DynamicUser *d, uid_t *ret) {
if (r < 0) {
if (r != -EAGAIN) {
/* OK, something bad happened, let's get rid of the bits we acquired. */
- unlink_uid_lock(uid_lock_fd, uid);
+ unlink_uid_lock(uid_lock_fd, uid, d->name);
goto finish;
}
@@ -416,7 +452,7 @@ int dynamic_user_realize(DynamicUser *d, uid_t *ret) {
/* Hmm, so as it appears there's now something stored in the storage socket. Throw away what we
* acquired, and use what's stored now. */
- unlink_uid_lock(uid_lock_fd, uid);
+ unlink_uid_lock(uid_lock_fd, uid, d->name);
safe_close(uid_lock_fd);
uid = new_uid;
@@ -513,7 +549,7 @@ static int dynamic_user_close(DynamicUser *d) {
goto finish;
/* This dynamic user was realized and dynamically allocated. In this case, let's remove the lock file. */
- unlink_uid_lock(lock_fd, uid);
+ unlink_uid_lock(lock_fd, uid, d->name);
r = 1;
finish:
@@ -634,11 +670,8 @@ int dynamic_user_lookup_uid(Manager *m, uid_t uid, char **ret) {
assert(m);
assert(ret);
- /* A friendly way to translate a dynamic user's UID into a his name. */
-
- if (uid < UID_PICK_MIN)
- return -ESRCH;
- if (uid > UID_PICK_MAX)
+ /* A friendly way to translate a dynamic user's UID into a name. */
+ if (!uid_is_dynamic(uid))
return -ESRCH;
xsprintf(lock_path, "/run/systemd/dynamic-uid/" UID_FMT, uid);
diff --git a/src/core/execute.c b/src/core/execute.c
index 6019df7ea6..0af8eb5a02 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -91,6 +91,7 @@
#include "selinux-util.h"
#include "signal-util.h"
#include "smack-util.h"
+#include "special.h"
#include "string-table.h"
#include "string-util.h"
#include "strv.h"
@@ -1384,6 +1385,7 @@ static void do_idle_pipe_dance(int idle_pipe[4]) {
}
static int build_environment(
+ Unit *u,
const ExecContext *c,
const ExecParameters *p,
unsigned n_fds,
@@ -1401,7 +1403,7 @@ static int build_environment(
assert(c);
assert(ret);
- our_env = new0(char*, 12);
+ our_env = new0(char*, 13);
if (!our_env)
return -ENOMEM;
@@ -1436,6 +1438,16 @@ static int build_environment(
our_env[n_env++] = x;
}
+ /* If this is D-Bus, tell the nss-systemd module, since it relies on being able to use D-Bus look up dynamic
+ * users via PID 1, possibly dead-locking the dbus daemon. This way it will not use D-Bus to resolve names, but
+ * check the database directly. */
+ if (unit_has_name(u, SPECIAL_DBUS_SERVICE)) {
+ x = strdup("SYSTEMD_NSS_BYPASS_BUS=1");
+ if (!x)
+ return -ENOMEM;
+ our_env[n_env++] = x;
+ }
+
if (home) {
x = strappend("HOME=", home);
if (!x)
@@ -1723,11 +1735,12 @@ static int close_remaining_fds(
const ExecParameters *params,
ExecRuntime *runtime,
DynamicCreds *dcreds,
+ int user_lookup_fd,
int socket_fd,
int *fds, unsigned n_fds) {
unsigned n_dont_close = 0;
- int dont_close[n_fds + 11];
+ int dont_close[n_fds + 12];
assert(params);
@@ -1755,9 +1768,40 @@ static int close_remaining_fds(
append_socket_pair(dont_close, &n_dont_close, dcreds->group->storage_socket);
}
+ if (user_lookup_fd >= 0)
+ dont_close[n_dont_close++] = user_lookup_fd;
+
return close_all_fds(dont_close, n_dont_close);
}
+static int send_user_lookup(
+ Unit *unit,
+ int user_lookup_fd,
+ uid_t uid,
+ gid_t gid) {
+
+ assert(unit);
+
+ /* Send the resolved UID/GID to PID 1 after we learnt it. We send a single datagram, containing the UID/GID
+ * data as well as the unit name. Note that we suppress sending this if no user/group to resolve was
+ * specified. */
+
+ if (user_lookup_fd < 0)
+ return 0;
+
+ if (!uid_is_valid(uid) && !gid_is_valid(gid))
+ return 0;
+
+ if (writev(user_lookup_fd,
+ (struct iovec[]) {
+ { .iov_base = &uid, .iov_len = sizeof(uid) },
+ { .iov_base = &gid, .iov_len = sizeof(gid) },
+ { .iov_base = unit->id, .iov_len = strlen(unit->id) }}, 3) < 0)
+ return -errno;
+
+ return 0;
+}
+
static int exec_child(
Unit *unit,
ExecCommand *command,
@@ -1769,6 +1813,7 @@ static int exec_child(
int socket_fd,
int *fds, unsigned n_fds,
char **files_env,
+ int user_lookup_fd,
int *exit_status) {
_cleanup_strv_free_ char **our_env = NULL, **pass_env = NULL, **accum_env = NULL, **final_argv = NULL;
@@ -1815,7 +1860,7 @@ static int exec_child(
log_forget_fds();
- r = close_remaining_fds(params, runtime, dcreds, socket_fd, fds, n_fds);
+ r = close_remaining_fds(params, runtime, dcreds, user_lookup_fd, socket_fd, fds, n_fds);
if (r < 0) {
*exit_status = EXIT_FDS;
return r;
@@ -1862,7 +1907,7 @@ static int exec_child(
return r;
}
- if (uid == UID_INVALID || gid == GID_INVALID) {
+ if (!uid_is_valid(uid) || !gid_is_valid(gid)) {
*exit_status = EXIT_USER;
return -ESRCH;
}
@@ -1902,6 +1947,14 @@ static int exec_child(
}
}
+ r = send_user_lookup(unit, user_lookup_fd, uid, gid);
+ if (r < 0) {
+ *exit_status = EXIT_USER;
+ return r;
+ }
+
+ user_lookup_fd = safe_close(user_lookup_fd);
+
/* If a socket is connected to STDIN/STDOUT/STDERR, we
* must sure to drop O_NONBLOCK */
if (socket_fd >= 0)
@@ -2059,6 +2112,7 @@ static int exec_child(
}
r = build_environment(
+ unit,
context,
params,
n_fds,
@@ -2501,6 +2555,7 @@ int exec_spawn(Unit *unit,
socket_fd,
fds, n_fds,
files_env,
+ unit->manager->user_lookup_fds[1],
&exit_status);
if (r < 0) {
log_open();
diff --git a/src/core/execute.h b/src/core/execute.h
index 106154f81a..6082c42aba 100644
--- a/src/core/execute.h
+++ b/src/core/execute.h
@@ -178,6 +178,7 @@ struct ExecContext {
bool no_new_privileges;
bool dynamic_user;
+ bool remove_ipc;
/* This is not exposed to the user but available
* internally. We need it to make sure that whenever we spawn
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index 4ad6c4d79b..d5185cf6a0 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -2999,30 +2999,36 @@ int config_parse_tasks_max(
void *data,
void *userdata) {
- uint64_t *tasks_max = data, u;
+ uint64_t *tasks_max = data, v;
+ Unit *u = userdata;
int r;
- if (isempty(rvalue) || streq(rvalue, "infinity")) {
- *tasks_max = (uint64_t) -1;
+ if (isempty(rvalue)) {
+ *tasks_max = u->manager->default_tasks_max;
+ return 0;
+ }
+
+ if (streq(rvalue, "infinity")) {
+ *tasks_max = CGROUP_LIMIT_MAX;
return 0;
}
r = parse_percent(rvalue);
if (r < 0) {
- r = safe_atou64(rvalue, &u);
+ r = safe_atou64(rvalue, &v);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Maximum tasks value '%s' invalid. Ignoring.", rvalue);
return 0;
}
} else
- u = system_tasks_max_scale(r, 100U);
+ v = system_tasks_max_scale(r, 100U);
- if (u <= 0 || u >= UINT64_MAX) {
+ if (v <= 0 || v >= UINT64_MAX) {
log_syntax(unit, LOG_ERR, filename, line, 0, "Maximum tasks value '%s' out of range. Ignoring.", rvalue);
return 0;
}
- *tasks_max = u;
+ *tasks_max = v;
return 0;
}
diff --git a/src/core/main.c b/src/core/main.c
index 02324d325e..125cfb28f0 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -1505,7 +1505,8 @@ int main(int argc, char *argv[]) {
if (getpid() == 1) {
/* Don't limit the core dump size, so that coredump handlers such as systemd-coredump (which honour the limit)
* will process core dumps for system services by default. */
- (void) setrlimit(RLIMIT_CORE, &RLIMIT_MAKE_CONST(RLIM_INFINITY));
+ if (setrlimit(RLIMIT_CORE, &RLIMIT_MAKE_CONST(RLIM_INFINITY)) < 0)
+ log_warning_errno(errno, "Failed to set RLIMIT_CORE: %m");
/* But at the same time, turn off the core_pattern logic by default, so that no coredumps are stored
* until the systemd-coredump tool is enabled via sysctl. */
@@ -1559,7 +1560,7 @@ int main(int argc, char *argv[]) {
(void) reset_all_signal_handlers();
(void) ignore_signals(SIGNALS_IGNORE, -1);
- arg_default_tasks_max = system_tasks_max_scale(15U, 100U); /* 15% the system PIDs equals 4915 by default. */
+ arg_default_tasks_max = system_tasks_max_scale(DEFAULT_TASKS_MAX_PERCENTAGE, 100U);
if (parse_config_file() < 0) {
error_message = "Failed to parse config file";
@@ -2013,9 +2014,6 @@ finish:
log_error_errno(r, "Failed to switch root, trying to continue: %m");
}
- /* Reopen the console */
- (void) make_console_stdio();
-
args_size = MAX(6, argc+1);
args = newa(const char*, args_size);
@@ -2063,6 +2061,9 @@ finish:
arg_serialization = safe_fclose(arg_serialization);
fds = fdset_free(fds);
+ /* Reopen the console */
+ (void) make_console_stdio();
+
for (j = 1, i = 1; j < (unsigned) argc; j++)
args[i++] = argv[j];
args[i++] = NULL;
diff --git a/src/core/manager.c b/src/core/manager.c
index 5c58e64a1b..7576d038a2 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -45,6 +45,7 @@
#include "bus-error.h"
#include "bus-kernel.h"
#include "bus-util.h"
+#include "clean-ipc.h"
#include "dbus-job.h"
#include "dbus-manager.h"
#include "dbus-unit.h"
@@ -81,6 +82,7 @@
#include "transaction.h"
#include "umask-util.h"
#include "unit-name.h"
+#include "user-util.h"
#include "util.h"
#include "virt.h"
#include "watchdog.h"
@@ -98,6 +100,7 @@ static int manager_dispatch_cgroups_agent_fd(sd_event_source *source, int fd, ui
static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata);
static int manager_dispatch_time_change_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata);
static int manager_dispatch_idle_pipe_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata);
+static int manager_dispatch_user_lookup_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata);
static int manager_dispatch_jobs_in_progress(sd_event_source *source, usec_t usec, void *userdata);
static int manager_dispatch_run_queue(sd_event_source *source, void *userdata);
static int manager_run_generators(Manager *m);
@@ -590,6 +593,8 @@ int manager_new(UnitFileScope scope, bool test_run, Manager **_m) {
m->dev_autofs_fd = m->private_listen_fd = m->kdbus_fd = m->cgroup_inotify_fd =
m->ask_password_inotify_fd = -1;
+ m->user_lookup_fds[0] = m->user_lookup_fds[1] = -1;
+
m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */
m->have_ask_password = -EINVAL; /* we don't know */
@@ -812,6 +817,59 @@ static int manager_setup_cgroups_agent(Manager *m) {
return 0;
}
+static int manager_setup_user_lookup_fd(Manager *m) {
+ int r;
+
+ assert(m);
+
+ /* Set up the socket pair used for passing UID/GID resolution results from forked off processes to PID
+ * 1. Background: we can't do name lookups (NSS) from PID 1, since it might involve IPC and thus activation,
+ * and we might hence deadlock on ourselves. Hence we do all user/group lookups asynchronously from the forked
+ * off processes right before executing the binaries to start. In order to be able to clean up any IPC objects
+ * created by a unit (see RemoveIPC=) we need to know in PID 1 the used UID/GID of the executed processes,
+ * hence we establish this communication channel so that forked off processes can pass their UID/GID
+ * information back to PID 1. The forked off processes send their resolved UID/GID to PID 1 in a simple
+ * datagram, along with their unit name, so that we can share one communication socket pair among all units for
+ * this purpose.
+ *
+ * You might wonder why we need a communication channel for this that is independent of the usual notification
+ * socket scheme (i.e. $NOTIFY_SOCKET). The primary difference is about trust: data sent via the $NOTIFY_SOCKET
+ * channel is only accepted if it originates from the right unit and if reception was enabled for it. The user
+ * lookup socket OTOH is only accessible by PID 1 and its children until they exec(), and always available.
+ *
+ * Note that this function is called under two circumstances: when we first initialize (in which case we
+ * allocate both the socket pair and the event source to listen on it), and when we deserialize after a reload
+ * (in which case the socket pair already exists but we still need to allocate the event source for it). */
+
+ if (m->user_lookup_fds[0] < 0) {
+
+ /* Free all secondary fields */
+ safe_close_pair(m->user_lookup_fds);
+ m->user_lookup_event_source = sd_event_source_unref(m->user_lookup_event_source);
+
+ if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, m->user_lookup_fds) < 0)
+ return log_error_errno(errno, "Failed to allocate user lookup socket: %m");
+
+ (void) fd_inc_rcvbuf(m->user_lookup_fds[0], NOTIFY_RCVBUF_SIZE);
+ }
+
+ if (!m->user_lookup_event_source) {
+ r = sd_event_add_io(m->event, &m->user_lookup_event_source, m->user_lookup_fds[0], EPOLLIN, manager_dispatch_user_lookup_fd, m);
+ if (r < 0)
+ return log_error_errno(errno, "Failed to allocate user lookup event source: %m");
+
+ /* Process even earlier than the notify event source, so that we always know first about valid UID/GID
+ * resolutions */
+ r = sd_event_source_set_priority(m->user_lookup_event_source, SD_EVENT_PRIORITY_NORMAL-8);
+ if (r < 0)
+ return log_error_errno(errno, "Failed to set priority ot user lookup event source: %m");
+
+ (void) sd_event_source_set_description(m->user_lookup_event_source, "user-lookup");
+ }
+
+ return 0;
+}
+
static int manager_connect_bus(Manager *m, bool reexecuting) {
bool try_bus_connect;
@@ -853,8 +911,7 @@ enum {
_GC_OFFSET_MAX
};
-static void unit_gc_mark_good(Unit *u, unsigned gc_marker)
-{
+static void unit_gc_mark_good(Unit *u, unsigned gc_marker) {
Iterator i;
Unit *other;
@@ -1021,12 +1078,14 @@ Manager* manager_free(Manager *m) {
sd_event_source_unref(m->time_change_event_source);
sd_event_source_unref(m->jobs_in_progress_event_source);
sd_event_source_unref(m->run_queue_event_source);
+ sd_event_source_unref(m->user_lookup_event_source);
safe_close(m->signal_fd);
safe_close(m->notify_fd);
safe_close(m->cgroups_agent_fd);
safe_close(m->time_change_fd);
safe_close(m->kdbus_fd);
+ safe_close_pair(m->user_lookup_fds);
manager_close_ask_password(m);
@@ -1052,6 +1111,9 @@ Manager* manager_free(Manager *m) {
assert(hashmap_isempty(m->units_requiring_mounts_for));
hashmap_free(m->units_requiring_mounts_for);
+ hashmap_free(m->uid_refs);
+ hashmap_free(m->gid_refs);
+
free(m);
return NULL;
}
@@ -1221,6 +1283,10 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
if (q < 0 && r == 0)
r = q;
+ q = manager_setup_user_lookup_fd(m);
+ 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);
@@ -1232,6 +1298,10 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
/* Release any dynamic users no longer referenced */
dynamic_user_vacuum(m, true);
+ /* Release any references to UIDs/GIDs no longer referenced, and destroy any IPC owned by them */
+ manager_vacuum_uid_refs(m);
+ manager_vacuum_gid_refs(m);
+
if (serialization) {
assert(m->n_reloading > 0);
m->n_reloading--;
@@ -2396,6 +2466,20 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) {
fprintf(f, "cgroups-agent-fd=%i\n", copy);
}
+ if (m->user_lookup_fds[0] >= 0) {
+ int copy0, copy1;
+
+ copy0 = fdset_put_dup(fds, m->user_lookup_fds[0]);
+ if (copy0 < 0)
+ return copy0;
+
+ copy1 = fdset_put_dup(fds, m->user_lookup_fds[1]);
+ if (copy1 < 0)
+ return copy1;
+
+ fprintf(f, "user-lookup=%i %i\n", copy0, copy1);
+ }
+
if (m->kdbus_fd >= 0) {
int copy;
@@ -2412,6 +2496,9 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) {
if (r < 0)
return r;
+ manager_serialize_uid_refs(m, f);
+ manager_serialize_gid_refs(m, f);
+
fputc('\n', f);
HASHMAP_FOREACH_KEY(u, t, m->units, i) {
@@ -2578,6 +2665,18 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
m->cgroups_agent_fd = fdset_remove(fds, fd);
}
+ } else if (startswith(l, "user-lookup=")) {
+ int fd0, fd1;
+
+ if (sscanf(l + 12, "%i %i", &fd0, &fd1) != 2 || fd0 < 0 || fd1 < 0 || fd0 == fd1 || !fdset_contains(fds, fd0) || !fdset_contains(fds, fd1))
+ log_debug("Failed to parse user lookup fd: %s", l + 12);
+ else {
+ m->user_lookup_event_source = sd_event_source_unref(m->user_lookup_event_source);
+ safe_close_pair(m->user_lookup_fds);
+ m->user_lookup_fds[0] = fdset_remove(fds, fd0);
+ m->user_lookup_fds[1] = fdset_remove(fds, fd1);
+ }
+
} else if (startswith(l, "kdbus-fd=")) {
int fd;
@@ -2590,6 +2689,10 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
} else if (startswith(l, "dynamic-user="))
dynamic_user_deserialize_one(m, l + 13, fds);
+ else if (startswith(l, "destroy-ipc-uid="))
+ 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;
@@ -2672,6 +2775,8 @@ int manager_reload(Manager *m) {
lookup_paths_flush_generator(&m->lookup_paths);
lookup_paths_free(&m->lookup_paths);
dynamic_user_vacuum(m, false);
+ m->uid_refs = hashmap_free(m->uid_refs);
+ m->gid_refs = hashmap_free(m->gid_refs);
q = lookup_paths_init(&m->lookup_paths, m->unit_file_scope, 0, NULL);
if (q < 0 && r >= 0)
@@ -2705,12 +2810,20 @@ int manager_reload(Manager *m) {
if (q < 0 && r >= 0)
r = q;
+ q = manager_setup_user_lookup_fd(m);
+ if (q < 0 && r >= 0)
+ r = q;
+
/* Third, fire things up! */
manager_coldplug(m);
/* Release any dynamic users no longer referenced */
dynamic_user_vacuum(m, true);
+ /* Release any references to UIDs/GIDs no longer referenced, and destroy any IPC owned by them */
+ manager_vacuum_uid_refs(m);
+ manager_vacuum_gid_refs(m);
+
/* Sync current state of bus names with our set of listening units */
if (m->api_bus)
manager_sync_bus_names(m, m->api_bus);
@@ -3144,6 +3257,300 @@ ManagerState manager_state(Manager *m) {
return MANAGER_RUNNING;
}
+#define DESTROY_IPC_FLAG (UINT32_C(1) << 31)
+
+static void manager_unref_uid_internal(
+ Manager *m,
+ Hashmap **uid_refs,
+ uid_t uid,
+ bool destroy_now,
+ int (*_clean_ipc)(uid_t uid)) {
+
+ uint32_t c, n;
+
+ assert(m);
+ assert(uid_refs);
+ assert(uid_is_valid(uid));
+ assert(_clean_ipc);
+
+ /* A generic implementation, covering both manager_unref_uid() and manager_unref_gid(), under the assumption
+ * that uid_t and gid_t are actually defined the same way, with the same validity rules.
+ *
+ * We store a hashmap where the UID/GID is they key and the value is a 32bit reference counter, whose highest
+ * bit is used as flag for marking UIDs/GIDs whose IPC objects to remove when the last reference to the UID/GID
+ * is dropped. The flag is set to on, once at least one reference from a unit where RemoveIPC= is set is added
+ * on a UID/GID. It is reset when the UID's/GID's reference counter drops to 0 again. */
+
+ assert_cc(sizeof(uid_t) == sizeof(gid_t));
+ assert_cc(UID_INVALID == (uid_t) GID_INVALID);
+
+ if (uid == 0) /* We don't keep track of root, and will never destroy it */
+ return;
+
+ c = PTR_TO_UINT32(hashmap_get(*uid_refs, UID_TO_PTR(uid)));
+
+ n = c & ~DESTROY_IPC_FLAG;
+ assert(n > 0);
+ n--;
+
+ if (destroy_now && n == 0) {
+ hashmap_remove(*uid_refs, UID_TO_PTR(uid));
+
+ if (c & DESTROY_IPC_FLAG) {
+ log_debug("%s " UID_FMT " is no longer referenced, cleaning up its IPC.",
+ _clean_ipc == clean_ipc_by_uid ? "UID" : "GID",
+ uid);
+ (void) _clean_ipc(uid);
+ }
+ } else {
+ c = n | (c & DESTROY_IPC_FLAG);
+ assert_se(hashmap_update(*uid_refs, UID_TO_PTR(uid), UINT32_TO_PTR(c)) >= 0);
+ }
+}
+
+void manager_unref_uid(Manager *m, uid_t uid, bool destroy_now) {
+ manager_unref_uid_internal(m, &m->uid_refs, uid, destroy_now, clean_ipc_by_uid);
+}
+
+void manager_unref_gid(Manager *m, gid_t gid, bool destroy_now) {
+ manager_unref_uid_internal(m, &m->gid_refs, (uid_t) gid, destroy_now, clean_ipc_by_gid);
+}
+
+static int manager_ref_uid_internal(
+ Manager *m,
+ Hashmap **uid_refs,
+ uid_t uid,
+ bool clean_ipc) {
+
+ uint32_t c, n;
+ int r;
+
+ assert(m);
+ assert(uid_refs);
+ assert(uid_is_valid(uid));
+
+ /* A generic implementation, covering both manager_ref_uid() and manager_ref_gid(), under the assumption
+ * that uid_t and gid_t are actually defined the same way, with the same validity rules. */
+
+ assert_cc(sizeof(uid_t) == sizeof(gid_t));
+ assert_cc(UID_INVALID == (uid_t) GID_INVALID);
+
+ if (uid == 0) /* We don't keep track of root, and will never destroy it */
+ return 0;
+
+ r = hashmap_ensure_allocated(uid_refs, &trivial_hash_ops);
+ if (r < 0)
+ return r;
+
+ c = PTR_TO_UINT32(hashmap_get(*uid_refs, UID_TO_PTR(uid)));
+
+ n = c & ~DESTROY_IPC_FLAG;
+ n++;
+
+ if (n & DESTROY_IPC_FLAG) /* check for overflow */
+ return -EOVERFLOW;
+
+ c = n | (c & DESTROY_IPC_FLAG) | (clean_ipc ? DESTROY_IPC_FLAG : 0);
+
+ return hashmap_replace(*uid_refs, UID_TO_PTR(uid), UINT32_TO_PTR(c));
+}
+
+int manager_ref_uid(Manager *m, uid_t uid, bool clean_ipc) {
+ return manager_ref_uid_internal(m, &m->uid_refs, uid, clean_ipc);
+}
+
+int manager_ref_gid(Manager *m, gid_t gid, bool clean_ipc) {
+ return manager_ref_uid_internal(m, &m->gid_refs, (uid_t) gid, clean_ipc);
+}
+
+static void manager_vacuum_uid_refs_internal(
+ Manager *m,
+ Hashmap **uid_refs,
+ int (*_clean_ipc)(uid_t uid)) {
+
+ Iterator i;
+ void *p, *k;
+
+ assert(m);
+ assert(uid_refs);
+ assert(_clean_ipc);
+
+ HASHMAP_FOREACH_KEY(p, k, *uid_refs, i) {
+ uint32_t c, n;
+ uid_t uid;
+
+ uid = PTR_TO_UID(k);
+ c = PTR_TO_UINT32(p);
+
+ n = c & ~DESTROY_IPC_FLAG;
+ if (n > 0)
+ continue;
+
+ if (c & DESTROY_IPC_FLAG) {
+ log_debug("Found unreferenced %s " UID_FMT " after reload/reexec. Cleaning up.",
+ _clean_ipc == clean_ipc_by_uid ? "UID" : "GID",
+ uid);
+ (void) _clean_ipc(uid);
+ }
+
+ assert_se(hashmap_remove(*uid_refs, k) == p);
+ }
+}
+
+void manager_vacuum_uid_refs(Manager *m) {
+ manager_vacuum_uid_refs_internal(m, &m->uid_refs, clean_ipc_by_uid);
+}
+
+void manager_vacuum_gid_refs(Manager *m) {
+ manager_vacuum_uid_refs_internal(m, &m->gid_refs, clean_ipc_by_gid);
+}
+
+static void manager_serialize_uid_refs_internal(
+ Manager *m,
+ FILE *f,
+ Hashmap **uid_refs,
+ const char *field_name) {
+
+ Iterator i;
+ void *p, *k;
+
+ assert(m);
+ assert(f);
+ assert(uid_refs);
+ assert(field_name);
+
+ /* Serialize the UID reference table. Or actually, just the IPC destruction flag of it, as the actual counter
+ * of it is better rebuild after a reload/reexec. */
+
+ HASHMAP_FOREACH_KEY(p, k, *uid_refs, i) {
+ uint32_t c;
+ uid_t uid;
+
+ uid = PTR_TO_UID(k);
+ c = PTR_TO_UINT32(p);
+
+ if (!(c & DESTROY_IPC_FLAG))
+ continue;
+
+ fprintf(f, "%s=" UID_FMT "\n", field_name, uid);
+ }
+}
+
+void manager_serialize_uid_refs(Manager *m, FILE *f) {
+ manager_serialize_uid_refs_internal(m, f, &m->uid_refs, "destroy-ipc-uid");
+}
+
+void manager_serialize_gid_refs(Manager *m, FILE *f) {
+ manager_serialize_uid_refs_internal(m, f, &m->gid_refs, "destroy-ipc-gid");
+}
+
+static void manager_deserialize_uid_refs_one_internal(
+ Manager *m,
+ Hashmap** uid_refs,
+ const char *value) {
+
+ uid_t uid;
+ uint32_t c;
+ int r;
+
+ assert(m);
+ assert(uid_refs);
+ assert(value);
+
+ r = parse_uid(value, &uid);
+ if (r < 0 || uid == 0) {
+ log_debug("Unable to parse UID reference serialization");
+ return;
+ }
+
+ r = hashmap_ensure_allocated(uid_refs, &trivial_hash_ops);
+ if (r < 0) {
+ log_oom();
+ return;
+ }
+
+ c = PTR_TO_UINT32(hashmap_get(*uid_refs, UID_TO_PTR(uid)));
+ if (c & DESTROY_IPC_FLAG)
+ return;
+
+ c |= DESTROY_IPC_FLAG;
+
+ r = hashmap_replace(*uid_refs, UID_TO_PTR(uid), UINT32_TO_PTR(c));
+ if (r < 0) {
+ log_debug("Failed to add UID reference entry");
+ return;
+ }
+}
+
+void manager_deserialize_uid_refs_one(Manager *m, const char *value) {
+ manager_deserialize_uid_refs_one_internal(m, &m->uid_refs, value);
+}
+
+void manager_deserialize_gid_refs_one(Manager *m, const char *value) {
+ manager_deserialize_uid_refs_one_internal(m, &m->gid_refs, value);
+}
+
+int manager_dispatch_user_lookup_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
+ struct buffer {
+ uid_t uid;
+ gid_t gid;
+ char unit_name[UNIT_NAME_MAX+1];
+ } _packed_ buffer;
+
+ Manager *m = userdata;
+ ssize_t l;
+ size_t n;
+ Unit *u;
+
+ assert_se(source);
+ assert_se(m);
+
+ /* Invoked whenever a child process succeeded resolving its user/group to use and sent us the resulting UID/GID
+ * in a datagram. We parse the datagram here and pass it off to the unit, so that it can add a reference to the
+ * UID/GID so that it can destroy the UID/GID's IPC objects when the reference counter drops to 0. */
+
+ l = recv(fd, &buffer, sizeof(buffer), MSG_DONTWAIT);
+ if (l < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ return 0;
+
+ return log_error_errno(errno, "Failed to read from user lookup fd: %m");
+ }
+
+ if ((size_t) l <= offsetof(struct buffer, unit_name)) {
+ log_warning("Received too short user lookup message, ignoring.");
+ return 0;
+ }
+
+ if ((size_t) l > offsetof(struct buffer, unit_name) + UNIT_NAME_MAX) {
+ log_warning("Received too long user lookup message, ignoring.");
+ return 0;
+ }
+
+ if (!uid_is_valid(buffer.uid) && !gid_is_valid(buffer.gid)) {
+ log_warning("Got user lookup message with invalid UID/GID pair, ignoring.");
+ return 0;
+ }
+
+ n = (size_t) l - offsetof(struct buffer, unit_name);
+ if (memchr(buffer.unit_name, 0, n)) {
+ log_warning("Received lookup message with embedded NUL character, ignoring.");
+ return 0;
+ }
+
+ buffer.unit_name[n] = 0;
+ u = manager_get_unit(m, buffer.unit_name);
+ if (!u) {
+ log_debug("Got user lookup message but unit doesn't exist, ignoring.");
+ return 0;
+ }
+
+ log_unit_debug(u, "User lookup succeeded: uid=" UID_FMT " gid=" GID_FMT, buffer.uid, buffer.gid);
+
+ unit_notify_user_lookup(u, buffer.uid, buffer.gid);
+ return 0;
+}
+
static const char *const manager_state_table[_MANAGER_STATE_MAX] = {
[MANAGER_INITIALIZING] = "initializing",
[MANAGER_STARTING] = "starting",
diff --git a/src/core/manager.h b/src/core/manager.h
index c681d5dc46..b9f2e4b5a1 100644
--- a/src/core/manager.h
+++ b/src/core/manager.h
@@ -143,6 +143,9 @@ struct Manager {
sd_event_source *jobs_in_progress_event_source;
+ int user_lookup_fds[2];
+ sd_event_source *user_lookup_event_source;
+
UnitFileScope unit_file_scope;
LookupPaths lookup_paths;
Set *unit_path_cache;
@@ -234,7 +237,6 @@ struct Manager {
bool dispatching_dbus_queue:1;
bool taint_usr:1;
-
bool test_run:1;
/* If non-zero, exit with the following value when the systemd
@@ -301,6 +303,10 @@ struct Manager {
/* Dynamic users/groups, indexed by their name */
Hashmap *dynamic_users;
+ /* Keep track of all UIDs and GIDs any of our services currently use. This is useful for the RemoveIPC= logic. */
+ Hashmap *uid_refs;
+ Hashmap *gid_refs;
+
/* When the user hits C-A-D more than 7 times per 2s, reboot immediately... */
RateLimit ctrl_alt_del_ratelimit;
@@ -378,5 +384,20 @@ ManagerState manager_state(Manager *m);
int manager_update_failed_units(Manager *m, Unit *u, bool failed);
+void manager_unref_uid(Manager *m, uid_t uid, bool destroy_now);
+int manager_ref_uid(Manager *m, uid_t uid, bool clean_ipc);
+
+void manager_unref_gid(Manager *m, gid_t gid, bool destroy_now);
+int manager_ref_gid(Manager *m, gid_t gid, bool destroy_now);
+
+void manager_vacuum_uid_refs(Manager *m);
+void manager_vacuum_gid_refs(Manager *m);
+
+void manager_serialize_uid_refs(Manager *m, FILE *f);
+void manager_deserialize_uid_refs_one(Manager *m, const char *value);
+
+void manager_serialize_gid_refs(Manager *m, FILE *f);
+void manager_deserialize_gid_refs_one(Manager *m, const char *value);
+
const char *manager_state_to_string(ManagerState m) _const_;
ManagerState manager_state_from_string(const char *s) _pure_;
diff --git a/src/core/mount.c b/src/core/mount.c
index f3ccf6d48a..f2ac8d171f 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -769,6 +769,8 @@ static void mount_enter_dead(Mount *m, MountResult f) {
exec_context_destroy_runtime_directory(&m->exec_context, manager_get_runtime_prefix(UNIT(m)->manager));
+ unit_unref_uid_gid(UNIT(m), true);
+
dynamic_creds_destroy(&m->dynamic_creds);
}
diff --git a/src/core/service.c b/src/core/service.c
index ef5f506fa1..969c62bd83 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -1471,6 +1471,9 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
/* Also, remove the runtime directory */
exec_context_destroy_runtime_directory(&s->exec_context, manager_get_runtime_prefix(UNIT(s)->manager));
+ /* Get rid of the IPC bits of the user */
+ unit_unref_uid_gid(UNIT(s), true);
+
/* Release the user, and destroy it if we are the only remaining owner */
dynamic_creds_destroy(&s->dynamic_creds);
diff --git a/src/core/socket.c b/src/core/socket.c
index 50872e8366..70d55dd9ed 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -1905,6 +1905,8 @@ static void socket_enter_dead(Socket *s, SocketResult f) {
exec_context_destroy_runtime_directory(&s->exec_context, manager_get_runtime_prefix(UNIT(s)->manager));
+ unit_unref_uid_gid(UNIT(s), true);
+
dynamic_creds_destroy(&s->dynamic_creds);
}
diff --git a/src/core/swap.c b/src/core/swap.c
index 2c802da3b5..fb222b6858 100644
--- a/src/core/swap.c
+++ b/src/core/swap.c
@@ -683,6 +683,8 @@ static void swap_enter_dead(Swap *s, SwapResult f) {
exec_context_destroy_runtime_directory(&s->exec_context, manager_get_runtime_prefix(UNIT(s)->manager));
+ unit_unref_uid_gid(UNIT(s), true);
+
dynamic_creds_destroy(&s->dynamic_creds);
}
diff --git a/src/core/unit.c b/src/core/unit.c
index d690cf89de..c58c501bc3 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -100,7 +100,8 @@ Unit *unit_new(Manager *m, size_t size) {
u->on_failure_job_mode = JOB_REPLACE;
u->cgroup_inotify_wd = -1;
u->job_timeout = USEC_INFINITY;
- u->sigchldgen = 0;
+ u->ref_uid = UID_INVALID;
+ u->ref_gid = GID_INVALID;
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);
@@ -550,6 +551,8 @@ void unit_free(Unit *u) {
unit_release_cgroup(u);
+ unit_unref_uid_gid(u, false);
+
(void) manager_update_failed_units(u->manager, u, false);
set_remove(u->manager->startup_units, u);
@@ -2614,6 +2617,11 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
unit_serialize_item(u, f, "cgroup", u->cgroup_path);
unit_serialize_item(u, f, "cgroup-realized", yes_no(u->cgroup_realized));
+ if (uid_is_valid(u->ref_uid))
+ unit_serialize_item_format(u, f, "ref-uid", UID_FMT, u->ref_uid);
+ if (gid_is_valid(u->ref_gid))
+ unit_serialize_item_format(u, f, "ref-gid", GID_FMT, u->ref_gid);
+
if (serialize_jobs) {
if (u->job) {
fprintf(f, "job\n");
@@ -2851,6 +2859,28 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
u->cgroup_realized = b;
continue;
+
+ } else if (streq(l, "ref-uid")) {
+ uid_t uid;
+
+ r = parse_uid(v, &uid);
+ if (r < 0)
+ log_unit_debug(u, "Failed to parse referenced UID %s, ignoring.", v);
+ else
+ unit_ref_uid_gid(u, uid, GID_INVALID);
+
+ continue;
+
+ } else if (streq(l, "ref-gid")) {
+ gid_t gid;
+
+ r = parse_gid(v, &gid);
+ if (r < 0)
+ log_unit_debug(u, "Failed to parse referenced GID %s, ignoring.", v);
+ else
+ unit_ref_uid_gid(u, UID_INVALID, gid);
+
+ continue;
}
if (unit_can_serialize(u)) {
@@ -3310,6 +3340,7 @@ int unit_patch_contexts(Unit *u) {
}
ec->private_tmp = true;
+ ec->remove_ipc = true;
}
}
@@ -3932,3 +3963,144 @@ pid_t unit_main_pid(Unit *u) {
return 0;
}
+
+static void unit_unref_uid_internal(
+ Unit *u,
+ uid_t *ref_uid,
+ bool destroy_now,
+ void (*_manager_unref_uid)(Manager *m, uid_t uid, bool destroy_now)) {
+
+ assert(u);
+ assert(ref_uid);
+ assert(_manager_unref_uid);
+
+ /* Generic implementation of both unit_unref_uid() and unit_unref_gid(), under the assumption that uid_t and
+ * gid_t are actually the same time, with the same validity rules.
+ *
+ * Drops a reference to UID/GID from a unit. */
+
+ assert_cc(sizeof(uid_t) == sizeof(gid_t));
+ assert_cc(UID_INVALID == (uid_t) GID_INVALID);
+
+ if (!uid_is_valid(*ref_uid))
+ return;
+
+ _manager_unref_uid(u->manager, *ref_uid, destroy_now);
+ *ref_uid = UID_INVALID;
+}
+
+void unit_unref_uid(Unit *u, bool destroy_now) {
+ unit_unref_uid_internal(u, &u->ref_uid, destroy_now, manager_unref_uid);
+}
+
+void unit_unref_gid(Unit *u, bool destroy_now) {
+ unit_unref_uid_internal(u, (uid_t*) &u->ref_gid, destroy_now, manager_unref_gid);
+}
+
+static int unit_ref_uid_internal(
+ Unit *u,
+ uid_t *ref_uid,
+ uid_t uid,
+ bool clean_ipc,
+ int (*_manager_ref_uid)(Manager *m, uid_t uid, bool clean_ipc)) {
+
+ int r;
+
+ assert(u);
+ assert(ref_uid);
+ assert(uid_is_valid(uid));
+ assert(_manager_ref_uid);
+
+ /* Generic implementation of both unit_ref_uid() and unit_ref_guid(), under the assumption that uid_t and gid_t
+ * are actually the same type, and have the same validity rules.
+ *
+ * Adds a reference on a specific UID/GID to this unit. Each unit referencing the same UID/GID maintains a
+ * reference so that we can destroy the UID/GID's IPC resources as soon as this is requested and the counter
+ * drops to zero. */
+
+ assert_cc(sizeof(uid_t) == sizeof(gid_t));
+ assert_cc(UID_INVALID == (uid_t) GID_INVALID);
+
+ if (*ref_uid == uid)
+ return 0;
+
+ if (uid_is_valid(*ref_uid)) /* Already set? */
+ return -EBUSY;
+
+ r = _manager_ref_uid(u->manager, uid, clean_ipc);
+ if (r < 0)
+ return r;
+
+ *ref_uid = uid;
+ return 1;
+}
+
+int unit_ref_uid(Unit *u, uid_t uid, bool clean_ipc) {
+ return unit_ref_uid_internal(u, &u->ref_uid, uid, clean_ipc, manager_ref_uid);
+}
+
+int unit_ref_gid(Unit *u, gid_t gid, bool clean_ipc) {
+ return unit_ref_uid_internal(u, (uid_t*) &u->ref_gid, (uid_t) gid, clean_ipc, manager_ref_gid);
+}
+
+static int unit_ref_uid_gid_internal(Unit *u, uid_t uid, gid_t gid, bool clean_ipc) {
+ int r = 0, q = 0;
+
+ assert(u);
+
+ /* Reference both a UID and a GID in one go. Either references both, or neither. */
+
+ if (uid_is_valid(uid)) {
+ r = unit_ref_uid(u, uid, clean_ipc);
+ if (r < 0)
+ return r;
+ }
+
+ if (gid_is_valid(gid)) {
+ q = unit_ref_gid(u, gid, clean_ipc);
+ if (q < 0) {
+ if (r > 0)
+ unit_unref_uid(u, false);
+
+ return q;
+ }
+ }
+
+ return r > 0 || q > 0;
+}
+
+int unit_ref_uid_gid(Unit *u, uid_t uid, gid_t gid) {
+ ExecContext *c;
+ int r;
+
+ assert(u);
+
+ c = unit_get_exec_context(u);
+
+ r = unit_ref_uid_gid_internal(u, uid, gid, c ? c->remove_ipc : false);
+ if (r < 0)
+ return log_unit_warning_errno(u, r, "Couldn't add UID/GID reference to unit, proceeding without: %m");
+
+ return r;
+}
+
+void unit_unref_uid_gid(Unit *u, bool destroy_now) {
+ assert(u);
+
+ unit_unref_uid(u, destroy_now);
+ unit_unref_gid(u, destroy_now);
+}
+
+void unit_notify_user_lookup(Unit *u, uid_t uid, gid_t gid) {
+ int r;
+
+ assert(u);
+
+ /* This is invoked whenever one of the forked off processes let's us know the UID/GID its user name/group names
+ * resolved to. We keep track of which UID/GID is currently assigned in order to be able to destroy its IPC
+ * objects when no service references the UID/GID anymore. */
+
+ r = unit_ref_uid_gid(u, uid, gid);
+ if (r > 0)
+ bus_unit_send_change_signal(u);
+}
diff --git a/src/core/unit.h b/src/core/unit.h
index a6c69938dd..53875653d7 100644
--- a/src/core/unit.h
+++ b/src/core/unit.h
@@ -180,6 +180,10 @@ struct Unit {
/* Make sure we never enter endless loops with the check unneeded logic, or the BindsTo= logic */
RateLimit auto_stop_ratelimit;
+ /* Reference to a specific UID/GID */
+ uid_t ref_uid;
+ gid_t ref_gid;
+
/* Cached unit file state and preset */
UnitFileState unit_file_state;
int unit_file_preset;
@@ -195,8 +199,6 @@ struct Unit {
CGroupMask cgroup_members_mask;
int cgroup_inotify_wd;
- uint32_t cgroup_netclass_id;
-
/* How to start OnFailure units */
JobMode on_failure_job_mode;
@@ -373,8 +375,7 @@ struct UnitVTable {
/* Called whenever a process of this unit sends us a message */
void (*notify_message)(Unit *u, pid_t pid, char **tags, FDSet *fds);
- /* Called whenever a name this Unit registered for comes or
- * goes away. */
+ /* Called whenever a name this Unit registered for comes or goes away. */
void (*bus_name_owner_change)(Unit *u, const char *name, const char *old_owner, const char *new_owner);
/* Called for each property that is being set */
@@ -623,6 +624,17 @@ int unit_fail_if_symlink(Unit *u, const char* where);
int unit_start_limit_test(Unit *u);
+void unit_unref_uid(Unit *u, bool destroy_now);
+int unit_ref_uid(Unit *u, uid_t uid, bool clean_ipc);
+
+void unit_unref_gid(Unit *u, bool destroy_now);
+int unit_ref_gid(Unit *u, gid_t gid, bool clean_ipc);
+
+int unit_ref_uid_gid(Unit *u, uid_t uid, gid_t gid);
+void unit_unref_uid_gid(Unit *u, bool destroy_now);
+
+void unit_notify_user_lookup(Unit *u, uid_t uid, gid_t gid);
+
/* Macros which append UNIT= or USER_UNIT= to the message */
#define log_unit_full(unit, level, error, ...) \
diff --git a/src/hostname/hostnamectl.c b/src/hostname/hostnamectl.c
index c16a324232..4795324667 100644
--- a/src/hostname/hostnamectl.c
+++ b/src/hostname/hostnamectl.c
@@ -251,7 +251,7 @@ static int set_simple_string(sd_bus *bus, const char *method, const char *value)
static int set_hostname(sd_bus *bus, char **args, unsigned n) {
_cleanup_free_ char *h = NULL;
- char *hostname = args[1];
+ const char *hostname = args[1];
int r;
assert(args);
@@ -263,27 +263,29 @@ static int set_hostname(sd_bus *bus, char **args, unsigned n) {
if (arg_pretty) {
const char *p;
- /* If the passed hostname is already valid, then
- * assume the user doesn't know anything about pretty
- * hostnames, so let's unset the pretty hostname, and
- * just set the passed hostname as static/dynamic
+ /* If the passed hostname is already valid, then assume the user doesn't know anything about pretty
+ * hostnames, so let's unset the pretty hostname, and just set the passed hostname as static/dynamic
* hostname. */
-
- if (arg_static && hostname_is_valid(hostname, true)) {
- p = "";
- /* maybe get rid of trailing dot */
- hostname = hostname_cleanup(hostname);
- } else {
- p = h = strdup(hostname);
- if (!p)
- return log_oom();
-
- hostname_cleanup(hostname);
- }
+ if (arg_static && hostname_is_valid(hostname, true))
+ p = ""; /* No pretty hostname (as it is redundant), just a static one */
+ else
+ p = hostname; /* Use the passed name as pretty hostname */
r = set_simple_string(bus, "SetPrettyHostname", p);
if (r < 0)
return r;
+
+ /* Now that we set the pretty hostname, let's clean up the parameter and use that as static
+ * hostname. If the hostname was already valid as static hostname, this will only chop off the trailing
+ * dot if there is one. If it was not valid, then it will be made fully valid by truncating, dropping
+ * multiple dots, and and dropping weird chars. Note that we clean the name up only if we also are
+ * supposed to set the pretty name. If the pretty name is not being set we assume the user knows what
+ * he does and pass the name as-is. */
+ h = strdup(hostname);
+ if (!h)
+ return log_oom();
+
+ hostname = hostname_cleanup(h); /* Use the cleaned up name as static hostname */
}
if (arg_static) {
diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c
index 6f841efb69..381e219390 100644
--- a/src/journal/journalctl.c
+++ b/src/journal/journalctl.c
@@ -310,7 +310,7 @@ static void help(void) {
" -m --merge Show entries from all available journals\n"
" -D --directory=PATH Show journal files from directory\n"
" --file=PATH Show journal file\n"
- " --root=ROOT Operate on catalog files below a root directory\n"
+ " --root=ROOT Operate on files below a root directory\n"
#ifdef HAVE_GCRYPT
" --interval=TIME Time interval for changing the FSS sealing key\n"
" --verify-key=KEY Specify FSS verification key\n"
@@ -848,8 +848,8 @@ static int parse_argv(int argc, char *argv[]) {
if (arg_follow && !arg_no_tail && !arg_since && arg_lines == ARG_LINES_DEFAULT)
arg_lines = 10;
- if (!!arg_directory + !!arg_file + !!arg_machine > 1) {
- log_error("Please specify either -D/--directory= or --file= or -M/--machine=, not more than one.");
+ if (!!arg_directory + !!arg_file + !!arg_machine + !!arg_root > 1) {
+ log_error("Please specify at most one of -D/--directory=, --file=, -M/--machine=, --root.");
return -EINVAL;
}
@@ -1267,7 +1267,7 @@ static int add_boot(sd_journal *j) {
* We can do this only when we logs are coming from the current machine,
* so take the slow path if log location is specified. */
if (arg_boot_offset == 0 && sd_id128_is_null(arg_boot_id) &&
- !arg_directory && !arg_file)
+ !arg_directory && !arg_file && !arg_root)
return add_match_this_boot(j, arg_machine);
@@ -2161,6 +2161,8 @@ int main(int argc, char *argv[]) {
if (arg_directory)
r = sd_journal_open_directory(&j, arg_directory, arg_journal_type);
+ else if (arg_root)
+ r = sd_journal_open_directory(&j, arg_root, arg_journal_type | SD_JOURNAL_OS_ROOT);
else if (arg_file_stdin) {
int ifd = STDIN_FILENO;
r = sd_journal_open_files_fd(&j, &ifd, 1, 0);
diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c
index 587c343b31..2a043a95b1 100644
--- a/src/journal/journald-server.c
+++ b/src/journal/journald-server.c
@@ -267,6 +267,82 @@ static int open_journal(
return r;
}
+static int system_journal_open(Server *s, bool flush_requested) {
+ 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)) {
+
+ /* If in auto mode: first try to create the machine
+ * path, but not the prefix.
+ *
+ * If in persistent mode: create /var/log/journal and
+ * the machine path */
+
+ if (s->storage == STORAGE_PERSISTENT)
+ (void) mkdir_p("/var/log/journal/", 0755);
+
+ fn = strjoina("/var/log/journal/", SERVER_MACHINE_ID(s));
+ (void) mkdir(fn, 0755);
+
+ fn = strjoina(fn, "/system.journal");
+ r = open_journal(s, true, fn, O_RDWR|O_CREAT, s->seal, &s->system_metrics, &s->system_journal);
+ if (r >= 0) {
+ server_add_acls(s->system_journal, 0);
+ (void) determine_space_for(s, &s->system_metrics, "/var/log/journal/", "System journal", true, true, NULL, NULL);
+ } else if (r < 0) {
+ if (r != -ENOENT && r != -EROFS)
+ log_warning_errno(r, "Failed to open system journal: %m");
+
+ r = 0;
+ }
+ }
+
+ if (!s->runtime_journal &&
+ (s->storage != STORAGE_NONE)) {
+
+ fn = strjoina("/run/log/journal/", SERVER_MACHINE_ID(s), "/system.journal");
+
+ if (s->system_journal) {
+
+ /* Try to open the runtime journal, but only
+ * if it already exists, so that we can flush
+ * it into the system journal */
+
+ r = open_journal(s, false, fn, O_RDWR, false, &s->runtime_metrics, &s->runtime_journal);
+ if (r < 0) {
+ if (r != -ENOENT)
+ log_warning_errno(r, "Failed to open runtime journal: %m");
+
+ r = 0;
+ }
+
+ } else {
+
+ /* OK, we really need the runtime journal, so create
+ * it if necessary. */
+
+ (void) mkdir("/run/log", 0755);
+ (void) mkdir("/run/log/journal", 0755);
+ (void) mkdir_parents(fn, 0750);
+
+ r = open_journal(s, true, fn, O_RDWR|O_CREAT, false, &s->runtime_metrics, &s->runtime_journal);
+ if (r < 0)
+ return log_error_errno(r, "Failed to open runtime journal: %m");
+ }
+
+ if (s->runtime_journal) {
+ server_add_acls(s->runtime_journal, 0);
+ (void) determine_space_for(s, &s->runtime_metrics, "/run/log/journal/", "Runtime journal", true, true, NULL, NULL);
+ }
+ }
+
+ return r;
+}
+
static JournalFile* find_journal(Server *s, uid_t uid) {
_cleanup_free_ char *p = NULL;
int r;
@@ -275,6 +351,17 @@ static JournalFile* find_journal(Server *s, uid_t uid) {
assert(s);
+ /* A rotate that fails to create the new journal (ENOSPC) leaves the
+ * rotated journal as NULL. Unless we revisit opening, even after
+ * space is made available we'll continue to return NULL indefinitely.
+ *
+ * system_journal_open() is a noop if the journals are already open, so
+ * we can just call it here to recover from failed rotates (or anything
+ * else that's left the journals as NULL).
+ *
+ * Fixes https://github.com/systemd/systemd/issues/3968 */
+ (void) system_journal_open(s, false);
+
/* We split up user logs only on /var, not on /run. If the
* runtime file is open, we write to it exclusively, in order
* to guarantee proper order as soon as we flush /run to
@@ -283,7 +370,7 @@ static JournalFile* find_journal(Server *s, uid_t uid) {
if (s->runtime_journal)
return s->runtime_journal;
- if (uid <= SYSTEM_UID_MAX)
+ if (uid <= SYSTEM_UID_MAX || uid_is_dynamic(uid))
return s->system_journal;
r = sd_id128_get_machine(&machine);
@@ -979,83 +1066,6 @@ finish:
dispatch_message_real(s, iovec, n, m, ucred, tv, label, label_len, unit_id, priority, object_pid);
}
-
-static int system_journal_open(Server *s, bool flush_requested) {
- 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)) {
-
- /* If in auto mode: first try to create the machine
- * path, but not the prefix.
- *
- * If in persistent mode: create /var/log/journal and
- * the machine path */
-
- if (s->storage == STORAGE_PERSISTENT)
- (void) mkdir_p("/var/log/journal/", 0755);
-
- fn = strjoina("/var/log/journal/", SERVER_MACHINE_ID(s));
- (void) mkdir(fn, 0755);
-
- fn = strjoina(fn, "/system.journal");
- r = open_journal(s, true, fn, O_RDWR|O_CREAT, s->seal, &s->system_metrics, &s->system_journal);
- if (r >= 0) {
- server_add_acls(s->system_journal, 0);
- (void) determine_space_for(s, &s->system_metrics, "/var/log/journal/", "System journal", true, true, NULL, NULL);
- } else if (r < 0) {
- if (r != -ENOENT && r != -EROFS)
- log_warning_errno(r, "Failed to open system journal: %m");
-
- r = 0;
- }
- }
-
- if (!s->runtime_journal &&
- (s->storage != STORAGE_NONE)) {
-
- fn = strjoina("/run/log/journal/", SERVER_MACHINE_ID(s), "/system.journal");
-
- if (s->system_journal) {
-
- /* Try to open the runtime journal, but only
- * if it already exists, so that we can flush
- * it into the system journal */
-
- r = open_journal(s, false, fn, O_RDWR, false, &s->runtime_metrics, &s->runtime_journal);
- if (r < 0) {
- if (r != -ENOENT)
- log_warning_errno(r, "Failed to open runtime journal: %m");
-
- r = 0;
- }
-
- } else {
-
- /* OK, we really need the runtime journal, so create
- * it if necessary. */
-
- (void) mkdir("/run/log", 0755);
- (void) mkdir("/run/log/journal", 0755);
- (void) mkdir_parents(fn, 0750);
-
- r = open_journal(s, true, fn, O_RDWR|O_CREAT, false, &s->runtime_metrics, &s->runtime_journal);
- if (r < 0)
- return log_error_errno(r, "Failed to open runtime journal: %m");
- }
-
- if (s->runtime_journal) {
- server_add_acls(s->runtime_journal, 0);
- (void) determine_space_for(s, &s->runtime_metrics, "/run/log/journal/", "Runtime journal", true, true, NULL, NULL);
- }
- }
-
- return r;
-}
-
int server_flush_to_var(Server *s) {
sd_id128_t machine;
sd_journal *j = NULL;
diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c
index 2a3824d0e8..98c8a47afe 100644
--- a/src/journal/sd-journal.c
+++ b/src/journal/sd-journal.c
@@ -1719,9 +1719,16 @@ static sd_journal *journal_new(int flags, const char *path) {
j->data_threshold = DEFAULT_DATA_THRESHOLD;
if (path) {
- j->path = strdup(path);
- if (!j->path)
+ char *t;
+
+ t = strdup(path);
+ if (!t)
goto fail;
+
+ if (flags & SD_JOURNAL_OS_ROOT)
+ j->prefix = t;
+ else
+ j->path = t;
}
j->files = ordered_hashmap_new(&string_hash_ops);
@@ -1737,12 +1744,17 @@ fail:
return NULL;
}
+#define OPEN_ALLOWED_FLAGS \
+ (SD_JOURNAL_LOCAL_ONLY | \
+ SD_JOURNAL_RUNTIME_ONLY | \
+ SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER)
+
_public_ int sd_journal_open(sd_journal **ret, int flags) {
sd_journal *j;
int r;
assert_return(ret, -EINVAL);
- assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_RUNTIME_ONLY|SD_JOURNAL_SYSTEM|SD_JOURNAL_CURRENT_USER)) == 0, -EINVAL);
+ assert_return((flags & ~OPEN_ALLOWED_FLAGS) == 0, -EINVAL);
j = journal_new(flags, NULL);
if (!j)
@@ -1761,6 +1773,9 @@ fail:
return r;
}
+#define OPEN_CONTAINER_ALLOWED_FLAGS \
+ (SD_JOURNAL_LOCAL_ONLY | SD_JOURNAL_SYSTEM)
+
_public_ int sd_journal_open_container(sd_journal **ret, const char *machine, int flags) {
_cleanup_free_ char *root = NULL, *class = NULL;
sd_journal *j;
@@ -1772,7 +1787,7 @@ _public_ int sd_journal_open_container(sd_journal **ret, const char *machine, in
assert_return(machine, -EINVAL);
assert_return(ret, -EINVAL);
- assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM)) == 0, -EINVAL);
+ assert_return((flags & ~OPEN_CONTAINER_ALLOWED_FLAGS) == 0, -EINVAL);
assert_return(machine_name_is_valid(machine), -EINVAL);
p = strjoina("/run/systemd/machines/", machine);
@@ -1787,13 +1802,10 @@ _public_ int sd_journal_open_container(sd_journal **ret, const char *machine, in
if (!streq_ptr(class, "container"))
return -EIO;
- j = journal_new(flags, NULL);
+ j = journal_new(flags, root);
if (!j)
return -ENOMEM;
- j->prefix = root;
- root = NULL;
-
r = add_search_paths(j);
if (r < 0)
goto fail;
@@ -1806,13 +1818,17 @@ fail:
return r;
}
+#define OPEN_DIRECTORY_ALLOWED_FLAGS \
+ (SD_JOURNAL_OS_ROOT | \
+ SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER )
+
_public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
sd_journal *j;
int r;
assert_return(ret, -EINVAL);
assert_return(path, -EINVAL);
- assert_return((flags & ~SD_JOURNAL_OS_ROOT) == 0, -EINVAL);
+ assert_return((flags & ~OPEN_DIRECTORY_ALLOWED_FLAGS) == 0, -EINVAL);
j = journal_new(flags, path);
if (!j)
@@ -1861,6 +1877,10 @@ fail:
return r;
}
+#define OPEN_DIRECTORY_FD_ALLOWED_FLAGS \
+ (SD_JOURNAL_OS_ROOT | \
+ SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER )
+
_public_ int sd_journal_open_directory_fd(sd_journal **ret, int fd, int flags) {
sd_journal *j;
struct stat st;
@@ -1868,7 +1888,7 @@ _public_ int sd_journal_open_directory_fd(sd_journal **ret, int fd, int flags) {
assert_return(ret, -EINVAL);
assert_return(fd >= 0, -EBADF);
- assert_return((flags & ~SD_JOURNAL_OS_ROOT) == 0, -EINVAL);
+ assert_return((flags & ~OPEN_DIRECTORY_FD_ALLOWED_FLAGS) == 0, -EINVAL);
if (fstat(fd, &st) < 0)
return -errno;
diff --git a/src/libudev/libudev-device.c b/src/libudev/libudev-device.c
index 995bf56586..c5f36725dc 100644
--- a/src/libudev/libudev-device.c
+++ b/src/libudev/libudev-device.c
@@ -495,7 +495,7 @@ _public_ struct udev_device *udev_device_get_parent_with_subsystem_devtype(struc
return NULL;
}
- /* then walk the chain of udev_device parents until the correspanding
+ /* then walk the chain of udev_device parents until the corresponding
one is found */
while ((udev_device = udev_device_get_parent(udev_device))) {
if (udev_device->device == parent)
diff --git a/src/login/logind-action.c b/src/login/logind-action.c
index 8ef48dbaa1..a950409254 100644
--- a/src/login/logind-action.c
+++ b/src/login/logind-action.c
@@ -85,7 +85,7 @@ int manager_handle_action(
}
/* If the key handling is inhibited, don't do anything */
- if (!ignore_inhibited && inhibit_key > 0) {
+ if (inhibit_key > 0) {
if (manager_is_inhibited(m, inhibit_key, INHIBIT_BLOCK, NULL, true, false, 0, NULL)) {
log_debug("Refusing operation, %s is inhibited.", inhibit_what_to_string(inhibit_key));
return 0;
diff --git a/src/login/logind-user.c b/src/login/logind-user.c
index 63363035e7..e0e73b034d 100644
--- a/src/login/logind-user.c
+++ b/src/login/logind-user.c
@@ -26,6 +26,7 @@
#include "bus-common-errors.h"
#include "bus-error.h"
#include "bus-util.h"
+#include "cgroup-util.h"
#include "clean-ipc.h"
#include "conf-parser.h"
#include "escape.h"
@@ -612,9 +613,14 @@ int user_finalize(User *u) {
if (k < 0)
r = k;
- /* Clean SysV + POSIX IPC objects */
- if (u->manager->remove_ipc) {
- k = clean_ipc(u->uid);
+ /* Clean SysV + POSIX IPC objects, but only if this is not a system user. Background: in many setups cronjobs
+ * are run in full PAM and thus logind sessions, even if the code run doesn't belong to actual users but to
+ * system components. Since enable RemoveIPC= globally for all users, we need to be a bit careful with such
+ * cases, as we shouldn't accidentally remove a system service's IPC objects while it is running, just because
+ * a cronjob running as the same user just finished. Hence: exclude system users generally from IPC clean-up,
+ * and do it only for normal users. */
+ if (u->manager->remove_ipc && u->uid > SYSTEM_UID_MAX) {
+ k = clean_ipc_by_uid(u->uid);
if (k < 0)
r = k;
}
@@ -891,7 +897,17 @@ int config_parse_user_tasks_max(
assert(rvalue);
assert(data);
- /* First, try to parse as percentage */
+ if (isempty(rvalue)) {
+ *m = system_tasks_max_scale(DEFAULT_USER_TASKS_MAX_PERCENTAGE, 100U);
+ return 0;
+ }
+
+ if (streq(rvalue, "infinity")) {
+ *m = CGROUP_LIMIT_MAX;
+ return 0;
+ }
+
+ /* Try to parse as percentage */
r = parse_percent(rvalue);
if (r >= 0)
k = system_tasks_max_scale(r, 100U);
diff --git a/src/login/logind.c b/src/login/logind.c
index 5ce36d28c7..bbbf4aef57 100644
--- a/src/login/logind.c
+++ b/src/login/logind.c
@@ -38,6 +38,7 @@
#include "signal-util.h"
#include "strv.h"
#include "udev-util.h"
+#include "cgroup-util.h"
static void manager_free(Manager *m);
@@ -62,7 +63,7 @@ static void manager_reset_config(Manager *m) {
m->idle_action = HANDLE_IGNORE;
m->runtime_dir_size = physical_memory_scale(10U, 100U); /* 10% */
- m->user_tasks_max = system_tasks_max_scale(33U, 100U); /* 33% */
+ m->user_tasks_max = system_tasks_max_scale(DEFAULT_USER_TASKS_MAX_PERCENTAGE, 100U); /* 33% */
m->sessions_max = 8192;
m->inhibitors_max = 8192;
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..9076ba3b0b
--- /dev/null
+++ b/src/mount/mount-tool.c
@@ -0,0 +1,1112 @@
+/***
+ 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;
+
+ 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 NULL;
+
+ 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-route.c b/src/network/networkd-route.c
index cedaf47cf8..b197a5da6b 100644
--- a/src/network/networkd-route.c
+++ b/src/network/networkd-route.c
@@ -41,7 +41,7 @@ int route_new(Route **ret) {
route->family = AF_UNSPEC;
route->scope = RT_SCOPE_UNIVERSE;
route->protocol = RTPROT_UNSPEC;
- route->table = RT_TABLE_DEFAULT;
+ route->table = RT_TABLE_MAIN;
route->lifetime = USEC_INFINITY;
*ret = route;
@@ -322,7 +322,8 @@ int route_add(
} else
return r;
- *ret = route;
+ if (ret)
+ *ret = route;
return 0;
}
@@ -440,20 +441,14 @@ static int route_expire_callback(sd_netlink *rtnl, sd_netlink_message *m, void *
assert(m);
assert(link);
assert(link->ifname);
- assert(link->link_messages > 0);
if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
return 1;
- link->link_messages--;
-
r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST)
log_link_warning_errno(link, r, "could not remove route: %m");
- if (link->link_messages == 0)
- log_link_debug(link, "route removed");
-
return 1;
}
@@ -466,11 +461,8 @@ int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata) {
r = route_remove(route, route->link, route_expire_callback);
if (r < 0)
log_warning_errno(r, "Could not remove route: %m");
- else {
- /* route may not be exist in kernel. If we fail still remove it */
- route->link->link_messages++;
+ else
route_free(route);
- }
return 1;
}
@@ -557,14 +549,12 @@ int route_configure(
if (r < 0)
return log_error_errno(r, "Could not set flags: %m");
- if (route->table != RT_TABLE_DEFAULT) {
-
+ if (route->table != RT_TABLE_MAIN) {
if (route->table < 256) {
r = sd_rtnl_message_route_set_table(req, route->table);
if (r < 0)
return log_error_errno(r, "Could not set route table: %m");
} else {
-
r = sd_rtnl_message_route_set_table(req, RT_TABLE_UNSPEC);
if (r < 0)
return log_error_errno(r, "Could not set route table: %m");
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/nss-mymachines/nss-mymachines.c b/src/nss-mymachines/nss-mymachines.c
index 8d57b26cbc..895f61c462 100644
--- a/src/nss-mymachines/nss-mymachines.c
+++ b/src/nss-mymachines/nss-mymachines.c
@@ -25,6 +25,7 @@
#include "alloc-util.h"
#include "bus-common-errors.h"
+#include "env-util.h"
#include "hostname-util.h"
#include "in-addr-util.h"
#include "macro.h"
@@ -434,6 +435,12 @@ enum nss_status _nss_mymachines_getpwnam_r(
if (!machine_name_is_valid(machine))
goto not_found;
+ if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0)
+ /* Make sure we can't deadlock if we are invoked by dbus-daemon. This way, it won't be able to resolve
+ * these UIDs, but that should be unproblematic as containers should never be able to connect to a bus
+ * running on the host. */
+ goto not_found;
+
r = sd_bus_open_system(&bus);
if (r < 0)
goto fail;
@@ -514,6 +521,9 @@ enum nss_status _nss_mymachines_getpwuid_r(
if (uid < HOST_UID_LIMIT)
goto not_found;
+ if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0)
+ goto not_found;
+
r = sd_bus_open_system(&bus);
if (r < 0)
goto fail;
@@ -605,6 +615,9 @@ enum nss_status _nss_mymachines_getgrnam_r(
if (!machine_name_is_valid(machine))
goto not_found;
+ if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0)
+ goto not_found;
+
r = sd_bus_open_system(&bus);
if (r < 0)
goto fail;
@@ -682,6 +695,9 @@ enum nss_status _nss_mymachines_getgrgid_r(
if (gid < HOST_GID_LIMIT)
goto not_found;
+ if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0)
+ goto not_found;
+
r = sd_bus_open_system(&bus);
if (r < 0)
goto fail;
diff --git a/src/nss-systemd/nss-systemd.c b/src/nss-systemd/nss-systemd.c
index 7078c0c50c..17d04e958d 100644
--- a/src/nss-systemd/nss-systemd.c
+++ b/src/nss-systemd/nss-systemd.c
@@ -21,11 +21,14 @@
#include "sd-bus.h"
+#include "alloc-util.h"
#include "bus-common-errors.h"
#include "env-util.h"
+#include "fs-util.h"
#include "macro.h"
#include "nss-util.h"
#include "signal-util.h"
+#include "stdio-util.h"
#include "string-util.h"
#include "user-util.h"
#include "util.h"
@@ -75,15 +78,50 @@ static const struct group nobody_group = {
NSS_GETPW_PROTOTYPES(systemd);
NSS_GETGR_PROTOTYPES(systemd);
+static int direct_lookup_name(const char *name, uid_t *ret) {
+ _cleanup_free_ char *s = NULL;
+ const char *path;
+ int r;
+
+ assert(name);
+
+ /* Normally, we go via the bus to resolve names. That has the benefit that it is available from any mount
+ * namespace and subject to proper authentication. However, there's one problem: if our module is called from
+ * dbus-daemon itself we really can't use D-Bus to communicate. In this case, resort to a client-side hack,
+ * and look for the dynamic names directly. This is pretty ugly, but breaks the cyclic dependency. */
+
+ path = strjoina("/run/systemd/dynamic-uid/direct:", name);
+ r = readlink_malloc(path, &s);
+ if (r < 0)
+ return r;
+
+ return parse_uid(s, ret);
+}
+
+static int direct_lookup_uid(uid_t uid, char **ret) {
+ char path[strlen("/run/systemd/dynamic-uid/direct:") + DECIMAL_STR_MAX(uid_t) + 1], *s;
+ int r;
+
+ xsprintf(path, "/run/systemd/dynamic-uid/direct:" UID_FMT, uid);
+
+ r = readlink_malloc(path, &s);
+ if (r < 0)
+ return r;
+ if (!valid_user_group_name(s)) { /* extra safety check */
+ free(s);
+ return -EINVAL;
+ }
+
+ *ret = s;
+ return 0;
+}
+
enum nss_status _nss_systemd_getpwnam_r(
const char *name,
struct passwd *pwd,
char *buffer, size_t buflen,
int *errnop) {
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
- _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
uint32_t translated;
size_t l;
int r;
@@ -114,30 +152,45 @@ enum nss_status _nss_systemd_getpwnam_r(
if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
goto not_found;
- r = sd_bus_open_system(&bus);
- if (r < 0)
- goto fail;
+ if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0) {
- r = sd_bus_call_method(bus,
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "LookupDynamicUserByName",
- &error,
- &reply,
- "s",
- name);
- if (r < 0) {
- if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
+ /* Access the dynamic UID allocation directly if we are called from dbus-daemon, see above. */
+ r = direct_lookup_name(name, (uid_t*) &translated);
+ if (r == -ENOENT)
goto not_found;
-
- goto fail;
+ if (r < 0)
+ goto fail;
+
+ } else {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+
+ r = sd_bus_open_system(&bus);
+ if (r < 0)
+ goto fail;
+
+ r = sd_bus_call_method(bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "LookupDynamicUserByName",
+ &error,
+ &reply,
+ "s",
+ name);
+ if (r < 0) {
+ if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
+ goto not_found;
+
+ goto fail;
+ }
+
+ r = sd_bus_message_read(reply, "u", &translated);
+ if (r < 0)
+ goto fail;
}
- r = sd_bus_message_read(reply, "u", &translated);
- if (r < 0)
- goto fail;
-
l = strlen(name);
if (buflen < l+1) {
*errnop = ENOMEM;
@@ -175,6 +228,7 @@ enum nss_status _nss_systemd_getpwuid_r(
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ _cleanup_free_ char *direct = NULL;
const char *translated;
size_t l;
int r;
@@ -204,30 +258,42 @@ enum nss_status _nss_systemd_getpwuid_r(
if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
goto not_found;
- r = sd_bus_open_system(&bus);
- if (r < 0)
- goto fail;
+ if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0) {
- r = sd_bus_call_method(bus,
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "LookupDynamicUserByUID",
- &error,
- &reply,
- "u",
- (uint32_t) uid);
- if (r < 0) {
- if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
+ r = direct_lookup_uid(uid, &direct);
+ if (r == -ENOENT)
goto not_found;
-
- goto fail;
+ if (r < 0)
+ goto fail;
+
+ translated = direct;
+
+ } else {
+ r = sd_bus_open_system(&bus);
+ if (r < 0)
+ goto fail;
+
+ r = sd_bus_call_method(bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "LookupDynamicUserByUID",
+ &error,
+ &reply,
+ "u",
+ (uint32_t) uid);
+ if (r < 0) {
+ if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
+ goto not_found;
+
+ goto fail;
+ }
+
+ r = sd_bus_message_read(reply, "s", &translated);
+ if (r < 0)
+ goto fail;
}
- r = sd_bus_message_read(reply, "s", &translated);
- if (r < 0)
- goto fail;
-
l = strlen(translated) + 1;
if (buflen < l) {
*errnop = ENOMEM;
@@ -262,9 +328,6 @@ enum nss_status _nss_systemd_getgrnam_r(
char *buffer, size_t buflen,
int *errnop) {
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
- _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
uint32_t translated;
size_t l;
int r;
@@ -294,30 +357,45 @@ enum nss_status _nss_systemd_getgrnam_r(
if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
goto not_found;
- r = sd_bus_open_system(&bus);
- if (r < 0)
- goto fail;
+ if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0) {
- r = sd_bus_call_method(bus,
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "LookupDynamicUserByName",
- &error,
- &reply,
- "s",
- name);
- if (r < 0) {
- if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
+ /* Access the dynamic GID allocation directly if we are called from dbus-daemon, see above. */
+ r = direct_lookup_name(name, (uid_t*) &translated);
+ if (r == -ENOENT)
goto not_found;
-
- goto fail;
+ if (r < 0)
+ goto fail;
+ } else {
+
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+
+ r = sd_bus_open_system(&bus);
+ if (r < 0)
+ goto fail;
+
+ r = sd_bus_call_method(bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "LookupDynamicUserByName",
+ &error,
+ &reply,
+ "s",
+ name);
+ if (r < 0) {
+ if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
+ goto not_found;
+
+ goto fail;
+ }
+
+ r = sd_bus_message_read(reply, "u", &translated);
+ if (r < 0)
+ goto fail;
}
- r = sd_bus_message_read(reply, "u", &translated);
- if (r < 0)
- goto fail;
-
l = sizeof(char*) + strlen(name) + 1;
if (buflen < l) {
*errnop = ENOMEM;
@@ -353,6 +431,7 @@ enum nss_status _nss_systemd_getgrgid_r(
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ _cleanup_free_ char *direct = NULL;
const char *translated;
size_t l;
int r;
@@ -382,30 +461,41 @@ enum nss_status _nss_systemd_getgrgid_r(
if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
goto not_found;
- r = sd_bus_open_system(&bus);
- if (r < 0)
- goto fail;
+ if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0) {
- r = sd_bus_call_method(bus,
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "LookupDynamicUserByUID",
- &error,
- &reply,
- "u",
- (uint32_t) gid);
- if (r < 0) {
- if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
+ r = direct_lookup_uid(gid, &direct);
+ if (r == -ENOENT)
goto not_found;
-
- goto fail;
+ if (r < 0)
+ goto fail;
+
+ translated = direct;
+ } else {
+ r = sd_bus_open_system(&bus);
+ if (r < 0)
+ goto fail;
+
+ r = sd_bus_call_method(bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "LookupDynamicUserByUID",
+ &error,
+ &reply,
+ "u",
+ (uint32_t) gid);
+ if (r < 0) {
+ if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
+ goto not_found;
+
+ goto fail;
+ }
+
+ r = sd_bus_message_read(reply, "s", &translated);
+ if (r < 0)
+ goto fail;
}
- r = sd_bus_message_read(reply, "s", &translated);
- if (r < 0)
- goto fail;
-
l = sizeof(char*) + strlen(translated) + 1;
if (buflen < l) {
*errnop = ENOMEM;
diff --git a/src/run/run.c b/src/run/run.c
index 1917ffd857..0797547684 100644
--- a/src/run/run.c
+++ b/src/run/run.c
@@ -83,9 +83,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,7 +92,7 @@ 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"
@@ -107,15 +105,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) {
@@ -178,7 +176,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 +193,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;
@@ -410,18 +408,15 @@ static int parse_argv(int argc, char *argv[]) {
}
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;
}
@@ -1144,7 +1139,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);
diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c
index f9e12e0578..feb4a06737 100644
--- a/src/shared/bus-unit-util.c
+++ b/src/shared/bus-unit-util.c
@@ -204,7 +204,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
"IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit",
"PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers", "NoNewPrivileges",
"SyslogLevelPrefix", "Delegate", "RemainAfterElapse", "MemoryDenyWriteExecute",
- "RestrictRealtime", "DynamicUser")) {
+ "RestrictRealtime", "DynamicUser", "RemoveIPC")) {
r = parse_boolean(eq);
if (r < 0)
@@ -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/clean-ipc.c b/src/shared/clean-ipc.c
index a3ac7aeb82..d5db604f03 100644
--- a/src/shared/clean-ipc.c
+++ b/src/shared/clean-ipc.c
@@ -41,8 +41,20 @@
#include "macro.h"
#include "string-util.h"
#include "strv.h"
+#include "user-util.h"
-static int clean_sysvipc_shm(uid_t delete_uid) {
+static bool match_uid_gid(uid_t subject_uid, gid_t subject_gid, uid_t delete_uid, gid_t delete_gid) {
+
+ if (uid_is_valid(delete_uid) && subject_uid == delete_uid)
+ return true;
+
+ if (gid_is_valid(delete_gid) && subject_gid == delete_gid)
+ return true;
+
+ return false;
+}
+
+static int clean_sysvipc_shm(uid_t delete_uid, gid_t delete_gid) {
_cleanup_fclose_ FILE *f = NULL;
char line[LINE_MAX];
bool first = true;
@@ -77,7 +89,7 @@ static int clean_sysvipc_shm(uid_t delete_uid) {
if (n_attached > 0)
continue;
- if (uid != delete_uid)
+ if (!match_uid_gid(uid, gid, delete_uid, delete_gid))
continue;
if (shmctl(shmid, IPC_RMID, NULL) < 0) {
@@ -89,7 +101,8 @@ static int clean_sysvipc_shm(uid_t delete_uid) {
ret = log_warning_errno(errno,
"Failed to remove SysV shared memory segment %i: %m",
shmid);
- }
+ } else
+ log_debug("Removed SysV shared memory segment %i.", shmid);
}
return ret;
@@ -98,7 +111,7 @@ fail:
return log_warning_errno(errno, "Failed to read /proc/sysvipc/shm: %m");
}
-static int clean_sysvipc_sem(uid_t delete_uid) {
+static int clean_sysvipc_sem(uid_t delete_uid, gid_t delete_gid) {
_cleanup_fclose_ FILE *f = NULL;
char line[LINE_MAX];
bool first = true;
@@ -128,7 +141,7 @@ static int clean_sysvipc_sem(uid_t delete_uid) {
&semid, &uid, &gid, &cuid, &cgid) != 5)
continue;
- if (uid != delete_uid)
+ if (!match_uid_gid(uid, gid, delete_uid, delete_gid))
continue;
if (semctl(semid, 0, IPC_RMID) < 0) {
@@ -140,7 +153,8 @@ static int clean_sysvipc_sem(uid_t delete_uid) {
ret = log_warning_errno(errno,
"Failed to remove SysV semaphores object %i: %m",
semid);
- }
+ } else
+ log_debug("Removed SysV semaphore %i.", semid);
}
return ret;
@@ -149,7 +163,7 @@ fail:
return log_warning_errno(errno, "Failed to read /proc/sysvipc/sem: %m");
}
-static int clean_sysvipc_msg(uid_t delete_uid) {
+static int clean_sysvipc_msg(uid_t delete_uid, gid_t delete_gid) {
_cleanup_fclose_ FILE *f = NULL;
char line[LINE_MAX];
bool first = true;
@@ -180,7 +194,7 @@ static int clean_sysvipc_msg(uid_t delete_uid) {
&msgid, &cpid, &lpid, &uid, &gid, &cuid, &cgid) != 7)
continue;
- if (uid != delete_uid)
+ if (!match_uid_gid(uid, gid, delete_uid, delete_gid))
continue;
if (msgctl(msgid, IPC_RMID, NULL) < 0) {
@@ -192,7 +206,8 @@ static int clean_sysvipc_msg(uid_t delete_uid) {
ret = log_warning_errno(errno,
"Failed to remove SysV message queue %i: %m",
msgid);
- }
+ } else
+ log_debug("Removed SysV message queue %i.", msgid);
}
return ret;
@@ -201,13 +216,13 @@ fail:
return log_warning_errno(errno, "Failed to read /proc/sysvipc/msg: %m");
}
-static int clean_posix_shm_internal(DIR *dir, uid_t uid) {
+static int clean_posix_shm_internal(DIR *dir, uid_t uid, gid_t gid) {
struct dirent *de;
int ret = 0, r;
assert(dir);
- FOREACH_DIRENT(de, dir, goto fail) {
+ FOREACH_DIRENT_ALL(de, dir, goto fail) {
struct stat st;
if (STR_IN_SET(de->d_name, "..", "."))
@@ -217,12 +232,11 @@ static int clean_posix_shm_internal(DIR *dir, uid_t uid) {
if (errno == ENOENT)
continue;
- log_warning_errno(errno, "Failed to stat() POSIX shared memory segment %s: %m", de->d_name);
- ret = -errno;
+ ret = log_warning_errno(errno, "Failed to stat() POSIX shared memory segment %s: %m", de->d_name);
continue;
}
- if (st.st_uid != uid)
+ if (!match_uid_gid(st.st_uid, st.st_gid, uid, gid))
continue;
if (S_ISDIR(st.st_mode)) {
@@ -230,12 +244,10 @@ static int clean_posix_shm_internal(DIR *dir, uid_t uid) {
kid = xopendirat(dirfd(dir), de->d_name, O_NOFOLLOW|O_NOATIME);
if (!kid) {
- if (errno != ENOENT) {
- log_warning_errno(errno, "Failed to enter shared memory directory %s: %m", de->d_name);
- ret = -errno;
- }
+ if (errno != ENOENT)
+ ret = log_warning_errno(errno, "Failed to enter shared memory directory %s: %m", de->d_name);
} else {
- r = clean_posix_shm_internal(kid, uid);
+ r = clean_posix_shm_internal(kid, uid, gid);
if (r < 0)
ret = r;
}
@@ -245,9 +257,9 @@ static int clean_posix_shm_internal(DIR *dir, uid_t uid) {
if (errno == ENOENT)
continue;
- log_warning_errno(errno, "Failed to remove POSIX shared memory directory %s: %m", de->d_name);
- ret = -errno;
- }
+ ret = log_warning_errno(errno, "Failed to remove POSIX shared memory directory %s: %m", de->d_name);
+ } else
+ log_debug("Removed POSIX shared memory directory %s", de->d_name);
} else {
if (unlinkat(dirfd(dir), de->d_name, 0) < 0) {
@@ -255,20 +267,19 @@ static int clean_posix_shm_internal(DIR *dir, uid_t uid) {
if (errno == ENOENT)
continue;
- log_warning_errno(errno, "Failed to remove POSIX shared memory segment %s: %m", de->d_name);
- ret = -errno;
- }
+ ret = log_warning_errno(errno, "Failed to remove POSIX shared memory segment %s: %m", de->d_name);
+ } else
+ log_debug("Removed POSIX shared memory segment %s", de->d_name);
}
}
return ret;
fail:
- log_warning_errno(errno, "Failed to read /dev/shm: %m");
- return -errno;
+ return log_warning_errno(errno, "Failed to read /dev/shm: %m");
}
-static int clean_posix_shm(uid_t uid) {
+static int clean_posix_shm(uid_t uid, gid_t gid) {
_cleanup_closedir_ DIR *dir = NULL;
dir = opendir("/dev/shm");
@@ -279,10 +290,10 @@ static int clean_posix_shm(uid_t uid) {
return log_warning_errno(errno, "Failed to open /dev/shm: %m");
}
- return clean_posix_shm_internal(dir, uid);
+ return clean_posix_shm_internal(dir, uid, gid);
}
-static int clean_posix_mq(uid_t uid) {
+static int clean_posix_mq(uid_t uid, gid_t gid) {
_cleanup_closedir_ DIR *dir = NULL;
struct dirent *de;
int ret = 0;
@@ -295,7 +306,7 @@ static int clean_posix_mq(uid_t uid) {
return log_warning_errno(errno, "Failed to open /dev/mqueue: %m");
}
- FOREACH_DIRENT(de, dir, goto fail) {
+ FOREACH_DIRENT_ALL(de, dir, goto fail) {
struct stat st;
char fn[1+strlen(de->d_name)+1];
@@ -312,7 +323,7 @@ static int clean_posix_mq(uid_t uid) {
continue;
}
- if (st.st_uid != uid)
+ if (!match_uid_gid(st.st_uid, st.st_gid, uid, gid))
continue;
fn[0] = '/';
@@ -325,7 +336,8 @@ static int clean_posix_mq(uid_t uid) {
ret = log_warning_errno(errno,
"Failed to unlink POSIX message queue %s: %m",
fn);
- }
+ } else
+ log_debug("Removed POSIX message queue %s", fn);
}
return ret;
@@ -334,32 +346,44 @@ fail:
return log_warning_errno(errno, "Failed to read /dev/mqueue: %m");
}
-int clean_ipc(uid_t uid) {
+int clean_ipc(uid_t uid, gid_t gid) {
int ret = 0, r;
- /* Refuse to clean IPC of the root and system users */
- if (uid <= SYSTEM_UID_MAX)
+ /* Anything to do? */
+ if (!uid_is_valid(uid) && !gid_is_valid(gid))
+ return 0;
+
+ /* Refuse to clean IPC of the root user */
+ if (uid == 0 && gid == 0)
return 0;
- r = clean_sysvipc_shm(uid);
+ r = clean_sysvipc_shm(uid, gid);
if (r < 0)
ret = r;
- r = clean_sysvipc_sem(uid);
+ r = clean_sysvipc_sem(uid, gid);
if (r < 0)
ret = r;
- r = clean_sysvipc_msg(uid);
+ r = clean_sysvipc_msg(uid, gid);
if (r < 0)
ret = r;
- r = clean_posix_shm(uid);
+ r = clean_posix_shm(uid, gid);
if (r < 0)
ret = r;
- r = clean_posix_mq(uid);
+ r = clean_posix_mq(uid, gid);
if (r < 0)
ret = r;
return ret;
}
+
+int clean_ipc_by_uid(uid_t uid) {
+ return clean_ipc(uid, GID_INVALID);
+}
+
+int clean_ipc_by_gid(gid_t gid) {
+ return clean_ipc(UID_INVALID, gid);
+}
diff --git a/src/shared/clean-ipc.h b/src/shared/clean-ipc.h
index 44a83afcf7..6ca57f44fd 100644
--- a/src/shared/clean-ipc.h
+++ b/src/shared/clean-ipc.h
@@ -21,4 +21,6 @@
#include <sys/types.h>
-int clean_ipc(uid_t uid);
+int clean_ipc(uid_t uid, gid_t gid);
+int clean_ipc_by_uid(uid_t uid);
+int clean_ipc_by_gid(gid_t gid);
diff --git a/src/shared/install.c b/src/shared/install.c
index e740ef3910..6a16f8985b 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -393,19 +393,40 @@ void unit_file_dump_changes(int r, const char *verb, const UnitFileChange *chang
log_error_errno(r, "Failed to %s: %m.", verb);
}
+/**
+ * Checks if two paths or symlinks from wd are the same, when root is the root of the filesystem.
+ * wc should be the full path in the host file system.
+ */
+static bool chroot_symlinks_same(const char *root, const char *wd, const char *a, const char *b) {
+ assert(path_is_absolute(wd));
+
+ /* This will give incorrect results if the paths are relative and go outside
+ * of the chroot. False negatives are possible. */
+
+ a = strjoina(path_is_absolute(a) ? root : wd, "/", a);
+ b = strjoina(path_is_absolute(b) ? root : wd, "/", b);
+ return path_equal_or_files_same(a, b);
+}
+
static int create_symlink(
+ const LookupPaths *paths,
const char *old_path,
const char *new_path,
bool force,
UnitFileChange **changes,
unsigned *n_changes) {
- _cleanup_free_ char *dest = NULL;
+ _cleanup_free_ char *dest = NULL, *dirname = NULL;
+ const char *rp;
int r;
assert(old_path);
assert(new_path);
+ rp = skip_root(paths, old_path);
+ if (rp)
+ old_path = rp;
+
/* Actually create a symlink, and remember that we did. Is
* smart enough to check if there's already a valid symlink in
* place.
@@ -436,7 +457,11 @@ static int create_symlink(
return r;
}
- if (path_equal(dest, old_path))
+ dirname = dirname_malloc(new_path);
+ if (!dirname)
+ return -ENOMEM;
+
+ if (chroot_symlinks_same(paths->root_dir, dirname, dest, old_path))
return 1;
if (!force) {
@@ -620,7 +645,7 @@ static int remove_marked_symlinks(
fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
if (fd < 0)
- return -errno;
+ return errno == ENOENT ? 0 : -errno;
do {
int q, cfd;
@@ -903,6 +928,10 @@ static int install_info_may_process(
return 0;
}
+/**
+ * Adds a new UnitFileInstallInfo entry under name in the InstallContext.will_process
+ * hashmap, or retrieves the existing one if already present.
+ */
static int install_info_add(
InstallContext *c,
const char *name,
@@ -1334,9 +1363,8 @@ static int install_info_follow(
}
/**
- * Search for the unit file. If the unit name is a symlink,
- * follow the symlink to the target, maybe more than once.
- * Propagate the instance name if present.
+ * Search for the unit file. If the unit name is a symlink, follow the symlink to the
+ * target, maybe more than once. Propagate the instance name if present.
*/
static int install_info_traverse(
UnitFileScope scope,
@@ -1421,6 +1449,10 @@ static int install_info_traverse(
return 0;
}
+/**
+ * Call install_info_add() with name_or_path as the path (if name_or_path starts with "/")
+ * or the name (otherwise). root_dir is prepended to the path.
+ */
static int install_info_add_auto(
InstallContext *c,
const LookupPaths *paths,
@@ -1479,7 +1511,6 @@ static int install_info_symlink_alias(
STRV_FOREACH(s, i->aliases) {
_cleanup_free_ char *alias_path = NULL, *dst = NULL;
- const char *rp;
q = install_full_printf(i, *s, &dst);
if (q < 0)
@@ -1489,9 +1520,7 @@ static int install_info_symlink_alias(
if (!alias_path)
return -ENOMEM;
- rp = skip_root(paths, i->path);
-
- q = create_symlink(rp ?: i->path, alias_path, force, changes, n_changes);
+ q = create_symlink(paths, i->path, alias_path, force, changes, n_changes);
if (r == 0)
r = q;
}
@@ -1535,7 +1564,6 @@ static int install_info_symlink_wants(
STRV_FOREACH(s, list) {
_cleanup_free_ char *path = NULL, *dst = NULL;
- const char *rp;
q = install_full_printf(i, *s, &dst);
if (q < 0)
@@ -1550,9 +1578,7 @@ static int install_info_symlink_wants(
if (!path)
return -ENOMEM;
- rp = skip_root(paths, i->path);
-
- q = create_symlink(rp ?: i->path, path, true, changes, n_changes);
+ q = create_symlink(paths, i->path, path, true, changes, n_changes);
if (r == 0)
r = q;
}
@@ -1569,7 +1595,6 @@ static int install_info_symlink_link(
unsigned *n_changes) {
_cleanup_free_ char *path = NULL;
- const char *rp;
int r;
assert(i);
@@ -1587,9 +1612,7 @@ static int install_info_symlink_link(
if (!path)
return -ENOMEM;
- rp = skip_root(paths, i->path);
-
- return create_symlink(rp ?: i->path, path, force, changes, n_changes);
+ return create_symlink(paths, i->path, path, force, changes, n_changes);
}
static int install_info_apply(
@@ -1663,6 +1686,17 @@ static int install_context_apply(
if (r < 0)
return r;
+ /* We can attempt to process a masked unit when a different unit
+ * that we were processing specifies it in DefaultInstance= or 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. */
+ r += 1;
+ continue;
+ }
+
if (i->type != UNIT_FILE_TYPE_REGULAR)
continue;
@@ -1765,7 +1799,7 @@ int unit_file_mask(
if (!path)
return -ENOMEM;
- q = create_symlink("/dev/null", path, force, changes, n_changes);
+ q = create_symlink(&paths, "/dev/null", path, force, changes, n_changes);
if (q < 0 && r >= 0)
r = q;
}
@@ -1925,14 +1959,12 @@ int unit_file_link(
r = 0;
STRV_FOREACH(i, todo) {
_cleanup_free_ char *new_path = NULL;
- const char *old_path;
- old_path = skip_root(&paths, *i);
new_path = path_make_absolute(basename(*i), config_path);
if (!new_path)
return -ENOMEM;
- q = create_symlink(old_path ?: *i, new_path, force, changes, n_changes);
+ q = create_symlink(&paths, *i, new_path, force, changes, n_changes);
if (q < 0 && r >= 0)
r = q;
}
@@ -1967,7 +1999,6 @@ int unit_file_revert(
unsigned *n_changes) {
_cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
- /* _cleanup_(install_context_done) InstallContext c = {}; */
_cleanup_lookup_paths_free_ LookupPaths paths = {};
_cleanup_strv_free_ char **todo = NULL;
size_t n_todo = 0, n_allocated = 0;
@@ -2312,7 +2343,7 @@ int unit_file_set_default(
_cleanup_lookup_paths_free_ LookupPaths paths = {};
_cleanup_(install_context_done) InstallContext c = {};
UnitFileInstallInfo *i;
- const char *new_path, *old_path;
+ const char *new_path;
int r;
assert(scope >= 0);
@@ -2335,10 +2366,8 @@ int unit_file_set_default(
if (r < 0)
return r;
- old_path = skip_root(&paths, i->path);
new_path = strjoina(paths.persistent_config, "/" SPECIAL_DEFAULT_TARGET);
-
- return create_symlink(old_path ?: i->path, new_path, force, changes, n_changes);
+ return create_symlink(&paths, i->path, new_path, force, changes, n_changes);
}
int unit_file_get_default(
@@ -2685,18 +2714,25 @@ static int preset_prepare_one(
InstallContext *plus,
InstallContext *minus,
LookupPaths *paths,
- UnitFilePresetMode mode,
const char *name,
Presets presets,
UnitFileChange **changes,
unsigned *n_changes) {
+ _cleanup_(install_context_done) InstallContext tmp = {};
UnitFileInstallInfo *i;
int r;
- if (install_info_find(plus, name) ||
- install_info_find(minus, name))
+ if (install_info_find(plus, name) || install_info_find(minus, name))
+ return 0;
+
+ r = install_info_discover(scope, &tmp, paths, name, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i);
+ if (r < 0)
+ return r;
+ if (!streq(name, i->name)) {
+ log_debug("Skipping %s because is an alias for %s", name, i->name);
return 0;
+ }
r = query_presets(name, presets);
if (r < 0)
@@ -2748,7 +2784,7 @@ int unit_file_preset(
return r;
STRV_FOREACH(i, files) {
- r = preset_prepare_one(scope, &plus, &minus, &paths, mode, *i, presets, changes, n_changes);
+ r = preset_prepare_one(scope, &plus, &minus, &paths, *i, presets, changes, n_changes);
if (r < 0)
return r;
}
@@ -2809,7 +2845,7 @@ int unit_file_preset_all(
continue;
/* we don't pass changes[] in, because we want to handle errors on our own */
- r = preset_prepare_one(scope, &plus, &minus, &paths, mode, de->d_name, presets, NULL, 0);
+ r = preset_prepare_one(scope, &plus, &minus, &paths, de->d_name, presets, NULL, 0);
if (r == -ERFKILL)
r = unit_file_changes_add(changes, n_changes,
UNIT_FILE_IS_MASKED, de->d_name, NULL);
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index b4ce6fba5a..4444e3064d 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -5093,7 +5093,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 +5123,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/sysv-generator/sysv-generator.c b/src/sysv-generator/sysv-generator.c
index 3ed8f23ff9..39821687b9 100644
--- a/src/sysv-generator/sysv-generator.c
+++ b/src/sysv-generator/sysv-generator.c
@@ -247,7 +247,7 @@ static char *sysv_translate_name(const char *name) {
return res;
}
-static int sysv_translate_facility(const char *name, const char *filename, char **ret) {
+static int sysv_translate_facility(SysvStub *s, unsigned line, const char *name, char **ret) {
/* We silently ignore the $ prefix here. According to the LSB
* spec it simply indicates whether something is a
@@ -266,15 +266,18 @@ static int sysv_translate_facility(const char *name, const char *filename, char
"time", SPECIAL_TIME_SYNC_TARGET,
};
+ const char *filename;
char *filename_no_sh, *e, *m;
const char *n;
unsigned i;
int r;
assert(name);
- assert(filename);
+ assert(s);
assert(ret);
+ filename = basename(s->path);
+
n = *name == '$' ? name + 1 : name;
for (i = 0; i < ELEMENTSOF(table); i += 2) {
@@ -299,7 +302,7 @@ static int sysv_translate_facility(const char *name, const char *filename, char
if (*name == '$') {
r = unit_name_build(n, NULL, ".target", ret);
if (r < 0)
- return log_error_errno(r, "Failed to build name: %m");
+ return log_error_errno(r, "[%s:%u] Could not build name for facility %s: %m", s->path, line, name);
return r;
}
@@ -337,11 +340,11 @@ static int handle_provides(SysvStub *s, unsigned line, const char *full_text, co
r = extract_first_word(&text, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX);
if (r < 0)
- return log_error_errno(r, "Failed to parse word from provides string: %m");
+ return log_error_errno(r, "[%s:%u] Failed to parse word from provides string: %m", s->path, line);
if (r == 0)
break;
- r = sysv_translate_facility(word, basename(s->path), &m);
+ r = sysv_translate_facility(s, line, word, &m);
if (r <= 0) /* continue on error */
continue;
@@ -403,11 +406,11 @@ static int handle_dependencies(SysvStub *s, unsigned line, const char *full_text
r = extract_first_word(&text, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX);
if (r < 0)
- return log_error_errno(r, "Failed to parse word from provides string: %m");
+ return log_error_errno(r, "[%s:%u] Failed to parse word from provides string: %m", s->path, line);
if (r == 0)
break;
- r = sysv_translate_facility(word, basename(s->path), &m);
+ r = sysv_translate_facility(s, line, word, &m);
if (r <= 0) /* continue on error */
continue;
diff --git a/src/test/test-condition.c b/src/test/test-condition.c
index 66003aa6bd..6f7d71ef9a 100644
--- a/src/test/test-condition.c
+++ b/src/test/test-condition.c
@@ -37,66 +37,82 @@ static void test_condition_test_path(void) {
Condition *condition;
condition = condition_new(CONDITION_PATH_EXISTS, "/bin/sh", false, false);
+ assert_se(condition);
assert_se(condition_test(condition));
condition_free(condition);
condition = condition_new(CONDITION_PATH_EXISTS, "/bin/s?", false, false);
+ assert_se(condition);
assert_se(!condition_test(condition));
condition_free(condition);
condition = condition_new(CONDITION_PATH_EXISTS_GLOB, "/bin/s?", false, false);
+ assert_se(condition);
assert_se(condition_test(condition));
condition_free(condition);
condition = condition_new(CONDITION_PATH_EXISTS_GLOB, "/bin/s?", false, true);
+ assert_se(condition);
assert_se(!condition_test(condition));
condition_free(condition);
condition = condition_new(CONDITION_PATH_EXISTS, "/thiscertainlywontexist", false, false);
+ assert_se(condition);
assert_se(!condition_test(condition));
condition_free(condition);
condition = condition_new(CONDITION_PATH_EXISTS, "/thiscertainlywontexist", false, true);
+ assert_se(condition);
assert_se(condition_test(condition));
condition_free(condition);
condition = condition_new(CONDITION_PATH_IS_DIRECTORY, "/bin", false, false);
+ assert_se(condition);
assert_se(condition_test(condition));
condition_free(condition);
condition = condition_new(CONDITION_DIRECTORY_NOT_EMPTY, "/bin", false, false);
+ assert_se(condition);
assert_se(condition_test(condition));
condition_free(condition);
condition = condition_new(CONDITION_FILE_NOT_EMPTY, "/bin/sh", false, false);
+ assert_se(condition);
assert_se(condition_test(condition));
condition_free(condition);
condition = condition_new(CONDITION_FILE_IS_EXECUTABLE, "/bin/sh", false, false);
+ assert_se(condition);
assert_se(condition_test(condition));
condition_free(condition);
condition = condition_new(CONDITION_FILE_IS_EXECUTABLE, "/etc/passwd", false, false);
+ assert_se(condition);
assert_se(!condition_test(condition));
condition_free(condition);
condition = condition_new(CONDITION_PATH_IS_MOUNT_POINT, "/proc", false, false);
+ assert_se(condition);
assert_se(condition_test(condition));
condition_free(condition);
condition = condition_new(CONDITION_PATH_IS_MOUNT_POINT, "/", false, false);
+ assert_se(condition);
assert_se(condition_test(condition));
condition_free(condition);
condition = condition_new(CONDITION_PATH_IS_MOUNT_POINT, "/bin", false, false);
+ assert_se(condition);
assert_se(!condition_test(condition));
condition_free(condition);
condition = condition_new(CONDITION_PATH_IS_READ_WRITE, "/tmp", false, false);
+ assert_se(condition);
assert_se(condition_test(condition));
condition_free(condition);
condition = condition_new(CONDITION_PATH_IS_SYMBOLIC_LINK, "/dev/stdout", false, false);
+ assert_se(condition);
assert_se(condition_test(condition));
condition_free(condition);
}
@@ -105,38 +121,44 @@ static void test_condition_test_ac_power(void) {
Condition *condition;
condition = condition_new(CONDITION_AC_POWER, "true", false, false);
+ assert_se(condition);
assert_se(condition_test(condition) == on_ac_power());
condition_free(condition);
condition = condition_new(CONDITION_AC_POWER, "false", false, false);
+ assert_se(condition);
assert_se(condition_test(condition) != on_ac_power());
condition_free(condition);
condition = condition_new(CONDITION_AC_POWER, "false", false, true);
+ assert_se(condition);
assert_se(condition_test(condition) == on_ac_power());
condition_free(condition);
}
static void test_condition_test_host(void) {
+ _cleanup_free_ char *hostname = NULL;
+ char sid[SD_ID128_STRING_MAX];
Condition *condition;
sd_id128_t id;
int r;
- char sid[SD_ID128_STRING_MAX];
- _cleanup_free_ char *hostname = NULL;
r = sd_id128_get_machine(&id);
assert_se(r >= 0);
assert_se(sd_id128_to_string(id, sid));
condition = condition_new(CONDITION_HOST, sid, false, false);
+ assert_se(condition);
assert_se(condition_test(condition));
condition_free(condition);
condition = condition_new(CONDITION_HOST, "garbage value jjjjjjjjjjjjjj", false, false);
+ assert_se(condition);
assert_se(!condition_test(condition));
condition_free(condition);
condition = condition_new(CONDITION_HOST, sid, false, true);
+ assert_se(condition);
assert_se(!condition_test(condition));
condition_free(condition);
@@ -148,6 +170,7 @@ static void test_condition_test_host(void) {
log_notice("hostname is an id128, skipping test");
else {
condition = condition_new(CONDITION_HOST, hostname, false, false);
+ assert_se(condition);
assert_se(condition_test(condition));
condition_free(condition);
}
@@ -165,14 +188,17 @@ static void test_condition_test_architecture(void) {
assert_se(sa);
condition = condition_new(CONDITION_ARCHITECTURE, sa, false, false);
+ assert_se(condition);
assert_se(condition_test(condition) > 0);
condition_free(condition);
condition = condition_new(CONDITION_ARCHITECTURE, "garbage value", false, false);
+ assert_se(condition);
assert_se(condition_test(condition) == 0);
condition_free(condition);
condition = condition_new(CONDITION_ARCHITECTURE, sa, false, true);
+ assert_se(condition);
assert_se(condition_test(condition) == 0);
condition_free(condition);
}
@@ -181,10 +207,12 @@ static void test_condition_test_kernel_command_line(void) {
Condition *condition;
condition = condition_new(CONDITION_KERNEL_COMMAND_LINE, "thisreallyshouldntbeonthekernelcommandline", false, false);
+ assert_se(condition);
assert_se(!condition_test(condition));
condition_free(condition);
condition = condition_new(CONDITION_KERNEL_COMMAND_LINE, "andthis=neither", false, false);
+ assert_se(condition);
assert_se(!condition_test(condition));
condition_free(condition);
}
@@ -193,10 +221,12 @@ static void test_condition_test_null(void) {
Condition *condition;
condition = condition_new(CONDITION_NULL, NULL, false, false);
+ assert_se(condition);
assert_se(condition_test(condition));
condition_free(condition);
condition = condition_new(CONDITION_NULL, NULL, false, true);
+ assert_se(condition);
assert_se(!condition_test(condition));
condition_free(condition);
}
@@ -205,31 +235,36 @@ static void test_condition_test_security(void) {
Condition *condition;
condition = condition_new(CONDITION_SECURITY, "garbage oifdsjfoidsjoj", false, false);
+ assert_se(condition);
assert_se(!condition_test(condition));
condition_free(condition);
condition = condition_new(CONDITION_SECURITY, "selinux", false, true);
+ assert_se(condition);
assert_se(condition_test(condition) != mac_selinux_have());
condition_free(condition);
condition = condition_new(CONDITION_SECURITY, "ima", false, false);
+ assert_se(condition);
assert_se(condition_test(condition) == use_ima());
condition_free(condition);
condition = condition_new(CONDITION_SECURITY, "apparmor", false, false);
+ assert_se(condition);
assert_se(condition_test(condition) == mac_apparmor_use());
condition_free(condition);
condition = condition_new(CONDITION_SECURITY, "smack", false, false);
+ assert_se(condition);
assert_se(condition_test(condition) == mac_smack_use());
condition_free(condition);
condition = condition_new(CONDITION_SECURITY, "audit", false, false);
+ assert_se(condition);
assert_se(condition_test(condition) == use_audit());
condition_free(condition);
}
-
int main(int argc, char *argv[]) {
log_parse_environment();
log_open();
diff --git a/src/test/test-hostname-util.c b/src/test/test-hostname-util.c
index 17fde9f27e..1c3d13ed1d 100644
--- a/src/test/test-hostname-util.c
+++ b/src/test/test-hostname-util.c
@@ -42,6 +42,7 @@ static void test_hostname_is_valid(void) {
assert_se(!hostname_is_valid("foo..bar", false));
assert_se(!hostname_is_valid("foo.bar..", false));
assert_se(!hostname_is_valid("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", false));
+ assert_se(!hostname_is_valid("au-xph5-rvgrdsb5hcxc-47et3a5vvkrc-server-wyoz4elpdpe3.openstack.local", false));
assert_se(hostname_is_valid("foobar", true));
assert_se(hostname_is_valid("foobar.com", true));
diff --git a/src/test/test-ipcrm.c b/src/test/test-ipcrm.c
index c5bcaf47bb..551eba7215 100644
--- a/src/test/test-ipcrm.c
+++ b/src/test/test-ipcrm.c
@@ -32,5 +32,5 @@ int main(int argc, char *argv[]) {
return EXIT_FAILURE;
}
- return clean_ipc(uid) < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return clean_ipc_by_uid(uid) < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/system-preset/90-systemd.preset b/system-preset/90-systemd.preset
index ee1b864bcf..0f494b7552 100644
--- a/system-preset/90-systemd.preset
+++ b/system-preset/90-systemd.preset
@@ -15,6 +15,7 @@ enable getty@.service
enable systemd-timesyncd.service
enable systemd-networkd.service
enable systemd-resolved.service
+enable systemd-networkd-wait-online.service
disable console-getty.service
disable console-shell.service
@@ -23,10 +24,12 @@ disable debug-shell.service
disable halt.target
disable kexec.target
disable poweroff.target
-disable reboot.target
+enable reboot.target
disable rescue.target
+disable exit.target
disable syslog.socket
disable systemd-journal-gatewayd.*
-disable systemd-networkd-wait-online.service
+disable systemd-journal-remote.*
+disable systemd-journal-upload.*
diff --git a/test/udev-test.pl b/test/udev-test.pl
index 129af854f1..9723386b23 100755
--- a/test/udev-test.pl
+++ b/test/udev-test.pl
@@ -37,7 +37,7 @@ my $EXIT_TEST_SKIP = 77;
my $rules_10k_tags = "";
for (my $i = 1; $i <= 10000; ++$i) {
- $rules_10k_tags .= 'KERNEL=="sda", TAG+="test' . $i . "\"\n";
+ $rules_10k_tags .= 'KERNEL=="sda", TAG+="test' . $i . "\"\n";
}
my @tests = (
@@ -1596,6 +1596,6 @@ system("umount", "$udev_tmpfs");
rmdir($udev_tmpfs);
if ($error > 0) {
- exit(1);
+ exit(1);
}
exit(0);
diff --git a/units/user/bluetooth.target b/units/user/bluetooth.target
new file mode 120000
index 0000000000..72e74be0a1
--- /dev/null
+++ b/units/user/bluetooth.target
@@ -0,0 +1 @@
+../bluetooth.target \ No newline at end of file
diff --git a/units/user/busnames.target b/units/user/busnames.target
new file mode 120000
index 0000000000..04f4ba1345
--- /dev/null
+++ b/units/user/busnames.target
@@ -0,0 +1 @@
+../busnames.target \ No newline at end of file
diff --git a/units/user/paths.target b/units/user/paths.target
new file mode 120000
index 0000000000..33545d24f3
--- /dev/null
+++ b/units/user/paths.target
@@ -0,0 +1 @@
+../paths.target \ No newline at end of file
diff --git a/units/user/printer.target b/units/user/printer.target
new file mode 120000
index 0000000000..8b8d5511cd
--- /dev/null
+++ b/units/user/printer.target
@@ -0,0 +1 @@
+../printer.target \ No newline at end of file
diff --git a/units/user/shutdown.target b/units/user/shutdown.target
new file mode 120000
index 0000000000..a9de83782f
--- /dev/null
+++ b/units/user/shutdown.target
@@ -0,0 +1 @@
+../shutdown.target \ No newline at end of file
diff --git a/units/user/smartcard.target b/units/user/smartcard.target
new file mode 120000
index 0000000000..f7a23b6b6d
--- /dev/null
+++ b/units/user/smartcard.target
@@ -0,0 +1 @@
+../smartcard.target \ No newline at end of file
diff --git a/units/user/sockets.target b/units/user/sockets.target
new file mode 120000
index 0000000000..a9e4b97184
--- /dev/null
+++ b/units/user/sockets.target
@@ -0,0 +1 @@
+../sockets.target \ No newline at end of file
diff --git a/units/user/sound.target b/units/user/sound.target
new file mode 120000
index 0000000000..17c8e9d6e1
--- /dev/null
+++ b/units/user/sound.target
@@ -0,0 +1 @@
+../sound.target \ No newline at end of file
diff --git a/units/user/timers.target b/units/user/timers.target
new file mode 120000
index 0000000000..f98b68a84d
--- /dev/null
+++ b/units/user/timers.target
@@ -0,0 +1 @@
+../timers.target \ No newline at end of file