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