/***********************************************************************
* *
* This software is part of the ast package *
* Copyright (c) 1989-2011 AT&T Intellectual Property *
* and is licensed under the *
* Eclipse Public License, Version 1.0 *
* by AT&T Intellectual Property *
* *
* A copy of the License is available at *
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
* *
* Information and Software Systems Research *
* AT&T Research *
* Florham Park NJ *
* *
* Glenn Fowler <gsf@research.att.com> *
* *
***********************************************************************/
#pragma prototyped
/*
* Glenn Fowler
* AT&T Research
*
* ps -- list process status
*
*/
static const char usage[] =
"[-1o?\n@(#)$Id: ps (AT&T Research) 2011-12-13 $\n]"
"[+NAME?ps - report process status]"
"[+DESCRIPTION?\bps\b lists process information subject to the appropriate"
" privilege. If \apid\a arguments are specified then only those"
" processes are listed, otherwise all processes with the effective"
" user id and controlling terminal of the caller are listed. The options"
" may alter this default behavior.]"
"[+?The listings are sorted by <\bUID,START,PID\b>. Options taking list"
" arguments accept either space or comma separators.]"
"[a:interactive?List all processes associated with terminals.]"
"[B!:branch?Print tree branch prefixes for \bcommand\b and \bargs\b."
" Implied by \b--children\b, \b--parents\b, and \b--tree\b.]"
"[C:children?Display the process tree hierarchy, including the children"
" of all selected processes, in the \bCMD\b field list.]"
"[d:no-session?List all processes except session leaders.]"
"[D:define?Define \akey\a with optional \avalue\a. \avalue\a will be expanded"
" when \b%(\b\akey\a\b)\b is specified in \b--format\b. \akey\a may"
" override internal \b--format\b identifiers.]:[key[=value]]]"
"[e|A:all?List all processes.]"
"[E!:escape?Escape non-printing characters in \bcommand\b and \bargs\b.]"
"[F:format?Append to the listing format string (if \b--format\b is specified"
" then \b--fields\b and all options that modify \b--fields\b are"
" ignored.) The \bdf\b(1), \bls\b(1) and \bpax\b(1) commands also have"
" \b--format\b options in this same style. \aformat\a follows"
" \bprintf\b(3) conventions, except that \bsfio\b(3) inline ids are used"
" instead of arguments:"
" %[#-+]][\awidth\a[.\aprecis\a[.\abase\a]]]]]](\aid\a[:\aheading\a]])\achar\a."
" If \b#\b is specified then the internal width and precision are used."
" If \achar\a is \bs\b then the string form of the item is listed,"
" otherwise the corresponding numeric form is listed. If \achar\a is"
" \bq\b then the string form of the item is $'...' quoted if it contains"
" space or non-printing characters. If \awidth\a is omitted then the"
" default width is assumed. \aheading\a overrides the default"
" heading for \aid\a. Supported \aid\as"
" are:]:[format]{\fformats\f}"
"[g:pgrps|process-groups?List processes with group leaders in the \apgrp\a"
" list.]:[pgrp...]"
"[G:groups?List processes with real group id names or numbers in the \agroup\a"
" list.]:[group...]"
"[h!:heading?Output a heading line.]"
"[L:leaders?List session leaders.]"
"[n:namelist?Specifies an alternate system namelist \afile\a. Ignored by"
" this implementation.]"
" format when \b--fields\b is not specified.]"
"[o:fields?(\b--format\b is more general.) List information according to"
" \akey\a. Multiple \b--fields\b options may be specified; the"
" resulting format is a left-right ordered list with duplicate entries"
" deleted from the right. The default width can be overriden by"
" appending \a+width\a to \akey\a, and the default \alabel\a can be"
" overridden by appending \a=label\a to \akey\a. The keys, labels and"
" widths are listed under \b--format\b.]:[key[+width]][=label]]...]"
"[p:pids?List processes in the \apid\a list.]:[pid...]"
"[P:parents?Display the process tree hierarchy, including the parents"
" of all selected processes, in the \bCMD\b field list.]"
"[r|R:recursive?Recursively list the children of all selected processes.]"
"[s:sessions?List processes with session leaders in the \asid\a list.]:[sid...]"
"[t:terminals|ttys?List processes with controlling terminals in the \atty\a"
" list.]:[tty...]"
"[T:tree|forest?Display the process tree hierarchy, including the parents and"
" children of all selected processes, in the \bCMD\b field list.]"
"[u|U:users?List processes with real user id names or numbers in the \auser\a"
" list.]:[user...]"
"[v:verbose?List verbose error messages for inaccessible processes.]"
"[w:wide?Ignored by this implementation.]"
"[x:detached?List all processes not associated with terminals.]"
"[X:hex?List numeric entries in hexadecimal notation.]"
"\n"
"\n[ pid ... ]\n"
"\n"
"[+SEE ALSO?\bdf\b(1), \bkill\b(1), \bls\b(1), \bnice\b(1), \bpax\b(1),"
" \bps\b(1), \bsh\b(1), \btop\b(1)]"
;
#include <ast.h>
#include <ast_dir.h>
#include <cdt.h>
#include <ctype.h>
#include <dirent.h>
#include <error.h>
#include <ls.h>
#include <pss.h>
#include <sfdisc.h>
#include <tm.h>
#if !_mem_st_rdev_stat
#endif
#define KEY_alias 0
{
} Key_t;
{
} List_t;
{
} Ps_t;
{
} State_t;
{
{
0
},
{
"addr",
"ADDR",
"Physical address.",
8
},
{
"class",
"CLS",
"Scheduling class.",
3,
},
{
"cmd",
"CMD",
"Command path with arguments.",
-32, 0,
0,0,0,
},
{
"comm",
"COMMAND",
"Command file base name.",
-24, 0,
0,0,0,
},
{
"cpu",
"%CPU",
"Cpu percent usage.",
4
},
{
"etime",
"ELAPSED",
"Elapsed time since start.",
0,
7
},
{
"flags",
"F",
"State flags (octal and additive).",
3
},
{
"gid",
"GROUP",
"Numeric group id.",
8, 0,
0,0,0,
},
{
"group",
"GROUP",
"Group id name.",
8, 0,
0,0,0,
},
{
"job",
"JOB",
"Job id.",
5, PID_MAX,
1,0,0
},
{
"nice",
"NI",
"Adjusted scheduling priority.",
4
},
{
"npid",
"NPID",
"Native process id.",
5, 0,
1,
},
{
"pgrp",
"PGRP",
"Process group id.",
5, PID_MAX,
1,0,0
},
{
"pid",
"PID",
"Process id.",
5, PID_MAX,
1,0,0
},
{
"ppid",
"PPID",
"Parent process id.",
5, PID_MAX,
1
},
{
"pri",
"PRI",
"Scheduling priority.",
3
},
{
"processor",
"PROC",
"Assigned processor.",
3
},
{
"refcount",
"REFS",
"Reference count.",
4, 0,
1,
},
{
"rss",
"RSS",
"Resident page set size in kilobytes.",
5
},
{
"sid",
"SID",
"Session id.",
5, PID_MAX,
1
},
{
"size",
"SIZE",
"Virtual memory size in kilobytes.",
6
},
{
"start",
"START",
"Start time.",
8
},
{
"state",
"S",
"Basic state.",
1
},
{
"tgrp",
"TGRP",
"Terminal group id.",
5, PID_MAX,
1,
},
{
"time",
"TIME",
"usr+sys time.",
6
},
{
"tty",
"TT",
"Controlling terminal base name.",
-7,
},
{
"uid",
"USER",
"Numeric user id.",
8, 0,
0,0,0,
},
{
"user",
"USER",
"User id name.",
8, 0,
0,0,0,
},
{
"wchan",
"WCHAN",
"Wait address.",
8
},
/* aliases after this point */
{ "args", 0, 0, 0, KEY_cmd },
{ "command", 0, 0, 0, KEY_cmd },
{ "f", 0, 0, 0, KEY_flags },
{ "jid", 0, 0, 0, KEY_job },
{ "ntpid", 0, 0, 0, KEY_npid },
{ "pcpu", 0, 0, 0, KEY_cpu },
{ "pgid", 0, 0, 0, KEY_pgrp },
{ "proc", 0, 0, 0, KEY_proc },
{ "psr", 0, 0, 0, KEY_proc },
{ "rgroup", 0, 0, 0, KEY_group },
{ "ruser", 0, 0, 0, KEY_user },
{ "s", 0, 0, 0, KEY_state },
{ "sess", 0, 0, 0, KEY_sid },
{ "stime", 0, 0, 0, KEY_start },
{ "tid", 0, 0, 0, KEY_tgrp },
{ "vsz", 0, 0, 0, KEY_size },
};
/*
* optget() info discipline function
*/
static int
{
register int i;
if (streq(s, "formats"))
{
sfprintf(sp, "%s The title string is \b%s\b and the default width is %d.%s]", keys[i].desc, keys[i].head, keys[i].width, (!keys[i].field || (state.pss->meth->fields & keys[i].field)) ? "" : " Not available on this system.");
else
}
return 0;
}
/*
* return device id given tty base name
*/
static int
{
}
/*
* sfkeyprintf() lookup
* handle==0 for heading
*/
static int
{
register char* s = 0;
register Sflong_t n = 0;
register int i;
int j;
unsigned long u;
return 0;
{
{
return 0;
}
}
{
}
else if (!pp)
{
{
return -1;
}
{
{
{
i = -i;
}
}
{
}
}
}
else
{
{
}
{
case KEY_addr:
goto zombie;
goto number;
case KEY_class:
goto zombie;
break;
case KEY_cmd:
goto branch;
case KEY_comm:
if (!s)
s = "<defunct>";
{
for (i = 0, j--; i < j; i++)
fmtesc(s + i);
}
break;
case KEY_cpu:
goto zombie;
goto percent;
case KEY_etime:
else
break;
case KEY_flags:
goto number;
case KEY_group:
{
break;
}
/*FALLTHROUGH*/
case KEY_gid:
goto number;
case KEY_nice:
goto zombie;
goto number;
case KEY_npid:
goto number;
case KEY_pgrp:
goto number;
case KEY_pid:
goto number;
case KEY_ppid:
break;
case KEY_pri:
goto zombie;
goto number;
case KEY_refcount:
goto number;
case KEY_rss:
goto zombie;
goto number;
case KEY_sid:
goto number;
case KEY_size:
goto zombie;
goto number;
case KEY_start:
goto zombie;
{
}
else
break;
case KEY_state:
*(s + 1) = 0;
break;
case KEY_tgrp:
goto number;
case KEY_time:
else
break;
case KEY_tty:
goto zombie;
{
if (s[0] == 'p' && s[1] == 't')
{
if (s[2] == 'y')
s += 3;
else
s += 2;
}
else if (s[0] == 't' && s[1] == 't' && s[2] == 'y')
s += 3;
else
s += i;
}
break;
case KEY_user:
{
break;
}
/*FALLTHROUGH*/
case KEY_uid:
goto number;
case KEY_wchan:
goto zombie;
goto number;
default:
return 0;
s = "";
break;
break;
{
}
break;
}
if (s)
*ps = s;
else
*pn = n;
}
return 1;
}
/*
* ps a single proc
*/
static void
{
register char* s;
register int i;
register long n;
unsigned long u;
int j;
{
return;
}
{
{
case KEY_addr:
goto zombie;
goto hex;
case KEY_class:
goto zombie;
goto string;
case KEY_cmd:
goto branch;
case KEY_comm:
if (!s)
s = "<defunct>";
{
for (i = 0, j--; i < j; i++)
}
s = fmtesc(s);
goto string;
case KEY_cpu:
goto zombie;
goto percent;
case KEY_etime:
goto string;
case KEY_flags:
goto octal;
case KEY_gid:
goto number;
case KEY_group:
goto string;
case KEY_nice:
goto zombie;
goto number;
case KEY_npid:
goto number;
case KEY_pgrp:
goto number;
case KEY_pid:
goto number;
case KEY_ppid:
goto number;
case KEY_pri:
goto zombie;
goto number;
case KEY_refcount:
goto number;
case KEY_rss:
goto zombie;
goto number;
case KEY_sid:
goto number;
case KEY_size:
goto zombie;
goto number;
case KEY_start:
goto zombie;
goto string;
case KEY_state:
*(s + 1) = 0;
goto string;
case KEY_tgrp:
goto number;
case KEY_time:
goto string;
case KEY_tty:
goto zombie;
{
if (s[0] == 'p' && s[1] == 't')
{
if (s[2] == 'y')
s += 3;
else
s += 2;
}
else if (s[0] == 't' && s[1] == 't' && s[2] == 'y')
s += 3;
else
s += i;
}
goto string;
case KEY_uid:
goto number;
case KEY_user:
goto string;
case KEY_wchan:
goto zombie;
goto hex;
}
s = "????";
else
continue;
s = "-";
goto string;
goto string;
{
continue;
}
hex:
else
continue;
continue;
}
}
/*
* ps() a process and its children
*/
static void
{
{
if (level > 0)
level++;
}
else
}
/*
* ps() the selected procs
*/
static void
list(void)
{
{
/*
*/
{
do
{
}
{
do
{
{
do
{
break;
}
}
{
if (pp->ps->ppid != pp->ps->pid && (xp = (Ps_t*)dtmatch(state.bypid, &pp->ps->ppid)) && (xp->ps->pss & (PSS_EXPLICIT|PSS_MATCHED|PSS_PARENT)))
{
else
}
else if (zp)
else
}
}
else
{
/*
* list by order
*/
{
}
}
}
/*
* finalize the field formats and optionally list the heading
*/
static void
head(void)
{
register int n;
{
{
{
break;
}
}
n = 0;
{
n = 1;
}
{
}
}
else
{
}
}
/*
* order procs by <uid,start,pid>
*/
static int
{
register int i;
return i;
return -1;
return 1;
return i;
return -1;
return 1;
return -1;
return 1;
return 0;
}
/*
* add the procs in the pid list
*/
static void
{
register char* t;
register int c;
char* e;
long n;
do
{
if (s)
{
for (; isspace(*s) || *s == ','; s++);
for (t = s; *s && !isspace(*s) && *s != ','; s++);
c = *s;
*s = 0;
if (!*t)
break;
errno = 0;
n = strtol(t, &e, 0);
{
continue;
}
pid = n;
}
if (pe)
{
{
break;
}
}
} while (s && (*s++ = c));
}
/*
* add the ids in s into state.pssdisc.match
* getid!=0 translates alnum to id number
*/
static void
{
register char* t;
register int c;
char* e;
long n;
unsigned long field;
if (!mp)
{
}
do
{
for (; isspace(*s) || *s == ','; s++);
for (t = s; *s && !isspace(*s) && *s != ','; s++);
if (!*t)
break;
if (isdigit(*t))
{
n = strtol(t, &e, 10);
if (*e)
{
continue;
}
}
{
continue;
}
} while (*s++);
}
/*
* add the format key in s into state.fields
*/
static void
{
register char* s = (char*)k;
register char* t;
register int c;
char* e;
int w;
if (streq(s, "?"))
{
{
sfprintf(sfstdout, "%-8s %-8s %s%s%s\n", kp->name, ap->head, ap->desc, ap == kp ? "" : " [alias]", (!ap->field || (state.pss->meth->fields & ap->field)) ? "" : " [not available]");
}
exit(0);
}
do
{
for (; isspace(*s) || *s == ','; s++);
if (!*t)
break;
if (*s == ':' || *s == '+')
{
s = e;
}
else
c = 0;
{
continue;
}
/*
* aliases have Key_t.head == 0
*/
/*
* adjust the width field
*/
if (*s == '=')
{
for (t = ++s; *s && !isspace(*s) && *s != ','; s++);
w = s - t;
if (w < c)
w = c;
}
{
if (!ignore)
continue;
}
/*
* except for width and head adjustments
* we ignore keys already specified to let
* shell aliases work with least suprise
*/
{
else
{
}
}
} while (*s != '=' && *s++);
}
/*
* add pid vector for subsequent poppids()
*/
static void
{
register List_t* p;
}
/*
* pop state.pids by calling addpid()
*/
static void
poppids(void)
{
register List_t* p;
register int i;
unsigned long flags;
{
if (i = p->argc)
while (--i >= 0)
else
free(p);
}
}
int
{
register int n;
register char* s;
/*
* set up the disciplines
*/
/*
* open the ps stream
*/
{
{
argv[0] = s;
}
{
}
}
/*
* initialize the format key table
*/
if (!(state.keys = dtopen(&kd, Dtset)) || !(state.bypid = dtopen(&nd, Dtset)) || !(state.byorder = dtopen(&sd, Dtset)))
/*
* grab the options
*/
n = 0;
for (;;)
{
{
case 'a':
continue;
case 'c':
continue;
case 'd':
continue;
case 'e':
case 'A':
continue;
case 'f':
continue;
case 'h':
continue;
case 'g':
continue;
case 'j':
continue;
case 'l':
continue;
case 'n':
continue;
case 'o':
continue;
case 'p':
continue;
case 'r':
case 'R':
continue;
case 's':
continue;
case 't':
continue;
case 'u':
continue;
case 'v':
continue;
case 'w':
/* ignored by this implementation */
continue;
case 'x':
continue;
case 'B':
continue;
case 'C':
continue;
case 'D':
*s++ = 0;
{
s = 0;
}
{
if (!s)
continue;
}
stresc(s);
continue;
case 'E':
continue;
case 'F':
continue;
case 'G':
continue;
case 'L':
continue;
case 'N':
continue;
case 'P':
continue;
case 'T':
continue;
case 'X':
continue;
case '?':
break;
case ':':
break;
}
break;
}
if (error_info.errors)
if (n)
{
}
else
{
fmt = 0;
}
head();
/*
* add each proc by name
*/
if (*argv)
{
if (!state.pids && !state.pssdisc.match && !(state.pssdisc.flags & (PSS_ALL|PSS_ATTACHED|PSS_DETACHED|PSS_LEADER|PSS_NOLEADER)))
{
for (n = 0; n <= 2; n++)
{
break;
}
}
poppids();
}
else
poppids();
/*
* list the procs
*/
list();
return error_info.errors != 0;
}