/* mp_power.c — 功耗管理域:meow_powermgr IPC(/api/power*,policy,window,events)。 */ #include "mp.h" int ipc_pm_query(uint32_t subset, char *out, int cap) { int fd = ipc_connect_tmo(g_sock_pm, IPC_TIMEOUT_MS); if (fd < 0) return MEOW_ERR_NODEV; int rc = MEOW_ERR_INTERNAL; if (ipc_hello(fd) < 0) { rc = MEOW_ERR_INTERNAL; goto done; } m_pm_query_t q; memset(&q, 0, sizeof q); q.subset = subset; if (send_frame(fd, MSG_PM_QUERY, 0x31, &q, sizeof q) < 0) { rc = MEOW_ERR_IO; goto done; } uint8_t buf[MEOW_IPC_MAX]; uint16_t type; uint32_t rid; int n = recv_frame(fd, &type, &rid, buf, sizeof buf); if (n < 12) { rc = MEOW_ERR_IO; goto done; } /* code(4)+subset(4)+json_len(2)+pad(2) */ if (type != MSG_PM_RESULT) { rc = MEOW_ERR_INVAL; goto done; } uint32_t c; memcpy(&c, buf, 4); if (c != MEOW_OK) { rc = (int)c; goto done; } /* powermgr 发**紧凑**负载:json 从偏移 12 起(json_len 在偏移 8);同 m_pm_result_t 布局,兼容全量/紧凑。 */ uint16_t jl; memcpy(&jl, buf + 8, 2); if (jl > MEOW_IPC_MAX - 12) jl = (uint16_t)(MEOW_IPC_MAX - 12); if ((size_t)jl >= (size_t)cap) jl = (uint16_t)(cap - 1); memcpy(out, buf + 12, jl); out[jl] = 0; rc = MEOW_OK; done: close(fd); return rc; } int api_power(int sock) { char js[MEOW_IPC_MAX]; int rc = ipc_pm_query(0, js, sizeof js); if (rc != MEOW_OK || !utf8_valid_n(js, strlen(js))) /* 上游坏字节 → 不返回,降级 */ return send_err(sock, 503, "POWER_UNAVAILABLE", "电源管理服务不可用"); return send_json(sock, 200, js); } int ipc_pm_send(uint16_t req_type, const void *pl, uint32_t plen, uint16_t *rtype, char *out, int cap) { int fd = ipc_connect_tmo(g_sock_pm, IPC_TIMEOUT_MS); if (fd < 0) return MEOW_ERR_NODEV; int rc = MEOW_ERR_INTERNAL; if (rtype) *rtype = 0; if (ipc_hello(fd) < 0) { rc = MEOW_ERR_INTERNAL; goto done; } if (send_frame(fd, req_type, 0x41, pl, plen) < 0) { rc = MEOW_ERR_IO; goto done; } uint8_t buf[MEOW_IPC_MAX]; uint16_t type; uint32_t rid; int n = recv_frame(fd, &type, &rid, buf, sizeof buf); if (n <= 0) { rc = MEOW_ERR_IO; goto done; } if (rtype) *rtype = type; if (type == MSG_ERR) { m_err_t e; memcpy(&e, buf, sizeof e); rc = (int)e.code; goto done; } if (n < 12) { rc = MEOW_ERR_IO; goto done; } /* code(4)+subset(4)+json_len(2)+pad(2) */ uint32_t c; memcpy(&c, buf, 4); if (c != MEOW_OK) { rc = (int)c; goto done; } uint16_t jl; memcpy(&jl, buf + 8, 2); if (jl > MEOW_IPC_MAX - 12) jl = (uint16_t)(MEOW_IPC_MAX - 12); if ((size_t)jl >= (size_t)cap) jl = (uint16_t)(cap - 1); memcpy(out, buf + 12, jl); out[jl] = 0; rc = (int)jl; done: close(fd); 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]; uint16_t rt = 0; int rc = ipc_pm_send(MSG_PM_POLICY_GET, NULL, 0, &rt, js, sizeof js); if (rc <= 0) 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", "电源管理服务应答包含非法字节"); /* 追加顶层 `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"); char item[20] = "", val[40] = ""; if (ji && ji->type == J_STR && ji->str) snprintf(item, sizeof item, "%s", ji->str); if (jv && jv->type == J_STR && jv->str) snprintf(val, sizeof val, "%s", jv->str); else if (jv && jv->type == J_NUM) snprintf(val, sizeof val, "%lld", jv->inum); jv_free(root); if (!item[0]) return send_err(sock, 422, "UNSUPPORTED", "缺 item(profile/low-pct/crit-pct/bri-cap/scr-cap)"); if (!val[0]) return send_err(sock, 422, "UNSUPPORTED", "缺 value"); m_pm_policy_set_t s; memset(&s, 0, sizeof s); snprintf(s.item, sizeof s.item, "%.15s", item); /* item[16] 留一个 NUL;防越界截断告警 */ snprintf(s.val, sizeof s.val, "%.31s", val); /* val[32] 同理 */ char js[MEOW_IPC_MAX]; uint16_t rt = 0; int rc = ipc_pm_send(MSG_PM_POLICY_SET, &s, sizeof s, &rt, js, sizeof js); if (rc <= 0) { if (rt == MSG_ERR) /* 权限不足:powermgr 拒绝(面板 root 仅当被白名单才放行;如实回传) */ return send_err(sock, 403, "FORBIDDEN", rc == MEOW_ERR_PERM ? "设备身份不允许写电源策略" : "电源策略写入被拒绝"); 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", "电源管理服务应答包含非法字节"); /* 服务端对非法值回 ok:false(HTTP 层 200,code 仍 OK);按其 why 映射 422。 */ if (strstr(js, "\"ok\":false")) { char why[96] = ""; jv_t *jr = jv_parse(js, (int)strlen(js)); if (jr) { jv_t *w = jv_obj_get(jr, "why"); if (w && w->type == J_STR && w->str) snprintf(why, sizeof why, "%s", w->str); jv_free(jr); } return send_err(sock, 422, "UNSUPPORTED", why[0] ? why : "非法电源策略值"); } return send_json(sock, 200, js); } int api_power_window(int sock, uint16_t req_type, uint16_t want_type, const char *unavail) { uint32_t count = 0; char js[MEOW_IPC_MAX]; uint16_t rt = 0; int rc = ipc_pm_send(req_type, &count, sizeof count, &rt, js, sizeof js); if (rc <= 0) return send_err(sock, 503, "POWER_UNAVAILABLE", unavail); if (rt != want_type || !utf8_valid_n(js, strlen(js))) return send_err(sock, 503, "POWER_UNAVAILABLE", "电源管理服务应答包含非法字节"); return send_json(sock, 200, js); } int api_power_history(int sock) { return api_power_window(sock, MSG_PM_HISTORY, MSG_PM_HISTORY_RESULT, "电源历史采样不可用(powermgr 未运行)"); } int api_power_events(int sock) { return api_power_window(sock, MSG_PM_EVENTS, MSG_PM_EVENTS_RESULT, "电源事件不可用(powermgr 未运行)"); }