ubus是openwrt平台的RPC机制,它包含3个部分:libubus,ubus,ubusd。
ubus是中心服务型,ubusd负责客户端和服务端的消息转发。
client <-> ubusd <-> server
ubus是命令行客户端。libubus是ubus的c库,可以实现客户端和服务端。
ubus消息,有一个消息头,然后跟着payload。
struct ubus_msghdr {
uint8_t version;
uint8_t type;
uint16_t seq;
uint32_t peer;
} __packetdata;
type是消息的类型:
enum ubus_msg_type {
/* initial server message */
UBUS_MSG_HELLO,
/* generic command response */
UBUS_MSG_STATUS,
/* data message response */
UBUS_MSG_DATA,
/* ping request */
UBUS_MSG_PING,
/* look up one or more objects */
UBUS_MSG_LOOKUP,
/* invoke a method on a single object */
UBUS_MSG_INVOKE,
UBUS_MSG_ADD_OBJECT,
UBUS_MSG_REMOVE_OBJECT,
/*
* subscribe/unsubscribe to object notifications
* The unsubscribe message is sent from ubusd when
* the object disappears
*/
UBUS_MSG_SUBSCRIBE,
UBUS_MSG_UNSUBSCRIBE,
/*
* send a notification to all subscribers of an object.
* when sent from the server, it indicates a subscription
* status change
*/
UBUS_MSG_NOTIFY,
UBUS_MSG_MONITOR,
/* must be last */
__UBUS_MSG_LAST,
};
seq是消息的序号,每个endpoint都保存一个seq到 context,请求的时候,这个seq不停的递增,这样就可以标识每一个request。
peer表示对端是谁,如果是发给ubusd,比如add object消息,peer为0,如果是ubus call,则peer是object id。
struct ubus_context {
struct list_head requests;
struct avl_tree objects;
struct list_head pending;
struct uloop_fd sock;
struct uloop_timeout pending_timer;
uint32_t local_id;
uint16_t request_seq;
bool cancel_poll;
int stack_depth;
void (*connection_lost)(struct ubus_context *ctx);
void (*monitor_cb)(struct ubus_context *ctx, uint32_t seq, struct blob_attr *data);
struct ubus_msghdr_buf msgbuf;
uint32_t msgbuf_data_len;
int msgbuf_reduction_counter;
};
connection_lost成员是一个回调函数,在连接丢失的时候执行。
缺省是 ubus_default_connection_lost
static void ubus_default_connection_lost(struct ubus_context *ctx)
{
if (ctx->sock.registered)
uloop_end();
}
会执行uloop_end()
static inline void uloop_end(void)
{
uloop_cancelled = true;
}
结束uloop。
1 接口 ubus_connect()
struct ubus_context *ubus_connect(const char *path)
path是ubusd服务器的unix socket路径,传入NULL,使用默认的路径。
这个接口分配一个ubus_context 对象,然后调用ubus_connect_ctx()连接到ubusd。
其对应的释放接口是: void ubus_free(struct ubus_context *ctx)
调用 ubus_shutdown()。然后 free(ctx)
2 ubus_connect_ctx()
int ubus_connect_ctx(struct ubus_context *ctx, const char *path)
初始化 ctx。调用 ubus_reconnect(ctx, path) 连接到 ubusd。
其对应的释放接口为:void ubus_shutdown(struct ubus_context *ctx);
3 ubus_reconnect()
第一次连接,和重新连接都是调用这个接口。连接的实例id保存到 local_id。
注意:它会调用 ubus_refresh_state() 重新注册objects。
4 ubus_add_uloop()
static inline void ubus_add_uloop(struct ubus_context *ctx)
将ubus的socket加到uloop中运行。
客户端连接后,ubusd会回复一个hello消息。
自动连接,是在连接断开的时候,自动重新连接。连接的失败的时候,会起一个定时器重新连接,直接到连接成功。
struct ubus_auto_conn {
struct ubus_context ctx;
struct uloop_timeout timer;
const char *path;
ubus_connect_handler_t cb;
};
自动连接将ubus_context嵌入到自己的结构体中,timer用来做失败和重连时的定时器。
cb用来在连接成功之后执行,通常用来添加objects。
连接成功后,会调用 ubus_add_uloop()。
struct ubus_method {
const char *name;
ubus_handler_t handler;
unsigned long mask;
unsigned long tags;
const struct blobmsg_policy *policy;
int n_policy;
};
struct ubus_object_type {
const char *name;
uint32_t id;
const struct ubus_method *methods;
int n_methods;
};
struct ubus_object {
struct avl_node avl;
const char *name;
uint32_t id;
const char *path;
struct ubus_object_type *type;
ubus_state_handler_t subscribe_cb;
bool has_subscribers;
const struct ubus_method *methods;
int n_methods;
};
一个定义object的例子:
static const struct blobmsg_policy route_policy[__HR_MAX] = {
[HR_TARGET] = { .name = "target", .type = BLOBMSG_TYPE_STRING },
[HR_V6] = { .name = "v6", .type = BLOBMSG_TYPE_BOOL },
[HR_INTERFACE] = { .name = "interface", .type = BLOBMSG_TYPE_STRING },
};
static struct ubus_method main_object_methods[] = {
{ .name = "restart", .handler = netifd_handle_restart },
{ .name = "reload", .handler = netifd_handle_reload },
UBUS_METHOD("add_host_route", netifd_add_host_route, route_policy),
{ .name = "get_proto_handlers", .handler = netifd_get_proto_handlers },
UBUS_METHOD("add_dynamic", netifd_add_dynamic, dynamic_policy),
UBUS_METHOD("netns_updown", netifd_netns_updown, netns_updown_policy),
};
static struct ubus_object_type main_object_type =
UBUS_OBJECT_TYPE("netifd", main_object_methods);
static struct ubus_object main_object = {
.name = "network",
.type = &main_object_type,
.methods = main_object_methods,
.n_methods = ARRAY_SIZE(main_object_methods),
};
ubus_object的type是用来定义object有哪些method,method的policy有哪些。type会发给ubusd,ubusd用来来做数据合法性验证。
ubus_object的methods是给自己用的,不发给ubusd,当有消息来时,用来找到method,并执行相关的cb。
有些特殊的object,不需要type,比如 event和subscribe添加的object,他们只需要一个object id用来追踪事件等。
添加、删除object的接口,注意object需要long live,不能是一个临时变量,其作为一个指针保存在context的avl树中。
int ubus_add_object(struct ubus_context *ctx, struct ubus_object *obj);
int ubus_remove_object(struct ubus_context *ctx, struct ubus_object *obj);
查找一个或所有object:
int ubus_lookup(struct ubus_context *ctx, const char *path,
ubus_lookup_handler_t cb, void *priv);
根据名字查找object id:
int ubus_lookup_id(struct ubus_context *ctx, const char *path, uint32_t *id);
ubus call 方式,就是调用的invoke接口,参数信息:
obj: obj的id
method:method的名字(字符串类型)
msg: blob格式的payload
cb: 回调函数
timeout: 超时时间
static inline int
ubus_invoke(struct ubus_context *ctx, uint32_t obj, const char *method,
struct blob_attr *msg, ubus_data_handler_t cb, void *priv,
int timeout)
{
return ubus_invoke_fd(ctx, obj, method, msg, cb, priv, timeout, -1);
}
static inline int
ubus_invoke_async(struct ubus_context *ctx, uint32_t obj, const char *method,
struct blob_attr *msg, struct ubus_request *req)
{
return ubus_invoke_async_fd(ctx, obj, method, msg, req, -1);
}
订阅一个object的事件
struct ubus_subscriber {
struct ubus_object obj;
ubus_handler_t cb;
ubus_remove_handler_t remove_cb;
};
订阅者,也要添加一个object,不过这个object,由libubus的接口自己封装好了:
int ubus_register_subscriber(struct ubus_context *ctx, struct ubus_subscriber *obj);
static inline int
ubus_unregister_subscriber(struct ubus_context *ctx, struct ubus_subscriber *obj)
{
return ubus_remove_object(ctx, &obj->obj);
}
上述接口ubus_register_subscriber注册一个订阅者后,还需要调用下面的接口,来订阅object。
int ubus_subscribe(struct ubus_context *ctx, struct ubus_subscriber *obj, uint32_t id);
int ubus_unsubscribe(struct ubus_context *ctx, struct ubus_subscriber *obj, uint32_t id);
id是object的id。
通知者,使用下面的接口,来通知subscribe:
int ubus_notify(struct ubus_context *ctx, struct ubus_object *obj,
const char *type, struct blob_attr *msg, int timeout);
typedef void (*ubus_event_handler_t)(struct ubus_context *ctx, struct ubus_event_handler *ev,
const char *type, struct blob_attr *msg);
struct ubus_event_handler {
struct ubus_object obj;
ubus_event_handler_t cb;
};
obj不用自己去封装,由libubus的注册接口自己封装。
事件包括事件名、事件payload两部分,事件名是一个字符串,payload是一个json格式的数据。
发送事件:
int ubus_send_event(struct ubus_context *ctx, const char *id,
struct blob_attr *data);
id,是事件名,字符串类型。
data,是事件的payload,blob json格式。
注册事件接收:
int ubus_register_event_handler(struct ubus_context *ctx,
struct ubus_event_handler *ev,
const char *pattern);
pattern是要监听的事件名,* 表示所有事件。
这个接口可以调用多次,来同时监听多个事件。第一次注册时,会添加一个object。
static inline int ubus_unregister_event_handler(struct ubus_context *ctx,
struct ubus_event_handler *ev)
{
return ubus_remove_object(ctx, &ev->obj);
}
例子:
// server
void test_event_child()
{
struct ubus_context * ctx;
struct blob_buf b = {0};
blob_buf_init(&b, 0);
ctx = ubus_connect(NULL);
if (!ctx) {
fprintf(stderr, "child connect ubus failed\n");
return;
}
ubus_send_event(ctx, "myevent", b.head);
}
// client
void my_event_handler(struct ubus_context *ctx,
struct ubus_event_handler *ev,
const char *type, struct blob_attr *msg)
{
printf("recv event: %s\n", type);
}
struct ubus_event_handler ev_handler = {
.cb = my_event_handler,
};
void test_event_parent()
{
struct ubus_context * ctx;
int ret;
uloop_init();
ctx = ubus_connect(NULL);
if (!ctx) {
fprintf(stderr, "parent connect ubus failed\n");
return;
}
ret = ubus_register_event_handler(ctx, &ev_handler, "myevent");
if (ret < 0) {
fprintf(stderr, "register event handler failed: %s\n", ubus_strerror(ret));
return;
}
ubus_add_uloop(ctx);
uloop_run();
}
参考openwrt使用到ubus的包:
netifd/ubus.c
ubox/logd.c
https://e-mailky.github.io/2017-08-14-ubus2