ubus是Openwrt實現進程間通信的一種總線機制, 由三部分協作完成通信過程:ubusd守護進程,ubus服務端,ubus客戶端。
1. ubusd守護進程: 管理ubus服務端和客戶端的註冊,並作爲服務端和客戶端的中間人, 進行消息轉發, 所有消息均封裝成json格式。向ubus服務端傳遞ubus客戶端的請求(call),向ubus客戶端傳遞ubus服務端的執行結果。
2. ubus服務端(ubus server object): 提供軟件各種具體功能模塊的實現(methods), 服務端的對象名稱和具體的mothd名稱向ubusd進行註冊後,就可以被其他ubus客戶端進行傳呼(call) 。
3. ubus客戶端(ubus client object): 是具體功能(method)的傳呼方(caller), 其通用ubusd向某個ubus服務端呼叫請求(call)執行指定method。
4. 實際上,一個ubus object對象,即可以作爲服務端提供各種方法(method),也可以作爲客戶端來傳呼(call)其他對象的方法。
5. 下面是具體的實現例子:
ubus_server.c: 向ubusd註冊了一個名爲"ering_uobj"的對象 , 提供一個名稱爲"ering_method“的方法, 這個方法的實現依靠3個名稱爲"id","data","msg"的參數。 爲了簡單實現這個例子, 程序只將收到的這3個參數打印出來, 完成後向客戶端傳送一條確認消息。方法請求是通過回調函數來實現的,程序本身不會退出。
/*------------------------ ubus_server.c -----------------------------
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation.
An example for Openwrt UBUST communication.
Midas Zhou
---------------------------------------------------------------------*/
#include <stdio.h>
#include <stdint.h>
#include <libubus.h>
static struct ubus_context *ctx;
static struct ubus_request_data req_data;
static struct blob_buf bb;
static int ering_handler( struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg );
/* Enum for EGI policy order */
enum {
ERING_ID,
ERING_DATA,
ERING_MSG,
__ERING_MAX,
};
/* Ubus Policy */
static const struct blobmsg_policy ering_policy[] =
{
[ERING_ID] = { .name="id", .type=BLOBMSG_TYPE_INT32},
[ERING_DATA] = { .name="data", .type=BLOBMSG_TYPE_INT32 },
[ERING_MSG] = { .name="msg", .type=BLOBMSG_TYPE_STRING },
};
/* Ubus Methods */
static const struct ubus_method ering_methods[] =
{
UBUS_METHOD("ering_method", ering_handler, ering_policy),
};
/* Ubus object type */
static struct ubus_object_type ering_obj_type =
UBUS_OBJECT_TYPE("ering_uobj", ering_methods);
/* Ubus object */
static struct ubus_object ering_obj=
{
.name = "ering.host", /* with APP name */
.type = &ering_obj_type,
.methods = ering_methods,
.n_methods = ARRAY_SIZE(ering_methods),
//.path= /* useless */
};
void main(void)
{
int ret;
const char *ubus_socket=NULL; /* use default UNIX sock path: /var/run/ubus.sock */
/* 1. create an epoll instatnce descriptor poll_fd */
uloop_init();
/* 2. connect to ubusd and get ctx */
ctx=ubus_connect(ubus_socket);
if(ctx==NULL) {
printf("Fail to connect to ubusd!\n");
return;
}
/* 3. registger epoll events to uloop, start sock listening */
ubus_add_uloop(ctx);
/* 4. register a usb_object to ubusd */
ret=ubus_add_object(ctx, &ering_obj);
if(ret!=0) {
printf("Fail to register an object to ubus.\n");
goto UBUS_FAIL;
} else {
printf("Add '%s' to ubus @%u successfully.\n", ering_obj.name, ering_obj.id);
}
/* 5. uloop routine: events monitoring and callback provoking */
uloop_run();
uloop_done();
UBUS_FAIL:
ubus_free(ctx);
}
/*---------------------------------------------
A callback function for ubus methods handling
----------------------------------------------*/
static int ering_handler( struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg )
{
struct blob_attr *tb[__ERING_MAX]; /* for parsed attr */
/* Parse blob_msg from the caller to request policy */
blobmsg_parse(ering_policy, ARRAY_SIZE(ering_policy), tb, blob_data(msg), blob_len(msg));
/* print request msg */
printf("Receive msg from caller: ");
if(tb[ERING_ID])
printf("UBUS_ID=%u ", blobmsg_get_u32(tb[ERING_ID]));
if(tb[ERING_DATA])
printf("DATA=%u ", blobmsg_get_u32(tb[ERING_DATA]));
if(tb[ERING_MSG])
printf("MSG='%s' \n", blobmsg_data(tb[ERING_MSG]));
/* Do some job here according to caller's request */
/* send a reply msg to the caller for information */
blob_buf_init(&bb, 0);
blobmsg_add_string(&bb,"Ering reply", "Request is being proceeded!");
ubus_send_reply(ctx, req, bb.head);
/* ----- reply results to the caller -----
* NOTE: we may put proceeding job in a timeout task, just to speed up service response.
*/
ubus_defer_request(ctx, req, &req_data);
ubus_complete_deferred_request(ctx, req, UBUS_STATUS_OK);
}
ubus_client.c: 向ubusd註冊了一個名爲"ering_caller" 的對象, 沒有註冊任何method。它向"ering_uobj"對象傳呼(call)了"ering_method"方法, 在接收到服務端反饋的消息後將它打印出來,然後退出程序。
/*------------------------- ubus_client.c ------------------------
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation.
An example for Openwrt UBUS communication.
Midas Zhou
------------------------------------------------------------------*/
#include <stdio.h>
#include <libubus.h>
#include <libubox/blobmsg_json.h>
static struct ubus_context *ctx;
static struct blob_buf bb;
static void result_handler(struct ubus_request *req, int type, struct blob_attr *msg);
/* ubus object assignment */
static struct ubus_object ering_obj=
{
.name = "ering_caller",
#if 0
.type = &ering_obj_type,
.methods = ering_methods,
.n_methods = ARRAY_SIZE(ering_methods),
.path= /* useless */
#endif
};
void main(void)
{
int ret;
uint32_t host_id; /* ering host id */
const char *ubus_socket=NULL; /* use default UNIX sock path: /var/run/ubus.sock */
/* 1. create an epoll instatnce descriptor poll_fd */
uloop_init();
/* 2. connect to ubusd and get ctx */
ctx=ubus_connect(ubus_socket);
if(ctx==NULL) {
printf("Fail to connect to ubusd!\n");
return;
}
/* 3. registger epoll events to uloop, start sock listening */
ubus_add_uloop(ctx);
/* 4. register a usb_object to ubusd */
ret=ubus_add_object(ctx, &ering_obj);
if(ret!=0) {
printf("Fail to register an object to ubus.\n");
goto UBUS_FAIL;
} else {
printf("Add '%s' to ubus @%u successfully.\n",ering_obj.name, ering_obj.id);
}
/* 5. search a registered object with a given name */
if( ubus_lookup_id(ctx, "ering.host", &host_id) ) {
printf("ering.host is NOT found in ubus!\n");
goto UBUS_FAIL;
}
printf("ering.host is found in ubus @%u.\n",host_id);
/* 6. prepare request method policy and data */
blob_buf_init(&bb,0);
blobmsg_add_u32(&bb,"id", ering_obj.id); /* ERING_ID */
blobmsg_add_u32(&bb,"data", 123456); /* ERING_DATA */
blobmsg_add_string(&bb,"msg", "Hello, ERING!"); /* ERING_DATA */
/* 7. call the ubus host object */
ret=ubus_invoke(ctx, host_id, "ering_method", bb.head, result_handler, 0, 3000);
printf("Call result: %s\n", ubus_strerror(ret));
/* 8. uloop routine: events monitoring and callback provoking
However, we just ignore uloop in this example.
*/
//uloop_run();
uloop_done();
UBUS_FAIL:
ubus_free(ctx);
}
/* callback function for ubus_invoke to process result from the host
* Here we just print out the message.
*/
static void result_handler(struct ubus_request *req, int type, struct blob_attr *msg)
{
char *strmsg;
if(!msg)
return;
strmsg=blobmsg_format_json_indent(msg,true, 0); /* 0 type of format */
printf("Response from the host: %s\n", strmsg);
free(strmsg); /* need to free strmsg */
}
6. 本例子在openwrt widora環境下編譯通過, Makefile 如下, Makefile環境變量請根據自己實際情況修改:
export STAGING_DIR=/home/midas-zhou/openwrt_widora/staging_dir
COMMON_USRDIR=/home/midas-zhou/openwrt_widora/staging_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/usr/
CC= $(STAGING_DIR)/toolchain-mipsel_24kec+dsp_gcc-4.8-linaro_uClibc-0.9.33.2/bin/mipsel-openwrt-linux-gcc
CFLAGS += -I$(COMMON_USRDIR)/include
LDFLAGS += -L$(COMMON_USRDIR)/lib
LIBS += -lubus -lubox -lblobmsg_json -ljson_script -ljson-c
all: ubus_server ubus_client
ubus_server: ubus_server.c
$(CC) $(CFLAGS) $(LDFLAGS) $(LIBS) ubus_server.c -o ubus_server
ubus_client: ubus_client.c
$(CC) $(CFLAGS) $(LDFLAGS) $(LIBS) ubus_client.c -o ubus_client
clean:
rm -rf *.o ubus_server ubus_client
7. 試着將ubus封裝成EGI RING模塊, 以方便程序調用。具體代碼見 https://github.com/widora/ctest/tree/master/wegi/ering