Openwrt下C调用UCI API

本文参考http://blog.csdn.net/bywayboy/article/details/20866287

“uci”是”Unified Configuration Interface”(统一配置界面)的缩写,意在OpenWrt整个系统的配置集中化。

许多程序在系统某处拥有自己的配置文件,比如/etc/network/interfaces, /etc/exports, /etc/dnsmasq.conf或者 /etc/samba/samba.conf,有时它们还使用稍有不同的语法。

共同原则

OpenWrt的所有配置文件皆位于/etc/config/目录下。每个文件大致与它所配置的那部分系统相关。可用文本编辑器、”uci” 命令行实用程序或各种编程API(比如 Shell, Lua and C)来编辑/修改这些配置文件。

配置文件

| 文件位置 | 描述 |

| 基本配置 |

|/etc/config/dhcp | dnsmasq和DHCP的配置

|/etc/config/dropbear | SSH服务端选项

|/etc/config/firewall | 中央防火墙配置

|/etc/config/network | 交换,接口和路由配置

|/etc/config/system | 杂项与系统配置

|/etc/config/timeserver | rdate的时间服务器列表

|/etc/config/wireless | 无线设置和无线网络的定义

IPv6

|/etc/config/ahcpd | Ad-Hoc配置协议(AHCP) 服务端配置以及转发器配置

|/etc/config/aiccu | AICCU 客户端配置

|/etc/config/dhcp6c | WIDE-DHCPv6 客户端配置

|/etc/config/dhcp6s | WIDE-DHCPv6 服务端配置

|/etc/config/gw6c | GW6c 客户端配置

|/etc/config/radvd | 路由通告 (radvd) 配置

其他

|/etc/config/etherwake | 以太网唤醒: etherwake

|/etc/config/fstab | 挂载点及swap

|/etc/config/hd-idle | 另一个可选的硬盘空闲休眠进程(需要路由器支持usb硬盘)

|/etc/config/httpd | 网页服务器配置选项(Busybox 自带httpd, 已被舍弃)

|/etc/config/luci | 基础 LuCI 配置

|/etc/config/luci_statistics | 包统计配置

|/etc/config/mini_snmpd | mini_snmpd 配置

|/etc/config/mountd | OpenWrt 自动挂载进程(类似autofs)

|/etc/config/multiwan | 简单多WAN出口配置

|/etc/config/ntpclient | ntp客户端配置,用以获取正确时间

|/etc/config/pure-ftpd | Pure-FTPd 服务端配置

|/etc/config/qos | QoS配置(流量限制与整形)

|/etc/config/samba | samba配置(Microsoft文件共享)

|/etc/config/snmpd | SNMPd(snmp服务进程) 配置

|/etc/config/sshtunnel | sshtunnel配置

|/etc/config/stund | STUN 服务端配置

|/etc/config/transmission | BitTorrent配置

|/etc/config/uhttpd | Web服务器配置(uHTTPd)

|/etc/config/upnpd | miniupnpd UPnP服务器配置

|/etc/config/ushare | uShare UPnP 服务器配置

|/etc/config/vblade | vblade 用户空间AOE(ATA over Ethernet)配置

|/etc/config/vnstat | vnstat 下载器配置

|/etc/config/wifitoogle | 使用按钮来开关WiFi的脚本

|/etc/config/wol | Wake-on-Lan: wol

|/etc/config/znc | ZNC 配置

文件语法

在UCI的配置文件通常包含一个或多个配置语句,包含一个或多个用来定义实际值的选项语句的所谓的节。

下面是一个简单的配置示例文件:

package ‘example‘

config ‘example‘ ‘test‘
        option   ‘string‘      ‘some value‘
        option   ‘boolean‘     ‘1‘
        list     ‘collection‘  ‘first item‘
        list     ‘collection‘  ‘second item‘
  • config ‘example’ ‘test’ 语句标志着一个节的开始。这里的配置类型是example,配置名是test。配置中也允许出现匿名节,即自定义了配置类型,而没有配置名的节。配置类型对应配置处理程序来说是十分重要的,因为配置程序需要根据这些信息来处理这些配置项。
  • option ‘string’ ‘some value’ 和 option ‘boolean’ ‘1’ 定义了一些简单值。文本选项和布尔选项在语法上并没有差异。布尔选项中可以用’0’ ,’no’, ‘off’, 或者’false’来表示false值,或者也可以用’1’, ‘yes’,’on’或者’true’来表示真值。
  • 以list关键字开头的多个行,可用于定义包含多个值的选项。所有共享一个名称的list语句,会组装形成一个值列表,列表中每个值出现的顺序,和它在配置文件中的顺序相同。如上例种中,列表的名称是’collection’,它包含了两个值,即’first item’和’second item’。
  • ‘option’和’list’语句的缩进可以增加配置文件的可读性,但是在语法不是必须的。

