1. 程式人生 > >unix/linux who命令的實現

unix/linux who命令的實現

Understanding Unix/Linux Programming(Unix/Linux程式設計實踐教程)

學習模式:

(1)它能做什麼?

(2)它是如何實現的?

(3)能不能自己編寫一個?

(實驗環境:gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4) )

who命令的學習

(1) who命令能做什麼? 


(2)如何實現的?

         聯機幫助告訴我們,who命令通過讀取utmp這個檔案以實現對應的功能。utmp這個結構包含了我們需要的who所需要的全部資訊,除此之外還提供了其他資訊。

命令輸入: man 5 utmp


輸入命令:cat /usr/include/utmp.h

可知道_UTMP_FILE儲存了使用者登入的資訊。



(3)編碼實現

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

#define SHOWHOST /*include remote machine on output*/
void show_info(struct utmp* utbufp)
{
	printf("%-8.8s ",utbufp->ut_name); /*thie logname */
	printf("%-8.8s ",utbufp->ut_line); /*the logtty*/
	printf("%10ld ",utbufp->ut_time); /*the logtime*/
	
	#ifdef SHOWHOST
		printf("s",utbufp->ut_host);  /*the host*/ 
	#endif
		printf("\n");
}

int main()
{
	struct utmp current_record; /*read info into here*/
	int    utmpfd;              /*read from the descriptor*/
	int    reclen  = sizeof(current_record);
	
	//if((utmpfd = open("UTMP_FILE",O_RDONLY)) == -1)
	if((utmpfd = open(UTMP_FILE,O_RDONLY)) == -1)
	{
		perror(UTMP_FILE);       /* UTMP_FILE is in utmp.h  ,  #define UTMP_FILE  _PATH_UTMP
					 /*#define _PATH_UTMP      "/var/run/utmp"*/
		exit(1);
	}
	
	while(read(utmpfd,¤t_record,reclen) == reclen)
		show_info(¤t_record);
	close(utmpfd);
	return 0;
}
執行結果:可知跟我們shell提供的命令列印不太一樣。



完善:

(1) 清除空白記錄(通過判斷使用者是否線上即可)

同樣可通過命令:man 5 utmp


(2)時間格式化列印(使用系統呼叫將long int轉為ASCII 列印輸出)

通過聯機查詢步驟:

1.man -k time | grep transform

2.man 3 ctime

程式碼完善實現如下:

#include<stdio.h>
#include<stdlib.h>
#include<utmp.h>
#include<fcntl.h>
#include<unistd.h>
#include<time.h>
#define SHOWHOST /*include remote machine on output*/
void showtime(long timeval)  
{
	char* cp;
	cp = ctime(&timeval);
	printf("%12.12s",cp+4);
}
void show_info(struct utmp* utbufp)
{
	/*USER_PROCESS indicate the user is active or not*/
	if(utbufp->ut_type!= USER_PROCESS)
		return ;
	
	printf("%-8.8s ",utbufp->ut_name); /*thie logname */
	printf("%-8.8s ",utbufp->ut_line); /*the logtty*/
	showtime(utbufp->ut_time);
	#ifdef SHOWHOST
		if(utbufp->ut_host[0] != '\0')
			printf("s",utbufp->ut_host);  /*the host*/ 
	#endif
		printf("\n");
}

int main()
{
	struct utmp current_record; /*read info into here*/
	int    utmpfd;              /*read from the descriptor*/
	int    reclen  = sizeof(current_record);
	
	//if((utmpfd = open("UTMP_FILE",O_RDONLY)) == -1)
	if((utmpfd = open(UTMP_FILE,O_RDONLY)) == -1)
	{
		perror(UTMP_FILE);  /* UTMP_FILE is in utmp.h  ,  #define UTMP_FILE  _PATH_UTMP
							/*#define _PATH_UTMP      "/var/run/utmp"*/
		exit(1);
	}
	
	while(read(utmpfd,¤t_record,reclen) == reclen)
		show_info(¤t_record);
	close(utmpfd);
	return 0;
}

還記得前面那三個問題嗎?這裡增加一個問題,如何使程式的執行更加高效呢?



程式碼實現:

utmplib.h

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<utmp.h>

#define NRECS 16
#define NULLUT ((struct utmp*) NULL)
#define UTSIZE (sizeof(struct utmp))

static char utmpbuf[NRECS* UTSIZE]; /*storage*/
static int  num_recs;               /*num stored*/
static int  cur_rec;				/*next to go */
static int  fd_utmp = -1;  			/*read from*/

