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でechoサーバをつくってみた

こちらは、リナで普通に動いた。ソケットでのややこしい使い方が分かったよ。

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には加盟(変な言い方だ)してないから、昔からの伝統が脈々と流れています。

ああ、伝統と言えば、ある本からのチェーンで、『手妻の話』ってのが面白いよと言われて、最近読み終わった。蝶が舞う手品、水が出て来る、水芸ってのが、日本オリジナルの手妻との事。

一時は外国のマジシャンが絶賛して真似したりしたけど、衰退。日本でも諸般の事情で演舞する人が絶えた。それを頑張って復活させた人による書。なかなか大変な世界なのね。一度、生演技を見てみたいな。