ubus接口调用解析时遇到的一点问题
本文主要描述在openwrt环境下,使用blobmsg_parse
接口解析双层BLOBMSG_TYPE_TABLE
出现的Segmentation fault问题。带解析的数据来源于ubus call system info
返回的值。
问题描述
前置条件:
ubus call system info
接口的返回值内容如下:$ubus call system info { "localtime": 1628263880, "uptime": 77629, "load": [ 0, 704, 576 ], "memory": { "total": 233041920, "free": 51875840, "shared": 188416, "buffered": 0, "available": 96661504, "cached": 8239104 }, "swap": { "total": 0, "free": 0 } }
我的目的是获取
memory
中的available
字段的值,从而得到系统中可以的内存大小。但是在解析第二层memory,BLOBMSG_TYPE_TABLE
却出现了Segmentation fault。下面是出错的部分代码:
// 定义第一层system info结构 enum { DEF_SYS_LOCAL_TIME, DEF_SYS_UPTIME, DEF_SYS_LOAD, DEF_SYS_MEM, __SYS_INFO_MAXMUM }; static const struct blobmsg_policy sys_info_policy[] = { [DEF_SYS_LOCAL_TIME] = {.name = "localtime", .type = BLOBMSG_TYPE_INT32}, [DEF_SYS_UPTIME] = {.name = "uptime", .type = BLOBMSG_TYPE_INT32}, [DEF_SYS_LOAD] = {.name = "load", .type = BLOBMSG_TYPE_ARRAY}, [DEF_SYS_MEM] = {.name = "memory", .type = BLOBMSG_TYPE_TABLE}, }; /* 解析BLOBMSG_TYPE_ARRAY */ enum { DEF_SYS_LOAD_ITEM1, DEF_SYS_LOAD_ITEM2, DEF_SYS_LOAD_ITEM3, __SYS_LOAD_ITEM_MAXMUM }; static const struct blobmsg_policy sys_load_policy[] = { [DEF_SYS_LOAD_ITEM1] = {.type = BLOBMSG_TYPE_INT32}, [DEF_SYS_LOAD_ITEM2] = {.type = BLOBMSG_TYPE_INT32}, [DEF_SYS_LOAD_ITEM3] = {.type = BLOBMSG_TYPE_INT32}, }; // 定义第二层memory的结构 enum { DEF_SYS_MEM_TOTAL, DEF_SYS_MEM_FREE, DEF_SYS_MEM_SHARED, DEF_SYS_MEM_BUFFERED, DEF_SYS_MEM_AVAILABLE, DEF_SYS_MEM_CACHED, __SYS_MEM_INFO_MAXMUM }; static const struct blobmsg_policy sys_mem_info_policy[] = { [DEF_SYS_MEM_TOTAL] = {.name = "total", .type = BLOBMSG_TYPE_INT32}, [DEF_SYS_MEM_FREE] = {.name = "free", .type = BLOBMSG_TYPE_INT32}, [DEF_SYS_MEM_SHARED] = {.name = "shared", .type = BLOBMSG_TYPE_INT32}, [DEF_SYS_MEM_BUFFERED] = {.name = "buffered", .type = BLOBMSG_TYPE_INT32}, [DEF_SYS_MEM_AVAILABLE] = {.name = "available", .type = BLOBMSG_TYPE_INT32}, [DEF_SYS_MEM_CACHED] = {.name = "cached", .type = BLOBMSG_TYPE_INT32}, }; static void handle_sys_mem_cb(struct ubus_request *req, int type, struct blob_attr *msg) { if (msg != NULL) { struct blob_attr *tb[__SYS_INFO_MAXMUM]; // 解析最外层 blobmsg_parse(sys_info_policy, __SYS_INFO_MAXMUM, tb, blob_data(msg), blob_len(msg)); // 打印localtime if (tb[DEF_SYS_LOCAL_TIME]) { printf("%d---%u\n", __LINE__, blobmsg_get_u32(tb[DEF_SYS_LOCAL_TIME])); } for (int i = 0; i < __SYS_INFO_MAXMUM; i++) { printf("%d--%s\n", __LINE__, blobmsg_name(tb[i])); printf("%d--%d\n", __LINE__, blobmsg_type(tb[i])); printf("%d--%d\n", __LINE__, blobmsg_data_len(tb[i])); printf("%d--%d\n", __LINE__, blobmsg_len(tb[i])); } // 解析load if (tb[DEF_SYS_LOAD]) { struct blob_attr *sub_tb[__SYS_LOAD_ITEM_MAXMUM]; blobmsg_parse_array(sys_load_policy, ARRAY_SIZE(sys_load_policy), sub_tb, blobmsg_data(tb[DEF_SYS_LOAD]), blobmsg_data_len(tb[DEF_SYS_LOAD])); for (int i = 0; i < __SYS_LOAD_ITEM_MAXMUM; i++) { printf("%d--%u\n", __LINE__, blobmsg_get_u32(sub_tb[i])); printf("%d--%d\n", __LINE__, blobmsg_data_len(sub_tb[i])); printf("%d--%d\n", __LINE__, blobmsg_len(sub_tb[i])); } } // 解析memory if (tb[DEF_SYS_MEM]) { printf("%d--%d\n", __LINE__, blobmsg_type(tb[DEF_SYS_MEM]) == BLOBMSG_TYPE_TABLE); struct blob_attr *mem_attr[__SYS_MEM_INFO_MAXMUM]; int iRet = blobmsg_parse(sys_mem_info_policy, ARRAY_SIZE(sys_mem_info_policy), mem_attr, blobmsg_data(tb[DEF_SYS_MEM]), blobmsg_data_len(tb[DEF_SYS_MEM])); printf("%d--%d\n", __LINE__, iRet); for (int i = 0; i < __SYS_MEM_INFO_MAXMUM; i++) { printf("%d--%d\n", __LINE__, i); printf("%d--%s\n", __LINE__, blobmsg_name(mem_attr[i])); //Segmentation fault printf("%d--%ld\n", __LINE__, blobmsg_get_u32(mem_attr[i])); printf("%d--%d\n", __LINE__, blobmsg_type(mem_attr[i])); printf("%d--%d\n", __LINE__, blobmsg_data_len(mem_attr[i])); printf("%d--%d\n", __LINE__, blobmsg_len(mem_attr[i])); } } } }
当代码调试到98行时出现段错误。我查看了
u32_t
的大小范围是0~4294976295,很明显memory
下的变量的值都在这个范围内,看起来是没有问题的,真是百思不得其解。最后我想到一个办法,那就是将
tb[DEF_SYS_MEM]
这个变量写入二进制文件中,查看一下具体内容。
排查步骤
将
tb[DEF_SYS_MEM]
写入二进制文件,采用下面的代码:FILE *fp = fopen("./mem.txt", "wb"); if (fp != NULL) { fwrite(blobmsg_data(tb[DEF_SYS_MEM]), blobmsg_data_len(tb[DEF_SYS_MEM]), 1, fp); fclose(fp); }
根据位置对应关系可以看到每一条数据的开头是
0x8400
;然后接下来的2字节是blob_attr
的长度为0x0014
;再接下来的2字节是标题长度0x0005
,也就是total
的长度;再接下来的5字节就是total
,还有1字节是total
字符串的结尾’\0’;这一条数据的最后8个字节就是我们想要获取的数据。通过计算可以知道2+2+2+5+1+8=20=0x14。从这儿可以看出
memory
下的数据是采用8字节进行存储的,所以应该使用BLOBMSG_TYPE_INT64
,而非之前使用的BLOBMSG_TYPE_INT32
。
确定原因
通过上面的步骤逐步排查,可以确定是因为之前的代码中采用了
BLOBMSG_TYPE_INT32
来解析memory
导致的问题。// 定义第二层memory的结构 enum { DEF_SYS_MEM_TOTAL, DEF_SYS_MEM_FREE, DEF_SYS_MEM_SHARED, DEF_SYS_MEM_BUFFERED, DEF_SYS_MEM_AVAILABLE, DEF_SYS_MEM_CACHED, __SYS_MEM_INFO_MAXMUM }; static const struct blobmsg_policy sys_mem_info_policy[] = { [DEF_SYS_MEM_TOTAL] = {.name = "total", .type = BLOBMSG_TYPE_INT32}, [DEF_SYS_MEM_FREE] = {.name = "free", .type = BLOBMSG_TYPE_INT32}, [DEF_SYS_MEM_SHARED] = {.name = "shared", .type = BLOBMSG_TYPE_INT32}, [DEF_SYS_MEM_BUFFERED] = {.name = "buffered", .type = BLOBMSG_TYPE_INT32}, [DEF_SYS_MEM_AVAILABLE] = {.name = "available", .type = BLOBMSG_TYPE_INT32}, [DEF_SYS_MEM_CACHED] = {.name = "cached", .type = BLOBMSG_TYPE_INT32}, }; // 需要修改如下 enum { DEF_SYS_MEM_TOTAL, DEF_SYS_MEM_FREE, DEF_SYS_MEM_SHARED, DEF_SYS_MEM_BUFFERED, DEF_SYS_MEM_AVAILABLE, DEF_SYS_MEM_CACHED, __SYS_MEM_INFO_MAXMUM }; static const struct blobmsg_policy sys_mem_info_policy[] = { [DEF_SYS_MEM_TOTAL] = {.name = "total", .type = BLOBMSG_TYPE_INT64}, [DEF_SYS_MEM_FREE] = {.name = "free", .type = BLOBMSG_TYPE_INT64}, [DEF_SYS_MEM_SHARED] = {.name = "shared", .type = BLOBMSG_TYPE_INT64}, [DEF_SYS_MEM_BUFFERED] = {.name = "buffered", .type = BLOBMSG_TYPE_INT64}, [DEF_SYS_MEM_AVAILABLE] = {.name = "available", .type = BLOBMSG_TYPE_INT64}, [DEF_SYS_MEM_CACHED] = {.name = "cached", .type = BLOBMSG_TYPE_INT64}, }; // 同时取值时应该使用blobmsg_get_u64 printf("%d--%ld\n",__LINE__, blobmsg_get_u64(mem_attr[i]));
解决方法
将
memory
的所有字段都改为BLOBMSG_TYPE_INT64
,同时采用blobmsg_get_u64
获取值即可。下面是修改过后的所有代码:
#include <stdio.h> #include <unistd.h> #include <string.h> #include "libubox/uloop.h" #include "libubus.h" /* { "localtime": 1628263880, "uptime": 77629, "load": [ 0, 704, 576 ], "memory": { "total": 233041920, "free": 51875840, "shared": 188416, "buffered": 0, "available": 96661504, "cached": 8239104*40 }, "swap": { "total": 0, "free": 0 } } */ enum { DEF_SYS_LOCAL_TIME, DEF_SYS_UPTIME, DEF_SYS_LOAD, DEF_SYS_MEM, __SYS_INFO_MAXMUM }; static const struct blobmsg_policy sys_info_policy[] = { [DEF_SYS_LOCAL_TIME] = {.name = "localtime", .type = BLOBMSG_TYPE_INT32}, [DEF_SYS_UPTIME] = {.name = "uptime", .type = BLOBMSG_TYPE_INT32}, [DEF_SYS_LOAD] = {.name = "load", .type = BLOBMSG_TYPE_ARRAY}, [DEF_SYS_MEM] = {.name = "memory", .type = BLOBMSG_TYPE_TABLE}, }; /* 解析BLOBMSG_TYPE_ARRAY */ enum{ DEF_SYS_LOAD_ITEM1, DEF_SYS_LOAD_ITEM2, DEF_SYS_LOAD_ITEM3, __SYS_LOAD_ITEM_MAXMUM }; static const struct blobmsg_policy sys_load_policy[]={ [DEF_SYS_LOAD_ITEM1]={.type=BLOBMSG_TYPE_INT32}, [DEF_SYS_LOAD_ITEM2]={.type=BLOBMSG_TYPE_INT32}, [DEF_SYS_LOAD_ITEM3]={.type=BLOBMSG_TYPE_INT32}, }; enum { DEF_SYS_MEM_TOTAL, DEF_SYS_MEM_FREE, DEF_SYS_MEM_SHARED, DEF_SYS_MEM_BUFFERED, DEF_SYS_MEM_AVAILABLE, DEF_SYS_MEM_CACHED, __SYS_MEM_INFO_MAXMUM }; static const struct blobmsg_policy sys_mem_info_policy[] = { [DEF_SYS_MEM_TOTAL] = {.name = "total", .type = BLOBMSG_TYPE_INT64}, [DEF_SYS_MEM_FREE] = {.name = "free", .type = BLOBMSG_TYPE_INT64}, [DEF_SYS_MEM_SHARED] = {.name = "shared", .type = BLOBMSG_TYPE_INT64}, [DEF_SYS_MEM_BUFFERED] = {.name = "buffered", .type = BLOBMSG_TYPE_INT64}, [DEF_SYS_MEM_AVAILABLE] = {.name = "available", .type = BLOBMSG_TYPE_INT64}, [DEF_SYS_MEM_CACHED] = {.name = "cached", .type = BLOBMSG_TYPE_INT64}, }; static void handle_sys_mem_cb(struct ubus_request* req, int type, struct blob_attr* msg) { if (msg != NULL) { struct blob_attr* tb[__SYS_INFO_MAXMUM]; int localtime = 0; blobmsg_parse(sys_info_policy, __SYS_INFO_MAXMUM, tb, blob_data(msg), blob_len(msg)); if(tb[DEF_SYS_LOCAL_TIME]){ printf("%d---%u\n",__LINE__, blobmsg_get_u32(tb[DEF_SYS_LOCAL_TIME])); } for(int i=0;i<__SYS_INFO_MAXMUM;i++){ printf("%d--%s\n",__LINE__, blobmsg_name(tb[i])); printf("%d--%d\n",__LINE__, blobmsg_type(tb[i])); printf("%d--%d\n",__LINE__, blobmsg_data_len(tb[i])); printf("%d--%d\n",__LINE__, blobmsg_len(tb[i])); } if(tb[DEF_SYS_LOAD]){ struct blob_attr* sub_tb[__SYS_LOAD_ITEM_MAXMUM]; blobmsg_parse_array(sys_load_policy, ARRAY_SIZE(sys_load_policy), sub_tb, blobmsg_data(tb[DEF_SYS_LOAD]), blobmsg_data_len(tb[DEF_SYS_LOAD])); for(int i=0; i<__SYS_LOAD_ITEM_MAXMUM;i++){ printf("%d--%u\n",__LINE__, blobmsg_get_u32(sub_tb[i])); printf("%d--%d\n",__LINE__, blobmsg_data_len(sub_tb[i])); printf("%d--%d\n",__LINE__, blobmsg_len(sub_tb[i])); } } if(tb[DEF_SYS_MEM]){ printf("%d--%d\n", __LINE__, blobmsg_type(tb[DEF_SYS_MEM])==BLOBMSG_TYPE_TABLE); FILE* fp=fopen("./mem.txt", "wb"); if(fp!=NULL){ fwrite(blobmsg_data(tb[DEF_SYS_MEM]), blobmsg_data_len(tb[DEF_SYS_MEM]), 1, fp); fclose(fp); } struct blob_attr* mem_attr[__SYS_MEM_INFO_MAXMUM]; int iRet=blobmsg_parse(sys_mem_info_policy, ARRAY_SIZE(sys_mem_info_policy), mem_attr, blobmsg_data(tb[DEF_SYS_MEM]), blobmsg_data_len(tb[DEF_SYS_MEM])); printf("%d--%d\n",__LINE__, iRet); for(int i=0;i<__SYS_MEM_INFO_MAXMUM;i++){ printf("%d--%d\n",__LINE__, i); printf("%d--%s\n",__LINE__, blobmsg_name(mem_attr[i])); printf("%d--%ld\n",__LINE__, blobmsg_get_u64(mem_attr[i])); printf("%d--%d\n",__LINE__, blobmsg_type(mem_attr[i])); printf("%d--%d\n",__LINE__, blobmsg_data_len(mem_attr[i])); printf("%d--%d\n",__LINE__, blobmsg_len(mem_attr[i])); } // if(mem_attr[DEF_SYS_MEM_TOTAL]){ // printf("buffered:%d---%u\n",__LINE__, blobmsg_get_u32(mem_attr[DEF_SYS_MEM_AVAILABLE])); // } // if(mem_attr[DEF_SYS_MEM_AVAILABLE]){ // printf("available:%d---%u\n",__LINE__, blobmsg_get_u32(mem_attr[DEF_SYS_MEM_AVAILABLE])); // } } } } /***************** FUN:获取系统可用内存 ARG1: signal_strength,字符串指针,用来保存返回值 ARG2: len,signal_strength的长度 RETURN:成功0、失败-1 *****************/ int get_sys_mem() { int iRet = -1; struct ubus_context* ubus_ctx = ubus_connect(NULL); uint32_t sys_mem_id; if (ubus_ctx != NULL) { if (ubus_lookup_id(ubus_ctx, "system", &sys_mem_id) != UBUS_STATUS_OK) { return iRet; } ubus_invoke(ubus_ctx, sys_mem_id, "info", NULL, handle_sys_mem_cb, NULL, 5000); ubus_free(ubus_ctx); iRet = 0; } return iRet; } int main(){ get_sys_mem(); }
总结
- 针对这种二进制中含有字符串的数据,如果不知道其中的结构,可以使用
fwrite
将其写入文件然后使用WinHex工具查看其中的值。 - 那么问题来了,如何提前判断该接口的返回值是64为还是32位呢?总不能每次调用接口都查看二进制数据吧!我觉得在调用接口之前接口文档中必须明确说明返回字段的类型。
- 原文作者:生如夏花
- 原文链接:https://blduan.top/post/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/openwrt/ubus%E6%8E%A5%E5%8F%A3%E8%B0%83%E7%94%A8%E8%A7%A3%E6%9E%90/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。