公開日: 2021年9月12日

USB メモリが挿入されたら通知を受け取って手動でマウントする

FAT32 でフォーマットされた USB メモリを手動でマウントする方法を紹介します。また、その際にデバイスノード (/dev/da0*) の情報が必要となるので、USB メモリが挿入されたらデバイスノードをシステムから通知してもらって端末画面に表示するようにもしてみます。

そして、すっかり忘れていましたが、FreeBSD と Windows をデュアルブートにしたときマルチブートにしたときに両者のデータ共有スペースとして FAT32 領域を確保してあったので、ついでにそれもマウントして利用できるようにします。

オートマウントでよいんじゃ…

冒頭に書いたようなややこしいことをしなくても、FreeBSD には USB メモリをオートマウントしてくれる autofs という仕組みがあります。が、試してみたところ複数の USB メモリで挿抜を繰り返したときの挙動がいまいち理解できなかったため、諦めまして、とりあえず手動マウントに落ち着きました。あまり詳しく確認していませんが、その挙動とは

  • USB メモリを交換すると、直前にマウントしていた別の USB メモリのディレクトリ構造が残っている。
  • USB メモリを交換すると、その USB メモリのデータが何も表示されない。

などです。autofs が USB メモリの情報をキャッシュしているらしく、そのあたりが原因ではないかと思いますが調べきれませんでした。

autofs を試してみたい、という場合は下記の情報が参考になります。

USB メモリを手動でマウントする

そういう訳で、ここからは手動マウントの話です。とりあえずそのへんに転がっている USB メモリをポートに挿して /var/log/messages を見てみると
Sep 12 06:55:38 yudoufu kernel: da0 at umass-sim0 bus 0 scbus0 target 0 lun 0
Sep 12 06:55:38 yudoufu kernel: da0: <BUFFALO USB Flash Disk 3.10> Removable Direct Access SCSI device
となることからデバイスノードは /dev/da0* っぽいなということがわかり、さらに geom part list コマンドで
% geom part list | grep da0
Geom name: da0
1. Name: da0p1
1. Name: da0
となることから /dev/da0p1 なのだなとわかります。あとは次のようにして mount_msdosfs コマンドでマウント、umount コマンドでアンマウントすることができます。
% sudo mount_msdosfs -L ja_JP.UTF-8 -D CP932 /dev/da0p1 /media
% sudo umount /media
-L や -D のオプションは日本語のファイル名に対する措置です。ファイル名に日本語を使わないのであれば不要でしょう。それにしてもこれではタイプ量が多すぎるので ~/.zshrc や ~/.bashrc にエイリアスを設定しておきます。 私は USB メモリを 2 個も 3 個も同時に挿すことはないので、/dev/da0p1 決め打ちで
~/.zshrc
alias sudo='sudo '
alias mm='mount_msdosfs -L ja_JP.UTF-8 -D CP932 /dev/da0p1 /media; echo "mount /dev/da0p1 on /media"'
としておけば sudo mm とだけ打てばよいことになります。さて、これで万事 OK かと思いきや、いろいろ試すと da0p1 ではなく da0s1 になる USB メモリが見られます。何が違うのかというと、パーティション形式です。geom part list コマンドを実行して出力を眺めるとわかるのですが、p1 は GPT のパーティションを、s1 は MBR のスライスを表しています。という訳で、なんだかウィンチェスターハウスのようになってきましたが、エイリアスを追加して da0p1 と da0s1 とで使い分けるようにします。
~/.zshrc
alias sudo='sudo '
alias mmp='mount_msdosfs -L ja_JP.UTF-8 -D CP932 /dev/da0p1 /media; echo "mount /dev/da0p1 on /media"'
alias mms='mount_msdosfs -L ja_JP.UTF-8 -D CP932 /dev/da0s1 /media; echo "mount /dev/da0s1 on /media"'
使い分けるからには、USB メモリを挿したときに作成されるデバイスノードをその都度知る必要があります。毎回自分で調べるのは面倒なので、システムから通知してもらえる方法を検討しましょう。

デバイスノードをシステムから通知してもらう

システムにデバイスがアタッチ/デタッチされたときに何らかの処理を行いたい場合は /etc/devd/ の下にファイルを作成します。ファイル名は任意ですが、拡張子は conf にします。書き方は /etc/devd.conf が参考になります。特に今回の場合は次の部分にヒントがあります。
/etc/devd.conf
# Discard autofs caches, useful for the -media special map.
notify 100 {
    match "system" "GEOM";
    match "subsystem" "DEV";
    action "/usr/sbin/automount -c";
};
これは autofs の設定なのですが、USB メモリが挿入されたときにカーネルからユーザランドに通知されるイベントをとらえて、キャッシュをクリアするための設定 (らしい) です。そこで、これをもとにいろいろと試行錯誤して、結局次のようになりました。ファイル名は /etc/devd/umem.conf です。
/etc/devd/umem.conf
notify 100 {
    match "system" "GEOM";
    match "subsystem" "DEV";
    match "type" "CREATE";
    match "cdev" "da[0-9]+[ps][0-9]+";
    action "echo '/dev/'$cdev | wall -g operator";
};
USB メモリを挿したときに作成されるデバイスノードのファイル名が変数 cdev に格納されているので、それを wall コマンドで operator グループに所属するユーザに通知します。もちろん通知を受けたいユーザを operator グループに追加しておきます。このようにすると、USB メモリを挿したときにユーザの端末に次のように表示させることができます。
Broadcast Message from mijinco@yudoufu
        (no tty) at 11:07 JST...

/dev/da0p1
あとは通知された内容によって da0p1 か da0s1 のどちらかを手動でマウントします。なんだか思いのほか面倒くさいことになってしまいましたが、勉強にはなりました…。

ハードディスク/SSD 上の FAT32 パーティションをマウントする

ここでついでに FreeBSD と Windows の共有スペースとして確保しておいた FAT32 パーティションをマウントしてみます。例えば「マルチブート事例 2: Windows 10 と FreeBSD」のようなパーティション構成にした場合。geom part list コマンドの出力のうち、Mediasize、type、end、start などの情報から、目的のパーティションが /dev/nvd0p7 であるとわかります。
geom part list の出力 (抜粋)
7. Name: nvd0p7
   Mediasize: 4294967296 (4.0G)
   type: ms-basic-data
   end: 703268863
   start: 694880256
あとは USB メモリのときと同様に mount_msodsfs コマンドでマウント、umount コマンドでアンマウントすることができます。
% sudo mount_msdosfs -L ja_JP.UTF-8 -D CP932 /dev/nvd0p7 /mnt
% sudo umount /mnt
が、USB メモリと違ってこのパーティションは現れたり消えたりせず、常に存在していることは明らかなので、システムの起動時に /mnt にマウントするようにしましょう。/etc/fstab に次のように書きます。
/etc/fstab
/dev/nvd0p7     /mnt        msdosfs     rw     0   0
そして mount -a コマンドで設定を反映させます。
# mount -a
ちなみに -L オプションや -D オプションの指定の仕方がよくわからなかったので、このままでは日本語のファイル名は正しく表示されません。ですが今回はここまで。