MeowPanel 后端(meowpanel.c)
MeowOS(HiBy R3 Pro II 第三方系统,MIPS32 静态交叉编译)的外置 Web 面板后端:单进程、零第三方依赖的
C 静态二进制,内嵌 HTTP/JSON 服务器 + 前端静态托管 + 框架专项服务 IPC 客户端 + settings.conf 设置镜像。
- 定位(
MEOWPANEL_DESIGN.md):外置于 TF 卡MeowOS/opt/MeowPanel/的系统组件,由meow_svcd读 TF 卡MeowOS/services.d/meowpanel.conf启动、meow_sysui监管。不进固件;无卡时系统核心 (launcher/sdk/设置/通知)绝不依赖它。 - 协议:
devdocs/plan/MEOWPANEL_PROTOCOL.md(HTTP/JSON 端点;本实现以「实现为准,协议/README 记录出入」)。 - 数据路径(
SYSTEM_FRAMEWORK_SERVICES.md §2/§4):读侧(/api/{status,perf,system})经meow_svc_perfIPC(MSG_PERF_QUERY→RESULT)聚合,绝不各自爬 /proc;动作侧(/api/system/{reboot,shutdown,ota})经meow_svc_powerIPC(MSG_POWER_ACTION→STATUS),不再直连poweroff/写 mtd5。两专项服务为板载 svcd 托管 (trigger=boot),拔卡保留,与无卡依赖最小化自洽。
1. 编译
工具链:交叉编译器 /home/Aska/Project/R3ProII/.tools/zig-mipsel-cc(mipsel 32 位、musl 静态,可用)。
cd /home/Aska/Project/R3ProII/patch_v2/meowpanel
./build_meowpanel.sh # 交叉编译 → meowpanel(mipsel 静态,firmware-ready)
./build_meowpanel.sh host # 宿主编译 → meowpanel_host(本机 ld,仅供调试)
- 两分支均以
-Os -Wall -Wextra复核,0 warning。 - 产出(本次构建实测):
meowpanel:mipsel 静态,ELF 32-bit LSB executable, MIPS, MIPS32 rel2, statically linked, stripped, 体积约 166 KB(固件阈值内)。md5:ea1d6a58cbc0d312498d3925e2fd2127。meowpanel_host:aarch64 动态链接(宿主编译,仅供调试),体积约 77 KB。
- 依赖:仅 libc + Linux 内核头(
linux/input.h),零第三方库(设备无 python3/lua)。 - 头文件引用:
-I patch_v2/meow_launcherd(meow_ipc.h帧格式 /m_perf_*/m_power_*/POWER_*, header 为权威源,结构体字段以它为准)。
2. 部署到 TF 卡
# 1) 后端 + 前端(web/index.html + meowpanel.css + meowpanel.js)放在同一目录
sudo mkdir -p /usr/data/mnt/sd_0/MeowOS/opt/MeowPanel
sudo cp patch_v2/meowpanel/meowpanel /usr/data/mnt/sd_0/MeowOS/opt/MeowPanel/
sudo cp patch_v2/meowpanel/web/index.html /usr/data/mnt/sd_0/MeowOS/opt/MeowPanel/
sudo cp patch_v2/meowpanel/web/meowpanel.css /usr/data/mnt/sd_0/MeowOS/opt/MeowPanel/
sudo cp patch_v2/meowpanel/web/meowpanel.js /usr/data/mnt/sd_0/MeowOS/opt/MeowPanel/
# 2) 服务配置(svcd 读 TF 卡 services.d 拉起)
sudo mkdir -p /usr/data/mnt/sd_0/MeowOS/services.d
sudo cp patch_v2/meowpanel/services.d/meowpanel.conf /usr/data/mnt/sd_0/MeowOS/services.d/
说明:
- 前端资源与后端同目录(后端
GET /→index.html,GET /meowpanel.css/meowpanel.js同目录取)。 后端也支持GET /assets/<file>(映射到同目录文件),前端当前用根相对路径。 - 服务启动后整卡由
meow_svcd按trigger=boot拉起;meow_sysui监管(restart/backoff/max_restart)。 - 无卡:
MeowOS/services.d/不可读 → svcd 跳过本服务;系统核心照常。
3. 访问
浏览器(同局域网):
http://<host>.local:8080/
- mDNS 由板载
meow_mdns应答<host>.local。 - 端口默认 8080,可用
-p PORT/ 环境变量MEOWPORT/ 与后端同目录meowpanel.conf(port=N)覆盖。 (优先级:-p>MEOWPORT>meowpanel.conf> 默认 8080。) - 无鉴权(设备内网信任模型;token 鉴权为 MEOWPANEL_PROTOCOL §1.3 草案项,未实现)。
4. 端点实现清单(对照 MEOWPANEL_PROTOCOL §2 端点总览)
已实现(返回 200/2xx 或语义错误码)
| 方法 | 路径 | 说明 | 实现 |
|---|---|---|---|
| GET | /、/meowpanel.css、/meowpanel.js、/assets/<f> |
静态托管 | ✅ |
| GET | /api/status |
性能+系统(meow_svc_perf IPC subset=all 透传) |
✅ |
| GET | /api/perf |
仅性能(subset=perf 透传) | ✅ |
| GET | /api/system |
perf IPC + boot/network/ota 局部补全(见注) | ✅ |
| POST | /api/system/reboot、/shutdown、/ota |
meow_svc_power IPC |
✅ |
| GET | /api/settings |
全量+元数据(items/count/groups) | ✅ |
| GET | /api/settings/:key |
单项 | ✅ |
| PUT | /api/settings/:key |
写单项(clamp/枚举校验) | ✅(越界/非法枚举→422) |
| POST | /api/settings |
批量写 | ✅ |
| POST | /api/settings/restore |
恢复默认 | ✅ |
| GET | /api/theme |
读主题 | ✅ |
| PUT | /api/theme |
写主题(style/theme/background 子集) | ✅(非法→422) |
| GET | /api/files?path= |
目录列表(白名单) | ✅(无卡→503) |
| GET | /api/files/read?path= |
读文本(<1MB;二进制→415) | ✅ |
| GET | /api/files/download?path= |
下载流 | ✅ |
| POST | /api/files/upload |
multipart 上传 | ✅ |
| POST | /api/files/mkdir |
新建目录(已存在→409) | ✅ |
| POST | /api/files/rename |
重命名 | ✅ |
| DELETE | /api/files?path= |
删除(可 recursive=1;非空未 recursive→409) | ✅ |
| GET | /api/apps |
扫描 MeowAPPs/ 读 app.json |
✅ |
| GET | /api/apps/:pkg |
应用详情 | ✅ |
| POST | /api/apps/install |
经 nyai install(multipart 或 path) |
✅ |
| GET | /api/apps/:pkg/export |
nyai export 下载流 |
✅ |
| DELETE | /api/apps/:pkg |
nyai remove [--purge] |
✅ |
| POST | /api/key/:code |
按键注入(tap/long) | ✅(物理键红线内) |
| POST | /api/key |
批量/顺序注入 | ✅ |
| GET | /api/logs |
MeowOS/logs 文件列表 |
✅(无卡→503) |
| GET | /api/logs/read?file=&lines= |
读日志(上限 1000 行;../绝对→403;敏感名→403) |
✅ |
| GET | /api/notifications |
501 | ⚠️ |
| DELETE | /api/notifications/:id |
501 | ⚠️ |
| POST | /api/notifications/dismiss_all |
501 | ⚠️ |
返回 501 NOT_IMPLEMENTED 的端点(诚实标注)
| 端点 | 原因 |
|---|---|
/api/notifications、/api/notifications/:id、/api/notifications/dismiss_all |
通知存储为 launcher 进程内存(meow_notif.c g_slots[],NOTIFICATION_SYSTEM §4),后端为独立 svcd 子进程,跨进程不可达那批内存槽;框架只在关机前 dump 到 TF MeowOS/notifications/notifications.log(CSV 追加,非活跃快照)。需为通知后端另开 IPC 查询/清撤通道(如 MSG_NOTIFY_LIST 应答)或持久化活跃集,见 MEOWPANEL_PROTOCOL §6 待定案。 |
/api/key/:code 的 press:"hold" |
hold 需常驻持续注入通道(阶段 D),后端未实现;tap/long 已实现。 |
OTA 升级模式(/api/system/ota 带 url) |
升级为草案(MEOWPANEL_PROTOCOL §6 待定案);当前 POST /api/system/ota 空 body 触发 meow_svc_power 的 POWER_UPGRADE(写 mtd5 进升级模式),带 url 的下载+升级未实现。 |
前端对
501显示「该功能后端尚未实现」(ok:false,error.code=NOT_IMPLEMENTED)。
5. 关键实现说明
5.1 HTTP 路由 / JSON 序列化
- 单线程 + poll 串行(面板访问量极小)。
accept→read_request(先读头到\r\n\r\n,在缓冲区内搜索头边界, 处理「头+body 同段到达」,再按Content-Length精确读 body)→handle→ 写响应 → 关闭连接(Connection: close)。 - 读/写均有
poll超时(读头部 5s、body 分段 5s,IPC 2s)。SIGPIPE忽略。 - JSON 响应信封:成功
{"ok":true,"data":<payload>};失败{"ok":false,"error":{"code","message"}}(MEOWPANEL_PROTOCOL §1.1/§1.2,HTTP 状态码映射照抄)。 - 无 JSON 库:手写最小递归下降解析器(对象/数组/字符串转义/数字/bool/null),仅覆盖实际用到的收发。
- 下载/上传用流式(
multipart/form-data极简解析:按 boundary 切,取path+file字段)。
5.2 IPC 对接(meow_svc_perf / meow_svc_power)
- 帧:复用
meow_ipc.h——16B 头(magic0x4D4F5701/version 1/type/req_id/len,全小端)+ 变长负载,MEOW_IPC_MAX=4096。帧读写x_read_full/x_write_full+send_frame/recv_frame(与 meow_svc_perf.c 对称)。 - 握手:连接后先
MSG_HELLO(app_id="os.meow.panel",系统面板身份,§5 二期拟名)→ 读MSG_ACK(code=0)。 - 读侧:
MSG_PERF_QUERY(m_perf_query_t.subset)→MSG_PERF_RESULT(m_perf_result_t,code/json_len/json), 直接透传服务端聚合 JSON(/api/status=全部、/api/perf=perf 子集、/api/system=全量取 uptime/storage/tfcard/system)。 读取防溢出:json_len以sizeof(m_perf_result_t.json)-1与cap双钳位。 - 动作侧:
MSG_POWER_ACTION(m_power_action_t.cmd=POWER_REBOOT/SHUTDOWN/UPGRADE)→MSG_POWER_STATUS(code/msg)。 收到MEOW_OK→{"accepted":true};否则按code(PERM→403 / NOENT/IO→503 / 其余→500)返回错误信封。 - 失败语义:socket 不存在/连接拒/超时/应答非 OK → 返回 500
INTERNAL(或 503NO_CARD当缺升级文件) 并带中文 message。绝不静默失败、绝不降级直连 /proc 或 poweroff(单一权威源)。 - 连接超时:非阻塞 connect +
poll(POLLOUT)2s,SO_RCVTIMEO/SO_SNDTIMEO兜底。
5.3 设置镜像(读写板载 settings.conf)
- 不旁路:只经
settings_get_value(=切分 + key 去首尾空白精确匹配,防audio.volume误配audio.volume_max, 照meow_ui_vol_read/meow_svc_power settings_get_int_trim同款解析)与settings_write_one("key = value\n", 保留# meow-settings 1头、逐行重写、缺键追加;目录缺失自动mkdir)。不绕过此函数直接改文件。 - 键表/默认值来源:
patch_v2/meow_launcherd/meow_settings.c的g_defs[](权威来源,逐键一致), 含 type/def/min/max/enum_names/enum_labels;GET /api/settings返回items/count/groups(分组按键前缀, 如display.*→显示、font.*|ui.*→字体主题…)。 - 类型:
int→number(带 min/max/def 字符串)、bool→原生布尔、enum→options(name/label)+default_name+def、str→string。越界/非法枚举 → 422UNSUPPORTED。 - 语义 clamp:
display.brightness读/写都 clamp 到[display.brightness_min(默认1), 100](防 101 闪屏, 与meow_settings_brightness_clamped/clamp_brightness同口径);boot.default写后同步/data/meow_boot_default(meow→0/hiby→1,同meow_settings_save)。 - 注:后端为独立进程,无法调用 launcher 进程内的
meow_settings_*(那是其内存表),故直接镜像同一份settings.conf,保持与实体设置页/CLI 同一存储契约(协议 §3.2 说「经 meow_settings_*」,此处以 「镜像同一文件 + 同款 parse/save 契约」落地,见 §8 出入记录)。
5.4 文件白名单
- 根
/usr/data/mnt/sd_0;白名单子目录MeowOS/Music/MeowData/MeowAPPs/book(allowed数组)。 - 路径经
normalize_path规范化(去./..,拒绝逃逸出根)后path_in_whitelist校验(== 根或属于某一白名单 一级子目录)。../绝对路径逃逸 → 403FORBIDDEN;无卡 → 503NO_CARD。 - 读取拒绝敏感名(
settings.conf/setting.bak/.env/passwd/shadow/meow.conf/user.ini/config.json)。 - 列表/上传/下载/读文本(1MB+二进制→415)/mkdir(已存在→409)/rename(new_name 不含
/)/delete(递归,非空未 recursive→409)。
5.5 nyai 对接
- 包管理器 CLI 绝对路径
/usr/data/mnt/sd_0/MeowOS/opt/bin/nyai(M2.12+ 固件经meow_card_path.sh前插 PATH,可直接调; 本后端用绝对路径,兼容更早固件)。包名 =app.json的package=.mpk文件名茎。 install:multipart 上传.mpk落临时文件(MeowOS/tmp/)或 JSON{path}→nyai install <file>;用popen捕获输出+退出码, 非 0 → 422。export:nyai export <pkg> <tmp>→ 以Content-Disposition: attachment流式下发。remove:nyai remove <pkg> [--purge]。GET /api/apps/:pkg详情:直接扫描MeowAPPs/<dir>/app.json(手写 JSON 解析提取 name/version/exec/framework/immersive/fullscreen), 不依赖 nyai(nyai 未部署时列表/详情仍可用)。解析失败项invalid:true。nyai未部署 → install/export/remove 返回 500(带 nyai 输出)。
5.6 按键注入通道
- 复用现有先例:
patch_v2/uinput_tap/uinput_tap.c的write-到-evdev 注入机制。launcher 实际轮询meow_launcher_internal.h:/dev/input/event0(md-gpio-keys: POWER/PREVIOUS)、/dev/input/event2(jz adc keyboard: VOL/PLAY/NEXT)。后端 openO_WRONLY写EV_KEY press/release + SYN_REPORT即可到达 launcher 按键处理。 - 物理键红线(APPS.md / 任务约束):只注入音量(114/115)/电源(116)/播放(164)/上下曲(163/165);
禁止注入 返回(158)/主页(172)/确认(28)/退出(96) → 403
FORBIDDEN。 tap(默认,抬起间隔 80ms)、long(按住 ms,缺省 3000);hold→ 501。- 局限:若
/dev/input/event0/event2在设备上不可写(权限/路径差异),注入失败 → 500;注入为即时性注入, 不在 launcher 内维护长按状态机(与实体按键的longpressed语义近似,见 README 已知边界)。
5.7 日志
GET /api/logs:MeowOS/logs下常规文件列表(跳过敏感名)。GET /api/logs/read?file=&lines=:file必须相对MeowOS/logs/,禁../绝对路径 → 403,敏感名 → 403; 缺省lines=200,上限1000;返回{file,lines[],total,truncated}。
5.8 通知(501 及其依据)
见 §4 501 表:通知存储为 launcher 进程内存,后端跨进程不可读/清撤。已在源码 api_notifications* 注释「为何未实现 +
后续如何接」(通知后端加 MSG_NOTIFY_LIST 查询/清撤 IPC,或持久化活跃集到 TF MeowOS/notifications/ 供只读)。
6. 与协议的出入(「实现为准 + 原因」,供回改 MEOWPANEL_PROTOCOL.md)
ui.style值域:meow_settings.c g_defs源码为"md3,miui"(label "Material You,MIUI"),但 SYSTEM_SETTINGS.md 定案 + MEOWPANEL_PROTOCOL §5 一致行为miuix(对应MEOW_STYLE_MIUIX),前端data-style也用miuix。 → 本表对齐为"md3,miuix"/ "Material You,MIUIx"。建议同步改回meow_settings.c g_defs(否则实体设置页与面板值域漂移)。- 设置项映射:协议 §3.2 称后端经
meow_settings_*读写;因后端为独立进程,无法链接 launcher 内 API, 改为直接镜像同一份settings.conf(同款 parse/save 契约)。改哪边都会落同一文件,实体/网页/CLI 仍同步(listener/落盘)。 display.brightness元数据:g_defs为min=0 max=255 def=101;但语义 clamp 到[brightness_min(1),100](防 101 闪屏)。后端给前端min=1 max=100(协议 §3.2 期望),读值经 clamp 后返回(默认→100)。若回改协议可注明「原始表 0-255,面板展示语义 1-100」。/api/system的network/ota:由性能服务聚合(SYSTEM_FRAMEWORK_SERVICES §4.1)后,后端只补boot.default(读/data/meow_boot_default)、network.hostname_local(hostname+.local)与ota{current=version,available:false,url:null}; wifi/ethernet/ota.available 不编造(服务未聚合,前端对缺失字段显示占位)。若回改协议可注明「network/ota 待并入 perf 服务」。/api/files上传/删除/重命名等 path:前端对这些操作传绝对路径(d.path/子项),列表传相对 root; 后端normalize_path对两者均容错(相对则拼 root,绝对则直用),符合协议「白名单根 + 规范化」。GET /api/files返回值:files_list返回500语义时以 500 封装(协议未定义root之外错误枚举,用通用错误信封)。
7. 已知边界 / 风险
- 单线程串行:面板访问量极小,但并发请求会被排队(长上传/下载期间其它请求等待)。对面板用途可接受。
- 无鉴权:设备内网信任模型(协议 §1.3 token 鉴权待定案)。高危端点(
reboot/shutdown/ota/apps/删除/上传)无鉴权, 请勿把 8080 暴露到不可信网络。meow_svc_power服务端做SO_PEERCRED uid==0` 基础校验(root 才受理动作),后端以 root 运行可过;launcher 电源菜单/后端同链路。 - IPC 服务未起:
meow_svc_perf/meow_svc_power为板载trigger=boot服务,正常恒在;若异常退出,读侧返回 500INTERNAL, 动作侧返回 500/503,绝不静默。请勿拔除这两服务。 - 升级保护:
POWER_UPGRADE由meow_svc_power在写 mtd5 前校验卡根r3proii.upt(存在/可读/非空/size>0),失败不写 mtd5;期望 md5/hash 校验留二期。 - 文件/日志/应用均依赖 TF 卡:无卡统一 503
NO_CARD(不阻塞系统核心)。settings/system/perf/key无卡也可用(板载/rootfs)。 /api/files/upload上传路径:multipart 需要path字段(目标目录);缺失时上传文件会被忽略(前端总是带着path)。- 数据一致性:设置直接写
settings.conf;display.brightness/boot.default的 clamp/同步与 launchermeow_settings_*同口径。 若 launcher 未来改 clamp 逻辑,需同步本表。 - 存储为
meowpanel编译时刻写死的 socket/路径会因--sock-perf/--sock-power(qemu sim)可覆盖,但默认走/usr/data/meow_*.sock。
8. 文件清单(本目录)
patch_v2/meowpanel/
├── meowpanel.c # 后端源码(单文件,约 2700 行)
├── build_meowpanel.sh # 交叉编译 + host 分支(-Wall -Wextra 0 warning)
├── meowpanel # 交叉产物(mipsel 静态,165 KB,md5 a8224ef9…)
├── meowpanel_host # 宿主编译(本机 ld,调试用)
├── services.d/
│ └── meowpanel.conf # 部署到 TF 卡 MeowOS/services.d/meowpanel.conf
├── web/ # 前端(已存在;部署时拷到后端同目录)
│ ├── index.html
│ ├── meowpanel.css
│ └── meowpanel.js
└── README.md # 本文件