meow_netmgrd 期1:网络管理服务骨架+接口统一层+CLI+IPC+日志框架

NETMGR_DESIGN.md 期1范围(→ M6.0)落地:
- meow_netmgrd 守护:svcd 托管 trigger=boot/restart=on-fail,进程内 HibyOS 只读降级
  (/tmp/meow_boot_mode != meow → 读可查/写拒+日志,热切换)。
- 接口统一层(§3):iface 枚举(/sys/class/net+/proc/net/dev,lo/wlan0/usb-eth 热插拔节拍检测);
  WiFi 状态/扫描/网络列表/连接/断开(wpa_cli popen 有界收割防挂死,257 教训);IP/路由/DNS 只读
  (ip 优先 /proc/net/route+resolv.conf);BT 状态/开关/已配对(hciconfig/bluetoothctl);rfkill 读写;udhcpc 续租/释放(MeowOS)。
- IPC 0x00E0-0x00E7(STATUS/IFACE/WIFI_CTL/BT_CTL+各自 RESULT):读级任意、写级仅系统身份(SO_PEERCRED
  同 svc_power),0xE8-0xEF 期2/3 预留(仅注释)。
- CLI 双形态 meow_net(status/iface/wifi/bt/rfkill/help):IPC 薄壳,help 本地必可用。
- 日志框架:TF MeowOS/logs/net_<bootts>.log(无卡降级 /tmp),接口状态变化/写操作必记。
- 单线程 poll、静态表、热路径禁 malloc;mipsel 静态(0 warning),宿主+qemu-mipsel 全过,
  真机只读验证 status/wifi scan/bt status 正确。

meow_ipc.h 仅追加 0x00E0-0x00E7 段(不改既有行)。
This commit is contained in:
MeowOS committed 2026-09-01 19:55:26 +08:00
1 parent cfeb9b775b
commit 79a0fed336
9 files changed
+2247

No files matched your search

