feat(crash): meow_crash 系统级崩溃捕获 (agent-187) — svcd 服务异常退出上报(AF_UNIX best-effort,不崩svcd)+dmesg增量扫segfault/oops; 落盘 TF MeowOS/logs/crash_<bootts>.log 无卡回退/tmp+迁移; CLI dump/tail; S89 预置 crash.conf(主会话补)

meow_crash 62781084; meow_svcd da6ebc66→605646c4(仅+68行上报挂点); 宿主5场景全过(上报链路/dmesg增量/无卡迁移/铁律验证/CLI)
This commit is contained in:
MeowOS committed 2026-08-31 04:39:34 +08:00
1 parent 243148e440
commit cf86cbd387
10 files changed
+885 -1

No files matched your search

+16
View File
@@ -94,6 +94,22 @@ EOF
chmod 644 /usr/data/meow/services.d/meow_svc_power.conf
echo -n "provisioned power.conf "
fi
if [ -f /usr/data/meow/services.d/meow_crash.conf ]; then
echo -n "crash.conf exists "
else
cat > /usr/data/meow/services.d/meow_crash.conf <<'EOF'
exec=/usr/bin/meow_crash
args=
enabled=1
restart=on-fail
backoff_ms=2000
max_restart=5
trigger=boot
cap=
EOF
chmod 644 /usr/data/meow/services.d/meow_crash.conf
echo -n "provisioned crash.conf "
fi
else
echo -n "skip svc confs (/usr/data/meow not writable) "
fi
Binary file not shown.
+68 -1
View File
@@ -97,6 +97,8 @@
#define SVCD_SOCK_PATH_MAX 108
#define SVCD_SIGCHLD_CHAR 'C' /* 自管道字节(拉起 poll) */
#define SVCD_SIGTERM_CHAR 'T'
#define SVCD_CRASH_SOCK "/tmp/meow_crash.sock" /* meow_crash 崩溃上报 socket(best-effort) */
#define SVCD_CRASH_POLL_MS 150 /* 上报 connect 的排空等待上限(绝不重试) */
/* ---- ANSI 终端配色(status 回显用;仅客户端 stdout 为 TTY 时保留,非 TTY 去色) ---- */
#define AN_RESET "\033[0m"
@@ -532,6 +534,65 @@ static void sig_term(int sig)
(void)w;
}
/* /proc/uptime 首字段 → 秒(整数)。失败返回 0。 */
static long svcd_uptime(void)
{
int fd = open("/proc/uptime", O_RDONLY);
if (fd < 0) return 0;
char b[64];
ssize_t n = read(fd, b, sizeof b - 1);
close(fd);
if (n <= 0) return 0;
b[n] = '\0';
return (long)atof(b);
}
/* 服务异常退出 → 上报 meow_crash(best-effort,绝不阻塞/重试 svcd)。
*
* 铁律:服务崩不崩 svcd。上报全程非阻塞:
* - 客户端 socket 置 O_NONBLOCK;
* - connect 失败即放弃;若 EINPROGRESS/EWOULDBLOCK,短 poll(POLLOUT) ≤150ms 排空
* 本地 listen backlog,仍不可写即丢弃;
* - 单行小消息一次 write(失败即弃),绝不重试。
* meow_crash 不在线(connect ECONNREFUSED 等)→ 静默丢,不影响 svcd 任何状态。
*/
static void notify_crash(const char *name, int status)
{
const char *kind;
int num;
if (WIFSIGNALED(status)) { kind = "sig"; num = WTERMSIG(status); }
else { kind = "exit"; num = WIFEXITED(status) ? WEXITSTATUS(status) : 1; }
char msg[256];
snprintf(msg, sizeof msg, "SVC %s %s %d %ld %ld\n",
name, kind, num, (long)time(NULL), svcd_uptime());
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;
safe_strcpy(a.sun_path, sizeof a.sun_path, SVCD_CRASH_SOCK);
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, SVCD_CRASH_POLL_MS);
if (r <= 0 || !(pf.revents & POLLOUT)) { close(fd); return; }
} else {
close(fd);
return;
}
}
ssize_t w = write(fd, msg, strlen(msg));
(void)w;
close(fd);
}
/* 排空 SIGCHLD 自管道,waitpid 匹配所有已退出子进程。 */
static void reap_children(void)
{
@@ -545,7 +606,13 @@ static void reap_children(void)
for (int i = 0; i < g_nsvc; i++)
if (g_svcs[i].pid == pid) { idx = i; break; }
if (idx < 0) { svcd_log("reap unknown pid=%d", (int)pid); continue; }
apply_exit(&g_svcs[idx], status);
svc_t *s = &g_svcs[idx];
/* 服务异常退出(非 0 / 被信号杀)且非受控 stop/restart → 上报 meow_crash。
* 必须在 apply_exit 前判(apply_exit 会清 stop_requested/restart_pending)。 */
int failed = !(WIFEXITED(status) && WEXITSTATUS(status) == 0);
if (failed && !s->stop_requested && !s->restart_pending)
notify_crash(s->name, status);
apply_exit(s, status);
}
build_status_file();
}
Binary file not shown.