1. 程式人生 > >Openwrt下C呼叫UCI API

Openwrt下C呼叫UCI API

“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); //釋放上下文  

相關推薦

OpenwrtC呼叫UCI API

“uci”是”Unified Configuration Interface”(統一配置介面)的縮寫,意在OpenWrt整個系統的配置集中化。 許多程式在系統某處擁有自己的配置檔案,比如/etc/network/interfaces, /etc/expor

用Visual C#呼叫Windows API函式

用Visual C#呼叫Windows API函式         Api函式是構築Windws應用程式的基石,每一種Windows應用程式開發工具,它提供的底層函式都間接或直接地呼叫了Windows API函式,同時為了實現功能擴充套

WindowsC++呼叫系統軟鍵盤及其需要注意的點

Windows下系統軟鍵盤的程式名是osk.exe,系統軟鍵盤在有鍵盤的時候一點用都沒有,但是沒有鍵盤的時候想要輸入點東西,系統軟鍵盤就至關重要了。 osk.exe為微軟系統自帶的虛擬鍵盤程式,功能與真的鍵盤差不多.只需要在執行中輸入"osk"即可啟動虛擬鍵盤。 W

LinuxC++呼叫Java簡單例項(jni)

假設工作目錄為/home1. Java端1)  mkdir leixf && cd leixf2) 建立SubClass.java檔案,內容如下:package leixf;public class SubClass {private String name

win64C++呼叫python指令碼

#include <iostream> #include <Python.h> using namespace std; int main() { // D:\Python27\include\pyconfig.h // 修改 https://blog.csdn.n

C#呼叫RESTful API

現在很多的網路服務都用RESTful API來實現。比如百度的搜尋推廣API介紹使用Rest原因:REST+JSON風格的API相比SOAP+XML,好處是:呼叫更加靈活,也更容易擴充套件;JSON格式傳輸資訊比XML減少約30%的資料量,效率更高。因此建議開發

debian系列c++呼叫mysql, linux下面安裝mysql.h檔案

  1.介紹需求:   python呼叫資料庫,並做邏輯處理,時間為92.5s,從執行sql到得到資料150w條為22s,邏輯處理(2個for迴圈)為60s。前端處理30s,pending為2min左右,需要處理這個問題   於是思考解決方案:       1. 取資料時資料拆分  

C#呼叫Windows API和其它程序通訊及C# 獲得另外一程式 控制代碼 後控制該程式 控制其透明 窗體大小

作者:邊城浪子◎2005-01-16 關鍵字:  C#,API,FindWindow,FindWindowEx,SendMessage,程序,登錄檔 設計初衷:公司為了便於網路管理,使用了IEEE 802.1X的網路訪問控制,這樣每次開機需要輸入兩次登入密碼,於是我就研究了

VS2015C++呼叫Python指令碼

C++呼叫Python在一些特殊的場景下有一定的價值。比如,要呼叫一個REST服務,c++本身做HTTP Client會很煩,如果使用Python指令碼去呼叫服務,返回結果給c++端就可以繞開這個問題。因為最近在一個原型開發中遇到了這個問題,所以為了快速拿出東西來,我決定使

linux系統呼叫API、系統命令,核心函式的區別與聯絡

1.系統呼叫: 應用程式和核心間的橋樑,是應用程式訪問核心的入口點;但通常情況下,應用程式通過作業系統提供的API進行程式設計而不是使用系統呼叫直接程式設計; linux的全部系統呼叫加起來大約只有250個左右。 2.API:   API常以c庫(libc)的形式提供,

Linuxc++呼叫自己編寫的matlab函式:通過mcc動態連結庫.so實現

之前在這裡和這裡呼叫了matlab自帶的一些函式,是通過matlab引擎來實現的。那裡呼叫的是matlab自帶的函式,那麼如果想呼叫自己寫的.m函式該怎麼辦呢?其實很簡單,原理類似,方法也不止一種。這篇筆記我先嚐試通過mcc將.m函式編譯成動態連結庫供c++呼叫的方式。在另

C#呼叫Windows API(示例:顯示工作管理員裡的程式名稱)

作為初學者來說,在C#中使用API確是一件令人頭疼的問題。 在使用API之前你必須知道如何在C#中使用結構、型別轉換、安全/不安全程式碼,可控/不可控程式碼等許多知識。 在.Net Framework SDK文件中,關於呼叫Windows API的指示比較零散,並且其中稍全面一點的是針對Visual Basi

C程式碼呼叫uciAPIopenwrt配置檔案指南

實戰背景 倘若我們自己寫了一個應用程式,也想用uci來集中化管理配置該應用的配置檔案,怎麼辦呢?  看了arvik的上一篇部落格後相信新手能很快的使用uci對某個配置檔案進行配置,只是如何讓我們的應用程式讀取配置檔案內容呢,本篇arvik將解答這個問題。 簡單

OpenWRT UCI API的使用——C語言

UCI 是OpenWRT為實現配置集中化而引入的一個軟體包, 通過修改UCI,可以實現對OpenWRT的絕對部分配置的修改.LUCI(OpenWRT 的WEB配置介面)也是通過讀UCI配置檔案的操作來實現使用者對路由的配置的。通過掌握UCI的API的使用,可以方便地將您的軟

centosC程式設計呼叫libvirt的API訪問KVM虛擬機器

一、簡介         libvirt是一套免費、開源的支援Linux下主流虛擬化工具的C函式庫,其旨在為包括Xen在內的各種虛擬化工具提供一套方便、可靠的程式設計介面,支援與C,C++,Ruby,Python等多種主流開發語言的繫結。當前主流Linux平臺上預設的虛擬

windows c++ 如何呼叫 python 3

本文所使用環境: win10 (x64) +  VS2013 + python 3.5.2 (x64) python官網下載地址 一、先配置環境 1.安裝python 2.把 python 新增到VS2013     (1) VS新建一個空的Win3

LinuxC程式動態庫的生成和呼叫

Linux下C程式動態庫的生成和呼叫 文章目錄 Linux下C程式動態庫的生成和呼叫 1 動態庫的打包和呼叫 2 靜態庫打包和呼叫 3 常用命令 4 gcc 和 g++ 區別 5 編譯和連結的理解

windows C++動態庫的封裝以及呼叫

1、一個程式從原始檔編譯生成可執行檔案的步驟:預編譯 -->  編譯 -->  彙編 --> 連結(1)預編譯,即預處理,主要處理在原始碼檔案中以“#”開始的預編譯指令,如巨集展開、處理條件編譯指令、處理#include指令等。(2)編譯過程就是把預處理完的檔案進行一系列

c++ 呼叫Python指令碼或者動態庫——環境Ubuntu 16.04用codeblocks

背景:因為使用的是python版本的程式,最終要整合到C++環境的架構中,也就是說架構是c++的,交付使用者為c++的介面,但是呼叫的是python的庫,因此需要學習在c++環境下呼叫python。因為對python不熟悉,可以說有點一抹黑,因此從簡到難逐步探索。首先在c++

C#呼叫百度api,根據經度和緯度獲取地理位置資訊

        /// <summary>         /// 百度api 根據經緯度獲取地理位置         /// </summary>         /// <param name="lng">經度</param&g