Files
dock/实时 history 监控

590 lines
18 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.2
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'
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
}
# 获取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=""
[ -n "$country" ] && result="$country"
[ -n "$region" ] && result="$result-$region"
[ -n "$city" ] && result="$result-$city"
[ -n "$isp" ] && result="$result($isp)"
echo "$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 -n 1 "$history_file" 2>/dev/null | tr -d '\0')
if [ -n "$new_content" ]; then
local line=$(echo "$new_content" | 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
fi
file_sizes["$user"]=$current_size
fi
fi
fi
done
sleep 1
done
}
# 安装脚本到系统
install_script() {
echo -e "${GREEN}正在安装脚本到系统...${NC}"
# 创建简单的本地版本
cat > "$SCRIPT_PATH" << 'EOF'
#!/bin/bash
# 命令监控脚本 - 本地安装版
LOG_DIR="/root/command_monitor_logs"
PID_FILE="/tmp/command_monitor.pid"
start_monitor() {
mkdir -p "$LOG_DIR"
nohup bash -c '
while true; do
for user_dir in /home/* /root; do
if [ -d "$user_dir" ]; then
user=$(basename "$user_dir")
history_file="$user_dir/.bash_history"
if [ -f "$history_file" ]; then
current_size=$(stat -c%s "$history_file" 2>/dev/null || echo 0)
last_size=$(cat "/tmp/last_size_$user" 2>/dev/null || echo 0)
if [ "$current_size" -gt "$last_size" ]; then
new_command=$(tail -n 1 "$history_file" 2>/dev/null)
if [ -n "$new_command" ] && [ "${#new_command}" -gt 2 ]; then
client_ip=$(who -m 2>/dev/null | awk "{print \$5}" | sed "s/[()]//g" | head -1)
echo "[$(date "+%Y-%m-%d %H:%M:%S")] 用户: $user | 命令: $new_command | 来源: $client_ip" >> "$LOG_DIR/monitor.log"
fi
echo "$current_size" > "/tmp/last_size_$user"
fi
fi
fi
done
sleep 1
done
' > /dev/null 2>&1 &
echo $! > "$PID_FILE"
echo "监控已启动 (PID: $!)"
}
stop_monitor() {
if [ -f "$PID_FILE" ]; then
pid=$(cat "$PID_FILE")
kill "$pid" 2>/dev/null && echo "监控已停止" || echo "停止监控失败"
rm -f "$PID_FILE"
else
echo "监控未运行"
fi
}
case "$1" in
start) start_monitor ;;
stop) stop_monitor ;;
status)
if [ -f "$PID_FILE" ] && ps -p $(cat "$PID_FILE") >/dev/null 2>&1; then
echo "监控运行中 (PID: $(cat "$PID_FILE"))"
else
echo "监控未运行"
fi
;;
*) echo "使用: $0 {start|stop|status}" ;;
esac
EOF
chmod +x "$SCRIPT_PATH"
echo -e "${GREEN}✓ 脚本已安装到: $SCRIPT_PATH${NC}"
echo -e "${YELLOW}使用方法:${NC}"
echo -e " ${CYAN}command_monitor.sh start${NC} # 启动监控"
echo -e " ${CYAN}command_monitor.sh stop${NC} # 停止监控"
echo -e " ${CYAN}command_monitor.sh status${NC} # 查看状态"
return 0
}
# 设置开机自启动
setup_auto_start() {
if [ ! -f "$SCRIPT_PATH" ]; then
install_script
fi
# 创建简单的rc.local启动兼容性更好
if [ -d "/etc/rc.d" ]; then
# 对于使用rc.local的系统
local rclocal_file="/etc/rc.d/rc.local"
else
local rclocal_file="/etc/rc.local"
fi
# 确保rc.local存在并可执行
touch "$rclocal_file"
chmod +x "$rclocal_file"
# 添加启动命令(如果不存在)
if ! grep -q "command_monitor.sh" "$rclocal_file"; then
echo "# 命令监控服务自启动" >> "$rclocal_file"
echo "nohup $SCRIPT_PATH start > /dev/null 2>&1 &" >> "$rclocal_file"
fi
echo -e "${GREEN}✓ 已设置开机自启动${NC}"
echo -e "${YELLOW}重启后会自动启动监控服务${NC}"
}
# 取消开机自启动
remove_auto_start() {
if [ -d "/etc/rc.d" ]; then
local rclocal_file="/etc/rc.d/rc.local"
else
local rclocal_file="/etc/rc.local"
fi
# 移除启动命令
sed -i '/command_monitor.sh/d' "$rclocal_file" 2>/dev/null || true
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
configure_realtime_history
# 记录启动信息
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"
# 启动监控主循环
monitor_history_files
) &
local main_pid=$!
# 等待并检查启动状态
sleep 2
if ps -p $main_pid >/dev/null 2>&1; 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}选择菜单选项4${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
# 显示启动信息
local client_ip=$(get_client_ip)
local location_info=$(get_ip_location "$client_ip")
log_message "INFO" "前台监控启动 - PID: $$"
log_message "INFO" "客户端IP: $client_ip"
log_message "INFO" "地理位置: $location_info"
echo -e "${BLUE}监控信息:${NC}"
echo -e " 客户端IP: $client_ip"
echo -e " 地理位置: $location_info"
echo -e " 日志文件: $CURRENT_LOG"
echo -e " 输入 ${CYAN}TO${NC} 切换到后台模式"
echo -e "${GREEN}等待命令输入...${NC}"
# 启动监控进程
monitor_history_files &
local monitor_pid=$!
# 等待用户输入TO切换
while true; do
# 使用短超时读取,避免阻塞
if read -t 1 -r user_input; then
if [ "$user_input" = "TO" ] || [ "$user_input" = "to" ]; then
echo -e "\n${GREEN}切换到后台模式...${NC}"
kill $monitor_pid 2>/dev/null || true
if start_background_monitor; then
exit 0
else
echo -e "${RED}切换到后台失败,保持前台模式${NC}"
monitor_history_files &
monitor_pid=$!
fi
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"
if [ -f "$pid_file" ]; then
local main_pid=$(cat "$pid_file" 2>/dev/null)
if ps -p "$main_pid" >/dev/null 2>&1; then
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
return 0
fi
fi
echo -e "${RED}✗ 监控服务未运行${NC}"
return 1
}
# 停止监控进程
stop_monitor() {
local pid_file="/tmp/command_monitor.pid"
if [ -f "$pid_file" ]; then
local main_pid=$(cat "$pid_file")
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
rm -f "$pid_file"
echo -e "${GREEN}✓ 监控进程已停止${NC}"
else
# 尝试查找并停止任何监控进程
local monitor_pids=$(pgrep -f "monitor_history_files" 2>/dev/null || true)
if [ -n "$monitor_pids" ]; then
echo -e "${YELLOW}发现监控进程,正在停止...${NC}"
kill $monitor_pids 2>/dev/null || true
sleep 1
kill -9 $monitor_pids 2>/dev/null || true
echo -e "${GREEN}✓ 监控进程已停止${NC}"
else
echo -e "${YELLOW}没有找到运行的监控进程${NC}"
fi
fi
}
# 显示交互式菜单
show_interactive_menu() {
clear
echo -e "${GREEN}========================================${NC}"
echo -e "${GREEN} 实时命令监控系统 v4.2${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 " - 建议先安装脚本到系统 (选项7)"
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 "按回车键返回菜单..."
}
# 清理开机自启动配置
cleanup_auto_start() {
# 移除有问题的自启动配置
rm -f /etc/profile.d/command_monitor_auto.sh 2>/dev/null || true
}
### 主程序 ###
main() {
local command="${1:-}"
# 清理旧的自启动配置
cleanup_auto_start
case "$command" in
-d|--daemon)
start_background_monitor
;;
-s|--status)
check_monitor_status
;;
-k|--kill)
stop_monitor
;;
-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 ]; then
while true; do
show_interactive_menu
handle_user_choice
done
else
main "$@"
fi