Getting started with OpenWrt Micro Bus - ubus

OpenWrt is an opensource Linux distribution which enables several Routers and Access Points. It is designed to be a lean router stack, but with a scaleable architecture where,it can be customized and extended with more features. It is possible that we can develop our own custom component and add it to the router stack conveniently.
This is the first part of a mini series dedicated to introduce openwrt bus architecture and adding a shell based custom component to Openwrt.
OpenWrt Micro Bus (UBus)
-
OpenWrt micro bus (ubus) provides system-level Inter-process Communication(IPC) for the services running in OpenWrt. UBus allows services to perform remote procedure call.
-
The heart of ubus is ubusd daemon, which allows services to register to a specific namespace and allow other components to call the procedures using the registered namespace.
-
Client applications can connect to the Ubus, ask for specific objects on the bus and call methods of these objects or they can create new objects and methods itself.
-
UBUS calls uses JSON format for the parameters passing and response messages.
UBUS Architecture

UBUS Client To Explore
-
Openwrt provides different tools to access ubus. One is command-line ubus tools
-
The ubus command line tool allows to interact with the ubusd server (with all currently registered services).
-
To list out the existing objects in ubus we can use the ubus list command as mentioned below
root@OpenWrt:/usr/libexec/rpcd# ubus list
dhcp
dnsmasq
iwinfo
network.device
uci
..
...
-
To get information on the methods registered with the object , tryout the below command.
root@OpenWrt:/usr/libexec/rpcd# ubus -v list system
'system' @64d9bfba
"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"}
-
To invoke the methods registered in ubus, call command can be used. For example, we can get the board info using ubus itself.
root@OpenWrt:/usr/libexec/rpcd# ubus call system board
{
"kernel": "6.6.74",
"hostname": "OpenWrt",
"system": "ARMv8 Processor rev 4",
"board_name": "linux,dummy-virt",
"rootfs_type": "squashfs",
"release": {
"distribution": "OpenWrt",
"version": "SNAPSHOT",
"revision": "r28724-807074309d",
"target": "armsr/armv8",
"description": "OpenWrt SNAPSHOT r28724-807074309d",
"builddate": "1738094344"
}
}
-
Other options also available in ubus command line, please checkout help options for more info.
Remote Procedure Call Daemon (RPCD)
-
Rpcd is an OpenWrt daemon that uses the ubus micro bus architecture to provide RPC services for small utils which doesn’t need to run as a background service.
-
It is possible to expose shell script functionality over ubus by using rpcd plugin executables functionality.
-
Executables stored in /usr/libexec/rpcd/ directory will be run by rpcd and registers them as UBUS procedures, to be called from other clients.
RPCD Architecture

RPCD Plugin Format
-
List and Call functions - A RPCD plugin must support call and list functions to be attached to the ubus
-
Input/Output
-
All input/output of ubus shell scripts are in the form of JSON format.
-
This format provides compatibility between all ubus services
-
A JSON object contains zero, one, or more key-value pairs, also called properties. The object is surrounded by curly braces {}. Every key-value pair is separated by a comma.
-
A key-value pair consists of a key and a value, separated by a colon (:). The key is a string, which identifies the key-value pair
-
Example
-
JSON
'{"key1":"value1","key2":"value2","key3":"value3"}'
Creating RPCD Plugin
-
Lets create a ubus object to fetch the current date and time of the openwrt system.
-
current.date and current.time are the objects registered to the sys_time method.
root@OpenWrt:/usr/libexec/rpcd# cat sys_time
#!/bin/sh
case "$1" in
list)
echo '{ "current.time" : { }, "current.date" : { }}'
;;
call)
case "$2" in
current.time)
current_time=$(uptime -s | awk '{print $2}')
echo '{ "current_time" : "'$current_time '" }'
;;
current.date)
current_date=$(uptime -s | awk '{print $1}')
echo '{ "current_date" : "'$current_date '" }'
esac
;;
esac
root@OpenWrt:/usr/libexec/rpcd# chmod a+x sys_time
-
Once script is placed inside the rpcd folder, rpcd service has to be restarted
root@OpenWrt:/usr/libexec/rpcd# /etc/init.d/rpcd reload
-
This will expose the script to the ubus daemon.
root@OpenWrt:/usr/libexec/rpcd# ubus -v list sys_time
'sys_time' @9186a0cf
"current.time":{}
"current.date":{}
-
To invoke the ubus call for sys_time daemon with two methods, current.time and current.date
root@OpenWrt:/usr/libexec/rpcd# ubus call sys_time current.time
{
"current_time": "21:56:44 "
}
root@OpenWrt:/usr/libexec/rpcd# ubus call sys_time current.date
{
"current_date": "2025-02-27 "
}
UBUS Call Introspection

