Ubuntu Weekly Recipe

第555回 いま,あらためてudev

この記事を読むのに必要な時間:およそ 7 分

アクセス権の変更

udevでルールを記述する場合の代表的な例がアクセス権限の変更です。

USBなどのデバイスをホットプラグしたものの,rootでしか読み書きできないデバイスにsudo経由で実行したという経験がある読者もいることでしょう。udevを使うと,新規に生成されたデバイスのアクセス権限を変更できます。

たとえばUSBシリアルコンソールデバイスを接続したときを考えてみましょう。一般的なUSBシリアルコンソールデバイスなら接続すると同時に「/dev/ttyUSB0」が作られます。このデバイスファイルはオーナーがroot,グループがdialoutで,パーミッションが「0660」です。つまりrootかdialoutグループに所属するユーザーしか読み書きできません。

$ ls -l /dev/ttyUSB0
crw-rw---- 1 root dialout 188, 0  2月  3 19:48 /dev/ttyUSB0

そして一般ユーザーは,初期設定だとdialoutグループには所属していません。もし/dev/ttyUSB0を読み書きしたいなら,次のようにdialoutグループに所属する必要があります。

$ sudo adduser $USER dialout

ちなみにグループ変更の設定を反映するためには,一度ログインしなおしてください。

さて,実はシリアルコンソールデバイスに関してはすでにルールファイル「/lib/udev/rules.d/60-serial.rules」が存在します。今回の説明に関連しそうなところを抜粋すると次のような内容です。

ACTION=="remove", GOTO="serial_end"
SUBSYSTEM!="tty", GOTO="serial_end"

SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id", IMPORT{builtin}="hwdb --subsystem=usb"
SUBSYSTEMS=="usb-serial", ENV{.ID_PORT}="$attr{port_number}"
SUBSYSTEMS=="usb", ENV{ID_USB_INTERFACE_NUM}="$attr{bInterfaceNumber}"

LABEL="serial_end"

最初の2行からこのルールは切断されたとき以外でなおかつサブシステムがttyのときのみ適用されることがわかります。またusbサブシステムやusb-serialサブシステムの下に接続されている場合は,各種デバイスプロパティが追加設定されることもわかります。

ポイントはIMPORT{builtin}の部分です。IMPORT変数は指定した値を「実行し,その結果をデバイスプロパティとして取り込む」変数です。builtinはRUNと同様に,udevのビルトインコマンドを意味します。usb_idはUSBのベンダーIDやモデルIDなどを出力します。

hwdbはもう少し複雑です。実はシステム上には「/lib/udev/hwdb.d/」などにハードウェア情報を格納するデータベースが存在します。このデータベースにはベンダーIDに紐づく実際のベンダー名などの汎用的な情報やタッチパッドの座標設定のようなデバイス固有の情報などがテキスト形式で保存されているのです。hwdbを使えば,ハードウェア固有の雑多な情報をudevのコードの外に記述できるというわけです。

ちなみに次のようにsysfs上のパスを指定することで,実際に得られる値を参照できます。

$ udevadm test-builtin usb_id /sys/class/tty/ttyUSB0
calling: test-builtin
Load module index
Parsed configuration file /lib/systemd/network/99-default.link
Created link configuration context.
/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0: if_class 255 protocol 0
ID_VENDOR=FTDI
ID_VENDOR_ENC=FTDI
ID_VENDOR_ID=0403
ID_MODEL=TTL232R-3V3
ID_MODEL_ENC=TTL232R-3V3
ID_MODEL_ID=6001
ID_REVISION=0600
ID_SERIAL=FTDI_TTL232R-3V3_FTGC0U6E
ID_SERIAL_SHORT=FTGC0U6E
ID_TYPE=generic
ID_BUS=usb
ID_USB_INTERFACES=:ffffff:
ID_USB_INTERFACE_NUM=00
ID_USB_DRIVER=ftdi_sio
Unload module index
Unloaded link configuration context.

