經過幾年發展,DBus 已經取代早年 Linux 桌面環境所用的 GNOME Bonobo, KDE DCOP,用於許多應用程式,成為主流 IPC 系統。隨著軟體原件逐漸成熟,眾多程式語言都已經支援 DBus APIs,DBus daemon 的 footprint 也逐漸能夠被嵌入式所接受,而被行動裝置作業系統如 MeeGo, WebOS 所採用。

Linux 開發者難免因爲介接需求,需要測試或使用 DBus 除錯。這裡分享幾個常用的小技巧。DBus 使用物件導向的 API 界面,所有 ServicesObject 函式都是以 Method, Signals, Properties 的概念揭露給外界存取,配合 Introspectable API,很容易讓第三方介接。

最常用到的工具之一是 dbus-send,它可以用來從指令列測試接傳 DBus messages,像是列出系統上所有註冊在 Session BusServices

$ dbus-send --session --print-reply --reply-timeout=2000 \
--type=method_call --dest=org.freedesktop.DBus /org/freedesktop/DBus  \
org.freedesktop.DBus.ListActivatableNames

有了 Services 名稱,接下來你就可以用 Service 為名以 Introspection 界面去查詢其所開放之 API,如

$ dbus-send --session --print-reply --reply-timeout=2000 --type=method_call \
--dest=org.gnome.ScreenSaver / org.freedesktop.DBus.Introspectable.Introspect
method return sender=:1.7256 -> dest=:1.7286 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="data" direction="out" type="s"/>
 </method>
 </interface>
 <interface name="org.gnome.ScreenSaver">
 <method name="Lock">
 </method>
 <method name="Cycle">
 </method>
 <method name="SimulateUserActivity">
 </method>
 <method name="Inhibit">
 <arg name="application_name" direction="in" type="s"/>
 <arg name="reason" direction="in" type="s"/>
 <arg name="cookie" direction="out" type="u"/>
 </method>
 <method name="UnInhibit">
 <arg name="cookie" direction="in" type="u"/>
 </method>
 <method name="GetInhibitors">
 <arg name="list" direction="out" type="as"/>
 </method>
 <method name="Throttle">
 <arg name="application_name" direction="in" type="s"/>
 <arg name="reason" direction="in" type="s"/>
 <arg name="cookie" direction="out" type="u"/>
 </method>
 <method name="UnThrottle">
 <arg name="cookie" direction="in" type="u"/>
 </method>
 <method name="GetActive">
 <arg name="value" direction="out" type="b"/>
 </method>
 <method name="GetActiveTime">
 <arg name="seconds" direction="out" type="u"/>
 </method>
 <method name="SetActive">
 <arg name="value" direction="in" type="b"/>
 </method>
 <method name="ShowMessage">
 <arg name="summary" direction="in" type="s"/>
 <arg name="body" direction="in" type="s"/>
 <arg name="icon" direction="in" type="s"/>
 </method>
 <signal name="ActiveChanged">
 <arg name="new_value" type="b"/>
 </signal>
 </interface>
</node>
"
$ dbus-send --session --print-reply --reply-timeout=2000 \
--type=method_call --dest=org.gnome.Tomboy \
/org/gnome/Tomboy/RemoteControl org.freedesktop.DBus.Introspectable.Introspect

 string "<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<!-- NDesk.DBus 0.6.0 -->
