Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 26 Apr 2001 18:06:27 -0700
From:      Dima Dorfman <dima@unixfreak.org>
To:        audit@freebsd.org
Subject:   Patch to add snapshots to last(1)
Message-ID:  <20010427010627.BA0873E2A@bazooka.unixfreak.org>

next in thread | raw e-mail | index | archive | help

OpenBSD's last(1) has a nice snapshot feature which allows one to get
a list of users logged in at a certain date and time.  Attached is a
patch which implements this functionality in FreeBSD's last(1).
Although the majority of the patch is from OpenBSD, the delta is quite
large; our last(1) has other features that theirs doesn't.

I'd appreciate it if someone could please review and/or test the patch.

Thanks,

					Dima Dorfman
					dima@unixfreak.org

Index: last.1
===================================================================
RCS file: /st/src/FreeBSD/src/usr.bin/last/last.1,v
retrieving revision 1.8
diff -u -r1.8 last.1
--- last.1	2001/04/26 06:10:46	1.8
+++ last.1	2001/04/26 23:50:43
@@ -41,6 +41,7 @@
 .Sh SYNOPSIS
 .Nm
 .Op Fl Ns Ar n
+.Op Fl d Ar [[CC]YY][MMDD]hhmm[.SS]
 .Op Fl f Ar file
 .Op Fl h Ar host
 .Op Fl s
@@ -49,12 +50,13 @@
 .Op user ...
 .Sh DESCRIPTION
 .Nm Last
-will list the sessions of specified
+will either list the sessions of specified
 .Ar users ,
 .Ar ttys ,
 and
 .Ar hosts ,
-in reverse time order.
+in reverse time order,
+or list the users logged in at a specified date and time.
 Each line of output contains
 the user name, the tty from which the session was conducted, any
 hostname, the start and stop times for the session, and the duration
@@ -69,6 +71,60 @@
 Limits the report to
 .Ar n
 lines.
+.It Fl d Ar date
+Specify the snapshot date and time.
+All users logged in at the snapshot date and time will
+be reported.
+This may be used with the
+.Fl f
+option to derive the results from stored wtmp files.
+When this argument is provided, all other options except for
+.Fl f
+and
+.Fl Ar n
+are ignored.
+The argument should be in the form
+.Dq [[CC]YY][MMDD]hhmm[.SS]
+where each pair of letters represents the following:
+.Pp
+.Bl -tag -width Ds -compact -offset indent
+.It Ar CC
+The first two digits of the year (the century).
+.It Ar YY
+The second two digits of the year.
+If
+.Dq YY
+is specified, but
+.Dq CC
+is not, a value for
+.Dq YY
+between 69 and 99 results in a
+.Dq CC
+value of 19.
+Otherwise, a
+.Dq CC
+value of 20 is used.
+.It Ar MM
+Month of the year, from 1 to 12.
+.It Ar DD
+Day of the month, from 1 to 31.
+.It Ar hh
+Hour of the day, from 0 to 23.
+.It Ar mm
+Minute of the hour, from 0 to 59.
+.It Ar SS
+Second of the minute, from 0 to 61.
+.El
+.Pp
+If the
+.Dq CC
+and
+.Dq YY
+letter pairs are not specified, the values default to the current
+year.
+If the
+.Dq SS
+letter pair is not specified, the value defaults to 0.
 .It Fl f Ar file
 .Nm Last
 reads the file
@@ -94,8 +150,9 @@
 default days, hours and minutes.
 .El
 .Pp
-If
-multiple arguments are given, the information which applies to any of the
+If multiple arguments are given,
+and a snapshot time is not specified,
+the information which applies to any of the
 arguments is printed, e.g.,
 .Dq Li "last root -t console"
 would list all of
Index: last.c
===================================================================
RCS file: /st/src/FreeBSD/src/usr.bin/last/last.c,v
retrieving revision 1.16
diff -u -r1.16 last.c
--- last.c	2001/03/21 19:08:01	1.16
+++ last.c	2001/04/26 23:50:43
@@ -62,6 +62,7 @@
 
 #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 */
 
@@ -89,11 +90,16 @@
 static int	sflag = 0;			/* show delta in seconds */
 static int	width = 5;			/* show seconds in delta */
 static int      d_first;
+static time_t	snaptime;			/* if != 0, we will only
+						 * report users logged in
+						 * at this snapshot time
+						 */
 
 void	 addarg __P((int, char *));
 void	 hostconv __P((char *));
 void	 onintr __P((int));
 char	*ttyconv __P((char *));
+time_t	 dateconv __P((char *));
 int	 want __P((struct utmp *));
 void	 wtmp __P((void));
 
@@ -117,7 +123,8 @@
 	d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
 
 	maxrec = -1;
-	while ((ch = getopt(argc, argv, "0123456789f:h:st:w")) != -1)
+	snaptime = 0;
+	while ((ch = getopt(argc, argv, "0123456789d:f:h:st:w")) != -1)
 		switch (ch) {
 		case '0': case '1': case '2': case '3': case '4':
 		case '5': case '6': case '7': case '8': case '9':
@@ -135,6 +142,9 @@
 					exit(0);
 			}
 			break;
+		case 'd':
+			snaptime = dateconv(optarg);
+			break;
 		case 'f':
 			file = optarg;
 			break;
@@ -189,6 +199,7 @@
 	char    *crmsg;
 	char ct[80];
 	struct tm *tm;
+	int	 snapfound = 0;			/* found snapshot entry? */
 
 	LIST_INIT(&ttylist);
 
@@ -220,7 +231,19 @@
 				currentout = -bp->ut_time;
 				crmsg = strncmp(bp->ut_name, "shutdown",
 				    UT_NAMESIZE) ? "crash" : "shutdown";
-				if (want(bp)) {
+				/*
+				 * if we're in snapshot mode, we want to
+				 * exit if this shutdown/reboot appears
+				 * while we we are tracking the active
+				 * range
+				 */
+				if (snaptime && snapfound)
+					return;
+				/*
+				 * don't print shutdown/reboot entries
+				 * unless flagged for 
+				 */ 
+				if (!snaptime && want(bp)) {
 					tm = localtime(&bp->ut_time);
 					(void) strftime(ct, sizeof(ct),
 						     d_first ? "%a %e %b %R" :
@@ -243,7 +266,7 @@
 			 */
 			if ((bp->ut_line[0] == '{' || bp->ut_line[0] == '|')
 			    && !bp->ut_line[1]) {
-				if (want(bp)) {
+				if (want(bp) && !snaptime) {
 					tm = localtime(&bp->ut_time);
 					(void) strftime(ct, sizeof(ct),
 						     d_first ? "%a %e %b %R" :
@@ -259,77 +282,82 @@
 				}
 				continue;
 			}
-			if (bp->ut_name[0] == '\0' || want(bp)) {
-				/* find associated tty */
-				LIST_FOREACH(tt, &ttylist, list)
-					if (!strncmp(tt->tty, bp->ut_line, UT_LINESIZE))
-						break;
-
-				if (tt == NULL) {
-					/* add new one */
-					tt = malloc(sizeof(struct ttytab));
-					if (tt == NULL)
-						err(1, "malloc failure");
-					tt->logout = currentout;
-					strncpy(tt->tty, bp->ut_line, UT_LINESIZE);
-					LIST_INSERT_HEAD(&ttylist, tt, list);
-				}
-
-				if (bp->ut_name[0]) {
-					/*
-					 * 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';
-					tm = localtime(&bp->ut_time);
-					(void) strftime(ct, sizeof(ct),
-						     d_first ? "%a %e %b %R" :
-							       "%a %b %e %R",
-						     tm);
-					printf("%-*.*s %-*.*s %-*.*s %s ",
-					    UT_NAMESIZE, UT_NAMESIZE, bp->ut_name,
-					    UT_LINESIZE, UT_LINESIZE, bp->ut_line,
-					    UT_HOSTSIZE, UT_HOSTSIZE, bp->ut_host,
-					    ct);
-					if (!tt->logout)
-						puts("  still logged in");
+			/* find associated tty */
+			LIST_FOREACH(tt, &ttylist, list)
+			    if (!strncmp(tt->tty, bp->ut_line, UT_LINESIZE))
+				    break;
+			
+			if (tt == NULL) {
+				/* add new one */
+				tt = malloc(sizeof(struct ttytab));
+				if (tt == NULL)
+					err(1, "malloc failure");
+				tt->logout = currentout;
+				strncpy(tt->tty, bp->ut_line, UT_LINESIZE);
+				LIST_INSERT_HEAD(&ttylist, 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 &&
+				(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';
+				tm = localtime(&bp->ut_time);
+				(void) strftime(ct, sizeof(ct),
+				    d_first ? "%a %e %b %R" :
+				    "%a %b %e %R",
+				    tm);
+				printf("%-*.*s %-*.*s %-*.*s %s ",
+				    UT_NAMESIZE, UT_NAMESIZE, bp->ut_name,
+				    UT_LINESIZE, UT_LINESIZE, bp->ut_line,
+				    UT_HOSTSIZE, UT_HOSTSIZE, bp->ut_host,
+				    ct);
+				if (!tt->logout)
+					puts("  still logged in");
+				else {
+					if (tt->logout < 0) {
+						tt->logout = -tt->logout;
+						printf("- %s", crmsg);
+					}
 					else {
-						if (tt->logout < 0) {
-							tt->logout = -tt->logout;
-							printf("- %s", crmsg);
-						}
-						else {
-							tm = localtime(&tt->logout);
-							(void) strftime(ct, sizeof(ct), "%R", tm);
-							printf("- %s", ct);
-						}
-						delta = tt->logout - bp->ut_time;
-						if ( sflag ) {
-							printf("  (%8lu)\n", 
-								delta);
-						} else {
-						    tm = gmtime(&delta);
-						    (void) strftime(ct, sizeof(ct),
-						     width >= 8 ? "%T" : "%R",
-						     tm);
-						    if (delta < 86400)
+						tm = localtime(&tt->logout);
+						(void) strftime(ct, sizeof(ct), "%R", tm);
+						printf("- %s", ct);
+					}
+					delta = tt->logout - bp->ut_time;
+					if ( sflag ) {
+						printf("  (%8lu)\n", 
+						    delta);
+					} else {
+						tm = gmtime(&delta);
+						(void) strftime(ct, sizeof(ct),
+						    width >= 8 ? "%T" : "%R",
+						    tm);
+						if (delta < 86400)
 							printf("  (%s)\n", ct);
-						    else
+						else
 							printf(" (%ld+%s)\n",
 							    delta / 86400,  ct);
-						}
 					}
-					LIST_REMOVE(tt, list);
-					free(tt);
-					if (maxrec != -1 && !--maxrec)
-						return;
-				} else {
-					tt->logout = bp->ut_time;
 				}
+				LIST_REMOVE(tt, list);
+				free(tt);
+				if (maxrec != -1 && !--maxrec)
+					return;
+			} else {
+				tt->logout = bp->ut_time;
 			}
 		}
 	}
@@ -348,6 +376,9 @@
 {
 	ARG *step;
 
+	if (snaptime)
+		return (NO);
+
 	if (!arglist)
 		return (YES);
 
@@ -444,6 +475,80 @@
 		return (arg + 5);
 	return (arg);
 }
+
+/*
+ * dateconv --
+ * 	Convert the snapshot time in command line given in the format
+ * 	[[CC]YY]MMDDhhmm[.SS]] to a time_t.
+ * 	Derived from atime_arg1() in usr.bin/touch/touch.c
+ */
+time_t
+dateconv(arg)
+        char *arg;
+{
+        time_t timet;
+        struct tm *t;
+        int yearset;
+        char *p;
+
+        /* Start with the current time. */
+        if (time(&timet) < 0)
+                err(1, "time");
+        if ((t = localtime(&timet)) == NULL)
+                err(1, "localtime");
+
+        /* [[CC]YY]MMDDhhmm[.SS] */
+        if ((p = strchr(arg, '.')) == NULL)
+                t->tm_sec = 0; 		/* Seconds defaults to 0. */
+        else {
+                if (strlen(p + 1) != 2)
+                        goto terr;
+                *p++ = '\0';
+                t->tm_sec = ATOI2(p);
+        }
+
+        yearset = 0;
+        switch (strlen(arg)) {
+        case 12:                	/* CCYYMMDDhhmm */
+                t->tm_year = ATOI2(arg);
+                t->tm_year *= 100;
+                yearset = 1;
+                /* FALLTHOUGH */
+        case 10:                	/* YYMMDDhhmm */
+                if (yearset) {
+                        yearset = ATOI2(arg);
+                        t->tm_year += yearset;
+                } else {
+                        yearset = ATOI2(arg);
+                        if (yearset < 69)
+                                t->tm_year = yearset + 2000;
+                        else
+                                t->tm_year = yearset + 1900;
+                }
+                t->tm_year -= 1900;     /* Convert to UNIX time. */
+                /* FALLTHROUGH */
+        case 8:				/* MMDDhhmm */
+                t->tm_mon = ATOI2(arg);
+                --t->tm_mon;    	/* Convert from 01-12 to 00-11 */
+                t->tm_mday = ATOI2(arg);
+                t->tm_hour = ATOI2(arg);
+                t->tm_min = ATOI2(arg);
+                break;
+        case 4:				/* hhmm */
+                t->tm_hour = ATOI2(arg);
+                t->tm_min = ATOI2(arg);
+                break;
+        default:
+                goto terr;
+        }
+        t->tm_isdst = -1;       	/* Figure out DST. */
+        timet = mktime(t);
+        if (timet == -1)
+terr:           errx(1,
+        "out of range or illegal time specification: [[CC]YY]MMDDhhmm[.SS]");
+        return timet;
+}
+
 
 /*
  * onintr --

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-audit" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20010427010627.BA0873E2A>