SVX日記
2005-11-08(Tue) 電車でBind!!
coLinuxをノートPCにインストールしてからというもの、電車の中では快適三昧である。Cygwinも悪くないが、どうしたってある意味では本物のLinux環境にはかなわない。しかもディストリビューションはWBEL。仕事で慣れ親しんでいる環境とほぼ同じであるし、rpmも使えるし、コンパイルもできるし、manページもすべて揃っている。ハードウェア関係以外は完璧。まったくもってcoLinux万歳である。
しかしながら、調子が悪いのがネームサーバのBind。別に必ずしもBindが動かなくても困らないのだが、仕事で軽い動作検証くらいには使いたいし、やっぱりイジり回したいのである。前回インストールした時は、nslookupやdigを打った瞬間にセグメント例外で落っこちる現象が出ていて、どうにも根が深そうなトコロ(UDP?)に問題がありそうだったのでアキらめたが、なぜか今回インストールしなおしたら、シスログにやたらとエラーが出るだけで動いているっぽいので、そのやたらと出るシスログだけは、なんとかしようと思い立ったのである。
Nov 10 23:18:22 colinux named[5877]: gettimeofday returned bad tv_usec: corrected
Nov 10 23:18:35 colinux last message repeated 151273 times
秒間一万数千というレベルで出ている……笑ってしまうような状況だ。これだけ出ると、事実上messagesが使い物にならなくなる。とりあえず、問題の根を探るために、Bindソースを確認し、このメッセージを出している部分を見てみよう。もしかしたら、最新版のBindでは直っているかもしれないと淡い期待を抱きつつ、某所からBindの最新版のsrpmを持ってくる。
[root@colinux root]# wget ftp://ftp.redhat.com/pub/redhat/linux/updates/enterprise/3ES/en/os/SRPMS/bind-9.2.4-7_EL3.src.rpm
[root@colinux root]# rpm -ivh bind-9.2.4-7_EL3.src.rpm
[root@colinux root]# rpmbuild -bp /usr/src/redhat/SPECS/bind.spec
[root@colinux root]# cd /usr/src/redhat/BUILD/bind*
[root@colinux bind-9.2.4]# grep -r 'gettimeofday returned bad tv_usec: corrected' *
lib/isc/unix/stdtime.c: syslog(LOG_ERR, "gettimeofday returned bad tv_usec: corrected");
lib/isc/unix/time.c: syslog(LOG_ERR, "gettimeofday returned bad tv_usec: corrected");
[root@colinux bind-9.2.4]# vi lib/isc/unix/stdtime.c +/gettimeofday
ソースを読む。2箇所ともコードは同じ構造で、tv_usecが負だったり、上限値であるUS_PER_S(1000000)を超えていたりすると出るらしい。tv_usecとはカーネル関数のgettimeofdayを呼び出したときに返る、現時刻の秒以下の時刻成分のことである。単位はマイクロ秒だから、通常は0〜999999までが返るハズである。このコードにからは、明らかにヘンな値を読んでいるとしか考えられない。coLinuxカーネルのバグか? しかし、Bind側ではちゃんと補正らしきことをしているし、特に重要な処理部分でもなさそうだ。イチイチ、こんなメッセージを出力しなくたっていいのになぁ。
[root@colinux SOURCES]# man gettimeofday
GETTIMEOFDAY(2) Linux Programmer's Manual GETTIMEOFDAY(2)
名前
gettimeofday, settimeofday - 時間を取得/設定する
書式
#include
int gettimeofday(struct timeval *tv, struct timezone *tz);
int settimeofday(const struct timeval *tv , const struct timezone *tz);
説明
関数 gettimeofday と settimeofday は時間とタイムゾーン (timezone) を 取
得または設定する。 tv 引き数は /usr/include/sys/time.h に定義されている
timeval 構造体である:
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
これにより紀元 (the Epoch: time(2) を参照) からの秒とマイクロ秒が取得で
きる。 tz 引き数は timezone 構造体である:
<以下略>
1 #include
2 #include
3
4 main() {
5 int ret;
6 struct timeval tv;
7 struct timezone tz;
8
9 ret = gettimeofday(&tv, &tz);
10
11 printf("%d, %d, %d, %x\n", ret, tv.tv_sec, tv.tv_usec, tv.tv_usec);
12 }
[root@colinux furuta]# cc gettime.c
[root@colinux furuta]# ./a.out
0, 1131740415, -27341712, fe5ecc70
[root@colinux furuta]# ./a.out
0, 1131740416, -27489711, fe5c8a51
[root@colinux furuta]# ./a.out
0, 1131740417, -28040701, fe542203
[root@colinux furuta]# ./a.out
0, 1131740417, -27594655, fe5af061
[root@colinux furuta]# ./a.out
0, 1131740417, -27213469, fe60c163
……なんと!! ホントに負の値が出ている!! 大バグではないか。機種依存かなぁ。家の骨董デスクトップ上のcoLinuxでも試してみないといかんなぁ。そっちにはcoLinuxの0.6.2が入っているし。
なんにしても、coLinuxのカーネルに手を入れることはできないので、Bind側のメッセージ出力をコメントアウトしてしまおう。せっかくsrpmパッケージを入手しているので、コードを直して、自分のrpmをrebuildし、それをインストールすることにする。
[root@colinux root]# rpmbuild -ba /usr/src/redhat/SPECS/bind.spec
このrebuild、電車の中で行ったのだが、30分以上かかった気がする。職場のサーバでコンパイルすれば5分もかからないのになぁ。仕方ないので、フリーセルしたり、Rubyで別のお仕事コード書いたりして待つ。うーん、でも、マルチタスクっていいねぇ……と、できた。
[root@colinux root]# ls -lrt /usr/src/redhat/RPMS/i386
合計 5264
-rw-r--r-- 1 root root 470258 11月 10 23:38 bind-9.2.4-7_EL3.i386.rpm
-rw-r--r-- 1 root root 558046 11月 10 23:38 bind-libs-9.2.4-7_EL3.i386.rpm
-rw-r--r-- 1 root root 134058 11月 10 23:38 bind-utils-9.2.4-7_EL3.i386.rpm
-rw-r--r-- 1 root root 2250325 11月 10 23:39 bind-devel-9.2.4-7_EL3.i386.rpm
-rw-r--r-- 1 root root 32640 11月 10 23:39 bind-chroot-9.2.4-7_EL3.i386.rpm
-rw-r--r-- 1 root root 1914506 11月 10 23:39 bind-debuginfo-9.2.4-7_EL3.i386.rpm
-ba Build binary and source packages (after doing the %prep, %build,
and %install stages).
という記述があり、ソース展開とパッチ当てをする「%prep」コンパイルする「%build」インストールする「%install」の段階を踏んでからバイナリ及びソースパッケージを作る、とあるが、実際のインストールはされないっぽい。どうやら、tmpディレクトリに対して空インストールを行い、パッケージを作っているようだ。
[root@colinux root]# cd /usr/src/redhat/RPMS/i386
[root@colinux i386]# rpm -Uvh bind*
[root@colinux i386]# service named start
一応、最新パッケージではあるが、今回の問題はバグとしては扱われていないようだ。現象は再現してしまう。まぁ、カーネル側がロクな時間を返さないのがそもそもの原因だから、Bindを責めるには無理があるよな。でも、対処はBind側でするしかない。やはり、予定通り、Bind側のメッセージ出力部のコードをコメントアウトし、自分オリジナルのrpmパッケージをrebuildし、それをインストールして解決を図ることにしよう。まずは、rpmbuildコマンドと使って「%prep」処理を行わせる。これにより、修正パッチが当たった、最新バージョンのソースが「/usr/src/redhat/BUILD/bind*」の下に生成される。
[root@colinux root]# rpmbuild -bp /usr/src/redhat/SPECS/bind.spec
[root@colinux bind-xxxx]# vi lib/isc/unix/stdtime.c
[root@colinux bind-xxxx]# vi lib/isc/unix/time.c
/* if (fixed)
syslog(LOG_ERR, "gettimeofday returned bad tv_usec: corrected"); */
[root@colinux root]# rpmbuild -bc --short-circuit /usr/src/redhat/SPECS/bind.spec
[root@colinux root]# rpmbuild -bi --short-circuit /usr/src/redhat/SPECS/bind.spec
[root@colinux root]# rpmbuild -ba --short-circuit /usr/src/redhat/SPECS/bind.spec
これすると、また頭からソースが展開されてしまうので、先ほどの修正が作業が無駄になってしまう。失敗……仕方ない「/usr/src/redhat/SOURCES」の下のアーカイブを直接修正してしまおう。この方法だと、不幸にも修正部分にパッチが当たった場合、ややこしいコトになるかもしれない。まぁ、ややこしいコトになったら、パッチ当てに失敗するだろうし、とりあえずやってみる。
[root@colinux SOURCES]# tar xvfz bind-9.2.4.tar.gz
[root@colinux SOURCES]# vi lib/isc/unix/stdtime.c
[root@colinux SOURCES]# vi lib/isc/unix/time.c
[root@colinux SOURCES]# tar cvfz bind-9.2.4.tar.gz bind-9.2.4
[root@colinux root]# rpmbuild -ba /usr/src/redhat/SPECS/bind.spec
[root@colinux i386]# rpm -Uvh --force *
[root@colinux i386]# service named start