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