yunohost/helpers/helpers.v2.1.d/systemd
Félix Piédallu eb97b3632a helpers: ynh_remove_systemd_config: Also remove the systemd service from YunoHost.
Every app or almost will do that, because it doesn't make sense to remove the systemd config but not the associated yunohost configuration.

This will clean up a bit the remove scripts.
2024-06-28 11:52:56 +02:00

186 lines
7.5 KiB
Bash

#!/bin/bash
# Create a dedicated systemd config
#
# usage: ynh_config_add_systemd [--service=service] [--template=template]
# | arg: --service= - Service name (optionnal, `$app` by default)
# | arg: --template= - Name of template file (optionnal, this is 'systemd' by default, meaning `../conf/systemd.service` will be used as template)
#
# This will use the template `../conf/<templatename>.service`.
#
# See the documentation of `ynh_config_add` for a description of the template
# format and how placeholders are replaced with actual variables.
ynh_config_add_systemd() {
# ============ Argument parsing =============
local -A args_array=([s]=service= [t]=template=)
local service
local template
ynh_handle_getopts_args "$@"
service="${service:-$app}"
template="${template:-systemd.service}"
# ===========================================
ynh_config_add --template="$template" --destination="/etc/systemd/system/$service.service"
systemctl enable $service --quiet
systemctl daemon-reload
}
# Remove the dedicated systemd config, and if configured into YunoHost, removes the service.
#
# usage: ynh_config_remove_systemd service
# | arg: service - Service name (optionnal, $app by default)
ynh_config_remove_systemd() {
local service="${1:-$app}"
if ynh_exec_warn_less yunohost service status "$app" >/dev/null; then
yunohost service remove "$app"
fi
if [ -e "/etc/systemd/system/$service.service" ]; then
ynh_systemctl --service="$service" --action=stop
systemctl disable "$service" --quiet
ynh_safe_rm "/etc/systemd/system/$service.service"
systemctl daemon-reload
fi
}
# Start (or other actions) a service, print a log in case of failure and optionnaly wait until the service is completely started
#
# usage: ynh_systemctl [--service=service] [--action=action] [ [--wait_until="line to match"] [--log_path=log_path] [--timeout=300] [--length=20] ]
# | arg: --service= - Name of the service to start. Default : `$app`
# | arg: --action= - Action to perform with systemctl. Default: start
# | arg: --wait_until= - The pattern to find in the log to attest the service is effectively fully started.
# | arg: --log_path= - Log file - Path to the log file. Default : `/var/log/$app/$app.log`
# | arg: --timeout= - Timeout - The maximum time to wait before ending the watching. Default : 60 seconds.
# | arg: --length= - Length of the error log displayed for debugging : Default : 20
ynh_systemctl() {
# ============ Argument parsing =============
local -A args_array=([n]=service= [a]=action= [w]=wait_until= [p]=log_path= [t]=timeout= [e]=length=)
local service
local action
local wait_until
local length
local log_path
local timeout
ynh_handle_getopts_args "$@"
service="${service:-$app}"
action=${action:-start}
wait_until=${wait_until:-}
length=${length:-20}
log_path="${log_path:-/var/log/$service/$service.log}"
timeout=${timeout:-60}
# ===========================================
# On CI, use length=100 because it's sometime hell to debug otherwise for super-long output
if ynh_in_ci_tests && [ $length -le 20 ]
then
length=100
fi
# Manage case of service already stopped
if [ "$action" == "stop" ] && ! systemctl is-active --quiet $service; then
return 0
fi
# Start to read the log
if [[ -n "$wait_until" ]]; then
local templog="$(mktemp)"
# Following the starting of the app in its log
if [ "$log_path" == "systemd" ]; then
# Read the systemd journal
journalctl --unit=$service --follow --since=-0 --quiet >"$templog" &
# Get the PID of the journalctl command
local pid_tail=$!
else
# Read the specified log file
tail --follow=name --retry --lines=0 "$log_path" >"$templog" 2>&1 &
# Get the PID of the tail command
local pid_tail=$!
fi
fi
# Use reload-or-restart instead of reload. So it wouldn't fail if the service isn't running.
if [ "$action" == "reload" ]; then
action="reload-or-restart"
fi
local time_start="$(date --utc --rfc-3339=seconds | cut -d+ -f1) UTC"
# If the service fails to perform the action
if ! systemctl $action $service; then
# Show syslog for this service
journalctl --quiet --no-hostname --no-pager --lines=$length --unit=$service >&2
# If a log is specified for this service, show also the content of this log
if [ -e "$log_path" ]; then
tail --lines=$length "$log_path" >&2
fi
_ynh_clean_check_starting
return 1
fi
# Start the timeout and try to find wait_until
if [[ -n "${wait_until:-}" ]]; then
set +o xtrace # set +x
local i=0
local starttime=$(date +%s)
for i in $(seq 1 $timeout); do
# Read the log until the sentence is found, that means the app finished to start. Or run until the timeout
if [ "$log_path" == "systemd" ]; then
# For systemd services, we in fact dont rely on the templog, which for some reason is not reliable, but instead re-read journalctl every iteration, starting at the timestamp where we triggered the action
if journalctl --unit=$service --since="$time_start" --quiet --no-pager --no-hostname | grep --extended-regexp --quiet "$wait_until"; then
ynh_print_info "The service $service has correctly executed the action ${action}."
break
fi
else
if grep --extended-regexp --quiet "$wait_until" "$templog"; then
ynh_print_info "The service $service has correctly executed the action ${action}."
break
fi
fi
if [ $i -eq 30 ]; then
echo "(this may take some time)" >&2
fi
# Also check the timeout using actual timestamp, because sometimes for some reason,
# journalctl may take a huge time to run, and we end up waiting literally an entire hour
# instead of 5 min ...
if [[ "$(( $(date +%s) - $starttime))" -gt "$timeout" ]]
then
i=$timeout
break
fi
sleep 1
done
set -o xtrace # set -x
if [ $i -ge 3 ]; then
echo "" >&2
fi
if [ $i -eq $timeout ]; then
ynh_print_warn "The service $service didn't fully executed the action ${action} before the timeout."
ynh_print_warn "Please find here an extract of the end of the log of the service $service:"
journalctl --quiet --no-hostname --no-pager --lines=$length --unit=$service >&2
if [ -e "$log_path" ]; then
ynh_print_warn "==="
tail --lines=$length "$log_path" >&2
fi
# If we tried to reload/start/restart the service but systemctl consider it to be still inactive/broken, then handle it as a failure
if ([ "$action" == "reload" ] || [ "$action" == "start" ] || [ "$action" == "restart" ]) && ! systemctl --quiet is-active $service
then
_ynh_clean_check_starting
return 1
fi
fi
_ynh_clean_check_starting
fi
}
_ynh_clean_check_starting() {
if [ -n "${pid_tail:-}" ]; then
# Stop the execution of tail.
kill -SIGTERM $pid_tail 2>&1
fi
if [ -n "${templog:-}" ]; then
ynh_safe_rm "$templog" 2>&1
fi
}