MacOSX El Capitan(10.11)で起きたトラブルです(2020-06-06追記: Mojave(10.14)でも状況は同じです)。
某OSSのtar ballを$TMPDIR
以下に展開してビルドしていたのですが、なぜか展開後のファイルのうち一部のファイルだけ1日後に消える事件が発生しました。
消されるファイルのうちinstall-sh
はconfigure
で必要なファイルなので、昨日作業したディレクトリで今日も./configure
しようと思ったら次のようにエラーで怒られてしまいます。
$ ./configure
checking for grep that handles long lines and -e... /usr/bin/grep
checking for egrep... /usr/bin/grep -E
checking for a sed that does not truncate output... /usr/local/bin/gsed
configure: error: cannot find install-sh, install.sh, or shtool in "." "./.." "./../.."
手元の環境の$TMPDIR
は次のようにランダム文字列を含むディレクトリになっていました。
$ echo $TMPDIR
/var/folders/_0/lkp460qj0j1ftylt6w_3mjxr0000gn/T/
犯人はperiodic
か?
調べてみると近い話題で苦しんでいる人が見つかります。
macOS上でJenkinsのファイルが消えてしまうトラブルがあり、periodic
で起動される日次バッチが悪さをしているのでは?という内容です。
しかし、日次のファイル消去スクリプト /etc/periodic/daily/110.clean-tmps
は /tmp
以下が対象で、今回話題にしている /var/folders
以下を消すようには見えません。また、このスクリプトが消したファイルが/var/log/daily.out
に出力されるはずですが、該当ファイルを消した記録は見当たりませんでした。
というわけでperiodic
が犯人ではないようです。
毎晩3時35分にdirhelper
がファイルを消している
dtraceを有効にしてfbt::VNOP_REMOVE:entry
を監視してみたところ、毎日3時35分に$TMPDIR
以下のファイルを消しているdirhelper
というプロセスを見つけました。
1414517 0 17049 dirhelper VNOP_REMOVE tests/empty.html
1414519 0 17049 dirhelper VNOP_REMOVE fileinfo/EXPERIMENTAL
1414530 0 17049 dirhelper VNOP_REMOVE pdo_oci/EXPERIMENTAL
1414531 0 17049 dirhelper VNOP_REMOVE bug53872/second.txt
1414535 0 17049 dirhelper VNOP_REMOVE skeleton/EXPERIMENTAL
1414546 0 17049 dirhelper VNOP_REMOVE image/blank_file.bmp
1414551 0 17049 dirhelper VNOP_REMOVE 5.6.31/install-sh
1414552 0 17049 dirhelper VNOP_REMOVE 5.6.31/missing
1414552 0 17049 dirhelper VNOP_REMOVE 5.6.31/mkinstalldirs
1414552 0 17049 dirhelper VNOP_REMOVE cgi/php.sym
1414553 0 17049 dirhelper VNOP_REMOVE pi3web/php.sym
1414554 0 17049 dirhelper VNOP_REMOVE thttpd/stub.c
1414556 0 17049 dirhelper VNOP_REMOVE build/default.manifest
今回問題になったinstall-sh
も含め、多くのファイルが消されていることがわかります。
このdirhelper
とは何者でしょうか。man dirhelper
によれば、/System/Library/LaunchDaemons/com.apple.bsd.dirhelper.plist
の設定で起動されている管理用コマンドのようです。このplist中に次の内容が見つかります。
<dict>
<key>CLEAN_FILES_OLDER_THAN_DAYS</key>
<string>3</string>
</dict>
3日前より古いファイルを消す設定、ということのようです。またdirhelperのソースコードを見ると、/var/folders
以下の掃除をしているらしいこと、ファイルのbirthtimeとatimeの両方が指定日数より古かったらファイルを消していることがわかります。
下記URLのコメントも参考になりました。
なぜ一部のファイルだけ消えたのか
macOSでは$TMPDIR
以下のファイルは寿命が3日しかないということなら設定通りであり何の問題もありませんが、なぜ一部のファイルは1日で消えてしまったのでしょうか。
これはmacOS標準のbsdtar
の挙動が関係しています。bsdtar
でファイルを展開するとbirthtimeはアーカイブの時刻と同じ時刻(大抵は3日前より古い)になります。また、atimeはunix epoch(1970-01-01 0時)になるようです。
つまり、tar ballをbsdtar
で展開した場合、大半のファイルは展開直後からdirhelper
の削除対象になるのです。夜中の3時30分にtar ballを展開したのが3時35分に勝手に消えているようだと悲惨ですね。
私の場合ビルドしていたので、ビルドに必要なファイルはコンパイラが読むためatimeが更新されて削除対象から外れたものの、問題となったinstall-sh
はファイルの存在チェックしか行われないためatimeが書き換わらず、dirhelper
に消されてしまったというわけです。
ちなみにGNU tarを使えばファイル展開時にatimeが現在時刻になるため、展開後3日間は消されることはありません。
こんないつファイルが消されるかわからないディレクトリに居られるか!俺は出て行くぞ!
どうやらmacOSの$TMPDIR
以下でOSSのビルドをしてはいけない、という身も蓋もない結論のような気がしますね。$HOME/src/
以下で行うなどした方が平和なんじゃないでしょうか。
(2018-01-23追記)誰が悪いのか
この問題はdirhelper
のバグと言って差し支えないでしょう。使われていないファイルを探すのにbirthtimeとatimeしか見ないというのは現実に即していません。
アーカイバ類が作るファイルが過去のbirthtimeになるのは当然の挙動です。既に指摘したようにbsdtar
だとatimeもアテにならないわけで、まず見るべきはctimeでしょう。昨日展開したファイルであればctimeだけは確実に昨日の日付になるわけですから、これを見ない理由がありません。今のままだと3時30分に展開したファイルを3時35分に消されてしまう可能性さえあるので、dirhelper
がctimeも見るような修正をぜひ入れて頂きたいところです。
一方で、ctimeを見ていたとしても上記の問題が完全に解決するわけではありません。configure
はinstall-sh
のファイル存在チェックしか行わないので、毎日ビルドを繰り返していたとしてもinstall-sh
のbirthtime・atime・ctimeのいずれも更新されず、どのみち4日後には削除される運命です。
そもそも論として、使われていないファイルだけを消すという要求自体が無茶だとも言えるでしょう。つまり、dirhelper
の実装上の問題というよりは要求仕様の問題なのかもしれません。
(2020-06-06追記)最近のmacOSでも挙動は変わっていない
macOS Mojave(10.14)でも上記の挙動は変わっていませんでした。この挙動はバグだろうと私は考えていますが、Appleはそう考えていないようですね。
バグかどうかはさておき、macOSのdirhelper
がファイルを消す基準とbsdtar
の相性が悪いのは間違いありません。ユーザーとしては$TMPDIR
の使い方に注意するしかないでしょう。