モブ沢工房

プログラミングとかLinux関連(特にOSSのグラフィックツール関連)とかレトロゲームとか3Dプリンタやら日曜大工等、色々。

せっかくdbusについて学んだのでメモしておきます

この記事はfc2から引っ越した記事です

APTonCDが動かなかったため、pythondbus(のUDisks2)について学んでしまいました。
それも、諦めて超手抜きの解決策を打ち出した直後、「もしかして、こういうことなんじゃないか?」と閃き、やってみたらできてしまいましたw

おいらも資料が少なくて七転八倒してしまったため、今後どなたかがdbusに手を出した時のために。
ここに一応置いておこうと思います。検索で来た方の参考などになればよいなと…

ていうかいずれホームページの方にまとめておきたいな〜、などとw

dbusの基本


dbusでは、何がなくともまずシステムバスを得なければならないのです。

import dbus
sb=dbus.SystemBus()



このシステムバスオブジェクトからインターフェース(を実装したオブジェクトのインスタンス)を得ることで、高度に抽象的なプログラミングを可能にしているのですね。
そしてシステムバスオブジェクトのget_objectで基本デバイスを得るのも、とても基本的で大切なことなのです。

たとえば、/dev/sda1に相当するブロックデバイスのインターフェース(のプロキシオブジェクト)を得るには…

b_dev=sb.get_object("org.freedesktop.UDisks2","/org/freedesktop/UDisks2/block_devices/sda1")



とするわけです。
ここで注意しなければならないのは、第一引数は「org.freedesktopAPIであるUDisk2を示すための名前文字列」であり、第二引数は「dbusシステム上のUDisks2のオブジェクトパス指定」なのです。
よって、第一引数はあまり変化しないが、第二引数は頻繁に変わる、と。

これで得られたプロキシオブジェクトは複数のインターフェースを実装している(事が多い、必然的に)
そしてそれら、多種多様なインターフェースで取得できる情報は、プロパティインターフェースのGetメソッドに対象インタフェースを指定する事によりアクセスできます。

つまり、多態のような。

たとえば上の例のようなorg.freedesktop.UDisks2.Blockなブロックデバイスの、今現在のMountPointsを知りたいとします。

この時、ブロックデバイスインタフェースを持つプロキシからプロパティインターフェースを得て、そのGetメソッドでMountPointsの値を得るという手間をかけることになります。
その時、プロパティインターフェースのGetメソッドの第一引数は、マウントポイントを知ることのできるインターフェース「org.freedesktop.UDisks2.Filesystem」なのです。
これにより、同じ名前のプロパティで違う操作対象・違う挙動ということもありうるわけです。

対象となるインターフェースを実装しているプロキシオブジェクトのみ、そのプロパティを使用できると。

実例で言えば上のようなorg.freedesktop.UDisks2.BlockのプロキシはFilesystemインターフェースを持っていますが、org.freedesktop.UDisks2.block_devicesのプロキシは、そのようなインターフェースを実装していません。


大抵のプロキシが実装しているのは必然的にプロパティ、そして、「Introspect」メソッドです。
このIntrospectメソッドにより、xmlでそのプロキシオブジェクトが実装するインターフェース、そしてそのプロパティやメソッドの名前、要件が取得できるのです。

ついでに言えば、Pythondbus(UDisks2)をいじろうという時、あまりにもドキュメントが少なすぎるということがかなり障害になりましたが、Introspectの吐くxmlがかなり参考になりました。

しかし、ubuntuの場合、ドキュメント不足でもっともよい対策はapt-get source udisks2 することですね! orz
まったくお恥ずかしいところを見せてしまいました…
まぁいいや、とw

続けます。

xml=blocks.Introspect(dbus_interface='org.freedesktop.DBus.Introspectable')


これにより、xmlを得られる。得られたxmlには様々な情報が入っています。それこそ、メソッド名からプロパティ名、そしてメソッドの引数に至るまで。

<interface name="org.freedesktop.DBus.Properties">
<method name="Get">
<arg type="s" name="interface_name" direction="in"/>
<arg type="s" name="property_name" direction="in"/>
<arg type="v" name="value" direction="out"/>
</method>


こんなふうに、ですね。

