いま書いてるコードで、forkしてexecするんだけど、execする前にSTDIN/STDOUTを任意のファイルハンドルに置き換えたいなー、もっというとexecするプログラムのSTDINにソケットのREADから流れてくるデータを流し込んで、STDOUTの出力をソケットのWRITEに流し込んでやりたいなー、というようなことを考えていた。
で、これが例えば今のプロセスのSTDOUTの出力をファイルに置き換えるには、以下のようにすればいい。
open(STDOUT, '>', '/path/to/file');
exec >> /path/to/file
さて、STDIN/STDOUTとconnect済みのソケットを結合したい。connect済みのソケットはファイルディスクリプタは持っているがファイルパスを持っていない、ので、普通にopenし直すだけではうまくいかない。perlでどうすんだっけ、と思っていたら親切な人が教えてくれた。
@tagomoris fdopen (open STDIN, "<&=".fileno($socket)) とかでいけませんかね。
2012-09-17 23:35:53 via Echofon to @tagomoris
上記tweetだ2引数openだけど、以下のように3引数openで良いですね。ありがとうありがとう!
# $socket is already connected open(STDIN, '<&=', fileno($socket)); open(STDOUT, '>&=', fileno($socket));
'&=' ってなんだ、とperlのopenのリファレンスを熟読すると、おお、ちゃんと書いてある。& はopen済みのファイルディスクリプタと接続することを示し、さらにそのときに '=' を指定すると対象のファイルディスクリプタをdupしないで同一のものを指すようになる*2、とある。へー、知らなんだ。
で、ファイルハンドル($socket)からファイルディスクリプタを取り出すために fileno() を使っている。
ちなみに一度reopenしたファイルディスクリプタ(STDIN/STDOUT)は exec() で他のプログラムをロードしてもそのまま引き継がれるので、このように準備を整えてから外部プログラムを exec() すれば、呼ばれるほうのプログラムでは STDIN/STDOUT を読み書きするだけで内容がネットワーク越しに転送される。これはだいぶ便利なので、覚えておくと困ったちゃんな構造のツールが綺麗に作れたることがあったりします。