fix: 后台页杀按钮点不动 — app.json framework=1 被误当「不可杀」门控 + am kill 无前台会话不可执行
根因(2026-11-04 真机 dev.player.ncm pid=7520): - bg_page.c bgm_app_meta 把 app.json 的 framework 字段映射进 bgmgr_row.framework,而 bgmgr_can_kill 把它当「系统/框架 App 禁止 kill」。但 framework=1 在 MeowOS 里意为「用 meow_ui.a 的 SDK 框架 App」(docs/APP_DEV_GUIDE.md §9,推荐值,真机 4 个 App 全写 1) → 后台页每行「杀」按钮被置灰,bgm_row_tap 命中后直接 return 1、不调 meow_bg_terminate; 「一键清除后台」同理 0 行可杀。整条链路无日志,故只能靠翻 app.json 才定位(全部设备日志里 "后台管理页终止后台App" 出现 0 次 = 4 次进页从未真正杀过)。 - 面板 killfg 正常是因为走 am kill → amsd → MSG_AMS_KILL,无 framework 门控;但它其实靠 amsd 1200ms×2 超时兜底 kill(pid,SIGTERM) 才杀成:util.c 原本在无前台 App 会话时只回 ACK -2((uint32_t)MEOW_ERR_NOENT),ACK 及时到达时 amsd 判「launcher refused」完全不杀 (ams_20260915_031339.log 实证),ACK 迟到才侥幸成功(2026-11-04 真机)。 修复: - meow_bgmgr: 门控位 framework → protected_app(系统级受保护标记;当前无 App 置位,launcher 自身组件本就不在 App 表/后台表内)。bg_page 不再把 a->framework 送进门控。 - bg_page: 行副文本改存 ctx 持久缓冲(原 rbuf[].sub 指向 bgm_rows 栈帧 → 悬垂,副标题乱码); 门控拒绝/未命中后台表各留一行 logmsg,「点了没反应」从此可查。 - util.c MSG_AMS_KILL: 先查后台保活表 → meow_bg_terminate 就地走真退序列(与页面「杀」按钮 同一通道,故 am kill 与页面按钮行为对称),无前台会话也能杀;否则仍走 pending / ACK -2。 - amsd cmd_kill/cmd_start: ipc 失败原因细分(connect failed / timeout, no reply / ...),日志 不再笼统写 "launcher offline"(响应串保留该字样,MeowPanel api_ams_ctl 依此映射 503)。 - meow_bgplay: meow_bg_terminate 注释/日志写明「后台管理页 / am kill」双入口同通道。 测试(宿主 gcc,产物在 $TMPDIR): - test_bgpage 40 checks:新增 framework=1 仍判可杀且按钮真 terminate(本 bug 回归)、 受保护行按钮不杀但留诊断日志、sub 持久化 + 栈抖动不变(悬垂回归)。 - test_bgmgr 30 checks:protected_app 门控矩阵 + framework=1 可杀回归。 - test_bgplay 92 PASS:新增 4b meow_bg_terminate 全链路(受理→term_at_ms→tick SIGTERM→ on_bg_dead→清槽;空/不在表分别 INVAL/NOENT);TEST_SVC_PATH 改取 $TMPDIR (Termux 无 /tmp,原 fputs(NULL) 直接 abort)。 - amsd 宿主集成(fake launcher 桩,4 例):ACK 0→terminate 且不盲杀;ACK -2→refused 不盲杀; 无 launcher→direct SIGTERM (connect failed);挂起不回→direct SIGTERM (timeout, no reply)。 0warning:util.c 与 main 同告警逐行一致(仅 SDK 头既有 typedef 重定义),其余文件干净。
This commit is contained in:
1 parent
a73bcb9841
commit
6b52740ff1
11 files changed
+249
-84
No files matched your search
@@ -784,11 +784,35 @@ static void out_append(const char *pkg, pid_t pid, long epoch, long up, const ch
|
||||
}
|
||||
|
||||
/* ---- launcher IPC 转发(xchg 一帧,读回一帧) ---- */
|
||||
/* 最近一次 ipc_xchg 的失败原因:区分「连不上」与「连上但没及时回」。此前两者都记成
|
||||
* "launcher offline",2026-11-04 真机即误诊——launcher 活着且回了 ACK,只是晚于 1200ms 超时。 */
|
||||
enum { IPC_ERR_NONE = 0, IPC_ERR_CONN, IPC_ERR_IO, IPC_ERR_TIMEOUT, IPC_ERR_PROTO };
|
||||
static int g_ipc_err = IPC_ERR_NONE;
|
||||
|
||||
static const char *ipc_err_str(void)
|
||||
{
|
||||
switch (g_ipc_err) {
|
||||
case IPC_ERR_CONN: return "connect failed";
|
||||
case IPC_ERR_IO: return "io error";
|
||||
case IPC_ERR_TIMEOUT: return "timeout, no reply";
|
||||
case IPC_ERR_PROTO: return "unexpected reply";
|
||||
default: return "ok";
|
||||
}
|
||||
}
|
||||
|
||||
static int ipc_fail(int fd, int err)
|
||||
{
|
||||
if (fd >= 0) close(fd);
|
||||
g_ipc_err = err;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int ipc_xchg(uint16_t type, const void *payload, uint32_t len,
|
||||
uint8_t *rbuf, uint32_t rcap)
|
||||
{
|
||||
g_ipc_err = IPC_ERR_NONE;
|
||||
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (fd < 0) return -1;
|
||||
if (fd < 0) { g_ipc_err = IPC_ERR_IO; return -1; }
|
||||
/* 收发加超时:launcher 刚刚重启/主循环未就绪(接受但不回包)时,amsd 的阻塞
|
||||
* read 会永远挂起,冻结整个单线程守护。设 SO_SNDTIMEO/SO_RCVTIMEO 让单次转发
|
||||
* 有界,超时即返回失败 → ams_request_ctl 退避重连,绝不无限阻塞。
|
||||
@@ -802,7 +826,7 @@ static int ipc_xchg(uint16_t type, const void *payload, uint32_t len,
|
||||
memset(&a, 0, sizeof a);
|
||||
a.sun_family = AF_UNIX;
|
||||
strncpy(a.sun_path, g_ipc_sock, sizeof a.sun_path - 1);
|
||||
if (connect(fd, (struct sockaddr *)&a, sizeof a) != 0) { close(fd); return -1; }
|
||||
if (connect(fd, (struct sockaddr *)&a, sizeof a) != 0) return ipc_fail(fd, IPC_ERR_CONN);
|
||||
uint8_t hdr[MEOW_IPC_HDR_LEN];
|
||||
uint32_t magic = MEOW_IPC_MAGIC, rmagic, rlen;
|
||||
uint32_t reqid = 0; /* amsd→launcher 控制请求无需回显 */
|
||||
@@ -813,31 +837,35 @@ static int ipc_xchg(uint16_t type, const void *payload, uint32_t len,
|
||||
size_t off = 0;
|
||||
while (off < sizeof hdr) {
|
||||
ssize_t w = write(fd, hdr + off, sizeof hdr - off);
|
||||
if (w <= 0) { close(fd); return -1; }
|
||||
if (w <= 0) return ipc_fail(fd, (w < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
|
||||
? IPC_ERR_TIMEOUT : IPC_ERR_IO);
|
||||
off += (size_t)w;
|
||||
}
|
||||
if (len && payload) {
|
||||
size_t p = 0;
|
||||
while (p < len) {
|
||||
ssize_t w = write(fd, (const uint8_t *)payload + p, len - p);
|
||||
if (w <= 0) { close(fd); return -1; }
|
||||
if (w <= 0) return ipc_fail(fd, (w < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
|
||||
? IPC_ERR_TIMEOUT : IPC_ERR_IO);
|
||||
p += (size_t)w;
|
||||
}
|
||||
}
|
||||
off = 0;
|
||||
while (off < sizeof hdr) {
|
||||
ssize_t r = read(fd, hdr + off, sizeof hdr - off);
|
||||
if (r <= 0) { close(fd); return -1; }
|
||||
if (r <= 0) return ipc_fail(fd, (r < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
|
||||
? IPC_ERR_TIMEOUT : IPC_ERR_IO);
|
||||
off += (size_t)r;
|
||||
}
|
||||
memcpy(&rmagic, hdr, 4); memcpy(&rver, hdr + 4, 2);
|
||||
memcpy(&rtype, hdr + 6, 2); memcpy(&rlen, hdr + 12, 4);
|
||||
if (rmagic != MEOW_IPC_MAGIC || rver != MEOW_IPC_VERSION) { close(fd); return -1; }
|
||||
if (rmagic != MEOW_IPC_MAGIC || rver != MEOW_IPC_VERSION) return ipc_fail(fd, IPC_ERR_PROTO);
|
||||
if (rlen > rcap) rlen = rcap;
|
||||
off = 0;
|
||||
while (off < rlen) {
|
||||
ssize_t r = read(fd, rbuf + off, rlen - off);
|
||||
if (r <= 0) { close(fd); return -1; }
|
||||
if (r <= 0) return ipc_fail(fd, (r < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
|
||||
? IPC_ERR_TIMEOUT : IPC_ERR_IO);
|
||||
off += (size_t)r;
|
||||
}
|
||||
close(fd);
|
||||
@@ -858,11 +886,12 @@ static int ams_request_ctl(uint16_t type, const void *payload, uint32_t len, uin
|
||||
if (code) *code = a.code;
|
||||
return 0;
|
||||
}
|
||||
if (rt == MSG_ERR) return MEOW_ERR_INTERNAL;
|
||||
if (rt == MSG_ERR) { g_ipc_err = IPC_ERR_PROTO; return MEOW_ERR_INTERNAL; }
|
||||
/* 一次连接失败/超时(launcher 未就绪)→ 短退避再试;仍失败则返回 -1。 */
|
||||
if (attempt + 1 < AMS_IPC_RETRY)
|
||||
usleep((useconds_t)(AMS_IPC_BACKOFF_MS * (attempt + 1)) * 1000);
|
||||
}
|
||||
if (g_ipc_err == IPC_ERR_NONE) g_ipc_err = IPC_ERR_PROTO; /* 收到非 ACK/ERR 帧:不是「无回执」 */
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1228,9 +1257,14 @@ static int cmd_start(const char *spec, char *resp, int rn)
|
||||
/* launcher ACK ack.code 携带正幅度(busy=8,见 util.c `rc<0?-rc:MEOW_OK`),非二补。 */
|
||||
snprintf(detail, sizeof detail, "busy (app running)");
|
||||
emit_log("AMS start %s -> BUSY (app running)", pkg);
|
||||
} else if (rt == 0) {
|
||||
snprintf(detail, sizeof detail, "launcher refused code=%d", (int)code);
|
||||
emit_log("AMS start %s -> launcher refused code=%u(%d)", pkg, code, (int)code);
|
||||
} else {
|
||||
snprintf(detail, sizeof detail, "launcher offline");
|
||||
emit_log("AMS start %s -> launcher offline", pkg);
|
||||
/* rt!=0 只代表「这次没拿到回执」,不等于 launcher 死了(同 cmd_kill 注释)。
|
||||
* 响应串保留 "launcher offline"(MeowPanel 据此映射 503),日志补真实原因。 */
|
||||
snprintf(detail, sizeof detail, "launcher offline (%s)", ipc_err_str());
|
||||
emit_log("AMS start %s -> launcher offline (%s)", pkg, ipc_err_str());
|
||||
}
|
||||
snprintf(resp, rn, "%s %s/%s\n", strstr(detail, "ok forwarded") ? "OK" : "ERR",
|
||||
pkg, act[0] ? act : "(default)");
|
||||
@@ -1251,14 +1285,21 @@ static int cmd_kill(const char *pkg, char *resp, int rn)
|
||||
uint32_t code = 0;
|
||||
int rt = ams_request_ctl(MSG_AMS_KILL, &k, sizeof k, &code);
|
||||
if (rt != 0 && a->pid > 0) {
|
||||
/* 兜底:launcher 不在,直接 SIGTERM(与 launcher 的 SIGTERM 语义一致) */
|
||||
/* 兜底:launcher 没给回执 → 直接 SIGTERM(与 launcher 的 SIGTERM 语义一致)。
|
||||
* 注意 rt!=0 **不等于 launcher 死了**:主循环被 IPC 洪泛拖慢时 ACK 会晚于
|
||||
* AMS_IPC_TIMEOUT_MS,故日志写实际原因(connect failed / timeout, no reply / ...),
|
||||
* 而不是笼统的 "launcher offline"(2026-11-04 真机即此误判)。 */
|
||||
kill(a->pid, SIGTERM);
|
||||
emit_log("AMS kill %s -> direct SIGTERM (launcher offline)", pkg);
|
||||
emit_log("AMS kill %s -> direct SIGTERM (launcher %s)", pkg, ipc_err_str());
|
||||
} else if (rt != 0) {
|
||||
return snprintf(resp, rn, "ERR launcher offline: %s\n", pkg);
|
||||
/* 响应串保留 "launcher offline" 字样(MeowPanel api_ams_ctl 依此映射 503 AMS_UNAVAILABLE),
|
||||
* 只在括号里补真实原因,便于区分「连不上」与「连上但超时」。 */
|
||||
return snprintf(resp, rn, "ERR launcher offline (%s): %s\n", ipc_err_str(), pkg);
|
||||
} else if (code != MEOW_OK) {
|
||||
emit_log("AMS kill %s -> launcher refused code=%u", pkg, code);
|
||||
return snprintf(resp, rn, "ERR launcher refused: %s (code=%u)\n", pkg, code);
|
||||
/* launcher 明确拒绝:不改判为直杀(pid 可能已被回收,盲杀会打错进程),如实回错。
|
||||
* code 是负错误码的 uint32 呈现(-2 → 4294967294),括号里给有符号值便于对照。 */
|
||||
emit_log("AMS kill %s -> launcher refused code=%u(%d)", pkg, code, (int)code);
|
||||
return snprintf(resp, rn, "ERR launcher refused: %s (code=%d)\n", pkg, (int)code);
|
||||
}
|
||||
hist_push(a, "ams_kill", "terminate (via launcher)", (long)time(NULL), read_uptime_sec());
|
||||
int m = snprintf(resp, rn, "OK terminate %s\n", pkg);
|
||||
|
||||
Reference in new issue
Block a user