1 /* $OpenBSD: print.c,v 1.78 2021/12/01 18:21:23 deraadt Exp $ */ 2 /* $NetBSD: print.c,v 1.27 1995/09/29 21:58:12 cgd Exp $ */ 3 4 /*- 5 * Copyright (c) 1990, 1993, 1994 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #include <sys/param.h> /* PZERO NODEV */ 34 #include <sys/types.h> 35 #include <sys/signal.h> 36 #include <sys/proc.h> 37 #include <sys/stat.h> 38 39 #include <sys/sysctl.h> 40 #define PLEDGENAMES 41 #include <sys/pledge.h> 42 43 #include <err.h> 44 #include <grp.h> 45 #include <kvm.h> 46 #include <math.h> 47 #include <nlist.h> 48 #include <stddef.h> 49 #include <stdio.h> 50 #include <stdlib.h> 51 #include <string.h> 52 #include <unistd.h> 53 #include <limits.h> 54 #include <pwd.h> 55 56 #include "ps.h" 57 58 extern kvm_t *kd; 59 extern int needenv, needcomm, neednlist, commandonly; 60 61 int mbswprint(const char *, int, int); /* utf8.c */ 62 63 static char *cmdpart(char *); 64 65 #define min(a,b) ((a) < (b) ? (a) : (b)) 66 67 static char * 68 cmdpart(char *arg0) 69 { 70 char *cp; 71 72 return ((cp = strrchr(arg0, '/')) != NULL ? cp + 1 : arg0); 73 } 74 75 void 76 printheader(void) 77 { 78 VAR *v; 79 struct varent *vent; 80 81 if (!needheader) 82 return; 83 for (vent = vhead; vent; vent = vent->next) { 84 v = vent->var; 85 if (v->flag & LJUST) { 86 if (vent->next == NULL) /* last one */ 87 (void)printf("%s", v->header); 88 else 89 (void)printf("%-*s", v->width, v->header); 90 } else 91 (void)printf("%*s", v->width, v->header); 92 if (vent->next != NULL) 93 (void)putchar(' '); 94 } 95 (void)putchar('\n'); 96 } 97 98 void 99 command(const struct kinfo_proc *kp, VARENT *ve) 100 { 101 VAR *v; 102 int left, wantspace = 0; 103 char **p; 104 105 /* 106 * Determine the available number of display columns. 107 * Always decrement and check after writing. 108 * No check is needed before mbswprint() 109 * and after writing the last data, though. 110 */ 111 112 v = ve->var; 113 if (ve->next != NULL || termwidth != UNLIMITED) { 114 if (ve->next == NULL) { 115 left = termwidth - (totwidth - v->width); 116 if (left < 1) /* already wrapped, just use std width */ 117 left = v->width; 118 } else 119 left = v->width; 120 } else 121 left = INT_MAX; 122 123 if (needenv && kd != NULL) { 124 char **envp = kvm_getenvv(kd, kp, termwidth); 125 if ((p = envp) != NULL) { 126 while (*p) { 127 if (wantspace) { 128 putchar(' '); 129 left--; 130 } 131 left -= mbswprint(*p, left, 0); 132 if (left == 0) 133 return; 134 p++; 135 wantspace = 1; 136 } 137 } 138 } 139 140 if (needcomm) { 141 if (!commandonly) { 142 char **argv = NULL; 143 144 if (kd != NULL) { 145 argv = kvm_getargv(kd, kp, termwidth); 146 if ((p = argv) != NULL) { 147 while (*p) { 148 if (wantspace) { 149 putchar(' '); 150 left--; 151 } 152 left -= mbswprint(*p, left, 0); 153 if (left == 0) 154 return; 155 p++; 156 wantspace = 1; 157 } 158 } 159 } 160 if (argv == NULL || argv[0] == NULL || 161 strcmp(cmdpart(argv[0]), kp->p_comm)) { 162 if (wantspace) { 163 putchar(' '); 164 if (--left == 0) 165 return; 166 } 167 putchar('('); 168 left--; 169 left -= mbswprint(kp->p_comm, left, 0); 170 if (left == 0) 171 return; 172 putchar(')'); 173 left--; 174 } 175 } else { 176 if (wantspace) { 177 putchar(' '); 178 left--; 179 } 180 left -= mbswprint(kp->p_comm, left, 0); 181 } 182 } 183 if (ve->next != NULL) 184 while (left-- > 0) 185 putchar(' '); 186 } 187 188 void 189 ucomm(const struct kinfo_proc *kp, VARENT *ve) 190 { 191 mbswprint(kp->p_comm, ve->var->width, ve->next != NULL); 192 } 193 194 void 195 curwd(const struct kinfo_proc *kp, VARENT *ve) 196 { 197 int name[] = { CTL_KERN, KERN_PROC_CWD, kp->p_pid }; 198 char path[PATH_MAX]; 199 size_t pathlen = sizeof path; 200 201 if (!kvm_sysctl_only || sysctl(name, 3, path, &pathlen, NULL, 0) != 0) 202 *path = '\0'; 203 204 mbswprint(path, ve->var->width, ve->next != NULL); 205 } 206 207 void 208 logname(const struct kinfo_proc *kp, VARENT *ve) 209 { 210 VAR *v; 211 212 v = ve->var; 213 if (kp->p_login[0]) { 214 int n = min(v->width, LOGIN_NAME_MAX); 215 mbswprint(kp->p_login, n, ve->next != NULL); 216 if (ve->next != NULL) 217 while (n++ < v->width) 218 putchar(' '); 219 } else 220 (void)printf("%-*s", v->width, "-"); 221 } 222 223 #define pgtok(a) (((unsigned long long)(a)*getpagesize())/1024) 224 225 void 226 printstate(const struct kinfo_proc *kp, VARENT *ve) 227 { 228 int flag; 229 char *cp, state = '\0'; 230 VAR *v; 231 char buf[16]; 232 233 v = ve->var; 234 flag = kp->p_flag; 235 cp = buf; 236 237 switch (kp->p_stat) { 238 239 case SSTOP: 240 *cp = 'T'; 241 break; 242 243 case SSLEEP: 244 if (flag & P_SINTR) /* interruptible (long) */ 245 *cp = kp->p_slptime >= maxslp ? 'I' : 'S'; 246 else 247 *cp = 'D'; 248 break; 249 250 case SRUN: 251 case SIDL: 252 case SONPROC: 253 state = *cp = 'R'; 254 break; 255 256 case SDEAD: 257 *cp = 'Z'; 258 break; 259 260 default: 261 *cp = '?'; 262 } 263 cp++; 264 265 if (kp->p_nice < NZERO) 266 *cp++ = '<'; 267 else if (kp->p_nice > NZERO) 268 *cp++ = 'N'; 269 if (kp->p_psflags & PS_TRACED) 270 *cp++ = 'X'; 271 if ((kp->p_psflags & (PS_EXITING | PS_ZOMBIE)) == PS_EXITING) 272 *cp++ = 'E'; 273 if (kp->p_psflags & PS_ISPWAIT) 274 *cp++ = 'V'; 275 if (flag & P_SYSTEM) 276 *cp++ = 'K'; 277 if ((flag & P_SYSTEM) == 0 && 278 kp->p_rlim_rss_cur / 1024 < pgtok(kp->p_vm_rssize)) 279 *cp++ = '>'; 280 if (kp->p_eflag & EPROC_SLEADER) 281 *cp++ = 's'; 282 if ((kp->p_psflags & PS_CONTROLT) && kp->p__pgid == kp->p_tpgid) 283 *cp++ = '+'; 284 if (kp->p_psflags & PS_PLEDGE) 285 *cp++ = 'p'; 286 if (kp->p_eflag & EPROC_UNVEIL) { 287 if (kp->p_eflag & EPROC_LKUNVEIL) 288 *cp++ = 'U'; 289 else 290 *cp++ = 'u'; 291 } 292 *cp = '\0'; 293 294 if (state == 'R' && kp->p_cpuid != KI_NOCPU) { 295 char pbuf[16]; 296 297 snprintf(pbuf, sizeof pbuf, "/%llu", kp->p_cpuid); 298 *++cp = '\0'; 299 strlcat(buf, pbuf, sizeof buf); 300 cp = buf + strlen(buf); 301 } 302 303 (void)printf("%-*s", v->width, buf); 304 } 305 306 void 307 printpledge(const struct kinfo_proc *kp, VARENT *ve) 308 { 309 int i; 310 VAR *v; 311 char buf[1024]; 312 313 v = ve->var; 314 buf[0] = '\0'; 315 316 for (i = 0; pledgenames[i].bits != 0; i++) { 317 if (pledgenames[i].bits & kp->p_pledge) { 318 if (*buf != '\0') 319 strlcat(buf, ",", sizeof buf); 320 strlcat(buf, pledgenames[i].name, sizeof buf); 321 } 322 } 323 324 (void)printf("%-*s", v->width, buf); 325 } 326 327 void 328 pri(const struct kinfo_proc *kp, VARENT *ve) 329 { 330 VAR *v; 331 332 v = ve->var; 333 (void)printf("%*d", v->width, kp->p_priority - PZERO); 334 } 335 336 void 337 pnice(const struct kinfo_proc *kp, VARENT *ve) 338 { 339 VAR *v; 340 v = ve->var; 341 (void)printf("%*d", v->width, kp->p_nice - NZERO); 342 } 343 344 void 345 euname(const struct kinfo_proc *kp, VARENT *ve) 346 { 347 mbswprint(user_from_uid(kp->p_uid, 0), ve->var->width, 348 ve->next != NULL); 349 } 350 351 void 352 runame(const struct kinfo_proc *kp, VARENT *ve) 353 { 354 mbswprint(user_from_uid(kp->p_ruid, 0), ve->var->width, 355 ve->next != NULL); 356 } 357 358 void 359 gname(const struct kinfo_proc *kp, VARENT *ve) 360 { 361 mbswprint(group_from_gid(kp->p_gid, 0), ve->var->width, 362 ve->next != NULL); 363 } 364 365 void 366 rgname(const struct kinfo_proc *kp, VARENT *ve) 367 { 368 mbswprint(group_from_gid(kp->p_rgid, 0), ve->var->width, 369 ve->next != NULL); 370 } 371 372 void 373 supgid(const struct kinfo_proc *kp, VARENT *ve) 374 { 375 char buf[1024]; 376 char *p = buf; 377 ssize_t size = sizeof(buf); 378 int i, len; 379 380 for (i = 0; i < kp->p_ngroups; i++) { 381 len = snprintf(p, size, "%s%u", 382 p == buf ? "" : ",", 383 kp->p_groups[i]); 384 if (len < 0 || len >= size) 385 break; 386 p += len; 387 size -= len; 388 } 389 390 (void)printf("%-*s", ve->var->width, buf); 391 } 392 393 void 394 supgrp(const struct kinfo_proc *kp, VARENT *ve) 395 { 396 char buf[1024]; 397 char *p = buf; 398 ssize_t size = sizeof(buf); 399 int i, len; 400 401 for (i = 0; i < kp->p_ngroups; i++) { 402 len = snprintf(p, size, "%s%s", 403 p == buf ? "" : ",", 404 group_from_gid(kp->p_groups[i], 0)); 405 if (len < 0 || len >= size) 406 break; 407 p += len; 408 size -= len; 409 } 410 411 (void)printf("%-*s", ve->var->width, buf); 412 } 413 414 void 415 tdev(const struct kinfo_proc *kp, VARENT *ve) 416 { 417 VAR *v; 418 dev_t dev; 419 420 v = ve->var; 421 dev = kp->p_tdev; 422 if (dev == NODEV) 423 (void)printf("%*s", v->width, "??"); 424 else { 425 char buff[10+1+10+1]; 426 427 (void)snprintf(buff, sizeof(buff), 428 "%u/%u", major(dev), minor(dev)); 429 (void)printf("%*s", v->width, buff); 430 } 431 } 432 433 void 434 tname(const struct kinfo_proc *kp, VARENT *ve) 435 { 436 VAR *v; 437 dev_t dev; 438 char *ttname; 439 440 v = ve->var; 441 dev = kp->p_tdev; 442 if (dev == NODEV || (ttname = devname(dev, S_IFCHR)) == NULL) 443 (void)printf("%-*s", v->width, "??"); 444 else { 445 if (strncmp(ttname, "tty", 3) == 0) 446 ttname += 3; 447 (void)printf("%*.*s%c", v->width-1, v->width-1, ttname, 448 kp->p_eflag & EPROC_CTTY ? ' ' : '-'); 449 } 450 } 451 452 void 453 longtname(const struct kinfo_proc *kp, VARENT *ve) 454 { 455 VAR *v; 456 dev_t dev; 457 char *ttname; 458 459 v = ve->var; 460 dev = kp->p_tdev; 461 if (dev == NODEV || (ttname = devname(dev, S_IFCHR)) == NULL) 462 (void)printf("%-*s", v->width, "??"); 463 else 464 (void)printf("%-*s", v->width, ttname); 465 } 466 467 void 468 started(const struct kinfo_proc *kp, VARENT *ve) 469 { 470 VAR *v; 471 static time_t now; 472 time_t startt; 473 struct tm *tp; 474 char buf[100]; 475 476 v = ve->var; 477 if (!kp->p_uvalid) { 478 (void)printf("%-*s", v->width, "-"); 479 return; 480 } 481 482 #define SECSPERHOUR (60 * 60) 483 #define SECSPERDAY (24 * 60 * 60) 484 485 startt = kp->p_ustart_sec; 486 tp = localtime(&startt); 487 if (!now) 488 (void)time(&now); 489 if (now - kp->p_ustart_sec < 12 * SECSPERHOUR) { 490 (void)strftime(buf, sizeof(buf) - 1, "%l:%M%p", tp); 491 } else if (now - kp->p_ustart_sec < 7 * SECSPERDAY) { 492 (void)strftime(buf, sizeof(buf) - 1, "%a%I%p", tp); 493 } else 494 (void)strftime(buf, sizeof(buf) - 1, "%e%b%y", tp); 495 (void)printf("%-*s", v->width, buf); 496 } 497 498 void 499 lstarted(const struct kinfo_proc *kp, VARENT *ve) 500 { 501 VAR *v; 502 time_t startt; 503 char buf[100]; 504 505 v = ve->var; 506 if (!kp->p_uvalid) { 507 (void)printf("%-*s", v->width, "-"); 508 return; 509 } 510 startt = kp->p_ustart_sec; 511 (void)strftime(buf, sizeof(buf) -1, "%c", 512 localtime(&startt)); 513 (void)printf("%-*s", v->width, buf); 514 } 515 516 void elapsed(const struct kinfo_proc *kp, VARENT *ve) 517 { 518 VAR *v; 519 static time_t now; 520 time_t secs; 521 char buf[64]; 522 long days, hours, minutes, seconds; 523 524 v = ve->var; 525 if (!kp->p_uvalid) { 526 (void)printf("%*s", v->width, "-"); 527 return; 528 } 529 530 if (!now) 531 (void)time(&now); 532 secs = now - kp->p_ustart_sec; 533 534 if (secs < 0) { 535 (void)printf("%*s", v->width, "-"); 536 return; 537 } 538 539 days = secs / SECSPERDAY; 540 secs %= SECSPERDAY; 541 542 hours = secs / SECSPERHOUR; 543 secs %= SECSPERHOUR; 544 545 minutes = secs / 60; 546 seconds = secs % 60; 547 548 if (days > 0) 549 (void)snprintf(buf, sizeof(buf), "%ld-%02ld:%02ld:%02ld", 550 days, hours, minutes, seconds); 551 else if (hours > 0) 552 (void)snprintf(buf, sizeof(buf), "%02ld:%02ld:%02ld", 553 hours, minutes, seconds); 554 else 555 (void)snprintf(buf, sizeof(buf), "%02ld:%02ld", 556 minutes, seconds); 557 (void)printf("%*s", v->width, buf); 558 } 559 560 void 561 wchan(const struct kinfo_proc *kp, VARENT *ve) 562 { 563 VAR *v; 564 565 v = ve->var; 566 if (kp->p_wmesg[0]) { 567 (void)printf("%-*s", (int)v->width, kp->p_wmesg); 568 } else 569 (void)printf("%-*s", v->width, "-"); 570 } 571 572 void 573 vsize(const struct kinfo_proc *kp, VARENT *ve) 574 { 575 VAR *v; 576 577 v = ve->var; 578 (void)printf("%*llu", v->width, 579 pgtok(kp->p_vm_dsize + kp->p_vm_ssize + kp->p_vm_tsize)); 580 } 581 582 void 583 rssize(const struct kinfo_proc *kp, VARENT *ve) 584 { 585 VAR *v; 586 587 v = ve->var; 588 /* XXX don't have info about shared */ 589 (void)printf("%*llu", v->width, (kp->p_flag & P_SYSTEM) ? 0 : 590 pgtok(kp->p_vm_rssize)); 591 } 592 593 void 594 p_rssize(const struct kinfo_proc *kp, VARENT *ve) 595 { 596 VAR *v; 597 598 v = ve->var; 599 (void)printf("%*llu", v->width, (kp->p_flag & P_SYSTEM) ? 0 : 600 pgtok(kp->p_vm_rssize)); 601 } 602 603 void 604 cputime(const struct kinfo_proc *kp, VARENT *ve) 605 { 606 VAR *v; 607 long secs; 608 long psecs; /* "parts" of a second. first micro, then centi */ 609 char obuff[128]; 610 611 v = ve->var; 612 if (kp->p_stat == SDEAD || !kp->p_uvalid) { 613 secs = 0; 614 psecs = 0; 615 } else { 616 /* 617 * This counts time spent handling interrupts. XXX 618 */ 619 secs = kp->p_rtime_sec; 620 psecs = kp->p_rtime_usec; 621 if (sumrusage) { 622 secs += kp->p_uctime_sec; 623 psecs += kp->p_uctime_usec; 624 } 625 /* 626 * round and scale to 100's 627 */ 628 psecs = (psecs + 5000) / 10000; 629 secs += psecs / 100; 630 psecs = psecs % 100; 631 } 632 (void)snprintf(obuff, sizeof(obuff), 633 "%3ld:%02ld.%02ld", secs/60, secs%60, psecs); 634 (void)printf("%*s", v->width, obuff); 635 } 636 637 double 638 getpcpu(const struct kinfo_proc *kp) 639 { 640 if (fscale == 0) 641 return (0.0); 642 643 #define fxtofl(fixpt) ((double)(fixpt) / fscale) 644 645 return (100.0 * fxtofl(kp->p_pctcpu)); 646 } 647 648 void 649 pcpu(const struct kinfo_proc *kp, VARENT *ve) 650 { 651 VAR *v; 652 653 v = ve->var; 654 (void)printf("%*.1f", v->width, getpcpu(kp)); 655 } 656 657 double 658 getpmem(const struct kinfo_proc *kp) 659 { 660 double fracmem; 661 662 if (mempages == 0) 663 return (0.0); 664 665 if (kp->p_flag & P_SYSTEM) 666 return (0.0); 667 /* XXX don't have info about shared */ 668 fracmem = ((float)kp->p_vm_rssize)/mempages; 669 return (100.0 * fracmem); 670 } 671 672 void 673 pmem(const struct kinfo_proc *kp, VARENT *ve) 674 { 675 VAR *v; 676 677 v = ve->var; 678 (void)printf("%*.1f", v->width, getpmem(kp)); 679 } 680 681 void 682 pagein(const struct kinfo_proc *kp, VARENT *ve) 683 { 684 VAR *v; 685 686 v = ve->var; 687 (void)printf("%*llu", v->width, 688 kp->p_uvalid ? kp->p_uru_majflt : 0); 689 } 690 691 void 692 maxrss(const struct kinfo_proc *kp, VARENT *ve) 693 { 694 VAR *v; 695 696 v = ve->var; 697 (void)printf("%*llu", v->width, kp->p_rlim_rss_cur / 1024); 698 } 699 700 void 701 tsize(const struct kinfo_proc *kp, VARENT *ve) 702 { 703 VAR *v; 704 705 v = ve->var; 706 (void)printf("%*llu", v->width, pgtok(kp->p_vm_tsize)); 707 } 708 709 void 710 dsize(const struct kinfo_proc *kp, VARENT *ve) 711 { 712 VAR *v; 713 714 v = ve->var; 715 (void)printf("%*llu", v->width, pgtok(kp->p_vm_dsize)); 716 } 717 718 void 719 ssize(const struct kinfo_proc *kp, VARENT *ve) 720 { 721 VAR *v; 722 723 v = ve->var; 724 (void)printf("%*llu", v->width, pgtok(kp->p_vm_ssize)); 725 } 726 727 /* 728 * Generic output routines. Print fields from various prototype 729 * structures. 730 */ 731 static void 732 printval(char *bp, VAR *v) 733 { 734 char ofmt[32]; 735 736 snprintf(ofmt, sizeof(ofmt), "%%%s*%s", (v->flag & LJUST) ? "-" : "", 737 v->fmt); 738 739 /* 740 * Note that the "INF127" check is nonsensical for types 741 * that are or can be signed. 742 */ 743 #define GET(type) (*(type *)bp) 744 #define CHK_INF127(n) (((n) > 127) && (v->flag & INF127) ? 127 : (n)) 745 746 switch (v->type) { 747 case INT8: 748 (void)printf(ofmt, v->width, GET(int8_t)); 749 break; 750 case UINT8: 751 (void)printf(ofmt, v->width, CHK_INF127(GET(u_int8_t))); 752 break; 753 case INT16: 754 (void)printf(ofmt, v->width, GET(int16_t)); 755 break; 756 case UINT16: 757 (void)printf(ofmt, v->width, CHK_INF127(GET(u_int16_t))); 758 break; 759 case INT32: 760 (void)printf(ofmt, v->width, GET(int32_t)); 761 break; 762 case UINT32: 763 (void)printf(ofmt, v->width, CHK_INF127(GET(u_int32_t))); 764 break; 765 case INT64: 766 (void)printf(ofmt, v->width, GET(int64_t)); 767 break; 768 case UINT64: 769 (void)printf(ofmt, v->width, CHK_INF127(GET(u_int64_t))); 770 break; 771 default: 772 errx(1, "unknown type %d", v->type); 773 } 774 #undef GET 775 #undef CHK_INF127 776 } 777 778 void 779 pvar(const struct kinfo_proc *kp, VARENT *ve) 780 { 781 VAR *v; 782 783 v = ve->var; 784 if ((v->flag & USER) && !kp->p_uvalid) 785 (void)printf("%*s", v->width, "-"); 786 else 787 printval((char *)kp + v->off, v); 788 } 789 790 void 791 emulname(const struct kinfo_proc *kp, VARENT *ve) 792 { 793 VAR *v; 794 795 v = ve->var; 796 797 (void)printf("%-*s", (int)v->width, kp->p_emul); 798 } 799