1 /*- 2 * Copyright (c) 1980, 1991, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of the University nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * @(#) Copyright (c) 1980, 1991, 1993, 1994 The Regents of the University of California. All rights reserved. 30 * @(#)w.c 8.4 (Berkeley) 4/16/94 31 * $FreeBSD: src/usr.bin/w/w.c,v 1.38.2.6 2002/03/12 19:51:51 phantom Exp $ 32 */ 33 34 /* 35 * w - print system status (who and what) 36 * 37 * This program is similar to the systat command on Tenex/Tops 10/20 38 * 39 */ 40 #include <sys/user.h> 41 #include <sys/param.h> 42 #include <sys/time.h> 43 #include <sys/stat.h> 44 #include <sys/sysctl.h> 45 #include <sys/ioctl.h> 46 #include <sys/socket.h> 47 #include <sys/tty.h> 48 49 #include <machine/cpu.h> 50 #include <netinet/in.h> 51 #include <arpa/inet.h> 52 53 #include <ctype.h> 54 #include <err.h> 55 #include <errno.h> 56 #include <fcntl.h> 57 #include <kvm.h> 58 #include <limits.h> 59 #include <langinfo.h> 60 #include <locale.h> 61 #include <netdb.h> 62 #include <nlist.h> 63 #include <paths.h> 64 #include <stdio.h> 65 #include <stdlib.h> 66 #include <string.h> 67 #include <unistd.h> 68 #include <utmpx.h> 69 #include <vis.h> 70 71 #include <arpa/nameser.h> 72 #include <resolv.h> 73 74 #include "extern.h" 75 76 static struct timeval boottime; 77 static struct winsize ws; 78 static kvm_t *kd; 79 static time_t now; /* the current time of day */ 80 static time_t then; 81 static int ttywidth; /* width of tty */ 82 static int argwidth; /* width of tty */ 83 static int header = 1; /* true if -h flag: don't print heading */ 84 static int nflag; /* true if -n flag: don't convert addrs */ 85 static int dflag; /* true if -d flag: output debug info */ 86 static int sortidle; /* sort by idle time */ 87 int use_ampm; /* use AM/PM time */ 88 static int use_comma; /* use comma as floats separator */ 89 static char **sel_users; /* login array of particular users selected */ 90 static char domain[MAXHOSTNAMELEN]; 91 static int maxname = 8, maxline = 3, maxhost = 16; 92 93 /* 94 * One of these per active utmpx entry. 95 */ 96 static struct entry { 97 struct entry *next; 98 char name[UTX_USERSIZE + 1]; 99 char line[UTX_LINESIZE + 1]; 100 char host[UTX_HOSTSIZE + 1]; 101 char type[2]; 102 struct timeval tv; 103 dev_t tdev; /* dev_t of terminal */ 104 time_t idle; /* idle time of terminal in seconds */ 105 struct kinfo_proc *kp; /* `most interesting' proc */ 106 char *args; /* arg list of interesting process */ 107 struct kinfo_proc *dkp; /* debug option proc list */ 108 pid_t pid; /* pid or ~0 if not known */ 109 } *ep, *ehead = NULL, **nextp = &ehead; 110 111 #define debugproc(p) *((struct kinfo_proc **)&(p)->kp_spare[0]) 112 113 static void pr_header(time_t *, int); 114 static struct stat *ttystat(char *, int); 115 static void process(struct entry *); 116 static void usage(int); 117 static int this_is_uptime(const char *s); 118 119 char *fmt_argv(char **, char *, int); /* ../../bin/ps/fmt.c */ 120 121 int 122 main(int argc, char **argv) 123 { 124 struct kinfo_proc *kp; 125 struct kinfo_proc *dkp; 126 struct hostent *hp; 127 in_addr_t l; 128 int ch, i, nentries, nusers, wcmd, longidle, dropgid; 129 char *memf, *nlistf, *p, *x; 130 struct utmpx *utx; 131 char buf[MAXHOSTNAMELEN], errbuf[_POSIX2_LINE_MAX]; 132 133 (void)setlocale(LC_ALL, ""); 134 use_ampm = (*nl_langinfo(T_FMT_AMPM) != '\0'); 135 use_comma = (*nl_langinfo(RADIXCHAR) != ','); 136 137 /* Are we w(1) or uptime(1)? */ 138 if (this_is_uptime(argv[0]) == 0) { 139 wcmd = 0; 140 p = ""; 141 } else { 142 wcmd = 1; 143 p = "dhiflM:N:nsuw"; 144 } 145 146 dropgid = 0; 147 memf = nlistf = _PATH_DEVNULL; 148 while ((ch = getopt(argc, argv, p)) != -1) 149 switch (ch) { 150 case 'd': 151 dflag = 1; 152 break; 153 case 'h': 154 header = 0; 155 break; 156 case 'i': 157 sortidle = 1; 158 break; 159 case 'M': 160 header = 0; 161 memf = optarg; 162 dropgid = 1; 163 break; 164 case 'N': 165 nlistf = optarg; 166 dropgid = 1; 167 break; 168 case 'n': 169 nflag = 1; 170 break; 171 case 'f': case 'l': case 's': case 'u': case 'w': 172 warnx("[-flsuw] no longer supported"); 173 /* FALLTHROUGH */ 174 case '?': 175 default: 176 usage(wcmd); 177 } 178 argc -= optind; 179 argv += optind; 180 181 if (!(_res.options & RES_INIT)) 182 res_init(); 183 _res.retrans = 2; /* resolver timeout to 2 seconds per try */ 184 _res.retry = 1; /* only try once.. */ 185 186 /* 187 * Discard setgid privileges if not the running kernel so that bad 188 * guys can't print interesting stuff from kernel memory. 189 */ 190 if (dropgid) 191 setgid(getgid()); 192 193 if ((kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf)) == NULL) 194 errx(1, "%s", errbuf); 195 196 (void)time(&now); 197 setutxent(); 198 199 if (*argv) 200 sel_users = argv; 201 202 nusers = 0; 203 while ((utx = getutxent()) != NULL) { 204 if (utx->ut_type != USER_PROCESS) 205 continue; 206 ++nusers; 207 208 if (sel_users) { 209 int usermatch; 210 char **user; 211 212 usermatch = 0; 213 for (user = sel_users; !usermatch && *user; user++) 214 if (!strncmp(utx->ut_name, *user, UTX_USERSIZE)) 215 usermatch = 1; 216 if (!usermatch) 217 continue; 218 } 219 220 if ((ep = calloc(1, sizeof(struct entry))) == NULL) 221 err(1, NULL); 222 (void)memcpy(ep->name, utx->ut_name, sizeof(utx->ut_name)); 223 (void)memcpy(ep->line, utx->ut_line, sizeof(utx->ut_line)); 224 (void)memcpy(ep->host, utx->ut_host, sizeof(utx->ut_host)); 225 ep->name[sizeof(utx->ut_name)] = '\0'; 226 ep->line[sizeof(utx->ut_line)] = '\0'; 227 ep->host[sizeof(utx->ut_host)] = '\0'; 228 #if 1 229 /* XXX: Actually we don't support the utx->ut_ss stuff yet */ 230 if (!nflag || getnameinfo((struct sockaddr *)&utx->ut_ss, 231 utx->ut_ss.ss_len, ep->host, sizeof(ep->host), NULL, 0, 232 NI_NUMERICHOST) != 0) { 233 (void)memcpy(ep->host, utx->ut_host, 234 sizeof(utx->ut_host)); 235 ep->host[sizeof(utx->ut_host)] = '\0'; 236 } 237 #endif 238 ep->type[0] = 'x'; 239 ep->tv = utx->ut_tv; 240 ep->pid = utx->ut_pid; 241 242 *nextp = ep; 243 nextp = &(ep->next); 244 if (wcmd != 0) 245 process(ep); 246 } 247 endutxent(); 248 249 if (header || wcmd == 0) { 250 pr_header(&now, nusers); 251 if (wcmd == 0) { 252 (void)kvm_close(kd); 253 exit(0); 254 } 255 256 #define HEADER_USER "USER" 257 #define HEADER_TTY "TTY" 258 #define HEADER_FROM "FROM" 259 #define HEADER_LOGIN_IDLE "LOGIN@ IDLE " 260 #define HEADER_WHAT "WHAT\n" 261 #define WUSED (maxname + maxline + maxhost + \ 262 sizeof(HEADER_LOGIN_IDLE) + 3) /* header width incl. spaces */ 263 (void)printf("%-*.*s %-*.*s %-*.*s %s", 264 maxname, maxname, HEADER_USER, 265 maxline, maxline, HEADER_TTY, 266 maxhost, maxhost, HEADER_FROM, 267 HEADER_LOGIN_IDLE HEADER_WHAT); 268 } 269 270 if ((kp = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nentries)) == NULL) 271 err(1, "%s", kvm_geterr(kd)); 272 for (i = 0; i < nentries; i++, kp++) { 273 if (kp->kp_stat == SIDL || kp->kp_stat == SZOMB) 274 continue; 275 for (ep = ehead; ep != NULL; ep = ep->next) { 276 if (ep->tdev == kp->kp_tdev) { 277 /* 278 * proc is associated with this terminal 279 */ 280 if (ep->kp == NULL && kp->kp_pgid == kp->kp_tpgid) { 281 /* 282 * Proc is 'most interesting' 283 */ 284 if (proc_compare(ep->kp, kp)) 285 ep->kp = kp; 286 } 287 /* 288 * Proc debug option info; add to debug 289 * list using kinfo_proc kp_eproc.e_spare 290 * as next pointer; ptr to ptr avoids the 291 * ptr = long assumption. 292 */ 293 dkp = ep->dkp; 294 ep->dkp = kp; 295 debugproc(kp) = dkp; 296 } 297 if (ep->pid != 0 && ep->pid == kp->kp_pid) { 298 ep->kp = kp; 299 break; 300 } 301 } 302 } 303 if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 && 304 ioctl(STDERR_FILENO, TIOCGWINSZ, &ws) == -1 && 305 ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) || ws.ws_col == 0) 306 ttywidth = 79; 307 else 308 ttywidth = ws.ws_col - 1; 309 argwidth = ttywidth - WUSED; 310 if (argwidth < 4) 311 argwidth = 8; 312 for (ep = ehead; ep != NULL; ep = ep->next) { 313 if (ep->kp == NULL) { 314 ep->args = "-"; 315 continue; 316 } 317 ep->args = fmt_argv(kvm_getargv(kd, ep->kp, argwidth), 318 ep->kp->kp_comm, MAXCOMLEN); 319 if (ep->args == NULL) 320 err(1, NULL); 321 } 322 /* sort by idle time */ 323 if (sortidle && ehead != NULL) { 324 struct entry *from, *save; 325 326 from = ehead; 327 ehead = NULL; 328 while (from != NULL) { 329 for (nextp = &ehead; 330 (*nextp) && from->idle >= (*nextp)->idle; 331 nextp = &(*nextp)->next) 332 continue; 333 save = from; 334 from = from->next; 335 save->next = *nextp; 336 *nextp = save; 337 } 338 } else if (ehead != NULL) { 339 struct entry *from = ehead, *save; 340 341 ehead = NULL; 342 while (from != NULL) { 343 for (nextp = &ehead; 344 (*nextp) && strcmp(from->line, (*nextp)->line) > 0; 345 nextp = &(*nextp)->next) 346 continue; 347 save = from; 348 from = from->next; 349 save->next = *nextp; 350 *nextp = save; 351 } 352 } 353 354 if (!nflag) { 355 if (gethostname(domain, sizeof(domain)) < 0 || 356 (p = strchr(domain, '.')) == NULL) 357 domain[0] = '\0'; 358 else { 359 domain[sizeof(domain) - 1] = '\0'; 360 memmove(domain, p, strlen(p) + 1); 361 } 362 } 363 364 for (ep = ehead; ep != NULL; ep = ep->next) { 365 char host_buf[UTX_HOSTSIZE + 1]; 366 367 host_buf[UTX_HOSTSIZE] = '\0'; 368 strncpy(host_buf, ep->host, maxhost); 369 p = *host_buf ? host_buf : "-"; 370 if ((x = strchr(p, ':')) != NULL) 371 *x++ = '\0'; 372 if (!nflag && isdigit(*p) && 373 (l = inet_addr(p)) != INADDR_NONE && 374 (hp = gethostbyaddr(&l, sizeof(l), AF_INET))) { 375 if (domain[0] != '\0') { 376 p = hp->h_name; 377 p += strlen(hp->h_name); 378 p -= strlen(domain); 379 if (p > hp->h_name && 380 strcasecmp(p, domain) == 0) 381 *p = '\0'; 382 } 383 p = hp->h_name; 384 } 385 if (nflag && *p && strcmp(p, "-") && 386 inet_addr(p) == INADDR_NONE) { 387 hp = gethostbyname(p); 388 389 if (hp != NULL) { 390 struct in_addr in; 391 392 memmove(&in, hp->h_addr, sizeof(in)); 393 p = inet_ntoa(in); 394 } 395 } 396 if (x) { 397 (void)snprintf(buf, sizeof(buf), "%s:%s", p, x); 398 p = buf; 399 } 400 if (dflag) { 401 for (dkp = ep->dkp; dkp != NULL; dkp = debugproc(dkp)) { 402 char *ptr; 403 404 ptr = fmt_argv(kvm_getargv(kd, dkp, argwidth), 405 dkp->kp_comm, MAXCOMLEN); 406 if (ptr == NULL) 407 ptr = "-"; 408 (void)printf("\t\t%-9d %s\n", 409 dkp->kp_pid, ptr); 410 } 411 } 412 (void)printf("%-*.*s %-*.*s %-*.*s ", 413 maxname, maxname, ep->name, 414 maxline, maxline, 415 strncmp(ep->line, "tty", 3) && 416 strncmp(ep->line, "cua", 3) ? 417 ep->line : ep->line + 3, 418 maxhost, maxhost, *p ? p : "-"); 419 then = (time_t)ep->tv.tv_sec; 420 pr_attime(&then, &now); 421 longidle = pr_idle(ep->idle); 422 (void)printf("%.*s\n", argwidth - longidle, ep->args); 423 } 424 (void)kvm_close(kd); 425 exit(0); 426 } 427 428 static void 429 pr_header(time_t *nowp, int nusers) 430 { 431 double avenrun[3]; 432 time_t uptime; 433 int days, hrs, i, mins, secs; 434 int mib[2]; 435 size_t size; 436 char buf[256]; 437 438 /* 439 * Print time of day. (use "AM"/"PM" for all locales) 440 */ 441 (void)strftime_l(buf, sizeof(buf) - 1, 442 use_ampm ? "%l:%M%p" : "%k:%M", 443 localtime(nowp), NULL); 444 buf[sizeof(buf) - 1] = '\0'; 445 (void)printf("%s ", buf); 446 447 /* 448 * Print how long system has been up. 449 * (Found by looking at "boottime" from the kernel) 450 */ 451 mib[0] = CTL_KERN; 452 mib[1] = KERN_BOOTTIME; 453 size = sizeof(boottime); 454 if (sysctl(mib, 2, &boottime, &size, NULL, 0) != -1 && 455 boottime.tv_sec != 0) { 456 uptime = now - boottime.tv_sec; 457 if (uptime > 60) 458 uptime += 30; 459 days = uptime / 86400; 460 uptime %= 86400; 461 hrs = uptime / 3600; 462 uptime %= 3600; 463 mins = uptime / 60; 464 secs = uptime % 60; 465 (void)printf(" up"); 466 if (days > 0) 467 (void)printf(" %d day%s,", days, days > 1 ? "s" : ""); 468 if (hrs > 0 && mins > 0) 469 (void)printf(" %2d:%02d,", hrs, mins); 470 else if (hrs > 0) 471 (void)printf(" %d hr%s,", hrs, hrs > 1 ? "s" : ""); 472 else if (mins > 0) 473 (void)printf(" %d min%s,", mins, mins > 1 ? "s" : ""); 474 else 475 (void)printf(" %d sec%s,", secs, secs > 1 ? "s" : ""); 476 } 477 478 /* Print number of users logged in on system */ 479 (void)printf(" %d user%s", nusers, nusers == 1 ? "" : "s"); 480 481 /* 482 * Print 1, 5, and 15 minute load averages. 483 */ 484 if (getloadavg(avenrun, NELEM(avenrun)) == -1) 485 (void)printf(", no load average information available\n"); 486 else { 487 (void)printf(", load averages:"); 488 for (i = 0; i < (int)NELEM(avenrun); i++) { 489 if (use_comma && i > 0) 490 (void)printf(","); 491 (void)printf(" %.2f", avenrun[i]); 492 } 493 (void)printf("\n"); 494 } 495 } 496 497 static struct stat * 498 ttystat(char *line, int sz) 499 { 500 static struct stat sb; 501 char ttybuf[MAXPATHLEN]; 502 503 (void)snprintf(ttybuf, sizeof(ttybuf), "%s%.*s", _PATH_DEV, sz, line); 504 if (stat(ttybuf, &sb)) { 505 warn("%s", ttybuf); 506 return (NULL); 507 } 508 return (&sb); 509 } 510 511 static void 512 usage(int wcmd) 513 { 514 if (wcmd) 515 (void)fprintf(stderr, 516 "usage: w [-dhin] [-M core] [-N system] [user ...]\n"); 517 else 518 (void)fprintf(stderr, "usage: uptime\n"); 519 exit(1); 520 } 521 522 static int 523 this_is_uptime(const char *s) 524 { 525 const char *u; 526 527 if ((u = strrchr(s, '/')) != NULL) 528 ++u; 529 else 530 u = s; 531 if (strcmp(u, "uptime") == 0) 532 return (0); 533 return (-1); 534 } 535 536 static void 537 process(struct entry *ep) 538 { 539 struct stat *stp; 540 time_t touched; 541 int max; 542 543 if ((max = strlen(ep->name)) > maxname) 544 maxname = max; 545 if ((max = strlen(ep->line)) > maxline) 546 maxline = max; 547 if ((max = strlen(ep->host)) > maxhost) 548 maxhost = max; 549 550 ep->tdev = 0; 551 ep->idle = (time_t)-1; 552 553 if (!(stp = ttystat(ep->line, maxline))) 554 return; /* corrupted record */ 555 556 ep->tdev = stp->st_rdev; 557 #ifdef CPU_CONSDEV 558 /* 559 * If this is the console device, attempt to ascertain 560 * the true console device dev_t. 561 */ 562 if (ep->tdev == 0) { 563 int mib[2]; 564 size_t size; 565 566 mib[0] = CTL_MACHDEP; 567 mib[1] = CPU_CONSDEV; 568 size = sizeof(dev_t); 569 (void)sysctl(mib, 2, &ep->tdev, &size, NULL, 0); 570 } 571 #endif 572 573 touched = stp->st_atime; 574 if (touched < ep->tv.tv_sec) { 575 /* tty untouched since before login */ 576 touched = ep->tv.tv_sec; 577 } 578 if ((ep->idle = now - touched) < 0) 579 ep->idle = 0; 580 } 581