<node>
 <interface name="org.freedesktop.DBus.Introspectable">
 <method name="Introspect">
 <arg name="data" direction="out" type="s" />
 </method>
 </interface>
 <interface name="org.gnome.Tomboy.RemoteControl">
 <method name="Version">
 <arg name="ret" direction="out" type="s" />
 </method>
 <method name="DisplayNote">
 <arg name="uri" direction="in" type="s" />
 <arg name="ret" direction="out" type="b" />
 </method>
 <method name="HideNote">
 <arg name="uri" direction="in" type="s" />
 <arg name="ret" direction="out" type="b" />
 </method>
 <method name="DisplayNoteWithSearch">
 <arg name="uri" direction="in" type="s" />
 <arg name="search" direction="in" type="s" />
 <arg name="ret" direction="out" type="b" />
 </method>
 <method name="FindNote">
 <arg name="linked_title" direction="in" type="s" />
 <arg name="ret" direction="out" type="s" />
 </method>
 <method name="FindStartHereNote">
 <arg name="ret" direction="out" type="s" />
 </method>
 <method name="CreateNote">
 <arg name="ret" direction="out" type="s" />
 </method>
 <method name="CreateNamedNote">
 <arg name="linked_title" direction="in" type="s" />
 <arg name="ret" direction="out" type="s" />
 </method>
 <method name="CreateNamedNoteWithUri">
 <arg name="linked_title" direction="in" type="s" />
 <arg name="uri" direction="in" type="s" />
 <arg name="ret" direction="out" type="s" />
 </method>
 <method name="DeleteNote">
 <arg name="uri" direction="in" type="s" />
 <arg name="ret" direction="out" type="b" />
 </method>
 <method name="DisplaySearch" />
 <method name="DisplaySearchWithText">
 <arg name="search_text" direction="in" type="s" />
 </method>
 <method name="NoteExists">
 <arg name="uri" direction="in" type="s" />
 <arg name="ret" direction="out" type="b" />
 </method>
 <method name="ListAllNotes">
 <arg name="ret" direction="out" type="as" />
 </method>
 <method name="GetNoteContents">
 <arg name="uri" direction="in" type="s" />
 <arg name="ret" direction="out" type="s" />
 </method>
 <method name="GetNoteTitle">
 <arg name="uri" direction="in" type="s" />
 <arg name="ret" direction="out" type="s" />
 </method>
 <method name="GetNoteCreateDate">
 <arg name="uri" direction="in" type="s" />
 <arg name="ret" direction="out" type="x" />
 </method>
 <method name="GetNoteChangeDate">
 <arg name="uri" direction="in" type="s" />
 <arg name="ret" direction="out" type="x" />
 </method>
 <method name="GetNoteContentsXml">
 <arg name="uri" direction="in" type="s" />
 <arg name="ret" direction="out" type="s" />
 </method>
 <method name="GetNoteCompleteXml">
 <arg name="uri" direction="in" type="s" />
 <arg name="ret" direction="out" type="s" />
 </method>
 <method name="SetNoteContents">
 <arg name="uri" direction="in" type="s" />
 <arg name="text_contents" direction="in" type="s" />
 <arg name="ret" direction="out" type="b" />
 </method>
 <method name="SetNoteContentsXml">
 <arg name="uri" direction="in" type="s" />
 <arg name="xml_contents" direction="in" type="s" />
 <arg name="ret" direction="out" type="b" />
 </method>
 <method name="SetNoteCompleteXml">
 <arg name="uri" direction="in" type="s" />
 <arg name="xml_contents" direction="in" type="s" />
 <arg name="ret" direction="out" type="b" />
 </method>
 <method name="GetTagsForNote">
 <arg name="uri" direction="in" type="s" />
 <arg name="ret" direction="out" type="as" />
 </method>
 <method name="AddTagToNote">
 <arg name="uri" direction="in" type="s" />
 <arg name="tag_name" direction="in" type="s" />
 <arg name="ret" direction="out" type="b" />
 </method>
 <method name="RemoveTagFromNote">
 <arg name="uri" direction="in" type="s" />
 <arg name="tag_name" direction="in" type="s" />
 <arg name="ret" direction="out" type="b" />
 </method>
 <method name="GetAllNotesWithTag">
 <arg name="tag_name" direction="in" type="s" />
 <arg name="ret" direction="out" type="as" />
 </method>
 <method name="GetNotebookForNote">
 <arg name="uri" direction="in" type="s" />
 <arg name="ret" direction="out" type="s" />
 </method>
 <method name="AddNoteToNotebook">
 <arg name="uri" direction="in" type="s" />
 <arg name="notebook_name" direction="in" type="s" />
 <arg name="ret" direction="out" type="b" />
 </method>
 <method name="GetAllNotesInNotebook">
 <arg name="notebook_name" direction="in" type="s" />
 <arg name="ret" direction="out" type="as" />
 </method>
 <method name="AddNotebook">
 <arg name="notebook_name" direction="in" type="s" />
 <arg name="ret" direction="out" type="b" />
 </method>
 <method name="SearchNotes">
 <arg name="query" direction="in" type="s" />
 <arg name="case_sensitive" direction="in" type="b" />
 <arg name="ret" direction="out" type="as" />
 </method>
 <signal name="NoteDeleted">
 <arg name="uri" direction="out" type="s" />
 <arg name="title" direction="out" type="s" />
 </signal>
 <signal name="NoteAdded">
 <arg name="uri" direction="out" type="s" />
 </signal>
 <signal name="NoteSaved">
 <arg name="uri" direction="out" type="s" />
 </signal>
 </interface>
</node>"

注意其中 object name 因各 Service 定義不同,首次查詢可從 / 開始查,Dbus 會答覆其子路徑。

有了 API 界面之後,就可以直接用 dbus-send 送些指令給這些 Services 啦。如下啟動螢幕保護程式。

dbus-send --session --dest=org.gnome.ScreenSaver \
--type=method_call --print-reply --reply-timeout=20000 \
/ org.gnome.ScreenSaver.SetActive boolean:true

參數的格式可參考 DBus 規格中的 Type Signatures

如果你是在 Desktop 環境想查詢測試 DBus APIs, 比較容易的工具是使用 d-feet,這是 Python/GTK 所寫的工具,只消滑鼠點點就可以查詢各種 API 與送出指令訊息。(以下圖片出自 d-feet)

知道了基本的測試工具後,測試期間通常需要觀測訊息的傳送與反應是否正確,此時可以利用 dbus-monitor。它可以用來監測系統中所有的 Dbus messages,方便查詢軟體是否運作正常。

由於安全性的考量,dbus-monitor 預設只能監錄 Session Bus 中的訊息,意即在每個 Login session 中使用者所開啟的軟體。至於 System Bus,像是 Network Manager 等預設是無法存取的,這是避免惡意軟體竊取隱私,如密碼等資訊。

必須手動開啟權限,設定方法是更改以下設定檔,開啟 eavesdrop policy,並重啟 Dbus daemon. 這樣才能竊聽相關通訊。

cat > /etc/dbus-1/system-local.conf
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
 "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>

  <policy context="default">
    <!-- All messages may be received by default -->
    <allow receive_requested_reply="false" receive_type="method_call" eavesdrop="true"/>
    <allow receive_requested_reply="false" receive_type="method_return" eavesdrop="true"/>
    <allow receive_requested_reply="false" receive_type="error" eavesdrop="true"/>
    <allow receive_requested_reply="false" receive_type="signal" eavesdrop="true"/>
    <allow eavesdrop="true"/>
  </policy>
  <policy user="root">
      <allow send_destination="*" eavesdrop="true"/>
      <allow receive_sender="*" eavesdrop="true"/>
  </policy>
</busconfig>

詳盡資訊請參考