Files
MeowOS-For-R3P2/patch_v2/meow_svc_power/meow_svc_power.c
T
MeowOS 23727cf400 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回退
2026-08-30 23:16:12 +08:00

746 lines
26 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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);
}