make install でインストールされる物を見つける方法
野良ビルドを趣味とする自分にとって、make は友達。
さて、その友達 (make) が何をインストールするのか、ふと取得してみたくなった。
一般的に、make install をすると次のような感じになる。
# make install if test -f /usr/local/bin/vim; then \ mv -f /usr/local/bin/vim /usr/local/bin/vim.rm; \ rm -f /usr/local/bin/vim.rm; \ fi cp vim /usr/local/bin strip /usr/local/bin/vim chmod 755 /usr/local/bin/vim (中略) cp ../runtime/doc/*.pl /usr/local/share/vim/vim74/doc chmod 755 /usr/local/share/vim/vim74/doc/*.pl (中略) /bin/sh ./installman.sh install /usr/local/share/man/fr.UTF-8/man1 "-fr.UTF-8" /usr/local/share/vim /usr/local/share/vim/vim74 /usr/local/share/vim ../runtime/doc 644 vim vimdiff evim installing /usr/local/share/man/fr.UTF-8/man1/vim.1 installing /usr/local/share/man/fr.UTF-8/man1/vimtutor.1 installing /usr/local/share/man/fr.UTF-8/man1/vimdiff.1 installing /usr/local/share/man/fr.UTF-8/man1/evim.1 (以下略)
単純に cp や install コマンドでやっているだけならまだしも、if文とかで分岐されると、単純な解析では出来なくなる。
パケージ管理
ところで、野良ビルドのお供、パッケージ管理ソフト (?) には paco (現在 porg) という物がある。
paco - a source code pacKAGE oRGANIZER for Unix/Linux
porg - a source code package organizer
このコマンドを経由して make を流すと、インストールされたファイルが全て取得できる。
さて、どんなマジックを使っているのか?
fork/exec
paco 2.0.9 のコードを見ると、次の処理がある。
/// paco/log.cpp /// Log::getFilesFromCommand pid_t pid = fork(); if (pid == 0) { // child string command; string libpaco = searchLibpaco(); for (unsigned i = 0; i < mOpt.args().size(); ++i) command += mOpt.args()[i] + " "; setEnv("PACO_TMPFILE", mTmpFile); setEnv("LD_PRELOAD", libpaco); setEnv("PACO_DEBUG", gOut.verbosity() > Out::VERBOSE ? "yes" : ""); /// (中略) char* cmd[] = { (char*)"sh", (char*)"-c", const_cast<char*>(command.c_str()), NULL }; execv("/bin/sh", cmd); throw XErrno("execv()"); } else if (pid == -1) throw XErrno("fork()");
みんな大好き fork/exec で、別プロセスでコマンド(例えば make)を実行している。
さて、この処理だけでは、別プロセスで make が走って、結果を親プロセスは見ていないようだが・・・
LD_PRELOAD
肝心なのは、次の箇所である。
setEnv("LD_PRELOAD", libpaco);
これは libc の中の関数をフックする仕組みであり、LD_PRELOADが指定されたプロセスで有効となる。 (詳細はGoogle先生へ)
ここで指定されているライブラリ libpaco は、次のようになっている。
/// lib/paco-log/log.c static int (*libc_creat) (const char*, mode_t); static int (*libc_link) (const char*, const char*); static int (*libc_open) (const char*, int, ...); static int (*libc_rename) (const char*, const char*); static int (*libc_symlink) (const char*, const char*); static int (*libc_truncate) (const char*, off_t); static FILE*(*libc_fopen) (const char*, const char*); static FILE*(*libc_freopen) (const char*, const char*, FILE*); /// 中略 libc_creat = lp_dlsym("creat"); libc_link = lp_dlsym("link"); libc_open = lp_dlsym("open"); libc_rename = lp_dlsym("rename"); libc_symlink = lp_dlsym("symlink"); libc_truncate = lp_dlsym("truncate"); libc_fopen = lp_dlsym("fopen"); libc_freopen = lp_dlsym("freopen"); /// 中略 int creat(const char* path, mode_t mode) { int ret; CHECK_INIT; ret = libc_open(path, O_CREAT | O_WRONLY | O_TRUNC, mode); if (ret != -1) lp_log(path, "creat(\"%s\", 0%o)", path, (int)mode); return ret; }
フックする関数として libc_creat が定義されており、それをローカル関数の create に紐付けている。
そして、実際の create は処理をフックして、ログを取っている。
[訂正]
同名の関数でフックが出来るらしい。
libc_creat はフック前の関数を保存しているようだ。
このように、make 等で実際に呼ばれる処理 (open/rename/symlinkなど) をフックして、それをログファイルに記録することで、何がインストールされているのかを確認しているようである。
なるほど、これなら make install の出力に悩まさずに済む。
本当に欲しかった物
さて、これで make install でインストールされる物が分かった。
これを使えば、野良ビルドでインストールされる無法者たちを、煮るなり焼くなりし放題である。
しかし、よく考えたら、アンインストールすることなんて早々無かった。
うーむ、何か面白いことが出来ないだろうかー??