Ubuntu Weekly Recipe

第559回デバイスの認識をモニタリング・コントロールできる「udevadm」

第555回ではデバイスの自動認識やデバイス名の設定など、システムの裏方として大活躍しているudevの基本とルールファイルの書き方について紹介しました。今回はルールを書く上で便利なツールであるudevadmを紹介します。

udevのデバッグ方法

udevはまずinitシステムからudevdが起動され、以降はudevdが諸々の処理を行います。よってudevの挙動をより細かく知るためには、udevdのログレベルを初期状態のinfoからdebugにあげて起動しておくと便利です。udevdのログレベルを変更するには4種類の方法が存在します。

  • カーネルパラメーターから指定する
  • /etc/udev/udev.confから設定する
  • udevadmコマンドから設定する
  • 環境変数SYSTEMD_LOG_LEVELで設定する

このうち起動直後からdebugレベルにしたり、恒久的に設定したいなら、カーエネルパラメーターかudev.confを使うことになります。起動後に一時的にオン・オフするならudevadmコマンドが便利です。

カーネルパラメーターから指定する

比較的設定しやすいのがカーネルパラメーターに設定する方法です。次のようなパラメーターを設定します。

udev.log_priority=debug rd.udev.log_priority=debug

rd.で始まっているパラメーターは、initramfs環境でのみ効きます。通常の環境と両方で有効化したいなら、前者だけでかまいません。

カーネルパラメーターは/etc/default/grubGRUB_CMDLINE_LINUXに設定します。具体的には次のような値になります。

GRUB_CMDLINE_LINUX="udev.log_priority=debug"

エディタを使用しないなら次のコマンドを実行します。

$ sudo sed -i 's/^\(GRUB_CMDLINE_LINUX=".*\)"/\1 udev.log_priority=debug"/' /etc/default/grub
$ sudo update-grub

エディタを使用するしないに関わらず、更新したらupdate-grubを実行してください。変更したら再起動しましょう。

カーネルパラメーターに指定したパラメーターが表示されていることを確認しましょう。

$ cat /proc/cmdline
(略) ro udev.log_priority=debug quiet splash vt.handoff=1

ログを見るとudev関連のログが大量に出ています。たとえば次の内容から、ロードされているルールファイルがわかります。

$ journalctl -b 0 | grep Reading
(略) systemd-udevd[304]: Reading rules file: /lib/udev/rules.d/39-usbmuxd.rules
(略) systemd-udevd[304]: Reading rules file: /lib/udev/rules.d/40-usb-media-players.rules
(後略)

デバッグログはかなりの量が出力されますし、起動時間も長くなってしまいます。必要がなくなったら元に戻しておきましょう。

/etc/udev/udev.confから設定する

もうひとつよく紹介されるのがudev.confに設定する方法です。特定のinitramfsでのみログレベルなどの設定を反映したい場合に使えますし、カーネルパラメーターと異なり、ログレベル以外も設定できます。

設定ファイルは/etc/udev/udev.confです。#で始まる行はコメント行なので、次のように設定します。

udev_log="debug"

さらにこの設定ファイルはudevが起動するinitramfsの中に含まれている必要があります。Ubuntuの場合、initramfsを更新したら最新のudev.confが含まれるようになっています。

$ sudo update-initramfs -u

ちなみに/usr/share/initramfs-tools/scripts/init-top/udevはinitramfs内部のudevの起動スクリプトです。ログレベル以外の挙動を一時的に変えたいなら、こちらのファイルを直接編集してupdate-initramfsを実行しても良いでしょう。ただしこのファイルはudevパッケージのアップデートによって上書きされるので注意してください。

udevadmの使い方

udevadmはudevに問い合わせ・操作を行うためのコマンドラインツールです[1]⁠。たとえば認識済みのデバイスのudevイベント情報を表示してルールファイルを作る際の参考にしたり、イベントの内容をリアルタイム標準出力に出すことでホットプラグ時のイベントを確認できます。

udevのイベントを表示する「monitor」

