From 11eaad3501b899a8ceddf856c90089fa8ebc1f3d Mon Sep 17 00:00:00 2001 From: David van Moolenbroek Date: Tue, 26 Aug 2014 16:03:05 +0000 Subject: [PATCH] Import NetBSD w(1) and uptime(1) Change-Id: Id6cc36f4befbce4be3a471ae920d75972a44bef1 --- bin/ps/extern.h | 98 +++++ bin/ps/fmt.c | 60 +++ bin/ps/ps.h | 94 +++++ distrib/sets/lists/minix/mi | 4 + minix/usr.bin/w/minix_proc.c | 253 +++++++++++++ minix/usr.bin/w/minix_proc.h | 47 +++ usr.bin/Makefile | 2 +- usr.bin/w/Makefile | 24 ++ usr.bin/w/extern.h | 38 ++ usr.bin/w/pr_time.c | 116 ++++++ usr.bin/w/uptime.1 | 58 +++ usr.bin/w/w.1 | 126 +++++++ usr.bin/w/w.c | 692 +++++++++++++++++++++++++++++++++++ 13 files changed, 1611 insertions(+), 1 deletion(-) create mode 100644 bin/ps/extern.h create mode 100644 bin/ps/fmt.c create mode 100644 bin/ps/ps.h create mode 100644 minix/usr.bin/w/minix_proc.c create mode 100644 minix/usr.bin/w/minix_proc.h create mode 100644 usr.bin/w/Makefile create mode 100644 usr.bin/w/extern.h create mode 100644 usr.bin/w/pr_time.c create mode 100644 usr.bin/w/uptime.1 create mode 100644 usr.bin/w/w.1 create mode 100644 usr.bin/w/w.c diff --git a/bin/ps/extern.h b/bin/ps/extern.h new file mode 100644 index 000000000..401d863ec --- /dev/null +++ b/bin/ps/extern.h @@ -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 index 000000000..489a0ec99 --- /dev/null +++ b/bin/ps/fmt.c @@ -0,0 +1,60 @@ +/* $NetBSD: fmt.c,v 1.21 2007/12/12 22:55:43 lukem Exp $ */ + +#include +__RCSID("$NetBSD: fmt.c,v 1.21 2007/12/12 22:55:43 lukem Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#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 index 000000000..3db5fc178 --- /dev/null +++ b/bin/ps/ps.h @@ -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 + +#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" diff --git a/distrib/sets/lists/minix/mi b/distrib/sets/lists/minix/mi index 0668fe6f9..979e984d4 100644 --- a/distrib/sets/lists/minix/mi +++ b/distrib/sets/lists/minix/mi @@ -538,6 +538,7 @@ ./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 @@ -549,6 +550,7 @@ ./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 @@ -2529,6 +2531,7 @@ ./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 @@ -2537,6 +2540,7 @@ ./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 index 000000000..a5d112fdd --- /dev/null +++ b/minix/usr.bin/w/minix_proc.c @@ -0,0 +1,253 @@ +/* MINIX3 implementations of a subset of some BSD-kernel-specific functions. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include /* for KERN_PROC_ */ + +#include +#include + +#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 index 000000000..a216dbb45 --- /dev/null +++ b/minix/usr.bin/w/minix_proc.h @@ -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 */ diff --git a/usr.bin/Makefile b/usr.bin/Makefile index 0777053ad..910735f8f 100644 --- a/usr.bin/Makefile +++ b/usr.bin/Makefile @@ -29,7 +29,7 @@ SUBDIR= asa \ 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 diff --git a/usr.bin/w/Makefile b/usr.bin/w/Makefile new file mode 100644 index 000000000..67e1cd4f1 --- /dev/null +++ b/usr.bin/w/Makefile @@ -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 + +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 diff --git a/usr.bin/w/extern.h b/usr.bin/w/extern.h new file mode 100644 index 000000000..8155dc6e3 --- /dev/null +++ b/usr.bin/w/extern.h @@ -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 index 000000000..a64cb6962 --- /dev/null +++ b/usr.bin/w/pr_time.c @@ -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 +#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 +#include + +#include +#include +#include +#include + +#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 index 000000000..230167416 --- /dev/null +++ b/usr.bin/w/uptime.1 @@ -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 index 000000000..660344044 --- /dev/null +++ b/usr.bin/w/w.1 @@ -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 index 000000000..1516ef357 --- /dev/null +++ b/usr.bin/w/w.c @@ -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 +#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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef SUPPORT_UTMP +#include +#endif +#ifdef SUPPORT_UTMPX +#include +#endif +#include + +#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); +}