最小系统模式(Mini System Mode)
适用范围:
mc::app(mcapp)应用框架。 相关代码:libraries/mcapp/include/mc/app/mini_system_mode.h、libraries/mcapp/src/mini_system_mode.cpp, 启动管理入口libraries/mcapp/src/service.cpp(service::start())。
1. 背景与目标
最小系统模式是系统进入「恢复 / 配置还原」流程时使用的一种受限启动模式:本次启动只运行
mini_system.json 白名单内的组件,白名单之外的组件不允许启动。
进入最小系统模式时,/data 会被临时挂载为空的 tmpfs(见 rc.sysinit),持久化数据分区
暂不可用。此时若拉起白名单之外的业务组件,它们可能读不到 / 写坏正在恢复中的状态,
因此必须在启动阶段被挡在门外。
2. 触发与退出
- 触发:恢复流程(如 maca 的 sysctl/reset,写入
/dev/shm/trust/recovery_mode_flag)结束后 系统重启。rc.sysinit 检测到该标志时,以目录形式创建标记/dev/shm/.recovery_mode_on(chmod 744),并临时挂载/data,本次启动即进入最小系统模式。 - 退出:完成恢复后关闭最小系统开关并重启操作系统。重启后
/dev/shm清空、 标记消失,系统进入正常启动流程。
因此最小系统模式下,标记会持续存在整个启动周期;非白名单组件不会在本周期内启动。
3. 白名单 mini_system.json
-
位置:
/opt/bmc/trust/mini_system.json(部署根可用环境变量MC_TRUST_DIR覆盖,测试用)。 -
结构:
{ "applications": ["maca", "persistence", "redfish", "..."] } -
名单为程序区只读文件,不涉及动态修改;匹配粒度是组件短名 (D-Bus 全名
bmc.kepler.xxx的末段)。
名单的另一处既有消费方是本地调试配置持久化路径的选择 (
debug.cpp的get_local_db_path:白名单组件落到trust/persistence.local, 其余落到opt/bmc/persistence.local),两者统一走is_mini_system_app(),避免两套名单解析。
4. libmcpp 实现
4.1 对外接口(mc::app)
| 接口 | 说明 |
|---|---|
bool is_mini_system_mode() |
是否处于最小系统模式:标记是否存在。默认 /dev/shm/.recovery_mode_on(产品侧沿用恢复场景命名);可用 MC_MINI_SYSTEM_MODE_MARKER 覆盖(测试)。 |
bool is_mini_system_app(mc::string_view app_name) |
组件短名或 D-Bus 全名(统一取末段)是否在 mini_system.json 的 applications 名单中。 |
bool is_allowed_to_start_in_mini_system_mode(mc::string_view component_name) |
启动判定入口:未处于最小系统模式放行;处于该模式时仅白名单组件放行。组件名取末段短名后匹配。 |
4.2 启动管理:按 service 粒度
一个 application(进程)可以包含多个 service(例如 devmon 进程承载 devmon / hwproxy / hwdiscovery 三个 service),因此启动管理落在 service 粒度而不是 application 粒度:只阻止 名单外的 service 启动,同进程内名单内的 service 照常启动。
启动管理位于 mc::app::service::start() 入口(建立 D-Bus 连接、初始化引擎、注册微组件之前):
| service 身份 | 行为 |
|---|---|
| 无 context(如 application 的内部 root service) | 不参与启动管理,正常启动 |
| 未处于最小系统模式 | 正常启动 |
| 属于白名单 | 正常启动 |
| 处于最小系统模式 且不在白名单 | 不启动:置为 stopped 并直接返回,不建立 D-Bus/引擎连接、不注册微组件;同进程其他 service 不受影响 |
被跳过的 service 不进入 running:其 state() 为 stopped、has_dbus() 为 false、
micro_component() 为 nullptr,on_start() 不会被调用。service::stop() 对未启动(stopped)
的 service 保持幂等,避免对从未启动的 service 触发 on_stop()/on_cleanup()。
调用约定:被跳过时 service::start() 返回 true(不触发上层回滚),但该 service
并未运行。调用方若需区分「已运行」与「被跳过」,应检查 state()(为 stopped),
而不是仅依据 start() 的返回值。
说明:本实现不再在 application 层「死等」。application 层的整体阻塞会导致同一进程内名单内的 service 也无法启动(如 devmon 进程内 hwproxy 需要随最小系统启动),与「最小系统模式只启动 名单内组件」的语义不符。
4.3 身份来源与名单异常的取舍
service 身份取 D-Bus 服务名的末段短名(bmc.kepler.devmon → devmon),与
configure()/start() 日志作用域、dispatcher 派发所用短名一致——直接复用
mc::engine::service_short_name(),不再自行实现末段截取。
名单不可用(文件缺失、不可读、非 JSON 对象、无 applications 数组、解析异常)时的取舍为
fail-open:启动管理 is_allowed_to_start_in_mini_system_mode() 无法判定时空降级放行
并打印 wlog 告警,避免名单异常把整个恢复流程(含恢复链路组件)挡死;只有名单可用且组件
确实不在其中时才拦截。
注意区分两处语义:
- 启动管理(本模块
is_allowed_to_start_in_mini_system_mode):名单不可用 → 放行(fail-open)。- 白名单成员判定(
is_mini_system_app,亦被本地持久化路径选择复用):名单不可用 → 返回 false(fail-closed),即"不属于白名单"。两者面向不同用途,异常取舍不同。
5. 与既有实现的关系
| 实现 | 行为 |
|---|---|
| 老 skynet / Lua(ibmc 仓库) | is_recovery_mode()(旧命名,语义即最小系统模式)+ is_mini_system_app();app_mgmt/base.lua、app_preloader.lua 把非名单组件过滤 / 退出 |
| 产品 mcc(C 框架) | mcc_start() 中 mcc_check_recovery_mode() 在进程启动处拦截;is_mini_system_app() 静态缓存名单 |
| 本实现(C++ / mcapp) | 框架在 service::start() 入口自动执行启动管理,按 service 粒度跳过名单外组件,同一 application 内名单内 service 不受影响 |
6. 关键设计取舍
- service 粒度而非 application 粒度:一个进程多个 service 时,按 application 整体拦截会 误伤同进程内的名单组件。
- 「不启动」而非「死等」:被跳过的 service 直接不进入启动流程,进程内其余 service 照常工作; 避免 application 级阻塞。
- 不引入进程级全局解析缓存:
MC_TRUST_DIR在测试中按用例重定向,全局缓存易命中过期 目录的旧名单;保持逐次读取的既有行为,语义简单、可测试。 stop()幂等:跳过(未启动)的 service 在应用停止时不应触发on_stop()/on_cleanup()。- 名单不可用时启动管理 fail-open:名单缺失/损坏无法判定时降级放行(告警),避免把恢复流程
挡死;成员判定
is_mini_system_app仍保持 fail-closed(不属于白名单),两处语义在 §4.3 说明。 - 短名截取复用既有工具:统一走
mc::engine::service_short_name(),两个公共接口对同一输入 (短名/全名)判定一致,避免多处重复实现导致口径漂移。
7. 部署 / 配置要求
- 需要在最小系统模式下运行的组件,其 service 名末段(组件短名,如
devmon)必须出现在/opt/bmc/trust/mini_system.json名单中;多服务单进程组件按各 service 短名分别判定, 名单外的 service 不启动、名单内的照常启动。 - 恢复流程创建标记沿用产品侧路径
/dev/shm/.recovery_mode_on;测试通过MC_MINI_SYSTEM_MODE_MARKER/MC_TRUST_DIR重定向,不触碰部署路径。
8. 单元测试
libraries/mcapp/tests/test_mini_system_mode.cpp 覆盖:
- 白名单匹配(含空串、非法 JSON、无
applications字段等); is_mini_system_mode()随标记创建 / 清除翻转;- 启动管理判定
is_allowed_to_start_in_mini_system_mode():未处于最小系统模式放行、该模式下仅白名单 (含全名末段匹配)放行、名单缺失/损坏时 fail-open 降级放行; - service 启动管理:最小系统模式下名单外 service 不启动(
stopped、on_start未调用、无 D-Bus/微组件), 且跳过后stop()幂等。