src/bin/ps/print.c

846 lines
17 KiB
C

/* $OpenBSD: print.c,v 1.89 2024/04/28 16:43:15 florian Exp $ */
/* $NetBSD: print.c,v 1.27 1995/09/29 21:58:12 cgd Exp $ */
/*-
* Copyright (c) 1990, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/param.h> /* PZERO NODEV */
#include <sys/types.h>
#include <sys/signal.h>
#include <sys/proc.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
#define PLEDGENAMES
#include <sys/pledge.h>
#include <err.h>
#include <grp.h>
#include <kvm.h>
#include <math.h>
#include <nlist.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <pwd.h>
#include "ps.h"
extern kvm_t *kd;
extern int needenv, needcomm, neednlist, commandonly;
int mbswprint(const char *, int, int); /* utf8.c */
static char *cmdpart(char *);
#define min(a,b) ((a) < (b) ? (a) : (b))
static char *
cmdpart(char *arg0)
{
char *cp;
return ((cp = strrchr(arg0, '/')) != NULL ? cp + 1 : arg0);
}
void
printheader(void)
{
VAR *v;
struct varent *vent;
if (!needheader)
return;
for (vent = vhead; vent; vent = vent->next) {
v = vent->var;
if (v->flag & LJUST) {
if (vent->next == NULL) /* last one */
(void)printf("%s", v->header);
else
(void)printf("%-*s", v->width, v->header);
} else
(void)printf("%*s", v->width, v->header);
if (vent->next != NULL)
(void)putchar(' ');
}
(void)putchar('\n');
}
static int
print_comm_name(const struct kinfo_proc *kp, int left, int trail)
{
left -= mbswprint(kp->p_comm, left, trail);
if (left > 1 && kp->p_name[0] != '\0') {
putchar('/');
left--;
left -= mbswprint(kp->p_name, left, trail);
}
return left;
}
void
command(const struct pinfo *pi, VARENT *ve)
{
const struct kinfo_proc *kp = pi->ki;
VAR *v;
int left, wantspace = 0;
char **p;
/*
* Determine the available number of display columns.
* Always decrement and check after writing.
* No check is needed before mbswprint()
* and after writing the last data, though.
*/
v = ve->var;
if (ve->next != NULL || termwidth != UNLIMITED) {
if (ve->next == NULL) {
left = termwidth - (totwidth - v->width);
if (left < 1) /* already wrapped, just use std width */
left = v->width;
} else
left = v->width;
} else
left = INT_MAX;
if (needenv && kd != NULL) {
char **envp = kvm_getenvv(kd, kp, termwidth);
if ((p = envp) != NULL) {
while (*p) {
if (wantspace) {
putchar(' ');
left--;
}
left -= mbswprint(*p, left, 0);
if (left == 0)
return;
p++;
wantspace = 1;
}
}
}
if (needcomm) {
if (pi->prefix)
left -= mbswprint(pi->prefix, left, 0);
if (!commandonly) {
char **argv = NULL;
if (kd != NULL) {
argv = kvm_getargv(kd, kp, termwidth);
if ((p = argv) != NULL) {
while (*p) {
if (wantspace) {
putchar(' ');
left--;
}
left -= mbswprint(*p, left, 0);
if (left == 0)
return;
p++;
wantspace = 1;
}
}
}
if (argv == NULL || argv[0] == NULL ||
kp->p_name[0] != '\0' ||
strcmp(cmdpart(argv[0]), kp->p_comm)) {
if (wantspace) {
putchar(' ');
if (--left == 0)
return;
}
putchar('(');
left--;
left = print_comm_name(kp, left, 0);
if (left == 0)
return;
putchar(')');
left--;
}
} else {
if (wantspace) {
putchar(' ');
left--;
}
left = print_comm_name(kp, left, 0);
}
}
if (ve->next != NULL)
while (left-- > 0)
putchar(' ');
}
void
ucomm(const struct pinfo *pi, VARENT *ve)
{
const struct kinfo_proc *kp = pi->ki;
mbswprint(kp->p_comm, ve->var->width, ve->next != NULL);
}
void
curwd(const struct pinfo *pi, VARENT *ve)
{
const struct kinfo_proc *kp = pi->ki;
int name[] = { CTL_KERN, KERN_PROC_CWD, kp->p_pid };
char path[PATH_MAX];
size_t pathlen = sizeof path;
if (!kvm_sysctl_only || sysctl(name, 3, path, &pathlen, NULL, 0) != 0)
*path = '\0';
mbswprint(path, ve->var->width, ve->next != NULL);
}
void
logname(const struct pinfo *pi, VARENT *ve)
{
const struct kinfo_proc *kp = pi->ki;
VAR *v;
v = ve->var;
if (kp->p_login[0]) {
int n = min(v->width, LOGIN_NAME_MAX);
mbswprint(kp->p_login, n, ve->next != NULL);
if (ve->next != NULL)
while (n++ < v->width)
putchar(' ');
} else
(void)printf("%-*s", v->width, "-");
}
#define pgtok(a) (((unsigned long long)(a)*getpagesize())/1024)
void
printstate(const struct pinfo *pi, VARENT *ve)
{
const struct kinfo_proc *kp = pi->ki;
int flag;
char *cp, state = '\0';
VAR *v;
char buf[16];
v = ve->var;
flag = kp->p_flag;
cp = buf;
switch (kp->p_stat) {
case SSTOP:
*cp = 'T';
break;
case SSLEEP:
if (flag & P_SINTR) /* interruptible (long) */
*cp = kp->p_slptime >= maxslp ? 'I' : 'S';
else
*cp = 'D';
break;
case SRUN:
case SIDL:
case SONPROC:
state = *cp = 'R';
break;
case SDEAD:
*cp = 'Z';
break;
default:
*cp = '?';
}
cp++;
if (kp->p_nice < NZERO)
*cp++ = '<';
else if (kp->p_nice > NZERO)
*cp++ = 'N';
if (kp->p_psflags & PS_TRACED)
*cp++ = 'X';
if ((kp->p_psflags & (PS_EXITING | PS_ZOMBIE)) == PS_EXITING)
*cp++ = 'E';
if (kp->p_psflags & PS_ISPWAIT)
*cp++ = 'V';
if (flag & P_SYSTEM)
*cp++ = 'K';
if ((flag & P_SYSTEM) == 0 &&
kp->p_rlim_rss_cur / 1024 < pgtok(kp->p_vm_rssize))
*cp++ = '>';
if (kp->p_eflag & EPROC_SLEADER)
*cp++ = 's';
if ((kp->p_psflags & PS_CONTROLT) && kp->p__pgid == kp->p_tpgid)
*cp++ = '+';
if (kp->p_psflags & PS_PLEDGE)
*cp++ = 'p';
if (kp->p_eflag & EPROC_UNVEIL) {
if (kp->p_eflag & EPROC_LKUNVEIL)
*cp++ = 'U';
else
*cp++ = 'u';
}
if (kp->p_psflags & PS_CHROOT)
*cp++ = 'c';
*cp = '\0';
if (state == 'R' && kp->p_cpuid != KI_NOCPU) {
char pbuf[16];
snprintf(pbuf, sizeof pbuf, "/%llu", kp->p_cpuid);
*++cp = '\0';
strlcat(buf, pbuf, sizeof buf);
cp = buf + strlen(buf);
}
(void)printf("%-*s", v->width, buf);
}
void
printpledge(const struct pinfo *pi, VARENT *ve)
{
const struct kinfo_proc *kp = pi->ki;
int i;
VAR *v;
char buf[1024];
v = ve->var;
buf[0] = '\0';
for (i = 0; pledgenames[i].bits != 0; i++) {
if (pledgenames[i].bits & kp->p_pledge) {
if (*buf != '\0')
strlcat(buf, ",", sizeof buf);
strlcat(buf, pledgenames[i].name, sizeof buf);
}
}
(void)printf("%-*s", v->width, buf);
}
void
pri(const struct pinfo *pi, VARENT *ve)
{
const struct kinfo_proc *kp = pi->ki;
VAR *v;
v = ve->var;
(void)printf("%*d", v->width, kp->p_priority - PZERO);
}
void
pnice(const struct pinfo *pi, VARENT *ve)
{
const struct kinfo_proc *kp = pi->ki;
VAR *v;
v = ve->var;
(void)printf("%*d", v->width, kp->p_nice - NZERO);
}
void
euname(const struct pinfo *pi, VARENT *ve)
{
const struct kinfo_proc *kp = pi->ki;
mbswprint(user_from_uid(kp->p_uid, 0), ve->var->width,
ve->next != NULL);
}
void
runame(const struct pinfo *pi, VARENT *ve)
{
const struct kinfo_proc *kp = pi->ki;
mbswprint(user_from_uid(kp->p_ruid, 0), ve->var->width,
ve->next != NULL);
}
void
gname(const struct pinfo *pi, VARENT *ve)
{
const struct kinfo_proc *kp = pi->ki;
mbswprint(group_from_gid(kp->p_gid, 0), ve->var->width,
ve->next != NULL);
}
void
rgname(const struct pinfo *pi, VARENT *ve)
{
const struct kinfo_proc *kp = pi->ki;
mbswprint(group_from_gid(kp->p_rgid, 0), ve->var->width,
ve->next != NULL);
}
void
supgid(const struct pinfo *pi, VARENT *ve)
{
const struct kinfo_proc *kp = pi->ki;
char buf[1024];
char *p = buf;
ssize_t size = sizeof(buf);
int i, len;
for (i = 0; i < kp->p_ngroups; i++) {
len = snprintf(p, size, "%s%u",
p == buf ? "" : ",",
kp->p_groups[i]);
if (len < 0 || len >= size)
break;
p += len;
size -= len;
}
(void)printf("%-*s", ve->var->width, buf);
}
void
supgrp(const struct pinfo *pi, VARENT *ve)
{
const struct kinfo_proc *kp = pi->ki;
char buf[1024];
char *p = buf;
ssize_t size = sizeof(buf);
int i, len;
for (i = 0; i < kp->p_ngroups; i++) {
len = snprintf(p, size, "%s%s",
p == buf ? "" : ",",
group_from_gid(kp->p_groups[i], 0));
if (len < 0 || len >= size)
break;
p += len;
size -= len;
}
(void)printf("%-*s", ve->var->width, buf);
}
void
tdev(const struct pinfo *pi, VARENT *ve)
{
const struct kinfo_proc *kp = pi->ki;
VAR *v;
dev_t dev;
v = ve->var;
dev = kp->p_tdev;
if (dev == NODEV)
(void)printf("%*s", v->width, "??");
else {
char buff[10+1+10+1];
(void)snprintf(buff, sizeof(buff),
"%u/%u", major(dev), minor(dev));
(void)printf("%*s", v->width, buff);
}
}
void
tname(const struct pinfo *pi, VARENT *ve)
{
const struct kinfo_proc *kp = pi->ki;
VAR *v;
dev_t dev;
char *ttname;
v = ve->var;
dev = kp->p_tdev;
if (dev == NODEV || (ttname = devname(dev, S_IFCHR)) == NULL)
(void)printf("%-*s", v->width, "??");
else {
if (strncmp(ttname, "tty", 3) == 0)
ttname += 3;
(void)printf("%*.*s%c", v->width-1, v->width-1, ttname,
kp->p_eflag & EPROC_CTTY ? ' ' : '-');
}
}
void
longtname(const struct pinfo *pi, VARENT *ve)
{
const struct kinfo_proc *kp = pi->ki;
VAR *v;
dev_t dev;
char *ttname;
v = ve->var;
dev = kp->p_tdev;
if (dev == NODEV || (ttname = devname(dev, S_IFCHR)) == NULL)
(void)printf("%-*s", v->width, "??");
else
(void)printf("%-*s", v->width, ttname);
}
void
started(const struct pinfo *pi, VARENT *ve)
{
const struct kinfo_proc *kp = pi->ki;
VAR *v;
static time_t now;
time_t startt;
struct tm *tp;
char buf[100];
v = ve->var;
if (!kp->p_uvalid) {
(void)printf("%-*s", v->width, "-");
return;
}
#define SECSPERHOUR (60 * 60)
#define SECSPERDAY (24 * 60 * 60)
startt = kp->p_ustart_sec;
tp = localtime(&startt);
if (tp == NULL) {
(void)printf("%-*s", v->width, "-");
return;
}
if (!now)
(void)time(&now);
if (now - kp->p_ustart_sec < 12 * SECSPERHOUR) {
(void)strftime(buf, sizeof(buf) - 1, "%l:%M%p", tp);
} else if (now - kp->p_ustart_sec < 7 * SECSPERDAY) {
(void)strftime(buf, sizeof(buf) - 1, "%a%I%p", tp);
} else
(void)strftime(buf, sizeof(buf) - 1, "%e%b%y", tp);
(void)printf("%-*s", v->width, buf);
}
void
lstarted(const struct pinfo *pi, VARENT *ve)
{
const struct kinfo_proc *kp = pi->ki;
VAR *v;
time_t startt;
char buf[100];
v = ve->var;
if (!kp->p_uvalid) {
(void)printf("%-*s", v->width, "-");
return;
}
startt = kp->p_ustart_sec;
(void)strftime(buf, sizeof(buf) -1, "%c",
localtime(&startt));
(void)printf("%-*s", v->width, buf);
}
void elapsed(const struct pinfo *pi, VARENT *ve)
{
const struct kinfo_proc *kp = pi->ki;
VAR *v;
static time_t now;
time_t secs;
char buf[64];
long days, hours, minutes, seconds;
v = ve->var;
if (!kp->p_uvalid) {
(void)printf("%*s", v->width, "-");
return;
}
if (!now)
(void)time(&now);
secs = now - kp->p_ustart_sec;
if (secs < 0) {
(void)printf("%*s", v->width, "-");
return;
}
days = secs / SECSPERDAY;
secs %= SECSPERDAY;
hours = secs / SECSPERHOUR;
secs %= SECSPERHOUR;
minutes = secs / 60;
seconds = secs % 60;
if (days > 0)
(void)snprintf(buf, sizeof(buf), "%ld-%02ld:%02ld:%02ld",
days, hours, minutes, seconds);
else if (hours > 0)
(void)snprintf(buf, sizeof(buf), "%02ld:%02ld:%02ld",
hours, minutes, seconds);
else
(void)snprintf(buf, sizeof(buf), "%02ld:%02ld",
minutes, seconds);
(void)printf("%*s", v->width, buf);
}
void
wchan(const struct pinfo *pi, VARENT *ve)
{
const struct kinfo_proc *kp = pi->ki;
VAR *v;
v = ve->var;
if (kp->p_wmesg[0]) {
(void)printf("%-*s", (int)v->width, kp->p_wmesg);
} else
(void)printf("%-*s", v->width, "-");
}
void
vsize(const struct pinfo *pi, VARENT *ve)
{
const struct kinfo_proc *kp = pi->ki;
VAR *v;
v = ve->var;
(void)printf("%*llu", v->width,
pgtok(kp->p_vm_dsize + kp->p_vm_ssize + kp->p_vm_tsize));
}
void
rssize(const struct pinfo *pi, VARENT *ve)
{
const struct kinfo_proc *kp = pi->ki;
VAR *v;
v = ve->var;
/* XXX don't have info about shared */
(void)printf("%*llu", v->width, (kp->p_flag & P_SYSTEM) ? 0 :
pgtok(kp->p_vm_rssize));
}
void
p_rssize(const struct pinfo *pi, VARENT *ve)
{
const struct kinfo_proc *kp = pi->ki;
VAR *v;
v = ve->var;
(void)printf("%*llu", v->width, (kp->p_flag & P_SYSTEM) ? 0 :
pgtok(kp->p_vm_rssize));
}
void
cputime(const struct pinfo *pi, VARENT *ve)
{
const struct kinfo_proc *kp = pi->ki;
VAR *v;
long secs;
long psecs; /* "parts" of a second. first micro, then centi */
char obuff[128];
v = ve->var;
if (kp->p_stat == SDEAD || !kp->p_uvalid) {
secs = 0;
psecs = 0;
} else {
/*
* This counts time spent handling interrupts. XXX
*/
secs = kp->p_rtime_sec;
psecs = kp->p_rtime_usec;
if (sumrusage) {
secs += kp->p_uctime_sec;
psecs += kp->p_uctime_usec;
}
/*
* round and scale to 100's
*/
psecs = (psecs + 5000) / 10000;
secs += psecs / 100;
psecs = psecs % 100;
}
(void)snprintf(obuff, sizeof(obuff),
"%3ld:%02ld.%02ld", secs/60, secs%60, psecs);
(void)printf("%*s", v->width, obuff);
}
double
getpcpu(const struct kinfo_proc *kp)
{
if (fscale == 0)
return (0.0);
#define fxtofl(fixpt) ((double)(fixpt) / fscale)
return (100.0 * fxtofl(kp->p_pctcpu));
}
void
pcpu(const struct pinfo *pi, VARENT *ve)
{
VAR *v;
v = ve->var;
(void)printf("%*.1f", v->width, getpcpu(pi->ki));
}
double
getpmem(const struct kinfo_proc *kp)
{
double fracmem;
if (mempages == 0)
return (0.0);
if (kp->p_flag & P_SYSTEM)
return (0.0);
/* XXX don't have info about shared */
fracmem = ((float)kp->p_vm_rssize)/mempages;
return (100.0 * fracmem);
}
void
pmem(const struct pinfo *pi, VARENT *ve)
{
VAR *v;
v = ve->var;
(void)printf("%*.1f", v->width, getpmem(pi->ki));
}
void
pagein(const struct pinfo *pi, VARENT *ve)
{
const struct kinfo_proc *kp = pi->ki;
VAR *v;
v = ve->var;
(void)printf("%*llu", v->width,
kp->p_uvalid ? kp->p_uru_majflt : 0);
}
void
maxrss(const struct pinfo *pi, VARENT *ve)
{
const struct kinfo_proc *kp = pi->ki;
VAR *v;
v = ve->var;
(void)printf("%*llu", v->width, kp->p_rlim_rss_cur / 1024);
}
void
tsize(const struct pinfo *pi, VARENT *ve)
{
const struct kinfo_proc *kp = pi->ki;
VAR *v;
v = ve->var;
(void)printf("%*llu", v->width, pgtok(kp->p_vm_tsize));
}
void
dsize(const struct pinfo *pi, VARENT *ve)
{
const struct kinfo_proc *kp = pi->ki;
VAR *v;
v = ve->var;
(void)printf("%*llu", v->width, pgtok(kp->p_vm_dsize));
}
void
ssize(const struct pinfo *pi, VARENT *ve)
{
const struct kinfo_proc *kp = pi->ki;
VAR *v;
v = ve->var;
(void)printf("%*llu", v->width, pgtok(kp->p_vm_ssize));
}
/*
* Generic output routines. Print fields from various prototype
* structures.
*/
static void
printval(char *bp, VAR *v)
{
char ofmt[32];
snprintf(ofmt, sizeof(ofmt), "%%%s*%s", (v->flag & LJUST) ? "-" : "",
v->fmt);
/*
* Note that the "INF127" check is nonsensical for types
* that are or can be signed.
*/
#define GET(type) (*(type *)bp)
#define CHK_INF127(n) (((n) > 127) && (v->flag & INF127) ? 127 : (n))
switch (v->type) {
case INT8:
(void)printf(ofmt, v->width, GET(int8_t));
break;
case UINT8:
(void)printf(ofmt, v->width, CHK_INF127(GET(u_int8_t)));
break;
case INT16:
(void)printf(ofmt, v->width, GET(int16_t));
break;
case UINT16:
(void)printf(ofmt, v->width, CHK_INF127(GET(u_int16_t)));
break;
case INT32:
(void)printf(ofmt, v->width, GET(int32_t));
break;
case UINT32:
(void)printf(ofmt, v->width, CHK_INF127(GET(u_int32_t)));
break;
case INT64:
(void)printf(ofmt, v->width, GET(int64_t));
break;
case UINT64:
(void)printf(ofmt, v->width, CHK_INF127(GET(u_int64_t)));
break;
default:
errx(1, "unknown type %d", v->type);
}
#undef GET
#undef CHK_INF127
}
void
pvar(const struct pinfo *pi, VARENT *ve)
{
const struct kinfo_proc *kp = pi->ki;
VAR *v;
v = ve->var;
if ((v->flag & USER) && !kp->p_uvalid)
(void)printf("%*s", v->width, "-");
else
printval((char *)kp + v->off, v);
}