/* mp_ipc.c — IPC 帧读写 + meow_svc_perf/power/netmgr 客户端(单点 IPC 收发)。 */ #include "mp.h" const char *g_sock_perf = PERF_SOCK; const char *g_sock_power = POWER_SOCK; const char *g_sock_launcher = LAUNCHER_SOCK; const char *g_sock_audio = AUDIO_SOCK; const char *g_sock_net = NET_SOCK; const char *g_sock_pm = PM_SOCK; int x_read_full(int fd, uint8_t *buf, size_t n) { size_t off = 0; while (off < n) { ssize_t r = read(fd, buf + off, n - off); if (r <= 0) return -1; off += (size_t)r; } return 0; } int x_write_full(int fd, const uint8_t *buf, size_t n) { size_t off = 0; while (off < n) { ssize_t w = write(fd, buf + off, n - off); if (w <= 0) return -1; off += (size_t)w; } return 0; } int send_frame(int fd, uint16_t type, uint32_t req_id, const void *payload, uint32_t len) { uint8_t hdr[MEOW_IPC_HDR_LEN]; uint32_t magic = MEOW_IPC_MAGIC, rl = len; uint16_t ver = MEOW_IPC_VERSION, t = type; memcpy(hdr + 0, &magic, 4); memcpy(hdr + 4, &ver, 2); memcpy(hdr + 6, &t, 2); memcpy(hdr + 8, &req_id, 4); memcpy(hdr + 12, &rl, 4); if (x_write_full(fd, hdr, MEOW_IPC_HDR_LEN) < 0) return -1; if (len && payload && x_write_full(fd, payload, len) < 0) return -1; return 0; } int recv_frame(int fd, uint16_t *type, uint32_t *req_id, uint8_t *buf, uint32_t cap) { uint8_t hdr[MEOW_IPC_HDR_LEN]; if (x_read_full(fd, hdr, sizeof hdr) < 0) return -1; uint32_t magic, rlen, rid; uint16_t ver, t; memcpy(&magic, hdr, 4); memcpy(&ver, hdr + 4, 2); memcpy(&t, hdr + 6, 2); memcpy(&rid, hdr + 8, 4); memcpy(&rlen, hdr + 12, 4); if (magic != MEOW_IPC_MAGIC || ver != MEOW_IPC_VERSION) return -1; if (rlen > MEOW_IPC_MAX) return -1; if (type) *type = t; if (req_id) *req_id = rid; uint32_t n = (rlen < cap) ? rlen : cap; if (n && x_read_full(fd, buf, n) < 0) return -1; /* 若实际更长,丢弃剩余 */ for (uint32_t i = n; i < rlen; i++) { uint8_t c; if (x_read_full(fd, &c, 1) < 0) return -1; } return (int)n; } int ipc_connect_tmo(const char *sock, int tmo_ms) { int fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd < 0) return -1; struct sockaddr_un a; memset(&a, 0, sizeof a); a.sun_family = AF_UNIX; strncpy(a.sun_path, sock, sizeof a.sun_path - 1); int fl = fcntl(fd, F_GETFL, 0); fcntl(fd, F_SETFL, fl | O_NONBLOCK); int rc = connect(fd, (struct sockaddr *)&a, sizeof a); if (rc < 0 && errno == EINPROGRESS) { struct pollfd pf = { fd, POLLOUT, 0 }; int pr = poll(&pf, 1, tmo_ms); if (pr <= 0) { close(fd); return -1; } int soerr = 0; socklen_t sl = sizeof soerr; getsockopt(fd, SOL_SOCKET, SO_ERROR, &soerr, &sl); if (soerr != 0) { close(fd); return -1; } } else if (rc < 0) { close(fd); return -1; } fcntl(fd, F_SETFL, fl); /* 读/写超时兜底(正常服务应即时响应) */ struct timeval tv = { tmo_ms / 1000, (tmo_ms % 1000) * 1000 }; setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv); setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv); return fd; } int ipc_hello(int fd) { m_hello_t h; memset(&h, 0, sizeof h); strncpy(h.app_id, "os.meow.panel", sizeof h.app_id - 1); h.version = 1; if (send_frame(fd, MSG_HELLO, 1, &h, sizeof h) < 0) return -1; uint8_t buf[64]; uint16_t type; uint32_t rid; int n = recv_frame(fd, &type, &rid, buf, sizeof buf); if (n < (int)sizeof(m_ack_t)) return -1; if (type != MSG_ACK) return -1; m_ack_t a; memcpy(&a, buf, sizeof a); return (a.code == MEOW_OK) ? 0 : -1; } const char *perf_code_str(int code) { switch (code) { case MEOW_OK: return ""; /* 不应使用 */ case MEOW_ERR_INVAL: return "参数非法"; case MEOW_ERR_IO: return "读取失败"; case MEOW_ERR_NODEV: return "设备不存在"; default: return "服务端错误"; } } int perf_subset_for_path(const char *path) { if (!strcmp(path, "/api/perf")) return PERF_SUBSET_PERF; if (!strcmp(path, "/api/system")) return 0; /* system 需 uptime/storage,取全量 */ return 0; /* /api/status */ } int ipc_perf_query(int subset, char *out, int cap) { int fd = ipc_connect_tmo(g_sock_perf, IPC_TIMEOUT_MS); if (fd < 0) return MEOW_ERR_NODEV; int rc = MEOW_ERR_INTERNAL; char msg[128] = ""; if (ipc_hello(fd) < 0) { rc = MEOW_ERR_INTERNAL; snprintf(msg, sizeof msg, "握手中断:性能服务未就绪"); goto done; } m_perf_query_t q; memset(&q, 0, sizeof q); q.subset = (uint32_t)subset; if (send_frame(fd, MSG_PERF_QUERY, 2, &q, sizeof q) < 0) { rc = MEOW_ERR_IO; snprintf(msg, sizeof msg, "发送查询失败"); goto done; } uint8_t buf[MEOW_IPC_MAX + MEOW_IPC_HDR_LEN]; uint16_t type; uint32_t rid; int n = recv_frame(fd, &type, &rid, buf, sizeof buf); if (n < (int)sizeof(m_perf_result_t)) { rc = MEOW_ERR_IO; snprintf(msg, sizeof msg, "应答帧过短"); goto done; } if (type != MSG_PERF_RESULT) { rc = MEOW_ERR_INVAL; snprintf(msg, sizeof msg, "非预期应答类型 0x%04x", type); goto done; } m_perf_result_t r; memcpy(&r, buf, sizeof r); if (r.code != MEOW_OK) { rc = (int)r.code; snprintf(msg, sizeof msg, "性能服务错误: %s", perf_code_str((int)r.code)); goto done; } uint16_t jl = r.json_len; if (jl > sizeof r.json - 1) jl = (uint16_t)(sizeof r.json - 1); if ((size_t)jl >= (size_t)cap) jl = (uint16_t)(cap - 1); memcpy(out, r.json, jl); out[jl] = 0; rc = MEOW_OK; done: close(fd); /* 服务不可达/非 OK 由调用方据此返回错误;绝不静默失败、绝不降级直连 /proc。 */ return rc; } const char *power_code_str(int code) { switch (code) { case MEOW_OK: return ""; case MEOW_ERR_PERM: return "无权限(需 root)"; case MEOW_ERR_INVAL: return "未知命令"; case MEOW_ERR_NODEV: return "设备/能力不支持"; case MEOW_ERR_NOENT: return "缺少升级文件"; case MEOW_ERR_IO: return "读写失败"; default: return "电源服务错误"; } } int ipc_power_action(uint32_t cmd, uint32_t flag, const char *arg, m_power_status_t *out_st) { int fd = ipc_connect_tmo(g_sock_power, 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_power_action_t a; memset(&a, 0, sizeof a); a.cmd = cmd; a.flag = flag; if (arg) snprintf(a.arg, sizeof a.arg, "%s", arg); if (send_frame(fd, MSG_POWER_ACTION, 3, &a, sizeof a) < 0) { rc = MEOW_ERR_IO; goto done; } uint8_t buf[256]; uint16_t type; uint32_t rid; int n = recv_frame(fd, &type, &rid, buf, sizeof buf); if (n < (int)sizeof(m_power_status_t)) { rc = MEOW_ERR_IO; goto done; } if (type != MSG_POWER_STATUS) { rc = MEOW_ERR_INVAL; goto done; } m_power_status_t st; memcpy(&st, buf, sizeof st); if (out_st) *out_st = st; rc = (int)st.code; done: close(fd); return rc; } void svc_msg_kv(const char *msg, const char *key, char *out, size_t cap) { out[0] = 0; if (!msg || !key) return; size_t kl = strlen(key); if (strncmp(msg, key, kl) == 0 && msg[kl] == '=' && msg[kl + 1]) snprintf(out, cap, "%s", msg + kl + 1); }