Files
MeowOS-For-R3P2/patch_v2/meow_netmgrd/test_netmgrd.sh
T
worker-m49 239cebae01 fix(bt): M49 r1 评审 — 写操作预算压回 1500ms 窗口内 + A2DP 总预算/TTL 缓存 + 测试可重复 + rfkill 顺序;rebase 到 main 保住 M43 time32
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 未动。
2026-11-11 19:15:00 +08:00

477 lines
22 KiB
Bash
Executable File
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
# 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 ]