rebase 到 main(00e7271,M43 time32 已合入) 时解开 build_netmgrd.sh 冲突:**两者都保留**—— M43 的 `../sdk/meow_time32.c` + `-DMEOW_TIME32_LIBC_OVERRIDES`,以及我的 ROOT/ZIGCC/OUT 环境变量化。编译改在 mktemp 目录里做(产物名仍是字面量 `-o meow_netmgrd`,供 sdk/test_time32_abi.sh 的按名定位),编完再落到 OUT —— 设了 OUT 也不会覆盖/删除仓库里 那份被跟踪的二进制(r1 期间 mv 曾把它删掉过)。门禁自证:`--builds` → build meow_netmgrd src=yes macro=yes;`--no-builds` → 4263=3 4403=0 16B-layout=OK ALL PASS。 P1-1(写操作越过客户端 1500ms 窗口):预算模型改成**每一层都受限**—— `BT_ACT_TMO_MS 800 / BT_ACT_SHORT_MS 600 / BT_VERIFY_BUDGET_MS 220 / gap 90`, `bt_poll_probe` 把**剩余预算**传给探测函数(`bt_acl_has_tri`/`bt_info_flag` 新增 tmo 形参), 探测自身那次 run_cmd 也受同一个 deadline 约束;`bt_onoff` 同理(up 失败只在剩余预算内重试 一次);成功文案不再补设备名(省掉 1-2 次 fork,UI 本来就按 MAC join)。 探测改为三态:命令失败/超时 = -1(**未知**),不再被当成「没有 ACL」——否则 disconnect 会在 查不到的情况下报「已断开」,又是一次假成功。 实测(宿主桩驱动 bt_ctl_execute,命令卡死 5s 的形态):connect 1042-1047ms、pair 1042-1045ms、 bt on 831ms,全部 < 1450 且有 400ms+ 余量(r1 实测 1528/2424ms)。设备侧实测 fork 成本 ≈25-30ms/次(bt a2dp 50ms、bt status 100ms),余量足够。 P2-1(A2DP 1+N 子进程塞进 status JSON):`bt_a2dp_query(buf,cap,budget)` 加**总预算**, list-pcms 单命令 ≤400ms、每个 info ≤300ms、剩下端点超预算就不再查;`bt a2dp` 命令用 900ms。 status JSON 不在无连接时查(0 次 fork),有连接时走 10s TTL 缓存 + 500ms 预算; `a2dp_codec`/`a2dp_rate` 字段保留(MeowPanel btCard 依赖,见 docs/plan/FUTURE_MEOWPANEL.md:48)。 实测 4 端点全卡死:911ms(r1 6053ms);status JSON 首次 350-384ms、缓存命中 31-49ms。 P2-2(test_bt_ctl 不可重复):开跑先 `rm -rf` 状态目录、退出再清;连跑 3 次 ALL PASS(贴了输出)。 P2-3(bt_onoff 顺序 + 多节点):开=先 rfkill unblock(**全部**同类型节点)再 hciconfig up, up 未生效时剩余预算内重试一次;关=先 down 再 block。新增 `rfkill_set_block_all()`(旧 `rfkill_set_block` 仍只写首个节点,WiFi 路径行为不变)。桩改成「blocked 时 up 必败」,并加了 反向用例证明桩真的会失败(否则断言空转)。 P3:①info 的 RUNCMD_EMPTY(exit 0 无输出)不再当失败跳过;②`bt_pcm_paths_parse` 的 `/a2dpsrc` 搜索限制在行内 [p,e)(原来会跨 \n 命中下一行);③`bt_devices_text` 注释写明 「本轮扫描见到过的设备」而非「此刻在场」([DEL] 不删除、devices 块含 BlueZ 历史缓存)。 附带(塔 11:06 建议,BT 作用域内先落地):连续 3 次 RUNCMD_TIMEOUT 后熔断 30s,冷却期内 直接回「熔断中」不再 fork(真机 55 个挂死 bluetoothctl 吃光进程槽的事故);任何非超时返回清零。 单测覆盖:第 4 次调用 0ms 返回。范围只限 BT 写操作,WiFi/DNS 调用点未动。 真机(本轮只读,设备已恢复在线):新件 md5 e62e7512 上卡只读验收 status/paired/connected/ devices/a2dp 全通;`ps w | grep -c bluetoothctl` 前后均为 2(≤3);Music du 199164544; 临时目录/独立实例清理干净,/usr/bin 未动。
477 lines
22 KiB
Bash
Executable File
477 lines
22 KiB
Bash
Executable File
#!/bin/bash
|
||
# test_netmgrd.sh — meow_netmgrd 宿主桩测试(假 sysfs/procfs 节点 + 桩后端命令,MEOW_* env 覆盖)。
|
||
# 用法:./test_netmgrd.sh [host二进制路径](默认 ./meow_netmgrd_host;可传 mipsel 二进制 + QEMU 前缀)
|
||
# QEMU="qemu-mipsel" ./test_netmgrd.sh ./meow_netmgrd # qemu 验证
|
||
set -u
|
||
cd "$(dirname "$0")"
|
||
BIN="${1:-./meow_netmgrd_host}"
|
||
Q="${QEMU:-}"
|
||
T=$(mktemp -d "${TMPDIR:-/tmp}/netmgrdtest.XXXXXX")
|
||
trap 'kill $DPID 2>/dev/null; rm -rf "$T"' EXIT
|
||
PASS=0; FAIL=0
|
||
ck() { # ck <名> <期望子串> <实际>
|
||
if echo "$3" | grep -qF "$2"; then PASS=$((PASS+1)); echo "PASS $1";
|
||
else FAIL=$((FAIL+1)); echo "FAIL $1: 期望含 [$2] 实际=[$3]"; fi
|
||
}
|
||
|
||
# ---- 搭假节点 ----
|
||
NET="$T/net"; RF="$T/rfkill"; BT="$T/bt"
|
||
mkdir -p "$NET/lo" "$NET/wlan0" "$RF/rfkill0" "$RF/rfkill1" "$BT/hci0"
|
||
echo up > "$NET/lo/operstate"
|
||
echo up > "$NET/wlan0/operstate"
|
||
echo wlan > "$RF/rfkill0/type"; echo 0 > "$RF/rfkill0/soft"; echo 0 > "$RF/rfkill0/hard"
|
||
echo bluetooth > "$RF/rfkill1/type"; echo 1 > "$RF/rfkill1/soft"; echo 0 > "$RF/rfkill1/hard"
|
||
echo 00:1A:7D:DA:71:13 > "$BT/hci0/address"
|
||
echo on > "$BT/hci0/power"
|
||
cat > "$T/netdev" <<'EOF'
|
||
Inter-| Receive | Transmit
|
||
face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
|
||
lo: 1000 10 0 0 0 0 0 0 1000 10 0 0 0 0 0 0
|
||
wlan0: 5000000 5000 0 0 0 0 0 0 1000000 1000 0 0 0 0 0 0
|
||
EOF
|
||
cat > "$T/route" <<'EOF'
|
||
Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
|
||
wlan0 005EC20A 6F3DC20A 0003 0 0 0 00FFFFFF 0 0 0
|
||
lo 0100007F 00000000 0001 0 0 0 000000FF 0 0 0
|
||
EOF
|
||
printf 'nameserver 223.5.5.5\nnameserver 10.78.193.1\n' > "$T/resolv"
|
||
echo meow > "$T/boot_mode"
|
||
|
||
# ---- 期2 观测层打桩:/proc/net/{tcp,udp}(IPv4 LE hex)+ 域名规则 conf + amsd/svcd 缺省 ----
|
||
cat > "$T/tcp" <<'EOF'
|
||
sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
|
||
0: 32C14E0A:5137 0905CD8C:01BB 01 00000000:00000000 00:00000000 00000000 1000 0 12345 1 0 0 10 0
|
||
1: 32C14E0A:4F32 32C34E0A:01BB 01 00000000:00000000 00:00000000 00000000 1000 0 12346 1 0 0 10 0
|
||
2: 32C14E0A:0A00 00000000:0000 07 00000000:00000000 00:00000000 00000000 1000 0 67890 1 0 0 10 0
|
||
3: 32C14E0A:4F33 32C34E0A:01BB 01 00000000:00000000 00:00000000 00000000 1000 0 0 1 0 0 10 0
|
||
EOF
|
||
cat > "$T/udp" <<'EOF'
|
||
sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
|
||
0: 0100007F:D301 0100007F:0035 07 00000000:00000000 00:00000000 00000000 0 0 99999 1 0 0 10 0
|
||
EOF
|
||
printf 'app:dev.player.ncm domain=-ads.example.com\nproc:dropbear domain=+x\n' > "$T/policy"
|
||
|
||
# 桩后端命令(popen stubbing)
|
||
cat > "$T/wpa_cli" <<'EOF'
|
||
#!/bin/sh
|
||
case "$1" in
|
||
status) echo "wpa_state=COMPLETED"; echo "ssid=MyWiFi"; echo "ip_address=10.78.193.50"; echo "signal=-45";;
|
||
scan) echo OK;;
|
||
scan_results) printf 'bssid / frequency / signal level / flags / ssid\nxx:xx / 2412 / -50 / WPA2 / TestAP\n';;
|
||
list_networks) printf 'network id / ssid / bssid / flags\n0\tMyWiFi\tany\t[CURRENT]\n';;
|
||
add_network) echo 1;;
|
||
set_network) echo OK;;
|
||
enable_network) echo OK;;
|
||
select_network) echo OK;;
|
||
disconnect) echo OK;;
|
||
esac
|
||
exit 0
|
||
EOF
|
||
cat > "$T/hciconfig" <<'EOF'
|
||
#!/bin/sh
|
||
echo "hci0: Type: Primary Bus: USB"
|
||
echo " BD Address: 00:1A:7D:DA:71:13 ACL MTU: 1021:8 SCO MTU: 64:1"
|
||
echo " UP RUNNING PSCAN"
|
||
EOF
|
||
cat > "$T/bluetoothctl" <<'EOF'
|
||
#!/bin/sh
|
||
case "${1:-}" in
|
||
paired|paired-devices)
|
||
echo "Device 00:1A:7D:DA:71:13 My Headset"
|
||
echo "Device 11:22:33:44:55:66 Speaker";;
|
||
devices)
|
||
echo "Device 00:1A:7D:DA:71:13 My Headset"
|
||
echo "Device 11:22:33:44:55:66 Speaker"
|
||
echo "Device 33:DE:2E:21:AD:30 Pudding";;
|
||
connect|disconnect|pair|remove)
|
||
echo "Controller 00:1A:7D:DA:71:13 Connected: yes";;
|
||
*) echo "Device 00:1A:7D:DA:71:13 My Headset"; echo "Device 11:22:33:44:55:66 Speaker";;
|
||
esac
|
||
exit 0
|
||
EOF
|
||
cat > "$T/hcitool" <<'EOF'
|
||
#!/bin/sh
|
||
echo " < ACL 33:DE:2E:21:AD:30 handle 12 state 1 lm MASTER"
|
||
echo " < ACL 11:22:33:44:55:66 handle 13 state 1 lm SLAVE"
|
||
exit 0
|
||
EOF
|
||
cat > "$T/bluealsa-cli" <<'EOF'
|
||
#!/bin/sh
|
||
# v4.1.1 实际命令表:list-services/list-pcms/status/info/codec/volume/...(没有 a2dp 子命令)
|
||
case "${1:-}" in
|
||
list-pcms)
|
||
# 真机路径带末端段,且结尾字面量不可依赖(首连实测是 /a2dpsrc/sink):
|
||
# /org/bluealsa/hci0/dev_XX/a2dpsrc/sink
|
||
echo "/org/bluealsa/hci0/dev_33_DE_2E_21_AD_30/a2dpsrc/sink";;
|
||
info)
|
||
# 真机 v4.1.1 原文(M50 2026-11-11 18:26 抓,PCM 被 audiod 撑住时);此处取「正在传音频」态
|
||
echo "Device: /org/bluez/hci0/dev_33_DE_2E_21_AD_30"
|
||
echo "Sequence: 3"
|
||
echo "Transport: A2DP-source"
|
||
echo "Mode: sink"
|
||
echo "Running: true"
|
||
echo "Format: S16_LE"
|
||
echo "Channels: 2"
|
||
echo "Sampling: 48000 Hz"
|
||
echo "Available codecs: SBC AAC"
|
||
echo "Selected codec: AAC"
|
||
echo "Delay: 17.2 ms"
|
||
echo "SoftVolume: false"
|
||
echo "Volume: L: 127 R: 127"
|
||
echo "Muted: L: false R: false";;
|
||
esac
|
||
exit 0
|
||
EOF
|
||
cat > "$T/ip" <<'EOF'
|
||
#!/bin/sh
|
||
for a in "$@"; do
|
||
if [ "$a" = "wlan0" ]; then echo "2: wlan0 inet 10.78.193.50/24 brd 255.255.255.255 scope global wlan0"; fi
|
||
if [ "$a" = "lo" ]; then echo "1: lo inet 127.0.0.1/8 scope host lo"; fi
|
||
done
|
||
exit 0
|
||
EOF
|
||
for f in wpa_cli hciconfig bluetoothctl hcitool bluealsa-cli ip; do chmod +x "$T/$f"; done
|
||
|
||
mkdir -p "$T/logs" "$T/pid"
|
||
# 期2:DNS 禁用(宿主无 root 不绑 53),观测层经 MEOW_TCP/UDP 打桩;amsd/svcd 缺省(实体归因走 proc:comm→unknown)。
|
||
ENVS="MEOW_NET_DIR=$NET MEOW_NETDEV=$T/netdev MEOW_NET_ROUTE=$T/route MEOW_RESOLV=$T/resolv
|
||
MEOW_RFKILL_DIR=$RF MEOW_BT_DIR=$BT MEOW_BOOT_MODE=$T/boot_mode MEOW_BOOTTS_CACHE=$T/bootts
|
||
MEOW_IP=$T/ip MEOW_WPA_CLI=$T/wpa_cli MEOW_HCICONFIG=$T/hciconfig MEOW_BLUETOOTHCTL=$T/bluetoothctl
|
||
MEOW_HCITOOL=$T/hcitool MEOW_BLUEALSA_CLI=$T/bluealsa-cli MEOW_BT_SCAN_CACHE=$T/bt_scan.txt
|
||
MEOW_NET_SOCK=$T/net.sock MEOW_NET_POLL_SEC=1 MEOW_LOG_DIR=$T/logs
|
||
MEOW_TCP=$T/tcp MEOW_UDP=$T/udp MEOW_NET_POLICY=$T/policy
|
||
MEOW_AMS_SOCK=$T/no_ams MEOW_SVCD_SOCK=$T/no_svcd MEOW_DNS_DISABLE=1"
|
||
ln -sf "$(readlink -f "$BIN")" "$T/meow_net" # argv[0] 双形态 → CLI
|
||
|
||
# ---- T0: 守护起 ----
|
||
env $ENVS $Q "$BIN" --sock "$T/net.sock" >"$T/daemon.log" 2>&1 &
|
||
DPID=$!
|
||
for i in $(seq 1 30); do [ -S "$T/net.sock" ] && break; sleep 0.2; done
|
||
[ -S "$T/net.sock" ] || { echo "FAIL T0: daemon socket 未出现"; cat "$T/daemon.log"; exit 1; }
|
||
PASS=$((PASS+1)); echo "PASS T0 daemon up"
|
||
|
||
# ---- T1: status(读级,任意调用者)----
|
||
OUT=$(env $ENVS $Q "$T/meow_net" status 2>&1)
|
||
ck T1a "mode=meow src=daemon" "$OUT"
|
||
ck T1b "iface:lo kind=lo up=true" "$OUT"
|
||
ck T1c "iface:wlan0 kind=wifi up=true" "$OUT"
|
||
ck T1d "ip=10.78.193.50" "$OUT" # 若 iface 行 ip 解析成功(ip 桩已修)——实际由 wlan gw 行给,见下
|
||
ck T1e "gw=10.194.61.111" "$OUT"
|
||
ck T1f "wifi connected=1 state=COMPLETED ssid=MyWiFi" "$OUT"
|
||
ck T1g "bt available=1 powered=1 address=00:1A:7D:DA:71:13 paired=2" "$OUT"
|
||
ck T1h "rfkill:wlan soft=0 hard=0" "$OUT"
|
||
ck T1i "rfkill:bluetooth soft=1 hard=0" "$OUT"
|
||
|
||
# ---- T2: iface 单/全 ----
|
||
OUT=$(env $ENVS $Q "$T/meow_net" iface wlan0 2>&1)
|
||
ck T2a '"name":"wlan0"' "$OUT"
|
||
ck T2b '"kind":"wifi"' "$OUT"
|
||
ck T2c '"ip":"10.78.193.50"' "$OUT"
|
||
OUT=$(env $ENVS $Q "$T/meow_net" iface 2>&1)
|
||
ck T2d '"name":"lo"' "$OUT"
|
||
|
||
# ---- T3: wifi networks(读级)----
|
||
OUT=$(env $ENVS $Q "$T/meow_net" wifi networks 2>&1)
|
||
ck T3a "MyWiFi" "$OUT"
|
||
|
||
# ---- T4: wifi scan(写级;宿主 CLI=CALL_OTHER → 拒)----
|
||
OUT=$(env $ENVS $Q "$T/meow_net" wifi scan 2>&1)
|
||
ck T4a "permission denied" "$OUT"
|
||
|
||
# ---- T5: bt status / bt paired(读级)----
|
||
OUT=$(env $ENVS $Q "$T/meow_net" bt status 2>&1)
|
||
ck T5a "available=1 powered=1" "$OUT"
|
||
ck T5b "paired=2" "$OUT"
|
||
ck T5b2 "connected=2" "$OUT" # P1:内核 ACL 权威已连接数
|
||
OUT=$(env $ENVS $Q "$T/meow_net" bt paired 2>&1)
|
||
ck T5c "My Headset" "$OUT"
|
||
ck T5d "Speaker" "$OUT"
|
||
|
||
# ---- T5e: bt connected(P1:内核 ACL 权威,名经 devices 缓存兜底)----
|
||
OUT=$(env $ENVS $Q "$T/meow_net" bt connected 2>&1)
|
||
ck T5e "Device 33:DE:2E:21:AD:30 Pudding" "$OUT"
|
||
ck T5e2 "Device 11:22:33:44:55:66 Speaker" "$OUT"
|
||
|
||
# ---- T5f: bt a2dp(M49:bluealsa-cli list-pcms → info <path> → codec/rate/acquired/role)----
|
||
# v4.1.1 没有 `a2dp list` 子命令,桩只认 list-pcms/info;acquired 由 `Running: true` 推出。
|
||
OUT=$(env $ENVS $Q "$T/meow_net" bt a2dp 2>&1)
|
||
ck T5f "A2DP 33:DE:2E:21:AD:30" "$OUT"
|
||
ck T5f2 "codec=AAC" "$OUT" # 真机 Selected codec(不是 Available codecs 列表)
|
||
ck T5f3 "rate=48000 Hz" "$OUT"
|
||
ck T5f4 "channels=2" "$OUT"
|
||
ck T5f5 "bits=16" "$OUT" # 从 Format: S16_LE 推出
|
||
ck T5f6 "acquired=1" "$OUT"
|
||
ck T5f7 "role=src" "$OUT"
|
||
|
||
# ---- T5g: bt devices(P1:netmgrd 权威扫描缓存;M49:实时 [NEW] 行带 ANSI ESC 也要能解析)----
|
||
# 真机 /tmp/meow_bt_scan.txt 的实时行以 ESC[0;94m[bluetooth]ESC[0m# \rESC[K[001 ESC[0;92m 002NEW 001 ESC[0m 002] 开头
|
||
{ printf '%s\n' 'Waiting to connect to bluetoothd...'
|
||
printf '\033[0;94m[bluetooth]\033[0m# \r\033[K[\001\033[0;92m\002NEW\001\033[0m\002] Device 79:85:D6:7B:73:63 79-85-D6-7B-73-63\n'
|
||
printf '\033[0;94m[bluetooth]\033[0m# scan off\n'
|
||
printf '\033[0;94m[bluetooth]\033[0m# devices\n'
|
||
printf 'Device AA:BB:CC:DD:EE:FF Earbuds\n'
|
||
printf '[DEL] Device 00:11:22:33:44:55\n'
|
||
} > "$T/bt_scan.txt"
|
||
OUT=$(env $ENVS $Q "$T/meow_net" bt devices 2>&1)
|
||
ck T5g "Device 79:85:D6:7B:73:63 79-85-D6-7B-73-63" "$OUT" # 实时 ANSI 行(旧实现全丢)
|
||
ck T5g2 "Device AA:BB:CC:DD:EE:FF Earbuds" "$OUT" # 尾部 devices 块
|
||
if echo "$OUT" | grep -q "00:11:22:33:44:55"; then
|
||
FAIL=$((FAIL+1)); echo "FAIL T5g3 [DEL] 行被误当成附近设备: $OUT"
|
||
else PASS=$((PASS+1)); echo "PASS T5g3 [DEL] 不算附近设备"; fi
|
||
|
||
# ---- T5h: bt 写级(connect/pair 等)在宿主 CLI=CALL_OTHER → 拒(仅系统身份+MeowOS 放行,见 T6)----
|
||
OUT=$(env $ENVS $Q "$T/meow_net" bt connect 33:DE:2E:21:AD:30 2>&1); RC=$?
|
||
ck T5h "permission denied" "$OUT"
|
||
ck T5h2 "rc=1" "rc=$RC"
|
||
OUT=$(env $ENVS $Q "$T/meow_net" bt scan on 2>&1)
|
||
ck T5i "permission denied" "$OUT"
|
||
|
||
# ---- T6: HibyOS 只读降级(读可查,写拒)----
|
||
echo hiby > "$T/boot_mode"
|
||
sleep 2 # 等守护下一拍热切换
|
||
OUT=$(env $ENVS $Q "$T/meow_net" status 2>&1)
|
||
ck T6a "mode=readonly" "$OUT"
|
||
ck T6b "iface:wlan0" "$OUT" # 读侧仍可查
|
||
OUT=$(env $ENVS $Q "$T/meow_net" wifi networks 2>&1)
|
||
ck T6c "MyWiFi" "$OUT" # 读级子命令仍可用
|
||
OUT=$(env $ENVS $Q "$T/meow_net" wifi on 2>&1); RC=$?
|
||
ck T6d "permission denied" "$OUT"
|
||
echo meow > "$T/boot_mode"
|
||
sleep 2
|
||
OUT=$(env $ENVS $Q "$T/meow_net" status 2>&1)
|
||
ck T6e "mode=meow" "$OUT" # 热切换回全功能
|
||
|
||
# ---- T7: 守护日志(每次开机新文件;接口 add/写拒绝 必记)----
|
||
sleep 1
|
||
LOGS=$(ls "$T/logs"/net_*.log 2>/dev/null | head -1)
|
||
[ -n "$LOGS" ] && { PASS=$((PASS+1)); echo "PASS T7a log file: $(basename "$LOGS")"; } || { FAIL=$((FAIL+1)); echo "FAIL T7a 无日志文件"; }
|
||
[ -n "$LOGS" ] && { grep -q "iface add kind=wifi name=wlan0" "$LOGS" && { PASS=$((PASS+1)); echo "PASS T7b iface add 已记"; } || { FAIL=$((FAIL+1)); echo "FAIL T7b 未记 iface add"; }; }
|
||
|
||
# ---- T8: 守护停 → CLI help 本地可用;status 报守护不可达 ----
|
||
kill $DPID 2>/dev/null; wait $DPID 2>/dev/null; DPID=""
|
||
sleep 0.5
|
||
OUT=$(env $ENVS $Q "$T/meow_net" help 2>&1)
|
||
ck T8a "usage: meow_net" "$OUT"
|
||
OUT=$(env $ENVS $Q "$T/meow_net" status 2>&1)
|
||
ck T8b "守护不可达" "$OUT"
|
||
|
||
# ---- T9: 无参 → help;未知子命令 → usage ----
|
||
OUT=$(env $ENVS $Q "$T/meow_net" 2>&1)
|
||
ck T9a "usage: meow_net" "$OUT"
|
||
OUT=$(env $ENVS $Q "$T/meow_net" bogus 2>&1); RC=$?
|
||
ck T9b "usage: meow_net" "$OUT"
|
||
|
||
# ---- 重启 daemon A(T8 已停)供期2 观测/连接表/日志测试 ----
|
||
env $ENVS $Q "$BIN" --sock "$T/net.sock" >"$T/daemon2.log" 2>&1 &
|
||
DPID=$!
|
||
for i in $(seq 1 30); do [ -S "$T/net.sock" ] && break; sleep 0.2; done
|
||
[ -S "$T/net.sock" ] || { echo "FAIL T10-pre: daemon 未重启"; cat "$T/daemon2.log"; exit 1; }
|
||
|
||
# ---- T10: 期2 conns(读级;观测层经 MEOW_TCP/UDP 桩 + amsd/svcd 缺省 → proc:unknown 归因)----
|
||
sleep 1 # 等守护普查一拍
|
||
OUT=$(env $ENVS $Q "$T/meow_net" conns 2>&1)
|
||
ck T10a "proc:unknown" "$OUT"
|
||
ck T10b "10.78.193.50:20791" "$OUT"
|
||
ck T10b2 "140.205.5.9:443" "$OUT"
|
||
ck T10c "ESTAB" "$OUT"
|
||
ck T10d "UDP" "$OUT"
|
||
ck T10e "connection" "$OUT"
|
||
# ---- T11: apps(实体聚合)----
|
||
OUT=$(env $ENVS $Q "$T/meow_net" apps 2>&1)
|
||
ck T11a "entity" "$OUT"
|
||
ck T11b "proc:unknown" "$OUT"
|
||
# ---- T12: dns status/stats(DNS 禁用 → 显示 disabled)----
|
||
OUT=$(env $ENVS $Q "$T/meow_net" dns status 2>&1)
|
||
ck T12a "proxy disabled" "$OUT"
|
||
OUT=$(env $ENVS $Q "$T/meow_net" dns stats 2>&1)
|
||
ck T12b "proxy disabled" "$OUT"
|
||
# ---- T13: dns flush(写级;宿主 CLI=CALL_OTHER → 拒)----
|
||
OUT=$(env $ENVS $Q "$T/meow_net" dns flush 2>&1)
|
||
ck T13a "permission denied" "$OUT"
|
||
|
||
# ---- T13b: 端到端 JSON 合法性(STATUS_0xE0 / IFACE_0xE2 可被完整解析,括号配对/无尾逗号)----
|
||
JOUT=$(python3 - "$T/net.sock" <<'PYEOF'
|
||
import socket,struct,sys,json
|
||
s=socket.socket(socket.AF_UNIX,socket.SOCK_STREAM); s.connect(sys.argv[1]); s.settimeout(2)
|
||
def recvn(n):
|
||
d=b""
|
||
while len(d)<n:
|
||
c=s.recv(n-len(d))
|
||
if not c: return d
|
||
d+=c
|
||
return d
|
||
def req(typ,payload=b""):
|
||
s.sendall(struct.pack('<IHHII',0x4D4F5701,1,typ,1,len(payload))+payload)
|
||
H=struct.unpack('<IHHII', recvn(16)); body=recvn(H[4])
|
||
code,jlen=struct.unpack('<IH',body[:6]); return body[8:8+jlen].decode('utf-8','replace')
|
||
ok=1
|
||
for nm,typ,pl in [("STATUS",0x00E0,b""),("IFACE_ALL",0x00E2,b"\x00"*32),("IFACE_WLAN",0x00E2,b"wlan0"+b"\x00"*27)]:
|
||
js=req(typ,pl)
|
||
try:
|
||
json.loads(js); sys.stdout.write("%s VALID\n" % nm)
|
||
except Exception as e:
|
||
sys.stdout.write("%s INVALID %s\n" % (nm,e))
|
||
PYEOF
|
||
)
|
||
ck T13b "STATUS VALID" "$JOUT"
|
||
ck T13c "IFACE_ALL VALID" "$JOUT"
|
||
ck T13d "IFACE_WLAN VALID" "$JOUT"
|
||
echo "$JOUT" | grep -q "INVALID" && { FAIL=$((FAIL+1)); echo "FAIL T13e 存在无效 JSON"; } || { PASS=$((PASS+1)); echo "PASS T13e no INVALID"; }
|
||
|
||
# ---- T15: apps 无原始端点(tcp 桩含 inode=0 未归因 → proc:unknown,绝不落成 IP:port entity)----
|
||
OUT=$(env $ENVS $Q "$T/meow_net" conns 2>&1)
|
||
ck T15a "10.78.195.50:443" "$OUT" # inode=0 的连接仍在表中
|
||
OUT=$(env $ENVS $Q "$T/meow_net" apps 2>&1)
|
||
ck T15b "proc:unknown" "$OUT"
|
||
echo "$OUT" | grep -qE '^[0-9][0-9.]*:' && { FAIL=$((FAIL+1)); echo "FAIL T15c apps 出现原始端点行"; } || { PASS=$((PASS+1)); echo "PASS T15c no raw endpoint row"; }
|
||
|
||
# ---- T16: policy(读级 show 任意调用者;写级仅系统身份 → 宿主 CLI=CALL_OTHER 拒)----
|
||
OUT=$(env $ENVS $Q "$T/meow_net" policy show 2>&1)
|
||
ck T16a "app:dev.player.ncm" "$OUT"
|
||
ck T16b "domain=" "$OUT"
|
||
OUT=$(env $ENVS $Q "$T/meow_net" policy set app:dev.player.ncm allow 2>&1); RC=$?
|
||
ck T16c "permission denied" "$OUT"
|
||
[ $RC -eq 1 ] && { PASS=$((PASS+1)); echo "PASS T16d rc=1"; } || { FAIL=$((FAIL+1)); echo "FAIL T16d rc=$RC"; }
|
||
OUT=$(env $ENVS $Q "$T/meow_net" policy domain app:dev.player.ncm +x 2>&1)
|
||
ck T16e "permission denied" "$OUT"
|
||
OUT=$(env $ENVS $Q "$T/meow_net" policy del app:dev.player.ncm 2>&1)
|
||
ck T16f "permission denied" "$OUT"
|
||
|
||
# ---- T17: SDK 自愿通道 end-to-end(meow_net_policy_check/report 对守护 A)----
|
||
SDK_BIN="$T/test_net_ipc"
|
||
if [ -n "$Q" ]; then
|
||
"$HOME/Project/R3ProII/.tools/zig-mipsel-cc" -Os -Wall -Wextra -I../sdk -I../sdk/include -I../meow_launcherd \
|
||
../sdk/test_net_ipc.c ../sdk/meow_net_ipc.c -o "$SDK_BIN" 2>/dev/null
|
||
else
|
||
cc -Os -Wall -Wextra -I../sdk -I../sdk/include -I../meow_launcherd \
|
||
../sdk/test_net_ipc.c ../sdk/meow_net_ipc.c -o "$SDK_BIN" 2>/dev/null
|
||
fi
|
||
if [ -x "$SDK_BIN" ]; then
|
||
OUT=$(env MEOW_LOG_APP=dev.player.ncm MEOW_NET_SOCK="$T/net.sock" $Q "$SDK_BIN" 2>&1)
|
||
ck T17a "CHECK_GOOD=0" "$OUT" # music.163.com 放行
|
||
ck T17b "CHECK_BLOCK=1" "$OUT" # ads.example.com 被策略 deny → block
|
||
ck T17c "REPORT_DONE" "$OUT"
|
||
else
|
||
FAIL=$((FAIL+1)); echo "FAIL T17 SDK test 构建失败"
|
||
fi
|
||
|
||
# ---- T18: SDK report → status JSON entity_bytes 回读 ----
|
||
JOUT2=$(python3 - "$T/net.sock" <<'PYEOF'
|
||
import socket,struct,sys,json
|
||
s=socket.socket(socket.AF_UNIX,socket.SOCK_STREAM); s.connect(sys.argv[1]); s.settimeout(2)
|
||
def recvn(n):
|
||
d=b""
|
||
while len(d)<n:
|
||
c=s.recv(n-len(d))
|
||
if not c: return d
|
||
d+=c
|
||
return d
|
||
s.sendall(struct.pack('<IHHII',0x4D4F5701,1,0x00E0,1,0))
|
||
H=struct.unpack('<IHHII', recvn(16)); body=recvn(H[4])
|
||
code,jlen=struct.unpack('<IH',body[:6]); js=body[8:8+jlen].decode('utf-8','replace')
|
||
o=json.loads(js)
|
||
eb=o.get("entity_bytes") or []
|
||
hit=[e for e in eb if e["entity"]=="app:dev.player.ncm"]
|
||
sys.stdout.write("EB_HIT=%d RX=%d TX=%d\n" % (len(hit), hit[0]["rx"] if hit else -1, hit[0]["tx"] if hit else -1))
|
||
PYEOF
|
||
)
|
||
ck T18a "EB_HIT=1" "$JOUT2"
|
||
ck T18b "RX=12345" "$JOUT2"
|
||
ck T18c "TX=67890" "$JOUT2"
|
||
# ---- RSSI 回读:status wifi rssi 应 >=0(signal_poll 桩已给)----
|
||
OUT=$(env $ENVS $Q "$T/meow_net" status 2>&1)
|
||
ck T18d "rssi=-45" "$OUT"
|
||
|
||
# ---- T14: DNS 代理 wire 实测(守护 B:高端口 + 桩上游 + 全局 deny;测 deny/forward/cache)----
|
||
# 需要「非回环的本机地址」当上游:daemon 明确把 127.0.0.1 排除在上游之外(resolv_backup_apply
|
||
# 防环路,meow_netmgrd.c:2588)。宿主拿不到这样的地址(例如 Android/Termux 的 hostname 不支持
|
||
# -I)时这套 wire 测试无法成立 —— 显式 SKIP,而不是留一堆假失败。
|
||
HOSTIP=$(hostname -I 2>/dev/null | awk '{print $1}')
|
||
if [ -z "$HOSTIP" ] || [ "$HOSTIP" = "127.0.0.1" ]; then
|
||
echo "SKIP T14 DNS wire(本机无 hostname -I 或只有回环地址;daemon 拒绝把 127.0.0.1 当上游以防环路)"
|
||
else
|
||
cat > "$T/policy_dns" <<'EOF'
|
||
global domain=-ads.example.com dns.filter_aaaa=1
|
||
EOF
|
||
printf 'nameserver %s\n' "$HOSTIP" > "$T/resolv_dns"
|
||
cat > "$T/dns_test.py" <<'PYEOF'
|
||
import socket, struct, threading, sys, os
|
||
UP_IP = os.environ.get("T_UP_IP", "0.0.0.0")
|
||
up = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||
up.bind((UP_IP, 5311)) # 0.0.0.0 通配,绑到宿主自身 IP
|
||
up.settimeout(6)
|
||
def serve():
|
||
try:
|
||
while True:
|
||
d,a = up.recvfrom(1536)
|
||
off=12
|
||
while d[off]!=0: off += 1+d[off]
|
||
off += 1+4
|
||
qsec = d[12:off]
|
||
resp = d[:2] + b'\x81\x80' + b'\x00\x01\x00\x01\x00\x00\x00\x00' + qsec
|
||
resp += b'\xc0\x0c' + b'\x00\x01\x00\x01' + struct.pack('>I',60) + b'\x00\x04' + socket.inet_aton('11.22.33.44')
|
||
up.sendto(resp, a)
|
||
except Exception:
|
||
pass
|
||
threading.Thread(target=serve, daemon=True).start()
|
||
def query(name, qtype=1):
|
||
s=socket.socket(socket.AF_INET, socket.SOCK_DGRAM); s.settimeout(3)
|
||
hdr=b'\x12\x34'+b'\x01\x00'+b'\x00\x01\x00\x00\x00\x00\x00\x00'
|
||
q=b''.join(bytes([len(l)])+l.encode() for l in name.split('.'))+b'\x00'+struct.pack('>HH',qtype,1)
|
||
s.sendto(hdr+q, ("127.0.0.1", 5300))
|
||
r,_=s.recvfrom(1536); rco=r[3]&0xf; an=struct.unpack('>H',r[6:8])[0]
|
||
return rco,an
|
||
try:
|
||
d1,a1 = query("ads.example.com") # deny → NXDOMAIN (rcode 3)
|
||
d2,a2 = query("music.163.com") # allow → forward → A 11.22.33.44
|
||
da,aa = query("music.163.com", 28) # AAAA + dns.filter_aaaa=1 → 空应答(回落 A)
|
||
sys.stdout.write("DENY_RCODE=%d\nALLOW_AN=%d\nAAAA_RCODE=%d AAAA_AN=%d\n" % (d1, a2, da, aa))
|
||
except Exception as e:
|
||
sys.stdout.write("ERR=%s\n" % e)
|
||
PYEOF
|
||
# 追加式覆写(env 左→右,后者胜出):DNS 启用、换独立 socket/resolv/policy、上游端口。
|
||
DNSENV="$ENVS MEOW_DNS_DISABLE=0 MEOW_NET_SOCK=$T/dns.sock MEOW_RESOLV=$T/resolv_dns
|
||
MEOW_NET_POLICY=$T/policy_dns MEOW_DNS_PORT=5300 MEOW_DNS_UPSTREAM_PORT=5311"
|
||
ln -sf "$(readlink -f "$BIN")" "$T/dns_netmgrd"
|
||
env $DNSENV $Q "$BIN" --sock "$T/dns.sock" >"$T/dnsd.log" 2>&1 &
|
||
D2PID=$!
|
||
for i in $(seq 1 30); do [ -S "$T/dns.sock" ] && break; sleep 0.2; done
|
||
sleep 1
|
||
OUT=$(env $DNSENV $Q "$T/meow_net" dns status 2>&1)
|
||
ck T14a "enabled=1 upstream=$HOSTIP" "$OUT"
|
||
WIRE=$(python3 "$T/dns_test.py" 2>&1)
|
||
ck T14b "DENY_RCODE=3" "$WIRE"
|
||
ck T14c "ALLOW_AN=1" "$WIRE"
|
||
ck T14c2 "AAAA_RCODE=0" "$WIRE" # 期4 AAAA 过滤:rcode 0
|
||
ck T14c3 "AAAA_AN=0" "$WIRE" # 且 0 条应答(回落 A)
|
||
OUT=$(env $DNSENV $Q "$T/meow_net" dns stats 2>&1)
|
||
ck T14d "q=3" "$OUT" # deny + allow + AAAA = 3 查询
|
||
ck T14e "deny=1" "$OUT"
|
||
kill $D2PID 2>/dev/null; wait $D2PID 2>/dev/null
|
||
# 恢复守护 A 的 MEOW_RESOLV(其未被 DNS 触碰,此处确认)
|
||
grep -q "nameserver 223.5.5.5" "$T/resolv" && { PASS=$((PASS+1)); echo "PASS T14f daemonA resolv untouched"; } || { FAIL=$((FAIL+1)); echo "FAIL T14f daemonA resolv touched"; }
|
||
fi # T14 上游能力守卫
|
||
|
||
# ---- T19: WiFi connect 状态机/持久化/隐藏网络/中文 SSID(宿主桩直驱 wifi_connect_ex)----
|
||
# daemon IPC 写级需系统身份(SO_PEERCRED /proc/exe),宿主 CLI=CALL_OTHER 必拒;故用 #include
|
||
# meow_netmgrd.c 的独立宿主二进制直驱 static wifi_connect_ex,桩 wpa_cli 由二进制自建。
|
||
if [ -z "$Q" ]; then
|
||
WCON="$T/test_wifi_connect"
|
||
if cc -Os -Wall -Wextra -Wno-format-truncation -I../meow_launcherd \
|
||
test_wifi_connect.c -o "$WCON" >/dev/null 2>&1; then
|
||
if "$WCON" >"$T/wcon.out" 2>/dev/null && grep -q "FAIL=0" "$T/wcon.out"; then
|
||
PASS=$((PASS+1)); echo "PASS T19 wifi-connect host (12 asserts, FAIL=0)"
|
||
else
|
||
FAIL=$((FAIL+1)); echo "FAIL T19 wifi-connect host"; cat "$T/wcon.out"
|
||
fi
|
||
else
|
||
FAIL=$((FAIL+1)); echo "FAIL T19 wifi-connect host build"
|
||
fi
|
||
else
|
||
echo "SKIP T19 wifi-connect(qemu 下 fork/exec 宿主 /bin/sh 不便,宿主已覆盖)"
|
||
fi
|
||
|
||
echo "=============================="
|
||
echo "PASS=$PASS FAIL=$FAIL"
|
||
[ $FAIL -eq 0 ]
|