[comp.sources.misc] v05i049: "last" command for System V

moran@tron.UUCP (11/15/88)

Posting-number: Volume 5, Issue 49
Submitted-by: "A. Nonymous" <moran@tron.UUCP>
Archive-name: s5last

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:    Shell Archiver
#	Run the following text with /bin/sh to create:
#	last.1
#	Makefile
#	last.c
# This archive created: Thu Nov 10 00:50:45 1988
echo shar: extracting last.1
sed 's/^X //' << \SHAR_EOF > last.1
X .\"  Copyright (c) 1988  Harvey R. Moran Jr.
X .\"                      1936 Altavue Rd.
X .\"                      Catonsville, Md., 21228
X .\"
X .\"                      moran%tron.UUCP@umbc3.UMD.EDU   Internet
X .\"                      {wb3ffv,netsys}!hrmhpc!harvey   UUCP
X .\"
X .\"  This software may be freely distributed provided that this
X .\"  copyright notice is left intact and provided that:
X .\"     a) The source code in machine readable format 
X .\"        is included with any binary distribution.
X .\"     b) If a binary version of this program or a derived work is sold,
X .\"        the source code must be provided in machine readable
X .\"        format for no additional charge.
X .\"     c) No derived works may impose restrictions limiting free
X .\"        distribution of the source code.
X .\"  Source code in this context includes the C language source code
X .\"  provided here as file "last.c" and the manual page provide here as
X .\"  file "last.1".  It also includes any works derived from either of these.
X .TH last 1L LOCAL
X .SH NAME
X last \- indicate last logins of users
X .SH SYNTAX
X .B last
X [
X .I -N
X ] [
X .I name
X ]
X .SH DESCRIPTION
X The
X .I last
X command
X looks back in the
X .I wtmp
X file which records all logins and logouts for information about
X a user, or all users in reverse time order.
X The optional argument
X .I -N
X specifies the maximum number of lines to print.
X The optional
X .I name
X specifies the name of a user of interest.
X If the session is still continuing \fBlast\fR
X so indicates.
X .PP
X The
X .I last
X command
X with no arguments prints a record of all logins and logouts.
X .SH ORIGIN
X This command is modeled on the
X .I last
X command which comes with the
X Berkeley Software Distribution (BSD) of UNIX(tm).   The Berkeley version
X also permits specifying a terminal name.  It also includes the
X field specifying the remote machine in the case of a network login.
X No such field is present in the System V.2 version of /etc/wtmp.
X .SH FILES
X /etc/wtmp		login data base
X .SH SEE\ ALSO
X utmp(4) login data base format description
SHAR_EOF
if test 2013 -ne "`wc -c last.1`"
then
echo shar: error transmitting last.1 '(should have been 2013 characters)'
fi
echo shar: extracting Makefile
sed 's/^X //' << \SHAR_EOF > Makefile
X DEFS = -DMAXENTRIES=1024	# maximum number of entries in /etc/wtmp handled
X CFLAGS = -O -s $(DEFS)		# -O(ptomize) -s(trip symbols)
X 
X last:	last.c
X 	cc $(CFLAGS) -o last last.c
SHAR_EOF
if test 172 -ne "`wc -c Makefile`"
then
echo shar: error transmitting Makefile '(should have been 172 characters)'
fi
echo shar: extracting last.c
sed 's/^X //' << \SHAR_EOF > last.c
X /*
X  * last.c -- examine the wtmp file for last login information
X  *
X  * =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=
X  *  Copyright (c) 1988  Harvey R. Moran Jr.
X  *                      1936 Altavue Rd.
X  *                      Catonsville, Md., 21228
X  *
X  *                      moran%tron.UUCP@umbc3.UMD.EDU   Internet
X  *                      {wb3ffv,netsys}!hrmhpc!harvey   UUCP
X  *
X  *  This software may be freely distributed provided that this
X  *  copyright notice is left intact and provided that:
X  *     a) The source code in machine readable format 
X  *        is included with any binary distribution.
X  *     b) If a binary version of this program or a derived work is sold,
X  *        the source code must be provided in machine readable
X  *        format for no additional charge.
X  *     c) No derived works may impose restrictions limiting free
X  *        distribution of the source code.
X  *  Source code in this context includes the C language source code
X  *  provided here as file "last.c" and the manual page provide here as
X  *  file "last.1".  It also includes any works derived from either of these.
X  *  
X  * =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=
X  *  Developed using Microport System V/AT 2.3U
X  *
X  *   usage:
X  *        last [ -N ] [ username ]
X  *
X  *  This will print the usage of the system as
X  * recorded in WTMP_FILE (defined in /usr/include/utmp.h) for
X  * all users except the ones listed in exclude[] (below).
X  * The option -N specifies that a maximum of N lines of
X  * output will be generated.
X  * The optional username form will restrict information printed
X  * to be logins by the specified user.
X  *
X  * This command is modeled on the "last" command which is distributed
X  * with the Berkeley Software Distribution (BSD) of UNIX(tm).
X  * The print formats are slightly different. The field which
X  * specifies a network login identification are not provided with this
X  * version because the information is not available in the System V.2
X  * UNIX(tm) WTMP_FILE file.
X  *
X  * It will also quit (with error message) if there are more than
X  * MAXENTRIES entries in your WTMP_FILE (/etc/wtmp on my system).
X  *
X  *  Harvey Moran  11/10/88
X  */
X #include <stdio.h>
X #include <fcntl.h>
X #include <sys/types.h>
X #include <utmp.h>
X #include <time.h>
X 
X #ifndef MAXENTRIES
X #	define MAXENTRIES 1024	/* maximum entries in WTMP_FILE handled */
X #endif
X 
X #define STILL_IN -1L
X #define AVOID    -1L
X 
X typedef int BOOL;
X #define YES 1
X #define NO  0
X 
X typedef struct utmp WTMP_t;
X WTMP_t proc_tab;
X 
X typedef struct {
X     char user_name[9];
X 	short pid;
X 	time_t x_time;
X 	time_t y_time;
X 	} ENTRY;
X 
X ENTRY entries[MAXENTRIES];
X 
X /*
X  * user process user names to be excluded from display
X  * change per local installation.  Perhaps these should be
X  * in an environment variable instead, but there are already
X  * so many of them.
X  */
X char *exclude[] = { "rc2", "crtsaver", "LOGIN", NULL };
X 
X char *keep_list = (char *) NULL;
X 
X /*
X  * forward declared functions
X  */
X 
X char *delta_time();
X void qsort();
X int cmp_ENTRY(), cmp_ENTRY_1();
X 
X main(ac, av)
X int ac;
X char *av[];
X {
X 	int i, j, fd;
X 	unsigned  count = (unsigned) -1;	/* BIG unsigned int. */
X 	char intime[27], outtime[27], elapsed[27];
X 	long time_tmp;
X 
X 	if ( ac > 1 && (i = atoi(av[1])) < 0 ) {
X 		count = (unsigned) -i;
X 		for ( i = 2; i < ac; ++i )
X 			av[i-1] = av[i];
X 		av[i] = NULL;
X 		--ac;
X 		}
X 	if ( (fd=open(WTMP_FILE, (O_RDONLY|O_NDELAY))) == -1 ) {
X 		fprintf(stderr, "%s:Can't open %s\n", av[0], WTMP_FILE);
X 		exit(1);
X 		}
X 	i = 0;
X 	while ( read(fd, &proc_tab, sizeof(WTMP_t)) == sizeof(WTMP_t) ) {
X 		switch ( proc_tab.ut_type ) {
X 			case EMPTY:			/* Empty slot in the WTMP_FILE */
X 			case INIT_PROCESS:	/* Process started by "init" */
X 			case LOGIN_PROCESS:	/* A "getty" process waiting for login */
X 			case ACCOUNTING:
X 				break;			/* IGNORE all these forever */
X 
X 			case RUN_LVL:		/* Change of Run Level */
X 			case BOOT_TIME:		/* Boot time */
X 			case OLD_TIME:		/* Old time of Day was */
X 			case NEW_TIME:		/* New Time of Day set */
X 				break;			/* IGNORE all these for now */
X 
X 			case USER_PROCESS:	/* A user process was started */
X 			case DEAD_PROCESS:	/* A user (I think) process died */
X 				strncpy(entries[i].user_name, proc_tab.ut_user, sizeof(proc_tab.ut_user));
X 				if ( omit(entries[i].user_name, ac, av) )
X 					break;
X 				if ( ac == 2 && strcmp(av[1], entries[i].user_name) )
X 					break;	/* not the user of interest */
X 				entries[i].pid = proc_tab.ut_pid;
X 				entries[i].x_time = proc_tab.ut_time;
X 				if ( ++i > (sizeof(entries)/sizeof(ENTRY)) ) {
X 					fprintf(stderr, "Too many entries in %s\n", WTMP_FILE);
X 					fprintf(stderr, "Fix program %s\n", av[0]);
X 					exit(2);
X 					}
X 				break;
X 
X 			default:
X 				fprintf(stderr, "Ignoring GARBAGE in file %s. (ut_type = 0x%02x)\n", WTMP_FILE, proc_tab.ut_type);
X 				break;
X 			}
X 		}
X 	/*
X      * sort by pid, x_time
X      */
X 	qsort((char *) entries, i, sizeof(ENTRY), cmp_ENTRY);
X 	j = 0;
X #define E entries	/* minimize line wrapping of source code */
X 	do {
X 		if ( j == (i-1) ) {
X 			/*
X 			 * final login has no corresponding logout
X 			 * avoid referencing array element with is empty (or does not exist)
X 			 */
X 			E[j].y_time = STILL_IN;
X 			break;
X 			}
X 		else if ( E[j].pid != E[j+1].pid ) { /* login has no corresponding logout */
X 			E[j].y_time = STILL_IN;
X 			++j;
X 			}
X 		else { /* E[j].pid == E[j+1].pid , i.e. login/logout pair */
X 			E[j].y_time = E[j+1].x_time;
X 			E[j+1].x_time = AVOID;
X 			j += 2;
X 			}
X 		} while ( j < i );
X     /*
X      * sort by descending x_time
X      */
X 	qsort((char *) entries, i, sizeof(ENTRY), cmp_ENTRY_1);
X 	for ( j = 0; j < i && count; ++j ) {
X 		if ( E[j].x_time == AVOID )
X 			continue;
X 		else if ( E[j].y_time == STILL_IN ) {
X 			time_tmp = (long) E[j].x_time;
X 			(void) strcpy(intime, asctime(localtime(&time_tmp)));
X 			intime[20] = '\0';	/* strip year */
X 			printf("%-8s %s %s still-logged-in\n", E[j].user_name, intime, " ");
X 			--count;
X 			}
X 		else {
X 			time_tmp = (long) E[j].x_time;
X 			(void) strcpy(intime, asctime(localtime(&time_tmp)));
X 			time_tmp = (long) E[j].y_time;
X 			(void) strcpy(outtime, asctime(localtime(&time_tmp)));
X 			intime[20] = outtime[20] = '\0';	/* strip year */
X 			time_tmp = (long) (E[j].y_time - E[j].x_time);
X 			(void) strcpy(elapsed, delta_time(time_tmp));
X 			printf("%-8s %s-%s %s\n", E[j].user_name, intime, outtime+10, elapsed);
X 			--count;
X 			}
X 		}
X #undef E
X }
X 
X /*
X  * return comparison of 2 ENTRY type records
X  * suitable for sorting by: pid, x_time
X  */
X 
X cmp_ENTRY(a, b)
X ENTRY *a, *b;
X {
X 	if ( a->pid < b->pid )
X 		return (-1);
X 	if ( a->pid > b->pid )
X 		return (1);
X 	if ( a->x_time < b->x_time )
X 		return (-1);
X 	if ( a->x_time > b->x_time )
X 		return (1);
X 	return (0);
X }
X 
X /*
X  * return comparison of 2 ENTRY type records
X  * suitable for a descending sort by: x_time
X  */
X cmp_ENTRY_1(a,b)
X ENTRY *a, *b;
X {
X 	if ( a->x_time < b->x_time )
X 		return (1);
X 	if ( a->x_time > b->x_time )
X 		return (-1);
X 	return (0);
X }
X 
X 
X /*
X  * return whether to omit this name
X  * It is omitted if:
X  *       a) the name matches something in the exclude list
X  *       b) the name does not match something in the invocation argument list
X  *          and the argument list is not null
X  */
X BOOL
X omit(name, ac, av)
X char *name;
X int ac;
X char *av[];
X {
X 	char **p;
X 	int i;
X 
X 	for ( p = &exclude[0]; *p != (char *) NULL; ++p ) {
X 		if ( strcmp(*p, name) == 0 )
X 			return (YES);
X 		}
X 	if ( ac == 1 )	/* empty argument list, do not omit any */
X 		return (NO);
X 	for ( i = 1; i < ac; ++i ) {
X 		if ( strcmp(name, av[i]) == 0 )
X 			return (NO);
X 		}
X 	return (YES);
X }
X 
X 
X char *
X delta_time(t)
X long t;
X {
X 	static char AscTime[11];
X 	int sec, min, hr, da;
X 	long x, y;
X 
X 	sec = t % 60L;
X 	x = (t - (long) sec)/60L;
X 	min = x % 60L;
X 	x = (x - (long) min)/60L;
X 	hr  = x % 24;
X 	x   = (x - (long) hr)/24L;
X 	da  = x;
X 	if ( da )
X 		(void) sprintf(AscTime, "%3d+%02d:%02d:%02d", da, hr, min, sec);
X 	else
X 		(void) sprintf(AscTime, "    %02d:%02d:%02d", hr, min, sec);
X 	return (AscTime);
X }
SHAR_EOF
if test 7908 -ne "`wc -c last.c`"
then
echo shar: error transmitting last.c '(should have been 7908 characters)'
fi
#	End of shell archive
exit 0