通常不需要为标识符和值加引号,只有当值包括空格或者制表符的时候,才必须加引号。同时,在使用引号的时候,可以用双引号代替单引号。

UCI的几个结构体

1.struct uci_package: 包结构体。它对应一个配置文件内容

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;
};

2.struct uci_section:节结构体,它对应配置文件中的节

struct uci_section
{
    struct uci_element e;
    struct uci_list options;
    struct uci_package *package;
    bool anonymous;
    char *type;
};

3.struct uci_option:选项结构体,它对应配置文件里节中的option或者list

struct uci_option
{
    struct uci_element e;
    struct uci_section *section;
    enum uci_option_type type;
    union {
        struct uci_list list;
        char *string;
    } v;
};

4.struct uci_ptr:元素位置指针结构,用来查询并保存对应位置元素

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;
};

5.struct uci_context: uci上下文结构,贯穿查询、更改配置文件全过程。

struct uci_context
{
    /* 配置文件包列表 */
    struct uci_list root;

    /* 解析上下文,只用于错误处理 */
    struct uci_parse_context *pctx;

    /* 后端导入导出 */
    struct uci_backend *backend;
    struct uci_list backends;

    /* uci 运行标识 */
    enum uci_flags flags;

    char *confdir;
    char *savedir;

    /* search path for delta files */
    struct uci_list delta_path;

    /* 私有数据 */
    int err;
    const char *func;
    jmp_buf trap;
    bool internal, nested;
    char *buf;
    int bufsz;
};

几个基本的API函数

1.uci_alloc_context:动态申请一个uci上下文结构

struct uci_context *uci_alloc_context(void);

2.uci_free_contex:释放由uci_alloc_context申请的uci上下文结构且包括它的所有数据

void uci_free_context(struct uci_context *ctx);

3.uci_lookup_ptr:由给定的元组查找元素

/**
 * uci_lookup_ptr: 分离一个uci元组字符串且查找对应元素树
 * @ctx: uci context结构体指针
 * @ptr: 存放元素查询结果的结构体指针
 * @str: 待查找的uci元组字符串
 * @extended: 允许扩展语法查询
 *
 *如果extended被设为ture,则uci_lookup_ptr支持下列扩展语法:
 *
 *例子:
 *   network.@interface[0].ifname (‘ifname‘ option of the first interface section)
 *   network.@interface[-1]       (last interface section)
 * Note: 有必要的话uci_lookup_ptr将会自动加载配置文件包
 * @str 不能是一个const类型指针,它在使用的过程中将会被更改且用于将字符串填写到@ptr中,因此
 * 它只要@ptr还在使用,它就必须是可用的
 *
 * 这个函数在指定包元组的的字符串未被找到时返回UCI_ERR_NOTFOUND,否则返回UCI_OK
 *
 * 记住在查找其他部分失败的情况,如果它们同样被指定,包括section和option,同样会返回UCI_OK,
 * 但是ptr->flags * UCI_LOOKUP_COMPLETE标志位不会被置位
 */
int uci_lookup_ptr(struct uci_context *ctx, struct uci_ptr *ptr, char *str, bool extended);

现在我们来看一个示例来进一步了解:

#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <uci.h>

