Linux_自己编写一个who命令

1 分析
1.1 Linux的who命令可以做什么
通过who命令可以查看当前已登录的用户
1.2 linux的who命令是如何实现的
1.2.1 通过man获得信息
在命令行中输入

man who

在帮助文档中没有写出who是如何实现的,但是在最后

SEE ALSO
The full documentation for who is maintained as a Texinfo manual. If
the info and who programs are properly installed at your site, the com-
mand

         info coreutils 'who invocation'

  should give you access to the complete manual.

发现可以再 info中查看更详细的介绍
1.2.2 通过info获得信息
在命令行输入

info coreutils 'who invocation'

在显示的内容中有这么一段话

If given one non-option argument, who' uses that instead of a
default system-maintained file (often
/var/run/utmp’ or
/var/run/utmp') as the name of the file containing the record of users
logged on.
/var/log/wtmp’ is commonly given as an argument to `who’
to look at who has previously logged on.

可以知道默认情况who是读取/var/run/utmp的内容来显示的,如果直接打开该文件,会发现是一段乱码,上网查阅该文件

1.2.3 通过谷歌获取信息
在man.org7中可以看到对该文件的描述

The utmp file allows one to discover information about who is
currently using the system. There may be more users currently using
the system, because not all programs use utmp logging.
The file is a sequence of utmp structures, declared as follows in

/* Compatibility names for the strings of the canonical file names.  */
#define UTMP_FILE _PATH_UTMP
#define UTMP_FILENAME _PATH_UTMP

但是,在其中包含了一个

/* Get system dependent values and data structures.  */
#include <bits/utmp.h>

于是打开在该目录下的utmp.h



#ifndef _UTMP_H # error "Never include <bits/utmp.h> directly; use <utmp.h> instead." #endif #include <paths.h> #include <sys/time.h> #include <sys/types.h> #include <bits/wordsize.h> #define UT_LINESIZE 32 #define UT_NAMESIZE 32 #define UT_HOSTSIZE 256 /* The structure describing an entry in the database of previous logins. */ struct lastlog { #if __WORDSIZE == 64 && defined __WORDSIZE_COMPAT32 int32_t ll_time; #else __time_t ll_time; #endif char ll_line[UT_LINESIZE]; char ll_host[UT_HOSTSIZE]; }; /* The structure describing the status of a terminated process. This type is used in `struct utmp' below. */ struct exit_status { short int e_termination; /* Process termination status. */ short int e_exit; /* Process exit status. */ }; /* 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. */ #if __WORDSIZE == 64 && defined __WORDSIZE_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. */ }; /* Backwards compatibility hacks. */ #define ut_name ut_user #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 #define ut_xtime ut_tv.tv_sec #define ut_addr ut_addr_v6[0] /* Values for the `ut_type' field of a `struct utmp'. */ #define EMPTY 0 /* No valid user accounting information. */ #define RUN_LVL 1 /* The system's runlevel. */ #define BOOT_TIME 2 /* Time of system boot. */ #define NEW_TIME 3 /* Time after system clock changed. */ #define OLD_TIME 4 /* Time when system clock changed. */ #define INIT_PROCESS 5 /* Process spawned by the init process. */ #define LOGIN_PROCESS 6 /* Session leader of a logged in user. */ #define USER_PROCESS 7 /* Normal process. */ #define DEAD_PROCESS 8 /* Terminated process. */ #define ACCOUNTING 9 /* Old Linux name for the EMPTY type. */ #define UT_UNKNOWN EMPTY /* Tell the user that we have a modern system with UT_HOST, UT_PID, UT_TYPE, UT_ID and UT_TV fields. */ #define _HAVE_UT_TYPE 1 #define _HAVE_UT_PID 1 #define _HAVE_UT_ID 1 #define _HAVE_UT_TV 1 #define _HAVE_UT_HOST 1 

在这个文件中我们可以得到有关utmp结构体的信息,下面我们就可以自己写一个who命令了

2 编写who命令
2.1 读取/var/run/utmp文件的内容
在编写more命令中,已知可以通过fgets,getc等函数读取一行、一个字符,但是在who命令中我们要一次读取一个结构体,这时,我们使用linux下的read命令

#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count);

描述:
read函数从被文件描述符fd指向的文件中读取count个字节到buf中。
有关read的详细描述可以看man在线帮助文档

2.2 显示已经登陆的用户
2.2.1 筛选信息
在utmp.h中我们可以看到有很多种不同的登录方式,而我们需要的是用户的登陆,所以我们要对/var/log/utmp文件的文件内容进行筛选,只显示登录方式为USER_PROCESS的信息
2.2.2 显示时间
在utmp.h中可以看到时间是一个长整数,所以我们要进行一定的转换。在linux可以使用ctime函数

 #include <time.h
 char *ctime(const time_t *timep); 

该函数返回的是一个类似于”Wed Jun 30 21:49:08 1993\n”的字符串
timep记录了从11970年1月1日开始所经过的秒数,也是一个长整形

3 源码

#include <stdio.h>
#include <utmp.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>

void show_info(struct utmp*);
void show_time(long);

int main()
{
    struct utmp current_record;
    int         utmpfd;
    int         reclen = sizeof(current_record);

    if((utmpfd = open(UTMP_FILE, O_RDONLY)) == -1)
    {
        perror(UTMP_FILE);
        return 0;
    }

    while(read(utmpfd, &current_record, reclen) == reclen)
        show_info(&current_record);
    close(utmpfd);

    return 0;
}

void show_info(struct utmp *utbufp)
{
    if(utbufp->ut_type != USER_PROCESS) return;

    printf("% -8.8s", utbufp->ut_user);//左对齐,最多显示8个字符,不足右边补空格
    printf(" ");
    printf("% -8.8s", utbufp->ut_line);
    printf(" ");
    show_time(utbufp->ut_time); //显示时间
    printf("(%s)", utbufp->ut_host);
    printf("\n");
}

void show_time(long ltime)
{
    char * cp;
    cp = ctime(&ltime);
    printf("%12.12s", cp+4);
}

4 参考文献

  • Unix-Linux 编程实践教程
  • C字符串的输出格式

你可能感兴趣的:(linux)