udevルールの極意:USBデバイス名の変動を解決し、Linuxハードウェアを自動化する

Linux tutorial - IT technology blog
Linux tutorial - IT technology blog

デバイス名の変動という悩み

バックアップスクリプト/dev/sdb1 をターゲットにしていたために、本番環境のデータベースを消去しかけたことがあります。数週間は正常に動いていましたが、予備のUSBメモリを挿して再起動した瞬間に事態は一変しました。バックアップドライブが /dev/sdc1 になり、スクリプトは中身のない16GBのインストーラー用USBにデータを書き込もうとしていたのです。これが、私が udev を使い始めるきっかけとなりました。

Linuxにおいて、udevは /dev ディレクトリ内に存在するデバイスマネージャーです。カーネルを監視し、ハードウェアが接続された瞬間を捉えて、特定のルールを適用してデバイス名やアクセス権限を決定します。Arduinoを常に /dev/arduino としてマウントしたい場合でも、カメラが接続された瞬間にスクリプトを実行したい場合でも、udevがその答えとなります。

クイックスタート:5分で作成するudevルール

理論は抜きにして、すぐに解決策に入りましょう。例えば、どのポートに挿しても常に同じ名前でアクセスでき、全ユーザーに読み書き権限(0666)が必要な外付けSSDがあるとします。

1. ハードウェアの特定

デバイスを接続し、lsusb でIDを取得します:

lsusb
# 出力例:
# Bus 002 Device 003: ID 0bc2:ab44 Seagate RSS LLC Expansion Portable

この場合、0bc2idVendor で、ab44idProduct です。

2. ルールファイルの作成

Linuxは /etc/udev/rules.d/ 内のudevルールをアルファベット順に処理します。自分のルールを優先させるため、ファイル名は 99 などの大きな数字で始めます。

sudo nano /etc/udev/rules.d/99-external-ssd.rules

3. ロジックの記述

ファイルに以下の1行を記述し、取得したIDに書き換えます:

SUBSYSTEM=="usb", ATTRS{idVendor}=="0bc2", ATTRS{idProduct}=="ab44", MODE="0666", SYMLINK+="backup_disk"

4. システムの再読み込み

新しいルールをudevに認識させます:

sudo udevadm control --reload-rules
sudo udevadm trigger

これで、そのドライブを接続するたびに /dev/backup_disk というシンボリックリンクが作成されます。もう名前を推測する必要はありません。

深掘り:udevルールは実際にどのように機能するか

udevルールは単純な論理ゲートだと考えてください: もし [一致キー] ならば、 [代入キー] を実行する。

一致キー(「もし」の部分)

これらは == 演算子を使用して、デバイスが基準に一致するかを確認します。

  • KERNEL: カーネルのデフォルト名(sd*ttyUSB* など)でフィルタリングします。
  • SUBSYSTEM: デバイスのカテゴリ(ディスクなら block、ネットワークカードなら net など)を対象にします。
  • ATTRS{属性}: シリアル番号や製造元文字列など、ハードウェアレベルの詳細を確認します。

代入キー(「ならば」の部分)

udevが一致するものを見つかった場合、= または += を使用してこれらのアクションを実行します。

  • SYMLINK: /dev 内に分かりやすい別名を作成します。NAME を直接書き換える代わりに、これを使用するのが一般的です。
  • OWNER, GROUP, MODE: デバイスを実際に使用できる権限を決定します。
  • RUN: 外部スクリプトを実行します。

udevadm info の威力

標準の lsusb 出力では、複雑なルールを書くには情報が足りないことがよくあります。udevが追跡可能なすべての属性を確認するには、info コマンドを使用してデバイスツリーを辿ります:

udevadm info -a -p $(udevadm info -q path -n /dev/sdb)

このコマンドは、すべての親デバイスとその属性のリストを吐き出します。 重要なルールが1つあります: 異なる親デバイスから属性を引っ張ってくることはできますが、1つのルール行に2つの異なる親デバイスの属性を混ぜることはできません。

応用編:自動化と永続化

シンボリックリンクをマスターすれば、ワークフロー全体を自動化できます。

ネットワークインターフェース名の固定

カーネルのアップデート後に eth0eth1 に変わるのにうんざりしていませんか?MACアドレスでNICを特定し、名前を固定しましょう:

SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="00:e0:4c:53:44:58", NAME="wan_internal"

写真同期の自動実行

ここからが面白くなるところです。特定のSDカードが挿入されたときに、スクリプトを自動的に実行させることができます:

SUBSYSTEM=="block", ACTION=="add", ATTRS{serial}=="5744-2D58", RUN+="/usr/local/bin/backup_photos.sh"

注意: スクリプトの実行に時間がかかりすぎると、udevはハングします。タスクが5秒以上かかる場合は、systemd-run を使用するか、バックグラウンドで処理させてシステムのレスポンスを維持してください。

現場からの実践的なヒント

私は4年間で15台以上のLinuxサーバーを管理してきましたが、これら3つのルールによって数え切れないほどのトラブルシューティングの時間を節約できました。

1. 本番前にテストする

見た目が正しいからといって、ルールが動くと決めつけないでください。udevadm test を使用してデバイスイベントをシミュレートします。ケーブルに触れることなく、どのルールがトリガーされるかを正確に教えてくれます。

# /dev/sdb のイベントをシミュレートする
udevadm test $(udevadm info -q path -n /dev/sdb)

2. ライブモニタリングを活用する

ルールが実行されない場合は、別のターミナルを開いて udevadm monitor を実行してください。これはカーネルに届くすべてのハードウェアイベントのリアルタイムログのようなものです。マッチングプロセスのどこで失敗しているかが正確に分かります。

3. 0666ではなくグループを使用する

パーミッションを 0666 に設定することはセキュリティリスクです。ローカルユーザーや侵害されたサービスがハードウェアにアクセスできてしまうからです。代わりに、特定のグループにデバイスを割り当てましょう:

SUBSYSTEM=="usb", ATTRS{idVendor}=="1a86", GROUP="developer", MODE="0660"

これにより、developer グループのユーザーのみがデバイスを操作できるようになり、本番環境のセキュリティをより強固に保つことができます。

udevは二重等号や波括弧など一見難しそうに見えますが、Linuxスタックの中で最も信頼性の高いコンポーネントの1つです。ホットスワップ可能なハードウェアの混沌とした状態を、予測可能で自動化されたシステムへと変えてくれます。まずは簡単なシンボリックリンクから始めてみてください。もう二度と、変動するデバイス名を追いかける日々には戻れなくなるはずです。

Share: