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_basemc.service_app_basemc.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.macabmc.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 关键术语表

术语解释
libmcCMake 工程名与运行时目录名;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
SDBussd_bus 模块对会话总线连接的 Lua 封装。
_PACKED在代理对象方法名后追加该后缀时,返回结构化响应而不是多返回值。
ORM将 SQLite 表映射为 Lua 类,用对象代替手写 SQL。
Skynet宿主协程框架;libmc4lua 的服务脚本和 worker 线程运行在 Skynet 进程内。

1.5 外部交互边界图

构建依赖见 mds/service.json,包括 kmcsignature_verify_cbb_librarysqlite3jsonlibsoc_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

lua
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/lualibsrc/lualib/ 与代码生成产物 gen/
C 扩展/opt/bmc/libmc/luacliblib 前缀的 .so,如 dbus.socjson.so
Skynet 服务/opt/bmc/libmc/servicesd_bus.luaharbor.lua
启动配置/opt/bmc/libmc/config.cfgdist/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 时安装;snapshotmemstat 仅在非 Release / 指定条件下编译。

调试示例

lua
print(package.path)
print(package.cpath)
assert(require 'mc.logging')
assert(require 'sd_bus')
assert(require 'cjson')

2.2 mc.class 面向对象

功能说明

require 'mc.class' 返回一个类工厂函数。调用该函数得到类表;通过 ctorpre_initinit 管理实例生命周期,通过传入父类实现继承。

参数说明

方法名入参类型出参类型描述取值范围
Class(super, meta, array_map_store, ...)可选父类等class定义新类super 可为 nil
cls:ctor(...)任意构造阶段,先调用父类 ctor由业务定义
cls:pre_init()newctor 之后同步调用可选
cls:init()newpre_init 之后同步调用可选
cls.new(...)ctor 相同实例创建对象并跑完生命周期自动调用

返回值与异常

new 返回实例。若类设置了 __capture_error,生命周期函数中的错误会被捕获;否则按普通 Lua 错误抛出。

应用场景

定义服务类、MDB 对象类、ORM 行对象。mc.service_app_base 中的 ServiceClass(AppBase)

限制条件

  • 必须通过 cls.new(...) 创建实例,直接当函数调用类表不会执行完整生命周期。
  • pre_init / initnew 内同步执行,不要在其中做长时间阻塞而不切协程。

调试示例

lua
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)stringsyslog 返回值设置模块名,写入日志字段非空字符串
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)booleanlogger 或空操作对象条件为假时后续调用为空操作
attach_debug_console(port)integer状态码连接调试控制台0 成功

返回值与异常

级别不足时调用直接返回。period 在类型或范围非法时 errorraise 将格式化消息作为 Lua 错误抛出。

应用场景

组件运行日志、操作审计、Dump 前的定位信息、调试控制台。

限制条件

  • 默认调试级别为 NOTICEdebug/info 在未调高级别时不会输出。
  • 格式化失败时日志内容为 string.format error: ...,不会中断调用方。
  • 操作日志需要合法的 initiator;缺少上下文字段时内容不完整。

调试示例

lua
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(...)callok, ...安全调用,不抛错失败时 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 接口。

调试示例

lua
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),会占用协程时间。

调试示例

lua
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)
bash
busctl --user tree bmc.kepler.example
busctl --user introspect bmc.kepler.example /bmc/kepler/example/MicroComponent

2.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 方法的包装响应(含 ObjectSubObjectsSubPaths 等字段,以生成代码为准)。调用失败走 sd_bus.call 的错误路径。

应用场景

按接口枚举对象、分页取子路径、根据类名或对象名反查 Path。

限制条件

  • 依赖 bmc.kepler.maca 已运行。
  • get_sub_paths 只能返回带自定义接口的路径。
  • 部分查询走共享内存快路径,结果仍以 maca 数据为准。

调试示例

lua
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')
bash
busctl --user call bmc.kepler.storage /bmc/kepler/Systems/1/Storage \
    org.freedesktop.DBus.ObjectManager GetManagedObjects

2.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

调试示例

