hermes-agent/docker/tini-shim.sh
HexLab98 be3c160a85 fix(docker): strip tini -g flags in legacy entrypoint shim
A plain /usr/bin/tini → /init symlink forwarded tini's -g into
s6-overlay's rc.init as the container CMD, causing boot loops after
image updates that preserve old entrypoints (#66679).
2026-07-18 01:11:03 -07:00

89 lines
2.7 KiB
Bash
Executable file

#!/bin/sh
# shellcheck shell=sh
# /usr/bin/tini — compatibility shim for legacy orchestration templates.
#
# Background
# ----------
# The published image used to ship real `tini` as PID 1. After the
# s6-overlay migration, PID 1 is `/init`. Downstream catalogs (Hostinger
# Hermes WebUI, NAS "update" flows that preserve an old entrypoint, etc.)
# still pin entrypoints like:
#
# ["/usr/bin/tini", "-g", "--"]
# ["/usr/bin/tini", "-g", "--", "gateway", "run"]
#
# A plain `ln -sf /init /usr/bin/tini` (#34192) made the binary exist, but
# forwarded tini's own flags into s6-overlay. `/init` then handed `-g` to
# `rc.init` as the container CMD, which fails with:
#
# /run/s6/basedir/scripts/rc.init: 91: -g: not found
#
# …and with `restart: unless-stopped` the container boot-loops forever
# (#66679).
#
# This shim strips the tini CLI surface, then exec's `/init` with the
# image's main-wrapper so privilege drop + arg routing still apply.
#
# Tini flags we discard (see krallin/tini --help):
# -g / --kill-process-group, -s / --subreaper, -w, -v (repeatable),
# -h / --help, -l, --version, -p SIGNAL, -e EXIT_CODE, and `--`.
# s6-overlay already reaps zombies and forwards signals as PID 1; the
# flag semantics are intentionally no-ops here.
#
# Test hook: HERMES_TINI_SHIM_TARGET overrides the `/init` path (and
# HERMES_TINI_SHIM_WRAPPER the main-wrapper path) so unit tests can
# record argv without a real s6 tree.
set -e
INIT_TARGET="${HERMES_TINI_SHIM_TARGET:-/init}"
WRAPPER="${HERMES_TINI_SHIM_WRAPPER:-/opt/hermes/docker/main-wrapper.sh}"
while [ "$#" -gt 0 ]; do
case "$1" in
--)
shift
break
;;
-g|--kill-process-group|-s|--subreaper|-w|-l|-h|--help|--version)
shift
;;
-v)
# tini allows repeated -v; drop them all.
shift
;;
-p|-e)
# These take a mandatory argument.
shift
if [ "$#" -gt 0 ]; then
shift
fi
;;
-*)
# Unknown short/long option from an older/newer tini — drop
# rather than forwarding into /init (would recreate #66679).
shift
;;
*)
break
;;
esac
done
# No program left (entrypoint was just `tini -g --`) → same as the
# image's default ENTRYPOINT + empty CMD.
if [ "$#" -eq 0 ]; then
exec "$INIT_TARGET" "$WRAPPER"
fi
# Caller already supplied the wrapper or /init — don't double-wrap.
case "$1" in
/init|"$INIT_TARGET")
exec "$@"
;;
"$WRAPPER"|*/main-wrapper.sh)
exec "$INIT_TARGET" "$@"
;;
esac
exec "$INIT_TARGET" "$WRAPPER" "$@"