]> Zhao Yanbai Git Server - minix.git/commitdiff
Import NetBSD w(1) and uptime(1) 68/2768/4
authorDavid van Moolenbroek <david@minix3.org>
Tue, 26 Aug 2014 16:03:05 +0000 (16:03 +0000)
committerDavid van Moolenbroek <david@minix3.org>
Wed, 27 Aug 2014 10:13:37 +0000 (10:13 +0000)
Change-Id: Id6cc36f4befbce4be3a471ae920d75972a44bef1

13 files changed:
bin/ps/extern.h [new file with mode: 0644]
bin/ps/fmt.c [new file with mode: 0644]
bin/ps/ps.h [new file with mode: 0644]
distrib/sets/lists/minix/mi
minix/usr.bin/w/minix_proc.c [new file with mode: 0644]
minix/usr.bin/w/minix_proc.h [new file with mode: 0644]
usr.bin/Makefile
usr.bin/w/Makefile [new file with mode: 0644]
usr.bin/w/extern.h [new file with mode: 0644]
usr.bin/w/pr_time.c [new file with mode: 0644]
usr.bin/w/uptime.1 [new file with mode: 0644]
usr.bin/w/w.1 [new file with mode: 0644]
usr.bin/w/w.c [new file with mode: 0644]

diff --git a/bin/ps/extern.h b/bin/ps/extern.h
new file mode 100644 (file)
index 0000000..401d863
--- /dev/null
@@ -0,0 +1,98 @@
+/*     $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);
diff --git a/bin/ps/fmt.c b/bin/ps/fmt.c
new file mode 100644 (file)
index 0000000..489a0ec
--- /dev/null
@@ -0,0 +1,60 @@
+/*     $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);
+}
diff --git a/bin/ps/ps.h b/bin/ps/ps.h
new file mode 100644 (file)
index 0000000..3db5fc1
--- /dev/null
@@ -0,0 +1,94 @@
+/*     $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"
index 0668fe6f97928d88087df77bd83ca1ad2f7350cf..979e984d4dde01bb6426add2b20c33b4741c97ba 100644 (file)
 ./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
diff --git a/minix/usr.bin/w/minix_proc.c b/minix/usr.bin/w/minix_proc.c
new file mode 100644 (file)
index 0000000..a5d112f
--- /dev/null
@@ -0,0 +1,253 @@
+/* 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;
+}
diff --git a/minix/usr.bin/w/minix_proc.h b/minix/usr.bin/w/minix_proc.h
new file mode 100644 (file)
index 0000000..a216dbb
--- /dev/null
@@ -0,0 +1,47 @@
+#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 */
index 0777053adf4219da4ac2aca32655732ac819728f..910735f8f4659e7cf7438a54759012b1e62979bb 100644 (file)
@@ -29,7 +29,7 @@ SUBDIR= asa \
        tr true tsort tty ul uname unexpand unifdef \
        uniq units unvis unzip users \
        uuidgen vis \
-       \
+       \
        wall wc what whereis who whois \
        write xargs xinstall xstr yes
 
diff --git a/usr.bin/w/Makefile b/usr.bin/w/Makefile
new file mode 100644 (file)
index 0000000..67e1cd4
--- /dev/null
@@ -0,0 +1,24 @@
+#      $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>
diff --git a/usr.bin/w/extern.h b/usr.bin/w/extern.h
new file mode 100644 (file)
index 0000000..8155dc6
--- /dev/null
@@ -0,0 +1,38 @@
+/*     $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);
diff --git a/usr.bin/w/pr_time.c b/usr.bin/w/pr_time.c
new file mode 100644 (file)
index 0000000..a64cb69
--- /dev/null
@@ -0,0 +1,116 @@
+/*     $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));
+}
diff --git a/usr.bin/w/uptime.1 b/usr.bin/w/uptime.1
new file mode 100644 (file)
index 0000000..2301674
--- /dev/null
@@ -0,0 +1,58 @@
+.\"    $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 .
diff --git a/usr.bin/w/w.1 b/usr.bin/w/w.1
new file mode 100644 (file)
index 0000000..6603440
--- /dev/null
@@ -0,0 +1,126 @@
+.\"    $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.
diff --git a/usr.bin/w/w.c b/usr.bin/w/w.c
new file mode 100644 (file)
index 0000000..1516ef3
--- /dev/null
@@ -0,0 +1,692 @@
+/*     $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);
+}