lua
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_componenton_health_check(cb)注册 HealthCheck;默认在 HardwareCompleted 时返回 1,否则返回 0
同上set_status(status, app_name)更新 Status 并打 NOTICE 日志
mc.mdb.micro_component.config_manageon_import / on_export / on_backup / on_recover / on_verify / on_get_trusted_config / on_get_preserved_config配置管理
mc.mdb.micro_component.debugon_dump(cb)一键收集;框架先写 mdb_info.log 等再调业务回调
同上dlog_level_change / dlog_type_change调试日志级别/类型变化
mc.mdb.micro_component.rebooton_prepare / on_process / on_action / on_cancel平滑重启四阶段
mc.mdb.micro_component.reseton_reset_prepare / on_reset_action / on_reset_cancel热复位

HealthCheck 返回值(intf_mc.HEALTH):NEED_RESTART_NOW = -2ABNORMAL = -1NORMAL = 0HW_INIT_COMPLETED = 1

Status 字符串:StartingInitCompletedHardwareInitHardwareCompletedServiceInitServiceCompleted

返回值与异常

回调按各接口生成代码的签名返回。未注册时使用默认空实现(配置导入导出仅打 info 日志)。

应用场景

组件健康监控、配置导入导出、一键收集、升级前的平滑重启。

限制条件

  • 必须在 register_default_handler 之后注册业务回调。
  • Dump 路径由调用方传入;框架会在该目录写入 mdb_info.log,非 Release 还会写 skynet_info.json

调试示例

lua
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)
bash
busctl --user call bmc.kepler.example \
  /bmc/kepler/example/MicroComponent \
  bmc.kepler.MicroComponent HealthCheck

2.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)任务 IDtrue立即销毁

stateNewStartingRunningSuspendedInterruptedPendingStoppingCompletedKilledExceptionCancelled

statusOKWarningErrorCritical

Progress=100StateCompleted / 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,再异步更新,避免调用方拿不到任务对象。

调试示例

lua
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 id

2.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_nonename, val, min, max, errs, need_convert字符串长度
ranges / range_or_nonename, val, min, max, errs, need_convert数值范围
regex / regex_or_nonename, val, rx, errs, need_convertGLib 正则
Json(val)字符串是否为合法 JSON
Required / Optionalname, val, type_str, readonly, errs, need_convert标量类型
RequiredArray / OptionalArray同上数组元素类型
CheckUnknowProperty输入属性表, 原型属性表未知属性

*_or_noneOptional*val == nil 时视为通过。

返回值与异常

未传 errs 时直接 error(message)。传入 errs 时写入 errs[name]。典型错误包括 PropertyValueNotInListPropertyValueTypeErrorPropertyValueOutOfRangePropertyValueFormatErrorStringValueTooShort / StringValueTooLongPropertyUnknownPropertyMissing

应用场景

生成代码中的属性 Set 校验、Redfish/CLI 入参校验。

限制条件

  • 正则使用 utils.core.g_regex_match(GLib)。
  • enum_name 参数在实现中未参与校验逻辑,有效约束来自 enum_type 表。

调试示例

lua
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(databaseon_insert / on_update / on_delete)。

限制条件

不要在回调中无界递归 emit 同一信号。

调试示例

lua
local signal = require 'mc.signal'
local sig = signal.new()
sig:on(function(v)
    print(v)
end)
sig:emit(1)

2.12 mc.context 调用上下文

功能说明

在当前协程上保存 InterfaceUserNameClientAddr 等调用者信息,随 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

调试示例

lua
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 在此之上封装组件内存库。

参数说明

打开数据库:

lua
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):TextFieldBolbFieldIntegerFieldRealFieldBooleandFieldDateTimeFieldEnumFieldJsonField。链式配置:ciduniquenulldefaultprimary_keymax_length

where 条件:Field:eq/ne/lt/le/gt/ge/like/in_,以及 or_(...)。排序分页:order_bydesclimitoffset

磁盘库打开后权限设为 0640。空间不足时可能抛 InsufficientFreeSpace

返回值与异常

open_db 失败抛 open database failed: <path>。约束冲突、SQL 错误走 SQLite errmsg。

应用场景

组件持久化配置、对象属性落库、与 class_mgnt/ORM 生成代码配合。

限制条件

  • 字段类名 BolbFieldBooleandField 与源码拼写一致。
  • 用类构造函数查询时必须提供主键或唯一键,多行只返回第一行;不存在则创建新对象。
  • 写保护状态下文件写入受 filehook / mc.nandflash 限制。

