]> cygwin.com Git - cygwin-apps/cygutils.git/commitdiff
Add last implementation and supporting changes
authorCharles Wilson <cygwin@cwilson.fastmail.fm>
Thu, 17 Jan 2002 06:41:48 +0000 (06:41 +0000)
committerCharles Wilson <cygwin@cwilson.fastmail.fm>
Thu, 17 Jan 2002 06:41:48 +0000 (06:41 +0000)
AUTHORS
ChangeLog
PROGLIST
README
src-gpl/Makefile.am
src-gpl/Makefile.in
src-gpl/last.1 [new file with mode: 0644]
src-gpl/last.c [new file with mode: 0644]
src-gpl/lastb.1 [new file with mode: 0644]
src-gpl/oldutmp.h [new file with mode: 0644]

diff --git a/AUTHORS b/AUTHORS
index eacf1e292172c07349f9b72bee529cfd788790c5..a7798e92a1ec2e32464f63f027c0173a606b2ec2 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -7,3 +7,7 @@ Scott Burkett  <scottb@IntNet.net>
 Jorg Schaible  <joerg.schaible@gft.com>
   banner.c
 
+Mark Bradshaw  <bradshaw@staff.crosswalk.com>
+  last.c, last.1, lastb.1, oldutmp.h
+  (taken from sysvinit-2.84 package and adapted)
+
index a5de219afb4b5132ecbaac49b8a298dc08db4159..e3cf6631c0440bbbd848345e3faba67c9777ad77 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,19 @@
+2001-01-16  Charles Wilson  <cwilson@ece.gatech.edu>
+
+       * bootstrap: new file
+       * src-gpl/Makefile.am: add last to proglist
+       * src-gpl/Makefile.in: regenerate
+       * PROGLIST: add last
+       * README: mention last
+       * AUTHORS: add Mark Bradshaw
+
+2001-01-16  Mark Bradshaw  <bradshaw@staff.crosswalk.com>
+
+       * src-gpl/last.c: new file
+       * src-gpl/last.1: new file
+       * src-gpl/lastb.1: new file
+       * src-gpl/oldutmp.h: new file
+
 2001-12-05  Joerg Schaible  <joerg.schaible@gft.com>
 
        * src-gpl/banner.c: fix the -c option, use ANSI
index 6ad9e10607f07da4f1727505803a355604b2dffc..59569e0cdd8a7ca084cdb49b5b76909d7991a452 100644 (file)
--- a/PROGLIST
+++ b/PROGLIST
@@ -68,6 +68,9 @@ cal.exe
   displays a calendar
   (BSD-no-advert *)
 
+last.exe
+  show a listing of the last logged in users
+
 (*) originally BSD+advert, but falls under the blanket 
     conversion to BSD-no-advert, because it was originally
         part of the UCB BSD distro. 
@@ -77,3 +80,4 @@ cal.exe
     Director of the Office of Technology Licensing of the 
     University of California on July 22 1999. He states 
     that clause 3 is "hereby deleted in its entirety."
+
diff --git a/README b/README
index defbd09155415fa7bab1423ec57bf3eb86318d3d..98c64c70b29f5f700653e0618740a14cba490c86 100644 (file)
--- a/README
+++ b/README
@@ -36,7 +36,7 @@ What's in this package:
 -------------------------------
 GPL:
  ascii    u2d       unix2dos  putclip  shmtool 
- banner   d2u       getclip   semstat
+ banner   d2u       getclip   semstat  last
  conv     dos2unix  msgtool   semtool
  
 PublicDomain-now-GPL:
index 3045f6f84ac56c8d7e6a2de1be1b4a3aebeef595..c1058327d69378acb6a7ab71579a54dfb61e591f 100644 (file)
@@ -3,8 +3,12 @@
 INCLUDES = -I$(top_builddir) -I$(top_srcdir)
 
 bin_PROGRAMS = conv ascii dump getclip putclip \
-       semtool shmtool msgtool semstat banner
+       semtool shmtool msgtool semstat banner \
+       last
+man_MANS = last.1 lastb.1
+last_SOURCES = last.c oldutmp.h
 banner_LDADD = -lkernel32 -lgdi32
+noinst_HEADERS = oldutmp.h
 install-exec-hook: $(bin_PROGRAMS)
        @$(NORMAL_INSTALL)
        $(mkinstalldirs) $(DESTDIR)$(bindir)
index 760749706a55d8dae35be8972055e944bdf2abd7..1c5ebd146b579712932e0dee04c56197835a1241 100644 (file)
@@ -72,9 +72,13 @@ install_sh = @install_sh@
 INCLUDES = -I$(top_builddir) -I$(top_srcdir)
 
 bin_PROGRAMS = conv ascii dump getclip putclip \
-       semtool shmtool msgtool semstat banner
+       semtool shmtool msgtool semstat banner \
+       last
 
+man_MANS = last.1 lastb.1
+last_SOURCES = last.c oldutmp.h
 banner_LDADD = -lkernel32 -lgdi32
+noinst_HEADERS = oldutmp.h
 subdir = src-gpl
 mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
 CONFIG_HEADER = $(top_builddir)/config.h
@@ -82,7 +86,7 @@ CONFIG_CLEAN_FILES =
 bin_PROGRAMS = conv$(EXEEXT) ascii$(EXEEXT) dump$(EXEEXT) \
        getclip$(EXEEXT) putclip$(EXEEXT) semtool$(EXEEXT) \
        shmtool$(EXEEXT) msgtool$(EXEEXT) semstat$(EXEEXT) \
-       banner$(EXEEXT)
+       banner$(EXEEXT) last$(EXEEXT)
 PROGRAMS = $(bin_PROGRAMS)
 
 ascii_SOURCES = ascii.c
