utmp 和 wtmp 这两个文件包含了 utmpx 结构记录。utmpx 结构定义在 <utmpx.h> 头文件中。可以通过命令 man 5 utmpx 获得更详细的说明信息:
[C++] 纯文本查看 复制代码 /* Values for ut_type field, below */
#define EMPTY 0 /* Record does not contain valid info
(formerly known as UT_UNKNOWN on Linux) */
#define RUN_LVL 1 /* Change in system run-level (see
init(8)) */
#define BOOT_TIME 2 /* Time of system boot (in ut_tv) */
#define NEW_TIME 3 /* Time after system clock change
(in ut_tv) */
#define OLD_TIME 4 /* Time before system clock change
(in ut_tv) */
#define INIT_PROCESS 5 /* Process spawned by init(8) */
#define LOGIN_PROCESS 6 /* Session leader process for user login */
#define USER_PROCESS 7 /* Normal process */
#define DEAD_PROCESS 8 /* Terminated process */
#define ACCOUNTING 9 /* Not implemented */
#define UT_LINESIZE 32
#define UT_NAMESIZE 32
#define UT_HOSTSIZE 256
struct exit_status { /* Type for ut_exit, below */
short int e_termination; /* Process termination status */
short int e_exit; /* Process exit status */
};
struct utmp {
short ut_type; /* Type of record */
pid_t ut_pid; /* PID of login process */
char ut_line[UT_LINESIZE]; /* Device name of tty - "/dev/" */
char ut_id[4]; /* Terminal name suffix,
or inittab(5) ID */
char ut_user[UT_NAMESIZE]; /* Username */
char ut_host[UT_HOSTSIZE]; /* Hostname for remote login, or
kernel version for run-level
messages */
struct exit_status ut_exit; /* Exit status of a process
marked as DEAD_PROCESS; not
used by Linux init(8) */
/* The ut_session and ut_tv fields must be the same size when
compiled 32- and 64-bit. This allows data files and shared
memory to be shared between 32- and 64-bit applications. */
#if __WORDSIZE == 64 && defined __WORDSIZE_COMPAT32
int32_t ut_session; /* Session ID (getsid(2)),
used for windowing */
struct {
int32_t tv_sec; /* Seconds */
int32_t tv_usec; /* Microseconds */
} ut_tv; /* Time entry was made */
#else
long ut_session; /* Session ID */
struct timeval ut_tv; /* Time entry was made */
#endif
int32_t ut_addr_v6[4]; /* Internet address of remote
host; IPv4 address uses
just ut_addr_v6[0] */
char __unused[20]; /* Reserved for future use */
};
对于一个登录进程来说,ut_line 和 ut_id 存储了登录的设备信息。比如我们用直接运行 who 命令时,打印出来的第二列就是 ut_line 所存储信息,比如 pts/0,pts/1,tty/1 等;而 ut_id 则这些设备的后缀,如 /0, /1, /2 等。关于终端的一些概念可参考:http://www.groad.net/bbs/read.php?tid-5794.html
在一个窗口环境里,一些终端模拟器(虚拟终端,比如 putty, xshell 等)会使用 ut_session 这个域来记录该终端窗口的会话ID。关于会话ID可参考:getsid() -- 获得会话ID
ty_type 是一个整型值,它定义了写到 utmp 文件中的记录的类型。该值是可能是下面的一组常数中的一个:
EMPTY (0)
表示记录不包含有效的账号信息。
RUN_LVL (1)
表示记录了系统运行等级(run-level)的改变。关于系统运行等级的更多信息可以通过 man init 来查看。
BOOT_TIME (2)
表示记录了系统开机时间,该时间由 ut_tv 域给出。通常 RUN_LVL 和 BOOT_TIME 是由 init 程序记录的,它们会被写到 utmp 和 wtmp 两个文件中。
NEW_TIME (3)
表示记录了系统时钟改变后所使用的新的时间,该时间值由 ut_tv 给出。
OLD_TIME (4)
表示记录了在系统时钟改变前使用的旧的时间,该时间值由 ut_tv 给出。OLD_TIME 和 NEW_TIME 会由 NTP (或类似程序)守护进程在系统时钟改变时写到 utmp 和 wtmp 文件中。
INIT_PROCESS (5)
表示记录了一个由 init 衍生出来的进程,比如 getty 。可参考 inittab 获得更多的细节信息。
LOGIN_PROCESS (6)
表示记录了一个登录用户的会话首进程,比如当你按下 Ctrl + Alt + F1 键到从 tty1 登录时,就会显示该记录类型。
USER_PROCESS (7)
记录一般用户进程,通常是一个登录的会话,登录用户的用户名记录在 ut_user 中。
DEAD_PROCESS (8)
表示记录一个已经结束了的进程。
INIT_PROCESS 通常由 getty (或相似的程序,如 agetty,mingetty)的调用而产生。在系统启动时,init 进程会为每个终端和虚拟控制台创建一个子进程,然后每个子进程就会执行(exec) getty 程序。getty 程序会打开终端并提示用户输入用户名,然后执行 login 程序。在用户验证成功并执行了一些其他的步骤后,login 就 fork 出一个子进程用以执行用户的登录 shell 。一个完整的登录会话生命周期由以下 4 个写到 wtmp 里的 4 个记录表示:
(1) 一条 INIT_PROCESS 记录,由 init 来写。
(2) 一条 LOGIN_PROCESS 记录,由 getty 来写。
(3) 一条 USER_PROCESS 记录,由 login 来写。
(4) 一条 DEAD_PROCESS 记录,在检测到一个 login 子进程结束时(用户退出或注销),该记录由 init 来写。
测试代码:
[C++] 纯文本查看 复制代码
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <utmpx.h>
#include <paths.h>
int main(int argc, char *argv[])
{
struct utmpx *utp;
setutxent();
printf ("User Type PID Line Id Host Date/time\n");
while ((utp = getutxent()) != NULL) { /*顺序读取*/
printf("%-8s ", utp->ut_user);
printf("%-9.9s ",
(utp->ut_type == EMPTY) ? "EMPTY" :
(utp->ut_type == RUN_LVL) ? "RUN_LVL" :
(utp->ut_type == BOOT_TIME) ? "BOOT_TIME" :
(utp->ut_type == NEW_TIME) ? "NEW_TIME" :
(utp->ut_type == OLD_TIME) ? "OLD_TIME" :
(utp->ut_type == INIT_PROCESS)? "INIT_PR" :
(utp->ut_type == LOGIN_PROCESS)? "LOGIN_PR" :
(utp->ut_type == USER_PROCESS) ? "USER_PR" :
(utp->ut_type == DEAD_PROCESS) ? "DEAD_PR" : "???");
printf ("%-5d %-6.6s %-9.9s %-20.20s ", (long)utp->ut_pid, utp->ut_line, utp->ut_id, utp->ut_host);
printf ("%s", ctime((time_t *) & (utp->ut_tv.tv_sec)));
}
endutxent();
exit (EXIT_SUCCESS);
}
运行输出:
|