#!/bin/sh
### BEGIN INIT INFO
# Provides:          tailscaled
# Required-Start:    $network $remote_fs $syslog
# Required-Stop:     $network $remote_fs $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Tailscale node daemon
# Description:       Runs tailscaled, the Tailscale mesh VPN daemon.
#                    Configure PORT and FLAGS in /etc/default/tailscaled.
### END INIT INFO
# Scutarius sysvinit script; semantics follow upstream's tailscaled.openrc
# (--cleanup before start and after stop, pidfile, log file).

# $DAEMON is absolute, so PATH only has to find the helpers (start-stop-daemon,
# the LSB functions' tools). Put the system directories first: an init script
# must not prefer a local override of a system tool.
PATH=/sbin:/bin:/usr/sbin:/usr/bin
DAEMON=/usr/sbin/tailscaled
NAME=tailscaled
PIDFILE=/run/tailscaled.pid
LOG=/var/log/tailscaled.log
STATE=/var/lib/tailscale/tailscaled.state
SOCK=/run/tailscale/tailscaled.sock
PORT=41641
FLAGS=""
[ -r /etc/default/tailscaled ] && . /etc/default/tailscaled
. /lib/lsb/init-functions
[ -x "$DAEMON" ] || exit 0

do_start() {
    # ALREADY RUNNING -> success, and touch nothing. "start" on a live daemon
    # must be a no-op: "$DAEMON --cleanup" tears down the netfilter rules,
    # routes and DNS configuration the RUNNING daemon installed (it is meant
    # for a crashed daemon's leftovers), and start-stop-daemon --start would
    # then report "nothing done" -- rc 1 -- on top of the damage it just did.
    # start-stop-daemon --status: rc 0 = running, 1/3 = not, 4 = unknown.
    if start-stop-daemon --status --pidfile "$PIDFILE" --exec "$DAEMON" >/dev/null 2>&1; then
        return 0
    fi
    mkdir -p /run/tailscale /var/lib/tailscale
    chmod 0700 /var/lib/tailscale
    # Not running: clear whatever a previous instance left behind.
    "$DAEMON" --cleanup >> "$LOG" 2>&1 || true
    # --oknodo: a race that lost (something else started it between the status
    # check and here) is still "started", not a failure.
    start-stop-daemon --start --quiet --oknodo --background --no-close \
        --make-pidfile --pidfile "$PIDFILE" --exec "$DAEMON" -- \
        --state="$STATE" --socket="$SOCK" --port="$PORT" $FLAGS >> "$LOG" 2>&1
}

do_stop() {
    start-stop-daemon --stop --quiet --pidfile "$PIDFILE" --exec "$DAEMON" \
        --retry TERM/10/KILL/5 --remove-pidfile
    rc=$?
    # 0 = stopped: clean up netfilter/routes now that it is gone.
    # 1 = was not running: nothing to clean up, and running --cleanup here
    # would not be idempotent (it also touches netfilter/routes state).
    [ $rc -eq 0 ] && { "$DAEMON" --cleanup >> "$LOG" 2>&1 || true; }
    return $rc
}

case "$1" in
  start)
    log_daemon_msg "Starting Tailscale daemon" "$NAME"
    do_start; log_end_msg $? ;;
  stop)
    log_daemon_msg "Stopping Tailscale daemon" "$NAME"
    do_stop; rc=$?; [ $rc -le 1 ] && log_end_msg 0 || log_end_msg $rc ;;
  restart|force-reload)
    log_daemon_msg "Restarting Tailscale daemon" "$NAME"
    do_stop; do_start; log_end_msg $? ;;
  status)
    status_of_proc -p "$PIDFILE" "$DAEMON" "$NAME" ;;
  *)
    echo "Usage: /etc/init.d/tailscaled {start|stop|restart|force-reload|status}" >&2
    # LSB: 2 = invalid or excess argument(s). (3 means "unimplemented feature".)
    exit 2 ;;
esac
