linux系统编程:自己动手写一个who命令

时间:2022-11-23 22:30:17

who命令的作用用于显示当前有哪些用户登录到系统。

这个命令执行的原理是读取了系统上utmp文件中记录的所有登录信息,直接显示出来的

utmp文件在哪里呢?

man who的时候,在手册下面有这么一段说明:意思就是不指定文件参数,那么读取的就是/var/run/utmp,到底是不是,验证下

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.

当我指定file参数为/var/run/utmp或者省略这个参数的时候,结果都是一样, 当我用一个错误的文件时,没有任何结果,从这里可以推断,who命令确实从/var/run/utmp中读取用户登录的信息

ghostwu@ubuntu:~$ who
ghostwu tty7
2018-01-08 09:09 (:0)
ghostwu pts
/18 2018-01-08 12:59 (:0)
ghostwu pts
/19 2018-01-08 13:00 (:0)
ghostwu pts
/20 2018-01-08 13:03 (:0)
ghostwu@ubuntu:
~$ who -b
system boot
2018-01-08 09:08
ghostwu@ubuntu:
~$ who -b /var/run/utmp
system boot
2018-01-08 09:08
ghostwu@ubuntu:
~$ who -b /var/run/utmp2
ghostwu@ubuntu:
~$ who -b /var/run/utmp3

那么utmp到底在哪里?

利用man -k utmp 查找所有的可能:    推断--->  utmp (5) - login records  这里的可能性比较大,描述说,这里是记录登录信息的

ghostwu@ubuntu:~$ man -k utmp
endutent (
3) - access utmp file entries
endutxent (
3) - access utmp file entries
getutent (
3) - access utmp file entries
getutent_r (
3) - access utmp file entries
getutid (
3) - access utmp file entries
getutid_r (
3) - access utmp file entries
getutline (
3) - access utmp file entries
getutline_r (
3) - access utmp file entries
getutmp (
3) - copy utmp structure to utmpx, and vice versa
getutmpx (
3) - copy utmp structure to utmpx, and vice versa
getutxent (
3) - access utmp file entries
getutxid (
3) - access utmp file entries
getutxline (
3) - access utmp file entries
login (
3) - write utmp and wtmp entries
logout (
3) - write utmp and wtmp entries
pututline (
3) - access utmp file entries
pututxline (
3) - access utmp file entries
sessreg (
1) - manage utmpx/wtmpx entries for non-init clients
setutent (
3) - access utmp file entries
setutxent (
3) - access utmp file entries
systemd
-update-utmp (8) - Write audit and utmp updates at bootup, runlevel ch...
systemd
-update-utmp-runlevel.service (8) - Write audit and utmp updates at bo...
systemd
-update-utmp.service (8) - Write audit and utmp updates at bootup, run...
utmp (
5) - login records
utmpdump (
1) - dump UTMP and WTMP files in raw format
utmpname (
3) - access utmp file entries
utmpx (
5) - login records
utmpxname (
3) - access utmp file entries

接下来,我们去 man 5 utmp 看下,会发现有这么一段提示:

The file is a sequence of utmp structures, declared as follows in <utmp.h> (note that this
is only one of several definitions around; details depend on the version of libc):

意思是utmp文件的信息是一系列utmp结构体数据, 这个结构体定义在utmp.h文件中,  每个linux发行版可能不一样.

接下来,我用强大的find命令查找到了2个目标:

ghostwu@ubuntu:~$ find /usr/include -name "utmp.h"
/usr/include/x86_64-linux-gnu/bits/utmp.h
/usr/include/utmp.h

结构体的定义就在这个文件中( /usr/include/x86_64-linux-gnu/bits/utmp.h  )

这里有两个宏要注意下( ut_time和UTMP_FILE ), 下面的程序会用到

