w.c revision 87674
1179193Sjb/*-
2179193Sjb * Copyright (c) 1980, 1991, 1993, 1994
3179193Sjb *	The Regents of the University of California.  All rights reserved.
4179193Sjb *
5179193Sjb * Redistribution and use in source and binary forms, with or without
6179193Sjb * modification, are permitted provided that the following conditions
7179193Sjb * are met:
8179193Sjb * 1. Redistributions of source code must retain the above copyright
9179193Sjb *    notice, this list of conditions and the following disclaimer.
10179193Sjb * 2. Redistributions in binary form must reproduce the above copyright
11179193Sjb *    notice, this list of conditions and the following disclaimer in the
12179193Sjb *    documentation and/or other materials provided with the distribution.
13179193Sjb * 3. All advertising materials mentioning features or use of this software
14179193Sjb *    must display the following acknowledgement:
15179193Sjb *	This product includes software developed by the University of
16179193Sjb *	California, Berkeley and its contributors.
17179193Sjb * 4. Neither the name of the University nor the names of its contributors
18179193Sjb *    may be used to endorse or promote products derived from this software
19179193Sjb *    without specific prior written permission.
20211738Srpaulo *
21211738Srpaulo * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22211738Srpaulo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23211738Srpaulo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24179193Sjb * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25179193Sjb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26179193Sjb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27179198Sjb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28179193Sjb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29179193Sjb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30179193Sjb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31268097Spfg * SUCH DAMAGE.
32268097Spfg */
33268097Spfg
34179193Sjb#include <sys/cdefs.h>
35179193Sjb
36179193Sjb__FBSDID("$FreeBSD: head/usr.bin/w/w.c 87674 2001-12-11 22:18:47Z markm $");
37179193Sjb
38179193Sjb#ifndef lint
39179193Sjbstatic const char copyright[] =
40179193Sjb"@(#) Copyright (c) 1980, 1991, 1993, 1994\n\
41277300Ssmh	The Regents of the University of California.  All rights reserved.\n";
42179193Sjb#endif
43211738Srpaulo
44179193Sjb#ifndef lint
45179193Sjbstatic const char sccsid[] = "@(#)w.c	8.4 (Berkeley) 4/16/94";
46179193Sjb#endif
47277300Ssmh
48179193Sjb/*
49211738Srpaulo * w - print system status (who and what)
50179193Sjb *
51179193Sjb * This program is similar to the systat command on Tenex/Tops 10/20
52179193Sjb *
53179193Sjb */
54179193Sjb#include <sys/param.h>
55179193Sjb#include <sys/time.h>
56179193Sjb#include <sys/stat.h>
57179193Sjb#include <sys/sysctl.h>
58277300Ssmh#include <sys/proc.h>
59179193Sjb#include <sys/user.h>
60211738Srpaulo#include <sys/ioctl.h>
61211738Srpaulo#include <sys/socket.h>
62211738Srpaulo#include <sys/tty.h>
63277300Ssmh
64264434Smarkj#include <machine/cpu.h>
65264434Smarkj#include <netinet/in.h>
66268097Spfg#include <arpa/inet.h>
67211738Srpaulo#include <arpa/nameser.h>
68264434Smarkj
69264434Smarkj#include <ctype.h>
70264434Smarkj#include <err.h>
71264434Smarkj#include <errno.h>
72211738Srpaulo#include <fcntl.h>
73211738Srpaulo#include <kvm.h>
74179193Sjb#include <langinfo.h>
75179193Sjb#include <libutil.h>
76179193Sjb#include <limits.h>
77179193Sjb#include <locale.h>
78179193Sjb#include <netdb.h>
79179193Sjb#include <nlist.h>
80179193Sjb#include <paths.h>
81179193Sjb#include <resolv.h>
82179193Sjb#include <stdio.h>
83179193Sjb#include <stdlib.h>
84179193Sjb#include <string.h>
85179193Sjb#include <unistd.h>
86179193Sjb#include <utmp.h>
87179193Sjb#include <vis.h>
88179193Sjb
89179193Sjb#include "extern.h"
90179193Sjb
91179193Sjbstruct timeval	boottime;
92179193Sjbstruct utmp	utmp;
93179193Sjbstruct winsize	ws;
94179193Sjbkvm_t	       *kd;
95179193Sjbtime_t		now;		/* the current time of day */
96179193Sjbtime_t		uptime;		/* time of last reboot & elapsed time since */
97179193Sjbint		ttywidth;	/* width of tty */
98179193Sjbint		argwidth;	/* width of tty */
99179193Sjbint		header = 1;	/* true if -h flag: don't print heading */
100179193Sjbint		nflag;		/* true if -n flag: don't convert addrs */
101179193Sjbint		dflag;		/* true if -d flag: output debug info */
102179193Sjbint		sortidle;	/* sort by idle time */
103179193Sjbint		use_ampm;	/* use AM/PM time */
104179193Sjbint             use_comma;      /* use comma as floats separator */
105179193Sjbchar	      **sel_users;	/* login array of particular users selected */
106179193Sjb
107179193Sjb/*
108179193Sjb * One of these per active utmp entry.
109179193Sjb */
110179193Sjbstruct	entry {
111179193Sjb	struct	entry *next;
112179193Sjb	struct	utmp utmp;
113179193Sjb	dev_t	tdev;			/* dev_t of terminal */
114179193Sjb	time_t	idle;			/* idle time of terminal in seconds */
115179193Sjb	struct	kinfo_proc *kp;		/* `most interesting' proc */
116179193Sjb	char	*args;			/* arg list of interesting process */
117179193Sjb	struct	kinfo_proc *dkp;	/* debug option proc list */
118179193Sjb} *ep, *ehead = NULL, **nextp = &ehead;
119179193Sjb
120179193Sjb#define debugproc(p) *((struct kinfo_proc **)&(p)->ki_spare[0])
121179193Sjb
122179193Sjb/* W_DISPHOSTSIZE should not be greater than UT_HOSTSIZE */
123179193Sjb#define	W_DISPHOSTSIZE	16
124179193Sjb
125179193Sjbstatic void		 pr_header __P((time_t *, int));
126179193Sjbstatic struct stat	*ttystat __P((char *, int));
127179193Sjbstatic void		 usage __P((int));
128179193Sjbstatic int		 this_is_uptime __P((const char *s));
129179193Sjb
130179193Sjbchar *fmt_argv __P((char **, char *, int));	/* ../../bin/ps/fmt.c */
131179193Sjb
132179193Sjbint
133179193Sjbmain(argc, argv)
134179193Sjb	int argc;
135179193Sjb	char **argv;
136179193Sjb{
137179193Sjb	struct kinfo_proc *kp;
138179193Sjb	struct kinfo_proc *dkp;
139179193Sjb	struct stat *stp;
140179193Sjb	FILE *ut;
141179193Sjb	time_t touched;
142179193Sjb	int ch, i, nentries, nusers, wcmd, longidle, dropgid;
143179193Sjb	const char *memf, *nlistf, *p;
144179193Sjb	char *x_suffix;
145179193Sjb	char buf[MAXHOSTNAMELEN], errbuf[_POSIX2_LINE_MAX];
146179193Sjb	char fn[MAXHOSTNAMELEN];
147179193Sjb	char *dot;
148179193Sjb
149179193Sjb	(void)setlocale(LC_ALL, "");
150179193Sjb	use_ampm = (*nl_langinfo(T_FMT_AMPM) != '\0');
151179193Sjb	use_comma = (*nl_langinfo(RADIXCHAR) != ',');
152211738Srpaulo
153211738Srpaulo	/* Are we w(1) or uptime(1)? */
154211738Srpaulo	if (this_is_uptime(argv[0]) == 0) {
155211738Srpaulo		wcmd = 0;
156211738Srpaulo		p = "";
157211738Srpaulo	} else {
158211738Srpaulo		wcmd = 1;
159211738Srpaulo		p = "dhiflM:N:nsuw";
160211738Srpaulo	}
161211738Srpaulo
162179193Sjb	dropgid = 0;
163179193Sjb	memf = nlistf = _PATH_DEVNULL;
164250953Smarkj	while ((ch = getopt(argc, argv, p)) != -1)
165211738Srpaulo		switch (ch) {
166250953Smarkj		case 'd':
167179193Sjb			dflag = 1;
168179193Sjb			break;
169179193Sjb		case 'h':
170179193Sjb			header = 0;
171179193Sjb			break;
172179193Sjb		case 'i':
173179193Sjb			sortidle = 1;
174179193Sjb			break;
175179193Sjb		case 'M':
176179193Sjb			header = 0;
177179193Sjb			memf = optarg;
178179193Sjb			dropgid = 1;
179179193Sjb			break;
180179193Sjb		case 'N':
181179193Sjb			nlistf = optarg;
182179193Sjb			dropgid = 1;
183179193Sjb			break;
184248983Spfg		case 'n':
185248983Spfg			nflag = 1;
186248983Spfg			break;
187179193Sjb		case 'f': case 'l': case 's': case 'u': case 'w':
188179193Sjb			warnx("[-flsuw] no longer supported");
189179193Sjb			/* FALLTHROUGH */
190179193Sjb		case '?':
191179193Sjb		default:
192179193Sjb			usage(wcmd);
193179193Sjb		}
194179193Sjb	argc -= optind;
195179193Sjb	argv += optind;
196179193Sjb
197179193Sjb	if (!(_res.options & RES_INIT))
198179193Sjb		res_init();
199179193Sjb	_res.retrans = 2;	/* resolver timeout to 2 seconds per try */
200179193Sjb	_res.retry = 1;		/* only try once.. */
201179193Sjb
202179193Sjb	/*
203179193Sjb	 * Discard setgid privileges if not the running kernel so that bad
204179193Sjb	 * guys can't print interesting stuff from kernel memory.
205179193Sjb	 */
206179193Sjb	if (dropgid)
207179193Sjb		setgid(getgid());
208179193Sjb
209179193Sjb	if ((kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf)) == NULL)
210179193Sjb		errx(1, "%s", errbuf);
211179193Sjb
212179193Sjb	(void)time(&now);
213179193Sjb	if ((ut = fopen(_PATH_UTMP, "r")) == NULL)
214179193Sjb		err(1, "%s", _PATH_UTMP);
215277300Ssmh
216264434Smarkj	if (*argv)
217264434Smarkj		sel_users = argv;
218264434Smarkj
219179193Sjb	for (nusers = 0; fread(&utmp, sizeof(utmp), 1, ut);) {
220179193Sjb		if (utmp.ut_name[0] == '\0')
221179193Sjb			continue;
222179193Sjb		if (!(stp = ttystat(utmp.ut_line, UT_LINESIZE)))
223179193Sjb			continue;	/* corrupted record */
224277300Ssmh		++nusers;
225211925Srpaulo		if (wcmd == 0)
226264434Smarkj			continue;
227211925Srpaulo		if (sel_users) {
228211925Srpaulo			int usermatch;
229179193Sjb			char **user;
230179193Sjb
231179193Sjb			usermatch = 0;
232179193Sjb			for (user = sel_users; !usermatch && *user; user++)
233179193Sjb				if (!strncmp(utmp.ut_name, *user, UT_NAMESIZE))
234179193Sjb					usermatch = 1;
235179193Sjb			if (!usermatch)
236179193Sjb				continue;
237179193Sjb		}
238179193Sjb		if ((ep = calloc(1, sizeof(struct entry))) == NULL)
239179193Sjb			errx(1, "calloc");
240179193Sjb		*nextp = ep;
241179193Sjb		nextp = &ep->next;
242179193Sjb		memmove(&ep->utmp, &utmp, sizeof(struct utmp));
243179193Sjb		ep->tdev = stp->st_rdev;
244179193Sjb#ifdef CPU_CONSDEV
245179193Sjb		/*
246179193Sjb		 * If this is the console device, attempt to ascertain
247179193Sjb		 * the true console device dev_t.
248179193Sjb		 */
249179193Sjb		if (ep->tdev == 0) {
250179193Sjb			int mib[2];
251179193Sjb			size_t size;
252179193Sjb
253179193Sjb			mib[0] = CTL_MACHDEP;
254179193Sjb			mib[1] = CPU_CONSDEV;
255179193Sjb			size = sizeof(dev_t);
256179193Sjb			(void)sysctl(mib, 2, &ep->tdev, &size, NULL, 0);
257179193Sjb		}
258179193Sjb#endif
259179193Sjb		touched = stp->st_atime;
260179193Sjb		if (touched < ep->utmp.ut_time) {
261179193Sjb			/* tty untouched since before login */
262179193Sjb			touched = ep->utmp.ut_time;
263179193Sjb		}
264179193Sjb		if ((ep->idle = now - touched) < 0)
265179193Sjb			ep->idle = 0;
266179193Sjb	}
267179193Sjb	(void)fclose(ut);
268179193Sjb
269179193Sjb	if (header || wcmd == 0) {
270179193Sjb		pr_header(&now, nusers);
271179193Sjb		if (wcmd == 0) {
272179193Sjb			(void)kvm_close(kd);
273179193Sjb			exit(0);
274179193Sjb		}
275179193Sjb
276179193Sjb#define HEADER_USER		"USER"
277277300Ssmh#define HEADER_TTY		"TTY"
278179193Sjb#define HEADER_FROM		"FROM"
279179193Sjb#define HEADER_LOGIN_IDLE	"LOGIN@  IDLE "
280179193Sjb#define HEADER_WHAT		"WHAT\n"
281179193Sjb#define WUSED  (UT_NAMESIZE + UT_LINESIZE + W_DISPHOSTSIZE + \
282179193Sjb		sizeof(HEADER_LOGIN_IDLE) + 3)	/* header width incl. spaces */
283179193Sjb		(void)printf("%-*.*s %-*.*s %-*.*s  %s",
284179193Sjb				UT_NAMESIZE, UT_NAMESIZE, HEADER_USER,
285179193Sjb				UT_LINESIZE, UT_LINESIZE, HEADER_TTY,
286179193Sjb				W_DISPHOSTSIZE, W_DISPHOSTSIZE, HEADER_FROM,
287179193Sjb				HEADER_LOGIN_IDLE HEADER_WHAT);
288179193Sjb	}
289179193Sjb
290211738Srpaulo	if ((kp = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nentries)) == NULL)
291211738Srpaulo		err(1, "%s", kvm_geterr(kd));
292211738Srpaulo	for (i = 0; i < nentries; i++, kp++) {
293211738Srpaulo		if (kp->ki_stat == SIDL || kp->ki_stat == SZOMB)
294211738Srpaulo			continue;
295211738Srpaulo		for (ep = ehead; ep != NULL; ep = ep->next) {
296211738Srpaulo			if (ep->tdev == kp->ki_tdev) {
297211738Srpaulo				/*
298277914Smarkj				 * proc is associated with this terminal
299211738Srpaulo				 */
300211738Srpaulo				if (ep->kp == NULL && kp->ki_pgid == kp->ki_tpgid) {
301179193Sjb					/*
302179193Sjb					 * Proc is 'most interesting'
303277300Ssmh					 */
304179193Sjb					if (proc_compare(ep->kp, kp))
305264434Smarkj						ep->kp = kp;
306264434Smarkj				}
307264434Smarkj				/*
308264434Smarkj				 * Proc debug option info; add to debug
309264434Smarkj				 * list using kinfo_proc ki_spare[0]
310264434Smarkj				 * as next pointer; ptr to ptr avoids the
311264434Smarkj				 * ptr = long assumption.
312264434Smarkj				 */
313264434Smarkj				dkp = ep->dkp;
314264434Smarkj				ep->dkp = kp;
315264434Smarkj				debugproc(kp) = dkp;
316264434Smarkj			}
317264434Smarkj		}
318264434Smarkj	}
319264434Smarkj	if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 &&
320264434Smarkj	     ioctl(STDERR_FILENO, TIOCGWINSZ, &ws) == -1 &&
321264434Smarkj	     ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) || ws.ws_col == 0)
322264434Smarkj	       ttywidth = 79;
323264434Smarkj        else
324264434Smarkj	       ttywidth = ws.ws_col - 1;
325264434Smarkj	argwidth = ttywidth - WUSED;
326264434Smarkj	if (argwidth < 4)
327264434Smarkj		argwidth = 8;
328264434Smarkj	for (ep = ehead; ep != NULL; ep = ep->next) {
329264434Smarkj		if (ep->kp == NULL) {
330264434Smarkj			ep->args = strdup("-");
331264434Smarkj			continue;
332264434Smarkj		}
333264434Smarkj		ep->args = fmt_argv(kvm_getargv(kd, ep->kp, argwidth),
334264434Smarkj		    ep->kp->ki_comm, MAXCOMLEN);
335264434Smarkj		if (ep->args == NULL)
336264434Smarkj			err(1, NULL);
337264434Smarkj	}
338264434Smarkj	/* sort by idle time */
339264434Smarkj	if (sortidle && ehead != NULL) {
340264434Smarkj		struct entry *from, *save;
341264434Smarkj
342264434Smarkj		from = ehead;
343264434Smarkj		ehead = NULL;
344264434Smarkj		while (from != NULL) {
345264434Smarkj			for (nextp = &ehead;
346264434Smarkj			    (*nextp) && from->idle >= (*nextp)->idle;
347264434Smarkj			    nextp = &(*nextp)->next)
348264434Smarkj				continue;
349264434Smarkj			save = from;
350264434Smarkj			from = from->next;
351264434Smarkj			save->next = *nextp;
352264434Smarkj			*nextp = save;
353264434Smarkj		}
354264434Smarkj	}
355264434Smarkj
356264434Smarkj	for (ep = ehead; ep != NULL; ep = ep->next) {
357264434Smarkj		char host_buf[UT_HOSTSIZE + 1];
358264434Smarkj		struct sockaddr_storage ss;
359264434Smarkj		struct sockaddr *sa = (struct sockaddr *)&ss;
360264434Smarkj		struct sockaddr_in *lsin = (struct sockaddr_in *)&ss;
361264434Smarkj		struct sockaddr_in6 *lsin6 = (struct sockaddr_in6 *)&ss;
362264434Smarkj		int isaddr;
363264434Smarkj
364264434Smarkj		host_buf[UT_HOSTSIZE] = '\0';
365264434Smarkj		strncpy(host_buf, ep->utmp.ut_host, UT_HOSTSIZE);
366264434Smarkj		p = *host_buf ? host_buf : "-";
367264434Smarkj		if ((x_suffix = strrchr(p, ':')) != NULL) {
368264434Smarkj			if ((dot = strchr(x_suffix, '.')) != NULL &&
369264434Smarkj			    strchr(dot+1, '.') == NULL)
370264434Smarkj				*x_suffix++ = '\0';
371264434Smarkj			else
372264434Smarkj				x_suffix = NULL;
373264434Smarkj		}
374264434Smarkj		if (!nflag) {
375264434Smarkj			/* Attempt to change an IP address into a name */
376264434Smarkj			isaddr = 0;
377264434Smarkj			memset(&ss, '\0', sizeof(ss));
378264434Smarkj			if (inet_pton(AF_INET6, p, &lsin6->sin6_addr) == 1) {
379264434Smarkj				lsin6->sin6_len = sizeof(*lsin6);
380264434Smarkj				lsin6->sin6_family = AF_INET6;
381264434Smarkj				isaddr = 1;
382264434Smarkj			} else if (inet_pton(AF_INET, p, &lsin->sin_addr) == 1) {
383264434Smarkj				lsin->sin_len = sizeof(*lsin);
384264434Smarkj				lsin->sin_family = AF_INET;
385264434Smarkj				isaddr = 1;
386264434Smarkj			}
387264434Smarkj			if (isaddr && realhostname_sa(fn, sizeof(fn), sa,
388264434Smarkj			    sa->sa_len) == HOSTNAME_FOUND)
389264434Smarkj				p = fn;
390264434Smarkj		}
391264434Smarkj		if (x_suffix) {
392264434Smarkj			(void)snprintf(buf, sizeof(buf), "%s:%s", p, x_suffix);
393264434Smarkj			p = buf;
394264434Smarkj		}
395264434Smarkj		if (dflag) {
396264434Smarkj			for (dkp = ep->dkp; dkp != NULL; dkp = debugproc(dkp)) {
397264434Smarkj				const char *ptr;
398264434Smarkj
399264434Smarkj				ptr = fmt_argv(kvm_getargv(kd, dkp, argwidth),
400264434Smarkj				    dkp->ki_comm, MAXCOMLEN);
401264434Smarkj				if (ptr == NULL)
402264434Smarkj					ptr = "-";
403264434Smarkj				(void)printf("\t\t%-9d %s\n",
404264434Smarkj				    dkp->ki_pid, ptr);
405264434Smarkj			}
406264434Smarkj		}
407264434Smarkj		(void)printf("%-*.*s %-*.*s %-*.*s ",
408264434Smarkj		    UT_NAMESIZE, UT_NAMESIZE, ep->utmp.ut_name,
409264434Smarkj		    UT_LINESIZE, UT_LINESIZE,
410264434Smarkj		    strncmp(ep->utmp.ut_line, "tty", 3) &&
411264434Smarkj		    strncmp(ep->utmp.ut_line, "cua", 3) ?
412264434Smarkj		    ep->utmp.ut_line : ep->utmp.ut_line + 3,
413264434Smarkj		    W_DISPHOSTSIZE, W_DISPHOSTSIZE, *p ? p : "-");
414264434Smarkj		pr_attime(&ep->utmp.ut_time, &now);
415264434Smarkj		longidle = pr_idle(ep->idle);
416179193Sjb		(void)printf("%.*s\n", argwidth - longidle, ep->args);
417179193Sjb	}
418179193Sjb	(void)kvm_close(kd);
419179193Sjb	exit(0);
420179193Sjb}
421179193Sjb
422179193Sjbstatic void
423179193Sjbpr_header(nowp, nusers)
424179193Sjb	time_t *nowp;
425179193Sjb	int nusers;
426179193Sjb{
427179193Sjb	double avenrun[3];
428179193Sjb	time_t luptime;
429211925Srpaulo	int days, hrs, i, mins, secs;
430211925Srpaulo	int mib[2];
431211925Srpaulo	size_t size;
432179193Sjb	char buf[256];
433179193Sjb
434179193Sjb	/*
435179193Sjb	 * Print time of day.
436250953Smarkj	 */
437250953Smarkj	(void)strftime(buf, sizeof(buf)	- 1,
438250953Smarkj		       use_ampm	? "%l:%M%p" : "%k:%M", localtime(nowp));
439250953Smarkj	buf[sizeof(buf) - 1] = '\0';
440250953Smarkj	(void)printf("%s ", buf);
441179193Sjb
442179193Sjb	/*
443179193Sjb	 * Print how long system has been up.
444179193Sjb	 * (Found by looking getting "boottime" from the kernel)
445179193Sjb	 */
446179193Sjb	mib[0] = CTL_KERN;
447179193Sjb	mib[1] = KERN_BOOTTIME;
448179193Sjb	size = sizeof(boottime);
449248983Spfg	if (sysctl(mib, 2, &boottime, &size, NULL, 0) != -1 &&
450179193Sjb	    boottime.tv_sec != 0) {
451250953Smarkj		luptime = now - boottime.tv_sec;
452250953Smarkj		if (luptime > 60)
453179193Sjb			luptime += 30;
454211738Srpaulo		days = luptime / 86400;
455179193Sjb		luptime %= 86400;
456179193Sjb		hrs = luptime / 3600;
457179193Sjb		luptime %= 3600;
458179193Sjb		mins = luptime / 60;
459179193Sjb		secs = luptime % 60;
460179193Sjb		(void)printf(" up");
461179193Sjb		if (days > 0)
462179193Sjb			(void)printf(" %d day%s,", days, days > 1 ? "s" : "");
463179193Sjb		if (hrs > 0 && mins > 0)
464179193Sjb			(void)printf(" %2d:%02d,", hrs, mins);
465179193Sjb		else if (hrs > 0)
466179193Sjb			(void)printf(" %d hr%s,", hrs, hrs > 1 ? "s" : "");
467179193Sjb		else if (mins > 0)
468179193Sjb			(void)printf(" %d min%s,", mins, mins > 1 ? "s" : "");
469179193Sjb		else
470179193Sjb			(void)printf(" %d sec%s,", secs, secs > 1 ? "s" : "");
471179193Sjb	}
472179193Sjb
473179193Sjb	/* Print number of users logged in to system */
474179193Sjb	(void)printf(" %d user%s", nusers, nusers == 1 ? "" : "s");
475179193Sjb
476179193Sjb	/*
477179193Sjb	 * Print 1, 5, and 15 minute load averages.
478179193Sjb	 */
479179193Sjb	if (getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0])) == -1)
480179193Sjb		(void)printf(", no load average information available\n");
481179193Sjb	else {
482179193Sjb		(void)printf(", load averages:");
483179193Sjb		for (i = 0; i < (int)(sizeof(avenrun) / sizeof(avenrun[0])); i++) {
484179193Sjb			if (use_comma && i > 0)
485179193Sjb				(void)printf(",");
486179193Sjb			(void)printf(" %.2f", avenrun[i]);
487179193Sjb		}
488179193Sjb		(void)printf("\n");
489179193Sjb	}
490179193Sjb}
491179193Sjb
492179193Sjbstatic struct stat *
493179193Sjbttystat(line, sz)
494179193Sjb	char *line;
495179193Sjb	int sz;
496179193Sjb{
497179193Sjb	static struct stat sb;
498179193Sjb	char ttybuf[MAXPATHLEN];
499179193Sjb
500179193Sjb	(void)snprintf(ttybuf, sizeof(ttybuf), "%s%.*s", _PATH_DEV, sz, line);
501179193Sjb	if (stat(ttybuf, &sb)) {
502179193Sjb		warn("%s", ttybuf);
503179193Sjb		return (NULL);
504179193Sjb	}
505179193Sjb	return (&sb);
506179193Sjb}
507248983Spfg
508179193Sjbstatic void
509179193Sjbusage(wcmd)
510248983Spfg	int wcmd;
511248983Spfg{
512248983Spfg	if (wcmd)
513248983Spfg		(void)fprintf(stderr,
514179193Sjb		    "usage: w [-dhin] [-M core] [-N system] [user ...]\n");
515179193Sjb	else
516179193Sjb		(void)fprintf(stderr, "usage: uptime\n");
517179193Sjb	exit(1);
518179193Sjb}
519179193Sjb
520179193Sjbstatic int
521179193Sjbthis_is_uptime(s)
522179193Sjb	const char *s;
523250953Smarkj{
524179193Sjb	const char *u;
525250953Smarkj
526250953Smarkj	if ((u = strrchr(s, '/')) != NULL)
527250953Smarkj		++u;
528250953Smarkj	else
529250953Smarkj		u = s;
530250953Smarkj	if (strcmp(u, "uptime") == 0)
531250953Smarkj		return (0);
532250953Smarkj	return (-1);
533250953Smarkj}
534250953Smarkj