0
Help us understand the problem. What are the problem?

posted at

updated at

Organization

D-Bus と QtDBus (その2:CLIから叩く)

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() のときの動きを見てみると、以下のようになっています。

  1. dbusclient から 'setCount' にアクセス
  2. (dbusserver 内部の動き)
    1. dbusserver の setCount() が実行される
    2. 値が変更されると dbusserver が SIGNAL(countChanged) を emit
  3. SIGNAL が D-Bus 上に通知される
  4. それを受けた dbusclient が dbusserver のプロパティ 'count' を Get
    1. (これは QtQuick のプロパティバインディングによる)

まとめ

DBus サーバーアプリとクライアントアプリを作成して、CLI からそれを叩けるようになりました。
また、通信を監視してログがとれました。

デバッグなどに少しでも役立てば幸いです。

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
0
Help us understand the problem. What are the problem?