用系统调用实现who命令——mywho
用系统调用实现who命令——mywho
查看who命令的功能
使用man who
查看详细内容
可以看到,who命令用于显示目前登录系统的用户信息。
输入man -k utmp
,可以看到
输入man utmp
,可以看到utmp的结构
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 (1 */
/* 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 */
};
这是utmp结构体的定义,从注释中可以了解到:ut_line保存设备名,即用户的终端类型;ut_user保存登录名;ut_host保存用户用于登录的远程计算机的名字;用户的登录时间被保存在ut_tv结构体中。
who的原理便是读取/var/run/utmp
部分文件内容,输出到屏幕。
编写mywho
#include <stdio.h>
#include <stdlib.h>
#include <utmp.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#define SHOWOST
void showinfo(struct utmp *utbufp);
long showtime(long timeval);
int main()
{
struct utmp current_record;
int utmpfd;
int reclen = sizeof(current_record);
if((utmpfd = open(UTMP_FILE,O_RDONLY))==-1)
{
perror(UTMP_FILE);
exit(1);
}
while (read(utmpfd,¤t_record,reclen)==reclen)
showinfo(¤t_record);
close(utmpfd);
return 0;
}
void showinfo(struct utmp *utbufp){
if(utbufp->ut_type!=USER_PROCESS)
return;
else{
printf("%-8.8s",utbufp->ut_name);
printf(" ");
printf("%-8.8s",utbufp->ut_line);
printf(" ");
showtime(utbufp->ut_time);
printf(" ");
printf("(%s)",utbufp->ut_host);
printf("\n");
}
}
long showtime(long timeval)
{
struct tm *cp;
cp = gmtime(&timeval);
printf(" ");
printf("%d-%d-%d %d:%d ",cp->tm_year+1900,cp->tm_mon+1,cp->tm_mday,(cp->tm_hour+8)%24,cp->tm_min);
}
45,1 底端
main函数
main函数使用系统调用open,read,close对utmp文件进行操作
showinfo函数
用来保存数据的结构体中数据不断读出,但是在utmp中,系统对每种用户都给了标示符:
#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 (seeinit(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 */
而我们需要打印的是标识符为7的USER_PROCESS
showtime函数
我调用了头文件time.h,其中的储存时间的结构体内容如下所示:
struct tm {
int tm_sec; /* Seconds (0-60) */
int tm_min; /* Minutes (0-59) */
int tm_hour; /* Hours (0-23) */
int tm_mday; /* Day of the month (1-31) */
int tm_mon; /* Month (0-11) */
int tm_year; /* Year - 1900 */
int tm_wday; /* Day of the week (0-6, Sunday = 0) */
int tm_yday; /* Day in the year (0-365, 1 Jan = 0) */
int tm_isdst; /* Daylight saving time */
};
utbufp->ut_time是一个时间数据,其储存的是从1970年1月1日0:00开始到现在所经过的秒数。
通过gmtime可以将这个数据转换为结构体tm。
但是需要注意的是tm结构体中tm_year年份数值是减去了1900年,所以输出时需要加上1900。
printf("%d-%d-%d %d:%d ",cp->tm_year+1900,cp->tm_mon+1,cp->tm_mday,(cp->tm_hour+8)%24,cp->tm_min);