参考サイト:http://www.usupi.org/sysad/157.html
ファイルができたりなくなったり変更されたりすると、 即座に通知されるのは便利だなあ、というお話でした。
しかし、操作内容までは教えてもらえないため、自力で調べる必要があるというのは、 ちょっと不便でしたね。
さて、時は流れまして、Linux カーネル 2.6.13 で、 新たに inotify という監視機構が組み込まれました。
inotify は、dnotify と同様に、 ファイルやディレクトリに対して変更が行われると通知してくれる機構です。
ただ、dnotify と異なるのは、 操作されたファイルと操作内容も知らせてくれることです。
inotify を使えば、もう少し楽に、かつ詳細な監視ができそうですね。
というわけで今回は、inotify を使用して、ディレクトリの状態の変化を知る方法を、 ご紹介したいと思います。
まずは、inotify を利用するためのツールをインストールしましょう。
今回は、inotify-tools というツールを使用したいと思います。
幸い、Fedora や Ubuntu, Debian など、主要なディストリビューションには、 inotify-tools のパッケージが存在します。
su コマンドなどで root 権限を得てからか、sudo コマンド経由で、 以下のようにインストールしてください。
# yum install inotify-tools (RedHat 系の場合)
# apt-get install inotify-tools (Debian 系の場合)
残念ながら、わたしが普段使っている Vine や CentOS には、 パッケージが見当たりません。
ですので、以下からソース・アーカイブを入手し、パッケージを作成して対処しました。
inotify-tools
http://inotify-tools.sourceforge.net/
ただし、ソース・アーカイブには SPEC ファイルが含まれません。
ですので、わたしは、以下を inotify-tools.spec として使用しました。
(バージョン 3.13 用ですので、違うバージョンのパッケージを作成する場合は、 SPEC ファイルを修正するなどしてください。)
inotify-tools.spec
これらを所定の位置に置いて、rpmbuild コマンドを実行すれば、 なんの問題もなくパッケージが作成されました。
$ mv inotify-tools-3.13.tar.gz ~/rpmbuild/SOURCES/
$ mv inotify-tools.spec ~/rpmbuild/SPECS/
$ rpmbuild -bb ~/rpmbuild/SPECS/inotify-tools.spec
$ sudo rpm -i ~/rpmbuild/RPMS/i386/inotify-tools-3.13-1.i386.rpm
ただ、某レンタルサーバで使用している CentOS では、 パッケージ作成中にテストでコケてしまいました。
どうやら、テスト中にメモリが足りなくなってしまうようです。
ですので、libinotifytools/src/test.c の258行目の INNER_LIMIT の値を、 1000 くらいに減らして対処しました。
(同じ問題に遭遇するひとはほとんどいないと思いますが、念のため記載しておきます。 もし遭遇して途方に暮れた際には、ご連絡ください。)
さて、inotify-tools パッケージをインストールしますと、 inotifywait と inotifywatch というコマンドが使えるようになります。
inotifywait は、指定したファイルやディレクトリに変化があると、 その旨出力してくれるコマンドです。
inotifywatch は、指定したファイルやディレクトリに対する操作内容をまとめ、 統計結果を出力してくれるコマンドです。
今回目標とするのは、ディレクトリを監視して、 なにか操作されたらすぐ通知してもらうことです。ですので、以降では、 inotifywait コマンドを使って、目標に近付いていきたいと思います。
早速、inotifywait コマンドを使ってみたいと思います。
まず、/tmp/test というディレクトリを作成し、 /tmp/test を指定して inotifywait コマンドを実行してみます。
$ mkdir /tmp/test
$ inotifywait /tmp/test
Setting up watches.
Watches established.
フォアグラウンドで実行中になりますので、別の端末から、 /tmp/test/a というファイルを作ってみましょう。
$ touch /tmp/test/a
すると、inotifywait コマンドを実行中の端末に、以下が実行されます。
/tmp/test/ CREATE a
$
ただし、一発来たところで、inotifywait が終了してしまいます。
一発で終了せず、ずっと実行しつづけてほしいときには、 -m オプションをつけて実行します。
$ inotifywait -m /tmp/test
...
/tmp/test/ CREATE a
/tmp/test/ OPEN a
/tmp/test/ ATTRIB a
/tmp/test/ CLOSE_WRITE,CLOSE a
ただ、デフォルトでは、すべての操作を対象とします。
すべてではなく、特定の操作だけを監視したい場合は、 -e オプションを使用します。
たとえば、ファイルの生成、削除、変更および移動の操作だけを監視するには、 以下のように実行します。
$ inotifywait -m -e create,delete,modify,move /tmp/test
試しに、別の端末から、ファイルを削除してみましょう。
$ rm /tmp/test/a
すると、以下だけが出力されます。
/tmp/test/ DELETE a
また、ファイルを作成してファイル名を変更してみます。
$ touch /tmp/test/a && mv /tmp/test/a /tmp/test/b
すると、以下のように出力されます。
/tmp/test/ CREATE a
/tmp/test/ MOVED_FROM a
/tmp/test/ MOVED_TO b
ちなみに、パーミッションや時刻情報等の属性は監視していませんので、 chmod コマンドなどで変更しても、なにも出力されません。
(もしそれらを監視したい場合は、attrib を指定してください。)
$ chmod 444 /tmp/test/b
それから、あるディレクトリ以下すべてを対象としたい場合は、 dnotify コマンドと同様、-r オプションを指定します。
$ inotifywait -mr /tmp/test
Setting up watches. Beware: since -r was given, this may take \
a while!
Watches established.
他に、出力フォーマットを変更する、--format や -c というオプションもあります。 …が、ここでは割愛させていただきます。
inotifywait コマンドの使いかたがわかったところで、次に、 監視の自動化を試みたいと思います。
早速ですが、以下のスクリプトを作成してみました。
#!/bin/sh
/usr/bin/inotifywait -e create,delete,modify,move,attrib \
-mrq /etc | while read line; do
path=`echo $line | /usr/bin/awk '{print $1}'`
echo $line | /usr/bin/Mail -s "[notice] $path" $USER
done
/etc を監視し、inotifywait がなにか出力したら、 それをメールで通知するためのスクリプトです。
たとえば、これを /usr/local/sbin/inotify.sh という名前で保存して、 まずは手動で実行してみましょう。
# chmod 700 /usr/local/sbin/inotify.sh
# /usr/local/sbin/inotify.sh
すると、何か操作が行われると、メールが送られてきます。
…が、1つの操作ごとに、メールが送られてきてしまいます。
当初の目的は達成しているっぽいですが、メールがたくさん送られてくるのは、 あまりうれしくないように思います。
ですので、ちょっと改良を加えてみました。
#!/bin/sh
NOTIFYPATH=/etc
TIMEOUT=5
MAILTO=root
/usr/bin/inotifywait -e modify,attrib,create,delete \
-mrq $NOTIFYPATH | while [ 1 ]; do
messages=""
while read -t $TIMEOUT line; do
messages="${messages}$line@RET@"
done
if [ -n "$messages" ]; then
echo $messages | sed 's/@RET@/\n/g' | \
/usr/bin/Mail -s "[notice] $NOTIFYPATH" $MAILTO
fi
done
TIMEOUT で指定した秒数(上記では 5秒)分をまとめて、 ひとつのメールで送るようにしてみました。
たとえば、以下を実行して、/etc/X11/foo を1秒だけ存在させてみます。
# touch /etc/X11/foo; sleep 1; rm -f /etc/X11/foo
すると、以下の内容のメールが、1通だけ送られてきます。
(前のスクリプトだと、各行毎にメールが飛んできます。)
/etc/X11/ CREATE foo
/etc/X11/ ATTRIB foo
/etc/X11/ DELETE foo
これなら、実用に堪えられそうです。
あとは、前回と同様、たとえば init から自動的に実行されるように設定すれば、 目標に達成できた、と言えるのではないかと思います。
(/etc/inittab に追加して、kill -HUP 1 を実行します。)
以上、inotify でディレクトリを監視する方法を、ご紹介しました。
inotify や dnotify の詳細を理解したいと思われた貴兄は、 以下などを参照してください。
http://www.linux.or.jp/JM/html/LDP_man-pages/man7/inotify.7.html
http://www.linux.or.jp/JM/html/LDP_man-pages/man2/fcntl.2.html
自分でプログラムを作成しなくても、機能の詳細を確認し、 ツールの中で何が行われているかを推測することは、大事なことだと思います。
あわよくば、スクリプトなどを自分で作成して、 inotify や dnotify を活用していただければと思います。(ぜひ、実践してください!)
宿題の答え
前回の宿題は、
dnotify で監視していることを気づかれないよう細工してみましょう。
でした。
前回、init を使って、自動かつ繰り返し実行させましたが、ps コマンドで見ると、 dnotify で監視していることがバレバレでした。
$ ps ax | grep dnotify
13939 ? Ss 0:00 /usr/bin/dnotify -CDMRBr /etc -e \
/usr/local/sbin/dirnotify.sh {}
...
では、実行するもの全体をシェルスクリプトにしてしまえば、隠蔽できるでしょうか。
#!/bin/sh
/usr/bin/dnotify -CDMRBr /etc -e /usr/local/sbin/dirnotify.sh {}
これを、たとえば /usr/local/sbin/notify.sh という名前にして (実際はもっとわかりにくい名前すべきだと思いますが)、
# chmod 700 /usr/local/sbin/notify.sh
/etc/inittab の、前回追加した行をコメントアウトし、あらためて以下を追加します。
dn:235:respawn:/usr/local/sbin/notify.sh
そして、以下の手順で、init さんにお知らせします。
# kill -HUP 1
# kill
しかしながら、ps コマンドで確認すると、ばっちり出てきます。
(よく考えたら、sh から dnotify を起動してますので、当り前ですね。)
$ ps ax | grep dnotify
19974 ? S 0:00 /usr/bin/dnotify -CDMRBr /etc -e \
/usr/local/sbin/dirnotify.sh {}
...
それでは、dnotify コマンドをコピーして、違う名前にしてみましょう。
(安直なネーミングですが、先ほどと同様、目をつぶってください。)
# cp /usr/bin/dnotify /usr/local/sbin/yfitond
# chmod 500 /usr/local/sbin/yfitond
上記のようにコピーして、先ほどの notify.sh を以下に変更します。
#!/bin/sh
/usr/local/sbin/yfitond -CDMRBr /etc -e /usr/local/sbin/dirnotify.sh {}
そして、notify.sh を kill しますと…
# kill
# ps ax | grep dnotify
...
一応見えなくなりました。
ただし、ps コマンドの出力一覧を見ますと、
$ ps axw
...
21110 ? Ss 0:00 /bin/sh /usr/local/sbin/notify.sh
21111 ? S 0:00 /usr/local/sbin/yfitond -CDMRBr /etc -e \
/usr/local/sbin/dirnotify.sh {}
...
…ああ、オプションの指定が、dnotify の風貌のままです。
勘のいいひとなら、瞬時に見破ってしまうと思われます。
しかし、うっかり宿題にしてしまった以上、これでは引き下がれません。
ここは、dnotify のソースコードを修正するという反則技を使って、 隠蔽を試みたいと思います。
で、ここからは、模範解答ほど遠い内容になっております。
すごーく参考程度に、見るだけにしていただけますと幸いです。
さて、まず、以下からパッチを得ます。(即席で作りました。)
http://www.usupi.org/sysad/157_dnotify_intiki_patch.txt
このパッチを当てますと、引数を与えても与えなくても、 上記オプションがそのまま渡されるようになります。
以下の手順で、インチキ dnotify を作成してください。
$ cd ~/rpmbuild/BUILD/dnotify-0.18.0/src
$ patch -p0 < ~/157_dnotify_intiki_patch.txt
$ make
コンパイルとリンクが通ったら、/usr/local/sbin/yfitond2 という名前でコピーします。
# cp dnotify /usr/local/sbin/yfitond2
# chmod 500 /usr/local/sbin/yfitond2
そして、notify.sh を以下のようにします。(さっぱりしますね!)
#!/bin/sh
/usr/local/sbin/yfitond2
すると、あからさまなオプションが見えなくなりました。
$ ps axw
...
21425 ? Ss 0:00 /bin/sh /usr/local/sbin/notify.sh
21426 ? S 0:00 /usr/local/sbin/yfitond2
...
これで解決なのか、という疑問を拭いされていない気もしますが、一応、 目的地にはたどり着いたということにして、終わりにしたいと思います。
(strings /proc//exe や ls -l /proc//fd などで、 バレバレかもしれませんが…。今度から、宿題はよく考えて出そうと思います。)
Apache 2.0とTomcat 5.0の連携
dnotifyを利用する場合
SQLのテクニックを記
Oracleのテクニックを記
VARCHAR2をNUMBERに変換する際、桁数で怒られるときに桁数を無視するfunction
tracの設定メモ
inotifyを利用する場合
監視サーバーの設
SSLキー作
監視サーバーの設
| |