User Tools

Site Tools


zh-cn:doc:techref:ubus

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Previous revision
zh-cn:doc:techref:ubus [2013/02/22 10:49]
zh-cn:doc:techref:ubus [2014/12/02 07:16] (current)
czhongm
Line 1: Line 1:
 ====== ubus (OpenWrt micro bus 架构) ====== ====== ubus (OpenWrt micro bus 架构) ======
 +
 +为了在OpenWrt中提供守护进程和应用程序间的通讯,开发了ubus项目工程。它包含了守护进程、库以及一些额外的帮助程序。
 +
 +核心部分是ubusd守护进程,它提供了其他守护进程将自己注册以及发送消息的接口。因为这个,接口通过使用Unix socket来实现,并使用TLV(type-length-value)消息。
 +
 +为了简化软件的开发,可以使用已有的libubus库来使用ubus(连接ubus)。
 +
 +每个守护进程在自己的名称空间中注册自有的路径。每个路径可以提供多个带有不定数量参数的方法,方法可以通过消息回复调用。
 +
 +代码在LGPL 2.1授权方法下发布,你可以通过git在[[git://​nbd.name/​luci2/​ubus.git]]或通过http在[[http://​nbd.name/​gitweb.cgi?​p=luci2/​ubus.git;​a=summary]]获取。 ubus从[[https://​dev.openwrt.org/​changeset/​28499|r28499]]起被包含在OpenWrt中。
 +
 +===== ubus命令行工具 =====
 +
 +''​ubus''​可以和''​ubusd''​服务器交互(和当前所有已经注册的服务). 它对研究和调试注册的命名空间以及编写脚本非常有用。对于调用带参数和返回信息的方法,它使用友好的JSON格式。下面是它的命令说明。
 +==== list ====
 +
 +缺省列出所有通过RPC服务器注册的命名空间:​
 +
 +<​code>​root@uplink:​~#​ ubus list
 +network
 +network.device
 +network.interface.lan
 +network.interface.loopback
 +network.interface.wan
 +root@uplink:​~#</​code>​
 +
 +如果调用时包含参数''​-v'',​将会显示指定命名空间更多方法参数等信息:​
 +
 +<​code>​root@uplink:​~#​ ubus -v list network.interface.lan
 +'​network.interface.lan'​ @099f0c8b
 + "​up":​ {  }
 + "​down":​ {  }
 + "​status":​ {  }
 + "​prepare":​ {  }
 + "​add_device":​ { "​name":​ "​String"​ }
 + "​remove_device":​ { "​name":​ "​String"​ }
 + "​notify_proto":​ {  }
 + "​remove":​ {  }
 + "​set_data":​ {  }
 +root@uplink:​~#</​code>​
 +
 +==== call ====
 +
 +调用指定命名空间中指定的方法,并且通过消息传递给它:​
 +
 +<​code>​root@uplink:​~#​ ubus call network.interface.wan status
 +{
 + "​up":​ true,
 + "​pending":​ false,
 + "​available":​ true,
 + "​autostart":​ true,
 + "​uptime":​ 86017,
 + "​l3_device":​ "​eth1",​
 + "​device":​ "​eth1",​
 + "​address":​ [
 + {
 + "​address":​ "​178.25.65.236",​
 + "​mask":​ 21
 + }
 + ],
 + "​route":​ [
 + {
 + "​target":​ "​0.0.0.0",​
 + "​mask":​ 0,
 + "​nexthop":​ "​178.25.71.254"​
 + }
 + ],
 + "​data":​ {
 +
 + }
 +}
 +root@uplink:​~#</​code>​
 +
 +消息参数必须是有效的JSON字符串,并且携带函数所要求的键及值:​
 +
 +<​code>​root@uplink:​~#​ ubus call network.device status '{ "​name":​ "​eth0"​ }'
 +{
 + "​type":​ "​Network device",​
 + "​up":​ true,
 + "​link":​ true,
 + "​mtu":​ 1500,
 + "​macaddr":​ "​c6:​3d:​c7:​90:​aa:​da",​
 + "​txqueuelen":​ 1000,
 + "​statistics":​ {
 + "​collisions":​ 0,
 + "​rx_frame_errors":​ 0,
 + "​tx_compressed":​ 0,
 + "​multicast":​ 0,
 + "​rx_length_errors":​ 0,
 + "​tx_dropped":​ 0,
 + "​rx_bytes":​ 0,
 + "​rx_missed_errors":​ 0,
 + "​tx_errors":​ 0,
 + "​rx_compressed":​ 0,
 + "​rx_over_errors":​ 0,
 + "​tx_fifo_errors":​ 0,
 + "​rx_crc_errors":​ 0,
 + "​rx_packets":​ 0,
 + "​tx_heartbeat_errors":​ 0,
 + "​rx_dropped":​ 0,
 + "​tx_aborted_errors":​ 0,
 + "​tx_packets":​ 184546,
 + "​rx_errors":​ 0,
 + "​tx_bytes":​ 17409452,
 + "​tx_window_errors":​ 0,
 + "​rx_fifo_errors":​ 0,
 + "​tx_carrier_errors":​ 0
 + }
 +}
 +root@uplink:​~#</​code>​
 +
 +==== listen ====
 +
 +设置一个监听socket并观察进入的事件:​
 +
 +<​code>​root@uplink:​~#​ ubus listen &
 +root@uplink:​~#​ ubus call network.interface.wan down
 +{ "​network.interface":​ { "​action":​ "​ifdown",​ "​interface":​ "​wan"​ } }
 +root@uplink:​~#​ ubus call network.interface.wan up
 +{ "​network.interface":​ { "​action":​ "​ifup",​ "​interface":​ "​wan"​ } }
 +{ "​network.interface":​ { "​action":​ "​ifdown",​ "​interface":​ "​he"​ } }
 +{ "​network.interface":​ { "​action":​ "​ifdown",​ "​interface":​ "​v6"​ } }
 +{ "​network.interface":​ { "​action":​ "​ifup",​ "​interface":​ "​he"​ } }
 +{ "​network.interface":​ { "​action":​ "​ifup",​ "​interface":​ "​v6"​ } }
 +root@uplink:​~#​ </​code>​
 +
 +==== send ====
 +
 +发送一个事件提醒:​
 +
 +<​code>​root@uplink:​~#​ ubus listen &
 +root@uplink:​~#​ ubus send foo '{ "​bar":​ "​baz"​ }'
 +{ "​foo":​ { "​bar":​ "​baz"​ } }
 +root@uplink:​~#​ </​code>​
 +
 +===== 通过HTTP访问ubus =====
 +
 +这里有个叫做''​uhttpd-mod-ubus''​的''​uhttpd''​插件允许通过HTTP协议来调用ubus,请求必须通过POST方法发送''/​ubus''​(除非有修改)URL请求。这个接口使用了[[http://​www.jsonrpc.org/​specification|jsonrpc v2.0]]这里有几个步骤需要你去了解。 (Documentation written while using BarrierBreaker r40831, ymmv)
 +
 +==== ACL访问控制列表 ====
 +通过ssh登陆后,你可直接访问,并且拥有ubus的所有权限。但是无论何时,当你在uhttpd通过''/​ubus''​访问ubus时,uhttpd runs "ubus call session access '{ ubus-rpc-session,​ requested-object,​ requested-method }' and whoever is providing the ubus session.* namespace is in charge of implementing the ACL.  This happens to be ''​rpcd''​ at the moment, with the http-json interface, for friendly operation with browser code, but this is just one possible implmementation. ​ Because we're using rpcd to implement the ACLs at this time, this allows/​requires (depending on your point of view) ACLs to be configured in ''/​usr/​share/​rpcd/​acl.d/​*.json''​. ​ The __names__ of the files in ''/​usr/​share/​rpcd/​acl.d/​*.json''​ don't matter, but the top level keys define roles. ​ The default acl, listed below, __only__ defines the login methods, so you can login, but you still wouldn'​t be able to do anything.
 +<​code>​
 +{
 +        "​unauthenticated":​ {
 +                "​description":​ "​Access controls for unauthenticated requests",​
 +                "​read":​ {
 +                        "​ubus":​ {
 +                                "​session":​ [ "​access",​ "​login"​ ]
 +                        }
 +                }
 +        }
 +}
 +</​code>​
 +
 +An example of a complicated ACL, allowing quite fine grained access to different ubus modules and methods is [[http://​git.openwrt.org/?​p=project/​luci2/​ui.git;​a=blob;​f=luci2/​share/​acl.d/​luci2.json|available in the Luci2 project]]
 +
 +An example of a "​security is for suckers"​ config, where a "​superuser"​ ACL group is defined, allowing unrestricted access to everything, is shown below. (This illustrates the usage of '​*'​ definitions in the ACLs, but keep reading for better examples) ​ Placing this file in ''/​usr/​share/​rpcd/​acl.d/​superuser.json''​ will help you move forward to the next steps.
 +
 +<​code>​
 +{
 +        "​superuser":​ {
 +                "​description":​ "Super user access role",
 +                "​read":​ {
 +                        "​ubus":​ {
 +                                "​*":​ [ "​*"​ ]
 +                        },
 +                        "​uci":​ [ "​*"​ ]
 +                },
 +                "​write":​ {
 +                        "​ubus":​ {
 +                                "​*":​ [ "​*"​ ]
 +                        },
 +                        "​uci":​ [ "​*"​ ]
 +                }
 +        }
 +}
 +</​code>​
 +
 +Below is an example of an ACL definition that only allows access to some specific ubus modules, rather than unrestricted access to everything.
 +<​code>​
 +{
 +        "​lesssuperuser":​ {
 +                "​description":​ "not quite as super user",
 +                "​read":​ {
 +                        "​ubus":​ {
 +                                "​file":​ [ "​*"​ ],
 +                                "​log":​ [ "​*"​ ],
 +                                "​service":​ [ "​*"​ ],
 +                        },
 +                },
 +                "​write":​ {
 +                        "​ubus":​ {
 +                                "​file":​ [ "​*"​ ],
 +                                "​log":​ [ "​*"​ ],
 +                                "​service":​ [ "​*"​ ],
 +                        },
 +                }
 +        }
 +}
 +</​code>​
 +
 +Note: Before we leave this section, you may have noticed that there'​s both a "​ubus"​ and a "​uci"​ section, even though ubus has a uci method. ​ The uci: scope is used for the uci api provided by rpcd
 +to allow defining per-file permissions because using the ubus scope you can only say "uci set" is allowed or not allowed but not specify that it is allowed to e.g. modify /e/c/system but not                     /​e/​c/​network ​ If your application/​ACL doesn'​t need UCI access, you can just leave out the UCI section altogether.
 +
 +
 +==== Authentication ====
 +Now that we have an ACL that allows operations beyond just logging in, we can actually try this out.  As mentioned, ''​rpcd''​ is handling this, so you need an entry in ''/​etc/​config/​rpcd''​
 +<​code>​
 +config login
 + option username '​root'​
 + option password '​$p$root'​
 + list read '​*'​
 + list write '​*'​
 +</​code>​
 +The ''​$p''​ magic means to look in ''/​etc/​shadow''​ and the ''​$root''​ part means to use the password for the root user in that file.  The list of read and write sections, those map acl roles to user accounts. ​ You can also use ''​$1$<​hash>''​which is a "​crypt()"​ hash, using SHA1, exactly as used in /​etc/​shadow. ​ You can generate these with, eg, "​uhhtpd -m secret"​
 +
 +
 +To login and receive a session id:
 +<​code>​
 +$ curl -d '{ "​jsonrpc":​ "​2.0",​ "​id":​ 1, "​method":​ "​call",​ "​params":​ [ "​00000000000000000000000000000000",​ "​session",​ "​login",​ { "​username":​ "​root",​ "​password":​ "​secret" ​ } ] }' ​ http://​your.server.ip/​ubus
 +
 +{"​jsonrpc":"​2.0","​id":​1,"​result":​[0,​{"​ubus_rpc_session":"​c1ed6c7b025d0caca723a816fa61b668","​timeout":​300,"​expires":​299,"​acls":​{"​access-group":​{"​superuser":​["​read","​write"​],"​unauthenticated":​["​read"​]},"​ubus":​{"​*":​["​*"​],"​session":​["​access","​login"​]},"​uci":​{"​*":​["​read","​write"​]}},"​data":​{"​username":"​root"​}}]}
 +</​code>​
 +
 +The sessionid "​00000000000000000000000000000000"​ (32 zeros) is a special null-session which just has enough access rights for the session.login ubus call.  A session has a timeout, that is specified when you login, but has a default. ​ You can request a longer timeout in your initial login call, with a "​timeout"​ key in the login parameters section.
 +
 +
 +If you ever receive a response like, ''​{"​jsonrpc":"​2.0","​id":​1,"​result":​[6]}''​ That is a valid jsonrpc response, 6 is the ubus code for UBUS_STATUS_PERMISSION_DENIED ​ (you'​ll get this if you try and login before setting up the "​superuser"​ file, or any file that gives you anymore rights than just being allowed to attempt logins.
 +
 +To list all active sessions, try ''​ubus call session list''​
 +
 +==== Session management ====
 +
 +A session is automatically renewned on every use.  There are plans to use these sessions even for luci1, but at present, if you use this interface in a luci1 environment,​ you'll need to manage sessions yourself.
 +
 +
 +==== Actually making calls ====
 +
 +Now that you have a ''​ubus_rpc_session''​ you can make calls, based on your ACLs and the available ubus services. ​ ''​ubus list -v''​ is your primary documentation on what can be done, but see the rest of this page for more information. ​ For example, ''​ubus list file -v''​ returns ​
 +
 +<​code>​
 +'​file'​ @24a6bd4a
 + "​read":​{"​path":"​String","​data":"​String"​}
 + "​write":​{"​path":"​String","​data":"​String"​}
 + "​list":​{"​path":"​String","​data":"​String"​}
 + "​stat":​{"​path":"​String","​data":"​String"​}
 + "​exec":​{"​command":"​String","​params":"​Array","​env":"​Table"​}
 +</​code>​
 +
 +The json container format is:   
 +
 +<​code>​
 +{ "​jsonrpc":​ "​2.0",​
 +  "​id":​ <​unique-id-to-identify-request>, ​
 +  "​method":​ "​call",​
 +  "​params":​ [
 +             <​ubus_rpc_session>,​ <​ubus_object>,​ <​ubus_method>, ​
 +             { <​ubus_arguments>​ }
 +            ]
 +}
 +</​code>​
 +The "​id"​ key is merely echo'​ed by the server, so it needs not be strictly unique, it's mainly intended for client software to easily correlate responses to previously made requests. It's type is either a string or a number, so it can be an sha1 hash, md5 sum, sequence counter, unix timestamp, ....
 +
 +An example request to read a file would be: 
 +<​code>​
 +$ curl -d '{ "​jsonrpc":​ "​2.0",​ "​id":​ 1, "​method":​ "​call",​ "​params":​ [ "​7cba69a942c0e9db1eb7982cd91f3a48",​ "​file",​ "​read",​ { "​path":​ "/​tmp/​hello.karl"​ } ] }' ​ http://​eg-134867.local/​ubus
 +{"​jsonrpc":"​2.0","​id":​1,"​result":​[0,​{"​data":"​this is the contents of a file\n"​}]}
 +</​code>​
 +
 +
 +
 +===== Lua module for ubus =====
 +
 +This is even possible to use ''​ubus''​ in ''​lua''​ scripts. Of course it's not possible to use native libraries directly in ''​lua'',​ so an extra module has been created. It's simply called ''​ubus''​ and is a simple interface between ''​lua''​ scripts and the ''​ubus''​ (it uses ''​libubus''​ internally).
 +
 +==== Load module ====
 +
 +<​code>​require "​ubus"</​code>​
 +
 +==== Establish connection ====
 +
 +<​code>​local conn = ubus.connect()
 +if not conn then
 +    error("​Failed to connect to ubusd"​)
 +end</​code>​
 +
 +==== Iterate all namespaces and procedures ====
 +
 +<​code>​local namespaces = conn:​objects()
 +for i, n in ipairs(namespaces) do
 +    print("​namespace="​ .. n)
 +    local signatures = conn:​signatures(n)
 +    for p, s in pairs(signatures) do
 +        print("​\tprocedure="​ .. p)
 +        for k, v in pairs(s) do
 +            print("​\t\tattribute="​ .. k .. " type=" .. v)
 +        end
 +    end
 +end</​code>​
 +
 +==== Call a procedure ====
 +
 +<​code>​local status = conn:​call("​network.device",​ "​status",​ { name = "​eth0"​ })
 +for k, v in pairs(status) do
 +    print("​key="​ .. k .. " value="​ .. tostring(v))
 +end</​code>​
 +
 +==== Close connection ====
 +
 +<​code>​conn:​close()</​code>​
 +
 +===== Namespaces & Procedures =====
 +
 +As explained earlier, there can be many different daemons (services) registered in ''​ubus''​. Below you will find a list of the most common projects with namespaces, paths and procedures they provide.
 +
 +==== netifd ====
 +
 +[[http://​nbd.name/​gitweb.cgi?​p=luci2/​netifd.git;​a=blob;​f=DESIGN|Design of netifd]]
 +
 +^ Path ^ Procedure ^ Signature ^ Description ^
 +| ''​network''​ | ''​restart''​ | ''​{ }''​ | Restart the network, reconfigures all interfaces |
 +| ''​network''​ | ''​reload''​ | ''​{ }''​ | Reload the network, reconfigure as needed |
 +| ''​network.device''​ | ''​status''​ | ''​{ "​name":​ "//​ifname//"​ }''​ | Dump status of given network device ''//​ifname//''​ |
 +| ''​network.device''​ | ''​set_state''​ | ''​{ "​name":​ "//​ifname//",​ "​defer":​ //​deferred//​ }''​ | Defer or ready the given network device ''//​ifname//'',​ depending on the boolean value //​deferred//​ |
 +| ''​network.interface.//​name//''​ | ''​up''​ | ''​{ }''​ | Bring interface ''//​name//''​ up |
 +| ''​network.interface.//​name//''​ | ''​down''​ | ''​{ }''​ | Bring interface ''//​name//''​ down |
 +| ''​network.interface.//​name//''​ | ''​status''​ | ''​{ }''​ | Dump status of interface ''//​name//''​ |
 +| ''​network.interface.//​name//''​ | ''​prepare''​ | ''​{ }''​ | Prepare setup of interface ''//​name//''​ |
 +| ''​network.interface.//​name//''​ | ''​add_device''​ | ''​{ "​name":​ "//​ifname//"​ }''​ | Add network device ''//​ifname//''​ to interface ''//​name//''​ (e.g. for bridges: ''​brctl addif br-//name// //​ifname//''​) |
 +| ''​network.interface.//​name//''​ | ''​remove_device''​ | ''​{ "​name":​ "//​ifname//"​ }''​ | Remove network device ''//​ifname//''​ from interface ''//​name//''​ (e.g. for bridges: ''​brctl delif br-//name// //​ifname//''​) |
 +| ''​network.interface.//​name//''​ | ''​remove''​ | ''​{ }''​ | Remove interface ''//​name//''​ (?) |
 +
 +==== rpcd ====
 +
 +Project ''​rpcd''​ is set of small plugins providing sets of ''​ubus''​ procedures in separated namespaces. These plugins are not strictly related to any particular software (like ''​netifd''​ or ''​dhcp''​) so it wasn't worth it to implement them as separated projects.
 +
 +^ Path ^ Procedure ^ Signature ^ Description ^
 +| ''​file''​ | ''​read''​ | ? | ? |
 +| ''​file''​ | ''​write''​ | ? | ? |
 +| ''​file''​ | ''​list''​ | ? | ? |
 +| ''​file''​ | ''​stat''​ | ? | ? |
 +| ''​file''​ | ''​exec''​ | ? | ? |
 +\\
 +^ Path ^ Procedure ^ Signature ^ Description ^
 +| ''​iwinfo''​ | ''​info''​ | ? | ? |
 +| ''​iwinfo''​ | ''​scan''​ | ? | ? |
 +| ''​iwinfo''​ | ''​assoclist''​ | ? | ? |
 +| ''​iwinfo''​ | ''​freqlist''​ | ? | ? |
 +| ''​iwinfo''​ | ''​txpowerlist''​ | ? | ? |
 +| ''​iwinfo''​ | ''​countrylist''​ | ? | ? |
 +
 +^ Path ^ Procedure ^ Signature ^ Description ^
 +| ''​session''​ | ''​list''​ | ''​{ "​session":​ "//​sid//"​ }''​ | Dump session specified by ''//​sid//'',​ if no ID is given, dump all sessions |
 +| ''​session''​ | ''​create''​ | ''​{ "​timeout":​ //timeout// }''​ | Create a new session and return its ID, set the session timeout to ''//​timeout//''​ |
 +| ''​session''​ | ''​grant''​ | ''​{ "​session":​ "//​sid//",​
 +  "​objects":​ [ [ "​path",​ "​func"​ ], ... ] }''​ | Within the session identified by ''//​sid//''​ grant access to all specified procedures ''//​func//''​ in the namespace ''//​path//''​ listed in the ''//​objects//''​ array |
 +| ''​session''​ | ''​revoke''​ | ''​{ "​session":​ "//​sid//",​
 +  "​objects":​ [ [ "​path",​ "​func"​ ], ... ] }''​ | Within the session identified by ''//​sid//''​ revoke access to all specified procedures ''//​func//''​ in the namespace ''//​path//''​ listed in the ''//​objects//''​ array. If ''//​objects//''​ is unset, revoke all access |
 +| ''​session''​ | ''​access''​ | ? | ? |
 +| ''​session''​ | ''​set''​ | ''​{ "​session":​ "//​sid//",​
 +  "​values":​ { "//​key//":​ //value//, ... } }''​ | Within the session identified by ''//​sid//''​ store the given arbitrary values under their corresponding keys specified in the ''//​values//''​ object |
 +| ''​session''​ | ''​get''​ | ''​{ "​session":​ "//​sid//",​
 +  "​keys":​ [ "//​key//",​ ... ] }''​ | Within the session identified by ''//​sid//''​ retrieve all values associated with the given keys listed in the ''//​keys//''​ array. If the key array is unset, dump all key/value pairs |
 +| ''​session''​ | ''​unset''​ | ''​{ "​session":​ "//​sid//",​
 +  "​keys":​ [ "//​key//",​ ... ] }''​ | Within the session identified by ''//​sid//''​ unset all keys listed in the ''//​keys//''​ array. If the key list is unset, clear all keys |
 +| ''​session''​ | ''​destroy''​ | ''​{ "​session":​ "//​sid//"​ }''​ | Terminate the session identified by the given ID ''//​sid//''​ |
 +| ''​session''​ | ''​login''​ | ? | ? |
 +
 +^ Path ^ Procedure ^ Signature ^ Description ^
 +| ''​uci''​ | ''​get''​ | ''​{ "​package":​ "//​package//",​
 +  "​section":​ "//​sname//",​
 +  "​type": ​   "//​type//",​
 +  "​option": ​ "//​oname//"​ }''​ | <​WRAP>​Return the requested uci value(s), all arguments are optional.
 +  - When called without argument or with empty object: return an array of package names in the ''​packages''​ field
 +  - When called with ''//​package//''​ set: return an object containing all sections containing all options in a field named after the package
 +  - When called with ''//​package//''​ and ''//​type//''​ set: return an object containing all sections of type ''//​type//''​ containing all options in a field named after the package
 +  - When called with ''//​package//''​ and ''//​sname//''​ set: return an object containing all options of the section in a field named after the section
 +  - When called with ''//​package//''​ and ''//​type//''​ and ''//​oname//''​ set: return an object containing the value of each option named ''//​oname//''​ within a section of type ''//​type//''​ in a field named after the matched section
 +  - When called with ''//​package//''​ and ''//​sname//''​ and ''//​oname//''​ set: return the result string in a field named ''//​oname//''​ in case of options or an array of result strings in a field named ''//​oname//''​ in case of list options
 +
 +Return messages:
 +  - ''​{ "​packages":​ [ "​package1",​ ... ] }''​
 +  - ''​{ "//​package//":​ {
 +  "​sname1":​ {
 +    "​.type": ​  "​type1",​
 +    "​option1":​ "​value1",​
 +    "​option2":​ [ "​value2.1",​ ... ],
 +    ... }, ... } }''​
 +  - ''​{ "//​package//":​ {
 +  "​sname1":​ {
 +    "​.type": ​  "//​type//",​
 +    "​option1":​ "​value1",​
 +    "​option2":​ [ "​value2.1",​ ... ],
 +    ... }, ... } }''​
 +  - ''​{ "//​sname//":​ {
 +    "​.type": ​  "​type",​
 +    "​option1":​ "​value1",​
 +    "​option2":​ [ "​value2.1",​ ... ],
 +    ... } }''​
 +  - ''​{ "​sectionname1":​ "​value1",​
 +  "​sectionname2":​ [ "​value2.1",​ ... ],
 +  ... }''​
 +  -
 +    - ''​{ "//​oname//":​ "​value1"​ }''​
 +    - ''​{ "//​oname//":​ [ "​value1.1",​ ... ] }''​
 +</​WRAP>​ |
 +| ''​uci''​ | ''​set''​ | ''​{ "​package":​ "//​package//",​
 +  "​section":​ "//​sname//",​
 +  "​option": ​ "//​oname//",​
 +  "​value": ​  "//​value//"​ }''​ | <​WRAP>​Set the given value(s), the option argument is optional.
 +  - When called with ''//​package//''​ and ''//​sname//''​ and ''//​value//''​ set: add a new section ''//​sname//''​ in ''//​package//''​ and set it to the type given in ''//​value//''​
 +  - When called with ''//​package//''​ and ''//​sname//'',​ ''//​oname//''​ and ''//​value//''​ set:
 +    - If ''//​value//''​ is of type array: set strings in the ''​value''​ array as list option ''//​oname//''​
 +    - If ''//​value//''​ is of type string: set ''//​value//''​ as normal option ''//​oname//''​
 +The call does not produce any data, instead it returns with the following status codes:
 +  - If there already is a section called ''//​sname//'':​ ''​UBUS_STATUS_INVALID_ARGUMENT''​ else: ''​UBUS_STATUS_OK''​
 +  - If there is no section ''//​sname//''​ or if ''//​value//''​ is neither a string nor an array: ''​UBUS_STATUS_INVALID_ARGUMENT''​ else: ''​UBUS_STATUS_OK''​
 +</​WRAP>​ |
 +| ''​uci''​ | ''​add''​ | ''​{ "​package":​ "//​package//",​
 +  "​type": ​   "//​type//"​ }''​ | <​WRAP>​Add new anonymous section of given type.
 +  - When called with ''//​package//''​ and ''//​type//''​ set: Add a new anonymous section of type ''//​type//''​.
 +
 +Return message:
 +  - ''​{ "​section":​ "​sectionname"​ }''​
 +</​WRAP>​ |
 +| ''​uci''​ | ''​delete''​ | ''​{ "​package":​ "//​package//",​
 +  "​section":​ "//​sname//",​
 +  "​type": ​   "//​type//",​
 +  "​option": ​ "//​oname//"​ }''​ | <​WRAP>​Delete the given value(s) or section(s), the option and type arguments are optional.
 +  - When called with ''//​package//''​ and ''//​type//''​ set: delete all sections of type ''//​type//''​ in ''//​package//''​
 +  - When called with ''//​package//''​ and ''//​sname//''​ set: delete the section named ''//​sname//''​ in ''//​package//''​
 +  - When called with ''//​package//'',​ ''//​type//''​ and ''//​oname//''​ set: delete the option named ''//​oname//''​ within each section of type ''//​type//''​ in ''//​package//''​
 +  - When called with ''//​package//'',​ ''//​sname//''​ and ''//​oname//''​ set: delete the option named ''//​oname//''​ in section ''//​sname//''​ of ''//​package//''​
 +
 +The call does not result in any data, instead it returns the following status codes:
 +  - If no section of type ''//​type//''​ was found: ''​UBUS_STATUS_NOT_FOUND''​ else: ''​UBUS_STATUS_OK''​
 +  - If no section named ''//​sname//''​ was found: ''​UBUS_STATUS_NOT_FOUND''​ else: ''​UBUS_STATUS_OK''​
 +  - If no options named ''//​oname//''​ within sections of type ''//​type//''​ where found: ''​UBUS_STATUS_NOT_FOUND''​ else: ''​UBUS_STATUS_OK''​
 +  - If the option named ''//​oname//''​ within named section ''//​sname//''​ was not found: ''​UBUS_STATUS_NOT_FOUND''​ else: ''​UBUS_STATUS_OK''​
 +</​WRAP>​ |
 +
 +===== 代码片段 =====
 +
 +==== Check if Link is up using devstatus and Json ====
 +
 +<​code>​
 +#!/bin/sh
 +
 +. /​usr/​share/​libubox/​jshn.sh
 +
 +WANDEV="​$(uci get network.wan.ifname)"​
 +
 +json_load "​$(devstatus $WANDEV)"​
 +
 +json_get_var var1 speed
 +json_get_var var2 link
 +
 +echo "​Speed:​ $var1"
 +echo "Link: $var2"
 +</​code>​
zh-cn/doc/techref/ubus.1361526558.txt.bz2 · Last modified: 2013/02/22 10:49 (external edit)