11590Srgrimes/*- 21590Srgrimes * Copyright (c) 1980, 1991, 1993, 1994 31590Srgrimes * The Regents of the University of California. All rights reserved. 41590Srgrimes * 51590Srgrimes * Redistribution and use in source and binary forms, with or without 61590Srgrimes * modification, are permitted provided that the following conditions 71590Srgrimes * are met: 81590Srgrimes * 1. Redistributions of source code must retain the above copyright 91590Srgrimes * notice, this list of conditions and the following disclaimer. 101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111590Srgrimes * notice, this list of conditions and the following disclaimer in the 121590Srgrimes * documentation and/or other materials provided with the distribution. 131590Srgrimes * 4. Neither the name of the University nor the names of its contributors 141590Srgrimes * may be used to endorse or promote products derived from this software 151590Srgrimes * without specific prior written permission. 161590Srgrimes * 171590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 181590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 191590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 201590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 211590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 221590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 231590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 241590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 251590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 261590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 271590Srgrimes * SUCH DAMAGE. 281590Srgrimes */ 291590Srgrimes 3087674Smarkm#include <sys/cdefs.h> 3187674Smarkm 3287674Smarkm__FBSDID("$FreeBSD$"); 3387674Smarkm 341590Srgrimes#ifndef lint 3528694Scharnierstatic const char copyright[] = 361590Srgrimes"@(#) Copyright (c) 1980, 1991, 1993, 1994\n\ 371590Srgrimes The Regents of the University of California. All rights reserved.\n"; 3887674Smarkm#endif 391590Srgrimes 401590Srgrimes#ifndef lint 4187674Smarkmstatic const char sccsid[] = "@(#)w.c 8.4 (Berkeley) 4/16/94"; 4228694Scharnier#endif 431590Srgrimes 441590Srgrimes/* 451590Srgrimes * w - print system status (who and what) 461590Srgrimes * 471590Srgrimes * This program is similar to the systat command on Tenex/Tops 10/20 481590Srgrimes * 491590Srgrimes */ 501590Srgrimes#include <sys/param.h> 511590Srgrimes#include <sys/time.h> 521590Srgrimes#include <sys/stat.h> 531590Srgrimes#include <sys/sysctl.h> 541590Srgrimes#include <sys/proc.h> 551590Srgrimes#include <sys/user.h> 561590Srgrimes#include <sys/ioctl.h> 571590Srgrimes#include <sys/socket.h> 581590Srgrimes#include <sys/tty.h> 591590Srgrimes 601590Srgrimes#include <machine/cpu.h> 611590Srgrimes#include <netinet/in.h> 621590Srgrimes#include <arpa/inet.h> 6387674Smarkm#include <arpa/nameser.h> 641590Srgrimes 65200462Sdelphij#include <ctype.h> 661590Srgrimes#include <err.h> 671590Srgrimes#include <errno.h> 681590Srgrimes#include <fcntl.h> 691590Srgrimes#include <kvm.h> 7073365Sache#include <langinfo.h> 7180407Sbrian#include <libutil.h> 7277212Stmm#include <limits.h> 7373365Sache#include <locale.h> 741590Srgrimes#include <netdb.h> 751590Srgrimes#include <nlist.h> 761590Srgrimes#include <paths.h> 7787674Smarkm#include <resolv.h> 781590Srgrimes#include <stdio.h> 791590Srgrimes#include <stdlib.h> 801590Srgrimes#include <string.h> 81200462Sdelphij#include <timeconv.h> 821590Srgrimes#include <unistd.h> 83202199Sed#include <utmpx.h> 84200462Sdelphij#include <vis.h> 851590Srgrimes 861590Srgrimes#include "extern.h" 871590Srgrimes 881590Srgrimesstruct timeval boottime; 89200172Sedstruct utmpx *utmp; 901590Srgrimesstruct winsize ws; 911590Srgrimeskvm_t *kd; 921590Srgrimestime_t now; /* the current time of day */ 931590Srgrimesint ttywidth; /* width of tty */ 941590Srgrimesint argwidth; /* width of tty */ 951590Srgrimesint header = 1; /* true if -h flag: don't print heading */ 961590Srgrimesint nflag; /* true if -n flag: don't convert addrs */ 9729310Sacheint dflag; /* true if -d flag: output debug info */ 9842481Speterint sortidle; /* sort by idle time */ 9973365Sacheint use_ampm; /* use AM/PM time */ 10073385Sacheint use_comma; /* use comma as floats separator */ 10149177Sgreenchar **sel_users; /* login array of particular users selected */ 1021590Srgrimes 1031590Srgrimes/* 1041590Srgrimes * One of these per active utmp entry. 1051590Srgrimes */ 1061590Srgrimesstruct entry { 1071590Srgrimes struct entry *next; 108200172Sed struct utmpx utmp; 10929310Sache dev_t tdev; /* dev_t of terminal */ 11029310Sache time_t idle; /* idle time of terminal in seconds */ 11129310Sache struct kinfo_proc *kp; /* `most interesting' proc */ 11229310Sache char *args; /* arg list of interesting process */ 11329310Sache struct kinfo_proc *dkp; /* debug option proc list */ 1141590Srgrimes} *ep, *ehead = NULL, **nextp = &ehead; 1151590Srgrimes 116201611Sdwmalone#define debugproc(p) *(&((struct kinfo_proc *)p)->ki_udata) 1171590Srgrimes 118200172Sed#define W_DISPUSERSIZE 10 119200172Sed#define W_DISPLINESIZE 8 120200172Sed#define W_DISPHOSTSIZE 24 12180407Sbrian 12292922Simpstatic void pr_header(time_t *, int); 123200172Sedstatic struct stat *ttystat(char *); 12492922Simpstatic void usage(int); 12592922Simpstatic int this_is_uptime(const char *s); 12649177Sgreen 127247516Sjhbchar *fmt_argv(char **, char *, char *, size_t); /* ../../bin/ps/fmt.c */ 1281590Srgrimes 1291590Srgrimesint 13097981Sjmallettmain(int argc, char *argv[]) 1311590Srgrimes{ 1321590Srgrimes struct kinfo_proc *kp; 13329310Sache struct kinfo_proc *dkp; 1341590Srgrimes struct stat *stp; 13570242Sbrian time_t touched; 136196652Sume int ch, i, nentries, nusers, wcmd, longidle, longattime, dropgid; 13787674Smarkm const char *memf, *nlistf, *p; 13887674Smarkm char *x_suffix; 13977212Stmm char buf[MAXHOSTNAMELEN], errbuf[_POSIX2_LINE_MAX]; 14080407Sbrian char fn[MAXHOSTNAMELEN]; 14180407Sbrian char *dot; 1421590Srgrimes 14349177Sgreen (void)setlocale(LC_ALL, ""); 14473365Sache use_ampm = (*nl_langinfo(T_FMT_AMPM) != '\0'); 14573385Sache use_comma = (*nl_langinfo(RADIXCHAR) != ','); 14611755Sache 1471590Srgrimes /* Are we w(1) or uptime(1)? */ 14842055Sdillon if (this_is_uptime(argv[0]) == 0) { 1491590Srgrimes wcmd = 0; 1501590Srgrimes p = ""; 1511590Srgrimes } else { 1521590Srgrimes wcmd = 1; 15329310Sache p = "dhiflM:N:nsuw"; 1541590Srgrimes } 1551590Srgrimes 15653279Speter dropgid = 0; 157203688Sbrucec memf = _PATH_DEVNULL; 158203688Sbrucec nlistf = NULL; 15924360Simp while ((ch = getopt(argc, argv, p)) != -1) 1601590Srgrimes switch (ch) { 16129310Sache case 'd': 16229310Sache dflag = 1; 16329310Sache break; 1641590Srgrimes case 'h': 1651590Srgrimes header = 0; 1661590Srgrimes break; 1671590Srgrimes case 'i': 1681590Srgrimes sortidle = 1; 1691590Srgrimes break; 1701590Srgrimes case 'M': 1711590Srgrimes header = 0; 1721590Srgrimes memf = optarg; 17353279Speter dropgid = 1; 1741590Srgrimes break; 1751590Srgrimes case 'N': 1761590Srgrimes nlistf = optarg; 17753279Speter dropgid = 1; 1781590Srgrimes break; 1791590Srgrimes case 'n': 1801590Srgrimes nflag = 1; 1811590Srgrimes break; 1821590Srgrimes case 'f': case 'l': case 's': case 'u': case 'w': 1831590Srgrimes warnx("[-flsuw] no longer supported"); 1841590Srgrimes /* FALLTHROUGH */ 1851590Srgrimes case '?': 1861590Srgrimes default: 1871590Srgrimes usage(wcmd); 1881590Srgrimes } 1891590Srgrimes argc -= optind; 1901590Srgrimes argv += optind; 1911590Srgrimes 1929573Speter if (!(_res.options & RES_INIT)) 1939573Speter res_init(); 1949573Speter _res.retrans = 2; /* resolver timeout to 2 seconds per try */ 1959573Speter _res.retry = 1; /* only try once.. */ 1969573Speter 19715993Spst /* 19815993Spst * Discard setgid privileges if not the running kernel so that bad 19915993Spst * guys can't print interesting stuff from kernel memory. 20015993Spst */ 20153279Speter if (dropgid) 20215993Spst setgid(getgid()); 20315993Spst 2041590Srgrimes if ((kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf)) == NULL) 2051590Srgrimes errx(1, "%s", errbuf); 2061590Srgrimes 2071590Srgrimes (void)time(&now); 2081590Srgrimes 2091590Srgrimes if (*argv) 21049177Sgreen sel_users = argv; 2111590Srgrimes 212200172Sed setutxent(); 213200172Sed for (nusers = 0; (utmp = getutxent()) != NULL;) { 214200172Sed if (utmp->ut_type != USER_PROCESS) 2151590Srgrimes continue; 216200172Sed if (!(stp = ttystat(utmp->ut_line))) 21745201Sbrian continue; /* corrupted record */ 2181590Srgrimes ++nusers; 21949177Sgreen if (wcmd == 0) 2201590Srgrimes continue; 22149177Sgreen if (sel_users) { 22249177Sgreen int usermatch; 22349177Sgreen char **user; 22449177Sgreen 22549177Sgreen usermatch = 0; 22649177Sgreen for (user = sel_users; !usermatch && *user; user++) 227200172Sed if (!strcmp(utmp->ut_user, *user)) 22849177Sgreen usermatch = 1; 22949177Sgreen if (!usermatch) 23049177Sgreen continue; 23149177Sgreen } 2321590Srgrimes if ((ep = calloc(1, sizeof(struct entry))) == NULL) 23328694Scharnier errx(1, "calloc"); 2341590Srgrimes *nextp = ep; 23549177Sgreen nextp = &ep->next; 236200172Sed memmove(&ep->utmp, utmp, sizeof *utmp); 2371590Srgrimes ep->tdev = stp->st_rdev; 2381590Srgrimes /* 2391590Srgrimes * If this is the console device, attempt to ascertain 2401590Srgrimes * the true console device dev_t. 2411590Srgrimes */ 2421590Srgrimes if (ep->tdev == 0) { 2431590Srgrimes size_t size; 2441590Srgrimes 2451590Srgrimes size = sizeof(dev_t); 246158444Sphk (void)sysctlbyname("machdep.consdev", &ep->tdev, &size, NULL, 0); 2471590Srgrimes } 24870242Sbrian touched = stp->st_atime; 249200172Sed if (touched < ep->utmp.ut_tv.tv_sec) { 25070242Sbrian /* tty untouched since before login */ 251200172Sed touched = ep->utmp.ut_tv.tv_sec; 25270242Sbrian } 25370242Sbrian if ((ep->idle = now - touched) < 0) 2541590Srgrimes ep->idle = 0; 2551590Srgrimes } 256200172Sed endutxent(); 2571590Srgrimes 2581590Srgrimes if (header || wcmd == 0) { 2591590Srgrimes pr_header(&now, nusers); 26077367Sphk if (wcmd == 0) { 26177367Sphk (void)kvm_close(kd); 26249177Sgreen exit(0); 26377367Sphk } 2641590Srgrimes 26536276Sjkoshy#define HEADER_USER "USER" 26636276Sjkoshy#define HEADER_TTY "TTY" 26736276Sjkoshy#define HEADER_FROM "FROM" 26836276Sjkoshy#define HEADER_LOGIN_IDLE "LOGIN@ IDLE " 26936276Sjkoshy#define HEADER_WHAT "WHAT\n" 270200172Sed#define WUSED (W_DISPUSERSIZE + W_DISPLINESIZE + W_DISPHOSTSIZE + \ 27136276Sjkoshy sizeof(HEADER_LOGIN_IDLE) + 3) /* header width incl. spaces */ 27236276Sjkoshy (void)printf("%-*.*s %-*.*s %-*.*s %s", 273200172Sed W_DISPUSERSIZE, W_DISPUSERSIZE, HEADER_USER, 274200172Sed W_DISPLINESIZE, W_DISPLINESIZE, HEADER_TTY, 27580407Sbrian W_DISPHOSTSIZE, W_DISPHOSTSIZE, HEADER_FROM, 27636276Sjkoshy HEADER_LOGIN_IDLE HEADER_WHAT); 2779555Speter } 2781590Srgrimes 2791590Srgrimes if ((kp = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nentries)) == NULL) 2801590Srgrimes err(1, "%s", kvm_geterr(kd)); 2811590Srgrimes for (i = 0; i < nentries; i++, kp++) { 282202199Sed if (kp->ki_stat == SIDL || kp->ki_stat == SZOMB || 283202199Sed kp->ki_tdev == NODEV) 2841590Srgrimes continue; 2851590Srgrimes for (ep = ehead; ep != NULL; ep = ep->next) { 28669896Smckusick if (ep->tdev == kp->ki_tdev) { 2871590Srgrimes /* 28829310Sache * proc is associated with this terminal 2891590Srgrimes */ 29069896Smckusick if (ep->kp == NULL && kp->ki_pgid == kp->ki_tpgid) { 29129310Sache /* 29229310Sache * Proc is 'most interesting' 29329310Sache */ 29469896Smckusick if (proc_compare(ep->kp, kp)) 29529310Sache ep->kp = kp; 29629310Sache } 29729310Sache /* 29829310Sache * Proc debug option info; add to debug 29969896Smckusick * list using kinfo_proc ki_spare[0] 30029310Sache * as next pointer; ptr to ptr avoids the 30129310Sache * ptr = long assumption. 30229310Sache */ 30329310Sache dkp = ep->dkp; 30429310Sache ep->dkp = kp; 30549177Sgreen debugproc(kp) = dkp; 3061590Srgrimes } 3071590Srgrimes } 3081590Srgrimes } 3091590Srgrimes if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 && 3101590Srgrimes ioctl(STDERR_FILENO, TIOCGWINSZ, &ws) == -1 && 3111590Srgrimes ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) || ws.ws_col == 0) 3121590Srgrimes ttywidth = 79; 3131590Srgrimes else 3141590Srgrimes ttywidth = ws.ws_col - 1; 3151590Srgrimes argwidth = ttywidth - WUSED; 3161590Srgrimes if (argwidth < 4) 3171590Srgrimes argwidth = 8; 3181590Srgrimes for (ep = ehead; ep != NULL; ep = ep->next) { 3191590Srgrimes if (ep->kp == NULL) { 32087674Smarkm ep->args = strdup("-"); 3211590Srgrimes continue; 3221590Srgrimes } 3231590Srgrimes ep->args = fmt_argv(kvm_getargv(kd, ep->kp, argwidth), 324247516Sjhb ep->kp->ki_comm, NULL, MAXCOMLEN); 3251590Srgrimes if (ep->args == NULL) 3261590Srgrimes err(1, NULL); 3271590Srgrimes } 3281590Srgrimes /* sort by idle time */ 3291590Srgrimes if (sortidle && ehead != NULL) { 33049177Sgreen struct entry *from, *save; 3318874Srgrimes 33249177Sgreen from = ehead; 3331590Srgrimes ehead = NULL; 3341590Srgrimes while (from != NULL) { 3351590Srgrimes for (nextp = &ehead; 3361590Srgrimes (*nextp) && from->idle >= (*nextp)->idle; 3371590Srgrimes nextp = &(*nextp)->next) 3381590Srgrimes continue; 3391590Srgrimes save = from; 3401590Srgrimes from = from->next; 3411590Srgrimes save->next = *nextp; 3421590Srgrimes *nextp = save; 3431590Srgrimes } 3441590Srgrimes } 3458874Srgrimes 3461590Srgrimes for (ep = ehead; ep != NULL; ep = ep->next) { 347199655Sume struct addrinfo hints, *res; 34880407Sbrian struct sockaddr_storage ss; 34980407Sbrian struct sockaddr *sa = (struct sockaddr *)&ss; 35087674Smarkm struct sockaddr_in *lsin = (struct sockaddr_in *)&ss; 35187674Smarkm struct sockaddr_in6 *lsin6 = (struct sockaddr_in6 *)&ss; 352116998Speter time_t t; 35380407Sbrian int isaddr; 35445946Sache 355200172Sed p = *ep->utmp.ut_host ? ep->utmp.ut_host : "-"; 35680407Sbrian if ((x_suffix = strrchr(p, ':')) != NULL) { 35780407Sbrian if ((dot = strchr(x_suffix, '.')) != NULL && 35880407Sbrian strchr(dot+1, '.') == NULL) 35980407Sbrian *x_suffix++ = '\0'; 36080407Sbrian else 36180407Sbrian x_suffix = NULL; 3621590Srgrimes } 363199655Sume 364199655Sume isaddr = 0; 365199655Sume memset(&ss, '\0', sizeof(ss)); 366199655Sume if (inet_pton(AF_INET6, p, &lsin6->sin6_addr) == 1) { 367199655Sume lsin6->sin6_len = sizeof(*lsin6); 368199655Sume lsin6->sin6_family = AF_INET6; 369199655Sume isaddr = 1; 370199655Sume } else if (inet_pton(AF_INET, p, &lsin->sin_addr) == 1) { 371199655Sume lsin->sin_len = sizeof(*lsin); 372199655Sume lsin->sin_family = AF_INET; 373199655Sume isaddr = 1; 374199655Sume } 37580407Sbrian if (!nflag) { 37680407Sbrian /* Attempt to change an IP address into a name */ 37780407Sbrian if (isaddr && realhostname_sa(fn, sizeof(fn), sa, 37880407Sbrian sa->sa_len) == HOSTNAME_FOUND) 37980407Sbrian p = fn; 380199655Sume } else if (!isaddr) { 381199655Sume /* 382199655Sume * If a host has only one A/AAAA RR, change a 383199655Sume * name into an IP address 384199655Sume */ 385199655Sume memset(&hints, 0, sizeof(hints)); 386199655Sume hints.ai_flags = AI_PASSIVE; 387199655Sume hints.ai_family = AF_UNSPEC; 388199655Sume hints.ai_socktype = SOCK_STREAM; 389199655Sume if (getaddrinfo(p, NULL, &hints, &res) == 0) { 390199655Sume if (res->ai_next == NULL && 391199655Sume getnameinfo(res->ai_addr, res->ai_addrlen, 392199655Sume fn, sizeof(fn), NULL, 0, 393199655Sume NI_NUMERICHOST) == 0) 394199655Sume p = fn; 395199655Sume freeaddrinfo(res); 396199655Sume } 39716436Sache } 398199655Sume 39980407Sbrian if (x_suffix) { 40080407Sbrian (void)snprintf(buf, sizeof(buf), "%s:%s", p, x_suffix); 4011590Srgrimes p = buf; 4021590Srgrimes } 40342481Speter if (dflag) { 40449177Sgreen for (dkp = ep->dkp; dkp != NULL; dkp = debugproc(dkp)) { 40587674Smarkm const char *ptr; 40649177Sgreen 40745201Sbrian ptr = fmt_argv(kvm_getargv(kd, dkp, argwidth), 408247516Sjhb dkp->ki_comm, NULL, MAXCOMLEN); 40945201Sbrian if (ptr == NULL) 41045201Sbrian ptr = "-"; 41149177Sgreen (void)printf("\t\t%-9d %s\n", 41269896Smckusick dkp->ki_pid, ptr); 41329310Sache } 41429310Sache } 41535309Sphk (void)printf("%-*.*s %-*.*s %-*.*s ", 416200172Sed W_DISPUSERSIZE, W_DISPUSERSIZE, ep->utmp.ut_user, 417200172Sed W_DISPLINESIZE, W_DISPLINESIZE, 418202199Sed *ep->utmp.ut_line ? 419202199Sed (strncmp(ep->utmp.ut_line, "tty", 3) && 4203138Sache strncmp(ep->utmp.ut_line, "cua", 3) ? 421202199Sed ep->utmp.ut_line : ep->utmp.ut_line + 3) : "-", 42280407Sbrian W_DISPHOSTSIZE, W_DISPHOSTSIZE, *p ? p : "-"); 423200172Sed t = ep->utmp.ut_tv.tv_sec; 424196652Sume longattime = pr_attime(&t, &now); 42542481Speter longidle = pr_idle(ep->idle); 426196652Sume (void)printf("%.*s\n", argwidth - longidle - longattime, 427196652Sume ep->args); 4281590Srgrimes } 42977367Sphk (void)kvm_close(kd); 4301590Srgrimes exit(0); 4311590Srgrimes} 4321590Srgrimes 4331590Srgrimesstatic void 43497981Sjmallettpr_header(time_t *nowp, int nusers) 4351590Srgrimes{ 4361590Srgrimes double avenrun[3]; 43791837Sobrien time_t uptime; 438151417Sandre struct timespec tp; 43930389Sache int days, hrs, i, mins, secs; 4401590Srgrimes char buf[256]; 4411590Srgrimes 4421590Srgrimes /* 4431590Srgrimes * Print time of day. 4441590Srgrimes */ 445119854Scharnier if (strftime(buf, sizeof(buf), 446119854Scharnier use_ampm ? "%l:%M%p" : "%k:%M", localtime(nowp)) != 0) 447119854Scharnier (void)printf("%s ", buf); 4481590Srgrimes /* 4491590Srgrimes * Print how long system has been up. 4501590Srgrimes */ 451243293Semaste if (clock_gettime(CLOCK_UPTIME, &tp) != -1) { 452151417Sandre uptime = tp.tv_sec; 45391837Sobrien if (uptime > 60) 45491837Sobrien uptime += 30; 45591837Sobrien days = uptime / 86400; 45691837Sobrien uptime %= 86400; 45791837Sobrien hrs = uptime / 3600; 45891837Sobrien uptime %= 3600; 45991837Sobrien mins = uptime / 60; 46091837Sobrien secs = uptime % 60; 4611590Srgrimes (void)printf(" up"); 4621590Srgrimes if (days > 0) 4631590Srgrimes (void)printf(" %d day%s,", days, days > 1 ? "s" : ""); 4641590Srgrimes if (hrs > 0 && mins > 0) 4651590Srgrimes (void)printf(" %2d:%02d,", hrs, mins); 46630389Sache else if (hrs > 0) 46749177Sgreen (void)printf(" %d hr%s,", hrs, hrs > 1 ? "s" : ""); 46830389Sache else if (mins > 0) 46949177Sgreen (void)printf(" %d min%s,", mins, mins > 1 ? "s" : ""); 47030389Sache else 47149177Sgreen (void)printf(" %d sec%s,", secs, secs > 1 ? "s" : ""); 4721590Srgrimes } 4731590Srgrimes 4741590Srgrimes /* Print number of users logged in to system */ 4756542Ssmace (void)printf(" %d user%s", nusers, nusers == 1 ? "" : "s"); 4761590Srgrimes 4771590Srgrimes /* 4781590Srgrimes * Print 1, 5, and 15 minute load averages. 4791590Srgrimes */ 4801590Srgrimes if (getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0])) == -1) 4811590Srgrimes (void)printf(", no load average information available\n"); 4821590Srgrimes else { 4831590Srgrimes (void)printf(", load averages:"); 48487674Smarkm for (i = 0; i < (int)(sizeof(avenrun) / sizeof(avenrun[0])); i++) { 48573385Sache if (use_comma && i > 0) 48673385Sache (void)printf(","); 48773385Sache (void)printf(" %.2f", avenrun[i]); 48873385Sache } 4891590Srgrimes (void)printf("\n"); 4901590Srgrimes } 4911590Srgrimes} 4921590Srgrimes 4931590Srgrimesstatic struct stat * 494200172Sedttystat(char *line) 4951590Srgrimes{ 4961590Srgrimes static struct stat sb; 4971590Srgrimes char ttybuf[MAXPATHLEN]; 4981590Srgrimes 499200172Sed (void)snprintf(ttybuf, sizeof(ttybuf), "%s%s", _PATH_DEV, line); 500223786Sed if (stat(ttybuf, &sb) == 0 && S_ISCHR(sb.st_mode)) { 501102300Sseanc return (&sb); 502102300Sseanc } else 50349177Sgreen return (NULL); 5041590Srgrimes} 5051590Srgrimes 5061590Srgrimesstatic void 50797981Sjmallettusage(int wcmd) 5081590Srgrimes{ 5091590Srgrimes if (wcmd) 5101590Srgrimes (void)fprintf(stderr, 51149177Sgreen "usage: w [-dhin] [-M core] [-N system] [user ...]\n"); 5121590Srgrimes else 51349177Sgreen (void)fprintf(stderr, "usage: uptime\n"); 51449177Sgreen exit(1); 5151590Srgrimes} 51642055Sdillon 51742055Sdillonstatic int 51897981Sjmallettthis_is_uptime(const char *s) 51942055Sdillon{ 52042055Sdillon const char *u; 52142055Sdillon 52242055Sdillon if ((u = strrchr(s, '/')) != NULL) 52342055Sdillon ++u; 52442055Sdillon else 52542055Sdillon u = s; 52642055Sdillon if (strcmp(u, "uptime") == 0) 52749177Sgreen return (0); 52849177Sgreen return (-1); 52942055Sdillon} 530