]> git.ipfire.org Git - thirdparty/systemd.git/blob - test/units/testsuite-26.sh
1e11c420e1a3395a2abf2a2a3fe3957fe193311d
[thirdparty/systemd.git] / test / units / testsuite-26.sh
1 #!/usr/bin/env bash
2 # SPDX-License-Identifier: LGPL-2.1-or-later
3 # shellcheck disable=SC2016
4 set -eux
5 set -o pipefail
6
7 # shellcheck source=test/units/util.sh
8 . "$(dirname "$0")"/util.sh
9
10 at_exit() {
11 if [[ -v UNIT_NAME && -e "/usr/lib/systemd/system/$UNIT_NAME" ]]; then
12 rm -fvr "/usr/lib/systemd/system/$UNIT_NAME" "/etc/systemd/system/$UNIT_NAME.d" "+4"
13 fi
14
15 rm -f /etc/init.d/issue-24990
16 return 0
17 }
18
19 trap at_exit EXIT
20
21 # Create a simple unit file for testing
22 # Note: the service file is created under /usr on purpose to test
23 # the 'revert' verb as well
24 export UNIT_NAME="systemctl-test-$RANDOM.service"
25 cat >"/usr/lib/systemd/system/$UNIT_NAME" <<\EOF
26 [Unit]
27 Description=systemctl test
28
29 [Service]
30 ExecStart=sleep infinity
31 ExecReload=true
32
33 # For systemctl clean
34 CacheDirectory=%n
35 ConfigurationDirectory=%n
36 LogsDirectory=%n
37 RuntimeDirectory=%n
38 StateDirectory=%n
39
40 [Install]
41 WantedBy=multi-user.target
42 EOF
43
44 # Configure the preset setting for the unit file
45 mkdir /run/systemd/system-preset/
46 echo "disable $UNIT_NAME" >/run/systemd/system-preset/99-systemd-test.preset
47
48 EDITOR='true' script -ec 'systemctl edit "$UNIT_NAME"' /dev/null
49 [ ! -e "/etc/systemd/system/$UNIT_NAME.d/override.conf" ]
50
51 printf '%s\n' '[Service]' 'ExecStart=' 'ExecStart=sleep 10d' >"+4"
52 EDITOR='mv' script -ec 'systemctl edit "$UNIT_NAME"' /dev/null
53 printf '%s\n' '[Service]' 'ExecStart=' 'ExecStart=sleep 10d' | cmp - "/etc/systemd/system/$UNIT_NAME.d/override.conf"
54
55 printf '%b' '[Service]\n' 'ExecStart=\n' 'ExecStart=sleep 10d' >"+4"
56 EDITOR='mv' script -ec 'systemctl edit "$UNIT_NAME"' /dev/null
57 printf '%s\n' '[Service]' 'ExecStart=' 'ExecStart=sleep 10d' | cmp - "/etc/systemd/system/$UNIT_NAME.d/override.conf"
58
59 # Double free when editing a template unit (#26483)
60 EDITOR='true' script -ec 'systemctl edit user@0' /dev/null
61
62 # Argument help
63 systemctl --state help
64 systemctl --signal help
65 systemctl --type help
66
67 # list-dependencies
68 systemctl list-dependencies systemd-journald
69 systemctl list-dependencies --after systemd-journald
70 systemctl list-dependencies --before systemd-journald
71 systemctl list-dependencies --after --reverse systemd-journald
72 systemctl list-dependencies --before --reverse systemd-journald
73 systemctl list-dependencies --plain systemd-journald
74
75 # list-* verbs
76 systemctl list-units
77 systemctl list-units --recursive
78 systemctl list-units --type=socket
79 systemctl list-units --type=service,timer
80 # Compat: --type= allows load states for compatibility reasons
81 systemctl list-units --type=loaded
82 systemctl list-units --type=loaded,socket
83 systemctl list-units --legend=yes -a "systemd-*"
84 systemctl list-units --state=active
85 systemctl list-units --with-dependencies systemd-journald.service
86 systemctl list-units --with-dependencies --after systemd-journald.service
87 systemctl list-units --with-dependencies --before --reverse systemd-journald.service
88 systemctl list-sockets
89 systemctl list-sockets --legend=no -a "*journal*"
90 systemctl list-sockets --show-types
91 systemctl list-sockets --state=listening
92 systemctl list-timers -a -l
93 systemctl list-jobs
94 systemctl list-jobs --after
95 systemctl list-jobs --before
96 systemctl list-jobs --after --before
97 systemctl list-jobs "*"
98 systemctl list-dependencies sysinit.target --type=socket,mount
99 systemctl list-dependencies multi-user.target --state=active
100 systemctl list-dependencies sysinit.target --state=mounted --all
101 systemctl list-paths
102 systemctl list-paths --legend=no -a "systemd*"
103
104 test_list_unit_files() {
105 systemctl list-unit-files "$@"
106 systemctl list-unit-files "$@" "*journal*"
107 }
108
109 test_list_unit_files
110 test_list_unit_files --root=/
111
112 # is-* verbs
113 # Should return 4 for a missing unit file
114 assert_rc 4 systemctl --quiet is-active not-found.service
115 assert_rc 4 systemctl --quiet is-failed not-found.service
116 assert_rc 4 systemctl --quiet is-enabled not-found.service
117 # is-active: return 3 when the unit exists but inactive
118 assert_rc 3 systemctl --quiet is-active "$UNIT_NAME"
119 # is-enabled: return 1 when the unit exists but disabled
120 assert_rc 1 systemctl --quiet is-enabled "$UNIT_NAME"
121
122 # Basic service management
123 systemctl start --show-transaction "$UNIT_NAME"
124 systemctl status -n 5 "$UNIT_NAME"
125 systemctl is-active "$UNIT_NAME"
126 systemctl reload -T "$UNIT_NAME"
127 systemctl restart -T "$UNIT_NAME"
128 systemctl try-restart --show-transaction "$UNIT_NAME"
129 systemctl try-reload-or-restart --show-transaction "$UNIT_NAME"
130 systemctl kill "$UNIT_NAME"
131 (! systemctl is-active "$UNIT_NAME")
132 systemctl restart "$UNIT_NAME"
133 systemctl is-active "$UNIT_NAME"
134 systemctl restart "$UNIT_NAME"
135 systemctl stop "$UNIT_NAME"
136 (! systemctl is-active "$UNIT_NAME")
137
138 assert_eq "$(systemctl is-system-running)" "$(systemctl is-failed)"
139
140 # enable/disable/preset
141 test_enable_disable_preset() {
142 (! systemctl is-enabled "$@" "$UNIT_NAME")
143 systemctl enable "$@" "$UNIT_NAME"
144 systemctl is-enabled "$@" -l "$UNIT_NAME"
145 # We created a preset file for this unit above with a "disable" policy
146 systemctl preset "$@" "$UNIT_NAME"
147 (! systemctl is-enabled "$@" "$UNIT_NAME")
148 systemctl reenable "$@" "$UNIT_NAME"
149 systemctl is-enabled "$@" "$UNIT_NAME"
150 systemctl preset "$@" --preset-mode=enable-only "$UNIT_NAME"
151 systemctl is-enabled "$@" "$UNIT_NAME"
152 systemctl preset "$@" --preset-mode=disable-only "$UNIT_NAME"
153 (! systemctl is-enabled "$@" "$UNIT_NAME")
154 systemctl enable "$@" --runtime "$UNIT_NAME"
155 [[ -e "/run/systemd/system/multi-user.target.wants/$UNIT_NAME" ]]
156 systemctl is-enabled "$@" "$UNIT_NAME"
157 systemctl disable "$@" "$UNIT_NAME"
158 # The unit should be still enabled, as we didn't use the --runtime switch
159 systemctl is-enabled "$@" "$UNIT_NAME"
160 systemctl disable "$@" --runtime "$UNIT_NAME"
161 (! systemctl is-enabled "$@" "$UNIT_NAME")
162 }
163
164 test_enable_disable_preset
165 test_enable_disable_preset --root=/
166
167 # mask/unmask/revert
168 test_mask_unmask_revert() {
169 systemctl disable "$@" "$UNIT_NAME"
170 [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == disabled ]]
171 systemctl mask "$@" "$UNIT_NAME"
172 [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == masked ]]
173 systemctl unmask "$@" "$UNIT_NAME"
174 [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == disabled ]]
175 systemctl mask "$@" "$UNIT_NAME"
176 [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == masked ]]
177 systemctl revert "$@" "$UNIT_NAME"
178 [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == disabled ]]
179 systemctl mask "$@" --runtime "$UNIT_NAME"
180 [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == masked-runtime ]]
181 # This should be a no-op without the --runtime switch
182 systemctl unmask "$@" "$UNIT_NAME"
183 [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == masked-runtime ]]
184 systemctl unmask "$@" --runtime "$UNIT_NAME"
185 [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == disabled ]]
186 }
187
188 test_mask_unmask_revert
189 test_mask_unmask_revert --root=/
190
191 # add-wants/add-requires
192 (! systemctl show -P Wants "$UNIT_NAME" | grep "systemd-journald.service")
193 systemctl add-wants "$UNIT_NAME" "systemd-journald.service"
194 systemctl show -P Wants "$UNIT_NAME" | grep "systemd-journald.service"
195 (! systemctl show -P Requires "$UNIT_NAME" | grep "systemd-journald.service")
196 systemctl add-requires "$UNIT_NAME" "systemd-journald.service"
197 systemctl show -P Requires "$UNIT_NAME" | grep "systemd-journald.service"
198
199 # set-property
200 systemctl set-property "$UNIT_NAME" IPAccounting=yes MemoryMax=1234567
201 systemctl cat "$UNIT_NAME"
202 # These properties should be saved to a persistent storage
203 grep -r "IPAccounting=yes" "/etc/systemd/system.control/${UNIT_NAME}.d/"
204 grep -r "MemoryMax=1234567" "/etc/systemd/system.control/${UNIT_NAME}.d"
205 systemctl revert "$UNIT_NAME"
206 (! grep -r "IPAccounting=" "/etc/systemd/system.control/${UNIT_NAME}.d/")
207 (! grep -r "MemoryMax=" "/etc/systemd/system.control/${UNIT_NAME}.d/")
208 # Same stuff, but with --runtime, which should use /run
209 systemctl set-property --runtime "$UNIT_NAME" CPUAccounting=no CPUQuota=10%
210 systemctl cat "$UNIT_NAME"
211 grep -r "CPUAccounting=no" "/run/systemd/system.control/${UNIT_NAME}.d/"
212 grep -r "CPUQuota=10%" "/run/systemd/system.control/${UNIT_NAME}.d/"
213 systemctl revert "$UNIT_NAME"
214 (! grep -r "CPUAccounting=" "/run/systemd/system.control/${UNIT_NAME}.d/")
215 (! grep -r "CPUQuota=" "/run/systemd/system.control/${UNIT_NAME}.d/")
216
217 # Failed-unit related tests
218 (! systemd-run --wait --unit "failed.service" /bin/false)
219 systemctl is-failed failed.service
220 systemctl --state=failed | grep failed.service
221 systemctl --failed | grep failed.service
222 systemctl reset-failed "fail*.service"
223 (! systemctl is-failed failed.service)
224
225 # clean
226 systemctl restart "$UNIT_NAME"
227 systemctl stop "$UNIT_NAME"
228 # Check if the directories from *Directory= directives exist
229 # (except RuntimeDirectory= in /run, which is removed when the unit is stopped)
230 for path in /var/lib /var/cache /var/log /etc; do
231 [[ -e "$path/$UNIT_NAME" ]]
232 done
233 # Run the cleanup
234 for what in "" configuration state cache logs runtime all; do
235 systemctl clean ${what:+--what="$what"} "$UNIT_NAME"
236 done
237 # All respective directories should be removed
238 for path in /run /var/lib /var/cache /var/log /etc; do
239 [[ ! -e "$path/$UNIT_NAME" ]]
240 done
241
242 # --timestamp
243 for value in pretty us µs utc us+utc µs+utc; do
244 systemctl show -P KernelTimestamp --timestamp="$value"
245 done
246
247 # set-default/get-default
248 test_get_set_default() {
249 target="$(systemctl get-default "$@")"
250 systemctl set-default "$@" emergency.target
251 [[ "$(systemctl get-default "$@")" == emergency.target ]]
252 systemctl set-default "$@" "$target"
253 [[ "$(systemctl get-default "$@")" == "$target" ]]
254 }
255
256 test_get_set_default
257 test_get_set_default --root=/
258
259 # show/status
260 systemctl show --property ""
261 # Pick a heavily sandboxed unit for the best effect on coverage
262 systemctl show systemd-logind.service
263 systemctl status
264 # Ignore the exit code in this case, as it might try to load non-existing units
265 systemctl status -a >/dev/null || :
266 systemctl status -a --state active,running,plugged >/dev/null
267 systemctl status "systemd-*.timer"
268 systemctl status "systemd-journald*.socket"
269 systemctl status "sys-devices-*-ttyS0.device"
270 systemctl status -- -.mount
271 systemctl status 1
272
273 # --marked
274 systemctl restart "$UNIT_NAME"
275 systemctl set-property "$UNIT_NAME" Markers=needs-restart
276 systemctl show -P Markers "$UNIT_NAME" | grep needs-restart
277 systemctl reload-or-restart --marked
278 (! systemctl show -P Markers "$UNIT_NAME" | grep needs-restart)
279
280 # --dry-run with destructive verbs
281 # kexec is skipped intentionally, as it requires a bit more involved setup
282 VERBS=(
283 default
284 emergency
285 exit
286 halt
287 hibernate
288 hybrid-sleep
289 poweroff
290 reboot
291 rescue
292 suspend
293 suspend-then-hibernate
294 )
295
296 for verb in "${VERBS[@]}"; do
297 systemctl --dry-run "$verb"
298
299 if [[ "$verb" =~ (halt|poweroff|reboot) ]]; then
300 systemctl --dry-run --message "Hello world" "$verb"
301 systemctl --dry-run --no-wall "$verb"
302 systemctl --dry-run -f "$verb"
303 systemctl --dry-run -ff "$verb"
304 fi
305 done
306
307 # Aux verbs & assorted checks
308 systemctl is-active "*-journald.service"
309 systemctl cat "*journal*"
310 systemctl cat "$UNIT_NAME"
311 systemctl help "$UNIT_NAME"
312 systemctl service-watchdogs
313 systemctl service-watchdogs "$(systemctl service-watchdogs)"
314
315 # show/set-environment
316 # Make sure PATH is set
317 systemctl show-environment | grep -q '^PATH='
318 # Let's add an entry and override a built-in one
319 systemctl set-environment PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/testaddition FOO=BAR
320 # Check that both are set
321 systemctl show-environment | grep -q '^PATH=.*testaddition$'
322 systemctl show-environment | grep -q '^FOO=BAR$'
323 systemctl daemon-reload
324 # Check again after the reload
325 systemctl show-environment | grep -q '^PATH=.*testaddition$'
326 systemctl show-environment | grep -q '^FOO=BAR$'
327 # Check that JSON output is supported
328 systemctl show-environment --output=json | grep -q '^{.*"FOO":"BAR".*}$'
329 # Drop both
330 systemctl unset-environment FOO PATH
331 # Check that one is gone and the other reverted to the built-in
332 systemctl show-environment | grep '^FOO=$' && exit 1
333 systemctl show-environment | grep '^PATH=.*testaddition$' && exit 1
334 systemctl show-environment | grep -q '^PATH='
335 # Check import-environment
336 export IMPORT_THIS=hello
337 export IMPORT_THIS_TOO=world
338 systemctl import-environment IMPORT_THIS IMPORT_THIS_TOO
339 systemctl show-environment | grep "^IMPORT_THIS=$IMPORT_THIS"
340 systemctl show-environment | grep "^IMPORT_THIS_TOO=$IMPORT_THIS_TOO"
341 systemctl unset-environment IMPORT_THIS IMPORT_THIS_TOO
342 (! systemctl show-environment | grep "^IMPORT_THIS=")
343 (! systemctl show-environment | grep "^IMPORT_THIS_TOO=")
344
345 # test for sysv-generator (issue #24990)
346 if [[ -x /usr/lib/systemd/system-generators/systemd-sysv-generator ]]; then
347 # This is configurable via -Dsysvinit-path=, but we can't get the value
348 # at runtime, so let's just support the two most common paths for now.
349 [[ -d /etc/rc.d/init.d ]] && SYSVINIT_PATH="/etc/rc.d/init.d" || SYSVINIT_PATH="/etc/init.d"
350
351 # invalid dependency
352 cat >"${SYSVINIT_PATH:?}/issue-24990" <<\EOF
353 #!/bin/bash
354
355 ### BEGIN INIT INFO
356 # Provides:test1 test2
357 # Required-Start:test1 $remote_fs $network
358 # Required-Stop:test1 $remote_fs $network
359 # Description:Test
360 # Short-Description: Test
361 ### END INIT INFO
362
363 case "$1" in
364 start)
365 echo "Starting issue-24990.service"
366 sleep 1000 &
367 ;;
368 stop)
369 echo "Stopping issue-24990.service"
370 sleep 10 &
371 ;;
372 *)
373 echo "Usage: service test {start|stop|restart|status}"
374 ;;
375 esac
376 EOF
377
378 chmod +x "$SYSVINIT_PATH/issue-24990"
379 systemctl daemon-reload
380 [[ -L /run/systemd/generator.late/test1.service ]]
381 [[ -L /run/systemd/generator.late/test2.service ]]
382 assert_eq "$(readlink -f /run/systemd/generator.late/test1.service)" "/run/systemd/generator.late/issue-24990.service"
383 assert_eq "$(readlink -f /run/systemd/generator.late/test2.service)" "/run/systemd/generator.late/issue-24990.service"
384 output=$(systemctl cat issue-24990)
385 assert_in "SourcePath=$SYSVINIT_PATH/issue-24990" "$output"
386 assert_in "Description=LSB: Test" "$output"
387 assert_in "After=test1.service" "$output"
388 assert_in "After=remote-fs.target" "$output"
389 assert_in "After=network-online.target" "$output"
390 assert_in "Wants=network-online.target" "$output"
391 assert_in "ExecStart=$SYSVINIT_PATH/issue-24990 start" "$output"
392 assert_in "ExecStop=$SYSVINIT_PATH/issue-24990 stop" "$output"
393 systemctl status issue-24990 || :
394 systemctl show issue-24990
395 assert_not_in "issue-24990.service" "$(systemctl show --property=After --value)"
396 assert_not_in "issue-24990.service" "$(systemctl show --property=Before --value)"
397
398 if ! systemctl is-active network-online.target; then
399 systemctl start network-online.target
400 fi
401
402 systemctl restart issue-24990
403 systemctl stop issue-24990
404
405 # valid dependency
406 cat >"$SYSVINIT_PATH/issue-24990" <<\EOF
407 #!/bin/bash
408
409 ### BEGIN INIT INFO
410 # Provides:test1 test2
411 # Required-Start:$remote_fs
412 # Required-Stop:$remote_fs
413 # Description:Test
414 # Short-Description: Test
415 ### END INIT INFO
416
417 case "$1" in
418 start)
419 echo "Starting issue-24990.service"
420 sleep 1000 &
421 ;;
422 stop)
423 echo "Stopping issue-24990.service"
424 sleep 10 &
425 ;;
426 *)
427 echo "Usage: service test {start|stop|restart|status}"
428 ;;
429 esac
430 EOF
431
432 chmod +x "$SYSVINIT_PATH/issue-24990"
433 systemctl daemon-reload
434 [[ -L /run/systemd/generator.late/test1.service ]]
435 [[ -L /run/systemd/generator.late/test2.service ]]
436 assert_eq "$(readlink -f /run/systemd/generator.late/test1.service)" "/run/systemd/generator.late/issue-24990.service"
437 assert_eq "$(readlink -f /run/systemd/generator.late/test2.service)" "/run/systemd/generator.late/issue-24990.service"
438 output=$(systemctl cat issue-24990)
439 assert_in "SourcePath=$SYSVINIT_PATH/issue-24990" "$output"
440 assert_in "Description=LSB: Test" "$output"
441 assert_in "After=remote-fs.target" "$output"
442 assert_in "ExecStart=$SYSVINIT_PATH/issue-24990 start" "$output"
443 assert_in "ExecStop=$SYSVINIT_PATH/issue-24990 stop" "$output"
444 systemctl status issue-24990 || :
445 systemctl show issue-24990
446 assert_not_in "issue-24990.service" "$(systemctl show --property=After --value)"
447 assert_not_in "issue-24990.service" "$(systemctl show --property=Before --value)"
448
449 systemctl restart issue-24990
450 systemctl stop issue-24990
451 fi
452
453 # %J in WantedBy= causes ABRT (#26467)
454 cat >/run/systemd/system/test-WantedBy.service <<EOF
455 [Service]
456 ExecStart=true
457
458 [Install]
459 WantedBy=user-%i@%J.service
460 EOF
461 systemctl daemon-reload
462 systemctl enable --now test-WantedBy.service || :
463 systemctl daemon-reload
464
465 touch /testok