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 60253750Savg#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 88227199Sedstatic struct utmpx *utmp; 89227199Sedstatic struct winsize ws; 90227199Sedstatic kvm_t *kd; 91227199Sedstatic time_t now; /* the current time of day */ 92227199Sedstatic int ttywidth; /* width of tty */ 93227199Sedstatic int argwidth; /* width of tty */ 94227199Sedstatic int header = 1; /* true if -h flag: don't print heading */ 95227199Sedstatic int nflag; /* true if -n flag: don't convert addrs */ 96227199Sedstatic int dflag; /* true if -d flag: output debug info */ 97227199Sedstatic int sortidle; /* sort by idle time */ 9873365Sacheint use_ampm; /* use AM/PM time */ 99227199Sedstatic int use_comma; /* use comma as floats separator */ 100227199Sedstatic char **sel_users; /* login array of particular users selected */ 1011590Srgrimes 1021590Srgrimes/* 1031590Srgrimes * One of these per active utmp entry. 1041590Srgrimes */ 105227199Sedstatic struct entry { 1061590Srgrimes struct entry *next; 107200172Sed struct utmpx utmp; 10829310Sache dev_t tdev; /* dev_t of terminal */ 10929310Sache time_t idle; /* idle time of terminal in seconds */ 11029310Sache struct kinfo_proc *kp; /* `most interesting' proc */ 11129310Sache char *args; /* arg list of interesting process */ 11229310Sache struct kinfo_proc *dkp; /* debug option proc list */ 1131590Srgrimes} *ep, *ehead = NULL, **nextp = &ehead; 1141590Srgrimes 115201611Sdwmalone#define debugproc(p) *(&((struct kinfo_proc *)p)->ki_udata) 1161590Srgrimes 117200172Sed#define W_DISPUSERSIZE 10 118200172Sed#define W_DISPLINESIZE 8 119200172Sed#define W_DISPHOSTSIZE 24 12080407Sbrian 12192922Simpstatic void pr_header(time_t *, int); 122200172Sedstatic struct stat *ttystat(char *); 12392922Simpstatic void usage(int); 12492922Simpstatic int this_is_uptime(const char *s); 12549177Sgreen 126245635Sjhbchar *fmt_argv(char **, char *, char *, size_t); /* ../../bin/ps/fmt.c */ 1271590Srgrimes 1281590Srgrimesint 12997981Sjmallettmain(int argc, char *argv[]) 1301590Srgrimes{ 1311590Srgrimes struct kinfo_proc *kp; 13229310Sache struct kinfo_proc *dkp; 1331590Srgrimes struct stat *stp; 13470242Sbrian time_t touched; 135196652Sume int ch, i, nentries, nusers, wcmd, longidle, longattime, dropgid; 13687674Smarkm const char *memf, *nlistf, *p; 13787674Smarkm char *x_suffix; 13877212Stmm char buf[MAXHOSTNAMELEN], errbuf[_POSIX2_LINE_MAX]; 13980407Sbrian char fn[MAXHOSTNAMELEN]; 14080407Sbrian char *dot; 1411590Srgrimes 14249177Sgreen (void)setlocale(LC_ALL, ""); 14373365Sache use_ampm = (*nl_langinfo(T_FMT_AMPM) != '\0'); 14473385Sache use_comma = (*nl_langinfo(RADIXCHAR) != ','); 14511755Sache 1461590Srgrimes /* Are we w(1) or uptime(1)? */ 14742055Sdillon if (this_is_uptime(argv[0]) == 0) { 1481590Srgrimes wcmd = 0; 1491590Srgrimes p = ""; 1501590Srgrimes } else { 1511590Srgrimes wcmd = 1; 15229310Sache p = "dhiflM:N:nsuw"; 1531590Srgrimes } 1541590Srgrimes 15553279Speter dropgid = 0; 156203688Sbrucec memf = _PATH_DEVNULL; 157203688Sbrucec nlistf = NULL; 15824360Simp while ((ch = getopt(argc, argv, p)) != -1) 1591590Srgrimes switch (ch) { 16029310Sache case 'd': 16129310Sache dflag = 1; 16229310Sache break; 1631590Srgrimes case 'h': 1641590Srgrimes header = 0; 1651590Srgrimes break; 1661590Srgrimes case 'i': 1671590Srgrimes sortidle = 1; 1681590Srgrimes break; 1691590Srgrimes case 'M': 1701590Srgrimes header = 0; 1711590Srgrimes memf = optarg; 17253279Speter dropgid = 1; 1731590Srgrimes break; 1741590Srgrimes case 'N': 1751590Srgrimes nlistf = optarg; 17653279Speter dropgid = 1; 1771590Srgrimes break; 1781590Srgrimes case 'n': 1791590Srgrimes nflag = 1; 1801590Srgrimes break; 1811590Srgrimes case 'f': case 'l': case 's': case 'u': case 'w': 1821590Srgrimes warnx("[-flsuw] no longer supported"); 1831590Srgrimes /* FALLTHROUGH */ 1841590Srgrimes case '?': 1851590Srgrimes default: 1861590Srgrimes usage(wcmd); 1871590Srgrimes } 1881590Srgrimes argc -= optind; 1891590Srgrimes argv += optind; 1901590Srgrimes 1919573Speter if (!(_res.options & RES_INIT)) 1929573Speter res_init(); 1939573Speter _res.retrans = 2; /* resolver timeout to 2 seconds per try */ 1949573Speter _res.retry = 1; /* only try once.. */ 1959573Speter 19615993Spst /* 19715993Spst * Discard setgid privileges if not the running kernel so that bad 19815993Spst * guys can't print interesting stuff from kernel memory. 19915993Spst */ 20053279Speter if (dropgid) 20115993Spst setgid(getgid()); 20215993Spst 2031590Srgrimes if ((kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf)) == NULL) 2041590Srgrimes errx(1, "%s", errbuf); 2051590Srgrimes 2061590Srgrimes (void)time(&now); 2071590Srgrimes 2081590Srgrimes if (*argv) 20949177Sgreen sel_users = argv; 2101590Srgrimes 211200172Sed setutxent(); 212200172Sed for (nusers = 0; (utmp = getutxent()) != NULL;) { 213200172Sed if (utmp->ut_type != USER_PROCESS) 2141590Srgrimes continue; 215200172Sed if (!(stp = ttystat(utmp->ut_line))) 21645201Sbrian continue; /* corrupted record */ 2171590Srgrimes ++nusers; 21849177Sgreen if (wcmd == 0) 2191590Srgrimes continue; 22049177Sgreen if (sel_users) { 22149177Sgreen int usermatch; 22249177Sgreen char **user; 22349177Sgreen 22449177Sgreen usermatch = 0; 22549177Sgreen for (user = sel_users; !usermatch && *user; user++) 226200172Sed if (!strcmp(utmp->ut_user, *user)) 22749177Sgreen usermatch = 1; 22849177Sgreen if (!usermatch) 22949177Sgreen continue; 23049177Sgreen } 2311590Srgrimes if ((ep = calloc(1, sizeof(struct entry))) == NULL) 23228694Scharnier errx(1, "calloc"); 2331590Srgrimes *nextp = ep; 23449177Sgreen nextp = &ep->next; 235200172Sed memmove(&ep->utmp, utmp, sizeof *utmp); 2361590Srgrimes ep->tdev = stp->st_rdev; 2371590Srgrimes /* 2381590Srgrimes * If this is the console device, attempt to ascertain 2391590Srgrimes * the true console device dev_t. 2401590Srgrimes */ 2411590Srgrimes if (ep->tdev == 0) { 2421590Srgrimes size_t size; 2431590Srgrimes 2441590Srgrimes size = sizeof(dev_t); 245158444Sphk (void)sysctlbyname("machdep.consdev", &ep->tdev, &size, NULL, 0); 2461590Srgrimes } 24770242Sbrian touched = stp->st_atime; 248200172Sed if (touched < ep->utmp.ut_tv.tv_sec) { 24970242Sbrian /* tty untouched since before login */ 250200172Sed touched = ep->utmp.ut_tv.tv_sec; 25170242Sbrian } 25270242Sbrian if ((ep->idle = now - touched) < 0) 2531590Srgrimes ep->idle = 0; 2541590Srgrimes } 255200172Sed endutxent(); 2561590Srgrimes 2571590Srgrimes if (header || wcmd == 0) { 2581590Srgrimes pr_header(&now, nusers); 25977367Sphk if (wcmd == 0) { 26077367Sphk (void)kvm_close(kd); 26149177Sgreen exit(0); 26277367Sphk } 2631590Srgrimes 26436276Sjkoshy#define HEADER_USER "USER" 26536276Sjkoshy#define HEADER_TTY "TTY" 26636276Sjkoshy#define HEADER_FROM "FROM" 26736276Sjkoshy#define HEADER_LOGIN_IDLE "LOGIN@ IDLE " 26836276Sjkoshy#define HEADER_WHAT "WHAT\n" 269200172Sed#define WUSED (W_DISPUSERSIZE + W_DISPLINESIZE + W_DISPHOSTSIZE + \ 27036276Sjkoshy sizeof(HEADER_LOGIN_IDLE) + 3) /* header width incl. spaces */ 27136276Sjkoshy (void)printf("%-*.*s %-*.*s %-*.*s %s", 272200172Sed W_DISPUSERSIZE, W_DISPUSERSIZE, HEADER_USER, 273200172Sed W_DISPLINESIZE, W_DISPLINESIZE, HEADER_TTY, 27480407Sbrian W_DISPHOSTSIZE, W_DISPHOSTSIZE, HEADER_FROM, 27536276Sjkoshy HEADER_LOGIN_IDLE HEADER_WHAT); 2769555Speter } 2771590Srgrimes 2781590Srgrimes if ((kp = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nentries)) == NULL) 2791590Srgrimes err(1, "%s", kvm_geterr(kd)); 2801590Srgrimes for (i = 0; i < nentries; i++, kp++) { 281202199Sed if (kp->ki_stat == SIDL || kp->ki_stat == SZOMB || 282202199Sed kp->ki_tdev == NODEV) 2831590Srgrimes continue; 2841590Srgrimes for (ep = ehead; ep != NULL; ep = ep->next) { 28569896Smckusick if (ep->tdev == kp->ki_tdev) { 2861590Srgrimes /* 28729310Sache * proc is associated with this terminal 2881590Srgrimes */ 28969896Smckusick if (ep->kp == NULL && kp->ki_pgid == kp->ki_tpgid) { 29029310Sache /* 29129310Sache * Proc is 'most interesting' 29229310Sache */ 29369896Smckusick if (proc_compare(ep->kp, kp)) 29429310Sache ep->kp = kp; 29529310Sache } 29629310Sache /* 29729310Sache * Proc debug option info; add to debug 29869896Smckusick * list using kinfo_proc ki_spare[0] 29929310Sache * as next pointer; ptr to ptr avoids the 30029310Sache * ptr = long assumption. 30129310Sache */ 30229310Sache dkp = ep->dkp; 30329310Sache ep->dkp = kp; 30449177Sgreen debugproc(kp) = dkp; 3051590Srgrimes } 3061590Srgrimes } 3071590Srgrimes } 3081590Srgrimes if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 && 3091590Srgrimes ioctl(STDERR_FILENO, TIOCGWINSZ, &ws) == -1 && 3101590Srgrimes ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) || ws.ws_col == 0) 3111590Srgrimes ttywidth = 79; 3121590Srgrimes else 3131590Srgrimes ttywidth = ws.ws_col - 1; 3141590Srgrimes argwidth = ttywidth - WUSED; 3151590Srgrimes if (argwidth < 4) 3161590Srgrimes argwidth = 8; 3171590Srgrimes for (ep = ehead; ep != NULL; ep = ep->next) { 3181590Srgrimes if (ep->kp == NULL) { 31987674Smarkm ep->args = strdup("-"); 3201590Srgrimes continue; 3211590Srgrimes } 3221590Srgrimes ep->args = fmt_argv(kvm_getargv(kd, ep->kp, argwidth), 323245635Sjhb ep->kp->ki_comm, NULL, MAXCOMLEN); 3241590Srgrimes if (ep->args == NULL) 3251590Srgrimes err(1, NULL); 3261590Srgrimes } 3271590Srgrimes /* sort by idle time */ 3281590Srgrimes if (sortidle && ehead != NULL) { 32949177Sgreen struct entry *from, *save; 3308874Srgrimes 33149177Sgreen from = ehead; 3321590Srgrimes ehead = NULL; 3331590Srgrimes while (from != NULL) { 3341590Srgrimes for (nextp = &ehead; 3351590Srgrimes (*nextp) && from->idle >= (*nextp)->idle; 3361590Srgrimes nextp = &(*nextp)->next) 3371590Srgrimes continue; 3381590Srgrimes save = from; 3391590Srgrimes from = from->next; 3401590Srgrimes save->next = *nextp; 3411590Srgrimes *nextp = save; 3421590Srgrimes } 3431590Srgrimes } 3448874Srgrimes 3451590Srgrimes for (ep = ehead; ep != NULL; ep = ep->next) { 346199655Sume struct addrinfo hints, *res; 34780407Sbrian struct sockaddr_storage ss; 34880407Sbrian struct sockaddr *sa = (struct sockaddr *)&ss; 34987674Smarkm struct sockaddr_in *lsin = (struct sockaddr_in *)&ss; 35087674Smarkm struct sockaddr_in6 *lsin6 = (struct sockaddr_in6 *)&ss; 351116998Speter time_t t; 35280407Sbrian int isaddr; 35345946Sache 354200172Sed p = *ep->utmp.ut_host ? ep->utmp.ut_host : "-"; 35580407Sbrian if ((x_suffix = strrchr(p, ':')) != NULL) { 35680407Sbrian if ((dot = strchr(x_suffix, '.')) != NULL && 35780407Sbrian strchr(dot+1, '.') == NULL) 35880407Sbrian *x_suffix++ = '\0'; 35980407Sbrian else 36080407Sbrian x_suffix = NULL; 3611590Srgrimes } 362199655Sume 363199655Sume isaddr = 0; 364199655Sume memset(&ss, '\0', sizeof(ss)); 365199655Sume if (inet_pton(AF_INET6, p, &lsin6->sin6_addr) == 1) { 366199655Sume lsin6->sin6_len = sizeof(*lsin6); 367199655Sume lsin6->sin6_family = AF_INET6; 368199655Sume isaddr = 1; 369199655Sume } else if (inet_pton(AF_INET, p, &lsin->sin_addr) == 1) { 370199655Sume lsin->sin_len = sizeof(*lsin); 371199655Sume lsin->sin_family = AF_INET; 372199655Sume isaddr = 1; 373199655Sume } 37480407Sbrian if (!nflag) { 37580407Sbrian /* Attempt to change an IP address into a name */ 37680407Sbrian if (isaddr && realhostname_sa(fn, sizeof(fn), sa, 37780407Sbrian sa->sa_len) == HOSTNAME_FOUND) 37880407Sbrian p = fn; 379199655Sume } else if (!isaddr) { 380199655Sume /* 381199655Sume * If a host has only one A/AAAA RR, change a 382199655Sume * name into an IP address 383199655Sume */ 384199655Sume memset(&hints, 0, sizeof(hints)); 385199655Sume hints.ai_flags = AI_PASSIVE; 386199655Sume hints.ai_family = AF_UNSPEC; 387199655Sume hints.ai_socktype = SOCK_STREAM; 388199655Sume if (getaddrinfo(p, NULL, &hints, &res) == 0) { 389199655Sume if (res->ai_next == NULL && 390199655Sume getnameinfo(res->ai_addr, res->ai_addrlen, 391199655Sume fn, sizeof(fn), NULL, 0, 392199655Sume NI_NUMERICHOST) == 0) 393199655Sume p = fn; 394199655Sume freeaddrinfo(res); 395199655Sume } 39616436Sache } 397199655Sume 39880407Sbrian if (x_suffix) { 39980407Sbrian (void)snprintf(buf, sizeof(buf), "%s:%s", p, x_suffix); 4001590Srgrimes p = buf; 4011590Srgrimes } 40242481Speter if (dflag) { 40349177Sgreen for (dkp = ep->dkp; dkp != NULL; dkp = debugproc(dkp)) { 40487674Smarkm const char *ptr; 40549177Sgreen 40645201Sbrian ptr = fmt_argv(kvm_getargv(kd, dkp, argwidth), 407245635Sjhb dkp->ki_comm, NULL, MAXCOMLEN); 40845201Sbrian if (ptr == NULL) 40945201Sbrian ptr = "-"; 41049177Sgreen (void)printf("\t\t%-9d %s\n", 41169896Smckusick dkp->ki_pid, ptr); 41229310Sache } 41329310Sache } 41435309Sphk (void)printf("%-*.*s %-*.*s %-*.*s ", 415200172Sed W_DISPUSERSIZE, W_DISPUSERSIZE, ep->utmp.ut_user, 416200172Sed W_DISPLINESIZE, W_DISPLINESIZE, 417202199Sed *ep->utmp.ut_line ? 418202199Sed (strncmp(ep->utmp.ut_line, "tty", 3) && 4193138Sache strncmp(ep->utmp.ut_line, "cua", 3) ? 420202199Sed ep->utmp.ut_line : ep->utmp.ut_line + 3) : "-", 42180407Sbrian W_DISPHOSTSIZE, W_DISPHOSTSIZE, *p ? p : "-"); 422200172Sed t = ep->utmp.ut_tv.tv_sec; 423196652Sume longattime = pr_attime(&t, &now); 42442481Speter longidle = pr_idle(ep->idle); 425196652Sume (void)printf("%.*s\n", argwidth - longidle - longattime, 426196652Sume ep->args); 4271590Srgrimes } 42877367Sphk (void)kvm_close(kd); 4291590Srgrimes exit(0); 4301590Srgrimes} 4311590Srgrimes 4321590Srgrimesstatic void 43397981Sjmallettpr_header(time_t *nowp, int nusers) 4341590Srgrimes{ 4351590Srgrimes double avenrun[3]; 43691837Sobrien time_t uptime; 437151417Sandre struct timespec tp; 43830389Sache int days, hrs, i, mins, secs; 4391590Srgrimes char buf[256]; 4401590Srgrimes 4411590Srgrimes /* 4421590Srgrimes * Print time of day. 4431590Srgrimes */ 444119854Scharnier if (strftime(buf, sizeof(buf), 445119854Scharnier use_ampm ? "%l:%M%p" : "%k:%M", localtime(nowp)) != 0) 446119854Scharnier (void)printf("%s ", buf); 4471590Srgrimes /* 4481590Srgrimes * Print how long system has been up. 4491590Srgrimes */ 450241484Semaste if (clock_gettime(CLOCK_UPTIME, &tp) != -1) { 451151417Sandre uptime = tp.tv_sec; 45291837Sobrien if (uptime > 60) 45391837Sobrien uptime += 30; 45491837Sobrien days = uptime / 86400; 45591837Sobrien uptime %= 86400; 45691837Sobrien hrs = uptime / 3600; 45791837Sobrien uptime %= 3600; 45891837Sobrien mins = uptime / 60; 45991837Sobrien secs = uptime % 60; 4601590Srgrimes (void)printf(" up"); 4611590Srgrimes if (days > 0) 4621590Srgrimes (void)printf(" %d day%s,", days, days > 1 ? "s" : ""); 4631590Srgrimes if (hrs > 0 && mins > 0) 4641590Srgrimes (void)printf(" %2d:%02d,", hrs, mins); 46530389Sache else if (hrs > 0) 46649177Sgreen (void)printf(" %d hr%s,", hrs, hrs > 1 ? "s" : ""); 46730389Sache else if (mins > 0) 46849177Sgreen (void)printf(" %d min%s,", mins, mins > 1 ? "s" : ""); 46930389Sache else 47049177Sgreen (void)printf(" %d sec%s,", secs, secs > 1 ? "s" : ""); 4711590Srgrimes } 4721590Srgrimes 4731590Srgrimes /* Print number of users logged in to system */ 4746542Ssmace (void)printf(" %d user%s", nusers, nusers == 1 ? "" : "s"); 4751590Srgrimes 4761590Srgrimes /* 4771590Srgrimes * Print 1, 5, and 15 minute load averages. 4781590Srgrimes */ 4791590Srgrimes if (getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0])) == -1) 4801590Srgrimes (void)printf(", no load average information available\n"); 4811590Srgrimes else { 4821590Srgrimes (void)printf(", load averages:"); 48387674Smarkm for (i = 0; i < (int)(sizeof(avenrun) / sizeof(avenrun[0])); i++) { 48473385Sache if (use_comma && i > 0) 48573385Sache (void)printf(","); 48673385Sache (void)printf(" %.2f", avenrun[i]); 48773385Sache } 4881590Srgrimes (void)printf("\n"); 4891590Srgrimes } 4901590Srgrimes} 4911590Srgrimes 4921590Srgrimesstatic struct stat * 493200172Sedttystat(char *line) 4941590Srgrimes{ 4951590Srgrimes static struct stat sb; 4961590Srgrimes char ttybuf[MAXPATHLEN]; 4971590Srgrimes 498200172Sed (void)snprintf(ttybuf, sizeof(ttybuf), "%s%s", _PATH_DEV, line); 499223786Sed if (stat(ttybuf, &sb) == 0 && S_ISCHR(sb.st_mode)) { 500102300Sseanc return (&sb); 501102300Sseanc } else 50249177Sgreen return (NULL); 5031590Srgrimes} 5041590Srgrimes 5051590Srgrimesstatic void 50697981Sjmallettusage(int wcmd) 5071590Srgrimes{ 5081590Srgrimes if (wcmd) 5091590Srgrimes (void)fprintf(stderr, 51049177Sgreen "usage: w [-dhin] [-M core] [-N system] [user ...]\n"); 5111590Srgrimes else 51249177Sgreen (void)fprintf(stderr, "usage: uptime\n"); 51349177Sgreen exit(1); 5141590Srgrimes} 51542055Sdillon 51642055Sdillonstatic int 51797981Sjmallettthis_is_uptime(const char *s) 51842055Sdillon{ 51942055Sdillon const char *u; 52042055Sdillon 52142055Sdillon if ((u = strrchr(s, '/')) != NULL) 52242055Sdillon ++u; 52342055Sdillon else 52442055Sdillon u = s; 52542055Sdillon if (strcmp(u, "uptime") == 0) 52649177Sgreen return (0); 52749177Sgreen return (-1); 52842055Sdillon} 529