FreeBSD で、USB機器を接続したタイミングでなんかコマンドを実行(devd設定)

# 接続したらなんか実行したい。

FreeBSD でのデバイスに関する設定で、常時接続しているようなものなら常にスタート時に起動するスクリプト(/usr/local/etc/rc.d/以下に置く)を書いても問題無いですが、使いたいときだけ接続するデバイスも多いです。そんな動的に抜き差しするようなデバイスの設定をどうするかという話を、「[ゲームパッドのボタンを押したらFreeBSDでコマンド実行](https://qiita.com/s_mitu/items/d56521a1a730221e308c) 」の記事でも使ったゲームパッド、バッファローの [BSGP801](
https://www.amazon.co.jp/BUFFALO-USB%E3%82%B2%E3%83%BC%E3%83%A0%E3%83%91%E3%83%83%E3%83%89-8%E3%83%9C%E3%82%BF%E3%83%B3-%E3%82%B9%E3%83%BC%E3%83%91%E3%83%BC%E3%83%95%E3%82%A1%E3%83%9F%E3%82%B3%E3%83%B3%E9%A2%A8-%E4%B8%A6%E8%A1%8C%E8%BC%B8%E5%85%A5%E5%93%81/dp/B01GI3OOTU/ref=pd_sbs_147_t_0/357-3135414-9593463?_encoding=UTF8) を使って紹介します。

# FreeBSD でデバイス接続を検知する仕組み

FreeBSD でデバイス接続などのイベント検知をする仕組みとして、devd というデーモンがいます。FreeBSD を通常インストールしていれば、有効になってて裏でこっそり起動しています。
USBとか関係なく、「デバイスが接続とか切断した」とか「ネットワークIFがlinkup/linkdownした」などのイベントがkernel であったら、ユーザーランドのコマンドを実行してくれるデーモンです。

# デバイス情報を得る

devd の設定は、「このイベントで、このデバイス情報にマッチしたら、このアクションを実行する」という形です。USB を接続したイベントを取りたい場合は、イベントは「attach」で、アクションは任意のコマンド(リダイレクトなども使える)です。あとはデバイス情報がわかれば設定ができます。

デバイス情報は「devinfo」コマンドを使います。ゲームパッドは「/dev/uhid0」として生えたので、その場合は

```shell:devinfo

devinfo -p uhid0 -v 
```

を実行します。「/dev」は付けず、/dev以下をルートとして指定します。
すると以下のような結果を得ます。

```
uhid0 pnpinfo vendor=0x0583 product=0x2060 devclass=0x00 devsubclass=0x00 devproto=0x00 sernum="" release=0x0100 mode=host intclass=0x03 intsubclass=0x00 intprotocol=0x00 at bus=0 hubaddr=1 port=2 devaddr=3 interface=0 ugen=ugen0.3
 uhub1
 usbus0
 xhci0 pnpinfo vendor=0x8086 device=0x9cb1 subvendor=0x17aa subdevice=0x2226 class=0x0c0330 at slot=20 function=0 dbsf=pci0:0:20:0 handle=\_SB_.PCI0.XHCI
 pci0
 pcib0 pnpinfo _HID=PNP0A08 _UID=0 at handle=\_SB_.PCI0
 acpi0
 nexus0
```

色々出ていますが、とりあえず使うのは最初の行の「vendor」と「product」の部分、
<pre>
vendor=0x0583 product=0x2060
</pre>
です。

# 設定ファイルを書く
ここまで情報が集まって、実行したいコマンドが決まっていれば、設定ファイルがかけます。
設定ファイルは、/etc/devd.conf と /etc/devd/ディレクトリ以下の任意の *.conf ファイルです。自分の独自のオレオレ設定を書く場合は、OSのアップグレードで書き換わってしまう /etc/devd.conf を直接書き換えるのではなく /etc/devd以下に新規に設定ファイルを作ります。ファイル名が「*.conf」の形ならば、ファイルを置くだけで勝手に読み込んでくれます。

まずはとりあえず、ゲームパッドが挿さったらsyslog に出力するだけの設定を作ってみます。
/etc/devd/gamepad.conf というファイル名で

```/etc/devd/gamepad.conf
attach 100 {
        device-name "uhid[0-9]+";
        match "vendor" "0x0583";
        match "product" "0x2060";
        action "/usr/bin/logger -t gamepad $device-name $vendor $product";
};
```
デバイスが attach したときに{}内の比較を行い、一致したらactionを実行します。各行は最後にセミコロン";"を付けます。最後のカッコの後にも必要です。
「100」という数字はプライオリティで、 大きい方が優先度が高いです。２つの同じイベントに一致したアクションがあった場合、片方のみ実行されます。とりあえずここでは100を指定しています。
device-nameや match 〜の行でイベント補足したいデバイスの情報を指定します。単なる文字列ではなく正規表現が使えますから、上記の設定では「uhid(数字)」に一致したデバイスノードが attach したら、という条件になります。
action にはコマンドを指定します。shに渡されるので、リダイレクトなども使えます。$device-name などの変数には上の設定で一致した結果値が入ります。


ファイルを作成した後 root 権限で

```shell-session:
# service devd restart
```

を実行すると設定ファイルが反映されます。

そして、ゲームパッドを USBに挿すと、/var/log/messages に
```
Dec 15 15:24:56 hostname gamepad[4139]: uhid0 0x0583 0x2060
```
というようなログが出力されます。


「[ゲームパッドのボタンを押したらFreeBSDでコマンド実行](https://qiita.com/s_mitu/items/d56521a1a730221e308c) 」の記事で、ゲームパッドのボタンアクションを設定しました。この設定をデバイス挿入時に自動で行われるようにするには。action 部分を書き換えて、
```
attach 100 {
        device-name "uhid[0-9]+";
        match "vendor" "0x0583";
        match "product" "0x2060";
        action "usbhidaction -f /dev/$device-name -c /usr/local/etc/gamepad.conf";
};
```
などとします。

# devdのさらなる可能性

devd はデバイスイベントだけでなく、kernel が検知した様々なイベントを処理できます。devd のさらなる可能性については別の記事で探りたいと思います。