ubus (OpenWrt micro bus architecture)
See also:What is UBUS?
To provideInter-process communication between various daemons and applications in OpenWrt a project calledubus
has been developed.It consists of several parts including daemon, library and some extra helpers.
The heart of this project is theubusd
daemon.It provides an interface for other daemons to register themselves as well as sending messages.For those curious, this interface is implemented using Unix sockets and it usesTLV (type-length-value) messages.
To simplify development of software usingubus
(connecting to it) a library calledlibubus
has been created.
Every daemon registers a set of paths under a specific namespace.Every path can provide multiple procedures with any number of arguments.Procedures can reply with a message.
The code is published underLGPL 2.1 license and can be found via git athttps://git.openwrt.org/project/ubus.git.
Command-line ubus tool
Theubus
command line tool allows to interact with theubusd
server (with all currently registered services).It's useful for investigating/debugging registered namespaces as well as writing shell scripts.For calling procedures with parameters and returning responses it uses the user-friendly JSON format.Below is an explanation of its commands.
Help output
# ubusUsage: ubus [<options>] <command> [arguments...]Options: -s <socket>:Set the unix domain socket to connect to -t <timeout>:Set the timeout (in seconds) for a command to complete -S:Use simplified output (for scripts) -v:More verbose output -m <type>:(for monitor): include a specific message type(can be used more than once) -M <r|t>(for monitor): only capture received or transmitted trafficCommands: - list [<path>]List objects - call <path> <method> [<message>]Call an object method - listen [<path>...]Listen for events - send <type> [<message>]Send an event - wait_for <object> [<object>...]Wait for multiple objects to appear on ubus - monitorMonitor ubus traffic
list
To find out which services are currently running on the bus simply use theubus list
command.This will show a complete list of all namespaces registered with the RPC server:
# ubus listdhcpdnsmasqfileiwinfologluciluci-rpcnetworknetwork.devicenetwork.interfacenetwork.interface.lannetwork.interface.loopbacknetwork.interface.wannetwork.interface.wan6network.rrdnsnetwork.wirelessservicesessionsystemuci
You can filter the list by specifying a service path:
# ubus list network.interface.*network.interface.lannetwork.interface.loopbacknetwork.interface.wannetwork.interface.wan6
To find out which procedures/methods and their argument signatures a specific service provides add-v
in addition to the namespace path:
# 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": { }
call
Calls a given procedure within a given namespace and optionally pass arguments to it:
# 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":{ }}
The arguments must be a valid JSON string, with keys and values set according to the function signature:
# 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}}
listen
Set up a listening socket and observe incoming events:
# ubus listen &# ubus call network.interface.wan down{"network.interface":{"action":"ifdown","interface":"wan"}}# 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"}}
send
Send an event notification:
# ubus listen &# ubus send foo '{ "bar": "baz" }'{"foo":{"bar":"baz"}}
Access to ubus over HTTP
There is anuhttpd
plugin calleduhttpd-mod-ubus
that allowsubus
calls usingHTTP protocol.For example this is used inLuCI2.Requests have to be sent to the/ubus
URL (unless changed byubus_prefix
option) using thePOST
method.This interface usesjsonrpc v2.0 There are a few steps that you will need to understand.
If uhttpd-mod-ubus is installed, uhttpd automatically configures it and enables it, by default at /ubus, but you canconfigure thisAlso if you want to use the/ubus
directly from a web client you need to specifyubus_cors=1
option.
If you are using Nginx then you may trynginx-ubus-module.For other web servers like Lighttpd you may use theubus as CGI.
See thePostman collection with some examples of calling UBUS overHTTP
ACLs
ubus call session access'{ "ubus_rpc_session": "xxxxx", "object": "requested-object", "function": "requested-method" }'
This happens to berpcd
at the moment, with thehttp-json
interface, for friendly operation with browser code, but this is just one possible implementation.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
.Thenames of the files in/usr/share/rpcd/acl.d/*.json
don't matter, but the top level keys define roles.The defaultACL, listed below,only defines the login methods, so you can log in, but you still wouldn't be able to do anything.
{ "unauthenticated":{ "description":"Access controls for unauthenticated requests", "read":{ "ubus":{ "session":["access","login"]}}}}
An example of a complicatedACL, allowing quite fine grained access to different ubus modules and methods isavailable in the Luci2 project
An example of a “security is for suckers” config, where asuperuser
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.
{ "superuser":{ "description":"Super user access role", "read":{ "ubus":{ "*":["*"]}, "uci":["*"], "file":{ "*":["*"]}}, "write":{ "ubus":{ "*":["*"]}, "uci":["*"], "file":{ "*":["*"]}, "cgi-io":["*"]}}}
Below is an example of anACL definition that only allows access to some specific ubus modules, rather than unrestricted access to everything.
{ "lesssuperuser":{ "description":"not quite as super user", "read":{ "ubus":{ "file":["*"], "log":["*"], "service":["*"],},}, "write":{ "ubus":{ "file":["*"], "log":["*"], "service":["*"],},}}}
Note: Before we leave this section, you may have noticed that there's both aubus
and auci
section, even though ubus has a uci method.Theuci
scope is used for theuci api provided by rpcd to allow defining per-file permissions because using the ubus scope you can only sayuci set
is allowed or not, but not specify that it is allowed to e.g. modify/etc/config/system
but not/etc/config/network
.If your application/ACL doesn't need UCI access, you can just leave out the UCI section altogether.
Authentication
Now that we have anACL 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
configloginoption username'root'option password'$p$root'listread'*'listwrite'*' configlogin option username'blaer' option password'$p$blaer' listread lesssuperuser listwrite lesssuperuser
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 mapACL roles to user accounts.Solist read *
means the read credential of any group listed in the merged ACLs.Write implies read.
You can also use$1$<hash>
which is acrypt hash, using SHA1, exactly as used in/etc/shadow
.You can generate these withuhttpd -m secret
.
Session management
To login and receive a session id:
$ curl-H'Content-Type: application/json'-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"}}]}
The session id00000000000000000000000000000000
(32 zeros) is a special null-session which only has the rights from theunauthenticated
access group, only enabling thesession.login
ubus call.A session has a timeout that can be specified when you login, otherwise it defaults to 5 minutes.
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 forUBUS_STATUS_PERMISSION_DENIED
(you'll get this if you try and login before setting up thesuperuser
file, or any file that gives you more rights than just being allowed to attempt logins).
To list all active sessions, tryubus call session list
The session timeout is automatically reset 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 aubus_rpc_session
you can make calls, based on your ACLs and the available ubus services.ubus -v list
is your primary documentation on what can be done, but see the rest of this page for more information.For example,ubus -v list file
returns
'file'@ff0a2c92"read":{"path":"String","base64":"Boolean"}"write":{"path":"String","data":"String","append":"Boolean","mode":"Integer","base64":"Boolean"}"list":{"path":"String"}"stat":{"path":"String"}"md5":{"path":"String"}"exec":{"command":"String","params":"Array","env":"Table"}
The RPC-JSON container format is:
{"jsonrpc":"2.0","id":<unique-id-to-identify-request>,"method":"call","params":[<ubus_rpc_session>,<ubus_object>,<ubus_method>,{<ubus_arguments>}]}
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.Its type is either a string or a number, so it can be an UUID, sha1 hash, md5 sum, sequence counter, unix timestamp, etc.
An example request to read a file/etc/board.json
which contains device info would be:
$ curl-H'Content-Type: application/json'-d'{ "jsonrpc": "2.0", "id": 1, "method": "call", "params": [ "c1ed6c7b025d0caca723a816fa61b668", "file", "read", { "path": "/etc/board.json" } ] }' http://your.server.ip/ubus{"jsonrpc":"2.0","id":1,"result":[0,{"data":"{\n\t\"model\": {\n\t\t\"id\":\"innotek-gmbh-virtualbox\",\n\t\t\"name\":\"innotek GmbH VirtualBox\"\n\t},\n\t\"network\": {\n\t\t\"lan\": {\n\t\t\t\"ifname\":\"eth0\",\n\t\t\t\"protocol\":\"static\"\n\t\t}\n\t}\n}\n"}]}
Here the first paramc1ed6c7b025d0caca723a816fa61b668
is theubus_rpc_session
received during the login call.If you received a response like{“jsonrpc”:“2.0”,“id”:1,“error”:{“code”:-32002,“message”:“Access denied”}}
then probably your session token was expired and you need to request a new one.
To beautify output you can usejq utility:
curl-s-H'Content-Type: application/json'-d'{ "jsonrpc": "2.0", "id": 1, "method": "call", "params": [ "c1ed6c7b025d0caca723a816fa61b668", "file", "read", { "path": "/etc/board.json" } ] }' http://your.server.ip/ubus| jq{"jsonrpc":"2.0","id":1,"result":[0,{"data":"{\n\t\"model\": {\n\t\t\"id\":\"innotek-gmbh-virtualbox\",\n\t\t\"name\":\"innotek GmbH VirtualBox\"\n\t},\n\t\"network\": {\n\t\t\"lan\": {\n\t\t\t\"ifname\":\"eth0\",\n\t\t\t\"protocol\":\"static\"\n\t\t}\n\t}\n}\n"}]}
In the response JSON document in theresult[1].data
field contains escaped JSON with themodel
andnetwork
objects.To fetch the concrete model name you can parse the response with thejq program:
curl -s -H 'Content-Type: application/json' -d '{ "jsonrpc": "2.0", "id": 1, "method": "call", "params": [ "c1ed6c7b025d0caca723a816fa61b668", "file", "read", { "path": "/etc/board.json" } ] }' http://your.server.ip/ubus | jq -r '.result[1].data' | jq .model.name"innotek GmbH VirtualBox"
As you can see in the example the router model is in fact just a OpenWrt runned onvirtual machine in VirtualBox.
Lua module for ubus
This is even possible to useubus
inlua
scripts.Of course it's not possible to use native libraries directly inlua
, so an extra module has been created.It's simply calledubus
and is a simple interface betweenlua
scripts and theubus
(it useslibubus
internally).
-- Load modulerequire"ubus"-- Establish connectionlocal conn= ubus.connect()ifnot connthenerror("Failed to connect to ubusd")end -- Iterate all namespaces and procedureslocal namespaces= conn:objects()for i, ninipairs(namespaces)doprint("namespace=".. n)local signatures= conn:signatures(n)for p, sinpairs(signatures)doprint("\tprocedure=".. p)for k, vinpairs(s)doprint("\t\tattribute=".. k.." type=".. v)endendend -- Call a procedurelocal status= conn:call("network.device","status",{ name="eth0"})for k, vinpairs(status)doprint("key=".. k.." value="..tostring(v))end -- Close connectionconn:close()
Optional arguments toconnect()
are a path to use for sockets (pass nil to use the default) and a per call timeout value (in milliseconds)
local conn= ubus.connect(nil,500)-- default socket path, 500ms per call timeout
Namespaces & Procedures
As explained earlier, there can be many different daemons (services) registered inubus
.Below you will find a list of the most common projects with namespaces, paths and procedures they provide.
Path only contains the first context, e.g. network for network.interface.wan
path | Description | Package |
---|---|---|
dhcp | dhcp server | odhcpd |
file | file | rpcd |
hostapd | acesspoints | wpad/hostapd |
iwinfo | wireless informations | rpcd iwinfo |
log | logging | procd |
mdns | mdns avahi replacement | mdnsd |
network | network | netifd |
service | init/service | procd |
session | Session management | rpcd |
system | system misc | procd |
uci | Unified Configuration Interface | rpcd |
procd
ubus system
Package: procd
Procedure | Signature | Description |
---|---|---|
board | {} | returns board specific information like model and distribution code name, revision |
info | {} | returns real-time information about the system. “uptime”: 20756, “localtime”: 1444142264, “load”: [ 7264, 3040, 3520 ], “memory”: { “total”: 29601792, “free”: 7344128, “shared”: 458752, “buffered”: 2166784 }, “swap”: { “total”: 0, “free”: 0 } |
upgrade | {} | *TODO* |
watchdog | {“frequency”:“Integer”,“timeout”:“Integer”,“stop”:“Boolean”,“magicclose”:“Boolean”} | controls the watchdog. *ubus call system watchdog '{ “stop”: true“}'* only stops the thread triggering the watchdog. The watchdog is still counting down unless a second process is triggering the watchdog unless you enable 'magicclose', then you can manually tickle '/dev/watchdog'. |
signal | {“pid”:“Integer”,”signum“:“Integer”} | send a signal to a process. See man kill |
nandupgrade | {“path”:“String”} | *TODO* |
The values in load are the load averages over 1, 5, and 15 minutes. to get to the familiar values reported by uptime divide these numbers by 65536.0 and round to 2 decimals.
There is a detailedblog post showing how to use, configure and manually take control over hardware watchdog with ubus commands.
Examples
See all methods ofsystem
(to see all methods of all services registered to ubusd:ubus -v list
):
root@OpenWrt:/# ubus -v list system'system' @651f206c "board":{} "info":{} "reboot":{} "watchdog":{"frequency":"Integer","timeout":"Integer","magicclose":"Boolean","stop":"Boolean"} "signal":{"pid":"Integer","signum":"Integer"} "validate_firmware_image":{"path":"String"} "sysupgrade":{"path":"String","force":"Boolean","backup":"String","prefix":"String","command":"String","options":"Table"}
You can now call a remote method and receive a reply. A reply may be a simple integer return code or a more complete reply. Internally the bus uses a blob format, the CLI conveniently converts this to JSON.
root@OpenWrt:/# ubus call system board{ "kernel": "4.9.198", "hostname": "OpenWrt", "system": "Qualcomm Atheros QCA956X ver 1 rev 0", "model": "TP-Link TL-WR1043N\/ND v4", "board_name": "tl-wr1043nd-v4", "release": { "distribution": "OpenWrt", "version": "18.06.5", "revision": "r7897-9d401013fc", "target": "ar71xx\/generic", "description": "OpenWrt 18.06.5 r7897-9d401013fc" }}
You can call a method and pass it some parameters by simply appending a JSON structure to the CLI command.
root@OpenWrt:/# ubus call system signal '{ "pid": 123, "signum": 9 }'root@OpenWrt:/# echo $?0
ubus service
Package: procd
service
used by init scripts as well to register new services
Path | Procedure | Signature | Description |
---|---|---|---|
service | set | {“name”:“String”,“script”:“String”,“instances”:“Table”,“triggers”:“Array”,“validate”:“Array”,“autostart”:“Boolean”,“data”:“Table”} | *TODO* |
service | add | {“name”:“String”,“script”:“String”,“instances”:“Table”,“triggers”:“Array”,“validate”:“Array”,“autostart”:“Boolean”,“data”:“Table”} | *TODO* |
service | list | {“name”:“String”,“verbose”:“Boolean”} | Return a list of all services and their instances. Can be filtered by name |
service | delete | {“name”:“String”,“instance”:“String”} | Delete instance of a service |
service | update_start | {“name”:“String”} | *TODO* |
service | event | {“type”:“String”,“data”:“Table”} | *TODO* |
service | validate | {“package”:“String”,“type”:“String”,“service”:“String”} | *TODO* |
service | get_data | {“name”:“String”,“instance”:“String”,“type”:“String”} | *TODO* |
service | state | {“spawn”:“Boolean”,“name”:“String”} | *TODO* |
netifd
ubus network
Package: netifd
DESIGN document at repo 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 deviceifname or all network devices if none given |
network.device | set_state | { “name”: “ifname”, “defer”:deferred } | Defer or ready the given network deviceifname , depending on the boolean valuedeferred |
network.interface.name | up | { } | Bring interfacename up |
network.interface.name | down | { } | Bring interfacename down |
network.interface.name | status | { } | Dump status of interfacename |
network.interface.name | prepare | { } | Prepare setup of interfacename |
network.interface.name | add_device | { “name”: “ifname” } | Add network deviceifname to interfacename (e.g. for bridges:brctl addif br-nameifname ) |
network.interface.name | remove_device | { “name”: “ifname” } | Remove network deviceifname from interfacename (e.g. for bridges:brctl delif br-nameifname ) |
network.interface.name | remove | { } | Remove interfacename (?) |
network.wireless | status | { } | |
rpcd
Projectrpcd: OpenWrt ubus RPC daemon for backend server is a set of small plugins providing sets ofubus
procedures in separated namespaces.These plugins are not strictly related to any particular software (likenetifd
ordhcp
) so it wasn't worth it to implement them as separated projects.rpcd and the desired plugins must be available or installed via opkg.After installing remember to enable and start the service viaservice rpcd enable
andservice rpcd start
.
ubus file
Package: rpcd
With pluginrpcd-mod-file
enabled:
Path | Procedure | Signature | Description |
---|---|---|---|
file | read | { “path”: “String”, “base64”:Boolean } | Read a file contents. The file path is encoded in Base64 if thebase64 param set to “true” |
file | write | { “path”: “String”, “data”: “String”, “append”:Boolean, “mode”:Integer, “base64”:Boolean } | Write adata to a file bypath . The file path is encoded in Base64 if thebase64 param set to “true”. If theappend param is “true” then file is not overwritten but the new content is added to the end of the file. Themode param if specified represent file permission mode. |
file | list | { “path”: “String” } | ? |
file | stat | { “path”: “String” } | ? |
file | md5 | { “path”: “String” } | ? |
file | exec | { “command”: “String”, “params”: “Array”, “env”: “Table” } | ? |
ubus iwinfo
With pluginrpcd-mod-iwinfo
enabled:
Path | Procedure | Signature | Description |
---|---|---|---|
iwinfo | devices | { } | ? |
iwinfo | info | { “device”: “device” } | ? |
iwinfo | scan | { “device”: “device” } | ? |
iwinfo | assoclist | { “device”: “device”, “mac”: “mac” } | ? |
iwinfo | freqlist | { “device”: “device” } | ? |
iwinfo | txpowerlist | { “device”: “device” } | ? |
iwinfo | countrylist | { “device”: “device” } | ? |
iwinfo | phyname | { “section”: “device” } | ? |
Always included inrpcd
:
ubus session
Path | Procedure | Signature | Description |
---|---|---|---|
session | create | { “timeout”:timeout } | Create a new session and return its ID, set the session timeout totimeout in seconds (set0 for no expire) |
session | list | { “ubus_rpc_session”: “sid” } | Dump session info specified bysid , if no ID is given, list all sessions |
session | grant | { “ubus_rpc_session”: “sid”, “scope”: “scope”, “objects”: [ [ “path”, “func” ], ... ] } | Within the session identified bysid grant access to all specified proceduresfunc in the namespacepath listed in theobjects array |
session | revoke | { “ubus_rpc_session”: “sid”, “scope”: “scope”, “objects”: [ [ “path”, “func” ], ... ] } | Within the session identified bysid revoke access to all specified proceduresfunc in the namespacepath listed in theobjects array. Ifobjects is unset, revoke all access |
session | access | { “ubus_rpc_session”: “sid”, “scope”: “scope”, “object”: “path”, “function”: “function” } | Query whether access to the specifiedfunction in the namespacepath is allowed |
session | set | { “ubus_rpc_session”: “sid”, “values”: { “key”:value, ... } } | Within the session identified bysid store the given arbitrary values under their corresponding keys specified in thevalues object |
session | get | { “ubus_rpc_session”: “sid”, “keys”: [ “key”, ... ] } | Within the session identified bysid retrieve all values associated with the given keys listed in thekeys array. If the key array is unset, dump all key/value pairs |
session | unset | { “ubus_rpc_session”: “sid”, “keys”: [ “key”, ... ] } | Within the session identified bysid unset all keys listed in thekeys array. If the key list is unset, clear all keys |
session | destroy | { “ubus_rpc_session”: “sid” } | Terminate the session identified by the given IDsid |
session | login | { “username”: “username”, “password”: “password”, “timeout”:timeout } | Authenticate with rpcd and create a new session with access rights as specified in the ACLs |
Note: When using ubus overHTTP, settingubus_rpc_session
isn't allowed, it's automatically set to the calling session.
Note: Sessions are stored in memory so they will persist as long asrpcd
is running
login call description
Example of manual session creation
ubus uci
# ubus -v list uci'uci' @4eb774a8"configs":{}"get":{"config":"String","section":"String","option":"String","type":"String","match":"Table","ubus_rpc_session":"String"}"state":{"config":"String","section":"String","option":"String","type":"String","match":"Table","ubus_rpc_session":"String"}"add":{"config":"String","type":"String","name":"String","values":"Table","ubus_rpc_session":"String"}"set":{"config":"String","section":"String","type":"String","match":"Table","values":"Table","ubus_rpc_session":"String"}"delete":{"config":"String","section":"String","type":"String","match":"Table","option":"String","options":"Array","ubus_rpc_session":"String"}"rename":{"config":"String","section":"String","option":"String","name":"String","ubus_rpc_session":"String"}"order":{"config":"String","sections":"Array","ubus_rpc_session":"String"}"changes":{"config":"String","ubus_rpc_session":"String"}"revert":{"config":"String","ubus_rpc_session":"String"}"commit":{"config":"String","ubus_rpc_session":"String"}"apply":{"rollback":"Boolean","timeout":"Integer","ubus_rpc_session":"String"}"confirm":{"ubus_rpc_session":"String"}"rollback":{"ubus_rpc_session":"String"}"reload_config":{}
Path | Procedure | Signature | Description |
---|---|---|---|
uci | configs | { } | List all available configs Example: # ubus call uci configs '{"ubus_rpc_session":"2db687f321a60414e77677bbb5dd6d6f"}'{"configs": ["dhcp","dropbear","firewall","luci","network","radius","rpcd","system","ubootenv","ucitrack","uhttpd","wireless"]} |
uci | get | { “config”: “config”, “section”: “sname”, “type”: “type”, “option”: “oname” } | Return the requested uci value(s), all arguments are optional.
Return messages:
Example: # ubus call uci get '{"ubus_rpc_session":"2db687f321a60414e77677bbb5dd6d6f", "config":"wireless", "section":"wifinet2"}'{"values": {".anonymous": false,".type": "wifi-iface",".name": "wifinet2","device": "radio0","mode": "ap","ssid": "ilwf-guest","encryption": "sae-mixed","key": "XXXX","network": "lan","disabled": "0"} |
uci | state | { “config”: “config”, “section”: “sname”, “type”: “tname”, “option”: “oname” } | |
uci | set | { “config”: “config”, “section”: “sname”, “type”: “tname”, “values”: “array_of_values” } | Set the given value(s), the option argument is optional.
The call does not produce any data, instead it returns with the following status codes:
Example: # ubus call uci set '{"ubus_rpc_session":"2db687f321a60414e77677bbb5dd6d6f", "config":"wireless", "section":"wifinet2", "values":{"disabled":"1"}}'# ubus call uci get '{"ubus_rpc_session":"2db687f321a60414e77677bbb5dd6d6f", "config":"wireless", "section":"wifinet2"}'{"values": {".anonymous": false,".type": "wifi-iface",".name": "wifinet2","device": "radio0","mode": "ap","ssid": "ilwf-guest","encryption": "sae-mixed","key": "XXXXXXXX","network": "lan","disabled": "1"}} |
uci | add | { “config”: “config”, “type”: “type” } | Add new anonymous section of given type.
Return message:
|
uci | delete | { “config”: “config”, “section”: “sname”, “type”: “type”, “option”: “oname” } | Delete the given value(s) or section(s), the option and type arguments are optional.
The call does not result in any data, instead it returns the following status codes:
|
uci | rename | {“config”:“String”,“section”:“String”,“option”:“String”,“name”:“String” } | |
uci | order | {“config”:“String”,“sections”:“Array” } | |
uci | changes | {“config”:“String” } | |
uci | revert | {“config”:“String” } | |
uci | commit | {“config”:“String” } | |
uci | apply | {“rollback”:“Boolean”,“timeout”:“Integer” } | |
uci | confirm | { } | |
uci | rollback | { } | |
uci | reload_config | { } |
What's the difference between ubus vs dbus?
D-Bus is bloated, its CAPI is very annoying to use and requires writing large amounts of boilerplate code.In fact, the pure CAPI is so annoying that its ownAPI documentation states: “If you use this low-levelAPI directly, you're signing up for some pain.”
ubus
is tiny and has the advantage of being easy to use from regular C code, as well as automatically making all exportedAPI functionality also available to shell scripts with no extra effort.
Examples
FHEM
User mapping
In this example we map usernameroot
tofhem_acl
forFHEM server.Edit/etc/config/rpcd
:
- /etc/config/rpcd
# /etc/config/rpcdconfigloginoption username'root'option password'$p$root'listread'*'listwrite'*' configloginoption username'fhem'## '$p$<username> => reference to user passowrd on /etc/shadow# '$1$<hash>' => crypt() hash, using SHA1. generate via 'uhttpd -m fhem'## password: fhem#option password'$1$$xEODf2fNSQ0ArfkJu4L2i1'## map username to rpcd acl name "fhem_acl"# listread'fhem_acl'listwrite''
Then reload rpcd config:
uci commit rpcd
User ACL
All files under/usr/share/rpcd/acl.d/
will be merge byrpcd
to one config file!!
ubus call hostapd.wlan0 get_clientsubus call hostapd.wlan1 get_clients
Example permissions to run ubus calls remote by fhem over http/json.
- /usr/share/rpcd/acl.d/fhem.json
{ "fhem_acl":{ "description":"FHEM PRESENCE Module User.. https://github.com/janLo/OpenWRT-Wifi-Clients-POC", "read":{ "ubus":{ "hostapd.wlan0":["get_clients"], "hostapd.wlan1":["get_clients"]}}}}
FHEM Server - show all connected wireless clients:
$ python wifi_clients.pyUsage: wifi_clients.py.orig<OpenWrt_Host><User><Pass><WiFi_1>[<WiFi2> ...]$ python wifi_clients.py 192.168.1.71 fhem fhem wlan0 wlan1c8:f6:50:e1:a9:10, 2c:f0:a2:e2:c0:15, 8c:a9:82:f1:9c:2a, 7c:c3:a1:b8:d2:1e,64:9a:be:6a:6c:13, 00:1d:63:a3:8f:4a
Getting firmware version
Get firmware version withJSON RPC.
luci_rpc(){localLIB="${1}"localDATA="${2}"localURL="https://${HOST}/\cgi-bin/luci/rpc/${LIB}?auth=${TOKEN}"curl-s-k-d"${DATA}""${URL}" \| jsonfilter-e"$['result']"}HOST="localhost"USER="root"PASS=""DATA="{\"id\": 1,\"method\":\"login\",\"params\": [\"${USER}\",\"${PASS}\" ] }"TOKEN="$(luci_rpc auth "${DATA}")"DATA="{\"id\": 2,\"method\":\"exec\",\"params\": [\"ubus call system board\" ] }"VERSION="$(luci_rpc sys "${DATA}"\| jsonfilter -e "$['release']['version']")"echo"${VERSION}"
Get firmware version with ubus RPC.
ubus_rpc(){localDATA="${1}"localURL="https://${HOST}/ubus"curl-s-k-d"${DATA}""${URL}" \| jsonfilter-e"$['result']"}HOST="localhost"USER="root"PASS=""DATA="{ 'jsonrpc': '2.0', 'id': 1, 'method': 'call','params': [ '00000000000000000000000000000000', 'session','login', { 'username': '${USER}', 'password': '${PASS}' } ] }"SESSION="$(ubus_rpc "${DATA}"\| jsonfilter -e "$[*]['ubus_rpc_session']")"DATA="{ 'jsonrpc': '2.0', 'id': 2, 'method': 'call','params': [ '${SESSION}', 'system', 'board', {} ] }"VERSION="$(ubus_rpc "${DATA}"\| jsonfilter -e "$[*]['release']['version']")"echo"${VERSION}"