inotify(7) は Linux 2.6.13 以降で利用できるファイルシステムの監視用 API です。扱いやすくいろいろな用途に使えそうな気のきいたもので、コマンドラインツール inotify-tools などの応用があります。
仕組みとしては、まず inotify インスタンスを初期化し、次に監視対象のパスと通知してほしいイベントの2つをこのインスタンスに登録することで監視が始まります。このインスタンスはファイルディスクリプタで実現されています。それを read(2)
することでイベントを待ちます。
そこで気になったのは「このディスクリプタを表す proc ファイルシステムのパスを監視させたらどうなるか」ということでした。以下のようなコードを用意して実験です:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <sys/inotify.h>
int
main()
{
int ino, watch, len, x;
char path[128];
struct inotify_event *e;
if ( (ino = inotify_init()) < 0) {
perror(NULL);
return 1;
}
sprintf(path, "/proc/self/fd/%d", ino);
if ( (watch = inotify_add_watch(ino, path, IN_ALL_EVENTS)) < 0) {
perror(NULL);
return 2;
}
len = sizeof(struct inotify_event) + strlen(path) + 1;
e = (struct inotify_event *)malloc(len);
if (!e) {
perror(NULL);
return 3;
}
if ( (x = fork()) < 0) {
perror(NULL);
return 4;
} else if (x == 0) { /* child */
chmod(path, 0);
_Exit(0);
} else { /* parent */
int status;
ssize_t s;
wait(&status);
retry:
s = read(ino, e, len);
if (s < 0) {
perror(NULL);
_Exit(4);
}
printf("%d\n", x);
goto retry;
}
free(e);
return 0;
}
コンパイルし実行:
$ uname -mrsv
Linux 2.6.26-1-686 #1 SMP Mon Dec 15 18:15:07 UTC 2008 i686
$ gcc -Wall -W -o ino ino.c
$ ./ino
Permission denied
$ echo $?
2
$
どうも権限に従って inotify_add_watch(2)
の呼び出しでエラーになっているようです。それならということで root 権限で実行すると:
$ sudo ./ino
13517
13517
13517
13517
13517
(snip)
13517
13517
13517
13517
13517
^C
$
見事に自分自身に対する read(2)
で無限ループに陥ってしまいました!