#define UCI_CONFIG_FILE "/etc/config/meter"
static struct uci_context * ctx = NULL; //定义一个UCI上下文的静态变量.
/*********************************************
*   载入配置文件,并遍历Section.
*/
bool load_config()
{
    struct uci_package * pkg = NULL;
    struct uci_element *e;
    char *tmp;
    const char *value;

    ctx = uci_alloc_context(); // 申请一个UCI上下文.
    if (UCI_OK != uci_load(ctx, UCI_CONFIG_FILE, &pkg))
        goto cleanup; //如果打开UCI文件失败,则跳到末尾 清理 UCI 上下文.

    /*遍历UCI的每一个节*/
    uci_foreach_element(&pkg->sections, e)
    {
        struct uci_section *s = uci_to_section(e);

    printf("section s‘s type is %s.\n",s->type);

    if(!strcmp)

    if(!strcmp("meter",s->type)) //this section is a meter
    {
        printf("this seciton is a meter.\n");
            if (NULL != (value = uci_lookup_option_string(ctx, s, "modbus_id")))
            {
                    tmp = strdup(value); //如果您想持有该变量值,一定要拷贝一份。当 pkg销毁后value的内存会被释放。
                    printf("%s‘s modbus_id is %s.\n",s->e.name,value);
            }
            if (NULL != (value = uci_lookup_option_string(ctx, s, "num_attr")))
            {
                    tmp = strdup(value); //如果您想持有该变量值,一定要拷贝一份。当 pkg销毁后value的内存会被释放。
                    printf("%s‘s num_attr is %s.\n",s->e.name,value);
            }
            if (NULL != (value = uci_lookup_option_string(ctx, s, "sender_id")))
            {
                    tmp = strdup(value); //如果您想持有该变量值,一定要拷贝一份。当 pkg销毁后value的内存会被释放。
                    printf("%s‘s sender_id is %s.\n",s->e.name,value);
            }
            if (NULL != (value = uci_lookup_option_string(ctx, s, "customer_id")))
            {
                    tmp = strdup(value); //如果您想持有该变量值,一定要拷贝一份。当 pkg销毁后value的内存会被释放。
                    printf("%s‘s customer_id is %s.\n",s->e.name,value);
            }
            if (NULL != (value = uci_lookup_option_string(ctx, s, "customer_name")))
            {
                    tmp = strdup(value); //如果您想持有该变量值,一定要拷贝一份。当 pkg销毁后value的内存会被释放。
                    printf("%s‘s customer_name is %s.\n",s->e.name,value);
            }
            if (NULL != (value = uci_lookup_option_string(ctx, s, "account_id")))
            {
                    tmp = strdup(value); //如果您想持有该变量值,一定要拷贝一份。当 pkg销毁后value的内存会被释放。
                    printf("%s‘s account_id is %s.\n",s->e.name,value);
            }
            if (NULL != (value = uci_lookup_option_string(ctx, s, "account_name")))
            {
                    tmp = strdup(value); //如果您想持有该变量值,一定要拷贝一份。当 pkg销毁后value的内存会被释放。
                    printf("%s‘s account_name is %s.\n",s->e.name,value);
            }
            if (NULL != (value = uci_lookup_option_string(ctx, s, "meter_id")))
            {
                    tmp = strdup(value); //如果您想持有该变量值,一定要拷贝一份。当 pkg销毁后value的内存会被释放。
                    printf("%s‘s meter_id is %s.\n",s->e.name,value);
            }
            if (NULL != (value = uci_lookup_option_string(ctx, s, "commodity")))
            {
                    tmp = strdup(value); //如果您想持有该变量值,一定要拷贝一份。当 pkg销毁后value的内存会被释放。
                    printf("%s‘s commodity is %s.\n",s->e.name,value);
            }
    }
        // 如果您不确定是 string类型 可以先使用 uci_lookup_option() 函数得到Option 然后再判断.
        // Option 的类型有 UCI_TYPE_STRING 和 UCI_TYPE_LIST 两种.

    }
    uci_unload(ctx, pkg); // 释放 pkg
cleanup:
    uci_free_context(ctx);
    ctx = NULL;
}
int main(int argc, char* argv[])
{

    load_config();
}

遍历一个UCI_TYPE_LIST 类型

config  "server" "webserver"
    list    "index" "index.html"
    list    "index" "index.php"
    list    "index" "default.html"  

代码段:

// s 为 section.
struct uci_option * o = uci_lookup_option(ctx, s, "index");
if ((NULL != o) && (UCI_TYPE_LIST == o->type)) //o存在 且 类型是 UCI_TYPE_LIST则可以继续.
{
    struct uci_element *e;
    uci_foreach_element(&o->v.list, e)
    {
        //这里会循环遍历 list
        // e->name 的值依次是 index.html, index.php, default.html
    }
}  

写配置

UCI提供了一个简洁的办法来操作配置信息,例如有一个配置文件

#文件名: testconfig
config  ‘servver‘
    option  ‘value‘ ‘123‘ # 我们想修改 ‘value‘ 的值为 ‘456‘  

代码如下:

struct uci_context * ctx = uci_alloc_context(); //申请上下文
struct uci_ptr ptr ={
    .package = "config",
    .section = "servver",
    .option = "value",
    .value = "256",
};
uci_set(_ctx,&ptr); //写入配置
uci_commit(_ctx, &ptr.p, false); //提交保存更改
uci_unload(_ctx,ptr.p); //卸载包  

uci_free_context(ctx); //释放上下文  
时间: 2024-12-11 07:56:22

Openwrt下C调用UCI API的相关文章

【智能路由器】C代码调用uci的API读openwrt配置文件指南

