diff --git a/patch_v2/meow_launcherd/main_menu.c b/patch_v2/meow_launcherd/main_menu.c index 58e8e42..054b54c 100644 --- a/patch_v2/meow_launcherd/main_menu.c +++ b/patch_v2/meow_launcherd/main_menu.c @@ -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; diff --git a/patch_v2/meow_launcherd/meow_ipc.h b/patch_v2/meow_launcherd/meow_ipc.h index c60a4eb..adc337f 100644 --- a/patch_v2/meow_launcherd/meow_ipc.h +++ b/patch_v2/meow_launcherd/meow_ipc.h @@ -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 */ diff --git a/patch_v2/meow_launcherd/meow_launcher_internal.h b/patch_v2/meow_launcherd/meow_launcher_internal.h index 80e01ab..d724470 100644 --- a/patch_v2/meow_launcherd/meow_launcher_internal.h +++ b/patch_v2/meow_launcherd/meow_launcher_internal.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); diff --git a/patch_v2/meow_launcherd/meow_launcherd b/patch_v2/meow_launcherd/meow_launcherd index 1b40fe4..4a5ee31 100755 Binary files a/patch_v2/meow_launcherd/meow_launcherd and b/patch_v2/meow_launcherd/meow_launcherd differ diff --git a/patch_v2/meow_launcherd/notif_center.c b/patch_v2/meow_launcherd/notif_center.c index 817fb5b..4ae1d35 100644 --- a/patch_v2/meow_launcherd/notif_center.c +++ b/patch_v2/meow_launcherd/notif_center.c @@ -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); diff --git a/patch_v2/meow_launcherd/power_menu.c b/patch_v2/meow_launcherd/power_menu.c index bbe21ce..e9da6aa 100644 --- a/patch_v2/meow_launcherd/power_menu.c +++ b/patch_v2/meow_launcherd/power_menu.c @@ -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"); diff --git a/patch_v2/meow_launcherd/settings_page.c b/patch_v2/meow_launcherd/settings_page.c index a1e99f7..37f77f2 100644 --- a/patch_v2/meow_launcherd/settings_page.c +++ b/patch_v2/meow_launcherd/settings_page.c @@ -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; diff --git a/patch_v2/meow_svc_perf/build_svc_perf.sh b/patch_v2/meow_svc_perf/build_svc_perf.sh new file mode 100755 index 0000000..703a888 --- /dev/null +++ b/patch_v2/meow_svc_perf/build_svc_perf.sh @@ -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 diff --git a/patch_v2/meow_svc_perf/meow_svc_perf b/patch_v2/meow_svc_perf/meow_svc_perf new file mode 100755 index 0000000..56ab422 Binary files /dev/null and b/patch_v2/meow_svc_perf/meow_svc_perf differ diff --git a/patch_v2/meow_svc_perf/meow_svc_perf.c b/patch_v2/meow_svc_perf/meow_svc_perf.c new file mode 100644 index 0000000..8435687 --- /dev/null +++ b/patch_v2/meow_svc_perf/meow_svc_perf.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 第 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); +} diff --git a/patch_v2/meow_svc_perf/meow_svc_perf_host b/patch_v2/meow_svc_perf/meow_svc_perf_host new file mode 100755 index 0000000..02dfae7 Binary files /dev/null and b/patch_v2/meow_svc_perf/meow_svc_perf_host differ diff --git a/patch_v2/meow_svc_power/build_svc_power.sh b/patch_v2/meow_svc_power/build_svc_power.sh new file mode 100755 index 0000000..747c0ce --- /dev/null +++ b/patch_v2/meow_svc_power/build_svc_power.sh @@ -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 diff --git a/patch_v2/meow_svc_power/meow_svc_power b/patch_v2/meow_svc_power/meow_svc_power new file mode 100755 index 0000000..f0cc390 Binary files /dev/null and b/patch_v2/meow_svc_power/meow_svc_power differ diff --git a/patch_v2/meow_svc_power/meow_svc_power.c b/patch_v2/meow_svc_power/meow_svc_power.c new file mode 100644 index 0000000..ba362a5 --- /dev/null +++ b/patch_v2/meow_svc_power/meow_svc_power.c @@ -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 ` → 一次性 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 (%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); +} diff --git a/patch_v2/meow_svc_power/meow_svc_power_host b/patch_v2/meow_svc_power/meow_svc_power_host new file mode 100755 index 0000000..0fa93b9 Binary files /dev/null and b/patch_v2/meow_svc_power/meow_svc_power_host differ diff --git a/patch_v2/meow_svcd/S89meow_svcd b/patch_v2/meow_svcd/S89meow_svcd index 9738e07..1721f16 100755 --- a/patch_v2/meow_svcd/S89meow_svcd +++ b/patch_v2/meow_svcd/S89meow_svcd @@ -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