diff --git a/patch_v2/meow_amsd/am b/patch_v2/meow_amsd/am index 2275257..208b1f6 100755 Binary files a/patch_v2/meow_amsd/am and b/patch_v2/meow_amsd/am differ diff --git a/patch_v2/meow_amsd/am.c b/patch_v2/meow_amsd/am.c index e5d325d..4a84e21 100644 --- a/patch_v2/meow_amsd/am.c +++ b/patch_v2/meow_amsd/am.c @@ -9,6 +9,7 @@ * 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 @@ -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 ] status|list|history [pkg]|start [/]|kill \n" + "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); @@ -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; } diff --git a/patch_v2/meow_amsd/am_host b/patch_v2/meow_amsd/am_host index 4222771..3bc5960 100755 Binary files a/patch_v2/meow_amsd/am_host and b/patch_v2/meow_amsd/am_host differ diff --git a/patch_v2/meow_amsd/meow_amsd b/patch_v2/meow_amsd/meow_amsd index 9330bbe..9057612 100755 Binary files a/patch_v2/meow_amsd/meow_amsd and b/patch_v2/meow_amsd/meow_amsd differ diff --git a/patch_v2/meow_amsd/meow_amsd.c b/patch_v2/meow_amsd/meow_amsd.c index f07f694..8182789 100644 --- a/patch_v2/meow_amsd/meow_amsd.c +++ b/patch_v2/meow_amsd/meow_amsd.c @@ -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//stdout__.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.=) */ 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 + * ——无 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 `:查 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); diff --git a/patch_v2/meow_amsd/meow_amsd_host b/patch_v2/meow_amsd/meow_amsd_host index 16be5e3..4338029 100755 Binary files a/patch_v2/meow_amsd/meow_amsd_host and b/patch_v2/meow_amsd/meow_amsd_host differ diff --git a/scripts/build/fw_content.conf b/scripts/build/fw_content.conf index 4906723..37a0dbd 100644 --- a/scripts/build/fw_content.conf +++ b/scripts/build/fw_content.conf @@ -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