@@ -109,6 +113,11 @@ getclip_OBJECTS = getclip.$(OBJEXT)
 getclip_LDADD = $(LDADD)
 getclip_DEPENDENCIES =
 getclip_LDFLAGS =
+am_last_OBJECTS = last.$(OBJEXT)
+last_OBJECTS = $(am_last_OBJECTS)
+last_LDADD = $(LDADD)
+last_DEPENDENCIES =
+last_LDFLAGS =
 msgtool_SOURCES = msgtool.c
 msgtool_OBJECTS = msgtool.$(OBJEXT)
 msgtool_LDADD = $(LDADD)
@@ -143,18 +152,24 @@ LIBS = @LIBS@
 depcomp = $(SHELL) $(top_srcdir)/depcomp
 @AMDEP_TRUE@DEP_FILES = $(DEPDIR)/ascii.Po $(DEPDIR)/banner.Po \
 @AMDEP_TRUE@   $(DEPDIR)/conv.Po $(DEPDIR)/dump.Po \
-@AMDEP_TRUE@   $(DEPDIR)/getclip.Po $(DEPDIR)/msgtool.Po \
-@AMDEP_TRUE@   $(DEPDIR)/putclip.Po $(DEPDIR)/semstat.Po \
-@AMDEP_TRUE@   $(DEPDIR)/semtool.Po $(DEPDIR)/shmtool.Po
+@AMDEP_TRUE@   $(DEPDIR)/getclip.Po $(DEPDIR)/last.Po \
+@AMDEP_TRUE@   $(DEPDIR)/msgtool.Po $(DEPDIR)/putclip.Po \
+@AMDEP_TRUE@   $(DEPDIR)/semstat.Po $(DEPDIR)/semtool.Po \
+@AMDEP_TRUE@   $(DEPDIR)/shmtool.Po
 COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
        $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
 CCLD = $(CC)
 LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
 CFLAGS = @CFLAGS@
-DIST_SOURCES = ascii.c banner.c conv.c dump.c getclip.c msgtool.c \
-       putclip.c semstat.c semtool.c shmtool.c
-DIST_COMMON = Makefile.am Makefile.in
-SOURCES = ascii.c banner.c conv.c dump.c getclip.c msgtool.c putclip.c semstat.c semtool.c shmtool.c
+DIST_SOURCES = ascii.c banner.c conv.c dump.c getclip.c $(last_SOURCES) \
+       msgtool.c putclip.c semstat.c semtool.c shmtool.c
+
+NROFF = nroff
+MANS = $(man_MANS)
+HEADERS = $(noinst_HEADERS)
+
+DIST_COMMON = $(noinst_HEADERS) Makefile.am Makefile.in
+SOURCES = ascii.c banner.c conv.c dump.c getclip.c $(last_SOURCES) msgtool.c putclip.c semstat.c semtool.c shmtool.c
 
 all: all-am
 
@@ -205,6 +220,9 @@ dump$(EXEEXT): $(dump_OBJECTS) $(dump_DEPENDENCIES)
 getclip$(EXEEXT): $(getclip_OBJECTS) $(getclip_DEPENDENCIES) 
        @rm -f getclip$(EXEEXT)
        $(LINK) $(getclip_LDFLAGS) $(getclip_OBJECTS) $(getclip_LDADD) $(LIBS)
+last$(EXEEXT): $(last_OBJECTS) $(last_DEPENDENCIES) 
+       @rm -f last$(EXEEXT)
+       $(LINK) $(last_LDFLAGS) $(last_OBJECTS) $(last_LDADD) $(LIBS)
 msgtool$(EXEEXT): $(msgtool_OBJECTS) $(msgtool_DEPENDENCIES) 
        @rm -f msgtool$(EXEEXT)
        $(LINK) $(msgtool_LDFLAGS) $(msgtool_OBJECTS) $(msgtool_LDADD) $(LIBS)
@@ -232,6 +250,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/conv.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/dump.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/getclip.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/last.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/msgtool.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/putclip.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/semstat.Po@am__quote@
@@ -255,6 +274,45 @@ distclean-depend:
 CCDEPMODE = @CCDEPMODE@
 uninstall-info-am:
 
+man1dir = $(mandir)/man1
+install-man1: $(man1_MANS) $(man_MANS)
+       @$(NORMAL_INSTALL)
+       $(mkinstalldirs) $(DESTDIR)$(man1dir)
+       @list='$(man1_MANS) $(dist_man1_MANS) $(nodist_man1_MANS)'; \
+       l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
+       for i in $$l2; do \
+         case "$$i" in \
+           *.1*) list="$$list $$i" ;; \
+         esac; \
+       done; \
+       for i in $$list; do \
+         if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \
+         else file=$$i; fi; \
+         ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+         inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+         inst=`echo $$inst | sed -e 's/^.*\///'`; \
+         inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+         echo " $(INSTALL_DATA) $$file $(DESTDIR)$(man1dir)/$$inst"; \
+         $(INSTALL_DATA) $$file $(DESTDIR)$(man1dir)/$$inst; \
+       done
+uninstall-man1:
+       @$(NORMAL_UNINSTALL)
+       @list='$(man1_MANS) $(dist_man1_MANS) $(nodist_man1_MANS)'; \
+       l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
+       for i in $$l2; do \
+         case "$$i" in \
+           *.1*) list="$$list $$i" ;; \
+         esac; \
+       done; \
+       for i in $$list; do \
+         ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+         inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+         inst=`echo $$inst | sed -e 's/^.*\///'`; \
+         inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+         echo " rm -f $(DESTDIR)$(man1dir)/$$inst"; \
+         rm -f $(DESTDIR)$(man1dir)/$$inst; \
+       done
+
 tags: TAGS
 
 ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
