#!/usr/bin/env bash VERSION="3.0" SERVICE="chronyd" CONF="/etc/chrony.conf" LOGFILE="/tmp/chrony_doctor_$(date +%Y%m%d_%H%M%S).log" if systemctl list-unit-files | grep -q chrony.service; then SERVICE="chrony" fi pause(){ echo read -p "按回车继续..." } header(){ clear echo "========================================" echo " Chrony Doctor v$VERSION" echo "========================================" echo } log(){ echo "$1" | tee -a "$LOGFILE" } # ----------------------------- # UDP 123 检测 # ----------------------------- check_ntp_port(){ log "NTP UDP 123 连通性检测" log "--------------------------------" SERVERS=$(grep ^server $CONF | awk '{print $2}') for s in $SERVERS do echo "测试 $s ..." timeout 2 bash -c "echo > /dev/udp/$s/123" 2>/dev/null if [ $? -eq 0 ]; then log "$s : UDP 123 可达" else log "$s : UDP 123 不可达" fi done echo } # ----------------------------- # Chrony 服务 # ----------------------------- check_service(){ log "Chrony 服务状态" log "--------------------------------" STATUS=$(systemctl is-active $SERVICE) if [ "$STATUS" = "active" ]; then log "服务运行正常" else log "服务未运行" fi echo } # ----------------------------- # 时间同步状态 # ----------------------------- check_sync(){ log "系统时间同步状态" log "--------------------------------" timedatectl | tee -a "$LOGFILE" echo } # ----------------------------- # Chrony tracking # ----------------------------- check_tracking(){ log "Chrony Tracking" log "--------------------------------" TRACK=$(chronyc tracking) echo "$TRACK" | tee -a "$LOGFILE" OFFSET=$(echo "$TRACK" | grep "Last offset" | awk '{print $3}') OFFSET_NUM=$(echo $OFFSET | tr -d 's') echo if command -v bc >/dev/null; then if [[ $(echo "$OFFSET_NUM > 1" | bc) -eq 1 ]]; then log "警告: 时间偏差超过 1 秒" fi fi echo } # ----------------------------- # NTP sources # ----------------------------- check_sources(){ log "NTP Sources" log "--------------------------------" chronyc sources -v | tee -a "$LOGFILE" echo } # ----------------------------- # RTC检测 # ----------------------------- check_rtc(){ log "RTC 与系统时钟" log "--------------------------------" timedatectl | grep RTC | tee -a "$LOGFILE" echo } # ----------------------------- # 防火墙检测 # ----------------------------- check_firewall(){ log "防火墙状态" log "--------------------------------" if systemctl is-active firewalld >/dev/null 2>&1; then log "firewalld 正在运行" firewall-cmd --list-ports | tee -a "$LOGFILE" else log "firewalld 未运行" fi echo } # ----------------------------- # 虚拟化检测 # ----------------------------- check_virtualization(){ log "虚拟化环境检测" log "--------------------------------" virt=$(systemd-detect-virt) if [ "$virt" != "none" ]; then log "当前运行在虚拟机: $virt" log "虚拟机环境建议使用 chrony" else log "物理机环境" fi echo } # ----------------------------- # Chrony 配置 # ----------------------------- show_config(){ log "Chrony 配置" log "--------------------------------" grep -v '^#' $CONF | grep -v '^$' | tee -a "$LOGFILE" echo } # ----------------------------- # 强制同步 # ----------------------------- force_sync(){ header echo "执行时间同步..." chronyc -a makestep echo pause } # ----------------------------- # 完整诊断 # ----------------------------- full_diagnose(){ header echo "生成诊断报告..." echo "日志文件: $LOGFILE" echo check_service check_sync check_tracking check_sources check_ntp_port check_firewall check_rtc check_virtualization show_config echo log "诊断完成" pause } # ----------------------------- # 主菜单 # ----------------------------- menu(){ while true do header echo "1 系统时间状态" echo "2 Chrony 服务状态" echo "3 Chrony Sources" echo "4 Chrony Tracking" echo "5 UDP 123 连通性检测" echo "6 强制同步时间" echo "7 防火墙检测" echo "8 虚拟化环境检测" echo "9 查看 Chrony 配置" echo echo "10 一键完整诊断" echo echo "0 退出" echo read -p "选择操作: " CHOICE case $CHOICE in 1) timedatectl; pause ;; 2) systemctl status $SERVICE --no-pager; pause ;; 3) chronyc sources -v; pause ;; 4) chronyc tracking; pause ;; 5) check_ntp_port; pause ;; 6) force_sync ;; 7) check_firewall; pause ;; 8) check_virtualization; pause ;; 9) show_config; pause ;; 10) full_diagnose ;; 0) exit ;; *) echo "无效选项"; sleep 1 ;; esac done } menu