misc
authorAlex Hornung <ahornung@gmail.com>
Sun, 5 Dec 2010 09:20:00 +0000 (09:20 +0000)
committerAlex Hornung <ahornung@gmail.com>
Wed, 15 Dec 2010 17:46:04 +0000 (17:46 +0000)
include/utmp.h
lib/libc/gen/Makefile.inc
lib/libc/gen/utmp.c [new file with mode: 0644]
lib/pam_module/pam_lastlog/pam_lastlog.c
usr.bin/w/Makefile
usr.bin/w/w.c
usr.bin/who/Makefile
usr.bin/who/utmpentry.c [new file with mode: 0644]
usr.bin/who/utmpentry.h [new file with mode: 0644]
usr.bin/who/who.c

index 6431f8c..7316087 100644 (file)
@@ -64,4 +64,9 @@ struct utmp {
        time_t  ut_time;
 };
 
+int utmpname(const char *);
+void setutent(void);
+struct utmp *getutent(void);
+void endutent(void);
+
 #endif /* !_UTMP_H_ */
index faf62a1..8756c5a 100644 (file)
@@ -36,7 +36,7 @@ SRCS+=  _pthread_stubs.c _rand48.c _spinlock_stub.c _thread_init.c \
        sysctlnametomib.c syslog.c telldir.c termios.c time.c times.c \
        toascii.c tolower.c toupper.c ttyname.c ttyslot.c \
        ualarm.c ucontext.c ulimit.c uname.c unvis.c usleep.c utime.c \
-       utmpx.c \
+       utmp.c utmpx.c \
        valloc.c vis.c wait.c wait3.c waitpid.c wordexp.c
 
 .if ${LIB} != {c_rtld}
diff --git a/lib/libc/gen/utmp.c b/lib/libc/gen/utmp.c
new file mode 100644 (file)
index 0000000..6446d73
--- /dev/null
@@ -0,0 +1,102 @@
+/*     $NetBSD: utmp.c,v 1.8 2009/01/11 02:46:27 christos Exp $         */
+
+/*-
+ * Copyright (c) 2002 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <sys/cdefs.h>
+
+#include "namespace.h"
+#include <sys/types.h>
+#include <sys/param.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <utmp.h>
+#include <sys/stat.h>
+
+static struct utmp utmp;
+static FILE *ut;
+static char utfile[MAXPATHLEN] = _PATH_UTMP;
+
+void
+setutent(void)
+{
+       if (ut == NULL)
+               return;
+       (void)fseeko(ut, (off_t)0, SEEK_SET);
+}
+
+struct utmp *
+getutent(void)
+{
+       if (ut == NULL) {
+               struct stat st;
+               off_t numentries;
+               if ((ut = fopen(utfile, "r")) == NULL)
+                       return NULL;
+               if (fstat(fileno(ut), &st) == -1)
+                       goto out;
+               /*
+                * If we have a an old version utmp file bail.
+                */
+               numentries = st.st_size / sizeof(utmp);
+               if ((off_t)(numentries * sizeof(utmp)) != st.st_size)
+                       goto out;
+       }
+       if (fread(&utmp, sizeof(utmp), 1, ut) == 1)
+               return &utmp;
+out:
+       (void)fclose(ut);
+       return NULL;
+}
+
+void
+endutent(void)
+{
+       if (ut != NULL) {
+               (void)fclose(ut);
+               ut = NULL;
+       }
+}
+
+int
+utmpname(const char *fname)
+{
+       size_t len = strlen(fname);
+
+       if (len >= sizeof(utfile))
+               return 0;
+
+       /* must not end in x! */
+       if (fname[len - 1] == 'x')
+               return 0;
+
+       (void)strlcpy(utfile, fname, sizeof(utfile));
+       endutent();
+       return 1;
+}
index ab59265..3507813 100644 (file)
@@ -95,7 +95,7 @@ logit(int level, const char *fmt, ...)
 
        openlog("pam_lastlog", LOG_PID, LOG_AUTHPRIV);
        va_start(ap, fmt);
-       vsyslog_r(level, fmt, ap);
+       vsyslog(level, fmt, ap);
        va_end(ap);
        closelog();
 }
@@ -164,8 +164,8 @@ pam_sm_open_session(pam_handle_t *pamh, int flags,
 #endif
        }
 #ifdef SUPPORT_UTMPX
-       doutmpx(user, rhost, tty, ss, &now);
-       dolastlogx(pamh, quiet, pwd, rhost, tty, ss, &now);
+       doutmpx(user, rhost, tty, NULL, &now);
+       dolastlogx(pamh, quiet, pwd, rhost, tty, NULL, &now);
        quiet = 1;
 #endif
 #ifdef SUPPORT_UTMP
index fa49280..2e668bc 100644 (file)
@@ -12,6 +12,7 @@ LDADD=        -lkvm
 LINKS= ${BINDIR}/w ${BINDIR}/uptime
 WARNS?=        2
 
+CFLAGS+=       -DSUPPORT_UTMP -DSUPPORT_UTMPX
 .PATH: ${.CURDIR}/../../bin/ps
 
 .include <bsd.prog.mk>
index c208f5e..5830767 100644 (file)
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#ifdef SUPPORT_UTMP
 #include <utmp.h>
+#endif
+#ifdef SUPPORT_UTMPX
+#include <utmpx.h>
+#endif
 #include <vis.h>
 
 #include <arpa/nameser.h>
