# meow_svcd — MeowOS 服务管理器守护进程 类 systemd/systemctl 的角色:由 supervisor(meow_bootmenu/launcher)托管 `fork/exec` 启动的一个**独立**进程;每个服务又是 svcd 的独立子进程。svcd 极简: 无 `dlopen`、无外部依赖(仅 POSIX + Linux 基础接口),交叉编译为 **mipsel 静态** 打进固件(不入卡——拔卡 svcd 本体不消失)。 二进制入口:`patch_v2/meow_svcd/meow_svcd`(mipsel 静态,~98KB,<200KB 固件阈值)。 宿主编译:`patch_v2/meow_svcd/meow_svcd_host`。 > 本阶段(M2.54)范围:守护 + 双源 services.d + 宿主测试。**接入系统启动链**已定: > 固件预置 `etc/init.d/S89meow_svcd`(开机自启,`myow_svcd -f` 前台 + shell 重挂在 rcS 早期, > 先于 S90meow_bootmenu——因 S90 exec 引导选单后阻塞 rcS,故须排在 S90 之前)。 > 直接由 bootmenu/launcher `fork+exec` + `waitpid` 托管的方案仍成立(改固件有风险), > 作为备选——用 `-f` 前台即可被 waitpid。 --- ## 1. 路径契约 | 用途 | 默认值 | 覆盖方式 | |---|---|---| | TF 卡挂载点 | `/usr/data/mnt/sd_0`(SPEC §1.2) | `--card` / `MEOW_SVCD_CARD` | | 板载服务目录 | `/usr/data/meow/services.d`(首启预置/Data 区) | `--board` / `MEOW_SVCD_BOARD` | | TF 服务目录 | `/MeowOS/services.d/` | 随 `--card` | | 控制 socket | `/tmp/meow_svcd.sock` | `--sock` / `MEOW_SVCD_SOCK` | | 状态文件 | `/tmp/meow_svcd.status` | `--status` / `MEOW_SVCD_STATUS` | | pid 文件 | `/tmp/meow_svcd.pid` | `--pidfile` | | svcd 自身日志 | stderr(supervisor 捕获) | 追加 `--log` / `MEOW_SVCD_LOG` | > 状态文件写板载 `/tmp`(tmpfs,存活期)或可配置;svcd 日志走 stderr 便于 > supervisor 捕获(`-f` 前台时子进程 stdout/stderr 也落到同一 sink)。 ## 2. services.d 目录式配置(**双源**:板载 + TF,板载优先) 服务配置**两处都读**,一个服务一个 `.conf`: - **板载** `/usr/data/meow/services.d/.conf`:Data 区,恒定存在、权威优先; 首启由固件 `S89meow_svcd` 预置(如 `meow_mdns.conf`)。拔卡不丢。 - **TF** `/MeowOS/services.d/.conf`:随卡存在/消失。 **同名遮蔽规则**:先扫板载(权威),再扫 TF;若 TF 出现与板载同名服务 → **跳过 + WARN**「 板载与 TF 都存在,采用板载」。无双源冲突时行为不变。 极简 `key = value`,`#`/`;` 注释,空行忽略;**未知键忽略**(前向兼容)。行内注释可用 `\#` 转义。 | 字段 | 必填 | 类型 | 说明 | 默认 | |---|---|---|---|---| | `exec` | ✅ | 路径 | 服务二进制绝对路径(可卡上或固件) | 无(缺省 → 跳过该服务) | | `args` | 否 | 串 | 空格切分,支持单/双引号与 `\` 转义 | 无 | | `name` | 否 | 串 | 仅备忘;**实际名以文件名 `.conf` 为准** | =文件名 | | `env` | 否 | `K=V` | 可多条;子进程 exec 前 `setenv` | 无 | | `enabled` | 否 | 1/0 | 0=永不自动启动 | 0 | | `restart` | 否 | 枚举 | `always`/`on-fail`/`never` | `on-fail` | | `backoff_ms` | 否 | int | 崩溃后重启退避毫秒(固定退避) | 1000 | | `max_restart` | 否 | int | 连续重启次数上限,超限 → `failed` 停止;`-1`=无限 | 5 | | `cap` | 否 | 串 | 能力串如 `cap_net_bind_service,cap_net_raw`;**本阶段仅解析保留,不实装**(属后续) | 无 | | `trigger` | 否 | 枚举 | `boot`/`card-mount`/`wifi-up` | `card-mount` | `restart` 语义: - `always`:无论退出码都重启(受 `max_restart` 约束)。 - `on-fail`:仅非零退出/被信号杀(崩溃)重启;`exit 0` 不重启。 - `never`:崩溃不重启(记为 `failed`)。 `cap`/`wifi-up` 均属**扩展点**:本阶段解析并保留字符串 / 实现一个 `/sys/class/net//operstate` 的最小判定(本平台无 WiFi 接口时恒 `0`),真机能力绑定(`capset`)留待后续。 示例: ```ini # 网络栈辅助服务(固件二进制,卡上有配置) exec = /usr/bin/meow_netd args = --foreground -v env = MEOW_LOG_LEVEL=INFO enabled = 1 restart = on-fail backoff_ms = 2000 max_restart = 5 trigger = card-mount # 可选 setcap(本阶段不实装) # cap = cap_net_bind_service,cap_net_raw ``` **板载 mDNS 应答器示例**(固件预置 `/usr/data/meow/services.d/meow_mdns.conf`,首启建): ```ini # 自研 IPv4-only mDNS 应答器(固件 /usr/bin/meow_mdns;板载服务,拔卡不丢) exec = /usr/bin/meow_mdns args = enabled = 1 restart = on-fail backoff_ms = 2000 max_restart = 5 trigger = boot # 板载服务:boot 触发不依赖卡(恒定存在);cap 空=root 绑 5353/组播 cap = ``` > `trigger` 为单值枚举(`boot|card-mount|wifi-up`)。板载服务用 `boot`(恒存在即启动); > `wifi-up` 属扩展点(`/sys/class/net//operstate`),当前平台无无线接口恒 0。 ## 3. 状态机 ``` DISABLED enabled=0,永不自动启动 STOPPED 未运行(触发不满足 / 被停 / 等待退避) RUNNING 子进程存活 STOPPING 已发 SIGTERM,等退出(SVCD_STOP_GRACE_MS=2500 后 SIGKILL 兜底) FAILED 崩溃且不再重启(超 max_restart / restart=never) ``` 转换(主循环事件驱动 + 周期 tick): - **子进程死亡**(`waitpid` 匹配):若无 `stop_requested`/`restart_pending` → 按 `restart` 策略决定重建(先进 `backoff_ms` 退避)或落 `FAILED`/`STOPPED`; 连续稳定运行 `SVCD_STABLE_MS=30s` 后清零 `restart_count`。 - **`stop` 命令**:`STOPPING → STOPPED`(置 `user_stopped`,抑制触发自动拉起)。 - **`restart` 命令**:`STOPPING →` 收尸后原地 `respawn`(无视崩溃策略,清零计数)。 - **触发/禁用**:`tick` 中 `enabled && trigger 满足 && STOPPED && !user_stopped` → 启动; 运行中但 `!enabled || !trigger` → 停。 ## 4. 崩溃隔离 - **独立进程**:每个服务 `fork()+execv()`,svcd 只通过 `waitpid`/`SIGCHLD` 感知死亡; 无中间层内存共享、无 `dlopen`。 - **信号健壮**:`SIGPIPE` 置 `SIG_IGN`(服务关管道不杀 svcd);`SIGTERM`/`SIGCHLD` 处理器仅写自管道字节(async-signal-safe);`poll` 因信号中断 `EINTR` 续跑。 - **不读服务输出**:子进程继承 svcd stdio(stdout/stderr 落同一 sink),svcd **不 设管道读服务体**——服务写多少都不进 svcd 内存,天然规避「输出撑爆 → svcd 崩」。 - **后退避**:重启间隔受 `backoff_ms` 约束,防崩溃-重启热循环拖垮系统。 - **孤儿/僵死隔离**:`start_daemon()` 设 `PR_SET_CHILD_SUBREAPER`——服务若派生孙进程, 在服务(直接子进程)退出后由 svcd 收尸而非 reparent 到 init,避免游离/僵死进程;失败仅记日志继续。 - 服务崩**绝不**连带 supervisor:svcd 是 supervisor 的子进程且自身不死;服务是 svcd 的子进程,服务死只影响 svcd 内部一张表。 ## 5. 事件触发(低成本轮询) 每 `SVCD_SCAN_MS=1000ms` 一轮: - **卡挂载**:`card_present()`=目录存在 +(`services.d` 在 | 真实挂载点(子目录 `st_dev` 不同)| `MEOW_SVCD_CARD_PRESENT=0/1` 测试覆盖)。拔卡 → `on_card_out()` 优雅停所有服务**并保留 `restart_count`/`last_exit` 状态**;插卡 → `scan_and_reconcile()` 重建并按触发拉起。 - **`wifi-up`**:读 `/sys/class/net//operstate`,无无线接口恒 `0`(扩展点)。 - `boot`:TF 配置在卡 → 随卡存在即启动(拔卡即停);**板载配置**(`src=board`)→ 恒定存在即启动(不依赖卡,拔卡保留,供 mdnsd 等常驻服务)。 ## 6. CLI / 控制协议 守护进程默认后台常驻(无子命令即启动);`-f` 前台(supervisor 托管用)。控制走 AF_UNIX 单行文本: ``` meow_svcd status [name|all] # 列出各服务+全局(daemon 未运行则打印状态文件) meow_svcd reload # 重读卡配置 meow_svcd start meow_svcd stop meow_svcd restart meow_svcd pid ``` 应答:`OK` / `ERR no-such-service` / 多列对齐表格。每服务一行:服务名/状态/pid/ 重启次数+`max_restart`/`backoff_ms`/`trigger`/来源(`board`|`tf`)/最近退出码/退出时间/`exec` 路径; TTY 时带 ANSI 配色(服务名=加粗青、RUNNING=绿、FAILED=红、STOPPED·STOPPING=黄、DISABLED=灰、 数字=黄、表头/分隔线=dim),stdout 非 TTY(重定向/SSH 管道)自动去色。 ### 6.1 `systemctl` 式 CLI 用法(SSH 终端示例) 守护已由固件 `etc/init.d/S89meow_svcd` 开机自启,SSH 登录设备后**直接敲**命令即可,无需先知道 socket: ```bash meow_svcd status # 列出所有服务 + 全局状态(TTY 带色;管道/重定向自动去色) # meow_svcd status # card_present=1 wifi_up=0 services=3 # NAME STATE PID RESTART BACKOFF TRIGGER SRC EXIT EXIT_TIME EXEC # ------------------------------------------------------------------------------ # mdnsd running 1234 0/5 2000ms boot board - - /usr/bin/meow_mdns # meow_netd stopped 0 0/5 1000ms card-mount tf - - /usr/bin/meow_netd # ntpc disabled 0 0/inf 1000ms card-mount tf - - /usr/bin/ntpc meow_svcd status mdnsd # 只显示单个服务(其余列同;`status all` 等效于无参) meow_svcd restart mdnsd # 重启单个服务(如板载 mDNS 应答器) meow_svcd start meow_netd # 启动单个服务 meow_svcd stop all # 停止全部服务 meow_svcd reload # 重读板载 + TF 双源 services.d 配置 meow_svcd pid # 打印守护进程 pid ``` - **socket**:控制走 AF_UNIX 单行文本,默认 `/tmp/meow_svcd.sock`(`--sock`/`MEOW_SVCD_SOCK` 可改)。 守护**未运行**时命令回退打印状态文件 `/tmp/meow_svcd.status`(或 `card_present=0 services=0`)并以退出码 1 返回。 - **守护前台**:`-f`/`--foreground` —— 守护自身跑前台,供 supervisor `fork/exec`+`waitpid` 托管; 默认(无子命令)则为后台常驻。 - 应答:成功 `OK`(`pid`→pid 数字);服务不存在 `ERR no-such-service`;`status` 返回对齐表格, 行内含 `state/pid/restart(重启计数)/exit(上次退出码)/trigger/来源/backoff_ms/exec`。 ## 7. 编译 ```bash ./build_svcd.sh # mipsel 静态 → meow_svcd(0 warning) ./build_svcd.sh host # 宿主编译 → meow_svcd_host(集成测试) ``` ## 后续(不在本阶段) - ~~接入 supervisor 启动链~~(已改由 `etc/init.d/S89meow_svcd` 开机自启;bootmenu/launcher `fork+exec` + `waitpid` 托管仍为备选)。 - `cap` 真机实装(平台是否支持 capset/pthread caps 待确认;需先探测)。 - `wifi-up` 事件细节(是否用 `/proc/net/wireless` / 重试文件,接口判定收紧)。 - svcd/服务日志落位到卡 `MeowOS/logs/` 的行为(当前 svcd 日志 stderr + 可选 `--log`)。