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:
1 parent
7cb1bdace4
commit
ca2290e691
7 files changed
+102
-31
No files matched your search
Binary file not shown.
@@ -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.
@@ -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.
Reference in new issue
Block a user