193 lines
8.2 KiB
C
193 lines
8.2 KiB
C
/* mp_perf.c — 性能/系统域:/api/perf,/api/system,action,pin-unlock(经 meow_svc_perf IPC 聚合)。 */
|
||
#include "mp.h"
|
||
|
||
|
||
int api_perf(int sock, const char *path)
|
||
{
|
||
char json[4096];
|
||
int rc = ipc_perf_query(perf_subset_for_path(path), json, sizeof json);
|
||
if (rc != MEOW_OK || !utf8_valid_n(json, strlen(json))) /* 上游坏字节 → 不返回,降级 */
|
||
return send_err(sock, 500, "INTERNAL", "性能服务不可用(IPC 连接/应答失败)");
|
||
return send_json(sock, 200, json);
|
||
}
|
||
|
||
int perf_extract_network(const char *json, char *out, int cap)
|
||
{
|
||
const char *p = json;
|
||
for (;;) {
|
||
p = strstr(p, "\"network\"");
|
||
if (!p) return -1;
|
||
const char *q = p + 9; /* strlen("\"network\"") */
|
||
while (*q == ' ' || *q == '\t') q++;
|
||
if (*q == ':') { p = q + 1; break; } /* 顶层 network 键 */
|
||
p += 9; /* 嵌套同名键跳过 */
|
||
}
|
||
while (*p == ' ' || *p == '\t') p++;
|
||
if (*p != '{') return -1;
|
||
int depth = 0, in_str = 0, esc = 0;
|
||
const char *q;
|
||
for (q = p; *q; q++) {
|
||
char ch = *q;
|
||
if (in_str) {
|
||
if (esc) esc = 0;
|
||
else if (ch == '\\') esc = 1;
|
||
else if (ch == '"') in_str = 0;
|
||
continue;
|
||
}
|
||
if (ch == '"') { in_str = 1; continue; }
|
||
if (ch == '{') depth++;
|
||
else if (ch == '}') { depth--; if (depth == 0) { q++; break; } }
|
||
}
|
||
if (depth != 0) return -1;
|
||
size_t len = (size_t)(q - p);
|
||
if (len == 0) return -1;
|
||
if (len >= (size_t)cap) len = (size_t)cap - 1;
|
||
memcpy(out, p, len); out[len] = 0;
|
||
return 0;
|
||
}
|
||
|
||
int api_system(int sock)
|
||
{
|
||
char json[4096];
|
||
int rc = ipc_perf_query(0, json, sizeof json);
|
||
if (rc != MEOW_OK) return send_err(sock, 500, "INTERNAL", "性能服务不可用(IPC 连接/应答失败)");
|
||
jv_t *root = jv_parse(json, (int)strlen(json));
|
||
if (!root) return send_err(sock, 500, "INTERNAL", "性能服务 JSON 解析失败");
|
||
jv_t *jsys = jv_obj_get(root, "system");
|
||
jv_t *jupt = jv_obj_get(root, "uptime_sec");
|
||
jv_t *jst = jv_obj_get(root, "storage");
|
||
jv_t *jtf = jv_obj_get(root, "tfcard");
|
||
const char *ver = "-", *host = "-", *ker = "-", *arch = "-";
|
||
if (jsys && jsys->type == J_OBJ) {
|
||
jv_t *v = jv_obj_get(jsys, "version"); if (v && v->type == J_STR) ver = v->str;
|
||
v = jv_obj_get(jsys, "hostname"); if (v && v->type == J_STR) host = v->str;
|
||
v = jv_obj_get(jsys, "kernel"); if (v && v->type == J_STR) ker = v->str;
|
||
v = jv_obj_get(jsys, "arch"); if (v && v->type == J_STR) arch = v->str;
|
||
}
|
||
long long upt = jupt ? (long long)jupt->dnum : 0;
|
||
/* boot default:读权威 /data/meow_boot_default(0=meow 默认, 1=hiby),失败回退 meow */
|
||
int bdf = -1;
|
||
{
|
||
char b[16];
|
||
FILE *f = fopen(BOOT_DEFAULT_FILE, "r");
|
||
if (f) { if (fgets(b, sizeof b, f)) bdf = atoi(b); fclose(f); }
|
||
}
|
||
const char *bootname = bdf < 0 ? "meow" : (bdf ? "hiby" : "meow");
|
||
/* 当前运行模式:读 /tmp/meow_boot_mode(meow/hiby),缺失回退 default(PANEL_ENHANCE_PLAN 批1 前端读 boot.mode) */
|
||
char modebuf[16] = {0};
|
||
{
|
||
FILE *f = fopen("/tmp/meow_boot_mode", "r");
|
||
if (f) {
|
||
if (fgets(modebuf, sizeof modebuf, f)) {
|
||
char *p = modebuf;
|
||
while (*p) { if (*p == '\n' || *p == '\r' || *p == ' ') { *p = 0; break; } p++; }
|
||
}
|
||
fclose(f);
|
||
}
|
||
}
|
||
const char *bootmode = (modebuf[0] == 'm' || modebuf[0] == 'h') ? modebuf : bootname;
|
||
char host_local[80];
|
||
snprintf(host_local, sizeof host_local, "%s.local", host);
|
||
sbuf_t s = {0};
|
||
sA(&s, "{\"version\":");
|
||
sA_str(&s, ver);
|
||
sA(&s, ",\"hostname\":");
|
||
sA_str(&s, host);
|
||
sA(&s, ",\"kernel\":");
|
||
sA_str(&s, ker);
|
||
sA(&s, ",\"arch\":");
|
||
sA_str(&s, arch);
|
||
sA(&s, ",\"uptime_sec\":%lld,\"boot\":{\"default\":", upt);
|
||
sA_str(&s, bootname);
|
||
sA(&s, ",\"mode\":");
|
||
sA_str(&s, bootmode);
|
||
sA(&s, "},\"network\":");
|
||
char netbuf[1024];
|
||
/* 透传 perf 权威 network 段(wifi/bluetooth/hostname_local);含非法 UTF-8(如坏 ssid)→ 回退,不嵌坏字节 */
|
||
if (perf_extract_network(json, netbuf, sizeof netbuf) == 0 && utf8_valid_n(netbuf, strlen(netbuf))) {
|
||
sA(&s, "%s", netbuf);
|
||
} else {
|
||
/* 回退:perf 未含 network 段 → 仅 hostname_local(保持原语义,不崩) */
|
||
sA(&s, "{\"hostname_local\":");
|
||
sA_str(&s, host_local);
|
||
sA(&s, "}");
|
||
}
|
||
sA(&s, ",\"storage\":{");
|
||
if (jst && jst->type == J_OBJ) {
|
||
jv_t *t = jv_obj_get(jst, "total"); long long tot = t ? (long long)t->dnum : 0;
|
||
jv_t *u = jv_obj_get(jst, "used"); long long usd = u ? (long long)u->dnum : 0;
|
||
jv_t *a = jv_obj_get(jst, "avail"); long long ava = a ? (long long)a->dnum : 0;
|
||
sA(&s, "\"root\":{\"total\":%lld,\"used\":%lld,\"avail\":%lld}", tot, usd, ava);
|
||
} else {
|
||
sA(&s, "\"root\":{\"total\":0,\"used\":0,\"avail\":0}");
|
||
}
|
||
sA(&s, ",\"tfcard\":{");
|
||
if (jtf && jtf->type == J_OBJ) {
|
||
jv_t *pr = jv_obj_get(jtf, "present"); int present = pr && pr->bval;
|
||
long long tot = 0, usd = 0, ava = 0;
|
||
jv_t *t = jv_obj_get(jtf, "total"); if (t) tot = (long long)t->dnum;
|
||
jv_t *u = jv_obj_get(jtf, "used"); if (u) usd = (long long)u->dnum;
|
||
jv_t *a = jv_obj_get(jtf, "avail"); if (a) ava = (long long)a->dnum;
|
||
sA(&s, "\"present\":%s,\"total\":%lld,\"used\":%lld,\"avail\":%lld}", present ? "true" : "false", tot, usd, ava);
|
||
} else {
|
||
sA(&s, "\"present\":false,\"total\":0,\"used\":0,\"avail\":0}");
|
||
}
|
||
sA(&s, "},\"ota\":{\"current\":");
|
||
sA_str(&s, ver);
|
||
sA(&s, ",\"latest\":");
|
||
sA_str(&s, ver);
|
||
sA(&s, ",\"available\":false,\"url\":null}");
|
||
/* 重启原因 / 升级状态:svc_power 读级查询(POWER_REBOOT_REASON(8)/POWER_UPGRADE_STATUS(9),只读、
|
||
* 任意调用者)。st.msg 分别为 "coldboot=reboot|shutdown|unknown" 与 "upgrade=yes|no"。
|
||
* 前端批1-B 已留位(读 d.reboot_reason / d.upgrade_status),此处点亮。查询失败保持空串(不谎报)。 */
|
||
char rreason[48] = "", ustatus[32] = "";
|
||
{
|
||
m_power_status_t st;
|
||
memset(&st, 0, sizeof st);
|
||
if (ipc_power_action(POWER_REBOOT_REASON, 0, NULL, &st) == MEOW_OK)
|
||
svc_msg_kv(st.msg, "coldboot", rreason, sizeof rreason);
|
||
memset(&st, 0, sizeof st);
|
||
if (ipc_power_action(POWER_UPGRADE_STATUS, 0, NULL, &st) == MEOW_OK)
|
||
svc_msg_kv(st.msg, "upgrade", ustatus, sizeof ustatus);
|
||
}
|
||
sA(&s, ",\"reboot_reason\":");
|
||
sA_str(&s, rreason);
|
||
sA(&s, ",\"upgrade_status\":");
|
||
sA_str(&s, ustatus);
|
||
sA(&s, "}");
|
||
jv_free(root);
|
||
int ret = send_json(sock, 200, s.b ? s.b : "");
|
||
sbuf_free(&s);
|
||
return ret;
|
||
}
|
||
|
||
int api_pin_unlock_once(int sock)
|
||
{
|
||
const char *path = "/tmp/meow_pin_unlock_once";
|
||
FILE *f = fopen(path, "w");
|
||
if (!f) return send_err(sock, 500, "INTERNAL", "无法写入一次性解锁标志");
|
||
fputs("1\n", f);
|
||
fclose(f);
|
||
return send_json(sock, 200, "{\"unlock_once\":true}");
|
||
}
|
||
|
||
int api_system_action(int sock, const char *which, const req_t *r)
|
||
{
|
||
(void)r;
|
||
uint32_t cmd;
|
||
if (!strcmp(which, "reboot")) cmd = POWER_REBOOT;
|
||
else if (!strcmp(which, "shutdown")) cmd = POWER_SHUTDOWN;
|
||
else if (!strcmp(which, "ota")) cmd = POWER_UPGRADE;
|
||
else return send_err(sock, 404, "NOT_FOUND", "未知系统动作");
|
||
m_power_status_t st;
|
||
memset(&st, 0, sizeof st);
|
||
int rc = ipc_power_action(cmd, 0, NULL, &st);
|
||
if (rc == MEOW_OK) return send_json(sock, 200, "{\"accepted\":true}");
|
||
/* 系统动作类服务端通常「先应答受理再执行」;此处若返回非 OK 则说明服务未受理 */
|
||
const char *why = st.msg[0] ? st.msg : power_code_str(rc);
|
||
if (rc == MEOW_ERR_PERM) return send_err(sock, 403, "FORBIDDEN", "电源服务拒绝(非 root 调用)");
|
||
if (rc == MEOW_ERR_NODEV) return send_err(sock, 503, "NO_CARD", "升级文件缺失或服务不可达");
|
||
if (rc == MEOW_ERR_NOENT || rc == MEOW_ERR_IO) return send_err(sock, 503, "NO_CARD", why);
|
||
return send_err(sock, 500, "INTERNAL", why);
|
||
}
|