そんな重要なプロパティインターフェースですが、とりあえずちょろっとサンプルだけ出しておきます。前述のMountPointsでいうと…

p=dbus.Interface(b_dev,dbus.PROPERTIES_IFACE) # propertyインターフェースの取得
ret=p.Get("org.freedesktop.UDisks2.Filesystem","MountPoints")



こんなふうになります。この返り値はdbus専用の特殊バイト列で得られた、/dev/sda1のマウントポイントなのです。

dbus.Array([dbus.Array([dbus.Byte(47), dbus.Byte(98), dbus.Byte(111), dbus.Byte(111), dbus.Byte(116), dbus.Byte(47), dbus.Byte(101), dbus.Byte(102), dbus.Byte(105), dbus.Byte(0)], signature=dbus.Signature('y'))], signature=dbus.Signature('ay'), variant_level=1)


実際にやった結果ははこんなかんじのオブジェクトですね。ちなみにおいらのマシンの/dev/sda1であり、マウントポイントは/boot/efiなのです。このdbus.Byte(47)…のバイト列を一つづつchr()で変換すると、/boot/efi/\0になるのがわかると思います。

おっと、大変重要なことを書き忘れておりました。
このFilesystemのMountPoints、実に言うまでもなく当たり前ですが

既にマウントされているデバイスにしか効きません。

当たり前ですが…マウント後のマウントポイントを調べているのですからね…
USBに刺しただけでまだマウントされていないUSBメモリ相手に「なぜだ!なぜ呼び出しが失敗するんだ!」と苦戦していたお間抜けさんぶりは内緒w それで「もしかしてマウントしてないとダメじゃん?」とsda1にしてみたら通ったというww

ちなみにdbus.signatureが何を意味しているのかはイマイチ分かっていない(汗)

普通にlsすりゃいーじゃん…とか思うかも知れませんが、dbusで得られる情報は特殊なものが多く。
たとえばそのブロックデバイスが光学メディアかどうかとか、USBに刺さってるかとか、いろいろ面白い情報を得られるのですね。それも、Linuxカーネルとかファイルシステムへの深い知識無しに

あと、USBメモリなどをUSBに刺しただけで反応するのもdbusの働きなのだそうですよ〜

APTonCDを現代風にUDisk2で書き直すことができそうな感じです(しかし、意味ねえ〜)

APTonCDが動かなかったため、pythondbus(のUDisks2)について学んでしまいました。
それも、諦めて超手抜きの解決策を打ち出した直後、「もしかして、こういうことなんじゃないか?」と閃き、やってみたらできてしまいましたw

おいらも資料が少なくて七転八倒してしまったため、今後どなたかがdbusに手を出した時のために。
ここに一応置いておこうと思います。検索で来た方の参考などになればよいなと…

ていうかいずれホームページの方にまとめておきたいな〜、などとw

dbusの基本


dbusでは、何がなくともまずシステムバスを得なければならないのです。

import dbus
sb=dbus.SystemBus()



このシステムバスオブジェクトからインターフェース(を実装したオブジェクトのインスタンス)を得ることで、高度に抽象的なプログラミングを可能にしているのですね。
そしてシステムバスオブジェクトのget_objectで基本デバイスを得るのも、とても基本的で大切なことなのです。

たとえば、/dev/sda1に相当するブロックデバイスのインターフェース(のプロキシオブジェクト)を得るには…

b_dev=sb.get_object("org.freedesktop.UDisks2","/org/freedesktop/UDisks2/block_devices/sda1")



とするわけです。
ここで注意しなければならないのは、第一引数は「org.freedesktopAPIであるUDisk2を示すための名前文字列」であり、第二引数は「dbusシステム上のUDisks2のオブジェクトパス指定」なのです。
よって、第一引数はあまり変化しないが、第二引数は頻繁に変わる、と。

これで得られたプロキシオブジェクトは複数のインターフェースを実装している(事が多い、必然的に)
そしてそれら、多種多様なインターフェースで取得できる情報は、プロパティインターフェースのGetメソッドに対象インタフェースを指定する事によりアクセスできます。

つまり、多態のような。

たとえば上の例のようなorg.freedesktop.UDisks2.Blockなブロックデバイスの、今現在のMountPointsを知りたいとします。

