--- title: OpenWRT UBUS tags: description: View the slide with "Slide Mode". --- # OpenWrt UBUS ## What is UBUS? Ubus is an RPC tool of OpenWRT, a micro system bus architecture. The aim of Ubus is to provide system-level Inter-process Communication(IPC) for various background processes and applications. Ubus is basically consistent with D-Bus in the design conception, providing system-level bus function. In order to be compatible with constrained environment says embedded systems, ubus reduced memory footprint. - Provide various daemons and applications with IPC(Inter-process Communication) service. - Work as a broker, forward messages between related application(s) in different ways. - Several tools are invented for accessing UBUS - libubus: a library for software to access UBUS. - Cmd-line ubus tool. - Ubus Lua module --- ### IPC Inter-process Communication refers to mechanism allowing processes to manage shared data. There are different approaches to IPC tailored to different software requirements, says performance, modularity, network bandwidth and latency. Examples of IPC method: - File - Signal - Socket / Unix domain Socket - Shared memory Most of IPC methods use client-server model to share data between processes. ### Connection model of client-server model ```graphviz digraph { compound=true rankdir=LR graph [ fontname="Source Sans Pro", fontsize=20 ]; node [ fontname="Source Sans Pro", fontsize=18]; edge [ fontname="Source Sans Pro", fontsize=12 ]; subgraph cluster2 { concentrate=true s1 [label="Process 1"] [shape=box] s2 [label="Process 2"] [shape=box] d [label="Process 3"] [shape=box] e [label="Process 4"] [shape=box] f [label="Process 5"] [shape=box] g [label="Process 6"] [shape=box] #sync [label="File, Shared Memory,\nSocket, Signal" shape=plaintext ] s1 -> d d -> s1 e -> s1 s1 -> e f -> s1 s1 -> f e -> s2 s2 -> e f -> s2 s2 -> f s2 -> g g -> s2 s1 -> s2 s2 -> s1 d -> e e -> d f -> d d -> f label="Client-server Model(m:n)" } subgraph cluster1 { concentrate = true a [label="Process 1\nServer"] [shape=box] b [label="Process 2\nClient"] [shape=box] #sync [label="File, Shared Memory,\nSocket, Signal" shape=plaintext ] b -> a [dir="left"] [label="Request"] a -> b [dir="right"] [label="Response"] label="Client-server Model(1:1)" } } ``` While there are m servers, and each server has $$ n_k $$ clients. The number of IPC connections in client/server model is: $$ \sum_{k=1}^{m}n_k $$ ### Connection model of UBUS Ubus uses the broker pattern as its architecture. There are three components to perform IPC through ubus. 1. **ubus daemon**: Broker/middleman between ubus server object and ubus client object, manages registration, and forwards messages between server and client object. 2. **ubus server object**: Usually interface/daemon of some software. Registers to ubus daemon with methods provided for client. Server objects and the methods registered can be looked up and called by client objects. 3. **ubus client object**: Caller of server objects and methods. ```graphviz digraph { compound=true rankdir=UD graph [ fontname="Source Sans Pro", fontsize=20 ]; node [ fontname="Source Sans Pro", fontsize=18]; edge [ fontname="Source Sans Pro", fontsize=12 ]; subgraph ubus { label = "Ubus model" concentrate = true ubus [label = " Ubusd "] [shape=box] p1 [label = "Process 1\nServer Object"] [shape=box] p2 [label = "Process 2\nServer Object"] [shape=box] p3 [label = "Process 3\nClient Object"] [shape=box] p4 [label = "Process 4\nClient Object"] [shape=box] p5 [label = "Process 5\nClient Object"] [shape=box] p6 [label = "Process 6\nClient Object"] [shape=box] p1 -> ubus ubus -> p1 p2 -> ubus ubus -> p2 p3 -> ubus ubus -> p3 p4 -> ubus ubus -> p4 p5 -> ubus ubus -> p5 p6 -> ubus ubus -> p6 } } ``` In this architecture, the number of connections is the number of processes, including both service "clients" and "servers", which is much less than IPC implementations under client/server model. :cat: ### Roles in UBUS There are different roles in Ubus IPC processes. 1. Object - Process registered to ubusd, including service and service callers. 2. Method - Procedure provdied by objects. Object can provide various methods as a server. 3. Data - Information in JSON format carried by requests or replies. ![](https://i.imgur.com/K0h3BYJ.png) Relations of object, method and data under UBUS. ![](https://i.imgur.com/GhGSIpQ.png) List of object on Ubus, methods and related data signature of 'system'. 4. Subscriber - Object subscribed to target service object. Subscribers will be notified once target service object sends a notification to ubusd. 5. Event - Event in Ubus is identified by a string called "event pattern". Event can be registered by object. Object can send data to ubusd with event pattern. 6. Event Registrant - Object registered to event with "event pattern". Ubusd forwards data to event registrant once message with matched "event pattern" is received. ### Data format of UBUS - JSON Ubus uses JSON as data format to call object, method and respond to request. [https://www.json.org/json-en.html](https://www.json.org/json-en.html) Here is an status example in JSON format. We call the method `status` of object `network.interface.wan3` with null message `'{}'`. ```json=0 root@ugwcpe:/# ubus call network.interface.wan3 status { "up": true, "pending": false, "available": true, "autostart": true, "dynamic": false, "uptime": 5, "l3_device": "eth1_wan3", "proto": "dhcp", "device": "eth1_wan3", "updated": [ "addresses", "routes", "data" ], "metric": 0, "dns_metric": 0, "delegation": true, "ipv4-address": [ { "address": "192.168.121.101", "mask": 24 } ], "ipv6-address": [ ], "ipv6-prefix": [ ], "ipv6-prefix-assignment": [ ], "route": [ { "target": "0.0.0.0", "mask": 0, "nexthop": "192.168.121.1", "source": "192.168.121.101/32" } ], "dns-server": [ "192.168.121.1", "8.8.8.8" ], "dns-search": [ "gemteks.com" ], "inactive": { "ipv4-address": [ ], "ipv6-address": [ ], "route": [ ], "dns-server": [ ], "dns-search": [ ] }, "data": { "leasetime": 86400 } } ``` ### Approaches of data flow There are three delivery schemes to implement IPC in Ubus. 1. One-to-one - Post data or requests to specific object. ![](https://i.imgur.com/sxSHRIc.png) 2. One-to-many(group by object) - Post data to multiple subscribers that subscribed to this object. ![](https://i.imgur.com/r7wK89y.png) 3. One-to-many(group by event) - Post data to multiple listeners of same event pattern. ![](https://i.imgur.com/s359hus.png) --- ## Data flow sequence of UBUS As mentioned, There are three delivery schemes to implement IPC in Ubus 1. Invoke - Post data directly to one object with object ID. 2. Subscribe/Notify - Post data to multiple subscribers that subscribed to same object. 3. Event boardcast - Post data to multiple listeners of same event pattern. ### 1. Invoke Data flow of invoke. 0. Startup of UBUSD. 1. Connection establishment of Process 1. 2. Registration of Process 1 object with provided method. 3. Connection establishment of Process 2. 4. **Object id lookup.** 5. **Request with object id, method, and required data(msg).** 6. Deregistration of object. Syntax: ---> :Client to Ubusd -- ->:Ubusd to Client ```sequence UBUSD->UBUSD:0: create socket bind listen UBUS CLIENT 1\nProcess 1->UBUSD:1.1: Connect UBUSD-->UBUS CLIENT 1\nProcess 1:1.2: Accept UBUSD-->UBUS CLIENT 1\nProcess 1:1.3: Hello UBUS CLIENT 1\nProcess 1->UBUSD:2.1: Regist object, method UBUSD->UBUSD:2.2: Update Avl-tree Note right of UBUSD:Assign and add objpath, \nobjid, objtype, \nmethod to avl-tree. UBUS CLIENT 2\nProcess 2->UBUSD:3.1: Connect UBUSD-->UBUS CLIENT 2\nProcess 2:3.2: Accept UBUSD-->UBUS CLIENT 2\nProcess 2:3.3: Hello UBUS CLIENT 2\nProcess 2->UBUSD:4.1: Lookup:{objpath} UBUSD-->UBUS CLIENT 2\nProcess 2:4.2: Reply data:{objpath, objid, objtype, signature} Note left of UBUS CLIENT 2\nProcess 2: Signature includes \nmethods and required \nparameters of each method. UBUS CLIENT 2\nProcess 2->UBUSD:5.1: Invoke:{objid, method, msg} UBUSD->UBUS CLIENT 1\nProcess 1:5.2: Invoke:{objid, method, msg} UBUS CLIENT 1\nProcess 1-->UBUSD:5.3: Reply data:{objid, msg} UBUSD-->UBUS CLIENT 2\nProcess 2:5.4: Reply data:{objid, msg} UBUS CLIENT 2\nProcess 2->UBUSD:6.1: Remove:{objid} UBUSD->UBUSD:6.2:Update avl-tree Note right of UBUSD:Remove object \nwith matched objpath. UBUSD-->UBUS CLIENT 2\nProcess 2:6.2: Reply data:{objid} ``` #### Ubus Traffic Directions: -> ubusd to object <- object to ubusd ```bash=0 Dir. Obj.ID Ubus.ID Message Type Message -> 2d0a3716 #2d0a3716 hello: {} <- 2d0a3716 #00000000 add_object: {"objpath":"gserver.host","signature":{"gserver_post":{"id":5,"data":5,"msg":3},"gserver_stop":{}}} -> 47dfa5f0 #00000000 invoke: {"objid":-921749017,"method":"ubus.object.add","data":{"id":-862368938,"path":"gserver.host"}} -> 2d0a3716 #00000000 data: {"objid":-862368938,"objtype":619811862} -> 2d0a3716 #00000000 status: {"status":0} <- 47dfa5f0 #00000000 status: {"status":0,"objid":-921749017} -> 41a666fd #41a666fd hello: {} <- 41a666fd #00000000 lookup: {"objpath":"gserver.host"} -> 41a666fd #00000000 data: {"objpath":"gserver.host","objid":-862368938,"objtype":619811862,"signature":{"gserver_post":{"id":5,"data":5,"msg":3},"gserver_stop":{}}} -> 41a666fd #00000000 status: {"status":0} <- 41a666fd #cc994b56 invoke: {"objid":-862368938,"method":"gserver_post","data":{"id":123456,"data":987654321,"msg":"Hi!"}} -> 2d0a3716 #41a666fd invoke: {"objid":-862368938,"method":"gserver_post","data":{"id":123456,"data":987654321,"msg":"Hi!"},"user":"root","group":"root"} <- 2d0a3716 #41a666fd data: {"objid":-862368938,"data":{"Gserver reply":"Request is being proceeded!"}} -> 41a666fd #cc994b56 data: {"objid":-862368938,"data":{"Gserver reply":"Request is being proceeded!"}} <- 2d0a3716 #41a666fd status: {"status":0,"objid":-862368938}​ -> 41a666fd #cc994b56 status: {"status":0,"objid":-862368938} -> b5d12db5 #b5d12db5 hello: {} <- b5d12db5 #00000000 lookup: {"objpath":"gserver.host"} -> b5d12db5 #00000000 data: {"objpath":"gserver.host","objid":-862368938,"objtype":619811862,"signature":{"gserver_post":{"id":5,"data":5,"msg":3},"gserver_stop":{}}} -> b5d12db5 #00000000 status: {"status":0} <- b5d12db5 #cc994b56 invoke: {"objid":-862368938,"method":"gserver_stop","data":{}} -> 2d0a3716 #b5d12db5 invoke: {"objid":-862368938,"method":"gserver_stop","data":{},"user":"root","group":"root"} <- 2d0a3716 #00000000 remove_object: {"objid":-862368938} -> 2d0a3716 #00000000 data: {"objid":-862368938,"objtype":619811862} -> 2d0a3716 #00000000 status: {"status":0} <- 2d0a3716 #b5d12db5 status: {"status":0,"objid":-862368938} ``` ##### 1. Connection establishment of Gserver. ```bash=1 -> 2d0a3716 #2d0a3716 hello: {} ``` ##### 2. Registration of Gserver object with provided method. ```bash=2 <- 2d0a3716 #00000000 add_object: {"objpath":"gserver.host","signature":{"gserver_post":{"id":5,"data":5,"msg":3},"gserver_stop":{}}} -> 47dfa5f0 #00000000 invoke: {"objid":-921749017,"method":"ubus.object.add","data":{"id":-862368938,"path":"gserver.host"}} -> 2d0a3716 #00000000 data: {"objid":-862368938,"objtype":619811862} -> 2d0a3716 #00000000 status: {"status":0} <- 47dfa5f0 #00000000 status: {"status":0,"objid":-921749017} ``` ##### 3. Connection establishment of Process 2. ```bash=7 -> 41a666fd #41a666fd hello: {} ``` ##### 4. Object id lookup. ```bash=8 <- 41a666fd #00000000 lookup: {"objpath":"gserver.host"} -> 41a666fd #00000000 data: {"objpath":"gserver.host","objid":-862368938,"objtype":619811862,"signature":{"gserver_post":{"id":5,"data":5,"msg":3},"gserver_stop":{}}} -> 41a666fd #00000000 status: {"status":0} ``` ##### 5. Request with object id, method, and required data(msg). ```bash=11 <- 41a666fd #cc994b56 invoke: {"objid":-862368938,"method":"gserver_post","data":{"id":123456,"data":987654321,"msg":"Hi!"}} -> 2d0a3716 #41a666fd invoke: {"objid":-862368938,"method":"gserver_post","data":{"id":123456,"data":987654321,"msg":"Hi!"},"user":"root","group":"root"} <- 2d0a3716 #41a666fd data: {"objid":-862368938,"data":{"Gserver reply":"Request is being proceeded!"}} -> 41a666fd #cc994b56 data: {"objid":-862368938,"data":{"Gserver reply":"Request is being proceeded!"}} <- 2d0a3716 #41a666fd status: {"status":0,"objid":-862368938} -> 41a666fd #cc994b56 status: {"status":0,"objid":-862368938} ``` ##### 6. Deregistration of object. ```bash=23 <- 2d0a3716 #00000000 remove_object: {"objid":-862368938} -> 2d0a3716 #00000000 data: {"objid":-862368938,"objtype":619811862} -> 2d0a3716 #00000000 status: {"status":0} <- 2d0a3716 #b5d12db5 status: {"status":0,"objid":-862368938} ``` --- ### 2. Subscribe/notify Data flow of Subscribe/notify. 0. Startup of UBUSD. 1. Connection establishment of Process 1. 2. Registration of Process 1 object with provided method. 3. Connection establishment of Process 3. 4. Object id lookup. 5. **Client 3 subscribe with object id.** 6. **Notification process.** 7. **Client 3 unsubscribe with object id.** #### Sequence Diagram ```sequence UBUSD->UBUSD:0: create socket bind listen UBUS CLIENT 1\nProcess 1->UBUSD:2.1: Regist object, method UBUSD->UBUSD:2.2: Update Avl-tree UBUS CLIENT 3\nProcess 3->UBUSD:3.1: Connect UBUSD-->UBUS CLIENT 3\nProcess 3:3.2: Accept UBUSD-->UBUS CLIENT 3\nProcess 3:3.3: Hello UBUS CLIENT 3\nProcess 3->UBUSD:4.1: Lookup:{objpath} UBUSD-->UBUS CLIENT 3\nProcess 3:4.2: Reply data:{objpath, objid, objtype, signature} UBUS CLIENT 3\nProcess 3->UBUSD:5.1: Subscribe:{objid} UBUSD->UBUSD:5.2: Update Subscription-tree UBUSD-->UBUS CLIENT 1\nProcess 1:5.3: Notify:{objid(subscriber),\n active:true} UBUS CLIENT 1\nProcess 1->UBUS CLIENT 1\nProcess 1:6.0 Trigger Notification UBUS CLIENT 1\nProcess 1->UBUSD:6.1: Notify:{objid, msg} UBUSD->UBUSD:6.2: Lookup Subscription-tree UBUSD-->UBUS CLIENT 3\nProcess 3: 6.3: Invoke:{objid, msg} UBUS CLIENT 3\nProcess 3->UBUSD: 7.1: Unsubscribe:{objid} UBUSD->UBUSD: 7.2: Update\n Subscription-tree UBUSD-->UBUS CLIENT 1\nProcess 1: 7.3 Notify:{objid(subscriber),\n active:false} ``` #### Ubus Traffic ```bash=0 Dir. Obj.ID Ubus.ID Message Type Message -> 74f42091 #74f42091 hello: {} <- 74f42091 #00000000 add_object: {"objpath":"gserver.host","signature":{"gserver_post":{"id":5,"data":5,"msg":3},"gserver_stop":{}}} -> ea7bde21 #00000000 invoke: {"objid":-804320694,"method":"ubus.object.add","data":{"id":629913760,"path":"gserver.host"}} -> 74f42091 #00000000 data: {"objid":629913760,"objtype":-429408539} -> 74f42091 #00000000 status: {"status":0} <- ea7bde21 #00000000 status: {"status":0,"objid":-804320694} -> 478023e4 #478023e4 hello: {} <- 478023e4 #00000000 add_object: {} -> 478023e4 #00000000 data: {"objid":-1244318547} -> 478023e4 #00000000 status: {"status":0} <- 478023e4 #00000000 lookup: {"objpath":"gserver.host"} -> 478023e4 #00000000 data: {"objpath":"gserver.host","objid":629913760,"objtype":-429408539,"signature":{"gserver_post":{"id":5,"data":5,"msg":3},"gserver_stop":{}}} -> 478023e4 #00000000 status: {"status":0} <- 478023e4 #00000000 subscribe: {"objid":-1244318547} -> 74f42091 #00000000 notify: {"objid":629913760,"active":true} -> 478023e4 #00000000 status: {"status":0} ~~~~~~Notification Trigger~~~~~~ <- 74f42091 #258bb8a0 notify: {"objid":629913760,"method":"gserver_post","data":{"id":123,"data":321,"msg":"abcdef"},"no_reply":true} -> 478023e4 #74f42091 invoke: {"objid":-1244318547,"method":"gserver_post","data":{"id":123,"data":321,"msg":"abcdef"},"no_reply":true,"user":"root","group":"root"} <- 478023e4 #00000000 unsubscribe: {"objid":-1244318547} -> 74f42091 #00000000 notify: {"objid":629913760,"active":false} ``` ##### 5. Subscribe with object id. ```bash=14 <- 478023e4 #00000000 subscribe: {"objid":-1244318547} -> 74f42091 #00000000 notify: {"objid":629913760,"active":true} -> 478023e4 #00000000 status: {"status":0} ``` ##### 6. Notification process. ```bash=18 <- 74f42091 #258bb8a0 notify: {"objid":629913760,"method":"gserver_post","data":{"id":123,"data":321,"msg":"abcdef"},"no_reply":true} -> 478023e4 #74f42091 invoke: {"objid":-1244318547,"method":"gserver_post","data":{"id":123,"data":321,"msg":"abcdef"},"no_reply":true,"user":"root","group":"root"} ``` ##### 7. Unsubscribe with object id. ```bash=20 <- 478023e4 #00000000 unsubscribe: {"objid":-1244318547} -> 74f42091 #00000000 notify: {"objid":629913760,"active":false} ``` --- ### 3. Event Boardcast Data flow of Event Boardcast. 0. Startup of UBUSD. 1. Connection establishment. 2. **Receivers register to ubus event handler(objid:1) with interested event pattern.** 3. **Sender sends data to ubus event handler(objid:1) with event pattern. Ubus event handler boardcasts data to receivers registered with same event pattern.** #### Sequence Diagram ```sequence UBUSD->UBUSD:0: create socket bind listen UBUS CLIENT 1\nProcess 1->UBUSD:1.1: Connect UBUSD-->UBUS CLIENT 1\nProcess 1:1.2 Accept UBUSD-->UBUS CLIENT 1\nProcess 1:1.3 Hello UBUS CLIENT 2\nProcess 2->UBUSD:1.4: Connect UBUSD-->UBUS CLIENT 2\nProcess 2:1.5 Accept UBUSD-->UBUS CLIENT 2\nProcess 2:1.6 Hello UBUS CLIENT 1\nProcess 1->UBUSD:2.1: Invoke:{objid, method:"register",\ndata:{object, pattern}} UBUSD->UBUSD:2.2: Update Event \nRegistration-tree UBUS CLIENT 2\nProcess 2->UBUSD:3.1: Invoke:{objid, method:"send",\ndata:{pattern, data}} UBUSD->UBUSD:3.2: Lookup Event \nRegistration-tree UBUSD-->UBUS CLIENT 1\nProcess 1:3.3: Invoke:{objid, method:pattern, data} ``` #### Ubus Traffic ```bash=0 Dir. Obj.ID Ubus.ID Message Type Message -> 55484d34 #55484d34 hello: {} <- 55484d34 #00000000 add_object: {} -> 55484d34 #00000000 data: {"objid":-964689289} -> 55484d34 #00000000 status: {"status":0} <- 55484d34 #00000001 invoke: {"objid":1,"method":"register","data":{"object":-964689289,"pattern":"g_server"}} -> 55484d34 #00000001 status: {"status":0} -> b79aea68 #b79aea68 hello: {} <- b79aea68 #00000001 invoke: {"objid":1,"method":"send","data":{"id":"g_server","data":{"str":"gemtek"}}} -> 55484d34 #00000000 invoke: {"objid":-964689289,"method":"g_server","data":{"str":"gemtek"}} -> b79aea68 #00000001 status: {"status":0} <- 55484d34 #00000000 status: {"status":0,"objid":-964689289} ``` ##### 2. Receivers register to ubus event handler. ```bash=5 <- 55484d34 #00000001 invoke: {"objid":1,"method":"register","data":{"object":-964689289,"pattern":"g_server"}} -> 55484d34 #00000001 status: {"status":0} ``` ##### 3. Sender send data to ubus event handler(objid:1) with event pattern. Ubus event handler boardcasts data to receivers registered with *same event pattern. ```bash=7 <- b79aea68 #00000001 invoke: {"objid":1,"method":"send","data":{"id":"g_server","data":{"str":"gemtek"}}} -> 55484d34 #00000000 invoke: {"objid":-964689289,"method":"g_server","data":{"str":"gemtek"}} ``` --- ## UBUS tools OpenWrt provides four tools to access ubus 1. **Command-line ubus tool** 2. **C library libubus** 4. Ubus Lua module ### Command-line ubus tool The command "ubus" allows user to interact with the ubusd server. Services registered to ubusd server can be accessed by this command tool. #### Help output of command "ubus" ```bash root@ugwcpe:/# ubus Usage: 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 traffic Commands: - 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 - monitor Monitor ubus traffic ``` #### list To find out services currently running on the bus, just simply use the `ubus list` command. A complete list of all object registed with namespace will be shown. ```bash root@ugwcpe:/# ubus list block csd devmd dhcp diagnosticsd dwpald firewalld log network network.device network.interface network.interface.iface_eth0_1 network.interface.iface_eth0_2 network.interface.iface_eth0_3 network.interface.iface_eth0_4 network.interface.iface_eth1 network.interface.lan network.interface.loopback network.interface.wan3 network.wireless polld servd service system uci wsd ``` To find out methods and the argument signatures provided by specific service/object, we can type the service namespace, `system`, after the command `list` and use the option `-v`. ```bash root@ugwcpe:/# ubus -v list system 'system' @d1165900 "board":{} "info":{} "reboot":{} "upgrade":{} "done":{} "watchdog":{"frequency":"Integer","timeout":"Integer","magicclose":"Boolean","stop":"Boolean"} "signal":{"pid":"Integer","signum":"Integer"} "sysupgrade":{"path":"String","prefix":"String","command":"String"} "factoryreset":{"value":"String"} "hostname":{"value":"String"} ``` #### call To call method of specific object, we can use the `call` command. Here is an example of `call` command. As there is no required data in method `status`, we just need to type `info` after object `system`. The method being called takes actions, says return message, or turn on/off other services, depends on its callback function. In this case, the method `info` of `system` returns a message with system information to the callee. ```bash root@ugwcpe:/# ubus call system info { "localtime": 1610460661, "uptime": 82303, "load": [ 196608, 196608, 196608 ], "memory": { "total": 1021562880, "free": 884396032, "shared": 1159168, "buffered": 9117696 }, "swap": { "total": 0, "free": 0 } } ``` #### send To send message to an event with specific event pattern, we can use the `send` command. The following example is sending data `{"str":"gemtek"}` to event `event_a`. ```bash root@ugwcpe:/# ubus send event_a '{"str":"gemtek"}' ``` #### listen Command `listen` is for listening to events. The following example is listening to event `event_a`. The console prints data of related event update. ```bash root@ugwcpe:/# ubus listen event_a { "event_a": {"str":"gemtek"} } ``` #### wait_for Command `wait_for` returns when object waited is registered. ```bash root@(none):/# ubus wait_for gserver.host root@(none):/# ``` #### monitor Command `monitor` is for monitoring ubus traffic. ```bash root@ugwcpe:/# ubus monitor -> d17cae6a #00000003 status: {"status":0} -> 23f12bf1 #23f12bf1 hello: {} <- 23f12bf1 #00000001 invoke: {"objid":1,"method":"send","data":{"id":"event_a","data":{"str":"gemtek"}}} -> 716e9a5f #00000000 invoke: {"objid":-2003009711,"method":"event_a","data":{"str":"gemtek"}} -> 23f12bf1 #00000001 status: {"status":0} <- 716e9a5f #00000000 status: {"status":0,"objid":-2003009711} ``` ### Library libubus #### Function for object registration | Function | Description | | -------- | -------- | | `struct ubus_context *ubus_connect(const char *path)`|Connect the specified path, create and return the ubus context represented by the path. | | `inline void ubus_add_uloop(struct ubus_context *ctx)` | Activate UBUS; this tells U-Loop to check for ubus events (listens to ubus). It is blocking function, meaning that once we call uloop_run, it is waiting for ubus to get something. | | `int ubus_lookup_id(struct ubus_context *ctx, const char *path, uint32_t *id)` | This routine gets id from path. If it is successful, it returns UBUS_STATUS_OK, otherwise UBUS_STATUS_* error-code. | | `void ubus_free(struct ubus_context *ctx)` | It is identical to ubus_shutdown() + freeing the context. | | `int ubus_add_object(struct ubus_context *ctx, struct ubus_object *obj)` | Add a UBUS object into the list of objects to be queried. We must call this routine before doing ubus_lookup_id. | | `int ubus_remove_object(struct ubus_context *ctx, struct ubus_object *obj)` | The opposite of ubus_add_object; called when we need to cleanup the pending request. | #### Function for request | Function | Description | | -------- | -------- | | `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)` | Invoke RPC indicated in passed parameter method. `cb` is the callback object for server object responding with data. | | `int ubus_send_reply(struct ubus_context *ctx, struct ubus_request *req, struct blob_attr *msg)` | Send reply to the incoming object call, says `invoke`. | #### Function for subscription | Function | Description | | -------- | -------- | | `int ubus_register_subscriber(struct ubus_context *ctx, struct ubus_subscriber *obj)` | Register a callback object for receiving notifications from publisher. | | `int ubus_subscribe(struct ubus_context *ctx, struct ubus_subscriber *obj, uint32_t id)` | Subscribe to an object with a registed callback object. | | `int ubus_unsubscribe(struct ubus_context *ctx, struct ubus_subscriber *obj, uint32_t id)` | Unsubscribe from the object. | #### Function for event | Function | Description | | -------- | -------- | | `int ubus_notify(struct ubus_context *ctx, struct ubus_object *obj, const char *type, struct blob_attr *msg, int timeout)` | Send notification to ubus, ubus invokes subscribers with data carried by the notification. | | `int ubus_register_event_handler(struct ubus_context *ctx, struct ubus_event_handler *ev, const char *pattern)` | Register event handler `ev` to the event on ubus. | | `int ubus_unregister_event_handler(struct ubus_context *ctx, struct ubus_event_handler *ev)` | Deregister event handler `ev` on ubus. | | `int ubus_send_event(struct ubus_context *ctx, const char *id, struct blob_attr *data)` | Send data as an event with event pattern `id` to ubus. Ubus call related event handlers with the data. | #### Sample code of server object ```c=1 #include <stdio.h> #include <stdint.h> #include <libubus.h> #include <libubox/uloop.h> #include <libubox/blobmsg_json.h> /* Ubus method_call_functions */ static int gserver_post(struct ubus_context *ctx, struct ubus_object *obj, struct ubus_request_data *req, const char *method, struct blob_attr *msg); static int gserver_stop(struct ubus_context *ctx, struct ubus_object *obj, struct ubus_request_data *req, const char *method, struct blob_attr *msg); /* Enum for GSERVER policy order */ enum { GSERVER_ID, GSERVER_DATA, GSERVER_MSG, __GSERVER_MAX, }; /* Ubus method policy */ static const struct blobmsg_policy gserver_policy[] = { [GSERVER_ID] = { .name="id", .type=BLOBMSG_TYPE_INT32}, [GSERVER_DATA] = { .name="data", .type=BLOBMSG_TYPE_INT32 }, [GSERVER_MSG] = { .name="msg", .type=BLOBMSG_TYPE_STRING }, }; static const struct blobmsg_policy gserver_stop_policy[] = { }; /* Ubus object methods */ static const struct ubus_method gserver_methods[] = { /* UBUS_METHOD(method_name, method_call_function, method_policy) */ UBUS_METHOD("gserver_post", gserver_post, gserver_policy), UBUS_METHOD("gserver_stop", gserver_stop, gserver_stop_policy) }; /* Ubus object type */ static struct ubus_object_type gserver_obj_type = UBUS_OBJECT_TYPE("gserver_uobj", gserver_methods); /* Ubus object */ static struct ubus_object gserver_object= { .name = "gserver.host", //objpath .type = &gserver_obj_type, .methods = gserver_methods, .n_methods = ARRAY_SIZE(gserver_methods), }; /* Ubus method_call_functions */ static int gserver_post(struct ubus_context *ctx, struct ubus_object *obj, struct ubus_request_data *req, const char *method, struct blob_attr *msg) { /* do something */ return 0; } static int gserver_stop(struct ubus_context *ctx, struct ubus_object *obj, struct ubus_request_data *req, const char *method, struct blob_attr *msg) { /* do something */ return 0; } void server(void) { /* 1. create an epoll instatnce descriptor poll_fd */ uloop_init(); /* 2. connect to ubusd and get ctx(context) */ struct ubus_context *ctx = ubus_connect(NULL); // Use default UNIX sock path /* 3. registger epoll events to uloop, start socket listening */ ubus_add_uloop(ctx); /* 4. register a ubus_object to ubusd */ ubus_add_object(ctx, gserver_object); /* 5. uloop routine: events monitoring and callback provoking */ uloop_run(); /* 6. terminate uloop */ uloop_done(); return; } ``` #### Sample code of client object ```c= #include <stdio.h> #include <string.h> #include <unistd.h> #include <libubus.h> #include <libubox/uloop.h> #include <libubox/blobmsg_json.h> static struct ubus_context *ctx; static struct ubus_request_data req_data; static struct blob_buf b_buf; //message carried in invoke static uint32_t obj_id; /* Ubus object */ static struct ubus_object client_object = { }; /* callback */ static int callback (struct ubus_request *req, int type, struct blob_attr *msg) { /* do something */ return 0; } void client(void) { /* 1. create an epoll instatnce descriptor poll_fd */ uloop_init(); /* 2. connect to ubusd and get ctx(context) */ struct ubus_context *ctx = ubus_connect(NULL); // Use default UNIX sock path /* 3. registger epoll events to uloop, start socket listening */ ubus_add_uloop(ctx); /* 4. register a ubus_object to ubusd */ ubus_add_object(ctx, &client_object); /* 5. lookup object_id of service object */ ubus_lookup_id(ctx, "gserver.host", &obj_id); /* 6. send request by ubus_invoke to service object */ ubus_invoke(ctx, obj_id, "gserver_post", b_buf, callback, 0, 3000); /* 7. uloop routine: events monitoring and callback provoking */ uloop_run(); /* 8. terminate uloop */ uloop_done(); return; } ``` #### Sample code for subscriber ```c= #include <stdio.h> #include <string.h> #include <unistd.h> #include <libubus.h> #include <libubox/uloop.h> #include <libubox/blobmsg_json.h> static struct ubus_context *ctx; static uint32_t obj_id; static int notif_handler(struct ubus_context *ctx, struct ubus_object *obj, struct ubus_request_data *req, const char *method, struct blob_attr *msg) { /* do something */ return 0; } void subscriber_object(void) { /* 1. create an epoll instatnce descriptor poll_fd */ uloop_init(); /* 2. connect to ubusd and get ctx(context) */ struct ubus_context *ctx = ubus_connect(NULL); // Use default UNIX sock path /* 3. registger epoll events to uloop, start socket listening */ ubus_add_uloop(ctx); /* 4. register a ubus_object to ubusd */ ubus_add_object(ctx, &subscriber_object); /* 5. lookup object_id of service object */ ubus_lookup_id(ctx, "gserver.host", &obj_id); /* 6. subscribe service object */ ubus_subscribe(ctx, &notif_handler, obj_id); /* 7. uloop routine: events monitoring and callback provoking */ uloop_run(); /* 8. terminate uloop */ uloop_done(); return; } ``` #### Sample code for event listener ```c= #include <stdio.h> #include <string.h> #include <unistd.h> #include <libubus.h> #include <libubox/uloop.h> #include <libubox/blobmsg_json.h> static struct ubus_context *ctx; static struct ubus_request_data req_data; static struct blob_buf b_buf; static uint32_t obj_id; static void receive_event(struct ubus_context *ctx, struct ubus_event_handler *ev, const char *method, struct blob_attr *msg) { printf("Event received\n"); /* do something */ return; } int event_example () { /* 1. create an epoll instatnce descriptor poll_fd */ uloop_init(); /* 2. connect to ubusd and get ctx(context) */ struct ubus_context *ctx = ubus_connect(NULL); // Use default UNIX sock path /* 3. registger epoll events to uloop, start socket listening */ ubus_add_uloop(ctx); /* 4. register event on ubus */ const char *event = "gevent"; struct ubus_event_handler ev = { .cb = receive_event, }; ubus_register_event_handler(ctx, &ev, event); /* 5. uloop routine: events monitoring */ uloop_run(); /* 6. terminate uloop */ uloop_done(); return; } ``` --- ## References [https://openwrt.org/docs/techref/ubus](https://openwrt.org/docs/techref/ubus) [https://openwrt.org/docs/techref/libubox](https://openwrt.org/docs/techref/libubox) [https://www.programmersought.com/article/32835484946/](https://www.programmersought.com/article/32835484946/) [https://www.programmersought.com/article/26351479004/](https://www.programmersought.com/article/26351479004/) [https://www.programmersought.com/article/48733667380/](https://www.programmersought.com/article/48733667380/)