OpenBSD httpd (2)
年金支給日だったので、銀行へ行ってきた。
何時にも増して、警官と防犯協会だかのメンバーが多数いたぞ。そして、特殊詐欺防止の パンフレットをしっかり配布してた。 このエリアから被害者を出すと、署長が怒られるんだな。出世に響く。それで、号令一過、 配下の連中が一斉動員されたんだろう。
黄色に手提げ袋を渡してくる人が居たんで、もう十分に承知してますから、その資料は結構で ございますって断ったんだけど、持たされてしまったよ。途中で棄てる訳にもいかず、私は65歳を超えた人ですって宣伝してるみたいで、超恥ずかしかったぞ。
用を済ませて銀行を出ると、別の人に話しかけられた。怪しい電話がかかって来た事ありますか? 電話は無いですけど、友人の所に、パソコンの身代金を払えってメールが来て、五月蝿いから どうしたらいいか相談受けましたって答えたよ。身代金は現代風にビットコインで払えって言って(書いて)きたそうですって、話を振ってあげた。
ビットコインって何ですか?って聞かれちゃった。そうか、田舎のおまわりさんだと、遅れているのね。署長、そういう所から、再教育宜しく。
ただビラを配ればいいってもんじゃありませんぜ。
try libevent on Linux
前回予習用に挙げた、 C言語 libeventの使い方 tailを作ってみるを、久しぶりのウブで試してみる。インクルードファイルが無くて、コンパイルエラーになるのは、リナのお約束事項。apt install libevent-dev して、切り抜けろ。
sakae@usvr:/tmp$ touch foo sakae@usvr:/tmp$ ./a.out /tmp/foo [warn] Epoll ADD(1) on fd 3 failed. Old events were 0; read change was 1 (add); write change was 0 (none); close change was 0 (none): Operation not permitted
エラーになるのも、お約束事項?
38 if ( (fd = open (file, flags) ) == -1) { (gdb) 42 event_init (); (gdb) catch syscall group: ;; 困った時のTAB頼み。 descriptor ipc network signal file memory process (gdb) catch syscall group:descriptor ;; 感を働かせて選んでみた。 Catchpoint 2 (syscalls 'read' [0] 'write' [1] 'open' [2] 'close' [3] 'fstat' [5] 'poll' [7] 'lseek' [8] 'mmap' [9] 'ioctl' [16] 'pread64' [17] 'pwrite64' [18] 'readv' [19] 'writev' [20] 'pipe' [22] 'select' [23] 'dup' [32] 'dup2' [33] 'sendfile' [40] 'fcntl' [72] 'flock' [73] 'fsync' [74] 'fdatasync' [75] 'ftruncate' [77] 'getdents' [78] 'fchdir' [81] 'creat' [85] 'fchmod' [91] 'fchown' [93] 'fstatfs' [138] 'readahead' [187] 'fsetxattr' [190] 'fgetxattr' [193] 'flistxattr' [196] 'fremovexattr' [199] 'epoll_create' [213] 'getdents64' [217] 'fadvise64' [221] 'epoll_wait' [232] 'epoll_ctl' [233] 'inotify_init' [253] 'inotify_add_watch' [254] 'inotify_rm_watch' [255] 'openat' [257] 'mkdirat' [258] 'mknodat' [259] 'fchownat' [260] 'futimesat' [261] 'newfstatat' [262] 'unlinkat' [263] 'renameat' [264] 'linkat' [265] 'symlinkat' [266] 'readlinkat' [267] 'fchmodat' [268] 'faccessat' [269] 'pselect6' [270] 'ppoll' [271] 'splice' [275] 'tee' [276] 'sync_file_range' [277] 'vmsplice' [278] 'utimensat' [280] 'epoll_pwait' [281] 'signalfd' [282] 'timerfd_create' [283] 'eventfd' [284] 'fallocate' [285] 'timerfd_settime' [286] 'timerfd_gettime' [287] 'signalfd4' [289] 'eventfd2' [290] 'epoll_create1' [291] 'dup3' [292] 'pipe2' [293] 'inotify_init1' [294] 'preadv' [295] 'pwritev' [296]) (gdb) c Continuing. Catchpoint 2 (call to syscall epoll_create1), 0x00007ffff78b5217 in epoll_create1 () at ../sysdeps/unix/syscall-template.S:78 78 ../sysdeps/unix/syscall-template.S: No such file or directory. (gdb) bt #0 0x00007ffff78b5217 in epoll_create1 () at ../sysdeps/unix/syscall-template.S:78 #1 0x00007ffff7bacb45 in ?? () from /usr/lib/x86_64-linux-gnu/libevent-2.1.so.6 #2 0x00007ffff7ba4671 in event_base_new_with_config () from /usr/lib/x86_64-linux-gnu/libevent-2.1.so.6 #3 0x00007ffff7ba47ab in event_init () from /usr/lib/x86_64-linux-gnu/libevent-2.1.so.6 #4 0x0000555555554be2 in main (argc=2, argv=0x7fffffffe4d8) at t.c:42
syscallにまとめてBPを張る技を仕入れたので、試してみた。それにしても、ソースが無いのが リナのデフォルトってのは、味気ない。まあ、ヘッダーファイルを載せるのも渋っているぐらい ですからね。今日の所は、これぐらいで勘弁しといたる。
こちらは、リナで普通に動いた。ソケットでのややこしい使い方が分かったよ。
libevent on OpenBSD
上のtailを作るやつをOpenBSDにかけてみた。file_readの所でブロックしないで、ポーリングしてる風情(おかげで、CPUパワーを食いつぶしてしまう)。
念の為にFreeBSDとNetBSDでも、試してみた。こちらは、問題無く動く。するとOpenBSD特有の問題? それとも、特殊な作法が必要?
gdbで開いた所、openしたファイルのディスクリプターが8になってる事。普通は0,1,2が使われているので、次に選択されるのは3なんだけどな。(FreeBSDとかは3だった)
素直に考えると、3-7番さんに先客がいるんで、次に使えるのが8でしたって事になる。その先客さんて誰よ?気になる。
そういう場合には、確か lsof を使うんだったな。portsを探したけど無かった。世界のみんなに問うたら、昔はsysutiliに有ったよ。でも、OpenBSDはセキュリティー重視の独裁国家ゆえ、パージされたとの事。その代わりと言っちゃなんだけど、fstat(1)を使えとな。
ob6$ fstat -u sakae| egrep '(PID|a.out)' USER CMD PID FD MOUNT INUM MODE R/W SZ|DV sakae a.out 42416 wd /tmp 2 drwxrwxrwt r 512 sakae a.out 42416 0 / 889411 crw--w---- rw ttyp5 sakae a.out 42416 1 / 889411 crw--w---- rw ttyp5 sakae a.out 42416 2 / 889411 crw--w---- rw ttyp5 sakae a.out 42416 3 / 657855 -rw-r--r-- w 0 sakae a.out 42416 4 / 889408 crw-rw-rw- rw ptyp3 sakae a.out 42416 5 / 889407 crw--w---- rw ttyp3 sakae a.out 42416 6 / 657856 -rw-r--r-- w 17747 sakae a.out 42416 7 / 889409 crw--w---- r ttyp4 sakae a.out 42416 8 /tmp 4 -rwx------ r 0
gdbでステップ実行して、openした直後の状態。ファイル名が出て来ていないけど、modeを0700 にしておいたので、fd=8が相当してると思う。
ob6$ ls -i foo 4 foo*
おっと、こちらの方がDISK上の本籍だから、より正確だな。
余りここで時間を潰していてもしょうがないので、先へ行きます。 libeventの中に入っていけたので、取り合えず良しとする。manによると、libeventの元になるシステムコールは、kqueue,poll,selectの3つをサポートしてるとか。
何が選ばれているか、libeventに飛んで行った時に調べてみた。
(gdb) p *evsel $8 = { name = 0x9d5858b738b "kqueue", init = 0x9d5857b2dd0 <kq_init>, add = 0x9d5857b2ef0 <kq_add>, del = 0x9d5857b30a0 <kq_del>, dispatch = 0x9d5857b3230 <kq_dispatch>, dealloc = 0x9d5857b3440 <kq_dealloc>, need_reinit = 1 }
Breakpoint 1, file_read (fd=8, event=2, arg=0x7f7ffffe5b60) at tl.c:16 (gdb) bt #0 file_read (fd=8, event=2, arg=0x7f7ffffe5b60) at tl.c:16 #1 0x0000040dc638b361 in event_process_active (base=0x40df4570c00) at /usr/src/lib/libevent/event.c:350 #2 event_base_loop (base=0x40df4570c00, flags=0) at /usr/src/lib/libevent/event.c:502 #3 0x0000040b3c3007ce in main (argc=2, argv=0x7f7ffffe5c38) at tl.c:46
なお、使うシステムコールを環境変数で指定出来るので、昔ながらのselectとかに切り替えてみた。けど、現象に変化は無かった。もう、これぐらいにしておく。
注: 上でfdが8になる理由判明。fstatの結果を見てて、仮想端末が1個余分に開いている事に気が付いた。何故かなと思ったら、gdbならぬcgdbを使ってた。こいつが画面分割してソースを表示するのに仮想端末を使ってるのね。素直にgdbを使ったら、 fd=3 となったよ。 犬も歩けば棒に当たる、でしたな。おかげで、fstatなんてコマンドを知る事が出来たし佳としよう。
httpd/slowcgi 外観検査
fstatなんて言う楽しいコマンドを覚えたので、httpdとslowcgiの外観検査をしてみる。
ob6$ fstat | egrep '(httpd|PID)' USER CMD PID FD MOUNT INUM MODE R/W SZ|DV root httpd 90345 text / 988403 -r-xr-xr-x r 163488 root httpd 90345 wd / 51968 drwx------ r 512 root httpd 90345 0 / 890049 crw-rw-rw- rw null root httpd 90345 1 / 890049 crw-rw-rw- rw null root httpd 90345 2 / 890049 crw-rw-rw- rw null root httpd 90345 3* unix stream 0x0 root httpd 90345 4 kqueue 0x0 0 state: W root httpd 90345 5* unix stream 0x0 www httpd 68947 text / 988403 -r-xr-xr-x r 163488 www httpd 68947 wd / 493748 drwxr-xr-x r 512 www httpd 68947 root / 493748 drwxr-xr-x r 512 www httpd 68947 0 - - bad - www httpd 68947 1 / 890049 crw-rw-rw- w null www httpd 68947 2 / 890049 crw-rw-rw- w null www httpd 68947 3* unix stream 0x0 www httpd 68947 4 kqueue 0x0 0 state: W www httpd 68947 5* unix stream 0x0 www httpd 68947 6 / 493844 -rw-r--r-- w 1853 www httpd 68947 7 / 494080 -rw-r--r-- w 0 www httpd 18236 text / 988403 -r-xr-xr-x r 163488 www httpd 18236 wd / 493748 drwxr-xr-x r 512 www httpd 18236 root / 493748 drwxr-xr-x r 512 www httpd 18236 0 - - bad - www httpd 18236 1 / 890049 crw-rw-rw- w null www httpd 18236 2 / 890049 crw-rw-rw- w null www httpd 18236 3* unix stream 0x0 www httpd 18236 4 kqueue 0x0 0 state: W www httpd 18236 5* unix stream 0x0 www httpd 18236 6* internet stream tcp 0x0 127.0.0.1:80
FD欄に出て来るtextは、実行ファイル。wdはworking directory。rootは、root directoryを表しているとか。
PID=18236にtcpなんてのが見えるので、多分これが、本当のwwwサーバーだろう。確認してみる。
ob6$ ps awx | grep httpd 68947 ?? Isp 0:00.01 httpd: logger (httpd) 18236 ?? Isp 0:00.01 httpd: server (httpd) 90345 ?? Isp 0:00.01 /usr/sbin/httpd
そしてroot-dirは、/var/wwwのはず。これも確認する。
ob6$ find /var -inum 493748 : /var/www
これが、監獄の壁だ。壁の外には行けない閉じた空間。いわゆるサンドボックスってやつ。 実行ファイルは、あそこのはず。
ob6$ find /usr/sbin -inum 988403 /usr/sbin/httpd
それから、fd=4 に、kqueueってのが見えるけど、これって、libeventの大本だな。 その他、unix streamが2本用意されてるけど、これって、内部連絡用かな?
後、log用のサーバーのfdで言うと6,7番さんは、どこのファイル? 大体想像付くけど、確かめてみる。
ob6$ find /var/www -inum 493844 /var/www/logs/access.log ob6$ find /var/www -inum 494080 /var/www/logs/error.log
お次は、古い人の御用達サーバーです。
ob6$ fstat | egrep '(slowcgi|PID)' USER CMD PID FD MOUNT INUM MODE R/W SZ|DV www slowcgi 9252 text / 988228 -r-xr-xr-x r 26424 www slowcgi 9252 wd / 493748 drwxr-xr-x r 512 www slowcgi 9252 root / 493748 drwxr-xr-x r 512 www slowcgi 9252 0 / 890049 crw-rw-rw- rw null www slowcgi 9252 1 / 890049 crw-rw-rw- rw null www slowcgi 9252 2 / 890049 crw-rw-rw- rw null www slowcgi 9252 3* unix stream 0x0 www slowcgi 9252 4 kqueue 0x0 0 state: W
サーバーと言いつつ、外に開かれているのは、unix stream一本だけ。このstreamが何処にマッピングされてるかは、netstatしろとの事だけど、
ob6$ doas netstat -A | egrep '(PCB|slow)' doas (sakae@ob6.localdomain) password: PCB Proto Recv-Q Send-Q Local Address Foreign Address (state) 0xffff80000014fc00 stream 0 0 0xffffff006c33fc80 0x0 0x0 0x0 /var/www/run/slowcgi.sock
これじゃ、名前を知ってる人しか、対応を見極められないよ。このあたりが限界なんかな。それとも、安全上の理由から、隠してありますって国家機密? 余り深く追求するのは止そう。
たこ足
上の観察で、unix streamが、サーバー間との連絡に使われているであろうと想像した。 多分、root権限で動いているサーバーから、各webサーバー及びロガーへの通信用。 もう一つの系統は、各webサーバーからロガーへのログ転送用。
webサーバーがN台あったとする。root権限のサーバーは N + 1 本の足が出てるはず。 同じくロガーも、N + 1本の足が出てるはず。 各サーバーは、2本の足(rootとの通信とログ転送)になると思われる。
/etc/httpd.confに設定してる、preforkをコメントアウトしてから、httpdを再起動。
ob6$ ps ax | grep httpd 32414 ?? Isp 0:00.01 httpd: logger (httpd) 48703 ?? Isp 0:00.01 httpd: server (httpd) 35587 ?? Isp 0:00.01 httpd: server (httpd) 99218 ?? Isp 0:00.01 /usr/sbin/httpd 11469 ?? Isp 0:00.01 httpd: server (httpd) 39367 p1 R+/1 0:00.00 grep httpd
デフォの設定だと、webサーバーは3台の並列運転になるのね。で、足の数を数えてみる。
ob6$ fstat | grep httpd | grep unix root httpd 99218 3* unix stream 0x0 root httpd 99218 5* unix stream 0x0 root httpd 99218 7* unix stream 0x0 root httpd 99218 9* unix stream 0x0 www httpd 32414 3* unix stream 0x0 www httpd 32414 5* unix stream 0x0 www httpd 32414 6* unix stream 0x0 www httpd 32414 7* unix stream 0x0 www httpd 48703 3* unix stream 0x0 www httpd 48703 5* unix stream 0x0 www httpd 11469 3* unix stream 0x0 www httpd 11469 5* unix stream 0x0 www httpd 35587 3* unix stream 0x0 www httpd 35587 5* unix stream 0x0
予想とぴったり一致した。
fstat
ここでちょっと横道に逸れるけど、fstatについてみておく。
at OpenBSD
ob6$ fstat -u sakae | less USER CMD PID FD MOUNT INUM MODE R/W SZ|DV sakae less 14207 wd / 779521 drwxr-xr-x r 1024 sakae less 14207 0 pipe 0x0 state: sakae less 14207 1 / 889405 crw--w---- rw ttyp2 sakae less 14207 2 / 889405 crw--w---- rw ttyp2 sakae fstat 89085 wd / 779521 drwxr-xr-x r 1024 sakae fstat 89085 0 / 889405 crw--w---- rw ttyp2 sakae fstat 89085 1 pipe 0x0 state: sakae fstat 89085 2 / 889405 crw--w---- rw ttyp2
at FreeBSD
sakae@fb:~ % fstat -u sakae | less USER CMD PID FD MOUNT INUM MODE SZ|DV R/W sakae less 703 text / 1136195 -r-xr-xr-x 163472 r sakae less 703 ctty /dev 82 crw--w---- pts/0 rw sakae less 703 wd / 1204345 drwxr-xr-x 1024 r sakae less 703 root / 2 drwxr-xr-x 1024 r sakae less 703 0* pipe fffff80003aeabe0 <-> fffff80003aead48 0 rw sakae less 703 1 /dev 82 crw--w---- pts/0 rw sakae less 703 2 /dev 82 crw--w---- pts/0 rw sakae fstat 702 text / 1136105 -r-xr-xr-x 20136 r sakae fstat 702 ctty /dev 82 crw--w---- pts/0 rw sakae fstat 702 wd / 1204345 drwxr-xr-x 1024 r sakae fstat 702 root / 2 drwxr-xr-x 1024 r sakae fstat 702 0 /dev 82 crw--w---- pts/0 rw sakae fstat 702 1* pipe fffff80003aead48 <-> fffff80003aeabe0 0 rw sakae fstat 702 2 /dev 82 crw--w---- pts/0 rw sakae fstat 702 3 / 241174 -rw-r--r-- 40960 r
断然FreeBSDの方が、情報豊。ちゃんと公開してる。特にいいのは、パイプの接続状況。 fstatの標準出力がパイプだよ。その一端がlessの標準入力に繋がっているよってのが、手に取るように分かる事。
その点、OpenBSDは、出し惜しみしてるな。パイプの設置点が分かると悪戯される恐れがあるから、控えているのだろうか? ソース嫁。
OpenBSD
/* * We don't have enough space to fit both peer and own address, so * we select the higher address so both ends of the pipe have the * same visible addr. (it's the higher address because when the other * end closes, it becomes 0) */ maxaddr = (void *)(uintptr_t)MAXIMUM(kf->f_data, kf->pipe_peer); printf("pipe "); hide(maxaddr); printf(" state: %s%s%s", (kf->pipe_state & PIPE_WANTR) ? "R" : "", (kf->pipe_state & PIPE_WANTW) ? "W" : "", (kf->pipe_state & PIPE_EOF) ? "E" : "");
表示場所が無いんで、hideしたよと読める。一方、FreeBSDの方は
print_pipe_info(struct procstat *procstat, struct filestat *fst) { struct pipestat ps; char errbuf[_POSIX2_LINE_MAX]; int error; error = procstat_get_pipe_info(procstat, fst, &ps, errbuf); if (error != 0) { printf("* error"); return; } printf("* pipe %8lx <-> %8lx", (u_long)ps.addr, (u_long)ps.peer);
_POSIX2_LINE_MAX=2048 を、免罪符にして、構うもんか表示してやれという態度でしたよ。 そう言えば、リナのlsof表示、横幅は気にしないで、存分に表示してるな。これが、現代っ子ってものなのか。おじさん、80x25サイズ(縦は50でもいいけど)が好きなんだよな。
ついでに書いておくと、リナのコマンドって、man相当がコマンド内に埋め込まれてる。-hとかで、さーと表示されちゃう。現代っ子は、GUIな端末なんで、スクロールを自在に駆使するから、気にしないんだろうね。
オイラーはちまちまと、lessをかませて見てるんだけど、中にはstderrに出すものもあったりで、統一されていない。これなんとかならないんですかね。腹立つ事しきりですよ。
psは特殊か?
上で、何度かhttpdのPIDを調べるため、psのご厄介になってる。親切な事に、これはserverだよとかloggerだよとか、注釈が出て来てる。 これって、psさんの仕業? セキュリティーの名に置いて、プロセスは常に監査してますってか?
そう思って、psを調べてみたけど、一向にそれらしいのに当たらない。ならば、httpdが自己申告するしかないな。
だったら、httpdの名前を変えて、どういう表示になるか確認だな。名前を変えるついでに、gdbを仕込んでおこう。コンパイル結果は、httpdになるけど、オイラーの流儀に従って、a.outに改名しておきます。
幾ら改名したって、rootでしか実行出来ないのはお約束として、その結果は、
ob6$ ps ax | grep a.out 26098 ?? Ssp 0:00.01 ./a.out 95789 ?? Ssp 0:00.01 a.out: logger (a.out) 93530 ?? Ssp 0:00.01 a.out: server (a.out)
gdbをroot権限で実行し、動いているサーバーにattachする。まずは、履歴
#0 kevent () at -:3 #1 0x00000264f94d229f in kq_dispatch (base=<optimized out>, arg=0x26542d8a800, tv=<optimized out>) at /usr/src/lib/libevent/kqueue.c:193 #2 0x00000264f94d003f in event_base_loop (base=0x26542d83000, flags=0) at /usr/src/lib/libevent/event.c:493 #3 0x000002628be154b7 in proc_run (ps=0x26542d84000, p=0x2628c034020 <procs>, procs=0x2628c034140 <procs>, nproc=2, run=0x2628be15af0 <server_init>, arg=0x0) at proc.c:594 #4 0x000002628be15ad8 in server (ps=0x26542d84000, p=0x2628c034020 <procs>) at server.c:87 #5 0x000002628be14047 in proc_init (ps=0x26542d84000, procs=0x2628c034020 <procs>, nproc=2, argc=5, argv=0x7f7ffffc5f68, proc_id=PROC_SERVER) at proc.c:249 #6 0x000002628be0b4ca in main (argc=0, argv=0x7f7ffffc5f68) at httpd.c:218
frameの4あたりで、psの内容を見ると
$5 = { ps_pipes = {0x264b8b27040, 0x264b8b29e40, 0x264b8b03c60}, ps_pp = 0x264b8b29e40, ps_ievs = {0x264b8b05000, 0x0, 0x264b8b16000}, ps_title = {0x2628bf2d466 "parent", 0x7f7ffffc60a5 "server", 0x2628bf2d6f7 "logger"}, :
なんか、それっぽいのが出てきましたなあ。親子関係があって、こういうデータが提供されてる時は、psが気を利かせて、注釈を入れてくれると想像。
このps_titleとかは、httpd.hに定義されてる独自のものだから、汎用性は無いな。
そうなると、psをgdbで追うのが正解かな。
tcsh
ぼーと、FreeBSDでfstatしてたら、面白いのを見つけた。
USER CMD PID FD MOUNT INUM MODE SZ|DV R/W sakae tcsh 678 text / 321059 -r-xr-xr-x 427888 r sakae tcsh 678 ctty /dev 82 crw--w---- pts/0 rw sakae tcsh 678 wd / 1204345 drwxr-xr-x 1024 r sakae tcsh 678 root / 2 drwxr-xr-x 1024 r sakae tcsh 678 15 /dev 82 crw--w---- pts/0 rw sakae tcsh 678 16 /dev 82 crw--w---- pts/0 rw sakae tcsh 678 17 /dev 82 crw--w---- pts/0 rw sakae tcsh 678 18 /dev 82 crw--w---- pts/0 rw sakae tcsh 678 19 /dev 82 crw--w---- pts/0 rw
これの何処が面白いって? ぼーとしてんじゃないよ。fd=0,1,2 が居ないじゃないですか。 shellのお約束が破られています。fd=15-19 のうちで、どれが標準入力とかになってるんでしょう? これらは、みんな /dev/pts/0 になってるけどね。
そこで、実験開始。
sakae@fb:/tmp % cat - | tcsh pwd /tmp
こんな具合に、パイプから入力を与える環境を作りました。そして、親子関係を確認します。
sakae@fb:~ % pstree -+= 00001 root /sbin/init -- |-+= 00683 sakae tmux: server (/tmp//tmux-1001/default) (tmux) | \-+= 00693 sakae -tcsh (tcsh) | |--= 00771 sakae cat - | \--- 00772 sakae tcsh
ふむ、693なlogin用tcshの下で、771と772がツルンデいるのだな。そいつにターゲットを 絞ってみます。
sakae@fb:~ % fstat | egrep '(PID|771|772)' USER CMD PID FD MOUNT INUM MODE SZ|DV R/W sakae tcsh 772 text / 321059 -r-xr-xr-x 427888 r sakae tcsh 772 ctty /dev 85 crw--w---- pts/2 rw sakae tcsh 772 wd /tmp 2 drwxrwxrwt 320 r sakae tcsh 772 root / 2 drwxr-xr-x 1024 r sakae tcsh 772 16* pipe fffff80003b008e8 <-> fffff80003b00a50 0 rw sakae tcsh 772 17 /dev 85 crw--w---- pts/2 rw sakae tcsh 772 18 /dev 85 crw--w---- pts/2 rw sakae tcsh 772 19* pipe fffff80003b008e8 <-> fffff80003b00a50 0 rw sakae cat 771 text / 321045 -r-xr-xr-x 13400 r sakae cat 771 ctty /dev 85 crw--w---- pts/2 rw sakae cat 771 wd /tmp 2 drwxrwxrwt 320 r sakae cat 771 root / 2 drwxr-xr-x 1024 r sakae cat 771 0 /dev 85 crw--w---- pts/2 rw sakae cat 771 1* pipe fffff80003b00a50 <-> fffff80003b008e8 0 rw sakae cat 771 2 /dev 85 crw--w---- pts/2 rw
catの標準出力がパイプに接続。それをtcsh側は、fd=16,19で受けてる。こういうのは、Bill joy のマジックなのかな。
csh,tcshはPOSIXには加盟(変な言い方だ)してないから、昔からの伝統が脈々と流れています。
ああ、伝統と言えば、ある本からのチェーンで、『手妻の話』ってのが面白いよと言われて、最近読み終わった。蝶が舞う手品、水が出て来る、水芸ってのが、日本オリジナルの手妻との事。
一時は外国のマジシャンが絶賛して真似したりしたけど、衰退。日本でも諸般の事情で演舞する人が絶えた。それを頑張って復活させた人による書。なかなか大変な世界なのね。一度、生演技を見てみたいな。