svn commit: r202197 - head/usr.bin/last

Ed Schouten ed at FreeBSD.org
Wed Jan 13 18:06:31 UTC 2010


Author: ed
Date: Wed Jan 13 18:06:31 2010
New Revision: 202197
URL: http://svn.freebsd.org/changeset/base/202197

Log:
  Port last(1) to use utmpx.
  
  Basically there are three major things I changed about last(1):
  
  - It should use ut_type instead of determining by hand what type of
    record was given.
  - It should now keep track of ut_id's instead of TTYs. This means the
    ttylist has been renamed to the idlist, storing all the ut_id's it has
    processed until the next reboot.
  - I've removed the signal handler. Because our wtmp is rotated so often,
    it makes little sense. Even on a simple piece of hardware it should be
    capable of grinding through megabytes of logs in a second.

Modified:
  head/usr.bin/last/last.c

Modified: head/usr.bin/last/last.c
==============================================================================
--- head/usr.bin/last/last.c	Wed Jan 13 18:02:30 2010	(r202196)
+++ head/usr.bin/last/last.c	Wed Jan 13 18:06:31 2010	(r202197)
@@ -59,15 +59,13 @@ __FBSDID("$FreeBSD$");
 #include <time.h>
 #include <timeconv.h>
 #include <unistd.h>
-#include <utmp.h>
+#include <utmpx.h>
 #include <sys/queue.h>
 
 #define	NO	0				/* false/no */
 #define	YES	1				/* true/yes */
 #define	ATOI2(ar)	((ar)[0] - '0') * 10 + ((ar)[1] - '0'); (ar) += 2;
 
