Skip to content

Instantly share code, notes, and snippets.

@kawanet
Last active July 26, 2025 13:25
Show Gist options
  • Save kawanet/2eadbbf03f3619a28bb7cd6f36985f8d to your computer and use it in GitHub Desktop.
Save kawanet/2eadbbf03f3619a28bb7cd6f36985f8d to your computer and use it in GitHub Desktop.
Loop-run systemd’s ExecStart command
#!/bin/bash
#---
## @file exec-service.sh
## @brief Loop-run systemd’s ExecStart command
## @see https://gist.github.com/kawanet/2eadbbf03f3619a28bb7cd6f36985f8d
## @author Yusuke Kawasaki
## @license MIT
#---
die () {
echo "$1" >&2
exit 1
}
warn () {
echo "$1" >&2
}
prompt () {
local cmd=("$@")
if [ -z "$PROMPT" ]; then
local user=${USER:-$(whoami)}
if [ "$user" = "root" ]; then
PROMPT="[$user]#"
else
PROMPT="[$user]$"
fi
fi
echo "$PROMPT ${cmd[@]}" >&2
}
if command -v perl > /dev/null 2>&1; then
strip_nl() { perl -pe 's/\\\n//g' < "$1"; }
else
strip_nl() { sed ':a;N;$!ba;s/\\\n//g' < "$1"; }
fi
getconf () {
local unit="$1"
local key="$2"
local row=$(strip_nl "$unit" | grep "^$key")
local value="${row#*=}"
echo $value
}
start_unit () {
unit="$1"
WorkingDirectory=$(getconf "$unit" WorkingDirectory)
Environment=$(getconf "$unit" Environment)
ExecStartPre=$(getconf "$unit" ExecStartPre)
ExecStart=$(getconf "$unit" ExecStart)
[ -n "$ExecStart" ] || die "Error: ExecStart is required in the service file: $unit"
if [ -n "$WorkingDirectory" ]; then
cd "$WorkingDirectory" || die "Error: Failed to cd to '$WorkingDirectory'."
fi
if [ -n "$ExecStartPre" ]; then
prompt $ExecStartPre
env $Environment $ExecStartPre || die "Error: ExecStartPre failed with code $?"
fi
prompt $ExecStart
env $Environment $ExecStart &
}
forward() {
sig="$1"
warn "SIGNAL: $sig"
for pid in $(pgrep -P $$); do
kill -s "$sig" "$pid" 2>/dev/null
done
}
[ "$#" -gt 0 ] || die "Usage: $0 /etc/systemd/system/xxxx.service"
trap 'forward HUP' HUP
trap 'forward USR1' USR1
trap 'forward USR2' USR2
trap 'forward TERM; sleep 1; exit 0' TERM
trap 'forward INT; sleep 1; exit 0' INT
args=("$@")
pids=()
delay=1
start_all() {
local len=${#args[@]}
for (( idx=0; idx<len; idx++ )); do
local pid=${pids[idx]}
if [ -z "$pid" ] || ! kill -0 "$pid" 2>/dev/null; then
start_unit "${args[idx]}"
pids[$idx]=$!
fi
done
[ -n "$(jobs -p)" ] && wait -n
}
start_once() {
local startTime=$(date +%s)
start_all
local status=$?
local endTime=$(date +%s)
local runTime=$(( endTime - startTime ))
if (( runTime < delay )); then
delay=$(( delay * 2 ))
else
delay=1
fi
[ "$status" -eq 0 ] && prefix="INFO:" || prefix="ERR:"
warn "${prefix} Exit code ${status}; ran for ${runTime}s; restarting in ${delay}s..."
sleep "$delay"
}
while true; do
start_once
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment