diff --git a/APPS.md b/APPS.md index d03c298..2eea539 100644 --- a/APPS.md +++ b/APPS.md @@ -194,7 +194,7 @@ - **⚠️ 应用启动后须防 goodix 幽灵触摸**(v6.8,Bug 2 根因):goodix 在启动触摸的尾部发出一段幽灵触摸(实测启动后 1.2-2.25s,§18.16),无防护时应用会**误把幽灵当下按而立即退出画 BYE**,且 launcher 菜单会把同一幽灵重放成行点击而**重开应用**(用户看到「bye 后又回 hello 主界面、需再点一次」)。hello v6.8 增加 `COOLDOWN_MS=2500`:启动后前 2.5s 忽略一切按下边沿,只有用户**真正**的下按才触发退出。launcher 侧同步在应用退出后 drain + 冷却(见 §4.7)。 - 沉浸应用写 `/tmp/meowapp_state`,处理 `SIGUSR1` 作为「返回事件」 - 应用异常卡死 → 虚拟返回键(若可见)→ SIGKILL;沉浸态卡死 → 音量减连按 3 次(强杀序列) -- **应用日志**:launcher 通过环境变量 `MEOW_LOG_DIR` 告知 TF 卡日志目录(无卡时不设),应用自行决定文件名(示例 hello 写 `apps//logs/_.log`;[源:审计 2026-08-30]);启动时先 drain 输入队列、只响应"按下边沿"退出(释放事件/轨迹事件忽略) +- **应用日志**:launcher 通过环境变量 `MEOW_LOG_DIR` 直接告知 TF 卡上该应用的日志目录 `MeowOS/logs//`(无卡时不设),应用写 `_.log`([源:M2.x 简化 logs/apps//logs → logs/]);启动时先 drain 输入队列、只响应"按下边沿"退出(释放事件/轨迹事件忽略) ### 3.6 应用运行期卡移除(M2.11 最佳努力) @@ -275,12 +275,12 @@ meow_launcherd --sim-exit N # 菜单打开 N 毫秒后模拟退出 ### 4.5 统一日志系统(MeowAPPs 组件共用) -- **默认位置**:TF 卡 `/usr/data/mnt/sd_0/MeowOS/logs/meow_.log`(如 `meow_20260828_020123.log`;`bootts` = 当前墙钟 − /proc/uptime,同一 boot 会话内多次启动共用同一文件)。**系统日志**(launcher/watchdog 等 MeowOS 组件)写此文件;**应用日志**另按 `apps//logs/_.log` 分文件(见本 §4.5「组件环境变量」)。⚠️ `meow_bootmenu` 仍用 `MeowAPPs/logs`(代码侧不一致;bootmenu 与 launcher 日志目录**待统一为 `MeowOS/logs`**,[源:审计 2026-08-30]) +- **默认位置**:TF 卡 `/usr/data/mnt/sd_0/MeowOS/logs/meow_.log`(如 `meow_20260828_020123.log`;`bootts` = 当前墙钟 − /proc/uptime,同一 boot 会话内多次启动共用同一文件)。**系统日志**(launcher/watchdog 等 MeowOS 组件)写此文件;**应用日志**另按 `logs//_.log` 分文件(`` 即该应用目录;见本 §4.5「组件环境变量」)。⚠️ `meow_bootmenu` 仍用 `MeowAPPs/logs`(代码侧不一致;bootmenu 与 launcher 日志目录**待统一为 `MeowOS/logs`**,[源:审计 2026-08-30])。旧布局 `logs/apps//logs/` 已废弃:launcher 启动时一次性迁移到 `logs//` 后删除 - **无 TF 卡 / 卡晚挂载(S90 早于卡挂载)**:**开不了卡日志**——日志落 **/tmp 启动缓冲**(`/tmp/meow_boot.log`,上限 256KB 滚动),**绝不写板载存储**(/usr/data 只剩 ~10MB);**卡出现后自动迁移**(主循环 ~1s 周期重试 + 输入设备重试循环内也检查;无需开菜单)——`log_poll_card()` 打开卡日志并把 /tmp 缓冲整段搬入后删除 /tmp 文件 - **容量上限**:logs/ 目录总量 1GB,开机时超限按 mtime 删最旧 - **组件环境变量**: - `MEOW_LOG_FILE` → meow_watchdog:追加写同一 boot 日志文件(无该变量则走 stderr) - - `MEOW_LOG_DIR` → 应用:日志目录(每 App `MeowOS/logs/apps//logs/`,应用写 `_.log`;[源:审计 2026-08-30]) + - `MEOW_LOG_DIR` → 应用:日志目录(每 App `MeowOS/logs//`,应用写 `_.log`;[源:M2.x 简化,弃 `logs/apps//logs/`]) - 内容:时间戳 + 全部按键边沿/菜单/应用事件(真机按键证据) - PID 文件:`/usr/data/meow_launcherd.pid`;播放器发现:扫描 `/proc/N/exe` == `/usr/bin/hiby_player` diff --git a/patch_v2/files_app/files b/patch_v2/files_app/files index 8178819..b3f4a05 100755 Binary files a/patch_v2/files_app/files and b/patch_v2/files_app/files differ diff --git a/patch_v2/meow_launcherd/app_launcher.c b/patch_v2/meow_launcherd/app_launcher.c index e2e848f..b8949d1 100644 --- a/patch_v2/meow_launcherd/app_launcher.c +++ b/patch_v2/meow_launcherd/app_launcher.c @@ -807,10 +807,12 @@ int launch_app(struct app *a) /* child: exec the app. M2.12 注入 MEOW_LOG_DIR + MEOW_DATA_DIR/MEOW_CACHE_DIR * (exFAT 无 symlink,数据目录用 env 传给应用)。PATH 继承 launcher(含卡上 opt/bin * 若在),卡不在则回退普通 PATH。M2.52 追加注入 MEOW_LOG_APP(app_id)+MEOW_LOG_LEVEL - * (读全局 log.level),使 SDK meow_log() 按 app 分离写 apps/.log 并做级别过滤。 */ + * (读全局 log.level),使 SDK meow_log() 按 app 分离写 logs//_.log 并做级别过滤。 + * M2.x 简化:MEOW_LOG_DIR 从 logs/apps//logs 改注入 logs/(目录不存在则建)。 */ close(execpipe[0]); char *argv[] = { a->exec, NULL }; char env_path[320], env_log[320], env_data[320], env_cache[320]; + char app_log_dir[288]; char env_app[MEOW_APP_ID_MAX + 16], env_lvl[48]; char env_fpng[400], env_fidx[400], env_fapp[96]; char env_mf[24]; @@ -823,7 +825,15 @@ int launch_app(struct app *a) snprintf(env_path, sizeof env_path, "PATH=%s", cp ? cp : "/usr/bin:/bin"); envp[ne++] = env_path; envp[ne++] = "HOME=/root"; - if (log_dir[0]) { snprintf(env_log, sizeof env_log, "MEOW_LOG_DIR=%s", log_dir); envp[ne++] = env_log; } + /* M2.x 简化:MEOW_LOG_DIR 注入 logs/(id=app_id,空则 "app",与 SDK app 分离前缀一致)。 + * 目录不存在则用 meow_mkdir_p 建(幂等),失败不阻塞注入——SDK 侧还有 mkdir_p 兜底。 */ + if (log_dir[0]) { + const char *logid = a->id[0] ? a->id : "app"; + snprintf(app_log_dir, sizeof app_log_dir, "%s/%s", log_dir, logid); + meow_mkdir_p(app_log_dir); + snprintf(env_log, sizeof env_log, "MEOW_LOG_DIR=%s", app_log_dir); + envp[ne++] = env_log; + } /* M2.52:MEOW_LOG_APP=app_id(=app.json.package,缺省回退目录名;与注册表一致;无 id 则跳过,SDK 兜底 "app") */ if (a->id[0]) { snprintf(env_app, sizeof env_app, "MEOW_LOG_APP=%s", a->id); envp[ne++] = env_app; } /* M2.52:MEOW_LOG_LEVEL 读全局 log.level(error/warn/info/debug→索引0/1/2/3), diff --git a/patch_v2/meow_launcherd/meow_launcher_internal.h b/patch_v2/meow_launcherd/meow_launcher_internal.h index 9830bb3..784dddd 100644 --- a/patch_v2/meow_launcherd/meow_launcher_internal.h +++ b/patch_v2/meow_launcherd/meow_launcher_internal.h @@ -530,6 +530,8 @@ extern char ipc_sock_path[MEOW_IPC_SOCK_LEN]; extern char log_dir[256]; extern int log_fd; extern char log_file[256]; +/* M2.x 应用日志目录简化(apps//logs → ):mkdir -p 幂等;迁移用。 */ +int meow_mkdir_p(const char *path); extern uint32_t menu_bg; extern int menu_opened_blank; extern int meow_auto_slept; diff --git a/patch_v2/meow_launcherd/meow_launcherd b/patch_v2/meow_launcherd/meow_launcherd index 4961767..2f18786 100755 Binary files a/patch_v2/meow_launcherd/meow_launcherd and b/patch_v2/meow_launcherd/meow_launcherd differ diff --git a/patch_v2/meow_launcherd/util.c b/patch_v2/meow_launcherd/util.c index 5898bd9..9b0f9d3 100644 --- a/patch_v2/meow_launcherd/util.c +++ b/patch_v2/meow_launcherd/util.c @@ -2,6 +2,9 @@ /* 本文件由拆分工具从 meow_launcherd.c 按职责搬移;逻辑与原文 0-diff(仅 static 可见性按跨模块需求调整)。*/ #include "meow_launcher_internal.h" +/* M2.x 应用日志目录简化:一次性迁移 logs/apps//logs → logs/(定义见下,log_open_card 先调用)。 */ +static void log_migrate_apps(void); + int log_fd = -1; /* file log fd on the TF card */ char log_dir[256] = ""; /* "MeowOS/logs" on card, or "" */ @@ -133,6 +136,7 @@ int log_open_card(void) { if (log_fd >= 0) return 0; if (!card_available()) return -1; + log_migrate_apps(); /* M2.x 一次性迁移:logs/apps//logs → logs/(幂等,见下) */ char ts[64]; boot_timestamp(ts, sizeof ts); snprintf(log_dir, sizeof log_dir, "%s", LOG_DIR_CARD); @@ -144,6 +148,117 @@ int log_open_card(void) } +/* M2.x 应用日志目录简化:mkdir -p(幂等,已存在 EEXIST 视为成功)。返回 0=成功 -1=失败。 + * 供 launcher 为每 App 建新布局日志目录 logs// 用(沿用 SDK 同款逐分量 mkdir)。 */ +int meow_mkdir_p(const char *path) +{ + char tmp[256]; + size_t len, i; + if (!path || !path[0] || strlen(path) >= sizeof tmp) return -1; + len = strlen(path); + memcpy(tmp, path, len + 1); + if (tmp[len - 1] == '/') tmp[len - 1] = '\0'; /* 去尾斜杠 */ + for (i = 1; i <= strlen(tmp); i++) { + if (tmp[i] == '/' || tmp[i] == '\0') { + char ch = tmp[i]; + tmp[i] = '\0'; + if (tmp[0] && mkdir(tmp, 0755) != 0 && errno != EEXIST) return -1; + tmp[i] = ch; + } + } + return 0; +} + + +/* M2.x 一次性迁移(幂等,代码可后续移除):旧布局 LOG_DIR_CARD/apps//logs/.log + * 并入新布局 LOG_DIR_CARD//.log,然后删除旧 apps/ 整树。卡可用时随 log_open_card + * 跑一次;apps/ 已不存在则直接返回。同卡同 fs,文件用 rename() 移动(目标同名被覆盖——迁移 + * 内只挪旧文件,不写新日志)。总容量上限纪律不变:应用日志仍由 SDK 按 App 50MB 清理,boot 日志 + * 仍走 log_cleanup_old()。 */ +static void log_rm_rf(const char *path) +{ + struct stat st; + if (stat(path, &st) != 0) return; + if (S_ISDIR(st.st_mode)) { + DIR *d = opendir(path); + if (d) { + struct dirent *e; + while ((e = readdir(d)) != NULL) { + if (e->d_name[0] == '.' && (e->d_name[1] == '\0' || + (e->d_name[1] == '.' && e->d_name[2] == '\0'))) continue; + char sub[512]; + snprintf(sub, sizeof sub, "%s/%s", path, e->d_name); + log_rm_rf(sub); + } + closedir(d); + } + rmdir(path); + } else { + unlink(path); + } +} + +static void log_migrate_apps(void) +{ + char old_root[512], old_dir[512], old_logs[512], new_dir[512]; + DIR *d, *dl, *dd; + struct dirent *e; + + snprintf(old_root, sizeof old_root, "%s/apps", LOG_DIR_CARD); + d = opendir(old_root); + if (!d) return; /* 旧路径不存在,无需迁移 */ + + while ((e = readdir(d)) != NULL) { + struct stat st; + if (e->d_name[0] == '.') continue; + snprintf(old_dir, sizeof old_dir, "%s/%s", old_root, e->d_name); + if (stat(old_dir, &st) != 0 || !S_ISDIR(st.st_mode)) continue; + + snprintf(new_dir, sizeof new_dir, "%s/%s", LOG_DIR_CARD, e->d_name); + if (meow_mkdir_p(new_dir) != 0) continue; /* 新目录建不了,跳过该 App */ + + /* 旧布局 apps//logs/.log → logs//.log */ + snprintf(old_logs, sizeof old_logs, "%s/logs", old_dir); + dl = opendir(old_logs); + if (dl) { + struct dirent *el; + while ((el = readdir(dl)) != NULL) { + struct stat stl; + char src[512], dst[512]; + if (el->d_name[0] == '.') continue; + snprintf(src, sizeof src, "%s/%s", old_logs, el->d_name); + if (stat(src, &stl) != 0 || !S_ISREG(stl.st_mode)) continue; + snprintf(dst, sizeof dst, "%s/%s", new_dir, el->d_name); + rename(src, dst); + } + closedir(dl); + } + + /* 早期设计若直接把 .log 散在 apps// 下,一并并入(跳过已处理的 logs/ 子目录) */ + dd = opendir(old_dir); + if (dd) { + struct dirent *ed; + while ((ed = readdir(dd)) != NULL) { + struct stat stf; + char src[512], dst[512]; + if (ed->d_name[0] == '.') continue; + if (strcmp(ed->d_name, "logs") == 0) continue; + snprintf(src, sizeof src, "%s/%s", old_dir, ed->d_name); + if (stat(src, &stf) != 0 || !S_ISREG(stf.st_mode)) continue; + snprintf(dst, sizeof dst, "%s/%s", new_dir, ed->d_name); + rename(src, dst); + } + closedir(dd); + } + + log_rm_rf(old_dir); + } + closedir(d); + rmdir(old_root); /* apps/ 清空则删;非空则忽略 */ + fprintf(stderr, "log_migrate: logs/apps/* -> logs/ done\n"); +} + + /* flush the /tmp boot buffer into the file log (once the card mounts) */ void log_poll_card(void) {