简介
OpenWRT的是进程间通信的通用框架。在项目开发中,利用该技术可以将消息,从一个进程发送给另一个进程,它有封装的API,易于使用与理解的demo。避免了去编写非常底层的进程间通信代码(比如Linux中常用的进程间通信方法,管道,信号量,套接字),可以专注于逻辑实现。
实现原理
下面以一个例子说明ubus的工作原理:
ubusd提供ubus的Server总线功能,其下连接着无数的client客户端。
下图中,client2试图通过ubus修改ip地址,而修改ip地址的函数在client1中定义。
client2进行请求的整个过程为:
-
client1向ubusd注册了两个对象:“interface”和“dotalk”,其中“interface”对象中注册了两个method:“getlanip”和“setlanip”,对应的处理函数分别为func1()和func2()。“dotalk”对象中注册了两个method:“sayhi”和“saybye”,对应的处理函数分别为func3()和func4()。
-
接着创建一个client2用来与client1通信,注意,两个client之间不能直接通信,需要经ubusd(server)中转。
-
client2就是shell/lua/C客户端。假设这里使用shell客户端,在终端输入以下命令:
ubus call interface setlanip ‘{“ip”:“10.0.0.1”, “mask”:24}’
ubus的call命令带三个参数:请求的对象名,需要调用的方法名,要传给方法的参数。
-
消息发到server后,server根据对象名找到应该将请求转发给client1,然后将消息发送到client1,client1进而调用func2()接受参数并处理,如果处理完成后需要回复client2,则发送回复消息。
操作实例
查看挂载在ubus总线上的对象和方法,下图省略一些内容,只查看了system对象和提供的方法。
root@H1:/# ubus -v list
......
'system' @21b07ab8
"board":{}
"info":{}
"reboot":{}
"watchdog":{"frequency":"Integer","timeout":"Integer","magicclose":"Boolean","stop":"Boolean"}
"signal":{"pid":"Integer","signum":"Integer"}
"validate_firmware_image":{"path":"String"}
"sysupgrade":{"path":"String","force":"Boolean","backup":"String","prefix":"String","command":"String","options":"Table"}
......
其中,
- ‘system’ @@21b07ab8:表示应用创建了system对象,挂载在ubus总线上,并且其client id为21b07ab8
- “board”:{}:表示提供方法名’board’,调用时不需要提供参数(以下类推)
- “signal”:{“pid”:“Integer”,“signum”:“Integer”}:表示提供方法名’signal’,调用时需要提供json参数,分别为pid(数字类型)和signum(数字类型)
例如,我们获取系统的软件版本信息:
root@H1:/# ubus call system board
{
"kernel": "5.10.110",
"hostname": "H1",
"system": "ARMv8 Processor rev 2",
"model": "Firefly RK3308-ROC-CC-PLUS Board",
"board_name": "firefly,rk3308-roc-cc-plus",
"rootfs_type": "ext4",
"release": {
"distribution": "OpenWrt",
"version": "22.03.3",
"revision": "r20028-43d71ad93e",
"target": "rockchip/armv8",
"description": "OpenWrt 22.03.3 r20028-43d71ad93e"
}
}
例如:我们终止掉某个应用:
root@H1:/# ps | grep cat
4154 root 1248 S cat /proc/kmsg
root@H1:/# ubus call system signal "{'pid':4154, 'signum':2}"
[1]+ Interrupt cat /proc/kmsg
Ubus编程
Ubus编程,总结起来分为三种应用场景:
- invoke调用:上面操作实例就是使用了invoke调用。实际上通过ubus -v list查看到的对象和应用,是系统的netifd进程(OpenWRT的网络管理后端)和hostapd进程(AP无线热点进程),通过invoke接口注册到ubus总线上的,其他客户端(比如shell调用ubus call)发送请求,将参数发送到netifd或hostapd,之后,netifd和hostapd对发送来的消息进行处理,并进行回复。该注意的是,整个过程是同步的,发送请求的客户端会阻塞等待消息的回复。
- event调用:挂载在Ubus总线上的客户端A广播消息,挂载的其他客户端B,C,D自己去监听。
- sub和pub调用:挂载在Ubus总线的客户端不定期将消息发送到某个主题,挂载的其他客户单B,C,D去监听对应主题,从而获取信息
DEMO
01_ubus_server.c
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <libubox/blobmsg_json.h>
#include <libubox/list.h>
#include <libubox/uloop.h>
#include <libubox/ustream.h>
#include <libubox/utils.h>
#include <libubus.h>
struct ubus_context *ctx;
static struct blob_buf b;
enum {
LOOP_A,
LOOP_B,
LOOP_C,
_LOOP_MAX,
};
static const struct blobmsg_policy loop_policy[] = {
{ .name = "a", .type = BLOBMSG_TYPE_BOOL },
{ .name = "b", .type = BLOBMSG_TYPE_STRING },
{ .name = "c", .type = BLOBMSG_TYPE_INT32 },
};
// 定时器超时回调
static void ubus_reconn_timer_cb(struct uloop_timeout* timeout)
{
static struct uloop_timeout reconn_timer = {
.cb = ubus_reconn_timer_cb
};
if (ubus_reconnect(ctx, NULL) != 0) {
printf("Failed to reconnect, trying again in 2 seconds\n");
uloop_timeout_set(&reconn_timer, 2000);
return;
}
// 添加ubus到uloop
ubus_add_uloop(ctx);
fcntl(ctx->sock.fd, F_SETFD,
fcntl(ctx->sock.fd, F_GETFD) | FD_CLOEXEC);
printf("Reconnected to ubus, new id: %d\n", ctx->local_id);
}
// 连接断开回调
static void ubus_conn_lost_cb(struct ubus_context* ctx)
{
printf("Connection lost, trying to connect again after 2 seconds\n");
ubus_reconn_timer_cb(NULL);
}
static int hello_cb(struct ubus_context* ctx, struct ubus_object* obj, struct ubus_request_data* req, const char* method, struct blob_attr* msg)
{
blob_buf_init(&b, 0);
blobmsg_add_string(&b, "hello", "world");
ubus_send_reply(ctx, req, b.head);
return 0;
}
static int loop_cb(struct ubus_context* ctx, struct ubus_object* obj, struct ubus_request_data* req, const char* method, struct blob_attr* msg)
{
struct blob_attr *tb[_LOOP_MAX];
blobmsg_parse(loop_policy, _LOOP_MAX, tb, blob_data(msg), blob_len(msg));
blob_buf_init(&b, 0);
if (tb[LOOP_A]) {
bool a = blobmsg_get_bool(tb[LOOP_A]);
blobmsg_add_u8(&b, "a", a);
}
if (tb[LOOP_B]) {
char *str = blobmsg_get_string(tb[LOOP_B]);
blobmsg_add_string(&b, "b", str);
}
if (tb[LOOP_C]) {
int c = blobmsg_get_u32(tb[LOOP_C]);
blobmsg_add_u32(&b, "c", c);
}
ubus_send_reply(ctx, req, b.head);
return 0;
}
int main()
{
static struct ubus_method hello_methods[] = {
UBUS_METHOD_NOARG("hello", hello_cb),
UBUS_METHOD("loop", loop_cb, loop_policy),
};
static struct ubus_object_type hello_type = {
.name = "hello",
.id = 0,
.methods = hello_methods,
.n_methods = ARRAY_SIZE(hello_methods),
};
static struct ubus_object hello_obj = {
.name = "hello",
.type = &hello_type,
.methods = hello_methods,
.n_methods = ARRAY_SIZE(hello_methods),
};
uloop_init();
if ((ctx = ubus_connect(NULL))) {
printf("Connected ubus as id: %d\n", ctx->local_id);
ctx->connection_lost = ubus_conn_lost_cb;
}
// 添加ubus到uloop
ubus_add_uloop(ctx);
fcntl(ctx->sock.fd, F_SETFD,
fcntl(ctx->sock.fd, F_GETFD) | FD_CLOEXEC);
// 添加object到ubus
if (ubus_add_object(ctx, &hello_obj)) {
printf("Failed to add object %s to ubus\n", hello_obj.name);
}
uloop_run();
uloop_done();
return 0;
}
现象:
root@DataJar-0C7D:~# ubus -v list hello
'hello' @01678e9a
"hello":{}
"loop":{"a":"Boolean","b":"String","c":"Integer"}
root@DataJar-0C7D:~# ubus call hello hello
{
"hello": "world"
}
root@DataJar-0C7D:~# ubus call hello loop '{"a":false}'
{
"a": false
}
root@DataJar-0C7D:~# ubus call hello loop '{"a":false, "b":"123456"}'
{
"a": false,
"b": "123456"
}
注意点:
- 连接之后,可能会断开,比如 ubusd 被杀掉了,所以在 ubus_connect 之后,需要在 connection_lost 里面添加断开之后,添加定时器进行重连 ubus
02_ubus_client.c
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <libubox/blobmsg_json.h>
#include <libubox/list.h>
#include <libubox/uloop.h>
#include <libubox/ustream.h>
#include <libubox/utils.h>
#include <libubus.h>
static struct ubus_context* ctx;
static struct blob_buf b;
static void scan_cb(struct ubus_request *req, int type, struct blob_attr *msg)
{
if (!msg)
return;
// blobmsg_format_json 返回一块动态分配内存,需要释放
char* json_string = blobmsg_format_json(msg, true);
char** recv_string = (char**)(req->priv);
*recv_string = json_string;
}
int main(int argc, char **argv)
{
unsigned int id;
char *path = "iwinfo";
char *method = "scan";
// 连接ubus
if (!(ctx = ubus_connect(NULL))) {
printf("Failed to connect ubus!\n");
return -1;
}
// 查找iwinfo
if (ubus_lookup_id(ctx, path, &id)) {
printf("Failed to lookup ubus path: %s\n", path);
return -1;
}
blob_buf_init(&b, 0);
blobmsg_add_string(&b, "device", "wlan0");
// ubus_invoke的timeout参数为0,表示不进行超时处理
int ms_timeout = 0;
char *recv_string = NULL;
// 等价于:ubus call iwinfo scan '{"device":"wlan0"}'
// ubus_invoke 确保回调执行完才返回
int ret = ubus_invoke(ctx, id, "scan", b.head, scan_cb, &recv_string, ms_timeout);
if (ret) {
printf("Unable to invoke method: scan");
return -1;
}
if (recv_string) {
printf("recv: %s\n", recv_string);
free(recv_string);
}
return 0;
}
现象:
等价于调用: ubus call iwinfo scan ‘{“device”:“wlan0”}’
root@DataJar-0C7D:~# ./02_ubus_client
[ 2872.557492] start_addr=(0x8000), end_addr=(0x10000), buffer_size=(0x8000), smp_number_max=(4096)
recv: {"results":[{"ssid":"sjf_2.4G","bssid":"74:B9:1E:05:E2:B1","mode":"Master","band":2,"channel":1,"mhz":2412,"signal":-54,"quality":56,"quality_max":70,"ht_operation":{"primary_channel":}
注意点:
- ubus_invoke 是阻塞运行的函数,在ms_timeout设置了这个函数最长阻塞时间,其中的回调函数确保在ubus_invoke回调完成之前执行。
03_ubus_pub.c
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <libubox/blobmsg_json.h>
#include <libubox/list.h>
#include <libubox/uloop.h>
#include <libubox/ustream.h>
#include <libubox/utils.h>
#include <libubus.h>
struct ubus_context *ctx;
static struct blob_buf b;
static struct uloop_timeout notify_timer;
static int hello_cb(struct ubus_context* ctx, struct ubus_object* obj, struct ubus_request_data* req, const char* method, struct blob_attr* msg);
static struct ubus_method hello_methods[] = {
UBUS_METHOD_NOARG("hello", hello_cb),
};
static struct ubus_object_type hello_type = {
.name = "hello",
.id = 0,
.methods = hello_methods,
.n_methods = ARRAY_SIZE(hello_methods),
};
static struct ubus_object hello_obj = {
.name = "hello",
.type = &hello_type,
.methods = hello_methods,
.n_methods = ARRAY_SIZE(hello_methods),
};
static int hello_cb(struct ubus_context* ctx, struct ubus_object* obj, struct ubus_request_data* req, const char* method, struct blob_attr* msg)
{
blob_buf_init(&b, 0);
blobmsg_add_string(&b, "hello", "world");
ubus_send_reply(ctx, req, b.head);
return 0;
}
static void notify_timer_cb(struct uloop_timeout *timeout)
{
uloop_timeout_set(timeout, 1000);
// 如果没有人监听,直接退出
if (!hello_obj.has_subscribers)
return;
blob_buf_init(&b, 0);
blobmsg_add_string(&b, "hello", "world");
ubus_notify(ctx, &hello_obj, "say Hi!", b.head, -1);
}
// 定时器超时回调
static void ubus_reconn_timer_cb(struct uloop_timeout* timeout)
{
static struct uloop_timeout reconn_timer = {
.cb = ubus_reconn_timer_cb
};
if (ubus_reconnect(ctx, NULL) != 0) {
printf("Failed to reconnect, trying again in 2 seconds\n");
uloop_timeout_set(&reconn_timer, 2000);
return;
}
// 添加ubus到uloop
ubus_add_uloop(ctx);
fcntl(ctx->sock.fd, F_SETFD,
fcntl(ctx->sock.fd, F_GETFD) | FD_CLOEXEC);
printf("Reconnected to ubus, new id: %d\n", ctx->local_id);
}
// 连接断开回调
static void ubus_conn_lost_cb(struct ubus_context* ctx)
{
printf("Connection lost, trying to connect again after 2 seconds\n");
ubus_reconn_timer_cb(NULL);
}
int main()
{
uloop_init();
if ((ctx = ubus_connect(NULL))) {
printf("Connected ubus as id: %d\n", ctx->local_id);
ctx->connection_lost = ubus_conn_lost_cb;
}
// 添加ubus到uloop
ubus_add_uloop(ctx);
fcntl(ctx->sock.fd, F_SETFD,
fcntl(ctx->sock.fd, F_GETFD) | FD_CLOEXEC);
// 添加object到ubus
if (ubus_add_object(ctx, &hello_obj)) {
printf("Failed to add object %s to ubus\n", hello_obj.name);
}
// 初始化定时器
memset(¬ify_timer, 0, sizeof(notify_timer));
notify_timer.cb = notify_timer_cb;
uloop_timeout_set(¬ify_timer, 1000);
uloop_run();
uloop_done();
return 0;
}
现象:
root@DataJar-0C7D:~# ubus -v list hello
'hello' @c66a3a47
"hello":{}
root@DataJar-0C7D:~# ubus subscribe hello
{ "say Hi!": {"hello":"world"} }
{ "say Hi!": {"hello":"world"} }
{ "say Hi!": {"hello":"world"} }
{ "say Hi!": {"hello":"world"} }
注意点:
- 如果 ubus 上没有被监听,通过查询 ubus_object 的 has_subscribers 是否为 true,才进行通知,否则通知了也没有作用,这个消息是无用功
04_ubus_sub.c
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <libubox/blobmsg_json.h>
#include <libubox/list.h>
#include <libubox/uloop.h>
#include <libubox/ustream.h>
#include <libubox/utils.h>
#include <libubus.h>
static struct ubus_context *ctx;
static uint32_t obj_id;
static struct ubus_subscriber sub_event;
static struct uloop_timeout sub_timer;
static int subscriber_cb(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req,
const char *method, struct blob_attr *msg)
{
char* json_string = blobmsg_format_json(msg, true);
printf("notify_cb: %s\n", json_string);
free(json_string);
return 0;
}
static void subscriber_timeout_cb(struct uloop_timeout *timeout)
{
char* object = "hello";
int ret = ubus_lookup_id(ctx, object, &obj_id);
if (ret) {
printf("Failed to lookup object: %s\n", ubus_strerror(ret));
uloop_timeout_set(timeout, 1000);
return;
}
ret = ubus_subscribe(ctx, &sub_event, obj_id);
if (ret) {
printf("Failed to subscribe: %s\n", ubus_strerror(ret));
uloop_timeout_set(timeout, 1000);
return;
}
}
static void subscriber_remove_cb(struct ubus_context *ctx,
struct ubus_subscriber *obj, uint32_t id)
{
ubus_unsubscribe(ctx, obj, id);
subscriber_timeout_cb(&sub_timer);
}
int main(int argc, char **argv)
{
const char *ubus_socket = NULL;
uloop_init();
ctx = ubus_connect(ubus_socket);
if (!ctx) {
fprintf(stderr, "Failed to connect to ubus\n");
return -1;
}
// 添加ubus到uloop
ubus_add_uloop(ctx);
fcntl(ctx->sock.fd, F_SETFD,
fcntl(ctx->sock.fd, F_GETFD) | FD_CLOEXEC);
// 初始化结构体
memset(&sub_event, 0, sizeof(sub_event));
sub_event.cb = subscriber_cb; // 这个回调中处理通知
sub_event.remove_cb = subscriber_remove_cb; // 这个回调一般用来发起重监听操作
memset(&sub_timer, 0, sizeof(sub_timer));
sub_timer.cb = subscriber_timeout_cb;
// 注册监听
ubus_register_subscriber(ctx, &sub_event);
// 监听回调
subscriber_timeout_cb(&sub_timer);
uloop_run();
uloop_done();
return 0;
}
现象:
root@DataJar-0C7D:~# ./03_ubus_pub &
root@DataJar-0C7D:~# Connected ubus as id: -1614399908
root@DataJar-0C7D:~# ./04_ubus_sub
notify_cb: {"hello":"world"}
notify_cb: {"hello":"world"}
notify_cb: {"hello":"world"}
notify_cb: {"hello":"world"}
注意点:
- 监听之前需要用 ubus_lookup_id 查找 ubus,之后调用 ubus_subscribe 开始监听
- 如果监听失败了,可以添加定时器,超时重新监听
- 如果被监听的 ubus 断开了,remove_cb 会被回调,可以在里面添加定时器,重新监听
05_ubus_listener.c
#include <libubox/uloop.h>
#include <libubox/ustream.h>
#include <libubox/utils.h>
#include <libubus.h>
#include <libubox/blobmsg_json.h>
static struct ubus_context * ctx = NULL;
static const char * cli_path;
#define UBUS_EVENT_ADD_DEVICE "add_device"
#define UBUS_EVENT_REMOVE_DEVICE "rm_device"
static void ubus_probe_device_event(struct ubus_context *ctx, struct ubus_event_handler *ev,
const char *type, struct blob_attr *msg)
{
char *str;
if (!msg)
return;
/*
在这里实现收到事件后的动作。
event也可以传递消息,放在msg中。
本例子只是将返回的消息打印出来。
*/
str = blobmsg_format_json(msg, true);
printf("{ \"%s\": %s }\n", type, str);
free(str);
}
static int client_ubus_register_events()
{
static struct ubus_event_handler listener;
int ret = 0;
/* 注册特定event的listener。多个event可以使用同一个listener */
memset(&listener, 0, sizeof(listener));
listener.cb = ubus_probe_device_event;
ret = ubus_register_event_handler(ctx, &listener, UBUS_EVENT_ADD_DEVICE);
if (ret)
{
fprintf(stderr, "register event failed.\n");
return -1;
}
ret = ubus_register_event_handler(ctx, &listener, UBUS_EVENT_REMOVE_DEVICE);
if (ret)
{
ubus_unregister_event_handler(ctx, &listener);
fprintf(stderr, "register event failed.\n");
return -1;
}
return 0;
}
static void ubus_add_fd(void)
{
ubus_add_uloop(ctx);
#ifdef FD_CLOEXEC
fcntl(ctx->sock.fd, F_SETFD,
fcntl(ctx->sock.fd, F_GETFD) | FD_CLOEXEC);
#endif
}
static void ubus_reconn_timer(struct uloop_timeout *timeout)
{
static struct uloop_timeout retry =
{
.cb = ubus_reconn_timer,
};
int t = 2;
if (ubus_reconnect(ctx, cli_path) != 0) {
uloop_timeout_set(&retry, t * 1000);
return;
}
ubus_add_fd();
}
static void ubus_connection_lost(struct ubus_context *ctx)
{
ubus_reconn_timer(NULL);
}
static int client_ubus_init(const char *path)
{
uloop_init();
cli_path = path;
ctx = ubus_connect(path);
if (!ctx)
{
printf("ubus connect failed\n");
return -1;
}
printf("connected as %08x\n", ctx->local_id);
ctx->connection_lost = ubus_connection_lost;
ubus_add_fd();
return 0;
}
static void client_ubus_done(void)
{
if (ctx)
ubus_free(ctx);
}
int main(int argc, char * argv[])
{
char * path = NULL;
/* 连接ubusd */
if (UBUS_STATUS_OK != client_ubus_init(path))
{
printf("ubus connect failed!\n");
return -1;
}
/* 注册某个事件的处理函数 */
client_ubus_register_events();
uloop_run();
client_ubus_done();
return 0;
}
05_ubus_event_notify.c
#include <libubox/uloop.h>
#include <libubox/ustream.h>
#include <libubox/utils.h>
#include <libubus.h>
#include <libubox/blobmsg_json.h>
static struct ubus_context * ctx = NULL;
static struct blob_buf b;
static const char * sock_path;
static int server_ubus_send_event(void)
{
blob_buf_init(&b, 0);
/* 需要传递的参数 */
blobmsg_add_u32(&b, "major", 3);
blobmsg_add_u32(&b, "minor", 56);
blobmsg_add_string(&b, "name", "mmc01");
/* 广播名为"add_device"的事件 */
return ubus_send_event(ctx, "add_device", b.head);
}
static int display_ubus_init(const char *path)
{
uloop_init();
sock_path = path;
ctx = ubus_connect(path);
if (!ctx)
{
printf("ubus connect failed\n");
return -1;
}
printf("connected as %08x\n", ctx->local_id);
return 0;
}
static void display_ubus_done(void)
{
if (ctx)
ubus_free(ctx);
}
int main(int argc, char * argv[])
{
char * path = NULL;
if (-1 == display_ubus_init(path))
{
printf("ubus connect failed!\n");
return -1;
}
server_ubus_send_event();
display_ubus_done();
return 0;
}
06_ubus_rpc.c
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <libubox/blobmsg_json.h>
#include <libubox/list.h>
#include <libubox/uloop.h>
#include <libubox/ustream.h>
#include <libubox/utils.h>
#include <libubus.h>
struct ubus_context *ctx;
static struct blob_buf b;
static struct uloop_process proc;
static struct ubus_request_data new_req;
// 定时器超时回调
static void ubus_reconn_timer_cb(struct uloop_timeout* timeout)
{
static struct uloop_timeout reconn_timer = {
.cb = ubus_reconn_timer_cb
};
if (ubus_reconnect(ctx, NULL) != 0) {
printf("Failed to reconnect, trying again in 2 seconds\n");
uloop_timeout_set(&reconn_timer, 2000);
return;
}
// 添加ubus到uloop
ubus_add_uloop(ctx);
fcntl(ctx->sock.fd, F_SETFD,
fcntl(ctx->sock.fd, F_GETFD) | FD_CLOEXEC);
printf("Reconnected to ubus, new id: %d\n", ctx->local_id);
}
// 连接断开回调
static void ubus_conn_lost_cb(struct ubus_context* ctx)
{
printf("Connection lost, trying to connect again after 2 seconds\n");
ubus_reconn_timer_cb(NULL);
}
static int hello_cb(struct ubus_context* ctx, struct ubus_object* obj, struct ubus_request_data* req, const char* method, struct blob_attr* msg)
{
blob_buf_init(&b, 0);
blobmsg_add_string(&b, "hello", "world");
ubus_send_reply(ctx, req, b.head);
return 0;
}
static void process_cb(struct uloop_process *c, int ret)
{
// 判断是否是通过exit退出,而不是信号
if (WIFEXITED(ret)) {
printf("child process exit with: %d\n", WEXITSTATUS(ret));
blob_buf_init(&b, 0);
blobmsg_add_string(&b, "defer", "done");
// 返回数据
ubus_send_reply(ctx, &new_req, b.head);
// 完成延时请求
ubus_complete_deferred_request(ctx, &new_req, 0);
}
}
static int defer_cb(struct ubus_context* ctx, struct ubus_object* obj, struct ubus_request_data* req, const char* method, struct blob_attr* msg)
{
pid_t pid = fork();
const char* args[] = {"sleep", "10", NULL};
if (!pid) {
execvp(args[0], (char **)args);
exit(127);
}
proc.cb = process_cb;
proc.pid = pid;
uloop_process_add(&proc);
// 存储延迟请求
ubus_defer_request(ctx, req, &new_req);
return 0;
}
int main()
{
static struct ubus_method hello_methods[] = {
UBUS_METHOD_NOARG("hello", hello_cb),
UBUS_METHOD_NOARG("defer", defer_cb),
};
static struct ubus_object_type hello_type = {
.name = "hello",
.id = 0,
.methods = hello_methods,
.n_methods = ARRAY_SIZE(hello_methods),
};
static struct ubus_object hello_obj = {
.name = "hello",
.type = &hello_type,
.methods = hello_methods,
.n_methods = ARRAY_SIZE(hello_methods),
};
uloop_init();
if ((ctx = ubus_connect(NULL))) {
printf("Connected ubus as id: %d\n", ctx->local_id);
ctx->connection_lost = ubus_conn_lost_cb;
}
// 添加ubus到uloop
ubus_add_uloop(ctx);
fcntl(ctx->sock.fd, F_SETFD,
fcntl(ctx->sock.fd, F_GETFD) | FD_CLOEXEC);
// 添加object到ubus
if (ubus_add_object(ctx, &hello_obj)) {
printf("Failed to add object %s to ubus\n", hello_obj.name);
}
uloop_run();
uloop_done();
return 0;
}
现象:
root@DataJar-0C7D:~# time ubus call hello defer
child process exit with: 0
{
"defer": "done"
}
real 0m 10.01s
user 0m 0.00s
sys 0m 0.00s
root@DataJar-0C7D:~# ubus call hello defer &
root@DataJar-0C7D:~# ubus call hello hello
{
"hello": "world"
}
root@DataJar-0C7D:~# ubus call hello hello
{
"hello": "world"
}
root@DataJar-0C7D:~# ubus call hello hello
{
"hello": "world"
}
root@DataJar-0C7D:~# ubus call hello hello
{
"hello": "world"
}
root@DataJar-0C7D:~# child process exit with: 0
{
"defer": "done"
}
[1]+ Done ubus call hello defer
注意:
- 这个例子,显示ubus也可以被阻塞,在上面的defer请求中,执行了一个子进程,并等待子进程的结果,于是调用 ubus_defer_request 存储请求,在子进程执行完成后,回调函数 process_cb 被调用,最后使用 ubus_complete_deferred_request 标记请求已经完成,ubus最终返回。在这个过程中, hello 请求仍然可以正常调用