v05i078: last2 take 2: bugs fixed

Chris Kern ckern at killer.dallas.tx.us
Sat Dec 10 12:14:51 AEST 1988


Posting-number: Volume 5, Issue 78
Submitted-by: "Chris Kern" <ckern at 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 at cloud9.UUCP) for pointing this out.

Comments or reports of other bugs should be directed to my
account ckern at 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



More information about the Comp.sources.misc mailing list