who命令可以显示出登陆的用户和时间等信息。
它的工作原理是读取/var/run/utmp文件
再利用man -k utmp来搜索,知道跟它密切相关的应该是man 5 utmp
utmp文件记录了当前一部分正在使用系统的用户信息,但是它不是所有的用户,有些程序不在utmp文件中登记。
该文件在头文件utmp.h中定义了一个结构体来存储信息
#define UT_LINESIZE 32
#define UT_NAMESIZE 32
#define UT_HOSTSIZE 256
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 */
};
分析该结构体,可见:
ut_user存储的用户名,ut_line存储的终端名。
ut_tv 是存储的时间,包括两个成员tv_sec和tv_usec,它们是int32_t类型,这个类型是为了可移植,也即int型。
所以可以编写出如下代码:
#include <stdio.h>
#include <utmp.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
void showinfo(struct utmp*);
int main()
{
struct utmp record;
int fd;
int record_len=sizeof(record);
fd=open(UTMP_FILE,O_RDONLY);
if(fd==-1)
{
perror(UTMP_FILE);
exit(1);
}
while(read(fd,&record,record_len)==record_len)
showinfo(&record);
close(fd);
return 0;
}
void showinfo(struct utmp*putmp)
{
printf("%-8.8s",putmp->ut_user);
printf(" ");
printf("%-8.8s",putmp->ut_line);
printf(" ");
printf("%10d",putmp->ut_tv.tv_sec);
printf(" ");
printf("\n");
};
其中的UTMP_FILE是在 <utmp.h>头文件中定义。
注意read只有在读入的数据结构与缓冲区的数据结构相同时,返回值才相等。
执行结果为:
如果取消掉while,只读一次的话,执行结果是
根据ut_type的值来筛选出只需要用户的那部分。
void showinfo(struct utmp*putmp)
{
if (putmp->ut_type==USER_PROCESS)
{ printf("%-8.8s",putmp->ut_user);
printf(" ");
printf("%-8.8s",putmp->ut_line);
printf(" ");
printf("%10d",putmp->ut_tv.tv_sec);
printf(" ");
printf("\n");
}
};
下一步是如何改进时间的显示:
存储时间的结构time_t实际上就是 long int,ctime(t)和 asctime(localtime(t)) 等价。
char *ctime(const time_t *timep);
void showinfo(struct utmp*putmp)
{
time_t ti;
if (putmp->ut_type==USER_PROCESS)
{ printf("%-8.8s",putmp->ut_user);
printf(" ");
printf("%-8.8s",putmp->ut_line);
printf(" ");
ti=putmp->ut_tv.tv_sec;
printf("%10s",ctime(&ti));
printf(" ");
printf("\n");
}
};
但编译时出现警告:format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘int’ [-Wformat=]
ctime()的返回值是cha * 型啊,为什么会出现问题呢?
查资料知在64位的Linux 下,int是32位的,而指针(在这里是 char *)是64位的 ,加上头文件<time.h>即可解决。
话说一开始没有加<time.h>,编译时也没有报错,它也能认出ctime函数,有点怪。
只显示部分字符串的方法:
//从第4个字符开始显示
printf("%s",ctime(&ti)+4);
还是跟who命令有点不一样。
要完全一样,必须去操作tm结构
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 */
};
注意tm_year的值是当前年-1900年的差值。而月份tm_mon是0-11月。
整个代码修改为:
#include <stdio.h>
#include <utmp.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
void showinfo(struct utmp *);
char * myctime(const time_t *);
int main()
{
struct utmp record;
int fd;
int rd;
int record_len=sizeof(record);
fd=open(UTMP_FILE,O_RDONLY);
if(fd==-1)
{
perror(UTMP_FILE);
exit(1);
}
while((read(fd,&record,record_len))==record_len)
showinfo(&record);
close(fd);
return 0;
}
void showinfo(struct utmp*putmp)
{
time_t ti;
if (putmp->ut_type==USER_PROCESS)
{ printf("%-8.8s",putmp->ut_user);
printf(" ");
printf("%-8.8s",putmp->ut_line);
printf(" ");
ti=putmp->ut_tv.tv_sec;
printf("%s",myctime(&ti));
printf(" ");
printf("\n");
}
};
char * myctime(const time_t *timep)
{
struct tm *ptm;
static char buf[20];
ptm=localtime(timep);
sprintf(buf,"%d-%02d-%02d %02d:%d",ptm->tm_year+1900,ptm->tm_mon+1,ptm->tm_mday,ptm->tm_hour,ptm->tm_min);
return buf;
};
执行结果: