编写who命令
通过解答一下3个问题来解决
1who 命令能做什么?
2who 命令是如何工作的?
3如何编写who?
1who命令能做些什么?
只要输入who命令,输出如下
fantasy@fantasy-HP-ProBook-4436s:~$who
fantasy tty2 2014-07-15 16:38
fantasy tty1 2014-07-15 16:37
fantasy :0 2014-07-15 16:26 (:0)
fantasy pts/12 2014-07-15 16:27 (:0)
每一行代表一个已登录的用户,第一列代表用户名,第二列代表终端名,第三列代表用户的登陆时间,第四列代表登陆地址(默认状态不显示)
也就是说who命令能够显示用户名,用户登陆时间,用户登陆的终端名,所以要实现who,就是要求编写程序实现以上功能
2who 是如何工作的呢?
学会从unix中学习unix
阅读联机帮助
搜索联机帮助
阅读.h文件
从参阅部分(SEEALSO)得到启示
通过查找在utmp.h中保存有相应的登陆结构
structutmp {
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,
orinittab(5) ID */
char ut_user[UT_NAMESIZE]; /* Username */
char ut_host[UT_HOSTSIZE]; /* Hostname for remote login, or
kernelversion for run-level
messages*/
struct exit_status ut_exit; /* Exit status of a process
3如何编写who
我们知道可以从utmp中读取结构信息,可以用fgets读字符字符串,但是这样读取效率很低,那么如何读取一个结构呢?
使用系统自带的openclose read函数
open
目标 打开一个文件
头文件 #include<fcntl.h>
函数原型 intfd = open(char *name,int how)
参数 name文件名 howO_RDONLY(只读)O_WRONLY(只写)or O_RDWR(又读又写)
返回值 -1遇到错误int成功返回
如果成功读取就会返回一个正整数的值,这个值就叫做文件描述符。在unix系统中文件是可以被不同进程打开多次。每次打开文件描述符都是不同的,这样就可以知道是不同进程打开的。
read 目标 把数据读到缓冲区
头文件 #include<unistd.h>
函数原型 ssize_tnumread = read(int fd,void * buf,size_t qty)
参数 fd文件描述符buf用来存放数据的目的缓冲区 qty 要读取的字节数
返回值 -1遇到错误numread 成功读取
read这个系统调用从fd所指定的文件中读取qty个字节,存放到buf所指定内存空间的,内核如果成功地读取了数据,就返回所读取的字节数目,否则返回-1。当读到文件末尾时要再读的话,numread会为0。
close 目标 关闭一个文件
头文件 #include<unistd.h>
函数原型 intresult = close(int fd)
参数 fd文件描述符
返回值 遇到错误 -1 成功关闭 0
close会关闭进程和文件fd之间的连接
知道了who的工作原理,只需要打开文件,读取数据,关闭文件
第一个测试代码!!
/*who01.c -a first version of the who program
*open, read UTMP file ,and show results
*/
#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
#include<utmp.h>
#defineSHOWHOST //include matchine on output
/*show_info
*display contents of utmp struct in human readable form
**note these sizes should not be hardwired
*/
voidshow_info(struct utmp *utbufp){
printf("%-8.8s",utbufp->ut_name);//登录名
printf("");
printf("%-8.8s",utbufp->ut_line);//控制台
printf("");
// printf("%10ld",utbufp->ut_time); // 登录时间目前还不知道ut_time应该用什么类型time_t
printf("");
#ifdefSHOWHOST
printf("(%s)",utbufp->ut_host);//远程主机名
#endif
printf("\n");
}
intmain()
{
structutmp current_record ; //read info from here
intutmpfd; //read from this descriptor
intreclen = sizeof(current_record);
if((utmpfd = open(UTMP_FILE,O_RDONLY))== -1){
perror(UTMP_FILE);//UTMP_FILE is in utmp.h
exit(1);
}
while(read(utmpfd,¤t_record,reclen)== reclen)
show_info(¤t_record);
close(utmpfd);
return0;
}
这个who能正确显示主机名,终端名,远程主机名
需要改进的地方
1消除空白记录
系统所带的who会列出已登录的用户信息,而我们编写的who1还会显示出其他信息,这些信息都来源于utmp文件,所以要想办法出去未登录的那些终端用户
解决办法:过滤掉那些用户名为空的记录
LOGIN的那一行对应的是控制台,ut_type当值为7(USER_PROCESS),时就对应已经登陆的用户
utmp中
#defineINIT_PROCESS 5 /* Process spawned by init(8) */
#defineLOGIN_PROCESS 6 /* Session leader process for user login */
#defineUSER_PROCESS 7 /* Normal process */
voidshow_info(struct utmp *utbufp){ //users only
if(utbufp->ut_type!= USER_PROCESS) //the users name
return;
}
2正确显示登录时间
time_t是longint 类型
完整的who程序
/*who03.c - read /etc/utmp and list info therein
*supperess empty records
*formats time nicely
*/
#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
#include<utmp.h>
#include<time.h>
/*displaytime in a format fit for human consuption
*usesctime to build a string then picks parts out of it
*Note:%12.12s prints a string 12 chars wide and LIMITS it to 12 chars
*/
voidshowtime(long timeval){
char*cp;
cp= ctime(&timeval);
printf("%12.12s",cp+4);//picks 12 chars from pos 4
}
/*show_info
*display contents of utmp struct in human readable form
**note these sizes should not be hardwired
*/
voidshow_info(struct utmp *utbufp){
if(utbufp->ut_type!= USER_PROCESS) 判断一哈是不是正在登陆的用户
return;
printf("%-8.8s",utbufp->ut_name);//the logname
printf("");
printf("%-8.8s",utbufp->ut_line);//the tty
printf("");
showtime(utbufp->ut_time);
// printf("%10ld",utbufp->ut_time); //login time
printf("");
#ifdefSHOWHOST
printf("(%s)",utbufp->ut_host);//the host
#endif
printf("\n");
}
intmain()
{
structutmp current_record ; //read info from here
intutmpfd; //read from this descriptor
intreclen = sizeof(current_record);
if((utmpfd = open(UTMP_FILE,O_RDONLY)) == -1){
perror(UTMP_FILE);//UTMP_FILE is in utmp.h
exit(1);
}
while(read(utmpfd,¤t_record,reclen)== reclen)
show_info(¤t_record);
close(utmpfd);
return0;
}
代码最终实现
——————————————————————————————————————————————————————————