@@ -83,6 +88,7 @@ struct utmp   utmp;
 struct winsize ws;
 kvm_t         *kd;
 time_t         now;            /* the current time of day */
+time_t         then;
 time_t         uptime;         /* time of last reboot & elapsed time since */
 int            ttywidth;       /* width of tty */
 int            argwidth;       /* width of tty */
@@ -94,24 +100,33 @@ int                use_ampm;       /* use AM/PM time */
 int             use_comma;      /* use comma as floats separator */
 char         **sel_users;      /* login array of particular users selected */
 char           domain[MAXHOSTNAMELEN];
+int maxname = 8, maxline = 3, maxhost = 16;
 
 /*
  * One of these per active utmp entry.
  */
 struct entry {
        struct  entry *next;
-       struct  utmp utmp;
+       char name[UTX_USERSIZE + 1];
+       char line[UTX_LINESIZE + 1];
+       char host[UTX_HOSTSIZE + 1];
+       char type[2];
+       struct timeval tv;
        dev_t   tdev;                   /* dev_t of terminal */
        time_t  idle;                   /* idle time of terminal in seconds */
        struct  kinfo_proc *kp;         /* `most interesting' proc */
        char    *args;                  /* arg list of interesting process */
        struct  kinfo_proc *dkp;        /* debug option proc list */
+       pid_t   pid;                    /* pid or ~0 if not known */
 } *ep, *ehead = NULL, **nextp = &ehead;
 
 #define debugproc(p) *((struct kinfo_proc **)&(p)->kp_spare[0])
 
 static void             pr_header(time_t *, int);
+#if defined(SUPPORT_UTMP) || defined(SUPPORT_UTMPX)
 static struct stat     *ttystat(char *, int);
+static void    process(struct entry *);
+#endif
 static void             usage(int);
 static int              this_is_uptime(const char *s);
 
@@ -124,11 +139,15 @@ main(int argc, char **argv)
        struct kinfo_proc *dkp;
        struct hostent *hp;
        struct stat *stp;
-       FILE *ut;
        in_addr_t l;
-       time_t touched;
        int ch, i, nentries, nusers, wcmd, longidle, dropgid;
        char *memf, *nlistf, *p, *x;
+#ifdef SUPPORT_UTMP
+       struct utmp *ut;
+#endif
+#ifdef SUPPORT_UTMPX
+       struct utmpx *utx;
+#endif
        char buf[MAXHOSTNAMELEN], errbuf[_POSIX2_LINE_MAX];
 
        (void)setlocale(LC_ALL, "");
@@ -195,62 +214,118 @@ main(int argc, char **argv)
                errx(1, "%s", errbuf);
 
        (void)time(&now);
-       if ((ut = fopen(_PATH_UTMP, "r")) == NULL)
-               err(1, "%s", _PATH_UTMP);
+#ifdef SUPPORT_UTMPX
+       setutxent();
+#endif
+#ifdef SUPPORT_UTMP
+       setutent();
+#endif
 
        if (*argv)
                sel_users = argv;
 
