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