如何使用libsystemd里的sd_bus接口

// 此模板仅供参考,如果不适用可以修改

问题描述

写了一段c代码调用libsystemd里的sd-bus接口获取/bmc/kepler/Systems/1/Boards/CpuBoard/CpuBoard_1_010102下的BoardType,将c代码用交叉编译工具编成.so文件,然后在lua里调用.so里的接口函数。在cli命令下使用busctl命令能够获取到,但c语言里调用sd-bus里的接口返回-113。

image

环境信息

  • 操作系统:[如 Ubuntu 24.04]

  • 软件版本:[如 OpenUBMC2509]

  • 硬件配置:[如 CPU、内存等]

重现步骤

  1. [第一步]

  2. [第二步]

  3. [第三步]

期望结果

知道如何使用libsystemd里的sd_bus接口获取dbus属性及调用dbus方法

实际结果

[实际发生了什么,包含错误信息]

尝试过的解决方案

[列出您已经尝试过的解决方案及结果]

分析结论

[详细总结您对此问题的分析结论]

答案内容由AI生成,仅供参考:
:white_check_mark: 总结:解决方案步骤

  1. 修改代码:将 sd_bus_default_system 改为 sd_bus_default_user
  2. 校验属性名:确认 BoardType(而非 Boardtype)是正确的属性名
  3. 设置环境变量:确保程序运行在有效的用户会话环境中
  4. 增加错误日志:打印 strerror(-r) 获取详细错误信息
  5. 测试独立运行:先写一个独立的 C 可执行程序测试,成功后再封装为 .so
点击此处查看详细分析解答

如何正确使用 libsystemd 中的 sd-bus 接口获取 D-Bus 属性

根据您提供的问题描述,您在使用 C 代码调用 libsystemd 提供的 sd-bus 接口时,无法成功获取 D-Bus 属性,返回错误码 -113(即 EHOSTDOWNECONNREFUSED),但在命令行使用 busctl --user 命令却能成功获取属性值 "CpuBoard"


:magnifying_glass_tilted_left: 问题分析

1. 错误码 -113 的含义

在 Linux 系统中,-113 对应错误号 EHOSTDOWN,表示“主机已关闭”或“目标服务不可达”。但在 D-Bus 上下文中,该错误通常意味着:

  • 连接到了错误的总线类型(system bus vs user bus)
  • 要访问的服务未在当前总线中运行或已失效

关键点:busctl --user 表明目标服务运行在 用户总线(User Bus) 上,而您的 C 代码调用的是 sd_bus_default_system(&bus),即连接 系统总线(System Bus)总线不匹配导致服务无法查找


:white_check_mark: 正确使用 sd-bus 的方法

:hammer_and_wrench: 修正建议:使用 sd_bus_default_user() 而非 sd_bus_default_system()

由于 busctl --user 明确表明操作的是 用户会话总线(User Session Bus),您的 C 代码也必须连接到同一总线。

:white_check_mark: 修正后的核心代码如下:

{
    sd_bus *bus = NULL;
    char *val = NULL;
    int r;
    FILE *file;

    memset(out, 0, out_len);

    file = fopen("/var/log/bus.log", "w+");
    if (!file) {
        return -1;
    }

    // 修改点:使用 user bus 而非 system bus
    r = sd_bus_default_user(&bus);
    if (r < 0) {
        fprintf(file, "Failed to connect to user bus: %s\n", strerror(-r));
        fclose(file);
        return -1;
    }

    // 确保连接建立完成
    r = sd_bus_flush_close_unref(bus); // 临时释放测试前先 flush
    bus = NULL;

    r = sd_bus_default_user(&bus);
    if (r < 0) {
        fprintf(file, "Reconnect failed: %s\n", strerror(-r));
        fclose(file);
        return -1;
    }

    // 获取属性(注意接口名大小写)
    r = sd_bus_get_property_string(
        bus,
        "bmc.kepler.general_hardware",  // 服务名
        "/bmc/kepler/Systems/1/Boards/CpuBoard/CpuBoard_1_010102",  // 对象路径
        "bmc.kepler.Systems.Board",     // 接口名
        "BoardType",                    // 属性名(原文为 Boardtype,应为 BoardType)
        NULL,
        &val
    );

    fprintf(file, "r: %d\n", r);

    if (r >= 0 && val) {
        snprintf(out, out_len, "%s", val);
        fprintf(file, "out: %s\n", out);
    } else {
        fprintf(file, "get_property failed, error: %s\n", strerror(-r));
        sd_bus_flush_close_unref(bus);
        fclose(file);
        return -1;
    }

    sd_bus_flush_close_unref(bus); // 正确释放资源
    fclose(file);
    return 0;
}

:pushpin: 关键说明