-       for (nusers = 0; fread(&utmp, sizeof(utmp), 1, ut);) {
-               if (utmp.ut_name[0] == '\0')
+       nusers = 0;
+#ifdef SUPPORT_UTMPX
+       while ((utx = getutxent()) != NULL) {
+               if (utx->ut_type != USER_PROCESS)
                        continue;
-               if (!(stp = ttystat(utmp.ut_line, UT_LINESIZE)))
-                       continue;       /* corrupted record */
                ++nusers;
-               if (wcmd == 0)
-                       continue;
+
                if (sel_users) {
                        int usermatch;
                        char **user;
 
                        usermatch = 0;
                        for (user = sel_users; !usermatch && *user; user++)
-                               if (!strncmp(utmp.ut_name, *user, UT_NAMESIZE))
+                               if (!strncmp(utx->ut_name, *user, UT_NAMESIZE))
                                        usermatch = 1;
                        if (!usermatch)
                                continue;
                }
+
                if ((ep = calloc(1, sizeof(struct entry))) == NULL)
-                       errx(1, "calloc");
-               *nextp = ep;
-               nextp = &ep->next;
-               memmove(&ep->utmp, &utmp, sizeof(struct utmp));
-               ep->tdev = stp->st_rdev;
-#ifdef CPU_CONSDEV
-               /*
-                * If this is the console device, attempt to ascertain
-                * the true console device dev_t.
-                */
-               if (ep->tdev == 0) {
-                       int mib[2];
-                       size_t size;
-
-                       mib[0] = CTL_MACHDEP;
-                       mib[1] = CPU_CONSDEV;
-                       size = sizeof(dev_t);
-                       (void)sysctl(mib, 2, &ep->tdev, &size, NULL, 0);
+                       err(1, NULL);
+               (void)memcpy(ep->name, utx->ut_name, sizeof(utx->ut_name));
+               (void)memcpy(ep->line, utx->ut_line, sizeof(utx->ut_line));
+               (void)memcpy(ep->host, utx->ut_host, sizeof(utx->ut_host));
+               ep->name[sizeof(utx->ut_name)] = '\0';
+               ep->line[sizeof(utx->ut_line)] = '\0';
+               ep->host[sizeof(utx->ut_host)] = '\0';
+#if 1
+               /* XXX: Actually we don't support the utx->ut_ss stuff yet */
+               if (!nflag || getnameinfo((struct sockaddr *)&utx->ut_ss,
+                   utx->ut_ss.ss_len, ep->host, sizeof(ep->host), NULL, 0,
+                   NI_NUMERICHOST) != 0) {
+                       (void)memcpy(ep->host, utx->ut_host,
+                           sizeof(utx->ut_host));
+                       ep->host[sizeof(utx->ut_host)] = '\0';
                }
 #endif
-               touched = stp->st_atime;
-               if (touched < ep->utmp.ut_time) {
-                       /* tty untouched since before login */
-                       touched = ep->utmp.ut_time;
+               ep->type[0] = 'x';
+               ep->tv = utx->ut_tv;
+               ep->pid = utx->ut_pid;
+
+               *nextp = ep;
+               nextp = &(ep->next);
+               if (wcmd != 0)
+                       process(ep);
+       }
+#endif
+
+#ifdef SUPPORT_UTMP
+       while ((ut = getutent()) != NULL) {
+               if (ut->ut_name[0] == '\0')
+                       continue;
+               if (!(stp = ttystat(ut->ut_line, UT_LINESIZE)))
+                       continue;       /* corrupted record */
+               ++nusers;
+               if (sel_users) {
+                       int usermatch;
+                       char **user;
+
+                       usermatch = 0;
+                       for (user = sel_users; !usermatch && *user; user++)
+                               if (!strncmp(ut->ut_name, *user, UT_NAMESIZE))
+                                       usermatch = 1;
+                       if (!usermatch)
+                               continue;
                }
-               if ((ep->idle = now - touched) < 0)
-                       ep->idle = 0;
+
+               /* Don't process entries that we have utmpx for */
+               for (ep = ehead; ep != NULL; ep = ep->next) {
+                       if (strncmp(ep->line, ut->ut_line,
+                           sizeof(ut->ut_line)) == 0)
+                               break;
+               }
+               if (ep != NULL) {
+                       --nusers; /* Duplicate entry */
+                       continue;
+               }
+
+               if ((ep = calloc(1, sizeof(struct entry))) == NULL)
+                       err(1, NULL);
+               (void)memcpy(ep->name, ut->ut_name, sizeof(ut->ut_name));
+               (void)memcpy(ep->line, ut->ut_line, sizeof(ut->ut_line));
+               (void)memcpy(ep->host, ut->ut_host, sizeof(ut->ut_host));
+               ep->name[sizeof(ut->ut_name)] = '\0';
+               ep->line[sizeof(ut->ut_line)] = '\0';
+               ep->host[sizeof(ut->ut_host)] = '\0';
+               ep->tv.tv_sec = ut->ut_time;
+
+               *nextp = ep;
+               nextp = &(ep->next);
+               if (wcmd != 0)
+                       process(ep);
        }
-       (void)fclose(ut);
+#endif
 
+#ifdef SUPPORT_UTMPX
+       endutxent();
+#endif
+#ifdef SUPPORT_UTMP
+       endutent();
+#endif 
+       
        if (header || wcmd == 0) {
                pr_header(&now, nusers);
                if (wcmd == 0) {
@@ -266,9 +341,9 @@ main(int argc, char **argv)
 #define WUSED  (UT_NAMESIZE + UT_LINESIZE + UT_HOSTSIZE + \
                sizeof(HEADER_LOGIN_IDLE) + 3)  /* header width incl. spaces */ 
                (void)printf("%-*.*s %-*.*s %-*.*s  %s", 
-                               UT_NAMESIZE, UT_NAMESIZE, HEADER_USER,
-                               UT_LINESIZE, UT_LINESIZE, HEADER_TTY,
-                               UT_HOSTSIZE, UT_HOSTSIZE, HEADER_FROM,
+                               maxname, maxname, HEADER_USER,
+                               maxline, maxline, HEADER_TTY,
+                               maxhost, maxhost, HEADER_FROM,
                                HEADER_LOGIN_IDLE HEADER_WHAT);
        }
 
@@ -299,6 +374,10 @@ main(int argc, char **argv)
                                ep->dkp = kp;
                                debugproc(kp) = dkp;
                        }
+                       if (ep->pid != 0 && ep->pid == kp->kp_pid) {
+                               ep->kp = kp;
+                               break;
+                       }
                }
        }
        if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 &&
@@ -337,6 +416,23 @@ main(int argc, char **argv)
                        *nextp = save;
                }
        }
