w.c revision 274406
1264790Sbapt/*-
2264790Sbapt * Copyright (c) 1980, 1991, 1993, 1994
3272955Srodrigc *	The Regents of the University of California.  All rights reserved.
4264790Sbapt *
5264790Sbapt * Redistribution and use in source and binary forms, with or without
6264790Sbapt * modification, are permitted provided that the following conditions
7264790Sbapt * are met:
8264790Sbapt * 1. Redistributions of source code must retain the above copyright
9264790Sbapt *    notice, this list of conditions and the following disclaimer.
10264790Sbapt * 2. Redistributions in binary form must reproduce the above copyright
11264790Sbapt *    notice, this list of conditions and the following disclaimer in the
12264790Sbapt *    documentation and/or other materials provided with the distribution.
13264790Sbapt * 4. Neither the name of the University nor the names of its contributors
14264790Sbapt *    may be used to endorse or promote products derived from this software
15264790Sbapt *    without specific prior written permission.
16264790Sbapt *
17264790Sbapt * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18264790Sbapt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19264790Sbapt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20264790Sbapt * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21264790Sbapt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22264790Sbapt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23264790Sbapt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24264790Sbapt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25264790Sbapt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26264790Sbapt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27264790Sbapt * SUCH DAMAGE.
28264790Sbapt */
29264790Sbapt
30264790Sbapt#include <sys/cdefs.h>
31264790Sbapt
32264790Sbapt__FBSDID("$FreeBSD: head/usr.bin/w/w.c 274406 2014-11-11 21:52:10Z marcel $");
33264790Sbapt
34264790Sbapt#ifndef lint
35264790Sbaptstatic const char copyright[] =
36264790Sbapt"@(#) Copyright (c) 1980, 1991, 1993, 1994\n\
37264790Sbapt	The Regents of the University of California.  All rights reserved.\n";
38264790Sbapt#endif
39264790Sbapt
40264790Sbapt#ifndef lint
41264790Sbaptstatic const char sccsid[] = "@(#)w.c	8.4 (Berkeley) 4/16/94";
42264790Sbapt#endif
43264790Sbapt
44264790Sbapt/*
45264790Sbapt * w - print system status (who and what)
46264790Sbapt *
47264790Sbapt * This program is similar to the systat command on Tenex/Tops 10/20
48264790Sbapt *
49264790Sbapt */
50264790Sbapt#include <sys/param.h>
51264790Sbapt#include <sys/time.h>
52264790Sbapt#include <sys/stat.h>
53264790Sbapt#include <sys/sysctl.h>
54264790Sbapt#include <sys/proc.h>
55264790Sbapt#include <sys/user.h>
56264790Sbapt#include <sys/ioctl.h>
57264790Sbapt#include <sys/socket.h>
58264790Sbapt#include <sys/tty.h>
59264790Sbapt
60264790Sbapt#include <machine/cpu.h>
61264790Sbapt#include <netinet/in.h>
62264790Sbapt#include <arpa/inet.h>
63264790Sbapt#include <arpa/nameser.h>
64264790Sbapt
65264790Sbapt#include <ctype.h>
66264790Sbapt#include <err.h>
67264790Sbapt#include <errno.h>
68264790Sbapt#include <fcntl.h>
69264790Sbapt#include <kvm.h>
70264790Sbapt#include <langinfo.h>
71264790Sbapt#include <libgen.h>
72264790Sbapt#include <libutil.h>
73264790Sbapt#include <limits.h>
74264790Sbapt#include <locale.h>
75264790Sbapt#include <netdb.h>
76264790Sbapt#include <nlist.h>
77264790Sbapt#include <paths.h>
78264790Sbapt#include <resolv.h>
79264790Sbapt#include <stdio.h>
80264790Sbapt#include <stdlib.h>
81264790Sbapt#include <string.h>
82264790Sbapt#include <timeconv.h>
83264790Sbapt#include <unistd.h>
84264790Sbapt#include <utmpx.h>
85264790Sbapt#include <vis.h>
86264790Sbapt#include <libxo/xo.h>
87264790Sbapt
88264790Sbapt#include "extern.h"
89264790Sbapt
90264790Sbaptstatic struct utmpx *utmp;
91264790Sbaptstatic struct winsize ws;
92264790Sbaptstatic kvm_t   *kd;
93264790Sbaptstatic time_t	now;		/* the current time of day */
94264790Sbaptstatic int	ttywidth;	/* width of tty */
95264790Sbaptstatic int	argwidth;	/* width of tty */
96264790Sbaptstatic int	header = 1;	/* true if -h flag: don't print heading */
97264790Sbaptstatic int	nflag;		/* true if -n flag: don't convert addrs */
98264790Sbaptstatic int	dflag;		/* true if -d flag: output debug info */
99264790Sbaptstatic int	sortidle;	/* sort by idle time */
100264790Sbaptint		use_ampm;	/* use AM/PM time */
101264790Sbaptstatic int	use_comma;      /* use comma as floats separator */
102264790Sbaptstatic char   **sel_users;	/* login array of particular users selected */
103264790Sbapt
104264790Sbapt/*
105264790Sbapt * One of these per active utmp entry.
106264790Sbapt */
107264790Sbaptstatic struct entry {
108264790Sbapt	struct	entry *next;
109264790Sbapt	struct	utmpx utmp;
110264790Sbapt	dev_t	tdev;			/* dev_t of terminal */
111264790Sbapt	time_t	idle;			/* idle time of terminal in seconds */
112264790Sbapt	struct	kinfo_proc *kp;		/* `most interesting' proc */
113264790Sbapt	char	*args;			/* arg list of interesting process */
114264790Sbapt	struct	kinfo_proc *dkp;	/* debug option proc list */
115264790Sbapt} *ep, *ehead = NULL, **nextp = &ehead;
116264790Sbapt
117264790Sbapt#define	debugproc(p) *(&((struct kinfo_proc *)p)->ki_udata)
118264790Sbapt
119264790Sbapt#define	W_DISPUSERSIZE	10
120264790Sbapt#define	W_DISPLINESIZE	8
121264790Sbapt#define	W_DISPHOSTSIZE	24
122264790Sbapt
123264790Sbaptstatic void		 pr_header(time_t *, int);
124264790Sbaptstatic struct stat	*ttystat(char *);
125264790Sbaptstatic void		 usage(int);
126264790Sbapt
127264790Sbaptchar *fmt_argv(char **, char *, char *, size_t);	/* ../../bin/ps/fmt.c */
128264790Sbapt
129264790Sbaptint
130264790Sbaptmain(int argc, char *argv[])
131264790Sbapt{
132264790Sbapt	struct kinfo_proc *kp;
133264790Sbapt	struct kinfo_proc *dkp;
134264790Sbapt	struct stat *stp;
135264790Sbapt	time_t touched;
136264790Sbapt	int ch, i, nentries, nusers, wcmd, longidle, longattime, dropgid;
137264790Sbapt	const char *memf, *nlistf, *p, *save_p;
138264790Sbapt	char *x_suffix;
139264790Sbapt	char buf[MAXHOSTNAMELEN], errbuf[_POSIX2_LINE_MAX];
140264790Sbapt	char fn[MAXHOSTNAMELEN];
141264790Sbapt	char *dot;
142264790Sbapt
143264790Sbapt	(void)setlocale(LC_ALL, "");
144264790Sbapt	use_ampm = (*nl_langinfo(T_FMT_AMPM) != '\0');
145264790Sbapt	use_comma = (*nl_langinfo(RADIXCHAR) != ',');
146264790Sbapt
147264790Sbapt	argc = xo_parse_args(argc, argv);
148264790Sbapt	if (argc < 0)
149264790Sbapt		exit(1);
150264790Sbapt
151264790Sbapt	/* Are we w(1) or uptime(1)? */
152264790Sbapt	if (strcmp(basename(argv[0]), "uptime") == 0) {
153264790Sbapt		wcmd = 0;
154264790Sbapt		p = "";
155264790Sbapt	} else {
156264790Sbapt		wcmd = 1;
157264790Sbapt		p = "dhiflM:N:nsuw";
158264790Sbapt	}
159264790Sbapt
160264790Sbapt	dropgid = 0;
161264790Sbapt	memf = _PATH_DEVNULL;
162264790Sbapt	nlistf = NULL;
163264790Sbapt	while ((ch = getopt(argc, argv, p)) != -1)
164264790Sbapt		switch (ch) {
165264790Sbapt		case 'd':
166264790Sbapt			dflag = 1;
167264790Sbapt			break;
168264790Sbapt		case 'h':
169264790Sbapt			header = 0;
170264790Sbapt			break;
171264790Sbapt		case 'i':
172264790Sbapt			sortidle = 1;
173264790Sbapt			break;
174264790Sbapt		case 'M':
175264790Sbapt			header = 0;
176264790Sbapt			memf = optarg;
177264790Sbapt			dropgid = 1;
178264790Sbapt			break;
179264790Sbapt		case 'N':
180264790Sbapt			nlistf = optarg;
181264790Sbapt			dropgid = 1;
182264790Sbapt			break;
183264790Sbapt		case 'n':
184264790Sbapt			nflag = 1;
185264790Sbapt			break;
186264790Sbapt		case 'f': case 'l': case 's': case 'u': case 'w':
187264790Sbapt			warnx("[-flsuw] no longer supported");
188264790Sbapt			/* FALLTHROUGH */
189264790Sbapt		case '?':
190264790Sbapt		default:
191264790Sbapt			usage(wcmd);
192264790Sbapt		}
193264790Sbapt	argc -= optind;
194264790Sbapt	argv += optind;
195264790Sbapt
196264790Sbapt	if (!(_res.options & RES_INIT))
197264790Sbapt		res_init();
198264790Sbapt	_res.retrans = 2;	/* resolver timeout to 2 seconds per try */
199264790Sbapt	_res.retry = 1;		/* only try once.. */
200264790Sbapt
201264790Sbapt	/*
202264790Sbapt	 * Discard setgid privileges if not the running kernel so that bad
203264790Sbapt	 * guys can't print interesting stuff from kernel memory.
204264790Sbapt	 */
205264790Sbapt	if (dropgid)
206264790Sbapt		setgid(getgid());
207264790Sbapt
208264790Sbapt	if ((kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf)) == NULL)
209264790Sbapt		errx(1, "%s", errbuf);
210264790Sbapt
211264790Sbapt	(void)time(&now);
212264790Sbapt
213264790Sbapt	if (*argv)
214264790Sbapt		sel_users = argv;
215264790Sbapt
216264790Sbapt	setutxent();
217264790Sbapt	for (nusers = 0; (utmp = getutxent()) != NULL;) {
218264790Sbapt		if (utmp->ut_type != USER_PROCESS)
219264790Sbapt			continue;
220264790Sbapt		if (!(stp = ttystat(utmp->ut_line)))
221264790Sbapt			continue;	/* corrupted record */
222264790Sbapt		++nusers;
223264790Sbapt		if (wcmd == 0)
224264790Sbapt			continue;
225264790Sbapt		if (sel_users) {
226264790Sbapt			int usermatch;
227264790Sbapt			char **user;
228264790Sbapt
229264790Sbapt			usermatch = 0;
230264790Sbapt			for (user = sel_users; !usermatch && *user; user++)
231264790Sbapt				if (!strcmp(utmp->ut_user, *user))
232264790Sbapt					usermatch = 1;
233264790Sbapt			if (!usermatch)
234264790Sbapt				continue;
235264790Sbapt		}
236264790Sbapt		if ((ep = calloc(1, sizeof(struct entry))) == NULL)
237264790Sbapt			errx(1, "calloc");
238264790Sbapt		*nextp = ep;
239264790Sbapt		nextp = &ep->next;
240264790Sbapt		memmove(&ep->utmp, utmp, sizeof *utmp);
241272955Srodrigc		ep->tdev = stp->st_rdev;
242272955Srodrigc		/*
243272955Srodrigc		 * If this is the console device, attempt to ascertain
244272955Srodrigc		 * the true console device dev_t.
245272955Srodrigc		 */
246264790Sbapt		if (ep->tdev == 0) {
247264790Sbapt			size_t size;
248264790Sbapt
249264790Sbapt			size = sizeof(dev_t);
250264790Sbapt			(void)sysctlbyname("machdep.consdev", &ep->tdev, &size, NULL, 0);
251264790Sbapt		}
252264790Sbapt		touched = stp->st_atime;
253264790Sbapt		if (touched < ep->utmp.ut_tv.tv_sec) {
254264790Sbapt			/* tty untouched since before login */
255264790Sbapt			touched = ep->utmp.ut_tv.tv_sec;
256264790Sbapt		}
257264790Sbapt		if ((ep->idle = now - touched) < 0)
258264790Sbapt			ep->idle = 0;
259264790Sbapt	}
260264790Sbapt	endutxent();
261264790Sbapt
262264790Sbapt	xo_open_container("uptime-information");
263264790Sbapt
264264790Sbapt	if (header || wcmd == 0) {
265264790Sbapt		pr_header(&now, nusers);
266264790Sbapt		if (wcmd == 0) {
267264790Sbapt		        xo_close_container("uptime-information");
268264790Sbapt			(void)kvm_close(kd);
269264790Sbapt			exit(0);
270264790Sbapt		}
271264790Sbapt
272264790Sbapt#define HEADER_USER		"USER"
273264790Sbapt#define HEADER_TTY		"TTY"
274264790Sbapt#define HEADER_FROM		"FROM"
275264790Sbapt#define HEADER_LOGIN_IDLE	"LOGIN@  IDLE "
276264790Sbapt#define HEADER_WHAT		"WHAT\n"
277264790Sbapt#define WUSED  (W_DISPUSERSIZE + W_DISPLINESIZE + W_DISPHOSTSIZE + \
278264790Sbapt		sizeof(HEADER_LOGIN_IDLE) + 3)	/* header width incl. spaces */
279264790Sbapt		xo_emit("{T:/%-*.*s} {T:/%-*.*s} {T:/%-*.*s}  {T:/%s}",
280264790Sbapt				W_DISPUSERSIZE, W_DISPUSERSIZE, HEADER_USER,
281264790Sbapt				W_DISPLINESIZE, W_DISPLINESIZE, HEADER_TTY,
282264790Sbapt				W_DISPHOSTSIZE, W_DISPHOSTSIZE, HEADER_FROM,
283264790Sbapt				HEADER_LOGIN_IDLE HEADER_WHAT);
284264790Sbapt	}
285264790Sbapt
286264790Sbapt	if ((kp = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nentries)) == NULL)
287264790Sbapt		err(1, "%s", kvm_geterr(kd));
288264790Sbapt	for (i = 0; i < nentries; i++, kp++) {
289264790Sbapt		if (kp->ki_stat == SIDL || kp->ki_stat == SZOMB ||
290264790Sbapt		    kp->ki_tdev == NODEV)
291264790Sbapt			continue;
292264790Sbapt		for (ep = ehead; ep != NULL; ep = ep->next) {
293264790Sbapt			if (ep->tdev == kp->ki_tdev) {
294264790Sbapt				/*
295264790Sbapt				 * proc is associated with this terminal
296264790Sbapt				 */
297264790Sbapt				if (ep->kp == NULL && kp->ki_pgid == kp->ki_tpgid) {
298264790Sbapt					/*
299264790Sbapt					 * Proc is 'most interesting'
300264790Sbapt					 */
301264790Sbapt					if (proc_compare(ep->kp, kp))
302264790Sbapt						ep->kp = kp;
303264790Sbapt				}
304264790Sbapt				/*
305264790Sbapt				 * Proc debug option info; add to debug
306264790Sbapt				 * list using kinfo_proc ki_spare[0]
307264790Sbapt				 * as next pointer; ptr to ptr avoids the
308264790Sbapt				 * ptr = long assumption.
309264790Sbapt				 */
310264790Sbapt				dkp = ep->dkp;
311264790Sbapt				ep->dkp = kp;
312264790Sbapt				debugproc(kp) = dkp;
313264790Sbapt			}
314264790Sbapt		}
315264790Sbapt	}
316264790Sbapt	if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 &&
317264790Sbapt	     ioctl(STDERR_FILENO, TIOCGWINSZ, &ws) == -1 &&
318264790Sbapt	     ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) || ws.ws_col == 0)
319264790Sbapt	       ttywidth = 79;
320264790Sbapt        else
321264790Sbapt	       ttywidth = ws.ws_col - 1;
322264790Sbapt	argwidth = ttywidth - WUSED;
323264790Sbapt	if (argwidth < 4)
324264790Sbapt		argwidth = 8;
325264790Sbapt	for (ep = ehead; ep != NULL; ep = ep->next) {
326264790Sbapt		if (ep->kp == NULL) {
327264790Sbapt			ep->args = strdup("-");
328264790Sbapt			continue;
329264790Sbapt		}
330264790Sbapt		ep->args = fmt_argv(kvm_getargv(kd, ep->kp, argwidth),
331264790Sbapt		    ep->kp->ki_comm, NULL, MAXCOMLEN);
332264790Sbapt		if (ep->args == NULL)
333264790Sbapt			err(1, NULL);
334264790Sbapt	}
335264790Sbapt	/* sort by idle time */
336264790Sbapt	if (sortidle && ehead != NULL) {
337264790Sbapt		struct entry *from, *save;
338264790Sbapt
339264790Sbapt		from = ehead;
340264790Sbapt		ehead = NULL;
341264790Sbapt		while (from != NULL) {
342264790Sbapt			for (nextp = &ehead;
343264790Sbapt			    (*nextp) && from->idle >= (*nextp)->idle;
344264790Sbapt			    nextp = &(*nextp)->next)
345264790Sbapt				continue;
346264790Sbapt			save = from;
347264790Sbapt			from = from->next;
348264790Sbapt			save->next = *nextp;
349264790Sbapt			*nextp = save;
350264790Sbapt		}
351264790Sbapt	}
352264790Sbapt
353264790Sbapt	xo_open_container("user-table");
354264790Sbapt	xo_open_list("user-entry");
355264790Sbapt
356264790Sbapt	for (ep = ehead; ep != NULL; ep = ep->next) {
357264790Sbapt		struct addrinfo hints, *res;
358264790Sbapt		struct sockaddr_storage ss;
359264790Sbapt		struct sockaddr *sa = (struct sockaddr *)&ss;
360264790Sbapt		struct sockaddr_in *lsin = (struct sockaddr_in *)&ss;
361264790Sbapt		struct sockaddr_in6 *lsin6 = (struct sockaddr_in6 *)&ss;
362264790Sbapt		time_t t;
363264790Sbapt		int isaddr;
364264790Sbapt
365264790Sbapt		xo_open_instance("user-entry");
366264790Sbapt
367264790Sbapt		save_p = p = *ep->utmp.ut_host ? ep->utmp.ut_host : "-";
368264790Sbapt		if ((x_suffix = strrchr(p, ':')) != NULL) {
369264790Sbapt			if ((dot = strchr(x_suffix, '.')) != NULL &&
370264790Sbapt			    strchr(dot+1, '.') == NULL)
371264790Sbapt				*x_suffix++ = '\0';
372264790Sbapt			else
373264790Sbapt				x_suffix = NULL;
374264790Sbapt		}
375264790Sbapt
376264790Sbapt		isaddr = 0;
377264790Sbapt		memset(&ss, '\0', sizeof(ss));
378264790Sbapt		if (inet_pton(AF_INET6, p, &lsin6->sin6_addr) == 1) {
379264790Sbapt			lsin6->sin6_len = sizeof(*lsin6);
380264790Sbapt			lsin6->sin6_family = AF_INET6;
381264790Sbapt			isaddr = 1;
382264790Sbapt		} else if (inet_pton(AF_INET, p, &lsin->sin_addr) == 1) {
383264790Sbapt			lsin->sin_len = sizeof(*lsin);
384264790Sbapt			lsin->sin_family = AF_INET;
385264790Sbapt			isaddr = 1;
386264790Sbapt		}
387264790Sbapt		if (!nflag) {
388264790Sbapt			/* Attempt to change an IP address into a name */
389264790Sbapt			if (isaddr && realhostname_sa(fn, sizeof(fn), sa,
390264790Sbapt			    sa->sa_len) == HOSTNAME_FOUND)
391264790Sbapt				p = fn;
392264790Sbapt		} else if (!isaddr) {
393264790Sbapt			/*
394264790Sbapt			 * If a host has only one A/AAAA RR, change a
395264790Sbapt			 * name into an IP address
396264790Sbapt			 */
397264790Sbapt			memset(&hints, 0, sizeof(hints));
398264790Sbapt			hints.ai_flags = AI_PASSIVE;
399264790Sbapt			hints.ai_family = AF_UNSPEC;
400264790Sbapt			hints.ai_socktype = SOCK_STREAM;
401264790Sbapt			if (getaddrinfo(p, NULL, &hints, &res) == 0) {
402264790Sbapt				if (res->ai_next == NULL &&
403264790Sbapt				    getnameinfo(res->ai_addr, res->ai_addrlen,
404264790Sbapt					fn, sizeof(fn), NULL, 0,
405264790Sbapt					NI_NUMERICHOST) == 0)
406264790Sbapt					p = fn;
407264790Sbapt				freeaddrinfo(res);
408264790Sbapt			}
409264790Sbapt		}
410264790Sbapt
411272955Srodrigc		if (x_suffix) {
412264790Sbapt			(void)snprintf(buf, sizeof(buf), "%s:%s", p, x_suffix);
413264790Sbapt			p = buf;
414264790Sbapt		}
415264790Sbapt		if (dflag) {
416264790Sbapt		        xo_open_container("process-table");
417264790Sbapt		        xo_open_list("process-entry");
418264790Sbapt
419264790Sbapt			for (dkp = ep->dkp; dkp != NULL; dkp = debugproc(dkp)) {
420272955Srodrigc				const char *ptr;
421264790Sbapt
422264790Sbapt				ptr = fmt_argv(kvm_getargv(kd, dkp, argwidth),
423272955Srodrigc				    dkp->ki_comm, NULL, MAXCOMLEN);
424272955Srodrigc				if (ptr == NULL)
425264790Sbapt					ptr = "-";
426264790Sbapt				xo_open_instance("process-entry");
427264790Sbapt				xo_emit("\t\t{:process-id/%-9d/%d} {:command/%s}\n",
428264790Sbapt				    dkp->ki_pid, ptr);
429264790Sbapt				xo_close_instance("process-entry");
430264790Sbapt			}
431264790Sbapt		        xo_close_list("process-entry");
432264790Sbapt		        xo_close_container("process-table");
433264790Sbapt		}
434264790Sbapt		xo_emit("{:user/%-*.*s/%@**@s} {:tty/%-*.*s/%@**@s} ",
435264790Sbapt			W_DISPUSERSIZE, W_DISPUSERSIZE, ep->utmp.ut_user,
436264790Sbapt			W_DISPLINESIZE, W_DISPLINESIZE,
437264790Sbapt			*ep->utmp.ut_line ?
438264790Sbapt			(strncmp(ep->utmp.ut_line, "tty", 3) &&
439264790Sbapt			 strncmp(ep->utmp.ut_line, "cua", 3) ?
440264790Sbapt			 ep->utmp.ut_line : ep->utmp.ut_line + 3) : "-");
441264790Sbapt
442264790Sbapt		if (save_p && save_p != p)
443264790Sbapt		    xo_attr("address", "%s", save_p);
444264790Sbapt		xo_emit("{:from/%-*.*s/%@**@s} ",
445264790Sbapt		    W_DISPHOSTSIZE, W_DISPHOSTSIZE, *p ? p : "-");
446264790Sbapt		t = ep->utmp.ut_tv.tv_sec;
447264790Sbapt		longattime = pr_attime(&t, &now);
448264790Sbapt		longidle = pr_idle(ep->idle);
449264790Sbapt		xo_emit("{:command/%.*s/%@*@s}\n",
450264790Sbapt		    argwidth - longidle - longattime,
451264790Sbapt		    ep->args);
452264790Sbapt
453264790Sbapt		xo_close_instance("user-entry");
454264790Sbapt	}
455264790Sbapt
456264790Sbapt	xo_close_list("user-entry");
457264790Sbapt	xo_close_container("user-table");
458264790Sbapt	xo_close_container("uptime-information");
459264790Sbapt	xo_finish();
460264790Sbapt
461264790Sbapt	(void)kvm_close(kd);
462264790Sbapt	exit(0);
463264790Sbapt}
464264790Sbapt
465264790Sbaptstatic void
466264790Sbaptpr_header(time_t *nowp, int nusers)
467264790Sbapt{
468264790Sbapt	double avenrun[3];
469264790Sbapt	time_t uptime;
470264790Sbapt	struct timespec tp;
471264790Sbapt	int days, hrs, i, mins, secs;
472264790Sbapt	char buf[256];
473264790Sbapt
474264790Sbapt	/*
475264790Sbapt	 * Print time of day.
476264790Sbapt	 */
477264790Sbapt	if (strftime(buf, sizeof(buf),
478264790Sbapt	    use_ampm ? "%l:%M%p" : "%k:%M", localtime(nowp)) != 0)
479264790Sbapt		xo_emit("{:time-of-day/%s} ", buf);
480264790Sbapt	/*
481264790Sbapt	 * Print how long system has been up.
482264790Sbapt	 */
483264790Sbapt	if (clock_gettime(CLOCK_UPTIME, &tp) != -1) {
484264790Sbapt		uptime = tp.tv_sec;
485264790Sbapt		if (uptime > 60)
486264790Sbapt			uptime += 30;
487264790Sbapt		days = uptime / 86400;
488264790Sbapt		uptime %= 86400;
489264790Sbapt		hrs = uptime / 3600;
490264790Sbapt		uptime %= 3600;
491264790Sbapt		mins = uptime / 60;
492264790Sbapt		secs = uptime % 60;
493264790Sbapt		xo_emit(" up");
494264790Sbapt		xo_attr("seconds", "%lu", (unsigned long) tp.tv_sec);
495264790Sbapt		if (days > 0)
496264790Sbapt			xo_emit(" {:uptime/%d day%s},",
497264790Sbapt				days, days > 1 ? "s" : "");
498264790Sbapt		if (hrs > 0 && mins > 0)
499264790Sbapt			xo_emit(" {:uptime/%2d:%02d},", hrs, mins);
500264790Sbapt		else if (hrs > 0)
501264790Sbapt			xo_emit(" {:uptime/%d hr%s},",
502264790Sbapt				hrs, hrs > 1 ? "s" : "");
503264790Sbapt		else if (mins > 0)
504264790Sbapt			xo_emit(" {:uptime/%d min%s},",
505264790Sbapt				mins, mins > 1 ? "s" : "");
506264790Sbapt		else
507264790Sbapt			xo_emit(" {:uptime/%d sec%s},",
508264790Sbapt				secs, secs > 1 ? "s" : "");
509264790Sbapt	}
510264790Sbapt
511264790Sbapt	/* Print number of users logged in to system */
512264790Sbapt	xo_emit(" {:users/%d} {N:user%s}", nusers, nusers == 1 ? "" : "s");
513264790Sbapt
514264790Sbapt	/*
515264790Sbapt	 * Print 1, 5, and 15 minute load averages.
516264790Sbapt	 */
517264790Sbapt	if (getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0])) == -1)
518264790Sbapt		xo_emit(", no load average information available\n");
519264790Sbapt	else {
520264790Sbapt	        static const char *format[] = {
521264790Sbapt		    " {:load-average-1/%.2f}",
522264790Sbapt		    " {:load-average-5/%.2f}",
523264790Sbapt		    " {:load-average-15/%.2f}",
524264790Sbapt		};
525264790Sbapt		xo_emit(", load averages:");
526264790Sbapt		for (i = 0; i < (int)(sizeof(avenrun) / sizeof(avenrun[0])); i++) {
527264790Sbapt			if (use_comma && i > 0)
528264790Sbapt				xo_emit(",");
529264790Sbapt			xo_emit(format[i], avenrun[i]);
530264790Sbapt		}
531264790Sbapt		xo_emit("\n");
532264790Sbapt	}
533264790Sbapt}
534264790Sbapt
535264790Sbaptstatic struct stat *
536264790Sbaptttystat(char *line)
537264790Sbapt{
538264790Sbapt	static struct stat sb;
539264790Sbapt	char ttybuf[MAXPATHLEN];
540264790Sbapt
541264790Sbapt	(void)snprintf(ttybuf, sizeof(ttybuf), "%s%s", _PATH_DEV, line);
542264790Sbapt	if (stat(ttybuf, &sb) == 0 && S_ISCHR(sb.st_mode)) {
543264790Sbapt		return (&sb);
544264790Sbapt	} else
545264790Sbapt		return (NULL);
546264790Sbapt}
547264790Sbapt
548264790Sbaptstatic void
549264790Sbaptusage(int wcmd)
550264790Sbapt{
551264790Sbapt	if (wcmd)
552264790Sbapt		xo_error("usage: w [-dhin] [-M core] [-N system] [user ...]\n");
553264790Sbapt	else
554264790Sbapt		xo_error("usage: uptime\n");
555264790Sbapt	exit(1);
556264790Sbapt}
557264790Sbapt