diff options
Diffstat (limited to 'bin/ps/print.c')
| -rw-r--r-- | bin/ps/print.c | 867 |
1 files changed, 867 insertions, 0 deletions
diff --git a/bin/ps/print.c b/bin/ps/print.c new file mode 100644 index 000000000000..f59e843effc1 --- /dev/null +++ b/bin/ps/print.c @@ -0,0 +1,867 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * 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> +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/proc.h> +#include <sys/stat.h> + +#include <sys/mac.h> +#include <sys/user.h> +#include <sys/sysctl.h> +#include <sys/vmmeter.h> + +#include <grp.h> +#include <jail.h> +#include <langinfo.h> +#include <locale.h> +#include <math.h> +#include <nlist.h> +#include <pwd.h> +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <vis.h> +#include <libxo/xo.h> + +#include "ps.h" + +#define COMMAND_WIDTH 16 +#define ARGUMENTS_WIDTH 16 + +#define ps_pgtok(a) (((a) * getpagesize()) / 1024) + +void +printheader(void) +{ + const VAR *v; + struct varent *vent; + + STAILQ_FOREACH(vent, &varlist, next_ve) + if (*vent->header != '\0') + break; + if (!vent) + return; + + STAILQ_FOREACH(vent, &varlist, next_ve) { + v = vent->var; + if (v->flag & LJUST) { + if (STAILQ_NEXT(vent, next_ve) == NULL) /* last one */ + xo_emit("{T:/%hs}", vent->header); + else + xo_emit("{T:/%-*hs}", vent->width, vent->header); + } else + xo_emit("{T:/%*hs}", vent->width, vent->header); + if (STAILQ_NEXT(vent, next_ve) != NULL) + xo_emit("{P: }"); + } + xo_emit("\n"); +} + +char * +arguments(KINFO *k, VARENT *ve) +{ + char *vis_args; + + if ((vis_args = malloc(strlen(k->ki_args) * 4 + 1)) == NULL) + xo_errx(1, "malloc failed"); + strvis(vis_args, k->ki_args, VIS_TAB | VIS_NL | VIS_NOSLASH); + + if (STAILQ_NEXT(ve, next_ve) != NULL && strlen(vis_args) > ARGUMENTS_WIDTH) + vis_args[ARGUMENTS_WIDTH] = '\0'; + + return (vis_args); +} + +char * +command(KINFO *k, VARENT *ve) +{ + char *vis_args, *vis_env, *str; + + if (cflag) { + /* If it is the last field, then don't pad */ + if (STAILQ_NEXT(ve, next_ve) == NULL) { + asprintf(&str, "%s%s%s%s%s", + k->ki_d.prefix ? k->ki_d.prefix : "", + k->ki_p->ki_comm, + (showthreads && k->ki_p->ki_numthreads > 1) ? "/" : "", + (showthreads && k->ki_p->ki_numthreads > 1) ? k->ki_p->ki_tdname : "", + (showthreads && k->ki_p->ki_numthreads > 1) ? k->ki_p->ki_moretdname : ""); + } else + str = strdup(k->ki_p->ki_comm); + + return (str); + } + if ((vis_args = malloc(strlen(k->ki_args) * 4 + 1)) == NULL) + xo_errx(1, "malloc failed"); + strvis(vis_args, k->ki_args, VIS_TAB | VIS_NL | VIS_NOSLASH); + + if (STAILQ_NEXT(ve, next_ve) == NULL) { + /* last field */ + + if (k->ki_env) { + if ((vis_env = malloc(strlen(k->ki_env) * 4 + 1)) + == NULL) + xo_errx(1, "malloc failed"); + strvis(vis_env, k->ki_env, + VIS_TAB | VIS_NL | VIS_NOSLASH); + } else + vis_env = NULL; + + asprintf(&str, "%s%s%s%s", + k->ki_d.prefix ? k->ki_d.prefix : "", + vis_env ? vis_env : "", + vis_env ? " " : "", + vis_args); + + if (vis_env != NULL) + free(vis_env); + free(vis_args); + } else { + /* ki_d.prefix & ki_env aren't shown for interim fields */ + str = vis_args; + + if (strlen(str) > COMMAND_WIDTH) + str[COMMAND_WIDTH] = '\0'; + } + + return (str); +} + +char * +ucomm(KINFO *k, VARENT *ve) +{ + char *str; + + if (STAILQ_NEXT(ve, next_ve) == NULL) { /* last field, don't pad */ + asprintf(&str, "%s%s%s%s%s", + k->ki_d.prefix ? k->ki_d.prefix : "", + k->ki_p->ki_comm, + (showthreads && k->ki_p->ki_numthreads > 1) ? "/" : "", + (showthreads && k->ki_p->ki_numthreads > 1) ? k->ki_p->ki_tdname : "", + (showthreads && k->ki_p->ki_numthreads > 1) ? k->ki_p->ki_moretdname : ""); + } else { + if (showthreads && k->ki_p->ki_numthreads > 1) + asprintf(&str, "%s/%s%s", k->ki_p->ki_comm, + k->ki_p->ki_tdname, k->ki_p->ki_moretdname); + else + str = strdup(k->ki_p->ki_comm); + } + return (str); +} + +char * +tdnam(KINFO *k, VARENT *ve __unused) +{ + char *str; + + if (showthreads && k->ki_p->ki_numthreads > 1) + asprintf(&str, "%s%s", k->ki_p->ki_tdname, + k->ki_p->ki_moretdname); + else + str = strdup(" "); + + return (str); +} + +char * +logname(KINFO *k, VARENT *ve __unused) +{ + + if (*k->ki_p->ki_login == '\0') + return (NULL); + return (strdup(k->ki_p->ki_login)); +} + +char * +state(KINFO *k, VARENT *ve __unused) +{ + long flag, tdflags; + char *cp, *buf; + + buf = malloc(16); + if (buf == NULL) + xo_errx(1, "malloc failed"); + + flag = k->ki_p->ki_flag; + tdflags = k->ki_p->ki_tdflags; /* XXXKSE */ + cp = buf; + + switch (k->ki_p->ki_stat) { + + case SSTOP: + *cp = 'T'; + break; + + case SSLEEP: + if (tdflags & TDF_SINTR) /* interruptible (long) */ + *cp = k->ki_p->ki_slptime >= MAXSLP ? 'I' : 'S'; + else + *cp = 'D'; + break; + + case SRUN: + case SIDL: + *cp = 'R'; + break; + + case SWAIT: + *cp = 'W'; + break; + + case SLOCK: + *cp = 'L'; + break; + + case SZOMB: + *cp = 'Z'; + break; + + default: + *cp = '?'; + } + cp++; + if (k->ki_p->ki_nice < NZERO || k->ki_p->ki_pri.pri_class == PRI_REALTIME) + *cp++ = '<'; + else if (k->ki_p->ki_nice > NZERO || k->ki_p->ki_pri.pri_class == PRI_IDLE) + *cp++ = 'N'; + if (flag & P_TRACED) + *cp++ = 'X'; + if (flag & P_WEXIT && k->ki_p->ki_stat != SZOMB) + *cp++ = 'E'; + if (flag & P_PPWAIT) + *cp++ = 'V'; + if ((flag & P_SYSTEM) || k->ki_p->ki_lock > 0) + *cp++ = 'L'; + if ((k->ki_p->ki_cr_flags & KI_CRF_CAPABILITY_MODE) != 0) + *cp++ = 'C'; + if (k->ki_p->ki_kiflag & KI_SLEADER) + *cp++ = 's'; + if ((flag & P_CONTROLT) && k->ki_p->ki_pgid == k->ki_p->ki_tpgid) + *cp++ = '+'; + if (flag & P_JAILED) + *cp++ = 'J'; + *cp = '\0'; + return (buf); +} + +#define scalepri(x) ((x) - PUSER) + +char * +pri(KINFO *k, VARENT *ve __unused) +{ + char *str; + + asprintf(&str, "%d", scalepri(k->ki_p->ki_pri.pri_level)); + return (str); +} + +char * +upr(KINFO *k, VARENT *ve __unused) +{ + char *str; + + asprintf(&str, "%d", scalepri(k->ki_p->ki_pri.pri_user)); + return (str); +} +#undef scalepri + +char * +username(KINFO *k, VARENT *ve __unused) +{ + + return (strdup(user_from_uid(k->ki_p->ki_uid, 0))); +} + +char * +egroupname(KINFO *k, VARENT *ve __unused) +{ + + return (strdup(group_from_gid(k->ki_p->ki_groups[0], 0))); +} + +char * +rgroupname(KINFO *k, VARENT *ve __unused) +{ + + return (strdup(group_from_gid(k->ki_p->ki_rgid, 0))); +} + +char * +runame(KINFO *k, VARENT *ve __unused) +{ + + return (strdup(user_from_uid(k->ki_p->ki_ruid, 0))); +} + +char * +tdev(KINFO *k, VARENT *ve __unused) +{ + dev_t dev; + char *str; + + dev = k->ki_p->ki_tdev; + if (dev == NODEV) + str = strdup("-"); + else + asprintf(&str, "%#jx", (uintmax_t)dev); + + return (str); +} + +char * +tname(KINFO *k, VARENT *ve __unused) +{ + dev_t dev; + char *ttname, *str; + + dev = k->ki_p->ki_tdev; + if (dev == NODEV || (ttname = devname(dev, S_IFCHR)) == NULL) + str = strdup("- "); + else { + if (strncmp(ttname, "tty", 3) == 0 || + strncmp(ttname, "cua", 3) == 0) + ttname += 3; + if (strncmp(ttname, "pts/", 4) == 0) + ttname += 4; + asprintf(&str, "%s%c", ttname, + k->ki_p->ki_kiflag & KI_CTTY ? ' ' : '-'); + } + + return (str); +} + +char * +longtname(KINFO *k, VARENT *ve __unused) +{ + dev_t dev; + const char *ttname; + + dev = k->ki_p->ki_tdev; + if (dev == NODEV || (ttname = devname(dev, S_IFCHR)) == NULL) + ttname = "-"; + + return (strdup(ttname)); +} + +char * +started(KINFO *k, VARENT *ve __unused) +{ + time_t then; + struct tm *tp; + size_t buflen = 100; + char *buf; + + if (!k->ki_valid) + return (NULL); + + buf = malloc(buflen); + if (buf == NULL) + xo_errx(1, "malloc failed"); + + then = k->ki_p->ki_start.tv_sec; + tp = localtime(&then); + if (now - k->ki_p->ki_start.tv_sec < 24 * 3600) { + (void)strftime(buf, buflen, "%H:%M ", tp); + } else if (now - k->ki_p->ki_start.tv_sec < 7 * 86400) { + (void)strftime(buf, buflen, "%a%H ", tp); + } else + (void)strftime(buf, buflen, "%e%b%y", tp); + return (buf); +} + +char * +lstarted(KINFO *k, VARENT *ve __unused) +{ + time_t then; + char *buf; + size_t buflen = 100; + + if (!k->ki_valid) + return (NULL); + + buf = malloc(buflen); + if (buf == NULL) + xo_errx(1, "malloc failed"); + + then = k->ki_p->ki_start.tv_sec; + (void)strftime(buf, buflen, "%c", localtime(&then)); + return (buf); +} + +char * +lockname(KINFO *k, VARENT *ve __unused) +{ + char *str; + + if (k->ki_p->ki_kiflag & KI_LOCKBLOCK) { + if (k->ki_p->ki_lockname[0] != 0) + str = strdup(k->ki_p->ki_lockname); + else + str = strdup("???"); + } else + str = NULL; + + return (str); +} + +char * +wchan(KINFO *k, VARENT *ve __unused) +{ + char *str; + + if (k->ki_p->ki_wchan) { + if (k->ki_p->ki_wmesg[0] != 0) + str = strdup(k->ki_p->ki_wmesg); + else + asprintf(&str, "%lx", (long)k->ki_p->ki_wchan); + } else + str = NULL; + + return (str); +} + +char * +nwchan(KINFO *k, VARENT *ve __unused) +{ + char *str; + + if (k->ki_p->ki_wchan) + asprintf(&str, "%0lx", (long)k->ki_p->ki_wchan); + else + str = NULL; + + return (str); +} + +char * +mwchan(KINFO *k, VARENT *ve __unused) +{ + char *str; + + if (k->ki_p->ki_wchan) { + if (k->ki_p->ki_wmesg[0] != 0) + str = strdup(k->ki_p->ki_wmesg); + else + asprintf(&str, "%lx", (long)k->ki_p->ki_wchan); + } else if (k->ki_p->ki_kiflag & KI_LOCKBLOCK) { + if (k->ki_p->ki_lockname[0]) { + str = strdup(k->ki_p->ki_lockname); + } else + str = strdup("???"); + } else + str = NULL; + + return (str); +} + +char * +vsize(KINFO *k, VARENT *ve __unused) +{ + char *str; + + asprintf(&str, "%lu", (u_long)(k->ki_p->ki_size / 1024)); + return (str); +} + +static char * +printtime(KINFO *k, VARENT *ve __unused, long secs, long psecs) +/* psecs is "parts" of a second. first micro, then centi */ +{ + static char decimal_point; + char *str; + + if (decimal_point == '\0') + decimal_point = localeconv()->decimal_point[0]; + if (!k->ki_valid) { + secs = 0; + psecs = 0; + } else { + /* round and scale to 100's */ + psecs = (psecs + 5000) / 10000; + secs += psecs / 100; + psecs = psecs % 100; + } + asprintf(&str, "%ld:%02ld%c%02ld", + secs / 60, secs % 60, decimal_point, psecs); + return (str); +} + +char * +cputime(KINFO *k, VARENT *ve) +{ + long secs, psecs; + + /* + * This counts time spent handling interrupts. We could + * fix this, but it is not 100% trivial (and interrupt + * time fractions only work on the sparc anyway). XXX + */ + secs = k->ki_p->ki_runtime / 1000000; + psecs = k->ki_p->ki_runtime % 1000000; + if (sumrusage) { + secs += k->ki_p->ki_childtime.tv_sec; + psecs += k->ki_p->ki_childtime.tv_usec; + } + return (printtime(k, ve, secs, psecs)); +} + +char * +cpunum(KINFO *k, VARENT *ve __unused) +{ + char *cpu; + + if (k->ki_p->ki_stat == SRUN && k->ki_p->ki_oncpu != NOCPU) { + asprintf(&cpu, "%d", k->ki_p->ki_oncpu); + } else { + asprintf(&cpu, "%d", k->ki_p->ki_lastcpu); + } + return (cpu); +} + +char * +systime(KINFO *k, VARENT *ve) +{ + long secs, psecs; + + secs = k->ki_p->ki_rusage.ru_stime.tv_sec; + psecs = k->ki_p->ki_rusage.ru_stime.tv_usec; + if (sumrusage) { + secs += k->ki_p->ki_childstime.tv_sec; + psecs += k->ki_p->ki_childstime.tv_usec; + } + return (printtime(k, ve, secs, psecs)); +} + +char * +usertime(KINFO *k, VARENT *ve) +{ + long secs, psecs; + + secs = k->ki_p->ki_rusage.ru_utime.tv_sec; + psecs = k->ki_p->ki_rusage.ru_utime.tv_usec; + if (sumrusage) { + secs += k->ki_p->ki_childutime.tv_sec; + psecs += k->ki_p->ki_childutime.tv_usec; + } + return (printtime(k, ve, secs, psecs)); +} + +char * +elapsed(KINFO *k, VARENT *ve __unused) +{ + time_t val; + int days, hours, mins, secs; + char *str; + + if (!k->ki_valid) + return (NULL); + val = now - k->ki_p->ki_start.tv_sec; + days = val / (24 * 60 * 60); + val %= 24 * 60 * 60; + hours = val / (60 * 60); + val %= 60 * 60; + mins = val / 60; + secs = val % 60; + if (days != 0) + asprintf(&str, "%3d-%02d:%02d:%02d", days, hours, mins, secs); + else if (hours != 0) + asprintf(&str, "%02d:%02d:%02d", hours, mins, secs); + else + asprintf(&str, "%02d:%02d", mins, secs); + + return (str); +} + +char * +elapseds(KINFO *k, VARENT *ve __unused) +{ + time_t val; + char *str; + + if (!k->ki_valid) + return (NULL); + val = now - k->ki_p->ki_start.tv_sec; + asprintf(&str, "%jd", (intmax_t)val); + return (str); +} + +double +getpcpu(const KINFO *k) +{ + static int failure; + + if (!nlistread) + failure = donlist(); + if (failure) + return (0.0); + +#define fxtofl(fixpt) ((double)(fixpt) / fscale) + + /* XXX - I don't like this */ + if (k->ki_p->ki_swtime == 0) + return (0.0); + if (rawcpu) + return (100.0 * fxtofl(k->ki_p->ki_pctcpu)); + return (100.0 * fxtofl(k->ki_p->ki_pctcpu) / + (1.0 - exp(k->ki_p->ki_swtime * log(fxtofl(ccpu))))); +} + +char * +pcpu(KINFO *k, VARENT *ve __unused) +{ + char *str; + + asprintf(&str, "%.1f", getpcpu(k)); + return (str); +} + +static double +getpmem(KINFO *k) +{ + static int failure; + double fracmem; + + if (!nlistread) + failure = donlist(); + if (failure) + return (0.0); + + /* XXX want pmap ptpages, segtab, etc. (per architecture) */ + /* XXX don't have info about shared */ + fracmem = ((double)k->ki_p->ki_rssize) / mempages; + return (100.0 * fracmem); +} + +char * +pmem(KINFO *k, VARENT *ve __unused) +{ + char *str; + + asprintf(&str, "%.1f", getpmem(k)); + return (str); +} + +char * +pagein(KINFO *k, VARENT *ve __unused) +{ + char *str; + + asprintf(&str, "%ld", k->ki_valid ? k->ki_p->ki_rusage.ru_majflt : 0); + return (str); +} + +/* ARGSUSED */ +char * +maxrss(KINFO *k __unused, VARENT *ve __unused) +{ + + /* XXX not yet */ + return (NULL); +} + +char * +priorityr(KINFO *k, VARENT *ve __unused) +{ + struct priority *lpri; + char *str; + unsigned class, level; + + lpri = &k->ki_p->ki_pri; + class = lpri->pri_class; + level = lpri->pri_level; + switch (class) { + case RTP_PRIO_REALTIME: + /* alias for PRI_REALTIME */ + asprintf(&str, "real:%u", level - PRI_MIN_REALTIME); + break; + case RTP_PRIO_NORMAL: + /* alias for PRI_TIMESHARE */ + if (level >= PRI_MIN_TIMESHARE) + asprintf(&str, "normal:%u", level - PRI_MIN_TIMESHARE); + else + asprintf(&str, "kernel:%u", level - PRI_MIN_KERN); + break; + case RTP_PRIO_IDLE: + /* alias for PRI_IDLE */ + asprintf(&str, "idle:%u", level - PRI_MIN_IDLE); + break; + case RTP_PRIO_ITHD: + /* alias for PRI_ITHD */ + asprintf(&str, "intr:%u", level - PRI_MIN_ITHD); + break; + default: + asprintf(&str, "%u:%u", class, level); + break; + } + return (str); +} + +/* + * Generic output routines. Print fields from various prototype + * structures. + */ +static char * +printval(void *bp, const VAR *v) +{ + static char ofmt[32] = "%"; + const char *fcp; + char *cp, *str; + + cp = ofmt + 1; + fcp = v->fmt; + while ((*cp++ = *fcp++)); + +#define CHKINF127(n) (((n) > 127) && (v->flag & INF127) ? 127 : (n)) + + switch (v->type) { + case UNSPEC: + xo_errx(1, "cannot print value of unspecified type " + "(internal error)"); + break; + case CHAR: + (void)asprintf(&str, ofmt, *(char *)bp); + break; + case UCHAR: + (void)asprintf(&str, ofmt, *(u_char *)bp); + break; + case SHORT: + (void)asprintf(&str, ofmt, *(short *)bp); + break; + case USHORT: + (void)asprintf(&str, ofmt, *(u_short *)bp); + break; + case INT: + (void)asprintf(&str, ofmt, *(int *)bp); + break; + case UINT: + (void)asprintf(&str, ofmt, CHKINF127(*(u_int *)bp)); + break; + case LONG: + (void)asprintf(&str, ofmt, *(long *)bp); + break; + case ULONG: + (void)asprintf(&str, ofmt, *(u_long *)bp); + break; + case KPTR: + (void)asprintf(&str, ofmt, *(u_long *)bp); + break; + case PGTOK: + (void)asprintf(&str, ofmt, ps_pgtok(*(u_long *)bp)); + break; + default: + xo_errx(1, "unknown type (internal error)"); + break; + } + + return (str); +} + +char * +kvar(KINFO *k, VARENT *ve) +{ + const VAR *v; + + v = ve->var; + return (printval((char *)((char *)k->ki_p + v->off), v)); +} + +char * +rvar(KINFO *k, VARENT *ve) +{ + const VAR *v; + + v = ve->var; + if (!k->ki_valid) + return (NULL); + return (printval((char *)((char *)(&k->ki_p->ki_rusage) + v->off), v)); +} + +char * +emulname(KINFO *k, VARENT *ve __unused) +{ + + return (strdup(k->ki_p->ki_emul)); +} + +char * +label(KINFO *k, VARENT *ve __unused) +{ + char *string; + mac_t proclabel; + int error; + + string = NULL; + if (mac_prepare_process_label(&proclabel) == -1) { + xo_warn("mac_prepare_process_label"); + goto out; + } + error = mac_get_pid(k->ki_p->ki_pid, proclabel); + if (error == 0) { + if (mac_to_text(proclabel, &string) == -1) + string = NULL; + } + mac_free(proclabel); +out: + return (string); +} + +char * +loginclass(KINFO *k, VARENT *ve __unused) +{ + + /* + * Don't display login class for system processes; + * login classes are used for resource limits, + * and limits don't apply to system processes. + */ + if (k->ki_p->ki_flag & P_SYSTEM) { + return (strdup("-")); + } + return (strdup(k->ki_p->ki_loginclass)); +} + +char * +jailname(KINFO *k, VARENT *ve __unused) +{ + char *name; + + if (k->ki_p->ki_jid == 0) + return (strdup("-")); + name = jail_getname(k->ki_p->ki_jid); + if (name == NULL) + return (strdup("-")); + return (name); +} |
