9front (3)
寒いんで、家から15分の天然かけ流し温泉へ行ってきた。頭寒足暖でいくらでも入っていられそう。そのうちにゆだってしまわないか心配。
で、帰りに併設されてる地元産みやげをみたら、何処か遠くの村とコラボレーションして、クルミを売っていた。試供品と言うか試食品が置いてあったけど、クルミ割り器とかトンカチは無かったなあ。このあたりの人は、殻をかみ砕く丈夫な顎をしてるのかしらん。 家で食べてみようと、控え目に5個貰ってきた。 子供の頃は、形の良い鬼胡桃を油で磨いて、どうだカッコイイだろうとなんてやったものだけど、大人になると食い気だけになるのね。
やや、銀杏も置いてあるぞ。ポップを見ると、このあたりでは由緒ある神社の境内産。なんにでも御利益があるって言ってる神社。交通安全、商売繫盛、家内安全、安産保障、受験合格、くじ運抜群。まあ、言うのはただですからね。
天然天日干しらしです。結構お高くて、その値段ならオイラーが調達すれば4倍の分量を手に 入れられるぞ。ブランド品だし、適正価格を知らない人がかもになるんだろうね。
オイラーが買って来るやつは処理に難があり、ちとウンコ臭い。件のブランド品はそんな事は 無いんだろうね。まあ、フライパンで殻に焦げ目が付くぐらいに煎ってしまえば、そんなの関係ねぇーー。
トンカチで殻を割って、中身を取り出す。そして冷凍。冬の保存食。食べる時は、冷凍したままの物を、少量の油を馴染ませたフライパンで塩を一振りしてあぶればおk。
殻を割った時に取り切れなかった薄皮も綺麗に剥がれる。酒のつまみに最適。冬の楽しみですなあ。
listen1改造
9frontを始めた時、運よくunix側から9frontにセッションログをコピペ出来る方法を知った。 が、ちょっと不満が有るんだ。
不満その1として、プロンプトが表示されない。その2は、投入したコマンドがエラーを出しても、それが残らない。エラー報告は、9frontのターミナル上に出て来る。それは正に絵なので、 unix側のCUI端末には切り貼り出来ないんだ。
この2点を何とかしたいな。で、冷静に考えると、プロンプトもエラーもstderrに出てるんだろう。それがパイプに流れ込まないものだから、unix側に届かない。エラー情報は9frontのターミナル上に置いてきぼりされているんだろう。
以上の考察より、パイプの接続部分に問題があると結論付けられる。その施工主はどのコマンドだ? 悩む事なかれ、使ってるコマンドは、listen1しか無い。じっと目視検査。
term% p /sys/src/cmd/aux/listen1.c : bind(data, "/dev/cons", MREPL); dup(fd, 0); dup(fd, 1); /* dup(fd, 2); keep stderr */ close(fd); exec(argv[1], argv+1);
forkした後で、こんな部分が出て切る。パイプを複製してるんだけど、stderrの部分は置き去りにして複製していない。だから、パイプに流れないとな。
このコメントを外しておいてコンパイル。出来たものを$home/bin/mylisten1として置いて おく。後は、このコマンドを使うようにすれば(プロンプトも修正)、
deb9:~$ nc xxx.xxx.xxx.xxx 2323 term% pwd /usr/glenda term% p hogefuga p: can't open hogefuga - 'hogefuga' directory entry not found term% exit deb9:~$
こんな具合に、ちゃんとプロンプトを表示するし、エラーもきちんと出てきた。これにて一件落着。
term% cat bin/rc/riostart #!/bin/rc window 864,1,1024,119 stats -lmisce window -miny 130 bin/mytelnetd
なお、9front起動時からmytelnetdを動かしておきたかったので、ちょいとダエモン君(?)が 走るようにしておいた。
term% ps -a : glenda 474 0:00 0:00 252K Sleep stats -lmisce glenda 484 0:00 0:00 144K Await rc -c '/bin/window -x bin/mytelnetd ' glenda 486 0:00 0:00 148K Await mytelnetd ./bin/mytelnetd glenda 493 0:00 0:00 44K Open /usr/glenda/bin/mylisten1 -t tcp!*!2323 /usr/glenda/bin/chat1 glenda 494 0:00 0:00 0K Wakeme #I0tcpack glenda 546 0:00 0:00 148K Await chat1 /usr/glenda/bin/chat1 glenda 548 0:00 0:00 36K Pread tr -d \015 glenda 549 0:00 0:00 152K Await /bin/rc -i glenda 555 0:00 0:00 0K Wakeme closeproc
netmkaddr, dial
前回やったftpの改造時にポート番号の指定が問題になった。担当はnetmkaddrだったので ソースの在処を探してみた。
term% bin/find -D /sys/src | xargs grep netmkaddr : /sys/src/cmd/cfs/cfs.c: s.fd[0] = dial(netmkaddr(server, 0, "9fs"), 0, 0, 0); /sys/src/cmd/cifs/cifs.c: if((addr = netmkaddr(host, "tcp", "cifs")) == nil) /sys/src/cmd/cifs/netbios.c: if((addr = netmkaddr(host, "udp", "137")) == nil) /sys/src/cmd/cifs/netbios.c: if((addr = netmkaddr(addr, "tcp", "139")) == nil || /sys/src/cmd/cifs/ping.c: if((fd = dial(netmkaddr(host, "icmp", "1"), 0, 0, 0)) == -1) /sys/src/libc/port/netmkaddr.c:netmkaddr(char *linear, char *defnet, char *defsrv)
ポート番号をマジック番号で指定してるかと思うと、cifsみたいに名前で呼んでたりして、この 使い分けは何だと思っちゃう。とにかくソースをミレー。
netmkaddr(char *linear, char *defnet, char *defsrv) { static char addr[256]; char *cp; /* * dump network name */ : if(defsrv) snprint(addr, sizeof(addr), "%s!%s!%s", defnet, linear, defsrv); else snprint(addr, sizeof(addr), "%s!%s", defnet, linear);
なんの事はない。tcp!10.0.2.15!ftp こんな風に正規化してるだけだった。そうなると、本気で見なければいけないのは、dialさんだ。さすがATTは電話会社だけあるな。ダイアルって用語を使ってるよ。プッシュホンより古風な名前なのね。
そしてダイアルして回線が繋がったら、用を済ませ、最後は回線を切断。その関数がhangupとは どこまで電話用語なの。ああ、電話用語と言うとアーランってのを思い出したぞ。あのスウェーデンの電話会社が電子交換機用に落ちない言語を開発してたな。erlangって奴。これ、1時間当たり、どれだけお話し中になってるかを示すものだった。
話を戻して、 ソースはどこだ。manに書いてあったぞ。/sys/src/libc/9sys/dial.c 大事なものだから見ておけっていう親心? そして、素直に関数名がファイル名になってるっぽいな。manに出てこないような関数は、まあ下請けだから無視していいよって事かな。
term% sig netmkaddr dial char* netmkaddr(char *addr, char *defnet, char *defservice) int dial(char *addr, char *local, char *dir, int *cfdp)
念のために、型を確認。次に、acidにかけられるように、超簡単なプログラムを作成。 最初、u.hなんて要らないと思って省いたら、libc.hの所で盛大にエラーが出て来た。 セットで使えと言う事ね。
term% cat test.c #include <u.h> #include <libc.h> main(){ dial("tcp!*!ftp", 0, 0, 0); return 0; }
出来立てのほやほやを試験台に載せてみる。
term% acid 8.out 8.out:386 plan 9 executable /sys/lib/acid/port /sys/lib/acid/386 acid: new() 703: system call _main SUBL $0x48,SP 703: breakpoint main+0x3 MOVL $.string(SB),AX acid: bpset(dial) acid: bpset(csdial) acid: bpset(call) acid: bpset(_dial_string_parse) acid: bptab() 0x000010b6 dial SUBL $0x12c,SP 0x00001263 csdial SUBL $0x230,SP 0x00001513 call SUBL $0x32c,SP 0x00001844 _dial_string_parse SUBL $0x1c,SP
dial.cの関数全てに網を貼ってから、その所在地を確認
acid: cont() 703: breakpoint dial SUBL $0x12c,SP acid: cont() 703: breakpoint dial+0x6 MOVL local+0x4(FP),AX 703: breakpoint _dial_string_parse SUBL $0x1c,SP acid: cont() 703: breakpoint _dial_string_parse+0x3 MOVL ds+0x4(FP),AX 703: breakpoint csdial SUBL $0x230,SP acid: cont() 703: breakpoint csdial+0x6 LEAL buf+0x1a0(SP),AX 703: breakpoint call SUBL $0x32c,SP
そろりそろりと進めてみた。
acid: stk() call(clone=0xdfffed8c,ds=0xdfffeeb4,dest=0xdfffed9b)+0x0 /sys/src/libc/9sys/dial.c:127 csdial(ds=0xdfffeeb4)+0x1f8 /sys/src/libc/9sys/dial.c:109 dial(local=0x0,dir=0x0,cfdp=0x0,dest=0x782c)+0x85 /sys/src/libc/9sys/dial.c:50 main()+0x28 /usr/glenda/tmp/test.c:6 _main+0x31 /sys/src/libc/386/main9.s:16
現在の呼び出し履歴
acid: asm(0x00001513) call 0x00001513 SUBL $0x32c,SP call+0x1 0x00001514 INB DX,AL call+0x2 0x00001515 SUBB $0x3,AL call+0x4 0x00001517 ADDB AL,0x0(AX) call+0x6 0x00001519 MOVL clone+0x0(FP),DX call+0xd 0x00001520 MOVBSX 0x0(DX),AX call+0x10 0x00001523 CMPL AX,$0x2f call+0x13 0x00001526 JEQ call+0x304(SB) :
call関数のアセンブルリスト。目が眩むな。C語のソースをemacsで開いた方が得策。 こうしていても埒が明かないので、別の手をやる。
term% acid -l truss 8.out 8.out:386 plan 9 executable /sys/lib/acid/port /sys/lib/acid/386 /sys/lib/acid/truss acid: new() acid: truss() open("/net/cs", 2) return value: 3 pwrite(3, "tcp!*!ftp", 9, 4294967295) return value: 9 seek(0x000079fc, 3, 0, 0) return value: 0 pread(3, 0xdfffed8c, 127, 4294967295) return value: 17 data: "/net/tcp/clone 21" open("/net/tcp/clone", 2) return value: 4 pread(4, 0xdfffe9dc, 255, 4294967295) return value: 1 data: "2" pwrite(4, "connect 21", 10, 4294967295) return value: -1 close(4) return value: 0 errstr("...", 128) return value: 0 data: "malformed address" pread(3, 0xdfffed8c, 127, 4294967295) return value: 0 data: "" close(3) return value: 0 errstr("malformed addresstot open...", 128) return value: 0 data: "(null)" :
なかなかやるな、acid。ちゃんとしたマニュアルを(斜めって)読んでみるかな。 Acid Manual
acid
で、得た知見を少し試してみる。上の実験だと最初のsyscallで、ネットをオープンし、その ファイルにネット情報を書き込んで、ファイルポインターを戻す。そして読み出すと、どういう風にネットをオープンすべきか教えてくれるように見受けられる。
じゃ、ftpの代わりにhttpでやるとどうなる?
term% acid -l truss 8.out : acid: new() acid: truss() open("/net/cs", 2) return value: 3 pwrite(3, "tcp!*!http", 10, 4294967295) return value: 10 seek(0x000079fc, 3, 0, 0) return value: 0 pread(3, 0xdfffed8c, 127, 4294967295) return value: 17 data: "/net/tcp/clone 80"
ふむ、思った通り、名前解決ならぬport解決が行われている。そんじゃ、openとかのOSとの境界まで迫れるかやってみる。
acid: new() acid: bpset(open) acid: bpset(pread) acid: defn hoge() { cont(); stk(); } acid: hoge() 700: breakpoint open MOVL $0xe,AX open()+0x0 /sys/src/libc/9syscall/open.s:3 csdial(ds=0xdfffeeb4)+0x50 /sys/src/libc/9sys/dial.c:79 dial(local=0x0,dir=0x0,cfdp=0x0,dest=0x782c)+0x85 /sys/src/libc/9sys/dial.c:50 main()+0x28 /usr/glenda/tmp/test.c:6 _main+0x31 /sys/src/libc/386/main9.s:16
今回マニュアルを見てしったんだけど、自分で関数を定義出来るのね。gdbがpythonを導入して、ごちゃごちゃ出来るようになる前に、同様な事を実現してたのね。
acid: src(open) no source for /sys/src/libc/9syscall/open.s acid: asm(open) open 0x00001d1f MOVL $0xe,AX open+0x1 0x00001d20 PUSHL CS open+0x2 0x00001d21 ADDB AL,0x0(AX) open+0x4 0x00001d23 ADDB CL,CH open+0x6 0x00001d25 INCL AX open+0x7 0x00001d26 RET errstr 0x00001d27 MOVL $0x29,AX acid: casm() errstr+0x5 0x00001d2c INTB $0x40 errstr+0x7 0x00001d2e RET close 0x00001d2f MOVL $0x4,AX
残念ながらアセンブラのコードは入っていない。でもコード上で翻訳。casm()は、asm()の 続きを見る時に使うとな。
acid: hoge() 700: breakpoint open+0x5 INTB $0x40 700: breakpoint pread MOVL $0x32,AX pread()+0x0 /sys/src/libc/9syscall/pread.s:3 read(fd=0x3,buf=0xdfffed8c,n=0x7f)+0x2f /sys/src/libc/9sys/read.c:7 csdial(ds=0xdfffeeb4)+0x1aa /sys/src/libc/9sys/dial.c:102 dial(local=0x0,dir=0x0,cfdp=0x0,dest=0x782c)+0x85 /sys/src/libc/9sys/dial.c:50 main()+0x28 /usr/glenda/tmp/test.c:6 _main+0x31 /sys/src/libc/386/main9.s:16 acid: src(read+0x2f) /sys/src/libc/9sys/read.c:7 2 #include <libc.h> 3 4 long 5 read(int fd, void *buf, long n) 6 { >7 return pread(fd, buf, n, -1LL); 8 } acid: func() 700: breakpoint pread+0x5 INTB $0x40 700: breakpoint pread+0x7 RET 700: breakpoint read+0x2f ADDL $0x18,SP acid: mem(0xdfffed8c, "s") /net/tcp/clone 80
preadはOSとの境界なんで、まだデータは来ていない。(読みだされていない)そこで、一つ上の関数のソースを眺めてみる。繋ぎの関数なんだな。func()で出口付近まで進める。そしておいて、bufに相当するメモリーエリアを、文字列と見做してダンプしてみた。
こういうdebuggerの使い方も有りなんですな。
p9p のあれ
9frontばかりと戯れていてもしょうがないんで、p9p(Plan9port)ではどうなってるか当たってみる。
なんと、dial(1)が有ったぞ。その核心部分が下記。指定されたネットアドレスに接続を試みる。成功したらforkして、stdinからの入力をネットワーク側に流すとな。それをEOFになるまで繰り返す。超簡単な、p9p版のncなんだな。
if((fd = dial(argv[0], nil, nil, nil)) < 0) sysfatal("dial: %r"); switch(pid = fork()){ case -1: sysfatal("fork: %r"); case 0: while((n = read(0, buf, sizeof buf)) > 0) if(write(fd, buf, n) < 0) break; if(!waitforeof) postnote(PNPROC, getppid(), "kill"); exits(nil); }
ここで使ってるdial関数は、libc.hで、p9dialって定義されてて 使えるのは、tcp,udp,unixしかない。アドレスでは、0.0.0.0いわゆる*は戒めている。 p9dialparseでは、p9p用の特殊プロトコルを独自に用意してた。
static struct { char *net; char *service; int port; } porttbl[] = { "tcp", "9fs", 564, "tcp", "whoami", 565, "tcp", "guard", 566, "tcp", "ticket", 567, "tcp", "exportfs", 17007, "tcp", "rexexec", 17009, "tcp", "ncpu", 17010, "tcp", "cpu", 17013, "tcp", "venti", 17034, "tcp", "wiki", 17035, "tcp", "secstore", 5356, "udp", "dns", 53, "tcp", "dns", 53, };
ちょっと実験って事で、debianに建てたWebサーバーに接続(xxx.xxx.xxx.xxxは、debianのIP-address)
$ 9 dial tcp!xxx.xxx.xxx.xxx!8080 GET / HTTP/1.1 ;; 空行を送る事(RETを叩く) HTTP/1.0 200 OK Server: SimpleHTTP/0.6 Python/3.5.3 :
こちらは、ncを使って接続したもの
$ nc xxx.xxx.xxx.xxx 8080 GET / HTTP/1.1 HTTP/1.0 200 OK Server: SimpleHTTP/0.6 Python/3.5.3 :
tips
日頃お世話になってる、libc.hとかにどんな事が書いてあるかと、眺めてみたんだ。
term% cat /sys/include/libc.h #pragma lib "libc.a" #pragma src "/sys/src/ :
冒頭にコンパイラーのためと思われる情報が載ってた。このヘッダーファイルを使ったら、 libc.aをリンクしてね。acidみたいなdebuggerさんには、ソースの在処を教えてあげようって寸法だな。特に、ライブラリィーに何を使えってのは、日頃苦労してる身としては、有難い仕掛けと思うぞ。(Linuxは、この点に関しては落第)
で、今まで気にも留めなかったけど、libc.aとかは何処にあるの?updatedb/locateみたいな サービスは無いようだしね。無ければ自分で何とかしろが鉄則。
term% bin/find -D / >all term% grep libc\.a all //root/386/lib/libc.a term% wc all 47884 47884 2662882 all
grepで指定する検索パターンは正規表現なので、特殊文字のドットはエスケープして、普通の文字扱いにしてる。