登录记账
登录记账关注的是那些用户当前登录了系统以及记录过去的登录和登出行为。
一、utmp和wtmp文件概述
Unix系统维护着两个包含与用户登录和登出系统有关的信息的数据文件:
-
utmp文件维护着当前登录进系统的用户记录。
-
wtmp文件包含着所有用户登录和登出行为的留痕信息以供审计之用。
Linux,utmp位于文件位于/var/run/utmp,wtmp文件位于/var/log/wtmp。
二、utmpx结构
utmp和wtmp文件包含utmp记录。utmp结构在<utmp.h>中定义的。
#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; /* 进程终止状态。 */
short int e_exit; /* 进程退出状态。 */
};
struct utmp {
short ut_type; /* 登录类型*/
pid_t ut_pid; /* 登录进程的pid*/
char ut_line[UT_LINESIZE]; /* 登录终端 - "/dev/" */
char ut_id[4]; /* ID或缩写tt名称*/
char ut_user[UT_NAMESIZE]; /* 登录的用户名 */
char ut_host[UT_HOSTSIZE]; /* 远程登录的主机名 */
struct exit_status ut_exit; /* T进程的退出状态标记为DEAD_PROCESS. */
long ut_session; /*会话ID,用于窗口*/
struct timeval ut_tv; /* 输入时间. */
int32_t ut_addr_v6[4]; /* 远程主机的IP地址。*/
char pad[20]; /*保留供将来使用。*/
};
-
utmpx结构中的所有字符串字段都以null结尾,除非值完全填满了相应的数组。
-
ut_line字段包含了终端设备的完整文件名,
- ut_id字段包含了文件名的后缀——跟在 tty、pts或 pty 后面的字符串(后两个分别表示 System-V 和 BSD 风格的伪终端)。因此,对于/dev/tty2终端来讲,
ut_line
的值为 tty2,ut_id
的值为 2。 - ut_session字段为终端窗口记录会话ID。
- ut_id字段包含了文件名的后缀——跟在 tty、pts或 pty 后面的字符串(后两个分别表示 System-V 和 BSD 风格的伪终端)。因此,对于/dev/tty2终端来讲,
-
ut_type
字段是一个整数,它定义了写入文件的记录类型,其取值为下面一组常量中的一个(括号中给出了相应的数值):
-
EMPTY(0):这个记录不包含有效的记账信息
-
RUN_LVL(1):表明在系统启动或关闭时系统运行级别发生了变化;要在
<utmpx.h>
中取得这个常量的定义就必须要定义_GNU_SOURCE
特性测试宏。 -
BOOT_TIME (2) :记录包含 ut_tv 字段中的系统启动时间;写入
RUN_LVL 和 BOOT_TIME
字段的进程通常是 init。这些记录会同时被写入 utmp 和 wtmp 文件 -
NEW_TIME (3):这个记录包含系统时钟变更之后的新时间,记录在 ut_tv 字段中。
-
OLD_TIME (4) :记录包含系统时钟变更之前的旧时间,记录在ut_tv字段中;当系统时钟发生变更时,NTP daemon(或类似的进程)会将类型为
OLD_TIME 和 NEW_TIME
的记录写入到 utmp 和wtmp 文件中。 -
INIT_PROCESS (5) :记录由 init 进程孵化的进程,如 getty 进程。
-
LOGIN_PROCESS (6) :记录用户登录会话组长进程,如 login(1)进程。
-
USER_PROCESS (7) :记录用户进程,通常是登录会话,用户名会出现在ut_user字段中;登录会话可能是由login(1)启动,或者是ssh、ftp之类的的提供远程登录工具的应用程序启动。
-
DEAD_PROCESS (8):这个记录标识出已经退出的进程。
在系统启动的时候,init
进程会为每个命令行和虚拟控制台创建一个子进程,每个子进程会执行 getty
程序。getty
程序会打开终端,提示用户输入用户名,然后执行 login(1)
。当成功验证用户以及执行了其他一些动作之后,login
会创建一个子进程来执行用户登录 shell。这种登录会话的完整声明周期由写入 wtmp文件的四个记录来表示,其顺序如下所示:
-
一个
INIT_PROCESS
记录,由 init 写入 -
一个
LOGIN_PROCES
记录,由 getty 写入。 -
一个
USER_PROCESS
记录,由 login 写入。 -
一个
DEAD_PROCESS
记录,当 init 进程检测到 login 子进程死亡之后(发生在用户登出时)写入。
三、从utmp和wtmp文件中检索信息
1.setutxent和endutxent函数
此函数将utmp文件的当前位置设置到文件的起始位置。
#include<utmpx.h>
void setutxent(void);
通常,在使用任意getutx*()
函数之前应该调用setutxent函数,避免因程序中已经调用到的第三方函数在之前用过这些函数产生混淆。
当utmp文件没有被打开时,setutent和getutx*
函数会打开这个文件。当用完这个文件之后使用endutxent
函数关闭这个文件。
#include<utmpx.h>
void endutxent(void);
2.getutxent、getutxid和getutxline函数
下列函数从utmp文件中读取一个记录并返回一个指向utmpx结构的指针。
#include<utmpx.h>
struct utmpx *getutxent(void);
struct utmpx *getutxid(const struct utmpx *ut);
struct utmpx *getutline(const struct utmpx *ut);
getutxent
函数顺序读取utmp文件中的下一个记录。getutxid和getutxline
函数从当前位置开始搜索与ut参数指向的utmpx结构中指定的标准匹配的记录。getutxid
函数根据ut
参数中ut_type和ut_id
字段的值在utmp文件中搜索一个记录。
- 若
ut_type
字段是RUN_LVL、BOOT_TIME、NEW_TIME或OLD_TIME
,则getutxid
函数找出下一个ut_type
字段与指定的值匹配的记录。 - 若ut_type字段的取值是剩余的有效值中的一个
(INIT_PROCESS、LOGIN_PROCESS、USER_PROCESS或DEAD_PROCESS
),则getutxent
函数会找出下一个ut_type字段与这些值中的任意一个匹配并且ut_id字段与ut参数中指定的值匹配的