Rust で D-Bus 経由で systemd から情報を得る

色々試行錯誤したのでメモ。


結論

dbus crate を使う。


経緯

開発中の libspecinfra で、systemd 配下の service の状態を取得できるようにするための provider を書こうと思い、色々調べたりコード書いて試したりした。


使えそうな crates を探す

まずは目的に合う crates がないか crates.io で検索。ざっと以下のようなものが見つかる。

systemd crate は libsystemd の Rust インターフェースで、sd-daemonsd-id128sd-journalsd-login に対応している。が sd-busまだ実装が不完全 なようなので、目的には合わなさそう、と判断。

systemd-dbus crate は2年半以上更新がなく、rust 1.19.0 でコンパイルが通らなかったので断念。

他に systemd を直接扱える、目的に適いそうな crate が見当たらなかったので、D-Bus が扱える crate ってことで、dbus crate と dbus-bytestream create を試してみることにした。


dbus-send で D-Bus を理解する

dbus crate にしても dbus-bytestream crate にしても、ざっとドキュメントやコードを読んだ感じ、D-Bus でどういった形でメッセージのやりとりがなされているのかを理解しないと、使うのは無理だなこれは、と思ったので、まずは dbus-send コマンドで必要な情報が得られるかどうかトライしてみた。

libspecinfra でやりたいことのひとつは、サービスが動いているかどうかを調べること。dbus-send では以下のような形で実行すれば、この情報が得られることがわかった。

まずはユニット名(ssh.service)からオブジェクトパス(/org/freedesktop/systemd1/unit/ssh_2eservice)を取得。

$ dbus-send --system \
   --dest=org.freedesktop.systemd1 \
   --type=method_call \
   --print-reply \
   /org/freedesktop/systemd1 \
   org.freedesktop.systemd1.Manager.GetUnit \
   string:ssh.service

method return time=1507693363.257785 sender=:1.10 -> destination=:1.220 serial=3078 reply_serial=2
   object path "/org/freedesktop/systemd1/unit/ssh_2eservice"

--dest=org.freedesktop.systemd1 で接続先のバス名を指定。/org/freedesktop/systemd1 が操作対象のオブジェクトパス、org.freedesktop.systemd1.Manager がインターフェースで、それに続く GetUnit が呼び出すメソッド、string:ssh.service がメソッドに渡す引数、という形式になっている。

このオブジェクトパスの ActiveState プロパティを取得。

$ dbus-send --system \
   --dest=org.freedesktop.systemd1 \
   --type=method_call \
   --print-reply \
   /org/freedesktop/systemd1/unit/ssh_2eservice \
   org.freedesktop.DBus.Properties.Get \
   string:org.freedesktop.systemd1.Unit \
   string:ActiveState

method return time=1507693392.955268 sender=:1.10 -> destination=:1.221 serial=3079 reply_serial=2
   variant       string "active"

これでサービスの状態を得ることができた。

どのようなメソッドやプロパティがあるかは The D-Bus API of systemd/PID 1 に載っている。


Rust で dbus-send で得たのと同じ情報を得る

dbus crate を使って以下のようなコードを書けば良い。

試したコードは Cargo.toml 等も含めて GitHub に置いてある。

dbus crate は サンプルコード もあるし、ドキュメント もあるので、ドキュメントがほとんどない dbus-bytestream crate と比べると、比較的扱いやすいように思える(といっても、ドキュメント読めばすべてわかる、というわけではなく、コードも読んで色々試行錯誤したけど)。

dbus-bytestream も同時に試したけど、取得した ActiveState の情報をデコードする方法がよくわからないまま、dbus crate の方が動いたので、途中で断念した。なので、GitHub 上にある dbus-bytestream を使ったコードはエラーで動かない。