2 * Copyright 1999-2004 by Albert Cahalan; all rights reserved.
4 * This file may be used subject to the terms and conditions of the
5 * GNU Library General Public License Version 2, or any later version
6 * at your option, as published by the Free Software Foundation.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU Library General Public License for more details.
14 * This file is really gross, and I know it. I looked into several
15 * alternate ways to deal with the mess, and they were all ugly.
17 * FreeBSD has a fancy hack using offsets into a struct -- that
18 * saves code but it is _really_ gross. See the PO macro below.
20 * We could have a second column width for wide output format.
21 * For example, Digital prints the real-time signals.
28 * table 1 maps aix to specifier
29 * table 2 maps shortsort to specifier
30 * table 3 maps macro to specifiers
31 * table 4 maps specifier to title,datatype,offset,vendor,helptext
32 * table 5 maps datatype to justification,width,widewidth,sorting,printing
34 * Here, "datatype" could be user,uid,u16,pages,deltaT,signals,tty,longtty...
35 * It must be enough to determine printing and sorting.
37 * After the tables, increase width as needed to fit the header.
39 * Table 5 could go in a file with the output functions.
50 #include <sys/ioctl.h>
51 #include <sys/resource.h>
52 #include <sys/types.h>
58 #include "../proc/readproc.h"
59 #include "../proc/sysinfo.h"
60 #include "../proc/wchan.h"
61 #include "../proc/procps.h"
62 #include "../proc/devname.h"
63 #include "../proc/escape.h"
67 * Stop assuming system time is local time.
70 #define COLWID 240 /* satisfy snprintf, which is faster than sprintf */
72 static unsigned max_rightward = 0x12345678; /* space for RIGHT stuff */
73 static unsigned max_leftward = 0x12345678; /* space for LEFT stuff */
77 static int wide_signals; /* true if we have room */
79 static unsigned long seconds_since_1970;
80 static unsigned long time_of_boot;
81 static unsigned long page_shift;
84 /*************************************************************************/
85 /************ Lots of sort functions, starting with the NOP **************/
87 static int sr_nop(const proc_t* a, const proc_t* b){
88 (void)a;(void)b; /* shut up gcc */
92 #define CMP_STR(NAME) \
93 static int sr_ ## NAME(const proc_t* P, const proc_t* Q) { \
94 return strcmp(P->NAME, Q->NAME); \
97 #define CMP_INT(NAME) \
98 static int sr_ ## NAME (const proc_t* P, const proc_t* Q) { \
99 if (P->NAME < Q->NAME) return -1; \
100 if (P->NAME > Q->NAME) return 1; \
104 /* fast version, for values which either:
105 * a. differ by no more than 0x7fffffff
106 * b. only need to be grouped same w/ same
108 #define CMP_SMALL(NAME) \
109 static int sr_ ## NAME (const proc_t* P, const proc_t* Q) { \
110 return (int)(P->NAME) - (int)(Q->NAME); \
117 CMP_SMALL(priority) /* nice */
119 CMP_SMALL(nice) /* priority */
120 CMP_INT(rss) /* resident set size from stat file */ /* vm_rss, resident */
122 CMP_INT(size) /* total pages */ /* vm_size, vsize */
123 CMP_INT(resident) /* resident pages */ /* vm_rss, rss */
124 CMP_INT(share) /* shared pages */
125 CMP_INT(trs) /* executable pages */
126 CMP_INT(lrs) /* obsolete "library" pages above 0x60000000 */
127 CMP_INT(drs) /* other pages (assumed data?) */
128 CMP_INT(dt) /* dirty pages */
130 CMP_INT(vm_size) /* kB VM */ /* size, vsize */
131 CMP_INT(vm_lock) /* kB locked */
132 CMP_INT(vm_rss) /* kB rss */ /* rss, resident */
133 CMP_INT(vm_data) /* kB "data" == data-stack */
134 CMP_INT(vm_stack) /* kB stack */
135 CMP_INT(vm_exe) /* kB "exec" == exec-lib */
136 CMP_INT(vm_lib) /* kB "libraries" */
137 CMP_INT(vsize) /* pages VM */ /* size, vm_size */
145 CMP_INT(stime) /* Old: sort by systime. New: show start time. Uh oh. */
154 /* CMP_STR(*environ) */
155 /* CMP_STR(*cmdline) */
166 /* CMP_STR(ttyc) */ /* FIXME -- use strncmp with 8 max */
188 /* approximation to: kB of address space that could end up in swap */
189 static int sr_swapable(const proc_t* P, const proc_t* Q) {
190 unsigned long p_swapable = P->vm_data + P->vm_stack;
191 unsigned long q_swapable = Q->vm_data + Q->vm_stack;
192 if (p_swapable < q_swapable) return -1;
193 if (p_swapable > q_swapable) return 1;
198 /***************************************************************************/
199 /************ Lots of format functions, starting with the NOP **************/
201 // so popular it can't be "static"
202 int pr_nop(char *restrict const outbuf, const proc_t *restrict const pp){
204 return snprintf(outbuf, COLWID, "%c", '-');
208 /********* Unix 98 ************/
212 Only comm and args are allowed to contain blank characters; all others are
213 not. Any implementation-dependent variables will be specified in the system
214 documentation along with the default header and indicating if the field
215 may contain blank characters.
217 Some headers do not have a standardized specifier!
219 %CPU pcpu The % of cpu time used recently, with unspecified "recently".
220 ADDR The address of the process.
221 C Processor utilisation for scheduling.
222 CMD The command name, or everything with -f.
223 COMMAND args Command + args. May chop as desired. May use either version.
225 ELAPSED etime Elapsed time since the process was started. [[dd-]hh:]mm:ss
226 F Flags (octal and additive)
227 GROUP group Effective group ID, prefer text over decimal.
228 NI nice Decimal system scheduling priority, see nice(1).
229 PGID pgid The decimal value of the process group ID.
231 PPID ppid Decimal PID.
232 PRI Priority. Higher numbers mean lower priority.
233 RGROUP rgroup Real group ID, prefer text over decimal.
234 RUSER ruser Real user ID, prefer text over decimal.
235 S The state of the process.
236 STIME Starting time of the process.
237 SZ The size in blocks of the core image of the process.
238 TIME time Cumulative CPU time. [dd-]hh:mm:ss
239 TT tty Name of tty in format used by who(1).
240 TTY The controlling terminal for the process.
241 UID UID, or name when -f
242 USER user Effective user ID, prefer text over decimal.
243 VSZ vsz Virtual memory size in decimal kB.
244 WCHAN Where waiting/sleeping or blank if running.
246 The nice value is used to compute the priority.
248 For some undefined ones, Digital does:
250 F flag Process flags -- but in hex!
251 PRI pri Process priority
252 S state Symbolic process status
253 TTY tt,tty,tname,longtname -- all do "ttyp1", "console", "??"
254 UID uid Process user ID (effective UID)
255 WCHAN wchan Address of event on which a
257 For some undefined ones, Sun does:
259 ADDR addr memory address of the process
260 C c Processor utilization for scheduling (obsolete).
264 STIME start time, printed w/o blanks. If 24h old, months & days
265 SZ size (in pages) of the swappable process's image in main memory
270 For some undefined ones, SCO does:
271 ADDR addr Virtual address of the process' entry in the process table.
272 SZ swappable size in kB of the virtual data and stack
273 STIME stime hms or md time format
276 /* Source & destination are known. Return bytes or screen characters? */
277 static int forest_helper(char *restrict const outbuf){
278 char *p = forest_prefix;
280 int rightward=max_rightward;
282 /* Arrrgh! somebody defined unix as 1 */
283 if(forest_type == 'u') goto unixy;
286 case ' ': strcpy(q, " "); break;
287 case 'L': strcpy(q, " \\_ "); break;
288 case '+': strcpy(q, " \\_ "); break;
289 case '|': strcpy(q, " | "); break;
290 case '\0': return q-outbuf; /* redundant & not used */
292 if (rightward-4 < 0) {
294 return max_rightward;
300 return q-outbuf; /* gcc likes this here */
304 case ' ': strcpy(q, " "); break;
305 case 'L': strcpy(q, " "); break;
306 case '+': strcpy(q, " "); break;
307 case '|': strcpy(q, " "); break;
308 case '\0': return q-outbuf; /* redundant & not used */
310 if (rightward-2 < 0) {
312 return max_rightward;
318 return q-outbuf; /* gcc likes this here */
322 /* XPG4-UNIX, according to Digital:
323 The "args" and "command" specifiers show what was passed to the command.
324 Modifications to the arguments are not shown.
328 * pp->cmd short accounting name (comm & ucomm)
329 * pp->cmdline long name with args (args & command)
330 * pp->environ environment
333 // FIXME: some of these may hit the guard page in forest mode
335 /* "command" is the same thing: long unless c */
336 static int pr_args(char *restrict const outbuf, const proc_t *restrict const pp){
339 int rightward=max_rightward;
342 int fh = forest_helper(outbuf);
346 if(bsd_c_option) flags = ESC_DEFUNCT;
347 else flags = ESC_DEFUNCT | ESC_BRACKETS | ESC_ARGS;
348 endp += escape_command(endp, pp, OUTBUF_SIZE, &rightward, flags);
350 if(bsd_e_option && rightward>1){
351 const char **env = (const char**)pp->environ;
355 endp += escape_strlist(endp, env, OUTBUF_SIZE, &rightward);
358 //return endp - outbuf;
359 return max_rightward-rightward;
362 /* "ucomm" is the same thing: short unless -f */
363 static int pr_comm(char *restrict const outbuf, const proc_t *restrict const pp){
366 int rightward=max_rightward;
369 int fh = forest_helper(outbuf);
373 if(unix_f_option) flags = ESC_DEFUNCT | ESC_BRACKETS | ESC_ARGS;
374 else flags = ESC_DEFUNCT;
375 endp += escape_command(endp, pp, OUTBUF_SIZE, &rightward, flags);
377 if(bsd_e_option && rightward>1){
378 const char **env = (const char**)pp->environ;
382 endp += escape_strlist(endp, env, OUTBUF_SIZE, &rightward);
385 //return endp - outbuf;
386 return max_rightward-rightward;
388 /* Non-standard, from SunOS 5 */
389 static int pr_fname(char *restrict const outbuf, const proc_t *restrict const pp){
391 int rightward = max_rightward;
394 int fh = forest_helper(outbuf);
398 if (rightward>8) /* 8=default, but forest maybe feeds more */
401 endp += escape_str(endp, pp->cmd, OUTBUF_SIZE, &rightward);
402 //return endp - outbuf;
403 return max_rightward-rightward;
406 /* elapsed wall clock time, [[dd-]hh:]mm:ss format (not same as "time") */
407 static int pr_etime(char *restrict const outbuf, const proc_t *restrict const pp){
409 unsigned dd,hh,mm,ss;
411 t = seconds_since_boot - (unsigned long)(pp->start_time / Hertz);
419 cp +=( dd ? snprintf(cp, COLWID, "%u-", dd) : 0 );
420 cp +=( (dd || hh) ? snprintf(cp, COLWID, "%02u:", hh) : 0 );
421 cp += snprintf(cp, COLWID, "%02u:%02u", mm, ss) ;
422 return (int)(cp-outbuf);
425 /* "Processor utilisation for scheduling." --- we use %cpu w/o fraction */
426 static int pr_c(char *restrict const outbuf, const proc_t *restrict const pp){
427 unsigned long long total_time; /* jiffies used by this process */
428 unsigned pcpu = 0; /* scaled %cpu, 99 means 99% */
429 unsigned long long seconds; /* seconds of process life */
430 total_time = pp->utime + pp->stime;
431 if(include_dead_children) total_time += (pp->cutime + pp->cstime);
432 seconds = seconds_since_boot - pp->start_time / Hertz;
433 if(seconds) pcpu = (total_time * 100ULL / Hertz) / seconds;
434 if (pcpu > 99U) pcpu = 99U;
435 return snprintf(outbuf, COLWID, "%2u", pcpu);
437 /* normal %CPU in ##.# format. */
438 static int pr_pcpu(char *restrict const outbuf, const proc_t *restrict const pp){
439 unsigned long long total_time; /* jiffies used by this process */
440 unsigned pcpu = 0; /* scaled %cpu, 999 means 99.9% */
441 unsigned long long seconds; /* seconds of process life */
442 total_time = pp->utime + pp->stime;
443 if(include_dead_children) total_time += (pp->cutime + pp->cstime);
444 seconds = seconds_since_boot - pp->start_time / Hertz;
445 if(seconds) pcpu = (total_time * 1000ULL / Hertz) / seconds;
447 return snprintf(outbuf, COLWID, "%u", pcpu/10U);
448 return snprintf(outbuf, COLWID, "%u.%u", pcpu/10U, pcpu%10U);
450 /* this is a "per-mill" format, like %cpu with no decimal point */
451 static int pr_cp(char *restrict const outbuf, const proc_t *restrict const pp){
452 unsigned long long total_time; /* jiffies used by this process */
453 unsigned pcpu = 0; /* scaled %cpu, 999 means 99.9% */
454 unsigned long long seconds; /* seconds of process life */
455 total_time = pp->utime + pp->stime;
456 if(include_dead_children) total_time += (pp->cutime + pp->cstime);
457 seconds = seconds_since_boot - pp->start_time / Hertz ;
458 if(seconds) pcpu = (total_time * 1000ULL / Hertz) / seconds;
459 if (pcpu > 999U) pcpu = 999U;
460 return snprintf(outbuf, COLWID, "%3u", pcpu);
463 static int pr_pgid(char *restrict const outbuf, const proc_t *restrict const pp){
464 return snprintf(outbuf, COLWID, "%u", pp->pgrp);
466 static int pr_pid(char *restrict const outbuf, const proc_t *restrict const pp){
467 return snprintf(outbuf, COLWID, "%u", pp->tgid);
469 static int pr_ppid(char *restrict const outbuf, const proc_t *restrict const pp){
470 return snprintf(outbuf, COLWID, "%u", pp->ppid);
474 /* cumulative CPU time, [dd-]hh:mm:ss format (not same as "etime") */
475 static int pr_time(char *restrict const outbuf, const proc_t *restrict const pp){
477 unsigned dd,hh,mm,ss;
479 t = (pp->utime + pp->stime) / Hertz;
487 c =( dd ? snprintf(outbuf, COLWID, "%u-", dd) : 0 );
488 c +=( snprintf(outbuf+c, COLWID, "%02u:%02u:%02u", hh, mm, ss) );
492 /* HP-UX puts this (I forget, vsz or vsize?) in kB and uses "sz" for pages.
493 * Unix98 requires "vsz" to be kB.
494 * Tru64 does both vsize and vsz like "1.23M"
496 * Our pp->vm_size is kB and our pp->vsize is pages.
498 * TODO: add flag for "1.23M" behavior, on this and other columns.
500 static int pr_vsz(char *restrict const outbuf, const proc_t *restrict const pp){
501 return snprintf(outbuf, COLWID, "%lu", pp->vm_size);
504 //////////////////////////////////////////////////////////////////////////////////////
506 // "PRI" is created by "opri", or by "pri" when -c is used.
508 // Unix98 only specifies that a high "PRI" is low priority.
509 // Sun and SCO add the -c behavior. Sun defines "pri" and "opri".
510 // Linux may use "priority" for historical purposes.
512 // According to the kernel's fs/proc/array.c and kernel/sched.c source,
513 // the kernel reports it in /proc via this:
514 // p->prio - MAX_RT_PRIO
515 // such that "RT tasks are offset by -200. Normal tasks are centered
516 // around 0, value goes from -16 to +15" but who knows if that is
517 // before or after the conversion...
519 // <linux/sched.h> says:
520 // MAX_RT_PRIO is currently 100. (so we see 0 in /proc)
521 // RT tasks have a p->prio of 0 to 99. (so we see -100 to -1)
522 // non-RT tasks are from 100 to 139. (so we see 0 to 39)
523 // Lower values have higher priority, as in the UNIX standard.
525 // In any case, pp->priority+100 should get us back to what the kernel
528 // Test results with the "yes" program on a 2.6.x kernel:
530 // # ps -C19,_20 -o pri,opri,intpri,priority,ni,pcpu,pid,comm
531 // PRI PRI PRI PRI NI %CPU PID COMMAND
532 // 0 99 99 39 19 10.6 8686 19
533 // 34 65 65 5 -20 94.7 8687 _20
535 // Grrr. So the UNIX standard "PRI" must NOT be from "pri".
536 // Either of the others will do. We use "opri" for this.
537 // (and use "pri" when the "-c" option is used)
538 // Probably we should have Linux-specific "pri_for_l" and "pri_for_lc"
540 // sched_get_priority_min.2 says the Linux static priority is
541 // 1..99 for RT and 0 for other... maybe 100 is kernel-only?
543 // A nice range would be -99..0 for RT and 1..40 for normal,
544 // which is pp->priority+1. (3-digit max, positive is normal,
545 // negative or 0 is RT, and meets the standard for PRI)
548 // legal as UNIX "PRI"
549 // "priority" (was -20..20, now -100..39)
550 static int pr_priority(char *restrict const outbuf, const proc_t *restrict const pp){ /* -20..20 */
551 return snprintf(outbuf, COLWID, "%ld", pp->priority);
554 // legal as UNIX "PRI"
555 // "intpri" and "opri" (was 39..79, now -40..99)
556 static int pr_opri(char *restrict const outbuf, const proc_t *restrict const pp){ /* 39..79 */
557 return snprintf(outbuf, COLWID, "%ld", 60 + pp->priority);
560 // legal as UNIX "PRI"
561 // "pri_foo" -- match up w/ nice values of sleeping processes (-120..19)
562 static int pr_pri_foo(char *restrict const outbuf, const proc_t *restrict const pp){
563 return snprintf(outbuf, COLWID, "%ld", pp->priority - 20);
566 // legal as UNIX "PRI"
567 // "pri_bar" -- makes RT pri show as negative (-99..40)
568 static int pr_pri_bar(char *restrict const outbuf, const proc_t *restrict const pp){
569 return snprintf(outbuf, COLWID, "%ld", pp->priority + 1);
572 // legal as UNIX "PRI"
573 // "pri_baz" -- the kernel's ->prio value, as of Linux 2.6.8 (1..140)
574 static int pr_pri_baz(char *restrict const outbuf, const proc_t *restrict const pp){
575 return snprintf(outbuf, COLWID, "%ld", pp->priority + 100);
579 // not legal as UNIX "PRI"
580 // "pri" (was 20..60, now 0..139)
581 static int pr_pri(char *restrict const outbuf, const proc_t *restrict const pp){ /* 20..60 */
582 return snprintf(outbuf, COLWID, "%ld", 39 - pp->priority);
585 // not legal as UNIX "PRI"
586 // "pri_api" -- match up w/ RT API (-40..99)
587 static int pr_pri_api(char *restrict const outbuf, const proc_t *restrict const pp){
588 return snprintf(outbuf, COLWID, "%ld", -1 - pp->priority);
591 static int pr_nice(char *restrict const outbuf, const proc_t *restrict const pp){
592 if(pp->sched!=0 && pp->sched!=-1) return snprintf(outbuf, COLWID, "-");
593 return snprintf(outbuf, COLWID, "%ld", pp->nice);
596 // HP-UX "cls": RT RR RR2 ???? HPUX FIFO KERN
597 // Solaris "class": SYS TS FX IA RT FSS (FIFO is RR w/ Inf quant)
598 // FIFO+RR share RT; FIFO has Inf quant
599 // IA=interactive; FX=fixed; TS=timeshare; SYS=system
600 // FSS=fairshare; INTS=interrupts
601 // Tru64 "policy": FF RR TS
602 // IRIX "class": RT TS B BC WL GN
603 // RT=real-time; TS=time-share; B=batch; BC=batch-critical
604 // WL=weightless; GN=gang-scheduled
605 // see miser(1) for this; PRI has some letter codes too
606 static int pr_class(char *restrict const outbuf, const proc_t *restrict const pp){
608 case -1: return snprintf(outbuf, COLWID, "-"); // not reported
609 case 0: return snprintf(outbuf, COLWID, "TS"); // SCHED_OTHER SCHED_NORMAL
610 case 1: return snprintf(outbuf, COLWID, "FF"); // SCHED_FIFO
611 case 2: return snprintf(outbuf, COLWID, "RR"); // SCHED_RR
612 case 3: return snprintf(outbuf, COLWID, "B"); // SCHED_BATCH
613 case 4: return snprintf(outbuf, COLWID, "ISO"); // reserved for SCHED_ISO (Con Kolivas)
614 case 5: return snprintf(outbuf, COLWID, "IDL"); // SCHED_IDLE
615 case 6: return snprintf(outbuf, COLWID, "#6"); //
616 case 7: return snprintf(outbuf, COLWID, "#7"); //
617 case 8: return snprintf(outbuf, COLWID, "#8"); //
618 case 9: return snprintf(outbuf, COLWID, "#9"); //
619 default: return snprintf(outbuf, COLWID, "?"); // unknown value
622 // Based on "type", FreeBSD would do:
623 // REALTIME "real:%u", prio
625 // IDLE "idle:%u", prio
626 // default "%u:%u", type, prio
627 // We just print the priority, and have other keywords for type.
628 static int pr_rtprio(char *restrict const outbuf, const proc_t *restrict const pp){
629 if(pp->sched==0 || pp->sched==-1) return snprintf(outbuf, COLWID, "-");
630 return snprintf(outbuf, COLWID, "%ld", pp->rtprio);
632 static int pr_sched(char *restrict const outbuf, const proc_t *restrict const pp){
633 if(pp->sched==-1) return snprintf(outbuf, COLWID, "-");
634 return snprintf(outbuf, COLWID, "%ld", pp->sched);
637 ////////////////////////////////////////////////////////////////////////////////
639 static int pr_wchan(char *restrict const outbuf, const proc_t *restrict const pp){
641 * Unix98 says "blank if running" and also "no blanks"! :-(
642 * Unix98 also says to use '-' if something is meaningless.
643 * Digital uses both '*' and '-', with undocumented differences.
644 * (the '*' for -1 (rare) and the '-' for 0)
645 * Sun claims to use a blank AND use '-', in the same man page.
646 * Perhaps "blank" should mean '-'.
648 * AIX uses '-' for running processes, the location when there is
649 * only one thread waiting in the kernel, and '*' when there is
650 * more than one thread waiting in the kernel.
652 * The output should be truncated to maximal columns width -- overflow
653 * is not supported for the "wchan".
657 if(!(pp->wchan & 0xffffff)) return memcpy(outbuf,"-",2),1;
658 if(wchan_is_number) return snprintf(outbuf, COLWID, "%x", (unsigned)(pp->wchan) & 0xffffffu);
659 w = lookup_wchan(pp->wchan, pp->XXXID);
661 if(len>max_rightward) len=max_rightward;
662 memcpy(outbuf, w, len);
667 static int pr_wname(char *restrict const outbuf, const proc_t *restrict const pp){
668 /* SGI's IRIX always uses a number for "wchan", so "wname" is provided too.
670 * We use '-' for running processes, the location when there is
671 * only one thread waiting in the kernel, and '*' when there is
672 * more than one thread waiting in the kernel.
674 * The output should be truncated to maximal columns width -- overflow
675 * is not supported for the "wchan".
679 if(!(pp->wchan & 0xffffff)) return memcpy(outbuf,"-",2),1;
680 w = lookup_wchan(pp->wchan, pp->XXXID);
682 if(len>max_rightward) len=max_rightward;
683 memcpy(outbuf, w, len);
688 static int pr_nwchan(char *restrict const outbuf, const proc_t *restrict const pp){
689 if(!(pp->wchan & 0xffffff)) return memcpy(outbuf,"-",2),1;
690 return snprintf(outbuf, COLWID, "%x", (unsigned)(pp->wchan) & 0xffffffu);
693 /* Terrible trunctuation, like BSD crap uses: I999 J999 K999 */
694 /* FIXME: disambiguate /dev/tty69 and /dev/pts/69. */
695 static int pr_tty4(char *restrict const outbuf, const proc_t *restrict const pp){
696 /* snprintf(outbuf, COLWID, "%02x:%02x", pp->tty>>8, pp->tty&0xff); */
697 return dev_to_tty(outbuf, 4, pp->tty, pp->XXXID, ABBREV_DEV|ABBREV_TTY|ABBREV_PTS);
700 /* Unix98: format is unspecified, but must match that used by who(1). */
701 static int pr_tty8(char *restrict const outbuf, const proc_t *restrict const pp){
702 /* snprintf(outbuf, COLWID, "%02x:%02x", pp->tty>>8, pp->tty&0xff); */
703 return dev_to_tty(outbuf, COLWID, pp->tty, pp->XXXID, ABBREV_DEV);
707 /* This BSD state display may contain spaces, which is illegal. */
708 static int pr_oldstate(char *restrict const outbuf, const proc_t *restrict const pp){
709 return snprintf(outbuf, COLWID, "%s", status(pp));
713 // This state display is Unix98 compliant and has lots of info like BSD.
714 static int pr_stat(char *restrict const outbuf, const proc_t *restrict const pp){
716 outbuf[end++] = pp->state;
717 // if(pp->rss==0 && pp->state!='Z') outbuf[end++] = 'W'; // useless "swapped out"
718 if(pp->nice < 0) outbuf[end++] = '<';
719 if(pp->nice > 0) outbuf[end++] = 'N';
720 // In this order, NetBSD would add:
723 // exiting 'E' (not printed for zombies)
725 // system 'K' (and do not print 'L' too)
726 if(pp->vm_lock) outbuf[end++] = 'L';
727 if(pp->session == pp->tgid) outbuf[end++] = 's'; // session leader
728 if(pp->nlwp > 1) outbuf[end++] = 'l'; // multi-threaded
729 if(pp->pgrp == pp->tpgid) outbuf[end++] = '+'; // in foreground process group
734 /* This minimal state display is Unix98 compliant, like SCO and SunOS 5 */
735 static int pr_s(char *restrict const outbuf, const proc_t *restrict const pp){
736 outbuf[0] = pp->state;
741 static int pr_flag(char *restrict const outbuf, const proc_t *restrict const pp){
742 /* Unix98 requires octal flags */
743 /* this user-hostile and volatile junk gets 1 character */
744 return snprintf(outbuf, COLWID, "%o", (unsigned)(pp->flags>>6U)&0x7U);
747 // plus these: euid,ruid,egroup,rgroup (elsewhere in this file)
749 /*********** non-standard ***********/
753 (SCO has:Process session leader ID as a decimal value. (SESSION))
754 jobc job control count
755 cpu short-term cpu usage factor (for scheduling)
756 sl sleep time (in seconds; 127 = infinity)
757 re core residency time (in seconds; 127 = infinity)
758 pagein pageins (same as majflt)
759 lim soft memory limit
760 tsiz text size (in Kbytes)
763 static int pr_stackp(char *restrict const outbuf, const proc_t *restrict const pp){
764 return snprintf(outbuf, COLWID, "%08x", (unsigned)(pp->start_stack));
767 static int pr_esp(char *restrict const outbuf, const proc_t *restrict const pp){
768 return snprintf(outbuf, COLWID, "%08x", (unsigned)(pp->kstk_esp));
771 static int pr_eip(char *restrict const outbuf, const proc_t *restrict const pp){
772 return snprintf(outbuf, COLWID, "%08x", (unsigned)(pp->kstk_eip));
775 /* This function helps print old-style time formats */
776 static int old_time_helper(char *dst, unsigned long long t, unsigned long long rel) {
777 if(!t) return snprintf(dst, COLWID, " -");
778 if(t == ~0ULL) return snprintf(dst, COLWID, " xx");
779 if((long long)(t-=rel) < 0) t=0ULL;
780 if(t>9999ULL) return snprintf(dst, COLWID, "%5Lu", t/100ULL);
781 else return snprintf(dst, COLWID, "%2u.%02u", (unsigned)t/100U, (unsigned)t%100U);
784 static int pr_bsdtime(char *restrict const outbuf, const proc_t *restrict const pp){
785 unsigned long long t;
787 t = pp->utime + pp->stime;
788 if(include_dead_children) t += (pp->cutime + pp->cstime);
790 return snprintf(outbuf, COLWID, "%3u:%02u", u/60U, u%60U);
793 static int pr_bsdstart(char *restrict const outbuf, const proc_t *restrict const pp){
796 start = time_of_boot + pp->start_time / Hertz;
797 seconds_ago = seconds_since_1970 - start;
798 if(seconds_ago < 0) seconds_ago=0;
799 if(seconds_ago > 3600*24) strcpy(outbuf, ctime(&start)+4);
800 else strcpy(outbuf, ctime(&start)+10);
805 static int pr_alarm(char *restrict const outbuf, const proc_t *restrict const pp){
806 return old_time_helper(outbuf, pp->alarm, 0ULL);
809 /* HP-UX puts this in pages and uses "vsz" for kB */
810 static int pr_sz(char *restrict const outbuf, const proc_t *restrict const pp){
811 return snprintf(outbuf, COLWID, "%lu", (pp->vm_size)/(page_size/1024));
816 * FIXME: trs,drs,tsiz,dsiz,m_trs,m_drs,vm_exe,vm_data,trss
817 * I suspect some/all of those are broken. They seem to have been
818 * inherited by Linux and AIX from early BSD systems. FreeBSD only
819 * retains tsiz. The prefixed versions come from Debian.
820 * Sun and Digital have none of this crap. The code here comes
821 * from an old Linux ps, and might not be correct for ELF executables.
823 * AIX TRS size of resident-set (real memory) of text
824 * AIX TSIZ size of text (shared-program) image
825 * FreeBSD tsiz text size (in Kbytes)
826 * 4.3BSD NET/2 trss text resident set size (in Kbytes)
827 * 4.3BSD NET/2 tsiz text size (in Kbytes)
830 /* kB data size. See drs, tsiz & trs. */
831 static int pr_dsiz(char *restrict const outbuf, const proc_t *restrict const pp){
833 if(pp->vsize) dsiz += (pp->vsize - pp->end_code + pp->start_code) >> 10;
834 return snprintf(outbuf, COLWID, "%ld", dsiz);
837 /* kB text (code) size. See trs, dsiz & drs. */
838 static int pr_tsiz(char *restrict const outbuf, const proc_t *restrict const pp){
840 if(pp->vsize) tsiz += (pp->end_code - pp->start_code) >> 10;
841 return snprintf(outbuf, COLWID, "%ld", tsiz);
844 /* kB _resident_ data size. See dsiz, tsiz & trs. */
845 static int pr_drs(char *restrict const outbuf, const proc_t *restrict const pp){
847 if(pp->vsize) drs += (pp->vsize - pp->end_code + pp->start_code) >> 10;
848 return snprintf(outbuf, COLWID, "%ld", drs);
851 /* kB text _resident_ (code) size. See tsiz, dsiz & drs. */
852 static int pr_trs(char *restrict const outbuf, const proc_t *restrict const pp){
854 if(pp->vsize) trs += (pp->end_code - pp->start_code) >> 10;
855 return snprintf(outbuf, COLWID, "%ld", trs);
858 /* approximation to: kB of address space that could end up in swap */
859 static int pr_swapable(char *restrict const outbuf, const proc_t *restrict const pp){
860 return snprintf(outbuf, COLWID, "%ld", pp->vm_data + pp->vm_stack);
863 /* nasty old Debian thing */
864 static int pr_size(char *restrict const outbuf, const proc_t *restrict const pp){
865 return snprintf(outbuf, COLWID, "%ld", pp->size);
869 static int pr_minflt(char *restrict const outbuf, const proc_t *restrict const pp){
870 long flt = pp->min_flt;
871 if(include_dead_children) flt += pp->cmin_flt;
872 return snprintf(outbuf, COLWID, "%ld", flt);
875 static int pr_majflt(char *restrict const outbuf, const proc_t *restrict const pp){
876 long flt = pp->maj_flt;
877 if(include_dead_children) flt += pp->cmaj_flt;
878 return snprintf(outbuf, COLWID, "%ld", flt);
881 static int pr_lim(char *restrict const outbuf, const proc_t *restrict const pp){
882 if(pp->rss_rlim == RLIM_INFINITY){
888 return snprintf(outbuf, COLWID, "%5ld", pp->rss_rlim >> 10);
891 /* should print leading tilde ('~') if process is bound to the CPU */
892 static int pr_psr(char *restrict const outbuf, const proc_t *restrict const pp){
893 return snprintf(outbuf, COLWID, "%d", pp->processor);
896 static int pr_rss(char *restrict const outbuf, const proc_t *restrict const pp){
897 return snprintf(outbuf, COLWID, "%lu", pp->vm_rss);
900 /* pp->vm_rss * 1000 would overflow on 32-bit systems with 64 GB memory */
901 static int pr_pmem(char *restrict const outbuf, const proc_t *restrict const pp){
902 unsigned long pmem = 0;
903 pmem = pp->vm_rss * 1000ULL / kb_main_total;
904 if (pmem > 999) pmem = 999;
905 return snprintf(outbuf, COLWID, "%2u.%u", (unsigned)(pmem/10), (unsigned)(pmem%10));
908 static int pr_lstart(char *restrict const outbuf, const proc_t *restrict const pp){
910 t = time_of_boot + pp->start_time / Hertz;
911 return snprintf(outbuf, COLWID, "%24.24s", ctime(&t));
914 /* Unix98 specifies a STIME header for a column that shows the start
915 * time of the process, but does not specify a format or format specifier.
916 * From the general Unix98 rules, we know there must not be any spaces.
917 * Most systems violate that rule, though the Solaris documentation
918 * claims to print the column without spaces. (NOT!)
920 * So this isn't broken, but could be renamed to u98_std_stime,
921 * as long as it still shows as STIME when using the -f option.
923 static int pr_stime(char *restrict const outbuf, const proc_t *restrict const pp){
924 struct tm *proc_time;
930 our_time = localtime(&seconds_since_1970); /* not reentrant */
931 tm_year = our_time->tm_year;
932 tm_yday = our_time->tm_yday;
933 t = time_of_boot + pp->start_time / Hertz;
934 proc_time = localtime(&t); /* not reentrant, this corrupts our_time */
935 fmt = "%H:%M"; /* 03:02 23:59 */
936 if(tm_yday != proc_time->tm_yday) fmt = "%b%d"; /* Jun06 Aug27 */
937 if(tm_year != proc_time->tm_year) fmt = "%Y"; /* 1991 2001 */
938 return strftime(outbuf, 42, fmt, proc_time);
941 static int pr_start(char *restrict const outbuf, const proc_t *restrict const pp){
944 t = time_of_boot + pp->start_time / Hertz;
946 if(str[8]==' ') str[8]='0';
947 if(str[11]==' ') str[11]='0';
948 if((unsigned long)t+60*60*24 > seconds_since_1970)
949 return snprintf(outbuf, COLWID, "%8.8s", str+11);
950 return snprintf(outbuf, COLWID, " %6.6s", str+4);
955 static int help_pr_sig(char *restrict const outbuf, const char *restrict const sig){
959 if(len>8) return snprintf(outbuf, COLWID, "%s", sig);
960 return snprintf(outbuf, COLWID, "00000000%s", sig);
962 if(len-strspn(sig,"0") > 8)
963 return snprintf(outbuf, COLWID, "<%s", sig+len-8);
964 return snprintf(outbuf, COLWID, "%s", sig+len-8);
967 static int help_pr_sig(unsigned long long sig){
968 if(wide_signals) return snprintf(outbuf, COLWID, "%016Lx", sig);
969 if(sig>>32) return snprintf(outbuf, COLWID, "<%08Lx", sig&0xffffffffLL);
970 return snprintf(outbuf, COLWID, "%08Lx", sig&0xffffffffLL);
974 // This one is always thread-specific pending. (from Dragonfly BSD)
975 static int pr_tsig(char *restrict const outbuf, const proc_t *restrict const pp){
976 return help_pr_sig(outbuf, pp->_sigpnd);
978 // This one is (wrongly?) thread-specific when printing thread lines,
979 // but process-pending otherwise.
980 static int pr_sig(char *restrict const outbuf, const proc_t *restrict const pp){
981 return help_pr_sig(outbuf, pp->signal);
983 static int pr_sigmask(char *restrict const outbuf, const proc_t *restrict const pp){
984 return help_pr_sig(outbuf, pp->blocked);
986 static int pr_sigignore(char *restrict const outbuf, const proc_t *restrict const pp){
987 return help_pr_sig(outbuf, pp->sigignore);
989 static int pr_sigcatch(char *restrict const outbuf, const proc_t *restrict const pp){
990 return help_pr_sig(outbuf, pp->sigcatch);
994 ////////////////////////////////////////////////////////////////////////////////
997 * internal terms: ruid euid suid fuid
998 * kernel vars: uid euid suid fsuid
999 * command args: ruid uid svuid n/a
1002 static int pr_egid(char *restrict const outbuf, const proc_t *restrict const pp){
1003 return snprintf(outbuf, COLWID, "%d", pp->egid);
1005 static int pr_rgid(char *restrict const outbuf, const proc_t *restrict const pp){
1006 return snprintf(outbuf, COLWID, "%d", pp->rgid);
1008 static int pr_sgid(char *restrict const outbuf, const proc_t *restrict const pp){
1009 return snprintf(outbuf, COLWID, "%d", pp->sgid);
1011 static int pr_fgid(char *restrict const outbuf, const proc_t *restrict const pp){
1012 return snprintf(outbuf, COLWID, "%d", pp->fgid);
1015 static int pr_euid(char *restrict const outbuf, const proc_t *restrict const pp){
1016 return snprintf(outbuf, COLWID, "%d", pp->euid);
1018 static int pr_ruid(char *restrict const outbuf, const proc_t *restrict const pp){
1019 return snprintf(outbuf, COLWID, "%d", pp->ruid);
1021 static int pr_suid(char *restrict const outbuf, const proc_t *restrict const pp){
1022 return snprintf(outbuf, COLWID, "%d", pp->suid);
1024 static int pr_fuid(char *restrict const outbuf, const proc_t *restrict const pp){
1025 return snprintf(outbuf, COLWID, "%d", pp->fuid);
1028 // The Open Group Base Specifications Issue 6 (IEEE Std 1003.1, 2004 Edition)
1029 // requires that user and group names print as decimal numbers if there is
1030 // not enough room in the column, so tough luck if you don't like it.
1032 // The UNIX and POSIX way to change column width is to rename it:
1033 // ps -o pid,user=CumbersomeUserNames -o comm
1034 // The easy way is to directly specify the desired width:
1035 // ps -o pid,user:19,comm
1037 static int do_pr_name(char *restrict const outbuf, const char *restrict const name, unsigned u){
1038 if(!user_is_number){
1039 int rightward = OUTBUF_SIZE; /* max cells */
1040 int len; /* real cells */
1042 escape_str(outbuf, name, OUTBUF_SIZE, &rightward);
1043 len = OUTBUF_SIZE-rightward;
1045 if(len <= (int)max_rightward)
1046 return len; /* returns number of cells */
1048 return snprintf(outbuf, COLWID, "%u", u);
1051 static int pr_ruser(char *restrict const outbuf, const proc_t *restrict const pp){
1052 return do_pr_name(outbuf, pp->ruser, pp->ruid);
1054 static int pr_euser(char *restrict const outbuf, const proc_t *restrict const pp){
1055 return do_pr_name(outbuf, pp->euser, pp->euid);
1057 static int pr_fuser(char *restrict const outbuf, const proc_t *restrict const pp){
1058 return do_pr_name(outbuf, pp->fuser, pp->fuid);
1060 static int pr_suser(char *restrict const outbuf, const proc_t *restrict const pp){
1061 return do_pr_name(outbuf, pp->suser, pp->suid);
1064 static int pr_egroup(char *restrict const outbuf, const proc_t *restrict const pp){
1065 return do_pr_name(outbuf, pp->egroup, pp->egid);
1067 static int pr_rgroup(char *restrict const outbuf, const proc_t *restrict const pp){
1068 return do_pr_name(outbuf, pp->rgroup, pp->rgid);
1070 static int pr_fgroup(char *restrict const outbuf, const proc_t *restrict const pp){
1071 return do_pr_name(outbuf, pp->fgroup, pp->fgid);
1073 static int pr_sgroup(char *restrict const outbuf, const proc_t *restrict const pp){
1074 return do_pr_name(outbuf, pp->sgroup, pp->sgid);
1077 //////////////////////////////////////////////////////////////////////////////////
1079 // TID tid LWP lwp SPID spid
1080 static int pr_thread(char *restrict const outbuf, const proc_t *restrict const pp){
1081 return snprintf(outbuf, COLWID, "%u", pp->tid);
1084 static int pr_nlwp(char *restrict const outbuf, const proc_t *restrict const pp){
1085 return snprintf(outbuf, COLWID, "%d", pp->nlwp);
1088 static int pr_sess(char *restrict const outbuf, const proc_t *restrict const pp){
1089 return snprintf(outbuf, COLWID, "%u", pp->session);
1091 static int pr_tpgid(char *restrict const outbuf, const proc_t *restrict const pp){
1092 return snprintf(outbuf, COLWID, "%d", pp->tpgid);
1096 /* SGI uses "cpu" to print the processor ID with header "P" */
1097 static int pr_sgi_p(char *restrict const outbuf, const proc_t *restrict const pp){ /* FIXME */
1098 if(pp->state == 'R') return snprintf(outbuf, COLWID, "%d", pp->processor);
1099 return snprintf(outbuf, COLWID, "*");
1103 /****************** FLASK & seLinux security stuff **********************/
1104 // move the bulk of this to libproc sometime
1106 static int pr_context(char *restrict const outbuf, const proc_t *restrict const pp){
1112 // wchan file is suitable for testing
1113 //snprintf(filename, sizeof filename, "/proc/%d/wchan", pp->tgid);
1114 snprintf(filename, sizeof filename, "/proc/%d/attr/current", pp->tgid);
1116 fd = open(filename, O_RDONLY, 0);
1117 if(likely(fd==-1)) goto fail;
1118 num_read = read(fd, outbuf, 666);
1120 if(unlikely(num_read<=0)) goto fail;
1121 outbuf[num_read] = '\0';
1124 while(outbuf[len]>' ' && outbuf[len]<='~') len++;
1135 // This needs more study, considering:
1136 // 1. the static linking option (maybe disable this in that case)
1137 // 2. the -z and -Z option issue
1138 // 3. width of output
1139 static int pr_context(char *restrict const outbuf, const proc_t *restrict const pp){
1140 static int (*ps_getpidcon)(pid_t pid, char **context) = 0;
1141 static int tried_load = 0;
1145 if(!ps_getpidcon && !tried_load){
1146 void *handle = dlopen("libselinux.so.1", RTLD_NOW);
1149 ps_getpidcon = dlsym(handle, "getpidcon");
1155 if(ps_getpidcon && !ps_getpidcon(pp->tgid, &context)){
1156 size_t max_len = OUTBUF_SIZE-1;
1157 len = strlen(context);
1158 if(len > max_len) len = max_len;
1159 memcpy(outbuf, context, len);
1172 ////////////////////////////// Test code /////////////////////////////////
1175 static int pr_t_unlimited(char *restrict const outbuf, const proc_t *restrict const pp){
1176 static const char *const vals[] = {"[123456789-12345] <defunct>","ps","123456789-123456"};
1178 snprintf(outbuf, max_rightward+1, "%s", vals[lines_to_next_header%3u]);
1179 return strlen(outbuf);
1181 static int pr_t_unlimited2(char *restrict const outbuf, const proc_t *restrict const pp){
1182 static const char *const vals[] = {"unlimited", "[123456789-12345] <defunct>","ps","123456789-123456"};
1184 snprintf(outbuf, max_rightward+1, "%s", vals[lines_to_next_header%4u]);
1185 return strlen(outbuf);
1189 static int pr_t_right(char *restrict const outbuf, const proc_t *restrict const pp){
1190 static const char *const vals[] = {"999-23:59:59","99-23:59:59","9-23:59:59","59:59"};
1192 return snprintf(outbuf, COLWID, "%s", vals[lines_to_next_header%4u]);
1194 static int pr_t_right2(char *restrict const outbuf, const proc_t *restrict const pp){
1195 static const char *const vals[] = {"999-23:59:59","99-23:59:59","9-23:59:59"};
1197 return snprintf(outbuf, COLWID, "%s", vals[lines_to_next_header%3u]);
1201 static int pr_t_left(char *restrict const outbuf, const proc_t *restrict const pp){
1202 static const char *const vals[] = {"tty7","pts/9999","iseries/vtty42","ttySMX0","3270/tty4"};
1204 return snprintf(outbuf, COLWID, "%s", vals[lines_to_next_header%5u]);
1206 static int pr_t_left2(char *restrict const outbuf, const proc_t *restrict const pp){
1207 static const char *const vals[] = {"tty7","pts/9999","ttySMX0","3270/tty4"};
1209 return snprintf(outbuf, COLWID, "%s", vals[lines_to_next_header%4u]);
1212 /***************************************************************************/
1213 /*************************** other stuff ***********************************/
1216 * Old header specifications.
1218 * short Up " PID TTY STAT TIME COMMAND"
1219 * long l Pp " FLAGS UID PID PPID PRI NI SIZE RSS WCHAN STA TTY TIME COMMAND
1220 * user u up "USER PID %CPU %MEM SIZE RSS TTY STAT START TIME COMMAND
1221 * jobs j gPp " PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
1222 * sig s p " UID PID SIGNAL BLOCKED IGNORED CATCHED STAT TTY TIME COMMAND
1223 * vm v r " PID TTY STAT TIME PAGEIN TSIZ DSIZ RSS LIM %MEM COMMAND
1224 * m m r " PID TTY MAJFLT MINFLT TRS DRS SIZE SWAP RSS SHRD LIB DT COMMAND
1225 * regs X p "NR PID STACK ESP EIP TMOUT ALARM STAT TTY TIME COMMAND
1229 * Unix98 requires that the heading for tty is TT, though XPG4, Digital,
1230 * and BSD use TTY. The Unix98 headers are:
1231 * args,comm,etime,group,nice,pcpu,pgid
1232 * pid,ppid,rgroup,ruser,time,tty,user,vsz
1234 * BSD c: "command" becomes accounting name ("comm" or "ucomm")
1235 * BSD n: "user" becomes "uid" and "wchan" becomes "nwchan" (number)
1238 /* Justification control for flags field. */
1239 #define USER CF_USER // left if text, right if numeric
1240 #define LEFT CF_LEFT
1241 #define RIGHT CF_RIGHT
1242 #define UNLIMITED CF_UNLIMITED
1243 #define WCHAN CF_WCHAN // left if text, right if numeric
1244 #define SIGNAL CF_SIGNAL // right in 9, or 16 if room
1245 #define PIDMAX CF_PIDMAX
1246 #define TO CF_PRINT_THREAD_ONLY
1247 #define PO CF_PRINT_PROCESS_ONLY
1248 #define ET CF_PRINT_EVERY_TIME
1249 #define AN CF_PRINT_AS_NEEDED // no idea
1251 /* short names to save space */
1252 #define MEM PROC_FILLMEM /* read statm */
1253 #define ARG PROC_FILLARG /* read cmdline (cleared if c option) */
1254 #define COM PROC_FILLCOM /* read cmdline (cleared if not -f option) */
1255 #define ENV PROC_FILLENV /* read environ */
1256 #define USR PROC_FILLUSR /* uid_t -> user names */
1257 #define GRP PROC_FILLGRP /* gid_t -> group names */
1258 #define WCH PROC_FILLWCHAN /* do WCHAN lookup */
1262 * pull out annoying BSD aliases into another table (to macro table?)
1263 * add sorting functions here (to unify names)
1266 /* temporary hack -- mark new stuff grabbed from Debian ps */
1269 /* there are about 211 listed */
1271 /* Many of these are placeholders for unsupported options. */
1272 static const format_struct format_array[] = {
1273 /* code header print() sort() width need vendor flags */
1274 {"%cpu", "%CPU", pr_pcpu, sr_pcpu, 4, 0, BSD, ET|RIGHT}, /*pcpu*/
1275 {"%mem", "%MEM", pr_pmem, sr_nop, 4, 0, BSD, PO|RIGHT}, /*pmem*/
1276 {"_left", "LLLLLLLL", pr_t_left, sr_nop, 8, 0, TST, ET|LEFT},
1277 {"_left2", "L2L2L2L2", pr_t_left2, sr_nop, 8, 0, TST, ET|LEFT},
1278 {"_right", "RRRRRRRRRRR", pr_t_right, sr_nop, 11, 0, TST, ET|RIGHT},
1279 {"_right2", "R2R2R2R2R2R", pr_t_right2, sr_nop, 11, 0, TST, ET|RIGHT},
1280 {"_unlimited","U", pr_t_unlimited, sr_nop, 16, 0, TST, ET|UNLIMITED},
1281 {"_unlimited2","U2", pr_t_unlimited2, sr_nop, 16, 0, TST, ET|UNLIMITED},
1282 {"acflag", "ACFLG", pr_nop, sr_nop, 5, 0, XXX, AN|RIGHT}, /*acflg*/
1283 {"acflg", "ACFLG", pr_nop, sr_nop, 5, 0, BSD, AN|RIGHT}, /*acflag*/
1284 {"addr", "ADDR", pr_nop, sr_nop, 4, 0, XXX, AN|RIGHT},
1285 {"addr_1", "ADDR", pr_nop, sr_nop, 1, 0, LNX, AN|LEFT},
1286 {"alarm", "ALARM", pr_alarm, sr_alarm, 5, 0, LNX, AN|RIGHT},
1287 {"argc", "ARGC", pr_nop, sr_nop, 4, 0, LNX, PO|RIGHT},
1288 {"args", "COMMAND", pr_args, sr_cmd, 27, ARG, U98, PO|UNLIMITED}, /*command*/
1289 {"atime", "TIME", pr_time, sr_nop, 8, 0, SOE, ET|RIGHT}, /*cputime*/ /* was 6 wide */
1290 {"blocked", "BLOCKED", pr_sigmask, sr_nop, 9, 0, BSD, TO|SIGNAL}, /*sigmask*/
1291 {"bnd", "BND", pr_nop, sr_nop, 1, 0, AIX, TO|RIGHT},
1292 {"bsdstart", "START", pr_bsdstart, sr_nop, 6, 0, LNX, ET|RIGHT},
1293 {"bsdtime", "TIME", pr_bsdtime, sr_nop, 6, 0, LNX, ET|RIGHT},
1294 {"c", "C", pr_c, sr_pcpu, 2, 0, SUN, ET|RIGHT},
1295 {"caught", "CAUGHT", pr_sigcatch, sr_nop, 9, 0, BSD, TO|SIGNAL}, /*sigcatch*/
1296 {"class", "CLS", pr_class, sr_sched, 3, 0, XXX, TO|LEFT},
1297 {"cls", "CLS", pr_class, sr_sched, 3, 0, HPU, TO|RIGHT}, /*says HPUX or RT*/
1298 {"cmaj_flt", "-", pr_nop, sr_cmaj_flt, 1, 0, LNX, AN|RIGHT},
1299 {"cmd", "CMD", pr_args, sr_cmd, 27, ARG, DEC, PO|UNLIMITED}, /*ucomm*/
1300 {"cmin_flt", "-", pr_nop, sr_cmin_flt, 1, 0, LNX, AN|RIGHT},
1301 {"cnswap", "-", pr_nop, sr_nop, 1, 0, LNX, AN|RIGHT},
1302 {"comm", "COMMAND", pr_comm, sr_cmd, 15, COM, U98, PO|UNLIMITED}, /*ucomm*/
1303 {"command", "COMMAND", pr_args, sr_cmd, 27, ARG, XXX, PO|UNLIMITED}, /*args*/
1304 {"context", "CONTEXT", pr_context, sr_nop, 31, 0, LNX, ET|LEFT},
1305 {"cp", "CP", pr_cp, sr_pcpu, 3, 0, DEC, ET|RIGHT}, /*cpu*/
1306 {"cpu", "CPU", pr_nop, sr_nop, 3, 0, BSD, AN|RIGHT}, /* FIXME ... HP-UX wants this as the CPU number for SMP? */
1307 {"cpuid", "CPUID", pr_psr, sr_nop, 5, 0, BSD, TO|RIGHT}, // OpenBSD: 8 wide!
1308 {"cputime", "TIME", pr_time, sr_nop, 8, 0, DEC, ET|RIGHT}, /*time*/
1309 {"cstime", "-", pr_nop, sr_cstime, 1, 0, LNX, AN|RIGHT},
1310 {"ctid", "CTID", pr_nop, sr_nop, 5, 0, SUN, ET|RIGHT}, // resource contracts?
1311 {"cursig", "CURSIG", pr_nop, sr_nop, 6, 0, DEC, AN|RIGHT},
1312 {"cutime", "-", pr_nop, sr_cutime, 1, 0, LNX, AN|RIGHT},
1313 {"cwd", "CWD", pr_nop, sr_nop, 3, 0, LNX, AN|LEFT},
1314 {"drs", "DRS", pr_drs, sr_drs, 5, MEM, LNX, PO|RIGHT},
1315 {"dsiz", "DSIZ", pr_dsiz, sr_nop, 4, 0, LNX, PO|RIGHT},
1316 {"egid", "EGID", pr_egid, sr_egid, 5, 0, LNX, ET|RIGHT},
1317 {"egroup", "EGROUP", pr_egroup, sr_egroup, 8, GRP, LNX, ET|USER},
1318 {"eip", "EIP", pr_eip, sr_kstk_eip, 8, 0, LNX, TO|RIGHT},
1319 {"emul", "EMUL", pr_nop, sr_nop, 13, 0, BSD, PO|LEFT}, /* "FreeBSD ELF32" and such */
1320 {"end_code", "E_CODE", pr_nop, sr_end_code, 8, 0, LNx, PO|RIGHT},
1321 {"environ","ENVIRONMENT",pr_nop, sr_nop, 11, ENV, LNx, PO|UNLIMITED},
1322 {"esp", "ESP", pr_esp, sr_kstk_esp, 8, 0, LNX, TO|RIGHT},
1323 {"etime", "ELAPSED", pr_etime, sr_nop, 11, 0, U98, ET|RIGHT}, /* was 7 wide */
1324 {"euid", "EUID", pr_euid, sr_euid, 5, 0, LNX, ET|RIGHT},
1325 {"euser", "EUSER", pr_euser, sr_euser, 8, USR, LNX, ET|USER},
1326 {"f", "F", pr_flag, sr_flags, 1, 0, XXX, ET|RIGHT}, /*flags*/
1327 {"fgid", "FGID", pr_fgid, sr_fgid, 5, 0, LNX, ET|RIGHT},
1328 {"fgroup", "FGROUP", pr_fgroup, sr_fgroup, 8, GRP, LNX, ET|USER},
1329 {"flag", "F", pr_flag, sr_flags, 1, 0, DEC, ET|RIGHT},
1330 {"flags", "F", pr_flag, sr_flags, 1, 0, BSD, ET|RIGHT}, /*f*/ /* was FLAGS, 8 wide */
1331 {"fname", "COMMAND", pr_fname, sr_nop, 8, 0, SUN, PO|LEFT},
1332 {"fsgid", "FSGID", pr_fgid, sr_fgid, 5, 0, LNX, ET|RIGHT},
1333 {"fsgroup", "FSGROUP", pr_fgroup, sr_fgroup, 8, GRP, LNX, ET|USER},
1334 {"fsuid", "FSUID", pr_fuid, sr_fuid, 5, 0, LNX, ET|RIGHT},
1335 {"fsuser", "FSUSER", pr_fuser, sr_fuser, 8, USR, LNX, ET|USER},
1336 {"fuid", "FUID", pr_fuid, sr_fuid, 5, 0, LNX, ET|RIGHT},
1337 {"fuser", "FUSER", pr_fuser, sr_fuser, 8, USR, LNX, ET|USER},
1338 {"gid", "GID", pr_egid, sr_egid, 5, 0, SUN, ET|RIGHT},
1339 {"group", "GROUP", pr_egroup, sr_egroup, 8, GRP, U98, ET|USER},
1340 {"ignored", "IGNORED", pr_sigignore,sr_nop, 9, 0, BSD, TO|SIGNAL}, /*sigignore*/
1341 {"inblk", "INBLK", pr_nop, sr_nop, 5, 0, BSD, AN|RIGHT}, /*inblock*/
1342 {"inblock", "INBLK", pr_nop, sr_nop, 5, 0, DEC, AN|RIGHT}, /*inblk*/
1343 {"intpri", "PRI", pr_opri, sr_priority, 3, 0, HPU, TO|RIGHT},
1344 {"jid", "JID", pr_nop, sr_nop, 1, 0, SGI, PO|RIGHT},
1345 {"jobc", "JOBC", pr_nop, sr_nop, 4, 0, XXX, AN|RIGHT},
1346 {"ktrace", "KTRACE", pr_nop, sr_nop, 8, 0, BSD, AN|RIGHT},
1347 {"ktracep", "KTRACEP", pr_nop, sr_nop, 8, 0, BSD, AN|RIGHT},
1348 {"label", "LABEL", pr_context, sr_nop, 31, 0, SGI, ET|LEFT},
1349 {"lastcpu", "C", pr_psr, sr_nop, 3, 0, BSD, TO|RIGHT}, // DragonFly
1350 {"lim", "LIM", pr_lim, sr_rss_rlim, 5, 0, BSD, AN|RIGHT},
1351 {"login", "LOGNAME", pr_nop, sr_nop, 8, 0, BSD, AN|LEFT}, /*logname*/ /* double check */
1352 {"logname", "LOGNAME", pr_nop, sr_nop, 8, 0, XXX, AN|LEFT}, /*login*/
1353 {"longtname", "TTY", pr_tty8, sr_tty, 8, 0, DEC, PO|LEFT},
1354 {"lstart", "STARTED", pr_lstart, sr_nop, 24, 0, XXX, ET|RIGHT},
1355 {"luid", "LUID", pr_nop, sr_nop, 5, 0, LNX, ET|RIGHT}, /* login ID */
1356 {"luser", "LUSER", pr_nop, sr_nop, 8, USR, LNX, ET|USER}, /* login USER */
1357 {"lwp", "LWP", pr_thread, sr_tid, 5, 0, SUN, TO|PIDMAX|RIGHT},
1358 {"m_drs", "DRS", pr_drs, sr_drs, 5, MEM, LNx, PO|RIGHT},
1359 {"m_dt", "DT", pr_nop, sr_dt, 4, MEM, LNx, PO|RIGHT},
1360 {"m_lrs", "LRS", pr_nop, sr_lrs, 5, MEM, LNx, PO|RIGHT},
1361 {"m_resident", "RES", pr_nop, sr_resident, 5,MEM, LNx, PO|RIGHT},
1362 {"m_share", "SHRD", pr_nop, sr_share, 5, MEM, LNx, PO|RIGHT},
1363 {"m_size", "SIZE", pr_size, sr_size, 5, MEM, LNX, PO|RIGHT},
1364 {"m_swap", "SWAP", pr_nop, sr_nop, 5, 0, LNx, PO|RIGHT},
1365 {"m_trs", "TRS", pr_trs, sr_trs, 5, MEM, LNx, PO|RIGHT},
1366 {"maj_flt", "MAJFL", pr_majflt, sr_maj_flt, 6, 0, LNX, AN|RIGHT},
1367 {"majflt", "MAJFLT", pr_majflt, sr_maj_flt, 6, 0, XXX, AN|RIGHT},
1368 {"min_flt", "MINFL", pr_minflt, sr_min_flt, 6, 0, LNX, AN|RIGHT},
1369 {"minflt", "MINFLT", pr_minflt, sr_min_flt, 6, 0, XXX, AN|RIGHT},
1370 {"msgrcv", "MSGRCV", pr_nop, sr_nop, 6, 0, XXX, AN|RIGHT},
1371 {"msgsnd", "MSGSND", pr_nop, sr_nop, 6, 0, XXX, AN|RIGHT},
1372 {"mwchan", "MWCHAN", pr_nop, sr_nop, 6, WCH, BSD, TO|WCHAN}, /* mutex (FreeBSD) */
1373 {"ni", "NI", pr_nice, sr_nice, 3, 0, BSD, TO|RIGHT}, /*nice*/
1374 {"nice", "NI", pr_nice, sr_nice, 3, 0, U98, TO|RIGHT}, /*ni*/
1375 {"nivcsw", "IVCSW", pr_nop, sr_nop, 5, 0, XXX, AN|RIGHT},
1376 {"nlwp", "NLWP", pr_nlwp, sr_nlwp, 4, 0, SUN, PO|RIGHT},
1377 {"nsignals", "NSIGS", pr_nop, sr_nop, 5, 0, DEC, AN|RIGHT}, /*nsigs*/
1378 {"nsigs", "NSIGS", pr_nop, sr_nop, 5, 0, BSD, AN|RIGHT}, /*nsignals*/
1379 {"nswap", "NSWAP", pr_nop, sr_nop, 5, 0, XXX, AN|RIGHT},
1380 {"nvcsw", "VCSW", pr_nop, sr_nop, 5, 0, XXX, AN|RIGHT},
1381 {"nwchan", "WCHAN", pr_nwchan, sr_nop, 6, 0, XXX, TO|RIGHT},
1382 {"opri", "PRI", pr_opri, sr_priority, 3, 0, SUN, TO|RIGHT},
1383 {"osz", "SZ", pr_nop, sr_nop, 2, 0, SUN, PO|RIGHT},
1384 {"oublk", "OUBLK", pr_nop, sr_nop, 5, 0, BSD, AN|RIGHT}, /*oublock*/
1385 {"oublock", "OUBLK", pr_nop, sr_nop, 5, 0, DEC, AN|RIGHT}, /*oublk*/
1386 {"p_ru", "P_RU", pr_nop, sr_nop, 6, 0, BSD, AN|RIGHT},
1387 {"paddr", "PADDR", pr_nop, sr_nop, 6, 0, BSD, AN|RIGHT},
1388 {"pagein", "PAGEIN", pr_majflt, sr_maj_flt, 6, 0, XXX, AN|RIGHT},
1389 {"pcpu", "%CPU", pr_pcpu, sr_pcpu, 4, 0, U98, ET|RIGHT}, /*%cpu*/
1390 {"pending", "PENDING", pr_sig, sr_nop, 9, 0, BSD, ET|SIGNAL}, /*sig*/
1391 {"pgid", "PGID", pr_pgid, sr_pgrp, 5, 0, U98, PO|PIDMAX|RIGHT},
1392 {"pgrp", "PGRP", pr_pgid, sr_pgrp, 5, 0, LNX, PO|PIDMAX|RIGHT},
1393 {"pid", "PID", pr_pid, sr_tgid, 5, 0, U98, PO|PIDMAX|RIGHT},
1394 {"pmem", "%MEM", pr_pmem, sr_nop, 4, 0, XXX, PO|RIGHT}, /*%mem*/
1395 {"poip", "-", pr_nop, sr_nop, 1, 0, BSD, AN|RIGHT},
1396 {"policy", "POL", pr_class, sr_sched, 3, 0, DEC, TO|LEFT},
1397 {"ppid", "PPID", pr_ppid, sr_ppid, 5, 0, U98, PO|PIDMAX|RIGHT},
1398 {"pri", "PRI", pr_pri, sr_nop, 3, 0, XXX, TO|RIGHT},
1399 {"pri_api", "API", pr_pri_api, sr_nop, 3, 0, LNX, TO|RIGHT},
1400 {"pri_bar", "BAR", pr_pri_bar, sr_nop, 3, 0, LNX, TO|RIGHT},
1401 {"pri_baz", "BAZ", pr_pri_baz, sr_nop, 3, 0, LNX, TO|RIGHT},
1402 {"pri_foo", "FOO", pr_pri_foo, sr_nop, 3, 0, LNX, TO|RIGHT},
1403 {"priority", "PRI", pr_priority, sr_priority, 3, 0, LNX, TO|RIGHT},
1404 {"prmgrp", "PRMGRP", pr_nop, sr_nop, 12, 0, HPU, PO|RIGHT},
1405 {"prmid", "PRMID", pr_nop, sr_nop, 12, 0, HPU, PO|RIGHT},
1406 {"project", "PROJECT", pr_nop, sr_nop, 12, 0, SUN, PO|LEFT}, // see prm* andctid
1407 {"projid", "PROJID", pr_nop, sr_nop, 5, 0, SUN, PO|RIGHT},
1408 {"pset", "PSET", pr_nop, sr_nop, 4, 0, DEC, TO|RIGHT},
1409 {"psr", "PSR", pr_psr, sr_nop, 3, 0, DEC, TO|RIGHT},
1410 {"psxpri", "PPR", pr_nop, sr_nop, 3, 0, DEC, TO|RIGHT},
1411 {"re", "RE", pr_nop, sr_nop, 3, 0, BSD, AN|RIGHT},
1412 {"resident", "RES", pr_nop, sr_resident, 5,MEM, LNX, PO|RIGHT},
1413 {"rgid", "RGID", pr_rgid, sr_rgid, 5, 0, XXX, ET|RIGHT},
1414 {"rgroup", "RGROUP", pr_rgroup, sr_rgroup, 8, GRP, U98, ET|USER}, /* was 8 wide */
1415 {"rlink", "RLINK", pr_nop, sr_nop, 8, 0, BSD, AN|RIGHT},
1416 {"rss", "RSS", pr_rss, sr_rss, 5, 0, XXX, PO|RIGHT}, /* was 5 wide */
1417 {"rssize", "RSS", pr_rss, sr_vm_rss, 5, 0, DEC, PO|RIGHT}, /*rsz*/
1418 {"rsz", "RSZ", pr_rss, sr_vm_rss, 5, 0, BSD, PO|RIGHT}, /*rssize*/
1419 {"rtprio", "RTPRIO", pr_rtprio, sr_rtprio, 6, 0, BSD, TO|RIGHT},
1420 {"ruid", "RUID", pr_ruid, sr_ruid, 5, 0, XXX, ET|RIGHT},
1421 {"ruser", "RUSER", pr_ruser, sr_ruser, 8, USR, U98, ET|USER},
1422 {"s", "S", pr_s, sr_state, 1, 0, SUN, TO|LEFT}, /*stat,state*/
1423 {"sched", "SCH", pr_sched, sr_sched, 3, 0, AIX, TO|RIGHT},
1424 {"scnt", "SCNT", pr_nop, sr_nop, 4, 0, DEC, AN|RIGHT}, /* man page misspelling of scount? */
1425 {"scount", "SC", pr_nop, sr_nop, 4, 0, AIX, AN|RIGHT}, /* scnt==scount, DEC claims both */
1426 {"sess", "SESS", pr_sess, sr_session, 5, 0, XXX, PO|PIDMAX|RIGHT},
1427 {"session", "SESS", pr_sess, sr_session, 5, 0, LNX, PO|PIDMAX|RIGHT},
1428 {"sgi_p", "P", pr_sgi_p, sr_nop, 1, 0, LNX, TO|RIGHT}, /* "cpu" number */
1429 {"sgi_rss", "RSS", pr_rss, sr_nop, 4, 0, LNX, PO|LEFT}, /* SZ:RSS */
1430 {"sgid", "SGID", pr_sgid, sr_sgid, 5, 0, LNX, ET|RIGHT},
1431 {"sgroup", "SGROUP", pr_sgroup, sr_sgroup, 8, GRP, LNX, ET|USER},
1432 {"share", "-", pr_nop, sr_share, 1, MEM, LNX, PO|RIGHT},
1433 {"sid", "SID", pr_sess, sr_session, 5, 0, XXX, PO|PIDMAX|RIGHT}, /* Sun & HP */
1434 {"sig", "PENDING", pr_sig, sr_nop, 9, 0, XXX, ET|SIGNAL}, /*pending -- Dragonfly uses this for whole-proc and "tsig" for thread */
1435 {"sig_block", "BLOCKED", pr_sigmask, sr_nop, 9, 0, LNX, TO|SIGNAL},
1436 {"sig_catch", "CATCHED", pr_sigcatch, sr_nop, 9, 0, LNX, TO|SIGNAL},
1437 {"sig_ignore", "IGNORED",pr_sigignore, sr_nop, 9, 0, LNX, TO|SIGNAL},
1438 {"sig_pend", "SIGNAL", pr_sig, sr_nop, 9, 0, LNX, ET|SIGNAL},
1439 {"sigcatch", "CAUGHT", pr_sigcatch, sr_nop, 9, 0, XXX, TO|SIGNAL}, /*caught*/
1440 {"sigignore", "IGNORED", pr_sigignore,sr_nop, 9, 0, XXX, TO|SIGNAL}, /*ignored*/
1441 {"sigmask", "BLOCKED", pr_sigmask, sr_nop, 9, 0, XXX, TO|SIGNAL}, /*blocked*/
1442 {"size", "SZ", pr_swapable, sr_swapable, 5, 0, SCO, PO|RIGHT},
1443 {"sl", "SL", pr_nop, sr_nop, 3, 0, XXX, AN|RIGHT},
1444 {"spid", "SPID", pr_thread, sr_tid, 5, 0, SGI, TO|PIDMAX|RIGHT},
1445 {"stackp", "STACKP", pr_stackp, sr_start_stack, 8, 0, LNX, PO|RIGHT}, /*start_stack*/
1446 {"start", "STARTED", pr_start, sr_nop, 8, 0, XXX, ET|RIGHT},
1447 {"start_code", "S_CODE", pr_nop, sr_start_code, 8, 0, LNx, PO|RIGHT},
1448 {"start_stack", "STACKP", pr_stackp, sr_start_stack, 8, 0, LNX, PO|RIGHT}, /*stackp*/
1449 {"start_time", "START", pr_stime, sr_start_time, 5, 0, LNx, ET|RIGHT},
1450 {"stat", "STAT", pr_stat, sr_state, 4, 0, BSD, TO|LEFT}, /*state,s*/
1451 {"state", "S", pr_s, sr_state, 1, 0, XXX, TO|LEFT}, /*stat,s*/ /* was STAT */
1452 {"status", "STATUS", pr_nop, sr_nop, 6, 0, DEC, AN|RIGHT},
1453 {"stime", "STIME", pr_stime, sr_stime, 5, 0, XXX, ET|RIGHT}, /* was 6 wide */
1454 {"suid", "SUID", pr_suid, sr_suid, 5, 0, LNx, ET|RIGHT},
1455 {"suser", "SUSER", pr_suser, sr_suser, 8, USR, LNx, ET|USER},
1456 {"svgid", "SVGID", pr_sgid, sr_sgid, 5, 0, XXX, ET|RIGHT},
1457 {"svgroup", "SVGROUP", pr_sgroup, sr_sgroup, 8, GRP, LNX, ET|USER},
1458 {"svuid", "SVUID", pr_suid, sr_suid, 5, 0, XXX, ET|RIGHT},
1459 {"svuser", "SVUSER", pr_suser, sr_suser, 8, USR, LNX, ET|USER},
1460 {"systime", "SYSTEM", pr_nop, sr_nop, 6, 0, DEC, ET|RIGHT},
1461 {"sz", "SZ", pr_sz, sr_nop, 5, 0, HPU, PO|RIGHT},
1462 {"taskid", "TASKID", pr_nop, sr_nop, 5, 0, SUN, TO|PIDMAX|RIGHT}, // is this a thread ID?
1463 {"tdev", "TDEV", pr_nop, sr_nop, 4, 0, XXX, AN|RIGHT},
1464 {"thcount", "THCNT", pr_nlwp, sr_nlwp, 5, 0, AIX, PO|RIGHT},
1465 {"tid", "TID", pr_thread, sr_tid, 5, 0, AIX, TO|PIDMAX|RIGHT},
1466 {"time", "TIME", pr_time, sr_nop, 8, 0, U98, ET|RIGHT}, /*cputime*/ /* was 6 wide */
1467 {"timeout", "TMOUT", pr_nop, sr_nop, 5, 0, LNX, AN|RIGHT}, // 2.0.xx era
1468 {"tmout", "TMOUT", pr_nop, sr_nop, 5, 0, LNX, AN|RIGHT}, // 2.0.xx era
1469 {"tname", "TTY", pr_tty8, sr_tty, 8, 0, DEC, PO|LEFT},
1470 {"tpgid", "TPGID", pr_tpgid, sr_tpgid, 5, 0, XXX, PO|PIDMAX|RIGHT},
1471 {"trs", "TRS", pr_trs, sr_trs, 4, MEM, AIX, PO|RIGHT},
1472 {"trss", "TRSS", pr_trs, sr_trs, 4, MEM, BSD, PO|RIGHT}, /* 4.3BSD NET/2 */
1473 {"tsess", "TSESS", pr_nop, sr_nop, 5, 0, BSD, PO|PIDMAX|RIGHT},
1474 {"tsession", "TSESS", pr_nop, sr_nop, 5, 0, DEC, PO|PIDMAX|RIGHT},
1475 {"tsid", "TSID", pr_nop, sr_nop, 5, 0, BSD, PO|PIDMAX|RIGHT},
1476 {"tsig", "PENDING", pr_tsig, sr_nop, 9, 0, BSD, ET|SIGNAL}, /* Dragonfly used this for thread-specific, and "sig" for whole-proc */
1477 {"tsiz", "TSIZ", pr_tsiz, sr_nop, 4, 0, BSD, PO|RIGHT},
1478 {"tt", "TT", pr_tty8, sr_tty, 8, 0, BSD, PO|LEFT},
1479 {"tty", "TT", pr_tty8, sr_tty, 8, 0, U98, PO|LEFT}, /* Unix98 requires "TT" but has "TTY" too. :-( */ /* was 3 wide */
1480 {"tty4", "TTY", pr_tty4, sr_tty, 4, 0, LNX, PO|LEFT},
1481 {"tty8", "TTY", pr_tty8, sr_tty, 8, 0, LNX, PO|LEFT},
1482 {"u_procp", "UPROCP", pr_nop, sr_nop, 6, 0, DEC, AN|RIGHT},
1483 {"ucmd", "CMD", pr_comm, sr_cmd, 15, COM, DEC, PO|UNLIMITED}, /*ucomm*/
1484 {"ucomm", "COMMAND", pr_comm, sr_cmd, 15, COM, XXX, PO|UNLIMITED}, /*comm*/
1485 {"uid", "UID", pr_euid, sr_euid, 5, 0, XXX, ET|RIGHT},
1486 {"uid_hack", "UID", pr_euser, sr_euser, 8, USR, XXX, ET|USER},
1487 {"umask", "UMASK", pr_nop, sr_nop, 5, 0, DEC, AN|RIGHT},
1488 {"uname", "USER", pr_euser, sr_euser, 8, USR, DEC, ET|USER}, /* man page misspelling of user? */
1489 {"upr", "UPR", pr_nop, sr_nop, 3, 0, BSD, TO|RIGHT}, /*usrpri*/
1490 {"uprocp", "UPROCP", pr_nop, sr_nop, 8, 0, BSD, AN|RIGHT},
1491 {"user", "USER", pr_euser, sr_euser, 8, USR, U98, ET|USER}, /* BSD n forces this to UID */
1492 {"usertime", "USER", pr_nop, sr_nop, 4, 0, DEC, ET|RIGHT},
1493 {"usrpri", "UPR", pr_nop, sr_nop, 3, 0, DEC, TO|RIGHT}, /*upr*/
1494 {"util", "C", pr_c, sr_pcpu, 2, 0, SGI, ET|RIGHT}, // not sure about "C"
1495 {"utime", "UTIME", pr_nop, sr_utime, 6, 0, LNx, ET|RIGHT},
1496 {"vm_data", "DATA", pr_nop, sr_vm_data, 5, 0, LNx, PO|RIGHT},
1497 {"vm_exe", "EXE", pr_nop, sr_vm_exe, 5, 0, LNx, PO|RIGHT},
1498 {"vm_lib", "LIB", pr_nop, sr_vm_lib, 5, 0, LNx, PO|RIGHT},
1499 {"vm_lock", "LCK", pr_nop, sr_vm_lock, 3, 0, LNx, PO|RIGHT},
1500 {"vm_stack", "STACK", pr_nop, sr_vm_stack, 5, 0, LNx, PO|RIGHT},
1501 {"vsize", "VSZ", pr_vsz, sr_vsize, 6, 0, DEC, PO|RIGHT}, /*vsz*/
1502 {"vsz", "VSZ", pr_vsz, sr_vm_size, 6, 0, U98, PO|RIGHT}, /*vsize*/
1503 {"wchan", "WCHAN", pr_wchan, sr_wchan, 6, WCH, XXX, TO|WCHAN}, /* BSD n forces this to nwchan */ /* was 10 wide */
1504 {"wname", "WCHAN", pr_wname, sr_nop, 6, WCH, SGI, TO|WCHAN}, /* opposite of nwchan */
1505 {"xstat", "XSTAT", pr_nop, sr_nop, 5, 0, BSD, AN|RIGHT},
1506 {"zone", "ZONE", pr_context, sr_nop, 31, 0, SUN, ET|LEFT}, // Solaris zone == Linux context?
1507 {"zoneid", "ZONEID", pr_nop, sr_nop, 31, 0, SUN, ET|RIGHT},// Linux only offers context names
1508 {"~", "-", pr_nop, sr_nop, 1, 0, LNX, AN|RIGHT} /* NULL would ruin alphabetical order */
1523 static const int format_array_count = sizeof(format_array)/sizeof(format_struct);
1526 /****************************** Macro formats *******************************/
1527 /* First X field may be NR, which is p->start_code>>26 printed with %2ld */
1528 /* That seems useless though, and Debian already killed it. */
1529 /* The ones marked "Digital" have the name defined, not just the data. */
1530 static const macro_struct macro_array[] = {
1531 {"DFMT", "pid,tname,state,cputime,cmd"}, /* Digital's default */
1532 {"DefBSD", "pid,tname,stat,bsdtime,args"}, /* Our BSD default */
1533 {"DefSysV", "pid,tname,time,cmd"}, /* Our SysV default */
1534 {"END_BSD", "state,tname,cputime,comm"}, /* trailer for O */
1535 {"END_SYS5", "state,tname,time,command"}, /* trailer for -O */
1536 {"F5FMT", "uname,pid,ppid,c,start,tname,time,cmd"}, /* Digital -f */
1538 {"FB_", "pid,tt,stat,time,command"}, /* FreeBSD default */
1539 {"FB_j", "user,pid,ppid,pgid,sess,jobc,stat,tt,time,command"}, /* FreeBSD j */
1540 {"FB_l", "uid,pid,ppid,cpu,pri,nice,vsz,rss,wchan,stat,tt,time,command"}, /* FreeBSD l */
1541 {"FB_u", "user,pid,pcpu,pmem,vsz,rss,tt,stat,start,time,command"}, /* FreeBSD u */
1542 {"FB_v", "pid,stat,time,sl,re,pagein,vsz,rss,lim,tsiz,pcpu,pmem,command"}, /* FreeBSD v */
1544 {"FD_", "pid,tty,time,comm"}, /* Fictional Debian SysV default */
1545 {"FD_f", "user,pid,ppid,start_time,tty,time,comm"}, /* Fictional Debian -f */
1546 {"FD_fj", "user,pid,ppid,start_time,tty,time,pgid,sid,comm"}, /* Fictional Debian -jf */
1547 {"FD_j", "pid,tty,time,pgid,sid,comm"}, /* Fictional Debian -j */
1548 {"FD_l", "flags,state,uid,pid,ppid,priority,nice,vsz,wchan,tty,time,comm"}, /* Fictional Debian -l */
1549 {"FD_lj", "flags,state,uid,pid,ppid,priority,nice,vsz,wchan,tty,time,pgid,sid,comm"}, /* Fictional Debian -jl */
1551 {"FL5FMT", "f,state,uid,pid,ppid,pcpu,pri,nice,rss,wchan,start,time,command"}, /* Digital -fl */
1553 {"FLASK_context", "pid,context,command"}, /* Flask Linux context, --context */
1555 {"HP_", "pid,tty,time,comm"}, /* HP default */
1556 {"HP_f", "user,pid,ppid,cpu,stime,tty,time,args"}, /* HP -f */
1557 {"HP_fl", "flags,state,user,pid,ppid,cpu,intpri,nice,addr,sz,wchan,stime,tty,time,args"}, /* HP -fl */
1558 {"HP_l", "flags,state,uid,pid,ppid,cpu,intpri,nice,addr,sz,wchan,tty,time,comm"}, /* HP -l */
1560 {"J390", "pid,sid,pgrp,tname,atime,args"}, /* OS/390 -j */
1561 {"JFMT", "user,pid,ppid,pgid,sess,jobc,state,tname,cputime,command"}, /* Digital j and -j */
1562 {"L5FMT", "f,state,uid,pid,ppid,c,pri,nice,addr,sz,wchan,tt,time,ucmd"}, /* Digital -l */
1563 {"LFMT", "uid,pid,ppid,cp,pri,nice,vsz,rss,wchan,state,tname,cputime,command"}, /* Digital l */
1565 {"OL_X", "pid,start_stack,esp,eip,timeout,alarm,stat,tname,bsdtime,args"}, /* Old i386 Linux X */
1566 {"OL_j", "ppid,pid,pgid,sid,tname,tpgid,stat,uid,bsdtime,args"}, /* Old Linux j */
1567 {"OL_l", "flags,uid,pid,ppid,priority,nice,vsz,rss,wchan,stat,tname,bsdtime,args"}, /* Old Linux l */
1568 {"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 */
1569 {"OL_s", "uid,pid,pending,sig_block,sig_ignore,caught,stat,tname,bsdtime,args"}, /* Old Linux s */
1570 {"OL_u", "user,pid,pcpu,pmem,vsz,rss,tname,stat,start_time,bsdtime,args"}, /* Old Linux u */
1571 {"OL_v", "pid,tname,stat,bsdtime,maj_flt,m_trs,m_drs,rss,pmem,args"}, /* Old Linux v */
1573 {"RD_", "pid,tname,state,bsdtime,comm"}, /* Real Debian default */
1574 {"RD_f", "uid,pid,ppid,start_time,tname,bsdtime,args"}, /* Real Debian -f */
1575 {"RD_fj", "uid,pid,ppid,start_time,tname,bsdtime,pgid,sid,args"}, /* Real Debian -jf */
1576 {"RD_j", "pid,tname,state,bsdtime,pgid,sid,comm"}, /* Real Debian -j */
1577 {"RD_l", "flags,state,uid,pid,ppid,priority,nice,wchan,tname,bsdtime,comm"}, /* Real Debian -l */
1578 {"RD_lj", "flags,state,uid,pid,ppid,priority,nice,wchan,tname,bsdtime,pgid,sid,comm"}, /* Real Debian -jl */
1580 {"RUSAGE", "minflt,majflt,nswap,inblock,oublock,msgsnd,msgrcv,nsigs,nvcsw,nivcsw"}, /* Digital -o "RUSAGE" */
1581 {"SCHED", "user,pcpu,pri,usrpri,nice,psxpri,psr,policy,pset"}, /* Digital -o "SCHED" */
1582 {"SFMT", "uid,pid,cursig,sig,sigmask,sigignore,sigcatch,stat,tname,command"}, /* Digital s */
1584 {"Std_f", "uid_hack,pid,ppid,c,stime,tname,time,cmd"}, /* new -f */
1585 {"Std_fl", "f,s,uid_hack,pid,ppid,c,opri,ni,addr,sz,wchan,stime,tname,time,cmd"}, /* -fl */
1586 {"Std_l", "f,s,uid,pid,ppid,c,opri,ni,addr,sz,wchan,tname,time,ucmd"}, /* new -l */
1588 {"THREAD", "user,pcpu,pri,scnt,wchan,usertime,systime"}, /* Digital -o "THREAD" */
1589 {"UFMT", "uname,pid,pcpu,pmem,vsz,rss,tt,state,start,time,command"}, /* Digital u */
1590 {"VFMT", "pid,tt,state,time,sl,pagein,vsz,rss,pcpu,pmem,command"}, /* Digital v */
1591 {"~", "~"} /* NULL would ruin alphabetical order */
1594 static const int macro_array_count = sizeof(macro_array)/sizeof(macro_struct);
1597 /*************************** AIX formats ********************/
1598 /* Convert AIX format codes to normal format specifiers. */
1599 static const aix_struct aix_array[] = {
1600 {'C', "pcpu", "%CPU"},
1601 {'G', "group", "GROUP"},
1602 {'P', "ppid", "PPID"},
1603 {'U', "user", "USER"},
1604 {'a', "args", "COMMAND"},
1605 {'c', "comm", "COMMAND"},
1606 {'g', "rgroup", "RGROUP"},
1607 {'n', "nice", "NI"},
1608 {'p', "pid", "PID"},
1609 {'r', "pgid", "PGID"},
1610 {'t', "etime", "ELAPSED"},
1611 {'u', "ruser", "RUSER"},
1612 {'x', "time", "TIME"},
1613 {'y', "tty", "TTY"},
1614 {'z', "vsz", "VSZ"},
1615 {'~', "~", "~"} /* NULL would ruin alphabetical order */
1617 static const int aix_array_count = sizeof(aix_array)/sizeof(aix_struct);
1620 /********************* sorting ***************************/
1621 /* Convert short sorting codes to normal format specifiers. */
1622 static const shortsort_struct shortsort_array[] = {
1626 /* {'K', "stime" }, */ /* conflict, system vs. start time */
1632 {'T', "start_time" },
1633 {'U', "uid" }, /* euid */
1648 {'y', "priority" }, /* nice */
1649 {'~', "~" } /* NULL would ruin alphabetical order */
1651 static const int shortsort_array_count = sizeof(shortsort_array)/sizeof(shortsort_struct);
1654 /*********** print format_array **********/
1655 /* called by the parser in another file */
1656 void print_format_specifiers(void){
1657 const format_struct *walk = format_array;
1658 while(*(walk->spec) != '~'){
1659 if(walk->pr != pr_nop) printf("%-12.12s %-8.8s\n", walk->spec, walk->head);
1664 /************ comparison functions for bsearch *************/
1666 static int compare_format_structs(const void *a, const void *b){
1667 return strcmp(((const format_struct*)a)->spec,((const format_struct*)b)->spec);
1670 static int compare_macro_structs(const void *a, const void *b){
1671 return strcmp(((const macro_struct*)a)->spec,((const macro_struct*)b)->spec);
1674 /******** look up structs as needed by the sort & format parsers ******/
1676 const shortsort_struct *search_shortsort_array(const int findme){
1677 const shortsort_struct *walk = shortsort_array;
1678 while(walk->desc != '~'){
1679 if(walk->desc == findme) return walk;
1685 const aix_struct *search_aix_array(const int findme){
1686 const aix_struct *walk = aix_array;
1687 while(walk->desc != '~'){
1688 if(walk->desc == findme) return walk;
1694 const format_struct *search_format_array(const char *findme){
1697 return bsearch(&key, format_array, format_array_count,
1698 sizeof(format_struct), compare_format_structs
1702 const macro_struct *search_macro_array(const char *findme){
1705 return bsearch(&key, macro_array, macro_array_count,
1706 sizeof(macro_struct), compare_macro_structs
1710 static unsigned int active_cols; /* some multiple of screen_cols */
1712 /***** Last chance, avoid needless trunctuation. */
1713 static void check_header_width(void){
1714 format_node *walk = format_list;
1715 unsigned int total = 0;
1718 unsigned int sigs = 0;
1720 switch((walk->flags) & CF_JUST_MASK){
1722 total += walk->width;
1723 total += was_normal;
1728 total += walk->width;
1729 total += was_normal;
1732 case CF_UNLIMITED: /* could chop this a bit */
1733 if(walk->next) total += walk->width;
1734 else total += 3; /* not strlen(walk->name) */
1735 total += was_normal;
1739 total += walk->width;
1747 active_cols = screen_cols * i;
1748 if(active_cols>=total) break;
1749 if(screen_cols*i >= OUTBUF_SIZE/2) break; /* can't go over */
1751 wide_signals = (total+sigs*7 <= active_cols);
1755 /********** show one process (NULL proc prints header) **********/
1757 //#define SPACE_AMOUNT page_size
1758 #define SPACE_AMOUNT 144
1760 static char *saved_outbuf;
1762 void show_one_proc(const proc_t *restrict const p, const format_node *restrict fmt){
1763 /* unknown: maybe set correct & actual to 1, remove +/- 1 below */
1764 int correct = 0; /* screen position we should be at */
1765 int actual = 0; /* screen position we are at */
1766 int amount = 0; /* amount of text that this data is */
1767 int leftpad = 0; /* amount of space this column _could_ need */
1768 int space = 0; /* amount of space we actually need to print */
1769 int dospace = 0; /* previous column determined that we need a space */
1770 int legit = 0; /* legitimately stolen extra space */
1771 int sz = 0; /* real size of data in outbuffer */
1773 char *restrict const outbuf = saved_outbuf;
1774 static int did_stuff = 0; /* have we ever printed anything? */
1776 if(unlikely(-1==(long)p)){ /* true only once, at the end */
1777 if(did_stuff) return;
1778 /* have _never_ printed anything, but might need a header */
1779 if(!--lines_to_next_header){
1780 lines_to_next_header = header_gap;
1781 show_one_proc(NULL,fmt);
1783 /* fprintf(stderr, "No processes available.\n"); */ /* legal? */
1786 if(likely(p)){ /* not header, maybe we should call ourselves for it */
1787 if(unlikely(!--lines_to_next_header)){
1788 lines_to_next_header = header_gap;
1789 show_one_proc(NULL,fmt);
1793 if(unlikely(active_cols>(int)OUTBUF_SIZE)) fprintf(stderr,"Fix bigness error.\n");
1795 /* print row start sequence */
1798 /* set width suggestion which might be ignored */
1799 // if(likely(fmt->next)) max_rightward = fmt->width;
1800 // else max_rightward = active_cols-((correct>actual) ? correct : actual);
1802 if(likely(fmt->next)){
1803 max_rightward = fmt->width;
1806 tmpspace = correct-actual;
1809 max_rightward = active_cols-actual-tmpspace;
1811 max_rightward = active_cols - ( (correct>actual) ? correct : actual );
1814 max_leftward = fmt->width + actual - correct; /* TODO check this */
1816 // fprintf(stderr, "cols: %d, max_rightward: %d, max_leftward: %d, actual: %d, correct: %d\n",
1817 // active_cols, max_rightward, max_leftward, actual, correct);
1819 /* prepare data and calculate leftpad */
1820 if(likely(p) && likely(fmt->pr)) amount = (*fmt->pr)(outbuf,p);
1821 else amount = strlen(strcpy(outbuf, fmt->name)); /* AIX or headers */
1823 switch((fmt->flags) & CF_JUST_MASK){
1824 case 0: /* for AIX, assigned outside this file */
1827 case CF_LEFT: /* bad */
1830 case CF_RIGHT: /* OK */
1831 leftpad = fmt->width - amount;
1832 if(leftpad < 0) leftpad = 0;
1835 /* if the screen is wide enough, use full 16-character output */
1837 leftpad = 16 - amount;
1840 leftpad = 9 - amount;
1842 if(leftpad < 0) leftpad = 0;
1844 case CF_USER: /* bad */
1845 leftpad = fmt->width - amount;
1846 if(leftpad < 0) leftpad = 0;
1847 if(!user_is_number) leftpad = 0;
1849 case CF_WCHAN: /* bad */
1850 if(wchan_is_number){
1851 leftpad = fmt->width - amount;
1852 if(leftpad < 0) leftpad = 0;
1855 if ((active_cols-actual-tmpspace)<1)
1856 outbuf[1] = '\0'; /* oops, we (mostly) lose this column... */
1862 if(active_cols-actual-tmpspace < 1)
1863 outbuf[1] = '\0'; /* oops, we (mostly) lose this column... */
1868 fprintf(stderr, "bad alignment code\n");
1873 * correct from previous column
1874 * actual from previous column
1875 * amount not needed (garbage due to chopping)
1876 * leftpad left padding for this column alone (not make-up or gap)
1877 * space not needed (will recalculate now)
1878 * dospace if we require space between this and the prior column
1879 * legit space we were allowed to steal, and thus did steal
1881 space = correct - actual + leftpad;
1882 if(space<1) space=dospace;
1883 if(unlikely(space>SPACE_AMOUNT)) space=SPACE_AMOUNT; // only so much available
1885 /* real size -- don't forget in 'amount' is number of cells */
1886 sz = strlen(outbuf);
1888 /* print data, set x position stuff */
1889 if(unlikely(!fmt->next)){
1890 /* Last column. Write padding + data + newline all together. */
1892 fwrite(outbuf-space, space+sz+1, 1, stdout);
1895 /* Not the last column. Write padding + data together. */
1896 fwrite(outbuf-space, space+sz, 1, stdout);
1897 actual += space+amount;
1898 correct += fmt->width;
1899 correct += legit; /* adjust for SIGNAL expansion */
1900 if(fmt->pr && fmt->next->pr){ /* neither is AIX filler */
1909 * correct screen position we should be at
1910 * actual screen position we are at
1912 * leftpad not needed
1914 * dospace if have determined that we need a space next time
1922 static void sanity_check(void){
1923 format_struct *fs = format_array;
1924 while((fs->spec)[0] != '~'){
1925 if(strlen(fs->head) > fs->width) printf("%d %s\n",strlen(fs->head),fs->spec);
1932 void init_output(void){
1937 case 65536: page_shift = 16; break;
1938 case 32768: page_shift = 15; break;
1939 case 16384: page_shift = 14; break;
1940 case 8192: page_shift = 13; break;
1941 default: fprintf(stderr, "Unknown page size! (assume 4096)\n");
1942 case 4096: page_shift = 12; break;
1943 case 2048: page_shift = 11; break;
1944 case 1024: page_shift = 10; break;
1947 // add page_size-1 to round up
1948 outbuf_pages = (OUTBUF_SIZE+SPACE_AMOUNT+page_size-1)/page_size;
1951 page_size * (outbuf_pages+1), // 1 more, for guard page at high addresses
1952 PROT_READ | PROT_WRITE,
1953 MAP_PRIVATE | MAP_ANONYMOUS,
1957 memset(outbuf, ' ', SPACE_AMOUNT);
1958 if(SPACE_AMOUNT==page_size) mprotect(outbuf, page_size, PROT_READ);
1959 mprotect(outbuf + page_size*outbuf_pages, page_size, PROT_NONE); // gaurd page
1960 saved_outbuf = outbuf + SPACE_AMOUNT;
1961 // available space: page_size*outbuf_pages-SPACE_AMOUNT
1963 seconds_since_1970 = time(NULL);
1964 time_of_boot = seconds_since_1970 - seconds_since_boot;
1968 check_header_width();