[智能路由器]系列文章连接 http://blog.csdn.net/u012819339/article/category/5803489 上篇博客讲解了命令行下uci的使用方法,本篇博客arvik将简单剖析uci部分源码,带领大家使用c语言调用uci的API来读取配置文件. 实战背景 倘若我们自己写了一个应用程序,也想用uci来集中化管理配置该应用的配置文件,怎么办呢? 看了arvik的上一篇博客后相信新手能很快的使用uci对某个配置文件进行配置,只是如何让我们的应用程序读取配置文件内容呢,

OpenWRT下web框架初尝试之总结

OpenWRT下web总结 目  录 目  录 1 第一章 Web框架以及实现 2 第一节 luci框架 2 第二节 controller下文件(*.lua)的编写 2 第三节 model下文件(*.lua)编写 3 第四节 view下文件(*.htm)编写 4 第二章 web的访问流程 5 第三章 lua学习资料 6 参考资料 7 第一章 Web框架以及实现 第一节 luci框架 OpenWRT的web采用的是luci框架,该框架采用了MVC的设计模式.在luci目录下有三个重要的目录:con

Asp.Net Web API 2第三课——.NET客户端调用Web API

Asp.Net Web API 导航 Asp.Net Web API第一课——入门http://www.cnblogs.com/aehyok/p/3432158.html Asp.Net Web API第二课——CRUD操作http://www.cnblogs.com/aehyok/p/3434578.html 前言 本教程演示从一个控制台应用程序,使用HttpClient调用Web API.我们也将使用上一个教程中建立的Web API.你可以直接在http://www.cnblogs.com/

调用graph api上传图片到facebook

前言 最近实习的任务是做个类似facebook第三方客户端, 要求用graph api. 调用graph api就是普通的http请求, 但是facebook在这方面挖了不少坑, 特别恶心. 写篇文章记录一下, 顺便介绍一下思路和方法, 有好几个方法, 有的成功了, 有的失败了, 会具体分析, 最后有一个最佳的方法. 图片上传 sdk文档的错误 查询facebook的graph api文档可以知道如果使用sdk上传图片, 需要使用下面的代码 Bundle params = new Bundle(

C#调用windows API的一些方法

使用C#调用windows API(从其它地方总结来的,以备查询) C#调用windows API也可以叫做C#如何直接调用非托管代码,通常有2种方法: 1.  直接调用从 DLL 导出的函数. 2.  调用 COM 对象上的接口方法 我主要讨论从dll中导出函数,基本步骤如下: 1.使用 C# 关键字 static 和 extern 声明方法. 2.将 DllImport 属性附加到该方法.DllImport 属性允许您指定包含该方法的 DLL 的名称. 3.如果需要,为方法的参数和返回值指定

调用ZoomEye API获取信息

最近在提高自己编程能力,拿一些实用的小工具练下.该脚本为python语言,主要涉及模块urllib,json,os模块. 功能:调用ZoomEye API获取信息 import urllib.request import json import os def login(): username = input("username:") password = input("password:") url = "https://api.zoomeye.org/

C#中调用Outlook API 发起会议

原文:C#中调用Outlook API 发起会议 在我上一篇博文中曾提到了 SharePoint 中调用传出电子邮件中的邮件服务器及地址发送邮件 但是,里面的方法只能用于发送普通电子邮件.如果要发起会议之类的特殊邮件的话,可以使用Outlook 自身的API. 创建项目后,为它添加.NET引用:“Microsoft.Office.Interop.Outlook"的引用,即可调用,需要注意的是,在添加的时候,注意一下OFFICE版本号. 在调用其API发起会议的过程中,遇到了一个问题: 创建完一个

Python调用微博API

上头叫通过微博ID获取用户发布过的历史微博内容,于是研究了下新浪微博提供的API 1 首先在微博开放中心下"创建应用"创建一个应用,应用信息那些随便填,填写完毕后,不需要提交审核,需要的只是那个app-key和app-secret 2 在"微博开放平台"的"管理中心"找到刚才创建的应用,点开这个应用,点开左边"应用信息"栏,会看见"App key"和"App Secret"的字样,这两个

phonegap+cordova+ionic调用原生API

上一篇博客讲了phonegap+cordova+ionic的环境搭建,今天再来分享一篇cordova调用原生API的文章.从技术角度上来讲,这并不是很难,只是有些细节要是没有注意,或者某些步骤不知道的,那么在坑里一时半会很难爬出来.所以这两篇博客旨在帮助小伙伴们节省更多的时间去做其他有意义的事情. 1.新建工程 新建工程和添加平台支持的操作已经在上一篇博客中讲到了, 这里不再赘述. 2.Bower的使用 首先确认是否安装了bower,如果没有安装,打开cmd命名,输入npm install -g