うたカモ技術ブログ

Linux OpenWrt

OpenWrt   UBUSコマンドの使い方

post:     update: 

この記事は作成中です。

今回は、OpenWrtのUBUSコマンドの使い方について紹介します。

UBUSとは、OpenWrt専用アプリケーション間で利用されるプロセス間通信システム(IPC)です。

OpenWrt専用アプリケーションの中にはUBUSのIPC実装を組み込み、他アプリケーション(クライアント)に対して 自身の機能を公開するものがあります(機能を公開するものをUBUSサーバーアプリケーションと呼びます)。

UBUSコマンドは、それらUBUSサーバーアプリケーションが公開する機能の実行要求や通知イベントの送信・受信などをUBUSシステム(ubusd)に 依頼するユーティリティツールです。

そのため、主にクライアントアプリケーションが特定のUBUSサーバーアプリケーションの公開機能を実行したいときに使用します。

UBUSコマンドの使い方を理解すれば、OpenWrtアプリケーションに対してより深い理解が得られると思います。 少なくとも、プロセス間同士の連携や役割分担などについて詳細に動作を把握できるようになり、アプリケーション開発に役立つでしょう。

今回はそんなUBUSコマンドの使い方について紹介します。
この記事では、UBUSについてその仕組みやデータ構造から説明し、各コマンドの使い方と実行例を掲載します。

UBUS学習の参考サイト紹介
UBUSの学習に役立つサイトを紹介します。以下のサイトを読むことでコマンドの使用方法だけでなく、その実装方法についても理解できます。 私の記事と併用して読むとより理解に繋がると思います。

  1. What is UBUS?
  2. OpenWRT UBUS mechanism

目次

  1. UBUSの仕組み
  2. UBUSの通信種別
  3. UBUSのデータ構造(オブジェクト・メソッド)
  4. UBUSコマンド一覧
  5. listコマンド  ubus list [<path>]
  6. callコマンド  ubus call <path> <method> [<message>]
  7. subscribeコマンド  ubus subscribe <path> [<path>...]
  8. listenコマンド  ubus listen [<path>...]
  9. sendコマンド  ubus send <type> [<message>]
  10. wait_forコマンド  ubus wait_for <object> [<object>...]
  11. monitorコマンド  ubus monitor
  12. おわりに

UBUSの仕組み

全体動作について

冒頭でも説明したように、UBUSはOpenWrt専用アプリケーション間で利用されるプロセス間通信(IPC)システムです。

次の図に示すように、UBUSシステム(ubusd)を介してアプリケーション(実行プロセス)同士が処理の実行要求や結果応答 、通知イベントをやり取りします。

このとき、UBUSシステム(ubusd)はアプリケーション間の窓口として機能します。

これはクライアントアプリケーションがUBUSのプロセス間通信を利用してUBUSサーバーアプリケーション(UBUS APP)に公開機能の実行要求を出した場合、 その実行要求先はUBUSシステムになるということを意味します。

実行要求を受けたUBUSシステムは自身に接続されているUBUSサーバーアプリケーション(UBUS APP)の中に要求先が存在しないか確認します。

そして、要求先のアプリケーションが見つかった場合は、要求内容をその特定アプリケーションに渡し、UBUSシステムは要求元のクライアントアプリケーションに結果応答を返します。

このように、UBUSによるIPCでは常にUBUSシステムが制御主体(または通信の仲介)となってデータの送受信が実施されます。

UBUSのIPC実装について

上記で説明する通信シーケンスを実現するために、UBUSによるIPCを利用するアプリケーション同士はUBUSシステム(ubusd)への接続実装が必要です。 その上で機能を公開する側、それを利用するクライアント側で以下に挙げる処理を別々に実装する必要があります。

[機能を公開するアプリケーション側]
クライアントからの要求やイベント通知を受けて特定の処理を実行するようなUBUSサーバーアプリケーション(UBUS APP)では、自身の機能をUBUSシステムに 公開する処理の実装も必要です。これらの処理はlibubusと呼ばれるC言語ライブラリを使用することでアプリケーションに実装することができます。

[公開機能を使用するクライアント側]
クライアントアプリケーション(Client APP)はUBUSシステムに実行要求やイベント通知をするために、libubusを使用した実装またはユーティリティツール(ubus)の利用が必要です。 例えば、クライアントアプリケーションをスクリプトと仮定した場合、クライアントユーティリティツールのubusを利用することが考えられます。

ubusはUBUSシステムのubusdに接続し、UBUSサーバーアプリケーションの特定処理に対する実行要求やイベント通知をクライアントアプリケーションに代わって実施します。

ubusが持つコマンドを一般的にはUBUSコマンドと呼び、この記事で紹介するメインテーマになります。

UBUSの通信形態

UBUSの主な通信形態は、1対1通信と1対多通信、多対多通信の3つです。

1対1通信

UBUSによる最も一般的な通信です。
後の節で使い方を説明するcallコマンドやsendコマンド等で実行される通信です。

