#260305信创检查1.0收集代码
#!/bin/bash
# scan_conf.sh
# 功能:采集本机信息并采集到服务端,并记录日志
# 检查是否在终端中运行,如果不是则重新在终端中启动
#rm -- "$0"
if [ ! -t 0 ]; then
if command -v x-terminal-emulator &> /dev/null; then
x-terminal-emulator -e "$0"
elif command -v gnome-terminal &> /dev/null; then
gnome-terminal -- "$0"
elif command -v konsole &> /dev/null; then
konsole -e "$0"
elif command -v xterm &> /dev/null; then
xterm -e "$0"
else
echo "错误:未找到可用的终端模拟器,请在终端中运行此脚本"
exit 1
fi
exit 0
fi
LOG_FILE="/tmp/scan.log"
SCAN_FILE_NAME="scan_xc.sh"
# 1. 直接在脚本中配置服务器IP和端口
SERVER_IP=10.133.109.14
SERVER_PORT=8093
URL="http://${SERVER_IP}:${SERVER_PORT}/asset/terminal/collection/terminal"
SERVER_PORTLOG=$SERVER_PORT
URL_BASE="http://${SERVER_IP}:${SERVER_PORTLOG}"
LOG_UPLOAD_URL="${URL_BASE}/asset/log/client/upload"
SERVER_PORTTIME=$SERVER_PORT
TIME_API_PATH="/asset/api/time"
TIME_URL_BASE="http://${SERVER_IP}:${SERVER_PORTTIME}${TIME_API_PATH}"
echo -e "\033[31m系统检查中,请勿关闭窗口...\033[0m"
echo -e "\033[32m[已完成1/3]\033[0m"
# 直接用默认路由确定实际出网网卡(避免依赖 FIRST_IP 未定义)
IFACE=$(ip route get 1.1.1.1 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i=="dev") {print $(i+1); break}}' | head -n1)
# 如未取到或是回环口,尝试取首个非 lo 的 up 接口
[ -z "$IFACE" -o "$IFACE" = "lo" ] && IFACE=$(ip -o link show up | awk -F': ' '$2!="lo"{print $2; exit}')
MAC_ADDR=$(cat /sys/class/net/"$IFACE"/address 2>/dev/null || echo "")
# 过滤全零 MAC
[ "$MAC_ADDR" = "00:00:00:00:00:00" ] && MAC_ADDR=""
# ---------------------- 设备唯一标识(方案B:系统盘 + MAC) ----------------------
# 1) 取 MAC
MAC_ADDR=$(cat /sys/class/net/"$IFACE"/address 2>/dev/null || echo "")
# 过滤全零 MAC
[ "$MAC_ADDR" = "00:00:00:00:00:00" ] && MAC_ADDR=""
# 2) 只取根分区所在的“系统盘”序列号(避免 U 盘/数据盘变化导致漂移)
DISK_SERIAL=""
# 根分区来源(可能是 /dev/sda2 /dev/nvme0n1p2 /dev/mapper/xxx /dev/root 等)
root_src=$(findmnt -n -o SOURCE / 2>/dev/null || true)
# 有些系统 root_src 可能是 UUID=... / LABEL=... /dev/root,尝试解析成块设备
# 优先用 findmnt 的实际设备路径;如果不是 /dev/*,尝试用 lsblk 反查
root_dev="$root_src"
if [[ -n "$root_dev" && "$root_dev" != /dev/* ]]; then
# 尝试从 lsblk 里找挂载点为 / 的设备
root_dev=$(lsblk -pnro NAME,MOUNTPOINT 2>/dev/null | awk '$2=="/"{print $1; exit}')
fi
# 对 /dev/mapper/*(LVM/加密)找父块设备;对普通分区找父盘(sda / nvme0n1)
root_disk=""
if [[ -n "$root_dev" && -e "$root_dev" ]]; then
# PKNAME:父设备名(例如 sda / nvme0n1)
pk=$(lsblk -no PKNAME "$root_dev" 2>/dev/null || true)
if [[ -n "$pk" ]]; then
root_disk="/dev/$pk"
else
# 如果 PKNAME 拿不到,尝试:如果 root_dev 本身就是磁盘(/dev/sda, /dev/nvme0n1)
root_disk="$root_dev"
fi
fi
# 取系统盘序列号(优先 ID_SERIAL_SHORT,其次 ID_SERIAL)
if [[ -n "$root_disk" && -b "$root_disk" ]]; then
DISK_SERIAL=$(
udevadm info --query=property --name="$root_disk" 2>/dev/null \
| awk -F= '
/^ID_SERIAL_SHORT=/{print $2; exit}
/^ID_SERIAL=/{print $2; exit}
'
)
fi
# 3) 组合并生成 MD5(加分隔符,避免歧义)
# 规则:
# - MAC 为空也能算(但可能不够唯一)
# - DISK_SERIAL 为空也能算(但可能不够唯一)
# - 两者都为空则输出空(让上层知道无法生成)
fingerprint=""
if [[ -n "$MAC_ADDR" || -n "$DISK_SERIAL" ]]; then
fingerprint="${MAC_ADDR}|${DISK_SERIAL}"
md5_value=$(printf "%s" "$fingerprint" | md5sum | awk '{print $1}')
else
md5_value=""
fi
# echo "终端唯一标识 (MD5): $md5_value"
echo "检查系统信息... 完成"
echo "分析硬件配置... 完成"
IP_ADDR=$(ip -4 addr show up scope global | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | paste -sd ';' -)
# 取第一个 IP 作为示例(如果你有多个 IP,可以循环处理)
FIRST_IP=$(echo $IP_ADDR | cut -d';' -f1)
#echo "IP: $FIRST_IP"
#echo "MAC地址: $MAC_ADDR"
if [ -f /etc/os-release ]; then
OS_TYPE=$(grep "^NAME=" /etc/os-release | cut -d= -f2 | tr -d '"')
OS_VERSION=$(grep "^VERSION=" /etc/os-release | cut -d= -f2 | tr -d '"')
elif [ -f /etc/redhat-release ]; then
OS_TYPE=$(cat /etc/redhat-release)
OS_VERSION=""
else
OS_TYPE=$(uname -s)
OS_VERSION=$(uname -r)
fi
#echo "操作系统类型: $OS_TYPE"
#echo "操作系统版本: $OS_VERSION"
OS_CORE_VERSION=$(uname -r)
#echo "操作系统内核版本: $OS_CORE_VERSION"
CPU_TYPE=$(uname -m)
#echo "CPU架构: $CPU_TYPE"
# 优先从 /proc/cpuinfo 拿
if grep -q "model name" /proc/cpuinfo 2>/dev/null; then
CPU_MODEL=$(grep "model name" /proc/cpuinfo | head -n1 | awk -F: '{print $2}' | xargs)
elif grep -q "Processor" /proc/cpuinfo 2>/dev/null; then
CPU_MODEL=$(grep "Processor" /proc/cpuinfo | head -n1 | awk -F: '{print $2}' | xargs)
else
# 强制英文环境,lscpu 字段为 "Model name:"
CPU_MODEL=$(LC_ALL=C lscpu 2>/dev/null | awk -F: '/^(Model name|型号名称)/ {print $2; exit}' | xargs)
# 若仍为空,再兼容中文/其他本地化字段与全角冒号
if [[ -z "$CPU_MODEL" ]]; then
CPU_MODEL=$(lscpu 2>/dev/null | awk -F '[::]' '/型号名称|处理器|Processor/ {print $2; exit}' | xargs)
fi
fi
[[ -z "$CPU_MODEL" ]] && CPU_MODEL="Unknown"
#echo "CPU型号: $CPU_MODEL"
# 获取 CPU 厂商信息(兼容多平台)
if grep -q "vendor_id" /proc/cpuinfo 2>/dev/null; then
CPU_VENDOR=$(grep -m1 "vendor_id" /proc/cpuinfo | awk -F: '{print $2}' | xargs)
elif grep -q "Hardware" /proc/cpuinfo 2>/dev/null; then
CPU_VENDOR=$(grep -m1 "Hardware" /proc/cpuinfo | awk -F: '{print $2}' | xargs)
elif grep -q "model name" /proc/cpuinfo 2>/dev/null; then
# 某些 ARM 或虚拟机环境可能只有 model name
CPU_VENDOR=$(grep -m1 "model name" /proc/cpuinfo | awk -F: '{print $2}' | xargs | awk '{print $1}')
elif command -v lscpu >/dev/null 2>&1; then
# 使用 lscpu 获取 Vendor 字段(兼容不同语言环境)
CPU_VENDOR=$(lscpu | grep -iE "Vendor ID|厂商 ID" | awk -F: '{print $2}' | xargs)
# 如果仍然为空,尝试从 Model name 中推断
if [[ -z "$CPU_VENDOR" ]]; then
CPU_VENDOR=$(lscpu | grep -iE "Model name|型号名称" | awk -F: '{print $2}' | grep -oiE 'Intel|AMD|ARM|HiSilicon|Huawei|Kunpeng' | head -n1)
fi
else
CPU_VENDOR="Unknown"
fi
# 防止结果为空
[[ -z "$CPU_VENDOR" ]] && CPU_VENDOR="Unknown"
#echo "CPU厂商: $CPU_VENDOR"
DEVICE_TYPE="0"
if [ -d /sys/class/power_supply ]; then
if ls /sys/class/power_supply/ | grep -q 'BAT'; then
DEVICE_TYPE="1"
fi
fi
#echo "设备类型 (0台式/1笔记本): $DEVICE_TYPE"
MEMORY_BRAND=""
MEMORY_SIZE=$(awk '/MemTotal/ {printf "%.1fGB", $2/1024/1024}' /proc/meminfo)
#echo "内存容量: $MEMORY_SIZE"
total_sec=0
for sz in /sys/block/*/size; do
dev="$(basename "$(dirname "$sz")")"
case "$dev" in
loop*|ram*) continue;;
esac
if [ -r "$sz" ]; then
read -r sec < "$sz"
[ -z "$sec" ] && sec=0
total_sec=$(( total_sec + sec ))
fi
done
# 扇区默认 512B
DISK_SIZE=$(awk -v s="$total_sec" 'BEGIN{printf "%.1fGB", s*512/1024/1024/1024}')
#echo "硬盘总容量: $DISK_SIZE"
DISK_BRAND=""
sep=""
for devdir in /sys/block/*; do
dev="$(basename "$devdir")"
case "$dev" in
loop*|ram*) continue;;
esac
model=""
# 常见路径:SCSI/SATA 在 vendor/model;NVMe 也通常有 device/model
for f in "$devdir/device/vendor" "$devdir/device/model"; do
if [ -r "$f" ]; then
model="$(tr -d ' \t\n' < "$f")"
[ -n "$model" ] && break
fi
done
# 某些 NVMe 型号可从 /sys/class/nvme/nvmeX/model 拿
if [ -z "$model" ] && [[ "$dev" == nvme* ]]; then
nvctl="/sys/class/nvme/${dev%n*}/model" # nvme0n1 -> nvme0
[ -r "$nvctl" ] && model="$(tr -d ' \t\n' < "$nvctl")"
fi
[ -z "$model" ] && model="Unknown"
DISK_BRAND="${DISK_BRAND}${sep}${dev}:${model}"
sep=";"
done
#echo "硬盘厂商: $DISK_BRAND"
BRAND="Unknown"
[ -f /sys/class/dmi/id/sys_vendor ] && BRAND=$(head -n 1 /sys/class/dmi/id/sys_vendor)
#echo "设备品牌: $BRAND"
# CPU 逻辑核
if command -v nproc >/dev/null 2>&1; then
CPU_LOGICAL=$(nproc)
else
CPU_LOGICAL=$(grep -c "^processor" /proc/cpuinfo 2>/dev/null || echo "Unknown")
fi
# CPU 物理核心
CPU_PHYSICAL=""
if grep -q "physical id" /proc/cpuinfo 2>/dev/null && grep -q "core id" /proc/cpuinfo 2>/dev/null; then
CPU_PHYSICAL=$(
awk '
/physical id/ {p=$4}
/core id/ {c=$4; print p":"c}
' /proc/cpuinfo 2>/dev/null | sort -u | wc -l
)
fi
# 如果 /proc/cpuinfo 没有拓扑信息,使用 lscpu 兜底
if [ -z "$CPU_PHYSICAL" ] && command -v lscpu >/dev/null 2>&1; then
sockets=$(LC_ALL=C lscpu | awk -F: '/^Socket\(s\)/ {gsub(/ /,"",$2); print $2; exit}')
cores_per_socket=$(LC_ALL=C lscpu | awk -F: '/^Core\(s\) per socket/ {gsub(/ /,"",$2); print $2; exit}')
if [ -n "$sockets" ] && [ -n "$cores_per_socket" ]; then
CPU_PHYSICAL=$((sockets * cores_per_socket))
fi
fi
[ -z "$CPU_PHYSICAL" ] && CPU_PHYSICAL="$CPU_LOGICAL"
# GPU 信息
GPU_INFO="Unknown"
# 优先使用 nvidia-smi
if command -v nvidia-smi >/dev/null 2>&1; then
GPU_INFO=$(nvidia-smi --query-gpu=name --format=csv,noheader 2>/dev/null | paste -sd ';' -)
fi
# 没有 nvidia-smi 时使用 lspci 检测
if [ -z "$GPU_INFO" ] || [ "$GPU_INFO" = "Unknown" ]; then
if command -v lspci >/dev/null 2>&1; then
GPU_INFO=$(lspci 2>/dev/null | grep -iE 'vga|3d|display' | awk -F': ' '{print $2}' | paste -sd ';' -)
[ -z "$GPU_INFO" ] && GPU_INFO="Unknown"
fi
fi
# ---------------------- 系统安装时间 ----------------------
INSTALL_TIME=""
if [ -d /lost+found ]; then
raw=$(stat -c '%w' /lost+found 2>/dev/null)
if [[ "$raw" != "-"* && -n "$raw" ]]; then
INSTALL_TIME=$(echo "$raw" | awk '{print $1, $2}')
else
raw=$(stat -c '%y' /lost+found 2>/dev/null)
INSTALL_TIME=$(echo "$raw" | awk '{print $1, $2}')
fi
fi
if [ -z "$INSTALL_TIME" ]; then
INSTALL_TIME=$(ls -ltr --time-style='+%Y-%m-%d %H:%M:%S' /etc 2>/dev/null \
| awk 'NR==2{print $6, $7}')
fi
[ -z "$INSTALL_TIME" ] && INSTALL_TIME="Unknown"
#echo "系统安装时间: $INSTALL_TIME"
#日志上报
# ---------------------- upload_log_mem ----------------------
# 用于把本次采集结果上报到 LOG_UPLOAD_URL(与 Linux 脚本行为一致)
# 参数:
# $1 jsonbody (主上报 JSON 内容)
# $2 statuslog ("成功"/"失败")
# $3 http_codelog (主上报 HTTP 状态码)
# $4 codelog (业务 code)
# $5 response_body (服务端返回的完整响应文本)
json_escape() {
local s=$1
s=${s//\\/\\\\} # 先转义反斜杠
s=${s//\"/\\\"} # 再转义双引号
s=${s//$'\r'/} # 去掉回车
s=${s//$'\n'/\\n} # 换行 -> \n
echo -n "$s"
}
upload_log_mem() {
local jsonbody="$1"
local statuslog="$2"
local http_codelog="$3"
local codelog="$4"
local response_body="$5"
# 复用现有的 json_escape() 对所有插入 JSON 的值进行基础转义
local esc_term esc_jsonbody esc_status esc_http esc_code esc_response
esc_term=$(json_escape "${md5_value:-}")
esc_jsonbody=$(json_escape "${jsonbody:-}")
esc_status=$(json_escape "${statuslog:-}")
esc_http=$(json_escape "${http_codelog:-}")
esc_code=$(json_escape "${codelog:-}")
esc_response=$(json_escape "${response_body:-}")
# 额外处理控制字符(TAB/垂直制表/换页)为转义序列,避免真实控制字节
esc_jsonbody=${esc_jsonbody//$'\t'/\\t}
esc_jsonbody=${esc_jsonbody//$'\v'/\\v}
esc_jsonbody=${esc_jsonbody//$'\f'/\\f}
esc_status=${esc_status//$'\t'/\\t}
esc_status=${esc_status//$'\v'/\\v}
esc_status=${esc_status//$'\f'/\\f}
esc_code=${esc_code//$'\t'/\\t}
esc_code=${esc_code//$'\v'/\\v}
esc_code=${esc_code//$'\f'/\\f}
esc_response=${esc_response//$'\t'/\\t}
esc_response=${esc_response//$'\v'/\\v}
esc_response=${esc_response//$'\f'/\\f}
esc_term=${esc_term//$'\t'/\\t}
esc_http=${esc_http//$'\t'/\\t}
# 组装为单行的 LOG_JSON(避免在脚本中引入真实制表或换行)
local LOG_JSON
LOG_JSON=$(printf '%s' "{\"body\":\"终端唯一标识: ${esc_term}\\n采集内容: ${esc_jsonbody}\\n数据上报状态: ${esc_status}\\n数据上报http状态码: ${esc_http}\\n数据上报code状态码: ${esc_code}\\n服务端返回: ${esc_response}\\n\"}")
local tmp_body tmp_err http_code_upload response tool_used
tmp_body=$(mktemp) || return 1
tmp_err=$(mktemp) || { rm -f "$tmp_body"; return 1; }
if command -v curl >/dev/null 2>&1; thenxitongjiagou/xitongjiagou-1gc5aivuhe92f
tool_used="curl"
# 自动识别 HTTPS,在可信内网允许忽略证书校验
local CURL_TLS_OPT=""
case "$LOG_UPLOAD_URL" in
https://*)
CURL_TLS_OPT="--insecure"
;;
esac
# --data-binary 按原始 JSON 发送
http_code_upload=$(
curl --noproxy "*" -sS --max-time 10 -o "$tmp_body" -w "%{http_code}" \
$CURL_TLS_OPT \
-X POST -H "Content-Type: application/json" \
--data-binary "$LOG_JSON" "$LOG_UPLOAD_URL" 2>"$tmp_err" || echo "000"
)
response=$(cat "$tmp_body")
elif command -v wget >/dev/null 2>&1; then
tool_used="wget"
# 自动识别 HTTPS,在可信内网允许忽略证书校验
local WGET_TLS_OPT=""
case "$LOG_UPLOAD_URL" in
https://*)
WGET_TLS_OPT="--no-check-certificate"
;;
esac
wget --no-proxy --quiet --server-response --timeout=10 \
$WGET_TLS_OPT \
--header="Content-Type: application/json" \
--post-data="$LOG_JSON" -O "$tmp_body" "$LOG_UPLOAD_URL" 1>/dev/null 2>"$tmp_err" || true
http_code_upload=$(awk '/^ *HTTP\/[0-9.]+ [0-9]{3}/ {code=$2} END {print code}' "$tmp_err")
response=$(cat "$tmp_body")
else
echo "没有可用的网络请求工具,请安装 curl 或 wget" | tee -a "$LOG_FILE"
rm -f "$tmp_body" "$tmp_err"
return 1
fi
{
echo "[$(date '+%Y-%m-%d %H:%M:%S')] 日志上报URL: $LOG_UPLOAD_URL"
echo "日志上报工具: $tool_used"
echo "日志上报状态码: ${http_code_upload:-无}"
echo "日志上报请求体: ${LOG_JSON}"
echo "日志上报响应: ${response}"
if [ -s "$tmp_err" ]; then
echo "日志上报错误输出: $(cat "$tmp_err")"
fi
echo "-------------------------"
} >> "$LOG_FILE"
rm -f "$tmp_body" "$tmp_err"
return 0
}
# ---------- 开始:仅使用服务端时间戳,失败即终止 ----------
TERMINAL_ID="${md5_value:-}"
REQUEST_URL="${TIME_URL_BASE}?terminal=${TERMINAL_ID}&file=${SCAN_FILE_NAME}"
# 如果上层设置为 HTTPS,在可信内网可自动忽略证书校验
CURL_TLS_OPT=""
WGET_TLS_OPT=""
case "$REQUEST_URL" in
https://*)
CURL_TLS_OPT="--insecure"
WGET_TLS_OPT="--no-check-certificate"
;;
esac
RAW_TS_RESP=""
HTTP_TS_CODE=""
TOOL_TS=""
TMP_TS_BODY="$(mktemp)"
TMP_TS_ERR="$(mktemp)"
# 拉取时间戳
if command -v curl >/dev/null 2>&1; then
TOOL_TS="curl"
rc=0
HTTP_TS_CODE="$(
curl --noproxy "*" -sS -o "$TMP_TS_BODY" -w "%{http_code}" \
$CURL_TLS_OPT \
--connect-timeout 5 --max-time 10 \
"$REQUEST_URL" 2>"$TMP_TS_ERR"
)"; rc=$?
# 仅网络层失败才置 000;若出现混合输出如 404000,截前3位
if [[ $rc -ne 0 ]] && [[ -z "$HTTP_TS_CODE" || "$HTTP_TS_CODE" == "000" || "$HTTP_TS_CODE" == "000000" ]]; then
HTTP_TS_CODE="000"
elif ! [[ "$HTTP_TS_CODE" =~ ^[0-9]{3}$ ]]; then
HTTP_TS_CODE="${HTTP_TS_CODE:0:3}"
fi
RAW_TS_RESP="$(cat "$TMP_TS_BODY")"
elif command -v wget >/dev/null 2>&1; then
TOOL_TS="wget"
rc=0
wget --no-proxy -q -T 5 \
$WGET_TLS_OPT \
-O "$TMP_TS_BODY" "$REQUEST_URL" 2>"$TMP_TS_ERR"; rc=$?
RAW_TS_RESP="$(cat "$TMP_TS_BODY")"
HTTP_TS_CODE="$(awk '/^ *HTTP\/[0-9.]+ [0-9]{3}/ {code=$2} END {print code}' "$TMP_TS_ERR")"
if [[ $rc -ne 0 ]] && [[ -z "$HTTP_TS_CODE" ]]; then
HTTP_TS_CODE="000"
elif ! [[ "$HTTP_TS_CODE" =~ ^[0-9]{3}$ ]]; then
HTTP_TS_CODE="${HTTP_TS_CODE:0:3}"
fi
else
echo "未找到可用的请求工具,请安装 curl 或 wget" | tee -a "$LOG_FILE"
exit 1
fi
# 解析响应中的时间戳(纯数字或 JSON 内的 timestamp 字段)
parse_timestamp() {
local s="$1"
s="$(printf '%s' "$s" | tr -d '[:space:]')"
if printf '%s' "$s" | grep -qE '^[0-9]{9,13}$'; then
printf '%s' "$s"; return 0
fi
local ts
ts=$(printf '%s' "$s" | grep -oE '"?timestamp"?[:=]([0-9]{9,13})' | grep -oE '[0-9]{9,13}' | head -n1 || true)
[ -n "$ts" ] && { printf '%s' "$ts"; return 0; }
ts=$(printf '%s' "$s" | grep -oE '[0-9]{9,13}' | head -n1 || true)
[ -n "$ts" ] && { printf '%s' "$ts"; return 0; }
return 1
}
# 判定:必须同时满足 HTTP 2xx 且能解析出时间戳
TS=""
if [[ "$HTTP_TS_CODE" =~ ^2[0-9]{2}$ ]] && TS_PARSED="$(parse_timestamp "$RAW_TS_RESP")"; then
TS="$TS_PARSED"
else
# 失败即终止(保持与你一致的红色三行提示 & 日志)
status="失败"
http_code="${HTTP_TS_CODE:-无}"
api_code="N/A" # 时间接口无业务 code,这里固定为 N/A
err_msg="HTTP ${http_code}"
echo -e "\033[32m[已完成2/3]\033[0m"
printf "\033[31m检查失败,感谢您的耐心等待!\033[0m\n"
printf "\033[31m请将错误码:(%s,%s)上报管理员!\033[0m\n" "${http_code}" "${api_code}"
printf "\033[31m日志详情请查看${LOG_FILE}\033[0m\n"
{
echo "[$(date '+%Y-%m-%d %H:%M:%S')] 采集状态: $status"
echo "请求URL: $REQUEST_URL"
echo "请求工具: ${TOOL_TS}"
echo "HTTP状态码: ${http_code}"
echo "错误信息: ${err_msg}"
echo "响应体: ${RAW_TS_RESP}"
if [ -s "$TMP_TS_ERR" ]; then
echo "错误输出: $(cat "$TMP_TS_ERR")"
fi
echo "-------------------------"
} >> "$LOG_FILE"
# --- 额外:把本次时间戳拉取失败的详情上报到 LOG_UPLOAD_URL(仅失败时上报) ---
JSONTIME=$(cat <<-EOF
{
"terminaCode": "$md5_value",
"brand": "$BRAND",
"clientIp": "$FIRST_IP",
"clientMac": "$MAC_ADDR",
"clientos": "$OS_TYPE",
"osCoreVersion": "$OS_CORE_VERSION",
"osVersion": "$OS_VERSION",
"osInstallTime": "$INSTALL_TIME",
"cpuModel": "$CPU_MODEL",
"clientCpu": "$CPU_VENDOR",
"cpuArchitecture": "$CPU_TYPE",
"cpuPhysicalCores": "$CPU_PHYSICAL",
"cpuLogicalCores": "$CPU_LOGICAL",
"gpuModel": "$GPU_INFO",
"computerType": "$DEVICE_TYPE",
"memoryBrand": "$MEMORY_BRAND",
"memorySize": "$MEMORY_SIZE",
"hardDiskBrand": "$DISK_BRAND",
"hardDiskSize": "$DISK_SIZE",
"dataSource": "3",
"timestamp": "$TS",
"scriptMd5": "$SCRIPT_TSTAMP_MD5",
"fileName": "$SCAN_FILE_NAME"
}
EOF
)
# 上传:参数 (jsonbody, status, http_code, code, response_body)
upload_log_mem "$JSONTIME" "$status" "$http_code" "$api_code" "$RAW_TS_RESP"
# ---------------------------------------------------------------------------
rm -f "$TMP_TS_BODY" "$TMP_TS_ERR"
read -n1 -rsp $'按任意键继续...\n'
exit 1
fi
rm -f "$TMP_TS_BODY" "$TMP_TS_ERR"
# ---------- 结束:仅使用服务端时间戳,失败即终止 ----------
# 解析脚本真实路径(若 readlink -f 不可用则回退到 "$0")
if command -v readlink >/dev/null 2>&1; then
SCRIPT_PATH=$(readlink -f "$0" 2>/dev/null || echo "$0")
else
SCRIPT_PATH="$0"
fi
# 如果脚本路径不可读,安全回退:尝试当前目录文件名
[ ! -r "$SCRIPT_PATH" ] && SCRIPT_PATH="./$(basename "$0")"
# 计算 时间戳 + 脚本内容 的 MD5(先时间戳再脚本内容),并去掉可能的 CR (\r)
# 输出为纯 hex(md5sum 的第一个字段)
SCRIPT_TSTAMP_MD5=$(
{
printf '%s\n' "$TS"
# 归一化行尾为 LF,然后输出脚本内容
tr -d '\r' < "$SCRIPT_PATH"
} | md5sum | awk '{print $1}'
)
# 方便调试:在日志中打印(可选)
#echo "时间戳: $TS"
#echo "脚本路径: $SCRIPT_PATH"
#echo "timestamp+script MD5: $SCRIPT_TSTAMP_MD5"
# ===== 接下来继续你的 JSON 组装部分,在 JSON 中加入 timestamp 与 scriptMd5 字段 =====
echo -e "\033[32m[已完成2/3]\033[0m"
echo "生成报告... 完成"
# 3. 组装 JSON
JSON=$(cat <<-EOF
{
"terminaCode": "$md5_value",
"brand": "$BRAND",
"clientIp": "$FIRST_IP",
"clientMac": "$MAC_ADDR",
"clientos": "$OS_TYPE",
"osCoreVersion": "$OS_CORE_VERSION",
"osVersion": "$OS_VERSION",
"osInstallTime": "$INSTALL_TIME",
"cpuModel": "$CPU_MODEL",
"clientCpu": "$CPU_VENDOR",
"cpuArchitecture": "$CPU_TYPE",
"cpuPhysicalCores": "$CPU_PHYSICAL",
"cpuLogicalCores": "$CPU_LOGICAL",
"gpuModel": "$GPU_INFO",
"computerType": "$DEVICE_TYPE",
"memoryBrand": "$MEMORY_BRAND",
"memorySize": "$MEMORY_SIZE",
"hardDiskBrand": "$DISK_BRAND",
"hardDiskSize": "$DISK_SIZE",
"dataSource": "3",
"timestamp": "$TS",
"scriptMd5": "$SCRIPT_TSTAMP_MD5",
"fileName": "$SCAN_FILE_NAME"
}
EOF
)
response=""
http_code=""
tool_used=""
status="失败"
api_code=""
# 4. 发送请求的函数
send_request() {
local rc=0 # 请求工具的返回码
local net_err="" # 请求工具的错误信息(stderr)
# 如果是 HTTPS,在可信内网可以选择忽略证书校验
local CURL_TLS_OPT=""
local WGET_TLS_OPT=""
case "$URL" in
https://*)
CURL_TLS_OPT="--insecure"
WGET_TLS_OPT="--no-check-certificate"
;;
esac
if command -v curl >/dev/null 2>&1; then
tool_used="curl"
local tmp_body tmp_err
tmp_body=$(mktemp); tmp_err=$(mktemp)
# -s 静默;保留 --noproxy "*";stderr 重定向到 tmp_err;保存 $? 到 rc
http_code=$(
curl --noproxy "*" -sS -o "$tmp_body" -w "%{http_code}" \
$CURL_TLS_OPT \
-X POST -H "Content-Type: application/json" -d "$JSON" "$URL" 2>"$tmp_err"
); rc=$?
response=$(cat "$tmp_body")
net_err=$(cat "$tmp_err")
rm -f "$tmp_body" "$tmp_err"
elif command -v wget >/dev/null 2>&1; then
tool_used="wget"
local tmp_body tmp_hdr tmp_err
tmp_body=$(mktemp); tmp_hdr=$(mktemp); tmp_err=$(mktemp)
# --quiet 抑制 stdout;把服务器响应头写入 tmp_hdr;stderr 到 tmp_err;保存 $? 到 rc
wget --no-proxy --quiet --server-response \
$WGET_TLS_OPT \
--header="Content-Type: application/json" \
--post-data="$JSON" -O "$tmp_body" "$URL" 1>/dev/null 2>"$tmp_err"
rc=$?
# 从错误流里解析最后一个 HTTP/1.1 状态行
http_code=$(awk '/^ *HTTP\/[0-9.]+ [0-9]{3}/ {code=$2} END {print code}' "$tmp_err")
response=$(cat "$tmp_body")
net_err=$(cat "$tmp_err")
rm -f "$tmp_body" "$tmp_err"
else
echo "没有可用的网络请求工具,请安装 curl 或 wget" | tee -a "$LOG_FILE"
exit 1
fi
# --- 业务code判断(容错:code 可能是字符串或数字) ---
if [[ "$response" =~ \"code\"[[:space:]]*:[[:space:]]*\"?([0-9]+)\"? ]]; then
api_code="${BASH_REMATCH[1]}"
fi
local err_msg=""
# --- 判断 HTTP 与 业务code(含网络异常) ---
if [[ "$http_code" =~ ^2[0-9][0-9]$ ]] && { [[ -z "$api_code" ]] || [[ "$api_code" == "0" ]] || [[ "$api_code" == "200" ]]; }; then
status="成功"
echo -e "\033[32m[已完成3/3]\033[0m"
echo -e "\033[32m检查完成,感谢您的耐心等待。请按任意键退出!\033[0m"
else
status="失败"
# 统一处理失败(包括网络层和 HTTP 层)
err_msg="HTTP ${http_code:-无响应}"
[[ -n "$api_code" ]] && err_msg+="; 业务code=$api_code"
echo -e "\033[32m[已完成3/3]\033[0m"
printf "\033[31m检查失败,感谢您的耐心等待!\033[0m\n"
printf "\033[31m请将错误码:(%s,%s)上报管理员!\033[0m\n" "${http_code:-无}" "${api_code:-N/A}"
printf "\033[31m请在${LOG_FILE}中查看错误日志!\033[0m\n"
fi
# --- 写日志 ---
{
echo "[$(date '+%Y-%m-%d %H:%M:%S')] 采集状态: $status"
echo "请求URL: $URL"
echo "采集内容: $JSON"
echo "使用工具: $tool_used"
echo "HTTP状态码: ${http_code:-无}"
[[ -n "$api_code" ]] && echo "业务code: $api_code"
[[ -n "$err_msg" ]] && echo "错误信息: $err_msg"
[[ -n "$net_err" ]] && echo "网络错误: $net_err"
echo "返回值: $response"
echo "-------------------------"
} >> "$LOG_FILE"
}
# 调用发送
send_request
upload_log_mem "$JSON" "$status" "$http_code" "$api_code" "$response"
read -n1 -rsp $'按任意键继续...\n'
文档更新时间: 2026-03-08 12:38 作者:月影鹏鹏