いちばんわかりやすいのがudevのイベントを表示するmonitorサブコマンドでしょう。起動はudevadm monitorを実行するだけです。終了はCtrl-Cを押してください。

$ udevadm monitor
monitor will print the received events for:
UDEV - the event which udev sends out after rule processing
KERNEL - the kernel uevent

KERNEL[2372.548228] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1 (usb)
KERNEL[2372.557788] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0 (usb)
KERNEL[2372.557830] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0/bluetooth/hci1 (bluetooth)
KERNEL[2372.560659] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0/bluetooth/hci1/rfkill3 (rfkill)
KERNEL[2372.560726] bind     /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0 (usb)
KERNEL[2372.560765] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.1 (usb)
KERNEL[2372.560800] bind     /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.1 (usb)
KERNEL[2372.560842] bind     /devices/pci0000:00/0000:00:14.0/usb3/3-1 (usb)
UDEV  [2372.564150] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1 (usb)
UDEV  [2372.574859] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0 (usb)
UDEV  [2372.582759] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0/bluetooth/hci1 (bluetooth)
UDEV  [2372.583224] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.1 (usb)
UDEV  [2372.597518] bind     /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.1 (usb)
UDEV  [2372.603887] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0/bluetooth/hci1/rfkill3 (rfkill)
UDEV  [2372.610413] bind     /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0 (usb)
UDEV  [2372.613674] bind     /devices/pci0000:00/0000:00:14.0/usb3/3-1 (usb)

上記はBluetoothドングルを接続した時のイベントです。実際はcgroupあたりのイベントも表示されますが、今回は間引いています。

KERNELで始まる行はカーネルから届いたueventです。udevはueventを受け取って、それを元にルールファイルを処理し、UDEVで始まる行を出力します。第555回「ACTIONとGOTO変数」でも紹介しているように、⁠add」「デバイスの追加」「bind」「ドライバーとデバイスが紐付けられた」状態となります。

「/devices/」で始まる文字列はイベントターゲットのデバイスパスです。これはそのまま「/sys」以下のディレクトリパスです。

たとえばこのPCはPCIバスのBus 00、Device 14、Function 0にUSBコントローラーがぶら下がっています。デバイスパスの「/pci0000:00/0000:00:14.0/usb3/」がそれに該当します。

さらに今回つないだUSBポートは、Bus 3、Port 1のUSBハブにつながっているようです。実際にlsusbしてみるとそれらしきデバイスが見えます。

$ lsusb -t
/:  Bus 03.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/4p, 480M
    |__ Port 1: Dev 5, If 0, Class=Wireless, Driver=btusb, 12M
    |__ Port 1: Dev 5, If 1, Class=Wireless, Driver=btusb, 12M

このBluetoothドングルはインターフェースが2つあるようですね。

monitorサブシステムは-pオプションを渡すと、プロパティも表示してくれます。プロパティは第555回で紹介したマッチングルールで利用できる値です。manページには記載がありませんが、過去との互換性のために-pのエイリアスとして--envオプションを使うことも可能です。

$ udevadm monitor -p
monitor will print the received events for:
UDEV - the event which udev sends out after rule processing
KERNEL - the kernel uevent

KERNEL[4456.035750] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1 (usb)
ACTION=add
BUSNUM=003
DEVNAME=/dev/bus/usb/003/011
DEVNUM=011
DEVPATH=/devices/pci0000:00/0000:00:14.0/usb3/3-1
DEVTYPE=usb_device
MAJOR=189
MINOR=266
PRODUCT=a12/1/8891
SEQNUM=7674
SUBSYSTEM=usb
TYPE=224/1/1

KERNEL[4456.042898] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0 (usb)
ACTION=add
DEVPATH=/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0
DEVTYPE=usb_interface
INTERFACE=224/1/1
MODALIAS=usb:v0A12p0001d8891dcE0dsc01dp01icE0isc01ip01in00
PRODUCT=a12/1/8891
SEQNUM=7675
SUBSYSTEM=usb
TYPE=224/1/1

KERNEL[4456.043027] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0/bluetooth/hci1 (bluetooth)
ACTION=add
DEVPATH=/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0/bluetooth/hci1
DEVTYPE=host
SEQNUM=7676
SUBSYSTEM=bluetooth

