名前
utmp, wtmp - ログイン記録
書式
#include <utmp.h>
説明
utmp ファイルを見ることで、現在誰がシステムを使っているかという情報 が得られる。ただすべてのプログラムが utmp ファイルを 更新しているわけではないので、実際にはそれ以上のユーザーが システムを使っている可能性がある。
警告: 多くのシステムプログラムがその整合性に (愚かにも) 依存しているので、 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]
最初のエントリは 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;
ファイル
/var/run/utmp /var/log/wtmp
準拠
Linux での utmp のエントリは v7/BSD や System V のいずれにも準拠していない: その両方が混在したものである。v7/BSD ではより少しの項目しかない; もっとも重要なことは、ut_type が無いことである。 そのため v7/BSD 系のプログラムでは (たとえば) 死んだ状態のエントリや ログイン状態のエントリまで表示されてしまうことになった。 さらにセッション用のスロットを割り当てるための設定ファイルがない。 BSD に設定ファイルがあるのは ut_id がないからである。 Linux (System V 系)では、設定ファイルを必要とせず セッション用のスロットを割り当てるので、一旦設定 されてしまうとレコードの ut_id は決して変更されない。 ut_id をクリアすると競合状態におちいり、 utmp のエントリを壊したり、潜在的なセキュリティホールになる可能性がある。 System V での取り決めでは必要とはされていないが、上記項目をヌルバイトで クリアしておくのは、BSD での取り決めを前提としていて、 かつ utmp を更新しない多くのプログラムが動くようにするためである。 Linux ではここまで記述してきたように、行内容の表示は BSD の慣例に従っている。
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 フィールドにデータをコピーする必要がある)
バグ
この man ページは libc5 に基づいて作られていて、 最新のものでは違っているかもしれない。
関連項目
ac(1), date(1), last(1), login(1), who(1), getutent(3), updwtmp(3), init(8)