DOCUMENT ID: 1329-02

SYNOPSIS:    A program to print wtmp 

OS RELEASE:  2.x

PRODUCT:     Solaris x86

KEYWORDS:    wtmp listing login accounting


DESCRIPTION:

There are occasions when the "login" time of a user must be calculated
for a special purpose.  Wtmp is the system file containing this
information. 

This is a structured file consisting of both character and binary
elements as can be observed by inspecting /usr/include/utmp.h.  A
"login" session is defined by a pair of file entries, one with start
time and on with stop time.  Several pending "login" entries will exist
in a multiuser system environment. 

A C program is required to process the utmp structured file and produce
readable output. 

The sample program will illustrate several of the caveats of processing
wtmp.  Several items are of note:

Character elements in records are not null terminated.
"ut_time" is in UNIX seconds since 1970.
"ut_type" element must be monitored to determine entries related to
login sessions. 

The user name, start time and stop will be printed on stdout until one
of three conditions:
an EOF on the wtmp file,
max records are processed, or
the number of pending entries is exceeded.

------------------------program start--------------------------------------

 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 /*
 * Structure returned by ftime system call
 */
 struct timeb
 {
	time_t	time;
	unsigned short millitm;
	short	timezone;
	short	dstflag;
 };
	char	buffer[256];
	struct	utmp x_utmp;
 #define PENDING_CNT 50
 #define FIND_EMPTY 1
 #define FIND_MATCH 2

 static char *zentry[] =
 {
 "EMPTY",
 "RUN_LVL",
 "BOOT_TIME",
 "OLD_TIME",
 "NEW_TIME",
 "INIT_PROCESS",
 "LOGIN_PROCESS",
 "USER_PROCESS",
 "DEAD_PROCESS",
 "ACCOUNTING"
 };
 typedef struct
		{
		short used;
		struct utmp log_wtmp;
		} PENDINGS;
 PENDINGS pendings[PENDING_CNT];
 /*
 * argv[1] records to dump
 * argv[2] alternate wtmp formatted file
 */
 main(argc,argv)
	int  argc;
	char *argv[];
 {
	int     fd;
	int     record=0;
	char    wtmp_name[128];
	int     max_records=10000;
	int     u_size = sizeof(x_utmp);
	int     r_index;
	int     cc;
	if (argc < 2)
	{
	   record = atoi(argv[1]);
           if (!record) max_records = record;
	}
	if (argc < 3)
	  strcpy(wtmp_name,WTMP_FILE);
	else
	  strcpy(wtmp_name,argv[2]);
 /*
 *	Open a wtmp formatted file
 */
	  if( !(fd= open(wtmp_name,O_RDONLY)))
	  {
		fprintf(stderr,"error opening wtmp file (%s)\n",wtmp_name);
		exit(1);
	  }
        memset (pendings,(char) 0,sizeof(pendings));
	for (record = 0;;record++)
	{
		cc = read(fd,&x_utmp,u_size);
		if (!cc) break;
		  strncpy(buffer,x_utmp.ut_name,8);
		  buffer[8] = '\0';
		switch(x_utmp.ut_type)
		{
		  case DEAD_PROCESS:
		if (!findpend(FIND_MATCH,buffer,&r_index))
		{
		  /*
		  * print completed entry
		  */
		  pendings[r_index].used = 0; 
		  strncpy(buffer,pendings[r_index].log_wtmp.ut_name,8);
		  buffer[8] = '\0';
		  printf ("%8s ",buffer);
		  printtime(&pendings[r_index].log_wtmp.ut_time);
		  printtime(&x_utmp.ut_time);
		  printf(" total seconds %ld\n",
                x_utmp.ut_time-pendings[r_index].log_wtmp.ut_time);
		 }
		   break;
		  case USER_PROCESS:
			if(!findpend(FIND_EMPTY,buffer,&r_index))
			{
			  /*
			  * add entry to the pile
			  */
	   		  pendings[r_index].used++;
			  pendings[r_index].log_wtmp = x_utmp;
			}
			else
			{
			fprintf(stderr,"Too many pendings\n");
			exit(1);
			}
			break;
		}
	}
 }
 /*
 * dump entry
 */
 dumpentry(x_utmp)
 struct utmp *x_utmp;
 {
	char name[10];
	char line[20];
	strncpy(name,x_utmp->ut_name,8);
	name[8] = '\0';
	strncpy (line,x_utmp->ut_line,12);
	line[12] = '\0';
	printf ("[%8s] [%12s] %12s ",name,line,zentry[x_utmp->ut_type]);
	printtime(&x_utmp->ut_time);
	printf("\n");
 }
 /*
 * print time entry
 */
 printtime(time_entry)
	long *time_entry;
 {
	struct	tm *tm;
	tm = localtime(time_entry);
	printf("%02d/%02d/%02d %02d:%02d:%02d ",
	tm->tm_mon+1,tm->tm_mday,tm->tm_year,
	tm->tm_hour,tm->tm_min,tm->tm_sec);
 }
 /*
 * Find a pending entry
 * 
 * return zero on search success, -1 on failure
 * return_index will point to pending entry
 *
 */
 int findpend(control,name,return_index)
	int control;
	char name[];
	int *return_index;
 {
	int index;
	char buff[20];
	strncpy(buff,name,8);
	buff[8]='\0';
	for (index = 0; index <  PENDING_CNT; index++)
	{
	 if (control == FIND_EMPTY)
	 {
	   if (!pendings[index].used)
	   {
             *return_index = index;
             return(0);
           }
	 }
	 else
	 {
	   if (!strncmp(pendings[index].log_wtmp.ut_name,name,8))
	   {
             *return_index = index;
             return(0);
           }
	 }
	}
	return (-1);
 }
 ------------------------ program end---------------------------------------


DATE APPROVED: 05/08/95