@@ -310,10 +368,10 @@ distdir: $(DISTFILES)
        done
 check-am: all-am
 check: check-am
-all-am: Makefile $(PROGRAMS)
+all-am: Makefile $(PROGRAMS) $(MANS) $(HEADERS)
 
 installdirs:
-       $(mkinstalldirs) $(DESTDIR)$(bindir)
+       $(mkinstalldirs) $(DESTDIR)$(bindir) $(DESTDIR)$(man1dir)
 
 install: install-am
 install-exec: install-exec-am
@@ -355,7 +413,7 @@ info: info-am
 
 info-am:
 
-install-data-am:
+install-data-am: install-man
 
 install-exec-am: install-binPROGRAMS
        @$(NORMAL_INSTALL)
@@ -363,7 +421,7 @@ install-exec-am: install-binPROGRAMS
 
 install-info: install-info-am
 
-install-man:
+install-man: install-man1
 
 installcheck-am:
 
@@ -375,18 +433,21 @@ mostlyclean: mostlyclean-am
 
 mostlyclean-am: mostlyclean-compile mostlyclean-generic
 
-uninstall-am: uninstall-binPROGRAMS uninstall-info-am
+uninstall-am: uninstall-binPROGRAMS uninstall-info-am uninstall-man
+
+uninstall-man: uninstall-man1
 
 .PHONY: GTAGS all all-am check check-am clean clean-binPROGRAMS \
        clean-generic distclean distclean-compile distclean-depend \
        distclean-generic distclean-tags distdir dvi dvi-am info \
        info-am install install-am install-binPROGRAMS install-data \
        install-data-am install-exec install-exec-am install-info \
-       install-info-am install-man install-strip installcheck \
-       installcheck-am installdirs maintainer-clean \
+       install-info-am install-man install-man1 install-strip \
+       installcheck installcheck-am installdirs maintainer-clean \
        maintainer-clean-generic mostlyclean mostlyclean-compile \
        mostlyclean-generic tags uninstall uninstall-am \
-       uninstall-binPROGRAMS uninstall-info-am
+       uninstall-binPROGRAMS uninstall-info-am uninstall-man \
+       uninstall-man1
 
 install-exec-hook: $(bin_PROGRAMS)
        @$(NORMAL_INSTALL)
