1 1.93 wiz /* $NetBSD: w.c,v 1.93 2022/05/26 06:48:36 wiz Exp $ */ 2 1.18 thorpej 3 1.1 cgd /*- 4 1.11 cgd * Copyright (c) 1980, 1991, 1993, 1994 5 1.11 cgd * The Regents of the University of California. All rights reserved. 6 1.1 cgd * 7 1.1 cgd * Redistribution and use in source and binary forms, with or without 8 1.1 cgd * modification, are permitted provided that the following conditions 9 1.1 cgd * are met: 10 1.1 cgd * 1. Redistributions of source code must retain the above copyright 11 1.1 cgd * notice, this list of conditions and the following disclaimer. 12 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 cgd * notice, this list of conditions and the following disclaimer in the 14 1.1 cgd * documentation and/or other materials provided with the distribution. 15 1.60 agc * 3. Neither the name of the University nor the names of its contributors 16 1.1 cgd * may be used to endorse or promote products derived from this software 17 1.1 cgd * without specific prior written permission. 18 1.1 cgd * 19 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 1.1 cgd * SUCH DAMAGE. 30 1.1 cgd */ 31 1.1 cgd 32 1.26 mrg #include <sys/cdefs.h> 33 1.1 cgd #ifndef lint 34 1.73 lukem __COPYRIGHT("@(#) Copyright (c) 1980, 1991, 1993, 1994\ 35 1.73 lukem The Regents of the University of California. All rights reserved."); 36 1.1 cgd #endif /* not lint */ 37 1.1 cgd 38 1.1 cgd #ifndef lint 39 1.18 thorpej #if 0 40 1.19 tls static char sccsid[] = "@(#)w.c 8.6 (Berkeley) 6/30/94"; 41 1.18 thorpej #else 42 1.93 wiz __RCSID("$NetBSD: w.c,v 1.93 2022/05/26 06:48:36 wiz Exp $"); 43 1.18 thorpej #endif 44 1.1 cgd #endif /* not lint */ 45 1.1 cgd 46 1.1 cgd /* 47 1.1 cgd * w - print system status (who and what) 48 1.1 cgd * 49 1.1 cgd * This program is similar to the systat command on Tenex/Tops 10/20 50 1.1 cgd * 51 1.1 cgd */ 52 1.1 cgd #include <sys/param.h> 53 1.42 jdolecek #include <sys/types.h> 54 1.1 cgd #include <sys/time.h> 55 1.1 cgd #include <sys/stat.h> 56 1.11 cgd #include <sys/sysctl.h> 57 1.1 cgd #include <sys/proc.h> 58 1.1 cgd #include <sys/ioctl.h> 59 1.11 cgd #include <sys/socket.h> 60 1.11 cgd 61 1.11 cgd #include <netinet/in.h> 62 1.11 cgd #include <arpa/inet.h> 63 1.11 cgd 64 1.11 cgd #include <ctype.h> 65 1.11 cgd #include <err.h> 66 1.11 cgd #include <errno.h> 67 1.11 cgd #include <fcntl.h> 68 1.11 cgd #include <kvm.h> 69 1.20 explorer #include <limits.h> 70 1.11 cgd #include <netdb.h> 71 1.1 cgd #include <nlist.h> 72 1.1 cgd #include <paths.h> 73 1.11 cgd #include <stdio.h> 74 1.11 cgd #include <stdlib.h> 75 1.1 cgd #include <string.h> 76 1.28 kleink #include <time.h> 77 1.11 cgd #include <tzfile.h> 78 1.11 cgd #include <unistd.h> 79 1.48 christos #ifdef SUPPORT_UTMP 80 1.11 cgd #include <utmp.h> 81 1.48 christos #endif 82 1.48 christos #ifdef SUPPORT_UTMPX 83 1.48 christos #include <utmpx.h> 84 1.48 christos #endif 85 1.7 cgd #include <vis.h> 86 1.1 cgd 87 1.11 cgd #include "extern.h" 88 1.1 cgd 89 1.84 kre struct timespec boottime; 90 1.11 cgd struct winsize ws; 91 1.11 cgd kvm_t *kd; 92 1.11 cgd time_t now; /* the current time of day */ 93 1.11 cgd int ttywidth; /* width of tty */ 94 1.39 enami int argwidth; /* width of tty left to print process args */ 95 1.11 cgd int header = 1; /* true if -h flag: don't print heading */ 96 1.11 cgd int nflag; /* true if -n flag: don't convert addrs */ 97 1.65 christos int wflag; /* true if -w flag: wide printout */ 98 1.92 mrg int sortidle; /* sort by idle time */ 99 1.92 mrg int alphasort; /* sort by tty alphabeta, not numeric */ 100 1.11 cgd char *sel_user; /* login of particular user selected */ 101 1.29 mrg char domain[MAXHOSTNAMELEN + 1]; 102 1.48 christos int maxname = 8, maxline = 3, maxhost = 16; 103 1.1 cgd 104 1.1 cgd /* 105 1.11 cgd * One of these per active utmp entry. 106 1.1 cgd */ 107 1.1 cgd struct entry { 108 1.1 cgd struct entry *next; 109 1.72 christos char name[UTX_USERSIZE + 1]; 110 1.72 christos char line[UTX_LINESIZE + 1]; 111 1.72 christos char host[UTX_HOSTSIZE + 1]; 112 1.56 christos char type[2]; 113 1.48 christos struct timeval tv; 114 1.35 simonb dev_t tdev; /* dev_t of terminal */ 115 1.35 simonb time_t idle; /* idle time of terminal in seconds */ 116 1.58 christos struct kinfo_proc2 *tp; /* `most interesting' tty proc */ 117 1.58 christos struct kinfo_proc2 *pp; /* pid proc */ 118 1.55 christos pid_t pid; /* pid or ~0 if not known */ 119 1.74 lukem } *ehead = NULL, **nextp = &ehead; 120 1.1 cgd 121 1.64 christos static void pr_args(struct kinfo_proc2 *); 122 1.64 christos static void pr_header(time_t *, int); 123 1.76 christos static int proc_compare_wrapper(const struct kinfo_proc2 *, 124 1.76 christos const struct kinfo_proc2 *); 125 1.48 christos #if defined(SUPPORT_UTMP) || defined(SUPPORT_UTMPX) 126 1.64 christos static int ttystat(const char *, struct stat *); 127 1.48 christos static void process(struct entry *); 128 1.48 christos #endif 129 1.77 christos static void fixhost(struct entry *ep); 130 1.75 joerg __dead static void usage(int); 131 1.1 cgd 132 1.11 cgd int 133 1.45 mjl main(int argc, char **argv) 134 1.1 cgd { 135 1.35 simonb struct kinfo_proc2 *kp; 136 1.74 lukem struct entry *ep; 137 1.71 rpaulo int ch, i, nentries, nusers, wcmd, curtain, use_sysctl; 138 1.77 christos char *memf, *nlistf, *usrnp; 139 1.74 lukem const char *options; 140 1.48 christos time_t then; 141 1.71 rpaulo size_t len; 142 1.48 christos #ifdef SUPPORT_UTMP 143 1.48 christos struct utmp *ut; 144 1.48 christos #endif 145 1.48 christos #ifdef SUPPORT_UTMPX 146 1.48 christos struct utmpx *utx; 147 1.48 christos #endif 148 1.46 cgd const char *progname; 149 1.77 christos char errbuf[_POSIX2_LINE_MAX]; 150 1.11 cgd 151 1.66 christos setprogname(argv[0]); 152 1.66 christos 153 1.11 cgd /* Are we w(1) or uptime(1)? */ 154 1.46 cgd progname = getprogname(); 155 1.46 cgd if (*progname == '-') 156 1.46 cgd progname++; 157 1.46 cgd if (*progname == 'u') { 158 1.1 cgd wcmd = 0; 159 1.74 lukem options = ""; 160 1.11 cgd } else { 161 1.11 cgd wcmd = 1; 162 1.92 mrg options = "AhiM:N:nw"; 163 1.11 cgd } 164 1.1 cgd 165 1.11 cgd memf = nlistf = NULL; 166 1.74 lukem while ((ch = getopt(argc, argv, options)) != -1) 167 1.11 cgd switch (ch) { 168 1.92 mrg case 'A': 169 1.92 mrg alphasort = 1; 170 1.92 mrg break; 171 1.1 cgd case 'h': 172 1.1 cgd header = 0; 173 1.1 cgd break; 174 1.1 cgd case 'i': 175 1.11 cgd sortidle = 1; 176 1.11 cgd break; 177 1.11 cgd case 'M': 178 1.11 cgd header = 0; 179 1.11 cgd memf = optarg; 180 1.11 cgd break; 181 1.11 cgd case 'N': 182 1.11 cgd nlistf = optarg; 183 1.11 cgd break; 184 1.11 cgd case 'n': 185 1.11 cgd nflag = 1; 186 1.1 cgd break; 187 1.65 christos case 'w': 188 1.65 christos wflag = 1; 189 1.65 christos break; 190 1.1 cgd case '?': 191 1.1 cgd default: 192 1.11 cgd usage(wcmd); 193 1.1 cgd } 194 1.1 cgd argc -= optind; 195 1.1 cgd argv += optind; 196 1.18 thorpej 197 1.71 rpaulo use_sysctl = (memf == NULL && nlistf == NULL); 198 1.71 rpaulo 199 1.70 elad if ((kd = kvm_openfiles(nlistf, memf, NULL, 200 1.70 elad memf == NULL ? KVM_NO_FILES : O_RDONLY, errbuf)) == NULL) 201 1.70 elad errx(1, "%s", errbuf); 202 1.30 mrg 203 1.11 cgd (void)time(&now); 204 1.51 mycroft 205 1.71 rpaulo if (use_sysctl) { 206 1.71 rpaulo len = sizeof(curtain); 207 1.71 rpaulo if (sysctlbyname("security.curtain", &curtain, &len, 208 1.71 rpaulo NULL, 0) == -1) 209 1.71 rpaulo curtain = 0; 210 1.71 rpaulo } 211 1.71 rpaulo 212 1.85 kim if (!nflag) { 213 1.85 kim int rv; 214 1.85 kim char *p; 215 1.85 kim 216 1.85 kim rv = gethostname(domain, sizeof(domain)); 217 1.85 kim domain[sizeof(domain) - 1] = '\0'; 218 1.85 kim if (rv < 0 || (p = strchr(domain, '.')) == 0) 219 1.85 kim domain[0] = '\0'; 220 1.85 kim else 221 1.85 kim memmove(domain, p, strlen(p) + 1); 222 1.85 kim } 223 1.85 kim 224 1.51 mycroft #ifdef SUPPORT_UTMPX 225 1.51 mycroft setutxent(); 226 1.48 christos #endif 227 1.48 christos #ifdef SUPPORT_UTMP 228 1.51 mycroft setutent(); 229 1.48 christos #endif 230 1.11 cgd 231 1.1 cgd if (*argv) 232 1.1 cgd sel_user = *argv; 233 1.1 cgd 234 1.48 christos nusers = 0; 235 1.48 christos #ifdef SUPPORT_UTMPX 236 1.48 christos while ((utx = getutxent()) != NULL) { 237 1.48 christos if (utx->ut_type != USER_PROCESS) 238 1.1 cgd continue; 239 1.11 cgd ++nusers; 240 1.81 mrg 241 1.82 dennis #ifndef SUPPORT_UTMP 242 1.81 mrg if (wcmd == 0) 243 1.81 mrg continue; 244 1.82 dennis #endif /* !SUPPORT_UTMP */ 245 1.81 mrg 246 1.52 christos if (sel_user && 247 1.63 christos strncmp(utx->ut_name, sel_user, sizeof(utx->ut_name)) != 0) 248 1.1 cgd continue; 249 1.11 cgd if ((ep = calloc(1, sizeof(struct entry))) == NULL) 250 1.33 drochner err(1, NULL); 251 1.82 dennis (void)memcpy(ep->line, utx->ut_line, sizeof(utx->ut_line)); 252 1.82 dennis ep->line[sizeof(utx->ut_line)] = '\0'; 253 1.82 dennis *nextp = ep; 254 1.82 dennis nextp = &(ep->next); 255 1.82 dennis 256 1.82 dennis if (wcmd == 0) 257 1.82 dennis continue; 258 1.82 dennis 259 1.48 christos (void)memcpy(ep->name, utx->ut_name, sizeof(utx->ut_name)); 260 1.48 christos ep->name[sizeof(utx->ut_name)] = '\0'; 261 1.53 enami if (!nflag || getnameinfo((struct sockaddr *)&utx->ut_ss, 262 1.53 enami utx->ut_ss.ss_len, ep->host, sizeof(ep->host), NULL, 0, 263 1.53 enami NI_NUMERICHOST) != 0) { 264 1.53 enami (void)memcpy(ep->host, utx->ut_host, 265 1.53 enami sizeof(utx->ut_host)); 266 1.53 enami ep->host[sizeof(utx->ut_host)] = '\0'; 267 1.53 enami } 268 1.77 christos fixhost(ep); 269 1.56 christos ep->type[0] = 'x'; 270 1.48 christos ep->tv = utx->ut_tv; 271 1.55 christos ep->pid = utx->ut_pid; 272 1.82 dennis process(ep); 273 1.48 christos } 274 1.48 christos #endif 275 1.48 christos 276 1.48 christos #ifdef SUPPORT_UTMP 277 1.48 christos while ((ut = getutent()) != NULL) { 278 1.49 christos if (ut->ut_name[0] == '\0') 279 1.49 christos continue; 280 1.52 christos 281 1.52 christos if (sel_user && 282 1.63 christos strncmp(ut->ut_name, sel_user, sizeof(ut->ut_name)) != 0) 283 1.13 cgd continue; 284 1.48 christos 285 1.48 christos /* Don't process entries that we have utmpx for */ 286 1.48 christos for (ep = ehead; ep != NULL; ep = ep->next) { 287 1.48 christos if (strncmp(ep->line, ut->ut_line, 288 1.48 christos sizeof(ut->ut_line)) == 0) 289 1.48 christos break; 290 1.42 jdolecek } 291 1.48 christos if (ep != NULL) 292 1.48 christos continue; 293 1.44 mjl 294 1.50 christos ++nusers; 295 1.81 mrg 296 1.81 mrg if (wcmd == 0) 297 1.81 mrg continue; 298 1.81 mrg 299 1.48 christos if ((ep = calloc(1, sizeof(struct entry))) == NULL) 300 1.48 christos err(1, NULL); 301 1.48 christos (void)memcpy(ep->name, ut->ut_name, sizeof(ut->ut_name)); 302 1.48 christos (void)memcpy(ep->line, ut->ut_line, sizeof(ut->ut_line)); 303 1.48 christos (void)memcpy(ep->host, ut->ut_host, sizeof(ut->ut_host)); 304 1.48 christos ep->name[sizeof(ut->ut_name)] = '\0'; 305 1.48 christos ep->line[sizeof(ut->ut_line)] = '\0'; 306 1.48 christos ep->host[sizeof(ut->ut_host)] = '\0'; 307 1.77 christos fixhost(ep); 308 1.48 christos ep->tv.tv_sec = ut->ut_time; 309 1.48 christos *nextp = ep; 310 1.48 christos nextp = &(ep->next); 311 1.81 mrg process(ep); 312 1.1 cgd } 313 1.48 christos #endif 314 1.48 christos 315 1.48 christos #ifdef SUPPORT_UTMPX 316 1.51 mycroft endutxent(); 317 1.48 christos #endif 318 1.48 christos #ifdef SUPPORT_UTMP 319 1.51 mycroft endutent(); 320 1.48 christos #endif 321 1.1 cgd 322 1.1 cgd if (header || wcmd == 0) { 323 1.11 cgd pr_header(&now, nusers); 324 1.11 cgd if (wcmd == 0) 325 1.11 cgd exit (0); 326 1.11 cgd } 327 1.1 cgd 328 1.70 elad if ((kp = kvm_getproc2(kd, KERN_PROC_ALL, 0, 329 1.70 elad sizeof(struct kinfo_proc2), &nentries)) == NULL) 330 1.70 elad errx(1, "%s", kvm_geterr(kd)); 331 1.40 simonb 332 1.40 simonb /* Include trailing space because TTY header starts one column early. */ 333 1.11 cgd for (i = 0; i < nentries; i++, kp++) { 334 1.1 cgd 335 1.1 cgd for (ep = ehead; ep != NULL; ep = ep->next) { 336 1.58 christos if (ep->tdev != 0 && ep->tdev == kp->p_tdev && 337 1.58 christos kp->p__pgid == kp->p_tpgid) { 338 1.58 christos /* 339 1.58 christos * Proc is in foreground of this 340 1.58 christos * terminal 341 1.58 christos */ 342 1.76 christos if (proc_compare_wrapper(ep->tp, kp)) 343 1.58 christos ep->tp = kp; 344 1.58 christos break; 345 1.58 christos } 346 1.58 christos if (ep->pid != 0 && ep->pid == kp->p_pid) { 347 1.58 christos ep->pp = kp; 348 1.1 cgd break; 349 1.1 cgd } 350 1.1 cgd } 351 1.1 cgd } 352 1.37 simonb 353 1.11 cgd if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 && 354 1.54 enami ioctl(STDERR_FILENO, TIOCGWINSZ, &ws) == -1 && 355 1.54 enami ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) || ws.ws_col == 0) 356 1.47 enami ttywidth = 79; 357 1.47 enami else 358 1.47 enami ttywidth = ws.ws_col - 1; 359 1.67 kim 360 1.68 kim if (!wflag && maxhost > (ttywidth / 3)) 361 1.68 kim maxhost = ttywidth / 3; 362 1.67 kim 363 1.67 kim argwidth = printf("%-*s TTY %-*s %*s IDLE WHAT\n", 364 1.67 kim maxname, "USER", maxhost, "FROM", 365 1.67 kim 7 /* "dddhhXm" */, "LOGIN@"); 366 1.67 kim argwidth -= sizeof("WHAT\n") - 1 /* NUL */; 367 1.37 simonb argwidth = ttywidth - argwidth; 368 1.1 cgd if (argwidth < 4) 369 1.1 cgd argwidth = 8; 370 1.67 kim if (wflag) 371 1.67 kim argwidth = -1; 372 1.67 kim 373 1.1 cgd /* sort by idle time */ 374 1.1 cgd if (sortidle && ehead != NULL) { 375 1.1 cgd struct entry *from = ehead, *save; 376 1.54 enami 377 1.1 cgd ehead = NULL; 378 1.1 cgd while (from != NULL) { 379 1.11 cgd for (nextp = &ehead; 380 1.1 cgd (*nextp) && from->idle >= (*nextp)->idle; 381 1.1 cgd nextp = &(*nextp)->next) 382 1.11 cgd continue; 383 1.1 cgd save = from; 384 1.1 cgd from = from->next; 385 1.1 cgd save->next = *nextp; 386 1.1 cgd *nextp = save; 387 1.1 cgd } 388 1.1 cgd } 389 1.92 mrg #if defined(SUPPORT_UTMP) || defined(SUPPORT_UTMPX) 390 1.92 mrg else if (ehead != NULL && alphasort) { 391 1.48 christos struct entry *from = ehead, *save; 392 1.54 enami 393 1.48 christos ehead = NULL; 394 1.48 christos while (from != NULL) { 395 1.48 christos for (nextp = &ehead; 396 1.48 christos (*nextp) && strcmp(from->line, (*nextp)->line) > 0; 397 1.48 christos nextp = &(*nextp)->next) 398 1.48 christos continue; 399 1.48 christos save = from; 400 1.48 christos from = from->next; 401 1.48 christos save->next = *nextp; 402 1.48 christos *nextp = save; 403 1.48 christos } 404 1.48 christos } 405 1.48 christos #endif 406 1.54 enami 407 1.1 cgd for (ep = ehead; ep != NULL; ep = ep->next) { 408 1.58 christos if (ep->tp != NULL) 409 1.58 christos kp = ep->tp; 410 1.58 christos else if (ep->pp != NULL) 411 1.58 christos kp = ep->pp; 412 1.72 christos else if (ep->pid != 0) { 413 1.71 rpaulo if (curtain) 414 1.71 rpaulo kp = NULL; 415 1.71 rpaulo else { 416 1.71 rpaulo warnx("Stale utmp%s entry: %s %s %s", 417 1.71 rpaulo ep->type, ep->name, ep->line, ep->host); 418 1.71 rpaulo continue; 419 1.71 rpaulo } 420 1.11 cgd } 421 1.71 rpaulo usrnp = (kp == NULL) ? ep->name : kp->p_login; 422 1.62 christos (void)printf("%-*s %-7.7s %-*.*s ", 423 1.71 rpaulo maxname, usrnp, ep->line, 424 1.77 christos maxhost, maxhost, ep->host); 425 1.48 christos then = (time_t)ep->tv.tv_sec; 426 1.48 christos pr_attime(&then, &now); 427 1.11 cgd pr_idle(ep->idle); 428 1.58 christos pr_args(kp); 429 1.37 simonb (void)printf("\n"); 430 1.15 mycroft } 431 1.15 mycroft exit(0); 432 1.15 mycroft } 433 1.15 mycroft 434 1.15 mycroft static void 435 1.45 mjl pr_args(struct kinfo_proc2 *kp) 436 1.15 mycroft { 437 1.15 mycroft char **argv; 438 1.15 mycroft int left; 439 1.15 mycroft 440 1.16 mycroft if (kp == 0) 441 1.16 mycroft goto nothing; 442 1.15 mycroft left = argwidth; 443 1.70 elad argv = kvm_getargv2(kd, kp, (argwidth < 0) ? 0 : argwidth); 444 1.57 fredb if (argv == 0) { 445 1.79 joerg fmt_putc('(', &left); 446 1.79 joerg fmt_puts((char *)kp->p_comm, &left); 447 1.79 joerg fmt_putc(')', &left); 448 1.79 joerg return; 449 1.57 fredb } 450 1.70 elad while (*argv) { 451 1.70 elad fmt_puts(*argv, &left); 452 1.70 elad argv++; 453 1.70 elad fmt_putc(' ', &left); 454 1.1 cgd } 455 1.16 mycroft return; 456 1.16 mycroft nothing: 457 1.16 mycroft putchar('-'); 458 1.1 cgd } 459 1.1 cgd 460 1.11 cgd static void 461 1.45 mjl pr_header(time_t *nowp, int nusers) 462 1.1 cgd { 463 1.11 cgd double avenrun[3]; 464 1.11 cgd time_t uptime; 465 1.74 lukem int days, hrs, mins; 466 1.11 cgd int mib[2]; 467 1.74 lukem size_t size, i; 468 1.11 cgd char buf[256]; 469 1.1 cgd 470 1.11 cgd /* 471 1.11 cgd * Print time of day. 472 1.11 cgd */ 473 1.91 maya (void)strftime(buf, sizeof(buf), "%l:%M%p", localtime(nowp)); 474 1.23 mrg buf[sizeof(buf) - 1] = '\0'; 475 1.11 cgd (void)printf("%s ", buf); 476 1.1 cgd 477 1.11 cgd /* 478 1.11 cgd * Print how long system has been up. 479 1.11 cgd * (Found by looking getting "boottime" from the kernel) 480 1.11 cgd */ 481 1.11 cgd mib[0] = CTL_KERN; 482 1.11 cgd mib[1] = KERN_BOOTTIME; 483 1.11 cgd size = sizeof(boottime); 484 1.11 cgd if (sysctl(mib, 2, &boottime, &size, NULL, 0) != -1 && 485 1.11 cgd boottime.tv_sec != 0) { 486 1.11 cgd uptime = now - boottime.tv_sec; 487 1.11 cgd uptime += 30; 488 1.25 phil if (uptime > SECSPERMIN) { 489 1.25 phil days = uptime / SECSPERDAY; 490 1.25 phil uptime %= SECSPERDAY; 491 1.25 phil hrs = uptime / SECSPERHOUR; 492 1.25 phil uptime %= SECSPERHOUR; 493 1.25 phil mins = uptime / SECSPERMIN; 494 1.25 phil (void)printf(" up"); 495 1.25 phil if (days > 0) 496 1.25 phil (void)printf(" %d day%s,", days, 497 1.25 phil days > 1 ? "s" : ""); 498 1.25 phil if (hrs > 0 && mins > 0) 499 1.25 phil (void)printf(" %2d:%02d,", hrs, mins); 500 1.25 phil else { 501 1.25 phil if (hrs > 0) 502 1.25 phil (void)printf(" %d hr%s,", 503 1.25 phil hrs, hrs > 1 ? "s" : ""); 504 1.25 phil if (mins > 0) 505 1.25 phil (void)printf(" %d min%s,", 506 1.25 phil mins, mins > 1 ? "s" : ""); 507 1.25 phil } 508 1.11 cgd } 509 1.11 cgd } 510 1.11 cgd 511 1.11 cgd /* Print number of users logged in to system */ 512 1.14 cgd (void)printf(" %d user%s", nusers, nusers != 1 ? "s" : ""); 513 1.11 cgd 514 1.11 cgd /* 515 1.11 cgd * Print 1, 5, and 15 minute load averages. 516 1.11 cgd */ 517 1.11 cgd if (getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0])) == -1) 518 1.11 cgd (void)printf(", no load average information available\n"); 519 1.11 cgd else { 520 1.11 cgd (void)printf(", load averages:"); 521 1.11 cgd for (i = 0; i < (sizeof(avenrun) / sizeof(avenrun[0])); i++) { 522 1.11 cgd if (i > 0) 523 1.11 cgd (void)printf(","); 524 1.11 cgd (void)printf(" %.2f", avenrun[i]); 525 1.11 cgd } 526 1.11 cgd (void)printf("\n"); 527 1.11 cgd } 528 1.1 cgd } 529 1.1 cgd 530 1.48 christos #if defined(SUPPORT_UTMP) || defined(SUPPORT_UTMPX) 531 1.64 christos static int 532 1.64 christos ttystat(const char *line, struct stat *st) 533 1.1 cgd { 534 1.11 cgd char ttybuf[MAXPATHLEN]; 535 1.1 cgd 536 1.48 christos (void)snprintf(ttybuf, sizeof(ttybuf), "%s%s", _PATH_DEV, line); 537 1.64 christos return stat(ttybuf, st); 538 1.1 cgd } 539 1.48 christos 540 1.48 christos static void 541 1.48 christos process(struct entry *ep) 542 1.48 christos { 543 1.64 christos struct stat st; 544 1.48 christos time_t touched; 545 1.48 christos int max; 546 1.48 christos 547 1.48 christos if ((max = strlen(ep->name)) > maxname) 548 1.48 christos maxname = max; 549 1.48 christos if ((max = strlen(ep->line)) > maxline) 550 1.48 christos maxline = max; 551 1.48 christos if ((max = strlen(ep->host)) > maxhost) 552 1.48 christos maxhost = max; 553 1.48 christos 554 1.64 christos ep->tdev = 0; 555 1.64 christos ep->idle = (time_t)-1; 556 1.48 christos 557 1.55 christos #ifdef SUPPORT_UTMP 558 1.48 christos /* 559 1.48 christos * Hack to recognize and correctly parse 560 1.48 christos * ut entry made by ftpd. The "tty" used 561 1.48 christos * by ftpd is not a real tty, just identifier in 562 1.64 christos * form ftpPID. Pid parsed from the "tty name" 563 1.48 christos * is used later to match corresponding process. 564 1.55 christos * NB: This is only used for utmp entries. For utmpx, 565 1.55 christos * we already have the pid. 566 1.48 christos */ 567 1.55 christos if (ep->pid == 0 && strncmp(ep->line, "ftp", 3) == 0) { 568 1.55 christos ep->pid = strtol(ep->line + 3, NULL, 10); 569 1.48 christos return; 570 1.48 christos } 571 1.55 christos #endif 572 1.64 christos if (ttystat(ep->line, &st) == -1) 573 1.55 christos return; 574 1.48 christos 575 1.64 christos ep->tdev = st.st_rdev; 576 1.48 christos /* 577 1.48 christos * If this is the console device, attempt to ascertain 578 1.48 christos * the true console device dev_t. 579 1.48 christos */ 580 1.48 christos if (ep->tdev == 0) { 581 1.48 christos int mib[2]; 582 1.48 christos size_t size; 583 1.48 christos 584 1.48 christos mib[0] = CTL_KERN; 585 1.48 christos mib[1] = KERN_CONSDEV; 586 1.48 christos size = sizeof(dev_t); 587 1.48 christos (void) sysctl(mib, 2, &ep->tdev, &size, NULL, 0); 588 1.48 christos } 589 1.48 christos 590 1.64 christos touched = st.st_atime; 591 1.48 christos if (touched < ep->tv.tv_sec) { 592 1.48 christos /* tty untouched since before login */ 593 1.48 christos touched = ep->tv.tv_sec; 594 1.48 christos } 595 1.48 christos if ((ep->idle = now - touched) < 0) 596 1.48 christos ep->idle = 0; 597 1.48 christos } 598 1.48 christos #endif 599 1.1 cgd 600 1.76 christos static int 601 1.76 christos proc_compare_wrapper(const struct kinfo_proc2 *p1, 602 1.76 christos const struct kinfo_proc2 *p2) 603 1.76 christos { 604 1.76 christos struct kinfo_lwp *l1, *l2; 605 1.76 christos int cnt; 606 1.76 christos 607 1.76 christos if (p1 == NULL) 608 1.76 christos return 1; 609 1.76 christos 610 1.76 christos l1 = kvm_getlwps(kd, p1->p_pid, 0, sizeof(*l1), &cnt); 611 1.76 christos if (l1 == NULL || cnt == 0) 612 1.76 christos return 1; 613 1.76 christos 614 1.76 christos l2 = kvm_getlwps(kd, p2->p_pid, 0, sizeof(*l1), &cnt); 615 1.76 christos if (l2 == NULL || cnt == 0) 616 1.76 christos return 0; 617 1.76 christos 618 1.76 christos return proc_compare(p1, l1, p2, l2); 619 1.76 christos } 620 1.76 christos 621 1.11 cgd static void 622 1.77 christos fixhost(struct entry *ep) 623 1.77 christos { 624 1.77 christos char host_buf[sizeof(ep->host)]; 625 1.89 kim char *b, *m, *p, *r, *x; 626 1.77 christos struct hostent *hp; 627 1.83 christos union { 628 1.83 christos struct in_addr l4; 629 1.83 christos struct in6_addr l6; 630 1.83 christos } l; 631 1.77 christos 632 1.77 christos strlcpy(host_buf, *ep->host ? ep->host : "-", sizeof(host_buf)); 633 1.77 christos p = host_buf; 634 1.77 christos 635 1.77 christos /* 636 1.83 christos * One ':' in hostname means X display number, more is IPv6. 637 1.77 christos */ 638 1.77 christos for (x = p; x < &host_buf[sizeof(host_buf)]; x++) 639 1.77 christos if (*x == '\0' || *x == ':') 640 1.77 christos break; 641 1.77 christos if (x == p + sizeof(host_buf) || *x != ':') 642 1.83 christos m = x = NULL; 643 1.83 christos else { 644 1.83 christos for (m = x + 1; m < &host_buf[sizeof(host_buf)]; m++) 645 1.83 christos if (*m == '\0' || *m == ':') 646 1.83 christos break; 647 1.83 christos if (m == p + sizeof(host_buf) || *m != ':') { 648 1.83 christos *x++ = '\0'; 649 1.83 christos m = NULL; 650 1.83 christos } else 651 1.83 christos x = NULL; 652 1.83 christos } 653 1.87 kim 654 1.88 kim /* 655 1.88 kim * Leading '[' indicates an IP address inside brackets. 656 1.88 kim */ 657 1.89 kim b = NULL; 658 1.90 kim if (!nflag && (*p == '[')) { 659 1.89 kim for (b = p++; b < &host_buf[sizeof(host_buf)]; b++) 660 1.89 kim if (*b == '\0' || *b == ']') 661 1.88 kim break; 662 1.89 kim if (b < &host_buf[sizeof(host_buf)] && *b == ']') { 663 1.89 kim *b = '\0'; 664 1.89 kim for (x = b + 1; x < &host_buf[sizeof(host_buf)]; x++) 665 1.88 kim if (*x == '\0' || *x == ':') 666 1.88 kim break; 667 1.88 kim if (x < &host_buf[sizeof(host_buf)] && *x == ':') 668 1.88 kim *x++ = '\0'; 669 1.89 kim } else 670 1.89 kim b = NULL; 671 1.88 kim } 672 1.88 kim 673 1.83 christos int af = m ? AF_INET6 : AF_INET; 674 1.83 christos size_t alen = m ? sizeof(l.l6) : sizeof(l.l4); 675 1.83 christos if (!nflag && inet_pton(af, p, &l) && 676 1.86 kim (hp = gethostbyaddr((char *)&l, alen, af))) 677 1.87 kim r = hp->h_name; 678 1.89 kim else { 679 1.89 kim if (b) 680 1.89 kim *b = ']'; 681 1.87 kim r = host_buf; 682 1.89 kim } 683 1.86 kim 684 1.86 kim if (domain[0] != '\0') { 685 1.87 kim p = r; 686 1.87 kim p += strlen(r); 687 1.86 kim p -= strlen(domain); 688 1.87 kim if (p > r && 689 1.86 kim strcasecmp(p, domain) == 0) 690 1.86 kim *p = '\0'; 691 1.77 christos } 692 1.77 christos 693 1.77 christos if (x) 694 1.87 kim (void)snprintf(ep->host, sizeof(ep->host), "%s:%s", r, x); 695 1.77 christos else 696 1.87 kim strlcpy(ep->host, r, sizeof(ep->host)); 697 1.77 christos } 698 1.77 christos 699 1.77 christos static void 700 1.45 mjl usage(int wcmd) 701 1.1 cgd { 702 1.47 enami 703 1.11 cgd if (wcmd) 704 1.11 cgd (void)fprintf(stderr, 705 1.93 wiz "Usage: %s [-Ahinw] [-M core] [-N system] [user]\n", 706 1.66 christos getprogname()); 707 1.11 cgd else 708 1.66 christos (void)fprintf(stderr, "Usage: %s\n", getprogname()); 709 1.29 mrg exit(1); 710 1.1 cgd } 711