さてシリアルコンソールデバイスに関する諸々の設定は「60-serial.rules」で設定されていることがわかりました。そこで追加の設定は「/etc/udev/rules.d/65-serial.rules」に保存することにしましょう。アクセス権限に関する設定は次の3つです。

  • OWNER:所有者の設定
  • GROUP:所有グループの設定
  • MODE:パーミッションの設定

上記3つをどう設定するかはケースバイケースでしょう。よく例としてあがるのが,GROUPを使いたいユーザーが所属しているグループに設定し,パーミッションを「0660」にする設定です。特にシリアルコンソールデバイスとしてdialoutグループが使われることが多いようです。

そういえば前に出力していた/dev/ttyUSB0は,dialoutグループになっていましたね。話をひっくり返すようですが,実は上記の設定はすでに「/lib/udev/rules.d/50-udev-default.rules」に存在します。

SUBSYSTEM=="tty", KERNEL=="ptmx", GROUP="tty", MODE="0666"
SUBSYSTEM=="tty", KERNEL=="tty", GROUP="tty", MODE="0666"
SUBSYSTEM=="tty", KERNEL=="tty[0-9]*", GROUP="tty", MODE="0620"
SUBSYSTEM=="tty", KERNEL=="sclp_line[0-9]*", GROUP="tty", MODE="0620"
SUBSYSTEM=="tty", KERNEL=="ttysclp[0-9]*", GROUP="tty", MODE="0620"
SUBSYSTEM=="tty", KERNEL=="3270/tty[0-9]*", GROUP="tty", MODE="0620"
SUBSYSTEM=="vc", KERNEL=="vcs*|vcsa*", GROUP="tty"
KERNEL=="tty[A-Z]*[0-9]|ttymxc[0-9]*|pppox[0-9]*|ircomm[0-9]*|noz[0-9]*|rfcomm[0-9]*", GROUP="dialout"

この最後の行が効いていたために,ttyUSB0のグループが変更されていたのです。それに対してMODEはtty0などしか変更されないようになっています。というわけで,MODEだけ誰でもアクセスできるように変更してみましょう※3⁠。

※3
あまり良い例ではありません。本当は「0600」なものを「0660」にするような設定を例示したかったのですが手元に良いデバイスがありませんでした。

設定例はいくつかあります。まずシンプルにカーネル上のファイル名に合わせて一括変換する方法です。

ACTION=="remove", GOTO="serial_end"
SUBSYSTEM!="tty", GOTO="serial_end"
KERNEL=="ttyUSB[0-9]*", MODE="0666"
LABEL="serial_end"

ttyUSBxすべてではなく,特定のUSBデバイスのみ設定を変更したいなら,次のようにベンダーIDなどでマッチングルールを記述すると良いでしょう。

ACTION=="remove", GOTO="serial_end"
SUBSYSTEM!="tty", GOTO="serial_end"
ENV{ID_VENDOR_ID}=="0403", ENV{ID_MODEL_ID}=="6001", MODE="0666"
LABEL="serial_end"

上記ではベンダーIDとモデルIDで絞っています。これらの値はたとえばlsusbなどで確認できます。

$ lsusb
(中略)
Bus 003 Device 004: ID 0403:6001 Future Technology Devices International, Ltd FT232 USB-Serial (UART) IC

FTDIのチップが載った一般的なUSBシリアルコンソールケーブルです。ルールを追加したら,udevにルールセットの再読込を指示します。

$ sudo udevadm control --reload

さらにUSBシリアルコンソールを接続し直しましょう。

$ ls -l /dev/ttyUSB0
crw-rw-rw- 1 root dialout 188, 0  2月  3 20:11 /dev/ttyUSB0

これでシリアルコンソールを使うために,screenやminicomやkermitをユーザー権限で実行できるようになりました。

著者プロフィール

柴田充也(しばたみつや)

Ubuntu Japanese Team Member株式会社 創夢所属。数年前にLaunchpad上でStellariumの翻訳をしたことがきっかけで,Ubuntuの翻訳にも関わるようになりました。