feat: M6.40批 — 阅读器UI改版(书架list·grid+空态骨架+界面设置+App设置页+header·footer+背景黑默认) + 系统级音量翻页映射 + P1 MD3(select面板28/对话框按钮胶囊) + MeowPanel写端点护栏 + SDK公开仓·文档同步
- reader: app.c/shelf.c/reader.c 全套UI; 新增 reader_conf(reader.conf)/settings(App设置页)/reader_ipc(音量翻页IPC响应); app.json加volume_page - launcher: keys.c meow_volpage_*门 + util.c app_id→fd映射(HELLO记fd/断开清) + app_launcher.c 前台三处接线(进入sink/按键路径volpage_key/离开clear) → 前台声明volume_page的App音量键→MSG_KEY_PAGEUP/PAGEDOWN翻页(非阅读器前台音量照旧) - sdk: meow_select.c 底部面板28 + meow_ui_dialog.c 对话框按钮胶囊(h/2) + test_meow_ui_select.c修采样 - meowpanel: /api/net/policy·/api/power/policy 加防自锁+policy默认关两条护栏(panel.policy_enabled默认0) - 文档: MEOW_APP_SDK.md §12.6 + meowpanel README端点表 0warning;宿主测试全过;二进制留批量批次未入库
This commit is contained in:
1 parent
b364b6371a
commit
2826c1e905
27 files changed
+1704
-129
No files matched your search
@@ -270,6 +270,7 @@ int ipc_pm_send(uint16_t req_type, const void *pl, uint32_t plen, uint16_t *rtyp
|
||||
int api_power_policy_get(int sock);
|
||||
int api_power_policy_set(int sock, const char *body, size_t body_len);
|
||||
int api_power_window(int sock, uint16_t req_type, uint16_t want_type, const char *unavail);
|
||||
int power_policy_append_flag(sbuf_t *out, const char *obj); /* 拼 guard③ policy_write_enabled 顶层键(自校验) */
|
||||
int api_power_history(int sock);
|
||||
int api_power_events(int sock);
|
||||
const char *ams_bin(void);
|
||||
@@ -287,6 +288,8 @@ int api_net_ifaces(int sock);
|
||||
void net_policy_parse(const char *txt, sbuf_t *s);
|
||||
int api_net_policy_get(int sock);
|
||||
int api_net_policy_set(int sock, const char *body, size_t body_len);
|
||||
int net_locked_entity(const char *entity); /* 防自锁:系统关键网络/服务组件(dropbear/wpa_supplicant/udhcpc/netmgrd/svcd/sysui/init) */
|
||||
int panel_policy_write_enabled(void); /* policy 默认关 gate:settings.conf `panel.policy_enabled`,缺省 0=关(只读透传) */
|
||||
void panel_strip_ansi(char *s);
|
||||
int txt_tokens(char *buf, char **toks, int maxtoks);
|
||||
void join_tokens(char **toks, int from, int to, char *out, size_t cap);
|
||||
|
||||
@@ -285,11 +285,30 @@ int api_net_ifaces(int sock)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* 防自锁实体:对本机关键网络/服务组件禁设「阻断」(net=block / set_domain 域名限制),
|
||||
* 否则会把能连上设备/面板/系统核心的网络能力一并锁死(SSH / WiFi / DHCP / DNS / 面板 / init)。
|
||||
* netmgrd 加载时也会剔除自锁项,面板侧抢先硬拒(guard①),保证 0 warning + 如实回错误码。
|
||||
* 匹配口径:取 `svc:|app:|proc:` 之后的裸名精确比对(`svc:netmgrd`/`proc:sysui`/裸 `udhcpc` 均命中)。 */
|
||||
int net_locked_entity(const char *entity)
|
||||
{
|
||||
if (!entity || !entity[0]) return 0;
|
||||
const char *base = entity;
|
||||
const char *colon = strrchr(entity, ':');
|
||||
if (colon) base = colon + 1;
|
||||
static const char *locked[] = {
|
||||
"dropbear", "wpa_supplicant", "udhcpc", "netmgrd", "svcd", "sysui", "init", NULL
|
||||
};
|
||||
for (int i = 0; locked[i]; i++)
|
||||
if (!strcmp(base, locked[i])) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void net_policy_parse(const char *txt, sbuf_t *s)
|
||||
{
|
||||
char lvl[24] = "", enf[24] = "dns_only";
|
||||
const char *p = txt;
|
||||
sA(s, "{\"available\":true,\"enforce\":");
|
||||
sA(s, "{\"available\":true,\"policy_write_enabled\":%s,\"enforce\":",
|
||||
panel_policy_write_enabled() ? "true" : "false");
|
||||
/* 首行找 enforce=... */
|
||||
/* enforce 后面紧跟全角「(」多字节说明,须在非 ASCII 字节处停(否则会吞「(SOCK_DESTROY...)」)。 */
|
||||
const char *en = strstr(p, "enforce=");
|
||||
@@ -399,6 +418,9 @@ int api_net_policy_get(int sock)
|
||||
int api_net_policy_set(int sock, const char *body, size_t body_len)
|
||||
{
|
||||
if (!body || !body[0]) return send_err(sock, 400, "BAD_REQUEST", "缺少 body");
|
||||
/* guard③ policy 默认关:写端点默认关闭,须在设置开启 panel.policy_enabled 才放行(只读 GET 不受此限)。 */
|
||||
if (!panel_policy_write_enabled())
|
||||
return send_err(sock, 403, "POLICY_DISABLED", "网络策略写默认关闭:先到面板设置开启 panel.policy_enabled(危险操作)");
|
||||
jv_t *root = jv_parse(body, (int)body_len);
|
||||
if (!root) return send_err(sock, 400, "BAD_REQUEST", "body 非合法 JSON");
|
||||
jv_t *jo = jv_obj_get(root, "op"), *je = jv_obj_get(root, "entity"), *ja = jv_obj_get(root, "arg");
|
||||
@@ -415,6 +437,14 @@ int api_net_policy_set(int sock, const char *body, size_t body_len)
|
||||
else if (!strcmp(op, "del")) nop = NET_POLICY_DEL;
|
||||
else return send_err(sock, 422, "UNSUPPORTED", "op 须为 set_net|set_domain|del");
|
||||
if (nop != NET_POLICY_DEL && !arg[0]) return send_err(sock, 422, "UNSUPPORTED", "缺 arg");
|
||||
/* guard① 防自锁:对系统关键服务(dropbear/wpa_supplicant/udhcpc/netmgrd/svcd/sysui/init)禁设阻断。
|
||||
* 「放行 / 删除规则」不阻断(那是恢复连通),仍放行;仅 set_net=block 与 set_domain(域名限制)命中即拒。 */
|
||||
{
|
||||
int blocking = ((nop == NET_POLICY_SET_NET) && !strcmp(arg, "block")) ||
|
||||
(nop == NET_POLICY_SET_DOMAIN);
|
||||
if (blocking && net_locked_entity(entity))
|
||||
return send_err(sock, 422, "SELF_LOCK", "防自锁:禁止阻断系统关键服务(dropbear/wpa_supplicant/udhcpc/netmgrd/svcd/sysui/init)的网络");
|
||||
}
|
||||
|
||||
m_net_policy_t q; memset(&q, 0, sizeof q);
|
||||
q.op = (uint32_t)nop;
|
||||
|
||||
@@ -71,6 +71,18 @@ done:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* 在对象 JSON(已剥尾部 `}`、经 utf8 校验)上拼 guard③ `policy_write_enabled` 顶层键:
|
||||
* 拼完 jv_parse 自校验,绝不发非法 JSON(防 overview 教训);失败返回 -1(调用方回 500,绝不带病下发)。 */
|
||||
int power_policy_append_flag(sbuf_t *out, const char *obj)
|
||||
{
|
||||
sA(out, "%s", obj);
|
||||
sA(out, ",\"policy_write_enabled\":%s}", panel_policy_write_enabled() ? "true" : "false");
|
||||
jv_t *chk = jv_parse(out->b ? out->b : "{}", (int)(out->b ? out->n : 2));
|
||||
if (!chk) return -1;
|
||||
jv_free(chk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int api_power_policy_get(int sock)
|
||||
{
|
||||
char js[MEOW_IPC_MAX];
|
||||
@@ -80,12 +92,27 @@ int api_power_policy_get(int sock)
|
||||
return send_err(sock, 503, "POWER_UNAVAILABLE", "电源管理服务不可用(powermgr 未运行/IPC 不可达)");
|
||||
if (rt != MSG_PM_RESULT || !utf8_valid_n(js, strlen(js)))
|
||||
return send_err(sock, 503, "POWER_UNAVAILABLE", "电源管理服务应答包含非法字节");
|
||||
return send_json(sock, 200, js);
|
||||
/* 追加顶层 `policy_write_enabled`(面板侧 gate 状态,独立于 powermgr 的 `enacted`):
|
||||
* 稳妥剥外层括号再拼(同 net_block_fill 手法)。上游非对象(数组/文本)时原样透传、不带该键
|
||||
* (前端缺省视为已开启,兼容旧 powermgr)。 */
|
||||
size_t l = strlen(js);
|
||||
while (l && (js[l - 1] == ' ' || js[l - 1] == '\t' || js[l - 1] == '\n' || js[l - 1] == '\r')) js[--l] = 0;
|
||||
if (l < 2 || js[0] != '{' || js[l - 1] != '}') return send_json(sock, 200, js);
|
||||
js[l - 1] = 0; /* 去尾部 } */
|
||||
sbuf_t s = {0};
|
||||
if (power_policy_append_flag(&s, js) != 0) { sbuf_free(&s); return send_err(sock, 500, "INTERNAL", "电源策略聚合输出未通过 JSON 自校验"); }
|
||||
int out = send_json(sock, 200, s.b ? s.b : "{}");
|
||||
sbuf_free(&s);
|
||||
return out;
|
||||
}
|
||||
|
||||
int api_power_policy_set(int sock, const char *body, size_t body_len)
|
||||
{
|
||||
if (!body || !body[0]) return send_err(sock, 400, "BAD_REQUEST", "缺少 body");
|
||||
/* guard③ policy 默认关:写端点默认关闭,须在设置开启 panel.policy_enabled 才放行(只读 GET 不受此限)。
|
||||
* 注意:powermgr 侧另有 `enacted`(策略是否已应用),本 gate 是面板侧写入口门禁,二者独立。 */
|
||||
if (!panel_policy_write_enabled())
|
||||
return send_err(sock, 403, "POLICY_DISABLED", "电源策略写默认关闭:先到面板设置开启 panel.policy_enabled(危险操作)");
|
||||
jv_t *root = jv_parse(body, (int)body_len);
|
||||
if (!root) return send_err(sock, 400, "BAD_REQUEST", "body 非合法 JSON");
|
||||
jv_t *ji = jv_obj_get(root, "item"), *jv = jv_obj_get(root, "value");
|
||||
|
||||
@@ -517,6 +517,8 @@ int panel_selftest(void)
|
||||
if (!rules || rules->type != J_ARR || !rules->child) { printf("SELFTEST FAIL: net_policy_parse 缺 rules 数组\n"); jv_free(pr); sbuf_free(&p); return 1; }
|
||||
jv_t *enf = jv_obj_get(pr, "enforce");
|
||||
if (!enf || enf->type != J_STR || !enf->str || strcmp(enf->str, "dns_only") != 0) { printf("SELFTEST FAIL: net_policy_parse enforce 未如实透传 dns_only\n"); jv_free(pr); sbuf_free(&p); return 1; }
|
||||
jv_t *pwe = jv_obj_get(pr, "policy_write_enabled"); /* guard③ 状态键必须为布尔,且拼读无括号错位 */
|
||||
if (!pwe || pwe->type != J_BOOL) { printf("SELFTEST FAIL: net_policy_parse 缺/损坏 policy_write_enabled\n"); jv_free(pr); sbuf_free(&p); return 1; }
|
||||
jv_free(pr);
|
||||
sbuf_free(&p);
|
||||
}
|
||||
@@ -567,7 +569,66 @@ int panel_selftest(void)
|
||||
sbuf_free(&b);
|
||||
}
|
||||
|
||||
printf("SELFTEST PASS: 截断 UTF-8「流媒体客户\\xE7」+ net/power/ams 新增端点池 → 合法 JSON + 无非法字节\n");
|
||||
/* 7) net_locked_entity:防自锁实体精确匹配(svc:/proc:/裸名);非关键实体放行(guard①)。 */
|
||||
{
|
||||
struct { const char *e; int want; } c[] = {
|
||||
{ "svc:dropbear", 1 }, { "proc:netmgrd", 1 }, { "udhcpc", 1 },
|
||||
{ "svc:sysui", 1 }, { "svc:wpa_supplicant", 1 }, { "svcd", 1 }, { "init", 1 },
|
||||
{ "app:com.example", 0 }, { "svc:avahi", 0 }, { "proc:sh", 0 },
|
||||
{ "app:com.netmgrd.helper", 0 }, /* 含子串但不等于裸名,不得误判 */
|
||||
{ "", 0 }, { NULL, 0 }
|
||||
};
|
||||
for (int i = 0; c[i].e; i++) {
|
||||
int got = net_locked_entity(c[i].e);
|
||||
if (got != c[i].want) { printf("SELFTEST FAIL: net_locked_entity(%s)=%d 期望 %d\n", c[i].e, got, c[i].want); return 1; }
|
||||
}
|
||||
}
|
||||
|
||||
/* 8) policy 默认关 gate(guard③):无设置文件 → panel_policy_write_enabled()==0(默认关);
|
||||
* settings_write_one("panel.policy_enabled","1") 后 ==1(写临时 settings 文件验证,测完清理)。 */
|
||||
{
|
||||
const char *saved = g_settings_file;
|
||||
char tmpf[] = "/tmp/meowpanel_policy_gate_XXXXXX";
|
||||
int tfd = mkstemp(tmpf);
|
||||
if (tfd < 0) { printf("SELFTEST FAIL: mkstemp 失败\n"); return 1; }
|
||||
close(tfd);
|
||||
g_settings_file = tmpf;
|
||||
int before = panel_policy_write_enabled();
|
||||
if (settings_write_one("panel.policy_enabled", "1") != 0) {
|
||||
printf("SELFTEST FAIL: 写 panel.policy_enabled 失败\n"); unlink(tmpf); g_settings_file = saved; return 1;
|
||||
}
|
||||
int after = panel_policy_write_enabled();
|
||||
unlink(tmpf);
|
||||
g_settings_file = saved;
|
||||
if (before != 0 || after != 1) { printf("SELFTEST FAIL: policy gate 默认关/开启 %d->%d\n", before, after); return 1; }
|
||||
}
|
||||
|
||||
/* 9) power_policy_append_flag:对象 JSON 剥尾拼入 policy_write_enabled 后仍可解析、键为布尔、原字段保留
|
||||
* ——防括号错位重演(overview 教训)。测试在无 panel.policy_enabled 的临时 settings 下进行(期望 false=默认关)。 */
|
||||
{
|
||||
const char *saved = g_settings_file;
|
||||
char tmpf[] = "/tmp/meowpanel_policy_append_XXXXXX";
|
||||
int tfd = mkstemp(tmpf);
|
||||
if (tfd >= 0) { close(tfd); g_settings_file = tmpf; }
|
||||
sbuf_t p = {0};
|
||||
if (power_policy_append_flag(&p, "{\"profile\":\"balanced\",\"enacted\":true") != 0) {
|
||||
printf("SELFTEST FAIL: power_policy_append_flag 拼接失败\n"); sbuf_free(&p); if (tfd >= 0) { unlink(tmpf); g_settings_file = saved; } return 1;
|
||||
}
|
||||
if (!utf8_valid_n(p.b, p.n) || !strstr(p.b, "\"policy_write_enabled\":false")) {
|
||||
printf("SELFTEST FAIL: power_policy_append_flag 产物异常\n"); sbuf_free(&p); if (tfd >= 0) { unlink(tmpf); g_settings_file = saved; } return 1;
|
||||
}
|
||||
jv_t *pr = jv_parse(p.b, (int)p.n);
|
||||
if (!pr) { printf("SELFTEST FAIL: power_policy_append_flag 产物 JSON 无法解析\n"); sbuf_free(&p); if (tfd >= 0) { unlink(tmpf); g_settings_file = saved; } return 1; }
|
||||
jv_t *pwe = jv_obj_get(pr, "policy_write_enabled");
|
||||
if (!pwe || pwe->type != J_BOOL || pwe->bval != 0) { printf("SELFTEST FAIL: power_policy_append_flag policy_write_enabled 非 false(默认关)\n"); jv_free(pr); sbuf_free(&p); if (tfd >= 0) { unlink(tmpf); g_settings_file = saved; } return 1; }
|
||||
jv_t *pfl = jv_obj_get(pr, "profile");
|
||||
if (!pfl || pfl->type != J_STR || strcmp(pfl->str, "balanced") != 0) { printf("SELFTEST FAIL: power_policy_append_flag 丢失原字段\n"); jv_free(pr); sbuf_free(&p); if (tfd >= 0) { unlink(tmpf); g_settings_file = saved; } return 1; }
|
||||
jv_free(pr);
|
||||
sbuf_free(&p);
|
||||
if (tfd >= 0) { unlink(tmpf); g_settings_file = saved; }
|
||||
}
|
||||
|
||||
printf("SELFTEST PASS: 截断 UTF-8「流媒体客户\\xE7」+ net/power/ams 新增端点池 → 合法 JSON + 无非法字节;防自锁实体 + policy 门禁 默认关/开启 校验通过\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -69,6 +69,10 @@ static const setdef_t g_sets[] = {
|
||||
{ "panel.pin", ST_STR, "", 0,0, NULL, NULL, "面板" },
|
||||
{ "panel.auth_enabled", ST_BOOL, "0", 0,0, NULL, NULL, "面板" },
|
||||
{ "panel.auth_pass", ST_STR, "", 0,0, NULL, NULL, "面板" },
|
||||
/* panel.policy_enabled — 策略写「默认关」门禁(guard③):POST /api/net/policy 与 /api/power/policy
|
||||
* 默认关闭(缺省 0),置 1 才放行。落 settings.conf(「面板」组,前端设置页可开启),仅作面板侧门禁记录,
|
||||
* 不影响 powermgr 的 `enacted`(那是服务端「策略是否已应用」,二者独立)。 */
|
||||
{ "panel.policy_enabled", ST_BOOL, "0", 0,0, NULL, NULL, "面板" },
|
||||
};
|
||||
#define SETS_COUNT ((int)(sizeof g_sets / sizeof g_sets[0]))
|
||||
|
||||
@@ -143,6 +147,11 @@ int settings_get_int(const char *key, int defv)
|
||||
return r;
|
||||
}
|
||||
|
||||
int panel_policy_write_enabled(void)
|
||||
{
|
||||
return settings_get_int("panel.policy_enabled", 0) ? 1 : 0;
|
||||
}
|
||||
|
||||
int brightness_clamp(int v)
|
||||
{
|
||||
int mn = settings_get_int("display.brightness_min", 1);
|
||||
|
||||
Reference in new issue
Block a user