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

+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.