OpenWrt サンプルプログラム掲載! UBUS用C言語ライブラリ-libubus(listenコマンド実装)
この記事で紹介するソースコードは自由に使っていただいて構いません。 アプリケーションの開発や自己学習にお役立て下さい。ただし、当ブログでは掲載するソースコードを流用・利用したことによる損害等につきましては 一切の責任を負いません。自己責任で利用お願いします。
今回は、自作アプリケーションにUBUSのlistenコマンドと同等な処理を実装る方法について紹介します。
UBUSのlistenコマンドはUBUS上でやり取りされるイベント(JSON)をリッスンする処理です。
イベントのリッスン機能を実装すれば、他のプロセスと協調して特定処理を実行することが可能です。 (もちろん、実装に依存するところではありますが)
今回は、そんなUBUSのlistenコマンドと同等な処理のC言語実装について紹介します。
UBUS学習の参考サイト紹介
UBUSの学習に役立つサイトを紹介します。以下のサイトを読むことでコマンドの使用方法だけでなく、その実装方法についても理解できます。
私の記事と併用して読むとより理解に繋がると思います。
目次
サンプルプログラム(ubus-sample02)のダウンロード
今回取り上げるubus-sample02のソースは私のGitHubリポジトリ(UtakamoStudyApps)にあります。
読者ご自身の環境で本アプリをビルド・インストールしてみたい方は私のGitHubリポジトリをダウンロードするか、 次のようにクローンしてください。
OpenWrt:~# git clone https://github.com/utakamo/UtakamoStudyApps.git
なお、ビルド方法に関してはGitHubのリポジトリ説明を参照するか、以下の記事をご覧ください。
【第 6 回】OpenWrt開発入門 自作アプリのパッケージ作成とインストール - パッケージ作成手順
【ラズパイ限定】このサイトからパッケージを入手してインストールする。
このサイトでは、ラズベリーパイ限定でパッケージを配布しています。 シリーズ別のパッケージを以下に掲載しますので、インストールしてみてください。
モデル | パッケージ | ハッシュ(SHA256) |
---|---|---|
Raspberry Pi1 | ubus-sample02_1.0-1_arm_arm1176jzf-s_vfp.ipk | 34811e5ac41c297a6266d7007d972ea7097fe83df0d0f3c1db062494f10c60b4 |
Raspberry Pi2 | ubus-sample02_1.0-1_arm_cortex-a7_neon-vfpv4.ipk | 91eb648aa3959c1e3b0963bd472baf8bd7e417ce4857693e2114ae38b879687a |
Raspberry Pi3 | ubus-sample02_1.0-1_aarch64_cortex-a53.ipk | d45d787795df485c7611544835fcd6bfb08d9a83e5b3b31edbe0b62bbc06ac3a |
Raspberry Pi4 | ubus-sample02_1.0-1_aarch64_cortex-a72.ipk | c71af3cee5d619ae1db0c5e65c5bb521ad835d7961359e2dfd179433a37427a5 |
次のコマンドを実行すると、パッケージをインストールすることができます。(以下はRaspberry Pi3用パッケージのインストール例です。)
root@OpenWrt:~# wget https://utakamo.com/repo/openwrt/raspi3/ubus/ubus-sample02_1.0-1_aarch64_cortex-a53.ipk root@OpenWrt:~# opkg install ubus-sample02_1.0-1_aarch64_cortex-a53.ipk
ソースコードの紹介
ubus-sample02の初期化スクリプト
このサイトで紹介している他のサンプルプログラムと同じ内容です。
ubus-sample02はprocdによってシステムブート時に優先順位99番目(START=99)に起動され、シャットダウン時に 優先順位10番目(STOP=10)で終了するように設定しています。
#!/bin/sh /etc/rc.common
USE_PROCD=1
START=99
STOP=10
start_service() {
echo 'ubus-sample02 start'
procd_open_instance 'ubus-sample02'
procd_set_param command /usr/bin/ubus-sample02
procd_close_instance
}
ubus-sample02のソースコード
今回はubus-sample02と命名したサンプルプログラムにlistenコマンドと同等な処理を実装しました。
UBUSの各コマンド実装は、libubus.hに記述されていますので、これを利用して以下のように listenコマンドの処理をする関数(reply_sample_event)を定義しました。
#include <stdio.h> #include <string.h> #include <unistd.h> #include <libubus.h> #include <libubox/uloop.h> #include <libubox/blobmsg_json.h> enum { TEST_MESSAGE, TEST_MAX }; static const struct blobmsg_policy test_policy[] = { [TEST_MESSAGE] = { .name = "message", .type = BLOBMSG_TYPE_STRING }, }; void ubus_process(void); static void ubus_sample_handle_signal(int signo); static void ubus_sample_setup_signals(void); static int reply_cnt; static void reply_sample_event(struct ubus_context *, struct ubus_event_handler *, const char *, struct blob_attr *); struct ubus_event_handler ev = { .cb = reply_sample_event, }; int main () { ubus_sample_setup_signals(); ubus_process(); return 0; } void ubus_process(void) { uloop_init(); struct ubus_context *ctx = ubus_connect(NULL); ubus_add_uloop(ctx); ubus_register_event_handler(ctx, &ev, "sample-event"); uloop_run(); uloop_done(); } static void ubus_sample_handle_signal(int signo) { uloop_end(); } static void ubus_sample_setup_signals(void) { struct sigaction s; memset(&s, 0, sizeof(s)); s.sa_handler = ubus_sample_handle_signal; s.sa_flags = 0; sigaction(SIGTERM, &s, NULL); } static void reply_sample_event(struct ubus_context *ctx, struct ubus_event_handler *ev, const char *method, struct blob_attr *msg) { struct blob_attr *tb[TEST_MESSAGE]; blobmsg_parse(test_policy, TEST_MAX, tb, blob_data(msg), blob_len(msg)); if (!tb[TEST_MESSAGE]) { return; } char *message = blobmsg_get_string(tb[TEST_MESSAGE]); FILE *fp = NULL; mkdir("/tmp/ubus-sample02", 0755); if ((fp = fopen("/tmp/ubus-sample02/reply", "a")) == NULL) { return; } reply_cnt++; fprintf(fp, "[count:%d], message = %s\n", reply_cnt, message); fclose(fp); }
このubus-sample02はイベントのsample-eventをリッスンします。
sample-eventがubusdから通知されると、sample-eventが持つJSONのmessageの 内容を一時ファイルの/tmp/ubus-sample02/replyに描きだします。
受け取ったsample-eventのJSONを受け取り、その内容を一時ファイルに出力する処理がreply_sample_eventです。
reply_sample_eventは受け取ったsample-eventがメッセージデータ場合に限り反応します。 つまり、他のプロセスが次のようにイベントを発行しない限り、ubus-sample02は結果を一時ファイルに出力しません。
ubus send sample-event '{"message":"Hello"}'
reply_sample_eventはイベント受信時にコールバックされるように、ubus-sample02起動時にubus_register_event_handler関数によって 登録する必要があります。
実行結果
ubus-sample02を実行すると、sample-eventの受信待ちとなります。
そのため、UBUSのsendコマンドで以下のようにsample-eventを送信します。
root@OpenWrt:~# ubus send sample-event '{"message":"Good Morning!"}' root@OpenWrt:~# ubus send sample-event '{"message":"Hello!"}' root@OpenWrt:~# ubus send sample-event '{"message":"Good Bye!"}'
ubus-sample02がsample-eventを受信すると、イベントの受信回数と共にメッセージ内容を/tmp/ubus-sample02/replyに出力します。
root@OpenWrt:~# cat /tmp/ubus-sample02/reply [count:1], message = Good Morning! [count:2], message = Hello! [count:3], message = Good Bye!
おわりに
今回はUBUSのlistenコマンドと同等な処理を実現するためのC言語実装の方法について紹介しました。
他プロセスと協調して動作する必要があるサービス開発をするときなどで、イベントの受信待ちが必要なケースがあります。
適宜、実装を検討すると良いと思います。
参考文献
- ubus (OpenWrt micro bus architecture) https://openwrt.org/docs/techref/ubus
- What is UBUS? https://hackmd.io/@rYMqzC-9Rxy0Isn3zClURg/H1BY98bRw
- OpenWRT UBUS mechanism https://www.programmersought.com/article/96207892503/
- One, ubus module introduction https://www.programmersought.com/article/32835484946/
- Openwrt ubus: An example of interprocess communication https://www.programmersought.com/article/26351479004/
- The method of invoke realizes end-to-end communication https://www.programmersought.com/article/48733667380/