1/*- 2 * Copyright (c) 1980, 1991, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * Portions copyright (c) 2007 Apple Inc. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. Neither the name of the University nor the names of its contributors 15 * may be used to endorse or promote products derived from this software 16 * without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31#include <sys/cdefs.h> 32 33#ifndef __APPLE__ 34__FBSDID("$FreeBSD: src/usr.bin/w/w.c,v 1.58 2005/06/04 23:40:09 gad Exp $"); 35#endif 36 37#ifndef lint 38static const char copyright[] = 39"@(#) Copyright (c) 1980, 1991, 1993, 1994\n\ 40 The Regents of the University of California. All rights reserved.\n"; 41#endif 42 43#ifndef lint 44static const char sccsid[] = "@(#)w.c 8.4 (Berkeley) 4/16/94"; 45#endif 46 47/* 48 * w - print system status (who and what) 49 * 50 * This program is similar to the systat command on Tenex/Tops 10/20 51 * 52 */ 53#include <sys/param.h> 54#include <sys/time.h> 55#include <sys/stat.h> 56#include <sys/sysctl.h> 57#include <sys/proc.h> 58#include <sys/user.h> 59#include <sys/ioctl.h> 60#include <sys/socket.h> 61#include <sys/tty.h> 62 63#ifndef __APPLE__ 64#include <machine/cpu.h> 65#endif 66#include <netinet/in.h> 67#include <arpa/inet.h> 68#include <arpa/nameser.h> 69 70#include <ctype.h> 71#include <err.h> 72#include <errno.h> 73#include <fcntl.h> 74#if HAVE_KVM 75#include <kvm.h> 76#endif 77#include <langinfo.h> 78#include <libutil.h> 79#include <limits.h> 80#include <locale.h> 81#include <netdb.h> 82#include <nlist.h> 83#include <paths.h> 84#include <resolv.h> 85#include <stdio.h> 86#include <stdlib.h> 87#include <string.h> 88#include <timeconv.h> 89#include <unistd.h> 90#if HAVE_UTMPX 91#include <utmpx.h> 92/* use utmp values so formatting is the same */ 93#define UT_NAMESIZE 8 94#define UT_LINESIZE 8 95#else /* HAVE_UTMPX */ 96#include <utmp.h> 97#endif /* HAVE_UTMPX */ 98#include <vis.h> 99 100#include <TargetConditionals.h> 101 102#include "extern.h" 103 104struct timeval boottime; 105#if !HAVE_UTMPX 106struct utmp utmp; 107#endif 108struct winsize ws; 109#if HAVE_KVM 110kvm_t *kd; 111#endif 112time_t now; /* the current time of day */ 113int ttywidth; /* width of tty */ 114int argwidth; /* width of tty */ 115int header = 1; /* true if -h flag: don't print heading */ 116#if !HAVE_UTMPX 117int nflag; /* true if -n flag: don't convert addrs */ 118#endif 119#ifndef __APPLE__ 120int dflag; /* true if -d flag: output debug info */ 121#endif 122int sortidle; /* sort by idle time */ 123int use_ampm; /* use AM/PM time */ 124int use_comma; /* use comma as floats separator */ 125char **sel_users; /* login array of particular users selected */ 126 127/* 128 * One of these per active utmp entry. 129 */ 130struct entry { 131 struct entry *next; 132#if HAVE_UTMPX 133 struct utmpx utmp; 134#else 135 struct utmp utmp; 136#endif 137 dev_t tdev; /* dev_t of terminal */ 138 time_t idle; /* idle time of terminal in seconds */ 139 struct kinfo_proc *kp; /* `most interesting' proc */ 140 char *args; /* arg list of interesting process */ 141 struct kinfo_proc *dkp; /* debug option proc list */ 142} *ep, *ehead = NULL, **nextp = &ehead; 143 144#ifndef __APPLE__ 145#define debugproc(p) *((struct kinfo_proc **)&(p)->ki_udata) 146#else 147#define debugproc(p) *((struct kinfo_proc **)&(p)->ki_spare[0]) 148#endif 149 150/* W_DISPHOSTSIZE should not be greater than UT_HOSTSIZE */ 151#define W_DISPHOSTSIZE 16 152 153static void pr_header(time_t *, int); 154static struct stat *ttystat(char *, int); 155static void usage(int); 156static int this_is_uptime(const char *s); 157#if !HAVE_KVM 158static void w_getargv(void); 159#endif 160 161char *fmt_argv(char **, char *, int); /* ../../bin/ps/fmt.c */ 162 163int 164main(int argc, char *argv[]) 165{ 166 struct kinfo_proc *kp; 167 struct kinfo_proc *kprocbuf; 168 struct kinfo_proc *dkp; 169 struct stat *stp; 170#if HAVE_UTMPX 171 struct utmpx *ux; 172#else 173 FILE *ut; 174#endif 175 time_t touched; 176#if HAVE_KVM 177 int ch, i, nentries, nusers, wcmd, longidle, dropgid; 178 const char *memf, *nlistf, *p; 179#else 180 int ch, i, nentries, nusers, wcmd, longidle; 181 const char *p; 182#endif /* HAVE_KVM */ 183 char *x_suffix; 184#ifdef __APPLE__ 185 char buf[MAXHOSTNAMELEN]; 186#else 187 char buf[MAXHOSTNAMELEN], errbuf[_POSIX2_LINE_MAX]; 188 char fn[MAXHOSTNAMELEN]; 189#endif /* __APPLE__ */ 190 char *dot; 191#if !HAVE_KVM 192 int local_error = 0, retry_count = 0; 193 size_t bufSize = 0; 194 size_t orig_bufSize = 0; 195 int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 }; 196#endif 197 198 (void)setlocale(LC_ALL, ""); 199#ifndef __APPLE__ 200 use_ampm = (*nl_langinfo(T_FMT_AMPM) != '\0'); 201 use_comma = (*nl_langinfo(RADIXCHAR) != ','); 202#endif 203 204 /* Are we w(1) or uptime(1)? */ 205 if (this_is_uptime(argv[0]) == 0) { 206 wcmd = 0; 207 p = ""; 208 } else { 209 wcmd = 1; 210 p = "dhiflM:N:nsuw"; 211 } 212 213#if HAVE_KVM 214 dropgid = 0; 215 memf = nlistf = _PATH_DEVNULL; 216#endif 217 while ((ch = getopt(argc, argv, p)) != -1) 218 switch (ch) { 219#ifndef __APPLE__ 220 case 'd': 221 dflag = 1; 222 break; 223#endif 224 case 'h': 225 header = 0; 226 break; 227 case 'i': 228 sortidle = 1; 229 break; 230#if HAVE_KVM 231 case 'M': 232 header = 0; 233 memf = optarg; 234 dropgid = 1; 235 break; 236 case 'N': 237 nlistf = optarg; 238 dropgid = 1; 239 break; 240#endif /* HAVE_KVM */ 241#if !HAVE_UTMPX 242 case 'n': 243 nflag = 1; 244 break; 245#else /* !HAVE_UTMPX */ 246 case 'n': 247#endif /* !HAVE_UTMPX */ 248 case 'f': case 'l': case 's': case 'u': case 'w': 249#if !HAVE_KVM 250 case 'M': case 'N': 251#endif 252#ifdef __APPLE__ 253 case 'd': 254 warnx("[-MNdflnsuw] no longer supported"); 255#else 256 warnx("[-flsuw] no longer supported"); 257#endif 258 /* FALLTHROUGH */ 259 case '?': 260 default: 261 usage(wcmd); 262 } 263 argc -= optind; 264 argv += optind; 265 266#if !TARGET_OS_EMBEDDED 267 if (!(_res.options & RES_INIT)) 268 res_init(); 269 _res.retrans = 2; /* resolver timeout to 2 seconds per try */ 270 _res.retry = 1; /* only try once.. */ 271#endif 272 273#if HAVE_KVM 274 /* 275 * Discard setgid privileges if not the running kernel so that bad 276 * guys can't print interesting stuff from kernel memory. 277 */ 278 if (dropgid) 279 setgid(getgid()); 280 281 if ((kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf)) == NULL) 282 errx(1, "%s", errbuf); 283#endif 284 285 (void)time(&now); 286#if HAVE_UTMPX 287 setutxent(); 288#else 289 if ((ut = fopen(_PATH_UTMP, "r")) == NULL) 290 err(1, "%s", _PATH_UTMP); 291#endif 292 293 if (*argv) 294 sel_users = argv; 295 296#if HAVE_UTMPX 297 for (nusers = 0; (ux = getutxent());) { 298 if (ux->ut_user[0] == '\0' || ux->ut_type != USER_PROCESS) 299 continue; 300 if (!(stp = ttystat(ux->ut_line, sizeof(ux->ut_line)))) 301 continue; /* corrupted record */ 302#else 303 for (nusers = 0; fread(&utmp, sizeof(utmp), 1, ut);) { 304 if (utmp.ut_name[0] == '\0') 305 continue; 306 if (!(stp = ttystat(utmp.ut_line, UT_LINESIZE))) 307 continue; /* corrupted record */ 308#endif 309 ++nusers; 310 if (wcmd == 0) 311 continue; 312 if (sel_users) { 313 int usermatch; 314 char **user; 315 316 usermatch = 0; 317 for (user = sel_users; !usermatch && *user; user++) 318#if HAVE_UTMPX 319 if (!strncmp(ux->ut_user, *user, sizeof(ux->ut_user))) 320#else 321 if (!strncmp(utmp.ut_name, *user, UT_NAMESIZE)) 322#endif 323 usermatch = 1; 324 if (!usermatch) 325 continue; 326 } 327 if ((ep = calloc(1, sizeof(struct entry))) == NULL) 328 errx(1, "calloc"); 329 *nextp = ep; 330 nextp = &ep->next; 331#if HAVE_UTMPX 332 memmove(&ep->utmp, ux, sizeof(*ux)); 333#else 334 memmove(&ep->utmp, &utmp, sizeof(struct utmp)); 335#endif 336 ep->tdev = stp->st_rdev; 337#ifdef CPU_CONSDEV 338 /* 339 * If this is the console device, attempt to ascertain 340 * the true console device dev_t. 341 */ 342 if (ep->tdev == 0) { 343 int mib[2]; 344 size_t size; 345 346 mib[0] = CTL_MACHDEP; 347 mib[1] = CPU_CONSDEV; 348 size = sizeof(dev_t); 349 (void)sysctl(mib, 2, &ep->tdev, &size, NULL, 0); 350 } 351#endif 352 touched = stp->st_atime; 353#ifdef __APPLE__ 354 if (touched < ep->utmp.ut_tv.tv_sec) { 355 /* tty untouched since before login */ 356 touched = ep->utmp.ut_tv.tv_sec; 357 } 358#else 359 if (touched < ep->utmp.ut_time) { 360 /* tty untouched since before login */ 361 touched = ep->utmp.ut_time; 362 } 363#endif 364 if ((ep->idle = now - touched) < 0) 365 ep->idle = 0; 366 } 367#if HAVE_UTMPX 368 endutxent(); 369#else 370 (void)fclose(ut); 371#endif 372 373 if (header || wcmd == 0) { 374 pr_header(&now, nusers); 375 if (wcmd == 0) { 376#if HAVE_KVM 377 (void)kvm_close(kd); 378#endif 379 exit(0); 380 } 381 382#define HEADER_USER "USER" 383#define HEADER_TTY "TTY" 384#define HEADER_FROM "FROM" 385#define HEADER_LOGIN_IDLE "LOGIN@ IDLE " 386#define HEADER_WHAT "WHAT\n" 387#define WUSED (UT_NAMESIZE + UT_LINESIZE + W_DISPHOSTSIZE + \ 388 sizeof(HEADER_LOGIN_IDLE) + 3) /* header width incl. spaces */ 389 (void)printf("%-*.*s %-*.*s %-*.*s %s", 390 UT_NAMESIZE, UT_NAMESIZE, HEADER_USER, 391 UT_LINESIZE, UT_LINESIZE, HEADER_TTY, 392 W_DISPHOSTSIZE, W_DISPHOSTSIZE, HEADER_FROM, 393 HEADER_LOGIN_IDLE HEADER_WHAT); 394 } 395 396#if HAVE_KVM 397 if ((kp = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nentries)) == NULL) 398 err(1, "%s", kvm_geterr(kd)); 399#else 400 mib[0] = CTL_KERN; 401 mib[1] = KERN_PROC; 402 mib[2] = KERN_PROC_ALL; 403 mib[3] = 0; 404 405 if (sysctl(mib, 4, NULL, &bufSize, NULL, 0) < 0) { 406 perror("Failure calling sysctl"); 407 exit(1); 408 } 409 410 kprocbuf = kp = (struct kinfo_proc *)malloc(bufSize); 411 412 retry_count = 0; 413 orig_bufSize = bufSize; 414 for (retry_count = 0; ; retry_count++) { 415 local_error = 0; 416 bufSize = orig_bufSize; 417 if ((local_error = sysctl(mib, 4, kp, &bufSize, NULL, 0)) < 0) { 418 if (retry_count < 1000) { 419 sleep(1); 420 continue; 421 } 422 perror("Failure calling sysctl"); 423 exit(1); 424 } else if (local_error == 0) { 425 break; 426 } 427 sleep(1); 428 } 429 nentries = bufSize / sizeof(struct kinfo_proc); 430#endif /* !HAVE_KVM */ 431 432#if !HAVE_KVM 433#define ki_stat kp_proc.p_stat 434#define ki_pgid kp_eproc.e_pgid 435#define ki_tpgid kp_eproc.e_tpgid 436#define ki_tdev kp_eproc.e_tdev 437#endif /* !HAVE_KVM */ 438 for (i = 0; i < nentries; i++, kp++) { 439 if (kp->ki_stat == SIDL || kp->ki_stat == SZOMB) 440 continue; 441 for (ep = ehead; ep != NULL; ep = ep->next) { 442 if (ep->tdev == kp->ki_tdev) { 443 /* 444 * proc is associated with this terminal 445 */ 446 if (ep->kp == NULL && kp->ki_pgid == kp->ki_tpgid) { 447 /* 448 * Proc is 'most interesting' 449 */ 450 if (proc_compare(ep->kp, kp)) 451 ep->kp = kp; 452 } 453 /* 454 * Proc debug option info; add to debug 455 * list using kinfo_proc ki_spare[0] 456 * as next pointer; ptr to ptr avoids the 457 * ptr = long assumption. 458 */ 459 dkp = ep->dkp; 460 ep->dkp = kp; 461#ifndef __APPLE__ 462 debugproc(kp) = dkp; 463#endif 464 } 465 } 466 } 467 if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 && 468 ioctl(STDERR_FILENO, TIOCGWINSZ, &ws) == -1 && 469 ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) || ws.ws_col == 0) 470 ttywidth = 79; 471 else 472 ttywidth = ws.ws_col - 1; 473 argwidth = ttywidth - WUSED; 474 if (argwidth < 4) 475 argwidth = 8; 476 for (ep = ehead; ep != NULL; ep = ep->next) { 477 if (ep->kp == NULL) { 478 ep->args = strdup("-"); 479 continue; 480 } 481#if HAVE_KVM 482 ep->args = fmt_argv(kvm_getargv(kd, ep->kp, argwidth), 483 ep->kp->ki_comm, MAXCOMLEN); 484#else 485 w_getargv(); 486#endif /* HAVE_KVM */ 487 if (ep->args == NULL) 488 err(1, NULL); 489 } 490 /* sort by idle time */ 491 if (sortidle && ehead != NULL) { 492 struct entry *from, *save; 493 494 from = ehead; 495 ehead = NULL; 496 while (from != NULL) { 497 for (nextp = &ehead; 498 (*nextp) && from->idle >= (*nextp)->idle; 499 nextp = &(*nextp)->next) 500 continue; 501 save = from; 502 from = from->next; 503 save->next = *nextp; 504 *nextp = save; 505 } 506 } 507 508 for (ep = ehead; ep != NULL; ep = ep->next) { 509#if HAVE_UTMPX 510 char host_buf[sizeof(ep->utmp.ut_host) + 1]; 511 strlcpy(host_buf, ep->utmp.ut_host, sizeof(host_buf)); 512#else 513 char host_buf[UT_HOSTSIZE + 1]; 514 struct sockaddr_storage ss; 515 struct sockaddr *sa = (struct sockaddr *)&ss; 516 struct sockaddr_in *lsin = (struct sockaddr_in *)&ss; 517 struct sockaddr_in6 *lsin6 = (struct sockaddr_in6 *)&ss; 518 time_t t; 519 int isaddr; 520 521 host_buf[UT_HOSTSIZE] = '\0'; 522 strncpy(host_buf, ep->utmp.ut_host, UT_HOSTSIZE); 523#endif /* HAVE_UTMPX */ 524 p = *host_buf ? host_buf : "-"; 525 if ((x_suffix = strrchr(p, ':')) != NULL) { 526 if ((dot = strchr(x_suffix, '.')) != NULL && 527 strchr(dot+1, '.') == NULL) 528 *x_suffix++ = '\0'; 529 else 530 x_suffix = NULL; 531 } 532#if !HAVE_UTMPX 533 if (!nflag) { 534 /* Attempt to change an IP address into a name */ 535 isaddr = 0; 536 memset(&ss, '\0', sizeof(ss)); 537 if (inet_pton(AF_INET6, p, &lsin6->sin6_addr) == 1) { 538 lsin6->sin6_len = sizeof(*lsin6); 539 lsin6->sin6_family = AF_INET6; 540 isaddr = 1; 541 } else if (inet_pton(AF_INET, p, &lsin->sin_addr) == 1) { 542 lsin->sin_len = sizeof(*lsin); 543 lsin->sin_family = AF_INET; 544 isaddr = 1; 545 } 546 if (isaddr && realhostname_sa(fn, sizeof(fn), sa, 547 sa->sa_len) == HOSTNAME_FOUND) 548 p = fn; 549 } 550#endif /* !HAVE_UTMPX */ 551 if (x_suffix) { 552 (void)snprintf(buf, sizeof(buf), "%s:%s", p, x_suffix); 553 p = buf; 554 } 555#ifndef __APPLE__ 556 if (dflag) { 557 for (dkp = ep->dkp; dkp != NULL; dkp = debugproc(dkp)) { 558 const char *ptr; 559 560 ptr = fmt_argv(kvm_getargv(kd, dkp, argwidth), 561 dkp->ki_comm, MAXCOMLEN); 562 if (ptr == NULL) 563 ptr = "-"; 564 (void)printf("\t\t%-9d %s\n", 565 dkp->ki_pid, ptr); 566 } 567 } 568#endif /* !__APPLE__ */ 569 (void)printf("%-*.*s %-*.*s %-*.*s ", 570#if HAVE_UTMPX 571 UT_NAMESIZE, (int)sizeof(ep->utmp.ut_user), ep->utmp.ut_user, 572 UT_LINESIZE, (int)sizeof(ep->utmp.ut_line), 573#else 574 UT_NAMESIZE, UT_NAMESIZE, ep->utmp.ut_name, 575 UT_LINESIZE, UT_LINESIZE, 576#endif 577 strncmp(ep->utmp.ut_line, "tty", 3) && 578 strncmp(ep->utmp.ut_line, "cua", 3) ? 579 ep->utmp.ut_line : ep->utmp.ut_line + 3, 580 W_DISPHOSTSIZE, W_DISPHOSTSIZE, *p ? p : "-"); 581#ifdef __APPLE__ 582 pr_attime(&ep->utmp.ut_tv.tv_sec, &now); 583#else 584 t = _time_to_time32(ep->utmp.ut_time); 585 pr_attime(&t, &now); 586#endif 587 longidle = pr_idle(ep->idle); 588 (void)printf("%.*s\n", argwidth - longidle, ep->args); 589#ifdef __APPLE__ 590 free(ep->args); 591#endif 592 } 593#if HAVE_KVM 594 (void)kvm_close(kd); 595#else 596 free(kprocbuf); 597#endif /* HAVE_KVM */ 598 exit(0); 599} 600 601static void 602pr_header(time_t *nowp, int nusers) 603{ 604 double avenrun[3]; 605 time_t uptime; 606 int days, hrs, i, mins, secs; 607 int mib[2]; 608 size_t size; 609 char buf[256]; 610 611 /* 612 * Print time of day. 613 */ 614 if (strftime(buf, sizeof(buf), 615 use_ampm ? "%l:%M%p" : "%k:%M", localtime(nowp)) != 0) 616 (void)printf("%s ", buf); 617 /* 618 * Print how long system has been up. 619 * (Found by looking getting "boottime" from the kernel) 620 */ 621 mib[0] = CTL_KERN; 622 mib[1] = KERN_BOOTTIME; 623 size = sizeof(boottime); 624 if (sysctl(mib, 2, &boottime, &size, NULL, 0) != -1 && 625 boottime.tv_sec != 0) { 626 uptime = now - boottime.tv_sec; 627 if (uptime > 60) 628 uptime += 30; 629 days = uptime / 86400; 630 uptime %= 86400; 631 hrs = uptime / 3600; 632 uptime %= 3600; 633 mins = uptime / 60; 634 secs = uptime % 60; 635 (void)printf(" up"); 636 if (days > 0) 637 (void)printf(" %d day%s,", days, days > 1 ? "s" : ""); 638 if (hrs > 0 && mins > 0) 639 (void)printf(" %2d:%02d,", hrs, mins); 640 else if (hrs > 0) 641 (void)printf(" %d hr%s,", hrs, hrs > 1 ? "s" : ""); 642 else if (mins > 0) 643 (void)printf(" %d min%s,", mins, mins > 1 ? "s" : ""); 644 else 645 (void)printf(" %d sec%s,", secs, secs > 1 ? "s" : ""); 646 } 647 648 /* Print number of users logged in to system */ 649 (void)printf(" %d user%s", nusers, nusers == 1 ? "" : "s"); 650 651 /* 652 * Print 1, 5, and 15 minute load averages. 653 */ 654 if (getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0])) == -1) 655 (void)printf(", no load average information available\n"); 656 else { 657 (void)printf(", load averages:"); 658 for (i = 0; i < (int)(sizeof(avenrun) / sizeof(avenrun[0])); i++) { 659 if (use_comma && i > 0) 660 (void)printf(","); 661 (void)printf(" %.2f", avenrun[i]); 662 } 663 (void)printf("\n"); 664 } 665} 666 667static struct stat * 668ttystat(char *line, int sz) 669{ 670 static struct stat sb; 671 char ttybuf[MAXPATHLEN]; 672 673 (void)snprintf(ttybuf, sizeof(ttybuf), "%s%.*s", _PATH_DEV, sz, line); 674 if (stat(ttybuf, &sb) == 0) { 675 return (&sb); 676 } else { 677 warn("%s", ttybuf); 678 return (NULL); 679 } 680} 681 682static void 683usage(int wcmd) 684{ 685 if (wcmd) 686 (void)fprintf(stderr, 687 "usage: w [hi] [user ...]\n"); 688 else 689 (void)fprintf(stderr, "usage: uptime\n"); 690 exit(1); 691} 692 693static int 694this_is_uptime(const char *s) 695{ 696 const char *u; 697 698 if ((u = strrchr(s, '/')) != NULL) 699 ++u; 700 else 701 u = s; 702 if (strcmp(u, "uptime") == 0) 703 return (0); 704 return (-1); 705} 706 707#if !HAVE_KVM 708static void 709w_getargv(void) 710{ 711 int mib[3], argmax; 712 size_t size; 713 char *procargs, *sp, *np, *cp; 714 715 mib[0] = CTL_KERN; 716 mib[1] = KERN_ARGMAX; 717 718 size = sizeof(argmax); 719 if (sysctl(mib, 2, &argmax, &size, NULL, 0) == -1) { 720 goto ERROR; 721 } 722 723 procargs = malloc(argmax); 724 if (procargs == NULL) { 725 goto ERROR; 726 } 727 728 mib[0] = CTL_KERN; 729 mib[1] = KERN_PROCARGS; 730 mib[2] = KI_PROC(ep)->p_pid; 731 732 size = (size_t)argmax; 733 if (sysctl(mib, 3, procargs, &size, NULL, 0) == -1) { 734 goto ERROR_FREE; 735 } 736 737 for (cp = procargs; cp < &procargs[size]; cp++) { 738 if (*cp == '\0') { 739 break; 740 } 741 } 742 if (cp == &procargs[size]) { 743 goto ERROR_FREE; 744 } 745 746 sp = cp; 747 748 for (np = NULL; cp < &procargs[size]; cp++) { 749 if (*cp == '\0') { 750 if (np != NULL) { 751 *np = ' '; 752 } 753 np = cp; 754 } else if (*cp == '=') { 755 break; 756 } 757 } 758 759 for (np = sp; (np < &procargs[size]) && (*np == ' '); np++); 760 761 ep->args = strdup(np); 762 free(procargs); 763 return; 764 765ERROR_FREE: 766 free(procargs); 767ERROR: 768/* 769 ep->args = malloc(2); 770 ep->args[0] = '-'; 771 ep->args[1] = '\0'; 772*/ 773 asprintf(&ep->args, "%s", KI_PROC(ep)->p_comm); 774 return; 775} 776#endif /* HAVE_KVM */ 777