新手向-C接口调用dbus
工作需要用c接口调用dbus,在这里写篇博客记录一下。
1. 方案比较
用C接口调用dbus一般来说有3种方案,分别是libdbus、GDBus(GIO的一部分)和sd-bus(systemd的一部分),以下比较了3种方案的优劣:
接口方案 | libdbus | GDBus | sd-bus |
api类型 | 低层 C API | GLib 的高层封装 | 系统级 C API |
依赖关系 | 需要dbus | 需要glib | 需要systemd |
易用性 | 复杂 | 简单 | 较简单 |
性能 | 好 | 有少量开销 | 好 |
之前一直用的libdbus,不过太复杂了,维护成本高。
这次是在linux上使用,系统自带systemd,因此这次选择了sd-bus,简单且不需要额外的依赖。
2. sd-bus的使用
2.1 安装
sudo apt update
sudo apt install libsystemd-dev
2.2 Demo
// sd-bus的头文件
#include <systemd/sd-bus.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
sd_bus *bus = NULL;
sd_bus_message *msg = NULL;
sd_bus_error error = SD_BUS_ERROR_NULL;
int ret;
// 连接到system bus用sd_bus_open_system
// session bus的话用sd_bus_open_user
ret = sd_bus_open_system(&bus);
if (ret < 0) {
fprintf(stderr, "Failed to connect to system bus: %s\n", strerror(-ret));
return ret;
}
// 调用Reboot方法
ret = sd_bus_call_method(bus,
"org.freedesktop.login1", // Service to contact
"/org/freedesktop/login1", // Object path
"org.freedesktop.login1.Manager", // Interface name
"Reboot", // Method to call
&error, // No reply expected
&msg, // Output message
NULL); // No input arguments
if (ret < 0) {
fprintf(stderr, "Failed to call method: %s\n", strerror(-ret));
sd_bus_unref(bus);
return ret;
}
// 检查返回值
int response = 0;
// 假如返回值是int类型
ret = sd_bus_message_read(msg, "i", &response);
if (ret < 0) {
fprintf(stderr, "Failed to read response: %s\n", strerror(-ret));
} else {
printf("Method returned response: %d\n", response);
}
// Cleanup
sd_bus_message_unref(msg);
sd_bus_unref(bus);
return 0;
}
简单介绍一下,使用sd-bus的核心接口只有3个,连接到总线,调用method,以及读取返回值
2.2.1 连接到总线
连接到总线的接口有3个,函数原型如下:
int sd_bus_open_system(sd_bus **ret);
// 连接system bus
int sd_bus_open_user(sd_bus **ret);
// 连接session bus
int sd_bus_default(sd_bus **ret);
// 先连接session bus,不可用再连接system bus
2.2.2 调用method
函数原型如下:
// sd_bus_call_method 函数原型
int sd_bus_call_method(
sd_bus *bus, // 总线连接
const char *destination, // 目标服务名
const char *path, // 对象路径
const char *interface, // 接口名称
const char *member, // 方法名
sd_bus_error *ret_error, // 错误信息
sd_bus_message **reply, // 响应消息
const char *types, // 参数类型字符串
... // 可变参数列表
);
调用带多个参数的method
// 调用带多个参数的method
ret = sd_bus_call_method(bus,
"org.example.Service",
"/org/example/object",
"org.example.Interface",
"ComplexMethod",
&error,
&reply,
"siay", // string, int32, array of bytes
"test", // string参数
42, // int32参数
3, // 数组长度
(uint8_t[]){1,2,3}); // byte数组
/* 常用参数类型说明:
* s: string (const char *)
* u: uint32_t
* i: int32_t
* x: int64_t
* t: uint64_t
* b: boolean (int)
* y: byte (uint8_t)
* d: double
* ay: array of bytes
* as: array of strings
* a{sv}: dictionary of string keys to variant values
* v: variant
* o: object path
*/
调用带一个参数的method
// 调用带参数的方法
ret = sd_bus_call_method(bus,
"org.freedesktop.systemd1", // 服务
"/org/freedesktop/systemd1", // 路径
"org.freedesktop.systemd1.Manager", // 接口
"GetUnit", // 方法
&error, // 错误对象
&reply, // 响应消息
"s", // 参数类型: 字符串
"sshd.service"); // 参数值
2.2.3 读取返回值
函数原型如下,第一个入参是调用method,引用传递返回的reply。
// sd_bus_message_read 函数原型
int sd_bus_message_read(
sd_bus_message *m, // 消息对象
const char *types, // 数据类型字符串
... // 要读取的变量指针
);
读取多个返回值的示例如下:
int ret;
const char *str;
int32_t num;
uint8_t byte;
double dbl;
// 读取基本类型:字符串、32位整数、字节、双精度浮点数
ret = sd_bus_message_read(m, "siyd",
&str, // 字符串
&num, // 32位整数
&byte, // 字节
&dbl); // 双精度浮点数