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-
mandinfo 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
/var/run/utmp’ or
default system-maintained file (often
/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, ¤t_record, reclen) == reclen)
show_info(¤t_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(<ime);
printf("%12.12s", cp+4);
}
4 參考文獻
- Unix-Linux 程式設計實踐教程