Q1: who命令能做些什么?
$ who xxx :0 yyyy-mm-dd hh:mm (:0) xxx pts/0 yyyy-mm-dd hh:mm (:0)
这是在CentOS7上的输出结果,who的版本为8.22。每一行代表一个已经登陆的用户,第一列是用户名,第二列是终端名,第三列是登录时间。
可以通过阅读手册详细查看who命令。
$ man who
Q2: who命令是如何工作的?
首先我们先阅读联机帮助:
DESCRIPTION Print information about users who are currently logged in. If FILE is not specified, use /var/run/utmp. /var/log/wtmp as FILE is common. If ARG1 ARG2 given, -m presumed: ‘am i‘ or ‘mom likes‘ are usual.
通过描述得知已登录用户的信息一般会在/var/run/utmp或/var/log/wtmp中。
utmp文件中保存的是当前正在本系统中的用户的信息。wtmp文件中保存的是登录过本系统的用户的信息。
现在关注utmp文件,who通过读取该文件获得信息。使用带有-k命令可以根据关键字搜索联机帮助:
$ man -k utmp
注意观察描述,有一行包含可能有用的信息:
utmpx (5) - login records
接着使用man查看联机帮助:
$ man 5 utmp
浏览手册,发现该文件结构定义在头文件<utmp.h>中,首先去/usr/include/目录下寻找:
$ find /usr/include/utmp.h /usr/include/utmp.h
然后用more或者cat命令查看该文件:
$ more /usr/include/utmp.h
浏览发现该文件并不是声明原始数据结构的头文件:
/* Get system dependent values and data structures. */ #include <bits/utmp.h>
接着再到下一层目录bits当中查看,这里注意在C源文件中不能include<bits/utmp.h>,仍然是include<utmp.h>。
$ more /usr/include/bits/utmp.h
通过浏览代码,获取到结构体:
/* The structure describing an entry in the user accounting database. */ struct utmp { short int ut_type; /* Type of login. */ pid_t ut_pid; /* Process ID of login process. */ char ut_line[UT_LINESIZE]; /* Devicename. */ char ut_id[4]; /* Inittab ID. */ char ut_user[UT_NAMESIZE]; /* Username. */ char ut_host[UT_HOSTSIZE]; /* Hostname for remote login. */ struct exit_status ut_exit; /* Exit status of a process marked as DEAD_PROCESS. */ /* 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. */ #ifdef __WORDSIZE_TIME64_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]; /* Internet address of remote host. */ char __unused[20]; /* Reserved for future use. */ };
观察当中的变量,可以获取到who所使用的成员变量ut_type(who用来过滤空白而留下当前用户)、ut_line(显示设备名,即用户的终端)、ut_user(用户名)、ut_host(远程登录用户名,(:0)显示)、ut_time(时间)。
所以who命令其实是打开utmp文件,读取记录,显示记录,然后关闭utmp文件。
Q3: 如何编写who?
根据以上分析得知who命令按照打开文件,读取记录,显示记录,关闭文件的流程工作。打开文件用open,读取用read,显示printf,关闭close。其中printf就不用介绍了。另外三个具体如下:
open 目标 打开一个文件 头文件 #include <fcntl.h> 函数原型 int fd = open(char *name, int how) 参数 name 文件名 how O_RDONLY, O_WRONLY, O_RDWR 返回值 -1 遇到错误 int 成功返回