fix(amsd): 注册表扫空自愈 (agent-250) — 根因: svcd trigger=boot 拉起 amsd 时 TF 卡未挂载, 启动首扫打空挂载点且此后永不重扫 → am start 报 pkg not registered (M2.90 真机日志实证). 修: registry_scan 改合并式重扫(只刷静态字段, 运行时状态/DECLARE路由保留, 源不可达绝不清表) + 主循环自愈重扫(源未通 3s 快重试/常态 30s 合并重扫) + am rescan 手动触发 + 兼容 M2.90 固件 launcher 旧 5 段 EV LAUNCH(nt==5 无 chain, epoch 不再错当 pid). 宿主+qemu+真机全链 PASS: 空目录启动 4s 自愈出 3 App, 真机 am start dev.player.ncm→OK(真pid)→kill 全通. meow_amsd da9a5004 / am 9e323faf (fw_content.conf 已更新, 待 M2.91 入固件闭环冷启动)

This commit is contained in:
MeowOS committed 2026-09-01 03:41:19 +08:00
1 parent 7cb1bdace4
commit ca2290e691
7 files changed
+102 -31

No files matched your search

Binary file not shown.
+8 -1
View File
@@ -9,6 +9,7 @@
* am history [pkg] → 事件历史(每 App 最近 8 条;无 pkg=全部)
* am start <pkg>[/<activity>] → 经 amsd→launcher 拉起
* am kill <pkg> → 经 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
@@ -125,10 +126,15 @@ static int cmd_kill(const char *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 <path>] status|list|history [pkg]|start <pkg>[/<act>]|kill <pkg>\n"
"usage: %s [--sock <path>] status|list|history [pkg]|start <pkg>[/<act>]|kill <pkg>|rescan\n"
" %s resolve <pkg> <activity> | picker <action> | picker-status [reqid]\n"
" --sock <path> amsd socket (default %s, or $MEOW_AMS_SOCK)\n",
a0, a0, AMS_SOCK_DEFAULT);
@@ -181,6 +187,7 @@ int main(int argc, char **argv)
if (!strcmp(cmd, "picker-status")) {
return cmd_picker_status(arg);
}
if (!strcmp(cmd, "rescan")) return cmd_rescan();
usage(argv[0]);
return 2;
}
Binary file not shown.
Binary file not shown.
+92 -28
View File
@@ -6,6 +6,8 @@
* svcd fork+exec 直接拉起,故本进程【前台常驻】(不 daemonize),与 meow_crash 同款。
* - 一期:生命周期监控中心 + 应用注册表。launcher 经 AF_UNIX `/usr/data/meow_ams.sock`(0666)
* 上报 launch/exit/crash 事件;`am` CLI 同 socket 查询/下发 start/kill。
* - 注册表 = 启动首扫 + 周期合并重扫(卡未挂载扫空 3s 快重试、常态 30s 自愈;
* `am rescan` 手动触发)。重扫合并式,绝不清运行时状态(M2.90 真机扫空缺陷修复)。
* - 核心目的:从侧面详细抓 App 日志(尤其异常崩溃日志)。launcher 给 App 建 stdout/stderr
* 管道,前台循环按行转发(EV OUT);amsd 落盘 `logs/<pkg>/stdout_<ts>_<pid>.log`。
* - 崩溃双保险:amsd 检测到异常退出时另以 meow_crash 认识的 `SVC ...` 格式转发
@@ -61,6 +63,8 @@
#define AMS_MAX_PENDING 8 /* 进行中 picker 请求(结果关联) */
#define AMS_TICK_MS 1000
#define AMS_CLEANUP_MS 60000
#define AMS_RESCAN_MS 30000 /* 常态合并重扫周期(卡热插拔/App 热安装后注册表自愈) */
#define AMS_RESCAN_RETRY_MS 3000 /* 扫描源尚未成功打开(开机早于卡挂载扫空)→ 快重试 */
#define AMS_PATH_MAX 512
#define AMS_SOCK_PATH_MAX 108
#define AMS_BOOTTS_LEN 64
@@ -478,14 +482,31 @@ static void ams_parse_pickers(const char *content, const char *app_id)
}
}
/* 扫描 --apps 下每个目录(app_id=目录名;name/exec 从 app.json 抽,缺省回退)。 */
static void registry_scan(void)
/* 扫描源至少成功打开过一次(空表快重试的终止条件;卡未挂载时恒 0)。 */
static int g_scan_ok = 0;
static struct ams_app *app_find(const char *pkg); /* 定义在后(App 表辅助) */
/* 扫描 --apps 下每个目录(app_id=目录名;name/exec 从 app.json 抽,缺省回退)。
* 合并式(merge-preserving):已存在表项只刷新静态字段(known/name/exec/route 表/picker 表),
* 运行时状态(state/pid/launches/crashes/history、运行时 DECLARE 并集进来的路由)一律保留;
* 新目录追加建项。扫描源不可达(开机早于卡挂载/卡热拔)→ 保留现有表直接返回,绝不清空。
* verbose=1(启动/`am rescan`)记全量明细;周期调用仅在结果变化时记一行摘要(防刷屏)。 */
static void registry_scan(int verbose)
{
static int last_found = -1, last_napps = -1, last_npick = -1;
DIR *d = opendir(g_apps_dir);
if (!d) { emit_log("registry scan: %s not reachable (%s)", g_apps_dir, strerror(errno)); return; }
if (!d) {
if (verbose || !g_scan_ok)
emit_log("registry scan: %s not reachable (%s)", g_apps_dir, strerror(errno));
return;
}
g_scan_ok = 1;
for (int i = 0; i < g_napps; i++) g_apps[i].known = 0;
for (int i = 0; i < g_npickers; i++) g_pickers[i].used = 0;
struct dirent *de;
int n = 0;
while ((de = readdir(d)) != NULL && n < AMS_MAX_APPS) {
int found = 0;
while ((de = readdir(d)) != NULL) {
if (de->d_name[0] == '.') continue;
char nm[96];
scopy(nm, sizeof nm, de->d_name);
@@ -506,37 +527,49 @@ static void registry_scan(void)
fclose(jf);
content[rd] = '\0';
}
struct ams_app *a = &g_apps[n];
memset(a, 0, sizeof *a);
/* app_id 权威 = 目录名(APPS.md §2);app.json package 字段作为兜底校验(不一致时用目录名) */
scopy(a->pkg, sizeof a->pkg, de->d_name);
/* app_id 权威 = 目录名(APPS.md §2);已有表项(含运行时 EV/DECLARE 建的)原位刷新 */
struct ams_app *a = app_find(nm);
if (!a) {
if (g_napps >= AMS_MAX_APPS) continue;
a = &g_apps[g_napps++];
memset(a, 0, sizeof *a);
scopy(a->pkg, sizeof a->pkg, nm);
}
a->known = 1;
char tmp[96];
appjson_get(content, "name", tmp, sizeof tmp);
if (!tmp[0]) scopy(tmp, sizeof tmp, de->d_name);
if (!tmp[0]) scopy(tmp, sizeof tmp, nm);
scopy(a->name, sizeof a->name, tmp);
appjson_get(content, "exec", tmp, sizeof tmp);
if (tmp[0]) scopy(a->exec, sizeof a->exec, tmp);
else {
else if (!a->exec[0] || !strcmp(a->exec, "?")) {
scopy(a->exec, sizeof a->exec, dir);
path_append(a->exec, sizeof a->exec, "/meow");
}
/* §6.2 二期:路由权威表(静态 app.json route.*)+ picker 表(picker.<action>=<chain>) */
ams_parse_routes(content, a);
ams_parse_pickers(content, a->pkg);
n++;
found++;
}
closedir(d);
g_napps = n;
emit_log("registry scan: %d app(s) from %s", g_napps, g_apps_dir);
for (int i = 0; i < g_napps; i++) {
struct ams_app *a = &g_apps[i];
emit_log(" app %s name=%s exec=%s routes=%d", a->pkg, a->name, a->exec, a->nroute);
for (int r = 0; r < a->nroute; r++)
emit_log(" route %s=%s", a->routes[r].act, a->routes[r].chain);
if (verbose || found != last_found || g_napps != last_napps || g_npickers != last_npick) {
emit_log("registry scan: %d dir(s) from %s, %d app(s) registered",
found, g_apps_dir, g_napps);
if (verbose) {
for (int i = 0; i < g_napps; i++) {
struct ams_app *a = &g_apps[i];
emit_log(" app %s name=%s exec=%s known=%d routes=%d",
a->pkg, a->name, a->exec, a->known, a->nroute);
for (int r = 0; r < a->nroute; r++)
emit_log(" route %s=%s", a->routes[r].act, a->routes[r].chain);
}
for (int i = 0; i < g_npickers; i++)
if (g_pickers[i].used)
emit_log(" picker %s -> %s (chain=%s)",
g_pickers[i].action, g_pickers[i].app_id, g_pickers[i].chain);
}
}
for (int i = 0; i < g_npickers; i++)
emit_log(" picker %s -> %s (chain=%s)", g_pickers[i].action, g_pickers[i].app_id, g_pickers[i].chain);
last_found = found; last_napps = g_napps; last_npick = g_npickers;
}
/* ---- App 表辅助 ---- */
@@ -819,10 +852,22 @@ static int ams_request_ctl(uint16_t type, const void *payload, uint32_t len, uin
static void handle_launch(char **t, int nt)
{
if (nt < 4) return;
const char *pkg = t[0], *act = t[1], *chain = t[2];
pid_t pid = (pid_t)atoi(t[3]);
long epoch = (nt > 4) ? atol(t[4]) : (long)time(NULL);
long up = (nt > 5) ? atol(t[5]) : read_uptime_sec();
const char *pkg = t[0], *act = t[1], *chain = "-";
pid_t pid;
long epoch, up;
if (nt == 5) {
/* 兼容旧格式(M2.90 固件 launcher 实证):EV LAUNCH <pkg> <act> <pid> <epoch> <up>
* ——无 chain 字段。不兼容会把 epoch 错当 pid(am status 显示假 pid、
* `am kill` 兜底直发 SIGTERM 打错进程),故按 nt==5 识别旧布局。 */
pid = (pid_t)atoi(t[2]);
epoch = atol(t[3]);
up = atol(t[4]);
} else {
chain = t[2];
pid = (pid_t)atoi(t[3]);
epoch = (nt > 4) ? atol(t[4]) : (long)time(NULL);
up = (nt > 5) ? atol(t[5]) : read_uptime_sec();
}
struct ams_app *a = app_get(pkg);
if (!a) return;
a->state = AMS_FG;
@@ -1015,6 +1060,13 @@ static int cmd_picker_status(const char *reqstr, char *resp, int rn)
return l;
}
/* `am rescan`:手动触发注册表重扫(卡刚挂载/热装 App 后立即可见,不等 30s 周期)。 */
static int cmd_rescan(char *resp, int rn)
{
registry_scan(1);
return snprintf(resp, rn, "OK rescan %d app(s)\n", g_napps);
}
/* `am resolve <pkg> <act>`:查 amsd 权威路由表。 */
static int cmd_resolve(const char *pkg, const char *act, char *resp, int rn)
{
@@ -1218,13 +1270,14 @@ static int dispatch_line(char *line, char *resp, int rn)
return 0;
}
/* 命令:status/list/history/start/kill/resolve/picker/picker-status */
/* 命令:status/list/history/start/kill/resolve/picker/picker-status/rescan */
char *save = NULL;
char *cmd = strtok_r(line, " ", &save);
if (!cmd) return 0;
char *arg = strtok_r(NULL, " ", &save);
if (!strcmp(cmd, "status")) return cmd_status(resp, rn) > 0;
if (!strcmp(cmd, "list")) return cmd_list(resp, rn) > 0;
if (!strcmp(cmd, "rescan")) return cmd_rescan(resp, rn) > 0;
if (!strcmp(cmd, "history")) {
if (arg && arg[0]) cmd_history(arg, resp, rn);
else cmd_history(NULL, resp, rn);
@@ -1369,7 +1422,7 @@ static int run_daemon(void)
boot_ts(g_bootts, sizeof g_bootts);
log_open_card();
log_cleanup_old();
registry_scan();
registry_scan(1); /* 启动首扫:卡未挂载扫空也照常常驻,主循环 3s 快重试兜底 */
fprintf(stderr, "[meow_amsd] up: sock=%s ipc=%s apps=%s card=%s bootts=%s log=%s\n",
g_sock, g_ipc_sock, g_apps_dir, g_card, g_bootts,
@@ -1381,7 +1434,7 @@ static int run_daemon(void)
for (int i = 0; i < AMS_MAX_CONNS; i++) conns[i].fd = -1;
int nfds = 2;
unsigned long long last_cleanup = 0;
unsigned long long last_cleanup = 0, last_rescan = 0;
while (!g_shutdown) {
/* 每圈清零:谨防上一轮已关闭连接残留的 POLLHUP/POLLERR 落在 pfds[2+i] 上。
* 若不清,accept 后同一轮就用「陈旧 revents」处理新连接——误判其 HUP/ERR
@@ -1448,6 +1501,17 @@ static int run_daemon(void)
poll_card();
log_cleanup_old();
}
/* 注册表自愈重扫:扫描源从未成功打开(开机早于卡挂载扫空,M2.90 真机实证
* 「registry scan: MeowAPPs not reachable」后注册表永久为空 → am start 报
* pkg not registered)→ 3s 快重试直到扫到;常态 30s 合并重扫(卡热插拔 /
* App 热安装自愈,对齐 launcher 的应用列表目录热重载)。合并式重扫不清
* 运行时状态,见 registry_scan 注释。 */
{
unsigned long long nm = now_ms();
unsigned long long iv = g_scan_ok ? (unsigned long long)AMS_RESCAN_MS
: (unsigned long long)AMS_RESCAN_RETRY_MS;
if (nm - last_rescan >= iv) { last_rescan = nm; registry_scan(0); }
}
}
if (g_listen_fd >= 0) close(g_listen_fd);
Binary file not shown.
+2 -2
View File
@@ -44,8 +44,8 @@ patch_v2/meow_audiod/meow_audio|/usr/bin/meow_audio|5cc3c35459409e93672448c99543
# ---- 开机 init 脚本(etc/init.d,数字决定启动顺序,按文件名自然序)----
patch_v2/meow_sysui/S70meow_sysui|/etc/init.d/S70meow_sysui|e435affcb56499e5229728c6ae2524b9|sysui 自启 M2.85(launcher.conf enabled=1+迁移)
patch_v2/meow_svcd/S89meow_svcd|/etc/init.d/S89meow_svcd|77da8236453f763d9f09a02e2736b798|svcd 自启(meow_route.conf → audiod.conf + 清理旧 route conf,预置独占音频服务 agent-244/246)
patch_v2/meow_amsd/meow_amsd|/usr/bin/meow_amsd|c6c2eda2d63ac73c4b6fac29008e0c9b|活动管理器 M2.89(陈旧revents丢连接/收发超时/自动重连 agent-238)
patch_v2/meow_amsd/am|/usr/bin/am|d687d8bc87df102e625e6482b0423430|amsd 用户侧CLI(+resolve/picker)
patch_v2/meow_amsd/meow_amsd|/usr/bin/meow_amsd|da9a5004c6fdb7827c45c27daa825919|活动管理器 M2.91(注册表自愈:卡未挂载扫空 3s 快重试+30s 合并重扫+am rescan;兼容旧 launcher 5 段 EV LAUNCH;agent-250)
patch_v2/meow_amsd/am|/usr/bin/am|9e323faf1aeb0c6a6fb0425565cf525d|amsd 用户侧CLI(+resolve/picker/rescan)
patch_v2/bootmenu/S90meow_bootmenu|/etc/init.d/S90meow_bootmenu|e0ace111ce5ee446a1e3c6605ac1e9c9|bootmenu 自启 M2.85(注释同步)
patch_v2/bootmenu/S92_03_start_music_player|/etc/init.d/S92_03_start_music_player|c17257aa3d93f75da72903b57cbe1034|§4.3 兜底 patch(rcS 正常路径被 S90 阻塞不执行)
patch_v2/bootmenu/S95shutdown_logo|/etc/init.d/S95shutdown_logo|96db7b844d7c26f44835d4681824f578|rcK 关机时显示 /etc/shutdown_logo.jpeg