#!/bin/bash set -Eeuo pipefail IFS=$'\n\t' CONFIG_FILE="/etc/ssh-monitor.conf" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" if [ -f "$SCRIPT_DIR/ssh-monitor-perms.sh" ]; then # shellcheck source=ssh-monitor-perms.sh source "$SCRIPT_DIR/ssh-monitor-perms.sh" fi WATCHDOG_LOG_FILE="/var/log/ssh_monitor_watchdog.log" LAST_HEARTBEAT_FILE="/var/log/last_heartbeat.txt" WATCHDOG_MAX_HEARTBEAT_AGE=18000 WATCHDOG_SERVICE_NAME="ssh-monitor.service" WATCHDOG_NOTIFY_ON_RECOVERY="0" write_log() { local message="$1" local timestamp timestamp="$(date '+%Y-%m-%d %H:%M:%S')" echo "${timestamp} - ${message}" | tee -a "$WATCHDOG_LOG_FILE" >/dev/null } send_telegram_raw() { local message="$1" if [ -z "${TELEGRAM_BOT_TOKEN:-}" ] || [ -z "${TELEGRAM_CHAT_ID:-}" ]; then return 1 fi local url="https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" local body if ! body=$(curl -fsS -X POST "$url" \ --data-urlencode "chat_id=${TELEGRAM_CHAT_ID}" \ --data-urlencode "text=${message}" \ --data-urlencode "parse_mode=HTML" \ --max-time 10 2>/dev/null); then return 1 fi if echo "$body" | grep -q '"ok":true'; then return 0 fi return 1 } notify_send() { local message="$1" message="$(ssh_monitor_message_ensure_server_line "$message")" send_telegram_raw "$message" } load_config() { if [ -f "$CONFIG_FILE" ]; then local _strict_peek _strict_peek="$(ssh_monitor_config_peek CONFIG_STRICT_PERMS "$CONFIG_FILE" "0")" ssh_monitor_check_config_file_perms "$CONFIG_FILE" "$_strict_peek" || true ssh_monitor_config_load_file "$CONFIG_FILE" || true fi } is_service_active() { systemctl is-active --quiet "$WATCHDOG_SERVICE_NAME" } heartbeat_age_seconds() { if [ ! -f "$LAST_HEARTBEAT_FILE" ]; then echo 999999 return 0 fi local hb now hb="$(<"$LAST_HEARTBEAT_FILE")" now="$(date '+%s')" if [[ ! "$hb" =~ ^[0-9]+$ ]]; then echo 999999 return 0 fi echo $((now - hb)) } restart_monitor() { local reason="$1" write_log "Watchdog: перезапуск $WATCHDOG_SERVICE_NAME, причина: $reason" if systemctl restart "$WATCHDOG_SERVICE_NAME"; then local msg=" ⚠️ WATCHDOG: монитор перезапущен "$'\n' msg="${msg}Сервис: ${WATCHDOG_SERVICE_NAME}"$'\n' msg="${msg}Причина: ${reason}"$'\n' msg="${msg}Время: $(date '+%d.%m.%Y %H:%M:%S')" notify_send "$msg" else write_log "Watchdog: ошибка перезапуска $WATCHDOG_SERVICE_NAME" local msg=" ❌ WATCHDOG: не удалось перезапустить монитор "$'\n' msg="${msg}Сервис: ${WATCHDOG_SERVICE_NAME}"$'\n' msg="${msg}Причина: ${reason}"$'\n' msg="${msg}Время: $(date '+%d.%m.%Y %H:%M:%S')" notify_send "$msg" return 1 fi } main() { if [ "$EUID" -ne 0 ]; then echo "Запустите watchdog от root" exit 1 fi load_config mkdir -p "$(dirname "$WATCHDOG_LOG_FILE")" touch "$WATCHDOG_LOG_FILE" if ssh_monitor_update_in_progress 2>/dev/null; then write_log "Watchdog: пропуск — идёт agent-update (state file)" exit 0 fi local reason="" if ! is_service_active; then reason="systemd reported inactive service" else local age age="$(heartbeat_age_seconds)" if [ "$age" -ge "${WATCHDOG_MAX_HEARTBEAT_AGE:-5400}" ]; then reason="stale heartbeat (${age}s)" fi fi if [ -n "$reason" ]; then restart_monitor "$reason" elif [ "${WATCHDOG_NOTIFY_ON_RECOVERY:-1}" = "1" ]; then write_log "Watchdog: OK, сервис активен и heartbeat актуален" fi } main "$@"