#!/bin/sh
#
# tailscale init script for IPFire
#

### BEGIN INIT INFO
# Provides:          tailscale
# Required-Start:    $network
# Required-Stop:     $network
# Default-Start:
# Default-Stop:
# Short-Description: Tailscale service
### END INIT INFO

DAEMON="/usr/sbin/tailscaled"
CLI="/usr/local/bin/tailscale"
PIDFILE="/var/run/tailscaled.pid"
SOCK="/var/run/tailscale/tailscaled.sock"
STATE_DIR="/var/lib/tailscale"
SETTINGS="/var/ipfire/tailscale/settings"
STATEFILE="/var/ipfire/tailscale/state"
LOGFILE="/var/log/tailscale.log"

ENABLED="on"
AUTH_KEY=""
HOSTNAME="ipfire"
ACCEPT_ROUTES="on"
ACCEPT_DNS="off"
ADVERTISE_EXIT_NODE="off"
ADVERTISE_ROUTES=""
EXTRA_ARGS=""

load_settings() {
    [ -f "$SETTINGS" ] || return 0

    while IFS='=' read -r key value; do
        key=${key%%[ 	]*}
        value=$(printf '%s' "$value" | tr -d '\015')

        case "$key" in
            ''|\#*) continue ;;
            ENABLED) ENABLED=$value ;;
            AUTH_KEY) AUTH_KEY=$value ;;
            HOSTNAME) HOSTNAME=$value ;;
            ACCEPT_ROUTES) ACCEPT_ROUTES=$value ;;
            ACCEPT_DNS) ACCEPT_DNS=$value ;;
            ADVERTISE_EXIT_NODE) ADVERTISE_EXIT_NODE=$value ;;
            ADVERTISE_ROUTES) ADVERTISE_ROUTES=$value ;;
            EXTRA_ARGS) EXTRA_ARGS=$value ;;
        esac
    done < "$SETTINGS"
}

add_forward_rules() {
    command -v iptables >/dev/null 2>&1 || return 0
    iptables -C FORWARD -i tailscale0 -j ACCEPT 2>/dev/null || iptables -A FORWARD -i tailscale0 -j ACCEPT
    iptables -C FORWARD -o tailscale0 -j ACCEPT 2>/dev/null || iptables -A FORWARD -o tailscale0 -j ACCEPT
    command -v sysctl >/dev/null 2>&1 && sysctl -w net.ipv4.ip_forward=1 >/dev/null 2>&1 || true
}

remove_forward_rules() {
    command -v iptables >/dev/null 2>&1 || return 0
    iptables -D FORWARD -i tailscale0 -j ACCEPT 2>/dev/null || true
    iptables -D FORWARD -o tailscale0 -j ACCEPT 2>/dev/null || true
}

load_settings

mkdir -p /var/run/tailscale
mkdir -p "$STATE_DIR"

PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$PATH"
IP_CMD="$(command -v ip 2>/dev/null || echo /sbin/ip)"
ETHTOOL_CMD="$(command -v ethtool 2>/dev/null || echo /sbin/ethtool)"

is_running() {
    [ -f "$PIDFILE" ] || return 1
    PID="$(cat "$PIDFILE" 2>/dev/null)"
    [ -n "$PID" ] || return 1
    kill -0 "$PID" 2>/dev/null
}

write_state() {
    echo "RUNNING=$1" > "$STATEFILE"
    echo "UPDATED=$(date '+%Y-%m-%d %H:%M:%S')" >> "$STATEFILE"
}

optimize_network() {
    if ! [ -x "$ETHTOOL_CMD" ]; then
        echo "warning: ethtool not found: $ETHTOOL_CMD" >> "$LOGFILE"
        return 1
    fi

    if ! [ -x "$IP_CMD" ]; then
        echo "warning: ip command not found: $IP_CMD" >> "$LOGFILE"
        return 1
    fi

    i=0
    while [ $i -lt 20 ]; do
        NETDEV="$($IP_CMD -o route get 8.8.8.8 2>/dev/null | awk '{for (i=1;i<=NF;i++) if ($i=="dev") {print $(i+1); exit}}')"
        [ -z "$NETDEV" ] && NETDEV="$($IP_CMD -o route get 1.1.1.1 2>/dev/null | awk '{for (i=1;i<=NF;i++) if ($i=="dev") {print $(i+1); exit}}')"

        if [ -n "$NETDEV" ]; then
            "$ETHTOOL_CMD" -K "$NETDEV" rx-udp-gro-forwarding on rx-gro-list off >> "$LOGFILE" 2>&1 && {
                echo "tailscale optimization applied on $NETDEV" >> "$LOGFILE"
                return 0
            }
        fi

        i=$((i + 1))
        sleep 2
    done

    echo "warning: tailscale performance optimization failed after retries" >> "$LOGFILE"
    return 1
}

optimize_network_background() {
    (
        i=0
        while [ $i -lt 6 ]; do
            optimize_network && exit 0
            i=$((i + 1))
            sleep 5
        done
        echo "warning: tailscale delayed performance optimization was not applied" >> "$LOGFILE"
    ) >/dev/null 2>&1 &
}

