From owner-freebsd-bugs Wed May 2 21: 0:29 2001 Delivered-To: freebsd-bugs@hub.freebsd.org Received: from freefall.freebsd.org (freefall.freebsd.org [216.136.204.21]) by hub.freebsd.org (Postfix) with ESMTP id B16D237B506 for ; Wed, 2 May 2001 21:00:09 -0700 (PDT) (envelope-from gnats@FreeBSD.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.11.1/8.11.1) id f43409433706; Wed, 2 May 2001 21:00:09 -0700 (PDT) (envelope-from gnats) Received: from bazooka.unixfreak.org (bazooka.unixfreak.org [63.198.170.138]) by hub.freebsd.org (Postfix) with ESMTP id 1C22237B423 for ; Wed, 2 May 2001 20:51:09 -0700 (PDT) (envelope-from dima@unixfreak.org) Received: from hornet.unixfreak.org (hornet [63.198.170.140]) by bazooka.unixfreak.org (Postfix) with ESMTP id 5E2B03E28 for ; Wed, 2 May 2001 20:51:05 -0700 (PDT) Received: (from dima@localhost) by hornet.unixfreak.org (8.11.3/8.11.1) id f433p5q78347; Wed, 2 May 2001 20:51:05 -0700 (PDT) (envelope-from dima) Message-Id: <200105030351.f433p5q78347@hornet.unixfreak.org> Date: Wed, 2 May 2001 20:51:05 -0700 (PDT) From: dd@freebsd.org Reply-To: dd@freebsd.org To: FreeBSD-gnats-submit@freebsd.org X-Send-Pr-Version: 3.113 Subject: bin/27050: [PATCH] Add snapshot functionality to last(1) Sender: owner-freebsd-bugs@FreeBSD.ORG Precedence: bulk X-Loop: FreeBSD.org >Number: 27050 >Category: bin >Synopsis: [PATCH] Add snapshot functionality to last(1) >Confidential: no >Severity: non-critical >Priority: low >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: change-request >Submitter-Id: current-users >Arrival-Date: Wed May 02 21:00:09 PDT 2001 >Closed-Date: >Last-Modified: >Originator: >Release: FreeBSD 4.3-RELEASE i386 >Organization: Private >Environment: System: FreeBSD hornet.unixfreak.org 4.3-RELEASE FreeBSD 4.3-RELEASE #0: Sat Apr 21 16:42:47 PDT 2001 dima@hornet.unixfreak.org:/a/home/dima/w/f/43/sys/compile/HORNET i386 >Description: 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. >How-To-Repeat: >Fix: The following patch adds a -d option which implements the above functionality. I sent it to -hackers and -audit, but as far as I know nobody did anything more than skim it. It is "Obtained from: OpenBSD". 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/05/03 03:34:51 @@ -32,7 +32,7 @@ .\" @(#)last.1 8.1 (Berkeley) 6/6/93 .\" $FreeBSD: src/usr.bin/last/last.1,v 1.8 2001/04/26 06:10:46 dd Exp $ .\" -.Dd June 6, 1993 +.Dd May 2, 2001 .Dt LAST 1 .Os BSD 4 .Sh NAME @@ -41,6 +41,15 @@ .Sh SYNOPSIS .Nm .Op Fl Ns Ar n +.Oo +.Fl d +.Sm off +.Op Oo Ar CC Oc Ar YY +.Op Ar MM DD +.Ar hh mm +.Op Ar .SS +.Sm on +.Oc .Op Fl f Ar file .Op Fl h Ar host .Op Fl s @@ -49,12 +58,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 +79,65 @@ 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 +.Sm off +.Op Oo Ar CC Oc Ar YY +.Op Ar MM DD +.Ar hh mm +.Op Ar .SS +.Sm on +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 +.Ar YY +is specified, but +.Ar CC +is not, a value for +.Ar YY +between 69 and 99 results in a +.Ar CC +value of 19. +Otherwise, a +.Ar 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 +.Ar CC +and +.Ar YY +letter pairs are not specified, the values default to the current +year. +If the +.Ar SS +letter pair is not specified, the value defaults to 0. .It Fl f Ar file .Nm Last reads the file @@ -94,8 +163,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/05/03 03:34:51 @@ -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 -- >Release-Note: >Audit-Trail: >Unformatted: To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-bugs" in the body of the message