#!/bin/sh
# Klippy, as s6 supervises it. Exec'd by s6-supervise, never by hand.
#
# The process this script becomes IS the service, so it ends in one `exec` and
# everything above that is preparation. s6-supervise is the parent: it knows the
# pid, runs one copy of this servicedir at a time, and restarts what dies. So no
# forking, no pidfile, and no "is one already running?" test.
#
# THE MCU BRING-UP DOES NOT HAPPEN HERE. It is the `mcu-bringup` oneshot, which
# klipper depends on, so s6-rc runs it first. Doing it here as well would hand
# the same serial ports to two processes at once.
MODDIR=/usr/data/anvil

# READ, NOT INHERITED: s6-supervise hands this script the SCANNER's environment,
# which is firmwareExe's at boot but whatever an ssh session had on a restart by
# hand. anvil-env.sh names $FF_PYTHON and sets LD_LIBRARY_PATH.
if [ -f $MODDIR/anvil-env.sh ]; then
    . $MODDIR/anvil-env.sh
else
    echo "klipper/run: !! no $MODDIR/anvil-env.sh -- klippy will not find its libraries"
fi

# Both halves are ours and both are under $MODDIR -- nothing is read from
# /usr/prog, the partition a stock flash overwrites. klippy reaches c_helper.so
# through _cffi_backend, which is why pkgs/3rdparty/python is glibc, not musl.
KLIPPY=$MODDIR/klipper/klippy/klippy.py
PRINTER_CFG=/usr/data/config/printer.cfg
PRINTER_LOG=/usr/data/logs/printer.log
UDS=/tmp/uds

# klippy does not unlink its socket on exit, and everything here reads that
# socket as "klipper is running". Swept on the way in because s6 restarts this
# service without going near a stop().
rm -f "$UDS"

# THE ONE DUPLICATE s6 CANNOT HANDLE: a klippy that outlived its supervisor. An
# OOM kill leaves it holding /dev/ttyS4 while s6 starts a second one that cannot
# open the port and reports an MCU error for ever.
#
# Matched on klippy.py -- the interpreter would match moonraker, which runs
# $FF_PYTHON too. /proc rather than `ps | grep`, whose output busybox truncates
# to the terminal width.
for _kl_proc in /proc/[0-9]*; do
    [ -r "$_kl_proc/cmdline" ] || continue
    [ "${_kl_proc#/proc/}" = "$$" ] && continue
    case "`tr '\0' ' ' < "$_kl_proc/cmdline" 2>/dev/null`" in
        *klippy.py*) kill -9 "${_kl_proc#/proc/}" 2>/dev/null ;;
    esac
done
unset _kl_proc

# klipper_pri.sh puts klippy's main thread on SCHED_FIFO 50, so it has to run
# after the exec below; this waits for the socket that says klippy is serving.
# Bounded, and nothing reads the result: a missing chrt is a slower printer.
if [ -x /usr/prog/klipper/klipper_pri.sh ]; then
    (   _kp=0
        while [ $_kp -lt 60 ] && [ ! -S "$UDS" ]; do
            sleep 1
            _kp=$((_kp + 1))
        done
        [ -S "$UDS" ] && sh /usr/prog/klipper/klipper_pri.sh
    ) >/dev/null 2>&1 &
fi

# No nice value: SCHED_FIFO 50 above outranks every nice value there is.
exec "$FF_PYTHON" "$KLIPPY" "$PRINTER_CFG" -l "$PRINTER_LOG" -a "$UDS"
