2013年12月28日土曜日

FTPクライアントは転送が失敗してもリターンコードが0な理由をさぐる

会社の先輩から「FTPはファイル転送が失敗しても気付けないから気付けろ!」と注意を受けた。
 「そんな馬鹿な」と思って調べていくと、どうやらそれが「常識」らしい。。。

再現させてみた

とりあえず本当かどうか目で見るまで信じられなかったので、再現を試みることに。

意外とあっさり再現できた。

まずftp.confを用意する ftp.confの内容
open 192.168.1.2
user fetaro P@ssw0rd
bin
prompt
get CentOS-5.6-i386-bin-DVD.iso
bye
次に、設定ファイルをftpクライアントに食わせてコマンドを実行
# ftp -n < ftp.conf
この時、ファイル転送中にサーバをkillしたりすると、
[root@localhost ~]# ftp -n < ftp.conf
Interactive mode off.
OOPS: child died
[root@localhost ~]# echo $?
0
まじかよ!ほんとに0だ。
ちなみにサーバが起動していなくてもリターンは0だ。
なんて不親切なんだFTPクライアント!

なぜそうなっているのか


なぜこんなことになっているか気になったので、C言語はあまり得意じゃないけど、ソースコードを調べてみることにした。 ソースコードのRPMをダウンロード。
# wget http://ftp.redhat.com/redhat/linux/enterprise/6Server/en/os/SRPMS/ftp-0.17-54.el6.src.rpm
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
パッチと本体が入っているので、本体を解凍。
# 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
main.c の中を見ていくと、main関数の最後はコマンドをパースする無限ループがある。
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することにした。
[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)は4か所。
それぞれ見ていくとexit(1)に至る処理は以下の場合であることがわかった。

  •  "ftp: ftp/tcp: unknown service" のエラーが出るとき 
  •  "unknown option" のエラーが出るとき 
  •  shell関数の中 (ただしshell関数は呼ばれていなそう) 
  •  fatal関数の中 (Out of memoryの時っぽい) 

というわけで、上記の4パターン以外はftpクライアントは0でリターンするようだ。

結論!


FTPクライアントはメモリが溢れるか、オプションを間違えない限りは0リターン!
(と思われる)

ruby の ftpクライアント使おうっと。。。

0 件のコメント:

コメントを投稿