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