KERNEL[4456.043083] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0/bluetooth/hci1/rfkill10 (rfkill)
ACTION=add
DEVPATH=/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0/bluetooth/hci1/rfkill10
RFKILL_NAME=hci1
RFKILL_STATE=1
RFKILL_TYPE=bluetooth
SEQNUM=7677
SUBSYSTEM=rfkill

(中略)

UDEV  [4456.067158] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0 (usb)
ACTION=add
DEVPATH=/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0
DEVTYPE=usb_interface
DRIVER=btusb
ID_MODEL_FROM_DATABASE=Bluetooth Dongle (HCI mode)
ID_USB_CLASS_FROM_DATABASE=Wireless
ID_USB_PROTOCOL_FROM_DATABASE=Bluetooth
ID_USB_SUBCLASS_FROM_DATABASE=Radio Frequency
ID_VENDOR_FROM_DATABASE=Cambridge Silicon Radio, Ltd
INTERFACE=224/1/1
MODALIAS=usb:v0A12p0001d8891dcE0dsc01dp01icE0isc01ip01in00
PRODUCT=a12/1/8891
SEQNUM=7675
SUBSYSTEM=usb
TYPE=224/1/1
USEC_INITIALIZED=4456066552

UDEV  [4456.090457] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0/bluetooth/hci1/rfkill10 (rfkill)
ACTION=add
DEVPATH=/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0/bluetooth/hci1/rfkill10
ID_PATH=pci-0000:00:14.0-usb-0:1:1.0
ID_PATH_TAG=pci-0000_00_14_0-usb-0_1_1_0
RFKILL_NAME=hci1
RFKILL_STATE=1
RFKILL_TYPE=bluetooth
SEQNUM=7677
SUBSYSTEM=rfkill
SYSTEMD_RFKILL=1
USEC_INITIALIZED=4456090248

詳細な解説は省きますが、usbサブシステムの特定のポートにデバイスがaddされてから、udevにイベントが渡っていく様子がわかります。さらにudev自身がルールファイルを元に、さまざまなプロパティを追加していることもわかります。

USBなどのホットプラグなデバイスに対してルールファイルを作成する際は、この出力結果が参考になるでしょう。

デバイスのイベント情報を表示する「info」

認識済みのデバイスの情報(プロパティ)を表示したい場合は、infoサブコマンドを使います。表示したいデバイスのデバイスファイル名を-nオプションに指定して実行します。

$ udevadm info -n /dev/bus/usb/003/005
P: /devices/pci0000:00/0000:00:14.0/usb3/3-1
N: bus/usb/003/005
E: BUSNUM=003
E: DEVNAME=/dev/bus/usb/003/005
E: DEVNUM=005
E: DEVPATH=/devices/pci0000:00/0000:00:14.0/usb3/3-1
E: DEVTYPE=usb_device
E: DRIVER=usb
E: ID_BUS=usb
E: ID_MODEL=CSR8510_A10
E: ID_MODEL_ENC=CSR8510\x20A10
E: ID_MODEL_FROM_DATABASE=Bluetooth Dongle (HCI mode)
E: ID_MODEL_ID=0001
E: ID_REVISION=8891
E: ID_SERIAL=0a12_CSR8510_A10
E: ID_USB_INTERFACES=:e00101:
E: ID_VENDOR=0a12
E: ID_VENDOR_ENC=0a12
E: ID_VENDOR_FROM_DATABASE=Cambridge Silicon Radio, Ltd
E: ID_VENDOR_ID=0a12
E: MAJOR=189
E: MINOR=260
E: PRODUCT=a12/1/8891
E: SUBSYSTEM=usb
E: TYPE=224/1/1
E: USEC_INITIALIZED=481849865

USBデバイスは/dev/bus/usb/バス番号/デバイス番号のファイルが作られるのでこれを指定していますが、ストレージデバイスなら/dev/sda1なども指定可能です。