start() {
    if [ "$ENABLED" != "on" ]; then
        echo "tailscale is disabled in $SETTINGS"
        write_state 0
        return 0
    fi

    if ! [ -x "$DAEMON" ]; then
        echo "tailscaled not installed: $DAEMON"
        return 1
    fi

    if is_running; then
        echo "tailscaled already running"
        return 0
    fi

    echo "Starting tailscaled..."
    "$DAEMON" \
        --state="${STATE_DIR}/tailscaled.state" \
        --socket="$SOCK" \
        >"$LOGFILE" 2>&1 &

    echo $! > "$PIDFILE"
    sleep 2

    if is_running; then
        # ===== Tailscale 性能优化 =====
        add_forward_rules
        optimize_network || true
        optimize_network_background

        write_state 1
        echo "tailscaled started"
        return 0
    fi

    write_state 0
    echo "failed to start tailscaled"
    return 1
}

stop() {
    if ! is_running; then
        echo "tailscaled not running"
        write_state 0
        rm -f "$PIDFILE"
        return 0
    fi

    PID="$(cat "$PIDFILE")"
    echo "Stopping tailscaled..."
    kill "$PID" 2>/dev/null || true
    sleep 2

    if kill -0 "$PID" 2>/dev/null; then
        kill -9 "$PID" 2>/dev/null || true
    fi

    rm -f "$PIDFILE"
    remove_forward_rules
    write_state 0
    echo "tailscaled stopped"
}

restart() {
    stop
    start
}

status() {
    if ! is_running; then
        echo "tailscaled is stopped"
        return 1
    fi

    if [ -S "$SOCK" ] && [ -x "$CLI" ]; then
        if "$CLI" --socket="$SOCK" status >/dev/null 2>&1; then
            echo "tailscaled is running"
            return 0
        fi
    fi

    echo "tailscaled is running"
    return 0
}

up() {
    [ -x "$CLI" ] || { echo "tailscale CLI not found"; return 1; }
    is_running || start || return 1
    add_forward_rules
    optimize_network || true
    optimize_network_background

    set -- up

    [ -n "$AUTH_KEY" ] && set -- "$@" "--auth-key=$AUTH_KEY"
    [ -n "$HOSTNAME" ] && set -- "$@" "--hostname=$HOSTNAME"
    if [ "$ACCEPT_ROUTES" = "on" ]; then
        set -- "$@" "--accept-routes=true"
    else
        set -- "$@" "--accept-routes=false"
    fi

    if [ "$ACCEPT_DNS" = "on" ]; then
        set -- "$@" "--accept-dns=true"
    else
        set -- "$@" "--accept-dns=false"
    fi
    [ "$ADVERTISE_EXIT_NODE" = "on" ] && set -- "$@" "--advertise-exit-node"
    [ -n "$ADVERTISE_ROUTES" ] && set -- "$@" "--advertise-routes=$ADVERTISE_ROUTES"
    [ -n "$EXTRA_ARGS" ] && echo "warning: EXTRA_ARGS is ignored for safety" >> "$LOGFILE"

    echo "Running: tailscale up (auth key hidden)"
    "$CLI" --socket="$SOCK" "$@"
}

down() {
    [ -x "$CLI" ] || { echo "tailscale CLI not found"; return 1; }
    "$CLI" --socket="$SOCK" down
}

tsstatus() {
    [ -x "$CLI" ] || { echo "tailscale CLI not found: $CLI"; return 1; }
    [ -S "$SOCK" ] || { echo "tailscale socket not found: $SOCK"; return 1; }
    "$CLI" --socket="$SOCK" status
}

tsstatus_json() {
    [ -x "$CLI" ] || { echo "tailscale CLI not found: $CLI"; return 1; }
    [ -S "$SOCK" ] || { echo "tailscale socket not found: $SOCK"; return 1; }
    "$CLI" --socket="$SOCK" status --json
}

ip() {
    [ -x "$CLI" ] || { echo "tailscale CLI not found: $CLI"; return 1; }
    [ -S "$SOCK" ] || { echo "tailscale socket not found: $SOCK"; return 1; }

    IPV4="$("$CLI" --socket="$SOCK" ip -4 2>/dev/null)"
    IPV6="$("$CLI" --socket="$SOCK" ip -6 2>/dev/null)"

    [ -n "$IPV4" ] && echo "$IPV4"
    [ -n "$IPV6" ] && echo "$IPV6"

    [ -n "$IPV4$IPV6" ] && return 0
    return 1
}

case "$1" in
    start) start ;;
    stop) stop ;;
    restart) restart ;;
    status) status ;;
    up) up ;;
    down) down ;;
    tsstatus) tsstatus ;;
    tsstatus_json) tsstatus_json ;;
    ip) ip ;;
    *)
        echo "Usage: $0 {start|stop|restart|status|up|down|tsstatus|tsstatus_json|ip}"
        exit 1
        ;;
esac

exit $?
