Subject: better w(1)
To: None <current-users@netbsd.org>
From: Darren Reed <darrenr@reed.wattle.id.au>
List: current-users
Date: 06/20/1999 23:04:21
Years ago I rewrote w(1) to show users that were `hidden' care of xterm
and screen.  Recently I ported that to NetBSD-1.4, including taking the
time to make the appropriate changes in output.  The result could be
put in place of the current w except that it's probably slower (not using
utmp means more work has to be done) and may(?) do something strange on
some ports (I can only test i386/arm32).  For those who remember what
w(1) used to show, I added a "-1" command line option.  Please test out
the code below.  To compile, save it as "ww.c" and do as follows:

cc ww.c /usr/src/usr.bin/w/pr_time.c -o ww -lkvm

Oh, it still needs to run as root (setuid if you want to use it for more
than testing).

I'm interested in hearing from anyone with comments bugs/criticisms, etc.

Suitable as a wholesale replacement for the current w(1) ?

Cheers,
Darren

/*
 * Copyright (C) 1994-1999 Darren Reed. avalon@coombs.anu.edu.au
 * All rights reserved.
 */
#include <sys/types.h>
#include <sys/time.h>
#define _KERNEL
#define	_LKM
#include <sys/signal.h>
#define	_SYS_RESOURCE_H_
#include <sys/param.h>
#undef	_SYS_RESOURCE_H_
#include <sys/proc.h>
#include <sys/ucred.h>
#include <sys/file.h>
#include <sys/resource.h>
#include <sys/user.h>
#include <vm/vm_param.h>
#undef	_KERNEL
#include <sys/sysctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/dir.h>
#include <sys/tty.h>
#include <sys/socket.h>
#include <dev/cons.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <stddef.h>
#include <pwd.h>
#include <paths.h>
#include <nlist.h>
#include <kvm.h>
#include <fcntl.h>
#include <stdio.h>
#include <netdb.h>
#include <utmp.h>
#include <ttyent.h>
#define	memset(a,b,c)	bzero(a,c)
#define	strrchr		rindex

extern	char	*optarg;
extern	int	optind;

#ifndef	MIN
# define MIN(a,b) (((a)<(b))?(a):(b))
#endif
#ifndef	MAX
# define MAX(a,b) (((a)>(b))?(a):(b))
#endif

#define	OPT_DEBUG	0x01
#define	OPT_NOHDR	0x02
#define	OPT_LONG	0x04
#define	OPT_NOUSERS	0x08
#define	OPT_SMALL	0x10
#define	OPT_V1		0x20
#define	OPT_NORES	0x40
#define	OPT_WIDE	0x80

struct	nlist	names[] = {
	{ "_proc", 0, 0, 0, 0},
	{ "_nproc", 0, 0, 0, 0},
	{ "_avenrun", 0, 0, 0, 0},
	{ "_boottime", 0, 0, 0, 0},
	{ "_cn_tab", 0, 0, 0, 0},

#define	NL_PROC 0
#define	NL_NPROC 1
#define	NL_LOADAV 2
#define	NL_BOOT 3
#define	NL_CONS 4
	{ (char *)NULL, 0, 0, 0, 0 }
};

typedef	struct	Proc {
	struct	kinfo_proc	*p_k;
	struct	proc	*p_p;
	struct	user	p_u;
	struct	pstats	*p_us;
	struct	session	p_s;
	dev_t	p_t;
	int	p_gotu;
} Proc;


static	char	obuff[8192];
static	kvm_t	*kptr = NULL;
static	dev_t	condev = 0;
static	dev_t	ttyp0 = 0;
static	dev_t	tty00 = 0;
static	dev_t	ttyE0 = 0;
static	dev_t	ttyv0 = 0;
static	struct	utmp	*utblock = NULL,
			*utblockend = NULL;
static	char	*kernel = _PATH_UNIX;
static	char	*core = _PATH_MEM;
static	u_int	utblocksz = 0;
static	char	domain[MAXHOSTNAMELEN];


void
fatal(status, msg)
	int status;
	char *msg;
{
	perror(msg);
	kill(getpid(), 11);
}


void
kvminit()
{
	if (!kptr)
		kptr = kvm_openfiles(kernel, core, _PATH_DRUM, O_RDONLY, "ww");
	if (!kptr)
		fatal(-1, "open kmem");
	if (kvm_nlist(kptr, names) == -1)
		fatal(1, "nlist");
}


void
kmemcpy(buf, pos, n)
	char *buf;
	u_long pos;
	int n;
{
	if (!n)
		return;
	if (kvm_read(kptr, pos, buf, n) != n) {
		fprintf(stderr, "pos=%lx buf=%p n=%d\n", pos, buf, n);
		fprintf(stderr, "kvm_read: %s\n", kvm_geterr(kptr));
	 	fatal(-1, "kvm_read");
	}
}


char *
leading(buf)
	char *buf;
{
	register char *s = buf;

	while (*s && (*s != '0'))
		*s++;
	if (*s && (*(s-1) < '0' || *(s-1) > '9') && (*(s-1) != ':'))
		while (*s == '0')
			*s++ = ' ';
	if ((*s >= '0' && *s <= '9') || !*s)
		return buf;
	if (*s == ':')
		*s++ = ' ';
	while (*s == '0')
		*s++ = ' ';
	return buf;
}


char	*
printtime(now, login)
	register time_t	now;
	time_t login;
{
	static char tbuf[32];
	register char *s;

	register struct	tm	*tm;

	if (login == 0)
		return "       -";

	tm = localtime(&login);
	(void) sprintf(tbuf, " %02d:%02d%cm",
		(tm->tm_hour % 12) ? tm->tm_hour % 12 : 12,
		tm->tm_min, (tm->tm_hour > 11) ? 'p' : 'a');
	(void) leading(tbuf+1);
	return tbuf;
}


char *
cputime(pp)
	register struct	Proc *pp;
{
	static char longbuf[32];
	register u_long	tot1 = 0, tot2 = 0;
	register struct rusage *r;
	register long t1, t2;

	if (pp->p_gotu == 1) {
		r = &pp->p_us->p_ru;
		tot2 = r->ru_utime.tv_sec + r->ru_stime.tv_sec;
		t2 = r->ru_utime.tv_usec + r->ru_stime.tv_usec;
		r = &pp->p_us->p_cru;
		tot1 = r->ru_utime.tv_sec + r->ru_stime.tv_sec;
		t1 = r->ru_utime.tv_usec + r->ru_stime.tv_usec;
		tot1 += t1 / 1000000;
		tot2 += t2 / 1000000;
		tot1 += tot2;
	}

	(void) sprintf(longbuf, " %3d:%02d %3d:%02d",
		tot1/60, tot1%60, tot2/60, tot2%60);
	(void) leading(longbuf+1);
	(void) leading(strrchr(longbuf,' ') + 1);
	return longbuf;
}


int
sortproc(p1, p2)
	Proc **p1, **p2;
{
	register struct	proc *pr1, *pr2;
	register dev_t t1, t2;
	register long d;

	if (!*p1 && !*p2)
		return 0;
	else if (!*p1)
		return 1;
	else if (!*p2)
		return -1;

	pr1 = (*p1)->p_p, pr2 = (*p2)->p_p;
	if ((pr1->p_pid >= 0) && (pr1->p_pid <= PID_MAX)) {
		if ((pr2->p_pid < 0) || (pr2->p_pid > PID_MAX))
			return pr1->p_pid;
	}
	if ((pr2->p_pid >= 0) && (pr2->p_pid <= PID_MAX)) {
		if ((pr1->p_pid < 0) || (pr1->p_pid > PID_MAX))
			return -pr2->p_pid;
	}
	t1 = (*p1)->p_t, t2 = (*p2)->p_t;
	if (t1 == condev && t2 != condev)
		return -1;
	if (t1 != condev && t2 == condev)
		return 1;

	if (t1 >= 0 && t2 >= 0)
	{
		if (d = t1 - t2)
			return d;
		if (pr1->p_stat == SRUN && pr2->p_stat != SRUN)
			return 1;
		if (pr1->p_stat != SRUN && pr2->p_stat == SRUN)
			return -1;
		if (pr1->p_stat == SZOMB && pr2->p_stat != SZOMB)
			return -1;
		if (pr1->p_stat != SZOMB && pr2->p_stat == SZOMB)
			return 1;
		if (pr1->p_flag & P_INMEM && !(pr2->p_flag & P_INMEM))
			return 1;
		if (!(pr1->p_flag & P_INMEM) && pr2->p_flag & P_INMEM)
			return -1;
		return pr1->p_pid - pr2->p_pid;
	}
	if (t1 < 0 && t2 < 0)
		return pr1->p_pid - pr2->p_pid;
	else if (t1 < 0)
		return -1;
	return 1;
}


char *
findtty(tty, uid, idle, alttty)
	register dev_t	tty;
	register uid_t	*uid;
	register time_t	*idle;
	char **alttty;
{
	static char buf[64];
	static char hex[17] = "0123456789abcdef";
	register char *s = buf + 5;
	struct stat sb;
	dev_t maj;
	char c;

	*alttty = NULL;
	if (tty == -1)
		return NULL;

	(void) strcpy(buf, _PATH_DEV);

	maj = major(tty);
	if (maj == ttyp0 || maj == ttyE0 || maj == ttyv0 || maj == tty00)
	{
		*s++ = 't';
		*s++ = 't';
		*s++ = 'y';
		if (maj == ttyp0)
			c = 'p';
		else if (maj == ttyE0)
			c = 'E';
		else if (maj == ttyv0)
			c = 'v';
		else if (maj == tty00)
			c = '0';
		else {
			*uid = -1;
			*idle = time(NULL);
			return "???";
		}
		*s++ = c + ((tty & 0xff) >> 4);
		*s++ = hex[(tty & 0xf)];
		*s = '\0';
		if (stat(buf, &sb) == 0) {
			*uid = sb.st_uid;
			*idle = sb.st_atime;
			if ((tty == condev) && stat(_PATH_CONSOLE, &sb) == 0) {
				*alttty = buf + 5;
				return "console";
			}
			return buf+5;
		}
	}
	*uid = -1;
	*idle = time(NULL);
	return NULL;
}


void
init_utmp()
{
	struct stat sb;
	caddr_t map;
	int fd;

	fd = open(_PATH_UTMP, O_RDONLY);
	if (fd >= 0) {
		if (fstat(fd, &sb) == 0) {
			map = mmap(0, sb.st_size, PROT_READ, MAP_SHARED, fd,0);
			if (map == (caddr_t)-1)
				perror("mmap");
			else {
				utblocksz = sb.st_size;
				utblock = (struct utmp *)map;
				utblockend = (struct utmp*)((char*)utblock +
							    utblocksz);
			}
			close(fd);
		}
	}
}


struct utmp *
ut_findtty(tty)
	char *tty;
{
	struct utmp *u, *ue;

	if (!utblock || !utblockend || !utblocksz)
		return NULL;

	for (u = utblock; u < utblockend; u++)
		if (*u->ut_name && !strcmp(tty, u->ut_line))
			return u;
	return NULL;
}


void
init_console(flags)
	int flags;
{
	struct consdev	cd, *cons;
	char ttypath[32], *s;
	struct stat sb;

	if ((names[NL_CONS].n_value != 0)) {
		kmemcpy((char *)&cons, names[NL_CONS].n_value, sizeof(cons));
		kmemcpy((char *)&cd, cons, sizeof(cd));
		if (flags & OPT_DEBUG)
			fprintf(stderr, "consdev.cn_dev = %#x\n", cd.cn_dev);
		condev = cd.cn_dev;
	} else if (stat(_PATH_CONSOLE, &sb) != -1) {
		condev = sb.st_rdev;
		if (flags & OPT_DEBUG)
			fprintf(stderr, "console at %#x\n", condev);
	}

	snprintf(ttypath, sizeof(ttypath), "%sttyXX", _PATH_DEV);
	s = ttypath + strlen(ttypath);
	s -= 2;
	if (*s != 'X')
		return;

	s[1] = '0';
	s[0] = 'p';
	if (stat(ttypath, &sb) != -1) {
		ttyp0 = major(sb.st_rdev);
		if (flags & OPT_DEBUG)
			fprintf(stderr, "%s at %#x\n", ttypath, ttyp0);
	}

	s[0] = '0';
	if (stat(ttypath, &sb) != -1) {
		tty00 = major(sb.st_rdev);
		if (flags & OPT_DEBUG)
			fprintf(stderr, "%s at %#x\n", ttypath, tty00);
	}

	s[0] = 'E';
	if (stat(ttypath, &sb) != -1) {
		ttyE0 = major(sb.st_rdev);
		if (flags & OPT_DEBUG)
			fprintf(stderr, "%s at %#x\n", ttypath, ttyE0);
	}

	s[0] = 'v';
	if (stat(ttypath, &sb) != -1) {
		ttyv0 = major(sb.st_rdev);
		if (flags & OPT_DEBUG)
			fprintf(stderr, "%s at %#x\n", ttypath, ttyv0);
	}
}


struct	users {
	uid_t	uid;
	int	name[12];
	struct	users *next;
};

static	struct	users	*utab[2003];

void
init_utab()
{
	memset((char *)utab, 0, sizeof(utab));
}


int
hash(uid)
	uid_t uid;
{
	register int hv = (int)uid;
	hv += hv + hv & 0xf + hv << 3;
	hv %= 2003;
	return hv;
}
void
add_user_entry(pw)
	register struct passwd *pw;
{
	register struct users *up;
	register int hv;

	hv = hash((uid_t)pw->pw_uid);
	up = (struct users *)malloc(sizeof(*up));
	(void) sprintf((char *)up->name, "%-8.8s", pw->pw_name);
	up->uid = pw->pw_uid;
	up->next = utab[hv];
	utab[hv] = up;
}


int
checkprint(a0, login, tty)
	char *a0, *login, *tty;
{
	struct ttyent *t;

	if (!a0 || !*a0)
		return 0;
	t = getttynam(tty);
	if (!t || !(t->ty_status & TTY_ON))
		return 0;
	if (!strcmp(login, "root") && !strncmp(t->ty_getty, a0, strlen(a0)))
		return -1;
	return 0;
}


void
printhost(login, tty, idle, flags)
	char *login, *tty;
	time_t idle;
	int flags;
{
	struct hostent *h;
	char *s, host[19];
	struct utmp *u;
	time_t now;

	u = ut_findtty(tty);
	if (u == NULL || !*u->ut_host)
		strcpy(host, "-");
	else if (isdigit(*u->ut_host) && !(flags & OPT_NORES)) {
		struct in_addr in;

		in.s_addr = inet_addr(u->ut_host);
		h = gethostbyaddr((char *)&in, sizeof(in), AF_INET);
		if (h != NULL && h->h_name != NULL) {
			s = strchr(h->h_name, '.');
			if (s && !strcmp(s + 1, domain))
				*s = '\0';
			strncpy(host, h->h_name, sizeof(host));
		} else {
			snprintf(host, sizeof(host), "(%s)", u->ut_host);
		}
	} else
		strncpy(host, u->ut_host, MIN(UT_HOSTSIZE, sizeof(host)));
	printf(" %-16.16s", host);

	if (u == NULL) {
		printf("       ");
	} else {
		now = time(NULL);
		pr_attime(&u->ut_time, &now);
	}
	pr_idle(idle);
}


char *
getuser(uid, login)
	register uid_t uid;
	char *login;
{
	static char uidstr[6];
	register struct passwd *pw;
	register struct users *up;
	u_int hv = hash(uid);

	for (up = utab[hv]; up; up = up->next)
		if (uid == up->uid)
			break;
	if (!up && (pw = getpwuid((int)uid)))
	{
		add_user_entry(pw);
		return getuser(uid, login);
	}
	if ((login != NULL) && strcmp(login, (char *)up->name))
		return login;
	if (up == NULL) {
		snprintf(uidstr, sizeof(uidstr), "%u", uid);
		uidstr[sizeof(uidstr) - 1] = '\0';
		return uidstr;
	}
	return (char *)up->name;
}


void
printprocs(pl, np, fuser, flags)
	Proc **pl;
	int np;
	char *fuser;
	int flags;
{
	register Proc **p, *pp, *ps = NULL;
	register struct	proc *pr;
	register struct pstats *us;
	register int i;
	time_t	li = 0x7fffffff, ti, now = time((time_t *)NULL), diff;
	uid_t uid, who = 0;
	char tbuf[32], *tty, *login = NULL, **av, **ap, *realtty;
	char psargs[160];
	struct session	*sp;
	dev_t pst = 0;
	int len;

	for (i = np, p = pl; i >= 0; i--, p++)
	{
		if (!(pp = *p))
			if (ps)
				goto dump;
			else
				break;

		pr = pp->p_p;

		if (flags & OPT_DEBUG)
			fprintf(stderr, "\t[%s]\t%-6d [%s] %x %#x\n",
				pp->p_s.s_login, pr->p_pid, pr->p_comm,
				pp->p_t, *(p+1));

		sp = &pp->p_s;
		if (!sp->s_ttyp)
			continue;
		who = pp->p_k->kp_eproc.e_pcred.p_ruid;
		if (!pst || pst != pp->p_t)
			tty = findtty(pp->p_t, &uid, &ti, &realtty);
		if (!who && uid)
			who = uid;
		if (uid && who != uid)
			continue;
		if (!uid || uid == who)
			ps = pp, pst = pp->p_t;
		if (!ps)
			continue;
		if (!tty)
			continue;
		if (pp->p_t == -1)
			continue;

		pr->p_comm[MAXCOMLEN] = '\0';
		if (!*pr->p_comm)
			continue;

		if (!*(p+1))
			if (ps)
				goto dump;
			else
				break;

		if (pp->p_t == (*(p+1))->p_t || !ps)
			continue;
dump:
		pr = ps->p_p;
		login = ps->p_s.s_login;
		if (!*login) {
			putchar('#');
			login = NULL;
		}
		login = getuser(who, login);
		if (fuser && strcmp(fuser, login))
			continue;
		av = kvm_getargv(kptr, ps->p_k, sizeof(psargs));
		if (av && av[0] && ((checkprint(av[0], login, tty) == -1) ||
		    (realtty != NULL &&
		     checkprint(av[0], login, realtty) == -1)))
			continue;

		diff = now - ti;

		if (flags & OPT_V1) {
			if (pp->p_gotu == 1) {
				us = pp->p_us;
				if ((u_long)us->p_start.tv_sec < (u_long)li &&
				    us->p_start.tv_sec)
					li = us->p_start.tv_sec;
			} else
				li = 0;

			printf("%-8s ", login);
			if (flags & OPT_SMALL) {
				if (!strncmp(tty, "tty", 3))
					printf("%2.2s", tty + 3);
				else
					printf("%2.2s", tty);
			} else {
				(void) printf("%-8.8s", tty);
				(void) fputs(printtime(now, li), stdout);
			}
			if (diff < 0)
				diff = 0;
			diff /= 60;
			(void) sprintf(tbuf, " %03d:%02d", diff/60, diff%60);
			(void) fputs(leading(tbuf)+1, stdout);

			if (!(flags & OPT_SMALL)) {
				(void) fputs(cputime(ps), stdout);
				len = 32;
			} else
				len = 55;
			fputs("   ", stdout);
		} else {
			printf("%-10s ", login);
			if (!strncmp(tty, "tty", 3))
				printf("%2.2s", tty + 3);
			else
				printf("%2.2s", tty);
			printhost(login, tty, diff, flags);
			len = 34;
		}
		if (flags & OPT_WIDE)
			len += 80;
		if (len > sizeof(psargs))
			len = sizeof(psargs);
		*psargs = '\0';
		ap = av;
		for (ap = av; (ap != NULL) && (*ap != NULL); ap++) {
			if (*psargs)
				(void) strcat(psargs, " ");
			(void) strcat(psargs, *ap);
		}

		if (av && *av && strstr(*av, pr->p_comm)) {
			if (!(flags & OPT_WIDE))
				psargs[len] = '\0';
			printf("%s\n", psargs, len);
		} else {
			if (!(flags & OPT_WIDE)) {
				len -= strlen(pr->p_comm);
				len -= 3;
				psargs[len] = '\0';
			}
			printf("(%s) %s\n", pr->p_comm, psargs);
		}
		li = 0x7fffffff;
		tty = NULL;
		ps = NULL;
		pst = 0;
		fflush(stdout);
	}
}


int
user_count(pt, cnt)
	register Proc **pt;
	register int cnt;
{
	register struct	kinfo_proc *kp;
	register struct	session	*sp;
	register int uc = 0;
	dev_t i, majm = 0;

	for (; *pt && cnt; cnt--, pt++)
	{
		kp = (*pt)->p_k;
		if (!kp || !kp->kp_eproc.e_jobc  || !kp->kp_eproc.e_tsess)
			continue;
		sp = &(*pt)->p_s;
		if (!sp->s_ttyp || !sp->s_ttyvp)
			continue;
		if ((i = (*pt)->p_t) != majm)
		{
			uc++;
			majm = i;
		}
	}
	return uc;
}


Proc
**readprocs(flags, np)
	int flags;
	int *np;
{
	register struct	kinfo_proc *kp;
	register struct	proc *pr;
	register Proc *pp;
	register char *s;
	register int i;
	struct kinfo_proc *kt;
	Proc *p, **ppl;
	int mib[3] = { CTL_KERN, KERN_MAXPROC, 0 }, sz = 0, len;

	len = sizeof(sz);
	if (sysctl(mib, 2, &sz, &len, NULL, 0) == -1) {
		perror("sysctl(KERN,PROC_MAXPROC)");
		exit(-1);
	}
	if (flags & OPT_DEBUG)
		fprintf(stderr, "maxproc = %d\n", sz);
	len = sz * sizeof(*kt);
	kt = (struct kinfo_proc *)malloc(len);
	bzero((char *)kt, len);
	mib[1] = KERN_PROC;
	mib[2] = KERN_PROC_ALL;
	if (sysctl(mib, 3, kt, &len, NULL, 0) == -1) {
		perror("sysctl(KERN,PROC,PROC_ALL)");
		exit(-1);
	}
	*np = sz = len / sizeof(*kt);
	if (flags & OPT_DEBUG)
		fprintf(stderr, "numproc = %d\n", sz);
	p = (Proc *)malloc((u_int)sizeof(*p) * (sz + 1));
	ppl = (Proc **)malloc((u_int)sizeof(*ppl) * (sz + 1));
	bzero((char *)p, sizeof(*p) * (sz + 1));
	bzero((char *)ppl, sizeof(*ppl) * (sz + 1));

	for (i = 0, pp = p, kp = kt; i < sz; i++, pp++, kp++) {
		pp->p_p = pr = &kp->kp_proc;
		pp->p_k = kp;
		pp->p_t = kp->kp_eproc.e_tdev;
		if (kp->kp_eproc.e_tsess)
			kmemcpy((char *)&pp->p_s, (u_long)kp->kp_eproc.e_tsess,
				sizeof(pp->p_s));
		if (((flags & (OPT_V1|OPT_SMALL)) == OPT_V1) &&
		    (pr->p_addr != NULL)) {
			pp->p_us = &pp->p_u.u_stats;
			if (kvm_read(kptr, (u_long)&pr->p_addr->u_stats,
				     (char *)pp->p_us, sizeof(*pp->p_us)) ==
			    sizeof(*pp->p_us))
				pp->p_gotu = 1;
			else if (kvm_read(kptr, (u_long)pr->p_addr,
					  (char *)&pp->p_u, sizeof(pp->p_u)) ==
				 sizeof(pp->p_u))
				pp->p_gotu = 1;
			else
				pp->p_gotu = 0;
		}

		pp->p_p = pr;
		ppl[i] = pp;
	}
	qsort((char *)ppl, i, sizeof(p), sortproc);
	return ppl;
}


void
showload(pl, np, flags)
	Proc **pl;
	int np, flags;
{
	int users = 0, mib[2] = { CTL_KERN, KERN_BOOTTIME }, len;
	time_t now = time((time_t *)NULL), upfor;
	struct loadavg avg;
	struct timeval boot;
	struct tm *tm;
	double loadav[3];
	char c;

	len = sizeof(boot);
	(void) sysctl(mib, 2, &boot, &len, NULL, 0);

	mib[0] = CTL_VM;
	mib[1] = VM_LOADAVG;
	len = sizeof(avg);
	(void) sysctl(mib, 2, &avg, &len, NULL, 0);

	loadav[0] = (double)avg.ldavg[0] / avg.fscale;
	loadav[1] = (double)avg.ldavg[1] / avg.fscale;
	loadav[2] = (double)avg.ldavg[2] / avg.fscale;

	tm = localtime(&now);
	upfor = now - boot.tv_sec;
	upfor /= 60;
	users = user_count(pl, np);
	if (flags & OPT_V1)
		fputc(' ', stdout);
	(void) printf("%2d:%02d",
		(tm->tm_hour > 12) ? tm->tm_hour - 12 : tm->tm_hour,
		tm->tm_min);
	if (tm->tm_hour > 11)
		c = 'P';
	else
		c = 'A';
	if (flags & OPT_V1) {
		c = tolower(c);
		fputc(c, stdout);
		fputs("m ", stdout);
	} else {
		fputc(c, stdout);
		fputs("M  ", stdout);
	}

	fputs("up ", stdout);
	if (upfor > 1440) {
		(void) printf("%d days, ", upfor/1440);
		upfor %= 1440;
	}
	(void) printf("%2d:%02d, %d users,", upfor/60, upfor%60, users);
	(void) printf(" load average: %.2f, %.2f, %.2f\n",
		loadav[0], loadav[1], loadav[2]);
	fflush(stdout);
}


int
main(argc, argv)
	int argc;
	char **argv;
{
	int flags = 0, np = 0, c;
	char *s, *fuser = NULL;
	Proc **pl;

	init_utab();
	setbuffer(stdout, obuff, sizeof(obuff));

	while ((c = getopt(argc, argv, "1dhlM:nN:suw")) != -1)
		switch (c)
		{
		case '1' :
			flags |= OPT_V1;
			break;
		case 'd' :
			flags |= OPT_DEBUG;
			break;
		case 'h' :
			flags |= OPT_NOHDR;
			break;
		case 'l' :
			flags |= OPT_LONG;
			break;
		case 'M' :
			core = optarg;
			setgid(getgid());
			setuid(getuid());
			break;
		case 'N' :
			kernel = optarg;
			setgid(getgid());
			setuid(getuid());
			break;
		case 'n' :
			flags |= OPT_NORES;
			break;
		case 's' :
			flags |= OPT_SMALL;
			break;
		case 'u' :
			flags |= OPT_NOUSERS;
			break;
		case 'w' :
			flags |= OPT_WIDE;
			break;
		}

	if (argc - optind)
		fuser = argv[optind];
	kvminit();
	setgid(getgid());
	setuid(getuid());

	if (!(flags & OPT_NORES)) {
		gethostname(domain, sizeof(domain));
		s = strchr(domain, '.');
		if (s == NULL)
			domain[0] = '\0';
		else
			memmove(domain, s + 1, strlen(s + 1) + 1);
	} else
		domain[0] = '\0';
	init_utmp();
	init_console(flags);
	pl = readprocs(flags, &np);

	if (!(flags & OPT_NOHDR))
		showload(pl, np, flags);
	if (flags & OPT_NOUSERS)
		exit(0);

	if ((flags & OPT_V1) && !(flags & (OPT_NOHDR|OPT_SMALL)))
	    printf("User     tty       login@  idle   JCPU   PCPU   what\n");
	else if (!(flags & (OPT_V1|OPT_NOHDR)))
	    printf("USER      TTY FROM             LOGIN@  IDLE WHAT\n");
	else if (flags & OPT_SMALL)
		printf("User    tty  idle   what\n");
	fflush(stdout);

	(void) setpwent();
	printprocs(pl, np, fuser, flags);
	exit(0);
}