-static struct utmp	buf[1024];		/* utmp read buffer */
-
 typedef struct arg {
 	char	*name;				/* argument */
 #define	HOST_TYPE	-2
@@ -78,18 +76,18 @@ typedef struct arg {
 } ARG;
 ARG	*arglist;				/* head of linked list */
 
-LIST_HEAD(ttylisthead, ttytab) ttylist;
+LIST_HEAD(idlisthead, idtab) idlist;
 
-struct ttytab {
+struct idtab {
 	time_t	logout;				/* log out time */
-	char	tty[UT_LINESIZE + 1];		/* terminal name */
-	LIST_ENTRY(ttytab) list;
+	char	id[sizeof ((struct utmpx *)0)->ut_id]; /* identifier */
+	LIST_ENTRY(idtab) list;
 };
 
 static const	char *crmsg;			/* cause of last reboot */
-static long	currentout,			/* current logout value */
-		maxrec;				/* records to display */
-static const	char *file = _PATH_WTMP;		/* wtmp file */
+static time_t	currentout;			/* current logout value */
+static long	maxrec;				/* records to display */
+static const	char *file = NULL;		/* wtmp file */
 static int	sflag = 0;			/* show delta in seconds */
 static int	width = 5;			/* show seconds in delta */
 static int	yflag;				/* show year */
@@ -102,12 +100,11 @@ static time_t	snaptime;			/* if != 0, we
 
 void	 addarg(int, char *);
 time_t	 dateconv(char *);
-void	 doentry(struct utmp *);
+void	 doentry(struct utmpx *);
 void	 hostconv(char *);
-void	 onintr(int);
-void	 printentry(struct utmp *, struct ttytab *);
+void	 printentry(struct utmpx *, struct idtab *);
 char	*ttyconv(char *);
-int	 want(struct utmp *);
+int	 want(struct utmpx *);
 void	 usage(void);
 void	 wtmp(void);
 
@@ -199,6 +196,8 @@ main(int argc, char *argv[])
 	exit(0);
 }
 
+#define	MAXUTXENTRIES	1024
+
 /*
  * wtmp --
  *	read through the wtmp file
@@ -206,33 +205,34 @@ main(int argc, char *argv[])
 void
 wtmp(void)
 {
-	struct utmp	*bp;			/* current structure */
-	struct stat	stb;			/* stat of file for size */
-	long	bl;
-	int	bytes, wfd;
+	struct utmpx buf[MAXUTXENTRIES];
+	struct utmpx *ut;
+	static unsigned int first = 0, amount = 0;
+	time_t t;
 	char ct[80];
 	struct tm *tm;
-	time_t	t;
 
-	LIST_INIT(&ttylist);
+	LIST_INIT(&idlist);
+	(void)time(&t);
 
-	if ((wfd = open(file, O_RDONLY, 0)) < 0 || fstat(wfd, &stb) == -1)
+	/* Load the last entries from the file. */
+	if (setutxdb(UTXDB_LOG, file) != 0)
 		err(1, "%s", file);
-	bl = (stb.st_size + sizeof(buf) - 1) / sizeof(buf);
+	while ((ut = getutxent()) != NULL) {
+		memcpy(&buf[(first + amount) % MAXUTXENTRIES], ut, sizeof *ut);
+		if (amount == MAXUTXENTRIES)
+			first++;
+		else
+			amount++;
+		if (t > ut->ut_tv.tv_sec)
+			t = ut->ut_tv.tv_sec;
+	}
+	endutxent();
+
+	/* Display them in reverse order. */
+	while (amount > 0)
+		doentry(&buf[(first + amount--) % MAXUTXENTRIES]);
 
-	(void)time(&t);
-	buf[0].ut_time = _time_to_int(t);
-	(void)signal(SIGINT, onintr);
-	(void)signal(SIGQUIT, onintr);
-
-	while (--bl >= 0) {
-		if (lseek(wfd, (off_t)(bl * sizeof(buf)), L_SET) == -1 ||
-		    (bytes = read(wfd, buf, sizeof(buf))) == -1)
-			err(1, "%s", file);
-		for (bp = &buf[bytes / sizeof(buf[0]) - 1]; bp >= buf; --bp)
-			doentry(bp);
-	}
-	t = _int_to_time(buf[0].ut_time);
 	tm = localtime(&t);
 	(void) strftime(ct, sizeof(ct), "\nwtmp begins %+\n", tm);
 	printf("%s", ct);
@@ -243,24 +243,21 @@ wtmp(void)
  *	process a single wtmp entry
  */
 void
-doentry(struct utmp *bp)
+doentry(struct utmpx *bp)
 {
-	struct ttytab	*tt, *ttx;		/* ttylist entry */
+	struct idtab	*tt, *ttx;		/* idlist entry */
 
-	/*
-	 * if the terminal line is '~', the machine stopped.
-	 * see utmp(5) for more info.
-	 */
-	if (bp->ut_line[0] == '~' && !bp->ut_line[1]) {
+	/* the machine stopped */
+	if (bp->ut_type == BOOT_TIME || bp->ut_type == SHUTDOWN_TIME) {
 		/* everybody just logged out */
-		for (tt = LIST_FIRST(&ttylist); tt;) {
+		for (tt = LIST_FIRST(&idlist); tt;) {
 			LIST_REMOVE(tt, list);
 			ttx = tt;
 			tt = LIST_NEXT(tt, list);
 			free(ttx);
 		}
-		currentout = -bp->ut_time;
-		crmsg = strncmp(bp->ut_name, "shutdown", UT_NAMESIZE) ?
+		currentout = -bp->ut_tv.tv_sec;
+		crmsg = bp->ut_type != SHUTDOWN_TIME ?
 		    "crash" : "shutdown";
 		/*
 		 * if we're in snapshot mode, we want to exit if this
@@ -276,50 +273,42 @@ doentry(struct utmp *bp)
 			printentry(bp, NULL);
 		return;
 	}
-	/*
-	 * if the line is '{' or '|', date got set; see
-	 * utmp(5) for more info.
-	 */
-	if ((bp->ut_line[0] == '{' || bp->ut_line[0] == '|') &&
-	    !bp->ut_line[1]) {
+	/* date got set */
+	if (bp->ut_type == OLD_TIME || bp->ut_type == NEW_TIME) {
 		if (want(bp) && !snaptime)
 			printentry(bp, NULL);
 		return;
 	}
-	/* find associated tty */
-	LIST_FOREACH(tt, &ttylist, list)
-	    if (!strncmp(tt->tty, bp->ut_line, UT_LINESIZE))
+
+	if (bp->ut_type != USER_PROCESS && bp->ut_type != DEAD_PROCESS)
+		return;
+
+	/* find associated identifier */
+	LIST_FOREACH(tt, &idlist, list)
+	    if (!memcmp(tt->id, bp->ut_id, sizeof bp->ut_id))
 		    break;
 
 	if (tt == NULL) {
 		/* add new one */
-		tt = malloc(sizeof(struct ttytab));
+		tt = malloc(sizeof(struct idtab));
 		if (tt == NULL)
 			errx(1, "malloc failure");
 		tt->logout = currentout;
-		strncpy(tt->tty, bp->ut_line, UT_LINESIZE);
-		LIST_INSERT_HEAD(&ttylist, tt, list);
+		memcpy(tt->id, bp->ut_id, sizeof bp->ut_id);
+		LIST_INSERT_HEAD(&idlist, tt, list);
 	}
 
 	/*
 	 * print record if not in snapshot mode and wanted
 	 * or in snapshot mode and in snapshot range
 	 */
-	if (bp->ut_name[0] && (want(bp) || (bp->ut_time < snaptime &&
+	if (bp->ut_type == USER_PROCESS && (want(bp) ||
+	    (bp->ut_tv.tv_sec < snaptime &&
 	    (tt->logout > snaptime || tt->logout < 1)))) {
 		snapfound = 1;
-		/*
-		 * when uucp and ftp log in over a network, the entry in
-		 * the utmp file is the name plus their process id.  See
-		 * etc/ftpd.c and usr.bin/uucp/uucpd.c for more information.
-		 */
-		if (!strncmp(bp->ut_line, "ftp", sizeof("ftp") - 1))
-			bp->ut_line[3] = '\0';
-		else if (!strncmp(bp->ut_line, "uucp", sizeof("uucp") - 1))
-			bp->ut_line[4] = '\0';
 		printentry(bp, tt);
 	}
-	tt->logout = bp->ut_time;
+	tt->logout = bp->ut_tv.tv_sec;
 }
 
 /*
@@ -330,7 +319,7 @@ doentry(struct utmp *bp)
  * logout type (crash/shutdown) as appropriate.
  */
 void
-printentry(struct utmp *bp, struct ttytab *tt)
+printentry(struct utmpx *bp, struct idtab *tt)
 {
 	char ct[80];
 	struct tm *tm;
@@ -339,16 +328,30 @@ printentry(struct utmp *bp, struct ttyta
 
 	if (maxrec != -1 && !maxrec--)
 		exit(0);
-	t = _int_to_time(bp->ut_time);
+	t = bp->ut_tv.tv_sec;
 	tm = localtime(&t);
 	(void) strftime(ct, sizeof(ct), d_first ?
 	    (yflag ? "%a %e %b %Y %R" : "%a %e %b %R") :
 	    (yflag ? "%a %b %e %Y %R" : "%a %b %e %R"), tm);
-	printf("%-*.*s %-*.*s %-*.*s %s%c",
-	    UT_NAMESIZE, UT_NAMESIZE, bp->ut_name,
-	    UT_LINESIZE, UT_LINESIZE, bp->ut_line,
-	    UT_HOSTSIZE, UT_HOSTSIZE, bp->ut_host,
-	    ct, tt == NULL ? '\n' : ' ');
+	switch (bp->ut_type) {
+	case BOOT_TIME:
+		printf("%-42s", "boot time");
+		break;
+	case SHUTDOWN_TIME:
+		printf("%-42s", "shutdown time");
+		break;
+	case OLD_TIME:
+		printf("%-42s", "old time");
+		break;
+	case NEW_TIME:
+		printf("%-42s", "new time");
+		break;
+	case USER_PROCESS:
+		printf("%-10s %-8s %-22.22s",
+		    bp->ut_user, bp->ut_line, bp->ut_host);
+		break;
+	}
+	printf(" %s%c", ct, tt == NULL ? '\n' : ' ');
 	if (tt == NULL)
 		return;
 	if (!tt->logout) {
@@ -363,7 +366,7 @@ printentry(struct utmp *bp, struct ttyta
 		(void) strftime(ct, sizeof(ct), "%R", tm);
 		printf("- %s", ct);
 	}
-	delta = tt->logout - bp->ut_time;
+	delta = tt->logout - bp->ut_tv.tv_sec;
 	if (sflag) {
 		printf("  (%8ld)\n", (long)delta);
 	} else {
@@ -381,7 +384,7 @@ printentry(struct utmp *bp, struct ttyta
  *	see if want this entry
  */
 int
-want(struct utmp *bp)
+want(struct utmpx *bp)
 {
 	ARG *step;
 
@@ -394,15 +397,15 @@ want(struct utmp *bp)
 	for (step = arglist; step; step = step->next)
 		switch(step->type) {
 		case HOST_TYPE:
-			if (!strncasecmp(step->name, bp->ut_host, UT_HOSTSIZE))
+			if (!strcasecmp(step->name, bp->ut_host))
 				return (YES);
 			break;
 		case TTY_TYPE:
-			if (!strncmp(step->name, bp->ut_line, UT_LINESIZE))
+			if (!strcmp(step->name, bp->ut_line))
 				return (YES);
 			break;
 		case USER_TYPE:
-			if (!strncmp(step->name, bp->ut_name, UT_NAMESIZE))
+			if (!strcmp(step->name, bp->ut_user))
 				return (YES);
 			break;
 		}
@@ -552,25 +555,3 @@ terr:           errx(1,
         "out of range or illegal time specification: [[CC]YY]MMDDhhmm[.SS]");
         return timet;
 }
-
-
-/*
- * onintr --
- *	on interrupt, we inform the user how far we've gotten
- */
-void
-onintr(int signo)
-{
-	char ct[80];
-	struct tm *tm;
-	time_t t = _int_to_time(buf[0].ut_time);
-
-	tm = localtime(&t);
-	(void) strftime(ct, sizeof(ct),
-			d_first ? "%a %e %b %R" : "%a %b %e %R",
-			tm);
-	printf("\ninterrupted %s\n", ct);
-	if (signo == SIGINT)
-		exit(1);
-	(void)fflush(stdout);			/* fix required for rsh */
-}


More information about the svn-src-head mailing list