「そんな馬鹿な」と思って調べていくと、どうやらそれが「常識」らしい。。。
再現させてみた
とりあえず本当かどうか目で見るまで信じられなかったので、再現を試みることに。意外とあっさり再現できた。
まずftp.confを用意する ftp.confの内容
次に、設定ファイルをftpクライアントに食わせてコマンドを実行open 192.168.1.2 user fetaro P@ssw0rd bin prompt get CentOS-5.6-i386-bin-DVD.iso bye
この時、ファイル転送中にサーバをkillしたりすると、# ftp -n < ftp.conf
[root@localhost ~]# ftp -n < ftp.conf
Interactive mode off.
OOPS: child died
[root@localhost ~]# echo $?
0
まじかよ!ほんとに0だ。ちなみにサーバが起動していなくてもリターンは0だ。
なんて不親切なんだFTPクライアント!
なぜそうなっているのか
なぜこんなことになっているか気になったので、C言語はあまり得意じゃないけど、ソースコードを調べてみることにした。 ソースコードのRPMをダウンロード。
RPMをインストールして中身を確認。# wget http://ftp.redhat.com/redhat/linux/enterprise/6Server/en/os/SRPMS/ftp-0.17-54.el6.src.rpm
パッチと本体が入っているので、本体を解凍。# rpm -ivh ftp-0.17-54.el6.src.rpm # cd rpmbuild/SOURCES/ # ls netkit-ftp-0.17-C-Frame121.patch netkit-ftp-0.17-fprintf.patch netkit-ftp-0.17-runique_mget.patch (略) netkit-ftp-0.17.tar.gz
main.c の中を見ていくと、main関数の最後はコマンドをパースする無限ループがある。# tar zxvf netkit-ftp-0.17.tar.gz # cd netkit-ftp-0.17/ftp # ls Makefile cmds.c cmds.h cmdtab.c domacro.c ftp.1 ftp.c ftp_var.h glob.c glob.h main.c netrc.5 pathnames.h ruserpass.c
うーん。101 int 102 main(volatile int argc, char **volatile argv) 103 { (略) 217 for (;;) { # ←ここがコマンドをパースする無限ループ 218 cmdscanner(top); 219 top = 1; 220 } 221 }
この無限ループは抜け道がいないということは、cmdscanner関数の中のどこかでexitしているということになる。。。
C言語はこういう実装が普通なんだろうか、なんて追いにくいんだ。。。
この中を追って行ってもらちがあかなそうだったので、調査方法を変えてexitをgrepすることにした。
意外に少なくexit(1)は4か所。[root@localhost netkit-ftp-0.17]# grep -r "exit(" * ftp/cmds.c:1505: exit(1); ftp/cmds.c:1732: exit(0); ftp/cmds.c:1790: exit(1); ftp/main.c:115: exit(1); ftp/main.c:175: exit(0); ftp/main.c:180: exit(1); ftp/main.c:207: exit(0);
それぞれ見ていくとexit(1)に至る処理は以下の場合であることがわかった。
- "ftp: ftp/tcp: unknown service" のエラーが出るとき
- "unknown option" のエラーが出るとき
- shell関数の中 (ただしshell関数は呼ばれていなそう)
- fatal関数の中 (Out of memoryの時っぽい)
というわけで、上記の4パターン以外はftpクライアントは0でリターンするようだ。
結論!
FTPクライアントはメモリが溢れるか、オプションを間違えない限りは0リターン!
(と思われる)
ruby の ftpクライアント使おうっと。。。
0 件のコメント:
コメントを投稿