feat(system): 系统框架一期 — 专项服务(性能/电源)+IPC+电源CLI+电菜单改经服务(agent-155)
- meow_svc_power: 单一二进制(svcd空参IPC常驻 /usr/data/meow_power.sock + root CLI免鉴权), 12命令(shutdown/reboot强制=0/upgrade/set-default-hiby/reboot--hiby + set-default-meow/status/screen/dac(status仅)/battery/reboot-reason/upgrade-status); 动作同源sys_do_reboot/set_boot_default_hiby/mtd5=ota:kernel2; dac on/off无控制点不支持 - meow_svc_perf: IPC(/usr/data/meow_perf.sock MSG_PERF_*) + CLI, 聚合/proc/df/thermal/version/uname JSON(与MEOWPANEL_PROTOCOL对齐) - power_menu.c 改经 meow_power_action(launcher先转储再IPC), 删sys_do_reboot/set_boot_default_hiby; 4动作+8s强关+confirm_reboot_player+3处长按强关全经服务; launcher内 sys_do_reboot/bootmode.sh/set_boot_default_hiby 0调用 - meow_ipc.h 仅加 MSG_PERF_QUERY/RESULT(0x50/51)+MSG_POWER_ACTION/STATUS(0x60/61)+POWER_*(12)+m_perf/power_t; 未动既有 - S89meow_svcd 预置 meow_svc_perf/power.conf(trigger=boot); 各独立socket - IPC鉴权一期=SO_PEERCRED uid==0基础(完整os.meow.*二期); 通知转储=launcher先转储再IPC(二期补launcher→服务通道); 失败安全(服务不可达→按取消,不静默不直连) - meow_svc_power=32d6be92/meow_svc_perf=7b0f3b64/launcher=d90f09e9/svcd=fb2d608b(未动)/meow_ui.a=92e61fb8(未改); 全0warning; 备份power_menu.c.pre-svc.bak回退
This commit is contained in:
1 parent
898bc176c9
commit
23727cf400
16 files changed
+1559
-41
No files matched your search
Executable
+29
@@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
# build_svc_perf.sh — meow_svc_perf 交叉编译(mipsel 静态)+ 宿主编译可选。
|
||||
#
|
||||
# 目标:R3 Pro II / MeowOS(mipsel-linux-muslsf,静态)。性能服务仅 POSIX + Linux 基接口
|
||||
# (复用 meow_ipc.h 帧格式,无 launcher 依赖),单文件满足「固件内、不入卡」(<200KB 阈值)。
|
||||
#
|
||||
# 用法:
|
||||
# ./build_svc_perf.sh # 交叉编译 → patch_v2/meow_svc_perf/meow_svc_perf(mipsel 静态)
|
||||
# ./build_svc_perf.sh host # 宿主编译 → patch_v2/meow_svc_perf/meow_svc_perf_host
|
||||
set -e
|
||||
cd "$(dirname "$0")"
|
||||
ROOT="$HOME/Project/R3ProII"
|
||||
CC="$ROOT/.tools/zig-mipsel-cc"
|
||||
I_HDR="$ROOT/patch_v2/meow_launcherd" # meow_ipc.h(帧/m_perf_query_t/m_perf_result_t)
|
||||
|
||||
if [ "$1" = "host" ]; then
|
||||
HOST_CC="${CC_HOST:-cc}"
|
||||
"$HOST_CC" -Os -Wall -Wextra -I"$I_HDR" meow_svc_perf.c -o meow_svc_perf_host
|
||||
echo "== host build =="
|
||||
ls -la meow_svc_perf_host
|
||||
file meow_svc_perf_host
|
||||
exit 0
|
||||
fi
|
||||
|
||||
"$CC" -Os -Wall -Wextra -static -I"$I_HDR" meow_svc_perf.c -o meow_svc_perf
|
||||
echo "== meow_svc_perf (mipsel static) =="
|
||||
ls -la meow_svc_perf
|
||||
md5sum meow_svc_perf
|
||||
file meow_svc_perf
|
||||
Executable
BIN
Binary file not shown.
@@ -0,0 +1,523 @@
|
||||
#define _GNU_SOURCE /* gethostname 等 GNU/musl 扩展 */
|
||||
|
||||
/* meow_svc_perf.c — MeowOS 性能分析专项子服务(SYSTEM_FRAMEWORK_SERVICES §1.2/§2.2)。
|
||||
*
|
||||
* 单一二进制双形态:
|
||||
* - svcd 以**空参**拉起(§1.3 conf `args=` 空)→ IPC 服务常驻:bind `/usr/data/meow_perf.sock`
|
||||
* (`--sock`/`MEOW_IPC_PERF_SOCK` 可覆盖),收 MSG_HELLO→MSG_ACK、MSG_PERF_QUERY→MSG_PERF_RESULT。
|
||||
* - root shell `meow_svc_perf [query]` → 一次性 CLI(免鉴权)打印聚合 JSON(不常驻)。
|
||||
*
|
||||
* 聚合的数据源与 launcher `about.c`/`util.c`/bootmenu**同源**(/proc/stat、/proc/meminfo、
|
||||
* /proc/uptime、/proc/loadavg、/sys/class/thermal 各 zone、df/mount、/proc/cpuinfo、/etc/meowos_version、
|
||||
* gethostname/uname),聚合成与 MEOWPANEL_PROTOCOL §3.1 `/api/status`/.data 对齐的 JSON——
|
||||
* 单一权威源,MeowPanel 后端/launcher 不再各自爬 /proc。
|
||||
*
|
||||
* MSG_PERF_QUERY.subset 位掩码(m_perf_query_t):0=全部;PERF_SUBSET_PERF / PERF_SUBSET_SYSTEM。
|
||||
* 鉴权(一期):性能查询**只读安全**,本批不强制 os.meow.*;完整读级鉴权见 §5.3(二期)。
|
||||
*
|
||||
* 编译(mipsel 静态):build_svc_perf.sh;宿主编译 build_svc_perf.sh host。
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <poll.h>
|
||||
#include <time.h>
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#include "meow_ipc.h" /* 帧格式/消息号 + m_perf_query_t/m_perf_result_t(与 launcher 同源) */
|
||||
|
||||
#define VERSION_FILE "/etc/meowos_version"
|
||||
#define TF_MOUNT "/usr/data/mnt/sd_0"
|
||||
#define MEOW_PIPE_MAX 4096
|
||||
#define MEOW_TIMEOUT_MS 1500
|
||||
|
||||
static const char *g_sock = "/usr/data/meow_perf.sock";
|
||||
|
||||
static void svc_log(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
fprintf(stderr, "[meow_svc_perf] ");
|
||||
vfprintf(stderr, fmt, ap);
|
||||
fprintf(stderr, "\n");
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/* ---------------- 有界 JSON 追加 ---------------- */
|
||||
typedef struct { char *b; size_t n; size_t cap; } sbuf_t;
|
||||
|
||||
static void sA(sbuf_t *s, const char *fmt, ...)
|
||||
{
|
||||
if (s->n >= s->cap - 1) return;
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
int w = vsnprintf(s->b + s->n, s->cap - s->n, fmt, ap);
|
||||
va_end(ap);
|
||||
if (w < 0) return;
|
||||
if ((size_t)w >= s->cap - s->n) s->n = s->cap - 1;
|
||||
else s->n += (size_t)w;
|
||||
}
|
||||
|
||||
/* JSON 字符串转义(version/hostname 等;ASCII 常见字符直通,引号/反斜杠转义) */
|
||||
static void sA_str(sbuf_t *s, const char *v)
|
||||
{
|
||||
if (!v) v = "";
|
||||
sA(s, "\"");
|
||||
for (const char *p = v; *p; p++) {
|
||||
unsigned char c = (unsigned char)*p;
|
||||
if (c == '"' || c == '\\') { sA(s, "\\%c", (int)c); }
|
||||
else if (c < 0x20) { sA(s, "\\u%04x", c); }
|
||||
else sA(s, "%c", (int)c); /* buf 0x80.. 按字节透传(utf-8) */
|
||||
}
|
||||
sA(s, "\"");
|
||||
}
|
||||
|
||||
/* ---------------- 限时读整文件 ---------------- */
|
||||
static int read_file_txt(const char *path, char *buf, int cap)
|
||||
{
|
||||
int fd = open(path, O_RDONLY);
|
||||
if (fd < 0) return -1;
|
||||
int n = 0;
|
||||
while (n < cap - 1) {
|
||||
ssize_t r = read(fd, buf + n, (size_t)(cap - 1 - n));
|
||||
if (r <= 0) break;
|
||||
n += (int)r;
|
||||
}
|
||||
close(fd);
|
||||
buf[n] = 0;
|
||||
while (n > 0 && (buf[n - 1] == '\n' || buf[n - 1] == '\r')) buf[--n] = 0;
|
||||
return n;
|
||||
}
|
||||
|
||||
/* ---------------- popen 限时读(kill 子进程防挂) ---------------- */
|
||||
static char *popen_read_tmo(int tmo_ms, const char *cmd)
|
||||
{
|
||||
int p[2];
|
||||
if (pipe(p) < 0) return NULL;
|
||||
pid_t pid = fork();
|
||||
if (pid < 0) { close(p[0]); close(p[1]); return NULL; }
|
||||
if (pid == 0) { dup2(p[1], 1); close(p[0]); close(p[1]); execl("/bin/sh", "sh", "-c", cmd, (char *)0); _exit(127); }
|
||||
close(p[1]);
|
||||
fcntl(p[0], F_SETFL, fcntl(p[0], F_GETFL, 0) | O_NONBLOCK);
|
||||
char *buf = malloc(MEOW_PIPE_MAX);
|
||||
if (!buf) { close(p[0]); waitpid(pid, NULL, 0); return NULL; }
|
||||
size_t off = 0;
|
||||
int remain = tmo_ms;
|
||||
while (remain > 0 && off < MEOW_PIPE_MAX - 1) {
|
||||
struct pollfd pf = { p[0], POLLIN, 0 };
|
||||
int r = poll(&pf, 1, remain);
|
||||
if (r < 0) { if (errno == EINTR) continue; break; }
|
||||
if (r == 0) break;
|
||||
ssize_t rd = read(p[0], buf + off, MEOW_PIPE_MAX - 1 - off);
|
||||
if (rd == 0) break;
|
||||
if (rd < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK) continue; break; }
|
||||
off += (size_t)rd;
|
||||
}
|
||||
close(p[0]);
|
||||
waitpid(pid, NULL, 0);
|
||||
buf[off] = 0;
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* ---------------- CPU ---------------- */
|
||||
/* /proc/stat 首行 "cpu user nice system idle iowait irq softirq steal ..."。 */
|
||||
static int g_cpu_prev[10];
|
||||
static int g_cpu_prev_init = 0;
|
||||
|
||||
static double cpu_usage_pct(void)
|
||||
{
|
||||
FILE *f = fopen("/proc/stat", "r");
|
||||
char line[256];
|
||||
if (!f || !fgets(line, sizeof line, f)) { if (f) fclose(f); return -1; }
|
||||
fclose(f);
|
||||
unsigned long long vals[10];
|
||||
char *p = line + 3; /* skip "cpu" */
|
||||
int n = 0;
|
||||
while (n < 10) {
|
||||
while (*p == ' ') p++;
|
||||
if (!*p || !isdigit((unsigned char)*p)) break;
|
||||
vals[n++] = strtoull(p, &p, 10);
|
||||
}
|
||||
if (n < 4) return -1;
|
||||
unsigned long long total = 0, idle = 0;
|
||||
for (int i = 0; i < n; i++) total += vals[i];
|
||||
idle = vals[3] + (n > 4 ? vals[4] : 0);
|
||||
if (!g_cpu_prev_init) {
|
||||
for (int i = 0; i < 10; i++) g_cpu_prev[i] = (int)vals[i];
|
||||
g_cpu_prev_init = 1;
|
||||
return 0;
|
||||
}
|
||||
unsigned long long dtotal = total, didle = idle;
|
||||
for (int i = 0; i < n; i++) if (i < 10) dtotal -= (unsigned long long)g_cpu_prev[i];
|
||||
if (n > 4) didle -= (unsigned long long)g_cpu_prev[3] + (unsigned long long)g_cpu_prev[4];
|
||||
else didle -= (unsigned long long)g_cpu_prev[3];
|
||||
for (int i = 0; i < 10; i++) g_cpu_prev[i] = (int)vals[i];
|
||||
if (!dtotal) return 0;
|
||||
double u = 100.0 * (double)(dtotal - didle) / (double)dtotal;
|
||||
if (u < 0) u = 0;
|
||||
return u;
|
||||
}
|
||||
|
||||
static int core_count(void)
|
||||
{
|
||||
FILE *f = fopen("/proc/stat", "r");
|
||||
char line[256];
|
||||
int n = 0;
|
||||
if (!f) return 1;
|
||||
while (fgets(line, sizeof line, f)) {
|
||||
if (line[0] == 'c' && line[1] == 'p' && line[2] == 'u' && isdigit((unsigned char)line[3]))
|
||||
n++;
|
||||
if (line[0] != 'c') break;
|
||||
}
|
||||
fclose(f);
|
||||
return n > 0 ? n : 1;
|
||||
}
|
||||
|
||||
static void loadavg(double *l1, double *l5, double *l15)
|
||||
{
|
||||
FILE *f = fopen("/proc/loadavg", "r");
|
||||
*l1 = *l5 = *l15 = 0;
|
||||
if (f) { if (fscanf(f, "%lf %lf %lf", l1, l5, l15) != 3) { *l1 = *l5 = *l15 = 0; } fclose(f); }
|
||||
}
|
||||
|
||||
/* ---------------- 内存 ---------------- */
|
||||
static void meminfo(unsigned long *total, unsigned long *used, unsigned long *free_kb,
|
||||
unsigned long *avail, unsigned long *stotal, unsigned long *sused)
|
||||
{
|
||||
*total = *used = *free_kb = *avail = *stotal = *sused = 0;
|
||||
FILE *f = fopen("/proc/meminfo", "r");
|
||||
if (!f) return;
|
||||
char k[64], line[256];
|
||||
unsigned long mtot = 0, mfree = 0, mavail = 0, swapT = 0, swapF = 0;
|
||||
while (fgets(line, sizeof line, f)) {
|
||||
unsigned long v;
|
||||
if (sscanf(line, "%63s %lu", k, &v) != 2) continue;
|
||||
if (!strcmp(k, "MemTotal:")) mtot = v;
|
||||
else if (!strcmp(k, "MemFree:")) mfree = v;
|
||||
else if (!strcmp(k, "MemAvailable:")) mavail = v;
|
||||
else if (!strcmp(k, "SwapTotal:")) swapT = v;
|
||||
else if (!strcmp(k, "SwapFree:")) swapF = v;
|
||||
}
|
||||
fclose(f);
|
||||
*total = mtot;
|
||||
*free_kb = mfree;
|
||||
if (mavail > 0) { *avail = mavail; *used = (mtot >= mavail) ? mtot - mavail : 0; }
|
||||
else { *avail = mfree; *used = (mtot >= mfree) ? mtot - mfree : 0; }
|
||||
*stotal = swapT;
|
||||
*sused = (swapT >= swapF) ? swapT - swapF : 0;
|
||||
}
|
||||
|
||||
static double uptime_sec(void)
|
||||
{
|
||||
FILE *f = fopen("/proc/uptime", "r");
|
||||
double up = 0;
|
||||
if (f) { if (fscanf(f, "%lf", &up) != 1) up = 0; fclose(f); }
|
||||
return up;
|
||||
}
|
||||
|
||||
/* ---------------- 温度(/sys/class/thermal/thermal_zone*;type 匹配 cpu→cpu,其余→board) ---------------- */
|
||||
static void temp_json(sbuf_t *s, int *any)
|
||||
{
|
||||
DIR *d = opendir("/sys/class/thermal");
|
||||
*any = 0;
|
||||
if (!d) { sA(s, "{}"); return; }
|
||||
double cpu = -1, board = -1;
|
||||
struct dirent *e;
|
||||
while ((e = readdir(d))) {
|
||||
if (strncmp(e->d_name, "thermal_zone", 12) != 0) continue;
|
||||
char path[300], typ[64] = "", tmp[32] = "";
|
||||
snprintf(path, sizeof path, "/sys/class/thermal/%s/type", e->d_name);
|
||||
read_file_txt(path, typ, sizeof typ);
|
||||
snprintf(path, sizeof path, "/sys/class/thermal/%s/temp", e->d_name);
|
||||
read_file_txt(path, tmp, sizeof tmp);
|
||||
long millideg = atol(tmp);
|
||||
if (millideg <= 0) continue;
|
||||
double c = (double)millideg / 1000.0;
|
||||
if (strstr(typ, "cpu") || strstr(typ, "CPU")) { if (cpu < 0) cpu = c; }
|
||||
else if (board < 0) board = c;
|
||||
}
|
||||
closedir(d);
|
||||
if (cpu < 0 && board < 0) { sA(s, "{}"); return; }
|
||||
sA(s, "{");
|
||||
int first = 1;
|
||||
if (cpu >= 0) { sA(s, "\"cpu\":%.1f", cpu); first = 0; }
|
||||
if (board >= 0) { if (!first) sA(s, ","); sA(s, "\"board\":%.1f", board); }
|
||||
sA(s, "}");
|
||||
*any = 1;
|
||||
}
|
||||
|
||||
/* ---------------- 存储(df -k <path> 第 2 行:右 5 字段 = fs,total,used,avail,use%,mount) ---------------- */
|
||||
static int df_parse(const char *path, unsigned long *total, unsigned long *used, unsigned long *avail)
|
||||
{
|
||||
*total = *used = *avail = 0;
|
||||
char cmd[160];
|
||||
snprintf(cmd, sizeof cmd, "PATH=/bin:/sbin:/usr/bin:/usr/sbin df -k %s 2>/dev/null", path);
|
||||
char *o = popen_read_tmo(MEOW_TIMEOUT_MS, cmd);
|
||||
if (!o) return -1;
|
||||
char *ln = strchr(o, '\n');
|
||||
if (ln) ln++;
|
||||
char *save = NULL;
|
||||
char *toks[8]; int nt = 0;
|
||||
for (char *tk = strtok_r(ln ? ln : o, " \t", &save); tk && nt < 8; tk = strtok_r(NULL, " \t", &save))
|
||||
toks[nt++] = tk;
|
||||
if (nt >= 5) { *total = strtoul(toks[nt - 5], NULL, 10); *used = strtoul(toks[nt - 4], NULL, 10); *avail = strtoul(toks[nt - 3], NULL, 10); }
|
||||
free(o);
|
||||
return (*total || *avail) ? 0 : -1;
|
||||
}
|
||||
|
||||
static int tf_present(void)
|
||||
{
|
||||
FILE *f = fopen("/proc/mounts", "r");
|
||||
if (!f) return 0;
|
||||
char line[512];
|
||||
int present = 0;
|
||||
while (fgets(line, sizeof line, f)) {
|
||||
if (strstr(line, TF_MOUNT)) { present = 1; break; }
|
||||
}
|
||||
fclose(f);
|
||||
return present;
|
||||
}
|
||||
|
||||
/* ---------------- 系统静态(version/hostname/kernel/arch) ---------------- */
|
||||
static void sys_arch(char *out, int cap)
|
||||
{
|
||||
struct utsname u;
|
||||
const char *m = "mipsel";
|
||||
if (uname(&u) == 0) m = u.machine;
|
||||
if (strstr(m, "mips")) m = "mipsel"; /* mipsel-linux-muslsf 目标 */
|
||||
snprintf(out, cap, "%.31s", m);
|
||||
}
|
||||
|
||||
static void sys_kernel(char *out, int cap)
|
||||
{
|
||||
struct utsname u;
|
||||
size_t rl = 0;
|
||||
if (uname(&u) != 0) { snprintf(out, cap, "-"); return; }
|
||||
rl = strnlen(u.release, 64);
|
||||
if (rl > (size_t)(cap - 1)) rl = (size_t)(cap - 1);
|
||||
memcpy(out, u.release, rl);
|
||||
out[rl] = 0;
|
||||
}
|
||||
|
||||
/* ---------------- 聚合 JSON(subset 位掩码;与 MEOWPANEL_PROTOCOL §3.1 .data 对齐) ---------------- */
|
||||
static int agg_json(int subset, char *out, int cap)
|
||||
{
|
||||
int ip = (subset == 0) || (subset & PERF_SUBSET_PERF);
|
||||
int isys = (subset == 0) || (subset & PERF_SUBSET_SYSTEM);
|
||||
sbuf_t s = { out, 0, (size_t)cap };
|
||||
sA(&s, "{");
|
||||
int first = 1;
|
||||
#define SEP() do { if (!first) sA(&s, ","); first = 0; } while (0)
|
||||
|
||||
if (ip) {
|
||||
SEP(); sA(&s, "\"ts\":%ld", (long)time(NULL));
|
||||
SEP(); sA(&s, "\"uptime_sec\":%.0f", uptime_sec());
|
||||
double u = cpu_usage_pct(); int cores = core_count();
|
||||
double l1, l5, l15; loadavg(&l1, &l5, &l15);
|
||||
SEP(); sA(&s, "\"cpu\":{\"usage\":%.1f,\"cores\":%d,\"load\":[%.2f,%.2f,%.2f]}", u, cores, l1, l5, l15);
|
||||
unsigned long mtot, mused, mfree, mavail, swapT, swapU;
|
||||
meminfo(&mtot, &mused, &mfree, &mavail, &swapT, &swapU);
|
||||
SEP(); sA(&s, "\"memory\":{\"total\":%lu,\"used\":%lu,\"free\":%lu,\"available\":%lu,\"swap_total\":%lu,\"swap_used\":%lu}",
|
||||
mtot, mused, mfree, mavail, swapT, swapU);
|
||||
unsigned long st, su, sa;
|
||||
if (df_parse("/", &st, &su, &sa) == 0) {
|
||||
SEP();
|
||||
sA(&s, "\"storage\":{\"path\":\"/\",\"total\":%lu,\"used\":%lu,\"avail\":%lu}", st, su, sa);
|
||||
} else {
|
||||
SEP();
|
||||
sA(&s, "\"storage\":{\"path\":\"/\",\"total\":0,\"used\":0,\"avail\":0}");
|
||||
}
|
||||
if (tf_present()) {
|
||||
unsigned long tt, tu, ta;
|
||||
if (df_parse(TF_MOUNT, &tt, &tu, &ta) == 0) {
|
||||
SEP();
|
||||
sA(&s, "\"tfcard\":{\"present\":true,\"path\":\"" TF_MOUNT "\",\"total\":%lu,\"used\":%lu,\"avail\":%lu,\"filesystem\":\"exfat\"}", tt, tu, ta);
|
||||
} else {
|
||||
SEP();
|
||||
sA(&s, "\"tfcard\":{\"present\":true,\"path\":\"" TF_MOUNT "\",\"total\":0,\"used\":0,\"avail\":0,\"filesystem\":\"exfat\"}");
|
||||
}
|
||||
} else {
|
||||
SEP();
|
||||
sA(&s, "\"tfcard\":{\"present\":false,\"path\":\"" TF_MOUNT "\",\"total\":0,\"used\":0,\"avail\":0,\"filesystem\":null}");
|
||||
}
|
||||
int any;
|
||||
SEP(); sA(&s, "\"temp\":"); temp_json(&s, &any);
|
||||
}
|
||||
if (isys) {
|
||||
char ver[64] = "-", host[64] = "-", ker[64] = "-", arch[32] = "-";
|
||||
if (read_file_txt(VERSION_FILE, ver, sizeof ver) <= 0 || !ver[0]) snprintf(ver, sizeof ver, "-");
|
||||
if (gethostname(host, sizeof host) != 0 || !host[0]) snprintf(host, sizeof host, "-");
|
||||
sys_kernel(ker, sizeof ker);
|
||||
sys_arch(arch, sizeof arch);
|
||||
SEP(); sA(&s, "\"system\":{\"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, "}");
|
||||
}
|
||||
sA(&s, "}"); /* 外层闭合 */
|
||||
#undef SEP
|
||||
if (s.n >= (size_t)cap) s.n = (size_t)cap - 1;
|
||||
s.b[s.n] = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---------------- IPC 帧读写 ---------------- */
|
||||
static 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;
|
||||
}
|
||||
static 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;
|
||||
}
|
||||
static 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, rlen = 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, &rlen, 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;
|
||||
}
|
||||
static int send_ack(int fd, uint32_t req_id, uint32_t code, uint32_t status)
|
||||
{
|
||||
m_ack_t a; memset(&a, 0, sizeof a); a.code = code; a.status = status;
|
||||
return send_frame(fd, MSG_ACK, req_id, &a, sizeof a);
|
||||
}
|
||||
static int send_err(int fd, uint32_t req_id, uint32_t code, const char *msg)
|
||||
{
|
||||
m_err_t e; memset(&e, 0, sizeof e);
|
||||
uint16_t ml = msg ? (uint16_t)strlen(msg) : 0;
|
||||
if (ml > 63) ml = 63;
|
||||
e.code = code; e.msg_len = ml;
|
||||
if (ml) memcpy(e.msg, msg, ml);
|
||||
return send_frame(fd, MSG_ERR, req_id, &e, sizeof e);
|
||||
}
|
||||
static int send_perf_result(int fd, uint32_t req_id, int subset, const char *json)
|
||||
{
|
||||
m_perf_result_t r;
|
||||
memset(&r, 0, sizeof r);
|
||||
r.code = MEOW_OK;
|
||||
r.subset = (uint32_t)subset;
|
||||
uint16_t jl = (uint16_t)strlen(json);
|
||||
if (jl > sizeof r.json - 1) jl = (uint16_t)(sizeof r.json - 1);
|
||||
memcpy(r.json, json, jl);
|
||||
r.json[jl] = 0;
|
||||
r.json_len = jl;
|
||||
return send_frame(fd, MSG_PERF_RESULT, req_id, &r, sizeof r);
|
||||
}
|
||||
|
||||
/* MSG_PERF_QUERY:读取 subset 后聚合。0=全部。 */
|
||||
static void handle_query(int c, uint32_t req_id, const uint8_t *payload, uint32_t rlen)
|
||||
{
|
||||
m_perf_query_t q;
|
||||
memset(&q, 0, sizeof q);
|
||||
if (rlen > sizeof q) rlen = sizeof q;
|
||||
memcpy(&q, payload, rlen);
|
||||
int subset = (q.subset == 0) ? 0 : (int)q.subset;
|
||||
char json[MEOW_IPC_MAX - 12];
|
||||
agg_json(subset, json, sizeof json);
|
||||
send_perf_result(c, req_id, subset, json);
|
||||
}
|
||||
|
||||
static void handle_conn(int c)
|
||||
{
|
||||
for (;;) {
|
||||
uint8_t hdr[MEOW_IPC_HDR_LEN];
|
||||
if (x_read_full(c, hdr, sizeof hdr) < 0) break;
|
||||
uint32_t magic, rlen, req_id; uint16_t ver, type;
|
||||
memcpy(&magic, hdr, 4); memcpy(&ver, hdr + 4, 2); memcpy(&type, hdr + 6, 2);
|
||||
memcpy(&req_id, hdr + 8, 4); memcpy(&rlen, hdr + 12, 4);
|
||||
if (magic != MEOW_IPC_MAGIC || ver != MEOW_IPC_VERSION) { send_err(c, req_id, MEOW_ERR_INVAL, "bad magic/ver"); break; }
|
||||
if (rlen > MEOW_IPC_MAX) { send_err(c, req_id, MEOW_ERR_INVAL, "payload too big"); break; }
|
||||
uint8_t payload[MEOW_IPC_MAX];
|
||||
if (rlen && x_read_full(c, payload, rlen) < 0) break;
|
||||
if (type == MSG_HELLO) { send_ack(c, req_id, MEOW_OK, 0); continue; }
|
||||
if (type == MSG_PERF_QUERY) { handle_query(c, req_id, payload, rlen); continue; }
|
||||
send_err(c, req_id, MEOW_ERR_INVAL, "unknown type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int run_daemon(const char *sock)
|
||||
{
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
cpu_usage_pct(); /* 预热:设 /proc/stat 基线,首次查询即有值 */
|
||||
int lfd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (lfd < 0) { svc_log("socket fail: %s", strerror(errno)); 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);
|
||||
unlink(sock);
|
||||
if (bind(lfd, (struct sockaddr *)&a, sizeof a) < 0) { svc_log("bind %s fail: %s", sock, strerror(errno)); close(lfd); return 1; }
|
||||
chmod(sock, 0666);
|
||||
if (listen(lfd, 8) < 0) { svc_log("listen fail: %s", strerror(errno)); close(lfd); return 1; }
|
||||
svc_log("meow_svc_perf listening on %s", sock);
|
||||
for (;;) {
|
||||
int c = accept(lfd, NULL, NULL);
|
||||
if (c < 0) { if (errno == EINTR) continue; continue; }
|
||||
handle_conn(c);
|
||||
close(c);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---------------- CLI ---------------- */
|
||||
static int cli_run(int n, char **v)
|
||||
{
|
||||
int subset = 0;
|
||||
if (n >= 1) {
|
||||
if (!strcmp(v[0], "perf")) subset = PERF_SUBSET_PERF;
|
||||
else if (!strcmp(v[0], "system")) subset = PERF_SUBSET_SYSTEM;
|
||||
else if (!strcmp(v[0], "all") || !strcmp(v[0], "-")) subset = 0;
|
||||
else { fprintf(stderr, "usage: meow_svc_perf [all|perf|system]\n"); return 1; }
|
||||
}
|
||||
cpu_usage_pct(); /* 预热基线 */
|
||||
usleep(100000); /* 采样间隔,取 delta */
|
||||
char json[4096];
|
||||
agg_json(subset, json, sizeof json);
|
||||
puts(json);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *cli_usage(void)
|
||||
{
|
||||
return "meow_svc_perf [all|perf|system] (CLI 免鉴权;无参=全部)";
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const char *env = getenv("MEOW_IPC_PERF_SOCK");
|
||||
if (env && env[0]) g_sock = env;
|
||||
int i = 1;
|
||||
for (; i < argc; i++) {
|
||||
if (!strcmp(argv[i], "--sock") && i + 1 < argc) g_sock = argv[++i];
|
||||
else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) { puts(cli_usage()); return 0; }
|
||||
else break;
|
||||
}
|
||||
if (i >= argc) return run_daemon(g_sock); /* svcd 空参 → IPC 常驻 */
|
||||
return cli_run(argc - i, argv + i);
|
||||
}
|
||||
Executable
BIN
Binary file not shown.
Reference in new issue
Block a user