initrdの中でntpdateを実行する  

この内容はinitrdについて最低限の知識を前提とします。不用意に行うとシステムが起動しなくなる場合があります。

概要  

私は遊びサーバでALIX.2を使っているのですが、組み込みボードの悲しさでALIXは電池を持っていないため、電源を切るといちいちクロックが2000-01-01にリセットされてしまいます。まあ、起動後にntpdateで正しい時刻を入力すれば通常は特に困らないのですが、最終アクセス時刻とクロックが大幅にずれているため、ブート時にfsckがいちいち走って鬱陶しいです。fastbootをいちいち作るのも面倒ですし。そんなわけで、initrdのでntpdateが実行されるようにしてみました。
簡単にできるかと思いきや意外なほど苦戦したのでメモを残しておきます。

作成手順  

展開して必要なファイルをコピー  

まずはinitrd.imgを展開して作業環境の作成。ついでに必要なファイルをコピーしてきます。rootでやる方が楽かもしれません。

# cp /boot/initrd.img .
# mkdir work <-- 作業ディレクトリ作成
# cd work    <-- 作業ディレクトリへ
# gzip -dc ../initrd.img | cpio -idmv <-- initrdを展開

必要なファイルをコピーします。

binディレクトリへ
libディレクトリへ
etcディレクトリへ

設定ファイルの作成  

ntpdateを実行する際にいくつかのファイルが参照されますので作成します。

/etc/nsswitch.conf
services: files
/etc/services
ntp 123/udp
ntp 123/tcp

initスクリプトの修正  

すべてのファイルがそろったらinitスクリプトを修正します。環境にもよりますがだいたいこんな感じ。

#!/bin/nash
mount -t proc /proc /proc
setquiet
echo Mounted /proc filesystem
echo Mounting sysfs
mount -t sysfs none /sys
echo "Loading ipv6.ko module"
insmod /lib/ipv6.ko               <-- IPv6モジュール読み込み
echo "Loading ehci-hcd.ko module"
...モジュール読み込み
echo Creating block devices
mkdevices /dev
echo Creating root device
mkrootdev /dev/root
echo 1 > /sys/power/suspend2/do_resume
umount /sys
echo Sync NTP server...
ntpdate 192.168.1.1                <-- ntpdate実行
echo Setting hwclock
hwclock --systohc                  <-- hwclock実行
echo Mounting root filesystem
mount -o defaults --ro -t ext3 /dev/root /sysroot
echo Switching to new root
switchroot /sysroot

initrd.imgの作成  

最後にinitrd.imgを作成して完成です。

# find | cpio -H newc -o | gzip -9 > initrd-ntp.img

試行錯誤ログ  

試行錯誤したときのエラーメッセージなど。

ネームサービス  

Looking for host 192.168.1.1 and service ntp
Error : Servname not supported for ai_socktype
28 Oct 02:09:19 ntpdate[1477]: can't find host 192.168.1.1
28 Oct 02:09:19 ntpdate[1477]: no servers can be used, exiting

これは、service ntpが解決できてないようです。/etc/serviceを用意して、

ntp 123/tcp
ntp 123/udp

というのを作ってもだめ。straceで見てみると、/lib/libnss_files.so.2ってのを探していました。どうやらこれが必要らしく必要なライブラリファイルに追加。ついでに/etc/nsswitch.confと/etc/localtimeも必要。

IPv6  

28 Oct 11:04:41 ntpdate[289]: cannot find family compatible socket to send ntp packet

こんなエラーが。またまたstraceのログを見るとAF_INET6がどうとか書いてりました。どうやら内部的にはIPv6で処理しているようでipv6モジュールを読み込む必要がありそう(-4オプションは役に立たなかった・・・)。そんなわけで/lib/ipv6.koを用意。

ハードウェアクロック  

無事にできたと思ったときの落とし穴。

28 Oct 02:21:37 ntpdate[292]: step time server 192.168.1.1 offset -32401.216735 sec
...
INIT:
Setting clock  (localtime): Sat Jan  1 00:00:46 JST 2000 [  OK  ]

で、リセットされてしまいました。ハードウェアクロックにセットしないとダメなようです。initスクリプトに、

hwclock --systohc

を追加してようやく実現できました。こんなに苦労するとは・・・