本文寫作目的是方便以后查詢使用,以實用為主。前期完全不會ubus使用的時候看了一些文章,確實是很詳盡的,但是我很難一下子進行應用(本人水平有限),在經過一些時間的使用之后逐漸了解其中的使用方法,希望這篇文章能夠總結的很容易懂,能夠幫到最開始接觸ubus的人。
關於ubus的基本使用機制可以參看我之前的一篇文章(https://www.cnblogs.com/y-c-y/p/12187422.html),本篇更注重於代碼使用。
ubus server
在本文中假設您已經對object等ubus基本概念已經有所了解了(如果沒有可以參見我之前的文章),本次代碼講解將圍繞以下兩個表格進行講解。
作為ubus server進程實現以下兩個表格的功能:
根據以上兩個表格,可以得出以下這些命令是本ubus server 進程支持的:
ubus call YCY my_ubus_method_1 '{"mac_addr":"60:03:4f:a0:52:51","ipv4_addr":"192.2.1.2"}'
ubus call YCY my_ubus_method_2 '{"mac_addr":"60:03:4f:a0:52:51","ipv4_addr":"192.2.1.2"}'
ubus call YCY my_ubus_method_3 '{"mac_addr":"60:03:4f:a0:52:51","ipv4_addr":"192.2.1.2"}'
ubus call CCYY my_ubus_method_1 '{"mac_addr":"60:03:4f:a0:52:51","ipv4_addr":"192.2.1.2"}'
ubus call CCYY my_ubus_method_2 '{"mac_addr":"60:03:4f:a0:52:51","ipv4_addr":"192.2.1.2"}'
ubus call CCYY my_ubus_method_3 '{"mac_addr":"60:03:4f:a0:52:51","ipv4_addr":"192.2.1.2"}'
ubus send my_notify '{"mac_addr":"60:03:4f:a0:52:51","ipv4_addr":"192.2.1.2"}'
ubus send my_notify_1 '{"mac_addr":"60:03:4f:a0:52:51","ipv4_addr":"192.2.1.2"}'
PS:
1.ubus call YCY my_ubus_method_1 '{"mac_addr":"60:03:4f:a0:52:51","ipv4_addr":"192.2.1.2"}'
ubus :指的是ubus命令,所有代碼中實現的ubus命令都可以通過ubus -v list查看到,或者是ubus list -v 。
call:指的是call調用,ubus調用方法還有另外的send。
YCY:指的是ubus object
my_ubus_method_1:指的是ubus method
'{"mac_addr":"60:03:4f:a0:52:51","ipv4_addr":"192.22.1.2"}':指的是ubus call method需要使用的參數,其實如果對應的method要用到這些是需要傳送的,如果有些method不需要參數(例如代碼中的method2和method3)就算傳了參數也沒有關系,ubus不會判定語法出錯,只不過是對應method並不會解析這些參數而已。
2.ubus send my_notify '{"mac_addr":"60:03:4f:a0:52:51","ipv4_addr":"192.2.1.2"}'
ubus :指的是ubus命令,所有代碼中實現的ubus命令都可以通過ubus -v list查看到,或者是ubus list -v 。
send :指的是send調用
'{"mac_addr":"60:03:4f:a0:52:51","ipv4_addr":"192.22.1.2"}':指的是ubus send需要使用的參數,具體同上。
3.其實從表格上也可以看出對於ubus call和ubus send兩種命令的使用差別,ubus call是需要有明確調用對象的,類似於單播命令,而ubus send是沒有明確調用對象的,是對所有的ubus進程發送notify消息,類似於廣播命令;而所有接收到這個notify的ubus client進程,如果注冊了這個notify,ubus client進程就會觸發回調函數處理這個notify,ubus client進程沒有注冊這個notify的就無視它。
ubus server代碼
注意事項(我想還是把注意事項寫在代碼前面為好,正確使用代碼比使用代碼更為重要,可以避免很多不必要的錯誤):
1.同一個ubus進程可以注冊多個ubus object,例如 ubus call CCYY my_ubus_method_1 '{"mac_addr":"60:03:4f:a0:52:51","ipv4_addr":"192.22.1.2"}' 。
2.policy注意不能是NULL。
#define MY_UBUS_OBJECT_NAME "YCY"
#define MY_UBUS_OBJECT_NAME_1 "CCYY"
#define MY_UBUS_NOTIFY_EVENT "my_notify"
#define MY_UBUS_NOTIFY_EVENT_1 "my_notify_1"
#define MY_UBUS_METHOD_1 "my_ubus_method_1"
#define MY_UBUS_METHOD_2 "my_ubus_method_2"
#define MY_UBUS_METHOD_3 "my_ubus_method_3"
#define MY_UBUS_PARAM_NAME_1 "mac_addr"
#define MY_UBUS_PARAM_NAME_2 "ipv4_addr"
typedef (void *)my_ubus_event_handler(struct ubus_context *ctx, struct ubus_event_handler *ev, const char *type, struct blob_attr *msg);
/* global variables */
struct ubus_context *g_ubus_ctx;
struct blob_buf g_ubus_buf;
struct ubud_event_handler g_ubus_notify;
struct ubud_event_handler g_ubus_notify_1;
/* method function declare */
int my_ubus_method_1(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg);
int my_ubus_method_2(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg);
int my_ubus_method_3(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg);
void my_wifi_notify_handler(struct ubus_context *ctx, struct ubus_event_handler *ev,
const char *type, struct blob_attr *msg);
/* policy */
enum
{
PARAM_MAC_ADDR,
PARAM_IP4_ADDR,
PARAM_MAX
}
static const struct blobmsg_policy my_ubus_policy[PARAM_MAX] =
{
[PARAM_MAC_ADDR] = {.name = MY_UBUS_PARAM_NAME_1, .type = BLOBMSG_TYPE_STRING},
[PARAM_IP4_ADDR] = {.name = MY_UBUS_PARAM_NAME_2, .type = BLOBMSG_TYPE_STRING},
};
static const struct ubus_method g_ubus_methods[] =
{
UBUS_METHOD(MY_UBUS_METHOD_1, my_ubus_method_1, my_ubus_policy),//注意這里的policy絕對不能是NULL,程序運行會出錯
UBUS_METHOD(MY_UBUS_METHOD_2, my_ubus_method_2, my_ubus_policy),
UBUS_METHOD(MY_UBUS_METHOD_3, my_ubus_method_3, my_ubus_policy),
};
static struct ubus_object_type g_ubus_type = UBUS_OBJECT_TYPE(MY_UBUS_OBJECT_NAME, g_ubus_methods);
struct ubus_object g_ubus_object =
{
.name = MY_UBUS_OBJECT_NAME,
.type = &g_ubus_type,
.methods = g_ubus_methods,
.n_methods = ARRAY_SIZE(g_ubus_methods)
};
static struct ubus_object_type g_ubus_type_1 = UBUS_OBJECT_TYPE(MY_UBUS_OBJECT_NAME_1, g_ubus_methods);/* g_ubus_methods 可以是其他的,這里是為了去掉重復代碼簡便使用 */
struct ubus_object g_ubus_object_1 =
{
.name = MY_UBUS_OBJECT_NAME_1,
.type = &g_ubus_type_1,
/* g_ubus_methods 可以是其他的,這里是為了去掉重復代碼簡便使用 */
.methods = g_ubus_methods,
.n_methods = ARRAY_SIZE(g_ubus_methods)
};
int my_ubus_add_object()
{
int ret = 0;
ret = ubus_add_object(g_ubus_ctx, &g_ubus_object);
if (ret != 0)
{
printf("failed to add object to ubus\n");
return -1;
}
ret = ubus_add_object(g_ubus_ctx, &g_ubus_object_1);
if (ret != 0)
{
printf("failed to add object to ubus\n");
return -1;
}
return 0;
}
int my_ubus_register_event(struct ubus_context ctx, struct ubud_event_handler *notify, my_ubus_event_handler fun, char *notify_event)
{
int ret = 0;
memset(notify, 0, sizeof(struct ubus_event_handler));
notify->cb = fun;
ret = ubus_register_event_handler(ctx, notify, notify_event);
if (ret != 0)
{
printf("Failed to register wifi notify event to ubus server %s\n", ubus_strerror(ret));
return -1;
}
return 0;
}
int my_ubus_register_event_all()
{
int ret = 0;
ret = my_ubus_register_event(g_ubus_ctx, &g_ubus_notify, my_wifi_notify_handler, MY_UBUS_NOTIFY_EVENT);
/* your own handler function */
ret = my_ubus_register_event(g_ubus_ctx, &g_ubus_notify_1, my_wifi_notify_handler, MY_UBUS_NOTIFY_EVENT_1);
return ret;
}
int my_ubus_start(void)
{
int ret = 0;
int i = 0;
printf("wifison ubus start\n");
uloop_init();
g_ubus_ctx = ubus_connect(NULL);
if (g_ubus_ctx == NULL)
{
printf("failed to connect to ubus\n");
return -1;
}
ubus_add_uloop(g_ubus_ctx);
/* ubus add objects */
ret = my_ubus_add_object();
if (ret != 0)
{
printf("failed to add object to ubus\n");
return -1;
}
/* ubus register events */
my_ubus_register_event_all();
if (ret != 0)
{
printf("failed to register event to ubus\n");
return -1;
}
uloop_run();//loop
/* unregister ubus event */
ret = ubus_unregister_event_handler(g_ubus_ctx, &g_ubus_notify);
if(ret != 0)
{
printf("Failed to unregister notify event to ubus server %s\n", ubus_strerror(ret));
return -1;
}
/* free resource */
ubus_free(g_ubus_ctx);
uloop_done();
printf("return out of uloop_run\n");
return 0;
}
int main()
{
my_ubus_start();
return 0;
}
void my_wifi_notify_handler(struct ubus_context *ctx, struct ubus_event_handler *ev, const char *type, struct blob_attr *msg)
{
struct blob_attr *tb[PARAM_MAX] = {NULL};
char mac_addr[32] = {0};
char ipv4_addr[32] = {0};
if (!msg)
{
printf(" input msg is NULL\n");
return;
}
/* 這個函數按照my_ubus_policy規則解析收到的ubus參數,然后保存在tb臨時變量中 */
blobmsg_parse(my_ubus_policy, PARAM_MAX, tb, blob_data(msg), blob_len(msg));
//macaddr
if( NULL != tb[PARAM_MAC_ADDR] )
{
strcpy(mac_addr, blobmsg_get_string(tb[PARAM_IP4_ADDR]));
}
//ipv4_addr
if( NULL != tb[PARAM_IP4_ADDR] )
{
strcpy(ipv4_addr, blobmsg_get_string(tb[PARAM_IP4_ADDR]));
}
/* TO DO */
}
static int my_ubus_method_1(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
json_object *this_wifison_all_info = NULL;
json_object *this_cap_sys_info = NULL;
struct blob_attr *tb[PARAM_MAX] = {NULL};
char mac_addr[32] = {0};
char ipv4_addr[32] = {0};
if (!msg)
{
printf(" input msg is NULL\n");
return;
}
/* 這個函數按照my_ubus_policy規則解析收到的ubus參數,然后保存在tb臨時變量中 */
blobmsg_parse(my_ubus_policy, PARAM_MAX, tb, blob_data(msg), blob_len(msg));
//macaddr
if( NULL != tb[PARAM_MAC_ADDR] )
{
strcpy(mac_addr, blobmsg_get_string(tb[PARAM_IP4_ADDR]));
}
//ipv4_addr
if( NULL != tb[PARAM_IP4_ADDR] )
{
strcpy(ipv4_addr, blobmsg_get_string(tb[PARAM_IP4_ADDR]));
}
this_wifison_all_info = json_object_new_object();
this_cap_sys_info = json_object_new_object();
json_object_object_add(this_wifison_all_info, "sys_info", this_cap_sys_info);
json_object_object_add(this_cap_sys_info, "other_mac", json_object_new_string(g_wifison_info.cap_info.sys_info.sn));
json_object_object_add(this_cap_sys_info, "other_ipv4", json_object_new_string(g_wifison_info.cap_info.sys_info.model));
blob_buf_init(&g_ubus_buf, 0);
blobmsg_add_object(&g_ubus_buf, this_wifison_all_info);
ubus_send_reply(ctx, req, g_ubus_buf.head);
json_object_put(this_cap_sys_info);
json_object_put(this_wifison_all_info);
return 0;
}
static int my_ubus_method_2(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
/* TO DO */
return 0;
}
static int my_ubus_method_3(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
/* TO DO */
return 0;
}
ubus cleint
在這一部代碼中主要實現以下兩條命令:
ubus call YCY my_ubus_method_1 '{"mac_addr":"60:03:4f:a0:52:51","ipv4_addr":"192.2.1.2"}'
ubus send my_notify '{"mac_addr":"60:03:4f:a0:52:51","ipv4_addr":"192.2.1.2"}'
ubus client代碼
注意事項:
1.在命令行可以使用ubus call這樣的命令,那么代碼中如何調用另外進程的objetc的某個method是使用ubus_invoke函數,本例中將這個函數再進行了一層封裝方便使用。
2.在命令行可以使用ubus send這樣的命令,那么代碼中如何發送event是使用ubus_send_event函數,本例中將這個函數再進行了一層封裝方便使用。
3.請注意,在這個進程中,因為沒有常駐的ubus進程,所以可以將ubus call和ubus send這樣封裝成獨立的函數使用,如果本身這個ubus client也另外作為一個server運行了ubus常駐進程,就不可以按照我這里的封裝之后的函數使用,因為每個封裝之后的函數都有ctx的創建和free,這顯然和常駐進程的概念出現了沖突。
/* ubus send notify */
#define MY_UBUS_PARAM_NAME_1 "mac_addr"
#define MY_UBUS_PARAM_NAME_2 "ipv4_addr"
#define MY_UBUS_NOTIFY_EVENT "my_notify"
#define MY_UBUS_NOTIFY_EVENT_1 "my_notify_1"
#define MY_UBUS_RETURN_STATUS "status"
#define MY_UBUS_RETURN_ERR_STR "err_str"
struct ubus_context *g_ubus_ctx;
struct blob_buf g_ubus_buf;
struct ubud_event_handler g_ubus_notify;
enum
{
RETURN_STATUS,
RETURN_ERR_STR,
RETURN_MAX,
}
enum
{
PARAM_MAC_ADDR,
PARAM_IP4_ADDR,
PARAM_MAX
}
static const struct blobmsg_policy my_ubus_policy[PARAM_MAX] =
{
[PARAM_MAC_ADDR] = {.name = MY_UBUS_PARAM_NAME_1, .type = BLOBMSG_TYPE_STRING},
[PARAM_IP4_ADDR] = {.name = MY_UBUS_PARAM_NAME_2, .type = BLOBMSG_TYPE_STRING},
};
static const struct blobmsg_policy my_ubus_return_policy[PARAM_MAX] =
{
[RETURN_STATUS] = {.name = MY_UBUS_RETURN_STATUS, .type = BLOBMSG_TYPE_INT32},
[RETURN_ERR_STR] = {.name = MY_UBUS_RETURN_ERR_STR, .type = BLOBMSG_TYPE_STRING},
};
static int my_ubus_call_method(struct ubus_context *ubus_ctx,
char *ubus_name,
char *ubus_method,
struct blob_buf b_buf,
ubus_data_handler_t cb)
{
unsigned int id = 0;
int ret;
ret = ubus_lookup_id(ubus_ctx, ubus_name, &id);
if (0 != ret)
{
printf("Error. Can't find %s\n", ubus_name);
}
return ubus_invoke(ubus_ctx, id, ubus_method, b_buf.head, cb, NULL, 0);
}
static int my_invoke_ubus_init(struct ubus_context **ctx)
{
uloop_init();
signal(SIGPIPE, SIG_IGN);
*ctx = ubus_connect(NULL);
if (NULL == *ctx)
{
printf("ubus connect failed\n");
return -1;
}
ubus_add_uloop(*ctx);
return 0;
}
static void my_ubus_ctx_exit(struct ubus_context **ctx)
{
if (*ctx)
ubus_free(*ctx);
}
static void cb_reply_status(struct ubus_request *req, int type, struct blob_attr *msg)
{
struct blob_attr *tb[RETURN_MAX];
int status = 0;
char err_str[128] = {0};
blobmsg_parse(return_policy1, RETURN_MAX, tb, blob_data(msg), blob_len(msg));
if(tb[RETURN_STATUS] != NULL)
{
status = blobmsg_get_u32(tb[RETURN_STATUS]);
}
else
{
printf("cb_reply_status :status can't pick out\n");
}
if(status == 0)//success
{
/* TO DO with status */
printf("%d\n", status);
return ;
}
else
{
if(tb[RETURN_ERR_STR] != NULL)
{
strlcpy(err_str, blobmsg_get_string(tb[RETURN_ERR_STR]), 32);
}
else
{
printf("cb_reply_status :err_str can't pick out\n");
}
}
}
int ubus_call_object_method_with_params(char *macaddr, char *ipv4_addr)
{
int ret = -1;
ret = my_invoke_ubus_init(&g_ubus_ctx);
if (0 != ret)
{
printf("ubus init failed\n");
return -1;
}
/* add params */
blob_buf_init(&g_ubus_buf, 0);
blobmsg_add_string(&g_ubus_buf, MY_UBUS_PARAM_NAME_1, macaddr);
blobmsg_add_string(&g_ubus_buf, MY_UBUS_PARAM_NAME_2, ipv4_addr);
/* ubus call YCY my_ubus_method_1 */
my_ubus_call_method(g_ubus_ctx, MY_UBUS_OBJECT_NAME, MY_UBUS_METHOD_1, g_ubus_buf, cb_reply_status);
my_ubus_ctx_exit(&g_ubus_ctx);
return 0;
}
int ubus_send_event_with_params(char *macaddr, char *ipv4_addr)
{
int ret = -1;
ret = my_invoke_ubus_init(&g_ubus_ctx);
if (0 != ret)
{
printf("ubus init failed\n");
return -1;
}
/* add params */
blob_buf_init(&g_ubus_buf, 0);
blobmsg_add_string(&g_ubus_buf, MY_INVOKE_PARAMS_1, macaddr);
blobmsg_add_string(&g_ubus_buf, MY_INVOKE_PARAMS_2, ipv4_addr);
/* ubus send my_notify */
ubus_send_event(g_ubus_ctx, MY_UBUS_NOTIFY_EVENT, g_ubus_buf.head);
my_ubus_ctx_exit(&g_ubus_ctx);
return 0;
}
int main()
{
char macaddr[] = "60:03:4f:a0:52:51",
char ipv4_addr[] = "192.22.1.2",
/* ubus call YCY my_ubus_method_1 '{"mac_addr":"60:03:4f:a0:52:51","ipv4_addr":"192.2.1.2"}' */
ubus_call_object_method_with_params(macaddr, ipv4_addr);
/* ubus send my_notify '{"mac_addr":"60:03:4f:a0:52:51","ipv4_addr":"192.2.1.2"}' */
ubus_send_event_with_params(macaddr, ipv4_addr);
return 0;
}
ubus傳輸過程使用的blob數據我認為也有必要弄清楚一下,在下一篇文章中總結。
以上結束,這兩種使用法已經基本涵蓋了日常使用的部分。