Index: lastlogin.8 =================================================================== RCS file: /cvsroot/src/usr.sbin/lastlogin/lastlogin.8,v retrieving revision 1.12 diff -u -r1.12 lastlogin.8 --- lastlogin.8 8 Apr 2009 14:20:38 -0000 1.12 +++ lastlogin.8 6 May 2020 11:55:45 -0000 @@ -30,7 +30,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd March 4, 2005 +.Dd May 6, 2020 .Dt LASTLOGIN 8 .Os .Sh NAME @@ -38,7 +38,7 @@ .Nd indicate last login time of users .Sh SYNOPSIS .Nm -.Op Fl nrt +.Op Fl Fnrt .Op Fl f Ar filename .Op Fl H Ar hostsize .Op Fl L Ar linesize @@ -66,6 +66,8 @@ .Pp The following options are available: .Bl -tag -width indent +.It Fl F +Use fixed widths for all output fields. .It Fl f Ar filename Process input from .Ar filename . Index: lastlogin.c =================================================================== RCS file: /cvsroot/src/usr.sbin/lastlogin/lastlogin.c,v retrieving revision 1.15 diff -u -r1.15 lastlogin.c --- lastlogin.c 31 Aug 2011 13:31:29 -0000 1.15 +++ lastlogin.c 6 May 2020 11:55:45 -0000 @@ -57,12 +57,31 @@ #include #include +#ifndef UT_NAMESIZE +# define UT_NAMESIZE 8 +#endif +#ifndef UT_LINESIZE +# define UT_LINESIZE 8 +#endif +#ifndef UT_HOSTSIZE +# define UT_HOSTSIZE 16 +#endif + +#ifndef UTX_USERSIZE +# define UTX_USERSIZE 64 +#endif +#ifndef UTX_LINESIZE +# define UTX_LINESIZE 64 +#endif +#ifndef UTX_HOSTSIZE +# define UTX_HOSTSIZE 256 +#endif + struct output { struct timeval o_tv; - struct sockaddr_storage o_ss; - char o_name[64]; - char o_line[64]; - char o_host[256]; + char o_name[UTX_USERSIZE+1]; + char o_line[UTX_LINESIZE+1]; + char o_host[UTX_HOSTSIZE+1]; struct output *next; }; @@ -73,24 +92,35 @@ static int sortlog = SORT_NONE; static struct output *outstack = NULL; +static int fixed = 0; +#define FIXED_NAMELEN UT_NAMESIZE +#define FIXED_LINELEN UT_LINESIZE +/* + * This makes the "fixed" output fit in 79 columns. + * Using UT_HOSTSIZE (16) seems too conservative. + */ +#define FIXED_HOSTLEN 32 + static int numeric = 0; -static size_t namelen = UT_NAMESIZE; -static size_t linelen = UT_LINESIZE; -static size_t hostlen = UT_HOSTSIZE; +static size_t namelen = 0; +static size_t linelen = 0; +static size_t hostlen = 0; +#define SIZECOLUMNS (!(namelen && linelen && hostlen)) -static int comparelog(const void *, const void *); -static void output(struct output *); +static int comparelog(const void *, const void *); +static void output_record(struct output *); #ifdef SUPPORT_UTMP -static void process_entry(struct passwd *, struct lastlog *); -static void dolastlog(const char *, int, char *[]); +static void process_entry(struct passwd *, struct lastlog *); +static void dolastlog(const char *, int, char *[]); #endif #ifdef SUPPORT_UTMPX -static void process_entryx(struct passwd *, struct lastlogx *); -static void dolastlogx(const char *, int, char *[]); +static void process_entryx(struct passwd *, struct lastlogx *); +static void dolastlogx(const char *, int, char *[]); #endif -static void push(struct output *); -static const char *gethost(struct output *); -static void sortoutput(struct output *); +static void push_record(struct output *); +static void sizecolumns(struct output *); +static void output_stack(struct output *); +static void sort_and_output_stack(struct output *); __dead static void usage(void); int @@ -107,7 +137,7 @@ int ch; size_t len; - while ((ch = getopt(argc, argv, "f:H:L:nN:rt")) != -1) { + while ((ch = getopt(argc, argv, "f:FH:L:nN:rt")) != -1) { switch (ch) { case 'H': hostlen = atoi(optarg); @@ -115,6 +145,9 @@ case 'f': logfile = optarg; break; + case 'F': + fixed++; + break; case 'L': linelen = atoi(optarg); break; @@ -137,6 +170,15 @@ argc -= optind; argv += optind; + if (fixed) { + if (!namelen) + namelen = FIXED_NAMELEN; + if (!linelen) + linelen = FIXED_LINELEN; + if (!hostlen) + hostlen = FIXED_HOSTLEN; + } + len = strlen(logfile); setpassent(1); /* Keep passwd file pointers open */ @@ -152,8 +194,15 @@ setpassent(0); /* Close passwd file pointers */ - if (outstack && DOSORT(sortlog)) - sortoutput(outstack); + if (outstack) { + if (SIZECOLUMNS) + sizecolumns(outstack); + + if (DOSORT(sortlog)) + sort_and_output_stack(outstack); + else + output_stack(outstack); + } return 0; } @@ -224,18 +273,17 @@ (void)strlcpy(o.o_host, l->ll_host, sizeof(l->ll_host)); o.o_tv.tv_sec = l->ll_time; o.o_tv.tv_usec = 0; - (void)memset(&o.o_ss, 0, sizeof(o.o_ss)); o.next = NULL; /* - * If we are sorting it, we need all the entries in memory so - * push the current entry onto a stack. Otherwise, we can just - * output it. + * If we are dynamically sizing the columns or sorting the log, + * we need all the entries in memory so push the current entry + * onto a stack. Otherwise, we can just output it. */ - if (DOSORT(sortlog)) - push(&o); + if (SIZECOLUMNS || DOSORT(sortlog)) + push_record(&o); else - output(&o); + output_record(&o); } #endif @@ -343,25 +391,28 @@ else (void)strlcpy(o.o_name, p->pw_name, sizeof(o.o_name)); (void)strlcpy(o.o_line, l->ll_line, sizeof(l->ll_line)); - (void)strlcpy(o.o_host, l->ll_host, sizeof(l->ll_host)); - (void)memcpy(&o.o_ss, &l->ll_ss, sizeof(o.o_ss)); + if (!numeric) + (void)strlcpy(o.o_host, l->ll_host, sizeof(l->ll_host)); + else + (void)sockaddr_snprintf(o.o_host, sizeof(o.o_host), "%a", + (struct sockaddr *)&l->ll_ss); o.o_tv = l->ll_tv; o.next = NULL; /* - * If we are sorting it, we need all the entries in memory so - * push the current entry onto a stack. Otherwise, we can just - * output it. + * If we are dynamically sizing the columns or sorting the log, + * we need all the entries in memory so push the current entry + * onto a stack. Otherwise, we can just output it. */ - if (DOSORT(sortlog)) - push(&o); + if (SIZECOLUMNS || DOSORT(sortlog)) + push_record(&o); else - output(&o); + output_record(&o); } #endif static void -push(struct output *o) +push_record(struct output *o) { struct output *out; @@ -380,7 +431,44 @@ } static void -sortoutput(struct output *o) +sizecolumns(struct output *stack) +{ + struct output *o; + size_t len; + + if (!namelen) + for (o = stack; o; o = o->next) { + len = strlen(o->o_name); + if (namelen < len) + namelen = len; + } + + if (!linelen) + for (o = stack; o; o = o->next) { + len = strlen(o->o_line); + if (linelen < len) + linelen = len; + } + + if (!hostlen) + for (o = stack; o; o = o->next) { + len = strlen(o->o_host); + if (hostlen < len) + hostlen = len; + } +} + +static void +output_stack(struct output *stack) +{ + struct output *o; + + for (o = stack; o; o = o->next) + output_record(o); +} + +static void +sort_and_output_stack(struct output *o) { struct output **outs; struct output *tmpo; @@ -388,7 +476,7 @@ int i; /* count the number of entries to display */ - for (num=0, tmpo = o; tmpo; tmpo=tmpo->next, num++) + for (num=0, tmpo = o; tmpo; tmpo = tmpo->next, num++) ; outs = malloc(sizeof(*outs) * num); @@ -400,7 +488,7 @@ mergesort(outs, num, sizeof(*outs), comparelog); for (i=0; i < num; i++) - output(outs[i]); + output_record(outs[i]); } static int @@ -417,29 +505,15 @@ return -1 * order; } -static const char * -gethost(struct output *o) -{ - if (!numeric) - return o->o_host; - else { - static char buf[512]; - buf[0] = '\0'; - (void)sockaddr_snprintf(buf, sizeof(buf), "%a", - (struct sockaddr *)&o->o_ss); - return buf; - } -} - /* Duplicate the output of last(1) */ static void -output(struct output *o) +output_record(struct output *o) { time_t t = (time_t)o->o_tv.tv_sec; - printf("%-*.*s %-*.*s %-*.*s %s", + printf("%-*.*s %-*.*s %-*.*s %s", (int)namelen, (int)namelen, o->o_name, (int)linelen, (int)linelen, o->o_line, - (int)hostlen, (int)hostlen, gethost(o), + (int)hostlen, (int)hostlen, o->o_host, t ? ctime(&t) : "Never logged in\n"); }