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