実行要求元のクライアントアプリケーションと要求先のサーバーアプリケーションが1対1で通信します。

このような通信関係は、1つのクライアントアプリケーションが特定のUBUSサーバーアプリケーションから何らかの処理結果を取得する際に成立します。

1対多通信

subscribeコマンドを利用すると実現できる通信形態です。
UBUS上で通知される特定アプリケーションのイベントを購読(サブスクライブ)することで実現されます。

購読者(サブスクライバー)が複数になると1対多通信の関係になります。

このような通信関係は、1つのUBUSサーバーアプリケーションから何らかのサービス情報を配信してもらいたい複数のクライアントアプリケーション が存在する場合に成り立ちます。

多対多通信

1対多の通信もあるなら多対多通信も当然あります。
subscribeコマンドによるイベント購読に加え、特定イベントの受信をトリガーとするcallコマンド、sendコマンド などの実行がある場合です。

イベントの送受信を契機に相互に影響し合うプロセス間の通信で多対多通信が成立します。 このような関係での通信シーケンスは複雑になりがちです。

UBUSのデータ構造(オブジェクト・メソッド)

UBUSシステム(ubusd)上で取り扱うデータとして代表的なものにオブジェクトとメソッドがあります。

これはUBUSサーバーアプリケーション(UBUS APP)が自身の機能をUBUSを介して外部へ公開する際に、UBUSシステムに登録する公開機能の管理データです。 UBUSシステムへのオブジェクト・メソッドの登録はシステムブートやアプリの再起動、アプリのインストール時などで実施されます(①)。

この管理データの構造は非常に単純で、公開機能そのものを示すメソッド<method>と、関連するメソッドを集約するオブジェクト<path>という 2つのデータで構成されます。

上図は、'object'という名前のオブジェクトの中に'methodA'、'methodB'、'methodC'と呼ばれる3つメソッドが管理された状態で、UBUS APPの公開機能がUBUSシステムに登録される様子を表しています。

通常、クライアントアプリケーションはUBUSコマンドのcallコマンドを使用して、あるオブジェクト<path>が持つメソッド<method>の 実行要求をUBUSシステムに送信することが出来ます。つまり、上図のmethod Aの実行要求をUBUSシステムに送信したい場合は、ubus call object methodAとするということです(②)。

このようにして、UBUSコマンドのcallコマンドが呼び出されると指定オブジェクト・メソッドの実行要求が窓口であるUBUSシステム(ubusd)に送信されます。 そしてこの時に、UBUSシステムは自身が管理するデータの中で該当するオブジェクト・メソッドがないか確認します(③)。

確認の結果、該当のオブジェクト・メソッドがあれば、それを公開している大元のUBUSサーバーアプリケーションに実行要求を通知します(④)。

実行要求を受信した当該アプリケーションは、'method A'に対応する処理を実行します。 処理実行後、その結果応答を返す場合はUBUSシステムに対してその応答をJSONとして返します。

そして最終的に、UBUSを介して実行要求元のクライアントアプリケーションにJSONの結果応答が届きます。

以上のことから、クライアント側からはUBUSサーバーアプリケーションが公開している機能はオブジェクト・メソッドのデータモデルとして見えます。 そのため、任意のオブジェクト・メソッドがどんなアプリケーションの所属かはシステム上から意識されることはありません。

このように、UBUSシステムではオブジェクト・メソッドというデータ構造を利用して管理下にあるアプリケーションのプロセス機能を表現しています。

UBUSコマンド一覧

次はUBUSシステムのクライアントユーティリティであるubusのヘルプ画面です。 このヘルプ画面はコマンドプロンプト上で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
        - subscribe <path> [<path>...]         Subscribe to object(s) notifications
        - 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
    

これを見ると、UBUSコマンドはlistcallsubscribelistensendwait_formonitor の計7個ということが分かります。(ちなみに、subscribeコマンドは後から追加されたものらしいです。)

次節では、実行例を交えてUBUSコマンドの説明をします。

listコマンド

実行フォーマット
 ubus list [<path>] 

UBUSが管理するアプリケーションの公開オブジェクトをリストアップしてコンソールに表示します。

次は ubus list を実行して、UBUSに登録されている全てのオブジェクトを表示した例です。 このコマンドを実行することで、どんなオブジェクトが公開されているか確認できます。

root@OpenWrt:~# ubus list
container
dhcp
dnsmasq
dnsmasq.dns
hostapd
hostapd.phy0-ap0
hotplug.dhcp
hotplug.ieee80211
hotplug.iface
hotplug.neigh
hotplug.net
hotplug.ntp
hotplug.tftp
log
network
network.device
network.interface
network.interface.lan
network.interface.loopback
network.interface.wan
network.wireless
service
system
wpa_supplicant

ただし、普通はこれで満足できないものです。オブジェクトだけでなく、そのオブジェクトが管理するメソッドのリストと引数指定の情報 も欲しいからです。

この場合、 ubus -v list [<path>] でオブジェクトの詳細情報を確認することができます。

次は ubus -v list network でnetifdのnetworkオブジェクトが持つメソッドの詳細を表示させた例です。

