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:
MeowOS committed 2026-08-30 23:16:12 +08:00
1 parent 898bc176c9
commit 23727cf400
16 files changed
+1559 -41

No files matched your search

+1 -1
View File
@@ -555,7 +555,7 @@ static void run_menu(struct app apps[MAX_APPS], int napps, int state,
* M2.37:阈值接 power.forceoff_ms(默认 8000),强关保持 > 弹菜单上限。 */
if (t - pwr_hold_at >= power_forceoff_ms && !pwr_menu_state) {
if (sim_no_exec) { logmsg("pm: [sim-no-exec] POWER HOLD %dms -> would force poweroff", power_forceoff_ms); }
else { logmsg("pm: POWER HOLD %dms -> force poweroff", power_forceoff_ms); sys_do_reboot(LINUX_REBOOT_CMD_POWER_OFF); }
else { logmsg("pm: POWER HOLD %dms -> force poweroff", power_forceoff_ms); meow_power_action(POWER_SHUTDOWN); }
m_prev_pwr = pwr; continue;
} else if (t - pwr_hold_at >= power_longpress_menu_ms && !pwr_menu_state) {
pwr_menu_state = 1;
+56
View File
@@ -52,6 +52,10 @@ enum {
MSG_ACT_BACK = 0x0034, /* ↑ 上报回到根/无 activity(清 keep_awake,M2.17) */
MSG_NAV = 0x0040, /* ↑ 上报导航/请求退出 */
MSG_SHOW_ACTIVITY= 0x0041, /* ↓ 令前端应用显示某屏 */
MSG_PERF_QUERY = 0x0050, /* ↑ 性能/系统聚合查询(专项服务 meow_svc_perf) */
MSG_PERF_RESULT = 0x0051, /* ↓ 聚合 JSON(perf/system 子集) */
MSG_POWER_ACTION = 0x0060, /* ↑ 电源/系统动作信号(专项服务 meow_svc_power) */
MSG_POWER_STATUS = 0x0061, /* ↓ 动作受理/结果 + 状态文本 */
MSG_SYS = 0x00F0 /* ↓ 系统事件(守护关闭/服务死亡/播放器状态) */
};
@@ -165,4 +169,56 @@ typedef struct m_notify_progress {
int32_t progress; /* 0-100;<0=清除进度条 */
} m_notify_progress_t; /* 8 B */
/* ================= 专项服务 IPC(SYSTEM_FRAMEWORK_SERVICES §2.2/§2.3)= */
/* ---- MSG_PERF_QUERY 子集位(0=全部)---- */
#define PERF_SUBSET_PERF (1u << 0) /* cpu/mem/uptime/temp/storage/tfcard */
#define PERF_SUBSET_SYSTEM (1u << 1) /* system(version/hostname/kernel/arch) */
/* MSG_PERF_QUERY ↑:性能/系统聚合查询(服务端 meow_svc_perf) */
typedef struct m_perf_query {
uint32_t subset; /* 0=全部;PERF_SUBSET_* 组合 */
} m_perf_query_t; /* 4 B */
/* MSG_PERF_RESULT ↓:聚合 JSON(payload 为 NUL 结尾 JSON 文本) */
typedef struct m_perf_result {
uint32_t code; /* MEOW_OK / MEOW_ERR_* */
uint32_t subset; /* 回显请求 subset */
uint16_t json_len; /* json 有效字节(不含 NUL) */
uint16_t pad;
char json[MEOW_IPC_MAX - 12]; /* 聚合 JSON(.data 形状,§2.2) */
} m_perf_result_t;
/* ---- POWER_* 动作枚举(m_power_action_t.cmd;与 CLI §2.4 同源)---- */
enum {
POWER_SHUTDOWN = 0, /* 关机 */
POWER_REBOOT = 1, /* 重启回 MeowOS(强制写 boot_default=0) */
POWER_UPGRADE = 2, /* 写 mtd5=ota:kernel2 进升级模式 */
POWER_BACK_HIBY = 3, /* 写 boot_default=1 + reboot(回 HiBy) */
POWER_SET_DEFAULT_MEOW = 4, /* 只写 boot_default=0,不重启 */
POWER_SET_DEFAULT_HIBY = 5, /* 只写 boot_default=1,不重启 */
POWER_STATUS = 6, /* 综合状态查询 */
POWER_BATTERY = 7, /* 电池查询 */
POWER_REBOOT_REASON = 8, /* 上次重启原因 */
POWER_UPGRADE_STATUS = 9, /* 升级标志(mtd5=ota:kernel2?) */
POWER_SCREEN = 10, /* 屏幕电源:flag 0=off/1=on/2=timeout(秒取 arg) */
POWER_DAC = 11 /* DAC:flag 0=off/1=on/2=status */
};
/* MSG_POWER_ACTION ↑:电源/系统动作信号(服务端 meow_svc_power) */
typedef struct m_power_action {
uint32_t cmd; /* POWER_* 枚举 */
uint32_t flag; /* 随 cmd;POWER_SCREEN/DAC 用 */
char arg[32]; /* 可选文本参数(如 screen timeout 秒数字符串) */
} m_power_action_t; /* 40 B */
/* MSG_POWER_STATUS ↓:动作受理/结果 + 状态文本(CLI/IPC 展示) */
typedef struct m_power_status {
uint32_t code; /* MEOW_OK / MEOW_ERR_PERM / MEOW_ERR_INVAL / MEOW_ERR_NODEV */
uint32_t cmd; /* 回显 cmd */
uint16_t msg_len; /* msg 有效字节(不含 NUL) */
uint16_t pad;
char msg[96]; /* 结果/状态文本 */
} m_power_status_t;
#endif /* MEOW_IPC_H */
@@ -624,7 +624,9 @@ void settings_volume_adj(int delta);
int settings_volume_get(void);
int show_power_menu(void);
int stop_player(void);
void sys_do_reboot(int cmd);
/* 电源管理服务 IPC(SYSTEM_FRAMEWORK_SERVICES §3.2:launcher 电源动作经 meow_svc_power 执行) */
int pm_power_ipc(uint32_t cmd, uint32_t flag, const char *arg, char *res, int reslen);
int meow_power_action(uint32_t cmd);
void ui_draw(int x, int y, const char *s, uint32_t color, int size);
int ui_width(const char *s, int size);
void unblank_panel(void);
Binary file not shown.
+1 -1
View File
@@ -930,7 +930,7 @@ void run_notif_center(struct app *apps, int napps, int *want_launch,
if (pwr && pwr_hold_at) {
if (now - pwr_hold_at >= power_forceoff_ms && !pwr_menu_state) {
if (sim_no_exec) logmsg("notif-center pm: [sim-no-exec] hold %dms -> would forceoff", power_forceoff_ms);
else { logmsg("notif-center pm: hold %dms -> force poweroff", power_forceoff_ms); sys_do_reboot(LINUX_REBOOT_CMD_POWER_OFF); }
else { logmsg("notif-center pm: hold %dms -> force poweroff", power_forceoff_ms); meow_power_action(POWER_SHUTDOWN); }
} else if (now - pwr_hold_at >= power_longpress_menu_ms && !pwr_menu_state) {
pwr_menu_state = 1;
logmsg("notif-center pm: hold %dms -> power menu", power_longpress_menu_ms);
+129 -36
View File
@@ -70,25 +70,102 @@ static void draw_pm_button(int i, int selected)
}
}
void sys_do_reboot(int cmd)
/* ---- 电源管理服务 IPC 客户端(SYSTEM_FRAMEWORK_SERVICES §3.2:launcher 经服务触发电源动作)----
* launcher 不再直连 reboot(2)syscall/写 mtd5/写 boot 默认 —— 统一经 meow_svc_power 的
* `/usr/data/meow_power.sock`(`MEOW_IPC_POWER_SOCK` 可覆盖)发 MSG_POWER_ACTION。 */
#define PW_SOCK_DEFAULT "/usr/data/meow_power.sock"
static int pw_write_all(int fd, const uint8_t *b, size_t n)
{
/* M2a:正常电源事件(关机/重启)触发清除板载通知数据之前,先转储所有未上卡通知
* (NOTIFICATION_SYSTEM.md §8)。meow_notif_before_poweroff 幂等安全(未 init 直接返回)。 */
meow_notif_before_poweroff();
sync(); /* 先落盘,避免数据丢失 */
logmsg("sys_do_reboot cmd=0x%x", cmd);
syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, cmd, NULL);
logmsg("sys_do_reboot returned: %s", strerror(errno)); /* 正常不会返回 */
for (;;) usleep(1000); /* 防御:永不返回 */
size_t off = 0;
while (off < n) { ssize_t w = write(fd, b + off, n - off); if (w <= 0) return -1; off += (size_t)w; }
return 0;
}
static int pw_read_all(int fd, uint8_t *b, size_t n)
{
size_t off = 0;
while (off < n) { ssize_t r = read(fd, b + off, n - off); if (r <= 0) return -1; off += (size_t)r; }
return 0;
}
/* 发一帧并等应答(读超时 1.5s,服务不可达/无响应不至于卡死菜单)。 */
static int pw_xchg(int fd, uint16_t type, uint32_t req, const void *pl, uint32_t len,
uint8_t *rbuf, uint32_t rcap, uint16_t *rtype)
{
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, 4); memcpy(hdr + 12, &rlen, 4);
if (pw_write_all(fd, hdr, sizeof hdr) < 0) return -1;
if (len && pl && pw_write_all(fd, pl, len) < 0) return -1;
struct pollfd pf = { fd, POLLIN, 0 };
int pr = poll(&pf, 1, 1500);
if (pr <= 0) return -1;
uint8_t rhdr[MEOW_IPC_HDR_LEN];
if (pw_read_all(fd, rhdr, sizeof rhdr) < 0) return -1;
uint32_t rmagic, rlen2; uint16_t rver, rt;
memcpy(&rmagic, rhdr, 4); memcpy(&rver, rhdr + 4, 2); memcpy(&rt, rhdr + 6, 2);
memcpy(&rlen2, rhdr + 12, 4);
if (rmagic != MEOW_IPC_MAGIC || rver != MEOW_IPC_VERSION) return -1;
if (rlen2 > rcap) rlen2 = rcap;
if (rlen2 && pw_read_all(fd, rbuf, rlen2) < 0) return -1;
if (rtype) *rtype = rt;
return 0;
}
/* 回HibyOS:写板载 /data/meow_boot_default=1(开机默认权威标记,O_TRUNC 单值、不写 TF 卡),
* 再 reboot 回选单并默认 HibyOS。meow.conf 不再写/承载 boot_default(bootmenu 以板载标记为准)。 */
static void set_boot_default_hiby(void)
/* 经电源服务执行动作。返回 MEOW_OK 或错误码;res/reslen 可收状态文本。 */
int pm_power_ipc(uint32_t cmd, uint32_t flag, const char *arg, char *res, int reslen)
{
int fd = open("/data/meow_boot_default", O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd >= 0) { write(fd, "1\n", 2); close(fd); logmsg("pm: /data/meow_boot_default=1"); }
const char *sock = getenv("MEOW_IPC_POWER_SOCK");
if (!sock || !sock[0]) sock = PW_SOCK_DEFAULT;
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd < 0) return MEOW_ERR_IO;
struct sockaddr_un a;
memset(&a, 0, sizeof a);
a.sun_family = AF_UNIX;
strncpy(a.sun_path, sock, sizeof a.sun_path - 1);
if (connect(fd, (struct sockaddr *)&a, sizeof a) < 0) { close(fd); return MEOW_ERR_IO; }
m_hello_t h;
memset(&h, 0, sizeof h);
snprintf(h.app_id, sizeof h.app_id, "%s", "os.meow.launcher");
h.version = 1;
uint8_t rbuf[256]; uint16_t rtype = 0;
if (pw_xchg(fd, MSG_HELLO, 0x100, &h, sizeof h, rbuf, sizeof rbuf, &rtype) < 0 || rtype != MSG_ACK) {
close(fd); return MEOW_ERR_IO;
}
m_ack_t ak;
memcpy(&ak, rbuf, sizeof ak);
if (ak.code != MEOW_OK) { close(fd); return (int)ak.code; }
m_power_action_t pa;
memset(&pa, 0, sizeof pa);
pa.cmd = cmd;
pa.flag = flag;
if (arg) snprintf(pa.arg, sizeof pa.arg, "%s", arg);
if (pw_xchg(fd, MSG_POWER_ACTION, 0x101, &pa, sizeof pa, rbuf, sizeof rbuf, &rtype) < 0 || rtype != MSG_POWER_STATUS) {
close(fd); return MEOW_ERR_IO;
}
m_power_status_t st;
memcpy(&st, rbuf, sizeof st);
close(fd);
if (res && reslen > 0) {
uint16_t ml = st.msg_len;
if (ml >= (uint16_t)reslen) ml = (uint16_t)(reslen - 1);
memcpy(res, st.msg, ml);
res[ml] = 0;
}
return (int)st.code;
}
/* launcher 侧统一电源动作入口:先转储未上卡通知(进程本地,见 §3.2 定案),再经服务 IPC 执行。
* 返回 MEOW_OK=已受理(服务将执行,通常随即 reboot/poweroff);非 OK=服务不可达/拒绝,调用方应
* 稳妥处理(如菜单按「取消」返回,绝不误报动作成功导致 launcher 退出卡死)。 */
int meow_power_action(uint32_t cmd)
{
meow_notif_before_poweroff(); /* 幂等安全(未 init 直接返回) */
int rc = pm_power_ipc(cmd, 0, NULL, NULL, 0);
if (rc != MEOW_OK)
logmsg("meow_power_action cmd=%u FAILED rc=%d (power service unavailable?)", cmd, rc);
return rc;
}
@@ -225,8 +302,12 @@ int show_power_menu(void)
/* 8s 强关(绕过):仅当电源持续按住、且用户全程未与菜单互动才触发 —— 放在触摸/按键处理
* 之后,避免「用户已点选按钮」的同一轮被强关抢先(M2.10 Bug)。 */
if (pwr && !pm_interacted && now_ms() - pm_t0 >= 5000) {
if (sim_no_exec) { logmsg("pm: [sim-no-exec] POWER HOLD 8s -> would force poweroff"); }
else { logmsg("pm: POWER HOLD 8s -> force poweroff"); sys_do_reboot(LINUX_REBOOT_CMD_POWER_OFF); }
if (sim_no_exec) { logmsg("pm: [sim-no-exec] POWER HOLD 8s -> would force poweroff"); return 1; }
logmsg("pm: POWER HOLD 8s -> force poweroff");
if (meow_power_action(POWER_SHUTDOWN) != MEOW_OK) {
logmsg("pm: power service unavailable, abort force-off (menu closed, no action)");
return 0;
}
return 1;
}
@@ -247,35 +328,44 @@ int show_power_menu(void)
}
if (tfd >= 0) close(tfd);
/* M2.11 B:电源键在菜单 = 按当前项确认。act 与 syscall 类型逐项核对:
* act==4(取消) -> 只关菜单,绝不关机(return 0,永不到 reboot);
* act==0(关机) -> 才 poweroff;act==1(重启)/act==2(回HibyOS)/act==3(升级模式) -> 才 reboot。
* 只有 act 为 0/1/2/3 才可能执行 sys_do_reboot;power 键本身绝不触发关机。 */
/* M2.11 B:电源键在菜单 = 按当前项确认。act 与电源动作逐项核对:
* act==4(取消) -> 只关菜单,绝不关机(return 0,永不到电源动作);
* act==0(关机)/act==1(重启)/act==2(回HibyOS)/act==3(升级模式) -> 才经服务 IPC 执行电源动作。
* 只有 act 为 0/1/2/3 才可能发电源动作;power 键本身绝不触发关机。 */
logmsg("pm: act=%d -> %s", act,
act == 0 ? "关机" : act == 1 ? "重启" : act == 2 ? "回HibyOS" : act == 3 ? "升级模式" : "取消");
if (act == 4) { logmsg("pm: cancel (close menu only)"); return 0; }
/* 动作统一经电源管理服务 IPC(meow_svc_power);launcher 不再直连 poweroff/reboot(2)/写 mtd5/
* 写 boot 默认——关机/重启/升级/回HiBy 的动作原语都在服务内实现(§3.2 单一动作链路)。 */
/* 公共服务:执行动作;服务不可达 → 按「取消」返回(launcher 存活,绝不误报已动作卡死)。
* meow_power_action 内部已先 meow_notif_before_poweroff()(转储未上卡通知)。 */
if (act == 0) {
logmsg("pm: POWER OFF");
if (!sim_no_exec) {
/* 先显关机 logo(cmd_jpeg_display 是独立二进制,能正常显图),再直连 poweroff */
system("PATH=/bin:/sbin:/usr/bin:/usr/sbin cmd_jpeg_display /etc/shutdown_logo.jpeg >/dev/null 2>&1");
sys_do_reboot(LINUX_REBOOT_CMD_POWER_OFF);
if (sim_no_exec) { logmsg("pm: [sim-no-exec] would poweroff"); return 1; }
if (meow_power_action(POWER_SHUTDOWN) != MEOW_OK) {
logmsg("pm: power service unavailable, abort (menu closed, no action)");
return 0;
}
} else if (act == 1) {
logmsg("pm: REBOOT");
if (!sim_no_exec) sys_do_reboot(LINUX_REBOOT_CMD_RESTART);
if (sim_no_exec) { logmsg("pm: [sim-no-exec] would reboot"); return 1; }
if (meow_power_action(POWER_REBOOT) != MEOW_OK) {
logmsg("pm: power service unavailable, abort (menu closed, no action)");
return 0;
}
} else if (act == 2) {
logmsg("pm: RETURN TO HIBYOS");
set_boot_default_hiby();
if (!sim_no_exec) sys_do_reboot(LINUX_REBOOT_CMD_RESTART);
if (sim_no_exec) { logmsg("pm: [sim-no-exec] would reboot to hiby"); return 1; }
if (meow_power_action(POWER_BACK_HIBY) != MEOW_OK) {
logmsg("pm: power service unavailable, abort (menu closed, no action)");
return 0;
}
} else if (act == 3) {
/* M2.29 升级模式:写 mtd5=ota:kernel2(bootmode.sh Recovery = flash_erase + nandwrite)
* 再 reboot —— uboot 读 mtd5=ota:kernel2 引导 kernel2(升级模式,自动刷 TF 卡
* r3proii.upt,双进度条,跑完重启进新固件)。已验证此链路可靠。 */
logmsg("pm: ENTER UPGRADE");
if (!sim_no_exec) {
system("PATH=/bin:/sbin:/usr/bin:/usr/sbin /usr/bin/bootmode.sh Recovery >/dev/null 2>&1");
sys_do_reboot(LINUX_REBOOT_CMD_RESTART);
if (sim_no_exec) { logmsg("pm: [sim-no-exec] would enter upgrade"); return 1; }
if (meow_power_action(POWER_UPGRADE) != MEOW_OK) {
logmsg("pm: power service unavailable, abort (menu closed, no action)");
return 0;
}
}
return 1;
@@ -396,8 +486,11 @@ int confirm_reboot_player(void)
if (tfd >= 0) close(tfd);
if (act == 0) {
logmsg("reboot-to-player: YES -> default HibyOS + reboot");
set_boot_default_hiby();
if (!sim_no_exec) sys_do_reboot(LINUX_REBOOT_CMD_RESTART);
if (sim_no_exec) { logmsg("reboot-to-player: [sim-no-exec] would reboot to hiby"); return 1; }
if (meow_power_action(POWER_BACK_HIBY) != MEOW_OK) {
logmsg("reboot-to-player: power service unavailable, abort (stay in MeowOS)");
return 0;
}
return 1;
}
logmsg("reboot-to-player: cancel");
+1 -1
View File
@@ -1045,7 +1045,7 @@ void run_settings_page(void)
* 时触发;开过菜单则交给菜单内确认/取消,杜绝「开菜单->取消->仍按住->强关」残留。 */
if (now - pwr_hold_at >= power_forceoff_ms && !pwr_menu_state) {
if (sim_no_exec) { logmsg("settings pm: [sim-no-exec] POWER HOLD %dms -> would force poweroff", power_forceoff_ms); }
else { logmsg("settings pm: POWER HOLD %dms -> force poweroff", power_forceoff_ms); sys_do_reboot(LINUX_REBOOT_CMD_POWER_OFF); }
else { logmsg("settings pm: POWER HOLD %dms -> force poweroff", power_forceoff_ms); meow_power_action(POWER_SHUTDOWN); }
/* 关机一般不复返;若 reboot 失败返回仍按按住处理(m_prev 由下方统一复写) */
} else if (now - pwr_hold_at >= power_longpress_menu_ms && !pwr_menu_state) {
pwr_menu_state = 1;
+29
View File
@@ -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
Binary file not shown.
+523
View File
@@ -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);
}
Binary file not shown.
+29
View File
@@ -0,0 +1,29 @@
#!/bin/bash
# build_svc_power.sh — meow_svc_power 交叉编译(mipsel 静态)+ 宿主编译可选。
#
# 目标:R3 Pro II / MeowOS(mipsel-linux-muslsf,静态)。电源服务仅 POSIX + Linux 基接口
# (复用 meow_ipc.h 帧格式,无 launcher 依赖),单文件满足「固件内、不入卡」(<200KB 阈值)。
#
# 用法:
# ./build_svc_power.sh # 交叉编译 → patch_v2/meow_svc_power/meow_svc_power(mipsel 静态)
# ./build_svc_power.sh host # 宿主编译 → patch_v2/meow_svc_power/meow_svc_power_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(帧/SACK/POWER_*/m_power_*)
if [ "$1" = "host" ]; then
HOST_CC="${CC_HOST:-cc}"
"$HOST_CC" -Os -Wall -Wextra -I"$I_HDR" meow_svc_power.c -o meow_svc_power_host
echo "== host build =="
ls -la meow_svc_power_host
file meow_svc_power_host
exit 0
fi
"$CC" -Os -Wall -Wextra -static -I"$I_HDR" meow_svc_power.c -o meow_svc_power
echo "== meow_svc_power (mipsel static) =="
ls -la meow_svc_power
md5sum meow_svc_power
file meow_svc_power
Binary file not shown.
+745
View File
@@ -0,0 +1,745 @@
#define _GNU_SOURCE /* 启用 struct ucred / SO_PEERCRED、asprintf 等 GNU/musl 扩展 */
/* meow_svc_power.c — MeowOS 电源管理专项子服务(SYSTEM_FRAMEWORK_SERVICES §1.2/§2.3/§2.4)。
*
* 单一二进制双形态:
* - svcd 以**空参**拉起(§1.3 conf `args=` 空)→ IPC 服务常驻:bind `/usr/data/meow_power.sock`
* (`--sock`/`MEOW_IPC_POWER_SOCK` 可覆盖),收 MSG_HELLO→MSG_ACK、MSG_POWER_ACTION→MSG_POWER_STATUS。
* - root shell `meow_svc_power <cmd>` → 一次性 CLI(免鉴权)执行对应动作(不常驻)。
*
* 动作逻辑与 launcher `power_menu.c` 的 `sys_do_reboot`/`set_boot_default_hiby`/`bootmode.sh Recovery`
* **同源 0-diff 搬移**,仅入口(CLI vs IPC)与鉴权层不同。
*
* 鉴权(一期基础校验):IPC 服务端受理 MSG_POWER_ACTION 前以 `SO_PEERCRED` 校验调用方 `uid==0`(root)
* ——非 root 直接 `MEOW_ERR_PERM` 拒绝;完整 `os.meow.*` 包名鉴权(§5)留二期。CLI=root 免鉴权。
*
* 通知转储归属(§3.2/§3.3 已定案):通知存储为 launcher 进程内存(meow_notif.c g_slots[]),服务跨进程
* 不可达——**转储由调用方 launcher 先做**,服务对关机/重启/升级一律 `sync()` 落盘,不重实现转储。
*
* 编译(mipsel 静态):build_svc_power.sh;宿主编译 build_svc_power.sh host。
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.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 <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include "meow_ipc.h" /* 帧格式/消息号 + POWER_* 枚举 + m_power_* 负载(与 launcher 同源) */
/* ---- 路径常量(与 launcher 同源)---- */
#define FB_DEV "/dev/fb0"
#define FBIOBLANK 0x4611
#define BACKLIGHT "/sys/class/backlight/backlight_pwm0/brightness"
#define BACKLIGHT_POWER "/sys/class/backlight/backlight_pwm0/bl_power"
#define BOOT_DEFAULT "/data/meow_boot_default" /* 0=MeowOS(默认),1=HibyOS */
#define COLD_BOOT "/data/meow_cold_boot" /* 冷启标记:1=上次关机,0=上次重启 */
#define MTD_OTA "/dev/mtd5" /* ota 标记分区(8M) */
#define SETTINGS_FILE "/usr/data/meow/settings.conf"
#define SETTINGS_HDR "# meow-settings 1"
#define VERSION_FILE "/etc/meowos_version"
#define BATT_CAP "/sys/class/power_supply/battery/capacity"
#define BATT_STATUS "/sys/class/power_supply/battery/status"
#define BATT_VOLT "/sys/class/power_supply/battery/voltage_now"
#define DAC_NUMID "amixer -c 0 cget numid=%d 2>/dev/null"
#define LINUX_REBOOT_MAGIC1 0xfee1dead
#define LINUX_REBOOT_MAGIC2 672274793 /* 0x28121969 */
#define LINUX_REBOOT_CMD_RESTART 0x01234567
#define LINUX_REBOOT_CMD_POWER_OFF 0x4321fedc
#define SV_PIPE_MAX 4096 /* popen_read_tmo 上限 */
#define SV_TIMEOUT_MS 1500 /* popen 子命令读超时 */
static const char *g_sock = "/usr/data/meow_power.sock";
/* ---------------- 极简日志(stderr;svcd supervisor 捕获) ---------------- */
static void svc_log(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
fprintf(stderr, "[meow_svc_power] ");
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
va_end(ap);
}
/* ---------------- 文件小工具 ---------------- */
/* 读整个文本文件到 buf(NUL 结尾),返回字节数(不含 NUL);失败 -1。 */
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;
}
/* 读 int(atoi 首值);失败默认 def。 */
static int read_file_int_def(const char *path, int def)
{
char buf[32];
if (read_file_txt(path, buf, sizeof buf) <= 0) return def;
char *p = buf;
while (*p == ' ' || *p == '\t') p++;
int v = atoi(p);
return v;
}
static int write_file_str(const char *path, const char *s, mode_t mode)
{
int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, mode);
if (fd < 0) return -1;
size_t l = strlen(s);
ssize_t w = write(fd, s, l);
int ok = (w == (ssize_t)l) ? 0 : -1;
close(fd);
return ok;
}
/* 读 settings.conf 某键值(key=value,行首忽略空白),找不到→def。 */
static int settings_get_int(const char *key, int def)
{
FILE *f = fopen(SETTINGS_FILE, "r");
if (!f) return def;
char line[256];
int val = def, found = 0;
size_t klen = strlen(key);
while (fgets(line, sizeof line, f)) {
char *p = line;
while (*p == ' ' || *p == '\t') p++;
if (strncmp(p, key, klen) == 0 && p[klen] == '=') {
val = atoi(p + klen + 1);
found = 1;
break;
}
}
fclose(f);
return found ? val : def;
}
/* 更新/插入 settings.conf 某键(保留头,O_TRUNC 重写)。成功 0。 */
static int settings_set_int(const char *key, int val)
{
FILE *f = fopen(SETTINGS_FILE, "r");
char tmp[1024];
size_t klen = strlen(key);
int found = 0;
if (!f) {
/* 无文件 → 新建头 + 键 */
FILE *nf = fopen(SETTINGS_FILE, "w");
if (!nf) return -1;
fprintf(nf, "%s\n%s=%d\n", SETTINGS_HDR, key, val);
fclose(nf);
return 0;
}
/* 先把原文件读入 tmp(限 1KB,settings 为 KB 级小文件) */
size_t n = 0;
int c;
while (n < sizeof tmp - 1 && (c = fgetc(f)) != EOF) tmp[n++] = (char)c;
fclose(f);
tmp[n] = 0;
FILE *nf = fopen(SETTINGS_FILE, "w");
if (!nf) return -1;
/* 逐行写:头照抄;命中 key 行替换 */
char *save = NULL;
for (char *ln = strtok_r(tmp, "\n", &save); ln; ln = strtok_r(NULL, "\n", &save)) {
char *p = ln;
while (*p == ' ' || *p == '\t') p++;
if (strncmp(p, key, klen) == 0 && p[klen] == '=') {
fprintf(nf, "%s=%d\n", key, val);
found = 1;
} else {
fprintf(nf, "%s\n", ln);
}
}
if (!found) fprintf(nf, "%s=%d\n", key, val);
fclose(nf);
return 0;
}
/* ---------------- 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(SV_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 < SV_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, SV_PIPE_MAX - 1 - off);
if (rd == 0) break; /* EOF(子进程退出/关写端) */
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;
}
/* 前置声明:CLI 用法串(cli_run 用到)。 */
static const char *cli_usage(void);
/* ---------------- reboot / 关机 syscall ---------------- */
static void sys_reboot_do(int cmd)
{
/* 与 power_menu.c sys_do_reboot 同源:sync() 落盘 → reboot(2) syscall → 永不返回。 */
sync();
svc_log("sys_reboot_do cmd=0x%x", cmd);
syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, cmd, NULL);
svc_log("sys_reboot_do returned: %s", strerror(errno));
for (;;) usleep(1000);
}
/* ---------------- boot 默认 ---------------- */
/* 写 /data/meow_boot_default(0=MeowOS 默认 / 1=HibyOS 默认)。 */
static void write_boot_default(int v)
{
char b[8];
snprintf(b, sizeof b, "%d\n", v);
if (write_file_str(BOOT_DEFAULT, b, 0644) < 0) svc_log("write %s fail: %s", BOOT_DEFAULT, strerror(errno));
else svc_log("boot_default=%d", v);
}
/* ---------------- 关机 logo ---------------- */
static void show_shutdown_logo(void)
{
/* 独立二进制 cmd_jpeg_display 能显图;SSH 无 fb 时重定向无害。 */
int rc = system("PATH=/bin:/sbin:/usr/bin:/usr/sbin cmd_jpeg_display /etc/shutdown_logo.jpeg >/dev/null 2>&1");
(void)rc;
}
/* ---------------- 升级(mtd5=ota:kernel2) ---------------- */
static void enter_upgrade(void)
{
int rc = system("PATH=/bin:/sbin:/usr/bin:/usr/sbin /usr/bin/bootmode.sh Recovery >/dev/null 2>&1");
(void)rc;
}
/* 读 /dev/mtd5 头,命中 "ota:kernel2" → 升级模式已设。 */
static int mtd_ota_is_upgrade(void)
{
int fd = open(MTD_OTA, O_RDONLY);
if (fd < 0) return 0;
char buf[16];
memset(buf, 0, sizeof buf);
ssize_t n = read(fd, buf, sizeof buf - 1);
close(fd);
if (n <= 0) return 0;
return strstr(buf, "ota:kernel2") != NULL;
}
/* ---------------- 有界拼接(printf 截断分析免疫;host/cross 均 0 warning) ---------------- */
static void cat_b(char *dst, size_t cap, const char *src)
{
size_t dl = strlen(dst);
if (dl >= cap - 1) return;
size_t sl = strlen(src);
if (sl > cap - 1 - dl) sl = cap - 1 - dl;
if (sl) memcpy(dst + dl, src, sl);
dst[dl + sl] = 0;
}
/* ---------------- 电池/状态读组(同 util.c/about.c/bootmenu 数据源) ---------------- */
static void battery_fill(char *out, int cap)
{
int pct = read_file_int_def(BATT_CAP, -1);
char st[32] = "";
read_file_txt(BATT_STATUS, st, sizeof st);
int mv = read_file_int_def(BATT_VOLT, -1) / 1000; /* µV → mV */
if (pct < 0) { snprintf(out, cap, "battery=unknown"); return; }
char one[64];
snprintf(one, sizeof one, "battery=%d%%(%s)", pct, st[0] ? st : "?");
out[0] = 0;
cat_b(out, cap, one);
if (mv >= 0) { char more[24]; snprintf(more, sizeof more, " %dmV", mv); cat_b(out, cap, more); }
}
static const char *boot_default_str(void)
{
int v = read_file_int_def(BOOT_DEFAULT, -1);
if (v < 0) return "unknown";
return v ? "hiby" : "meow";
}
static int cold_boot_flag(void) { return read_file_int_def(COLD_BOOT, -1); }
static void status_fill(char *out, int cap)
{
char ver[32] = "-", batt[48] = "";
if (read_file_txt(VERSION_FILE, ver, sizeof ver) <= 0 || !ver[0]) snprintf(ver, sizeof ver, "-");
battery_fill(batt, sizeof batt);
int cold = cold_boot_flag();
char head[96];
snprintf(head, sizeof head, "system=%s boot=%s upgrade=%s coldboot=%s ",
ver, boot_default_str(),
mtd_ota_is_upgrade() ? "yes" : "no",
cold < 0 ? "unknown" : (cold ? "shutdown" : "reboot"));
out[0] = 0;
cat_b(out, cap, head);
cat_b(out, cap, batt);
}
/* ---------------- 屏幕电源(同 launcher keys.c meow_panel_off/on + backlight.c) ---------------- */
static int backlight_write(int val)
{
char b[16];
snprintf(b, sizeof b, "%d", val);
return write_file_str(BACKLIGHT, b, 0644) < 0 ? -1 : 0;
}
static int screen_on(void)
{
int f = open(FB_DEV, O_RDWR);
if (f >= 0) { ioctl(f, FBIOBLANK, 0); close(f); }
int bri = settings_get_int("display.brightness", 60);
int bmin = settings_get_int("display.brightness_min", 1);
if (bri < bmin) bri = bmin;
if (bri > 100) bri = 100;
backlight_write(bri);
svc_log("screen ON (brightness %d)", bri);
return 0;
}
static int screen_off(void)
{
int f = open(FB_DEV, O_RDWR);
if (f >= 0) { ioctl(f, FBIOBLANK, 4); close(f); }
backlight_write(0);
svc_log("screen OFF (blank 4 + brightness 0)");
return 0;
}
/* ---------------- DAC(CS43198) ---------------- */
/* 原机 cs43198 驱动 pwr_gpio=-1:无上电/下电控制点(sysfs/amixer),on/off 不支持。
* status 仅报 amixer numid=1/2(L/R)当前音量上报值。 */
static int dac_status(char *out, int cap)
{
char l[48] = "?", r[48] = "?";
char *o1 = popen_read_tmo(SV_TIMEOUT_MS, "PATH=/bin:/sbin:/usr/bin:/usr/sbin amixer -c 0 cget numid=1 2>/dev/null");
if (o1) {
char *p = strstr(o1, ": values=");
if (p) snprintf(l, sizeof l, "%.40s", p + 9);
free(o1);
}
char *o2 = popen_read_tmo(SV_TIMEOUT_MS, "PATH=/bin:/sbin:/usr/bin:/usr/sbin amixer -c 0 cget numid=2 2>/dev/null");
if (o2) {
char *p = strstr(o2, ": values=");
if (p) snprintf(r, sizeof r, "%.40s", p + 9);
free(o2);
}
out[0] = 0;
cat_b(out, cap, "dac=cs43198 L=");
cat_b(out, cap, l);
cat_b(out, cap, " R=");
cat_b(out, cap, r);
cat_b(out, cap, " powerctl=unsupported(no_gpio)");
return 0;
}
static int dac_cmd(const char *sub, char *out, int cap)
{
if (!sub) return MEOW_ERR_INVAL;
if (!strcmp(sub, "status")) { return dac_status(out, cap); }
if (!strcmp(sub, "on") || !strcmp(sub, "off")) {
snprintf(out, cap, "DAC %s: unsupported (cs43198 pwr_gpio=-1, no on/off control point)", sub);
return MEOW_ERR_NODEV;
}
return MEOW_ERR_INVAL;
}
/* ---------------- 动作执行(CLI + IPC 共用) ----------------
* 返回 0 = 已同步执行并填好 st(调用方直接应答 st 给客户端/CLI 已打印);
* 返回 1 = 系统动作(关机/重启/升级/回HiBy),调用方应「先应答 MEOW_OK 受理,再执行」(执行后通常不返回)。 */
static int execute_action(const m_power_action_t *a, m_power_status_t *st)
{
memset(st, 0, sizeof *st);
st->cmd = a->cmd;
switch (a->cmd) {
case POWER_SHUTDOWN:
sync(); /* 通知转储由 launcher 先做;服务 sync() 落盘 */
st->code = MEOW_OK;
snprintf(st->msg, sizeof st->msg, "shutdown accepted");
return 1; /* 应答后:logo + poweroff(不返回) */
case POWER_REBOOT:
write_boot_default(0); /* 普通重启恒回 MeowOS(无视之前) */
sync();
st->code = MEOW_OK;
snprintf(st->msg, sizeof st->msg, "reboot accepted (boot=meow)");
return 1;
case POWER_UPGRADE:
sync();
st->code = MEOW_OK;
snprintf(st->msg, sizeof st->msg, "upgrade accepted");
return 1;
case POWER_BACK_HIBY:
write_boot_default(1);
sync();
st->code = MEOW_OK;
snprintf(st->msg, sizeof st->msg, "reboot -> hiby accepted");
return 1;
case POWER_SET_DEFAULT_MEOW:
write_boot_default(0);
st->code = MEOW_OK;
snprintf(st->msg, sizeof st->msg, "boot_default=meow (next boot)");
return 0;
case POWER_SET_DEFAULT_HIBY:
write_boot_default(1);
st->code = MEOW_OK;
snprintf(st->msg, sizeof st->msg, "boot_default=hiby (next boot)");
return 0;
case POWER_STATUS:
status_fill(st->msg, sizeof st->msg);
st->code = MEOW_OK;
return 0;
case POWER_BATTERY:
battery_fill(st->msg, sizeof st->msg);
st->code = MEOW_OK;
return 0;
case POWER_REBOOT_REASON:
{ int c = cold_boot_flag();
snprintf(st->msg, sizeof st->msg, "coldboot=%s", c < 0 ? "unknown" : (c ? "shutdown" : "reboot"));
st->code = MEOW_OK; }
return 0;
case POWER_UPGRADE_STATUS:
snprintf(st->msg, sizeof st->msg, "upgrade=%s", mtd_ota_is_upgrade() ? "yes" : "no");
st->code = MEOW_OK;
return 0;
case POWER_SCREEN:
if (a->flag == 0) { screen_off(); st->code = MEOW_OK; snprintf(st->msg, sizeof st->msg, "screen off"); return 0; }
if (a->flag == 1) { screen_on(); st->code = MEOW_OK; snprintf(st->msg, sizeof st->msg, "screen on"); return 0; }
if (a->flag == 2) { /* timeout 秒(arg) */
int sec = a->arg[0] ? atoi(a->arg) : -1;
if (sec < 0 || sec > 86400) { st->code = MEOW_ERR_INVAL;
snprintf(st->msg, sizeof st->msg, "screen timeout: bad sec"); return 0; }
settings_set_int("display.screen_off_timeout", sec);
st->code = MEOW_OK;
snprintf(st->msg, sizeof st->msg, "screen_off_timeout=%ds", sec);
return 0;
}
st->code = MEOW_ERR_INVAL;
snprintf(st->msg, sizeof st->msg, "screen: bad flag");
return 0;
case POWER_DAC:
{ int rc = dac_cmd(a->flag == 2 ? "status" : (a->flag == 1 ? "on" : "off"),
st->msg, sizeof st->msg);
st->code = rc; }
return 0;
default:
st->code = MEOW_ERR_INVAL;
snprintf(st->msg, sizeof st->msg, "unknown cmd %u", a->cmd);
return 0;
}
}
/* 执行「系统动作」(在应答 MEOW_OK 之后调用;通常不返回)。 */
static void run_system_action(uint32_t cmd)
{
switch (cmd) {
case POWER_SHUTDOWN:
show_shutdown_logo();
sys_reboot_do(LINUX_REBOOT_CMD_POWER_OFF);
break;
case POWER_REBOOT:
sys_reboot_do(LINUX_REBOOT_CMD_RESTART);
break;
case POWER_UPGRADE:
enter_upgrade();
sys_reboot_do(LINUX_REBOOT_CMD_RESTART);
break;
case POWER_BACK_HIBY:
sys_reboot_do(LINUX_REBOOT_CMD_RESTART);
break;
default: break;
}
}
/* ---------------- IPC 帧读写(同 meow_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);
e.code = code;
uint16_t ml = msg ? (uint16_t)strlen(msg) : 0;
if (ml > 63) ml = 63;
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_power_status(int fd, uint32_t req_id, const m_power_status_t *st)
{
m_power_status_t s = *st;
uint16_t ml = (uint16_t)strlen(s.msg);
if (ml >= sizeof s.msg) ml = (uint16_t)(sizeof s.msg - 1);
s.msg_len = ml;
return send_frame(fd, MSG_POWER_STATUS, req_id, &s, sizeof s);
}
/* 处理单条连接(连接内循环读帧:客户端先 MSG_HELLO 握手,再 MSG_POWER_ACTION)。 */
static void handle_conn(int c)
{
/* 基础鉴权:调用方 uid==0(root)才受理动作。 */
struct ucred cred;
socklen_t cl = sizeof cred;
int uid0 = 0;
if (getsockopt(c, SOL_SOCKET, SO_PEERCRED, &cred, &cl) == 0 && cred.uid == 0)
uid0 = 1;
for (;;) {
uint8_t hdr[MEOW_IPC_HDR_LEN];
if (x_read_full(c, hdr, sizeof hdr) < 0) break; /* EOF/错误 */
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_POWER_ACTION) {
m_power_action_t a;
memset(&a, 0, sizeof a);
if (rlen > sizeof a) rlen = sizeof a;
memcpy(&a, payload, rlen);
if (!uid0) {
m_power_status_t st;
memset(&st, 0, sizeof st);
st.code = MEOW_ERR_PERM;
st.cmd = a.cmd;
snprintf(st.msg, sizeof st.msg, "permission denied (uid!=0)");
send_power_status(c, req_id, &st);
continue;
}
m_power_status_t st;
int act = execute_action(&a, &st);
if (act == 0) { send_power_status(c, req_id, &st); continue; }
/* 系统动作:先应答受理(客户端不阻塞),再执行(常不返回) */
send_power_status(c, req_id, &st);
run_system_action(a.cmd);
break;
}
send_err(c, req_id, MEOW_ERR_INVAL, "unknown type");
break;
}
}
static int run_daemon(const char *sock)
{
signal(SIGPIPE, SIG_IGN);
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_power 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)
{
const char *cmd = v[0];
if (!strcmp(cmd, "shutdown")) {
show_shutdown_logo();
sys_reboot_do(LINUX_REBOOT_CMD_POWER_OFF);
return 0; /* 不返回 */
}
if (!strcmp(cmd, "reboot")) {
if (n > 1 && !strcmp(v[1], "--hiby")) { write_boot_default(1); }
else write_boot_default(0); /* 普通重启恒回 MeowOS */
sys_reboot_do(LINUX_REBOOT_CMD_RESTART);
return 0;
}
if (!strcmp(cmd, "upgrade")) {
enter_upgrade();
sys_reboot_do(LINUX_REBOOT_CMD_RESTART);
return 0;
}
if (!strcmp(cmd, "set-default-hiby")) {
write_boot_default(1);
printf("boot_default=hiby (next boot)\n");
return 0;
}
if (!strcmp(cmd, "set-default-meow")) {
write_boot_default(0);
printf("boot_default=meow (next boot)\n");
return 0;
}
if (!strcmp(cmd, "status")) {
char out[128];
status_fill(out, sizeof out);
printf("%s\n", out);
return 0;
}
if (!strcmp(cmd, "battery")) {
char out[64];
battery_fill(out, sizeof out);
printf("%s\n", out);
return 0;
}
if (!strcmp(cmd, "reboot-reason")) {
int c = cold_boot_flag();
printf("coldboot=%s\n", c < 0 ? "unknown" : (c ? "shutdown" : "reboot"));
return 0;
}
if (!strcmp(cmd, "upgrade-status")) {
printf("upgrade=%s\n", mtd_ota_is_upgrade() ? "yes" : "no");
return 0;
}
if (!strcmp(cmd, "screen")) {
if (n < 2) { fprintf(stderr, "screen needs on|off|timeout<秒>\n"); return 1; }
if (!strcmp(v[1], "on")) return screen_on();
if (!strcmp(v[1], "off")) return screen_off();
if (!strcmp(v[1], "timeout")) {
if (n < 3) { fprintf(stderr, "screen timeout needs <秒>\n"); return 1; }
int sec = atoi(v[2]);
if (sec < 0 || sec > 86400) { fprintf(stderr, "screen timeout: bad sec\n"); return 1; }
settings_set_int("display.screen_off_timeout", sec);
printf("screen_off_timeout=%ds\n", sec);
return 0;
}
fprintf(stderr, "screen: bad sub %s\n", v[1]);
return 1;
}
if (!strcmp(cmd, "dac")) {
if (n < 2) { fprintf(stderr, "dac needs on|off|status\n"); return 1; }
char out[160];
int rc = dac_cmd(v[1], out, sizeof out);
printf("%s\n", out);
return rc == MEOW_OK ? 0 : 1;
}
fprintf(stderr, "usage: meow_svc_power <cmd> (%s)\n", cli_usage());
return 1;
}
static const char *cli_usage(void)
{
return "shutdown|reboot[--hiby]|upgrade|set-default-hiby|set-default-meow|"
"status|battery|reboot-reason|upgrade-status|screen on|off|timeout<秒>|dac on|off|status";
}
int main(int argc, char **argv)
{
const char *env = getenv("MEOW_IPC_POWER_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")) {
printf("meow_svc_power (%s)\n", cli_usage());
return 0;
}
else break;
}
if (i >= argc) return run_daemon(g_sock); /* svcd 空参 → IPC 常驻 */
return cli_run(argc - i, argv + i);
}
Binary file not shown.
+42 -1
View File
@@ -7,7 +7,8 @@
# --fork` 会阻塞 rcS(选单后 exec meow_launcherd 或 fork 播放器监督),故任何 S90
# 之后的 init.d(如 S91/S92)**都不会执行**——svcd 必须排在 S90 之前。
# * 本脚本只装「稳定框架/KB级设置」:主机名 + 板载服务配置
# (/usr/data/meow/services.d/,Data 区,首启预置 mdnsd.conf)。
# (/usr/data/meow/services.d/,Data 区,首启预置 mdnsd.conf + meow_svc_perf.conf +
# meow_svc_power.conf —— 后者为框架专项服务,trigger=boot 恒存在即启动)。
# * **svcd 拉起权**:若 meow_sysui(系统进程总监)已运行 → svcd 由 sysui 监管拉起
# (sysui.d/svcd.conf),本脚本仅在其已运行时 `meow_svcd reload` 让 svcd 补载 mdnsd.conf,
# 不重复拉起、不重挂(避免与 sysui 双重监护 svcd)。仅当 **sysui 未运行**时才回退到
@@ -57,6 +58,46 @@ EOF
echo -n "skip mdnsd.conf (/usr/data/meow not writable) "
fi
# 2b) 首启预置板载**专项服务**配置(性能/电源;trigger=boot 恒存在即启动,拔卡保留)。
# perf=on-fail(纯读组,崩即拉起);power=always(系统动作服务应随时可用)。
if [ -d /usr/data/meow ] && [ -w /usr/data/meow ]; then
mkdir -p /usr/data/meow/services.d
if [ -f /usr/data/meow/services.d/meow_svc_perf.conf ]; then
echo -n "perf.conf exists "
else
cat > /usr/data/meow/services.d/meow_svc_perf.conf <<'EOF'
exec=/usr/bin/meow_svc_perf
args=
enabled=1
restart=on-fail
backoff_ms=2000
max_restart=5
trigger=boot
cap=
EOF
chmod 644 /usr/data/meow/services.d/meow_svc_perf.conf
echo -n "provisioned perf.conf "
fi
if [ -f /usr/data/meow/services.d/meow_svc_power.conf ]; then
echo -n "power.conf exists "
else
cat > /usr/data/meow/services.d/meow_svc_power.conf <<'EOF'
exec=/usr/bin/meow_svc_power
args=
enabled=1
restart=always
backoff_ms=2000
max_restart=5
trigger=boot
cap=
EOF
chmod 644 /usr/data/meow/services.d/meow_svc_power.conf
echo -n "provisioned power.conf "
fi
else
echo -n "skip svc confs (/usr/data/meow not writable) "
fi
# 3) svcd 拉起权判归:sysui 运行 → 交给 sysui 监管;否则回退重挂循环。
rm -f /tmp/meow_svcd.stop
sysui_alive=0