项目 说明
:white_check_mark: sd_bus_default_user() 用于连接当前用户的会话总线(Session Bus),与 busctl --user 一致
:cross_mark: sd_bus_default_system() 连接的是系统总线,bmc.kepler.general_hardware 可能未在此总线上注册
:input_latin_letters: 属性名大小写敏感 D-Bus 属性名区分大小写。BoardTypeBoardtype,请确认实际接口定义
:puzzle_piece: sd_bus_get_property_string() 是高层封装函数,内部自动处理消息发送与类型解析

:magnifying_glass_tilted_right: 其他验证和调试建议

1. 确认用户总线环境变量

用户总线依赖环境变量 DBUS_SESSION_BUS_ADDRESSXDG_RUNTIME_DIR,若 C 程序未在用户登录会话中运行(如后台服务或 SSH 启动),可能无法访问用户总线。

解决方案:

  • 设置环境变量(以常见值为例):
    export XDG_RUNTIME_DIR=/run/user/1000
    export DBUS_SESSION_BUS_ADDRESS=unix:path=$XDG_RUNTIME_DIR/bus
    
  • 或通过 loginctl 查看用户 session 并注入环境:
    loginctl show-user $USER
    dbus-launch your_program
    

2. 使用更低级 API 以增强控制能力

如果高层函数仍失败,可改用底层 API 手动调用 org.freedesktop.DBus.Properties.Get 方法:

r = sd_bus_call_method(
    bus,
    "bmc.kepler.general_hardware",
    "/bmc/kepler/Systems/1/Boards/CpuBoard/CpuBoard_1_010102",
    "org.freedesktop.DBus.Properties",
    "Get",
    NULL,  // 错误输出回调
    NULL,  // 回复消息指针(可选)
    "ss",  // 输入参数类型:service interface, property name
    "bmc.kepler.Systems.Board", "BoardType"
);

然后解析返回的 variant 类型值。


3. 日志建议

您已记录 r 值,建议增加 strerror(-r) 输出具体错误信息,便于诊断:

fprintf(file, "Error: %s (%d)\n", strerror(-r), r);

:books: 参考信息(来自上下文知识库)

  • libsystemd 提供了 sd_bus_default_systemsd_bus_default_user 接口用于连接系统总线和用户总线。
  • busctl --user 操作的是 用户总线,因此所有程序必须与之一致。
  • 您的应用场景属于 BMC 环境下的 Lua-C 协作系统,需确保 C 库与 Lua 所处的 D-Bus 上下文一致。

:white_check_mark: 总结:解决方案步骤

  1. 修改代码:将 sd_bus_default_system 改为 sd_bus_default_user
  2. 校验属性名:确认 BoardType(而非 Boardtype)是正确的属性名
  3. 设置环境变量:确保程序运行在有效的用户会话环境中
  4. 增加错误日志:打印 strerror(-r) 获取详细错误信息
  5. 测试独立运行:先写一个独立的 C 可执行程序测试,成功后再封装为 .so

:puzzle_piece: 附加建议:Lua 中调用注意事项

当您从 Lua 调用此 .so 时,请确保:

  • Lua 进程本身也在用户会话中启动(如通过 snlua 启动且已配置好环境)
  • .so 不应直接 free(val),而应将其复制到 Lua 可访问内存区,避免跨语言内存管理问题

如有需要,可进一步提供 dbus-monitor --session 输出来确认服务是否存在及属性是否可读。

使用sd_bus_default_user可以正常获取到BoardType,但当获取自己组件定义的某个obj下的属性获取显示-110超时,使用busctl –user可以很快获取到。这是什么原因呢

有尝试过使用sd_bus_open_system吗

因为只有使用busctl –user才能获取到信息。看AI回答需要连接到用户总线,然后使用接口连接到用户总线能够获取到general_hardware组件里的obj下的属性。现在问题是同样的接口无法获取到自定义组件里的obj下的属性,报获取超时(-110)。但使用busctl –user命令可以正常获取到自定义组件下的obj的属性,使用c语言里的接口不行,多次检查c语言里的service path interface prop名称也没问题,不知道具体哪里出错

-110通常是bus对端没发通,多次检查c语言里的service path interface prop名称也没问题的话,问题大概率还是出现在bus上

我们自定义组件里的service使用sd_bus接口访问对应属性都会返回-110;但使用busctl –user都能成功;这个是我们自定义组件哪里缺少什么配置吗

试下用sd_bus_open_user()接口

使用这个接口依然报-110。尝试通过接口获取自定义组件里的service的tree,仍然报-110。使用其它华为组件里的service获取tree就正常获取。感觉像是自定义组件里哪里配置的不对,但是使用busctl命令直接查看又能获取到,这个就很奇怪。目前我们这边看了很久,还是未看出哪里有问题

这个自定义组件是不是没有拉起dbus客户端

请问如何查看是组件否拉起了dbus客户端,使用busctl –user访问组件内的对象就是正常的,使用sd-bus接口就不行

这是同组件调用还是跨组件

是直接把这调用dbus接口访问属性的操作用c语言封装成.so库,然后通过lua调用.so库的接口

libsystemd相关的上开源社区讨论下呢