libfaketimeでプロセスに通知する現在日時を変更する

 libfaketimeを用いると、プロセスに通知する現在日時を、マシンのシステムクロックとは異なる日時にすることができる。虚偽の日時を設定するというこの機能は、システム日時の参照に直接関連する機能だけでなく、更新日時など、ファイルのタイムスタンプ機能にも影響を及ぼす。libfaketimeを用いれば、マシンのシステムクロックを変更することなく、プログラムの将来の動作や異なるタイムゾーンでの動作をテストすることができる。ネットワークアプリケーションにおいては、タイムゾーンのテストができると便利である。ローカル環境ではまだ有効な証明書の期限が、あるタイムゾーンでは切れているということがあり得るからだ。

 libfaketimeは、プリロードのライブラリとして動作する。これを使用するには、プログラム実行時にこの共有ライブラリが含まれるように環境変数LD_PRELOADを設定する。たとえば、LD_PRELOADを設定してdateプログラムを実行すると、マシンの動的リンカは新しいプロセスにlibfaketimeライブラリをロードする。libfaketimeをロードすることにより、ある特定の関数群が、システム関数を直接使用する代わりにlibfaketimeライブラリにマッピングされる。libfaketimeは、静的にリンクされたプログラムやsetuidプログラムでは使用することができない。これらのプログラムにはLD_PRELOADが適用されないためである。

 虚偽の日時は、相対的なオフセット値(12時間前など)または絶対値で指定するか、ある絶対日時から開始するように設定することができる。libfaketimeは、環境変数FAKETIMEを用いて、システム日時の偽造を行う。デフォルトでは、システムによるファイルのタイムスタンプもFAKETIMEに応じて変更される。ファイルのタイムスタンプは変更したくないという場合は、環境変数NO_FAKE_STAT=1を設定するとよい。

 libfaketimeのバージョン0.5は、openSUSE 10.3向けに1-Clickインストールとして提供されている。FedoraとUbuntu向けのパッケージはない。以下の手順でソースからビルドすることができる。

$ tar xzvf /.../libfaketime-0.8.tar.gz
$ cd ./libfaketime*
$ make
$ sudo make install

 上記のコマンド列により、faketimeプログラムがマニュアルページと共有ライブラリlibfaketimeとともにインストールされる。faketimeを使用すれば、libfaketimeを使用するための環境変数へのライブラリパスの追加を行う必要はない。faketimeプログラムには、数種のオプションフラグ、タイムスタンプを指定した後に、日時設定を変更して実行したいプログラムの名前と引数を指定する。呼び出し形式はtime(1)やnice(1)などと同様で、コマンドの前にfaketimeとその引数を追加するだけで、普通にコマンドイラインから呼び出すことが可能である。以下に、時刻を10分間遅らせてdateコマンドを実行する例を示す。

$ date
Sat Sep  6 15:01:44 EST 2008
$ faketime -f  '-10m' date
Sat Sep  6 14:51:45 EST 2008

 日時を絶対値で指定する場合は、アプリケーションに通知される日時が決して変化しないという欠点がある。絶対値による指定では、ファイルのタイムスタンプを用いて虚偽の日時を設定することができる。以下のコードでは、まず1999年2月のタイムスタンプを持つ基準ファイルを作成している。このファイルのタイムスタンプをlibfaketimeへの絶対値として使用するには、環境変数FAKETIME_FMTをエクスポートする必要がある。これにより、虚偽の日時の設定値として、エポックからの秒数をlibfaketimeに引き渡すことを示す。その上で、faketimeプログラムを用いてアプリケーションをこの変更された日時で実行するか、または、以下のコマンド列の3つめのブロックに示すように、環境変数を用いてlibfaketimeを直接設定する。

$ touch -t 199902202359 party
$ ls -lt party
-rw-rw-r-- 1 ben ben 0 1999-02-20 23:59 party
$ stat -c %Y /tmp/party
919519140

$  export FAKETIME_FMT=%s
$ faketime -f `stat -c %Y /tmp/party` date
Sat Feb 20 23:59:00 EST 1999

