Files
dock/实时 history 监控

542 lines
16 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# 交互式实时命令监控脚本 - 修复版
# 版本: 4.1
set -e
### 配置区域 ###
LOG_DIR="/root/command_monitor_logs"
SCRIPT_NAME="command_monitor.sh"
SCRIPT_PATH="/usr/local/bin/$SCRIPT_NAME"
### 颜色定义 ###
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
PURPLE='\033[0;35m'
CYAN='\033[0;36m'
NC='\033[0m'
### 全局变量 ###
CURRENT_LOG=""
LATEST_LOG=""
DAEMON_MODE=false
# 获取时间戳
timestamp() {
date '+%Y-%m-%d %H:%M:%S'
}
# 日志函数
log_message() {
local level="$1"
local message="$2"
local color="$GREEN"
case "$level" in
"ERROR") color="$RED" ;;
"WARN") color="$YELLOW" ;;
"INFO") color="$BLUE" ;;
"SUCCESS") color="$GREEN" ;;
"COMMAND") color="$CYAN" ;;
esac
if [ "$DAEMON_MODE" = true ]; then
echo -e "${color}[$(timestamp)] [$level] $message${NC}" >> "$CURRENT_LOG"
else
echo -e "${color}[$(timestamp)] [$level] $message${NC}" | tee -a "$CURRENT_LOG"
fi
}
# 英文转中文函数
english_to_chinese() {
local text="$1"
# 简化翻译,只保留主要城市和国家
text=$(echo "$text" | sed 's/China/中国/g')
text=$(echo "$text" | sed 's/Beijing/北京/g')
text=$(echo "$text" | sed 's/Shanghai/上海/g')
text=$(echo "$text" | sed 's/Guangzhou/广州/g')
text=$(echo "$text" | sed 's/Shenzhen/深圳/g')
text=$(echo "$text" | sed 's/Hangzhou/杭州/g')
text=$(echo "$text" | sed 's/Nanjing/南京/g')
text=$(echo "$text" | sed 's/Wuhan/武汉/g')
text=$(echo "$text" | sed 's/Chengdu/成都/g')
text=$(echo "$text" | sed 's/Xi'"'"'an/西安/g')
text=$(echo "$text" | sed 's/Jiangxi/江西/g')
text=$(echo "$text" | sed 's/Nanchang/南昌/g')
text=$(echo "$text" | sed 's/Telecom/电信/g')
text=$(echo "$text" | sed 's/Unicom/联通/g')
text=$(echo "$text" | sed 's/Mobile/移动/g')
echo "$text"
}
# 获取IP地理位置
get_ip_location() {
local ip="$1"
if [[ "$ip" == "192.168."* ]] || [[ "$ip" == "10."* ]] || [[ "$ip" == "172."* ]]; then
echo "内网IP"
return 0
fi
if [[ "$ip" == "127.0.0.1" ]] || [[ "$ip" == "localhost" ]] || [[ "$ip" == "unknown" ]]; then
echo "本机"
return 0
fi
local location_info=$(curl -s -m 3 "http://ip-api.com/json/$ip?fields=status,country,regionName,city,isp" 2>/dev/null || true)
if [ -n "$location_info" ] && echo "$location_info" | grep -q '"status":"success"'; then
local country=$(echo "$location_info" | grep -o '"country":"[^"]*"' | cut -d'"' -f4)
local region=$(echo "$location_info" | grep -o '"regionName":"[^"]*"' | cut -d'"' -f4)
local city=$(echo "$location_info" | grep -o '"city":"[^"]*"' | cut -d'"' -f4)
local isp=$(echo "$location_info" | grep -o '"isp":"[^"]*"' | cut -d'"' -f4)
if [ -n "$country" ]; then
local result="$country"
[ -n "$region" ] && result="$result-$region"
[ -n "$city" ] && result="$result-$city"
[ -n "$isp" ] && result="$result($isp)"
echo $(english_to_chinese "$result")
return 0
fi
fi
echo "未知位置"
}
# 获取客户端IP
get_client_ip() {
local ip="unknown"
if [ -n "$SSH_CLIENT" ]; then
ip=$(echo "$SSH_CLIENT" | awk '{print $1}')
elif [ -n "$SSH_CONNECTION" ]; then
ip=$(echo "$SSH_CONNECTION" | awk '{print $1}')
else
ip=$(who -m 2>/dev/null | awk '{print $5}' | sed 's/[()]//g' | head -1)
if [[ "$ip" == ":0" ]] || [[ "$ip" == ":1" ]] || [[ -z "$ip" ]]; then
ip="localhost"
fi
fi
echo "$ip"
}
# 初始化日志系统
init_log_system() {
mkdir -p "$LOG_DIR"
local client_ip=$(get_client_ip | sed 's/\./_/g')
local log_date=$(date '+%Y%m%d_%H%M%S')
CURRENT_LOG="$LOG_DIR/monitor_${client_ip}_${log_date}.log"
LATEST_LOG="$LOG_DIR/latest.log"
ln -sf "$CURRENT_LOG" "$LATEST_LOG" 2>/dev/null || true
}
# 配置实时history记录
configure_realtime_history() {
for user_dir in /home/* /root; do
if [ -d "$user_dir" ]; then
local user=$(basename "$user_dir")
local bashrc="$user_dir/.bashrc"
if [ -f "$bashrc" ] && ! grep -q "REAL_TIME_HISTORY" "$bashrc"; then
cat >> "$bashrc" << 'EOF'
# REAL_TIME_HISTORY - 实时命令记录配置
export PROMPT_COMMAND='history -a; history -c; history -r'
export HISTTIMEFORMAT='%F %T '
export HISTSIZE=10000
export HISTFILESIZE=20000
shopt -s histappend
export HISTCONTROL=ignoredups:erasedups
EOF
fi
fi
done
}
# 监控命令历史文件变化
monitor_history_files() {
declare -A file_sizes
declare -A last_commands
# 初始化文件大小
for user_dir in /home/* /root; do
if [ -d "$user_dir" ]; then
local user=$(basename "$user_dir")
local history_file="$user_dir/.bash_history"
if [ -f "$history_file" ]; then
file_sizes["$user"]=$(stat -c%s "$history_file" 2>/dev/null || echo 0)
last_commands["$user"]=""
fi
fi
done
# 监控循环
while true; do
for user_dir in /home/* /root; do
if [ -d "$user_dir" ]; then
local user=$(basename "$user_dir")
local history_file="$user_dir/.bash_history"
if [ -f "$history_file" ]; then
local current_size=$(stat -c%s "$history_file" 2>/dev/null || echo 0)
local last_size=${file_sizes["$user"]}
if [ "$current_size" -gt "$last_size" ]; then
local new_content=$(tail -c +$((last_size + 1)) "$history_file" 2>/dev/null | tr -d '\0')
if [ -n "$new_content" ]; then
while IFS= read -r line; do
line=$(echo "$line" | sed 's/^[ \t]*//;s/[ \t]*$//')
# 过滤无用命令
if [ -n "$line" ] && [ "${#line}" -gt 1 ] && \
[[ ! "$line" =~ ^(ls|cd|pwd|ll|la|history|exit|clear|echo|date|whoami)$ ]] && \
[[ ! "$line" =~ ^#[0-9]+$ ]] && \
[ "$line" != "${last_commands["$user"]}" ]; then
local client_ip=$(get_client_ip)
local location_info=$(get_ip_location "$client_ip")
log_message "COMMAND" "用户: $user | 命令: $line | 来源: $client_ip [$location_info]"
last_commands["$user"]="$line"
fi
done <<< "$new_content"
fi
file_sizes["$user"]=$current_size
fi
fi
fi
done
sleep 1
done
}
# 安装脚本到系统
install_script() {
echo -e "${GREEN}正在安装脚本到系统...${NC}"
# 下载并保存脚本
local script_content=$(curl -sSL https://raw.githubusercontent.com/xzx3344521/dock/refs/heads/main/%E5%AE%9E%E6%97%B6%20history%20%E7%9B%91%E6%8E%A7 2>/dev/null || echo "")
if [ -z "$script_content" ]; then
echo -e "${RED}下载脚本失败${NC}"
return 1
fi
# 保存到本地文件
echo "$script_content" > "$SCRIPT_PATH"
chmod +x "$SCRIPT_PATH"
echo -e "${GREEN}✓ 脚本已安装到: $SCRIPT_PATH${NC}"
return 0
}
# 设置开机自启动
setup_auto_start() {
if [ ! -f "$SCRIPT_PATH" ]; then
install_script
fi
# 创建systemd服务
cat > /etc/systemd/system/command-monitor.service << EOF
[Unit]
Description=Command Monitor Service
After=network.target
[Service]
Type=forking
ExecStart=$SCRIPT_PATH --daemon
ExecStop=$SCRIPT_PATH --kill
Restart=always
User=root
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable command-monitor.service
echo -e "${GREEN}✓ 已设置开机自启动${NC}"
echo -e "${YELLOW}启动服务: systemctl start command-monitor${NC}"
echo -e "${YELLOW}停止服务: systemctl stop command-monitor${NC}"
}
# 取消开机自启动
remove_auto_start() {
systemctl stop command-monitor.service 2>/dev/null || true
systemctl disable command-monitor.service 2>/dev/null || true
rm -f /etc/systemd/system/command-monitor.service
systemctl daemon-reload
echo -e "${YELLOW}✓ 已取消开机自启动${NC}"
}
# 后台运行监控(修复版)
start_background_monitor() {
echo -e "${GREEN}启动后台监控服务...${NC}"
# 检查是否已经在运行
if [ -f "/tmp/command_monitor.pid" ]; then
local old_pid=$(cat "/tmp/command_monitor.pid" 2>/dev/null)
if ps -p "$old_pid" >/dev/null 2>&1; then
echo -e "${YELLOW}监控服务已经在运行 (PID: $old_pid)${NC}"
return 1
fi
fi
# 直接启动监控进程
(
# 设置环境
DAEMON_MODE=true
init_log_system
# 记录启动信息
log_message "INFO" "后台监控进程启动 - PID: $$"
log_message "INFO" "客户端IP: $(get_client_ip)"
log_message "INFO" "地理位置: $(get_ip_location $(get_client_ip))"
# 保存PID
echo $$ > "/tmp/command_monitor.pid"
# 配置实时记录
configure_realtime_history
# 启动监控
monitor_history_files
# 清理
rm -f "/tmp/command_monitor.pid"
) &
local main_pid=$!
sleep 3
# 检查是否启动成功
if ps -p $main_pid >/dev/null 2>&1 && [ -f "/tmp/command_monitor.pid" ]; then
echo -e "${GREEN}✓ 后台监控已启动!${NC}"
echo -e "主进程PID: $main_pid"
echo -e "日志文件: $LATEST_LOG"
echo -e "查看日志: ${GREEN}tail -f $LATEST_LOG${NC}"
echo -e "停止监控: ${RED}$0 --kill${NC}"
return 0
else
echo -e "${RED}✗ 后台监控启动失败${NC}"
echo -e "${YELLOW}尝试使用前台模式...${NC}"
return 1
fi
}
# 前台运行监控
start_foreground_monitor() {
echo -e "${YELLOW}前台运行模式 (输入 TO 切换后台Ctrl+C 停止)${NC}"
echo -e "${GREEN}开始监控...${NC}"
DAEMON_MODE=false
init_log_system
configure_realtime_history
# 显示启动信息
log_message "INFO" "前台监控启动 - PID: $$"
log_message "INFO" "客户端IP: $(get_client_ip)"
log_message "INFO" "地理位置: $(get_ip_location $(get_client_ip))"
# 启动监控进程
monitor_history_files &
local monitor_pid=$!
# 等待用户输入
while true; do
if read -t 1 -n 2 user_input 2>/dev/null; then
if [ "$user_input" = "TO" ] || [ "$user_input" = "to" ]; then
echo -e "\n${GREEN}切换到后台模式...${NC}"
kill $monitor_pid 2>/dev/null || true
start_background_monitor
break
fi
fi
# 检查监控进程是否还在运行
if ! ps -p $monitor_pid >/dev/null 2>&1; then
echo -e "${RED}监控进程异常退出${NC}"
break
fi
done
}
# 查看监控状态
check_monitor_status() {
local pid_file="/tmp/command_monitor.pid"
local main_pid=""
if [ -f "$pid_file" ]; then
main_pid=$(cat "$pid_file" 2>/dev/null)
fi
if [ -z "$main_pid" ] || ! ps -p "$main_pid" >/dev/null 2>&1; then
echo -e "${RED}✗ 监控服务未运行${NC}"
return 1
fi
echo -e "${GREEN}✓ 监控服务运行中${NC}"
echo "主进程PID: $main_pid"
echo "日志目录: $LOG_DIR"
if [ -f "$LATEST_LOG" ]; then
local log_size=$(du -h "$LATEST_LOG" 2>/dev/null | cut -f1 || echo "未知")
echo "当前日志大小: $log_size"
echo
echo -e "${YELLOW}最近3条记录:${NC}"
tail -3 "$LATEST_LOG" 2>/dev/null | while read line; do
echo " $line"
done || echo " 无法读取日志"
fi
}
# 停止监控进程
stop_monitor() {
local pid_file="/tmp/command_monitor.pid"
local main_pid=""
if [ -f "$pid_file" ]; then
main_pid=$(cat "$pid_file")
rm -f "$pid_file"
fi
if [ -z "$main_pid" ]; then
main_pid=$(pgrep -f "monitor_history_files" | head -1)
fi
if [ -z "$main_pid" ]; then
echo -e "${YELLOW}没有找到运行的监控进程${NC}"
return 0
fi
echo -e "${YELLOW}正在停止监控进程 (PID: $main_pid)...${NC}"
kill "$main_pid" 2>/dev/null || true
sleep 2
if ps -p "$main_pid" >/dev/null 2>&1; then
echo -e "${RED}强制停止监控进程...${NC}"
kill -9 "$main_pid" 2>/dev/null || true
fi
# 清理所有相关进程
pkill -f "monitor_history_files" 2>/dev/null || true
echo -e "${GREEN}✓ 监控进程已停止${NC}"
}
# 显示交互式菜单
show_interactive_menu() {
clear
echo -e "${GREEN}========================================${NC}"
echo -e "${GREEN} 实时命令监控系统 v4.1${NC}"
echo -e "${GREEN}========================================${NC}"
echo
echo -e "${YELLOW}请选择操作:${NC}"
echo
echo -e " ${CYAN}1${NC}. 启动后台监控模式"
echo -e " ${CYAN}2${NC}. 启动前台监控模式"
echo -e " ${CYAN}3${NC}. 查看监控状态"
echo -e " ${CYAN}4${NC}. 停止监控服务"
echo -e " ${CYAN}5${NC}. 设置开机自启动"
echo -e " ${CYAN}6${NC}. 取消开机自启动"
echo -e " ${CYAN}7${NC}. 安装脚本到系统"
echo -e " ${CYAN}0${NC}. 退出"
echo
echo -e "${GREEN}========================================${NC}"
echo -e "${YELLOW}提示:${NC}"
echo -e " - 前台模式下输入 ${CYAN}TO${NC} 可切换到后台"
echo -e " - 建议先安装脚本到系统再使用"
echo -e "${GREEN}========================================${NC}"
echo
}
# 处理用户选择
handle_user_choice() {
local choice
read -p "请输入选择 [0-7]: " choice
case $choice in
1)
start_background_monitor
;;
2)
start_foreground_monitor
;;
3)
check_monitor_status
;;
4)
stop_monitor
;;
5)
setup_auto_start
;;
6)
remove_auto_start
;;
7)
install_script
;;
0)
echo -e "${GREEN}再见!${NC}"
exit 0
;;
*)
echo -e "${RED}无效选择,请重新输入${NC}"
;;
esac
echo
read -p "按回车键返回菜单..."
}
### 主程序 ###
main() {
local command="${1:-}"
# 命令行参数模式
case "$command" in
-d|--daemon)
start_background_monitor
exit $?
;;
-s|--status)
check_monitor_status
exit $?
;;
-k|--kill)
stop_monitor
exit $?
;;
-h|--help|"")
show_interactive_menu
handle_user_choice
;;
*)
echo -e "${RED}未知选项: $command${NC}"
echo "使用: $0 [--daemon|--status|--kill|--help]"
exit 1
;;
esac
}
# 如果是直接运行且没有参数,显示菜单
if [ $# -eq 0 ] && [ -t 0 ]; then
while true; do
show_interactive_menu
handle_user_choice
done
else
main "$@"
fi