int utmp_open(char* filename)
{
	fd_utmp = open(filename,O_RDONLY);
	cur_rec = num_recs = 0;
	return fd_utmp;
}
int utmp_reload()
/*read next bunch of records into buffer*/
{
	int amt_read;
	amt_read = read(fd_utmp,utmpbuf,NRECS*UTSIZE);
	num_recs = amt_read/UTSIZE;
	cur_rec = 0;
	return num_recs;
}
struct utmp* utmp_next()
{
	struct utmp* recp;
	if(fd_utmp == -1)
		return NULLUT;
	if(cur_rec == num_recs && utmp_reload() == 0)
		return NULLUT;
	recp = (struct utmp* )&utmpbuf[cur_rec * UTSIZE];
	cur_rec ++;
	return recp;
}
void utmp_close()
{
	if(fd_utmp!=-1)
		close(fd_utmp);
}

who3.c

#include<stdio.h>
#include<stdlib.h>
#include<utmp.h>
#include<fcntl.h>
#include<unistd.h>
#include<time.h>
#include"utmplib.h"
#define SHOWHOST /*include remote machine on output*/
void showtime(long timeval)  
{
	char* cp;
	cp = ctime(&timeval);
	printf("%12.12s",cp+4);
}
void show_info(struct utmp* utbufp)
{
	/*USER_PROCESS indicate the user is active or not*/
	if(utbufp->ut_type!= USER_PROCESS)
		return ;
	
	printf("%-8.8s ",utbufp->ut_name); /*this logname */
	printf("%-8.8s ",utbufp->ut_line); /*the logtty*/
	showtime(utbufp->ut_time);
	#ifdef SHOWHOST
		if(utbufp->ut_host[0] != '\0')
			printf("s",utbufp->ut_host);  /*the host*/ 
	#endif
		printf("\n");
}

int main()
{
	struct utmp *utbufp; /*read info into here*/
	int    utmpfd;              /*read from the descriptor*/
	if(utmp_open(UTMP_FILE) == -1)
	{
		perror(UTMP_FILE);
		exit(1);
	}
	while((utbufp =utmp_next()) != ((struct utmp*)NULL))
	{
		show_info(utbufp);
	}
	utmp_close();
	return 0;
}

(1)who am i 能做什麼?


使用者當前使用的相關終端裝置。

(2)如何實現?

我們可以在who2的基礎上進行篩選出使用者名稱相關終端裝置的資訊。

問題在於如何獲取使用者名稱相關的當前終端裝置??


(3)程式碼實現

#include<stdio.h>
#include<unistd.h>
#include<utmp.h>
#include<fcntl.h>
#include<time.h>
#include<stdlib.h>
#include<string.h>
void showtime(long);
void show_info(struct utmp*);
char* whats_my_line(int);

int main(int argc,char* argv[])
{
	if(argc!=3)
	{
		printf("argument need : who am i");
		exit(1);
	}
	struct utmp utbuf;  //read info into here
	int utmpfd;         // read from this descriptor
	char* myline = NULL;

	myline = whats_my_line(0);// get the device name of tty
	if((utmpfd = open(UTMP_FILE,O_RDONLY)) == -1)
	{
		perror(UTMP_FILE);
		exit(1);
	}
	while(read(utmpfd,&utbuf,sizeof(utbuf)) == sizeof(utbuf))
	{
		if(myline == NULL || strcmp(utbuf.ut_line,myline) == 0)
			show_info(&utbuf);
	}
	close(utmpfd);
	return 0;
}
char* whats_my_line(int fd)
{
	char *rv;
	rv = ttyname(fd);  
	//puts(rv);  //debug print: dev/pts/6
 	if(rv)
	   if(strncmp(rv,"/dev/",5) == 0)
			return rv+5;
	return rv;
	// return pts/6
}
void showtime(long timeval)
{
	char* cp;
	cp = ctime(&timeval);
	printf("%12.12s",cp + 4);
}
void show_info(struct utmp* utbufp)
{
	if(utbufp->ut_type != USER_PROCESS) //online
		return ;
	printf("%-8.8s ",utbufp->ut_name);
	printf("%-8.8s ",utbufp->ut_line);
	showtime(utbufp->ut_time);
#ifdef SHOWHOST
	if(utbufp->host[0] != '\0')
		printf(" (%s) ",utbuf->ut_host);
#endif
	printf("\n");
}

(1)whoami能做什麼?


可知列印當前使用者名稱。

(2)如何實現的??

通過聯機幫助:man 2 geteuid, 可以通過獲取當前使用者id,通過使用getpwuid函式對應id找到使用者所對應的資訊,這些資訊包括使用者名稱。



cat /usr/include/pwd.h

(3)程式碼實現

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pwd.h>
#include<sys/types.h>

int main()
{
	uid_t id;           //effective user id of process
	struct passwd* p;   // will hold pwd data for user
	id = geteuid();     //manpage says geteuid always succeeds
	p = getpwuid(id);
	
	if(p == NULL)
		printf("I don't know thow you are \n");
	else
		printf("%s\n",p->pw_name);
	return 0;
}