SVX日記

2004|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|12|
2020|01|02|03|04|05|06|07|08|

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");

  2箇所出た。

[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側ではちゃんと補正らしきことをしているし、特に重要な処理部分でもなさそうだ。イチイチ、こんなメッセージを出力しなくたっていいのになぁ。

  とりあえず、ホントにヘンな値を返しているものか確認してみよう。gettimeofdayを使って小さなプログラムを書いてみる。こんな時は、本を開かず、ググりもせず、manするのが吉である。

[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 構造体である:
<以下略>

  ロクにmanを読まないクセに、やたらと本を買えと勧める先輩エンジニアはタコであると断言しておこう。これをもとにサラッと確認コードを書いてみる。

      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し、それをインストールすることにする。

  まずはフツーに全buildする。

[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

  rpmbuildのmanの、-baオプションのトコロには、

       -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

  ここで先の2つのファイルを修正する。

[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");        */

  でもって「%compile」処理、「%install」処理を順に行わせるが、ココでハタと困った。

[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

  -baオプションには--shurt-circuitオプションが効かないらしい。

[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

  パッケージ完成。バージョンナンバが同じため、インストールの時には、--forceオプションを付加する必要があることに注意。

[root@colinux i386]# rpm -Uvh --force *
[root@colinux i386]# service named start

  よし!! 思惑通り、問題のログが出なくなった。これにてプロジェクトの完了である。

  よく考えたら、ホントは「%prep」処理の後、自分の修正部分の差分をパッチファイルにまとめ、SPECファイルをイジって最後にパッチが当てるようにして、改めて全部をrebuildするといいんだろうな。でもまぁ、公開するわけでもないし、これでいいのだ!! あー、スッキリした。明日からはボチボチBindの基本設定をしていきたい。ネタになるほどの設定ではないので日記にはまとめないと思うけどね。では。