この時、ブロックデバイスインタフェースを持つプロキシからプロパティインターフェースを得て、そのGetメソッドでMountPointsの値を得るという手間をかけることになります。
その時、プロパティインターフェースのGetメソッドの第一引数は、マウントポイントを知ることのできるインターフェース「org.freedesktop.UDisks2.Filesystem」なのです。
これにより、同じ名前のプロパティで違う操作対象・違う挙動ということもありうるわけです。

対象となるインターフェースを実装しているプロキシオブジェクトのみ、そのプロパティを使用できると。

実例で言えば上のようなorg.freedesktop.UDisks2.BlockのプロキシはFilesystemインターフェースを持っていますが、org.freedesktop.UDisks2.block_devicesのプロキシは、そのようなインターフェースを実装していません。


大抵のプロキシが実装しているのは必然的にプロパティ、そして、「Introspect」メソッドです。
このIntrospectメソッドにより、xmlでそのプロキシオブジェクトが実装するインターフェース、そしてそのプロパティやメソッドの名前、要件が取得できるのです。

ついでに言えば、Pythondbus(UDisks2)をいじろうという時、あまりにもドキュメントが少なすぎるということがかなり障害になりましたが、Introspectの吐くxmlがかなり参考になりました。

しかし、ubuntuの場合、ドキュメント不足でもっともよい対策はapt-get source udisks2 することですね! orz
まったくお恥ずかしいところを見せてしまいました…
まぁいいや、とw

続けます。

xml=blocks.Introspect(dbus_interface='org.freedesktop.DBus.Introspectable')


これにより、xmlを得られる。得られたxmlには様々な情報が入っています。それこそ、メソッド名からプロパティ名、そしてメソッドの引数に至るまで。

<interface name="org.freedesktop.DBus.Properties">
<method name="Get">
<arg type="s" name="interface_name" direction="in"/>
<arg type="s" name="property_name" direction="in"/>
<arg type="v" name="value" direction="out"/>
</method>


こんなふうに、ですね。

そんな重要なプロパティインターフェースですが、とりあえずちょろっとサンプルだけ出しておきます。前述のMountPointsでいうと…

p=dbus.Interface(b_dev,dbus.PROPERTIES_IFACE) # propertyインターフェースの取得
ret=p.Get("org.freedesktop.UDisks2.Filesystem","MountPoints")



こんなふうになります。この返り値はdbus専用の特殊バイト列で得られた、/dev/sda1のマウントポイントなのです。

dbus.Array([dbus.Array([dbus.Byte(47), dbus.Byte(98), dbus.Byte(111), dbus.Byte(111), dbus.Byte(116), dbus.Byte(47), dbus.Byte(101), dbus.Byte(102), dbus.Byte(105), dbus.Byte(0)], signature=dbus.Signature('y'))], signature=dbus.Signature('ay'), variant_level=1)


実際にやった結果ははこんなかんじのオブジェクトですね。ちなみにおいらのマシンの/dev/sda1であり、マウントポイントは/boot/efiなのです。このdbus.Byte(47)…のバイト列を一つづつchr()で変換すると、/boot/efi/\0になるのがわかると思います。

おっと、大変重要なことを書き忘れておりました。
このFilesystemのMountPoints、実に言うまでもなく当たり前ですが

既にマウントされているデバイスにしか効きません。

当たり前ですが…マウント後のマウントポイントを調べているのですからね…
USBに刺しただけでまだマウントされていないUSBメモリ相手に「なぜだ!なぜ呼び出しが失敗するんだ!」と苦戦していたお間抜けさんぶりは内緒w それで「もしかしてマウントしてないとダメじゃん?」とsda1にしてみたら通ったというww

ちなみにdbus.signatureが何を意味しているのかはイマイチ分かっていない(汗)

普通にlsすりゃいーじゃん…とか思うかも知れませんが、dbusで得られる情報は特殊なものが多く。
たとえばそのブロックデバイスが光学メディアかどうかとか、USBに刺さってるかとか、いろいろ面白い情報を得られるのですね。それも、Linuxカーネルとかファイルシステムへの深い知識無しに

あと、USBメモリなどをUSBに刺しただけで反応するのもdbusの働きなのだそうですよ〜

APTonCDを現代風にUDisk2で書き直すことができそうな感じです(しかし、意味ねえ〜)