/* 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); }