diff --git a/src-gpl/last.1 b/src-gpl/last.1
new file mode 100644 (file)
index 0000000..009fed3
--- /dev/null
@@ -0,0 +1,95 @@
+.\"{{{}}}
+.\"{{{  Title
+.TH LAST,LASTB 1 "Jul 29, 1999" "" "Linux System Administrator's Manual"
+.\"}}}
+.\"{{{  Name
+.SH NAME
+last, lastb \- show listing of last logged in users
+.\"}}}
+.\"{{{  Synopsis
+.SH SYNOPSIS
+.B last
+.RB [ \-R ]
+.RB [ \-\fInum\fP ]
+.RB "[ \-\fBn\fP \fInum\fP ]"
+.RB [ \-adiox ]
+.RB "[ \-\fBf\fP \fIfile\fP ]"
+.RI [ name... ]
+.RI [ tty... ]
+.br
+.B lastb
+.RB [ \-R ]
+.RB [ \-\fInum\fP ]
+.RB "[ \-\fBn\fP \fInum\fP ]"
+.RB "[ \-\fBf\fP \fIfile\fP ]"
+.RB [ \-adiox ]
+.RI [ name... ]
+.RI [ tty... ]
+.\"}}}
+.\"{{{  Description
+.SH DESCRIPTION
+.B Last
+searches back through the file \fB/var/log/wtmp\fP (or the file
+designated by the \fB\-f\fP flag) and displays a list of all
+users logged in (and out) since that file was created.  Names of users
+and tty's can be given, in which case \fBlast\fP will show only those entries
+matching the arguments.  Names of ttys can be abbreviated, thus \fBlast
+0\fP is the same as \fBlast tty0\fP.  
+.PP
+When \fBlast\fP catches a \s-2SIGINT\s0 signal (generated by the interrupt key,
+usually control-C) or a \s-2SIGQUIT\s0 signal (generated by the quit key, 
+usually control-\e), \fBlast\fP will show how far it has searched through the 
+file; in the case of the \s-2SIGINT\s0 signal \fBlast\fP will then terminate.
+.PP
+The pseudo user \fBreboot\fP logs in each time the system is rebooted.
+Thus \fBlast reboot\fP will show a log of all reboots since the log file
+was created.
+.PP
+\fBLastb\fP is the same as \fBlast\fP, except that by default it shows a log
+of the file \fB/var/log/btmp\fP, which contains all the bad login attempts.
+.\"}}}
+.\"{{{  Options
+.SH OPTIONS
+.IP \fB\-\fP\fInum\fP
+This is a count telling \fBlast\fP how many lines to show.
+.IP "\fB\-n\fP \fInum\fP"
+The same.
+.IP \fB\-R\fP
+Suppresses the display of the hostname field.
+.IP \fB\-a\fP
+Display the hostname in the last column. Useful in combination
+with the next flag.
+.IP \fB\-d\fP
+For non-local logins, Linux stores not only the host name of the remote
+host but its IP number as well. This option translates the IP number
+back into a hostname.
+.IP \fB\-i\fP
+This option is like \fB-d\fP in that it displays the IP number of the remote
+host, but it displays the IP number in numbers-and-dots notation.
+.IP \fB\-o\fP
+Read an old-type wtmp file (written by linux-libc5 applications).
+.IP \fB\-x\fP
+Display the system shutdown entries and run level changes.
+.\"}}}
+.SH NOTES
+The files \fIwtmp\fP and \fIbtmp\fP might not be found. The system only
+logs information in these files if they are present. This is a local
+configuration issue. If you want the files to be used, they can be
+created with a simple \fBtouch\fP(1) command (for example, 
+\fItouch /var/log/wtmp\fP).
+.\"{{{  Files
+.SH FILES
+/var/log/wtmp
+.br
+/var/log/btmp
+.\"}}}
+.\"{{{  Author
+.SH AUTHOR
+Miquel van Smoorenburg, miquels@cistron.nl
+.\"}}}
+.\"{{{  See also
+.SH "SEE ALSO"
+.BR shutdown (8),
+.BR login (1),
+.BR init (8)
+.\"}}}
diff --git a/src-gpl/last.c b/src-gpl/last.c
new file mode 100644 (file)
index 0000000..b35897c
--- /dev/null
@@ -0,0 +1,815 @@
+/*
+ * last.c      Re-implementation of the 'last' command, this time
+ *             for Linux. Yes I know there is BSD last, but I
+ *             just felt like writing this. No thanks :-).
+ *             Also, this version gives lots more info (especially with -x)
+ *
+ * Author:     Miquel van Smoorenburg, miquels@cistron.nl
+ *
+ * Version:    @(#)last  2.79  13-Jun-2001  miquels@cistron.nl
+ *
+ *             This file is part of the sysvinit suite,
+ *             Copyright 1991-2001 Miquel van Smoorenburg.
+ *
+ *             This program is free software; you can redistribute it and/or
+ *             modify it under the terms of the GNU General Public License
+ *             as published by the Free Software Foundation; either version
+ *             2 of the License, or (at your option) any later version.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/fcntl.h>
+#include <time.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <utmp.h>
+#include <errno.h>
+#include <malloc.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <getopt.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include "oldutmp.h"
+
+#define RUN_LVL         1       /* The system's runlevel.  */
+#define BOOT_TIME       2       /* Time of system boot.  */
+#define NEW_TIME        3       /* Time after system clock changed.  */
+#define OLD_TIME        4       /* Time when system clock changed.  */
+
+#ifndef SHUTDOWN_TIME
+#  define SHUTDOWN_TIME 254
+#endif
+
+char *Version = "@(#) last 2.79 11-Sep-2000 miquels";
+
+#define CHOP_DOMAIN    0       /* Define to chop off local domainname. */
+#define NEW_UTMP       1       /* Fancy & fast utmp read code. */
+#define UCHUNKSIZE     16384   /* How much we read at once. */
+
+/* Double linked list of struct utmp's */
+struct utmplist {
+  struct utmp ut;
+  struct utmplist *next;
+  struct utmplist *prev;
+};
+struct utmplist *utmplist = NULL;
+
+/* Types of listing */
+#define R_CRASH                1 /* No logout record, system boot in between */
+#define R_DOWN         2 /* System brought down in decent way */
+#define R_NORMAL       3 /* Normal */
+#define R_NOW          4 /* Still logged in */
+#define R_REBOOT       5 /* Reboot record. */
+#define R_PHANTOM      6 /* No logout record but session is stale. */
+#define R_TIMECHANGE   7 /* NEW_TIME or OLD_TIME */
+
+/* Global variables */
+int maxrecs = 0;       /* Maximum number of records to list. */
+int recsdone = 0;      /* Number of records listed */
+int showhost = 1;      /* Show hostname too? */
+int altlist = 0;       /* Show hostname at the end. */
+int usedns = 0;                /* Use DNS to lookup the hostname. */
+int useip = 0;         /* Print IP address in number format */
+int oldfmt = 0;                /* Use old libc5 format? */
+char **show = NULL;    /* What do they want us to show */
+char *ufile;           /* Filename of this file */
+time_t lastdate;       /* Last date we've seen */
+char *progname;                /* Name of this program */
+#if CHOP_DOMAIN
+char hostname[256];    /* For gethostbyname() */
+char *domainname;      /* Our domainname. */
+#endif
+
+/*
+ *     Convert old utmp format to new.
+ */
+void uconv(struct oldutmp *oldut, struct utmp *utn)
+{
+       memset(utn, 0, sizeof(struct utmp));
+       utn->ut_type = oldut->ut_type;
+       utn->ut_pid  = oldut->ut_pid;
+       utn->ut_time = oldut->ut_oldtime;
+       utn->ut_addr = oldut->ut_oldaddr;
+       strncpy(utn->ut_line, oldut->ut_line, OLD_LINESIZE);
+       strncpy(utn->ut_user, oldut->ut_user, OLD_NAMESIZE);
+       strncpy(utn->ut_host, oldut->ut_host, OLD_HOSTSIZE);
+}
+
+#if NEW_UTMP
+/*
+ *     Read one utmp entry, return in new format.
+ *     Automatically reposition file pointer.
+ */
+int uread(FILE *fp, struct utmp *u, int *quit)
+{
+       static int utsize;
+       static char buf[UCHUNKSIZE];
+       char tmp[1024];
+       static off_t fpos;
+       static int bpos;
+       struct oldutmp uto;
+       int r;
+       off_t o;
+
+       if (quit == NULL && u != NULL) {
+               /*
+                *      Normal read.
+                */
+               if (oldfmt) {
+                       r = fread(&uto, sizeof(uto), 1, fp);
+                       uconv(&uto, u);
+               } else
+                       r = fread(u, sizeof(struct utmp), 1, fp);
+               return r;
+       }
+
+       if (u == NULL) {
+               /*
+                *      Initialize and position.
+                */
+               utsize = oldfmt ? sizeof(uto) : sizeof(struct utmp);
+               fseek(fp, 0L, SEEK_END);
+               fpos = ftell(fp);
+               if (fpos == 0)
+                       return 0;
+               o = ((fpos - 1) / UCHUNKSIZE) * UCHUNKSIZE;
+               if (fseek(fp, o, SEEK_SET) < 0) {
+                       fprintf(stderr, "%s: seek failed!\n", progname);
+                       return 0;
+               }
+               bpos = fpos - o;
+               if (fread(buf, bpos, 1, fp) != 1) {
+                       fprintf(stderr, "%s: read failed!\n", progname);
+                       return 0;
+               }
+               fpos = o;
+               return 1;
+       }
+
+       /*
+        *      Read one struct. From the buffer if possible.
+        */
+       bpos -= utsize;
+       if (bpos >= 0) {
+               if (oldfmt)
+                       uconv((struct oldutmp *)(buf + bpos), u);
+               else
+                       memcpy(u, buf + bpos, sizeof(struct utmp));
+               return 1;
+       }
+
+       /*
+        *      Oops we went "below" the buffer. We should be able to
+        *      seek back UCHUNKSIZE bytes.
+        */
+       fpos -= UCHUNKSIZE;
+       if (fpos < 0)
+               return 0;
+
+       /*
+        *      Copy whatever is left in the buffer.
+        */
+       memcpy(tmp + (-bpos), buf, utsize + bpos);
+       if (fseek(fp, fpos, SEEK_SET) < 0) {
+               perror("fseek");
+               return 0;
+       }
+
+       /*
+        *      Read another UCHUNKSIZE bytes.
+        */
+       if (fread(buf, UCHUNKSIZE, 1, fp) != 1) {
+               perror("fread");
+               return 0;
+       }
+
+       /*
+        *      The end of the UCHUNKSIZE byte buffer should be the first
+        *      few bytes of the current struct utmp.
+        */
+       memcpy(tmp, buf + UCHUNKSIZE + bpos, -bpos);
+       bpos += UCHUNKSIZE;
+
+       if (oldfmt)
+               uconv((struct oldutmp *)tmp, u);
+       else
+               memcpy(u, tmp, sizeof(struct utmp));
+
+       return 1;
+}
+
+#else /* NEW_UTMP */
+
+/*
+ *     Read one utmp entry, return in new format.
+ *     Automatically reposition file pointer.
+ */
+int uread(FILE *fp, struct utmp *u, int *quit)
+{
+       struct oldutmp uto;
+       int r;
+
+       if (u == NULL) {
+               r = oldfmt ? sizeof(struct oldutmp) : sizeof(struct utmp);
+               fseek(fp, -1L * r, SEEK_END);
+               return 1;
+       }
+
+       if (!oldfmt) {
+               r = fread(u, sizeof(struct utmp), 1, fp);
+               if (r == 1) {
+                       if (fseek(fp, -2L * sizeof(struct utmp), SEEK_CUR) < 0)
+                               if (quit) *quit = 1;
+               }
+               return r;
+       }
+       r = fread(&uto, sizeof(struct oldutmp), 1, fp);
+       if (r == 1) {
+               if (fseek(fp, -2L * sizeof(struct oldutmp), SEEK_CUR) < 0)
+                       if (quit) *quit = 1;
+               uconv(&uto, u);
+       }
+
+       return r;
+}
+#endif
+
+/*
+ *     Try to be smart about the location of the BTMP file
+ */
+#ifndef WTMP_FILE
+#define WTMP_FILE "/var/log/wtmp"
+#endif
+
+#ifndef BTMP_FILE
+#define BTMP_FILE getbtmp()
+char *getbtmp()
+{
+       static char btmp[128];
+       char *p;
+
+       strcpy(btmp, WTMP_FILE);
+       if ((p = strrchr(btmp, '/')) == NULL)
+               p = btmp;
+       else
+               p++;
+       *p = 0;
+       strcat(btmp, "btmp");
+       return btmp;
+}
+#endif
+
+/*
+ *     Print a short date.
+ */
+char *showdate()
+{
+       char *s = ctime(&lastdate);
+       s[16] = 0;
+       return s;
+}
+
+/*
+ *     SIGINT handler
+ */
+void int_handler()
+{
+       printf("Interrupted %s\n", showdate());
+       exit(1);
+}
+
+/*
+ *     SIGQUIT handler
+ */
+void quit_handler()
+{
+       printf("Interrupted %s\n", showdate());
+       signal(SIGQUIT, quit_handler);
+}
+
+/*
+ *     Get the basename of a filename
+ */
+char *mybasename(char *s)
+{
+       char *p;
+
+       if ((p = strrchr(s, '/')) != NULL)
+               p++;
+       else
+               p = s;
+       return p;
+}
+
+/*
+ *     Lookup a host with DNS.
+ */
+int dns_lookup(char *result, int size, char *org, unsigned int ip)
+{
+       struct hostent *h;
+
+       /*
+        *      Try to catch illegal IP numbers
+        */
+       if (ip == 0 || (int)ip == -1 || (ip >> 24) == 0 || (ip & 255) == 0) {
+               if (size > UT_HOSTSIZE) size = UT_HOSTSIZE+1;
+               strncpy(result, org, size - 1);
+               result[size-1] = 0;
+               return 0;
+       }
+
+       if ((h = gethostbyaddr((char *)&ip, 4, AF_INET)) == NULL) {
+               strncpy(result, inet_ntoa(*(struct in_addr *)&ip), size);
+               result[size-1] = 0;
+               return 0;
+       }
+       strncpy(result, h->h_name, size-1);
+       result[size-1] = 0;
+
+       return 0;
+}
+
+/*
+ *     Show one line of information on screen
+ */
+int list(struct utmp *p, time_t t, int what)
+{
+       struct in_addr in;
+       char logintime[32];
+       char logouttime[32];
+       char length[32];
+       char final[128];
+       char utline[UT_LINESIZE+1];
+       char domain[256];
+       time_t secs, tmp;
+       int mins, hours, days;
+       char *s, **walk;
+
+       /*
+        *      uucp and ftp have special-type entries
+        */
+       strncpy(utline, p->ut_line, UT_LINESIZE);
+       utline[UT_LINESIZE - 1] = 0;
+       if (strncmp(utline, "ftp", 3) == 0 && isdigit(utline[3]))
+               utline[3] = 0;
+       if (strncmp(utline, "uucp", 4) == 0 && isdigit(utline[4]))
+               utline[4] = 0;
+
+       /*
+        *      Is this something we wanna show?
+        */
+       if (show) {
+               for (walk = show; *walk; walk++) {
+                       if (strncmp(p->ut_name, *walk, UT_NAMESIZE) == 0 ||
+                           strcmp(utline, *walk) == 0 ||
+                           (strncmp(utline, "tty", 3) == 0 &&
+                            strcmp(utline + 3, *walk) == 0)) break;
+               }
+               if (*walk == NULL) return 0;
+       }
+
+       /*
+        *      Calculate times
+        */
+       tmp = (time_t)p->ut_time;
+       strcpy(logintime, ctime(&tmp));
+       logintime[16] = 0;
+       sprintf(logouttime, "- %s", ctime(&t) + 11);
+       logouttime[7] = 0;
+       secs = t - p->ut_time;
+       mins  = (secs / 60) % 60;
+       hours = (secs / 3600) % 24;
+       days  = secs / 86400;
+       if (days)
+               sprintf(length, "(%d+%02d:%02d)", days, hours, mins);
+       else
+               sprintf(length, " (%02d:%02d)", hours, mins);
+
+       switch(what) {
+               case R_CRASH:
+                       sprintf(logouttime, "- crash");
+                       break;
+               case R_DOWN:
+                       sprintf(logouttime, "- down ");
+                       break;
+               case R_NOW:
+                       length[0] = 0;
+                       sprintf(logouttime, "  still");
+                       sprintf(length, "logged in");
+                       break;
+               case R_PHANTOM:
+                       length[0] = 0;
+                       sprintf(logouttime, "   gone");
+                       sprintf(length, "- no logout");
+                       break;
+               case R_REBOOT:
+                       logouttime[0] = 0;      /* Print machine uptime */
+                       break;
+               case R_TIMECHANGE:
+                       logouttime[0] = 0;
+                       length[0] = 0;
+                       break;
+               case R_NORMAL:
+                       break;
+       }
+
+       /*
+        *      Look up host with DNS if needed.
+        */
+       if (usedns)
+               dns_lookup(domain, sizeof(domain), p->ut_host, p->ut_addr);
+       if (useip) {
+               in.s_addr = p->ut_addr;
+               strcpy(domain, inet_ntoa(in));
+       } else {
+               strncpy(domain, p->ut_host, UT_HOSTSIZE);
+               domain[UT_HOSTSIZE-1] = 0;
+       }
+
+       if (showhost) {
+#if CHOP_DOMAIN
+               /*
+                *      See if this is in our domain.
+                */
+               if (!usedns && (s = strchr(p->ut_host, '.')) != NULL &&
+                    strcmp(s + 1, domainname) == 0) *s = 0;
+#endif
+               if (!altlist) {
+                       snprintf(final, sizeof(final),
+                               "%-8.8s %-12.12s %-16.16s "
+                               "%-16.16s %-7.7s %-12.12s\n",
+                               p->ut_name, utline,
+                               domain, logintime, logouttime, length);
+               } else {
+                       snprintf(final, sizeof(final), 
+                               "%-8.8s %-12.12s %-16.16s %-7.7s %-12.12s %s\n",
+                               p->ut_name, utline,
+                               logintime, logouttime, length, domain);
+               }
+       } else
+               snprintf(final, sizeof(final),
+                       "%-8.8s %-12.12s %-16.16s %-7.7s %-12.12s\n",
+                       p->ut_name, utline,
+                       logintime, logouttime, length);
+
+       /*
+        *      Print out "final" string safely.
+        */
+       for (s = final; *s; s++) {
+               if (*s == '\n' || (*s >= 32 && (unsigned char)*s <= 126))
+                       putchar(*s);
+               else
+                       putchar('*');
+       }
+
+       recsdone++;
+       if (maxrecs && recsdone >= maxrecs)
+               return 1;
+
+       return 0;
+}
+
+
+/*
+ *     show usage
+ */
+void usage(char *s)
+{
+       fprintf(stderr, "Usage: %s [-num | -n num] [-f file] "
+                       "[-R] [-x] [-o] [username..] [tty..]\n", s);
+       exit(1);
+}
+
+int main(int argc, char **argv)
+{
+  FILE *fp;            /* Filepointer of wtmp file */
+
+  struct utmp ut;      /* Current utmp entry */
+  struct utmp oldut;   /* Old utmp entry to check for duplicates */
+  struct utmplist *p;  /* Pointer into utmplist */
+  struct utmplist *next;/* Pointer into utmplist */
+
+  time_t lastboot = 0;  /* Last boottime */
+  time_t lastrch = 0;  /* Last run level change */
+  time_t lastdown;     /* Last downtime */
+  time_t begintime;    /* When wtmp begins */
+  int whydown = 0;     /* Why we went down: crash or shutdown */
+
+  int c, x;            /* Scratch */
+  struct stat st;      /* To stat the [uw]tmp file */
+  int quit = 0;                /* Flag */
+  int down = 0;                /* Down flag */
+  int lastb = 0;       /* Is this 'lastb' ? */
+  int extended = 0;    /* Lots of info. */
+  char *altufile = NULL;/* Alternate wtmp */
+
+  progname = mybasename(argv[0]);
+
+  /* Process the arguments. */
+  while((c = getopt(argc, argv, "f:n:Rxadio0123456789")) != EOF)
+    switch(c) {
+       case 'R':
+               showhost = 0;
+               break;
+       case 'x':
+               extended = 1;
+               break;
+       case 'n':
+               maxrecs = atoi(optarg);
+               break;
+       case 'o':
+               oldfmt = 1;
+               break;
+       case 'f':
+               if((altufile = malloc(strlen(optarg)+1)) == NULL) {
+                       fprintf(stderr, "%s: out of memory\n",
+                               progname);
+                       exit(1);
+               }
+               strcpy(altufile, optarg);
+               break;
+       case 'd':
+               usedns++;
+               break;
+       case 'i':
+               useip++;
+               break;
+       case 'a':
+               altlist++;
+               break;
+       case '0': case '1': case '2': case '3': case '4':
+       case '5': case '6': case '7': case '8': case '9':
+               maxrecs = 10*maxrecs + c - '0';
+               break;
+       default:
+               usage(progname);
+               break;
+    }
+  if (optind < argc) show = argv + optind;
+
+  /*
+   *   Which file do we want to read?
+   */
+  if (strcmp(progname, "lastb") == 0) {
+       ufile = BTMP_FILE;
+       lastb = 1;
+  } else
+       ufile = WTMP_FILE;
+  if (altufile)
+       ufile = altufile;
+  time(&lastdown);
+  lastrch = lastdown;
+
+  /*
+   *   Fill in 'lastdate'
+   */
+  lastdate = lastdown;
+
+#if CHOP_DOMAIN
+  /*
+   *   Find out domainname.
+   *
+   *   This doesn't work on modern systems, where only a DNS
+   *   lookup of the result from hostname() will get you the domainname.
+   *   Remember that domainname() is the NIS domainname, not DNS.
+   *   So basically this whole piece of code is bullshit.
+   */
+  hostname[0] = 0;
+  (void) gethostname(hostname, sizeof(hostname));
+  if ((domainname = strchr(hostname, '.')) != NULL) domainname++;
+  if (domainname == NULL || domainname[0] == 0) {
+       hostname[0] = 0;
+       (void) getdomainname(hostname, sizeof(hostname));
+       hostname[sizeof(hostname) - 1] = 0;
+       domainname = hostname;
+       if (strcmp(domainname, "(none)") == 0 || domainname[0] == 0)
+               domainname = NULL;
+  }
+#endif
+
+  /*
+   *   Install signal handlers
+   */
+  signal(SIGINT, int_handler);
+  signal(SIGQUIT, quit_handler);
+
+  /*
+   *   Open the utmp file
+   */
+  if ((fp = fopen(ufile, "r")) == NULL) {
+       x = errno;
+       fprintf(stderr, "%s: %s: %s\n", progname, ufile, strerror(errno));
+       if (altufile == NULL && x == ENOENT)
+               fprintf(stderr, "Perhaps this file was removed by the "
+                       "operator to prevent logging %s info.\n", progname);
+       exit(1);
+  }
+
+  /*
+   *   Optimize the buffer size.
+   */
+  setvbuf(fp, NULL, _IOFBF, UCHUNKSIZE);
+
+  /*
+   *   Read first structure to capture the time field
+   */
+  if (uread(fp, &ut, NULL) == 1)
+       begintime = ut.ut_time;
+  else {
+       fstat(fileno(fp), &st);
+       begintime = st.st_ctime;
+       quit = 1;
+  }
+
+  /*
+   *   Go to end of file minus one structure
+   *   and/or initialize utmp reading code.
+   */
+  uread(fp, NULL, NULL);
+
+  /*
+   *   Read struct after struct backwards from the file.
+   */
+  while(!quit) {
+
+       if (uread(fp, &ut, &quit) != 1)
+               break;
+
+       if (memcmp(&ut, &oldut, sizeof(struct utmp)) == 0) continue;
+       memcpy(&oldut, &ut, sizeof(struct utmp));
+       lastdate = ut.ut_time;
+
+       if (lastb) {
+               quit = list(&ut, ut.ut_time, R_NORMAL);
+               continue;
+       }
+
+       /*
+        *      Set ut_type to the correct type.
+        */
+       if (strncmp(ut.ut_line, "~", 1) == 0) {
+               if (strncmp(ut.ut_user, "shutdown", 8) == 0)
+                       ut.ut_type = SHUTDOWN_TIME;
+               else if (strncmp(ut.ut_user, "reboot", 6) == 0)
+                       ut.ut_type = BOOT_TIME;
+               else if (strncmp(ut.ut_user, "runlevel", 7) == 0)
+                       ut.ut_type = RUN_LVL;
+       }
+#if 1 /*def COMPAT*/
+       /*
+        *      For stupid old applications that don't fill in
+        *      ut_type correctly.
+        */
+       else {
+               if (ut.ut_type != DEAD_PROCESS &&
+                   ut.ut_name[0] && ut.ut_line[0] &&
+                   strcmp(ut.ut_name, "LOGIN") != 0)
+                       ut.ut_type = USER_PROCESS;
+               /*
+                *      Even worse, applications that write ghost
+                *      entries: ut_type set to USER_PROCESS but
+                *      empty ut_name...
+                */
+               if (ut.ut_name[0] == 0)
+                       ut.ut_type = DEAD_PROCESS;
+
+               /*
+                *      Clock changes.
+                */
+               if (strcmp(ut.ut_name, "date") == 0) {
+                       if (ut.ut_line[0] == '|') ut.ut_type = OLD_TIME;
+                       if (ut.ut_line[0] == '{') ut.ut_type = NEW_TIME;
+               }
+       }
+#endif
+
+       switch (ut.ut_type) {
+               case SHUTDOWN_TIME:
+                       if (extended) {
+                               strcpy(ut.ut_line, "system down");
+                               quit = list(&ut, lastdown, R_NORMAL);
+                       }
+                       lastdown = lastrch = ut.ut_time;
+                       down = 1;
+                       break;
+               case OLD_TIME:
+               case NEW_TIME:
+                       if (extended) {
+                               strcpy(ut.ut_line,
+                               ut.ut_type == NEW_TIME ? "new time" :
+                                       "old time");
+                               quit = list(&ut, lastdown, R_TIMECHANGE);
+                       }
+                       break;
+               case BOOT_TIME:
+                       strcpy(ut.ut_line, "system boot");
+                       quit = list(&ut, lastdown, R_REBOOT);
+                       down = 1;
+                       break;
+               case RUN_LVL:
+                       x = ut.ut_pid & 255;
+                       if (extended) {
+                               sprintf(ut.ut_line, "(to lvl %c)", x);
+                               quit = list(&ut, lastrch, R_NORMAL);
+                       }
+                       if (x == '0' || x == '6') {
+                               lastdown = ut.ut_time;
+                               down = 1;
+                               ut.ut_type = SHUTDOWN_TIME;
+                       }
+                       lastrch = ut.ut_time;
+                       break;
+
+               case USER_PROCESS:
+                       /*
+                        *      This was a login - show the first matching
+                        *      logout record and delete all records with
+                        *      the same ut_line.
+                        */
+                       c = 0;
+                       for (p = utmplist; p; p = next) {
+                               next = p->next;
+                               if (strncmp(p->ut.ut_line, ut.ut_line,
+                                   UT_LINESIZE) == 0) {
+                                       /* Show it */
+                                       if (c == 0) {
+                                               quit = list(&ut, p->ut.ut_time,
+                                                       R_NORMAL);
+                                               c = 1;
+                                       }
+                                       if (p->next) p->next->prev = p->prev;
+                                       if (p->prev)
+                                               p->prev->next = p->next;
+                                       else
+                                               utmplist = p->next;
+                                       free(p);
+                               }
+                       }
+                       /*
+                        *      Not found? Then crashed, down, still
+                        *      logged in, or missing logout record.
+                        */
+                       if (c == 0) {
+                               if (lastboot == 0) {
+                                       c = R_NOW;
+                                       /* Is process still alive? */
+                                       if (ut.ut_pid > 0 &&
+                                           kill(ut.ut_pid, 0) != 0 &&
+                                           errno == ESRCH)
+                                               c = R_PHANTOM;
+                               } else
+                                       c = whydown;
+                               quit = list(&ut, lastboot, c);
+                       }
+                       /* FALLTHRU */
+
+               case DEAD_PROCESS:
+                       /*
+                        *      Just store the data if it is
+                        *      interesting enough.
+                        */
+                       if (ut.ut_line[0] == 0)
+                               break;
+                       if ((p = malloc(sizeof(struct utmplist))) == NULL) {
+                               fprintf(stderr, "%s: out of memory\n",
+                                       progname);
+                               exit(1);
+                       }
+                       memcpy(&p->ut, &ut, sizeof(struct utmp));
+                       p->next  = utmplist;
+                       p->prev  = NULL;
+                       if (utmplist) utmplist->prev = p;
+                       utmplist = p;
+                       break;
+
+       }
+       /*
+        *      If we saw a shutdown/reboot record we can remove
+        *      the entire current utmplist.
+        */
+       if (down) {
+               lastboot = ut.ut_time;
+               whydown = (ut.ut_type == SHUTDOWN_TIME) ? R_DOWN : R_CRASH;
+               for (p = utmplist; p; p = next) {
+                       next = p->next;
+                       free(p);
+               }
+               utmplist = NULL;
+               down = 0;
+       }
+  }
+  printf("\n%s begins %s", mybasename(ufile), ctime(&begintime));
+
+  fclose(fp);
+
+  /*
+   *   Should we free memory here? Nah. This is not NT :)
+   */
+  return 0;
+}
diff --git a/src-gpl/lastb.1 b/src-gpl/lastb.1
new file mode 100644 (file)
index 0000000..f57c02a
--- /dev/null
@@ -0,0 +1 @@
+.so man1/last.1
diff --git a/src-gpl/oldutmp.h b/src-gpl/oldutmp.h
new file mode 100644 (file)
index 0000000..8a90589
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * oldutmp.h   Definition of the old libc5 utmp structure.
+ *
+ * Version:    @(#)oldutmp.h  1.00  29-Mar-1998  miquels@cistron.nl
+ *
+ */
+#ifndef OLD_UTMP_H
+#define OLD_UTMP_H
+
+#define OLD_LINESIZE           12
+#define OLD_NAMESIZE           8
+#define OLD_HOSTSIZE           16
+
+struct oldutmp {
+       short   ut_type;
+       int     ut_pid;
+       char    ut_line[OLD_LINESIZE];
+       char    ut_id[4];
+       long    ut_oldtime;
+       char    ut_user[OLD_NAMESIZE];
+       char    ut_host[OLD_HOSTSIZE];
+       long    ut_oldaddr;
+};
+
+#endif
This page took 0.105345 seconds and 5 git commands to generate.