feat(amsd): meow_amsd 活动管理器一期 + am CLI (agent-212, 用户定案) — svcd托管(trigger=boot); 生命周期注册表(状态/时长/退出码/信号/崩溃计数+历史环形); launcher launch/exit/stdout管道捕获上报(logcat式,崩溃尾巴drain不丢); am status/list/history/start/kill(root门); crash双保险转发; S89预置conf
amsd f8d7f61b / am cfc4cbd4 / launcher 97c1b222(含字库扫描签名门控修CPU100%) 宿主+qemu全链路验证通过
This commit is contained in:
1 parent
b81e7438e4
commit
5fbf30755c
16 files changed
+1722
-4
No files matched your search
@@ -10,6 +10,189 @@ int g_naccts = 0;
|
||||
volatile int g_app_session_active = 0; /* §10.5:1=launch_app waitpid 前台会话中(util.c 读) */
|
||||
|
||||
|
||||
/* ---- amsd 上报 / 控制挂点(AMSD_DESIGN §4):best-effort,amsd 不在不影响 launcher ---- */
|
||||
#define MEOW_AMS_SOCK_DEFAULT "/usr/data/meow_ams.sock"
|
||||
|
||||
/* amsd socket 路径:运行时读 env MEOW_AMS_SOCK(宿主/qemu 覆写),缺省用默认。 */
|
||||
static const char *ams_sock_path(void)
|
||||
{
|
||||
static char p[108];
|
||||
static int once = 0;
|
||||
if (!once) {
|
||||
const char *e = getenv("MEOW_AMS_SOCK");
|
||||
snprintf(p, sizeof p, "%s", (e && e[0]) ? e : MEOW_AMS_SOCK_DEFAULT);
|
||||
once = 1;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/* up 秒(/proc/uptime 首字段)。失败 0。 */
|
||||
static long ams_uptime(void)
|
||||
{
|
||||
FILE *f = fopen("/proc/uptime", "r");
|
||||
if (!f) return 0;
|
||||
char b[64];
|
||||
long up = 0;
|
||||
if (fgets(b, sizeof b, f)) up = (long)atof(b);
|
||||
fclose(f);
|
||||
return up;
|
||||
}
|
||||
|
||||
/* 向 amsd 发一条多行负载:非阻塞 connect + 单次 write + close;失败静默丢。 */
|
||||
static void ams_send(const char *payload, int len)
|
||||
{
|
||||
if (!payload || len <= 0) return;
|
||||
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (fd < 0) return;
|
||||
int fl = fcntl(fd, F_GETFL, 0);
|
||||
if (fl >= 0) fcntl(fd, F_SETFL, fl | O_NONBLOCK);
|
||||
struct sockaddr_un a;
|
||||
memset(&a, 0, sizeof a);
|
||||
a.sun_family = AF_UNIX;
|
||||
strncpy(a.sun_path, ams_sock_path(), sizeof a.sun_path - 1);
|
||||
if (connect(fd, (struct sockaddr *)&a, sizeof a) != 0) {
|
||||
if (errno == EINPROGRESS || errno == EWOULDBLOCK) {
|
||||
struct pollfd pf;
|
||||
pf.fd = fd; pf.events = POLLOUT; pf.revents = 0;
|
||||
int r = poll(&pf, 1, 150);
|
||||
if (r <= 0 || !(pf.revents & POLLOUT)) { close(fd); return; }
|
||||
} else { close(fd); return; }
|
||||
}
|
||||
ssize_t w = write(fd, payload, (size_t)len);
|
||||
(void)w;
|
||||
close(fd);
|
||||
}
|
||||
|
||||
/* launch 上报:pkg/activity/chain/pid。amsd 建表项 + 开 stdout 会话。 */
|
||||
static void ams_report_launch(const struct app *a, const char *chain, pid_t pid)
|
||||
{
|
||||
if (!a) return;
|
||||
char act[MEOW_ACT_MAX + 1] = "";
|
||||
char ch[MEOW_CHAIN_MAX + 1] = "-";
|
||||
if (chain && chain[0]) {
|
||||
const char *top = strrchr(chain, ';');
|
||||
top = top ? top + 1 : chain;
|
||||
snprintf(act, sizeof act, "%s", top);
|
||||
snprintf(ch, sizeof ch, "%s", chain);
|
||||
}
|
||||
char line[512];
|
||||
int l = snprintf(line, sizeof line, "EV LAUNCH %s %s %s %d %ld %ld\n",
|
||||
a->id, act, ch, (int)pid, (long)time(NULL), ams_uptime());
|
||||
if (l > 0) ams_send(line, l);
|
||||
}
|
||||
|
||||
/* exit 上报:kind(exit|sig)/code/dur_ms。异常退出由 amsd 判 crash + 转发 meow_crash。 */
|
||||
static void ams_report_exit(const struct app *a, int status, long long dur_ms)
|
||||
{
|
||||
if (!a) return;
|
||||
const char *kind;
|
||||
int code;
|
||||
if (WIFSIGNALED(status)) { kind = "sig"; code = WTERMSIG(status); }
|
||||
else { kind = "exit"; code = WIFEXITED(status) ? WEXITSTATUS(status) : 1; }
|
||||
char line[256];
|
||||
int l = snprintf(line, sizeof line, "EV EXIT %s %s %d %lld %ld %ld\n",
|
||||
a->id, kind, code, dur_ms, (long)time(NULL), ams_uptime());
|
||||
if (l > 0) ams_send(line, l);
|
||||
}
|
||||
|
||||
/* ---- stdout/stderr 管道捕获(AMSD_DESIGN §5):前台 waitpid 循环排空 + 按行转发 ---- */
|
||||
static char g_ams_out_line[1024]; /* 未完结行缓冲(无 \n) */
|
||||
static int g_ams_out_n = 0;
|
||||
static char g_ams_out_flush[4096]; /* 已完结、待转发行 */
|
||||
static int g_ams_out_flush_n = 0;
|
||||
static int g_ams_out_had = 0; /* 本次迭代是否排空到数据(驱动短睡) */
|
||||
|
||||
static void ams_out_flush(void)
|
||||
{
|
||||
if (g_ams_out_flush_n > 0) {
|
||||
ams_send(g_ams_out_flush, g_ams_out_flush_n);
|
||||
g_ams_out_flush_n = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* 追加一行(line 为无 \n 的 NUL 串)到 flush 缓冲;满了先 flush。 */
|
||||
static void ams_out_line(const struct app *a, pid_t pid, const char *line)
|
||||
{
|
||||
if (!a || !line) return;
|
||||
int need = snprintf(NULL, 0, "EV OUT %s %d %ld %ld %s\n",
|
||||
a->id, (int)pid, (long)time(NULL), ams_uptime(), line);
|
||||
if (g_ams_out_flush_n + need + 1 > (int)sizeof g_ams_out_flush) ams_out_flush();
|
||||
if (need < (int)sizeof g_ams_out_flush - 1 - g_ams_out_flush_n)
|
||||
g_ams_out_flush_n += snprintf(g_ams_out_flush + g_ams_out_flush_n,
|
||||
sizeof g_ams_out_flush - g_ams_out_flush_n,
|
||||
"EV OUT %s %d %ld %ld %s\n",
|
||||
a->id, (int)pid, (long)time(NULL), ams_uptime(), line);
|
||||
}
|
||||
|
||||
/* 排空读端全部可用字节,按行切分转发;超长行截断标记。 */
|
||||
static void ams_drain_stdout(const struct app *a, int fd, pid_t pid)
|
||||
{
|
||||
char tmp[2048];
|
||||
ssize_t n;
|
||||
while ((n = read(fd, tmp, sizeof tmp)) > 0) {
|
||||
g_ams_out_had = 1;
|
||||
for (ssize_t i = 0; i < n; i++) {
|
||||
char c = tmp[i];
|
||||
if (c == '\n') {
|
||||
g_ams_out_line[g_ams_out_n] = '\0';
|
||||
ams_out_line(a, pid, g_ams_out_line);
|
||||
g_ams_out_n = 0;
|
||||
} else if (c == '\r') {
|
||||
/* 去尾 \r */
|
||||
} else if (g_ams_out_n < (int)sizeof g_ams_out_line - 1) {
|
||||
g_ams_out_line[g_ams_out_n++] = c;
|
||||
} else {
|
||||
/* 超长行:截断标记 + 重置(当前字符丢弃) */
|
||||
g_ams_out_line[ (int)sizeof g_ams_out_line - 2 ] = '\0';
|
||||
ams_out_line(a, pid, g_ams_out_line);
|
||||
ams_out_line(a, pid, "[...trunc]");
|
||||
g_ams_out_n = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- amsd start/kill 挂点(主循环单写者)---- */
|
||||
static struct { char pkg[MEOW_APP_ID_MAX]; char act[MEOW_ACT_MAX]; int pending; } g_ams_start;
|
||||
static struct { char pkg[MEOW_APP_ID_MAX]; int pending; } g_ams_kill;
|
||||
|
||||
int meow_ams_pending_start_set(const char *pkg, const char *act)
|
||||
{
|
||||
if (!pkg || !pkg[0]) return -MEOW_ERR_INVAL;
|
||||
if (g_app_session_active) return -MEOW_ERR_BUSY;
|
||||
snprintf(g_ams_start.pkg, sizeof g_ams_start.pkg, "%s", pkg);
|
||||
g_ams_start.act[0] = 0;
|
||||
if (act && act[0]) snprintf(g_ams_start.act, sizeof g_ams_start.act, "%s", act);
|
||||
g_ams_start.pending = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int meow_ams_take_start(char *pkg, int pn, char *act, int an)
|
||||
{
|
||||
if (!g_ams_start.pending) return 0;
|
||||
g_ams_start.pending = 0;
|
||||
if (pkg && pn > 0) snprintf(pkg, pn, "%s", g_ams_start.pkg);
|
||||
if (act && an > 0) snprintf(act, an, "%s", g_ams_start.act);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void meow_ams_pending_kill_set(const char *pkg)
|
||||
{
|
||||
if (!pkg || !pkg[0]) return;
|
||||
snprintf(g_ams_kill.pkg, sizeof g_ams_kill.pkg, "%s", pkg);
|
||||
g_ams_kill.pending = 1;
|
||||
}
|
||||
|
||||
int meow_ams_kill_pending(const char *pkg)
|
||||
{
|
||||
if (!g_ams_kill.pending) return 0;
|
||||
if (pkg && strcmp(g_ams_kill.pkg, pkg) != 0) return 0;
|
||||
g_ams_kill.pending = 0;
|
||||
g_ams_kill.pkg[0] = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* ---- 阶段 D · M1:路由注册表辅助(主循环单写者,无锁)---- */
|
||||
struct meow_acct *meow_acct_find(const char *app_id)
|
||||
{
|
||||
@@ -990,9 +1173,23 @@ static int launch_app_chain(struct app *a, const char *chain)
|
||||
if (pipe(execpipe) < 0) { logmsg("exec pipe failed: %s", strerror(errno)); return -1; }
|
||||
fcntl(execpipe[1], F_SETFD, FD_CLOEXEC);
|
||||
|
||||
/* AMSD_DESIGN §5:stdout/stderr 管道捕获(logcat 思路)。fork 前建管道;子进程 dup2 到
|
||||
* stdout/stderr;父进程保留读端(O_NONBLOCK)+ 前台 waitpid 循环排空转发 amsd。
|
||||
* 管道建失败仅 log 不崩(App 照常无捕获运行)。写端设 CLOEXEC:exec 成功后关闭写端,
|
||||
* App 死 → 读端 EOF(崩溃尾巴可排空)。 */
|
||||
int out_pipe[2] = { -1, -1 };
|
||||
if (pipe(out_pipe) != 0) {
|
||||
out_pipe[0] = -1; out_pipe[1] = -1;
|
||||
logmsg("ams: stdout pipe fail: %s", strerror(errno));
|
||||
} else {
|
||||
fcntl(out_pipe[1], F_SETFD, FD_CLOEXEC);
|
||||
}
|
||||
|
||||
pid_t pid = fork();
|
||||
if (pid < 0) {
|
||||
close(execpipe[0]); close(execpipe[1]);
|
||||
if (out_pipe[0] >= 0) close(out_pipe[0]);
|
||||
if (out_pipe[1] >= 0) close(out_pipe[1]);
|
||||
logmsg("fork failed: %s", strerror(errno)); return -1;
|
||||
}
|
||||
if (pid == 0) {
|
||||
@@ -1002,6 +1199,14 @@ static int launch_app_chain(struct app *a, const char *chain)
|
||||
* (读全局 log.level),使 SDK meow_log() 按 app 分离写 logs/<id>/<ts>_<pid>.log 并做级别过滤。
|
||||
* M2.x 简化:MEOW_LOG_DIR 从 logs/apps/<id>/logs 改注入 logs/<id>(目录不存在则建)。 */
|
||||
close(execpipe[0]);
|
||||
/* AMSD_DESIGN §5:stdout/stderr 合并重定向到管道写端(dup2 不清 CLOEXEC),
|
||||
* 原始 out_pipe 两根 fd 关闭;成功 exec 后仅 fd1/2 持有写端,App 死 → EOF。 */
|
||||
if (out_pipe[1] >= 0) {
|
||||
dup2(out_pipe[1], STDOUT_FILENO);
|
||||
dup2(out_pipe[1], STDERR_FILENO);
|
||||
close(out_pipe[1]);
|
||||
close(out_pipe[0]);
|
||||
}
|
||||
char *argv[] = { a->exec, NULL };
|
||||
char env_path[320], env_log[320], env_data[320], env_cache[320];
|
||||
char app_log_dir[288];
|
||||
@@ -1130,6 +1335,12 @@ static int launch_app_chain(struct app *a, const char *chain)
|
||||
_exit(127);
|
||||
}
|
||||
close(execpipe[1]);
|
||||
if (out_pipe[1] >= 0) close(out_pipe[1]); /* parent: 关闭写端 */
|
||||
int out_fd = out_pipe[0]; /* parent 保留读端,前台循环排空 */
|
||||
if (out_fd >= 0) {
|
||||
int fl = fcntl(out_fd, F_GETFL, 0);
|
||||
if (fl >= 0) fcntl(out_fd, F_SETFL, fl | O_NONBLOCK);
|
||||
}
|
||||
int exe_err = 0;
|
||||
ssize_t er = read(execpipe[0], &exe_err, sizeof exe_err);
|
||||
close(execpipe[0]);
|
||||
@@ -1138,9 +1349,13 @@ static int launch_app_chain(struct app *a, const char *chain)
|
||||
strerror(exe_err), exe_err);
|
||||
int st = 0;
|
||||
waitpid(pid, &st, 0); /* reap the failed child */
|
||||
if (out_fd >= 0) close(out_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* AMSD_DESIGN §4.1:启动上报(pkg/activity/chain/pid),amsd 建表项 + 开 stdout 捕获会话。 */
|
||||
ams_report_launch(a, chain, pid);
|
||||
|
||||
/* GESTURE_BACK §2.4:应用启动即写 immersive 状态,watchdog 依此隐藏/保留虚拟返回键 */
|
||||
meow_write_app_state(a->immersive == '1' ? 1 : 0);
|
||||
|
||||
@@ -1181,6 +1396,8 @@ static int launch_app_chain(struct app *a, const char *chain)
|
||||
* never fires on this unit, §18.12). Volume is snapped on the first
|
||||
* tap and restored when the sequence completes. */
|
||||
int status = 0;
|
||||
unsigned long long ams_t0 = now_ms(); /* AMSD_DESIGN §4.1:运行时长起点 */
|
||||
int ams_status_valid = 0; /* 仅真实 waitpid 回收才置 1(规避 sigterm/重启路径 status=0 假干净退出) */
|
||||
int nav_clean_exit = 0; /* M3:MSG_NAV{EXIT} 主动退出 → 不按崩溃上报 */
|
||||
int app_prev_vold = 0;
|
||||
int app_seq_hits = 0;
|
||||
@@ -1218,18 +1435,31 @@ static int launch_app_chain(struct app *a, const char *chain)
|
||||
meow_notif_open_clear();
|
||||
for (;;) {
|
||||
int r = waitpid(pid, &status, WNOHANG);
|
||||
if (r == pid) break;
|
||||
if (r == pid) { ams_status_valid = 1; break; }
|
||||
if (r < 0 && errno != EINTR) break;
|
||||
if (sigterm_flag) { logmsg("sigterm during app"); kill(pid, SIGTERM); break; }
|
||||
/* AMSD_DESIGN §5.1:排空 App stdout/stderr 管道(非阻塞),按行转发 amsd;App 写满不阻塞。 */
|
||||
g_ams_out_had = 0;
|
||||
if (out_fd >= 0) ams_drain_stdout(a, out_fd, pid);
|
||||
/* M3 导航栈:前台会话排空 IPC(App 经 MSG_NAV REPORT/EXIT 上报导航/请求退出)。
|
||||
* REPORT 更新 nav_stack(meow_ipc_process 内单写者);EXIT → 终止应用回主屏。 */
|
||||
meow_ipc_process();
|
||||
/* AMSD_DESIGN §4.3:amsd `am kill` → 前台循环消费 → SIGTERM(执行器=launcher)。 */
|
||||
if (meow_ams_kill_pending(a->id)) {
|
||||
logmsg("ams: kill %s -> SIGTERM", a->name);
|
||||
kill(pid, SIGTERM);
|
||||
usleep(300000);
|
||||
if (waitpid(pid, &status, WNOHANG) != pid) { kill(pid, SIGKILL); waitpid(pid, &status, 0); }
|
||||
ams_status_valid = 1;
|
||||
break;
|
||||
}
|
||||
if (meow_nav_exit_pending()) {
|
||||
logmsg("nav: app %s MSG_NAV{EXIT} -> terminate, return to home", a->name);
|
||||
nav_clean_exit = 1;
|
||||
kill(pid, SIGTERM);
|
||||
usleep(300000);
|
||||
if (waitpid(pid, &status, WNOHANG) != pid) { kill(pid, SIGKILL); waitpid(pid, &status, 0); }
|
||||
ams_status_valid = 1;
|
||||
break;
|
||||
}
|
||||
/* §10.5:App 前台顶部下滑(MSG_NOTIF_OPEN)→ 展开通知中心叠加会话。复用电源菜单
|
||||
@@ -1248,6 +1478,7 @@ static int launch_app_chain(struct app *a, const char *chain)
|
||||
/* App 会话中崩溃(overlay 内 proc_is_zombie 探测):回收僵尸取真实状态,
|
||||
* 走统一应用退出路径(不 SIGCONT 死进程、不恢复死帧;launcher 不崩)。 */
|
||||
waitpid(pid, &status, 0);
|
||||
ams_status_valid = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1259,6 +1490,7 @@ static int launch_app_chain(struct app *a, const char *chain)
|
||||
kill(pid, SIGTERM);
|
||||
usleep(300000);
|
||||
if (waitpid(pid, &status, WNOHANG) != pid) { kill(pid, SIGKILL); waitpid(pid, &status, 0); }
|
||||
ams_status_valid = 1;
|
||||
break;
|
||||
}
|
||||
/* in-app kill: VOL_DOWN x3 quick taps */
|
||||
@@ -1323,6 +1555,7 @@ static int launch_app_chain(struct app *a, const char *chain)
|
||||
usleep(500000);
|
||||
kill(pid, SIGKILL);
|
||||
waitpid(pid, &status, 0);
|
||||
ams_status_valid = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1373,12 +1606,31 @@ static int launch_app_chain(struct app *a, const char *chain)
|
||||
app_overlay_statusbar();
|
||||
}
|
||||
}
|
||||
/* 顶条收合/恢复过渡期把循环睡得短些(~20ms),驱动更平滑的上移出屏;稳定后回 100ms。 */
|
||||
usleep(meow_ui_statusbar_animating() ? 20000 : 100000);
|
||||
/* 顶条收合/恢复过渡期把循环睡得短些(~20ms),驱动更平滑的上移出屏;稳定后回 100ms。
|
||||
* AMSD_DESIGN §5.1:本轮排空到 stdout 数据 → 也睡短些,防止 App 写满管道被阻塞。 */
|
||||
usleep((meow_ui_statusbar_animating() || g_ams_out_had) ? 20000 : 100000);
|
||||
}
|
||||
/* §10.5:App 前台会话结束,清会话标记(ipc_dispatch 不再受理 MSG_NOTIF_OPEN) */
|
||||
g_app_session_active = 0;
|
||||
meow_notif_open_clear();
|
||||
/* AMSD_DESIGN §5.1:崩溃尾巴不丢 —— App 已死,终局排空剩余 stdout/stderr(含未完结行)
|
||||
* 先转发 amsd,再报 EXIT / crash。仅当有读端(管道建立成功)才 drain。 */
|
||||
if (out_fd >= 0) {
|
||||
g_ams_out_had = 0;
|
||||
ams_drain_stdout(a, out_fd, pid); /* 排空全部剩余(App 死 → read 返回 0=EOF) */
|
||||
if (g_ams_out_n > 0) { /* 未完结尾行:强制提交 */
|
||||
g_ams_out_line[g_ams_out_n] = '\0';
|
||||
ams_out_line(a, pid, g_ams_out_line);
|
||||
g_ams_out_n = 0;
|
||||
}
|
||||
ams_out_flush();
|
||||
close(out_fd);
|
||||
out_fd = -1;
|
||||
}
|
||||
/* AMSD_DESIGN §4.1:waitpid 回收上报(真实回收才报;规避 sigterm/电源菜单重启 status=0 的
|
||||
* 假干净退出)。amsd 依 kind/code 判 crash 并转发 meow_crash(SVC 格式)。 */
|
||||
if (ams_status_valid)
|
||||
ams_report_exit(a, status, (long long)(now_ms() - ams_t0));
|
||||
/* M2.x:App 异常退出(信号杀 / 非 0 退出码)→ best-effort 直报 meow_crash。
|
||||
* 判据与 svcd reap_children 一致:failed = !(WIFEXITED && WEXITSTATUS==0)。干净 exit(0)
|
||||
* 不上报;sigterm 关停/电源菜单重启路径 status 保持 0(未 waitpid)→ 视为干净退出不上报。 */
|
||||
|
||||
Reference in new issue
Block a user