1. 程式人生 > >who命令的編寫

who命令的編寫

一.who的功能

 檢視當前哪些使用者正在使用系統

二.who的工作流程:

 從 /var/run/utmp 檔案中讀取已登入使用者的資訊,然後輸出

                            (注意:我的操作環境是ubuntu11.10 ,有些地方與其他系統不同)

                              如在我的ubuntu11.10中utmp.h在  /usr/include   和    /usr/include/i386-linux-gnu/bits  中

三.內容詳解

/usr/include中的/utmp.h有:

#include <bits/utmp.h>
/* Compatibility names for the strings of the canonical file names.  */
#define UTMP_FILE   _PATH_UTMP 

path.h中有:

#define _PATH_UTMP             "/var/run/utmp"     
則 UTMP_FILE    就是檔案路徑/var/run/utmp

/usr/include/i386-linux-gnu/bits/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

struct utmp結構儲存登入資訊

四.自己寫的who命令的程式碼(版本一):

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

#define SHOWHOST

void showtime(long);
void show_info(struct utmp *utbufp);
int main()
{
    struct utmp  utbuf;        // read info into here
    int          utmpfd;       // read from this descriptor

    if( (utmpfd = open(UTMP_FILE,O_RDONLY)) == -1)
    {
    	perror(UTMP_FILE);     //UTMP_FILE is in utmp.h
    	exit(1);
    }
    while( read( utmpfd,&utbuf,sizeof(utbuf)) ==sizeof(utbuf))
    	show_info(&utbuf);
    close(utmpfd);
    return 0;
}
void show_info(struct utmp *utbufp)
{
	if(utbufp->ut_type != USER_PROCESS) //ut_type==USER_PROCESS時,表示已經登入的使用者
		return ;
    printf("%-8.8s",utbufp->ut_user);
    printf(" ");
    printf("%-8.8s",utbufp->ut_line);
    printf(" ");
    showtime( utbufp->ut_time);
   // printf("%12.12s",ctime(&(utbufp->ut_tv).tv_sec)+4);
    printf(" ");
#ifdef SHOWHOST
    if(utbufp->ut_host[0] != '\0')
    printf("(%s)",utbufp->ut_host);
#endif
    printf("\n");
}
void showtime(long timeval)
{
	char *cp;
	cp = ctime(&timeval);
	printf("%12.12s",cp + 4);
}

五.運用緩衝技術的who命令程式碼(版本二):

        在版本一中,每次只能讀取一個數據,因此需要不停的讀取,所以導致效率低下。可以加入緩衝機制提高程式的執行效率。緩衝技術的主要思想是一次讀入大量的資料放入緩衝區,需要的時候從緩衝區取得資料。

        程式使用一個 utmplib.c檔案實現緩衝演算法,在main函式中只要呼叫 utmplib.c中相應函式即可。在 utmplib.c中用一個能容納16個utmp結構的陣列作為緩衝區。utmp_next 函式來從緩衝區取得下一個utmp結構的資料。

       修改原來的main 函式,通過呼叫utmp_next 來取得資料,當緩衝區的資料都被取出後,utmp_next會呼叫read,通過核心再次獲得16條記錄充滿緩衝區,用這種方法可以是read的呼叫次數減少到原來的 1/16 。

utmplib.c 程式碼如下:

#include<stdio.h>
#include<utmp.h>
#include<fcntl.h>
#include<sys/types.h>

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

static char utmpbuf[NRECS * UTSIZE]; //一次可以儲存16個utmp結構的陣列
static int  num_recs;                //緩衝區中的資料個數
static int  cur_rec ;                //緩衝區中已使用的資料個數
static int  fd_utmp = -1;

int  utmp_open(char * filename)
{
	fd_utmp = open(filename,O_RDONLY);
	cur_rec = num_recs = 0;
	return fd_utmp;
}
/*
 *  utmp_reload返回值是緩衝區的資料個數
 */
int utmp_reload()
{
	int amt_read;
	amt_read = read(fd_utmp,utmpbuf,NRECS * UTSIZE);
	num_recs = amt_read/UTSIZE ;
	cur_rec  = 0;
	return num_recs ;
}
/*
 *   utmp_next 返回指向結構的指標
 */
struct utmp *utmp_next()
{
	struct utmp *recp;
	if( fd_utmp == -1)
		return NULLUT;
	if( cur_rec == num_recs && utmp_reload() == 0)
		return NULLUT;
	// recp指向下一個資料
	recp = (struct utmp *)&utmpbuf[cur_rec * UTSIZE];
	cur_rec++;
	return recp;
}

void utmp_close()
{
	if(fd_utmp != -1)
		close(fd_utmp);
}

who.c中只更改main函式,其餘不變,main函式程式碼如下:
int main()
{
    struct utmp  *utbufp,
            *utmp_next();

    if( (utmp_open(UTMP_FILE)) == -1)
    {
    	perror(UTMP_FILE);     //UTMP_FILE is in utmp.h
    	exit(1);
    }
    while(( utbufp = utmp_next()) != ((struct utmp *) NULL))
    	show_info(utbufp);
    utmp_close();
    return 0;
}