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 參考文獻

發佈了101 篇原創文章 · 獲贊 19 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章