summaryrefslogtreecommitdiffstats
path: root/test/units/TEST-26-SYSTEMCTL.sh
diff options
context:
space:
mode:
Diffstat (limited to 'test/units/TEST-26-SYSTEMCTL.sh')
-rwxr-xr-xtest/units/TEST-26-SYSTEMCTL.sh501
1 files changed, 501 insertions, 0 deletions
diff --git a/test/units/TEST-26-SYSTEMCTL.sh b/test/units/TEST-26-SYSTEMCTL.sh
new file mode 100755
index 0000000..ae7a5d6
--- /dev/null
+++ b/test/units/TEST-26-SYSTEMCTL.sh
@@ -0,0 +1,501 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+at_exit() {
+ if [[ -v UNIT_NAME && -e "/usr/lib/systemd/system/$UNIT_NAME" ]]; then
+ rm -fvr "/usr/lib/systemd/system/$UNIT_NAME" "/etc/systemd/system/$UNIT_NAME.d" "+4"
+ fi
+
+ rm -f /etc/init.d/issue-24990
+ return 0
+}
+
+# Create a simple unit file for testing
+# Note: the service file is created under /usr on purpose to test
+# the 'revert' verb as well
+export UNIT_NAME="systemctl-test-$RANDOM.service"
+export UNIT_NAME2="systemctl-test-$RANDOM.service"
+
+cat >"/usr/lib/systemd/system/$UNIT_NAME" <<\EOF
+[Unit]
+Description=systemctl test
+
+[Service]
+ExecStart=sleep infinity
+ExecReload=true
+
+# For systemctl clean
+CacheDirectory=%n
+ConfigurationDirectory=%n
+LogsDirectory=%n
+RuntimeDirectory=%n
+StateDirectory=%n
+
+[Install]
+WantedBy=multi-user.target
+EOF
+
+# Configure the preset setting for the unit file
+mkdir /run/systemd/system-preset/
+echo "disable $UNIT_NAME" >/run/systemd/system-preset/99-systemd-test.preset
+
+EDITOR='true' script -ec 'systemctl edit "$UNIT_NAME"' /dev/null
+[ ! -e "/etc/systemd/system/$UNIT_NAME.d/override.conf" ]
+
+printf '%s\n' '[Service]' 'ExecStart=' 'ExecStart=sleep 10d' >"+4"
+EDITOR='mv' script -ec 'systemctl edit "$UNIT_NAME"' /dev/null
+printf '%s\n' '[Service]' 'ExecStart=' 'ExecStart=sleep 10d' | cmp - "/etc/systemd/system/$UNIT_NAME.d/override.conf"
+
+printf '%b' '[Service]\n' 'ExecStart=\n' 'ExecStart=sleep 10d' >"+4"
+EDITOR='mv' script -ec 'systemctl edit "$UNIT_NAME"' /dev/null
+printf '%s\n' '[Service]' 'ExecStart=' 'ExecStart=sleep 10d' | cmp - "/etc/systemd/system/$UNIT_NAME.d/override.conf"
+
+systemctl edit "$UNIT_NAME" --stdin --drop-in=override2.conf <<EOF
+[Unit]
+Description=spectacular
+# this comment should remain
+
+EOF
+printf '%s\n' '[Unit]' 'Description=spectacular' '# this comment should remain' | \
+ cmp - "/etc/systemd/system/$UNIT_NAME.d/override2.conf"
+
+# Test simultaneous editing of two units and creation of drop-in for a nonexistent unit
+systemctl edit "$UNIT_NAME" "$UNIT_NAME2" --stdin --force --drop-in=override2.conf <<<'[X-Section]'
+printf '%s\n' '[X-Section]' | cmp - "/etc/systemd/system/$UNIT_NAME.d/override2.conf"
+printf '%s\n' '[X-Section]' | cmp - "/etc/systemd/system/$UNIT_NAME2.d/override2.conf"
+
+# Double free when editing a template unit (#26483)
+EDITOR='true' script -ec 'systemctl edit user@0' /dev/null
+
+# Argument help
+systemctl --state help
+systemctl --signal help
+systemctl --type help
+
+# list-dependencies
+systemctl list-dependencies systemd-journald
+systemctl list-dependencies --after systemd-journald
+systemctl list-dependencies --before systemd-journald
+systemctl list-dependencies --after --reverse systemd-journald
+systemctl list-dependencies --before --reverse systemd-journald
+systemctl list-dependencies --plain systemd-journald
+
+# list-* verbs
+systemctl list-units
+systemctl list-units --recursive
+systemctl list-units --type=socket
+systemctl list-units --type=service,timer
+# Compat: --type= allows load states for compatibility reasons
+systemctl list-units --type=loaded
+systemctl list-units --type=loaded,socket
+systemctl list-units --legend=yes -a "systemd-*"
+systemctl list-units --state=active
+systemctl list-units --with-dependencies systemd-journald.service
+systemctl list-units --with-dependencies --after systemd-journald.service
+systemctl list-units --with-dependencies --before --reverse systemd-journald.service
+systemctl list-sockets
+systemctl list-sockets --legend=no -a "*journal*"
+systemctl list-sockets --show-types
+systemctl list-sockets --state=listening
+systemctl list-timers -a -l
+systemctl list-jobs
+systemctl list-jobs --after
+systemctl list-jobs --before
+systemctl list-jobs --after --before
+systemctl list-jobs "*"
+systemctl list-dependencies sysinit.target --type=socket,mount
+systemctl list-dependencies multi-user.target --state=active
+systemctl list-dependencies sysinit.target --state=mounted --all
+systemctl list-paths
+systemctl list-paths --legend=no -a "systemd*"
+
+test_list_unit_files() {
+ systemctl list-unit-files "$@"
+ systemctl list-unit-files "$@" "*journal*"
+}
+
+test_list_unit_files
+test_list_unit_files --root=/
+
+# is-* verbs
+# Should return 4 for a missing unit file
+assert_rc 4 systemctl --quiet is-active not-found.service
+assert_rc 4 systemctl --quiet is-failed not-found.service
+assert_rc 4 systemctl --quiet is-enabled not-found.service
+# is-active: return 3 when the unit exists but inactive
+assert_rc 3 systemctl --quiet is-active "$UNIT_NAME"
+# is-enabled: return 1 when the unit exists but disabled
+assert_rc 1 systemctl --quiet is-enabled "$UNIT_NAME"
+
+# Basic service management
+systemctl start --show-transaction "$UNIT_NAME"
+systemctl status -n 5 "$UNIT_NAME"
+systemctl is-active "$UNIT_NAME"
+systemctl reload -T "$UNIT_NAME"
+systemctl restart -T "$UNIT_NAME"
+systemctl try-restart --show-transaction "$UNIT_NAME"
+systemctl try-reload-or-restart --show-transaction "$UNIT_NAME"
+timeout 10 systemctl kill --wait "$UNIT_NAME"
+(! systemctl is-active "$UNIT_NAME")
+systemctl restart "$UNIT_NAME"
+systemctl is-active "$UNIT_NAME"
+systemctl restart "$UNIT_NAME"
+systemctl stop "$UNIT_NAME"
+(! systemctl is-active "$UNIT_NAME")
+
+assert_eq "$(systemctl is-system-running)" "$(systemctl is-failed)"
+
+# enable/disable/preset
+test_enable_disable_preset() {
+ (! systemctl is-enabled "$@" "$UNIT_NAME")
+ systemctl enable "$@" "$UNIT_NAME"
+ systemctl is-enabled "$@" -l "$UNIT_NAME"
+ # We created a preset file for this unit above with a "disable" policy
+ systemctl preset "$@" "$UNIT_NAME"
+ (! systemctl is-enabled "$@" "$UNIT_NAME")
+ systemctl reenable "$@" "$UNIT_NAME"
+ systemctl is-enabled "$@" "$UNIT_NAME"
+ systemctl preset "$@" --preset-mode=enable-only "$UNIT_NAME"
+ systemctl is-enabled "$@" "$UNIT_NAME"
+ systemctl preset "$@" --preset-mode=disable-only "$UNIT_NAME"
+ (! systemctl is-enabled "$@" "$UNIT_NAME")
+ systemctl enable "$@" --runtime "$UNIT_NAME"
+ [[ -e "/run/systemd/system/multi-user.target.wants/$UNIT_NAME" ]]
+ systemctl is-enabled "$@" "$UNIT_NAME"
+ systemctl disable "$@" "$UNIT_NAME"
+ # The unit should be still enabled, as we didn't use the --runtime switch
+ systemctl is-enabled "$@" "$UNIT_NAME"
+ systemctl disable "$@" --runtime "$UNIT_NAME"
+ (! systemctl is-enabled "$@" "$UNIT_NAME")
+}
+
+test_enable_disable_preset
+test_enable_disable_preset --root=/
+
+# mask/unmask/revert
+test_mask_unmask_revert() {
+ systemctl disable "$@" "$UNIT_NAME"
+ [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == disabled ]]
+ systemctl mask "$@" "$UNIT_NAME"
+ [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == masked ]]
+ systemctl unmask "$@" "$UNIT_NAME"
+ [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == disabled ]]
+ systemctl mask "$@" "$UNIT_NAME"
+ [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == masked ]]
+ systemctl revert "$@" "$UNIT_NAME"
+ [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == disabled ]]
+ systemctl mask "$@" --runtime "$UNIT_NAME"
+ [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == masked-runtime ]]
+ # This should be a no-op without the --runtime switch
+ systemctl unmask "$@" "$UNIT_NAME"
+ [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == masked-runtime ]]
+ systemctl unmask "$@" --runtime "$UNIT_NAME"
+ [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == disabled ]]
+}
+
+test_mask_unmask_revert
+test_mask_unmask_revert --root=/
+
+# disable --now with template unit
+cat >/run/systemd/system/test-disable@.service <<EOF
+[Service]
+ExecStart=sleep infinity
+
+[Install]
+WantedBy=multi-user.target
+EOF
+systemctl enable --now test-disable@1.service test-disable@2.service
+systemctl is-active test-disable@1.service
+systemctl is-active test-disable@2.service
+systemctl disable --now test-disable@.service
+for u in test-disable@{1,2}.service; do
+ (! systemctl is-active "$u")
+ (! systemctl is-enabled "$u")
+done
+rm /run/systemd/system/test-disable@.service
+
+# add-wants/add-requires
+(! systemctl show -P Wants "$UNIT_NAME" | grep "systemd-journald.service")
+systemctl add-wants "$UNIT_NAME" "systemd-journald.service"
+systemctl show -P Wants "$UNIT_NAME" | grep "systemd-journald.service"
+(! systemctl show -P Requires "$UNIT_NAME" | grep "systemd-journald.service")
+systemctl add-requires "$UNIT_NAME" "systemd-journald.service"
+systemctl show -P Requires "$UNIT_NAME" | grep "systemd-journald.service"
+
+# set-property
+systemctl set-property "$UNIT_NAME" IPAccounting=yes MemoryMax=1234567
+systemctl cat "$UNIT_NAME"
+# These properties should be saved to a persistent storage
+grep -r "IPAccounting=yes" "/etc/systemd/system.control/${UNIT_NAME}.d/"
+grep -r "MemoryMax=1234567" "/etc/systemd/system.control/${UNIT_NAME}.d"
+systemctl revert "$UNIT_NAME"
+(! grep -r "IPAccounting=" "/etc/systemd/system.control/${UNIT_NAME}.d/")
+(! grep -r "MemoryMax=" "/etc/systemd/system.control/${UNIT_NAME}.d/")
+# Same stuff, but with --runtime, which should use /run
+systemctl set-property --runtime "$UNIT_NAME" CPUAccounting=no CPUQuota=10%
+systemctl cat "$UNIT_NAME"
+grep -r "CPUAccounting=no" "/run/systemd/system.control/${UNIT_NAME}.d/"
+grep -r "CPUQuota=10.00%" "/run/systemd/system.control/${UNIT_NAME}.d/"
+systemctl revert "$UNIT_NAME"
+(! grep -r "CPUAccounting=" "/run/systemd/system.control/${UNIT_NAME}.d/")
+(! grep -r "CPUQuota=" "/run/systemd/system.control/${UNIT_NAME}.d/")
+
+# Failed-unit related tests
+(! systemd-run --wait --unit "failed.service" /bin/false)
+systemctl is-failed failed.service
+systemctl --state=failed | grep failed.service
+systemctl --failed | grep failed.service
+systemctl reset-failed "fail*.service"
+(! systemctl is-failed failed.service)
+
+# clean
+systemctl restart "$UNIT_NAME"
+systemctl stop "$UNIT_NAME"
+# Check if the directories from *Directory= directives exist
+# (except RuntimeDirectory= in /run, which is removed when the unit is stopped)
+for path in /var/lib /var/cache /var/log /etc; do
+ [[ -e "$path/$UNIT_NAME" ]]
+done
+# Run the cleanup
+for what in "" configuration state cache logs runtime all; do
+ systemctl clean ${what:+--what="$what"} "$UNIT_NAME"
+done
+# All respective directories should be removed
+for path in /run /var/lib /var/cache /var/log /etc; do
+ [[ ! -e "$path/$UNIT_NAME" ]]
+done
+
+# --timestamp
+for value in pretty us µs utc us+utc µs+utc; do
+ systemctl show -P KernelTimestamp --timestamp="$value"
+done
+
+# set-default/get-default
+test_get_set_default() {
+ target="$(systemctl get-default "$@")"
+ systemctl set-default "$@" emergency.target
+ [[ "$(systemctl get-default "$@")" == emergency.target ]]
+ systemctl set-default "$@" "$target"
+ [[ "$(systemctl get-default "$@")" == "$target" ]]
+}
+
+test_get_set_default
+test_get_set_default --root=/
+
+# show/status
+systemctl show --property ""
+# Pick a heavily sandboxed unit for the best effect on coverage
+systemctl show systemd-logind.service
+systemctl status
+# Ignore the exit code in this case, as it might try to load non-existing units
+systemctl status -a >/dev/null || :
+systemctl status -a --state active,running,plugged >/dev/null
+systemctl status "systemd-*.timer"
+systemctl status "systemd-journald*.socket"
+systemctl status "sys-devices-*-ttyS0.device"
+systemctl status -- -.mount
+systemctl status 1
+
+# --marked
+systemctl restart "$UNIT_NAME"
+systemctl set-property "$UNIT_NAME" Markers=needs-restart
+systemctl show -P Markers "$UNIT_NAME" | grep needs-restart
+systemctl reload-or-restart --marked
+(! systemctl show -P Markers "$UNIT_NAME" | grep needs-restart)
+
+# --dry-run with destructive verbs
+# kexec is skipped intentionally, as it requires a bit more involved setup
+VERBS=(
+ default
+ emergency
+ exit
+ halt
+ hibernate
+ hybrid-sleep
+ poweroff
+ reboot
+ rescue
+ suspend
+ suspend-then-hibernate
+)
+
+for verb in "${VERBS[@]}"; do
+ systemctl --dry-run "$verb"
+
+ if [[ "$verb" =~ (halt|poweroff|reboot) ]]; then
+ systemctl --dry-run --message "Hello world" "$verb"
+ systemctl --dry-run --no-wall "$verb"
+ systemctl --dry-run -f "$verb"
+ systemctl --dry-run -ff "$verb"
+ fi
+done
+
+# Aux verbs & assorted checks
+systemctl is-active "*-journald.service"
+systemctl cat "*udevd*"
+systemctl cat "$UNIT_NAME"
+systemctl help "$UNIT_NAME"
+systemctl service-watchdogs
+systemctl service-watchdogs "$(systemctl service-watchdogs)"
+
+# show/set-environment
+# Make sure PATH is set
+systemctl show-environment | grep -q '^PATH='
+# Let's add an entry and override a built-in one
+systemctl set-environment PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/testaddition FOO=BAR
+# Check that both are set
+systemctl show-environment | grep -q '^PATH=.*testaddition$'
+systemctl show-environment | grep -q '^FOO=BAR$'
+systemctl daemon-reload
+# Check again after the reload
+systemctl show-environment | grep -q '^PATH=.*testaddition$'
+systemctl show-environment | grep -q '^FOO=BAR$'
+# Check that JSON output is supported
+systemctl show-environment --output=json | grep -q '^{.*"FOO":"BAR".*}$'
+# Drop both
+systemctl unset-environment FOO PATH
+# Check that one is gone and the other reverted to the built-in
+systemctl show-environment | grep '^FOO=$' && exit 1
+systemctl show-environment | grep '^PATH=.*testaddition$' && exit 1
+systemctl show-environment | grep -q '^PATH='
+# Check import-environment
+export IMPORT_THIS=hello
+export IMPORT_THIS_TOO=world
+systemctl import-environment IMPORT_THIS IMPORT_THIS_TOO
+systemctl show-environment | grep "^IMPORT_THIS=$IMPORT_THIS"
+systemctl show-environment | grep "^IMPORT_THIS_TOO=$IMPORT_THIS_TOO"
+systemctl unset-environment IMPORT_THIS IMPORT_THIS_TOO
+(! systemctl show-environment | grep "^IMPORT_THIS=")
+(! systemctl show-environment | grep "^IMPORT_THIS_TOO=")
+
+# test for sysv-generator (issue #24990)
+if [[ -x /usr/lib/systemd/system-generators/systemd-sysv-generator ]]; then
+ # This is configurable via -Dsysvinit-path=, but we can't get the value
+ # at runtime, so let's just support the two most common paths for now.
+ [[ -d /etc/rc.d/init.d ]] && SYSVINIT_PATH="/etc/rc.d/init.d" || SYSVINIT_PATH="/etc/init.d"
+
+ # OpenSUSE leaves sysvinit-path enabled, which means systemd-sysv-generator is built
+ # but may not create the directory if there's no services that use it.
+ mkdir -p "$SYSVINIT_PATH"
+
+ # invalid dependency
+ cat >"${SYSVINIT_PATH:?}/issue-24990" <<\EOF
+#!/bin/bash
+
+### BEGIN INIT INFO
+# Provides:test1 test2
+# Required-Start:test1 $remote_fs $network
+# Required-Stop:test1 $remote_fs $network
+# Description:Test
+# Short-Description: Test
+### END INIT INFO
+
+case "$1" in
+ start)
+ echo "Starting issue-24990.service"
+ sleep 1000 &
+ ;;
+ stop)
+ echo "Stopping issue-24990.service"
+ sleep 10 &
+ ;;
+ *)
+ echo "Usage: service test {start|stop|restart|status}"
+ ;;
+esac
+EOF
+
+ chmod +x "$SYSVINIT_PATH/issue-24990"
+ systemctl daemon-reload
+ [[ -L /run/systemd/generator.late/test1.service ]]
+ [[ -L /run/systemd/generator.late/test2.service ]]
+ assert_eq "$(readlink -f /run/systemd/generator.late/test1.service)" "/run/systemd/generator.late/issue-24990.service"
+ assert_eq "$(readlink -f /run/systemd/generator.late/test2.service)" "/run/systemd/generator.late/issue-24990.service"
+ output=$(systemctl cat issue-24990)
+ assert_in "SourcePath=$SYSVINIT_PATH/issue-24990" "$output"
+ assert_in "Description=LSB: Test" "$output"
+ assert_in "After=test1.service" "$output"
+ assert_in "After=remote-fs.target" "$output"
+ assert_in "After=network-online.target" "$output"
+ assert_in "Wants=network-online.target" "$output"
+ assert_in "ExecStart=$SYSVINIT_PATH/issue-24990 start" "$output"
+ assert_in "ExecStop=$SYSVINIT_PATH/issue-24990 stop" "$output"
+ systemctl status issue-24990 || :
+ systemctl show issue-24990
+ assert_not_in "issue-24990.service" "$(systemctl show --property=After --value)"
+ assert_not_in "issue-24990.service" "$(systemctl show --property=Before --value)"
+
+ if ! systemctl is-active network-online.target; then
+ systemctl start network-online.target
+ fi
+
+ systemctl restart issue-24990
+ systemctl stop issue-24990
+
+ # valid dependency
+ cat >"$SYSVINIT_PATH/issue-24990" <<\EOF
+#!/bin/bash
+
+### BEGIN INIT INFO
+# Provides:test1 test2
+# Required-Start:$remote_fs
+# Required-Stop:$remote_fs
+# Description:Test
+# Short-Description: Test
+### END INIT INFO
+
+case "$1" in
+ start)
+ echo "Starting issue-24990.service"
+ sleep 1000 &
+ ;;
+ stop)
+ echo "Stopping issue-24990.service"
+ sleep 10 &
+ ;;
+ *)
+ echo "Usage: service test {start|stop|restart|status}"
+ ;;
+esac
+EOF
+
+ chmod +x "$SYSVINIT_PATH/issue-24990"
+ systemctl daemon-reload
+ [[ -L /run/systemd/generator.late/test1.service ]]
+ [[ -L /run/systemd/generator.late/test2.service ]]
+ assert_eq "$(readlink -f /run/systemd/generator.late/test1.service)" "/run/systemd/generator.late/issue-24990.service"
+ assert_eq "$(readlink -f /run/systemd/generator.late/test2.service)" "/run/systemd/generator.late/issue-24990.service"
+ output=$(systemctl cat issue-24990)
+ assert_in "SourcePath=$SYSVINIT_PATH/issue-24990" "$output"
+ assert_in "Description=LSB: Test" "$output"
+ assert_in "After=remote-fs.target" "$output"
+ assert_in "ExecStart=$SYSVINIT_PATH/issue-24990 start" "$output"
+ assert_in "ExecStop=$SYSVINIT_PATH/issue-24990 stop" "$output"
+ systemctl status issue-24990 || :
+ systemctl show issue-24990
+ assert_not_in "issue-24990.service" "$(systemctl show --property=After --value)"
+ assert_not_in "issue-24990.service" "$(systemctl show --property=Before --value)"
+
+ systemctl restart issue-24990
+ systemctl stop issue-24990
+fi
+
+# %J in WantedBy= causes ABRT (#26467)
+cat >/run/systemd/system/test-WantedBy.service <<EOF
+[Service]
+ExecStart=true
+
+[Install]
+WantedBy=user-%i@%J.service
+EOF
+systemctl daemon-reload
+systemctl enable --now test-WantedBy.service || :
+systemctl daemon-reload
+
+touch /testok