+#if defined(SUPPORT_UTMP) && defined(SUPPORT_UTMPX)
+       else if (ehead != NULL) {
+               struct entry *from = ehead, *save;
+
+               ehead = NULL;
+               while (from != NULL) {
+                       for (nextp = &ehead;
+                           (*nextp) && strcmp(from->line, (*nextp)->line) > 0;
+                           nextp = &(*nextp)->next)
+                               continue;
+                       save = from;
+                       from = from->next;
+                       save->next = *nextp;
+                       *nextp = save;
+               }
+       }
+#endif
 
        if (!nflag) {
                if (gethostname(domain, sizeof(domain)) < 0 ||
@@ -352,7 +448,7 @@ main(int argc, char **argv)
                char host_buf[UT_HOSTSIZE + 1];
 
                host_buf[UT_HOSTSIZE] = '\0';
-               strncpy(host_buf, ep->utmp.ut_host, UT_HOSTSIZE);
+               strncpy(host_buf, ep->host, maxhost);
                p = *host_buf ? host_buf : "-";
                if ((x = strchr(p, ':')) != NULL)
                        *x++ = '\0';
@@ -396,13 +492,14 @@ main(int argc, char **argv)
                        }
                }
                (void)printf("%-*.*s %-*.*s %-*.*s ",
-                   UT_NAMESIZE, UT_NAMESIZE, ep->utmp.ut_name,
-                   UT_LINESIZE, UT_LINESIZE,
-                   strncmp(ep->utmp.ut_line, "tty", 3) &&
-                   strncmp(ep->utmp.ut_line, "cua", 3) ?
-                   ep->utmp.ut_line : ep->utmp.ut_line + 3,
-                   UT_HOSTSIZE, UT_HOSTSIZE, *p ? p : "-");
-               pr_attime(&ep->utmp.ut_time, &now);
+                   maxname, maxname, ep->name,
+                   maxline, maxline,
+                   strncmp(ep->line, "tty", 3) &&
+                   strncmp(ep->line, "cua", 3) ?
+                   ep->line : ep->line + 3,
+                   maxhost, maxhost, *p ? p : "-");
+               then = (time_t)ep->tv.tv_sec;
+               pr_attime(&then, &now);
                longidle = pr_idle(ep->idle);
                (void)printf("%.*s\n", argwidth - longidle, ep->args);
        }
@@ -516,3 +613,51 @@ this_is_uptime(const char *s)
                return (0);
        return (-1);
 }
+
+#if defined(SUPPORT_UTMP) || defined(SUPPORT_UTMPX)
+static void
+process(struct entry *ep)
+{
+       struct stat *stp;
+       time_t touched;
+       int max;
+
+       if ((max = strlen(ep->name)) > maxname)
+               maxname = max;
+       if ((max = strlen(ep->line)) > maxline)
+               maxline = max;
+       if ((max = strlen(ep->host)) > maxhost)
+               maxhost = max;
+
+       ep->tdev = 0;
+       ep->idle = (time_t)-1;
+
+       if (!(stp = ttystat(ep->line, maxline)))
+               return; /* corrupted record */
+
+       ep->tdev = stp->st_rdev;
+#ifdef CPU_CONSDEV
+       /*
+        * If this is the console device, attempt to ascertain
+        * the true console device dev_t.
+        */
+       if (ep->tdev == 0) {
+               int mib[2];
+               size_t size;
+
+               mib[0] = CTL_MACHDEP;
+               mib[1] = CPU_CONSDEV;
+               size = sizeof(dev_t);
+               (void)sysctl(mib, 2, &ep->tdev, &size, NULL, 0);
+       }
+#endif
+
+       touched = stp->st_atime;
+       if (touched < ep->tv.tv_sec) {
+               /* tty untouched since before login */
+               touched = ep->tv.tv_sec;
+       }
+       if ((ep->idle = now - touched) < 0)
+               ep->idle = 0;
+}
+#endif
index 63c15dd..68d3b1d 100644 (file)
@@ -3,4 +3,8 @@
 
 PROG=  who
 
+SRCS+= who.c utmpentry.c
+
+CFLAGS+=       -DSUPPORT_UTMPX -DSUPPORT_UTMP
+
 .include <bsd.prog.mk>