root@OpenWrt:~# ubus -v list network
'network' @845898a7
        "restart":{}
        "reload":{}
        "add_host_route":{"target":"String","v6":"Boolean","interface":"String","exclude":"Boolean"}
        "get_proto_handlers":{}
        "add_dynamic":{"name":"String"}
        "netns_updown":{"jail":"String","start":"Boolean"}

これを見ると、netifdのnetworkオブジェクトは合計で6つのメソッドを所有していることが分かります。 各メソッドの引数指定についても記載されています。この情報を基にcallコマンドでメソッドを適切に実行することが可能です。

callコマンド

実行フォーマット
 ubus call <path> <method> [<message>] 

UBUSサーバーアプリケーションの公開オブジェクトが管理するメソッドを実行します。

メソッドに対して引数が必要な場合、その引数をJSON形式のデータ(JSON[1])として指定する必要があります。 また、メソッドが呼び出し元に結果を返す場合、JSON形式の結果(JSON[2])が送信されます。

callコマンドによるJSONの流れはメソッドの動作仕様に依存します。引数なしのメソッドもありますし、 処理結果を呼び出し元に返さないメソッドもあります。

実行例を1つ挙げましょう。
これはnetifdが公開するnetwork.deviceオブジェクトのstatusメソッドを引数'{"name":"eth0"}'で呼び出した例です。

statusメソッドに対し、引数としてNIC名のeth0を指定してコール(実行要求)したことになります。

