udevによるデバイスの命名の固定化

※ はてなブログから技術ネタを移動しました
※ [初稿](http://qpshinqp.hateblo.jp/entry/20110214/1297651801)：2011-02-14

バイト先のサーバ運用上、Ubuntuサーバ機の中のデータを定期的にバックアップする必要性が生じた。
そのための方法として

- cronを用いて定期的にバックアップコマンドを記述したスクリプトを走らせる

ということをする。


### 【機器】

- Ubuntuサーバ機(Ubuntu Server Edition 10.10)
    - HDDを４台積んでいるため、/dev/sda-sddまでが占有されている。
- 不揮発メモリ(USBフラッシュディスク)2GB
- USB接続の外付けHDD

ここで、Ubuntu機でファイルシステムをマウントする上で問題が発生した。

- /etc/fstabを適切に記述しないと、バックアップファイル保存用のデバイスのマウントポイント名が一定しない。(厳密には一定しないのではなく、長くて扱いづらいというだけか。)

ということで、fstabに以下のような物を追記した

```:/etc/fstab
/dev/sde  /mnt/usb       vfat  defaults  0  0
/dev/sdf  /mnt/backup2T  ext4  defaults  0  0 
```

てな感じで。
ここでは/mnt/usbには不揮発メモリが、/mnt/backup2Tには外付けHDDがマウントされることを想定している。


ここでさらに問題が発生。あんまり再現性は高くないのだが、２つあるUSBに挿しっぱなしのバックアップ用のデバイス達と/dev/sde, /dev/sdfの対応が変わることがあるのだ。

というのも、このUSB接続のデバイス、接続順に/dev/sd○の○の部分にアルファベットを割り当てていくため、サーバ起動時になんらかの要因でこの２つのデバイスの認識順が変わると、/dev/sd*の対応が逆転してしまうのだ。

逆転すると、fstabでマウントする時に、/mnt/usbに外付けHDDが、/mnt/backup2Tに不揮発性メモリがマウントされてしまうことになる。
そうなると、/mnt/usbに不揮発性メモリがマウントされているものとして書かれているバックアップコマンドのスクリプトは意図したように動かなくなってしまい、バックアップも失敗するのだ。


つまり、せねばならんことは、/dev/ 以下に作成されるデバイス・ファイル名を、デバイス固有のものにする、ということだ。これにはudevというものが使えると、先輩がヒントをくださった。


> udev（userspace device management）とは，カーネルがパソコンへの接続を検出したデバイスに対して，動的に「デバイス・ファイル」を作成して割り当てるための仕組みです。
http://itpro.nikkeibp.co.jp/article/Keyword/20090703/333190/

udevの挙動を設定するには、/etc/udev/rules.d/以下にユーザー定義のルール記述ファイルを作成する。
今回は、当該フォルダに"10-local.rules"というテキストファイルを作成した。
このルール記述ファイルは、「こんなデバイスをこんな名前のデバイス・ファイルとして/dev/以下で扱えるようにします」という割り当てが記述されている。

udevは新しいデバイスの接続を認識すると、/etc/udev/rules.d/というフォルダと/lib/udev/rules.d/というフォルダ(後者にはシステム定義のルール記述ファイルが格納されている)の中のルール記述ファイルを調べていき、「こんなデバイス」という条件に合致したらそのルールにしたがってデバイス・ファイル名をデバイスに割り当てる。

それと、このルール記述ファイルを調べていく順番は、辞書順である。つまり、50-〇〇.rulesというファイルよりも10-〇〇.rulesというファイルのほうが先にチェックされる。ユーザー定義のルールは先に適応されてほしいことが多いため、若い番号をルールファイルの名前の先頭にくっつける。また、.rulesという拡張子を付けておかないと、ルールファイルと認識されずにスルーされる。


ルールの書き方の例を以下に挙げる。

>BUS="usb", SYSFS{serial}="HXOLL0012202323480", NAME="lp_epson", SYMLINK="printers/epson_stylus"
http://www.gentoo.gr.jp/transdocs/udevrules/udevrules.html

ここに示されるように、ルールは一行で書かれ、いくつかのキーとその値の組を指定することによって記述される。

キーには２種類ある。

- 識別キー

    これはデバイスを特定するために使われる。「こんなデバイス」がどんなデバイスかを示すために使われる。

    - BUS
    - KERNEL
    - DRIVER
    - SUBSYSTEM
    - ID
    - PLACE
    
    が例。

- 割当てキー

    これは、デバイスが識別キーによるルールにマッチしたら、そのデバイスにどの様なデバイス・ファイル名を割り当てるかとか、どの様な名前のシンボリックリンクを張るかという割り当てを指定するキーです。
    SYMLINK="printer/epson_lp"
    NAME="lp0"
    とか、そんな感じで指定します。割当てキーは、そんなに難しく無いはず。


つまり、識別キーによるデバイスの特定ができればいい。
で、例えば今、手元にあるUSBメモリを任意の名前でデバイス・ファイルにしたいと思う。そのためには、このUSBメモリをこのUSBメモリだと特定するための識別キーの指定が必要である。

識別キーにどのような値を指定すればいいかを調べる必要があるが、http://www.gentoo.gr.jp/transdocs/udevrules/udevrules.htmlによれば、
udevinfo
というものを使うらしい。

```shell-session:使用例)
# udevinfo -a -p $(udevinfo -q path -n /dev/sde)
```
だが、今回のサーバ(Ubuntu Server Edition 10.10)では、

```shell-session
command not found
```
...コマンドが見つからない。
どうもudevinfoはudevパッケージと同梱されている筈なのだが...
そして、

```shell-session
# sudo apt-get install udevinfo 
```
としてもインストールできない。


そんな時、次のような記述を発見

>Re: where is udevinfo?
use udevadm instead.

>```shell-session
# udevadm info -q all -n /dev/sda1
```
http://ubuntuforums.org/showthread.php?t=1265469

これと、http://shikabo.ddo.jp/140.htmlに書いてあるudevinfoの使い方に従い、

```shell-session
# udevadm info -a -p $(udevadm info -q path -n /dev/sde)
``` 
というコマンドを実行。すると、

```shell-session
Udevadm info starts with the device specified by the devpath and then
walks up the chain of parent devices. It prints for every device
found, all possible attributes in the udev rules key format.
A rule to match, can be composed by the attributes of the device
and the attributes from one single parent device.

  looking at device '/devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.4/1-1.4:1.0/host6/target6:0:0/6:0:0:0/block/sde':
    KERNEL=="sde"
    SUBSYSTEM=="block"
    DRIVER==""
    ATTR{range}=="16"
    ATTR{ext_range}=="256"
    ATTR{removable}=="1"
    ATTR{ro}=="0"
    ATTR{size}=="3854336"
    ATTR{alignment_offset}=="0"
    ATTR{discard_alignment}=="0"
    ATTR{capability}=="51"
    ATTR{stat}=="     333     1190     1971      476        0        0        0        0        0      476      476"
    ATTR{inflight}=="       0        0"

  looking at parent device '/devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.4/1-1.4:1.0/host6/target6:0:0/6:0:0:0':
    KERNELS=="6:0:0:0"
    SUBSYSTEMS=="scsi"
    DRIVERS=="sd"
    ATTRS{device_blocked}=="0"
    ATTRS{type}=="0"
    ATTRS{scsi_level}=="0"
    ATTRS{vendor}=="        "
    ATTRS{model}=="USB Reader      "
    ATTRS{rev}=="0001"
    ATTRS{state}=="running"
    ATTRS{timeout}=="30"
    ATTRS{iocounterbits}=="32"
    ATTRS{iorequest_cnt}=="0x1b0"
    ATTRS{iodone_cnt}=="0x1b0"
    ATTRS{ioerr_cnt}=="0x2"
    ATTRS{modalias}=="scsi:t-0x00"
    ATTRS{evt_media_change}=="0"
    ATTRS{dh_state}=="detached"
    ATTRS{queue_depth}=="1"
    ATTRS{queue_type}=="none"
    ATTRS{max_sectors}=="240"

  looking at parent device '/devic.....略
```
てな感じで、デバイスの情報をいっぱい表示してくれる。
自分は、

```shell-session
  looking at parent device '/devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.4':
      :
      :
    SUBSYSTEMS=="usb"
      :
      :
    ATTRS{serial}=="123456789ABC"
```
という部分を利用して、識別キーを指定することにした。

ルールファイルの行は次のようになった

```shell-session
# USB不揮発メモリ
SUBSYSTEM="usb", SYSFS{serial}="123456789ABC", NAME="usb_backup"
# USB接続外付けHDD
SUBSYSTEM="usb", SYSFS{serial}="000010B0B30029BB", NAME="2T_backup
```
そして、デバイスを再認識すべく、

```shell-session
udevstart
```
と入力してエンター。
....
command not found
どうも、Ubuntu 10.10には従来のudevユーティリティが入っていない模様。
おそらくudevadmを使うのだろうと思い、

```shell-session
udevadm
```
とコマンド入力してみると、udevstartに当たるものは見つからないにしても、

```shell-session
udevadm test [デバイス名]
```
というコマンドが見つかった。これはデバイスの挿入をエミュレートして、udevを走らせ、ログを吐かせるコマンド。で、これを使ってデバッグしてみた。

```shell-session
SUBSYSTEM="usb", SYSFS{serial}="123456789ABC", NAME="usb_backup" 
```
では怒られ、

```shell-session
SUBSYSTEM=="usb", ATTRS{serial}=="123456789ABC", NAME="usb_backup" 
```
なら受け入れられた。どうも、参考にしたページの書式とちょっと違うようだ。


で、udevadm testを実行してもルールファイルには問題が内容だったので、万を持してサーバー再起動。再起動後に/etc/fstabを記述し、上で設定したデバイス・ファイルをマウントしようとすると、

/dev/usb_backupはブロックデバイスじゃないからマウントできないと怒られた。
ブロックデバイスとしてのUSBメモリを識別するためにはおそらくudevadm infoで得られた情報のうち、上の方のものを使わないといけなさそうだという直感を得つつも、非常にめんどくさくなって途方に暮れた。


途方に暮れながらも、再度マウントポイントの固定についてググると、なんとuuidという物を使って/etc/fstabが書けるらしい。
問題は一挙に解決した。

だがしかし、udevを用いてブロックデバイスのデバイス・ファイル名を指定する方法は得られていない。