调试示例

lua
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}reqDestNetFnCmdPayload、可选 DestLun调用 IpmiCore.Request
ipmi_operation_log(ctx, module_name, fmt, ...)IPMI 上下文写操作日志
response(cc, data) / responseSuccess(data)完成码 0~0xFF拼响应字节

返回值与异常

request 返回对端 Request 的结果。responsecc 越界时 assert。

应用场景

业务组件实现 IPMI 命令处理函数,或向 ipmi_core 转发。

限制条件

依赖 bmc.kepler.ipmi_core 服务;通道类型取值见 mc.ipmi.types

调试示例

lua
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/luaclibcutils / filehook 安装到 usr/lib64)。仅列出已核实的 luaopen_* 与典型 require 名。

.sorequire入口说明
cjson.socjsonluaopen_cjsonJSON 编解码
lsqlite3.solsqlite3luaopen_lsqlite3SQLite3
dbus.sodbus.coreluaopen_dbus_coreD-Bus 连接、消息、共享内存、校验函数
worker.soworker.coreluaopen_worker_core工作线程;方法含 newstart_modulesendstop
utils.soutils.core / utils.file / utils.crypt / utils.vos对应 luaopen_utils_*文件、进程、加解密、VOS
syslog.sosyslog.coreluaopen_syslog_core日志后端
kmc.sokmc.coreluaopen_kmc_core密钥管理
network.sonetwork.coreluaopen_network_core网络接口
bitstring.sobitstring.coreluaopen_bitstring_core二进制编解码
shmlock.soshmlock.coreluaopen_shmlock_core共享内存锁
serialize.soserializeluaopen_serializeencode / decode
shared_table.soshared_tableluaopen_shared_tableload / get / keys
expr.soexpr.coreluaopen_expr_coreevaluate / init / cleanup
cms.socms.verifyluaopen_cms_verifyCMS 验签
debug_console.sodebug_console.clientluaopen_debug_console_*调试控制台
skynet_logger由 config logservice 使用替换 Skynet 日志服务
vce.sovce.drvluaopen_vce_drvVCE 驱动
mem_checker.somem_checkerluaopen_mem_checker内存检查
snapshot.sosnapshotluaopen_snapshot非 Release
memstat.somemstatluaopen_memstatLuaJIT 且非 Release
hpcomm.sohpcomm.snc.* / hpcomm.cnc.*luaopen_hpcomm_*hpcomm_enable

dbus.core 额外导出 get_privatenew_sessionnew_systemopenvalidate_path / check_path 等,以及 DBusConnectionDBusMessageDBusVariantSharedMemory 等类型。

3. 组件扩展案例

3.1 扩展能力概述

libmc4lua 作为基础库,扩展方式主要是:

  • 上层业务组件依赖本库,通过 mc.plugin.loader 加载组件插件和定制插件。
  • 在本仓库新增 Lua 模块或 C 扩展,并注册 luaopen_*
  • 通过 bingo gen 根据 MDS 生成 mdb.* 接口代码,由 mc.mdb.register_interface 注册。

mds/service.jsonrequired 为空,本库不声明对其他业务组件的运行时资源协作依赖。

3.2 扩展点说明

扩展点位置触发时机
组件插件APP_WORKING_DIRECTORY/plugin/<name>require('plugin.<name>')mc.plugin.loader.load
定制插件/opt/bmc/plugins/<APP_NAME>/<name>同上,后于组件插件加载
Lua APIsrc/lualib/require
C 扩展src/lualib-src/ 对应 CMakeLists 与 luaL_Regrequire 对应模块
MicroComponent 回调mc.mdb.micro_component*标准接口被调用
自发现回调mc.mdb.object_managehwdiscovery 派发对象
构建选项chipv2_enablehpcomm_enableCMake 宏

3.3 二次开发指导

步骤一:业务侧使用现有 API

在组件中 require 所需模块,通过 mc.service_app_base 继承服务类,在 init 中注册对象和回调。

步骤二:新增 Lua 模块

src/lualib/ 添加模块,保持 require 路径与目录一致(mc/xxx.lua 对应 mc.xxx)。

步骤三:新增 C 模块

  1. 实现 luaopen_<mod> 并在 luaL_Reg 中注册函数。
  2. 在对应 CMakeLists.txtPREFIX "" 安装到 opt/bmc/libmc/luaclib
  3. 同步更新本节与第 2 章接口表。

步骤四:插件

lua
local loader = require 'mc.plugin.loader'
loader.load()

插件目录必须是子目录;pcall(require, ...) 失败时只跳过该插件。成功时日志为 load component plugin feature[%s] successfullyload customized plugin feature[%s] successfully

验证方法

  1. 在组件仓执行既有 bingo 构建,确认 /opt/bmc/libmc/lualibluaclib 已部署。
  2. 运行 test/unit 下与目标模块对应的用例。
  3. 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 ==> %sNOTICE微组件 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 exceededERROR任务数达到 32等待任务结束或销毁旧任务
create task failed: invalid timeout valueERROR超时为负传入 nil 或不为负的分钟数
dump %s mdb failedERRORDump 写文件失败检查 Dump 目录权限与磁盘
[%s] load component plugin feature[%s] successfullyNOTICE插件加载成功
open database failed: %s错误抛出SQLite 打开失败检查路径、损坏修复和权限
ignore slot run error: %sERROR进程内信号有槽回调失败检查该信号的各个 on 回调
attach debug console %s failedERROR调试控制台占用或连接失败先 Detach 再重试

5. 问题定界指南

5.1 典型问题定界

问题描述是否为本组件问题判断依据关键证据收集方法
require 'mc.xxx' / C 模块失败可能是未部署 /opt/bmc/libmcpackage.path/cpath 未包含 libmc打印搜索路径,检查 .lua/.so 是否存在
invalid interface可能是接口未生成或未在 service.json 声明依赖对照 MDS、生成代码和 maca 上的接口
get_sub_paths 看不到某子路径通常是用法限制该路径没有自定义接口对父路径调用 GetManagedObjects
D-Bus 调用超时可能是对端或总线错误来自 sd_bus.callbusctl --user list、对端日志、maca 状态
自发现未创建对象可能是业务回调或 hwdiscovery未注册 on_add_object 或预处理阻止上树抓 ObjectGroup 信号,检查回调返回值
任务创建返回 -1已有 32 个任务Dump mdb_info.log 中的任务信息
SQLite 打开失败 / 磁盘满可能是ORM 打开或 hook 检测到 FULL检查库文件、inode 和写保护
日志没有 debug通常是级别默认 NOTICEDump SetDlogLevel 或 set_debug_log_level

5.2 错误码速查表

错误码来源含义排查建议
0task 创建/更新、HealthCheck 默认正常成功
-1任务数满 / HealthCheck ABNORMAL / 部分 C 绑定见具体 API区分 task 与健康检查
-2任务超时非法 / HealthCheck NEED_RESTART_NOW见具体 API检查 timeout 或组件状态
-3 ~ -9task_mgmt.update_task见 2.9 节按字段逐项核对
1HealthCheck HW_INIT_COMPLETED硬件初始化完成但业务未完成等待 ServiceCompleted
Lua error(message)validate / mdbRedfish 风格消息对象name / format
SQLite errmsgdatabaseSQL 或约束失败对照 schema 与 SQL

完整 D-Bus 方法错误以对端组件和 messages.* 生成为准。

5.3 最小化复现与证据收集

  1. 记录 require 模块名、D-Bus 服务/路径/接口/方法、上下文和返回错误。
  2. 执行宿主组件 Dump,收集 mdb_info.log 与进程日志。
  3. busctl --user tree/introspect/call 确认 maca 与目标对象。
  4. 无自定义接口的路径改用 GetManagedObjects
  5. 保留插件目录列表和 service.json 依赖声明。

5.4 调试方法

bash
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_mdbtest_loggingtest_validatetest_dbustest_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/?.lualua_cpath/opt/bmc/libmc/luaclib/?.so
  • 规避方案:通过 bingo/Conan 正式部署,不要只拷贝单个文件。
  • 适用版本:1.130.38。

Q2:为什么出现 invalid interface

  • 问题描述:mdb.get_objectinvalid 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_levelDLOG_NOTICE,更低级别被过滤。
  • 解决方案:调用 SetDlogLevel 并指定有效小时数;到期后恢复 NOTICE。
  • 规避方案:在复现窗口内打开 debug,避免生产环境长期 MASS/DEBUG。
  • 适用版本:1.130.38。