root@OpenWrt:~# ubus call network.device status '{"name":"eth0"}'
{
        "external": false,
        "present": true,
        "type": "Network device",
        "up": true,
        "carrier": true,
        "auth_status": false,
        "link-advertising": [
                "10baseT-H",
                "10baseT-F",
                "100baseT-H",
                "100baseT-F"
        ],
        "link-partner-advertising": [
                "10baseT-H",
                "10baseT-F",
                "100baseT-H",
                "100baseT-F"
        ],
        "link-supported": [
                "10baseT-H",
                "10baseT-F",
                "100baseT-H",
                "100baseT-F"
        ],
...以下省略

結果として、eth0のイーサーネットフレーム関連情報がJSONで返されることが確認できます。

今回は、JSONが実行要求と結果応答のそれぞれで送受信されました。 しかし、例えばlogreadが管理するlogオブジェクトのwriteメソッドなどでは実行要求の時のみ、JSONが送信されます。

このことからも、引数指定や結果応答の有無はオブジェクトとメソッドを公開する各アプリケーションの仕様に依存します。

subscribeコマンド

実行フォーマット
 ubus subscribe <path> [<path>]

任意のオブジェクト<path>の通知を購読(サブスクライブ)します。

このとき、通知をサブスクライブするプロセスを購読者(サブスクライバー)と呼びます。 コマンドプロンプト上でsubscribeコマンドを実行すれば、そのコンソールプロセスがオブジェクト<path>のサブスクライバーになります。

オブジェクト<path>を管理するUBUSサーバーアプリケーション はサブスクライバーに対する通知処理の実装が必要です。

つまり、subscribeコマンドはサブスクライバーに対して通知を送信できるオブジェクト<path> に対してのみ有効なコマンドです。利用者は予め、そのオブジェクトが通知サブスクリプションに対応しているのか知っている必要があります。

オブジェクト通知のサブスクライブはプロセス間通信において、1対多の通信を実現します。

上図のように複数プロセスがオブジェクトのサブスクライバーになったとき、サブスクライバーの全てがオブジェクトの通知を受信します。

これにより、UBUSサーバーアプリケーション側は複数の連携アプリケーション・機能に対して一斉にイベントデータを送信することができます。

実行例
作成中です。

listenコマンド

実行フォーマット
 ubus listen [<path>...] 

UBUS上でやり取りされるイベントをリッスンしてコンソールに表示します。

listenコマンドの使い方は2通りです。全てのイベントを受け取る ubus listen と、特定のイベントのみを受け取る  ubus listen <path> です。

実行例その1:全てのイベントをリッスンする
次はコンソール1でイベントをリッスンし、コンソール2でイベントを送信したときの手順とその結果です。
コンソール1
listenコマンドをバックプロセスで実行します。これにより、sendコマンドで送信されたイベント をリッスンしてその内容を表示することができます。
[1]:listenコマンドの実行
root@OpenWrt:~# ubus listen &
.......イベントのリッスン待ちになります。
コンソール2
上記とは別のコンソール上でsendコマンドを実行し、適当なイベント(JSONデータ)を送信します。
[2]:sendコマンドによるイベント送信
root@OpenWrt:~# ubus send sample '{"user":"utakamo","website":"https://utakamo.com"}'
コンソール1
listenコマンドを実行したコンソールを再度見ると、上記のイベントが表示されていることが確認できます。
root@OpenWrt:~# ubus listen &
root@OpenWrt:~# { "sample": {"user":"utakamo","website":"https://utakamo.com"} }  [3]:イベントのリッスン結果
実行例その2:特定のイベントのみをリッスンする
次はコンソール1でsampleイベントをリッスンし、コンソール2でsampleとcoffeeイベントを送信したときの手順とその結果です。
コンソール1
listenコマンドをバックプロセスで実行します。これにより、sendコマンドで送信されたsampleイベント をリッスンしてその内容を表示することができます。逆に、他のイベントは無視されます。
[1]:listenコマンドの実行
root@OpenWrt:~# ubus listen sample &
.......sampleイベントのリッスン待ちになります。
コンソール2
上記とは別のコンソール上でsendコマンドを実行し、適当なイベント(ここではsampleとcoffeeというイベント名のJSONデータ)を送信します。
[2]:sendコマンドによるイベント送信
root@OpenWrt:~# ubus send sample '{"user":"utakamo","website":"https://utakamo.com"}'
root@OpenWrt:~# ubus send coffee '{"name":"bluemountain","smell":"excellent","taste":"excellent"}'
コンソール1
listenコマンドを実行したコンソールを再度見みると、sampleイベントのみが表示されていることが確認できます。
root@OpenWrt:~# ubus listen sample &
root@OpenWrt:~# { "sample": {"user":"utakamo","website":"https://utakamo.com"} }  [3]:イベントのリッスン結果

sendコマンド

実行フォーマット
 ubus send <type> [<message>] 

UBUSに特定イベント<type>を送信するコマンドです。

このイベントはJSONデータ<message>を任意で持つことができます。

UBUSサーバーアプリケーションの中には、特定イベント<type>がUBUSに届いたときに それを受け取るものがあります。

これは、そのアプリが起動時に特定イベントの受け取りをUBUSに対して予め伝えているからです。 (この部分はlibubusライブラリ(C言語用)を使用して実装していたりします。ちなみにコンソールやスクリプト上ではubus listen [<path>]で受け取ることができます。)

これによって、UBUSサーバーアプリケーションはイベントの受信を契機とした特定処理の実行が可能です。

ポイント
UBUSに登録して、その詳細を外部へ公開するオブジェクト・メソッドと、今回の話で出たイベント受信時の処理は扱いが異なります。

オブジェクト・メソッドはその性質上、ubus -v listなどで、その詳細を利用者が容易に知ることができます。

これは、利用者が特定処理の実行をそのアプリに要求したいことがあり、必要な情報を知りたいケースがあるためです。 (公開オブジェクト・メソッドの機能を利用したアプリケーションの開発や単純なスクリプト作成などが考えられます。)

一方、イベント受信時の処理は、予め利用者がどんなイベントをUBUSに送信すれば望みの処理がされるのか知っている必要があります。 これらの知識はソースコード解析やアプリケーション開発者による仕様公開により手に入れられます。 つまり、特定のアプリケーションがどんなイベントを受信して、どんな処理をするのかという情報はUBUSコマンドでは公開されていません。

一般的に、特定イベント<type>の受信によって実行される処理は、そのアプリケーションまたは システム内部でのみ利用されるものです。外部へ提供されるようなものではありません。

そのため、イベントの送受信とその対応は、そのアプリケーション自身と密接に関連するもの同士だけで行われることが多いです。 エコシステムと今後の拡張性を考慮して外部に機能を提供するというニュアンスよりも、UBUSシステムではそのようなプロセス間通信機能 があるので一連のサービス実装で利用したというニュアンスが強いと考えられます。

よって、このコマンドに対する理解が必要な人は、イベント受信の仕組みを利用するサービスを実装した(したい)開発者です。

実行例
次はsampleイベントを受信(リッスン)するプログラム(ubus-sasmple02)をインストールして、 ubus sendコマンドでイベント(sample-event)を送信してみた例です。
ubus-sample02は、イベントを受け取った回数を/tmp/ubus-sample02/replyに記録します。 ※ちなみにubus-sample02はubus listenコマンド相当のC言語ソフトウェアです。
手順1
ubus-sample02をインストールします。
root@OpenWrt:~# opkg install ubus-sample02_1.0-1_aarch64_cortex-a53.ipk
上記はRaspberry Pi3B用ubus-sample02をインストールする例です。一応、このサイトではラズパイ限定で 各モデル別のパッケージを配布しています。読者の方が持つラズパイのモデルに合わせて対応する
モデル パッケージ ハッシュ(SHA256)
Raspberry Pi1 ubus-sample02_1.0-1_arm_arm1176jzf-s_vfp.ipk a7c4f92ca33294bdc146141bac01433fe0acf4da5b74371d4ecf21545aeb4460
Raspberry Pi2 ubus-sample02_1.0-1_arm_cortex-a7_neon-vfpv4.ipk ddba723629960a18c6aefa691d4373505944c968ad9fa3dc071b371109e86303
Raspberry Pi3 ubus-sample02_1.0-1_aarch64_cortex-a53.ipk b4b3ae504d5b0a2f69db24dcef4b710c2651593e29a853e8d1d4d495f901191f
Raspberry Pi4 ubus-sample02_1.0-1_aarch64_cortex-a72.ipk 08058889d766d537be12c13012dfe275e7539cb71cd98fbd027c8350331c4afa
手順2
次のsendコマンドでsampleイベントをUBUSに送信します。 root@OpenWrt:~# ubus send sample-event
手順3
sample-eventを1回以上送信すると、/tmp/ubus-sample02にreplyファイルが作成されます。 次のようにcatコマンドを実行してファイルの中身を見てみるとreplyファイルにsample-eventの受信回数が表示されます。
root@OpenWrt:~# cat /tmp/ubus-sample02/reply
count:1

wait_forコマンド

フォーマット
 ubus wait_for <object> [<object>...] 

指定オブジェクトがUBUSに公開されるまで実行元のプロセスを待機させるコマンドです。 デフォルトでは30秒の間、引数<object>で指定したオブジェクトの登録を待ちます。 30秒を超過した場合はタイムアウトになります。

wait_forコマンドはアプリケーションにおいて、特定のオブジェクトの登録(連携アプリケーションのインストールやシステムブートなどによるアプリ起動)によって 依存関係を解消する必要のある機能のために用意されています。

つまり、そのオブジェクトがwait_forコマンドのタイムアウト時間内に登録できれば依存関係にある機能を実行し、 逆に登録出来なければその機能の実行を事前にキャンセルするようなスクリプトを書くことができます。 (そのため、オブジェクト登録を待つ性質上、特定アプリケーションの再起動や新規インストール、システムブートなどの場面で利用されることが考えられます。)

その他、特定のアプリケーションの拡張機能としてオブジェクトの登録を待つときなどで使用できます。

このようにwait_forコマンド使用して指定オブジェクトの登録を待つことで、以後、そのオブジェクトが使用できるか否かを判断することができます。

実行例
次はコンソール1でwait_forコマンドを実行してsample01オブジェクトの登録を待ち、 コンソール2でサンプルプログラム(ubus-sample01)のインストールをして、その中のsample01オブジェクトをUBUSに登録してみたときの結果です。
コンソール1
wait_forコマンドを引数sample01で実行します。
これによって最大30秒の間、sample01オブジェクトの登録を待ちます。 root@OpenWrt:~# ubus wait_for sample01
コンソール2
別のコンソール画面を開き、sample01オブジェクトを持つubus-sample01(※)をインストールします。もちろん、30秒以内にインストールしないと コンソール1で実行したwait_forコマンドがタイムアウトしますので急いでやります。 root@OpenWrt:~# opkg install ubus-sample01_1.0-1_aarch64_cortex-a53.ipk
上記はRaspberry Pi3B用ubus-sample01をインストールする例です。一応、このサイトではラズパイ限定で 各モデル別のパッケージを配布しています。読者の方が持つラズパイのモデルに合わせて対応する ubus-sample01をインストールすれば、ここで紹介するwait_forコマンドの実行例を確認することができます。
モデル パッケージ ハッシュ(SHA256)
Raspberry Pi1 ubus-sample01_1.0-1_arm_arm1176jzf-s_vfp.ipk 9fa0bb4dfa60f27c3beeb0b0b86a570d4577f481c8ead7bc28d35ae8ec3ea7c1
Raspberry Pi2 ubus-sample01_1.0-1_arm_cortex-a7_neon-vfpv4.ipk 1a345977f494d1e5d4fef1cf974cdd7592e821ff283707306fb10e329f008dc3
Raspberry Pi3 ubus-sample01_1.0-1_aarch64_cortex-a53.ipk f1043b2e773dc8b37cc8f887c0e3ee45fc1d2ceca2e24de2d6b9c542350f6ef5
Raspberry Pi4 ubus-sample01_1.0-1_aarch64_cortex-a72.ipk bdd486f5f3e6e892846549e2744659ddafeeb594bcc89a756752bae5028f81ac
ラズパイ以外のデバイスで、ここで紹介するwait_forコマンドの実行確認をしたい方は私のGitHubリポジトリからubus-sample01を入手して自身の環境でビルドしたパッケージを使用する必要があります。
コンソール1
wait_forコマンドを実行したコンソール画面を見てみると、sample01オブジェクトの登録待機が完了して操作が戻っていることが確認できます。
root@OpenWrt:~# ubus wait_for sample01   <---前回のコマンド実行
root@OpenWrt:~#   <---sample01オブジェクトが登録された直後にコンソールに操作が戻ります。
以上がwait_forコマンドの実行例でした。ぶっちゃけ言って、これだけの処理です。

monitorコマンド

実行フォーマット
 ubus monitor 

UBUS上で送受信されるデータをモニタリングするコマンドです。実行後に、callコマンドやsendコマンド を使用することで、その際にやり取りされたデータの詳細を確認することができます。

実行例
次はコンソール1でubus monitorコマンドを実行してUBUS上のストリームをモニタリングし、 コンソール2で様々なUBUSサーバーアプリケーションが公開しているオブジェクト・メソッドを実行してみた例です。
コンソール1
コンソールを開き、次のmonitorコマンドを実行します。
root@OpenWrt:~# ubus monitor
-> 43fa907f #00000003         status: {"status":0}
コンソール2
別のコンソールを開き、callコマンドで公開されているオブジェクト・メソッドをテキトーに実行してみます。 ちなみに、今回実行するメソッドは以下の3つです。
  1. ubus call system board
  2. ubus call network.device status '{"name": "eth0"}'
  3. ubus call dnsmasq metrics
root@OpenWrt:~# ubus call system board
{
        "kernel": "5.15.102",
        "hostname": "OpenWrt",
        "system": "ARMv8 Processor rev 4",
        "model": "Raspberry Pi 3 Model B Rev 1.2",
        "board_name": "raspberrypi,3-model-b",
        "rootfs_type": "ext4",
        "release": {
                "distribution": "OpenWrt",
                "version": "SNAPSHOT",
                "revision": "r22371-0eebc6f0dd",
                "target": "bcm27xx/bcm2710",
                "description": "OpenWrt SNAPSHOT r22371-0eebc6f0dd"
        }
}
root@OpenWrt:~# ubus call network.device status '{"name": "eth0"}'
{
        "external": false,
        "present": true,
        "type": "Network device",
        "up": true,
        "carrier": true,
        "auth_status": false,
        "link-advertising": [
                "10baseT-H",
                "10baseT-F",
                "100baseT-H",
                "100baseT-F"
        ],
        "link-partner-advertising": [
                "10baseT-H",
                "10baseT-F",
                "100baseT-H",
                "100baseT-F"
        ],
        "link-supported": [
                "10baseT-H",
                "10baseT-F",
                "100baseT-H",
                "100baseT-F"
        ],
        "speed": "100F",
        "autoneg": true,
        "hw-tc-offload": false,
        "devtype": "ethernet",
        "mtu": 1500,
        "mtu6": 1500,
        "macaddr": "b8:27:eb:b8:6b:03",
        "txqueuelen": 1000,
        "ipv6": true,
        "ip6segmentrouting": false,
        "promisc": false,
        "rpfilter": 0,
        "acceptlocal": false,
        "igmpversion": 0,
        "mldversion": 0,
        "neigh4reachabletime": 30000,
        "neigh6reachabletime": 30000,
        "neigh4gcstaletime": 60,
        "neigh6gcstaletime": 60,
        "neigh4locktime": 100,
        "dadtransmits": 1,
        "multicast": true,
        "sendredirects": true,
        "drop_v4_unicast_in_l2_multicast": false,
        "drop_v6_unicast_in_l2_multicast": false,
        "drop_gratuitous_arp": false,
        "drop_unsolicited_na": false,
        "arp_accept": false,
        "statistics": {
                "collisions": 0,
                "rx_frame_errors": 0,
                "tx_compressed": 0,
                "multicast": 0,
                "rx_length_errors": 0,
                "tx_dropped": 0,
                "rx_bytes": 1354728,
                "rx_missed_errors": 0,
                "tx_errors": 0,
                "rx_compressed": 0,
                "rx_over_errors": 0,
                "tx_fifo_errors": 0,
                "rx_crc_errors": 0,
                "rx_packets": 2140,
                "tx_heartbeat_errors": 0,
                "rx_dropped": 0,
                "tx_aborted_errors": 0,
                "tx_packets": 1762,
                "rx_errors": 0,
                "tx_bytes": 487287,
                "tx_window_errors": 0,
                "rx_fifo_errors": 0,
                "tx_carrier_errors": 0
        }
}
root@OpenWrt:~# ubus call dnsmasq metrics
{
        "dns_cache_inserted": 274,
        "dns_cache_live_freed": 0,
        "dns_queries_forwarded": 124,
        "dns_auth_answered": 0,
        "dns_local_answered": 82,
        "dns_stale_answered": 0,
        "dns_unanswered": 1,
        "bootp": 0,
        "pxe": 0,
        "dhcp_ack": 1,
        "dhcp_decline": 0,
        "dhcp_discover": 1,
        "dhcp_inform": 0,
        "dhcp_nak": 0,
        "dhcp_offer": 1,
        "dhcp_release": 0,
        "dhcp_request": 1,
        "noanswer": 0,
        "leases_allocated_4": 1,
        "leases_pruned_4": 0,
        "leases_allocated_6": 0,
        "leases_pruned_6": 0
}
コンソール1
monitorコマンドを実行したコンソール画面を見てみると、上記で実行したcallコマンドによるUBUSストリーム上のデータの 流れが表示されていることが確認できます。
root@OpenWrt:~# ubus monitor
-> 43fa907f #00000003         status: {"status":0}
↑前回までのmonitorコマンドの実行結果。↓がモニタ内容。

[ubus call system board]のモニタリング内容
-> 517099f6 #517099f6          hello: {}
<- 517099f6 #00000000         lookup: {"objpath":"system"}
-> 517099f6 #00000000           data: {"objpath":"system","objid":-1792756799,"objtype":620610073,"signature":{"board":{},"info":{},"reboot":{},"watchdog":{"frequency":5,"timeout":5,"magicclose":7,"stop":7},"signal":{"pid":5,"signum":5},"validate_firmware_image":{"path":3},"sysupgrade":{"path":3,"force":7,"backup":3,"prefix":3,"command":3,"options":2}}}
-> 517099f6 #00000000         status: {"status":0}
<- 517099f6 #9524b3c1         invoke: {"objid":-1792756799,"method":"board","data":{}}
-> db052ea0 #517099f6         invoke: {"objid":-1792756799,"method":"board","data":{},"user":"root","group":"root"}
<- db052ea0 #517099f6           data: {"objid":-1792756799,"data":{"kernel":"5.15.102","hostname":"OpenWrt","system":"ARMv8 Processor rev 4","model":"Raspberry Pi 3 Model B Rev 1.2","board_name":"raspberrypi,3-model-b","rootfs_type":"ext4","release":{"distribution":"OpenWrt","version":"SNAPSHOT","revision":"r22371-0eebc6f0dd","target":"bcm27xx/bcm2710","description":"OpenWrt SNAPSHOT r22371-0eebc6f0dd"}}}
-> 517099f6 #9524b3c1           data: {"objid":-1792756799,"data":{"kernel":"5.15.102","hostname":"OpenWrt","system":"ARMv8 Processor rev 4","model":"Raspberry Pi 3 Model B Rev 1.2","board_name":"raspberrypi,3-model-b","rootfs_type":"ext4","release":{"distribution":"OpenWrt","version":"SNAPSHOT","revision":"r22371-0eebc6f0dd","target":"bcm27xx/bcm2710","description":"OpenWrt SNAPSHOT r22371-0eebc6f0dd"}}}
<- db052ea0 #517099f6         status: {"status":0,"objid":-1792756799}
-> 517099f6 #9524b3c1         status: {"status":0,"objid":-1792756799}

[ubus call network.device status '{"name": "eth0"}']のモニタリング内容
-> 4c75ad13 #4c75ad13          hello: {}
<- 4c75ad13 #00000000         lookup: {"objpath":"network.device"}
-> 4c75ad13 #00000000           data: {"objpath":"network.device","objid":1650086866,"objtype":909749173,"signature":{"status":{"name":3},"set_alias":{"alias":1,"device":3},"set_state":{"name":3,"defer":7,"auth_status":7},"stp_init":{}}}
-> 4c75ad13 #00000000         status: {"status":0}
<- 4c75ad13 #625a53d2         invoke: {"objid":1650086866,"method":"status","data":{"name":"eth0"}}
-> 43024ada #4c75ad13         invoke: {"objid":1650086866,"method":"status","data":{"name":"eth0"},"user":"root","group":"root"}
<- 43024ada #4c75ad13           data: {"objid":1650086866,"data":{"external":false,"present":true,"type":"Network device","up":true,"carrier":true,"auth_status":false,"link-advertising":["10baseT-H","10baseT-F","100baseT-H","100baseT-F"],"link-partner-advertising":["10baseT-H","10baseT-F","100baseT-H","100baseT-F"],"link-supported":["10baseT-H","10baseT-F","100baseT-H","100baseT-F"],"speed":"100F","autoneg":true,"hw-tc-offload":false,"devtype":"ethernet","mtu":1500,"mtu6":1500,"macaddr":"b8:27:eb:b8:6b:03","txqueuelen":1000,"ipv6":true,"ip6segmentrouting":false,"promisc":false,"rpfilter":0,"acceptlocal":false,"igmpversion":0,"mldversion":0,"neigh4reachabletime":30000,"neigh6reachabletime":30000,"neigh4gcstaletime":60,"neigh6gcstaletime":60,"neigh4locktime":100,"dadtransmits":1,"multicast":true,"sendredirects":true,"drop_v4_unicast_in_l2_multicast":false,"drop_v6_unicast_in_l2_multicast":false,"drop_gratuitous_arp":false,"drop_unsolicited_na":false,"arp_accept":false,"statistics":{"collisions":0,"rx_frame_errors":0,"tx_compressed":0,"multicast":0,"rx_length_errors":0,"tx_dropped":0,"rx_bytes":1354728,"rx_missed_errors":0,"tx_errors":0,"rx_compressed":0,"rx_over_errors":0,"tx_fifo_errors":0,"rx_crc_errors":0,"rx_packets":2140,"tx_heartbeat_errors":0,"rx_dropped":0,"tx_aborted_errors":0,"tx_packets":1762,"rx_errors":0,"tx_bytes":487287,"tx_window_errors":0,"rx_fifo_errors":0,"tx_carrier_errors":0}}}
-> 4c75ad13 #625a53d2           data: {"objid":1650086866,"data":{"external":false,"present":true,"type":"Network device","up":true,"carrier":true,"auth_status":false,"link-advertising":["10baseT-H","10baseT-F","100baseT-H","100baseT-F"],"link-partner-advertising":["10baseT-H","10baseT-F","100baseT-H","100baseT-F"],"link-supported":["10baseT-H","10baseT-F","100baseT-H","100baseT-F"],"speed":"100F","autoneg":true,"hw-tc-offload":false,"devtype":"ethernet","mtu":1500,"mtu6":1500,"macaddr":"b8:27:eb:b8:6b:03","txqueuelen":1000,"ipv6":true,"ip6segmentrouting":false,"promisc":false,"rpfilter":0,"acceptlocal":false,"igmpversion":0,"mldversion":0,"neigh4reachabletime":30000,"neigh6reachabletime":30000,"neigh4gcstaletime":60,"neigh6gcstaletime":60,"neigh4locktime":100,"dadtransmits":1,"multicast":true,"sendredirects":true,"drop_v4_unicast_in_l2_multicast":false,"drop_v6_unicast_in_l2_multicast":false,"drop_gratuitous_arp":false,"drop_unsolicited_na":false,"arp_accept":false,"statistics":{"collisions":0,"rx_frame_errors":0,"tx_compressed":0,"multicast":0,"rx_length_errors":0,"tx_dropped":0,"rx_bytes":1354728,"rx_missed_errors":0,"tx_errors":0,"rx_compressed":0,"rx_over_errors":0,"tx_fifo_errors":0,"rx_crc_errors":0,"rx_packets":2140,"tx_heartbeat_errors":0,"rx_dropped":0,"tx_aborted_errors":0,"tx_packets":1762,"rx_errors":0,"tx_bytes":487287,"tx_window_errors":0,"rx_fifo_errors":0,"tx_carrier_errors":0}}}
<- 43024ada #4c75ad13         status: {"status":0,"objid":1650086866}
-> 4c75ad13 #625a53d2         status: {"status":0,"objid":1650086866}

[ubus call dnsmasq metrics]のモニタリング内容
-> b4c77460 #b4c77460          hello: {}
<- b4c77460 #00000000         lookup: {"objpath":"dnsmasq"}
-> b4c77460 #00000000           data: {"objpath":"dnsmasq","objid":-387274241,"objtype":-761337583,"signature":{"metrics":{}}}
-> b4c77460 #00000000         status: {"status":0}
<- b4c77460 #e8eaa9ff         invoke: {"objid":-387274241,"method":"metrics","data":{}}
-> 22024e15 #b4c77460         invoke: {"objid":-387274241,"method":"metrics","data":{},"user":"root","group":"root"}
<- 22024e15 #b4c77460           data: {"objid":-387274241,"data":{"dns_cache_inserted":274,"dns_cache_live_freed":0,"dns_queries_forwarded":124,"dns_auth_answered":0,"dns_local_answered":82,"dns_stale_answered":0,"dns_unanswered":1,"bootp":0,"pxe":0,"dhcp_ack":1,"dhcp_decline":0,"dhcp_discover":1,"dhcp_inform":0,"dhcp_nak":0,"dhcp_offer":1,"dhcp_release":0,"dhcp_request":1,"noanswer":0,"leases_allocated_4":1,"leases_pruned_4":0,"leases_allocated_6":0,"leases_pruned_6":0}}
-> b4c77460 #e8eaa9ff           data: {"objid":-387274241,"data":{"dns_cache_inserted":274,"dns_cache_live_freed":0,"dns_queries_forwarded":124,"dns_auth_answered":0,"dns_local_answered":82,"dns_stale_answered":0,"dns_unanswered":1,"bootp":0,"pxe":0,"dhcp_ack":1,"dhcp_decline":0,"dhcp_discover":1,"dhcp_inform":0,"dhcp_nak":0,"dhcp_offer":1,"dhcp_release":0,"dhcp_request":1,"noanswer":0,"leases_allocated_4":1,"leases_pruned_4":0,"leases_allocated_6":0,"leases_pruned_6":0}}
<- 22024e15 #b4c77460         status: {"status":0,"objid":-387274241}
-> b4c77460 #e8eaa9ff         status: {"status":0,"objid":-387274241}

おわりに

今回はUBUSコマンドの使い方について説明しました。

正直、UBUSコマンドはエンドユーザーの方にとって、UCIコマンド程の重要度はないと思います。

エンドユーザーとしてOpenWrtの機能を使いたい人にとっては、意識する必要のないコマンドです。 (もちろん、問題対処のためにUBUSについての理解があると良いのは言うまでもありませんが。)

UBUSシステムが提供するIPCと、他モジュールから提供されるRPC(Remote Procedure Call)を組み合わせることで、開発者が実装した 機能をLuCiのWebUIに提供できるようになります。

つまり、UBUSシステムやUBUSコマンドは開発者なら知っておきたい知識です。

今後、関連記事として個人開発したアプリケーション用ページをWebUIのLuCiに組み込み方法について書きたいと思います。 このときに、LuCiとアプリケーション間の通信でUBUSが使用されますので、実際に使用されるケースが理解できるように書きたいと思います。

記事作成に時間が掛かると思いますが、しばらくお持ちください。

参考文献