/* am.c — MeowOS 活动管理器用户侧 CLI(AMSD_DESIGN.md §3,安卓习惯,进固件 /usr/bin/am)。 * * 连 amsd sock(--sock/MEOW_AMS_SOCK,默认 /usr/data/meow_ams.sock),发一条命令行, * 读响应直到 EOF,格式化输出。root 免鉴权(读);写操作(start/kill)一期仅 root/系统身份。 * * 子命令: * am status → 在跑 App 表(pkg/状态/pid/运行时长/activity) * am list → 注册表 + 状态(pkg/name/state/pid/launches/crashes) * am history [pkg] → 事件历史(每 App 最近 8 条;无 pkg=全部) * am start [/] → 经 amsd→launcher 拉起 * am kill → 经 amsd→launcher 终止(SIGTERM 语义) * am rescan → 手动触发注册表重扫(卡刚挂载/热装 App 后立即可见) * * 编译(mipsel 静态):zig-mipsel-cc -Os -Wall -Wextra -static am.c -o am * 宿主:gcc -Os -Wall -Wextra am.c -o am_host */ #include #include #include #include #include #include #include #define AMS_SOCK_DEFAULT "/usr/data/meow_ams.sock" #define AMS_SOCK_PATH_MAX 108 #define AMS_RESP_MAX 65536 static const char *g_sock = NULL; static int connect_path(const char *path) { int fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd < 0) { perror("socket"); return -1; } struct sockaddr_un a; memset(&a, 0, sizeof a); a.sun_family = AF_UNIX; snprintf(a.sun_path, sizeof a.sun_path, "%s", path); if (connect(fd, (struct sockaddr *)&a, sizeof a) < 0) { fprintf(stderr, "connect %s: %s\n", path, strerror(errno)); close(fd); return -1; } return fd; } /* 发送单行命令,读取响应直到 EOF,打印。返回 0=ok。 */ static int am_query(const char *cmd) { int fd = connect_path(g_sock); if (fd < 0) return 1; ssize_t w = write(fd, cmd, strlen(cmd)); (void)w; static char resp[AMS_RESP_MAX]; size_t n = 0; char tmp[2048]; ssize_t r; while ((r = read(fd, tmp, sizeof tmp)) > 0) { if (n + (size_t)r < sizeof resp - 1) { memcpy(resp + n, tmp, (size_t)r); n += (size_t)r; } } close(fd); resp[n] = '\0'; fputs(resp, stdout); return 0; } static int cmd_status(void) { return am_query("status\n"); } static int cmd_list(void) { return am_query("list\n"); } static int cmd_history(const char *pkg) { char line[512]; if (pkg && pkg[0]) snprintf(line, sizeof line, "history %s\n", pkg); else snprintf(line, sizeof line, "history\n"); return am_query(line); } static int cmd_start(const char *spec) { if (geteuid() != 0) { printf("ERR permission denied (root only)\n"); return 1; } char line[800]; snprintf(line, sizeof line, "start %s\n", spec); return am_query(line); } static int cmd_resolve(const char *spec) { /* spec: (两参数)。 */ char line[800]; snprintf(line, sizeof line, "resolve %s\n", spec); return am_query(line); } static int cmd_picker(const char *action) { if (geteuid() != 0) { printf("ERR permission denied (root only)\n"); return 1; } char line[800]; snprintf(line, sizeof line, "picker %s\n", action); return am_query(line); } static int cmd_picker_status(const char *reqid) { char line[800]; if (reqid && reqid[0]) snprintf(line, sizeof line, "picker-status %s\n", reqid); else snprintf(line, sizeof line, "picker-status\n"); return am_query(line); } static int cmd_kill(const char *pkg) { if (geteuid() != 0) { printf("ERR permission denied (root only)\n"); return 1; } char line[800]; snprintf(line, sizeof line, "kill %s\n", pkg); return am_query(line); } static int cmd_rescan(void) { return am_query("rescan\n"); } static void usage(const char *a0) { fprintf(stderr, "usage: %s [--sock ] status|list|history [pkg]|start [/]|kill |rescan\n" " %s resolve | picker | picker-status [reqid]\n" " --sock amsd socket (default %s, or $MEOW_AMS_SOCK)\n", a0, a0, AMS_SOCK_DEFAULT); } int main(int argc, char **argv) { const char *env = getenv("MEOW_AMS_SOCK"); g_sock = (env && env[0]) ? env : AMS_SOCK_DEFAULT; const char *cmd = NULL; char spec[768] = ""; /* 子命令后所有位置参数,空格拼接(resolve 需 pkg+act 两参) */ int spec_n = 0; for (int i = 1; i < argc; i++) { const char *a = argv[i]; if (!strcmp(a, "--sock") && i + 1 < argc) g_sock = argv[++i]; else if (!strcmp(a, "-h") || !strcmp(a, "--help")) { usage(argv[0]); return 0; } else if (a[0] == '-') { usage(argv[0]); return 2; } else if (!cmd) { cmd = a; } else { int w = snprintf(spec + spec_n, sizeof spec - spec_n, "%s%s", spec_n ? " " : "", a); if (w < 0) break; spec_n += w; if ((size_t)spec_n >= sizeof spec - 1) break; } } if (!cmd) { usage(argv[0]); return 2; } const char *arg = spec[0] ? spec : NULL; if (!strcmp(cmd, "status")) return cmd_status(); if (!strcmp(cmd, "list")) return cmd_list(); if (!strcmp(cmd, "history")) return cmd_history(arg); if (!strcmp(cmd, "start")) { if (!arg) { fprintf(stderr, "start needs [/]\n"); return 2; } return cmd_start(arg); } if (!strcmp(cmd, "kill")) { if (!arg) { fprintf(stderr, "kill needs \n"); return 2; } return cmd_kill(arg); } if (!strcmp(cmd, "resolve")) { if (!arg) { fprintf(stderr, "resolve needs \n"); return 2; } return cmd_resolve(arg); } if (!strcmp(cmd, "picker")) { if (!arg) { fprintf(stderr, "picker needs \n"); return 2; } return cmd_picker(arg); } if (!strcmp(cmd, "picker-status")) { return cmd_picker_status(arg); } if (!strcmp(cmd, "rescan")) return cmd_rescan(); usage(argv[0]); return 2; }