Files
systemd/shell-completion/bash/systemd-analyze
Daan De Meyer 91c98ff15c analyze: introduce restart-delays command
Working out how long the service manager keeps restarting a failing service
takes the exponential backoff formula from systemd.service(5) and a manual
simulation of the start rate limit. Getting either wrong means a service
that's supposed to fail after a while is restarted forever.

Let's add a restart-delays command that does both. It lists each restart
until the delay stops growing, with the total of the delays up to that
restart, and then says whether the start rate limit rejects a restart and
when. Like security, it reads the settings of loaded units from the service
manager, or loads unit files with --offline=yes. With --json=, it prints the
same data as a JSON object.

The command computes the delays with the same functions as the service
manager. It checks the start rate limit with ratelimit_below_at() for each
restart until the delay stops growing. After that, every restart waits the
same delay. We then compute the rejected restart from the number of starts
that the current window of the rate limit still allows.
2026-11-01 21:06:54 +01:00

327 lines
11 KiB
Bash

# shellcheck shell=bash
# systemd-analyze(1) completion -*- shell-script -*-
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is part of systemd.
#
# Copyright © 2010 Ran Benita
#
# 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
# 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 <https://www.gnu.org/licenses/>.
__contains_word () {
local w word=$1; shift
for w in "$@"; do
[[ $w = "$word" ]] && return
done
}
__get_machines() {
local a b
{ machinectl list --full --max-addresses=0 --no-legend --no-pager 2>/dev/null; echo ".host"; } |
{ while read -r a b; do echo " $a"; done; } |
sort -u
}
__get_units_all() {
systemctl list-units --no-legend --no-pager --plain --all $1 2>/dev/null |
{ while read -r a b c; do echo " $a"; done; }
}
__get_services() {
systemctl list-units --no-legend --no-pager --plain -t service --all $1 2>/dev/null |
{ while read -r a b c; do [[ $b == "loaded" ]] && echo " $a"; done; }
}
__get_syscall_sets() {
local line
systemd-analyze syscall-filter --no-pager 2>/dev/null | while IFS= read -r line; do
if [[ $line == @* ]]; then
printf '%s\n' "$line"
fi
done
}
__get_architectures() {
systemd-analyze --no-legend --no-pager architectures 2>/dev/null | { while read -r a b; do echo " $a"; done; }
}
__get_cpu_features() {
systemd-analyze --no-legend --no-pager cpu-features 2>/dev/null | { while read -r a b; do echo " $a"; done; }
}
__get_filesystem_sets() {
local line
systemd-analyze filesystems --no-pager 2>/dev/null | while IFS= read -r line; do
if [[ $line == @* ]]; then
printf '%s\n' "$line"
fi
done
}
_systemd_analyze() {
local i verb comps mode
local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} words cword
local -A OPTS=(
[STANDALONE]='-h --help --version --system --user --global --order --require --no-pager
--man=no --generators=yes -q --quiet'
[ARG]='-H --host -M --machine --fuzz --from-pattern --to-pattern --root --debugger
-A --debugger-arguments'
)
local -A VERBS=(
[STANDALONE]='time unit-files unit-paths compare-versions timestamp timespan srk has-tpm2 smbios11 image-policy'
[STANDALONE_JSON]='blame exit-status pcrs nvpcrs identify-tpm2 chid'
[CRITICAL_CHAIN]='critical-chain'
[DOT]='dot'
[DUMP]='dump'
[VERIFY]='verify'
[SECCOMP_FILTER]='syscall-filter'
[CAT_CONFIG]='cat-config'
[SECURITY]='security'
[CONDITION]='condition'
[INSPECT_ELF]='inspect-elf'
[DLOPEN_METADATA]='dlopen-metadata'
[PLOT]='plot'
[ARCHITECTURES]='architectures'
[CPU_FEATURES]='cpu-features'
[FDSTORE]='fdstore'
[RESTART_DELAYS]='restart-delays'
[CAPABILITY]='capability'
[TRANSIENT_SETTINGS]='transient-settings'
[UNIT_SHELL]='unit-shell'
[UNIT_GDB]='unit-gdb'
[FILESYSTEMS]='filesystems'
[CALENDAR]='calendar'
)
local CONFIGS='locale.conf systemd/bootchart.conf systemd/coredump.conf systemd/journald.conf
systemd/journal-remote.conf systemd/journal-upload.conf systemd/logind.conf
systemd/resolved.conf systemd/networkd.conf systemd/pstore.conf systemd/report-geoip.conf systemd/resolved.conf
systemd/sleep.conf systemd/system.conf systemd/timedated.conf
systemd/timesyncd.conf systemd/user.conf udev/udev.conf'
_init_completion || return
for ((i=0; i < COMP_CWORD; i++)); do
if __contains_word "${COMP_WORDS[i]}" ${VERBS[*]} &&
! __contains_word "${COMP_WORDS[i-1]}" ${OPTS[ARG]}; then
verb=${COMP_WORDS[i]}
break
fi
done
if __contains_word "--user" ${COMP_WORDS[*]}; then
mode=--user
else
mode=--system
fi
if __contains_word "$prev" ${OPTS[ARG]}; then
case $prev in
--host|-H)
comps=$(compgen -A hostname)
;;
--machine|-M)
comps=$( __get_machines )
;;
--debugger)
comps="gdb lldb"
;;
*)
return 0
;;
esac
COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
return 0
fi
if [[ -z ${verb-} && $cur = -* ]]; then
COMPREPLY=( $(compgen -W '${OPTS[*]}' -- "$cur") )
return 0
fi
if [[ -z ${verb-} ]]; then
comps=${VERBS[*]}
elif __contains_word "$verb" ${VERBS[STANDALONE]}; then
if [[ $cur = -* ]]; then
comps='--help --version --system --user --global --no-pager'
fi
elif __contains_word "$verb" ${VERBS[STANDALONE_JSON]}; then
if [[ $cur = -* ]]; then
comps='--help --version --system --user --global --no-pager --json=off --json=pretty --json=short'
fi
elif __contains_word "$verb" ${VERBS[CRITICAL_CHAIN]}; then
if [[ $cur = -* ]]; then
comps='--help --version --system --user --fuzz --no-pager'
else
comps=$( __get_units_all $mode )
fi
elif __contains_word "$verb" ${VERBS[DOT]}; then
if [[ $cur = -* ]]; then
comps='--help --version --system --user --global --from-pattern --to-pattern --order --require'
fi
elif __contains_word "$verb" ${VERBS[DUMP]}; then
if [[ $cur = -* ]]; then
comps='--help --version --system --user --no-pager'
else
comps=$( __get_units_all $mode )
fi
elif __contains_word "$verb" ${VERBS[SECCOMP_FILTER]}; then
if [[ $cur = -* ]]; then
comps='--help --version --no-pager'
else
comps=$( __get_syscall_sets )
fi
elif __contains_word "$verb" ${VERBS[VERIFY]}; then
if [[ $cur = -* ]]; then
comps='--help --version --system --user --global --man=no --generators=yes --root --image --recursive-errors=no --recursive-errors=yes --recursive-errors=one'
else
comps=$( compgen -A file -- "$cur"
__get_units_all $mode )
compopt -o filenames
fi
elif __contains_word "$verb" ${VERBS[CAT_CONFIG]}; then
if [[ $cur = -* ]]; then
comps='--help --version --root --no-pager'
elif [[ -z $cur ]]; then
comps="$CONFIGS"
compopt -o filenames
else
comps="$CONFIGS $( compgen -A file -- "$cur" )"
compopt -o filenames
fi
elif __contains_word "$verb" ${VERBS[SECURITY]}; then
if [[ $cur = -* ]]; then
comps='--help --version --no-pager --system --user -H --host -M --machine --offline --threshold --security-policy --json=off --json=pretty --json=short --root --image --profile=default --profile=nonetwork --profile=strict --profile=trusted'
elif ! __contains_word "--offline" ${COMP_WORDS[*]}; then
comps=$( __get_services $mode )
else
comps="$CONFIGS $( compgen -A file -- "$cur" )"
compopt -o filenames
fi
elif __contains_word "$verb" ${VERBS[CONDITION]}; then
if [[ $cur = -* ]]; then
comps='--help --version --system --user --global --no-pager --root --image'
elif [[ $prev = "-u" ]] || [[ $prev = "--unit" ]]; then
comps=$( __get_services $mode )
fi
elif __contains_word "$verb" ${VERBS[INSPECT_ELF]}; then
if [[ $cur = -* ]]; then
comps='--help --version --json=off --json=pretty --json=short'
else
comps=$( compgen -A file -- "$cur" )
compopt -o filenames
fi
elif __contains_word "$verb" ${VERBS[DLOPEN_METADATA]}; then
if [[ $cur = -* ]]; then
comps='--help --version --json=off --json=pretty --json=short'
else
comps=$( compgen -A file -- "$cur" )
compopt -o filenames
fi
elif __contains_word "$verb" ${VERBS[PLOT]}; then
if [[ $cur = -* ]]; then
comps='--help --version --system --user --global --no-pager --json=off --json=pretty --json=short --table --no-legend --scale-svg --detailed'
fi
elif __contains_word "$verb" ${VERBS[ARCHITECTURES]}; then
if [[ $cur = -* ]]; then
comps='--help --version --no-pager --json=off --json=pretty --json=short --no-legend'
else
comps=$( __get_architectures )
fi
elif __contains_word "$verb" ${VERBS[CPU_FEATURES]}; then
if [[ $cur = -* ]]; then
comps='--help --version --no-pager --json=off --json=pretty --json=short --no-legend'
else
comps=$( __get_cpu_features )
fi
elif __contains_word "$verb" ${VERBS[FDSTORE]}; then
if [[ $cur = -* ]]; then
comps='--help --version --system --user --global -H --host -M --machine --no-pager --json=off --json=pretty --json=short --root --image'
else
comps=$( __get_services $mode )
fi
elif __contains_word "$verb" ${VERBS[RESTART_DELAYS]}; then
if [[ $cur = -* ]]; then
comps='--help --version --system --user -H --host -M --machine --no-pager --no-legend --json=off --json=pretty --json=short --offline --root --image --instance --generators'
elif ! __contains_word "--offline" ${COMP_WORDS[*]}; then
comps=$( __get_services $mode )
else
comps=$( compgen -A file -- "$cur" )
compopt -o filenames
fi
elif __contains_word "$verb" ${VERBS[CAPABILITY]}; then
if [[ $cur = -* ]]; then
comps='--help --version --no-pager --json=off --json=pretty --json=short -m --mask'
fi
elif __contains_word "$verb" ${VERBS[TRANSIENT_SETTINGS]}; then
if [[ $cur = -* ]]; then
comps='--help --version --no-pager'
else
comps="$(systemctl --no-legend --no-pager -t help)"
fi
elif __contains_word "$verb" ${VERBS[UNIT_SHELL]}; then
if [[ $cur = -* ]]; then
comps='--help --version'
else
comps=$( __get_services $mode )
fi
elif __contains_word "$verb" ${VERBS[UNIT_GDB]}; then
if [[ $cur = -* ]]; then
comps='--help --version --debugger --debugger-arguments --root'
else
comps=$( __get_services $mode )
fi
elif __contains_word "$verb" ${VERBS[FILESYSTEMS]}; then
if [[ $cur = -* ]]; then
comps='--help --version --no-pager'
else
comps=$( __get_filesystem_sets )
fi
elif __contains_word "$verb" ${VERBS[CALENDAR]}; then
if [[ $cur = -* ]]; then
comps='--help --version --iterations --base-time'
fi
fi
COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
return 0
}
complete -F _systemd_analyze systemd-analyze