TL;DR
- QtDBus で書いたアプリと CLI から通信してみよう
- dbus-send で API を叩く
- dbus-monitor で監視してみる
はじめに
前回書いたサンプルの dbusserver/dbusclient について、
テストやデバッグのときは外からアクセス・監視ができると便利です。CLI から叩いてみましょう。
CLIから D-Bus を叩く
dbus-send コマンドを使います。
まず dbusserver に定義・設定した D-Bus 上の固有情報をメモっておきます。
Q_CLASSINFO("D-Bus Service", "local.myhost")
Q_CLASSINFO("D-Bus Path", "/QtDBusDemo/DBusServer")
Q_CLASSINFO("D-Bus Interface", "api.dbusserver")
サーバーの動作確認
まず D-Bus 上にサービスが登録されているのかを確認します。
D-Bus 上の共通インターフェースから情報を取ってみます。
$ dbus-send --session --print-reply --type=method_call --dest=org.freedesktop.DBus /org/freedesktop/DBus org.freedesktop.DBus.ListNames
method return time=1662279963.823035 sender=org.freedesktop.DBus -> destination=:1.171 serial=3 reply_serial=2
array [
string "org.freedesktop.DBus"
string "org.freedesktop.Notifications"
...
string "local.myhost"
...
]
local.myhost がいるのでサービスは登録されているみたいですね。
次に Path と Interface がどう認識されているか。local.myhost の "/" の情報をとってみます。
$ dbus-send --session --print-reply --type=method_call --dest=local.myhost / org.freedesktop.DBus.Introspectable.Introspect
method return time=1662280010.175647 sender=:1.170 -> destination=:1.172 serial=9 reply_serial=2
string "<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.freedesktop.DBus.Introspectable">
<method name="Introspect">
<arg name="xml_data" type="s" direction="out"/>
</method>
</interface>
<interface name="org.freedesktop.DBus.Peer">
...
</interface>
<node name="QtDBusDemo"/>
</node>
"
QtDBusDemo という node が見えているのでそこをもう一度。
$ dbus-send --session --dest=local.myhost --type=method_call --print-reply /QtDBusDemo org.freedesktop.DBus.Introspectable.Introspect
method return time=1662280043.904308 sender=:1.170 -> destination=:1.173 serial=10 reply_serial=2
string "<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.freedesktop.DBus.Introspectable">
<method name="Introspect">
<arg name="xml_data" type="s" direction="out"/>
</method>
</interface>
<interface name="org.freedesktop.DBus.Peer">
...
</interface>
<node name="DBusServer"/>
</node>
"
さらに DBusService が見えているのでさらにもう一度。
$ dbus-send --session --dest=local.myhost --type=method_call --print-reply /QtDBusDemo/DBusServer org.freedesktop.DBus.Introspectable.Introspect
method return time=1662280080.064612 sender=:1.170 -> destination=:1.174 serial=11 reply_serial=2
string "<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="api.dbusserver">
<property name="count" type="i" access="readwrite"/>
<signal name="countChanged">
<arg name="newCount" type="i" direction="out"/>
</signal>
<method name="setCount">
<arg name="newCount" type="i" direction="in"/>
</method>
<method name="reset">
</method>
</interface>
<interface name="org.freedesktop.DBus.Properties">
<method name="Get">
<arg name="interface_name" type="s" direction="in"/>
<arg name="property_name" type="s" direction="in"/>
<arg name="value" type="v" direction="out"/>
</method>
<method name="Set">
<arg name="interface_name" type="s" direction="in"/>
<arg name="property_name" type="s" direction="in"/>
<arg name="value" type="v" direction="in"/>
</method>
<method name="GetAll">
<arg name="interface_name" type="s" direction="in"/>
<arg name="values" type="a{sv}" direction="out"/>
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
</method>
<signal name="PropertiesChanged">
<arg name="interface_name" type="s" direction="out"/>
<arg name="changed_properties" type="a{sv}" direction="out"/>
<annotation name="org.qtproject.QtDBus.QtTypeName.Out1" value="QVariantMap"/>
<arg name="invalidated_properties" type="as" direction="out"/>
</signal>
</interface>
<interface name="org.freedesktop.DBus.Introspectable">
<method name="Introspect">
<arg name="xml_data" type="s" direction="out"/>
</method>
</interface>
<interface name="org.freedesktop.DBus.Peer">
<method name="Ping"/>
<method name="GetMachineId">
<arg name="machine_uuid" type="s" direction="out"/>
</method>
</interface>
</node>
"
長いのが返ってきました。
登録した Interface である ”api.dbusserver” も見えていますね。
- プロパティ count
- integer型で読み書き可能
- countChanged シグナル
- integer型の戻り値1つ
- setCount メソッド
- integer型の引数1つ
- reset メソッド
- 引数なし
”org.freedesktop.DBus.Properties” なるインターフェースは後述します。
サーバーへのアクセス
アクセスに必要な情報は揃ったので叩いてみましょう。
メソッドの呼び出し
reset() の呼び出し
引数なしのメソッドです。
## reset()
$ dbus-send --session --print-reply --type=method_call --dest=local.myhost /QtDBusDemo/DBusServer api.dbusserver.reset
method return time=1662345793.539809 sender=:1.217 -> destination=:1.219 serial=10 reply_serial=2
簡単ですね。
reset() を呼び出したので dbusserver のログに ”reset” とそこから呼ばれる ”setCount” が出ているはずです。
setCount() の呼び出し
引数ありのメソッドです。アクセスの仕方は同じ。
引数は定義したとおり int32 型です。
## setCount()
$ dbus-send --session --print-reply --type=method_call --dest=local.myhost /QtDBusDemo/DBusServer api.dbusserver.setCount int32:10
method return time=1662345887.700270 sender=:1.217 -> destination=:1.223 serial=13 reply_serial=2
dbusserver の画面上の表示も変わっていると思います。
また setCount() を呼び出したので dbusserver のログに ”setCount” が出ているはずです。
count() の呼び出し
count() にもアクセスしてみましょう。
コード上では公開してないし、D-Bus からとった情報にもないので失敗します。
## count() will FAIL
$ dbus-send --session --print-reply --type=method_call --dest=local.myhost /QtDBusDemo/DBusServer api.dbusserver.count
Error org.freedesktop.DBus.Error.UnknownMethod: No such method 'count' in interface 'api.dbusserver' at object path '/QtDBusDemo/DBusServer' (signature '')
とはいえ、これでは count() の値がとれません。
対応は2つ。
- count()に (reset()と同じように) Q_INVOKABLEをつけて公開しておく
- D-Bus property として Get する
Q_INVOKABLEをつけてみると、アクセスできます。
diff --git a/dbusserver/dbusserver.h b/dbusserver/dbusserver.h
index a7a11d4..76c2c6d 100644
--- a/dbusserver/dbusserver.h
+++ b/dbusserver/dbusserver.h
@@ -15,7 +15,7 @@ public:
explicit DBusServer(QObject *parent = nullptr);
~DBusServer();
- int count() const; // NOT export
+ Q_INVOKABLE int count() const; // export
Q_INVOKABLE void reset(); // export
public Q_SLOTS:
## Q_INVOKABLE count()
$ dbus-send --session --print-reply --type=method_call --dest=local.myhost /QtDBusDemo/DBusServer api.dbusserver.count
method return time=1662346098.213333 sender=:1.231 -> destination=:1.234 serial=12 reply_serial=2
int32 10
次はプロパティとしてアクセスしてみましょう。
プロパティの操作
「プロパティ」はD-Busの共通した操作としてアクセスできます。
<interface name="org.freedesktop.DBus.Properties">
<method name="Get"></method>
<method name="Set"></method>
<method name="GetAll"></method>
<signal name="PropertiesChanged"></signal>
</interface>
Get は、interface と プロパティ名を渡します。
## service.interface の count を読み出し
$ dbus-send --session --print-reply --type=method_call --dest=local.myhost /QtDBusDemo/DBusServer org.freedesktop.DBus.Properties.Get string:api.dbusserver string:count
method return time=1662346028.965687 sender=:1.217 -> destination=:1.229 serial=18 reply_serial=2
variant int32 10
dbusserver のログに ”count” が出ているので count() にアクセスしていることがわかります。
Set は、interface とプロパティ名、さらに variant 型で int32 型の値を渡します。
## service.interface の count に 21 を書き込み
$ dbus-send --session --print-reply --type=method_call --dest=local.myhost /QtDBusDemo/DBusServer org.freedesktop.DBus.Properties.Set string:api.dbusserver string:count variant:int32:21
method return time=1662346130.582755 sender=:1.231 -> destination=:1.235 serial=14 reply_serial=2
dbusserver のログに ”setCount” が出ているので setCcount() にアクセスしていることがわかります。
モニタリング
次は通信自体を監視してみましょう。
バス全体のモニタリング
dbusserver と dbusclient を実行して動作状態にしておきます。
以下のコマンドを実行して、
## セッションバス全体をモニタリング
$ dbus-monitor --session
dbusclient を操作するとログが出てきます。
しかし、セッションバス全体を見ているので dbusclient 以外の、例えば pulseaudio や WindowManager のアクセスも全部ログに出てきてしまいます。
監視対象を絞る
dbusserver の Path のアクセスだけに限定してみましょう。引数に指定するだけです。
## /QtDBusDemo/DBusServer のものだけをモニタリング
$ dbus-monitor --session path=/QtDBusDemo/DBusServer
signal time=1662346199.077912 sender=org.freedesktop.DBus -> destination=:1.237 serial=2 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameAcquired
string ":1.237"
signal time=1662346199.077940 sender=org.freedesktop.DBus -> destination=:1.237 serial=4 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameLost
string ":1.237"
## dbusclient で 13 を 'send'
method call time=1662346219.949451 sender=:1.236 -> destination=local.myhost serial=16 path=/QtDBusDemo/DBusServer; interface=api.dbusserver; member=setCount
int32 13
signal time=1662346219.950398 sender=:1.231 -> destination=(null destination) serial=17 path=/QtDBusDemo/DBusServer; interface=api.dbusserver; member=countChanged
int32 13
method call time=1662346219.951031 sender=:1.236 -> destination=local.myhost serial=17 path=/QtDBusDemo/DBusServer; interface=org.freedesktop.DBus.Properties; member=Get
string "api.dbusserver"
string "count"
## dbuclient で 'reset'
method call time=1662346268.393464 sender=:1.236 -> destination=local.myhost serial=18 path=/QtDBusDemo/DBusServer; interface=api.dbusserver; member=reset
signal time=1662346268.394331 sender=:1.231 -> destination=(null destination) serial=20 path=/QtDBusDemo/DBusServer; interface=api.dbusserver; member=countChanged
int32 0
method call time=1662346268.395057 sender=:1.236 -> destination=local.myhost serial=19 path=/QtDBusDemo/DBusServer; interface=org.freedesktop.DBus.Properties; member=Get
string "api.dbusserver"
string "count"
D-Bus 上の接続名が以下のようになっていて、senderの項目で誰のアクセスかがわかります。
- :1.231 dbusserver
- :1.236 dbusclient
- :1.237 dbus-monitor
例えば setCount() のときの動きを見てみると、以下のようになっています。
- dbusclient から 'setCount' にアクセス
- (dbusserver 内部の動き)
- dbusserver の setCount() が実行される
- 値が変更されると dbusserver が SIGNAL(countChanged) を emit
- SIGNAL が D-Bus 上に通知される
- それを受けた dbusclient が dbusserver のプロパティ 'count' を Get
- (これは QtQuick のプロパティバインディングによる)
まとめ
DBus サーバーアプリとクライアントアプリを作成して、CLI からそれを叩けるようになりました。
また、通信を監視してログがとれました。
デバッグなどに少しでも役立てば幸いです。