大部分内容来自libubox [3] - BLOB BLOGMSG,推荐阅读原文。
blob提供二进制数据处理能力。有几种支持的数据类型,并可以创建块数据在socket上发送。整型数字会在libubox库内部转换为网络字节序进行处理。
二进制块的处理方法是创建一个TLV(类型-长度-值)链表数据,支持嵌套类型数据,并提供设置和获取数据接口。blob定义在blob.h中。
blogmsg位于blob的上层,提供表格和数组等数据类型的处理,定义在blogmsg.h中。
TLV是用于表示可变长度的数据格式,Type表示数据的类型,length表示数据长度,value存储数据值。类型和长度的占用空间是固定的,在libubox库中共占用4个字节。
Value的长度由length指定。这样可以存储和传输任何类型的数据,只需预先定义服务器和客户端之间的TLV的类型和长度的空间大小即可。
1.blob
blob(binary large object),二进制大对象,用于二进制对象序列化;blob主要在一些系统级组件(ubox/libubox/ubus/netifd)内部使用,一般应用不需要使用blob封装,blob用数据结构blob_attr表示。blob_buf用于管理(多个)二进制大对象
1.1 数据结构
#define BLOB_COOKIE 0x01234567
enum {
BLOB_ATTR_UNSPEC,
BLOB_ATTR_NESTED,
BLOB_ATTR_BINARY,
BLOB_ATTR_STRING,
BLOB_ATTR_INT8,
BLOB_ATTR_INT16,
BLOB_ATTR_INT32,
BLOB_ATTR_INT64,
BLOB_ATTR_LAST
};
#define BLOB_ATTR_ID_MASK 0x7f000000
#define BLOB_ATTR_ID_SHIFT 24
#define BLOB_ATTR_LEN_MASK 0x00ffffff
#define BLOB_ATTR_ALIGN 4 //blob字节对齐
#define BLOB_ATTR_EXTENDED 0x80000000
// blob数据结构
struct blob_attr {
uint32_t id_len; //id占用最高字节,msb用于扩展,len占用低3个字节
char data[];
} __packed;
// 属性过滤
struct blob_attr_info {
unsigned int type;
unsigned int minlen;
unsigned int maxlen;
bool (*validate)(const struct blob_attr_info *, struct blob_attr *);
};
// 多个blob管理数据结构
struct blob_buf {
struct blob_attr *head; // 指向blob_buf的开头,分配一个4字节的blob_attr(仅有
//id_len),记录已使用的len。最初时等于blob_buf->buf
bool (*grow)(struct blob_buf *buf, int minlen); //内存扩展回调函数
int buflen; //buf总长度
void *buf; // 指向buf起始位置(开头)
};
1.2.函数
1.2.1获取blob属性
/**
* 返回指向BLOB属性数据区指针
*/
static inline void * blob_data(const struct blob_attr *attr)
/**
* 返回BLOB属性ID
*/
static inline unsigned int blob_id(const struct blob_attr *attr)
/**
* 判断BLOB属性扩展标志是否为真
*/
static inline bool blob_is_extended(const struct blob_attr *attr)
/**
* 返回BLOB属性有效存储空间大小
*/
static inline unsigned int blob_len(const struct blob_attr *attr)
/*
* 返回BLOB属性完全存储空间大小(包括头部)
*/
static inline unsigned int blob_raw_len(const struct blob_attr *attr)
/*
* 返回BLOB属性填补后存储空间大小(包括头部),存储空间补齐大小
*/
static inline unsigned int blob_pad_len(const struct blob_attr *attr)
{
unsigned int len = blob_raw_len(attr);
len = (len + BLOB_ATTR_ALIGN - 1) & ~(BLOB_ATTR_ALIGN - 1);
return len;
}
1.2.2获取blob数据
//获取uint8_t类型数据并返回
static inline uint8_t blob_get_u8(const struct blob_attr *attr)
//获取uint16_t类型数据并返回
static inline uint16_t blob_get_u16(const struct blob_attr *attr)
//获取uint32_t类型数据并返回
static inline uint32_t blob_get_u32(const struct blob_attr *attr)
//获取uint64_t类型数据并返回
static inline uint64_t blob_get_u64(const struct blob_attr *attr)
//获取int8_t类型数据并返回
static inline int8_t blob_get_int8(const struct blob_attr *attr)
//获取int16_t类型数据并返回
static inline int16_t blob_get_int16(const struct blob_attr *attr)
//获取int32_t类型数据并返回
static inline int32_t blob_get_int32(const struct blob_attr *attr)
//获取int64_t类型数据并返回
static inline int64_t blob_get_int64(const struct blob_attr *attr)
//获取const char* 类型数据并返回
static inline const char * blob_get_string(const struct blob_attr *attr)
1.2.3设置blob数据
//将str设置到buf数据结构中
static inline struct blob_attr *
blob_put_string(struct blob_buf *buf, int id, const char *str)
//将val设置到buf数据结构中
static inline struct blob_attr *
blob_put_u8(struct blob_buf *buf, int id, uint8_t val)
//将val设置到buf数据结构中
static inline struct blob_attr *
blob_put_u16(struct blob_buf *buf, int id, uint16_t val)
//将val设置到buf数据结构中
static inline struct blob_attr *
blob_put_u32(struct blob_buf *buf, int id, uint32_t val)
//将val设置到buf数据结构中
static inline struct blob_attr *
blob_put_u64(struct blob_buf *buf, int id, uint64_t val)
#define blob_put_int8 blob_put_u8
#define blob_put_int16 blob_put_u16
#define blob_put_int32 blob_put_u32
#define blob_put_int64 blob_put_u64
/**
* ptr - 指向struct blob_attr
*/
struct blob_attr * blob_put(struct blob_buf *buf, int id, const void *ptr, unsigned int len)
struct blob_attr * blob_put_raw(struct blob_buf *buf, const void *ptr, unsigned int len)
1.2.4遍历
static inline struct blob_attr * blob_next(const struct blob_attr *attr) {
return (struct blob_attr *) ((char *) attr + blob_pad_len(attr));
}
#define __blob_for_each_attr(pos, attr, rem) \
for (pos = (void *) attr; \
rem > 0 && (blob_pad_len(pos) <= rem) && \
(blob_pad_len(pos) >= sizeof(struct blob_attr)); \
rem -= blob_pad_len(pos), pos = blob_next(pos))
#define blob_for_each_attr(pos, attr, rem) \
for (rem = attr ? blob_len(attr) : 0, \
pos = attr ? blob_data(attr) : 0; \
rem > 0 && (blob_pad_len(pos) <= rem) && \
(blob_pad_len(pos) >= sizeof(struct blob_attr)); \
rem -= blob_pad_len(pos), pos = blob_next(pos))
1.2.5复制
extern struct blob_attr *blob_memdup(struct blob_attr *attr);
1.2.6 嵌套
extern void *blob_nest_start(struct blob_buf *buf, int id);
extern void blob_nest_end(struct blob_buf *buf, void *cookie);
1.2.7判断
bool blob_check_type(const void *ptr, unsigned int len, int type);
bool blob_attr_equal(const struct blob_attr *a1, const struct blob_attr *a2);
1.2.8 初始化和销毁
blob_buf一般声明为本地静态变量,id一般使用0(BLOBMSG_TYPE_UNSPEC)来初始化。
/**
* 初始化BLOB buffer
*/
int blob_buf_init(struct blob_buf *buf, int id)
/**
* 销毁BLOB buffer
*/
void blob_buf_free(struct blob_buf *buf)
1.2.9解析blob
/**
* 从attr串中根据info策略过滤,得到的结果存储在data属性数组中
*
* @param attr 输入BLOB属性串
* @param data 输出BLOB属性数组
* @param info 属性过滤策略
* @param max data数组大小
*/
int blob_parse(struct blob_attr *attr, struct blob_attr **data,
const struct blob_attr_info *info, int max)
2.blobmsg
blobmsg用于二进制对象网络序列化。嵌套在blob数据结构(blob_attr)的data区。因此形成:blob_buff <- blob_attr -< blobmsg,blob_buff可存储管理多个blob_attr,每个blob_attr又可存储管理一个blogmsg。且可存储在线性数据区,不需要链表指针。
blobmsg_policy用于解析和缓存blobmsg列表,一般声明为一个静态数组,用于指导消息解析。
blobmsg默认使用id为table。array类似C语言的数组,table类似C的结构
2.1数据结构
#define BLOBMSG_ALIGN 2
#define BLOBMSG_PADDING(len) (((len) + (1 << BLOBMSG_ALIGN) - 1) & ~((1 << BLOBMSG_ALIGN) - 1))
enum blobmsg_type {
BLOBMSG_TYPE_UNSPEC,
BLOBMSG_TYPE_ARRAY,
BLOBMSG_TYPE_TABLE,
BLOBMSG_TYPE_STRING,
BLOBMSG_TYPE_INT64,
BLOBMSG_TYPE_INT32,
BLOBMSG_TYPE_INT16,
BLOBMSG_TYPE_INT8,
__BLOBMSG_TYPE_LAST,
BLOBMSG_TYPE_LAST = __BLOBMSG_TYPE_LAST - 1,
BLOBMSG_TYPE_BOOL = BLOBMSG_TYPE_INT8,
};
struct blobmsg_hdr {
uint16_t namelen;
uint8_t name[];
} __packed;
// 解析blobmsg列表
struct blobmsg_policy {
const char *name; // 与blobmsg_hdr->name对应
enum blobmsg_type type; // 策略值的类型,数据打包解包时作为blob_attr id
};
2.2 获取blogmsg属性
/**
* 根据BLOB消息名字长度计算出blobmsg头部大小
*/
static inline int blobmsg_hdrlen(unsigned int namelen)
/**
* 获取BLOB消息名字
*/
static inline const char *blobmsg_name(const struct blob_attr *attr)
/**
* 获取BLOB消息类型
*/
static inline int blobmsg_type(const struct blob_attr *attr)
/**
* 获取BLOB消息数据内容
*/
static inline void *blobmsg_data(const struct blob_attr *attr)
/**
* 获取BLOB消息数据内容大小
*/
static inline int blobmsg_data_len(const struct blob_attr *attr) {
uint8_t *start, *end;
start = (uint8_t *) blob_data(attr);
end = (uint8_t *) blobmsg_data(attr);
return blob_len(attr) - (end - start);
}
static inline int blobmsg_len(const struct blob_attr *attr){
return blobmsg_data_len(attr);
}
2.3数据类型判断
/*
* 判断BLOBMSG属性类型是否合法
*/
bool blobmsg_check_attr(const struct blob_attr *attr, bool name)
2.4设置
int blobmsg_add_field(struct blob_buf *buf, int type, const char *name,
const void *data, unsigned int len)
static inline int
blobmsg_add_u8(struct blob_buf *buf, const char *name, uint8_t val)
static inline int
blobmsg_add_u16(struct blob_buf *buf, const char *name, uint16_t val)
static inline int
blobmsg_add_u32(struct blob_buf *buf, const char *name, uint32_t val)
static inline int
blobmsg_add_u64(struct blob_buf *buf, const char *name, uint64_t val)
static inline int
blobmsg_add_string(struct blob_buf *buf, const char *name, const char *string)
static inline int
blobmsg_add_blob(struct blob_buf *buf, struct blob_attr *attr)
/**
* 格式化设备BLOGMSG
*/
void blobmsg_printf(struct blob_buf *buf, const char *name, const char *format, ...)
2.5获取
static inline uint8_t blobmsg_get_u8(struct blob_attr *attr)
static inline bool blobmsg_get_bool(struct blob_attr *attr)
static inline uint16_t blobmsg_get_u16(struct blob_attr *attr)
static inline uint32_t blobmsg_get_u32(struct blob_attr *attr)
static inline uint64_t blobmsg_get_u64(struct blob_attr *attr)
static inline char *blobmsg_get_string(struct blob_attr *attr)
2.6创建
/**
* 创建BLOBMSG,返回数据区开始地址
*/
void *blobmsg_alloc_string_buffer(struct blob_buf *buf, const char *name, unsigned int maxlen)
/**
* 扩大BLOGMSG,返回数据区开始地址
*/
void *blobmsg_realloc_string_buffer(struct blob_buf *buf, unsigned int maxlen)
void blobmsg_add_string_buffer(struct blob_buf *buf)
2.7遍历
#define blobmsg_for_each_attr(pos, attr, rem) \
for (rem = attr ? blobmsg_data_len(attr) : 0, \
pos = attr ? blobmsg_data(attr) : 0; \
rem > 0 && (blob_pad_len(pos) <= rem) && \
(blob_pad_len(pos) >= sizeof(struct blob_attr)); \
rem -= blob_pad_len(pos), pos = blob_next(pos))
2.8嵌套
static inline void * blobmsg_open_array(struct blob_buf *buf, const char *name)
static inline void blobmsg_close_array(struct blob_buf *buf, void *cookie)
static inline void *blobmsg_open_table(struct blob_buf *buf, const char *name)
static inline void blobmsg_close_table(struct blob_buf *buf, void *cookie)
2.9解析blobmsg
/*
* 从data BLOGMSG串中根据policy策略过滤,得到的结果存储在tb BLOGATTR数组中
*
* @param policy 过滤策略
* @param policy_len 策略个数
* @param tb 返回属性数据
* @param len data属性个数
*/
int blobmsg_parse(const struct blobmsg_policy *policy, int policy_len,
struct blob_attr **tb, void *data, unsigned int len)
blobmsg根节点是一个纯粹的blob,所以blobmsg解析时需要注意:
(1)第一层解析,data必须取值为blob_data(root_blob),len必须取值为blob_len(root_blob)
(2)第二层及以上解析,data必须取值为blobmsg_data(sub_blob),len必须取值为blobmsg_data_len(sub_blob)
所以,应避免混合使用blob和blobmsg语义,比如第一层使用blob语义,第二层使用blobmsg语义
3.blob_msg
blobmsg_json用于json对象的序列化,json提供脚本级消息发送机制,如果应用需要脚本配合,则需要使用json。
4.举例
4.1 UCI配置文件: /etc/config/test
config policy test
option name 'test'
option enable '1'
option dns '1.1.1.1 2.2.2.2'
4.2定义参数列表
enum {
POLICY_ATTR_NAME, /* name */
POLICY_ATTR_ENABLE, /* enable */
POLICY_ATTR_DNS, /* dns */
__POLICY_ATTR_MAX
};
static const struct blobmsg_policy policy_attrs[__POLICY_ATTR_MAX] = {
[POLICY_ATTR_NAME] = { .name = "name", .type = BLOBMSG_TYPE_STRING },
[POLICY_ATTR_ENABLE] = { .name = "enable", .type = BLOBMSG_TYPE_BOOL },
[POLICY_ATTR_DNS] = { .name = "dns", .type = BLOBMSG_TYPE_ARRAY },
};
/* 定义BLOBMSG_TYPE_ARRAY类型参数的实际数据类型 */
static const struct uci_blob_param_info policy_attr_info[__POLICY_ATTR_MAX] = {
[POLICY_ATTR_DNS] = { .type = BLOBMSG_TYPE_STRING },
};
static const struct uci_blob_param_list policy_attr_list = {
.n_params = __POLICY_ATTR_MAX,
.params = policy_attrs,
.info = policy_attr_info,
};
4.3转化为blob
static struct uci_context *g_uci_ctx;
static struct blob_buf *b;
void transform(const char *config)
{
struct uci_context *ctx = g_uci_ctx;
struct uci_package *p = NULL;
if (!ctx) {
ctx = uci_alloc_context();
g_uci_ctx = ctx;
uci_set_confdir(ctx, NULL);
} else {
p = uci_lookup_package(ctx, config);
if (p)
uci_unload(ctx, p);
}
if (uci_load(ctx, config, &p))
return;
struct uci_element *e;
struct blob_attr *config = NULL;
uci_foreach_element(&p->sectons, e) {
struct uci_section *s = uci_to_section(e);
blob_buf_init(&b, 0);
uci_to_blob(&b, s, &policy_attr_list);
config = blob_memdup(b.head);
/*
* do something with `config`
* free(config), when not use it
*/
}
}
4.4使用转化后的blob
void
foo(blob_attr *confg)
{
struct blob_attr *tb[__POLICY_ATTR_MAX];
blobmsg_parse(policy_attrs, __POLICY_ATTR_MAX, tb,
blob_data(config), blob_len(config));
/*
* do something with *tb[]
*/
}
5.相关文件
5.1uci.h
/*
* libuci - Library for the Unified Configuration Interface
* Copyright (C) 2008 Felix Fietkau <nbd@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*/
#ifndef __LIBUCI_H
#define __LIBUCI_H
#ifdef __cplusplus
extern "C" {
#endif
#include "uci_config.h"
/*
* you can use these defines to enable debugging behavior for
* apps compiled against libuci:
*
* #define UCI_DEBUG_TYPECAST:
* enable uci_element typecast checking at run time
*
*/
#include <stdbool.h>
#include <setjmp.h>
#include <stdio.h>
#include <stdint.h>
#define UCI_CONFDIR "/etc/config"
#define UCI_SAVEDIR "/tmp/.uci"
#define UCI_DIRMODE 0700
#define UCI_FILEMODE 0600
enum
{
UCI_OK = 0,
UCI_ERR_MEM,
UCI_ERR_INVAL,
UCI_ERR_NOTFOUND,
UCI_ERR_IO,
UCI_ERR_PARSE,
UCI_ERR_DUPLICATE,
UCI_ERR_UNKNOWN,
UCI_ERR_LAST
};
struct uci_list;
struct uci_list
{
struct uci_list *next;
struct uci_list *prev;
};
struct uci_ptr;
struct uci_element;
struct uci_package;
struct uci_section;
struct uci_option;
struct uci_delta;
struct uci_context;
struct uci_backend;
struct uci_parse_option;
struct uci_parse_context;
/**
* uci_alloc_context: Allocate a new uci context
*/
extern struct uci_context *uci_alloc_context(void);
/**
* uci_free_context: Free the uci context including all of its data
*/
extern void uci_free_context(struct uci_context *ctx);
/**
* uci_perror: Print the last uci error that occured
* @ctx: uci context
* @str: string to print before the error message
*/
extern void uci_perror(struct uci_context *ctx, const char *str);
/**
* uci_geterror: Get an error string for the last uci error
* @ctx: uci context
* @dest: target pointer for the string
* @str: prefix for the error message
*
* Note: string must be freed by the caller
*/
extern void uci_get_errorstr(struct uci_context *ctx, char **dest, const char *str);
/**
* uci_import: Import uci config data from a stream
* @ctx: uci context
* @stream: file stream to import from
* @name: (optional) assume the config has the given name
* @package: (optional) store the last parsed config package in this variable
* @single: ignore the 'package' keyword and parse everything into a single package
*
* the name parameter is for config files that don't explicitly use the 'package <...>' keyword
* if 'package' points to a non-null struct pointer, enable delta tracking and merge
*/
extern int uci_import(struct uci_context *ctx, FILE *stream, const char *name, struct uci_package **package, bool single);
/**
* uci_export: Export one or all uci config packages
* @ctx: uci context
* @stream: output stream
* @package: (optional) uci config package to export
* @header: include the package header
*/
extern int uci_export(struct uci_context *ctx, FILE *stream, struct uci_package *package, bool header);
/**
* uci_load: Parse an uci config file and store it in the uci context
*
* @ctx: uci context
* @name: name of the config file (relative to the config directory)
* @package: store the loaded config package in this variable
*/
extern int uci_load(struct uci_context *ctx, const char *name, struct uci_package **package);
/**
* uci_unload: Unload a config file from the uci context
*
* @ctx: uci context
* @package: pointer to the uci_package struct
*/
extern int uci_unload(struct uci_context *ctx, struct uci_package *p);
/**
* uci_lookup_ptr: Split an uci tuple string and look up an element tree
* @ctx: uci context
* @ptr: lookup result struct
* @str: uci tuple string to look up
* @extended: allow extended syntax lookup
*
* if extended is set to true, uci_lookup_ptr supports the following
* extended syntax:
*
* Examples:
* network.@interface[0].ifname ('ifname' option of the first interface section)
* network.@interface[-1] (last interface section)
* Note: uci_lookup_ptr will automatically load a config package if necessary
* @str must not be constant, as it will be modified and used for the strings inside @ptr,
* thus it must also be available as long as @ptr is in use.
*
* This function returns UCI_ERR_NOTFOUND if the package specified in the tuple
* string cannot be found. Otherwise it will return UCI_OK.
*
* Note that failures in looking up other parts, if they are also specfied,
* including section and option, will also have a return value UCI_OK but with
* ptr->flags * UCI_LOOKUP_COMPLETE not set.
*/
extern int uci_lookup_ptr(struct uci_context *ctx, struct uci_ptr *ptr, char *str, bool extended);
/**
* uci_add_section: Add an unnamed section
* @ctx: uci context
* @p: package to add the section to
* @type: section type
* @res: pointer to store a reference to the new section in
*/
extern int uci_add_section(struct uci_context *ctx, struct uci_package *p, const char *type, struct uci_section **res);
/**
* uci_set: Set an element's value; create the element if necessary
* @ctx: uci context
* @ptr: uci pointer
*
* The updated/created element is stored in ptr->last
*/
extern int uci_set(struct uci_context *ctx, struct uci_ptr *ptr);
/**
* uci_add_list: Append a string to an element list
* @ctx: uci context
* @ptr: uci pointer (with value)
*
* Note: if the given option already contains a string value,
* it will be converted to an 1-element-list before appending the next element
*/
extern int uci_add_list(struct uci_context *ctx, struct uci_ptr *ptr);
/**
* uci_del_list: Remove a string from an element list
* @ctx: uci context
* @ptr: uci pointer (with value)
*
*/
extern int uci_del_list(struct uci_context *ctx, struct uci_ptr *ptr);
/**
* uci_reorder: Reposition a section
* @ctx: uci context
* @s: uci section to reposition
* @pos: new position in the section list
*/
extern int uci_reorder_section(struct uci_context *ctx, struct uci_section *s, int pos);
/**
* uci_rename: Rename an element
* @ctx: uci context
* @ptr: uci pointer (with value)
*/
extern int uci_rename(struct uci_context *ctx, struct uci_ptr *ptr);
/**
* uci_delete: Delete a section or option
* @ctx: uci context
* @ptr: uci pointer
*/
extern int uci_delete(struct uci_context *ctx, struct uci_ptr *ptr);
/**
* uci_save: save change delta for a package
* @ctx: uci context
* @p: uci_package struct
*/
extern int uci_save(struct uci_context *ctx, struct uci_package *p);
/**
* uci_commit: commit changes to a package
* @ctx: uci context
* @p: uci_package struct pointer
* @overwrite: overwrite existing config data and flush delta
*
* committing may reload the whole uci_package data,
* the supplied pointer is updated accordingly
*/
extern int uci_commit(struct uci_context *ctx, struct uci_package **p, bool overwrite);
/**
* uci_list_configs: List available uci config files
* @ctx: uci context
*
* caller is responsible for freeing the allocated memory behind list
*/
extern int uci_list_configs(struct uci_context *ctx, char ***list);
/**
* uci_set_savedir: override the default delta save directory
* @ctx: uci context
* @dir: directory name
*
* This will also try adding the specified dir to the end of delta pathes.
*/
extern int uci_set_savedir(struct uci_context *ctx, const char *dir);
/**
* uci_set_savedir: override the default config storage directory
* @ctx: uci context
* @dir: directory name
*/
extern int uci_set_confdir(struct uci_context *ctx, const char *dir);
/**
* uci_add_delta_path: add a directory to the search path for change delta files
* @ctx: uci context
* @dir: directory name
*
* This function allows you to add directories, which contain 'overlays'
* for the active config, that will never be committed.
*
* Adding a duplicate directory will cause UCI_ERR_DUPLICATE be returned.
*/
extern int uci_add_delta_path(struct uci_context *ctx, const char *dir);
/**
* uci_revert: revert all changes to a config item
* @ctx: uci context
* @ptr: uci pointer
*/
extern int uci_revert(struct uci_context *ctx, struct uci_ptr *ptr);
/**
* uci_parse_argument: parse a shell-style argument, with an arbitrary quoting style
* @ctx: uci context
* @stream: input stream
* @str: pointer to the current line (use NULL for parsing the next line)
* @result: pointer for the result
*/
extern int uci_parse_argument(struct uci_context *ctx, FILE *stream, char **str, char **result);
/**
* uci_set_backend: change the default backend
* @ctx: uci context
* @name: name of the backend
*
* The default backend is "file", which uses /etc/config for config storage
*/
extern int uci_set_backend(struct uci_context *ctx, const char *name);
/**
* uci_validate_text: validate a value string for uci options
* @str: value
*
* this function checks whether a given string is acceptable as value
* for uci options
*/
extern bool uci_validate_text(const char *str);
/**
* uci_parse_ptr: parse a uci string into a uci_ptr
* @ctx: uci context
* @ptr: target data structure
* @str: string to parse
*
* str is modified by this function
*/
int uci_parse_ptr(struct uci_context *ctx, struct uci_ptr *ptr, char *str);
/**
* uci_lookup_next: lookup a child element
* @ctx: uci context
* @e: target element pointer
* @list: list of elements
* @name: name of the child element
*
* if parent is NULL, the function looks up the package with the given name
*/
int uci_lookup_next(struct uci_context *ctx, struct uci_element **e, struct uci_list *list, const char *name);
/**
* uci_parse_section: look up a set of options
* @s: uci section
* @opts: list of options to look up
* @n_opts: number of options to look up
* @tb: array of pointers to found options
*/
void uci_parse_section(struct uci_section *s, const struct uci_parse_option *opts,
int n_opts, struct uci_option **tb);
/**
* uci_hash_options: build a hash over a list of options
* @tb: list of option pointers
* @n_opts: number of options
*/
uint32_t uci_hash_options(struct uci_option **tb, int n_opts);
/* UCI data structures */
enum uci_type {
UCI_TYPE_UNSPEC = 0,
UCI_TYPE_DELTA = 1,
UCI_TYPE_PACKAGE = 2,
UCI_TYPE_SECTION = 3,
UCI_TYPE_OPTION = 4,
UCI_TYPE_PATH = 5,
UCI_TYPE_BACKEND = 6,
UCI_TYPE_ITEM = 7,
UCI_TYPE_HOOK = 8,
};
enum uci_option_type {
UCI_TYPE_STRING = 0,
UCI_TYPE_LIST = 1,
};
enum uci_flags {
UCI_FLAG_STRICT = (1 << 0), /* strict mode for the parser */
UCI_FLAG_PERROR = (1 << 1), /* print parser error messages */
UCI_FLAG_EXPORT_NAME = (1 << 2), /* when exporting, name unnamed sections */
UCI_FLAG_SAVED_DELTA = (1 << 3), /* store the saved delta in memory as well */
};
struct uci_element
{
struct uci_list list;
enum uci_type type;
char *name;
};
struct uci_backend
{
struct uci_element e;
char **(*list_configs)(struct uci_context *ctx);
struct uci_package *(*load)(struct uci_context *ctx, const char *name);
void (*commit)(struct uci_context *ctx, struct uci_package **p, bool overwrite);
/* private: */
const void *ptr;
void *priv;
};
struct uci_context
{
/* list of config packages */
struct uci_list root;
/* parser context, use for error handling only */
struct uci_parse_context *pctx;
/* backend for import and export */
struct uci_backend *backend;
struct uci_list backends;
/* uci runtime flags */
enum uci_flags flags;
char *confdir;
char *savedir;
/* search path for delta files */
struct uci_list delta_path;
/* private: */
int err;
const char *func;
jmp_buf trap;
bool internal, nested;
char *buf;
int bufsz;
};
struct uci_package
{
struct uci_element e;
struct uci_list sections;
struct uci_context *ctx;
bool has_delta;
char *path;
/* private: */
struct uci_backend *backend;
void *priv;
int n_section;
struct uci_list delta;
struct uci_list saved_delta;
};
struct uci_section
{
struct uci_element e;
struct uci_list options;
struct uci_package *package;
bool anonymous;
char *type;
};
struct uci_option
{
struct uci_element e;
struct uci_section *section;
enum uci_option_type type;
union {
struct uci_list list;
char *string;
} v;
};
/*
* UCI_CMD_ADD is used for anonymous sections or list values
*/
enum uci_command {
UCI_CMD_ADD,
UCI_CMD_REMOVE,
UCI_CMD_CHANGE,
UCI_CMD_RENAME,
UCI_CMD_REORDER,
UCI_CMD_LIST_ADD,
UCI_CMD_LIST_DEL,
__UCI_CMD_MAX,
__UCI_CMD_LAST = __UCI_CMD_MAX - 1
};
extern char const uci_command_char[];
struct uci_delta
{
struct uci_element e;
enum uci_command cmd;
char *section;
char *value;
};
struct uci_ptr
{
enum uci_type target;
enum {
UCI_LOOKUP_DONE = (1 << 0),
UCI_LOOKUP_COMPLETE = (1 << 1),
UCI_LOOKUP_EXTENDED = (1 << 2),
} flags;
struct uci_package *p;
struct uci_section *s;
struct uci_option *o;
struct uci_element *last;
const char *package;
const char *section;
const char *option;
const char *value;
};
struct uci_parse_option {
const char *name;
enum uci_option_type type;
};
/* linked list handling */
#ifndef offsetof
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif
/**
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*/
#ifndef container_of
#define container_of(ptr, type, member) \
((type *) ((char *)ptr - offsetof(type,member)))
#endif
/**
* uci_list_entry: casts an uci_list pointer to the containing struct.
* @_type: config, section or option
* @_ptr: pointer to the uci_list struct
*/
#define list_to_element(ptr) \
container_of(ptr, struct uci_element, list)
/**
* uci_foreach_entry: loop through a list of uci elements
* @_list: pointer to the uci_list struct
* @_ptr: iteration variable, struct uci_element
*
* use like a for loop, e.g:
* uci_foreach(&list, p) {
* ...
* }
*/
#define uci_foreach_element(_list, _ptr) \
for(_ptr = list_to_element((_list)->next); \
&_ptr->list != (_list); \
_ptr = list_to_element(_ptr->list.next))
/**
* uci_foreach_entry_safe: like uci_foreach_safe, but safe for deletion
* @_list: pointer to the uci_list struct
* @_tmp: temporary variable, struct uci_element *
* @_ptr: iteration variable, struct uci_element *
*
* use like a for loop, e.g:
* uci_foreach(&list, p) {
* ...
* }
*/
#define uci_foreach_element_safe(_list, _tmp, _ptr) \
for(_ptr = list_to_element((_list)->next), \
_tmp = list_to_element(_ptr->list.next); \
&_ptr->list != (_list); \
_ptr = _tmp, _tmp = list_to_element(_ptr->list.next))
/**
* uci_list_empty: returns true if a list is empty
* @list: list head
*/
#define uci_list_empty(list) ((list)->next == (list))
/* wrappers for dynamic type handling */
#define uci_type_backend UCI_TYPE_BACKEND
#define uci_type_delta UCI_TYPE_DELTA
#define uci_type_package UCI_TYPE_PACKAGE
#define uci_type_section UCI_TYPE_SECTION
#define uci_type_option UCI_TYPE_OPTION
/* element typecasting */
#ifdef UCI_DEBUG_TYPECAST
static const char *uci_typestr[] = {
[uci_type_backend] = "backend",
[uci_type_delta] = "delta",
[uci_type_package] = "package",
[uci_type_section] = "section",
[uci_type_option] = "option",
};
static void uci_typecast_error(int from, int to)
{
fprintf(stderr, "Invalid typecast from '%s' to '%s'\n", uci_typestr[from], uci_typestr[to]);
}
#define BUILD_CAST(_type) \
static inline struct uci_ ## _type *uci_to_ ## _type (struct uci_element *e) \
{ \
if (e->type != uci_type_ ## _type) { \
uci_typecast_error(e->type, uci_type_ ## _type); \
} \
return (struct uci_ ## _type *) e; \
}
BUILD_CAST(backend)
BUILD_CAST(delta)
BUILD_CAST(package)
BUILD_CAST(section)
BUILD_CAST(option)
#else
#define uci_to_backend(ptr) container_of(ptr, struct uci_backend, e)
#define uci_to_delta(ptr) container_of(ptr, struct uci_delta, e)
#define uci_to_package(ptr) container_of(ptr, struct uci_package, e)
#define uci_to_section(ptr) container_of(ptr, struct uci_section, e)
#define uci_to_option(ptr) container_of(ptr, struct uci_option, e)
#endif
/**
* uci_alloc_element: allocate a generic uci_element, reserve a buffer and typecast
* @ctx: uci context
* @type: {package,section,option}
* @name: string containing the name of the element
* @datasize: additional buffer size to reserve at the end of the struct
*/
#define uci_alloc_element(ctx, type, name, datasize) \
uci_to_ ## type (uci_alloc_generic(ctx, uci_type_ ## type, name, sizeof(struct uci_ ## type) + datasize))
#define uci_dataptr(ptr) \
(((char *) ptr) + sizeof(*ptr))
/**
* uci_lookup_package: look up a package
* @ctx: uci context
* @name: name of the package
*/
static inline struct uci_package *
uci_lookup_package(struct uci_context *ctx, const char *name)
{
struct uci_element *e = NULL;
if (uci_lookup_next(ctx, &e, &ctx->root, name) == 0)
return uci_to_package(e);
else
return NULL;
}
/**
* uci_lookup_section: look up a section
* @ctx: uci context
* @p: package that the section belongs to
* @name: name of the section
*/
static inline struct uci_section *
uci_lookup_section(struct uci_context *ctx, struct uci_package *p, const char *name)
{
struct uci_element *e = NULL;
if (uci_lookup_next(ctx, &e, &p->sections, name) == 0)
return uci_to_section(e);
else
return NULL;
}
/**
* uci_lookup_option: look up an option
* @ctx: uci context
* @section: section that the option belongs to
* @name: name of the option
*/
static inline struct uci_option *
uci_lookup_option(struct uci_context *ctx, struct uci_section *s, const char *name)
{
struct uci_element *e = NULL;
if (uci_lookup_next(ctx, &e, &s->options, name) == 0)
return uci_to_option(e);
else
return NULL;
}
static inline const char *
uci_lookup_option_string(struct uci_context *ctx, struct uci_section *s, const char *name)
{
struct uci_option *o;
o = uci_lookup_option(ctx, s, name);
if (!o || o->type != UCI_TYPE_STRING)
return NULL;
return o->v.string;
}
#ifdef __cplusplus
}
#endif
#endif
5.2libubus.h
/*
* Copyright (C) 2011-2014 Felix Fietkau <nbd@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __LIBUBUS_H
#define __LIBUBUS_H
#include <libubox/avl.h>
#include <libubox/list.h>
#include <libubox/blobmsg.h>
#include <libubox/uloop.h>
#include <stdint.h>
#include "ubusmsg.h"
#include "ubus_common.h"
#define UBUS_MAX_NOTIFY_PEERS 16
struct ubus_context;
struct ubus_msg_src;
struct ubus_object;
struct ubus_request;
struct ubus_request_data;
struct ubus_object_data;
struct ubus_event_handler;
struct ubus_subscriber;
struct ubus_notify_request;
struct ubus_msghdr_buf {
struct ubus_msghdr hdr;
struct blob_attr *data;
};
typedef void (*ubus_lookup_handler_t)(struct ubus_context *ctx,
struct ubus_object_data *obj,
void *priv);
typedef int (*ubus_handler_t)(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req,
const char *method, struct blob_attr *msg);
typedef void (*ubus_state_handler_t)(struct ubus_context *ctx, struct ubus_object *obj);
typedef void (*ubus_remove_handler_t)(struct ubus_context *ctx,
struct ubus_subscriber *obj, uint32_t id);
typedef void (*ubus_event_handler_t)(struct ubus_context *ctx, struct ubus_event_handler *ev,
const char *type, struct blob_attr *msg);
typedef void (*ubus_data_handler_t)(struct ubus_request *req,
int type, struct blob_attr *msg);
typedef void (*ubus_fd_handler_t)(struct ubus_request *req, int fd);
typedef void (*ubus_complete_handler_t)(struct ubus_request *req, int ret);
typedef void (*ubus_notify_complete_handler_t)(struct ubus_notify_request *req,
int idx, int ret);
typedef void (*ubus_connect_handler_t)(struct ubus_context *ctx);
#define UBUS_OBJECT_TYPE(_name, _methods) \
{ \
.name = _name, \
.id = 0, \
.n_methods = ARRAY_SIZE(_methods), \
.methods = _methods \
}
#define __UBUS_METHOD_NOARG(_name, _handler) \
.name = _name, \
.handler = _handler
#define __UBUS_METHOD(_name, _handler, _policy) \
__UBUS_METHOD_NOARG(_name, _handler), \
.policy = _policy, \
.n_policy = ARRAY_SIZE(_policy)
#define UBUS_METHOD(_name, _handler, _policy) \
{ __UBUS_METHOD(_name, _handler, _policy) }
#define UBUS_METHOD_MASK(_name, _handler, _policy, _mask) \
{ \
__UBUS_METHOD(_name, _handler, _policy),\
.mask = _mask \
}
#define UBUS_METHOD_NOARG(_name, _handler) \
{ __UBUS_METHOD_NOARG(_name, _handler) }
struct ubus_method {
const char *name;
ubus_handler_t handler;
unsigned long mask;
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;
};
struct ubus_subscriber {
struct ubus_object obj;
ubus_handler_t cb;
ubus_remove_handler_t remove_cb;
};
struct ubus_event_handler {
struct ubus_object obj;
ubus_event_handler_t cb;
};
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;
int stack_depth;
void (*connection_lost)(struct ubus_context *ctx);
struct ubus_msghdr_buf msgbuf;
uint32_t msgbuf_data_len;
int msgbuf_reduction_counter;
};
struct ubus_object_data {
uint32_t id;
uint32_t type_id;
const char *path;
struct blob_attr *signature;
};
struct ubus_request_data {
uint32_t object;
uint32_t peer;
uint16_t seq;
/* internal use */
bool deferred;
int fd;
};
struct ubus_request {
struct list_head list;
struct list_head pending;
int status_code;
bool status_msg;
bool blocked;
bool cancelled;
bool notify;
uint32_t peer;
uint16_t seq;
ubus_data_handler_t raw_data_cb;
ubus_data_handler_t data_cb;
ubus_fd_handler_t fd_cb;
ubus_complete_handler_t complete_cb;
struct ubus_context *ctx;
void *priv;
};
struct ubus_notify_request {
struct ubus_request req;
ubus_notify_complete_handler_t status_cb;
ubus_notify_complete_handler_t complete_cb;
uint32_t pending;
uint32_t id[UBUS_MAX_NOTIFY_PEERS + 1];
};
struct ubus_auto_conn {
struct ubus_context ctx;
struct uloop_timeout timer;
const char *path;
ubus_connect_handler_t cb;
};
struct ubus_context *ubus_connect(const char *path);
void ubus_auto_connect(struct ubus_auto_conn *conn);
int ubus_reconnect(struct ubus_context *ctx, const char *path);
void ubus_free(struct ubus_context *ctx);
const char *ubus_strerror(int error);
static inline void ubus_add_uloop(struct ubus_context *ctx)
{
uloop_fd_add(&ctx->sock, ULOOP_BLOCKING | ULOOP_READ);
}
/* call this for read events on ctx->sock.fd when not using uloop */
static inline void ubus_handle_event(struct ubus_context *ctx)
{
ctx->sock.cb(&ctx->sock, ULOOP_READ);
}
/* ----------- raw request handling ----------- */
/* wait for a request to complete and return its status */
int ubus_complete_request(struct ubus_context *ctx, struct ubus_request *req,
int timeout);
/* complete a request asynchronously */
void ubus_complete_request_async(struct ubus_context *ctx,
struct ubus_request *req);
/* abort an asynchronous request */
void ubus_abort_request(struct ubus_context *ctx, struct ubus_request *req);
/* ----------- objects ----------- */
int ubus_lookup(struct ubus_context *ctx, const char *path,
ubus_lookup_handler_t cb, void *priv);
int ubus_lookup_id(struct ubus_context *ctx, const char *path, uint32_t *id);
/* make an object visible to remote connections */
int ubus_add_object(struct ubus_context *ctx, struct ubus_object *obj);
/* remove the object from the ubus connection */
int ubus_remove_object(struct ubus_context *ctx, struct ubus_object *obj);
/* add a subscriber notifications from another object */
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);
}
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);
/* ----------- rpc ----------- */
/* invoke a method on a specific object */
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);
/* asynchronous version of ubus_invoke() */
int ubus_invoke_async(struct ubus_context *ctx, uint32_t obj, const char *method,
struct blob_attr *msg, struct ubus_request *req);
/* send a reply to an incoming object method call */
int ubus_send_reply(struct ubus_context *ctx, struct ubus_request_data *req,
struct blob_attr *msg);
static inline void ubus_defer_request(struct ubus_context *ctx,
struct ubus_request_data *req,
struct ubus_request_data *new_req)
{
memcpy(new_req, req, sizeof(*req));
req->deferred = true;
}
static inline void ubus_request_set_fd(struct ubus_context *ctx,
struct ubus_request_data *req, int fd)
{
req->fd = fd;
}
void ubus_complete_deferred_request(struct ubus_context *ctx,
struct ubus_request_data *req, int ret);
/*
* send a notification to all subscribers of an object
* if timeout < 0, no reply is expected from subscribers
*/
int ubus_notify(struct ubus_context *ctx, struct ubus_object *obj,
const char *type, struct blob_attr *msg, int timeout);
int ubus_notify_async(struct ubus_context *ctx, struct ubus_object *obj,
const char *type, struct blob_attr *msg,
struct ubus_notify_request *req);
/* ----------- events ----------- */
int ubus_send_event(struct ubus_context *ctx, const char *id,
struct blob_attr *data);
int ubus_register_event_handler(struct ubus_context *ctx,
struct ubus_event_handler *ev,
const char *pattern);
static inline int ubus_unregister_event_handler(struct ubus_context *ctx,
struct ubus_event_handler *ev)
{
return ubus_remove_object(ctx, &ev->obj);
}
#endif
5.3ubusd_obj.h
/*
* Copyright (C) 2011 Felix Fietkau <nbd@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __UBUSD_OBJ_H
#define __UBUSD_OBJ_H
#include "ubusd_id.h"
extern struct avl_tree obj_types;
extern struct avl_tree objects;
extern struct avl_tree path;
struct ubus_client;
struct ubus_msg_buf;
struct ubus_object_type {
struct ubus_id id;
int refcount;
struct list_head methods;
};
struct ubus_method {
struct list_head list;
const char *name;
struct blob_attr data[];
};
struct ubus_subscription {
struct list_head list, target_list;
struct ubus_object *subscriber, *target;
};
struct ubus_object {
struct ubus_id id;
struct list_head list;
struct list_head events;
struct list_head subscribers, target_list;
struct ubus_object_type *type;
struct avl_node path;
struct ubus_client *client;
int (*recv_msg)(struct ubus_client *client, const char *method, struct blob_attr *msg);
int event_seen;
unsigned int invoke_seq;
};
struct ubus_object *ubusd_create_object(struct ubus_client *cl, struct blob_attr **attr);
struct ubus_object *ubusd_create_object_internal(struct ubus_object_type *type, uint32_t id);
void ubusd_free_object(struct ubus_object *obj);
static inline struct ubus_object *ubusd_find_object(uint32_t objid)
{
struct ubus_object *obj;
struct ubus_id *id;
id = ubus_find_id(&objects, objid);
if (!id)
return NULL;
obj = container_of(id, struct ubus_object, id);
return obj;
}
void ubus_subscribe(struct ubus_object *obj, struct ubus_object *target);
void ubus_unsubscribe(struct ubus_subscription *s);
void ubus_notify_unsubscribe(struct ubus_subscription *s);
void ubus_notify_subscription(struct ubus_object *obj);
#endif
5.4json.h
#include <stdio.h>
#include "cfg_json_interface.h"
/*
* 函数功能:创建一个json对象
*/
ZLINK_jsonObj *ZLINK_JSON_NewObject()
{
return json_object_new_object();
}
/*
* 函数功能:创建一个json对象数组
*/
ZLINK_jsonObj *ZLINK_JSON_NewObjectArray()
{
return json_object_new_array();
}
/*
* 函数功能:销毁json对象
*/
void ZLINK_JSON_DestroyObject(json_object *obj)
{
if (obj == NULL) {
return;
}
json_object_put(obj);
return;
}
/*
* 函数功能:增加json对象
*/
int ZLINK_JSON_AddObject(ZLINK_jsonObj *obj, const char *key, ZLINK_jsonObj *val)
{
if (obj == NULL || key == NULL || val == NULL) {
return ZLINK_FAILED;
}
return json_object_object_add(obj, key, val);
}
/*
* 函数功能:json对象中添加string的元素
*/
ZLINK_jsonObj *ZLINK_JSON_AddString(const char *str)
{
if (str == NULL) {
return NULL;
}
return json_object_new_string(str);
}
/*
* 函数功能:json对象中添加int的元素
*/
ZLINK_jsonObj *ZLINK_JSON_AddInt(int i)
{
return json_object_new_int(i);
}
/*
* 函数功能:获取json对象
*/
ZLINK_jsonObj *ZLINK_JSON_GetObject(ZLINK_jsonObj *obj, const char *key)
{
if (obj == NULL || key == NULL) {
return NULL;
}
return json_object_object_get(obj, key);
}
/*
* 函数功能:json对象中获取string的元素
*/
const char * ZLINK_JSON_GetString(ZLINK_jsonObj *obj)
{
if (obj == NULL) {
return NULL;
}
return json_object_get_string(obj);
}
/*
* 函数功能:json对象中获取int的元素
*/
int ZLINK_JSON_GetInt(ZLINK_jsonObj *obj)
{
if (obj == NULL) {
return ZLINK_FAILED;
}
return json_object_get_int(obj);
}
ZLINK_jsonObj *ZLINK_JSON_StringToObject(const char *str)
{
if (str == NULL) {
return NULL;
}
return json_tokener_parse(str);
}
/*
* 函数功能:将json obj转换为字符串
*/
const char *ZLINK_JSON_ObjectToString(ZLINK_jsonObj *obj)
{
if (obj == NULL) {
return NULL;
}
return json_object_to_json_string(obj);
}
/*
* 函数功能:计算json数组的长度
*/
int ZLINK_JSON_ObjectArrayLength(ZLINK_jsonObj *array)
{
if (array == NULL) {
return ZLINK_FAILED;
}
return json_object_array_length(array);
}
5.5blobmsg.h
16 #ifndef __BLOBMSG_H
17 #define __BLOBMSG_H
18
19 #include <stdarg.h>
20 #include "blob.h"
21
22 #define BLOBMSG_ALIGN 2
23 #define BLOBMSG_PADDING(len) (((len) + (1 << BLOBMSG_ALIGN) - 1) & ~((1 << BLOBMSG_ALIGN) - 1))
24
25 enum blobmsg_type {
26 BLOBMSG_TYPE_UNSPEC,
27 BLOBMSG_TYPE_ARRAY,
28 BLOBMSG_TYPE_TABLE,
29 BLOBMSG_TYPE_STRING,
30 BLOBMSG_TYPE_INT64,
31 BLOBMSG_TYPE_INT32,
32 BLOBMSG_TYPE_INT16,
33 BLOBMSG_TYPE_INT8,
34 __BLOBMSG_TYPE_LAST,
35 BLOBMSG_TYPE_LAST = __BLOBMSG_TYPE_LAST - 1,
36 BLOBMSG_TYPE_BOOL = BLOBMSG_TYPE_INT8,
37 };
38
39 struct blobmsg_hdr {
40 uint16_t namelen;
41 uint8_t name[];
42 } __packed;
43
44 struct blobmsg_policy {
45 const char *name;
46 enum blobmsg_type type;
47 };
48
49 static inline int blobmsg_hdrlen(unsigned int namelen)
50 {
51 return BLOBMSG_PADDING(sizeof(struct blobmsg_hdr) + namelen + 1);
52 }
53
54 static inline void blobmsg_clear_name(struct blob_attr *attr)
55 {
56 struct blobmsg_hdr *hdr = (struct blobmsg_hdr *) blob_data(attr);
57 hdr->name[0] = 0;
58 }
59
60 static inline const char *blobmsg_name(const struct blob_attr *attr)
61 {
62 struct blobmsg_hdr *hdr = (struct blobmsg_hdr *) blob_data(attr);
63 return (const char *) hdr->name;
64 }
65
66 static inline int blobmsg_type(const struct blob_attr *attr)
67 {
68 return blob_id(attr);
69 }
70
71 static inline void *blobmsg_data(const struct blob_attr *attr)
72 {
73 struct blobmsg_hdr *hdr = (struct blobmsg_hdr *) blob_data(attr);
74 char *data = (char *) blob_data(attr);
75
76 if (blob_is_extended(attr))
77 data += blobmsg_hdrlen(be16_to_cpu(hdr->namelen));
78
79 return data;
80 }
81
82 static inline int blobmsg_data_len(const struct blob_attr *attr)
83 {
84 uint8_t *start, *end;
85
86 start = (uint8_t *) blob_data(attr);
87 end = (uint8_t *) blobmsg_data(attr);
88
89 return blob_len(attr) - (end - start);
90 }
91
92 static inline int blobmsg_len(const struct blob_attr *attr)
93 {
94 return blobmsg_data_len(attr);
95 }
96
97 bool blobmsg_check_attr(const struct blob_attr *attr, bool name);
98 bool blobmsg_check_attr_list(const struct blob_attr *attr, int type);
99
100 /*
101 * blobmsg_check_array: validate array/table and return size
102 *
103 * Checks if all elements of an array or table are valid and have
104 * the specified type. Returns the number of elements in the array
105 */
106 int blobmsg_check_array(const struct blob_attr *attr, int type);
107
108 int blobmsg_parse(const struct blobmsg_policy *policy, int policy_len,
109 struct blob_attr **tb, void *data, unsigned int len);
110 int blobmsg_parse_array(const struct blobmsg_policy *policy, int policy_len,
111 struct blob_attr **tb, void *data, unsigned int len);
112
113 int blobmsg_add_field(struct blob_buf *buf, int type, const char *name,
114 const void *data, unsigned int len);
115
116 static inline int
117 blobmsg_add_u8(struct blob_buf *buf, const char *name, uint8_t val)
118 {
119 return blobmsg_add_field(buf, BLOBMSG_TYPE_INT8, name, &val, 1);
120 }
121
122 static inline int
123 blobmsg_add_u16(struct blob_buf *buf, const char *name, uint16_t val)
124 {
125 val = cpu_to_be16(val);
126 return blobmsg_add_field(buf, BLOBMSG_TYPE_INT16, name, &val, 2);
127 }
128
129 static inline int
130 blobmsg_add_u32(struct blob_buf *buf, const char *name, uint32_t val)
131 {
132 val = cpu_to_be32(val);
133 return blobmsg_add_field(buf, BLOBMSG_TYPE_INT32, name, &val, 4);
134 }
135
136 static inline int
137 blobmsg_add_u64(struct blob_buf *buf, const char *name, uint64_t val)
138 {
139 val = cpu_to_be64(val);
140 return blobmsg_add_field(buf, BLOBMSG_TYPE_INT64, name, &val, 8);
141 }
142
143 static inline int
144 blobmsg_add_string(struct blob_buf *buf, const char *name, const char *string)
145 {
146 return blobmsg_add_field(buf, BLOBMSG_TYPE_STRING, name, string, strlen(string) + 1);
147 }
148
149 static inline int
150 blobmsg_add_blob(struct blob_buf *buf, struct blob_attr *attr)
151 {
152 return blobmsg_add_field(buf, blobmsg_type(attr), blobmsg_name(attr),
153 blobmsg_data(attr), blobmsg_data_len(attr));
154 }
155
156 void *blobmsg_open_nested(struct blob_buf *buf, const char *name, bool array);
157
158 static inline void *
159 blobmsg_open_array(struct blob_buf *buf, const char *name)
160 {
161 return blobmsg_open_nested(buf, name, true);
162 }
163
164 static inline void *
165 blobmsg_open_table(struct blob_buf *buf, const char *name)
166 {
167 return blobmsg_open_nested(buf, name, false);
168 }
169
170 static inline void
171 blobmsg_close_array(struct blob_buf *buf, void *cookie)
172 {
173 blob_nest_end(buf, cookie);
174 }
175
176 static inline void
177 blobmsg_close_table(struct blob_buf *buf, void *cookie)
178 {
179 blob_nest_end(buf, cookie);
180 }
181
182 static inline int blobmsg_buf_init(struct blob_buf *buf)
183 {
184 return blob_buf_init(buf, BLOBMSG_TYPE_TABLE);
185 }
186
187 static inline uint8_t blobmsg_get_u8(struct blob_attr *attr)
188 {
189 return *(uint8_t *) blobmsg_data(attr);
190 }
191
192 static inline bool blobmsg_get_bool(struct blob_attr *attr)
193 {
194 return *(uint8_t *) blobmsg_data(attr);
195 }
196
197 static inline uint16_t blobmsg_get_u16(struct blob_attr *attr)
198 {
199 return be16_to_cpu(*(uint16_t *) blobmsg_data(attr));
200 }
201
202 static inline uint32_t blobmsg_get_u32(struct blob_attr *attr)
203 {
204 return be32_to_cpu(*(uint32_t *) blobmsg_data(attr));
205 }
206
207 static inline uint64_t blobmsg_get_u64(struct blob_attr *attr)
208 {
209 uint32_t *ptr = (uint32_t *) blobmsg_data(attr);
210 uint64_t tmp = ((uint64_t) be32_to_cpu(ptr[0])) << 32;
211 tmp |= be32_to_cpu(ptr[1]);
212 return tmp;
213 }
214
215 static inline char *blobmsg_get_string(struct blob_attr *attr)
216 {
217 if (!attr)
218 return NULL;
219
220 return (char *) blobmsg_data(attr);
221 }
222
223 void *blobmsg_alloc_string_buffer(struct blob_buf *buf, const char *name, unsigned int maxlen);
224 void *blobmsg_realloc_string_buffer(struct blob_buf *buf, unsigned int maxlen);
225 void blobmsg_add_string_buffer(struct blob_buf *buf);
226
227 void blobmsg_vprintf(struct blob_buf *buf, const char *name, const char *format, va_list arg);
228 void blobmsg_printf(struct blob_buf *buf, const char *name, const char *format, ...)
229 __attribute__((format(printf, 3, 4)));
230
231
232 /* blobmsg to json formatting */
233
234 #define blobmsg_for_each_attr(pos, attr, rem) \
235 for (rem = attr ? blobmsg_data_len(attr) : 0, \
236 pos = attr ? blobmsg_data(attr) : 0; \
237 rem > 0 && (blob_pad_len(pos) <= rem) && \
238 (blob_pad_len(pos) >= sizeof(struct blob_attr)); \
239 rem -= blob_pad_len(pos), pos = blob_next(pos))
240
241 #endif
5.6blob.h
19 #ifndef _BLOB_H__
20 #define _BLOB_H__
21
22 #include <stdbool.h>
23 #include <stdlib.h>
24 #include <stdint.h>
25 #include <string.h>
26 #include <stdio.h>
27 #include <errno.h>
28
29 #include "utils.h"
30
31 #define BLOB_COOKIE 0x01234567
32
33 enum {
34 BLOB_ATTR_UNSPEC,
35 BLOB_ATTR_NESTED,
36 BLOB_ATTR_BINARY,
37 BLOB_ATTR_STRING,
38 BLOB_ATTR_INT8,
39 BLOB_ATTR_INT16,
40 BLOB_ATTR_INT32,
41 BLOB_ATTR_INT64,
42 BLOB_ATTR_LAST
43 };
44
45 #define BLOB_ATTR_ID_MASK 0x7f000000
46 #define BLOB_ATTR_ID_SHIFT 24
47 #define BLOB_ATTR_LEN_MASK 0x00ffffff
48 #define BLOB_ATTR_ALIGN 4
49 #define BLOB_ATTR_EXTENDED 0x80000000
50
51 struct blob_attr {
52 uint32_t id_len;
53 char data[];
54 } __packed;
55
56 struct blob_attr_info {
57 unsigned int type;
58 unsigned int minlen;
59 unsigned int maxlen;
60 bool (*validate)(const struct blob_attr_info *, struct blob_attr *);
61 };
62
63 struct blob_buf {
64 struct blob_attr *head;
65 bool (*grow)(struct blob_buf *buf, int minlen);
66 int buflen;
67 void *buf;
68 };
69
70 /*
71 * blob_data: returns the data pointer for an attribute
72 */
73 static inline void *
74 blob_data(const struct blob_attr *attr)
75 {
76 return (void *) attr->data;
77 }
78
79 /*
80 * blob_id: returns the id of an attribute
81 */
82 static inline unsigned int
83 blob_id(const struct blob_attr *attr)
84 {
85 int id = (be32_to_cpu(attr->id_len) & BLOB_ATTR_ID_MASK) >> BLOB_ATTR_ID_SHIFT;
86 return id;
87 }
88
89 static inline bool
90 blob_is_extended(const struct blob_attr *attr)
91 {
92 return !!(attr->id_len & cpu_to_be32(BLOB_ATTR_EXTENDED));
93 }
94
95 /*
96 * blob_len: returns the length of the attribute's payload
97 */
98 static inline unsigned int
99 blob_len(const struct blob_attr *attr)
100 {
101 return (be32_to_cpu(attr->id_len) & BLOB_ATTR_LEN_MASK) - sizeof(struct blob_attr);
102 }
103
104 /*
105 * blob_raw_len: returns the complete length of an attribute (including the header)
106 */
107 static inline unsigned int
108 blob_raw_len(const struct blob_attr *attr)
109 {
110 return blob_len(attr) + sizeof(struct blob_attr);
111 }
112
113 /*
114 * blob_pad_len: returns the padded length of an attribute (including the header)
115 */
116 static inline unsigned int
117 blob_pad_len(const struct blob_attr *attr)
118 {
119 unsigned int len = blob_raw_len(attr);
120 len = (len + BLOB_ATTR_ALIGN - 1) & ~(BLOB_ATTR_ALIGN - 1);
121 return len;
122 }
123
124 static inline uint8_t
125 blob_get_u8(const struct blob_attr *attr)
126 {
127 return *((uint8_t *) attr->data);
128 }
129
130 static inline uint16_t
131 blob_get_u16(const struct blob_attr *attr)
132 {
133 uint16_t *tmp = (uint16_t*)attr->data;
134 return be16_to_cpu(*tmp);
135 }
136
137 static inline uint32_t
138 blob_get_u32(const struct blob_attr *attr)
139 {
140 uint32_t *tmp = (uint32_t*)attr->data;
141 return be32_to_cpu(*tmp);
142 }
143
144 static inline uint64_t
145 blob_get_u64(const struct blob_attr *attr)
146 {
147 uint32_t *ptr = (uint32_t *) blob_data(attr);
148 uint64_t tmp = ((uint64_t) be32_to_cpu(ptr[0])) << 32;
149 tmp |= be32_to_cpu(ptr[1]);
150 return tmp;
151 }
152
153 static inline int8_t
154 blob_get_int8(const struct blob_attr *attr)
155 {
156 return blob_get_u8(attr);
157 }
158
159 static inline int16_t
160 blob_get_int16(const struct blob_attr *attr)
161 {
162 return blob_get_u16(attr);
163 }
164
165 static inline int32_t
166 blob_get_int32(const struct blob_attr *attr)
167 {
168 return blob_get_u32(attr);
169 }
170
171 static inline int64_t
172 blob_get_int64(const struct blob_attr *attr)
173 {
174 return blob_get_u64(attr);
175 }
176
177 static inline const char *
178 blob_get_string(const struct blob_attr *attr)
179 {
180 return attr->data;
181 }
182
183 static inline struct blob_attr *
184 blob_next(const struct blob_attr *attr)
185 {
186 return (struct blob_attr *) ((char *) attr + blob_pad_len(attr));
187 }
188
189 extern void blob_fill_pad(struct blob_attr *attr);
190 extern void blob_set_raw_len(struct blob_attr *attr, unsigned int len);
191 extern bool blob_attr_equal(const struct blob_attr *a1, const struct blob_attr *a2);
192 extern int blob_buf_init(struct blob_buf *buf, int id);
193 extern void blob_buf_free(struct blob_buf *buf);
194 extern bool blob_buf_grow(struct blob_buf *buf, int required);
195 extern struct blob_attr *blob_new(struct blob_buf *buf, int id, int payload);
196 extern void *blob_nest_start(struct blob_buf *buf, int id);
197 extern void blob_nest_end(struct blob_buf *buf, void *cookie);
198 extern struct blob_attr *blob_put(struct blob_buf *buf, int id, const void *ptr, unsigned int len);
199 extern bool blob_check_type(const void *ptr, unsigned int len, int type);
200 extern int blob_parse(struct blob_attr *attr, struct blob_attr **data, const struct blob_attr_info *info, int max);
201 extern struct blob_attr *blob_memdup(struct blob_attr *attr);
202 extern struct blob_attr *blob_put_raw(struct blob_buf *buf, const void *ptr, unsigned int len);
203
204 static inline struct blob_attr *
205 blob_put_string(struct blob_buf *buf, int id, const char *str)
206 {
207 return blob_put(buf, id, str, strlen(str) + 1);
208 }
209
210 static inline struct blob_attr *
211 blob_put_u8(struct blob_buf *buf, int id, uint8_t val)
212 {
213 return blob_put(buf, id, &val, sizeof(val));
214 }
215
216 static inline struct blob_attr *
217 blob_put_u16(struct blob_buf *buf, int id, uint16_t val)
218 {
219 val = cpu_to_be16(val);
220 return blob_put(buf, id, &val, sizeof(val));
221 }
222
223 static inline struct blob_attr *
224 blob_put_u32(struct blob_buf *buf, int id, uint32_t val)
225 {
226 val = cpu_to_be32(val);
227 return blob_put(buf, id, &val, sizeof(val));
228 }
229
230 static inline struct blob_attr *
231 blob_put_u64(struct blob_buf *buf, int id, uint64_t val)
232 {
233 val = cpu_to_be64(val);
234 return blob_put(buf, id, &val, sizeof(val));
235 }
236
237 #define blob_put_int8 blob_put_u8
238 #define blob_put_int16 blob_put_u16
239 #define blob_put_int32 blob_put_u32
240 #define blob_put_int64 blob_put_u64
241
242 #define __blob_for_each_attr(pos, attr, rem) \
243 for (pos = (void *) attr; \
244 rem > 0 && (blob_pad_len(pos) <= rem) && \
245 (blob_pad_len(pos) >= sizeof(struct blob_attr)); \
246 rem -= blob_pad_len(pos), pos = blob_next(pos))
247
248
249 #define blob_for_each_attr(pos, attr, rem) \
250 for (rem = attr ? blob_len(attr) : 0, \
251 pos = attr ? blob_data(attr) : 0; \
252 rem > 0 && (blob_pad_len(pos) <= rem) && \
253 (blob_pad_len(pos) >= sizeof(struct blob_attr)); \
254 rem -= blob_pad_len(pos), pos = blob_next(pos))
255
256
257 #endif