libmc4lua
版本信息
| 项目 | 内容 |
|---|---|
| 组件版本 | 1.130.38 |
| 首发版本 | openUBMC 26.09 |
| 文档作者 | openUBMC 社区 |
| 最后更新 | 2026-09-15 |
| 许可证 | Mulan PSL v2 |
1. 组件概述
1.1 组件简介
libmc4lua 是 openUBMC 的 Lua 微组件基础框架,组件类型为 library。它向 Lua 业务进程提供面向对象、日志、D-Bus/MDB 资源树、ORM 数据库、参数校验、任务管理、MicroComponent 标准接口以及若干 C/C++ 扩展模块。
组件不作为独立业务服务常驻注册自己的 D-Bus 服务名。Lua 库安装到 /opt/bmc/libmc/lualib,C 扩展安装到 /opt/bmc/libmc/luaclib,Skynet 服务脚本安装到 /opt/bmc/libmc/service。CMake 工程名为 libmc,运行时根目录为 /opt/bmc/libmc/。业务组件通过 require 加载本库,在宿主 Skynet 进程内运行。
许可证为 Mulan PSL v2。mds/service.json 中的构建选项包括 chipv2_enable(默认 false)和 hpcomm_enable(默认 false)。
1.2 解决什么问题
openUBMC 的业务组件大量使用 Lua,需要统一完成以下基础能力,而不是由各组件自行封装:
- 在 Lua 中实现类继承、对象生命周期和资源树上树。
- 通过会话总线访问 MDB 资源树对象、属性和方法。
- 将 SQLite 持久化封装为面向对象的 ORM 接口。
- 统一日志、错误消息、调用上下文和参数校验。
- 为异步长流程提供 TaskService 任务对象。
- 为每个微组件自动暴露
bmc.kepler.MicroComponent*标准接口。
1.3 核心功能
- 面向对象:
mc.class提供类定义、继承、ctor/pre_init/init生命周期。 - 应用程序骨架:
mc.app_base、mc.service_app_base、mc.client_app_base构造服务名、对象路径并打开会话总线。 - 日志:
mc.logging封装 syslog 调试日志、操作日志和限流输出。 - D-Bus:
dbus.core提供 C 层连接/消息封装,sd_bus提供 Lua 会话总线call/pcall/signal/match。 - 资源树:
mc.mdb提供代理对象;mc.mdb.mdb_service通过bmc.kepler.maca的bmc.kepler.Mdb查询路径与对象。 - 自发现与信号:
mc.mdb.object_manage注册对象增删回调;mc.mdb.subscribe_signal订阅属性变更和接口增删。 - MicroComponent:注册健康检查、配置导入导出、Dump、平滑重启/复位等标准接口回调。
- 任务管理:
mc.mdb.task_mgmt创建、更新和销毁 TaskService 任务对象。 - 数据校验:
mc.validate提供枚举、长度、范围、正则和 JSON 校验。 - ORM:
database将 SQLite 表映射为 Lua 类,支持 insert/select/update/delete。 - IPMI 辅助:
mc.ipmi转发请求到bmc.kepler.ipmi_core并封装完成码响应。 - C 扩展:cjson、lsqlite3、worker、utils、kmc、syslog 等无
lib前缀的.so。
1.4 关键术语表
| 术语 | 解释 |
|---|---|
| libmc | CMake 工程名与运行时目录名;Lua 模块搜索根为 /opt/bmc/libmc/。 |
| MDB | 管理数据库 / 资源树,对象通过 D-Bus 路径和接口暴露。 |
| 代理对象 | mc.mdb.get_object 返回的对象,属性读写和方法调用被转发为 D-Bus 访问。 |
| MicroComponent | 微组件标准对象,路径形如 /bmc/kepler/<app>/MicroComponent。 |
| maca | 资源树索引服务,服务名 bmc.kepler.maca,路径 /bmc/kepler/MdbService。 |
| SDBus | sd_bus 模块对会话总线连接的 Lua 封装。 |
_PACKED | 在代理对象方法名后追加该后缀时,返回结构化响应而不是多返回值。 |
| ORM | 将 SQLite 表映射为 Lua 类,用对象代替手写 SQL。 |
| Skynet | 宿主协程框架;libmc4lua 的服务脚本和 worker 线程运行在 Skynet 进程内。 |
1.5 外部交互边界图
构建依赖见 mds/service.json,包括 kmc、signature_verify_cbb_library、sqlite3、json、libsoc_adapter 等。运行时还依赖 Skynet、glib、libdbus、securec 以及宿主进程的会话总线。组件自身没有独立监听端口。
2. API 使用说明与示例
以下接口均为 Lua 本地接口。除 MicroComponent 标准接口由框架代为注册到宿主组件的 D-Bus 树上外,不能通过 busctl 直接调用 libmc4lua 本身。
宿主进程通过 dist/config.cfg 将 Lua 搜索路径指向 /opt/bmc/libmc/lualib/?.lua 和 /opt/bmc/libmc/luaclib/?.so。
local Class = require 'mc.class'
local log = require 'mc.logging'
local sdbus = require 'sd_bus'
local mdb = require 'mc.mdb'2.1 模块加载与部署路径
功能说明
说明库的安装布局和 require 搜索路径,是调用其他 API 的前提。
参数说明
| 安装项 | 路径 | 说明 |
|---|---|---|
| Lua 库 | /opt/bmc/libmc/lualib | src/lualib/ 与代码生成产物 gen/。 |
| C 扩展 | /opt/bmc/libmc/luaclib | 无 lib 前缀的 .so,如 dbus.so、cjson.so。 |
| Skynet 服务 | /opt/bmc/libmc/service | sd_bus.lua、harbor.lua。 |
| 启动配置 | /opt/bmc/libmc/config.cfg | 由 dist/config.cfg 安装,设置 lua_path / lua_cpath。 |
| C 头文件 | include/luawrapper.h 等 | 供其他 C/C++ 组件链接 luawrapper。 |
config.cfg 还会把 /opt/bmc/lualib、/opt/bmc/luaclib、/opt/bmc/apps 和 /opt/bmc/plugins 加入搜索路径。
返回值与异常
require 失败时 Lua 抛出 module '...' not found。C 模块符号名须与 luaopen_* 对应,例如 require('dbus.core') 加载 dbus.so 并调用 luaopen_dbus_core。
应用场景
业务组件启动、本地 UT 配置 package.path / package.cpath、排查模块找不到问题。
限制条件
- 不要把本库的
.so单独拷贝到其他目录而不更新package.cpath。 hpcomm相关 Lua/C 模块仅在hpcomm_enable=true时安装;snapshot、memstat仅在非 Release / 指定条件下编译。
调试示例
print(package.path)
print(package.cpath)
assert(require 'mc.logging')
assert(require 'sd_bus')
assert(require 'cjson')2.2 mc.class 面向对象
功能说明
require 'mc.class' 返回一个类工厂函数。调用该函数得到类表;通过 ctor、pre_init、init 管理实例生命周期,通过传入父类实现继承。
参数说明
| 方法名 | 入参类型 | 出参类型 | 描述 | 取值范围 |
|---|---|---|---|---|
Class(super, meta, array_map_store, ...) | 可选父类等 | class | 定义新类 | super 可为 nil |
cls:ctor(...) | 任意 | 无 | 构造阶段,先调用父类 ctor | 由业务定义 |
cls:pre_init() | 无 | 无 | new 在 ctor 之后同步调用 | 可选 |
cls:init() | 无 | 无 | new 在 pre_init 之后同步调用 | 可选 |
cls.new(...) | 与 ctor 相同 | 实例 | 创建对象并跑完生命周期 | 自动调用 |
返回值与异常
new 返回实例。若类设置了 __capture_error,生命周期函数中的错误会被捕获;否则按普通 Lua 错误抛出。
应用场景
定义服务类、MDB 对象类、ORM 行对象。mc.service_app_base 中的 Service 即 Class(AppBase)。
限制条件
- 必须通过
cls.new(...)创建实例,直接当函数调用类表不会执行完整生命周期。 pre_init/init在new内同步执行,不要在其中做长时间阻塞而不切协程。
调试示例
local Class = require 'mc.class'
local Animal = Class()
function Animal:ctor(name)
self.name = name
end
local Cat = Class(Animal)
function Cat:ctor(name)
self.sound = 'meow'
end
local c = Cat.new('tom')
assert(c.name == 'tom')2.3 mc.logging 日志
功能说明
require 'mc.logging' 返回进程内日志单例,底层通过 syslog.core 输出。支持调试级别过滤、操作审计日志、按周期限流和按 SystemId 前缀。
参数说明
属性参数说明
| 级别常量 | 说明 |
|---|---|
MASS | 海量调试 |
DEBUG | 调试 |
INFO | 一般信息 |
NOTICE | 重要通知(默认调试级别) |
WARN | 警告 |
ERROR | 错误 |
输出类型:OUT_TYPE_FILE / OUT_TYPE_LOCAL / OUT_TYPE_SHM,字符串分别为 file / local / shm。
方法参数说明
| 方法名 | 入参类型 | 出参类型 | 描述 | 取值范围 |
|---|---|---|---|---|
set_log_module_name(module) | string | syslog 返回值 | 设置模块名,写入日志字段 | 非空字符串 |
debug/info/notice/warn/error(fmt, ...) | format 与参数 | 无 | 按级别输出;高于当前级别则丢弃 | string.format 风格 |
operation(initiator, executor, fmt, ...) | initiator、模块名、格式串 | 无 | 操作审计日志 | initiator 含 Interface/UserName/ClientAddr |
period(seconds) | number | 带周期的 logger | 限流日志 | 0 ~ 0xffffffff |
system(id) | id | 带 [SystemN] 前缀的 logger | 多系统场景 | — |
condition(ok) | boolean | logger 或空操作对象 | 条件为假时后续调用为空操作 | — |
attach_debug_console(port) | integer | 状态码 | 连接调试控制台 | 0 成功 |
返回值与异常
级别不足时调用直接返回。period 在类型或范围非法时 error。raise 将格式化消息作为 Lua 错误抛出。
应用场景
组件运行日志、操作审计、Dump 前的定位信息、调试控制台。
限制条件
- 默认调试级别为
NOTICE,debug/info在未调高级别时不会输出。 - 格式化失败时日志内容为
string.format error: ...,不会中断调用方。 - 操作日志需要合法的 initiator;缺少上下文字段时内容不完整。
调试示例
local log = require 'mc.logging'
log:set_log_module_name('example')
log:notice('service started')
log:error('open database failed: %s', err)
local ctx = require('mc.context').get_context_or_default()
log:operation(ctx:get_initiator(), 'example', 'Export config successfully')2.4 sd_bus 会话总线
功能说明
require 'sd_bus' 封装会话总线连接,供业务发起方法调用、发射信号和订阅 match 规则。默认走非阻塞实现;open_user(true) 会立即 start 事件循环。
参数说明
| 方法名 | 入参类型 | 出参类型 | 描述 | 取值范围 |
|---|---|---|---|---|
open_user(start_now, is_block, dbus) | bool, bool, 可选底层连接 | SDBus | 打开用户会话总线 | start_now=true 时启动循环 |
bus:request_name(name, flags) | string, flags | 底层返回 | 请求总线名 | 如 bmc.kepler.<app> |
bus:call(service, path, iface, method, signature, ...) | 服务/路径/接口/方法/签名/参数 | 多返回值 | 同步调用,失败抛错 | 签名须与接口一致 |
bus:pcall(...) | 同 call | ok, ... | 安全调用,不抛错 | 失败时 ok=false |
bus:timeout_call(timeout_ms, ...) | 超时毫秒 + 同 call | 多返回值 | 带超时的调用 | 正整数毫秒 |
bus:signal(path, iface, member, signature, ...) | 路径/接口/成员/签名/参数 | 底层返回 | 发射信号 | — |
bus:match(filter, cb) | MatchRule 或字符串, 回调 | slot | 订阅信号 | 回调参数为消息 |
返回值与异常
call 在对端返回错误或超时时抛出错误对象(mc.error.meta)。pcall 返回 false 和错误。路径、接口名非法会在底层校验失败。
应用场景
组件请求 bmc.kepler.<name>、调用其他组件方法、订阅 PropertiesChanged。
限制条件
- 必须在 Skynet 宿主中使用非阻塞模式;
is_block=true仅适用于特殊阻塞场景。 - 方法第一个业务参数通常是
a{ss}上下文,应使用mc.context.get_context_or_default()。 - 同进程共享内存快路径由框架自动选择,调用方仍使用同一套
call接口。
调试示例
local sdbus = require 'sd_bus'
local context = require 'mc.context'
local bus = sdbus.open_user(true)
bus:request_name('bmc.kepler.example')
local ok, err = bus:pcall(
'bmc.kepler.maca',
'/bmc/kepler/MdbService',
'bmc.kepler.Mdb',
'GetServiceNames',
'a{ss}',
context.get_context_or_default()
)2.5 mc.mdb 资源树代理对象
功能说明
require 'mc.mdb' 按路径和接口创建代理对象。属性读写走 bmc.kepler.Object.Properties 的 Get/SetWithContext;方法调用按 introspect 结果封包。方法名追加 _PACKED 时返回结构化响应。
参数说明
| 方法名 | 入参类型 | 出参类型 | 描述 | 取值范围 |
|---|---|---|---|---|
get_object(bus, path, interface, use_cached) | SDBus, string, string, bool | 代理对象 | 获取代理对象 | path 可含 * : { } 参数 |
try_get_object(bus, path, interface, try_times) | 同上, 重试次数 | 代理对象 | 失败则 sleep 后重试 | 默认最多 10 次 |
get_cached_object(bus, path, interface) | 同上 | 缓存代理 | 使用 LRU 缓存 | 默认容量 100 |
get_sub_objects(bus, path, interface, depth) | path/接口/深度 | Objects | 按接口收集子对象 | depth 为查询深度 |
get_object_with_service(bus, service, path, interface, use_cached) | 额外指定服务名 | 代理对象 | 跳过服务名查找 | 静态 path |
register_interface(...) / register_object(...) | 接口/对象元数据 | 无 | 注册本地接口与对象类 | 由 bingo gen 生成代码调用 |
set_bus(bus) | SDBus | 无 | 记录应用总线 | 服务启动时调用 |
属性访问:obj.Property 读取,obj.Property = value 写入。方法:obj:Method(args...) 或 obj:Method_PACKED(args...)。
返回值与异常
| 返回值 | 含义 | 触发条件 | 处理建议 |
|---|---|---|---|
| 代理对象 | 查找成功 | 服务、路径、接口存在 | 通过属性/方法访问 |
invalid interface:%s | 接口未注册 | g_interfaces 中无该接口 | 检查 service.json 依赖和代码生成 |
service not exists | 找不到属主服务 | maca 未返回服务名 | 确认目标组件已上树 |
try get object failed | 重试耗尽 | 对象尚未出现 | 增大 try_times 或等待自发现 |
应用场景
跨组件读属性、调方法、在生成的 client 中获取强/弱依赖对象。
限制条件
- 没有自定义接口的中间路径不能靠本模块的接口匹配找到,需使用
org.freedesktop.DBus.ObjectManager.GetManagedObjects。 - 代理对象默认缓存容量为 100,可通过
init_lru_cache(size)调整。 try_get_object每次失败skynet.sleep(50),会占用协程时间。
调试示例
local mdb = require 'mc.mdb'
local obj = mdb.get_object(bus, '/bmc/kepler/Systems/1', 'bmc.kepler.Systems')
local name = obj.Name
local rsp = obj:SomeMethod_PACKED(ctx, arg1)busctl --user tree bmc.kepler.example
busctl --user introspect bmc.kepler.example /bmc/kepler/example/MicroComponent2.6 mc.mdb.mdb_service 资源树查询
功能说明
对 bmc.kepler.maca / /bmc/kepler/MdbService / bmc.kepler.Mdb 的 Lua 封装,用于按路径、接口、类名查询对象。
参数说明
| 方法名 | 入参 | 描述 |
|---|---|---|
get_object(bus, path, interfaces) | 路径, 接口 | 对应 GetObject |
get_sub_objects(bus, path, depth, interfaces) | 路径, 深度, 接口 | 对应 GetSubObjects |
get_sub_paths(bus, path, depth, interfaces) | 路径, 深度, 接口 | 对应 GetSubPaths,仅返回带匹配接口的路径 |
get_parent_objects(bus, path, interfaces) | 路径, 接口 | 对应 GetParentObjects |
get_service_name(bus, sender) | sender | 对应 GetServiceName |
get_service_names(bus) | 无额外参数 | 对应 GetServiceNames |
get_path(bus, interface, filter, ignore_case) | 接口, 属性过滤 | 对应 GetPath |
get_interface_owners(bus, interface) | 接口 | 对应 GetInterfaceOwners |
is_valid_path(bus, path, ignore_case) | 路径 | 对应 IsValidPath |
get_sub_paths_paging(bus, path, depth, interfaces, skip, top) | 分页参数 | 对应 GetSubPathsPaging |
get_classes(bus, service) | 服务名 | 对应 GetClasses |
get_object_list(bus, class_name) | 类名 | 对应 GetObjectList |
get_object_owner(bus, object_name) | 对象名 | 对应 GetObjectOwner |
get_matched_objects(bus, service, interface_pattern) | 服务名, 接口模式 | 对应 GetMatchedObjects |
get_traced_objects(bus) | 无额外参数 | 对应 GetTracedObject |
返回值与异常
成功时返回 maca 方法的包装响应(含 Object、SubObjects、SubPaths 等字段,以生成代码为准)。调用失败走 sd_bus.call 的错误路径。
应用场景
按接口枚举对象、分页取子路径、根据类名或对象名反查 Path。
限制条件
- 依赖
bmc.kepler.maca已运行。 get_sub_paths只能返回带自定义接口的路径。- 部分查询走共享内存快路径,结果仍以 maca 数据为准。
调试示例
local mdb_service = require 'mc.mdb.mdb_service'
local rsp = mdb_service.get_sub_objects(bus, '/bmc/kepler/Systems/1', 1, 'bmc.kepler.Systems.Processor')busctl --user call bmc.kepler.storage /bmc/kepler/Systems/1/Storage \
org.freedesktop.DBus.ObjectManager GetManagedObjects2.7 信号订阅与自发现
功能说明
mc.mdb.subscribe_signal 订阅 D-Bus 信号;mc.mdb.object_manage 注册 hwdiscovery 派发对象的增删回调。
参数说明
mc.mdb.subscribe_signal:
| 方法名 | 描述 |
|---|---|
on_properties_changed(bus, path_namespace, cb, interface, properties) | 订阅属性变更 |
on_interfaces_added(bus, path_namespace, cb, expected_intf) | 订阅接口新增 |
on_interfaces_removed(...) | 订阅接口删除 |
subscribe_virtual_interface / subscribe_non_virtual_interface | 订阅虚/非虚接口变化 |
subscribe(bus, sig, cb) | 按 MatchRule 订阅自定义信号 |
mc.mdb.object_manage:
| 方法名 | 回调参数 | 描述 |
|---|---|---|
on_add_object(bus, cb, preprocess_cb, is_orm) | class_name, object, position | 对象新增;preprocess_cb 返回 true 则立即上树 |
on_delete_object(bus, cb) | class_name, object, position | 对象删除 |
on_add_object_complete(bus, cb, is_orm) | position | 本轮新增完成 |
on_delete_object_complete(bus, cb) | position | 本轮删除完成 |
enable_discovery(bus) / 禁用接口 | — | 控制自发现启动时机 |
返回值与异常
订阅接口返回 match slot。自发现回调中抛错会被框架日志记录,不保证中断整个派发循环。
应用场景
硬件对象上树、监听属性变化、在依赖对象就绪后再启动业务。
限制条件
on_add_object会启动 discovery,需在总线和模型初始化之后调用。preprocess_cb若返回 false,业务必须自行register_mdb_objects。
调试示例
local object_manage = require 'mc.mdb.object_manage'
object_manage.on_add_object(bus, function(class_name, object, position)
-- 创建业务对象
end)2.8 MicroComponent 标准接口回调
功能说明
框架在宿主组件上注册 bmc.kepler.MicroComponent* 对象。业务通过回调接入健康检查、配置管理、Dump、重启和复位。mc.service_app_base 会调用 mc.mdb.default.register_default_handler。
参数说明
| 模块 | 方法 | 说明 |
|---|---|---|
mc.mdb.micro_component | on_health_check(cb) | 注册 HealthCheck;默认在 HardwareCompleted 时返回 1,否则返回 0 |
| 同上 | set_status(status, app_name) | 更新 Status 并打 NOTICE 日志 |
mc.mdb.micro_component.config_manage | on_import / on_export / on_backup / on_recover / on_verify / on_get_trusted_config / on_get_preserved_config | 配置管理 |
mc.mdb.micro_component.debug | on_dump(cb) | 一键收集;框架先写 mdb_info.log 等再调业务回调 |
| 同上 | dlog_level_change / dlog_type_change | 调试日志级别/类型变化 |
mc.mdb.micro_component.reboot | on_prepare / on_process / on_action / on_cancel | 平滑重启四阶段 |
mc.mdb.micro_component.reset | on_reset_prepare / on_reset_action / on_reset_cancel | 热复位 |
HealthCheck 返回值(intf_mc.HEALTH):NEED_RESTART_NOW = -2,ABNORMAL = -1,NORMAL = 0,HW_INIT_COMPLETED = 1。
Status 字符串:Starting、InitCompleted、HardwareInit、HardwareCompleted、ServiceInit、ServiceCompleted。
返回值与异常
回调按各接口生成代码的签名返回。未注册时使用默认空实现(配置导入导出仅打 info 日志)。
应用场景
组件健康监控、配置导入导出、一键收集、升级前的平滑重启。
限制条件
- 必须在
register_default_handler之后注册业务回调。 - Dump 路径由调用方传入;框架会在该目录写入
mdb_info.log,非 Release 还会写skynet_info.json。
调试示例
local mc = require 'mc.mdb.micro_component'
mc.on_health_check(function()
return 0
end)
local debug = require 'mc.mdb.micro_component.debug'
debug.on_dump(function(ctx, path)
-- 将业务快照写入 path
end)busctl --user call bmc.kepler.example \
/bmc/kepler/example/MicroComponent \
bmc.kepler.MicroComponent HealthCheck2.9 mc.mdb.task_mgmt 任务管理
功能说明
创建 Redfish TaskService 风格的任务对象,路径为 <parent_path>/TaskService/Tasks/<id>。单组件最多 32 个任务。
参数说明
| 方法名 | 入参类型 | 出参类型 | 描述 |
|---|---|---|---|
create_task(bus, name, path, timeout, timeout_cb, properties) | 总线, 名称, 父路径, 超时分钟, 回调, 初始属性 | code, err_or_nil, id | 创建任务 |
update_task(id, data) | 任务 ID, 属性表 | 更新码 | 更新 Progress/State/Status 等 |
get_task_obj(id) | 任务 ID | 任务对象或 nil | 仅本服务任务 |
destroy_task(id) | 任务 ID | true | 立即销毁 |
state:New、Starting、Running、Suspended、Interrupted、Pending、Stopping、Completed、Killed、Exception、Cancelled。
status:OK、Warning、Error、Critical。
Progress=100 或 State 为 Completed / Exception / Cancelled 时,默认 10 分钟后销毁。
返回值与异常
创建码:0 成功,-1 任务数达上限,-2 超时值为负。
更新码:0 成功,-1 ID 不存在,-2 data 非 table,-3 Progress 越界,-4 State 非法,-5 Status 非法,-6 Parameters 非 table,-7 MessageId 非 string,-8 MessageArgs 非 table,-9 任务已结束。
应用场景
固件升级、配置导入等长流程,先返回任务 ID 再在 skynet.fork_once 中更新进度。
限制条件
- 单 APP 最多 32 个任务。
- 只能操作本服务创建的任务。
- 资源树方法应先返回 ID,再异步更新,避免调用方拿不到任务对象。
调试示例
local task_mgmt = require 'mc.mdb.task_mgmt'
local create, err, id = task_mgmt.create_task(bus, 'FirmwareUpdate', '/bmc/kepler/UpdateService', 30)
assert(create == task_mgmt.create_code.TASK_CREATE_SUCCESSFUL, err)
skynet.fork_once(function()
task_mgmt.update_task(id, { State = task_mgmt.state.Running, Progress = 10 })
task_mgmt.update_task(id, { State = task_mgmt.state.Completed })
end)
return id2.10 mc.validate 数据校验
功能说明
对资源树属性或方法入参做语义校验。失败时抛出 messages.base / messages.custom 错误,或写入 errs 表。need_convert 为真时属性名/值按 %Name、%Name:value 格式转换,以便北向映射。
参数说明
| 方法名 | 入参 | 描述 |
|---|---|---|
Enum(name, val, enum_name, enum_type, errs, need_convert) | 枚举表 enum_type | 值必须落在枚举中 |
lens / len_or_none | name, val, min, max, errs, need_convert | 字符串长度 |
ranges / range_or_none | name, val, min, max, errs, need_convert | 数值范围 |
regex / regex_or_none | name, val, rx, errs, need_convert | GLib 正则 |
Json(val) | 字符串 | 是否为合法 JSON |
Required / Optional | name, val, type_str, readonly, errs, need_convert | 标量类型 |
RequiredArray / OptionalArray | 同上 | 数组元素类型 |
CheckUnknowProperty | 输入属性表, 原型属性表 | 未知属性 |
*_or_none 与 Optional* 在 val == nil 时视为通过。
返回值与异常
未传 errs 时直接 error(message)。传入 errs 时写入 errs[name]。典型错误包括 PropertyValueNotInList、PropertyValueTypeError、PropertyValueOutOfRange、PropertyValueFormatError、StringValueTooShort / StringValueTooLong、PropertyUnknown、PropertyMissing。
应用场景
生成代码中的属性 Set 校验、Redfish/CLI 入参校验。
限制条件
- 正则使用
utils.core.g_regex_match(GLib)。 enum_name参数在实现中未参与校验逻辑,有效约束来自enum_type表。
调试示例
local validate = require 'mc.validate'
validate.ranges('ValidateTime', 200, 100, 5000)
validate.lens('FilePath', path, 1, 2048)
validate.regex('Medium', medium, [=[^(Air|Liquid)$]=])
validate.Json('{"a":1}')2.11 mc.signal 进程内信号
功能说明
与 D-Bus 信号不同,该模块只在当前 Lua 服务内做发布/订阅。
参数说明
| 方法名 | 入参 | 出参 | 描述 |
|---|---|---|---|
signal.new(combiner) | 可选合并函数 | signal | 创建信号 |
sig:on(cb) | 回调 | slot | 注册槽函数 |
sig:emit(...) | 任意 | 最后一次回调结果或 combiner 结果 | 触发 |
sig:reset() | 无 | 无 | 清空槽;若正在 emit 则延迟删除 |
sig:is_empty() / is_running() / running() | 无 | bool / slot | 状态查询 |
sig:get_slot(name) / handle_count() | 名称或下标 | slot / number | 查找槽 |
slot:disconnect() / with_name(name) | — | — | 断开或命名槽 |
返回值与异常
槽回调中的错误会记录到 sig.errors;多个错误时仅重新抛出最后一个,其余打 log:error('ignore slot run error: ...')。emit 嵌套超过 MAX_EMIT_NESTED_DEPTH(90)时抛 emit signal: nesting is not allowed。同名 with_name 冲突时抛 slot name conflict。
应用场景
服务内属性变更通知、数据库 hook(database 的 on_insert / on_update / on_delete)。
限制条件
不要在回调中无界递归 emit 同一信号。
调试示例
local signal = require 'mc.signal'
local sig = signal.new()
sig:on(function(v)
print(v)
end)
sig:emit(1)2.12 mc.context 调用上下文
功能说明
在当前协程上保存 Interface、UserName、ClientAddr 等调用者信息,随 D-Bus 方法在组件间传递。
参数说明
| 方法名 | 描述 |
|---|---|
context.new(interface, username, client_addr) | 构造上下文 |
context.new_smbus(protocol) | 构造 Protocol=smbus 上下文 |
get_context() / get_context_or_default() | 取当前协程上下文;后者在空时创建空表 |
set_context(ctx) / with_context | 绑定到协程 |
ctx:has_initiator() / ctx:get_initiator() | 是否具备三元组,以及转为 initiator |
whitelist_verification | 按 /opt/bmc/conf/context.json 校验上下文字段 |
返回值与异常
空上下文的 is_empty() 为 true。白名单校验失败时拒绝非法字段覆盖。
应用场景
资源树方法第一个 a{ss} 参数、操作日志 initiator、权限校验。
限制条件
上下文以协程为键,跨 skynet.fork 时需显式传递或使用 set_skynet_context。
调试示例
local context = require 'mc.context'
local ctx = context.new('Redfish', 'Administrator', '127.0.0.1')
context.set_context(ctx)2.13 database ORM
功能说明
require 'database' 返回 function(path),打开 SQLite 并返回 DataBase 对象。database.column 提供字段类型。mc.mdb.micro_component.db 在此之上封装组件内存库。
参数说明
打开数据库:
local open_db = require 'database'
local db = open_db('/data/opt/bmc/data/example.db') -- 或 ':memory:'| 方法名 | 描述 |
|---|---|
db:Table(name, columns) | 声明表,链式 :create() / :create_if_not_exist() / :unique(...) |
db:insert(Table):value({...}):exec() | 插入 |
db:select(Table):where(...):first()/all()/fold() | 查询 |
db:update(Table):value({...}):where(...):exec() | 更新 |
db:delete(Table):where(...):exec() | 删除 |
db:exec(sql) / db:prepare(sql) | 原始 SQL |
db:tx(fn) / db:tx_safe(fn) | 事务 |
Table({pk=...}):save() / :delete() | 对象级读写 |
字段类型(database.column):TextField、BolbField、IntegerField、RealField、BooleandField、DateTimeField、EnumField、JsonField。链式配置:cid、unique、null、default、primary_key、max_length。
where 条件:Field:eq/ne/lt/le/gt/ge/like/in_,以及 or_(...)。排序分页:order_by、desc、limit、offset。
磁盘库打开后权限设为 0640。空间不足时可能抛 InsufficientFreeSpace。
返回值与异常
open_db 失败抛 open database failed: <path>。约束冲突、SQL 错误走 SQLite errmsg。
应用场景
组件持久化配置、对象属性落库、与 class_mgnt/ORM 生成代码配合。
限制条件
- 字段类名
BolbField、BooleandField与源码拼写一致。 - 用类构造函数查询时必须提供主键或唯一键,多行只返回第一行;不存在则创建新对象。
- 写保护状态下文件写入受
filehook/mc.nandflash限制。
调试示例
local Col = require 'database.column'
local User = db:Table('t_user', {
user_name = Col.TextField():cid(1):max_length(32):unique(),
user_id = Col.IntegerField():cid(2):primary_key(1):max_length(8),
}):create()
User({ user_name = 'abc', user_id = 1 }):save()
local row = db:select(User):where(User.user_id:eq(1)):first()2.14 mc.ipmi IPMI 辅助接口
功能说明
向 bmc.kepler.ipmi_core 发送 Request,并提供完成码响应封装和操作日志。
参数说明
| 方法名 | 入参 | 描述 |
|---|---|---|
request(bus, chan_type, req) | chan_type 为通道数字,或数组 {chan_type, instance};req 含 DestNetFn、Cmd、Payload、可选 DestLun | 调用 IpmiCore.Request |
ipmi_operation_log(ctx, module_name, fmt, ...) | IPMI 上下文 | 写操作日志 |
response(cc, data) / responseSuccess(data) 等 | 完成码 0~0xFF | 拼响应字节 |
返回值与异常
request 返回对端 Request 的结果。response 在 cc 越界时 assert。
应用场景
业务组件实现 IPMI 命令处理函数,或向 ipmi_core 转发。
限制条件
依赖 bmc.kepler.ipmi_core 服务;通道类型取值见 mc.ipmi.types。
调试示例
local ipmi = require 'mc.ipmi'
-- chan_type 为数字;指定 instance 时传入 {chan_type, instance}
local cc, rsp = ipmi.request(bus, 1, {
DestNetFn = 0x30,
Cmd = 0x98,
Payload = '\x00\x07\xdb\x01\x00',
})
return ipmi.responseSuccess(rsp)2.15 C 扩展模块一览
以下模块由 src/lualib-src 编译,安装到 /opt/bmc/libmc/luaclib(cutils / filehook 安装到 usr/lib64)。仅列出已核实的 luaopen_* 与典型 require 名。
.so | require | 入口 | 说明 |
|---|---|---|---|
cjson.so | cjson | luaopen_cjson | JSON 编解码 |
lsqlite3.so | lsqlite3 | luaopen_lsqlite3 | SQLite3 |
dbus.so | dbus.core | luaopen_dbus_core | D-Bus 连接、消息、共享内存、校验函数 |
worker.so | worker.core | luaopen_worker_core | 工作线程;方法含 new、start_module、send、stop |
utils.so | utils.core / utils.file / utils.crypt / utils.vos | 对应 luaopen_utils_* | 文件、进程、加解密、VOS |
syslog.so | syslog.core | luaopen_syslog_core | 日志后端 |
kmc.so | kmc.core | luaopen_kmc_core | 密钥管理 |
network.so | network.core | luaopen_network_core | 网络接口 |
bitstring.so | bitstring.core | luaopen_bitstring_core | 二进制编解码 |
shmlock.so | shmlock.core | luaopen_shmlock_core | 共享内存锁 |
serialize.so | serialize | luaopen_serialize | encode / decode |
shared_table.so | shared_table | luaopen_shared_table | load / get / keys |
expr.so | expr.core | luaopen_expr_core | evaluate / init / cleanup |
cms.so | cms.verify | luaopen_cms_verify | CMS 验签 |
debug_console.so | debug_console.client 等 | luaopen_debug_console_* | 调试控制台 |
skynet_logger | 由 config logservice 使用 | — | 替换 Skynet 日志服务 |
vce.so | vce.drv | luaopen_vce_drv | VCE 驱动 |
mem_checker.so | mem_checker | luaopen_mem_checker | 内存检查 |
snapshot.so | snapshot | luaopen_snapshot | 非 Release |
memstat.so | memstat | luaopen_memstat | LuaJIT 且非 Release |
hpcomm.so | hpcomm.snc.* / hpcomm.cnc.* | luaopen_hpcomm_* | 仅 hpcomm_enable |
dbus.core 额外导出 get_private、new_session、new_system、open、validate_path / check_path 等,以及 DBusConnection、DBusMessage、DBusVariant、SharedMemory 等类型。
3. 组件扩展案例
3.1 扩展能力概述
libmc4lua 作为基础库,扩展方式主要是:
- 上层业务组件依赖本库,通过
mc.plugin.loader加载组件插件和定制插件。 - 在本仓库新增 Lua 模块或 C 扩展,并注册
luaopen_*。 - 通过 bingo gen 根据 MDS 生成
mdb.*接口代码,由mc.mdb.register_interface注册。
mds/service.json 的 required 为空,本库不声明对其他业务组件的运行时资源协作依赖。
3.2 扩展点说明
| 扩展点 | 位置 | 触发时机 |
|---|---|---|
| 组件插件 | APP_WORKING_DIRECTORY/plugin/<name>,require('plugin.<name>') | mc.plugin.loader.load |
| 定制插件 | /opt/bmc/plugins/<APP_NAME>/<name> | 同上,后于组件插件加载 |
| Lua API | src/lualib/ | require |
| C 扩展 | src/lualib-src/ 对应 CMakeLists 与 luaL_Reg | require 对应模块 |
| MicroComponent 回调 | mc.mdb.micro_component* | 标准接口被调用 |
| 自发现回调 | mc.mdb.object_manage | hwdiscovery 派发对象 |
| 构建选项 | chipv2_enable、hpcomm_enable | CMake 宏 |
3.3 二次开发指导
步骤一:业务侧使用现有 API
在组件中 require 所需模块,通过 mc.service_app_base 继承服务类,在 init 中注册对象和回调。
步骤二:新增 Lua 模块
在 src/lualib/ 添加模块,保持 require 路径与目录一致(mc/xxx.lua 对应 mc.xxx)。
步骤三:新增 C 模块
- 实现
luaopen_<mod>并在luaL_Reg中注册函数。 - 在对应
CMakeLists.txt中PREFIX ""安装到opt/bmc/libmc/luaclib。 - 同步更新本节与第 2 章接口表。
步骤四:插件
local loader = require 'mc.plugin.loader'
loader.load()插件目录必须是子目录;pcall(require, ...) 失败时只跳过该插件。成功时日志为 load component plugin feature[%s] successfully 或 load customized plugin feature[%s] successfully。
验证方法
- 在组件仓执行既有 bingo 构建,确认
/opt/bmc/libmc/lualib与luaclib已部署。 - 运行
test/unit下与目标模块对应的用例。 - 用
busctl --user检查宿主组件的 MicroComponent 接口。
注意事项
- 保持 Lua 模块名、D-Bus 接口名和错误消息 ID 向后兼容。
- 不得在日志中输出密钥、口令或完整敏感配置。
hpcomm、chipv2 相关代码受构建选项控制,文档与产物需同时说明。
4. 日志说明
4.1 一键日志收集
libmc4lua 不单独注册 Dump 服务。宿主组件执行 bmc.kepler.MicroComponent.Debug.Dump 时,框架 debug.lua 先收集公共信息,再调用业务 on_dump。
| 文件路径 | 内容说明 |
|---|---|
<dump_path>/mdb_info.log | 资源树快照、方法耗时(若启用)、数据库与日志写入量统计 |
<dump_path>/skynet_info.json | 非 Release 下 Skynet 服务内存与状态 |
| 宿主进程日志 | mc.logging 输出,路径由 syslog/skynet_logger 与系统配置决定 |
4.2 关键日志信息
| 日志片段 | 日志级别 | 含义解读 | 建议处理动作 |
|---|---|---|---|
Startup status has changed, %s ==> %s | NOTICE | 微组件 Status 切换 | 对照启动阶段是否卡在 HardwareInit/ServiceInit |
invalid interface:%s | 错误抛出 | 接口未在本进程注册 | 检查 MDS 依赖、bingo gen 和 register_interface |
service not exists, path:%s, interface:%s | 错误抛出 | 找不到属主服务 | 确认目标组件已 request_name 且 maca 已索引 |
try get object failed | 错误抛出 | 重试后仍无对象 | 等待自发现或检查路径 |
create task failed: task limit exceeded | ERROR | 任务数达到 32 | 等待任务结束或销毁旧任务 |
create task failed: invalid timeout value | ERROR | 超时为负 | 传入 nil 或不为负的分钟数 |
dump %s mdb failed | ERROR | Dump 写文件失败 | 检查 Dump 目录权限与磁盘 |
[%s] load component plugin feature[%s] successfully | NOTICE | 插件加载成功 | 无 |
open database failed: %s | 错误抛出 | SQLite 打开失败 | 检查路径、损坏修复和权限 |
ignore slot run error: %s | ERROR | 进程内信号有槽回调失败 | 检查该信号的各个 on 回调 |
attach debug console %s failed | ERROR | 调试控制台占用或连接失败 | 先 Detach 再重试 |
5. 问题定界指南
5.1 典型问题定界
| 问题描述 | 是否为本组件问题 | 判断依据 | 关键证据收集方法 |
|---|---|---|---|
require 'mc.xxx' / C 模块失败 | 可能是 | 未部署 /opt/bmc/libmc 或 package.path/cpath 未包含 libmc | 打印搜索路径,检查 .lua/.so 是否存在 |
invalid interface | 可能是 | 接口未生成或未在 service.json 声明依赖 | 对照 MDS、生成代码和 maca 上的接口 |
get_sub_paths 看不到某子路径 | 通常是用法限制 | 该路径没有自定义接口 | 对父路径调用 GetManagedObjects |
| D-Bus 调用超时 | 可能是对端或总线 | 错误来自 sd_bus.call | busctl --user list、对端日志、maca 状态 |
| 自发现未创建对象 | 可能是业务回调或 hwdiscovery | 未注册 on_add_object 或预处理阻止上树 | 抓 ObjectGroup 信号,检查回调返回值 |
任务创建返回 -1 | 是 | 已有 32 个任务 | Dump mdb_info.log 中的任务信息 |
| SQLite 打开失败 / 磁盘满 | 可能是 | ORM 打开或 hook 检测到 FULL | 检查库文件、inode 和写保护 |
| 日志没有 debug | 通常是级别 | 默认 NOTICE | Dump SetDlogLevel 或 set_debug_log_level |
5.2 错误码速查表
| 错误码 | 来源 | 含义 | 排查建议 |
|---|---|---|---|
0 | task 创建/更新、HealthCheck 默认正常 | 成功 | 无 |
-1 | 任务数满 / HealthCheck ABNORMAL / 部分 C 绑定 | 见具体 API | 区分 task 与健康检查 |
-2 | 任务超时非法 / HealthCheck NEED_RESTART_NOW | 见具体 API | 检查 timeout 或组件状态 |
-3 ~ -9 | task_mgmt.update_task | 见 2.9 节 | 按字段逐项核对 |
1 | HealthCheck HW_INIT_COMPLETED | 硬件初始化完成但业务未完成 | 等待 ServiceCompleted |
Lua error(message) | validate / mdb | Redfish 风格消息对象 | 读 name / format |
| SQLite errmsg | database | SQL 或约束失败 | 对照 schema 与 SQL |
完整 D-Bus 方法错误以对端组件和 messages.* 生成为准。
5.3 最小化复现与证据收集
- 记录
require模块名、D-Bus 服务/路径/接口/方法、上下文和返回错误。 - 执行宿主组件 Dump,收集
mdb_info.log与进程日志。 - 用
busctl --user tree/introspect/call确认 maca 与目标对象。 - 无自定义接口的路径改用
GetManagedObjects。 - 保留插件目录列表和
service.json依赖声明。
5.4 调试方法
busctl --user tree bmc.kepler.maca
busctl --user introspect bmc.kepler.example /bmc/kepler/example/MicroComponent
busctl --user call bmc.kepler.example \
/bmc/kepler/example/MicroComponent \
bmc.kepler.MicroComponent.Debug SetDlogLevel a{ss}sy 0 debug 1单元测试位于组件仓 test/unit/(如 test_mdb、test_logging、test_validate、test_dbus、test_database)。
6. 常见问题解答
Q1:require 'mc.mdb' 或 require 'dbus.core' 失败怎么办?
- 问题描述:Lua 报 module not found。
- 一句话答案:检查
/opt/bmc/libmc部署和package.path/package.cpath。 - 根因说明:
config.cfg未执行done(),或产物未安装到lualib/luaclib。 - 解决方案:确认
lua_path含/opt/bmc/libmc/lualib/?.lua,lua_cpath含/opt/bmc/libmc/luaclib/?.so。 - 规避方案:通过 bingo/Conan 正式部署,不要只拷贝单个文件。
- 适用版本:1.130.38。
Q2:为什么出现 invalid interface?
- 问题描述:
mdb.get_object抛invalid interface:<name>。 - 一句话答案:当前进程没有注册该接口的 introspect/元数据。
- 根因说明:未在
service.json声明接口依赖,或 bingo gen /register_interface未执行。 - 解决方案:补齐 MDS 依赖,重新生成并确认启动时已注册。
- 规避方案:对尚未依赖的接口改用
bus:call显式调用,并补依赖。 - 适用版本:1.130.38。
Q3:get_sub_paths 查不到已知子路径?
- 问题描述:
busctl tree看得到路径,查询 API 返回空。 - 一句话答案:该方法只返回带自定义接口的路径。
- 根因说明:中间目录节点可能没有业务接口,maca 的 GetSubPaths 不会列出它们。
- 解决方案:对父对象调用
org.freedesktop.DBus.ObjectManager.GetManagedObjects。 - 规避方案:为该节点补充接口,或在业务侧改用 GetManagedObjects。
- 适用版本:1.130.38。
Q4:任务创建失败或进度无法更新?
- 问题描述:
create_task返回非 0,或update_task返回-9。 - 一句话答案:任务数上限为 32;已结束任务不能再更新。
- 根因说明:未及时销毁已完成任务,或在 Completed/Exception/Cancelled 之后仍 update。
- 解决方案:查询本服务任务对象并
destroy_task;仅在结束前更新 Progress/State。 - 规避方案:长流程使用
skynet.fork_once,先返回任务 ID。 - 适用版本:1.130.38。
Q5:如何提高框架调试日志级别?
- 问题描述:只有 NOTICE/ERROR,看不到 debug。
- 一句话答案:通过 MicroComponent.Debug.SetDlogLevel 或
mc.logging的 set_debug_log_level 接口提升级别。 - 根因说明:默认
dlog_level为DLOG_NOTICE,更低级别被过滤。 - 解决方案:调用 SetDlogLevel 并指定有效小时数;到期后恢复 NOTICE。
- 规避方案:在复现窗口内打开 debug,避免生产环境长期 MASS/DEBUG。
- 适用版本:1.130.38。