1961 lines
80 KiB
C
1961 lines
80 KiB
C
/*
|
|
* Copyright 1999-2004 by Albert Cahalan; all rights reserved.
|
|
*
|
|
* This file may be used subject to the terms and conditions of the
|
|
* GNU Library General Public License Version 2, or any later version
|
|
* at your option, as published by the Free Software Foundation.
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Library General Public License for more details.
|
|
*/
|
|
|
|
/*
|
|
* This file is really gross, and I know it. I looked into several
|
|
* alternate ways to deal with the mess, and they were all ugly.
|
|
*
|
|
* FreeBSD has a fancy hack using offsets into a struct -- that
|
|
* saves code but it is _really_ gross. See the PO macro below.
|
|
*
|
|
* We could have a second column width for wide output format.
|
|
* For example, Digital prints the real-time signals.
|
|
*/
|
|
|
|
|
|
/*
|
|
* Data table idea:
|
|
*
|
|
* table 1 maps aix to specifier
|
|
* table 2 maps shortsort to specifier
|
|
* table 3 maps macro to specifiers
|
|
* table 4 maps specifier to title,datatype,offset,vendor,helptext
|
|
* table 5 maps datatype to justification,width,widewidth,sorting,printing
|
|
*
|
|
* Here, "datatype" could be user,uid,u16,pages,deltaT,signals,tty,longtty...
|
|
* It must be enough to determine printing and sorting.
|
|
*
|
|
* After the tables, increase width as needed to fit the header.
|
|
*
|
|
* Table 5 could go in a file with the output functions.
|
|
*/
|
|
|
|
#include <ctype.h>
|
|
#include <fcntl.h>
|
|
#include <grp.h>
|
|
#include <limits.h>
|
|
#include <pwd.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/resource.h>
|
|
#include <sys/types.h>
|
|
#include <sys/mman.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
#include <dlfcn.h>
|
|
|
|
#include "../proc/readproc.h"
|
|
#include "../proc/sysinfo.h"
|
|
#include "../proc/wchan.h"
|
|
#include "../proc/procps.h"
|
|
#include "../proc/devname.h"
|
|
#include "../proc/escape.h"
|
|
#include "common.h"
|
|
|
|
/* TODO:
|
|
* Stop assuming system time is local time.
|
|
*/
|
|
|
|
#define COLWID 240 /* satisfy snprintf, which is faster than sprintf */
|
|
|
|
static unsigned max_rightward = 0x12345678; /* space for RIGHT stuff */
|
|
static unsigned max_leftward = 0x12345678; /* space for LEFT stuff */
|
|
|
|
|
|
|
|
static int wide_signals; /* true if we have room */
|
|
|
|
static unsigned long seconds_since_1970;
|
|
static unsigned long time_of_boot;
|
|
static unsigned long page_shift;
|
|
|
|
|
|
/*************************************************************************/
|
|
/************ Lots of sort functions, starting with the NOP **************/
|
|
|
|
static int sr_nop(const proc_t* a, const proc_t* b){
|
|
(void)a;(void)b; /* shut up gcc */
|
|
return 0;
|
|
}
|
|
|
|
#define CMP_STR(NAME) \
|
|
static int sr_ ## NAME(const proc_t* P, const proc_t* Q) { \
|
|
return strcmp(P->NAME, Q->NAME); \
|
|
}
|
|
|
|
#define CMP_INT(NAME) \
|
|
static int sr_ ## NAME (const proc_t* P, const proc_t* Q) { \
|
|
if (P->NAME < Q->NAME) return -1; \
|
|
if (P->NAME > Q->NAME) return 1; \
|
|
return 0; \
|
|
}
|
|
|
|
/* fast version, for values which either:
|
|
* a. differ by no more than 0x7fffffff
|
|
* b. only need to be grouped same w/ same
|
|
*/
|
|
#define CMP_SMALL(NAME) \
|
|
static int sr_ ## NAME (const proc_t* P, const proc_t* Q) { \
|
|
return (int)(P->NAME) - (int)(Q->NAME); \
|
|
}
|
|
|
|
CMP_INT(rtprio)
|
|
CMP_SMALL(sched)
|
|
CMP_INT(cutime)
|
|
CMP_INT(cstime)
|
|
CMP_SMALL(priority) /* nice */
|
|
CMP_SMALL(nlwp)
|
|
CMP_SMALL(nice) /* priority */
|
|
CMP_INT(rss) /* resident set size from stat file */ /* vm_rss, resident */
|
|
CMP_INT(alarm)
|
|
CMP_INT(size) /* total pages */ /* vm_size, vsize */
|
|
CMP_INT(resident) /* resident pages */ /* vm_rss, rss */
|
|
CMP_INT(share) /* shared pages */
|
|
CMP_INT(trs) /* executable pages */
|
|
CMP_INT(lrs) /* obsolete "library" pages above 0x60000000 */
|
|
CMP_INT(drs) /* other pages (assumed data?) */
|
|
CMP_INT(dt) /* dirty pages */
|
|
|
|
CMP_INT(vm_size) /* kB VM */ /* size, vsize */
|
|
CMP_INT(vm_lock) /* kB locked */
|
|
CMP_INT(vm_rss) /* kB rss */ /* rss, resident */
|
|
CMP_INT(vm_data) /* kB "data" == data-stack */
|
|
CMP_INT(vm_stack) /* kB stack */
|
|
CMP_INT(vm_exe) /* kB "exec" == exec-lib */
|
|
CMP_INT(vm_lib) /* kB "libraries" */
|
|
CMP_INT(vsize) /* pages VM */ /* size, vm_size */
|
|
CMP_INT(rss_rlim)
|
|
CMP_SMALL(flags)
|
|
CMP_INT(min_flt)
|
|
CMP_INT(maj_flt)
|
|
CMP_INT(cmin_flt)
|
|
CMP_INT(cmaj_flt)
|
|
CMP_INT(utime)
|
|
CMP_INT(stime) /* Old: sort by systime. New: show start time. Uh oh. */
|
|
CMP_INT(start_code)
|
|
CMP_INT(end_code)
|
|
CMP_INT(start_stack)
|
|
CMP_INT(kstk_esp)
|
|
CMP_INT(kstk_eip)
|
|
CMP_INT(start_time)
|
|
CMP_INT(wchan)
|
|
|
|
/* CMP_STR(*environ) */
|
|
/* CMP_STR(*cmdline) */
|
|
|
|
CMP_STR(ruser)
|
|
CMP_STR(euser)
|
|
CMP_STR(suser)
|
|
CMP_STR(fuser)
|
|
CMP_STR(rgroup)
|
|
CMP_STR(egroup)
|
|
CMP_STR(sgroup)
|
|
CMP_STR(fgroup)
|
|
CMP_STR(cmd)
|
|
/* CMP_STR(ttyc) */ /* FIXME -- use strncmp with 8 max */
|
|
|
|
CMP_INT(ruid)
|
|
CMP_INT(rgid)
|
|
CMP_INT(euid)
|
|
CMP_INT(egid)
|
|
CMP_INT(suid)
|
|
CMP_INT(sgid)
|
|
CMP_INT(fuid)
|
|
CMP_INT(fgid)
|
|
CMP_SMALL(tid)
|
|
CMP_SMALL(tgid)
|
|
CMP_SMALL(ppid)
|
|
CMP_SMALL(pgrp)
|
|
CMP_SMALL(session)
|
|
CMP_INT(tty)
|
|
CMP_SMALL(tpgid)
|
|
|
|
CMP_SMALL(pcpu)
|
|
|
|
CMP_SMALL(state)
|
|
|
|
/* approximation to: kB of address space that could end up in swap */
|
|
static int sr_swapable(const proc_t* P, const proc_t* Q) {
|
|
unsigned long p_swapable = P->vm_data + P->vm_stack;
|
|
unsigned long q_swapable = Q->vm_data + Q->vm_stack;
|
|
if (p_swapable < q_swapable) return -1;
|
|
if (p_swapable > q_swapable) return 1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/***************************************************************************/
|
|
/************ Lots of format functions, starting with the NOP **************/
|
|
|
|
// so popular it can't be "static"
|
|
int pr_nop(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
(void)pp;
|
|
return snprintf(outbuf, COLWID, "%c", '-');
|
|
}
|
|
|
|
|
|
/********* Unix 98 ************/
|
|
|
|
/***
|
|
|
|
Only comm and args are allowed to contain blank characters; all others are
|
|
not. Any implementation-dependent variables will be specified in the system
|
|
documentation along with the default header and indicating if the field
|
|
may contain blank characters.
|
|
|
|
Some headers do not have a standardized specifier!
|
|
|
|
%CPU pcpu The % of cpu time used recently, with unspecified "recently".
|
|
ADDR The address of the process.
|
|
C Processor utilisation for scheduling.
|
|
CMD The command name, or everything with -f.
|
|
COMMAND args Command + args. May chop as desired. May use either version.
|
|
COMMAND comm argv[0]
|
|
ELAPSED etime Elapsed time since the process was started. [[dd-]hh:]mm:ss
|
|
F Flags (octal and additive)
|
|
GROUP group Effective group ID, prefer text over decimal.
|
|
NI nice Decimal system scheduling priority, see nice(1).
|
|
PGID pgid The decimal value of the process group ID.
|
|
PID pid Decimal PID.
|
|
PPID ppid Decimal PID.
|
|
PRI Priority. Higher numbers mean lower priority.
|
|
RGROUP rgroup Real group ID, prefer text over decimal.
|
|
RUSER ruser Real user ID, prefer text over decimal.
|
|
S The state of the process.
|
|
STIME Starting time of the process.
|
|
SZ The size in blocks of the core image of the process.
|
|
TIME time Cumulative CPU time. [dd-]hh:mm:ss
|
|
TT tty Name of tty in format used by who(1).
|
|
TTY The controlling terminal for the process.
|
|
UID UID, or name when -f
|
|
USER user Effective user ID, prefer text over decimal.
|
|
VSZ vsz Virtual memory size in decimal kB.
|
|
WCHAN Where waiting/sleeping or blank if running.
|
|
|
|
The nice value is used to compute the priority.
|
|
|
|
For some undefined ones, Digital does:
|
|
|
|
F flag Process flags -- but in hex!
|
|
PRI pri Process priority
|
|
S state Symbolic process status
|
|
TTY tt,tty,tname,longtname -- all do "ttyp1", "console", "??"
|
|
UID uid Process user ID (effective UID)
|
|
WCHAN wchan Address of event on which a
|
|
|
|
For some undefined ones, Sun does:
|
|
|
|
ADDR addr memory address of the process
|
|
C c Processor utilization for scheduling (obsolete).
|
|
CMD
|
|
F f
|
|
S s state: OSRZT
|
|
STIME start time, printed w/o blanks. If 24h old, months & days
|
|
SZ size (in pages) of the swappable process's image in main memory
|
|
TTY
|
|
UID uid
|
|
WCHAN wchan
|
|
|
|
For some undefined ones, SCO does:
|
|
ADDR addr Virtual address of the process' entry in the process table.
|
|
SZ swappable size in kB of the virtual data and stack
|
|
STIME stime hms or md time format
|
|
***/
|
|
|
|
/* Source & destination are known. Return bytes or screen characters? */
|
|
static int forest_helper(char *restrict const outbuf){
|
|
char *p = forest_prefix;
|
|
char *q = outbuf;
|
|
int rightward=max_rightward;
|
|
if(!*p) return 0;
|
|
/* Arrrgh! somebody defined unix as 1 */
|
|
if(forest_type == 'u') goto unixy;
|
|
while(*p){
|
|
switch(*p){
|
|
case ' ': strcpy(q, " "); break;
|
|
case 'L': strcpy(q, " \\_ "); break;
|
|
case '+': strcpy(q, " \\_ "); break;
|
|
case '|': strcpy(q, " | "); break;
|
|
case '\0': return q-outbuf; /* redundant & not used */
|
|
}
|
|
if (rightward-4 < 0) {
|
|
*(q+rightward)='\0';
|
|
return max_rightward;
|
|
}
|
|
q += 4;
|
|
rightward -= 4;
|
|
p++;
|
|
}
|
|
return q-outbuf; /* gcc likes this here */
|
|
unixy:
|
|
while(*p){
|
|
switch(*p){
|
|
case ' ': strcpy(q, " "); break;
|
|
case 'L': strcpy(q, " "); break;
|
|
case '+': strcpy(q, " "); break;
|
|
case '|': strcpy(q, " "); break;
|
|
case '\0': return q-outbuf; /* redundant & not used */
|
|
}
|
|
if (rightward-2 < 0) {
|
|
*(q+rightward)='\0';
|
|
return max_rightward;
|
|
}
|
|
q += 2;
|
|
rightward -= 2;
|
|
p++;
|
|
}
|
|
return q-outbuf; /* gcc likes this here */
|
|
}
|
|
|
|
|
|
/* XPG4-UNIX, according to Digital:
|
|
The "args" and "command" specifiers show what was passed to the command.
|
|
Modifications to the arguments are not shown.
|
|
*/
|
|
|
|
/*
|
|
* pp->cmd short accounting name (comm & ucomm)
|
|
* pp->cmdline long name with args (args & command)
|
|
* pp->environ environment
|
|
*/
|
|
|
|
// FIXME: some of these may hit the guard page in forest mode
|
|
|
|
/* "command" is the same thing: long unless c */
|
|
static int pr_args(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
char *endp = outbuf;
|
|
unsigned flags;
|
|
int rightward=max_rightward;
|
|
|
|
if(forest_prefix){
|
|
int fh = forest_helper(outbuf);
|
|
endp += fh;
|
|
rightward -= fh;
|
|
}
|
|
if(bsd_c_option) flags = ESC_DEFUNCT;
|
|
else flags = ESC_DEFUNCT | ESC_BRACKETS | ESC_ARGS;
|
|
endp += escape_command(endp, pp, OUTBUF_SIZE, &rightward, flags);
|
|
|
|
if(bsd_e_option && rightward>1){
|
|
const char **env = (const char**)pp->environ;
|
|
if(env && *env){
|
|
*endp++ = ' ';
|
|
rightward--;
|
|
endp += escape_strlist(endp, env, OUTBUF_SIZE, &rightward);
|
|
}
|
|
}
|
|
//return endp - outbuf;
|
|
return max_rightward-rightward;
|
|
}
|
|
|
|
/* "ucomm" is the same thing: short unless -f */
|
|
static int pr_comm(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
char *endp = outbuf;
|
|
unsigned flags;
|
|
int rightward=max_rightward;
|
|
|
|
if(forest_prefix){
|
|
int fh = forest_helper(outbuf);
|
|
endp += fh;
|
|
rightward -= fh;
|
|
}
|
|
if(unix_f_option) flags = ESC_DEFUNCT | ESC_BRACKETS | ESC_ARGS;
|
|
else flags = ESC_DEFUNCT;
|
|
endp += escape_command(endp, pp, OUTBUF_SIZE, &rightward, flags);
|
|
|
|
if(bsd_e_option && rightward>1){
|
|
const char **env = (const char**)pp->environ;
|
|
if(env && *env){
|
|
*endp++ = ' ';
|
|
rightward--;
|
|
endp += escape_strlist(endp, env, OUTBUF_SIZE, &rightward);
|
|
}
|
|
}
|
|
//return endp - outbuf;
|
|
return max_rightward-rightward;
|
|
}
|
|
/* Non-standard, from SunOS 5 */
|
|
static int pr_fname(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
char *endp = outbuf;
|
|
int rightward = max_rightward;
|
|
|
|
if(forest_prefix){
|
|
int fh = forest_helper(outbuf);
|
|
endp += fh;
|
|
rightward -= fh;
|
|
}
|
|
if (rightward>8) /* 8=default, but forest maybe feeds more */
|
|
rightward = 8;
|
|
|
|
endp += escape_str(endp, pp->cmd, OUTBUF_SIZE, &rightward);
|
|
//return endp - outbuf;
|
|
return max_rightward-rightward;
|
|
}
|
|
|
|
/* elapsed wall clock time, [[dd-]hh:]mm:ss format (not same as "time") */
|
|
static int pr_etime(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
unsigned long t;
|
|
unsigned dd,hh,mm,ss;
|
|
char *cp = outbuf;
|
|
t = seconds_since_boot - (unsigned long)(pp->start_time / Hertz);
|
|
ss = t%60;
|
|
t /= 60;
|
|
mm = t%60;
|
|
t /= 60;
|
|
hh = t%24;
|
|
t /= 24;
|
|
dd = t;
|
|
cp +=( dd ? snprintf(cp, COLWID, "%u-", dd) : 0 );
|
|
cp +=( (dd || hh) ? snprintf(cp, COLWID, "%02u:", hh) : 0 );
|
|
cp += snprintf(cp, COLWID, "%02u:%02u", mm, ss) ;
|
|
return (int)(cp-outbuf);
|
|
}
|
|
|
|
/* "Processor utilisation for scheduling." --- we use %cpu w/o fraction */
|
|
static int pr_c(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
unsigned long long total_time; /* jiffies used by this process */
|
|
unsigned pcpu = 0; /* scaled %cpu, 99 means 99% */
|
|
unsigned long long seconds; /* seconds of process life */
|
|
total_time = pp->utime + pp->stime;
|
|
if(include_dead_children) total_time += (pp->cutime + pp->cstime);
|
|
seconds = seconds_since_boot - pp->start_time / Hertz;
|
|
if(seconds) pcpu = (total_time * 100ULL / Hertz) / seconds;
|
|
if (pcpu > 99U) pcpu = 99U;
|
|
return snprintf(outbuf, COLWID, "%2u", pcpu);
|
|
}
|
|
/* normal %CPU in ##.# format. */
|
|
static int pr_pcpu(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
unsigned long long total_time; /* jiffies used by this process */
|
|
unsigned pcpu = 0; /* scaled %cpu, 999 means 99.9% */
|
|
unsigned long long seconds; /* seconds of process life */
|
|
total_time = pp->utime + pp->stime;
|
|
if(include_dead_children) total_time += (pp->cutime + pp->cstime);
|
|
seconds = seconds_since_boot - pp->start_time / Hertz;
|
|
if(seconds) pcpu = (total_time * 1000ULL / Hertz) / seconds;
|
|
if (pcpu > 999U)
|
|
return snprintf(outbuf, COLWID, "%u", pcpu/10U);
|
|
return snprintf(outbuf, COLWID, "%u.%u", pcpu/10U, pcpu%10U);
|
|
}
|
|
/* this is a "per-mill" format, like %cpu with no decimal point */
|
|
static int pr_cp(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
unsigned long long total_time; /* jiffies used by this process */
|
|
unsigned pcpu = 0; /* scaled %cpu, 999 means 99.9% */
|
|
unsigned long long seconds; /* seconds of process life */
|
|
total_time = pp->utime + pp->stime;
|
|
if(include_dead_children) total_time += (pp->cutime + pp->cstime);
|
|
seconds = seconds_since_boot - pp->start_time / Hertz ;
|
|
if(seconds) pcpu = (total_time * 1000ULL / Hertz) / seconds;
|
|
if (pcpu > 999U) pcpu = 999U;
|
|
return snprintf(outbuf, COLWID, "%3u", pcpu);
|
|
}
|
|
|
|
static int pr_pgid(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
return snprintf(outbuf, COLWID, "%u", pp->pgrp);
|
|
}
|
|
static int pr_pid(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
return snprintf(outbuf, COLWID, "%u", pp->tgid);
|
|
}
|
|
static int pr_ppid(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
return snprintf(outbuf, COLWID, "%u", pp->ppid);
|
|
}
|
|
|
|
|
|
/* cumulative CPU time, [dd-]hh:mm:ss format (not same as "etime") */
|
|
static int pr_time(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
unsigned long t;
|
|
unsigned dd,hh,mm,ss;
|
|
int c;
|
|
t = (pp->utime + pp->stime) / Hertz;
|
|
ss = t%60;
|
|
t /= 60;
|
|
mm = t%60;
|
|
t /= 60;
|
|
hh = t%24;
|
|
t /= 24;
|
|
dd = t;
|
|
c =( dd ? snprintf(outbuf, COLWID, "%u-", dd) : 0 );
|
|
c +=( snprintf(outbuf+c, COLWID, "%02u:%02u:%02u", hh, mm, ss) );
|
|
return c;
|
|
}
|
|
|
|
/* HP-UX puts this (I forget, vsz or vsize?) in kB and uses "sz" for pages.
|
|
* Unix98 requires "vsz" to be kB.
|
|
* Tru64 does both vsize and vsz like "1.23M"
|
|
*
|
|
* Our pp->vm_size is kB and our pp->vsize is pages.
|
|
*
|
|
* TODO: add flag for "1.23M" behavior, on this and other columns.
|
|
*/
|
|
static int pr_vsz(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
return snprintf(outbuf, COLWID, "%lu", pp->vm_size);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// "PRI" is created by "opri", or by "pri" when -c is used.
|
|
//
|
|
// Unix98 only specifies that a high "PRI" is low priority.
|
|
// Sun and SCO add the -c behavior. Sun defines "pri" and "opri".
|
|
// Linux may use "priority" for historical purposes.
|
|
//
|
|
// According to the kernel's fs/proc/array.c and kernel/sched.c source,
|
|
// the kernel reports it in /proc via this:
|
|
// p->prio - MAX_RT_PRIO
|
|
// such that "RT tasks are offset by -200. Normal tasks are centered
|
|
// around 0, value goes from -16 to +15" but who knows if that is
|
|
// before or after the conversion...
|
|
//
|
|
// <linux/sched.h> says:
|
|
// MAX_RT_PRIO is currently 100. (so we see 0 in /proc)
|
|
// RT tasks have a p->prio of 0 to 99. (so we see -100 to -1)
|
|
// non-RT tasks are from 100 to 139. (so we see 0 to 39)
|
|
// Lower values have higher priority, as in the UNIX standard.
|
|
//
|
|
// In any case, pp->priority+100 should get us back to what the kernel
|
|
// has for p->prio.
|
|
//
|
|
// Test results with the "yes" program on a 2.6.x kernel:
|
|
//
|
|
// # ps -C19,_20 -o pri,opri,intpri,priority,ni,pcpu,pid,comm
|
|
// PRI PRI PRI PRI NI %CPU PID COMMAND
|
|
// 0 99 99 39 19 10.6 8686 19
|
|
// 34 65 65 5 -20 94.7 8687 _20
|
|
//
|
|
// Grrr. So the UNIX standard "PRI" must NOT be from "pri".
|
|
// Either of the others will do. We use "opri" for this.
|
|
// (and use "pri" when the "-c" option is used)
|
|
// Probably we should have Linux-specific "pri_for_l" and "pri_for_lc"
|
|
//
|
|
// sched_get_priority_min.2 says the Linux static priority is
|
|
// 1..99 for RT and 0 for other... maybe 100 is kernel-only?
|
|
//
|
|
// A nice range would be -99..0 for RT and 1..40 for normal,
|
|
// which is pp->priority+1. (3-digit max, positive is normal,
|
|
// negative or 0 is RT, and meets the standard for PRI)
|
|
//
|
|
|
|
// legal as UNIX "PRI"
|
|
// "priority" (was -20..20, now -100..39)
|
|
static int pr_priority(char *restrict const outbuf, const proc_t *restrict const pp){ /* -20..20 */
|
|
return snprintf(outbuf, COLWID, "%ld", pp->priority);
|
|
}
|
|
|
|
// legal as UNIX "PRI"
|
|
// "intpri" and "opri" (was 39..79, now -40..99)
|
|
static int pr_opri(char *restrict const outbuf, const proc_t *restrict const pp){ /* 39..79 */
|
|
return snprintf(outbuf, COLWID, "%ld", 60 + pp->priority);
|
|
}
|
|
|
|
// legal as UNIX "PRI"
|
|
// "pri_foo" -- match up w/ nice values of sleeping processes (-120..19)
|
|
static int pr_pri_foo(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
return snprintf(outbuf, COLWID, "%ld", pp->priority - 20);
|
|
}
|
|
|
|
// legal as UNIX "PRI"
|
|
// "pri_bar" -- makes RT pri show as negative (-99..40)
|
|
static int pr_pri_bar(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
return snprintf(outbuf, COLWID, "%ld", pp->priority + 1);
|
|
}
|
|
|
|
// legal as UNIX "PRI"
|
|
// "pri_baz" -- the kernel's ->prio value, as of Linux 2.6.8 (1..140)
|
|
static int pr_pri_baz(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
return snprintf(outbuf, COLWID, "%ld", pp->priority + 100);
|
|
}
|
|
|
|
|
|
// not legal as UNIX "PRI"
|
|
// "pri" (was 20..60, now 0..139)
|
|
static int pr_pri(char *restrict const outbuf, const proc_t *restrict const pp){ /* 20..60 */
|
|
return snprintf(outbuf, COLWID, "%ld", 39 - pp->priority);
|
|
}
|
|
|
|
// not legal as UNIX "PRI"
|
|
// "pri_api" -- match up w/ RT API (-40..99)
|
|
static int pr_pri_api(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
return snprintf(outbuf, COLWID, "%ld", -1 - pp->priority);
|
|
}
|
|
|
|
static int pr_nice(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
if(pp->sched!=0 && pp->sched!=-1) return snprintf(outbuf, COLWID, "-");
|
|
return snprintf(outbuf, COLWID, "%ld", pp->nice);
|
|
}
|
|
|
|
// HP-UX "cls": RT RR RR2 ???? HPUX FIFO KERN
|
|
// Solaris "class": SYS TS FX IA RT FSS (FIFO is RR w/ Inf quant)
|
|
// FIFO+RR share RT; FIFO has Inf quant
|
|
// IA=interactive; FX=fixed; TS=timeshare; SYS=system
|
|
// FSS=fairshare; INTS=interrupts
|
|
// Tru64 "policy": FF RR TS
|
|
// IRIX "class": RT TS B BC WL GN
|
|
// RT=real-time; TS=time-share; B=batch; BC=batch-critical
|
|
// WL=weightless; GN=gang-scheduled
|
|
// see miser(1) for this; PRI has some letter codes too
|
|
static int pr_class(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
switch(pp->sched){
|
|
case -1: return snprintf(outbuf, COLWID, "-"); // not reported
|
|
case 0: return snprintf(outbuf, COLWID, "TS"); // SCHED_OTHER
|
|
case 1: return snprintf(outbuf, COLWID, "FF"); // SCHED_FIFO
|
|
case 2: return snprintf(outbuf, COLWID, "RR"); // SCHED_RR
|
|
case 3: return snprintf(outbuf, COLWID, "B"); // SCHED_BATCH? (will be "B")
|
|
case 4: return snprintf(outbuf, COLWID, "#4"); // SCHED_ISO? (Con Kolivas)
|
|
case 5: return snprintf(outbuf, COLWID, "#5"); //
|
|
case 8: return snprintf(outbuf, COLWID, "#8"); //
|
|
default: return snprintf(outbuf, COLWID, "?"); // unknown value
|
|
}
|
|
}
|
|
// Based on "type", FreeBSD would do:
|
|
// REALTIME "real:%u", prio
|
|
// NORMAL "normal"
|
|
// IDLE "idle:%u", prio
|
|
// default "%u:%u", type, prio
|
|
// We just print the priority, and have other keywords for type.
|
|
static int pr_rtprio(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
if(pp->sched==0 || pp->sched==-1) return snprintf(outbuf, COLWID, "-");
|
|
return snprintf(outbuf, COLWID, "%ld", pp->rtprio);
|
|
}
|
|
static int pr_sched(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
if(pp->sched==-1) return snprintf(outbuf, COLWID, "-");
|
|
return snprintf(outbuf, COLWID, "%ld", pp->sched);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static int pr_wchan(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
/*
|
|
* Unix98 says "blank if running" and also "no blanks"! :-(
|
|
* Unix98 also says to use '-' if something is meaningless.
|
|
* Digital uses both '*' and '-', with undocumented differences.
|
|
* (the '*' for -1 (rare) and the '-' for 0)
|
|
* Sun claims to use a blank AND use '-', in the same man page.
|
|
* Perhaps "blank" should mean '-'.
|
|
*
|
|
* AIX uses '-' for running processes, the location when there is
|
|
* only one thread waiting in the kernel, and '*' when there is
|
|
* more than one thread waiting in the kernel.
|
|
*
|
|
* The output should be truncated to maximal columns width -- overflow
|
|
* is not supported for the "wchan".
|
|
*/
|
|
const char *w;
|
|
size_t len;
|
|
if(!(pp->wchan & 0xffffff)) return memcpy(outbuf,"-",2),1;
|
|
if(wchan_is_number) return snprintf(outbuf, COLWID, "%x", (unsigned)(pp->wchan) & 0xffffffu);
|
|
w = lookup_wchan(pp->wchan, pp->XXXID);
|
|
len = strlen(w);
|
|
if(len>max_rightward) len=max_rightward;
|
|
memcpy(outbuf, w, len);
|
|
outbuf[len] = '\0';
|
|
return len;
|
|
}
|
|
|
|
static int pr_wname(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
/* SGI's IRIX always uses a number for "wchan", so "wname" is provided too.
|
|
*
|
|
* We use '-' for running processes, the location when there is
|
|
* only one thread waiting in the kernel, and '*' when there is
|
|
* more than one thread waiting in the kernel.
|
|
*
|
|
* The output should be truncated to maximal columns width -- overflow
|
|
* is not supported for the "wchan".
|
|
*/
|
|
const char *w;
|
|
size_t len;
|
|
if(!(pp->wchan & 0xffffff)) return memcpy(outbuf,"-",2),1;
|
|
w = lookup_wchan(pp->wchan, pp->XXXID);
|
|
len = strlen(w);
|
|
if(len>max_rightward) len=max_rightward;
|
|
memcpy(outbuf, w, len);
|
|
outbuf[len] = '\0';
|
|
return len;
|
|
}
|
|
|
|
static int pr_nwchan(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
if(!(pp->wchan & 0xffffff)) return memcpy(outbuf,"-",2),1;
|
|
return snprintf(outbuf, COLWID, "%x", (unsigned)(pp->wchan) & 0xffffffu);
|
|
}
|
|
|
|
/* Terrible trunctuation, like BSD crap uses: I999 J999 K999 */
|
|
/* FIXME: disambiguate /dev/tty69 and /dev/pts/69. */
|
|
static int pr_tty4(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
/* snprintf(outbuf, COLWID, "%02x:%02x", pp->tty>>8, pp->tty&0xff); */
|
|
return dev_to_tty(outbuf, 4, pp->tty, pp->XXXID, ABBREV_DEV|ABBREV_TTY|ABBREV_PTS);
|
|
}
|
|
|
|
/* Unix98: format is unspecified, but must match that used by who(1). */
|
|
static int pr_tty8(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
/* snprintf(outbuf, COLWID, "%02x:%02x", pp->tty>>8, pp->tty&0xff); */
|
|
return dev_to_tty(outbuf, COLWID, pp->tty, pp->XXXID, ABBREV_DEV);
|
|
}
|
|
|
|
#if 0
|
|
/* This BSD state display may contain spaces, which is illegal. */
|
|
static int pr_oldstate(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
return snprintf(outbuf, COLWID, "%s", status(pp));
|
|
}
|
|
#endif
|
|
|
|
// This state display is Unix98 compliant and has lots of info like BSD.
|
|
static int pr_stat(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
int end = 0;
|
|
outbuf[end++] = pp->state;
|
|
// if(pp->rss==0 && pp->state!='Z') outbuf[end++] = 'W'; // useless "swapped out"
|
|
if(pp->nice < 0) outbuf[end++] = '<';
|
|
if(pp->nice > 0) outbuf[end++] = 'N';
|
|
// In this order, NetBSD would add:
|
|
// traced 'X'
|
|
// systrace 'x'
|
|
// exiting 'E' (not printed for zombies)
|
|
// vforked 'V'
|
|
// system 'K' (and do not print 'L' too)
|
|
if(pp->vm_lock) outbuf[end++] = 'L';
|
|
if(pp->session == pp->tgid) outbuf[end++] = 's'; // session leader
|
|
if(pp->nlwp > 1) outbuf[end++] = 'l'; // multi-threaded
|
|
if(pp->pgrp == pp->tpgid) outbuf[end++] = '+'; // in foreground process group
|
|
outbuf[end] = '\0';
|
|
return end;
|
|
}
|
|
|
|
/* This minimal state display is Unix98 compliant, like SCO and SunOS 5 */
|
|
static int pr_s(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
outbuf[0] = pp->state;
|
|
outbuf[1] = '\0';
|
|
return 1;
|
|
}
|
|
|
|
static int pr_flag(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
/* Unix98 requires octal flags */
|
|
/* this user-hostile and volatile junk gets 1 character */
|
|
return snprintf(outbuf, COLWID, "%o", (unsigned)(pp->flags>>6U)&0x7U);
|
|
}
|
|
|
|
// plus these: euid,ruid,egroup,rgroup (elsewhere in this file)
|
|
|
|
/*********** non-standard ***********/
|
|
|
|
/*** BSD
|
|
sess session pointer
|
|
(SCO has:Process session leader ID as a decimal value. (SESSION))
|
|
jobc job control count
|
|
cpu short-term cpu usage factor (for scheduling)
|
|
sl sleep time (in seconds; 127 = infinity)
|
|
re core residency time (in seconds; 127 = infinity)
|
|
pagein pageins (same as majflt)
|
|
lim soft memory limit
|
|
tsiz text size (in Kbytes)
|
|
***/
|
|
|
|
static int pr_stackp(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
return snprintf(outbuf, COLWID, "%08x", (unsigned)(pp->start_stack));
|
|
}
|
|
|
|
static int pr_esp(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
return snprintf(outbuf, COLWID, "%08x", (unsigned)(pp->kstk_esp));
|
|
}
|
|
|
|
static int pr_eip(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
return snprintf(outbuf, COLWID, "%08x", (unsigned)(pp->kstk_eip));
|
|
}
|
|
|
|
/* This function helps print old-style time formats */
|
|
static int old_time_helper(char *dst, unsigned long long t, unsigned long long rel) {
|
|
if(!t) return snprintf(dst, COLWID, " -");
|
|
if(t == ~0ULL) return snprintf(dst, COLWID, " xx");
|
|
if((long long)(t-=rel) < 0) t=0ULL;
|
|
if(t>9999ULL) return snprintf(dst, COLWID, "%5Lu", t/100ULL);
|
|
else return snprintf(dst, COLWID, "%2u.%02u", (unsigned)t/100U, (unsigned)t%100U);
|
|
}
|
|
|
|
static int pr_bsdtime(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
unsigned long long t;
|
|
unsigned u;
|
|
t = pp->utime + pp->stime;
|
|
if(include_dead_children) t += (pp->cutime + pp->cstime);
|
|
u = t / Hertz;
|
|
return snprintf(outbuf, COLWID, "%3u:%02u", u/60U, u%60U);
|
|
}
|
|
|
|
static int pr_bsdstart(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
time_t start;
|
|
time_t seconds_ago;
|
|
start = time_of_boot + pp->start_time / Hertz;
|
|
seconds_ago = seconds_since_1970 - start;
|
|
if(seconds_ago < 0) seconds_ago=0;
|
|
if(seconds_ago > 3600*24) strcpy(outbuf, ctime(&start)+4);
|
|
else strcpy(outbuf, ctime(&start)+10);
|
|
outbuf[6] = '\0';
|
|
return 6;
|
|
}
|
|
|
|
static int pr_alarm(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
return old_time_helper(outbuf, pp->alarm, 0ULL);
|
|
}
|
|
|
|
/* HP-UX puts this in pages and uses "vsz" for kB */
|
|
static int pr_sz(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
return snprintf(outbuf, COLWID, "%lu", (pp->vm_size)/(page_size/1024));
|
|
}
|
|
|
|
|
|
/*
|
|
* FIXME: trs,drs,tsiz,dsiz,m_trs,m_drs,vm_exe,vm_data,trss
|
|
* I suspect some/all of those are broken. They seem to have been
|
|
* inherited by Linux and AIX from early BSD systems. FreeBSD only
|
|
* retains tsiz. The prefixed versions come from Debian.
|
|
* Sun and Digital have none of this crap. The code here comes
|
|
* from an old Linux ps, and might not be correct for ELF executables.
|
|
*
|
|
* AIX TRS size of resident-set (real memory) of text
|
|
* AIX TSIZ size of text (shared-program) image
|
|
* FreeBSD tsiz text size (in Kbytes)
|
|
* 4.3BSD NET/2 trss text resident set size (in Kbytes)
|
|
* 4.3BSD NET/2 tsiz text size (in Kbytes)
|
|
*/
|
|
|
|
/* kB data size. See drs, tsiz & trs. */
|
|
static int pr_dsiz(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
long dsiz = 0;
|
|
if(pp->vsize) dsiz += (pp->vsize - pp->end_code + pp->start_code) >> 10;
|
|
return snprintf(outbuf, COLWID, "%ld", dsiz);
|
|
}
|
|
|
|
/* kB text (code) size. See trs, dsiz & drs. */
|
|
static int pr_tsiz(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
long tsiz = 0;
|
|
if(pp->vsize) tsiz += (pp->end_code - pp->start_code) >> 10;
|
|
return snprintf(outbuf, COLWID, "%ld", tsiz);
|
|
}
|
|
|
|
/* kB _resident_ data size. See dsiz, tsiz & trs. */
|
|
static int pr_drs(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
long drs = 0;
|
|
if(pp->vsize) drs += (pp->vsize - pp->end_code + pp->start_code) >> 10;
|
|
return snprintf(outbuf, COLWID, "%ld", drs);
|
|
}
|
|
|
|
/* kB text _resident_ (code) size. See tsiz, dsiz & drs. */
|
|
static int pr_trs(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
long trs = 0;
|
|
if(pp->vsize) trs += (pp->end_code - pp->start_code) >> 10;
|
|
return snprintf(outbuf, COLWID, "%ld", trs);
|
|
}
|
|
|
|
/* approximation to: kB of address space that could end up in swap */
|
|
static int pr_swapable(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
return snprintf(outbuf, COLWID, "%ld", pp->vm_data + pp->vm_stack);
|
|
}
|
|
|
|
/* nasty old Debian thing */
|
|
static int pr_size(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
return snprintf(outbuf, COLWID, "%ld", pp->size);
|
|
}
|
|
|
|
|
|
static int pr_minflt(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
long flt = pp->min_flt;
|
|
if(include_dead_children) flt += pp->cmin_flt;
|
|
return snprintf(outbuf, COLWID, "%ld", flt);
|
|
}
|
|
|
|
static int pr_majflt(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
long flt = pp->maj_flt;
|
|
if(include_dead_children) flt += pp->cmaj_flt;
|
|
return snprintf(outbuf, COLWID, "%ld", flt);
|
|
}
|
|
|
|
static int pr_lim(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
if(pp->rss_rlim == RLIM_INFINITY){
|
|
outbuf[0] = 'x';
|
|
outbuf[1] = 'x';
|
|
outbuf[2] = '\0';
|
|
return 2;
|
|
}
|
|
return snprintf(outbuf, COLWID, "%5ld", pp->rss_rlim >> 10);
|
|
}
|
|
|
|
/* should print leading tilde ('~') if process is bound to the CPU */
|
|
static int pr_psr(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
return snprintf(outbuf, COLWID, "%d", pp->processor);
|
|
}
|
|
|
|
static int pr_rss(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
return snprintf(outbuf, COLWID, "%lu", pp->vm_rss);
|
|
}
|
|
|
|
/* pp->vm_rss * 1000 would overflow on 32-bit systems with 64 GB memory */
|
|
static int pr_pmem(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
unsigned long pmem = 0;
|
|
pmem = pp->vm_rss * 1000ULL / kb_main_total;
|
|
if (pmem > 999) pmem = 999;
|
|
return snprintf(outbuf, COLWID, "%2u.%u", (unsigned)(pmem/10), (unsigned)(pmem%10));
|
|
}
|
|
|
|
static int pr_lstart(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
time_t t;
|
|
t = time_of_boot + pp->start_time / Hertz;
|
|
return snprintf(outbuf, COLWID, "%24.24s", ctime(&t));
|
|
}
|
|
|
|
/* Unix98 specifies a STIME header for a column that shows the start
|
|
* time of the process, but does not specify a format or format specifier.
|
|
* From the general Unix98 rules, we know there must not be any spaces.
|
|
* Most systems violate that rule, though the Solaris documentation
|
|
* claims to print the column without spaces. (NOT!)
|
|
*
|
|
* So this isn't broken, but could be renamed to u98_std_stime,
|
|
* as long as it still shows as STIME when using the -f option.
|
|
*/
|
|
static int pr_stime(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
struct tm *proc_time;
|
|
struct tm *our_time;
|
|
time_t t;
|
|
const char *fmt;
|
|
int tm_year;
|
|
int tm_yday;
|
|
our_time = localtime(&seconds_since_1970); /* not reentrant */
|
|
tm_year = our_time->tm_year;
|
|
tm_yday = our_time->tm_yday;
|
|
t = time_of_boot + pp->start_time / Hertz;
|
|
proc_time = localtime(&t); /* not reentrant, this corrupts our_time */
|
|
fmt = "%H:%M"; /* 03:02 23:59 */
|
|
if(tm_yday != proc_time->tm_yday) fmt = "%b%d"; /* Jun06 Aug27 */
|
|
if(tm_year != proc_time->tm_year) fmt = "%Y"; /* 1991 2001 */
|
|
return strftime(outbuf, 42, fmt, proc_time);
|
|
}
|
|
|
|
static int pr_start(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
time_t t;
|
|
char *str;
|
|
t = time_of_boot + pp->start_time / Hertz;
|
|
str = ctime(&t);
|
|
if(str[8]==' ') str[8]='0';
|
|
if(str[11]==' ') str[11]='0';
|
|
if((unsigned long)t+60*60*24 > seconds_since_1970)
|
|
return snprintf(outbuf, COLWID, "%8.8s", str+11);
|
|
return snprintf(outbuf, COLWID, " %6.6s", str+4);
|
|
}
|
|
|
|
|
|
#ifdef SIGNAL_STRING
|
|
static int help_pr_sig(char *restrict const outbuf, const char *restrict const sig){
|
|
long len = 0;
|
|
len = strlen(sig);
|
|
if(wide_signals){
|
|
if(len>8) return snprintf(outbuf, COLWID, "%s", sig);
|
|
return snprintf(outbuf, COLWID, "00000000%s", sig);
|
|
}
|
|
if(len-strspn(sig,"0") > 8)
|
|
return snprintf(outbuf, COLWID, "<%s", sig+len-8);
|
|
return snprintf(outbuf, COLWID, "%s", sig+len-8);
|
|
}
|
|
#else
|
|
static int help_pr_sig(unsigned long long sig){
|
|
if(wide_signals) return snprintf(outbuf, COLWID, "%016Lx", sig);
|
|
if(sig>>32) return snprintf(outbuf, COLWID, "<%08Lx", sig&0xffffffffLL);
|
|
return snprintf(outbuf, COLWID, "%08Lx", sig&0xffffffffLL);
|
|
}
|
|
#endif
|
|
|
|
static int pr_sig(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
return help_pr_sig(outbuf, pp->signal);
|
|
}
|
|
static int pr_sigmask(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
return help_pr_sig(outbuf, pp->blocked);
|
|
}
|
|
static int pr_sigignore(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
return help_pr_sig(outbuf, pp->sigignore);
|
|
}
|
|
static int pr_sigcatch(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
return help_pr_sig(outbuf, pp->sigcatch);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/*
|
|
* internal terms: ruid euid suid fuid
|
|
* kernel vars: uid euid suid fsuid
|
|
* command args: ruid uid svuid n/a
|
|
*/
|
|
|
|
static int pr_egid(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
return snprintf(outbuf, COLWID, "%d", pp->egid);
|
|
}
|
|
static int pr_rgid(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
return snprintf(outbuf, COLWID, "%d", pp->rgid);
|
|
}
|
|
static int pr_sgid(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
return snprintf(outbuf, COLWID, "%d", pp->sgid);
|
|
}
|
|
static int pr_fgid(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
return snprintf(outbuf, COLWID, "%d", pp->fgid);
|
|
}
|
|
|
|
static int pr_euid(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
return snprintf(outbuf, COLWID, "%d", pp->euid);
|
|
}
|
|
static int pr_ruid(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
return snprintf(outbuf, COLWID, "%d", pp->ruid);
|
|
}
|
|
static int pr_suid(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
return snprintf(outbuf, COLWID, "%d", pp->suid);
|
|
}
|
|
static int pr_fuid(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
return snprintf(outbuf, COLWID, "%d", pp->fuid);
|
|
}
|
|
|
|
// The Open Group Base Specifications Issue 6 (IEEE Std 1003.1, 2004 Edition)
|
|
// requires that user and group names print as decimal numbers if there is
|
|
// not enough room in the column, so tough luck if you don't like it.
|
|
//
|
|
// The UNIX and POSIX way to change column width is to rename it:
|
|
// ps -o pid,user=CumbersomeUserNames -o comm
|
|
// The easy way is to directly specify the desired width:
|
|
// ps -o pid,user:19,comm
|
|
//
|
|
static int do_pr_name(char *restrict const outbuf, const char *restrict const name, unsigned u){
|
|
if(!user_is_number){
|
|
int rightward = OUTBUF_SIZE; /* max cells */
|
|
int len; /* real cells */
|
|
|
|
escape_str(outbuf, name, OUTBUF_SIZE, &rightward);
|
|
len = OUTBUF_SIZE-rightward;
|
|
|
|
if(len <= (int)max_rightward)
|
|
return len; /* returns number of cells */
|
|
}
|
|
return snprintf(outbuf, COLWID, "%u", u);
|
|
}
|
|
|
|
static int pr_ruser(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
return do_pr_name(outbuf, pp->ruser, pp->ruid);
|
|
}
|
|
static int pr_euser(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
return do_pr_name(outbuf, pp->euser, pp->euid);
|
|
}
|
|
static int pr_fuser(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
return do_pr_name(outbuf, pp->fuser, pp->fuid);
|
|
}
|
|
static int pr_suser(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
return do_pr_name(outbuf, pp->suser, pp->suid);
|
|
}
|
|
|
|
static int pr_egroup(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
return do_pr_name(outbuf, pp->egroup, pp->egid);
|
|
}
|
|
static int pr_rgroup(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
return do_pr_name(outbuf, pp->rgroup, pp->rgid);
|
|
}
|
|
static int pr_fgroup(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
return do_pr_name(outbuf, pp->fgroup, pp->fgid);
|
|
}
|
|
static int pr_sgroup(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
return do_pr_name(outbuf, pp->sgroup, pp->sgid);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// TID tid LWP lwp SPID spid
|
|
static int pr_thread(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
return snprintf(outbuf, COLWID, "%u", pp->tid);
|
|
}
|
|
// thcount THCNT
|
|
static int pr_nlwp(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
return snprintf(outbuf, COLWID, "%d", pp->nlwp);
|
|
}
|
|
|
|
static int pr_sess(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
return snprintf(outbuf, COLWID, "%u", pp->session);
|
|
}
|
|
static int pr_tpgid(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
return snprintf(outbuf, COLWID, "%d", pp->tpgid);
|
|
}
|
|
|
|
|
|
/* SGI uses "cpu" to print the processor ID with header "P" */
|
|
static int pr_sgi_p(char *restrict const outbuf, const proc_t *restrict const pp){ /* FIXME */
|
|
if(pp->state == 'R') return snprintf(outbuf, COLWID, "%d", pp->processor);
|
|
return snprintf(outbuf, COLWID, "*");
|
|
}
|
|
|
|
|
|
/****************** FLASK & seLinux security stuff **********************/
|
|
// move the bulk of this to libproc sometime
|
|
|
|
static int pr_context(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
char filename[48];
|
|
size_t len;
|
|
ssize_t num_read;
|
|
int fd;
|
|
|
|
// wchan file is suitable for testing
|
|
//snprintf(filename, sizeof filename, "/proc/%d/wchan", pp->tgid);
|
|
snprintf(filename, sizeof filename, "/proc/%d/attr/current", pp->tgid);
|
|
|
|
fd = open(filename, O_RDONLY, 0);
|
|
if(likely(fd==-1)) goto fail;
|
|
num_read = read(fd, outbuf, 666);
|
|
close(fd);
|
|
if(unlikely(num_read<=0)) goto fail;
|
|
outbuf[num_read] = '\0';
|
|
|
|
len = 0;
|
|
while(outbuf[len]>' ' && outbuf[len]<='~') len++;
|
|
outbuf[len] = '\0';
|
|
if(len) return len;
|
|
|
|
fail:
|
|
outbuf[0] = '-';
|
|
outbuf[1] = '\0';
|
|
return 1;
|
|
}
|
|
|
|
#if 0
|
|
// This needs more study, considering:
|
|
// 1. the static linking option (maybe disable this in that case)
|
|
// 2. the -z and -Z option issue
|
|
// 3. width of output
|
|
static int pr_context(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
static int (*ps_getpidcon)(pid_t pid, char **context) = 0;
|
|
static int tried_load = 0;
|
|
size_t len;
|
|
char *context;
|
|
|
|
if(!ps_getpidcon && !tried_load){
|
|
void *handle = dlopen("libselinux.so.1", RTLD_NOW);
|
|
if(handle){
|
|
dlerror();
|
|
ps_getpidcon = dlsym(handle, "getpidcon");
|
|
if(dlerror())
|
|
ps_getpidcon = 0;
|
|
}
|
|
tried_load++;
|
|
}
|
|
if(ps_getpidcon && !ps_getpidcon(pp->tgid, &context)){
|
|
size_t max_len = OUTBUF_SIZE-1;
|
|
len = strlen(context);
|
|
if(len > max_len) len = max_len;
|
|
memcpy(outbuf, context, len);
|
|
outbuf[len] = '\0';
|
|
free(context);
|
|
}else{
|
|
outbuf[0] = '-';
|
|
outbuf[1] = '\0';
|
|
len = 1;
|
|
}
|
|
return len;
|
|
}
|
|
#endif
|
|
|
|
|
|
////////////////////////////// Test code /////////////////////////////////
|
|
|
|
// like "args"
|
|
static int pr_t_unlimited(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
static const char *const vals[] = {"[123456789-12345] <defunct>","ps","123456789-123456"};
|
|
(void)pp;
|
|
snprintf(outbuf, max_rightward+1, "%s", vals[lines_to_next_header%3u]);
|
|
return strlen(outbuf);
|
|
}
|
|
static int pr_t_unlimited2(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
static const char *const vals[] = {"unlimited", "[123456789-12345] <defunct>","ps","123456789-123456"};
|
|
(void)pp;
|
|
snprintf(outbuf, max_rightward+1, "%s", vals[lines_to_next_header%4u]);
|
|
return strlen(outbuf);
|
|
}
|
|
|
|
// like "etime"
|
|
static int pr_t_right(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
static const char *const vals[] = {"999-23:59:59","99-23:59:59","9-23:59:59","59:59"};
|
|
(void)pp;
|
|
return snprintf(outbuf, COLWID, "%s", vals[lines_to_next_header%4u]);
|
|
}
|
|
static int pr_t_right2(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
static const char *const vals[] = {"999-23:59:59","99-23:59:59","9-23:59:59"};
|
|
(void)pp;
|
|
return snprintf(outbuf, COLWID, "%s", vals[lines_to_next_header%3u]);
|
|
}
|
|
|
|
// like "tty"
|
|
static int pr_t_left(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
static const char *const vals[] = {"tty7","pts/9999","iseries/vtty42","ttySMX0","3270/tty4"};
|
|
(void)pp;
|
|
return snprintf(outbuf, COLWID, "%s", vals[lines_to_next_header%5u]);
|
|
}
|
|
static int pr_t_left2(char *restrict const outbuf, const proc_t *restrict const pp){
|
|
static const char *const vals[] = {"tty7","pts/9999","ttySMX0","3270/tty4"};
|
|
(void)pp;
|
|
return snprintf(outbuf, COLWID, "%s", vals[lines_to_next_header%4u]);
|
|
}
|
|
|
|
/***************************************************************************/
|
|
/*************************** other stuff ***********************************/
|
|
|
|
/*
|
|
* Old header specifications.
|
|
*
|
|
* short Up " PID TTY STAT TIME COMMAND"
|
|
* long l Pp " FLAGS UID PID PPID PRI NI SIZE RSS WCHAN STA TTY TIME COMMAND
|
|
* user u up "USER PID %CPU %MEM SIZE RSS TTY STAT START TIME COMMAND
|
|
* jobs j gPp " PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
|
|
* sig s p " UID PID SIGNAL BLOCKED IGNORED CATCHED STAT TTY TIME COMMAND
|
|
* vm v r " PID TTY STAT TIME PAGEIN TSIZ DSIZ RSS LIM %MEM COMMAND
|
|
* m m r " PID TTY MAJFLT MINFLT TRS DRS SIZE SWAP RSS SHRD LIB DT COMMAND
|
|
* regs X p "NR PID STACK ESP EIP TMOUT ALARM STAT TTY TIME COMMAND
|
|
*/
|
|
|
|
/*
|
|
* Unix98 requires that the heading for tty is TT, though XPG4, Digital,
|
|
* and BSD use TTY. The Unix98 headers are:
|
|
* args,comm,etime,group,nice,pcpu,pgid
|
|
* pid,ppid,rgroup,ruser,time,tty,user,vsz
|
|
*
|
|
* BSD c: "command" becomes accounting name ("comm" or "ucomm")
|
|
* BSD n: "user" becomes "uid" and "wchan" becomes "nwchan" (number)
|
|
*/
|
|
|
|
/* Justification control for flags field. */
|
|
#define USER CF_USER // left if text, right if numeric
|
|
#define LEFT CF_LEFT
|
|
#define RIGHT CF_RIGHT
|
|
#define UNLIMITED CF_UNLIMITED
|
|
#define WCHAN CF_WCHAN // left if text, right if numeric
|
|
#define SIGNAL CF_SIGNAL // right in 9, or 16 if room
|
|
#define PIDMAX CF_PIDMAX
|
|
#define TO CF_PRINT_THREAD_ONLY
|
|
#define PO CF_PRINT_PROCESS_ONLY
|
|
#define ET CF_PRINT_EVERY_TIME
|
|
#define AN CF_PRINT_AS_NEEDED // no idea
|
|
|
|
/* short names to save space */
|
|
#define MEM PROC_FILLMEM /* read statm */
|
|
#define ARG PROC_FILLARG /* read cmdline (cleared if c option) */
|
|
#define COM PROC_FILLCOM /* read cmdline (cleared if not -f option) */
|
|
#define ENV PROC_FILLENV /* read environ */
|
|
#define USR PROC_FILLUSR /* uid_t -> user names */
|
|
#define GRP PROC_FILLGRP /* gid_t -> group names */
|
|
#define WCH PROC_FILLWCHAN /* do WCHAN lookup */
|
|
|
|
|
|
/* TODO
|
|
* pull out annoying BSD aliases into another table (to macro table?)
|
|
* add sorting functions here (to unify names)
|
|
*/
|
|
|
|
/* temporary hack -- mark new stuff grabbed from Debian ps */
|
|
#define LNx LNX
|
|
|
|
/* there are about 211 listed */
|
|
|
|
/* Many of these are placeholders for unsupported options. */
|
|
static const format_struct format_array[] = {
|
|
/* code header print() sort() width need vendor flags */
|
|
{"%cpu", "%CPU", pr_pcpu, sr_pcpu, 4, 0, BSD, ET|RIGHT}, /*pcpu*/
|
|
{"%mem", "%MEM", pr_pmem, sr_nop, 4, 0, BSD, PO|RIGHT}, /*pmem*/
|
|
{"_left", "LLLLLLLL", pr_t_left, sr_nop, 8, 0, TST, ET|LEFT},
|
|
{"_left2", "L2L2L2L2", pr_t_left2, sr_nop, 8, 0, TST, ET|LEFT},
|
|
{"_right", "RRRRRRRRRRR", pr_t_right, sr_nop, 11, 0, TST, ET|RIGHT},
|
|
{"_right2", "R2R2R2R2R2R", pr_t_right2, sr_nop, 11, 0, TST, ET|RIGHT},
|
|
{"_unlimited","U", pr_t_unlimited, sr_nop, 16, 0, TST, ET|UNLIMITED},
|
|
{"_unlimited2","U2", pr_t_unlimited2, sr_nop, 16, 0, TST, ET|UNLIMITED},
|
|
{"acflag", "ACFLG", pr_nop, sr_nop, 5, 0, XXX, AN|RIGHT}, /*acflg*/
|
|
{"acflg", "ACFLG", pr_nop, sr_nop, 5, 0, BSD, AN|RIGHT}, /*acflag*/
|
|
{"addr", "ADDR", pr_nop, sr_nop, 4, 0, XXX, AN|RIGHT},
|
|
{"addr_1", "ADDR", pr_nop, sr_nop, 1, 0, LNX, AN|LEFT},
|
|
{"alarm", "ALARM", pr_alarm, sr_alarm, 5, 0, LNX, AN|RIGHT},
|
|
{"argc", "ARGC", pr_nop, sr_nop, 4, 0, LNX, PO|RIGHT},
|
|
{"args", "COMMAND", pr_args, sr_cmd, 27, ARG, U98, PO|UNLIMITED}, /*command*/
|
|
{"atime", "TIME", pr_time, sr_nop, 8, 0, SOE, ET|RIGHT}, /*cputime*/ /* was 6 wide */
|
|
{"blocked", "BLOCKED", pr_sigmask, sr_nop, 9, 0, BSD, TO|SIGNAL}, /*sigmask*/
|
|
{"bnd", "BND", pr_nop, sr_nop, 1, 0, AIX, TO|RIGHT},
|
|
{"bsdstart", "START", pr_bsdstart, sr_nop, 6, 0, LNX, ET|RIGHT},
|
|
{"bsdtime", "TIME", pr_bsdtime, sr_nop, 6, 0, LNX, ET|RIGHT},
|
|
{"c", "C", pr_c, sr_pcpu, 2, 0, SUN, ET|RIGHT},
|
|
{"caught", "CAUGHT", pr_sigcatch, sr_nop, 9, 0, BSD, TO|SIGNAL}, /*sigcatch*/
|
|
{"class", "CLS", pr_class, sr_sched, 3, 0, XXX, TO|LEFT},
|
|
{"cls", "CLS", pr_class, sr_sched, 3, 0, HPU, TO|RIGHT}, /*says HPUX or RT*/
|
|
{"cmaj_flt", "-", pr_nop, sr_cmaj_flt, 1, 0, LNX, AN|RIGHT},
|
|
{"cmd", "CMD", pr_args, sr_cmd, 27, ARG, DEC, PO|UNLIMITED}, /*ucomm*/
|
|
{"cmin_flt", "-", pr_nop, sr_cmin_flt, 1, 0, LNX, AN|RIGHT},
|
|
{"cnswap", "-", pr_nop, sr_nop, 1, 0, LNX, AN|RIGHT},
|
|
{"comm", "COMMAND", pr_comm, sr_cmd, 15, COM, U98, PO|UNLIMITED}, /*ucomm*/
|
|
{"command", "COMMAND", pr_args, sr_cmd, 27, ARG, XXX, PO|UNLIMITED}, /*args*/
|
|
{"context", "CONTEXT", pr_context, sr_nop, 31, 0, LNX, ET|LEFT},
|
|
{"cp", "CP", pr_cp, sr_pcpu, 3, 0, DEC, ET|RIGHT}, /*cpu*/
|
|
{"cpu", "CPU", pr_nop, sr_nop, 3, 0, BSD, AN|RIGHT}, /* FIXME ... HP-UX wants this as the CPU number for SMP? */
|
|
{"cpuid", "CPUID", pr_psr, sr_nop, 5, 0, BSD, TO|RIGHT}, // OpenBSD: 8 wide!
|
|
{"cputime", "TIME", pr_time, sr_nop, 8, 0, DEC, ET|RIGHT}, /*time*/
|
|
{"cstime", "-", pr_nop, sr_cstime, 1, 0, LNX, AN|RIGHT},
|
|
{"ctid", "CTID", pr_nop, sr_nop, 5, 0, SUN, ET|RIGHT}, // resource contracts?
|
|
{"cursig", "CURSIG", pr_nop, sr_nop, 6, 0, DEC, AN|RIGHT},
|
|
{"cutime", "-", pr_nop, sr_cutime, 1, 0, LNX, AN|RIGHT},
|
|
{"cwd", "CWD", pr_nop, sr_nop, 3, 0, LNX, AN|LEFT},
|
|
{"drs", "DRS", pr_drs, sr_drs, 5, MEM, LNX, PO|RIGHT},
|
|
{"dsiz", "DSIZ", pr_dsiz, sr_nop, 4, 0, LNX, PO|RIGHT},
|
|
{"egid", "EGID", pr_egid, sr_egid, 5, 0, LNX, ET|RIGHT},
|
|
{"egroup", "EGROUP", pr_egroup, sr_egroup, 8, GRP, LNX, ET|USER},
|
|
{"eip", "EIP", pr_eip, sr_kstk_eip, 8, 0, LNX, TO|RIGHT},
|
|
{"emul", "EMUL", pr_nop, sr_nop, 13, 0, BSD, PO|LEFT}, /* "FreeBSD ELF32" and such */
|
|
{"end_code", "E_CODE", pr_nop, sr_end_code, 8, 0, LNx, PO|RIGHT},
|
|
{"environ","ENVIRONMENT",pr_nop, sr_nop, 11, ENV, LNx, PO|UNLIMITED},
|
|
{"esp", "ESP", pr_esp, sr_kstk_esp, 8, 0, LNX, TO|RIGHT},
|
|
{"etime", "ELAPSED", pr_etime, sr_nop, 11, 0, U98, ET|RIGHT}, /* was 7 wide */
|
|
{"euid", "EUID", pr_euid, sr_euid, 5, 0, LNX, ET|RIGHT},
|
|
{"euser", "EUSER", pr_euser, sr_euser, 8, USR, LNX, ET|USER},
|
|
{"f", "F", pr_flag, sr_flags, 1, 0, XXX, ET|RIGHT}, /*flags*/
|
|
{"fgid", "FGID", pr_fgid, sr_fgid, 5, 0, LNX, ET|RIGHT},
|
|
{"fgroup", "FGROUP", pr_fgroup, sr_fgroup, 8, GRP, LNX, ET|USER},
|
|
{"flag", "F", pr_flag, sr_flags, 1, 0, DEC, ET|RIGHT},
|
|
{"flags", "F", pr_flag, sr_flags, 1, 0, BSD, ET|RIGHT}, /*f*/ /* was FLAGS, 8 wide */
|
|
{"fname", "COMMAND", pr_fname, sr_nop, 8, 0, SUN, PO|LEFT},
|
|
{"fsgid", "FSGID", pr_fgid, sr_fgid, 5, 0, LNX, ET|RIGHT},
|
|
{"fsgroup", "FSGROUP", pr_fgroup, sr_fgroup, 8, GRP, LNX, ET|USER},
|
|
{"fsuid", "FSUID", pr_fuid, sr_fuid, 5, 0, LNX, ET|RIGHT},
|
|
{"fsuser", "FSUSER", pr_fuser, sr_fuser, 8, USR, LNX, ET|USER},
|
|
{"fuid", "FUID", pr_fuid, sr_fuid, 5, 0, LNX, ET|RIGHT},
|
|
{"fuser", "FUSER", pr_fuser, sr_fuser, 8, USR, LNX, ET|USER},
|
|
{"gid", "GID", pr_egid, sr_egid, 5, 0, SUN, ET|RIGHT},
|
|
{"group", "GROUP", pr_egroup, sr_egroup, 8, GRP, U98, ET|USER},
|
|
{"iac", "IAC", pr_nop, sr_nop, 4, 0, BSD, AN|RIGHT}, // DragonFly
|
|
{"ignored", "IGNORED", pr_sigignore,sr_nop, 9, 0, BSD, TO|SIGNAL}, /*sigignore*/
|
|
{"inblk", "INBLK", pr_nop, sr_nop, 5, 0, BSD, AN|RIGHT}, /*inblock*/
|
|
{"inblock", "INBLK", pr_nop, sr_nop, 5, 0, DEC, AN|RIGHT}, /*inblk*/
|
|
{"intpri", "PRI", pr_opri, sr_priority, 3, 0, HPU, TO|RIGHT},
|
|
{"jid", "JID", pr_nop, sr_nop, 1, 0, SGI, PO|RIGHT},
|
|
{"jobc", "JOBC", pr_nop, sr_nop, 4, 0, XXX, AN|RIGHT},
|
|
{"ktrace", "KTRACE", pr_nop, sr_nop, 8, 0, BSD, AN|RIGHT},
|
|
{"ktracep", "KTRACEP", pr_nop, sr_nop, 8, 0, BSD, AN|RIGHT},
|
|
{"label", "LABEL", pr_context, sr_nop, 31, 0, SGI, ET|LEFT},
|
|
{"lastcpu", "C", pr_psr, sr_nop, 3, 0, BSD, TO|RIGHT}, // DragonFly
|
|
{"lim", "LIM", pr_lim, sr_rss_rlim, 5, 0, BSD, AN|RIGHT},
|
|
{"login", "LOGNAME", pr_nop, sr_nop, 8, 0, BSD, AN|LEFT}, /*logname*/ /* double check */
|
|
{"logname", "LOGNAME", pr_nop, sr_nop, 8, 0, XXX, AN|LEFT}, /*login*/
|
|
{"longtname", "TTY", pr_tty8, sr_tty, 8, 0, DEC, PO|LEFT},
|
|
{"lstart", "STARTED", pr_lstart, sr_nop, 24, 0, XXX, ET|RIGHT},
|
|
{"luid", "LUID", pr_nop, sr_nop, 5, 0, LNX, ET|RIGHT}, /* login ID */
|
|
{"luser", "LUSER", pr_nop, sr_nop, 8, USR, LNX, ET|USER}, /* login USER */
|
|
{"lwp", "LWP", pr_thread, sr_tid, 5, 0, SUN, TO|PIDMAX|RIGHT},
|
|
{"m_drs", "DRS", pr_drs, sr_drs, 5, MEM, LNx, PO|RIGHT},
|
|
{"m_dt", "DT", pr_nop, sr_dt, 4, MEM, LNx, PO|RIGHT},
|
|
{"m_lrs", "LRS", pr_nop, sr_lrs, 5, MEM, LNx, PO|RIGHT},
|
|
{"m_resident", "RES", pr_nop, sr_resident, 5,MEM, LNx, PO|RIGHT},
|
|
{"m_share", "SHRD", pr_nop, sr_share, 5, MEM, LNx, PO|RIGHT},
|
|
{"m_size", "SIZE", pr_size, sr_size, 5, MEM, LNX, PO|RIGHT},
|
|
{"m_swap", "SWAP", pr_nop, sr_nop, 5, 0, LNx, PO|RIGHT},
|
|
{"m_trs", "TRS", pr_trs, sr_trs, 5, MEM, LNx, PO|RIGHT},
|
|
{"maj_flt", "MAJFL", pr_majflt, sr_maj_flt, 6, 0, LNX, AN|RIGHT},
|
|
{"majflt", "MAJFLT", pr_majflt, sr_maj_flt, 6, 0, XXX, AN|RIGHT},
|
|
{"min_flt", "MINFL", pr_minflt, sr_min_flt, 6, 0, LNX, AN|RIGHT},
|
|
{"minflt", "MINFLT", pr_minflt, sr_min_flt, 6, 0, XXX, AN|RIGHT},
|
|
{"msgrcv", "MSGRCV", pr_nop, sr_nop, 6, 0, XXX, AN|RIGHT},
|
|
{"msgsnd", "MSGSND", pr_nop, sr_nop, 6, 0, XXX, AN|RIGHT},
|
|
{"mwchan", "MWCHAN", pr_nop, sr_nop, 6, WCH, BSD, TO|WCHAN}, /* mutex (FreeBSD) */
|
|
{"ni", "NI", pr_nice, sr_nice, 3, 0, BSD, TO|RIGHT}, /*nice*/
|
|
{"nice", "NI", pr_nice, sr_nice, 3, 0, U98, TO|RIGHT}, /*ni*/
|
|
{"nivcsw", "IVCSW", pr_nop, sr_nop, 5, 0, XXX, AN|RIGHT},
|
|
{"nlwp", "NLWP", pr_nlwp, sr_nlwp, 4, 0, SUN, PO|RIGHT},
|
|
{"nsignals", "NSIGS", pr_nop, sr_nop, 5, 0, DEC, AN|RIGHT}, /*nsigs*/
|
|
{"nsigs", "NSIGS", pr_nop, sr_nop, 5, 0, BSD, AN|RIGHT}, /*nsignals*/
|
|
{"nswap", "NSWAP", pr_nop, sr_nop, 5, 0, XXX, AN|RIGHT},
|
|
{"nvcsw", "VCSW", pr_nop, sr_nop, 5, 0, XXX, AN|RIGHT},
|
|
{"nwchan", "WCHAN", pr_nwchan, sr_nop, 6, 0, XXX, TO|RIGHT},
|
|
{"opri", "PRI", pr_opri, sr_priority, 3, 0, SUN, TO|RIGHT},
|
|
{"osz", "SZ", pr_nop, sr_nop, 2, 0, SUN, PO|RIGHT},
|
|
{"oublk", "OUBLK", pr_nop, sr_nop, 5, 0, BSD, AN|RIGHT}, /*oublock*/
|
|
{"oublock", "OUBLK", pr_nop, sr_nop, 5, 0, DEC, AN|RIGHT}, /*oublk*/
|
|
{"p_ru", "P_RU", pr_nop, sr_nop, 6, 0, BSD, AN|RIGHT},
|
|
{"paddr", "PADDR", pr_nop, sr_nop, 6, 0, BSD, AN|RIGHT},
|
|
{"pagein", "PAGEIN", pr_majflt, sr_maj_flt, 6, 0, XXX, AN|RIGHT},
|
|
{"pcpu", "%CPU", pr_pcpu, sr_pcpu, 4, 0, U98, ET|RIGHT}, /*%cpu*/
|
|
{"pending", "PENDING", pr_sig, sr_nop, 9, 0, BSD, ET|SIGNAL}, /*sig*/
|
|
{"pgid", "PGID", pr_pgid, sr_pgrp, 5, 0, U98, PO|PIDMAX|RIGHT},
|
|
{"pgrp", "PGRP", pr_pgid, sr_pgrp, 5, 0, LNX, PO|PIDMAX|RIGHT},
|
|
{"pid", "PID", pr_pid, sr_tgid, 5, 0, U98, PO|PIDMAX|RIGHT},
|
|
{"pmem", "%MEM", pr_pmem, sr_nop, 4, 0, XXX, PO|RIGHT}, /*%mem*/
|
|
{"poip", "-", pr_nop, sr_nop, 1, 0, BSD, AN|RIGHT},
|
|
{"policy", "POL", pr_class, sr_sched, 3, 0, DEC, TO|LEFT},
|
|
{"ppid", "PPID", pr_ppid, sr_ppid, 5, 0, U98, PO|PIDMAX|RIGHT},
|
|
{"pri", "PRI", pr_pri, sr_nop, 3, 0, XXX, TO|RIGHT},
|
|
{"pri_api", "API", pr_pri_api, sr_nop, 3, 0, LNX, TO|RIGHT},
|
|
{"pri_bar", "BAR", pr_pri_bar, sr_nop, 3, 0, LNX, TO|RIGHT},
|
|
{"pri_baz", "BAZ", pr_pri_baz, sr_nop, 3, 0, LNX, TO|RIGHT},
|
|
{"pri_foo", "FOO", pr_pri_foo, sr_nop, 3, 0, LNX, TO|RIGHT},
|
|
{"priority", "PRI", pr_priority, sr_priority, 3, 0, LNX, TO|RIGHT},
|
|
{"prmgrp", "PRMGRP", pr_nop, sr_nop, 12, 0, HPU, PO|RIGHT},
|
|
{"prmid", "PRMID", pr_nop, sr_nop, 12, 0, HPU, PO|RIGHT},
|
|
{"project", "PROJECT", pr_nop, sr_nop, 12, 0, SUN, PO|LEFT}, // see prm* andctid
|
|
{"projid", "PROJID", pr_nop, sr_nop, 5, 0, SUN, PO|RIGHT},
|
|
{"pset", "PSET", pr_nop, sr_nop, 4, 0, DEC, TO|RIGHT},
|
|
{"psr", "PSR", pr_psr, sr_nop, 3, 0, DEC, TO|RIGHT},
|
|
{"psxpri", "PPR", pr_nop, sr_nop, 3, 0, DEC, TO|RIGHT},
|
|
{"re", "RE", pr_nop, sr_nop, 3, 0, BSD, AN|RIGHT},
|
|
{"resident", "RES", pr_nop, sr_resident, 5,MEM, LNX, PO|RIGHT},
|
|
{"rgid", "RGID", pr_rgid, sr_rgid, 5, 0, XXX, ET|RIGHT},
|
|
{"rgroup", "RGROUP", pr_rgroup, sr_rgroup, 8, GRP, U98, ET|USER}, /* was 8 wide */
|
|
{"rlink", "RLINK", pr_nop, sr_nop, 8, 0, BSD, AN|RIGHT},
|
|
{"rss", "RSS", pr_rss, sr_rss, 5, 0, XXX, PO|RIGHT}, /* was 5 wide */
|
|
{"rssize", "RSS", pr_rss, sr_vm_rss, 5, 0, DEC, PO|RIGHT}, /*rsz*/
|
|
{"rsz", "RSZ", pr_rss, sr_vm_rss, 5, 0, BSD, PO|RIGHT}, /*rssize*/
|
|
{"rtprio", "RTPRIO", pr_rtprio, sr_rtprio, 6, 0, BSD, TO|RIGHT},
|
|
{"ruid", "RUID", pr_ruid, sr_ruid, 5, 0, XXX, ET|RIGHT},
|
|
{"ruser", "RUSER", pr_ruser, sr_ruser, 8, USR, U98, ET|USER},
|
|
{"s", "S", pr_s, sr_state, 1, 0, SUN, TO|LEFT}, /*stat,state*/
|
|
{"sched", "SCH", pr_sched, sr_sched, 3, 0, AIX, TO|RIGHT},
|
|
{"scnt", "SCNT", pr_nop, sr_nop, 4, 0, DEC, AN|RIGHT}, /* man page misspelling of scount? */
|
|
{"scount", "SC", pr_nop, sr_nop, 4, 0, AIX, AN|RIGHT}, /* scnt==scount, DEC claims both */
|
|
{"sess", "SESS", pr_sess, sr_session, 5, 0, XXX, PO|PIDMAX|RIGHT},
|
|
{"session", "SESS", pr_sess, sr_session, 5, 0, LNX, PO|PIDMAX|RIGHT},
|
|
{"sgi_p", "P", pr_sgi_p, sr_nop, 1, 0, LNX, TO|RIGHT}, /* "cpu" number */
|
|
{"sgi_rss", "RSS", pr_rss, sr_nop, 4, 0, LNX, PO|LEFT}, /* SZ:RSS */
|
|
{"sgid", "SGID", pr_sgid, sr_sgid, 5, 0, LNX, ET|RIGHT},
|
|
{"sgroup", "SGROUP", pr_sgroup, sr_sgroup, 8, GRP, LNX, ET|USER},
|
|
{"share", "-", pr_nop, sr_share, 1, MEM, LNX, PO|RIGHT},
|
|
{"sid", "SID", pr_sess, sr_session, 5, 0, XXX, PO|PIDMAX|RIGHT}, /* Sun & HP */
|
|
{"sig", "PENDING", pr_sig, sr_nop, 9, 0, XXX, ET|SIGNAL}, /*pending*/
|
|
{"sig_block", "BLOCKED", pr_sigmask, sr_nop, 9, 0, LNX, TO|SIGNAL},
|
|
{"sig_catch", "CATCHED", pr_sigcatch, sr_nop, 9, 0, LNX, TO|SIGNAL},
|
|
{"sig_ignore", "IGNORED",pr_sigignore, sr_nop, 9, 0, LNX, TO|SIGNAL},
|
|
{"sig_pend", "SIGNAL", pr_sig, sr_nop, 9, 0, LNX, ET|SIGNAL},
|
|
{"sigcatch", "CAUGHT", pr_sigcatch, sr_nop, 9, 0, XXX, TO|SIGNAL}, /*caught*/
|
|
{"sigignore", "IGNORED", pr_sigignore,sr_nop, 9, 0, XXX, TO|SIGNAL}, /*ignored*/
|
|
{"sigmask", "BLOCKED", pr_sigmask, sr_nop, 9, 0, XXX, TO|SIGNAL}, /*blocked*/
|
|
{"size", "SZ", pr_swapable, sr_swapable, 5, 0, SCO, PO|RIGHT},
|
|
{"sl", "SL", pr_nop, sr_nop, 3, 0, XXX, AN|RIGHT},
|
|
{"spid", "SPID", pr_thread, sr_tid, 5, 0, SGI, TO|PIDMAX|RIGHT},
|
|
{"stackp", "STACKP", pr_stackp, sr_start_stack, 8, 0, LNX, PO|RIGHT}, /*start_stack*/
|
|
{"start", "STARTED", pr_start, sr_nop, 8, 0, XXX, ET|RIGHT},
|
|
{"start_code", "S_CODE", pr_nop, sr_start_code, 8, 0, LNx, PO|RIGHT},
|
|
{"start_stack", "STACKP", pr_stackp, sr_start_stack, 8, 0, LNX, PO|RIGHT}, /*stackp*/
|
|
{"start_time", "START", pr_stime, sr_start_time, 5, 0, LNx, ET|RIGHT},
|
|
{"stat", "STAT", pr_stat, sr_state, 4, 0, BSD, TO|LEFT}, /*state,s*/
|
|
{"state", "S", pr_s, sr_state, 1, 0, XXX, TO|LEFT}, /*stat,s*/ /* was STAT */
|
|
{"status", "STATUS", pr_nop, sr_nop, 6, 0, DEC, AN|RIGHT},
|
|
{"stime", "STIME", pr_stime, sr_stime, 5, 0, XXX, ET|RIGHT}, /* was 6 wide */
|
|
{"suid", "SUID", pr_suid, sr_suid, 5, 0, LNx, ET|RIGHT},
|
|
{"suser", "SUSER", pr_suser, sr_suser, 8, USR, LNx, ET|USER},
|
|
{"svgid", "SVGID", pr_sgid, sr_sgid, 5, 0, XXX, ET|RIGHT},
|
|
{"svgroup", "SVGROUP", pr_sgroup, sr_sgroup, 8, GRP, LNX, ET|USER},
|
|
{"svuid", "SVUID", pr_suid, sr_suid, 5, 0, XXX, ET|RIGHT},
|
|
{"svuser", "SVUSER", pr_suser, sr_suser, 8, USR, LNX, ET|USER},
|
|
{"systime", "SYSTEM", pr_nop, sr_nop, 6, 0, DEC, ET|RIGHT},
|
|
{"sz", "SZ", pr_sz, sr_nop, 5, 0, HPU, PO|RIGHT},
|
|
{"taskid", "TASKID", pr_nop, sr_nop, 5, 0, SUN, TO|PIDMAX|RIGHT}, // is this a thread ID?
|
|
{"tdev", "TDEV", pr_nop, sr_nop, 4, 0, XXX, AN|RIGHT},
|
|
{"thcount", "THCNT", pr_nlwp, sr_nlwp, 5, 0, AIX, PO|RIGHT},
|
|
{"tid", "TID", pr_thread, sr_tid, 5, 0, AIX, TO|PIDMAX|RIGHT},
|
|
{"time", "TIME", pr_time, sr_nop, 8, 0, U98, ET|RIGHT}, /*cputime*/ /* was 6 wide */
|
|
{"timeout", "TMOUT", pr_nop, sr_nop, 5, 0, LNX, AN|RIGHT}, // 2.0.xx era
|
|
{"tmout", "TMOUT", pr_nop, sr_nop, 5, 0, LNX, AN|RIGHT}, // 2.0.xx era
|
|
{"tname", "TTY", pr_tty8, sr_tty, 8, 0, DEC, PO|LEFT},
|
|
{"tpgid", "TPGID", pr_tpgid, sr_tpgid, 5, 0, XXX, PO|PIDMAX|RIGHT},
|
|
{"trs", "TRS", pr_trs, sr_trs, 4, MEM, AIX, PO|RIGHT},
|
|
{"trss", "TRSS", pr_trs, sr_trs, 4, MEM, BSD, PO|RIGHT}, /* 4.3BSD NET/2 */
|
|
{"tsess", "TSESS", pr_nop, sr_nop, 5, 0, BSD, PO|PIDMAX|RIGHT},
|
|
{"tsession", "TSESS", pr_nop, sr_nop, 5, 0, DEC, PO|PIDMAX|RIGHT},
|
|
{"tsid", "TSID", pr_nop, sr_nop, 5, 0, BSD, PO|PIDMAX|RIGHT},
|
|
{"tsiz", "TSIZ", pr_tsiz, sr_nop, 4, 0, BSD, PO|RIGHT},
|
|
{"tt", "TT", pr_tty8, sr_tty, 8, 0, BSD, PO|LEFT},
|
|
{"tty", "TT", pr_tty8, sr_tty, 8, 0, U98, PO|LEFT}, /* Unix98 requires "TT" but has "TTY" too. :-( */ /* was 3 wide */
|
|
{"tty4", "TTY", pr_tty4, sr_tty, 4, 0, LNX, PO|LEFT},
|
|
{"tty8", "TTY", pr_tty8, sr_tty, 8, 0, LNX, PO|LEFT},
|
|
{"u_procp", "UPROCP", pr_nop, sr_nop, 6, 0, DEC, AN|RIGHT},
|
|
{"ucmd", "CMD", pr_comm, sr_cmd, 15, COM, DEC, PO|UNLIMITED}, /*ucomm*/
|
|
{"ucomm", "COMMAND", pr_comm, sr_cmd, 15, COM, XXX, PO|UNLIMITED}, /*comm*/
|
|
{"uid", "UID", pr_euid, sr_euid, 5, 0, XXX, ET|RIGHT},
|
|
{"uid_hack", "UID", pr_euser, sr_euser, 8, USR, XXX, ET|USER},
|
|
{"umask", "UMASK", pr_nop, sr_nop, 5, 0, DEC, AN|RIGHT},
|
|
{"uname", "USER", pr_euser, sr_euser, 8, USR, DEC, ET|USER}, /* man page misspelling of user? */
|
|
{"upr", "UPR", pr_nop, sr_nop, 3, 0, BSD, TO|RIGHT}, /*usrpri*/
|
|
{"uprocp", "UPROCP", pr_nop, sr_nop, 8, 0, BSD, AN|RIGHT},
|
|
{"user", "USER", pr_euser, sr_euser, 8, USR, U98, ET|USER}, /* BSD n forces this to UID */
|
|
{"usertime", "USER", pr_nop, sr_nop, 4, 0, DEC, ET|RIGHT},
|
|
{"usrpri", "UPR", pr_nop, sr_nop, 3, 0, DEC, TO|RIGHT}, /*upr*/
|
|
{"util", "C", pr_c, sr_pcpu, 2, 0, SGI, ET|RIGHT}, // not sure about "C"
|
|
{"utime", "UTIME", pr_nop, sr_utime, 6, 0, LNx, ET|RIGHT},
|
|
{"vm_data", "DATA", pr_nop, sr_vm_data, 5, 0, LNx, PO|RIGHT},
|
|
{"vm_exe", "EXE", pr_nop, sr_vm_exe, 5, 0, LNx, PO|RIGHT},
|
|
{"vm_lib", "LIB", pr_nop, sr_vm_lib, 5, 0, LNx, PO|RIGHT},
|
|
{"vm_lock", "LCK", pr_nop, sr_vm_lock, 3, 0, LNx, PO|RIGHT},
|
|
{"vm_stack", "STACK", pr_nop, sr_vm_stack, 5, 0, LNx, PO|RIGHT},
|
|
{"vsize", "VSZ", pr_vsz, sr_vsize, 6, 0, DEC, PO|RIGHT}, /*vsz*/
|
|
{"vsz", "VSZ", pr_vsz, sr_vm_size, 6, 0, U98, PO|RIGHT}, /*vsize*/
|
|
{"wchan", "WCHAN", pr_wchan, sr_wchan, 6, WCH, XXX, TO|WCHAN}, /* BSD n forces this to nwchan */ /* was 10 wide */
|
|
{"wname", "WCHAN", pr_wname, sr_nop, 6, WCH, SGI, TO|WCHAN}, /* opposite of nwchan */
|
|
{"xstat", "XSTAT", pr_nop, sr_nop, 5, 0, BSD, AN|RIGHT},
|
|
{"zone", "ZONE", pr_context, sr_nop, 31, 0, SUN, ET|LEFT}, // Solaris zone == Linux context?
|
|
{"zoneid", "ZONEID", pr_nop, sr_nop, 31, 0, SUN, ET|RIGHT},// Linux only offers context names
|
|
{"~", "-", pr_nop, sr_nop, 1, 0, LNX, AN|RIGHT} /* NULL would ruin alphabetical order */
|
|
};
|
|
|
|
#undef USER
|
|
#undef LEFT
|
|
#undef RIGHT
|
|
#undef UNLIMITED
|
|
#undef WCHAN
|
|
#undef SIGNAL
|
|
#undef PIDMAX
|
|
#undef PO
|
|
#undef TO
|
|
#undef AN
|
|
#undef ET
|
|
|
|
static const int format_array_count = sizeof(format_array)/sizeof(format_struct);
|
|
|
|
|
|
/****************************** Macro formats *******************************/
|
|
/* First X field may be NR, which is p->start_code>>26 printed with %2ld */
|
|
/* That seems useless though, and Debian already killed it. */
|
|
/* The ones marked "Digital" have the name defined, not just the data. */
|
|
static const macro_struct macro_array[] = {
|
|
{"DFMT", "pid,tname,state,cputime,cmd"}, /* Digital's default */
|
|
{"DefBSD", "pid,tname,stat,bsdtime,args"}, /* Our BSD default */
|
|
{"DefSysV", "pid,tname,time,cmd"}, /* Our SysV default */
|
|
{"END_BSD", "state,tname,cputime,comm"}, /* trailer for O */
|
|
{"END_SYS5", "state,tname,time,command"}, /* trailer for -O */
|
|
{"F5FMT", "uname,pid,ppid,c,start,tname,time,cmd"}, /* Digital -f */
|
|
|
|
{"FB_", "pid,tt,stat,time,command"}, /* FreeBSD default */
|
|
{"FB_j", "user,pid,ppid,pgid,sess,jobc,stat,tt,time,command"}, /* FreeBSD j */
|
|
{"FB_l", "uid,pid,ppid,cpu,pri,nice,vsz,rss,wchan,stat,tt,time,command"}, /* FreeBSD l */
|
|
{"FB_u", "user,pid,pcpu,pmem,vsz,rss,tt,stat,start,time,command"}, /* FreeBSD u */
|
|
{"FB_v", "pid,stat,time,sl,re,pagein,vsz,rss,lim,tsiz,pcpu,pmem,command"}, /* FreeBSD v */
|
|
|
|
{"FD_", "pid,tty,time,comm"}, /* Fictional Debian SysV default */
|
|
{"FD_f", "user,pid,ppid,start_time,tty,time,comm"}, /* Fictional Debian -f */
|
|
{"FD_fj", "user,pid,ppid,start_time,tty,time,pgid,sid,comm"}, /* Fictional Debian -jf */
|
|
{"FD_j", "pid,tty,time,pgid,sid,comm"}, /* Fictional Debian -j */
|
|
{"FD_l", "flags,state,uid,pid,ppid,priority,nice,vsz,wchan,tty,time,comm"}, /* Fictional Debian -l */
|
|
{"FD_lj", "flags,state,uid,pid,ppid,priority,nice,vsz,wchan,tty,time,pgid,sid,comm"}, /* Fictional Debian -jl */
|
|
|
|
{"FL5FMT", "f,state,uid,pid,ppid,pcpu,pri,nice,rss,wchan,start,time,command"}, /* Digital -fl */
|
|
|
|
{"FLASK_context", "pid,context,command"}, /* Flask Linux context, --context */
|
|
|
|
{"HP_", "pid,tty,time,comm"}, /* HP default */
|
|
{"HP_f", "user,pid,ppid,cpu,stime,tty,time,args"}, /* HP -f */
|
|
{"HP_fl", "flags,state,user,pid,ppid,cpu,intpri,nice,addr,sz,wchan,stime,tty,time,args"}, /* HP -fl */
|
|
{"HP_l", "flags,state,uid,pid,ppid,cpu,intpri,nice,addr,sz,wchan,tty,time,comm"}, /* HP -l */
|
|
|
|
{"J390", "pid,sid,pgrp,tname,atime,args"}, /* OS/390 -j */
|
|
{"JFMT", "user,pid,ppid,pgid,sess,jobc,state,tname,cputime,command"}, /* Digital j and -j */
|
|
{"L5FMT", "f,state,uid,pid,ppid,c,pri,nice,addr,sz,wchan,tt,time,ucmd"}, /* Digital -l */
|
|
{"LFMT", "uid,pid,ppid,cp,pri,nice,vsz,rss,wchan,state,tname,cputime,command"}, /* Digital l */
|
|
|
|
{"OL_X", "pid,start_stack,esp,eip,timeout,alarm,stat,tname,bsdtime,args"}, /* Old i386 Linux X */
|
|
{"OL_j", "ppid,pid,pgid,sid,tname,tpgid,stat,uid,bsdtime,args"}, /* Old Linux j */
|
|
{"OL_l", "flags,uid,pid,ppid,priority,nice,vsz,rss,wchan,stat,tname,bsdtime,args"}, /* Old Linux l */
|
|
{"OL_m", "pid,tname,majflt,minflt,m_trs,m_drs,m_size,m_swap,rss,m_share,vm_lib,m_dt,args"}, /* Old Linux m */
|
|
{"OL_s", "uid,pid,pending,sig_block,sig_ignore,caught,stat,tname,bsdtime,args"}, /* Old Linux s */
|
|
{"OL_u", "user,pid,pcpu,pmem,vsz,rss,tname,stat,start_time,bsdtime,args"}, /* Old Linux u */
|
|
{"OL_v", "pid,tname,stat,bsdtime,maj_flt,m_trs,m_drs,rss,pmem,args"}, /* Old Linux v */
|
|
|
|
{"RD_", "pid,tname,state,bsdtime,comm"}, /* Real Debian default */
|
|
{"RD_f", "uid,pid,ppid,start_time,tname,bsdtime,args"}, /* Real Debian -f */
|
|
{"RD_fj", "uid,pid,ppid,start_time,tname,bsdtime,pgid,sid,args"}, /* Real Debian -jf */
|
|
{"RD_j", "pid,tname,state,bsdtime,pgid,sid,comm"}, /* Real Debian -j */
|
|
{"RD_l", "flags,state,uid,pid,ppid,priority,nice,wchan,tname,bsdtime,comm"}, /* Real Debian -l */
|
|
{"RD_lj", "flags,state,uid,pid,ppid,priority,nice,wchan,tname,bsdtime,pgid,sid,comm"}, /* Real Debian -jl */
|
|
|
|
{"RUSAGE", "minflt,majflt,nswap,inblock,oublock,msgsnd,msgrcv,nsigs,nvcsw,nivcsw"}, /* Digital -o "RUSAGE" */
|
|
{"SCHED", "user,pcpu,pri,usrpri,nice,psxpri,psr,policy,pset"}, /* Digital -o "SCHED" */
|
|
{"SFMT", "uid,pid,cursig,sig,sigmask,sigignore,sigcatch,stat,tname,command"}, /* Digital s */
|
|
|
|
{"Std_f", "uid_hack,pid,ppid,c,stime,tname,time,cmd"}, /* new -f */
|
|
{"Std_fl", "f,s,uid_hack,pid,ppid,c,opri,ni,addr,sz,wchan,stime,tname,time,cmd"}, /* -fl */
|
|
{"Std_l", "f,s,uid,pid,ppid,c,opri,ni,addr,sz,wchan,tname,time,ucmd"}, /* new -l */
|
|
|
|
{"THREAD", "user,pcpu,pri,scnt,wchan,usertime,systime"}, /* Digital -o "THREAD" */
|
|
{"UFMT", "uname,pid,pcpu,pmem,vsz,rss,tt,state,start,time,command"}, /* Digital u */
|
|
{"VFMT", "pid,tt,state,time,sl,pagein,vsz,rss,pcpu,pmem,command"}, /* Digital v */
|
|
{"~", "~"} /* NULL would ruin alphabetical order */
|
|
};
|
|
|
|
static const int macro_array_count = sizeof(macro_array)/sizeof(macro_struct);
|
|
|
|
|
|
/*************************** AIX formats ********************/
|
|
/* Convert AIX format codes to normal format specifiers. */
|
|
static const aix_struct aix_array[] = {
|
|
{'C', "pcpu", "%CPU"},
|
|
{'G', "group", "GROUP"},
|
|
{'P', "ppid", "PPID"},
|
|
{'U', "user", "USER"},
|
|
{'a', "args", "COMMAND"},
|
|
{'c', "comm", "COMMAND"},
|
|
{'g', "rgroup", "RGROUP"},
|
|
{'n', "nice", "NI"},
|
|
{'p', "pid", "PID"},
|
|
{'r', "pgid", "PGID"},
|
|
{'t', "etime", "ELAPSED"},
|
|
{'u', "ruser", "RUSER"},
|
|
{'x', "time", "TIME"},
|
|
{'y', "tty", "TTY"},
|
|
{'z', "vsz", "VSZ"},
|
|
{'~', "~", "~"} /* NULL would ruin alphabetical order */
|
|
};
|
|
static const int aix_array_count = sizeof(aix_array)/sizeof(aix_struct);
|
|
|
|
|
|
/********************* sorting ***************************/
|
|
/* Convert short sorting codes to normal format specifiers. */
|
|
static const shortsort_struct shortsort_array[] = {
|
|
{'C', "pcpu" },
|
|
{'G', "tpgid" },
|
|
{'J', "cstime" },
|
|
/* {'K', "stime" }, */ /* conflict, system vs. start time */
|
|
{'M', "maj_flt" },
|
|
{'N', "cmaj_flt" },
|
|
{'P', "ppid" },
|
|
{'R', "resident" },
|
|
{'S', "share" },
|
|
{'T', "start_time" },
|
|
{'U', "uid" }, /* euid */
|
|
{'c', "cmd" },
|
|
{'f', "flags" },
|
|
{'g', "pgrp" },
|
|
{'j', "cutime" },
|
|
{'k', "utime" },
|
|
{'m', "min_flt" },
|
|
{'n', "cmin_flt" },
|
|
{'o', "session" },
|
|
{'p', "pid" },
|
|
{'r', "rss" },
|
|
{'s', "size" },
|
|
{'t', "tty" },
|
|
{'u', "user" },
|
|
{'v', "vsize" },
|
|
{'y', "priority" }, /* nice */
|
|
{'~', "~" } /* NULL would ruin alphabetical order */
|
|
};
|
|
static const int shortsort_array_count = sizeof(shortsort_array)/sizeof(shortsort_struct);
|
|
|
|
|
|
/*********** print format_array **********/
|
|
/* called by the parser in another file */
|
|
void print_format_specifiers(void){
|
|
const format_struct *walk = format_array;
|
|
while(*(walk->spec) != '~'){
|
|
if(walk->pr != pr_nop) printf("%-12.12s %-8.8s\n", walk->spec, walk->head);
|
|
walk++;
|
|
}
|
|
}
|
|
|
|
/************ comparison functions for bsearch *************/
|
|
|
|
static int compare_format_structs(const void *a, const void *b){
|
|
return strcmp(((const format_struct*)a)->spec,((const format_struct*)b)->spec);
|
|
}
|
|
|
|
static int compare_macro_structs(const void *a, const void *b){
|
|
return strcmp(((const macro_struct*)a)->spec,((const macro_struct*)b)->spec);
|
|
}
|
|
|
|
/******** look up structs as needed by the sort & format parsers ******/
|
|
|
|
const shortsort_struct *search_shortsort_array(const int findme){
|
|
const shortsort_struct *walk = shortsort_array;
|
|
while(walk->desc != '~'){
|
|
if(walk->desc == findme) return walk;
|
|
walk++;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
const aix_struct *search_aix_array(const int findme){
|
|
const aix_struct *walk = aix_array;
|
|
while(walk->desc != '~'){
|
|
if(walk->desc == findme) return walk;
|
|
walk++;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
const format_struct *search_format_array(const char *findme){
|
|
format_struct key;
|
|
key.spec = findme;
|
|
return bsearch(&key, format_array, format_array_count,
|
|
sizeof(format_struct), compare_format_structs
|
|
);
|
|
}
|
|
|
|
const macro_struct *search_macro_array(const char *findme){
|
|
macro_struct key;
|
|
key.spec = findme;
|
|
return bsearch(&key, macro_array, macro_array_count,
|
|
sizeof(macro_struct), compare_macro_structs
|
|
);
|
|
}
|
|
|
|
static unsigned int active_cols; /* some multiple of screen_cols */
|
|
|
|
/***** Last chance, avoid needless trunctuation. */
|
|
static void check_header_width(void){
|
|
format_node *walk = format_list;
|
|
unsigned int total = 0;
|
|
int was_normal = 0;
|
|
unsigned int i = 0;
|
|
unsigned int sigs = 0;
|
|
while(walk){
|
|
switch((walk->flags) & CF_JUST_MASK){
|
|
default:
|
|
total += walk->width;
|
|
total += was_normal;
|
|
was_normal = 1;
|
|
break;
|
|
case CF_SIGNAL:
|
|
sigs++;
|
|
total += walk->width;
|
|
total += was_normal;
|
|
was_normal = 1;
|
|
break;
|
|
case CF_UNLIMITED: /* could chop this a bit */
|
|
if(walk->next) total += walk->width;
|
|
else total += 3; /* not strlen(walk->name) */
|
|
total += was_normal;
|
|
was_normal = 1;
|
|
break;
|
|
case 0: /* AIX */
|
|
total += walk->width;
|
|
was_normal = 0;
|
|
break;
|
|
}
|
|
walk = walk->next;
|
|
}
|
|
for(;;){
|
|
i++;
|
|
active_cols = screen_cols * i;
|
|
if(active_cols>=total) break;
|
|
if(screen_cols*i >= OUTBUF_SIZE/2) break; /* can't go over */
|
|
}
|
|
wide_signals = (total+sigs*7 <= active_cols);
|
|
}
|
|
|
|
|
|
/********** show one process (NULL proc prints header) **********/
|
|
|
|
//#define SPACE_AMOUNT page_size
|
|
#define SPACE_AMOUNT 144
|
|
|
|
static char *saved_outbuf;
|
|
|
|
void show_one_proc(const proc_t *restrict const p, const format_node *restrict fmt){
|
|
/* unknown: maybe set correct & actual to 1, remove +/- 1 below */
|
|
int correct = 0; /* screen position we should be at */
|
|
int actual = 0; /* screen position we are at */
|
|
int amount = 0; /* amount of text that this data is */
|
|
int leftpad = 0; /* amount of space this column _could_ need */
|
|
int space = 0; /* amount of space we actually need to print */
|
|
int dospace = 0; /* previous column determined that we need a space */
|
|
int legit = 0; /* legitimately stolen extra space */
|
|
int sz = 0; /* real size of data in outbuffer */
|
|
int tmpspace = 0;
|
|
char *restrict const outbuf = saved_outbuf;
|
|
static int did_stuff = 0; /* have we ever printed anything? */
|
|
|
|
if(unlikely(-1==(long)p)){ /* true only once, at the end */
|
|
if(did_stuff) return;
|
|
/* have _never_ printed anything, but might need a header */
|
|
if(!--lines_to_next_header){
|
|
lines_to_next_header = header_gap;
|
|
show_one_proc(NULL,fmt);
|
|
}
|
|
/* fprintf(stderr, "No processes available.\n"); */ /* legal? */
|
|
exit(1);
|
|
}
|
|
if(likely(p)){ /* not header, maybe we should call ourselves for it */
|
|
if(unlikely(!--lines_to_next_header)){
|
|
lines_to_next_header = header_gap;
|
|
show_one_proc(NULL,fmt);
|
|
}
|
|
}
|
|
did_stuff = 1;
|
|
if(unlikely(active_cols>(int)OUTBUF_SIZE)) fprintf(stderr,"Fix bigness error.\n");
|
|
|
|
/* print row start sequence */
|
|
for(;;){
|
|
legit = 0;
|
|
/* set width suggestion which might be ignored */
|
|
// if(likely(fmt->next)) max_rightward = fmt->width;
|
|
// else max_rightward = active_cols-((correct>actual) ? correct : actual);
|
|
|
|
if(likely(fmt->next)){
|
|
max_rightward = fmt->width;
|
|
tmpspace = 0;
|
|
}else{
|
|
tmpspace = correct-actual;
|
|
if (tmpspace<1){
|
|
tmpspace = dospace;
|
|
max_rightward = active_cols-actual-tmpspace;
|
|
}else{
|
|
max_rightward = active_cols - ( (correct>actual) ? correct : actual );
|
|
}
|
|
}
|
|
max_leftward = fmt->width + actual - correct; /* TODO check this */
|
|
|
|
// fprintf(stderr, "cols: %d, max_rightward: %d, max_leftward: %d, actual: %d, correct: %d\n",
|
|
// active_cols, max_rightward, max_leftward, actual, correct);
|
|
|
|
/* prepare data and calculate leftpad */
|
|
if(likely(p) && likely(fmt->pr)) amount = (*fmt->pr)(outbuf,p);
|
|
else amount = strlen(strcpy(outbuf, fmt->name)); /* AIX or headers */
|
|
|
|
switch((fmt->flags) & CF_JUST_MASK){
|
|
case 0: /* for AIX, assigned outside this file */
|
|
leftpad = 0;
|
|
break;
|
|
case CF_LEFT: /* bad */
|
|
leftpad = 0;
|
|
break;
|
|
case CF_RIGHT: /* OK */
|
|
leftpad = fmt->width - amount;
|
|
if(leftpad < 0) leftpad = 0;
|
|
break;
|
|
case CF_SIGNAL:
|
|
/* if the screen is wide enough, use full 16-character output */
|
|
if(wide_signals){
|
|
leftpad = 16 - amount;
|
|
legit = 7;
|
|
}else{
|
|
leftpad = 9 - amount;
|
|
}
|
|
if(leftpad < 0) leftpad = 0;
|
|
break;
|
|
case CF_USER: /* bad */
|
|
leftpad = fmt->width - amount;
|
|
if(leftpad < 0) leftpad = 0;
|
|
if(!user_is_number) leftpad = 0;
|
|
break;
|
|
case CF_WCHAN: /* bad */
|
|
if(wchan_is_number){
|
|
leftpad = fmt->width - amount;
|
|
if(leftpad < 0) leftpad = 0;
|
|
break;
|
|
}else{
|
|
if ((active_cols-actual-tmpspace)<1)
|
|
outbuf[1] = '\0'; /* oops, we (mostly) lose this column... */
|
|
leftpad = 0;
|
|
break;
|
|
}
|
|
case CF_UNLIMITED:
|
|
{
|
|
if(active_cols-actual-tmpspace < 1)
|
|
outbuf[1] = '\0'; /* oops, we (mostly) lose this column... */
|
|
leftpad = 0;
|
|
break;
|
|
}
|
|
default:
|
|
fprintf(stderr, "bad alignment code\n");
|
|
break;
|
|
}
|
|
/* At this point:
|
|
*
|
|
* correct from previous column
|
|
* actual from previous column
|
|
* amount not needed (garbage due to chopping)
|
|
* leftpad left padding for this column alone (not make-up or gap)
|
|
* space not needed (will recalculate now)
|
|
* dospace if we require space between this and the prior column
|
|
* legit space we were allowed to steal, and thus did steal
|
|
*/
|
|
space = correct - actual + leftpad;
|
|
if(space<1) space=dospace;
|
|
if(unlikely(space>SPACE_AMOUNT)) space=SPACE_AMOUNT; // only so much available
|
|
|
|
/* real size -- don't forget in 'amount' is number of cells */
|
|
sz = strlen(outbuf);
|
|
|
|
/* print data, set x position stuff */
|
|
if(unlikely(!fmt->next)){
|
|
/* Last column. Write padding + data + newline all together. */
|
|
outbuf[sz] = '\n';
|
|
fwrite(outbuf-space, space+sz+1, 1, stdout);
|
|
break;
|
|
}
|
|
/* Not the last column. Write padding + data together. */
|
|
fwrite(outbuf-space, space+sz, 1, stdout);
|
|
actual += space+amount;
|
|
correct += fmt->width;
|
|
correct += legit; /* adjust for SIGNAL expansion */
|
|
if(fmt->pr && fmt->next->pr){ /* neither is AIX filler */
|
|
correct++;
|
|
dospace = 1;
|
|
}else{
|
|
dospace = 0;
|
|
}
|
|
fmt = fmt->next;
|
|
/* At this point:
|
|
*
|
|
* correct screen position we should be at
|
|
* actual screen position we are at
|
|
* amount not needed
|
|
* leftpad not needed
|
|
* space not needed
|
|
* dospace if have determined that we need a space next time
|
|
* legit not needed
|
|
*/
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef TESTING
|
|
static void sanity_check(void){
|
|
format_struct *fs = format_array;
|
|
while((fs->spec)[0] != '~'){
|
|
if(strlen(fs->head) > fs->width) printf("%d %s\n",strlen(fs->head),fs->spec);
|
|
fs++;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
void init_output(void){
|
|
int outbuf_pages;
|
|
char *outbuf;
|
|
|
|
switch(page_size){
|
|
case 65536: page_shift = 16; break;
|
|
case 32768: page_shift = 15; break;
|
|
case 16384: page_shift = 14; break;
|
|
case 8192: page_shift = 13; break;
|
|
default: fprintf(stderr, "Unknown page size! (assume 4096)\n");
|
|
case 4096: page_shift = 12; break;
|
|
case 2048: page_shift = 11; break;
|
|
case 1024: page_shift = 10; break;
|
|
}
|
|
|
|
// add page_size-1 to round up
|
|
outbuf_pages = (OUTBUF_SIZE+SPACE_AMOUNT+page_size-1)/page_size;
|
|
outbuf = mmap(
|
|
0,
|
|
page_size * (outbuf_pages+1), // 1 more, for guard page at high addresses
|
|
PROT_READ | PROT_WRITE,
|
|
MAP_PRIVATE | MAP_ANONYMOUS,
|
|
-1,
|
|
0
|
|
);
|
|
memset(outbuf, ' ', SPACE_AMOUNT);
|
|
if(SPACE_AMOUNT==page_size) mprotect(outbuf, page_size, PROT_READ);
|
|
mprotect(outbuf + page_size*outbuf_pages, page_size, PROT_NONE); // gaurd page
|
|
saved_outbuf = outbuf + SPACE_AMOUNT;
|
|
// available space: page_size*outbuf_pages-SPACE_AMOUNT
|
|
|
|
seconds_since_1970 = time(NULL);
|
|
time_of_boot = seconds_since_1970 - seconds_since_boot;
|
|
|
|
meminfo();
|
|
|
|
check_header_width();
|
|
}
|