さてinfoサブコマンドで表示されるデータには次のようなヘッダーが付けられています。

  • P:デバイスパス
  • N:デバイス名
  • L:リンクプライオリティ[2]
  • S:シンボリックリンク
  • E:プロパティ

デバイスファイルが作られない場合、sysfs上のデバイスパスを-pオプションに指定します。

$ udevadm info -p /devices/pci0000:00/0000:00:14.0/usb3/3-1

得られる結果はデバイス名を指定したときと、原則同じです。

特定の情報だけを取得したい場合は-qオプションでフィルタリングできます。たとえばデバイスパスを取得したければ次のように「path」を指定してください。

$ udevadm info -n /dev/bus/usb/003/005 -q path
/devices/pci0000:00/0000:00:14.0/usb3/3-1

他にもデバイス名(name⁠⁠、シンボリックリンク(symlink⁠⁠、プロパティ(property⁠⁠、すべて(all)を指定できます。

-aオプションを付けると、親デバイスまでさかのぼって状態を表示してくれます。

$ udevadm info -n /dev/bus/usb/003/005 -a
(中略)
  looking at device '/devices/pci0000:00/0000:00:14.0/usb3/3-1':
    KERNEL=="3-1"
    SUBSYSTEM=="usb"
    DRIVER=="usb"
(中略)
    ATTR{urbnum}=="130"
    ATTR{version}==" 2.00"

  looking at parent device '/devices/pci0000:00/0000:00:14.0/usb3':
    KERNELS=="usb3"
    SUBSYSTEMS=="usb"
    DRIVERS=="usb"
(中略)
    ATTRS{urbnum}=="79"
    ATTRS{version}==" 2.00"

  looking at parent device '/devices/pci0000:00/0000:00:14.0':
    KERNELS=="0000:00:14.0"
    SUBSYSTEMS=="pci"
    DRIVERS=="xhci_hcd"
    ATTRS{subsystem_device}=="0x8338"
    ATTRS{subsystem_vendor}=="0x10f7"
    ATTRS{vendor}=="0x8086"

(中略)
  looking at parent device '/devices/pci0000:00':
    KERNELS=="pci0000:00"
    SUBSYSTEMS==""
    DRIVERS==""

イベントを発生させる「trigger」

triggerサブコマンドは、任意のイベントをカーネルから発生させられます。もし引数なしに実行した場合は、すべての認識済デバイスにchangeアクションのイベントを送ります。アクションの種類は第555回「ACTIONとGOTO変数」を参照してください。

$ sudo udevadm trigger

実際には事前に別のウィンドウで「udevadm monitor」を実行しておくと、状況がわかりやすいでしょう。

$ udevadm monitor
monitor will print the received events for:
UDEV - the event which udev sends out after rule processing
KERNEL - the kernel uevent

KERNEL[960.580785] change   /devices/LNXSYSTM:00 (acpi)
KERNEL[960.580831] change   /devices/LNXSYSTM:00/LNXCPU:00 (acpi)
KERNEL[960.580855] change   /devices/LNXSYSTM:00/LNXCPU:01 (acpi)
KERNEL[960.580883] change   /devices/LNXSYSTM:00/LNXCPU:02 (acpi)
KERNEL[960.580905] change   /devices/LNXSYSTM:00/LNXCPU:03 (acpi)
(中略)
UDEV  [961.259537] change   /devices/virtual/workqueue/writeback (workqueue)
UDEV  [961.259567] change   /devices/virtual/vc/vcsa6 (vc)
UDEV  [961.260298] change   /devices/virtual/vc/vcs4 (vc)
UDEV  [961.261334] change   /devices/pci0000:00/0000:00:1b.0/sound/card0 (sound)
UDEV  [961.268727] change   /devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda (block)

ひたすらchangeイベントが届いていますね。

-cオプションで任意のアクションを指定できますし、末尾にデバイス名やデバイスパスを指定すれば、イベントを送る対象を制限できます。さらに-vオプションを付けておくと、イベントを送る対象のデバイスが表示されます。

$ sudo udevadm trigger -v -c change /dev/bus/usb/003/005
/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1
/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0
/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0/bluetooth/hci1
/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0/bluetooth/hci1/rfkill4
/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.1

他にも属性やプロパティ、サブシステムなどでマッチングしてイベントを送る対象を制限できます。詳細はudevのmanページを参照してください。

単に特定のデバイス向けのルールファイルをテストしたいだけなら、testサブコマンドも使えます。

$ sudo udevadm test $(udevadm info -n /dev/bus/usb/003/005 -q path)
calling: test
version 239
This program is for debugging only, it does not run any program
specified by a RUN key. It may show incorrect results, because
some values may be different, or not available at a simulation run.

Load module index
Parsed configuration file /lib/systemd/network/99-default.link
Created link configuration context.
Reading rules file: /lib/udev/rules.d/39-usbmuxd.rules
Reading rules file: /lib/udev/rules.d/40-usb-media-players.rules
Reading rules file: /lib/udev/rules.d/40-usb_modeswitch.rules
(中略)
Reading rules file: /lib/udev/rules.d/99-systemd.rules
rules contain 393216 bytes tokens (32768 * 12 bytes), 37665 bytes strings
31410 strings (260972 bytes), 27952 de-duplicated (226766 bytes), 3459 trie nodes used
IMPORT builtin 'usb_id' /lib/udev/rules.d/50-udev-default.rules:13
IMPORT builtin 'hwdb' /lib/udev/rules.d/50-udev-default.rules:13
MODE 0664 /lib/udev/rules.d/50-udev-default.rules:42
handling device node '/dev/bus/usb/003/005', devnum=c189:260, mode=0664, uid=0, gid=0
preserve permissions /dev/bus/usb/003/005, 020664, uid=0, gid=0
preserve already existing symlink '/dev/char/189:260' to '../bus/usb/003/005'
ACTION=add
BUSNUM=003
DEVNAME=/dev/bus/usb/003/005
(後略)

testサブコマンドはデバイスパスしか渡せないので、上記のようにデバイス名からの変換が必要です。

udevデーモンを操作する「control」

controlサブコマンドはudevデーモンそのものを操作するサブコマンドです。あまり使うことはありませんが、ログレベルを一時的に変更できる-lやルールをリロードできる-R⁠、イベントの処理を一時的に停止・再開する-s-Sあたりは、覚えておくとデバッグ時に便利かもしれません。

udevのビルトインコマンドを実行する「test-builtin」

第555回「ENVとRUN変数」でも紹介したように、ルールファイルの中でudevのビルトインコマンドを実行することがあります。このビルトインコマンドをシェルから実行できるのがtest-builtinサブコマンドです。次のようなビルトインコマンドをサポートしています。

  • blkid:UUIDなどを取得する
  • btrfs:btrfs関連の情報取得
  • hwdb:hwdbからデータを取得
  • input_id:入力デバイス関連の情報を取得
  • keyboard:キーマッピングなどをスキャン
  • kmod:カーネルモジュールのロード
  • net_id:ネットワークデバイスの情報取得
  • net_setup_link:ネットワークのリンク設定
  • path_id:デバイスパスの元になる情報の取得
  • usb_id:USB関連の情報を取得
  • uaccess:アクセスコントロール関連の情報取得

ホットプラグ関連だと、usb_idによるUSBデバイス情報がルールファイル作成時の参考になるでしょう。

$ sudo udevadm test-builtin usb_id $(udevadm info -n /dev/bus/usb/003/005 -q path)
calling: test-builtin
Load module index
Parsed configuration file /lib/systemd/network/99-default.link
Created link configuration context.
ID_VENDOR=0a12
ID_VENDOR_ENC=0a12
ID_VENDOR_ID=0a12
ID_MODEL=CSR8510_A10
ID_MODEL_ENC=CSR8510\x20A10
ID_MODEL_ID=0001
ID_REVISION=8891
ID_SERIAL=0a12_CSR8510_A10
ID_BUS=usb
ID_USB_INTERFACES=:e00101:
Unload module index
Unloaded link configuration context.

ルールファイルの中でどのようなプロパティがセットされるか確認したいときに便利です。

おすすめ記事

記事・ニュース一覧