$ export LD_PRELOAD=/usr/lib/faketime/libfaketime.so.1
$ export FAKETIME=`stat -c %Y /tmp/party`
$ export FAKETIME_FMT=%s
$ date
Sat Feb 20 23:59:00 EST 1999

 クロックを開始する絶対日時を指定することもできる。以下にその例を示す。まずstatコマンドの%yオプションにより、faketimeの「start at」タイムスタンプとして有効な形式で日時を取得する。日時の前に@記号を付加することにより、「start at」タイムスタンプを引き渡すことを示している。libfaketimeライブラリをプリロードすると、システムクロックは、上の例の場合と同様にこのファイルのタイムスタンプと同じ日時になっている。しかしその下の小さなプログラムの中で、システム日時を取得し、2秒待ち、再びシステム日時を取得すると、「start at」日時を用いる場合の動作が先ほどとは異なることがわかる。出力結果が示すように、時刻は2秒経過している。(上の例のように)libfaketimeに対し絶対値を指定した場合は、./timetestを2度実行すると、2度とも同じ日時が得られる。

$ export FAKETIME="@`stat -c %y /tmp/party`"
$ export LD_PRELOAD=/usr/lib/faketime/libfaketime.so.1
$ date
Sat Feb 20 23:59:00 EST 1999

$ cat timetest.cpp
#include <iostream>
using namespace std;

int main( int, char** )
{
	time_t tt = 0;
	time( &tt );
	cout << tt << endl;
	sleep(2);
	time( &tt );
	cout << tt << endl;

	return 0;
}
$ g++ timetest.cpp -o timetest
$ ./timetest
919519140
919519142

 libfaketimeに虚偽の日時を設定する3つめの方法は、現在のシステムクロックからの相対オフセット値を指定する方法である。m(分)、h(時間)、d(日)、y(年)という1文字を後ろにつけることにより、オフセット値の単位を指定することができる。デフォルトの単位は秒である。マイナスまたはプラス記号を前につけることにより、現在のシステム日時からオフセット値を引くのか加えるのかを指定する。

 デフォルトの虚偽日時を設定したい場合は、ファイル~/.faketimercの中の環境変数FAKETIMEにより値を指定することもできる。faketimeプログラムで~/.faketimercを使用する方法を見つけることはできなかったが、以下のように環境変数LD_PRELOADをエクスポートすれば、うまく動作する。

$ date
Sat Sep  6 15:39:58 EST 2008
$ echo '+15m' >| ~/.faketimerc
$ export LD_PRELOAD=/usr/lib/faketime/libfaketime.so.1
$ date
Sat Sep  6 15:55:00 EST 2008

 最後にもう1つ、虚偽のクロックを実際の時間とは異なる速度で動作させることができるという機能を紹介しておこう。たとえば以下では、前出のtimetestプログラムが通常の2倍の速度で実行される。x2パラメータにより速度を指定している。x0.5など、小数値を指定して、速度を遅くすることもできる。以下の例では虚偽のクロックが実際の2倍の速度で動作するため、timetestで2秒間のsleepの後に取得した2回目の時刻は、4秒後の時刻となっている。

$ faketime -f '+2d x2' ./timetest
1220853830
1220853834

 libfaketimeにより、システム日時を変更することなく、ある1つのプログラムが認識するシステムクロックを変更することができる。これは、ネットワークアプリケーションにおいて別のタイムゾーンでの動作や将来の動作をテストしたい場合に便利だ。虚偽の時間の進行速度が変更できるという点は、時間に依存するロジックを有するアプリケーションが、テストマシンよりもずっと高速または低速なハードウェアで動作する場合の様子をテストする際に、ありがたい機能である。

Ben Martinは10年以上にわたってファイルシステムに取り組んでおり、博士課程の修了後、現在はlibferris、ファイルシステム、検索ソリューションを中心としたコンサルティング業に従事している。

Linux.com 原文(2008年9月22日)