#ifndef _NO_UT_TIME
/* We have a problem here: `ut_time' is also used otherwise. Define
_NO_UT_TIME if the compiler complains.
*/
# define ut_time ut_tv.tv_sec
#endif
ghostwu@ubuntu:~$ grep "UTMP_FILE" /usr/include/utmp.h 
#define UTMP_FILE _PATH_UTMP
#define UTMP_FILENAME _PATH_UTMP
ghostwu@ubuntu:
~$ grep "_PATH_UTMP" /usr/include/utmp.h
#define UTMP_FILE _PATH_UTMP
#define UTMP_FILENAME _PATH_UTMP
ghostwu@ubuntu:
~$ grep "_PATH_UTMP" /usr/include/x86_64-linux-gnu/bits/utmp.h
ghostwu@ubuntu:
~$ grep "_PATH_UTMP" /usr/include/*.h
/usr/include/paths.h:#define _PATH_UTMP "/var/run/utmp"
/usr/include/utmp.h:#define UTMP_FILE _PATH_UTMP
/usr/include/utmp.h:#define UTMP_FILENAME _PATH_UTMP
/usr/include/utmpx.h:# define UTMPX_FILE _PATH_UTMPX
/usr/include/utmpx.h:# define UTMPX_FILENAME _PATH_UTMPX
ghostwu@ubuntu:~$

UTMP_FILE的查找思路: 首先grep两个目录下面的文件utmp.h,在/usr/include/utmp.h找到一个宏定义 _PATH_UTMP,下一步就是确定 _PATH_UTMP到底是什么,利用grep "_PATH_UTMP" /usr/include/*.h

最终在paths.h头文件中,发现了他的真面目

who命令书写思路:

1)从/var/run/utmp读取文件,每次读取一个struct utmp结构体这么大,如果长度每次都有这么大,继续读取

2)格式化4个信息:用户名,主机,地址,时间

3)只打印当前活动的用户(当前登录的用户)

4)格式化时间( 小时,分钟,秒, >10的补0, <10的原样返回 )

源代码

linux系统编程:自己动手写一个who命令linux系统编程:自己动手写一个who命令
  1 /*================================================================
2 * Copyright (C) 2018 . All rights reserved.
3 *
4 * 文件名称:mywho.c
5 * 创 建 者:ghostwu(吴华)
6 * 创建日期:2018年01月08日
7 * 描 述:
8 *
9 ================================================================*/
10
11 #include <stdio.h>
12 #include <utmp.h>
13 #include <sys/types.h>
14 #include <sys/stat.h>
15 #include <fcntl.h>
16 #include <stdlib.h>
17 #include <unistd.h>
18 #include <time.h>
19 #include <string.h>
20
21 #ifndef UTMP_FILE
22 #define UTMP_FILE "/var/run/utmp"
23 #endif
24
25 int count = 0;
26
27 //格式化时间, <10 就补0, >10 原样返回
28 char* format_time( char* s, const char *time ) {
29 if( strlen( time ) < 2 ) {
30 return strcat( s, time );
31 }
32 return strcpy( s, time );
33 }
34
35 void show_info( struct utmp* t_utmp ) {
36 if ( t_utmp->ut_type != USER_PROCESS ) //不显示 非活跃的用户信息
37 return;
38
39 printf( "%-8.8s", t_utmp->ut_user );
40 printf( " " );
41 printf( "%-8.8s", t_utmp->ut_line );
42 printf( " " );
43
44 //printf( " " );
45 //printf( "%12.12s", ctime( (time_t*)&(t_utmp->ut_time) ) + 4 ); //+4--->去除天(day)和后面的空格
46
47 /*测试localtime用法
48 //当前时间
49 time_t now;
50 struct tm* pNow;
51 time( &now );
52 pNow = localtime( &now );
53 printf( "%d-%d-%d %d:%d", pNow->tm_year + 1900, pNow->tm_mon + 1, pNow->tm_mday, pNow->tm_hour, pNow->tm_min );
54 */
55
56 struct tm* ptm;
57 time_t u_time = t_utmp->ut_time;
58 ptm = localtime( &u_time );
59 int ihour = ptm->tm_hour;
60 int imin = ptm->tm_min;
61
62 char hour[3] = "0";
63 char hour2[3] = "0";
64 sprintf( hour2, "%d", ihour );
65 format_time( hour, hour2 );
66
67 char min[3] = "0";
68 char min2[3] = "0";
69 sprintf( min2, "%d", imin );
70 format_time( min, min2 );
71
72 //printf( "%d-%d-%d %d:%d", ptm->tm_year + 1900, ptm->tm_mon + 1, ptm->tm_mday, ihour, imin );
73 printf( "%d-%d-%d %s:%s", ptm->tm_year + 1900, ptm->tm_mon + 1, ptm->tm_mday, hour, min );
74
75 printf( " " );
76 printf( "%-8.8s", t_utmp->ut_host );
77
78 printf( "\n" );
79 }
80
81 int main(int argc, char *argv[])
82 {
83 struct utmp myutmp;
84 int fd = -1;
85 int reclen = sizeof( myutmp );
86
87 fd = open( UTMP_FILE, O_RDONLY );
88
89 if( -1 == fd ) {
90 perror( "open utmp" );
91 exit( -1 );
92 }
93
94 //printf( "fd = %d\n", fd );
95
96 while( read( fd, &myutmp, reclen ) == reclen ) {
97 count++;
98 show_info( &myutmp );
99 }
100 printf( "文件读取的次数:%d\n", count );
101 close( fd );
102
103
104 return 0;
105 }
View Code

 

总结:

 一个非常小的功能,囊括以下知识点:

1)文件读取

2)man手册与系统命令使用技巧

3)指针用法

4)字符串函数用法

5)时间函数用法

6)宏与typedef的用法