208 lines
11 KiB
Markdown
208 lines
11 KiB
Markdown
# 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 服务目录 | `<card>/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,板载优先)
|
||
|
||
服务配置**两处都读**,一个服务一个 `<name>.conf`:
|
||
|
||
- **板载** `/usr/data/meow/services.d/<name>.conf`:Data 区,恒定存在、权威优先;
|
||
首启由固件 `S89meow_svcd` 预置(如 `mdnsd.conf`)。拔卡不丢。
|
||
- **TF** `<card>/MeowOS/services.d/<name>.conf`:随卡存在/消失。
|
||
|
||
**同名遮蔽规则**:先扫板载(权威),再扫 TF;若 TF 出现与板载同名服务 → **跳过 +
|
||
WARN**「<name> 板载与 TF 都存在,采用板载」。无双源冲突时行为不变。
|
||
|
||
极简 `key = value`,`#`/`;` 注释,空行忽略;**未知键忽略**(前向兼容)。行内注释可用 `\#` 转义。
|
||
|
||
| 字段 | 必填 | 类型 | 说明 | 默认 |
|
||
|---|---|---|---|---|
|
||
| `exec` | ✅ | 路径 | 服务二进制绝对路径(可卡上或固件) | 无(缺省 → 跳过该服务) |
|
||
| `args` | 否 | 串 | 空格切分,支持单/双引号与 `\` 转义 | 无 |
|
||
| `name` | 否 | 串 | 仅备忘;**实际名以文件名 `<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/<iface>/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/mdnsd.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/<iface>/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` 约束,防崩溃-重启热循环拖垮系统。
|
||
- 服务崩**绝不**连带 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/<iface>/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 <name|all>
|
||
meow_svcd stop <name|all>
|
||
meow_svcd restart <name|all>
|
||
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`)。
|