-
To monitoring the ubus traffic , ubus monitor command can be used. This command gives the insight of messages hitting ubus daemon.
root@OpenWrt:/usr/libexec/rpcd# ubus monitor &
-
Connection Establishment and Method registration between RPCD and UBUSD can be seen as,
root@OpenWrt:/usr/libexec/rpcd# /etc/init.d/rpcd reload
-> d384ecad #00000003 status: {"status":0}
-> a2afb3d0 #a2afb3d0 hello: {}
<- a2afb3d0 #00000000 lookup: {"objpath":"service"}
-> a2afb3d0 #00000000 data: {"objpath":"service","objid":315447600,"objtype":118647020,"signature":{"set":{"name":3,"script":3,"instances":2,"triggers":1,"validate":1,"autostart":7,"data":2},"add":{"name":3,"script":3,"instances":2,"triggers":1,"validate":1,"autostart":7,"data":2},"list":{"name":3,"verbose":7},"delete":{"name":3,"instance":3},"signal":{"name":3,"instance":3,"signal":5},"update_start":{"name":3},"update_complete":{"name":3},"event":{"type":3,"data":2},"validate":{"package":3,"type":3,"service":3},"get_data":{"name":3,"instance":3,"type":3},"state":{"spawn":7,"name":3},"watchdog":{"mode":5,"timeout":5,"name":3,"instance":3}}}
-> a2afb3d0 #00000000 status: {"status":0}
<- a2afb3d0 #12cd5930 invoke: {"objid":315447600,"method":"signal","data":{"name":"rpcd"}}
-> da4e6639 #a2afb3d0 invoke: {"objid":315447600,"method":"signal","data":{"name":"rpcd"},"user":"root","group":"root"}
-> fa317069 #fa317069 hello: {}
<- fa317069 #00000000 add_object: {"objpath":"session","signature":{"create":{"timeout":5},"list":{"ubus_rpc_session":3},"grant":{"ubus_rpc_session":3,"scope":3,"objects":1},"revoke":{"ubus_rpc_session":3,"scope":3,"objects":1},"access":{"ubus_rpc_session":3,"scope":3,"object":3,"function":3},"set":{"ubus_rpc_session":3,"values":2},"get":{"ubus_rpc_session":3,"keys":1},"unset":{"ubus_rpc_session":3,"keys":1},"destroy":{"ubus_rpc_session":3},"login":{"username":3,"password":3,"timeout":5}}}
-> da4e6639 #00000000 invoke: {"objid":-1425863935,"method":"ubus.object.add","data":{"id":-137075399,"path":"session"}}
-> da4e6639 #00000000 invoke: {"objid":-1425863935,"method":"ubus.object.add","data":{"id":875435327,"path":"uci"}}
-> fa317069 #00000000 data: {"objid":-1174288804,"objtype":1304846968}
-> fa317069 #00000000 status: {"status":0}
<- fa317069 #00000000 add_object: {"objpath":"sys_time","signature":{"current.time":{},"current.date":{}}}
-
Invoking the data from ubus from ubusd is given below,
root@OpenWrt:/usr/libexec/rpcd# ubus call sys_time current.time
-> bf475fc1 #bf475fc1 hello: {}
<- bf475fc1 #00000000 lookup: {"objpath":"sys_time"}
-> bf475fc1 #00000000 data: {"objpath":"sys_time","objid":-849228508,"objtype":-155009503,"signature":{"current.time":{},"current.date":{}}}
-> bf475fc1 #00000000 status: {"status":0}
<- bf475fc1 #cd61cd24 invoke: {"objid":-849228508,"method":"current.time","data":{}}
-> 45563b02 #bf475fc1 invoke: {"objid":-849228508,"method":"current.time","data":{},"user":"root","group":"root"}
<- 45563b02 #00000000 lookup: {}
-> 45563b02 #00000000 data: {"objpath":"file","objid":1221431004,"objtype":-790314395,"signature":{"read":{"path":3,"base64":7,"ubus_rpc_session":3},"write":{"path":3,"data":3,"append":7,"mode":5,"base64":7,"ubus_rpc_session":3},"list":{"path":3,"ubus_rpc_session":3},"stat":{"path":3,"ubus_rpc_session":3},"md5":{"path":3,"ubus_rpc_session":3},"remove":{"path":3,"ubus_rpc_session":3},"exec":{"command":3,"params":1,"env":2,"ubus_rpc_session":3}}}
-> 45563b02 #00000000 data: {"objpath":"log","objid":-922528967,"objtype":953641379,"signature":{"read":{"lines":5,"stream":7,"oneshot":7},"write":{"event":3}}}
-> 45563b02 #00000000 data: {"objpath":"sys_time","objid":-849228508,"objtype":-155009503,"signature":{"current.time":{},"current.date":{}}}
<- 45563b02 #bf475fc1 data: {"objid":-849228508,"data":{"current_time":"21:56:44 "}}
<- 45563b02 #bf475fc1 status: {"status":0,"objid":-849228508}
-> bf475fc1 #cd61cd24 status: {"status":0,"objid":-849228508}
Conclusion
This exploratory article covered the introduction to OpenWrt bus architecture and the steps to create a custom component using RFC Daemon. The upcoming article will uncover the jshn library to replace the current shell script approach.