+60
View File
@@ -504,4 +504,64 @@ typedef struct m_audio_volume {
int32_t pct; /* -1=查询;0-100=设置 */
} m_audio_volume_t; /* 4 B */
/* ================= meow_netmgrd 网络管理服务(devdocs/plan/NETMGR_DESIGN.md §3/§10)=====
* 语义:接口统一层(WiFi/BT/USB-eth/rfkill/IP/路由/DNS 只读)查询与控制的系统权威。
* 独立 socket /usr/data/meow_net.sock(--sock/MEOW_NET_SOCK 覆盖)。进程形态对齐 powermgr:
* svcd 托管 trigger=boot + 单一二进制双形态(/usr/bin/meow_netmgrd 守护 + /usr/bin/meow_net CLI)。
* 鉴权分级同 svc_power(§10/期1):读级(STATUS/IFACE、WIFI_CTL 内 status/networks、BT_CTL 内
* status/paired)任意调用者;写级(WIFI_CTL 内 scan/connect/disconnect/on/off/rfkill_set/udhcpc、
* BT_CTL 内 on/off)仅系统身份(SO_PEERCRED → /usr/bin/meow_* 或 os.meow.* 包)。
* HibyOS 模式(/tmp/meow_boot_mode=hiby):读级可查,写级拒 + 记日志。
* 期 1 实装 0x00E0-0x00E7;0x00E8-0x00EF 保留(期2:连接普查/DNS 代理;期3:管控策略/事件推送)。 */
enum { /* 与主 enum 分列,追加式不改既有行 */
MSG_NET_STATUS_REQ = 0x00E0, /* ↑ 概览查询(空负载=全部) */
MSG_NET_STATUS_RESULT = 0x00E1, /* ↓ 概览 JSON */
MSG_NET_IFACE_REQ = 0x00E2, /* ↑ 接口列表/详情(name 空=全部) */
MSG_NET_IFACE_RESULT = 0x00E3, /* ↓ 接口 JSON */
MSG_NET_WIFI_CTL = 0x00E4, /* ↑ 子命令字串(scan/connect/... + rfkill/udhcpc) */
MSG_NET_WIFI_RESULT = 0x00E5, /* ↓ 结果/文本 */
MSG_NET_BT_CTL = 0x00E6, /* ↑ 子命令字串(status/on/off/paired) */
MSG_NET_BT_RESULT = 0x00E7 /* ↓ 结果/文本 */
/* 0x00E8-0x00EF 保留:0xE8 连接表 / 0xEA 策略读写 / 0xEC DNS 控制 / 0xEE 事件推送(期 2/3) */
};
/* MSG_NET_STATUS_REQ ↑(空负载=全部;预留 subset 位,期 1 恒 0)→ MSG_NET_STATUS_RESULT ↓:
* 概览 JSON(mode/ifaces[]/wifi/bt/rfkill[],见 NETMGR_DESIGN §3 统一模型)。*/
typedef struct m_net_status_result {
uint32_t code; /* MEOW_OK / MEOW_ERR_* */
uint16_t json_len; /* json 有效字节(不含 NUL) */
uint16_t pad;
char json[MEOW_IPC_MAX - 8]; /* 概览 JSON(.data 形状) */
} m_net_status_result_t;
#define NET_IFACE_NAME_MAX 32 /* 接口名上限(lo/wlan0/usb-eth) */
/* MSG_NET_IFACE_REQ ↑:name 空=全部接口;否则单接口详情。→ MSG_NET_IFACE_RESULT ↓。 */
typedef struct m_net_iface_req {
char name[NET_IFACE_NAME_MAX];
} m_net_iface_req_t; /* 32 B */
typedef struct m_net_iface_result {
uint32_t code;
uint16_t json_len;
uint16_t pad;
char json[MEOW_IPC_MAX - 8];
} m_net_iface_result_t;
/* MSG_NET_WIFI_CTL / MSG_NET_BT_CTL ↑:子命令字串 + 变长 arg(connect 的 ssid/psk)。→ *_RESULT ↓。
* cmd(WIFI_CTL): status|scan|networks|connect|disconnect|on|off + rfkill/udhcpc 命名空间
* (rfkill arg="list"|"<type> <on|off>";udhcpc arg="renew <if>"|"release <if>")。
* cmd(BT_CTL) : status|on|off|paired。 */
typedef struct m_net_cmd {
char cmd[24]; /* 子命令字串(大写不敏感;接收侧截断) */
char arg[132]; /* connect: "<ssid>[,<psk>]";rfkill: "<type> <on|off>";udhcpc: "renew|release <if>" */
} m_net_cmd_t; /* 156 B */
typedef struct m_net_cmd_result {
uint32_t code; /* MEOW_OK / MEOW_ERR_PERM / MEOW_ERR_INVAL / MEOW_ERR_NODEV */
uint16_t text_len; /* text 有效字节(不含 NUL) */
uint16_t pad;
char text[MEOW_IPC_MAX - 8];
} m_net_cmd_result_t;
#endif /* MEOW_IPC_H */
+33
View File
@@ -0,0 +1,33 @@
#!/bin/bash
# build_netmgrd.sh — meow_netmgrd 交叉编译(mipsel 静态)+ 宿主编译可选。
#
# 目标:R3 Pro II / MeowOS(mipsel-linux-muslsf,静态)。netmgrd 仅 POSIX + Linux 基接口
# (复用 meow_ipc.h 帧格式 + m_net_* 负载,无 launcher 依赖),单文件满足「固件内、不入卡」。
# 单一二进制双形态:/usr/bin/meow_netmgrd(svcd 空参=守护)+ /usr/bin/meow_net(CLI,同一份
# 文件两次安装,argv[0] basename 分形态;fw_content.conf 两行同 src)。
#
# 用法:
# ./build_netmgrd.sh # 交叉编译 → patch_v2/meow_netmgrd/meow_netmgrd(mipsel 静态)
# ./build_netmgrd.sh host # 宿主编译 → patch_v2/meow_netmgrd/meow_netmgrd_host
set -e
cd "$(dirname "$0")"
ROOT="$HOME/Project/R3ProII"
CC="$ROOT/.tools/zig-mipsel-cc"
I_HDR="$ROOT/patch_v2/meow_launcherd" # meow_ipc.h(帧/MSG_NET_*/m_net_*)
if [ "$1" = "host" ]; then
HOST_CC="${CC_HOST:-cc}"
# -Wno-format-truncation:宿主 glibc 对固定缓冲 snprintf 的「安全截断」误报(musl 交叉不报)。
"$HOST_CC" -Os -Wall -Wextra -U_FORTIFY_SOURCE -Wno-format-truncation -I"$I_HDR" \
meow_netmgrd.c -o meow_netmgrd_host
echo "== host build =="
ls -la meow_netmgrd_host
file meow_netmgrd_host
exit 0
fi
"$CC" -Os -Wall -Wextra -static -I"$I_HDR" meow_netmgrd.c -o meow_netmgrd
echo "== meow_netmgrd (mipsel static) =="
ls -la meow_netmgrd
md5sum meow_netmgrd
file meow_netmgrd
BIN
View File
Binary file not shown.
File diff suppressed because it is too large. Load diff
+16
View File
@@ -0,0 +1,16 @@
# meow_netmgrd 板载服务配置(svcd 托管;由 S89meow_svcd 首启预置到 /usr/data/meow/services.d/)
#
# trigger=boot(NETMGR_DESIGN §1/§13,用户定案 2026-09-01):开机即常驻,接口统一层(WiFi/BT/
# USB-eth/rfkill/IP/路由/DNS)的查询与控制权威。HibyOS 只读降级在进程内门控:
# /tmp/meow_boot_mode != meow(含开机早期文件缺失)→ 读级查询放行,一切写操作拒 + 记日志;
# 文件落地为 meow 后下一采样拍自动转全功能(热切换,无需重启)。
# 与 audiod 的 trigger=manual+exit(0) 门控不同:本服务读侧天然无害,HibyOS 下挂只读查询器零干扰。
# restart=on-fail:崩溃只丢接口层缓存,svcd 退避重拉,不级联(FRAMEWORK §10)。
exec=/usr/bin/meow_netmgrd
args=
enabled=1
restart=on-fail
backoff_ms=2000
max_restart=5
trigger=boot
cap=
Binary file not shown.
Binary file not shown.
+218
View File
@@ -0,0 +1,218 @@
/* netmgrd_authtest.c — meow_netmgrd IPC 鉴权/写级决策 宿主自测(本程序非生产代码)。
*
* 通过 #include 生产源文件(meow_netmgrd.c,用 #define main 改名避免入口冲突),在同一 TU 内直接
* 访问其 FILE-STATIC 函数/全局:classify_exe / is_sys_identity / wifi_cmd_is_write / bt_cmd_is_write /
* wifi_write_authorize / bt_write_authorize / rfkill_set_block / wifi_networks / wifi_scan / bt_status,
* 验证鉴别/分级/授权决策表与接口层动作(假 sysfs/proc + 桩命令)。
* 覆盖「真机需特权路径 /proc/pid/exe 判定」之外的所有纯逻辑分支;真实 pid→exe 解析留真机验证。
*
* 用途:dev docs 复现级宿主自测;不进入固件/不部署。用法:
* cc -Wall -Wextra -I../meow_launcherd netmgrd_authtest.c -o netmgrd_authtest && ./netmgrd_authtest
*/
#define main meow_netmgrd_prod_main_unused
#include "meow_netmgrd.c"
#undef main
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
static int g_fail = 0;
#define CHECK(cond) do { if (!(cond)) { printf(" FAIL: %s (line %d)\n", #cond, __LINE__); g_fail++; } \
else { printf(" ok : %s\n", #cond); } } while (0)
static void wf(const char *p, const char *s)
{
FILE *f = fopen(p, "w");
if (f) { fputs(s, f); fclose(f); }
}
int main(void)
{
static const char *T = "/tmp/nmg_authtest";
char p[192];
caller_t c;
printf("== classify_exe ==\n");
memset(&c, 0, sizeof c);
classify_exe("/usr/data/mnt/sd_0/MeowAPPs/os.meow.tst/meow", &c);
CHECK(c.kind == CALL_PKG && strcmp(c.pkg, "os.meow.tst") == 0);
memset(&c, 0, sizeof c);
classify_exe("/usr/data/mnt/sd_0/MeowAPPs/com.example.tool/meow", &c);
CHECK(c.kind == CALL_PKG && strcmp(c.pkg, "com.example.tool") == 0);
memset(&c, 0, sizeof c);
classify_exe("/usr/data/mnt/sd_0/MeowAPPs/os.meow.tst/sub/meow", &c);
CHECK(c.kind == CALL_PKG && strcmp(c.pkg, "os.meow.tst") == 0);
memset(&c, 0, sizeof c);
classify_exe("/usr/bin/meow_net", &c);
CHECK(c.kind == CALL_SYS && strcmp(c.pkg, "meow_net") == 0); /* /usr/bin/meow_* 前缀 → 系统 */
memset(&c, 0, sizeof c);
classify_exe("/usr/bin/meow_launcherd", &c);
CHECK(c.kind == CALL_SYS && strcmp(c.pkg, "os.meow.launcher") == 0);
memset(&c, 0, sizeof c);
classify_exe("/usr/bin/wpa_cli", &c);
CHECK(c.kind == CALL_OTHER); /* 非 meow_* 系统二进制 */
memset(&c, 0, sizeof c);
classify_exe("/usr/data/mnt/sd_0/MeowAPPs/justadirty/k", &c);
CHECK(c.kind == CALL_PKG && strcmp(c.pkg, "justadirty") == 0); /* 第三方包(非 os.meow.*) */
CHECK(is_sys_identity(&c) == 0);
printf("== is_sys_identity ==\n");
c.kind = CALL_PKG; snprintf(c.pkg, sizeof c.pkg, "%s", "os.meow.tst");
CHECK(is_sys_identity(&c) == 1);
c.kind = CALL_PKG; snprintf(c.pkg, sizeof c.pkg, "%s", "com.example.tool");
CHECK(is_sys_identity(&c) == 0);
c.kind = CALL_SYS; snprintf(c.pkg, sizeof c.pkg, "%s", "os.meow.launcher");
CHECK(is_sys_identity(&c) == 1);
c.kind = CALL_OTHER; c.pkg[0] = 0;
CHECK(is_sys_identity(&c) == 0);
printf("== wifi_cmd_is_write / bt_cmd_is_write ==\n");
CHECK(wifi_cmd_is_write("status", NULL) == 0);
CHECK(wifi_cmd_is_write("networks", NULL) == 0);
CHECK(wifi_cmd_is_write("scan", NULL) == 1);
CHECK(wifi_cmd_is_write("connect", "ssid") == 1);
CHECK(wifi_cmd_is_write("disconnect", NULL) == 1);
CHECK(wifi_cmd_is_write("on", NULL) == 1);
CHECK(wifi_cmd_is_write("off", NULL) == 1);
CHECK(wifi_cmd_is_write("rfkill", "list") == 0);
CHECK(wifi_cmd_is_write("rfkill", "wlan on") == 1);
CHECK(wifi_cmd_is_write("udhcpc", "renew wlan0") == 1);
CHECK(bt_cmd_is_write("status") == 0);
CHECK(bt_cmd_is_write("paired") == 0);
CHECK(bt_cmd_is_write("on") == 1);
CHECK(bt_cmd_is_write("off") == 1);
printf("== authorize 决策(系统身份 × MeowOS 模式)==\n");
caller_t sys; memset(&sys, 0, sizeof sys); sys.kind = CALL_SYS; snprintf(sys.pkg, sizeof sys.pkg, "%s", "meow_net");
caller_t pkgm; memset(&pkgm, 0, sizeof pkgm); pkgm.kind = CALL_PKG; snprintf(pkgm.pkg, sizeof pkgm.pkg, "%s", "os.meow.panel");
caller_t third; memset(&third, 0, sizeof third); third.kind = CALL_PKG; snprintf(third.pkg, sizeof third.pkg, "%s", "com.example.tool");
caller_t other; memset(&other, 0, sizeof other); other.kind = CALL_OTHER; other.uid = 1000;
/* 读级:任意放行,与身份/模式无关 */
CHECK(wifi_write_authorize(&other, 0, "networks", NULL) == MEOW_OK);
CHECK(bt_write_authorize(&other, 0, "status") == MEOW_OK);
CHECK(wifi_write_authorize(&third, 1, "rfkill", "list") == MEOW_OK);
/* 写级:only 系统身份 + meow 模式 */
CHECK(wifi_write_authorize(&sys, 1, "on", NULL) == MEOW_OK);
CHECK(wifi_write_authorize(&pkgm, 1, "connect", "ssid") == MEOW_OK);
CHECK(bt_write_authorize(&sys, 1, "off") == MEOW_OK);
CHECK(wifi_write_authorize(&third, 1, "scan", NULL) == MEOW_ERR_PERM); /* 第三方包 → 拒 */
CHECK(wifi_write_authorize(&other, 1, "on", NULL) == MEOW_ERR_PERM); /* CALL_OTHER + uid → 拒 */
CHECK(wifi_write_authorize(&sys, 0, "on", NULL) == MEOW_ERR_PERM); /* HibyOS 只读 → 拒 */
CHECK(bt_write_authorize(&sys, 0, "on") == MEOW_ERR_PERM); /* HibyOS 只读 → 拒 */
CHECK(bt_write_authorize(&pkgm, 1, "off") == MEOW_OK);
printf("== 接口层动作(假 sysfs + 桩命令)==\n");
/* 搭假 /sys/class/net + rfkill + bt(先建祖先目录) */
mkdir(T, 0755);
snprintf(p, sizeof p, "%s/net", T); mkdir(p, 0755);
snprintf(p, sizeof p, "%s/net/lo", T); mkdir(p, 0755);
snprintf(p, sizeof p, "%s/net/wlan0", T); mkdir(p, 0755);
snprintf(p, sizeof p, "%s/rfkill", T); mkdir(p, 0755);
snprintf(p, sizeof p, "%s/rfkill/rfkill0", T); mkdir(p, 0755);
snprintf(p, sizeof p, "%s/bt", T); mkdir(p, 0755);
snprintf(p, sizeof p, "%s/bt/hci0", T); mkdir(p, 0755);
snprintf(p, sizeof p, "%s/net/lo/operstate", T); wf(p, "up\n");
snprintf(p, sizeof p, "%s/net/wlan0/operstate", T); wf(p, "up\n");
snprintf(p, sizeof p, "%s/rfkill/rfkill0/type", T); wf(p, "wlan\n");
snprintf(p, sizeof p, "%s/rfkill/rfkill0/soft", T); wf(p, "0\n");
snprintf(p, sizeof p, "%s/rfkill/rfkill0/hard", T); wf(p, "0\n");
snprintf(p, sizeof p, "%s/bt/hci0/address", T); wf(p, "00:1A:7D:DA:71:13\n");
snprintf(p, sizeof p, "%s/bt/hci0/power", T); wf(p, "on\n");
snprintf(p, sizeof p, "%s/netdev", T); wf(p, "Inter-| Receive | Transmit\n lo: 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0\n wlan0: 5000000 5000 0 0 0 0 0 0 1000000 1000 0 0 0 0 0 0\n");
snprintf(p, sizeof p, "%s/route", T); wf(p, "Iface\tDestination\tGateway \tFlags\nwlan0\t005EC20A\t6F3DC20A\t0003\nlo\t0100007F\t00000000\t0001\n");
snprintf(p, sizeof p, "%s/resolv", T); wf(p, "nameserver 223.5.5.5\nnameserver 10.78.193.1\n");
snprintf(p, sizeof p, "%s/boot_mode", T); wf(p, "meow\n");
/* 桩工具 */
snprintf(p, sizeof p, "%s/wpa_cli", T); wf(p,
"#!/bin/sh\n"
"case \"$1\" in\n"
" status) echo 'wpa_state=COMPLETED'; echo 'ssid=MyWiFi'; echo 'ip_address=10.78.193.50'; echo 'signal=-45';;\n"
" scan) echo OK;;\n"
" scan_results) echo 'bssid / frequency / signal / flags / ssid'; echo 'xx:xx / 2412 / -50 / WPA2 / TestAP';;\n"
" list_networks) echo 'network id / ssid / bssid / flags'; echo '0\tMyWiFi\tany\t[CURRENT]';;\n"
" add_network) echo 5;;\n"
" set_network) echo OK;;\n"
" enable_network) echo OK;;\n"
" select_network) echo OK;;\n"
" disconnect) echo OK;;\n"
"esac\n"
"exit 0\n");
snprintf(p, sizeof p, "%s/hciconfig", T); wf(p,
"#!/bin/sh\n"
"echo 'hci0: Type: Primary Bus: USB'\n"
"echo ' BD Address: 00:1A:7D:DA:71:13 ACL MTU: 1021:8'\n"
"echo ' UP RUNNING PSCAN'\n");
snprintf(p, sizeof p, "%s/bluetoothctl", T); wf(p,
"#!/bin/sh\n"
"echo 'Device 00:1A:7D:DA:71:13 My Headset'\n"
"echo 'Device 11:22:33:44:55:66 Speaker'\n");
snprintf(p, sizeof p, "%s/ip", T); wf(p,
"#!/bin/sh\n"
"if [ \"$6\" = \"wlan0\" ]; then echo \"2: wlan0 inet 10.78.193.50/24 brd 255.255.255.255 scope global wlan0\"; fi\n"
"exit 0\n");
snprintf(p, sizeof p, "%s/wpa_cli", T); chmod(p, 0755);
snprintf(p, sizeof p, "%s/hciconfig", T); chmod(p, 0755);
snprintf(p, sizeof p, "%s/bluetoothctl", T); chmod(p, 0755);
snprintf(p, sizeof p, "%s/ip", T); chmod(p, 0755);
g_net_dir[0] = 0; snprintf(g_net_dir, sizeof g_net_dir, "%s/net", T);
g_netdev[0] = 0; snprintf(g_netdev, sizeof g_netdev, "%s/netdev", T);
g_net_route[0] = 0; snprintf(g_net_route, sizeof g_net_route, "%s/route", T);
g_resolv[0] = 0; snprintf(g_resolv, sizeof g_resolv, "%s/resolv", T);
g_rfkill_dir[0] = 0; snprintf(g_rfkill_dir, sizeof g_rfkill_dir, "%s/rfkill", T);
g_bt_dir[0] = 0; snprintf(g_bt_dir, sizeof g_bt_dir, "%s/bt", T);
g_boot_mode[0] = 0; snprintf(g_boot_mode, sizeof g_boot_mode, "%s/boot_mode", T);
g_wpa_cli = ""; { char x[160]; snprintf(x, sizeof x, "%s/wpa_cli", T); static char wc[160]; snprintf(wc, sizeof wc, "%s", x); g_wpa_cli = wc; }
g_hciconfig = ""; { char x[160]; snprintf(x, sizeof x, "%s/hciconfig", T); static char hc[160]; snprintf(hc, sizeof hc, "%s", x); g_hciconfig = hc; }
g_bluetoothctl = ""; { char x[160]; snprintf(x, sizeof x, "%s/bluetoothctl", T); static char bc[160]; snprintf(bc, sizeof bc, "%s", x); g_bluetoothctl = bc; }
g_ip = ""; { char x[160]; snprintf(x, sizeof x, "%s/ip", T); static char ip[160]; snprintf(ip, sizeof ip, "%s", x); g_ip = ip; }
/* rfkill_set_block:wlan → soft=1 */
CHECK(rfkill_set_block("wlan", 1) == 0);
snprintf(p, sizeof p, "%s/rfkill/rfkill0/soft", T);
char rb[8]; read_file_txt(p, rb, sizeof rb);
CHECK(strcmp(rb, "1") == 0);
/* enum_ifaces(detail=1):ip/gw/rx/tx */
enum_ifaces(1);
CHECK(g_iface_n >= 2);
int wlan = -1;
for (int i = 0; i < g_iface_n; i++) if (!strcmp(g_ifaces[i].name, "wlan0")) wlan = i;
CHECK(wlan >= 0);
CHECK(strcmp(g_ifaces[wlan].kind, "wifi") == 0);
CHECK(g_ifaces[wlan].up == 1);
CHECK(strcmp(g_ifaces[wlan].gw, "10.194.61.111") == 0); /* 0x6F3DC20A LE → 10.194.61.111 */
CHECK(g_ifaces[wlan].rx == 5000000);
/* wifi_networks(读) + wifi_scan(写执行路径) */
ctl_result_t r;
wifi_networks(&r);
CHECK(r.code == MEOW_OK && strstr(r.text, "MyWiFi") != NULL);
wifi_scan(&r);
CHECK(r.code == MEOW_OK && strstr(r.text, "TestAP") != NULL);
/* bt_status:available=1 powered=1 address=... paired=2 */
bt_t b;
bt_status(&b);
CHECK(b.available == 1 && b.powered == 1);
CHECK(strcmp(b.address, "00:1A:7D:DA:71:13") == 0);
CHECK(b.paired_n == 2);
/* 写级执行(wifi_onoff off → rfkill block wlan)已在 rfkill_set_block 覆盖;此处联调 wifi_ctl_execute */
wifi_ctl_execute("networks", NULL, &r);
CHECK(r.code == MEOW_OK && strstr(r.text, "MyWiFi") != NULL);
/* connect:现有 ssid 复用(enable+select,network_id=0);新 ssid 走 add/set/enable/select(id=5)。 */
wifi_ctl_execute("connect", "MyWiFi", &r);
CHECK(r.code == MEOW_OK && strstr(r.text, "ssid=MyWiFi") != NULL && strstr(r.text, "network_id=0") != NULL);
wifi_ctl_execute("connect", "NewAP,secret", &r);
CHECK(r.code == MEOW_OK && strstr(r.text, "ssid=NewAP") != NULL && strstr(r.text, "network_id=5") != NULL);
wifi_ctl_execute("disconnect", NULL, &r);
CHECK(r.code == MEOW_OK);
if (g_fail) { printf("\nNETMGR AUTH TEST: %d FAIL\n", g_fail); return 1; }
printf("\nNETMGR AUTH TEST: ALL PASS\n");
return 0;
}
+164
View File
@@ -0,0 +1,164 @@
#!/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 /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"
# 桩后端命令(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
echo "Device 00:1A:7D:DA:71:13 My Headset"
echo "Device 11:22:33:44:55:66 Speaker"
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 ip; do chmod +x "$T/$f"; done
mkdir -p "$T/logs" "$T/pid"
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_NET_SOCK=$T/net.sock MEOW_NET_POLL_SEC=1 MEOW_LOG_DIR=$T/logs"
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"
OUT=$(env $ENVS $Q "$T/meow_net" bt paired 2>&1)
ck T5c "My Headset" "$OUT"
ck T5d "Speaker" "$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"
echo "=============================="
echo "PASS=$PASS FAIL=$FAIL"
[ $FAIL -eq 0 ]