1#!/usr/bin/env bash
2# SPDX-License-Identifier: LGPL-2.1-or-later
3set -eux
4set -o pipefail
5
6systemd-analyze log-level debug
7systemd-analyze log-target console
8
9test "$(systemctl show -P MainPID testsuite-20.service)" -eq $$
10
11# Start a test process inside of our own cgroup
12sleep infinity &
13INTERNALPID=$!
14disown
15
16# Start a test process outside of our own cgroup
17systemd-run -p DynamicUser=1 --unit=test20-sleep.service /bin/sleep infinity
18EXTERNALPID="$(systemctl show -P MainPID test20-sleep.service)"
19
20# Update our own main PID to the external test PID, this should work
21systemd-notify MAINPID="$EXTERNALPID"
22test "$(systemctl show -P MainPID testsuite-20.service)" -eq "$EXTERNALPID"
23
24# Update our own main PID to the internal test PID, this should work, too
25systemd-notify MAINPID=$INTERNALPID
26test "$(systemctl show -P MainPID testsuite-20.service)" -eq "$INTERNALPID"
27
28# Update it back to our own PID, this should also work
29systemd-notify MAINPID=$$
30test "$(systemctl show -P MainPID testsuite-20.service)" -eq $$
31
32# Try to set it to PID 1, which it should ignore, because that's the manager
33systemd-notify MAINPID=1
34test "$(systemctl show -P MainPID testsuite-20.service)" -eq $$
35
36# Try to set it to PID 0, which is invalid and should be ignored
37systemd-notify MAINPID=0
38test "$(systemctl show -P MainPID testsuite-20.service)" -eq $$
39
40# Try to set it to a valid but non-existing PID, which should be ignored. (Note
41# that we set the PID to a value well above any known /proc/sys/kernel/pid_max,
42# which means we can be pretty sure it doesn't exist by coincidence)
43systemd-notify MAINPID=1073741824
44test "$(systemctl show -P MainPID testsuite-20.service)" -eq $$
45
46# Change it again to the external PID, without privileges this time. This should be ignored, because the PID is from outside of our cgroup and we lack privileges.
47systemd-notify --uid=1000 MAINPID="$EXTERNALPID"
48test "$(systemctl show -P MainPID testsuite-20.service)" -eq $$
49
50# Change it again to the internal PID, without privileges this time. This should work, as the process is on our cgroup, and that's enough even if we lack privileges.
51systemd-notify --uid=1000 MAINPID="$INTERNALPID"
52test "$(systemctl show -P MainPID testsuite-20.service)" -eq "$INTERNALPID"
53
54# Update it back to our own PID, this should also work
55systemd-notify --uid=1000 MAINPID=$$
56test "$(systemctl show -P MainPID testsuite-20.service)" -eq $$
57
58cat >/tmp/test20-mainpid.sh <<EOF
59#!/usr/bin/env bash
60
61set -eux
62set -o pipefail
63
64# Create a number of children, and make one the main one
65sleep infinity &
66disown
67
68sleep infinity &
69MAINPID=\$!
70disown
71
72sleep infinity &
73disown
74
75echo \$MAINPID >/run/mainpidsh/pid
76EOF
77chmod +x /tmp/test20-mainpid.sh
78
79systemd-run --unit=test20-mainpidsh.service -p StandardOutput=tty -p StandardError=tty -p Type=forking -p RuntimeDirectory=mainpidsh -p PIDFile=/run/mainpidsh/pid /tmp/test20-mainpid.sh
80test "$(systemctl show -P MainPID test20-mainpidsh.service)" -eq "$(cat /run/mainpidsh/pid)"
81
82cat >/tmp/test20-mainpid2.sh <<EOF
83#!/usr/bin/env bash
84
85set -eux
86set -o pipefail
87
88# Create a number of children, and make one the main one
89sleep infinity &
90disown
91
92sleep infinity &
93MAINPID=\$!
94disown
95
96sleep infinity &
97disown
98
99echo \$MAINPID >/run/mainpidsh2/pid
100chown 1001:1001 /run/mainpidsh2/pid
101EOF
102chmod +x /tmp/test20-mainpid2.sh
103
104systemd-run --unit=test20-mainpidsh2.service -p StandardOutput=tty -p StandardError=tty -p Type=forking -p RuntimeDirectory=mainpidsh2 -p PIDFile=/run/mainpidsh2/pid /tmp/test20-mainpid2.sh
105test "$(systemctl show -P MainPID test20-mainpidsh2.service)" -eq "$(cat /run/mainpidsh2/pid)"
106
107cat >/dev/shm/test20-mainpid3.sh <<EOF
108#!/usr/bin/env bash
109
110set -eux
111set -o pipefail
112
113sleep infinity &
114disown
115
116sleep infinity &
117disown
118
119sleep infinity &
120disown
121
122# Let's try to play games, and link up a privileged PID file
123ln -s ../mainpidsh/pid /run/mainpidsh3/pid
124
125# Quick assertion that the link isn't dead
126test -f /run/mainpidsh3/pid
127EOF
128chmod 755 /dev/shm/test20-mainpid3.sh
129
130# This has to fail, as we shouldn't accept the dangerous PID file, and then
131# inotify-wait on it to be corrected which we never do.
132systemd-run --unit=test20-mainpidsh3.service \
133            -p StandardOutput=tty \
134            -p StandardError=tty \
135            -p Type=forking \
136            -p RuntimeDirectory=mainpidsh3 \
137            -p PIDFile=/run/mainpidsh3/pid \
138            -p DynamicUser=1 \
139            -p TimeoutStartSec=2s \
140            /dev/shm/test20-mainpid3.sh \
141    && { echo 'unexpected success'; exit 1; }
142
143# Test that this failed due to timeout, and not some other error
144test "$(systemctl show -P Result test20-mainpidsh3.service)" = timeout
145
146# Test that scope units work
147systemd-run --scope --unit test20-true.scope /bin/true
148test "$(systemctl show -P Result test20-true.scope)" = success
149
150# Test that user scope units work as well
151
152runas() {
153    declare userid=$1
154    shift
155    # shellcheck disable=SC2016
156    su "$userid" -s /bin/sh -c 'XDG_RUNTIME_DIR=/run/user/$UID exec "$@"' -- sh "$@"
157}
158
159systemctl start user@4711.service
160runas testuser systemd-run --scope --user --unit test20-true.scope /bin/true
161test "$(systemctl show -P Result test20-true.scope)" = success
162
163systemd-analyze log-level info
164
165echo OK >/testok
166
167exit 0
168