NetBSD Advent Calendar 2021 6日目の記事です。
今日はファイルの変更を監視するfilemonの紹介をしようと思います。
filemon
filemonはファイルの変更イベントを監視するための疑似デバイスで、NetBSD-6以降でサポートされています。
マニュアルを参照すると、ファイルに対する以下のイベントを監視できます。
イベントタイプ | 意味 |
---|---|
C |
ディレクトリの変更(chdir(2)) |
D |
ファイルの削除(unlink(2)) |
E |
ファイルの実行(exec(3)) |
F |
プロセスのフォーク(fork(2), vfork(2)) |
L |
シンボリックリンクの操作(link(2), symlink(2)) |
M |
ファイル名の変更(rename(2)) |
R |
読み込み専用でのファイルのオープン(open(2)) |
W |
読み書きするファイルのオープン(open(2)) |
X |
プロセスの終了(exit(3)) |
V |
filemonのバージョン |
filemonを試してみる
さて、このfilemonですが、マニュアルだけだとイマイチ使い方が分かりません。そこで、実際に filemon
を動かして振る舞いを見てみます。
filemonの準備
filemon
は疑似デバイスとして提供されており、カーネルコンフィグに以下を追記することで利用可能となります。
pseudo-device filemon
が、NetBSD-amd64の GENERIC
カーネルには pseudo-device filemon
は組み込まれておらず、カーネルモジュールとしてビルドしておく必要があります。
# cd /usr/src/sys/modules/filemon/
# make
...
# file filemon.kmod
filemon.kmod: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped
あとは filemon.kmod
をロードして準備完了です。 /dev/filemon
は最初から存在しているようです(なので mknod
は不要)。
filemonによるファイルイベントの監視
/dev/filemon
を使う準備はできたので、実際にファイルイベントを監視してみます。filemonには /dev/filemon
を用いたファイルイベント監視のサンプルコードがあるのですが、実はこのコードはそのままではビルドできない+ mkstemp("/tmp/filemon.XXXXXXX")
で SEGV
してしまいます…(文字列リテラル "/tmp/filemon.XXXXXXX" は実行ファイルのread onlyな領域に配置されるため、 char temp_path[] = "/tmp/filemonXXXXXXXX";
みたいな感じでスタックorヒープ領域に文字列データを置く必要があるっぽい…)。
とはいえ、大まかな処理の流れは把握できるので、このコードを参考にサンプルコードを作成してみました。
さっそくサンプルを動かして振る舞いを見てみましょう。コードの挙動としては、 /dev/filemon
を open()
したのち、 fork()
した子プロセスの側で ioctl(filemon_fd, FILEMON_SET_PID, &pid);
でファイルイベントを監視したいプロセスのID(この場合は子プロセスID)を指定し、シェルを立ち上げるという流れになります。
発生したファイルイベントは、あらかじめ temp_fd = mkstemp(temp_path);
で作成しておいたファイルに逐次記録される形になります。
# gcc -Wall -Werror -g -o filemon_sample filemon_sample.c
# ./filemon_sample
pid= 484
filemon= /tmp/filemondnp1bePE
#
ここで cal
コマンドを実行してみます。シェル上は普通にコマンドの実行結果が表示されます。
# cal
December 2021
Su Mo Tu We Th Fr Sa
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
ファイルイベントを記録している /tmp/filemondnp1bePE
を見ると、以下が出力されています。
F 893 752
E 752 /usr/bin/cal
R 752 /usr/lib/libterminfo.so.1
R 752 /usr/lib/libc.so.12
R 752 /etc/localtime
R 752 /usr/share/zoneinfo/posixrules
X 752 0
プロセスが fork()
( F
)したのち、 cal
が実行( E
)され、 /usr/lib/libc.so.12
等が読み込み専用でオープンされ( R
)、最後にプロセスが終了( X
)したことが見て取れます。
この機能を応用すれば、特定のファイルが更新された場合の処理を(ポーリング等を行わない)小さな負荷で実現できそうです。
まとめ
filemonの機能を簡単なサンプルを踏まえて紹介してみました。
GENERIC
カーネルに含まれていないのと、マニュアルのサンプルを少し修正する必要があるのが難点ですが、ファイルイベントの監視は応用範囲が広そうです。