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