diff --git a/usr.bin/who/utmpentry.c b/usr.bin/who/utmpentry.c
new file mode 100644 (file)
index 0000000..0c9ba60
--- /dev/null
@@ -0,0 +1,359 @@
+/*     $NetBSD: utmpentry.c,v 1.16 2008/10/28 14:01:46 christos Exp $  */
+
+/*-
+ * Copyright (c) 2002 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+
+#include <sys/stat.h>
+
+#include <time.h>
+#include <string.h>
+#include <err.h>
+#include <stdlib.h>
+
+#ifdef SUPPORT_UTMP
+#include <utmp.h>
+#endif
+#ifdef SUPPORT_UTMPX
+#include <utmpx.h>
+#endif
+
+#include "utmpentry.h"
+
+/* Operations on timespecs. */
+#define        timespecclear(tsp)      (tsp)->tv_sec = (time_t)((tsp)->tv_nsec = 0L)
+#define        timespecisset(tsp)      ((tsp)->tv_sec || (tsp)->tv_nsec)
+#define        timespeccmp(tsp, usp, cmp)                                      \
+       (((tsp)->tv_sec == (usp)->tv_sec) ?                             \
+           ((tsp)->tv_nsec cmp (usp)->tv_nsec) :                       \
+           ((tsp)->tv_sec cmp (usp)->tv_sec))
+#define        timespecadd(tsp, usp, vsp)                                      \
+       do {                                                            \
+               (vsp)->tv_sec = (tsp)->tv_sec + (usp)->tv_sec;          \
+               (vsp)->tv_nsec = (tsp)->tv_nsec + (usp)->tv_nsec;       \
+               if ((vsp)->tv_nsec >= 1000000000L) {                    \
+                       (vsp)->tv_sec++;                                \
+                       (vsp)->tv_nsec -= 1000000000L;                  \
+               }                                                       \
+       } while (/* CONSTCOND */ 0)
+#define        timespecsub(tsp, usp, vsp)                                      \
+       do {                                                            \
+               (vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec;          \
+               (vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec;       \
+               if ((vsp)->tv_nsec < 0) {                               \
+                       (vsp)->tv_sec--;                                \
+                       (vsp)->tv_nsec += 1000000000L;                  \
+               }                                                       \
+       } while (/* CONSTCOND */ 0)
+#define timespec2ns(x) (((uint64_t)(x)->tv_sec) * 1000000000L + (x)->tv_nsec)
+
+
+/* Fail the compile if x is not true, by constructing an illegal type. */
+#define COMPILE_ASSERT(x) /*LINTED null effect */ \
+       ((void)sizeof(struct { unsigned : ((x) ? 1 : -1); }))
+
+
+#ifdef SUPPORT_UTMP
+static void getentry(struct utmpentry *, struct utmp *);
+static struct timespec utmptime = {0, 0};
+#endif
+#ifdef SUPPORT_UTMPX
+static void getentryx(struct utmpentry *, struct utmpx *);
+static struct timespec utmpxtime = {0, 0};
+#endif
+#if defined(SUPPORT_UTMPX) || defined(SUPPORT_UTMP)
+static int setup(const char *);
+static void adjust_size(struct utmpentry *e);
+#endif
+
+int maxname = 8, maxline = 8, maxhost = 16;
+int etype = 1 << USER_PROCESS;
+static int numutmp = 0;
+static struct utmpentry *ehead;
+
+#if defined(SUPPORT_UTMPX) || defined(SUPPORT_UTMP)
+static void
+adjust_size(struct utmpentry *e)
+{
+       int max;
+
+       if ((max = strlen(e->name)) > maxname)
+               maxname = max;
+       if ((max = strlen(e->line)) > maxline)
+               maxline = max;
+       if ((max = strlen(e->host)) > maxhost)
+               maxhost = max;
+}
+
+static int
+setup(const char *fname)
+{
+       int what = 3;
+       struct stat st;
+       const char *sfname;
+
+       if (fname == NULL) {
+#ifdef SUPPORT_UTMPX
+               setutxent();
+#endif
+#ifdef SUPPORT_UTMP
+               setutent();
+#endif
+       } else {
+               size_t len = strlen(fname);
+               if (len == 0)
+                       errx(1, "Filename cannot be 0 length.");
+               what = fname[len - 1] == 'x' ? 1 : 2;
+               if (what == 1) {
+#ifdef SUPPORT_UTMPX
+                       if (utmpxname(fname) == 0)
+                               warnx("Cannot set utmpx file to `%s'",
+                                   fname);
+#else
+                       warnx("utmpx support not compiled in");
+#endif
+               } else {
+#ifdef SUPPORT_UTMP
+                       if (utmpname(fname) == 0)
+                               warnx("Cannot set utmp file to `%s'",
+                                   fname);
+#else
+                       warnx("utmp support not compiled in");
+#endif
+               }
+       }
+#ifdef SUPPORT_UTMPX
+       if (what & 1) {
+               sfname = fname ? fname : _PATH_UTMPX;
+               if (stat(sfname, &st) == -1) {
+                       warn("Cannot stat `%s'", sfname);
+                       what &= ~1;
+               } else {
+                       if (timespeccmp(&st.st_mtimespec, &utmpxtime, >))
+                           utmpxtime = st.st_mtimespec;
+                       else
+                           what &= ~1;
+               }
+       }
+#endif
+#ifdef SUPPORT_UTMP
+       if (what & 2) {
+               sfname = fname ? fname : _PATH_UTMP;
+               if (stat(sfname, &st) == -1) {
+                       warn("Cannot stat `%s'", sfname);
+                       what &= ~2;
+               } else {
+                       if (timespeccmp(&st.st_mtimespec, &utmptime, >))
+                               utmptime = st.st_mtimespec;
+                       else
+                               what &= ~2;
+               }
+       }
+#endif
+       return what;
+}
+#endif
+
+void
+endutentries(void)
+{
+       struct utmpentry *ep;
+
+#ifdef SUPPORT_UTMP
+       timespecclear(&utmptime);
+#endif
+#ifdef SUPPORT_UTMPX
+       timespecclear(&utmpxtime);
+#endif
+       ep = ehead;
+       while (ep) {
+               struct utmpentry *sep = ep;
+               ep = ep->next;
+               free(sep);
+       }
+       ehead = NULL;
+       numutmp = 0;
+}
+
+int
+getutentries(const char *fname, struct utmpentry **epp)
+{
+#ifdef SUPPORT_UTMPX
+       struct utmpx *utx;
+#endif
+#ifdef SUPPORT_UTMP
+       struct utmp *ut;
+#endif
+#if defined(SUPPORT_UTMP) || defined(SUPPORT_UTMPX)
+       struct utmpentry *ep;
+       int what = setup(fname);
+       struct utmpentry **nextp = &ehead;
+       switch (what) {
+       case 0:
+               /* No updates */
+               *epp = ehead;
+               return numutmp;
+       default:
+               /* Need to re-scan */
+               ehead = NULL;
+               numutmp = 0;
+       }
+#endif
+
+#ifdef SUPPORT_UTMPX
+       while ((what & 1) && (utx = getutxent()) != NULL) {
+               if (fname == NULL && ((1 << utx->ut_type) & etype) == 0) {
+                       warnx("woops, debug debug etype");
+                       continue;
+               }
+               if ((ep = calloc(1, sizeof(struct utmpentry))) == NULL) {
+                       warn(NULL);
+                       return 0;
+               }
+               getentryx(ep, utx);
+               *nextp = ep;
+               nextp = &(ep->next);
+       }
+#endif
+
+#ifdef SUPPORT_UTMP
+       if ((etype & (1 << USER_PROCESS)) != 0) {
+               while ((what & 2) && (ut = getutent()) != NULL) {
+                       if (fname == NULL && (*ut->ut_name == '\0' ||
+                           *ut->ut_line == '\0'))
+                               continue;
+                       /* Don't process entries that we have utmpx for */
+                       for (ep = ehead; ep != NULL; ep = ep->next) {
+                               if (strncmp(ep->line, ut->ut_line,
+                                   sizeof(ut->ut_line)) == 0)
+                                       break;
+                       }
+                       if (ep != NULL)
+                               continue;
+                       if ((ep = calloc(1, sizeof(*ep))) == NULL) {
+                               warn(NULL);
+                               return 0;
+                       }
+                       getentry(ep, ut);
+                       *nextp = ep;
+                       nextp = &(ep->next);
+               }
+       }
+#endif
+       numutmp = 0;
+#if defined(SUPPORT_UTMP) || defined(SUPPORT_UTMPX)
+       if (ehead != NULL) {
+               struct utmpentry *from = ehead, *save;
+               
+               ehead = NULL;
+               while (from != NULL) {
+                       for (nextp = &ehead;
+                           (*nextp) && strcmp(from->line, (*nextp)->line) > 0;
+                           nextp = &(*nextp)->next)
+                               continue;
+                       save = from;
+                       from = from->next;
+                       save->next = *nextp;
+                       *nextp = save;
+                       numutmp++;
+               }
+       }
+       *epp = ehead;
+       return numutmp;
+#else
+       *epp = NULL;
+       return 0;
+#endif
+}
+
+#ifdef SUPPORT_UTMP
+static void
+getentry(struct utmpentry *e, struct utmp *up)
+{
+#if 1
+       COMPILE_ASSERT(sizeof(e->name) > sizeof(up->ut_name));
+       COMPILE_ASSERT(sizeof(e->line) > sizeof(up->ut_line));
+       COMPILE_ASSERT(sizeof(e->host) > sizeof(up->ut_host));
+#endif
+
+       /*
+        * e has just been calloc'd. We don't need to clear it or
+        * append null-terminators, because its length is strictly
+        * greater than the source string. Use strncpy to _read_
+        * up->ut_* because they may not be terminated. For this
+        * reason we use the size of the _source_ as the length
+        * argument.
+        */
+       (void)strncpy(e->name, up->ut_name, sizeof(up->ut_name));
+       (void)strncpy(e->line, up->ut_line, sizeof(up->ut_line));
+       (void)strncpy(e->host, up->ut_host, sizeof(up->ut_host));
+
+       e->tv.tv_sec = up->ut_time;
+       e->tv.tv_usec = 0;
+       e->pid = 0;
+       e->term = 0;
+       e->exit = 0;
+       e->sess = 0;
+       e->type = USER_PROCESS;
+       adjust_size(e);
+}
+#endif
+
+#ifdef SUPPORT_UTMPX
+static void
+getentryx(struct utmpentry *e, struct utmpx *up)
+{
+#if 1
+       COMPILE_ASSERT(sizeof(e->name) > sizeof(up->ut_name));
+       COMPILE_ASSERT(sizeof(e->line) > sizeof(up->ut_line));
+       COMPILE_ASSERT(sizeof(e->host) > sizeof(up->ut_host));
+#endif
+
+       /*
+        * e has just been calloc'd. We don't need to clear it or
+        * append null-terminators, because its length is strictly
+        * greater than the source string. Use strncpy to _read_
+        * up->ut_* because they may not be terminated. For this
+        * reason we use the size of the _source_ as the length
+        * argument.
+        */
+       (void)strncpy(e->name, up->ut_name, sizeof(up->ut_name));
+       (void)strncpy(e->line, up->ut_line, sizeof(up->ut_line));
+       (void)strncpy(e->host, up->ut_host, sizeof(up->ut_host));
+
+       e->tv = up->ut_tv;
+       e->pid = up->ut_pid;
+       e->term = up->ut_exit.e_termination;
+       e->exit = up->ut_exit.e_exit;
+       e->sess = up->ut_session;
+       e->type = up->ut_type;
+       adjust_size(e);
+}
+#endif
diff --git a/usr.bin/who/utmpentry.h b/usr.bin/who/utmpentry.h
new file mode 100644 (file)
index 0000000..421f36a
--- /dev/null
@@ -0,0 +1,76 @@
+/*     $NetBSD: utmpentry.h,v 1.6 2008/04/28 20:24:15 martin Exp $     */
+
+/*-
+ * Copyright (c) 2002 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(SUPPORT_UTMPX)
+# include <utmpx.h>
+# define WHO_NAME_LEN          _UTX_USERSIZE
+# define WHO_LINE_LEN          _UTX_LINESIZE
+# define WHO_HOST_LEN          _UTX_HOSTSIZE
+#elif defined(SUPPORT_UTMP)
+# include <utmp.h>
+# define WHO_NAME_LEN          UT_NAMESIZE
+# define WHO_LINE_LEN          UT_LINESIZE
+# define WHO_HOST_LEN          UT_HOSTSIZE
+#else
+# error Either SUPPORT_UTMPX or SUPPORT_UTMP must be defined!
+#endif
+
+
+struct utmpentry {
+       char name[WHO_NAME_LEN + 1];
+       char line[WHO_LINE_LEN + 1];
+       char host[WHO_HOST_LEN + 1];
+       struct timeval tv;
+       pid_t pid;
+       uint16_t term;
+       uint16_t exit;
+       uint16_t sess;
+       uint16_t type;
+       struct utmpentry *next;
+};
+
+extern int maxname, maxline, maxhost;
+extern int etype;
+
+/*
+ * getutentries provides a linked list of struct utmpentry and returns
+ * the number of entries. The first argument, if not null, names an 
+ * alternate utmp(x) file to look in.
+ *
+ * The memory returned by getutentries belongs to getutentries. The
+ * list returned (or elements of it) may be returned again later if
+ * utmp hasn't changed in the meantime.
+ *
+ * endutentries clears and frees the cached data.
+ */
+
+int getutentries(const char *, struct utmpentry **);
+void endutentries(void);
index 1176155..f3de8f2 100644 (file)
@@ -31,6 +31,7 @@
 #include <sys/ioctl.h>
 #include <sys/stat.h>
 #include <sys/sysctl.h>
+#include <sys/time.h>
 
 #include <err.h>
 #include <errno.h>
 #include <string.h>
 #include <time.h>
 #include <unistd.h>
+#ifdef SUPPORT_UTMP
 #include <utmp.h>
+#endif
+#ifdef SUPPORT_UTMPX
+#include <utmpx.h>
+#endif
+
+#include "utmpentry.h"
 
 static void    print_boottime(void);
 static void    heading(void);
-static void    process_utmp(FILE *);
-static void    quick(FILE *);
-static void    row(struct utmp *);
+static void    process(const char *);
+static void    quick(const char *);
+static void    row(struct utmpentry *);
 static int     ttywidth(void);
 static void    usage(void);
-static void    whoami(FILE *);
+static void    whoami(const char *);
 
 static int     bflag;                  /* Show date and time of last reboot */
 static int     Hflag;                  /* Write column headings */
@@ -63,12 +71,35 @@ static int  sflag;                  /* Show name, line, time */
 static int     Tflag;                  /* Show terminal state */
 static int     uflag;                  /* Show idle time */
 
+struct ut_type_names {
+  int type;
+  const char *name;
+} ut_type_names[] = {
+#ifdef SUPPORT_UTMPX
+  { EMPTY, "empty" }, 
+  { RUN_LVL, "run level" }, 
+  { BOOT_TIME, "boot time" }, 
+  { OLD_TIME, "old time" }, 
+  { NEW_TIME, "new time" }, 
+  { INIT_PROCESS, "init process" }, 
+  { LOGIN_PROCESS, "login process" }, 
+  { USER_PROCESS, "user process" }, 
+  { DEAD_PROCESS, "dead process" }, 
+#if defined(_NETBSD_SOURCE)
+  { ACCOUNTING, "accounting" }, 
+  { SIGNATURE, "signature" },
+  { DOWN_TIME, "down time" },
+#endif /* _NETBSD_SOURCE */
+#endif /* SUPPORT_UTMPX */
+  { -1, "unknown" }
+};
+
 int
 main(int argc, char **argv)
 {
        int ch;
        const char *file;
-       FILE *fp;
+       int et = 0;
 
        setlocale(LC_TIME, "");
 
@@ -81,6 +112,9 @@ main(int argc, char **argv)
                        Tflag = 1;
                        break;
                case 'b':               /* Show time and date since last boot */
+#ifdef SUPPORT_UTMPX
+                       et |= (1 << BOOT_TIME);
+#endif
                        bflag = 1;
                        break;
                case 'm':               /* Show info about current terminal */
@@ -103,6 +137,9 @@ main(int argc, char **argv)
        argc -= optind;
        argv += optind;
 
+       if (et != 0)
+               etype = et;
+
        if (argc >= 2 && strcmp(argv[0], "am") == 0 &&
            strcasecmp(argv[1], "i") == 0) {
                /* "who am i" or "who am I", equivalent to -m */
@@ -116,12 +153,10 @@ main(int argc, char **argv)
        if (*argv != NULL)
                file = *argv;
        else
-               file = _PATH_UTMP;
-       if ((fp = fopen(file, "r")) == NULL)
-               err(1, "%s", file);
+               file = NULL;
 
        if (qflag)
-               quick(fp);
+               quick(file);
        else {
                if (sflag)
                        Tflag = uflag = 0;
@@ -130,13 +165,11 @@ main(int argc, char **argv)
                if (Hflag)
                        heading();
                if (mflag)
-                       whoami(fp);
+                       whoami(file);
                else
-                       process_utmp(fp);
+                       process(file);
        }
 
-       fclose(fp);
-
        exit(0);
 }
 
@@ -163,19 +196,19 @@ print_boottime(void)
 static void
 heading(void)
 {
-       printf("%-*s ", UT_NAMESIZE, "NAME");
+       printf("%-*s ", maxname, "NAME");
        if (Tflag)
                printf("S ");
-       printf("%-*s ", UT_LINESIZE, "LINE");
+       printf("%-*s ", maxline, "LINE");
        printf("%-*s ", 12, "TIME");
        if (uflag)
                printf("IDLE  ");
-       printf("%-*s", UT_HOSTSIZE, "FROM");
+       printf("%-*s", maxhost, "FROM");
        putchar('\n');
 }
 
 static void
-row(struct utmp *ut)
+row(struct utmpentry *ut)
 {
        char buf[80], tty[sizeof(_PATH_DEV) + UT_LINESIZE];
        struct stat sb;
@@ -183,13 +216,22 @@ row(struct utmp *ut)
        static int d_first = -1;
        struct tm *tm;
        char state = '?';
+       const char *types = NULL;
+       size_t i;
+
+       for (i = 0; ut_type_names[i].type >= 0; i++) {
+               types = ut_type_names[i].name;
+               if (ut_type_names[i].type == ut->type)
+                       break;
+       }
+       /* XXX: add the detail stuff about types */
 
        if (d_first < 0)
                d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
 
        if (Tflag || uflag) {
                snprintf(tty, sizeof(tty), "%s%.*s", _PATH_DEV,
-                       UT_LINESIZE, ut->ut_line);
+                       maxline, ut->line);
                if (stat(tty, &sb) == 0) {
                        state = sb.st_mode & (S_IWOTH|S_IWGRP) ?
                            '+' : '-';
@@ -199,11 +241,11 @@ row(struct utmp *ut)
                }
        }
 
-       printf("%-*.*s ", UT_NAMESIZE, UT_NAMESIZE, ut->ut_name);
+       printf("%-*.*s ", maxname, maxname, ut->name);
        if (Tflag)
                printf("%c ", state);
-       printf("%-*.*s ", UT_LINESIZE, UT_LINESIZE, ut->ut_line);
-       tm = localtime(&ut->ut_time);
+       printf("%-*.*s ", maxline, maxline, ut->line);
+       tm = localtime(&ut->tv.tv_sec);
        strftime(buf, sizeof(buf), d_first ? "%e %b %R" : "%b %e %R", tm);
        printf("%-*s ", 12, buf);
        if (uflag) {
@@ -215,34 +257,38 @@ row(struct utmp *ut)
                else
                        printf(" old  ");
        }
-       if (*ut->ut_host != '\0')
-               printf("(%.*s)", UT_HOSTSIZE, ut->ut_host);
+       if (*ut->host != '\0')
+               printf("(%.*s)", maxhost, ut->host);
        putchar('\n');
 }
 
 static void
-process_utmp(FILE *fp)
+process(const char *fname)
 {
-       struct utmp ut;
-
-       while (fread(&ut, sizeof(ut), 1, fp) == 1)
-               if (*ut.ut_name != '\0')
-                       row(&ut);
+       struct utmpentry *ehead, *ep;
+
+       getutentries(fname, &ehead);
+#if 0
+       if (show_labels)
+               output_labels();
+#endif
+       for (ep = ehead; ep != NULL; ep = ep->next)
+               row(ep);
 }
 
 static void
-quick(FILE *fp)
+quick(const char *fname)
 {
-       struct utmp ut;
+       struct utmpentry *ehead, *ep;
        int col, ncols, num;
 
        ncols = ttywidth();
        col = num = 0;
-       while (fread(&ut, sizeof(ut), 1, fp) == 1) { 
-               if (*ut.ut_name == '\0')
-                       continue;
-               printf("%-*.*s", UT_NAMESIZE, UT_NAMESIZE, ut.ut_name);
-               if (++col < ncols / (UT_NAMESIZE + 1))
+
+       getutentries(fname, &ehead);
+       for (ep = ehead; ep != NULL; ep = ep->next) {
+               printf("%-*.*s", maxname, maxname, ep->name);
+               if (++col < ncols / (maxname + 1))
                        putchar(' ');
                else {
                        col = 0;
@@ -257,35 +303,39 @@ quick(FILE *fp)
 }
 
 static void
-whoami(FILE *fp)
+whoami(const char *fname)
 {
-       struct utmp ut;
        struct passwd *pwd;
-       const char *name, *p, *tty;
+       const char *name, *tty;
+       struct utmpentry *ehead, *ep, dummy;
 
        if ((tty = ttyname(STDIN_FILENO)) == NULL)
                tty = "tty??";
-       else if ((p = strrchr(tty, '/')) != NULL)
-               tty = p + 1;
+       if (strncmp(tty, _PATH_DEV, sizeof(_PATH_DEV -1)) == 0)
+               tty = tty + sizeof(_PATH_DEV - 1);
 
        /* Search utmp for our tty, dump first matching record. */
-       while (fread(&ut, sizeof(ut), 1, fp) == 1)
-               if (*ut.ut_name != '\0' && strncmp(ut.ut_line, tty,
-                   UT_LINESIZE) == 0) {
-                       row(&ut);
+       (void)getutentries(fname, &ehead);
+       for (ep = ehead; ep; ep = ep->next)
+               if (strcmp(ep->line, tty) == 0) {
+#if 0
+                       if (show_labels)
+                               output_labels();
+#endif
+                       row(ep);
                        return;
                }
 
        /* Not found; fill the utmp structure with the information we have. */
-       memset(&ut, 0, sizeof(ut));
+       memset(&dummy, 0, sizeof(dummy));
        if ((pwd = getpwuid(getuid())) != NULL)
                name = pwd->pw_name;
        else
                name = "?";
-       strncpy(ut.ut_name, name, UT_NAMESIZE);
-       strncpy(ut.ut_line, tty, UT_LINESIZE);
-       time(&ut.ut_time);
-       row(&ut);
+       strncpy(dummy.name, name, UT_NAMESIZE);
+       strncpy(dummy.line, tty, UT_LINESIZE);
+       gettimeofday(&dummy.tv, NULL);
+       row(&dummy);
 }
 
 static int