ckern@killer.Dallas.TX.US (Chris Kern) (11/27/88)
Posting-number: Volume 5, Issue 62 Submitted-by: "Chris Kern" <ckern@killer.Dallas.TX.US> Archive-name: pd-last I don't like to complain about any source offering, and certainly the copyright holder of any code has a right to impose limitations on its use, but I thought Harvey Moran's restrictions on his Berkeley `last' look-alike were excessively severe. Taken at face value, they would prevent an administrator of several systems from installing an executable on each system and archiving the source on only one machine. ("The source code in machine readable format [must be] included with any binary distribution.") Anyway, at the risk initiating "source wars" (hmmm, better perhaps than "flame wars"), I'm enclosing a version of last.c that I wrote a couple of years ago for our System V machines. Its output is slightly different from Harvey's, and it is even less Berkeley compatible (although, if someone insists on "still logged in" as an alternative to not showing the logout time, the change would be easy to make), but it is not encumbered with restrictions of any kind. I have placed the code in the public domain. Any replies should be directed to my account ckern@killer, since voa3 is not currently on the net. Cheers, Chris Kern #!/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 Sun Nov 20 14:54:34 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 */ 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 (strcmp(p->rec.ut_user, argv[i]) == 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[12 + 1], user[8 + 1]; /* magic numbers courtesy of /usr/include/utmp.h */ XX struct tm *time; XX extern struct tm *localtime(); XX strncpy(line, wtmp.ut_line, 12); XX strncpy(user, wtmp.ut_user, 8); XX line[12] = user[8] = '\0'; XX time = localtime(&wtmp.ut_time); XX switch (wtmp.ut_type) { XX case USER_PROCESS: XX printf("%-8s %-12s %s %s %2d %02d:%02d", XX user, line, 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 <<\!!! 134 418 2944 last.c 30 88 522 last.1 164 506 3466 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