--- /dev/null
+/* $NetBSD: extern.h,v 1.33 2010/05/31 03:18:33 rmind Exp $ */
+
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * 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.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * @(#)extern.h 8.3 (Berkeley) 4/2/94
+ */
+
+/*
+ * We expect to be included by ps.h, which will already have
+ * defined the types we use.
+ */
+
+extern double ccpu;
+extern int eval, fscale, mempages, nlistread, rawcpu, maxslp, uspace;
+extern int sumrusage, termwidth, totwidth;
+extern int needenv, needcomm, commandonly;
+extern uid_t myuid;
+extern kvm_t *kd;
+extern VAR var[];
+extern VARLIST displaylist;
+extern VARLIST sortlist;
+
+void command(void *, VARENT *, int);
+void cpuid(void *, VARENT *, int);
+void cputime(void *, VARENT *, int);
+int donlist(void);
+int donlist_sysctl(void);
+void fmt_puts(char *, int *);
+void fmt_putc(int, int *);
+void elapsed(void *, VARENT *, int);
+double getpcpu(const struct kinfo_proc2 *);
+double getpmem(const struct kinfo_proc2 *);
+void gname(void *, VARENT *, int);
+void groups(void *, VARENT *, int);
+void groupnames(void *, VARENT *, int);
+void logname(void *, VARENT *, int);
+void longtname(void *, VARENT *, int);
+void lname(void *, VARENT *, int);
+void lstarted(void *, VARENT *, int);
+void lstate(void *, VARENT *, int);
+void maxrss(void *, VARENT *, int);
+void nlisterr(struct nlist *);
+void p_rssize(void *, VARENT *, int);
+void pagein(void *, VARENT *, int);
+void parsefmt(const char *);
+void parsefmt_insert(const char *, VARENT **);
+void parsesort(const char *);
+VARENT * varlist_find(VARLIST *, const char *);
+void emul(void *, VARENT *, int);
+void pcpu(void *, VARENT *, int);
+void pmem(void *, VARENT *, int);
+void pnice(void *, VARENT *, int);
+void pri(void *, VARENT *, int);
+void printheader(void);
+void putimeval(void *, VARENT *, int);
+void pvar(void *, VARENT *, int);
+void rgname(void *, VARENT *, int);
+void rssize(void *, VARENT *, int);
+void runame(void *, VARENT *, int);
+void showkey(void);
+void started(void *, VARENT *, int);
+void state(void *, VARENT *, int);
+void svgname(void *, VARENT *, int);
+void svuname(void *, VARENT *, int);
+void tdev(void *, VARENT *, int);
+void tname(void *, VARENT *, int);
+void tsize(void *, VARENT *, int);
+void ucomm(void *, VARENT *, int);
+void uname(void *, VARENT *, int);
+void uvar(void *, VARENT *, int);
+void vsize(void *, VARENT *, int);
+void wchan(void *, VARENT *, int);
--- /dev/null
+/* $NetBSD: fmt.c,v 1.21 2007/12/12 22:55:43 lukem Exp $ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: fmt.c,v 1.21 2007/12/12 22:55:43 lukem Exp $");
+
+#include <kvm.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <vis.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include "ps.h"
+
+void
+fmt_puts(char *s, int *leftp)
+{
+ static char *v = 0;
+ static int maxlen = 0;
+ char *nv;
+ int len, nlen;
+
+ if (*leftp == 0)
+ return;
+ len = strlen(s) * 4 + 1;
+ if (len > maxlen) {
+ if (maxlen == 0)
+ nlen = getpagesize();
+ else
+ nlen = maxlen;
+ while (len > nlen)
+ nlen *= 2;
+ nv = realloc(v, nlen);
+ if (nv == 0)
+ return;
+ v = nv;
+ maxlen = nlen;
+ }
+ len = strvis(v, s, VIS_TAB | VIS_NL | VIS_CSTYLE);
+ if (*leftp != -1) {
+ if (len > *leftp) {
+ v[*leftp] = '\0';
+ *leftp = 0;
+ } else
+ *leftp -= len;
+ }
+ (void)printf("%s", v);
+}
+
+void
+fmt_putc(int c, int *leftp)
+{
+
+ if (*leftp == 0)
+ return;
+ if (*leftp != -1)
+ *leftp -= 1;
+ putchar(c);
+}
--- /dev/null
+/* $NetBSD: ps.h,v 1.26 2006/10/02 17:54:35 apb Exp $ */
+
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * 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.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * @(#)ps.h 8.1 (Berkeley) 5/31/93
+ */
+
+#include <sys/queue.h>
+
+#define UNLIMITED 0 /* unlimited terminal width */
+
+#define PRINTMODE 0 /* print values */
+#define WIDTHMODE 1 /* determine width of column */
+
+enum type {
+ UNSPECIFIED,
+ CHAR, UCHAR, SHORT, USHORT, INT, UINT, LONG, ULONG,
+ KPTR, KPTR24, INT32, UINT32, SIGLIST, INT64, UINT64,
+ TIMEVAL, CPUTIME, PCPU, VSIZE
+};
+
+/* Variables. */
+typedef SIMPLEQ_HEAD(varlist, varent) VARLIST;
+
+typedef struct varent {
+ SIMPLEQ_ENTRY(varent) next;
+ struct var *var;
+} VARENT;
+
+typedef struct var {
+ const char *name; /* name(s) of variable */
+ const char *header; /* header, possibly changed from default */
+#define COMM 0x01 /* needs exec arguments and environment (XXX) */
+#define ARGV0 0x02 /* only print argv[0] */
+#define LJUST 0x04 /* left adjust on output (trailing blanks) */
+#define INF127 0x08 /* 127 = infinity: if > 127, print 127. */
+#define LWP 0x10 /* dispatch to kinfo_lwp routine */
+#define UAREA 0x20 /* need to check p_uvalid */
+#define ALIAS 0x40 /* entry is alias for 'header' */
+ u_int flag;
+ /* output routine */
+ void (*oproc)(void *, struct varent *, int);
+ /*
+ * The following (optional) elements are hooks for passing information
+ * to the generic output routine: pvar (that which prints simple
+ * elements from struct kinfo_proc2).
+ */
+ int off; /* offset in structure */
+ enum type type; /* type of element */
+ const char *fmt; /* printf format */
+
+ /* current longest element */
+ int width; /* printing width */
+ int64_t longestp; /* longest positive signed value */
+ int64_t longestn; /* longest negative signed value */
+ u_int64_t longestu; /* longest unsigned value */
+ double longestpd; /* longest positive double */
+ double longestnd; /* longest negative double */
+} VAR;
+
+#define OUTPUT(vent, ki, kl, mode) do { \
+ if ((vent)->var->flag & LWP) \
+ ((vent)->var->oproc)((void *)(kl), (vent), (mode)); \
+ else \
+ ((vent)->var->oproc)((void *)(ki), (vent), (mode)); \
+ } while (/*CONSTCOND*/ 0)
+
+#include "extern.h"
./usr/bin/unxz minix-sys
./usr/bin/unzip minix-sys
./usr/bin/update minix-sys
+./usr/bin/uptime minix-sys
./usr/bin/users minix-sys
./usr/bin/uud minix-sys
./usr/bin/uudecode minix-sys
./usr/bin/view minix-sys
./usr/bin/vis minix-sys
./usr/bin/vol minix-sys
+./usr/bin/w minix-sys
./usr/bin/wall minix-sys
./usr/bin/wc minix-sys
./usr/bin/what minix-sys
./usr/man/man1/unvis.1 minix-sys
./usr/man/man1/unxz.1 minix-sys
./usr/man/man1/unzip.1 minix-sys
+./usr/man/man1/uptime.1 minix-sys
./usr/man/man1/users.1 minix-sys
./usr/man/man1/uud.1 minix-sys
./usr/man/man1/uue.1 minix-sys
./usr/man/man1/view.1 minix-sys
./usr/man/man1/vis.1 minix-sys
./usr/man/man1/vol.1 minix-sys
+./usr/man/man1/w.1 minix-sys
./usr/man/man1/wait.1 minix-sys
./usr/man/man1/wall.1 minix-sys
./usr/man/man1/wc.1 minix-sys
--- /dev/null
+/* MINIX3 implementations of a subset of some BSD-kernel-specific functions. */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/fcntl.h>
+#include <unistd.h>
+#include <assert.h>
+#include <dirent.h>
+#include <errno.h>
+
+#include <sys/sysctl.h> /* for KERN_PROC_ */
+
+#include <minix/paths.h>
+#include <minix/procfs.h>
+
+#include "minix_proc.h"
+
+/*
+ * Parse a procfs psinfo file, and fill the given minix_proc structure with the
+ * results. Return 1 on success, or 0 if this process should be skipped.
+ */
+static int
+parse_psinfo(FILE *fp, int op, int __unused arg, pid_t pid,
+ struct minix_proc *p)
+{
+ char type, state, pstate, name[256];
+ unsigned int uid, pgrp, dev;
+ int version;
+
+ assert(op == KERN_PROC_ALL); /* this is all we support right now */
+
+ if (fscanf(fp, "%d", &version) != 1 || version != PSINFO_VERSION)
+ return 0;
+
+ if (fscanf(fp, " %c %*d %255s %c %*d %*d %*u %*u %*u %*u",
+ &type, name, &state) != 3)
+ return 0;
+
+ if (type != TYPE_USER)
+ return 0; /* user processes only */
+
+ if (fscanf(fp, " %*u %*u %*u %c %*d %u %*u %u %*d %*c %*d %u",
+ &pstate, &uid, &pgrp, &dev) != 4)
+ return 0;
+
+ /* The fields as expected by the main w(1) code. */
+ p->p_pid = pid;
+ p->p__pgid = (pid_t)pgrp;
+ p->p_tpgid = (dev != 0) ? (pid_t)pgrp : 0;
+ p->p_tdev = (dev_t)dev;
+ strlcpy(p->p_comm, name, sizeof(p->p_comm));
+
+ /* Some fields we need for ranking ("sorting") processes later. */
+ p->p_minix_state = state;
+ p->p_minix_pstate = pstate;
+
+ return 1;
+}
+
+/*
+ * The w(1)-specific implementation of kvm_getproc2. Return an array of
+ * process information structures (of type minix_proc), along with the number
+ * of processes in the resulting array. Return NULL on failure, in which case
+ * errno should be set to something meaningful.
+ */
+struct minix_proc *
+minix_getproc(void * __unused dummy, int op, int arg, int elemsize, int *cnt)
+{
+ struct minix_proc *procs;
+ char path[PATH_MAX];
+ DIR *dp;
+ FILE *fp;
+ struct dirent *de;
+ pid_t pid, *pids;
+ unsigned int i, npids, size;
+ int e;
+
+ assert(elemsize == sizeof(struct minix_proc));
+
+ /*
+ * First see how much memory we will need in order to store the actual
+ * process data. We store the PIDs in a (relatively small) allocated
+ * memory area immediately, so that we don't have to reiterate through
+ * the /proc directory twice.
+ */
+ if ((dp = opendir(_PATH_PROC)) == NULL)
+ return NULL;
+
+ if ((pids = malloc(size = sizeof(pid_t) * 64)) == NULL) {
+ e = errno;
+ closedir(dp);
+ errno = e;
+ return NULL;
+ }
+ npids = 0;
+
+ while ((de = readdir(dp)) != NULL) {
+ if ((pid = (pid_t)atoi(de->d_name)) > 0) {
+ if (sizeof(pid_t) * npids == size &&
+ (pids = realloc(pids, size *= 2)) == NULL)
+ break;
+ pids[npids++] = pid;
+ }
+ }
+
+ closedir(dp);
+
+ /* No results, or out of memory? Then bail out. */
+ if (npids == 0 || pids == NULL) {
+ if (pids != NULL) {
+ e = errno;
+ free(pids);
+ errno = e;
+ } else
+ errno = ENOENT; /* no processes found */
+ return NULL;
+ }
+
+ /* Now obtain actual process data for the PIDs we obtained. */
+ if ((procs = malloc(sizeof(struct minix_proc) * npids)) == NULL) {
+ e = errno;
+ free(pids);
+ errno = e;
+ return NULL;
+ }
+
+ *cnt = 0;
+ for (i = 0; i < npids; i++) {
+ pid = pids[i];
+
+ snprintf(path, sizeof(path), _PATH_PROC "/%u/psinfo", pid);
+
+ /* Processes may legitimately disappear between calls. */
+ if ((fp = fopen(path, "r")) == NULL)
+ continue;
+
+ if (parse_psinfo(fp, op, arg, pid, &procs[*cnt]))
+ (*cnt)++;
+
+ fclose(fp);
+ }
+
+ free(pids);
+
+ /* The returned data is not freed, but we are called only once. */
+ return procs;
+}
+
+/*
+ * A w(1)-specific MINIX3 implementation of kvm_getargv2. Return an array of
+ * strings representing the command line of the given process, optionally (if
+ * not 0) limited to a number of printable characters if the arguments were
+ * to be printed with a space in between. Return NULL on failure. Since the
+ * caller will not use earlier results after calling this function again, we
+ * can safely return static results.
+ */
+char **
+minix_getargv(void * __unused dummy, const struct minix_proc * p, int nchr)
+{
+#define MAX_ARGS 32
+ static char *argv[MAX_ARGS+1], buf[4096];
+ char path[PATH_MAX];
+ ssize_t i, bytes;
+ int fd, argc;
+
+ /* Get the command line of the process from procfs. */
+ snprintf(path, sizeof(path), _PATH_PROC "/%u/cmdline", p->p_pid);
+
+ if ((fd = open(path, O_RDONLY)) < 0)
+ return NULL;
+
+ bytes = read(fd, buf, sizeof(buf));
+
+ close(fd);
+
+ if (bytes <= 0)
+ return NULL;
+
+ /*
+ * Construct an array of arguments. Stop whenever we run out of bytes
+ * or printable characters (simply counting the null characters as
+ * spaces), or whenever we fill up our argument array. Note that this
+ * is security-sensitive code, as it effectively processes (mostly-)
+ * arbitrary input from other users.
+ */
+ bytes--; /* buffer should/will be null terminated */
+ if (nchr != 0 && bytes > nchr)
+ bytes = nchr;
+ argc = 0;
+ for (i = 0; i < bytes && argc < MAX_ARGS; i++) {
+ if (i == 0 || buf[i-1] == 0)
+ argv[argc++] = &buf[i];
+ }
+ buf[i] = 0;
+ argv[argc] = NULL;
+
+ return argv;
+}
+
+/*
+ * A w(1)-specific implementation of proc_compare_wrapper. Return 0 if the
+ * first given process is more worthy of being shown as the representative
+ * process of what a user is doing, or 1 for the second process. Since procfs
+ * currently does not expose enough information to do this well, we use some
+ * very basic heuristics, and leave a proper implementation to future work.
+ */
+int
+minix_proc_compare(const struct minix_proc * p1, const struct minix_proc * p2)
+{
+ static const int state_prio[] = /* best to worst */
+ { STATE_RUN, STATE_WAIT, STATE_SLEEP, STATE_STOP, STATE_ZOMBIE };
+ unsigned int i;
+ int sp1 = -1, sp2 = -1;
+
+ if (p1 == NULL) return 1;
+ if (p2 == NULL) return 0;
+
+ /*
+ * Pick any runnable candidate over a waiting candidate, any waiting
+ * candidate over a sleeping candidate, etcetera. The rationale is
+ * roughly as follows: runnable means that something is definitely
+ * happening; waiting means that probably something interesting is
+ * happening, which eliminates e.g. shells; sleeping means that not
+ * much is going on; stopped and zombified means that definitely
+ * nothing is going on.
+ */
+ for (i = 0; i < sizeof(state_prio) / sizeof(state_prio[0]); i++) {
+ if (p1->p_minix_state == state_prio[i]) sp1 = i;
+ if (p2->p_minix_state == state_prio[i]) sp2 = i;
+ }
+ if (sp1 != sp2)
+ return (sp1 > sp2);
+
+ /*
+ * Pick any non-PM-sleeping process over any PM-sleeping process.
+ * Here the rationale is that PM-sleeping processes are typically
+ * waiting for another process, which means that the other process is
+ * probably more worthy of reporting. Again, the shell is an example
+ * of a process we'd rather not report if there's something else.
+ */
+ if (sp1 == STATE_SLEEP) {
+ if (p1->p_minix_pstate != PSTATE_NONE) return 1;
+ if (p2->p_minix_pstate != PSTATE_NONE) return 0;
+ }
+
+ /*
+ * Pick the candidate with the largest PID. The rationale is that
+ * statistically that will most likely yield the most recently spawned
+ * process, which makes it the most interesting process as well.
+ */
+ return p1->p_pid < p2->p_pid;
+}
--- /dev/null
+#ifndef _W_MINIX_PROC_H
+#define _W_MINIX_PROC_H
+
+/*
+ * This header file is tailored very specifically to the needs of w(1), which
+ * needs process information but uses a BSD infrastructure (namely, the Kernel
+ * Data Access Library or, eh, KVM.. huh?) which we cannot implement in MINIX3
+ * without making a very, very large number of changes all over the place.
+ *
+ * In order to allow w(1) to function on MINIX3, we override some of the KVM
+ * functionality with MINIX3 implementations, by means of #defines. While that
+ * is indeed butt ugly, this approach does have advantages: we are able to
+ * implement the full expected functionality with minimal changes to w(1)
+ * itself, and whenever w(1) uses a KVM function or a process information field
+ * that this header does not supply, w(1) will break during compilation.
+ */
+
+struct minix_proc {
+ pid_t p_pid; /* process ID */
+ pid_t p__pgid; /* process group ID */
+ pid_t p_tpgid; /* tty process group ID (= p_pgid or 0) */
+ dev_t p_tdev; /* controlling terminal (= tty rdev or 0) */
+ char p_minix_state; /* minix specific: process kernel state */
+ char p_minix_pstate; /* minix specific: process PM state */
+ char p_comm[256]; /* simple command line (= process name) */
+};
+#undef kinfo_proc2
+#define kinfo_proc2 minix_proc
+
+struct minix_proc *minix_getproc(void *dummy, int op, int arg, int elemsize,
+ int *cnt);
+#undef kvm_getproc2
+#define kvm_getproc2 minix_getproc
+
+#undef kvm_geterr
+#define kvm_geterr(dummy) strerror(errno)
+
+char **minix_getargv(void *dummy, const struct minix_proc *p, int nchr);
+#undef kvm_getargv2
+#define kvm_getargv2 minix_getargv
+
+int minix_proc_compare(const struct minix_proc *p1,
+ const struct minix_proc *p2);
+#undef proc_compare_wrapper
+#define proc_compare_wrapper minix_proc_compare
+
+#endif /* !_W_MINIX_PROC_H */
tr true tsort tty ul uname unexpand unifdef \
uniq units unvis unzip users \
uuidgen vis \
- \
+ w \
wall wc what whereis who whois \
write xargs xinstall xstr yes
--- /dev/null
+# $NetBSD: Makefile,v 1.21 2011/10/21 02:26:09 christos Exp $
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+.include <bsd.own.mk>
+
+PROG= w
+SRCS= fmt.c pr_time.c w.c
+MAN= w.1 uptime.1
+.if defined(__MINIX)
+.PATH: ${NETBSDSRCDIR}/minix/usr.bin/w
+SRCS+= minix_proc.c
+CPPFLAGS+= -I${NETBSDSRCDIR}/minix/usr.bin/w
+.else
+DPADD= ${LIBKVM} ${LIBUTIL}
+LDADD= -lkvm -lutil
+.endif
+LINKS= ${BINDIR}/w ${BINDIR}/uptime
+CPPFLAGS+= -DSUPPORT_UTMP -DSUPPORT_UTMPX
+
+.PATH: ${NETBSDSRCDIR}/bin/ps
+
+COPTS.pr_time.c += -Wno-format-y2k
+
+.include <bsd.prog.mk>
--- /dev/null
+/* $NetBSD: extern.h,v 1.7 2011/10/21 02:26:09 christos Exp $ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * 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.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 6/6/93
+ */
+
+struct kinfo_proc2;
+void fmt_puts(char *, int *);
+void fmt_putc(int, int *);
+void pr_attime(time_t *, time_t *);
+void pr_idle(time_t);
--- /dev/null
+/* $NetBSD: pr_time.c,v 1.18 2011/08/17 13:48:11 christos Exp $ */
+
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * 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.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)pr_time.c 8.2 (Berkeley) 4/4/94";
+#else
+__RCSID("$NetBSD: pr_time.c,v 1.18 2011/08/17 13:48:11 christos Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <tzfile.h>
+
+#include "extern.h"
+
+/*
+ * pr_attime --
+ * Print the time since the user logged in.
+ *
+ * Note: SCCS forces the bizarre string manipulation, things like
+ * %I% get replaced in the source code.
+ */
+void
+pr_attime(time_t *started, time_t *now)
+{
+ static char buf[256];
+ int tnow_yday;
+ struct tm *tp;
+ time_t diff;
+
+ tnow_yday = localtime(now)->tm_yday;
+ tp = localtime(started);
+ diff = *now - *started;
+
+ if (diff > SECSPERDAY * DAYSPERWEEK) {
+ /* If more than a week, use day-month-year. */
+ (void)strftime(buf, sizeof(buf), "%d%b%y", tp);
+ } else if (tp->tm_yday != tnow_yday) {
+ /* If not today, use day-hour-am/pm. Damn SCCS */
+ (void)strftime(buf, sizeof(buf), "%a%" "I%p", tp);
+ } else {
+ /* Default is hh:mm{am,pm}. Damn SCCS */
+ (void)strftime(buf, sizeof(buf), "%l:%" "M%p", tp);
+ }
+
+ buf[sizeof(buf) - 1] = '\0';
+ (void)fputs(buf, stdout);
+}
+
+/*
+ * pr_idle --
+ * Display the idle time.
+ */
+void
+pr_idle(time_t idle)
+{
+ int days;
+
+ if (idle == (time_t)-1) {
+ (void)printf(" ? ");
+ return;
+ }
+
+ days = idle / SECSPERDAY;
+
+ /* If idle more than 36 hours, print as a number of days. */
+ if (idle >= 48 * SECSPERHOUR)
+ printf(" %ddays ", days);
+ else if (idle >= 36 * SECSPERHOUR)
+ printf(" 1day ");
+
+ /* If idle more than an hour, print as HH:MM. */
+ else if (idle >= SECSPERHOUR)
+ (void)printf(" %2d:%02d ",
+ (int)(idle / SECSPERHOUR),
+ (int)((idle % SECSPERHOUR) / SECSPERMIN));
+
+ /* Else print the minutes idle. */
+ else
+ (void)printf(" %2d ", (int)(idle / SECSPERMIN));
+}
--- /dev/null
+.\" $NetBSD: uptime.1,v 1.10 2003/08/07 11:17:13 agc Exp $
+.\"
+.\" Copyright (c) 1980, 1990, 1993, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" 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.
+.\" 3. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+.\"
+.\" @(#)uptime.1 8.2 (Berkeley) 4/18/94
+.\"
+.Dd April 18, 1994
+.Dt UPTIME 1
+.Os
+.Sh NAME
+.Nm uptime
+.Nd show how long system has been running
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+The
+.Nm
+utility displays the current time,
+the length of time the system has been up,
+the number of users, and the load average of the system over the last
+1, 5, and 15 minutes.
+.Sh FILES
+.Bl -tag -width /netbsd
+.It Pa /netbsd
+system name list
+.El
+.Sh SEE ALSO
+.Xr w 1
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 3.0 .
--- /dev/null
+.\" $NetBSD: w.1,v 1.18 2005/01/11 09:39:12 wiz Exp $
+.\"
+.\" Copyright (c) 1980, 1990, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" 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.
+.\" 3. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+.\"
+.\" @(#)w.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd January 11, 2005
+.Dt W 1
+.Os
+.Sh NAME
+.Nm w
+.Nd who present users are and what they are doing
+.Sh SYNOPSIS
+.Nm
+.Op Fl hinw
+.Op Fl M Ar core
+.Op Fl N Ar system
+.Op Ar user
+.Sh DESCRIPTION
+The
+.Nm
+utility prints a summary of the current activity on the system,
+including what each user is doing.
+The first line displays the current time of day, how long the system has
+been running, the number of users logged into the system, and the load
+averages.
+The load average numbers give the number of jobs in the run queue averaged
+over 1, 5, and 15 minutes.
+.Pp
+The fields output are the user's login name, the name of the terminal the
+user is on, the host from which the user is logged in, the time the user
+logged on, the time since the user last typed anything,
+and the name and arguments of the current process.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl h
+Suppress the heading.
+.It Fl i
+Output is sorted by idle time.
+.It Fl M
+Extract values associated with the name list from the specified
+core instead of the default
+.Dq /dev/kmem .
+.It Fl N
+Extract the name list from the specified system instead of the
+default
+.Dq /netbsd .
+.It Fl n
+Show network addresses as numbers (normally
+.Nm
+interprets addresses and attempts to display them symbolically).
+.It Fl w
+Show wide output without truncating any fields.
+.El
+.Pp
+If a
+.Ar user
+name is specified, the output is restricted to that user.
+.Sh FILES
+.Bl -tag -width /var/run/utmp -compact
+.It Pa /var/run/utmp
+list of users on the system
+.El
+.Sh SEE ALSO
+.Xr finger 1 ,
+.Xr ps 1 ,
+.Xr uptime 1 ,
+.Xr who 1
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 3.0 .
+.Sh BUGS
+The notion of the
+.Dq current process
+is muddy.
+The current algorithm is ``the highest numbered process on the terminal
+that is not ignoring interrupts, or, if there is none, the highest numbered
+process on the terminal''.
+This fails, for example, in critical sections of programs like the shell
+and editor, or when faulty programs running in the background fork and fail
+to ignore interrupts.
+(In cases where no process can be found,
+.Nm
+prints
+.Dq \- . )
+.Pp
+Background processes are not shown, even though they account for
+much of the load on the system.
+.Pp
+Sometimes processes, typically those in the background, are printed with
+null or garbaged arguments.
+In these cases, the name of the command is printed in parentheses.
+.Pp
+The
+.Nm
+utility does not know about the new conventions for detection of background
+jobs.
+It will sometimes find a background job instead of the right one.
--- /dev/null
+/* $NetBSD: w.c,v 1.77 2013/09/09 19:20:38 christos Exp $ */
+
+/*-
+ * Copyright (c) 1980, 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * 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.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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>
+#ifndef lint
+__COPYRIGHT("@(#) Copyright (c) 1980, 1991, 1993, 1994\
+ The Regents of the University of California. All rights reserved.");
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)w.c 8.6 (Berkeley) 6/30/94";
+#else
+__RCSID("$NetBSD: w.c,v 1.77 2013/09/09 19:20:38 christos Exp $");
+#endif
+#endif /* not lint */
+
+/*
+ * w - print system status (who and what)
+ *
+ * This program is similar to the systat command on Tenex/Tops 10/20
+ *
+ */
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <kvm.h>
+#include <limits.h>
+#include <netdb.h>
+#include <nlist.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <tzfile.h>
+#include <unistd.h>
+#ifdef SUPPORT_UTMP
+#include <utmp.h>
+#endif
+#ifdef SUPPORT_UTMPX
+#include <utmpx.h>
+#endif
+#include <vis.h>
+
+#include "extern.h"
+
+#ifdef __minix
+/* MINIX3 note: please see this header for information about the port. */
+#include "minix_proc.h"
+#endif /* __minix */
+
+struct timeval boottime;
+struct winsize ws;
+kvm_t *kd;
+time_t now; /* the current time of day */
+int ttywidth; /* width of tty */
+int argwidth; /* width of tty left to print process args */
+int header = 1; /* true if -h flag: don't print heading */
+int nflag; /* true if -n flag: don't convert addrs */
+int wflag; /* true if -w flag: wide printout */
+int sortidle; /* sort bu idle time */
+char *sel_user; /* login of particular user selected */
+char domain[MAXHOSTNAMELEN + 1];
+int maxname = 8, maxline = 3, maxhost = 16;
+
+/*
+ * One of these per active utmp entry.
+ */
+struct entry {
+ struct entry *next;
+ 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_proc2 *tp; /* `most interesting' tty proc */
+ struct kinfo_proc2 *pp; /* pid proc */
+ pid_t pid; /* pid or ~0 if not known */
+} *ehead = NULL, **nextp = &ehead;
+
+static void pr_args(struct kinfo_proc2 *);
+static void pr_header(time_t *, int);
+#ifndef __minix
+static int proc_compare_wrapper(const struct kinfo_proc2 *,
+ const struct kinfo_proc2 *);
+#endif /* !__minix */
+#if defined(SUPPORT_UTMP) || defined(SUPPORT_UTMPX)
+static int ttystat(const char *, struct stat *);
+static void process(struct entry *);
+#endif
+static void fixhost(struct entry *ep);
+__dead static void usage(int);
+
+int
+main(int argc, char **argv)
+{
+ struct kinfo_proc2 *kp;
+ struct entry *ep;
+ int ch, i, nentries, nusers, wcmd, curtain, use_sysctl;
+ char *memf, *nlistf, *usrnp;
+ const char *options;
+ time_t then;
+#ifndef __minix
+ size_t len;
+#endif /* !__minix */
+#ifdef SUPPORT_UTMP
+ struct utmp *ut;
+#endif
+#ifdef SUPPORT_UTMPX
+ struct utmpx *utx;
+#endif
+ const char *progname;
+#ifndef __minix
+ char errbuf[_POSIX2_LINE_MAX];
+#endif /* !__minix */
+
+ setprogname(argv[0]);
+
+ /* Are we w(1) or uptime(1)? */
+ progname = getprogname();
+ if (*progname == '-')
+ progname++;
+ if (*progname == 'u') {
+ wcmd = 0;
+ options = "";
+ } else {
+ wcmd = 1;
+ options = "hiM:N:nw";
+ }
+
+ memf = nlistf = NULL;
+ while ((ch = getopt(argc, argv, options)) != -1)
+ switch (ch) {
+ case 'h':
+ header = 0;
+ break;
+ case 'i':
+ sortidle = 1;
+ break;
+ case 'M':
+ header = 0;
+ memf = optarg;
+ break;
+ case 'N':
+ nlistf = optarg;
+ break;
+ case 'n':
+ nflag = 1;
+ break;
+ case 'w':
+ wflag = 1;
+ break;
+ case '?':
+ default:
+ usage(wcmd);
+ }
+ argc -= optind;
+ argv += optind;
+
+ use_sysctl = (memf == NULL && nlistf == NULL);
+
+#ifndef __minix
+ if ((kd = kvm_openfiles(nlistf, memf, NULL,
+ memf == NULL ? KVM_NO_FILES : O_RDONLY, errbuf)) == NULL)
+ errx(1, "%s", errbuf);
+#else
+ if (!use_sysctl)
+ errx(1, "The -M and -N flags are not supported on MINIX3.");
+ kd = NULL;
+#endif /* __minix */
+
+ (void)time(&now);
+
+ if (use_sysctl) {
+#ifndef __minix
+ len = sizeof(curtain);
+ if (sysctlbyname("security.curtain", &curtain, &len,
+ NULL, 0) == -1)
+#endif /* !__minix */
+ curtain = 0;
+ }
+
+#ifdef SUPPORT_UTMPX
+ setutxent();
+#endif
+#ifdef SUPPORT_UTMP
+ setutent();
+#endif
+
+ if (*argv)
+ sel_user = *argv;
+
+ nusers = 0;
+#ifdef SUPPORT_UTMPX
+ while ((utx = getutxent()) != NULL) {
+ if (utx->ut_type != USER_PROCESS)
+ continue;
+ ++nusers;
+ if (sel_user &&
+ strncmp(utx->ut_name, sel_user, sizeof(utx->ut_name)) != 0)
+ continue;
+ if ((ep = calloc(1, sizeof(struct entry))) == NULL)
+ 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));
+ ep->name[sizeof(utx->ut_name)] = '\0';
+ ep->line[sizeof(utx->ut_line)] = '\0';
+ 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';
+ }
+ fixhost(ep);
+ 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 (sel_user &&
+ strncmp(ut->ut_name, sel_user, sizeof(ut->ut_name)) != 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;
+
+ ++nusers;
+ 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';
+ fixhost(ep);
+ ep->tv.tv_sec = ut->ut_time;
+ *nextp = ep;
+ nextp = &(ep->next);
+ if (wcmd != 0)
+ process(ep);
+ }
+#endif
+
+#ifdef SUPPORT_UTMPX
+ endutxent();
+#endif
+#ifdef SUPPORT_UTMP
+ endutent();
+#endif
+
+ if (header || wcmd == 0) {
+ pr_header(&now, nusers);
+ if (wcmd == 0)
+ exit (0);
+ }
+
+ if ((kp = kvm_getproc2(kd, KERN_PROC_ALL, 0,
+ sizeof(struct kinfo_proc2), &nentries)) == NULL)
+ errx(1, "%s", kvm_geterr(kd));
+
+ /* Include trailing space because TTY header starts one column early. */
+ for (i = 0; i < nentries; i++, kp++) {
+
+ for (ep = ehead; ep != NULL; ep = ep->next) {
+ if (ep->tdev != 0 && ep->tdev == kp->p_tdev &&
+ kp->p__pgid == kp->p_tpgid) {
+ /*
+ * Proc is in foreground of this
+ * terminal
+ */
+ if (proc_compare_wrapper(ep->tp, kp))
+ ep->tp = kp;
+ break;
+ }
+ if (ep->pid != 0 && ep->pid == kp->p_pid) {
+ ep->pp = kp;
+ break;
+ }
+ }
+ }
+
+ if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 &&
+ ioctl(STDERR_FILENO, TIOCGWINSZ, &ws) == -1 &&
+ ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) || ws.ws_col == 0)
+ ttywidth = 79;
+ else
+ ttywidth = ws.ws_col - 1;
+
+ if (!wflag && maxhost > (ttywidth / 3))
+ maxhost = ttywidth / 3;
+
+ argwidth = printf("%-*s TTY %-*s %*s IDLE WHAT\n",
+ maxname, "USER", maxhost, "FROM",
+ 7 /* "dddhhXm" */, "LOGIN@");
+ argwidth -= sizeof("WHAT\n") - 1 /* NUL */;
+ argwidth = ttywidth - argwidth;
+ if (argwidth < 4)
+ argwidth = 8;
+ if (wflag)
+ argwidth = -1;
+
+ /* sort by idle time */
+ if (sortidle && ehead != NULL) {
+ struct entry *from = ehead, *save;
+
+ ehead = NULL;
+ while (from != NULL) {
+ for (nextp = &ehead;
+ (*nextp) && from->idle >= (*nextp)->idle;
+ nextp = &(*nextp)->next)
+ continue;
+ save = from;
+ from = from->next;
+ save->next = *nextp;
+ *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) {
+ int rv;
+ char *p;
+
+ rv = gethostname(domain, sizeof(domain));
+ domain[sizeof(domain) - 1] = '\0';
+ if (rv < 0 || (p = strchr(domain, '.')) == 0)
+ domain[0] = '\0';
+ else
+ memmove(domain, p, strlen(p) + 1);
+ }
+
+ for (ep = ehead; ep != NULL; ep = ep->next) {
+ if (ep->tp != NULL)
+ kp = ep->tp;
+ else if (ep->pp != NULL)
+ kp = ep->pp;
+ else if (ep->pid != 0) {
+ if (curtain)
+ kp = NULL;
+ else {
+ warnx("Stale utmp%s entry: %s %s %s",
+ ep->type, ep->name, ep->line, ep->host);
+ continue;
+ }
+ }
+#ifndef __minix
+ usrnp = (kp == NULL) ? ep->name : kp->p_login;
+#else
+ usrnp = ep->name; /* TODO: implement getlogin/setlogin */
+#endif /* __minix */
+ (void)printf("%-*s %-7.7s %-*.*s ",
+ maxname, usrnp, ep->line,
+ maxhost, maxhost, ep->host);
+ then = (time_t)ep->tv.tv_sec;
+ pr_attime(&then, &now);
+ pr_idle(ep->idle);
+ pr_args(kp);
+ (void)printf("\n");
+ }
+ exit(0);
+}
+
+static void
+pr_args(struct kinfo_proc2 *kp)
+{
+ char **argv;
+ int left;
+
+ if (kp == 0)
+ goto nothing;
+ left = argwidth;
+ argv = kvm_getargv2(kd, kp, (argwidth < 0) ? 0 : argwidth);
+ if (argv == 0) {
+ if (kp->p_comm == 0) {
+ goto nothing;
+ } else {
+ fmt_putc('(', &left);
+ fmt_puts((char *)kp->p_comm, &left);
+ fmt_putc(')', &left);
+ return;
+ }
+ }
+ while (*argv) {
+ fmt_puts(*argv, &left);
+ argv++;
+ fmt_putc(' ', &left);
+ }
+ return;
+nothing:
+ putchar('-');
+}
+
+static void
+pr_header(time_t *nowp, int nusers)
+{
+ double avenrun[3];
+ time_t uptime;
+ int days, hrs, mins;
+ int mib[2];
+ size_t size, i;
+ char buf[256];
+
+ /*
+ * Print time of day.
+ *
+ * SCCS forces the string manipulation below, as it replaces
+ * %, M, and % in a character string with the file name.
+ */
+ (void)strftime(buf, sizeof(buf), "%l:%" "M%p", localtime(nowp));
+ buf[sizeof(buf) - 1] = '\0';
+ (void)printf("%s ", buf);
+
+ /*
+ * Print how long system has been up.
+ * (Found by looking getting "boottime" from the kernel)
+ */
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_BOOTTIME;
+ size = sizeof(boottime);
+ if (sysctl(mib, 2, &boottime, &size, NULL, 0) != -1 &&
+ boottime.tv_sec != 0) {
+ uptime = now - boottime.tv_sec;
+ uptime += 30;
+ if (uptime > SECSPERMIN) {
+ days = uptime / SECSPERDAY;
+ uptime %= SECSPERDAY;
+ hrs = uptime / SECSPERHOUR;
+ uptime %= SECSPERHOUR;
+ mins = uptime / SECSPERMIN;
+ (void)printf(" up");
+ if (days > 0)
+ (void)printf(" %d day%s,", days,
+ days > 1 ? "s" : "");
+ if (hrs > 0 && mins > 0)
+ (void)printf(" %2d:%02d,", hrs, mins);
+ else {
+ if (hrs > 0)
+ (void)printf(" %d hr%s,",
+ hrs, hrs > 1 ? "s" : "");
+ if (mins > 0)
+ (void)printf(" %d min%s,",
+ mins, mins > 1 ? "s" : "");
+ }
+ }
+ }
+
+ /* Print number of users logged in to system */
+ (void)printf(" %d user%s", nusers, nusers != 1 ? "s" : "");
+
+ /*
+ * Print 1, 5, and 15 minute load averages.
+ */
+ if (getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0])) == -1)
+ (void)printf(", no load average information available\n");
+ else {
+ (void)printf(", load averages:");
+ for (i = 0; i < (sizeof(avenrun) / sizeof(avenrun[0])); i++) {
+ if (i > 0)
+ (void)printf(",");
+ (void)printf(" %.2f", avenrun[i]);
+ }
+ (void)printf("\n");
+ }
+}
+
+#if defined(SUPPORT_UTMP) || defined(SUPPORT_UTMPX)
+static int
+ttystat(const char *line, struct stat *st)
+{
+ char ttybuf[MAXPATHLEN];
+
+ (void)snprintf(ttybuf, sizeof(ttybuf), "%s%s", _PATH_DEV, line);
+ return stat(ttybuf, st);
+}
+
+static void
+process(struct entry *ep)
+{
+ struct stat st;
+ 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;
+
+#ifdef SUPPORT_UTMP
+ /*
+ * Hack to recognize and correctly parse
+ * ut entry made by ftpd. The "tty" used
+ * by ftpd is not a real tty, just identifier in
+ * form ftpPID. Pid parsed from the "tty name"
+ * is used later to match corresponding process.
+ * NB: This is only used for utmp entries. For utmpx,
+ * we already have the pid.
+ */
+ if (ep->pid == 0 && strncmp(ep->line, "ftp", 3) == 0) {
+ ep->pid = strtol(ep->line + 3, NULL, 10);
+ return;
+ }
+#endif
+ if (ttystat(ep->line, &st) == -1)
+ return;
+
+ ep->tdev = st.st_rdev;
+ /*
+ * 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_KERN;
+ mib[1] = KERN_CONSDEV;
+ size = sizeof(dev_t);
+ (void) sysctl(mib, 2, &ep->tdev, &size, NULL, 0);
+ }
+
+ touched = st.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
+
+#ifndef __minix
+static int
+proc_compare_wrapper(const struct kinfo_proc2 *p1,
+ const struct kinfo_proc2 *p2)
+{
+ struct kinfo_lwp *l1, *l2;
+ int cnt;
+
+ if (p1 == NULL)
+ return 1;
+
+ l1 = kvm_getlwps(kd, p1->p_pid, 0, sizeof(*l1), &cnt);
+ if (l1 == NULL || cnt == 0)
+ return 1;
+
+ l2 = kvm_getlwps(kd, p2->p_pid, 0, sizeof(*l1), &cnt);
+ if (l2 == NULL || cnt == 0)
+ return 0;
+
+ return proc_compare(p1, l1, p2, l2);
+}
+#endif /* !__minix */
+
+static void
+fixhost(struct entry *ep)
+{
+ char host_buf[sizeof(ep->host)];
+ char *p, *x;
+ struct hostent *hp;
+ struct in_addr l;
+
+ strlcpy(host_buf, *ep->host ? ep->host : "-", sizeof(host_buf));
+ p = host_buf;
+
+ /*
+ * XXX: Historical behavior, ':' in hostname means X display number,
+ * IPv6 not handled.
+ */
+ for (x = p; x < &host_buf[sizeof(host_buf)]; x++)
+ if (*x == '\0' || *x == ':')
+ break;
+ if (x == p + sizeof(host_buf) || *x != ':')
+ x = NULL;
+ else
+ *x++ = '\0';
+
+ if (!nflag && inet_aton(p, &l) &&
+ (hp = gethostbyaddr((char *)&l, sizeof(l), AF_INET))) {
+ if (domain[0] != '\0') {
+ p = hp->h_name;
+ p += strlen(hp->h_name);
+ p -= strlen(domain);
+ if (p > hp->h_name &&
+ strcasecmp(p, domain) == 0)
+ *p = '\0';
+ }
+ p = hp->h_name;
+ }
+
+ if (x)
+ (void)snprintf(ep->host, sizeof(ep->host), "%s:%s", p, x);
+ else
+
+ strlcpy(ep->host, p, sizeof(ep->host));
+}
+
+static void
+usage(int wcmd)
+{
+
+ if (wcmd)
+ (void)fprintf(stderr,
+ "Usage: %s [-hinw] [-M core] [-N system] [user]\n",
+ getprogname());
+ else
+ (void)fprintf(stderr, "Usage: %s\n", getprogname());
+ exit(1);
+}