警告: 多くのシステムプログラムがその整合性に (愚かにも) 依存しているので、 utmp ファイルは書き込み可能にしてはいけない。 utmp ファイルを 不特定のユーザーから書き込み可能なままにしておくと、 システムのログファイルを偽造されたり、システムファイルの 改ざんを受けるといったリスクをおかす事になる。
このファイルはインクルードファイルで宣言されている次の ような構造体を持つエントリの繰り返しからできている。 (ここに記述してあるのは幾つかの大まかな定義のみで、 詳細は libc のバージョンにより変わる事に注意する必要がある):
#define UT_UNKNOWN 0 #define RUN_LVL 1 #define BOOT_TIME 2 #define NEW_TIME 3 #define OLD_TIME 4 #define INIT_PROCESS 5 #define LOGIN_PROCESS 6 #define USER_PROCESS 7 #define DEAD_PROCESS 8 #define ACCOUNTING 9 #define UT_LINESIZE 12 #define UT_NAMESIZE 32 #define UT_HOSTSIZE 256 struct exit_status { short int e_termination; /* process termination status */ short int e_exit; /* process exit status */ }; struct utmp { short ut_type; /* type of login */ pid_t ut_pid; /* PID of login process */ char ut_line[UT_LINESIZE]; /* device name of tty - "/dev/" */ char ut_id[4]; /* init id or abbrev. ttyname */ char ut_user[UT_NAMESIZE]; /* user name */ char ut_host[UT_HOSTSIZE]; /* hostname for remote login */ struct exit_status ut_exit; /* The exit status of a process marked as DEAD_PROCESS */ /* ut_session と ut_tv フィールドは、32ビットでコンパイルされた場合と 64ビットでコンパイルされた場合で同じサイズでなければならない。 こうすることで、32ビットと64ビットのアプリケーションで、 データファイルと共有メモリを共有することができるようになる */ #if __WORDSIZE == 64 && defined __WORDSIZE_COMPAT32 int32_t ut_session; /* Session ID, used for windowing */ struct { int32_t tv_sec; /* Seconds */ int32_t tv_usec; /* Microseconds */ } ut_tv; /* Time entry was made */ #else long int ut_session; /* Session ID, used for windowing */ struct timeval ut_tv; /* Time entry was made */ #endif int32_t ut_addr_v6[4]; /* IP address of remote host */ char __unused[20]; /* Reserved for future use */ }; /* Backwards compatibility hacks. */ #define ut_name ut_user #ifndef _NO_UT_TIME #define ut_time ut_tv.tv_sec #endif #define ut_xtime ut_tv.tv_sec #define ut_addr ut_addr_v6[0]この構造体からユーザーの使っている端末のスペシャルファイル名、 ユーザーのログイン名、 (time(2) 形式での)ログイン時刻がわかる。文字列フィールドは、 フィールドの長さより文字列が短い場合には、'\0' によって終端される。
最初のエントリは init(8) コマンドが inittab(5) を処理することで作られる。 あるエントリを処理する前に、 init(8) は ut_type を DEAD_PROCESS に初期化する。 レコードの ut_type が DEAD_PROCESS と RUN_LVL のいずれでもなく、 かつ PID が ut_pid であるプロセスがいない場合は、ut_user, ut_host, ut_time をヌルバイトでクリアして初期化を行う。 必要な ut_id を持つ空のレコードを見つけられなかった場合、 init は新しいレコードを作る。inittab から ut_id を設定し、 ut_pid および ut_time を現在値に、 ut_type を INIT_PROCESS に設定する。
getty(8) は pid でエントリを特定し、 ut_type を LOGIN_PROCESS に変更し、 ut_time を更新し、ut_lineを設定した後、接続が確立されるのを待つ。 login(8) はユーザー認証が終了すると、 ut_type を USER_PROCESS に変更し、 ut_time を更新し、ut_host と ut_addrを設定する。 getty(8) と login(8) により異なるが、 ut_pid の代わりに ut_line を使ってレコードの特定が行われることもある (ut_pid を使う方が望ましい) 。
init(8) はプロセスの終了を検出した場合、 ut_pid をキーとして utmp のエントリを特定し、 ut_type を DEAD_PROCESS に設定し、 ut_user, ut_host, ut_time をヌルバイトでクリアする。
xterm(1) コマンドや他の端末エミュレータは 直接 USER_PROCESS のレコードを作り、 /dev/ttyp%c の最後の2文字か、 /dev/pts/%d の p%d を使って ut_id の値を生成する。 この id を持つエントリが DEAD_PROCESS であった場合には再利用し、 それ以外の場合には新しいエントリが作られる。 可能な場合にはプロセス終了時に DEAD_PROCESS と設定し、 さらに ut_line, ut_time, ut_user, ut_host をヌルバイトでクリアすることが奨励されている。
telnetd(8) は LOGIN_PROCESS を設定するだけでよく、 残りの処理は通常通り login(8) に任せればよい。 telnet のセッションが終了した後、前述のように telnetd(8) が utmp のエントリを初期化する。
wtmp ファイルには、すべてのログインとログアウトが記録される。 そのフォーマットは、ログアウト済の端末でユーザー名がヌルとなること以外は utmp とまったく同じである。 ユーザー名が shutdown もしくは reboot である 端末名 ~ はシステムの停止 (shutdown) または再起動 (reboot) を意味する。またその端末名が | と } の対は date(1) コマンドで変更した新/旧のシステム時刻を記録している。 wtmp ファイルは login(1) 、 init(1) やいくつかのバージョンの getty(8) により管理されている。 これらのプログラムはいずれもファイルを新たに作成したりしないので、 ファイルを削除する事で情報の記録 (record-keeping) を止める事ができる。
注意すべき点としては、 biarch なプラットフォーム、つまり 32ビットと 64ビットの両方の アプリケーションを実行できるシステム (x86-64, ppc64, s390x など) では、 ut_tv のサイズは 32ビットモードと 64ビットモードで同じである。 ut_session と ut_time も、存在する場合には同様に同じサイズ である。これにより、32ビットアプリケーションと 64ビットアプリケーション の間でデータファイルと共有メモリを共有することが可能になる。 ut_tv は struct timeval と同じサイズとは限らないので、
gettimeofday((struct timeval *) &ut.ut_tv, NULL);
のような呼び出しをするのではなく、 以下のように各フィールドを設定する方法が推奨される:
struct utmp ut; struct timeval tv; gettimeofday(&tv, NULL); ut.ut_tv.tv_sec = tv.tv_sec; ut.ut_tv.tv_usec = tv.tv_usec;
System V では ut_type を目印にしたり、たとえば "new time" のような 通知メッセージを記録するためのみに使っている。 UT_UNKNOWN は Linux で作られたもののようである。 ACCOUNTING という種類は Linux には存在しない。 System V には ut_host も ut_addr_v6 も存在しない。
ファイルを削除することで utmp への記録を止められる 他の様々なシステムとは違い、Linux では utmp ファイルを必ずおいて おく必要がある。 who(1) コマンドが機能しないようにしたい場合には、 utmp ファイルの全ユーザーに対する読み取り許可を設定しないようにする。
utmp 構造体は libc5 から libc6 で変更された。そのため昔の libc5 の構造体 を使ったプログラムは /var/run/utmp や /var/log/wtmp ファイルを壊してしまう。
32ビットと64ビットのアプリケーションの両方を実行できるプラットフォーム (x86-64, ppc64, s390x 等) では、32ビットモードと64ビットモードで 構造体 utmp の各フィールドのサイズを同じにしなければいけない点に 留意すること。 そのためには、 ut_session を int32_t 型に、 ut_tv を 2つの int32_t 型のフィールド tv_sec , tv_usec を持つ構造体に変更すればよい。 (この場合、ut_tv にデータを入れるには、本当の timeval 構造体に時間を取得してから、 ut_tv の 2 フィールドにデータをコピーする必要がある)