[comp.sources.misc] v05i078: last2 take 2: bugs fixed

ckern@killer.dallas.tx.us (Chris Kern) (12/10/88)

Posting-number: Volume 5, Issue 78
Submitted-by: "Chris Kern" <ckern@killer.dallas.tx.us>
Archive-name: last2.2.s5

Brandon --

My idiosyncratic implementation of a BSD last(1)-style program
for System V had one idiosyncrasy I didn't intend: when invoked
with a user name, it failed to match logins that were longer than
eight characters.  That is the size of the ut_user field in
/etc/wtmp.  However, it is possible to create a login name that
has more than eight characters.  (Always test for obvious
violations of boundary conditions.  Sigh.)

Because the program is short, I am providing a fixed version
rather than posting context diffs for the original version,
which you distributed as v05i062.

Thanks to Doug Wells (dmw@cloud9.UUCP) for pointing this out.

Comments or reports of other bugs should be directed to my
account ckern@killer.Dallas.TX.US.

Chris

#!/bin/sh
# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by ck on Thu Dec  8 11:44:39 EST 1988
# Contents:  last.c last.1
 
echo x - last.c
sed 's/^XX//' > "last.c" <<'@//E*O*F last.c//'
XX#include <stdio.h>
XX#include <sys/types.h>
XX#include <string.h>
XX#include <time.h>
XX#include <utmp.h>


XX#define	 OWTMP_FILE		"/usr/adm/acct/nite/owtmp"		/* file to search after /etc/wtmp */

XX#define  LINE_FIELD_LEN		12					/* magic numbers courtesy of /usr/include/utmp.h */
XX#define  USER_FIELD_LEN		 8

XXstatic char *prog;
XXstatic char *wtmpfile[] = { OWTMP_FILE, WTMP_FILE, NULL };

XXstruct list {
XX		struct utmp rec;
XX		struct list *next;
XX		struct list *previous;
XX};



XXmain(argc, argv)			/* last: show recent logins in last-to-first order */
XXint argc;
XXchar *argv[];
XX{
XX	int    i;
XX	void   prproc();
XX	struct list *listp = NULL, *p, *addlist();
XX	struct utmp *entry;
XX	extern void utmpname();
XX	extern struct utmp *getutent();

XX	prog = argv[0];

XX	for (i = 0; wtmpfile[i] != NULL; i++) {
XX		utmpname(wtmpfile[i]);
XX		while ((entry = getutent()) != NULL)
XX			listp = addlist(listp, entry);
XX	}

XX	/* listp points to most recent wtmp entry */

XX	for (p = listp; p != NULL; p = p->previous)
XX		if (p->rec.ut_type == USER_PROCESS) {
XX			if (argc == 1)
XX				prproc(p, listp);
XX			else
XX				for (i = 1; i < argc; i++) {
XX					if (strncmp(p->rec.ut_user, argv[i], USER_FIELD_LEN) == 0) {
XX						prproc(p, listp);
XX						break;
XX					}
XX				}
XX		}

XX	return (0);
XX}



XXstruct list *addlist(head, wtmp)	/* add new wtmp entry to head of list */
XXstruct list *head;
XXstruct utmp *wtmp;
XX{
XX	void	 errexit();
XX	register struct list *new;
XX	extern	 char *malloc();

XX	if ((new = (struct list *) malloc(sizeof(struct list))) == NULL)
XX		errexit("memory error", NULL);
XX	else {
XX		new->rec = *wtmp;
XX		new->next = new;		/* no next yet */
XX		new->previous = head;
XX		if (head != NULL)
XX			head->next = new;
XX	}
XX	return (new);
XX}



XXvoid prproc(start, last)		/* print entries for process */
XXstruct list *start, *last;
XX{
XX	void	 prentry();
XX	register struct list *p;

XX	prentry(start->rec);
XX	for (p = start->next; p != last; p = p->next)
XX		if (p->rec.ut_pid == start->rec.ut_pid)
XX			prentry(p->rec);
XX	putchar('\n');
XX}


XX	
XXvoid prentry(wtmp)			/* print wtmp entry */
XXstruct utmp wtmp;
XX{
XX	static char *wkday[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
XX	static char *month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };

XX	char   line[LINE_FIELD_LEN + 1], user[USER_FIELD_LEN + 1];
XX	struct tm *time;
XX	extern struct tm *localtime();

XX	strncpy(line, wtmp.ut_line, LINE_FIELD_LEN);
XX	strncpy(user, wtmp.ut_user, USER_FIELD_LEN);
XX	line[LINE_FIELD_LEN] = user[USER_FIELD_LEN] = '\0';
XX	time = localtime(&wtmp.ut_time);
XX	switch (wtmp.ut_type) {
XX	case USER_PROCESS:
XX		printf("%-*s %-*s %s %s %2d %02d:%02d", USER_FIELD_LEN, user, LINE_FIELD_LEN, line,
XX			wkday[time->tm_wday], month[time->tm_mon], time->tm_mday, time->tm_hour, time->tm_min);
XX		break;
XX	case DEAD_PROCESS:
XX		printf("  -  %02d:%02d %s", time->tm_hour, time->tm_min, wkday[time->tm_wday]);
XX		break;
XX	default:
XX		sprintf(line, "%d", wtmp.ut_type);
XX		errexit("illegal wtmp.ut_type entry:", line);
XX	}
XX}



XXvoid errexit(s1, s2)			/* print error message and die */
XXchar s1[], s2[];
XX{
XX	extern void exit();

XX	fprintf(stderr, s2 == NULL ? "%s: %s\n" : "%s: %s %s\n", prog, s1, s2);
XX	exit(-1);
XX}
@//E*O*F last.c//
chmod u=rw,g=r,o=r last.c
 
echo x - last.1
sed 's/^XX//' > "last.1" <<'@//E*O*F last.1//'
XX.TH LAST 1 VOA
XX.SH NAME
XXlast  \-  show recent logins in last-to-first order
XX.SH SYNOPSIS
XX.B last
XX[
XXuser ...
XX]
XX.SH DESCRIPTION
XX.I Last
XXdisplays recent login and logout times
XXin last-to-first order.
XXIf invoked with
XX.IR user s,
XXoutput is restricted
XXto the login and logout times
XXof the specified account(s).
XX.SH FILES
XX.TP 30
XX/etc/wtmp
XXcurrent accounting file
XX.TP 30
XX/usr/adm/acct/nite/owtmp
XXprevious day's accounting file
XX.SH BUGS
XXCertain types of system errors
XXwill result in
XX.I last
XXfailing to report
XXa user's logout time.
@//E*O*F last.1//
chmod u=rw,g=r,o=r last.1
 
echo Inspecting for damage in transit...
temp=/tmp/sharin$$; dtemp=/tmp/sharout$$
trap "rm -f $temp $dtemp; exit" 0 1 2 3 15
cat > $temp <<\!!!
    137    427   3126 last.c
     30     88    522 last.1
    167    515   3648 total
!!!
wc  last.c last.1 | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp
if test -s $dtemp
then echo "Ouch [diff of wc output]:" ; cat $dtemp
else echo "No problems found."
fi
exit 0