1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

NetBSDAdvent Calendar 2021

Day 8

filemonのファイル書き込みイベントをアプリからハンドリングしてみる

Posted at

NetBSD Advent Calendar 2021 8日目の記事です。今日は6日目の記事で紹介したfilemon(4)を利用した少し実用的なアプリを作成してみようと思います。

ファイルイベントをトリガーにしてスクリプトを走らせる

6日目の記事filemon(4)を使用する簡単なサンプルを紹介していました。

サンプルでは指定したプロセスで発生したファイルイベントが /tmp 以下のファイルに書き出されてくるというものでした。filemonの挙動はこれで観察できるのですが、もう少し実用的なアプリが欲しいものです。

ifwatchdコマンドっぽくしてみる

NetBSDにはifwatchd(8)というコマンドがあり、ネットワークインタフェースのUP/DOWN等をトリガーにして、指定したスクリプトを走らせることができます。

ifwatchd コマンドに指定可能なオプションは以下のようになっています。 -u up-script のような形で、指定したイベントに紐づけて実行するスクリプトを指定できます。

     ifwatchd [-hiqv] [-A arrival-script] [-c carrier-script]
              [-D departure-script] [-d down-script] [-u up-script]
              [-n no-carrier-script] ifname(s)

filemon でも -w write-handler-script のように、ファイルに書き込みがあった場合に指定されたスクリプトを実行できるようにしてみます。

今回作成するアプリ

ここまでの内容を踏まえて、作成するアプリのインタフェースを考えてみます。とりあえずは実行したいコマンドとファイル書き込み時に実行させるスクリプトを指定できれば良さそうです。
( -w を指定しない場合は、単にコマンドを実行するだけ)

$ filemon_watch [-w write-handler-script] <command>

filemonでの書き込みイベントの監視

filemon では、 W のイベントタイプを補足すれば書き込みイベントの補足が可能です。

イベントタイプ 意味
W 読み書きするファイルのオープン(open(2))

ファイルイベントが発生するたびに監視用のファイルに追記されるため、都度ファイルをオープンして追記された部分のみをチェックする感じになります。
(ちょうど tail -f コマンドのような挙動になります)

アプリの大まかな構造は以下のようになります。べた書きで処理を記述していますが、指定したコマンドの実行と、ファイルイベントのチェックと書き込みイベントに紐づくスクリプトの実行が一連のコードで実現できています。

アプリのソースコードは以下のGistに置いてあります。

    pid = fork();
    switch(pid) {
        case -1:
            fprintf(stderr, "cannot fork");
            break;
        case 0:
            printf("filemon: %s\n", temp_path);
            pid = getpid();
            ioctl(filemon_fd, FILEMON_SET_PID, &pid); // ファイルイベントが書き込まれるファイル。
            execvp(argv[0], argv); // 指定したコマンドを実行する。
            _exit(1);

            break;
        default:
           lseek(temp_fd, SEEK_SET, 0);

           // 指定したコマンドが終了するまでループする。
           is_done = 1;
           while (is_done) {
               fp = fopen(temp_path, "r");  // ファイルイベントが書き込まれているファイルを開く。
               if (fp) {
                   count = 0;
                   while (fgets(buf, BUFSIZ-1, fp)) {
                       if (count++ < read_line) {  // すでにチェックしたイベントはスキップする。
                           continue;
                       }

                       if (buf[0] == 'W') {  // ファイルへの書き込みイベントがあった場合。
                           // "W <pid> <file>"のフォーマットで書き込まれているのパースする。
                           // "W 521 cal.txt" => "cal.txt"
                           char *path = strchr(buf, ' ');
                           path = strchr(++path, ' ');
                           ++path;

                           switch(fork()) {
                                case -1:
                                    fprintf(stderr, "cannot fork");
                                    break;
                                case 0:
                                    // 書き込みイベントに紐づけられたスクリプトがあったら実行する。
                                    if (w_cmd) {
                                        char *script[] = { w_cmd, path, NULL };
                                        execvp(script[0], script);
                                    }
                                default:
                                    wait(&status);
                           }
                       }
                       if (buf[0] == '#') { // 指定したコマンドが終了したらループを抜ける。
                           is_done = 0;
                           break;
                       }
                   }
                   read_line += count - read_line;  // 読み込んだ行数(=ファイルイベントをチェックした行数)を保持しておく。

                   fclose(fp);
                   usleep(700000);  // 一定時間スリープする(ここでは700ミリ秒)。
               }
           }

           wait(&status);
           close(filemon_fd);
           close(temp_fd);

           break;
    }

実行例

作成したアプリを実行してみます。 -w オプションで書き込みイベントをハンドリングするスクリプトはになります(単に通知ダイアログを表示するだけです)。

#!/bin/sh
notify-send -u normal 'ファイル書き込み' "$1 に書き込みがありました。"

シェルの実行・終了時にもファイルが書き込まれていたり、C言語プログラムをコンパイルする際には、 .s .o が生成されていることが見て取れます。

nbsd1208_01.gif

まとめ

filemon(4)を利用した少し実用的なアプリを作成してみました。filemonで取得可能なイベントは他にもあるため、各イベントに実行したいスクリプトを紐づけられるようにすると実用度が上がりそうです。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?