- 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回退
746 lines
26 KiB
C
746 lines
26 KiB
C
#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);
|
||
}
|