time_t ut_time;
};
+int utmpname(const char *);
+void setutent(void);
+struct utmp *getutent(void);
+void endutent(void);
+
#endif /* !_UTMP_H_ */
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}
--- /dev/null
+/* $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;
+}
openlog("pam_lastlog", LOG_PID, LOG_AUTHPRIV);
va_start(ap, fmt);
- vsyslog_r(level, fmt, ap);
+ vsyslog(level, fmt, ap);
va_end(ap);
closelog();
}
#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
LINKS= ${BINDIR}/w ${BINDIR}/uptime
WARNS?= 2
+CFLAGS+= -DSUPPORT_UTMP -DSUPPORT_UTMPX
.PATH: ${.CURDIR}/../../bin/ps
.include <bsd.prog.mk>
#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>
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 */
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);
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, "");
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) {
#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);
}
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 &&
*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 ||
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';
}
}
(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);
}
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
PROG= who
+SRCS+= who.c utmpentry.c
+
+CFLAGS+= -DSUPPORT_UTMPX -DSUPPORT_UTMP
+
.include <bsd.prog.mk>
--- /dev/null
+/* $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
--- /dev/null
+/* $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);
#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 */
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, "");
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 */
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 */
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;
if (Hflag)
heading();
if (mflag)
- whoami(fp);
+ whoami(file);
else
- process_utmp(fp);
+ process(file);
}
- fclose(fp);
-
exit(0);
}
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;
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) ?
'+' : '-';
}
}
- 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) {
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;
}
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