212 lines
7.4 KiB
C
212 lines
7.4 KiB
C
/* 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);
|
||
}
|