supgid/supgrp support, improved library interface

Library changes
   readproc
    . added support for supplementary groups
    . eliminated 2 potential mem leak sources
       . shortcut used for multi-threaded str
         vectors & ptrs was obsoleted
       . freeing of proc_t related dynamic
         memory now rests with the library
    . standardized/normalized many c comments
   sysinfo
    . corrected note regarding glibc & cpuinfo
   library.map
    . made the visible freeproc accessable
 Program changes
   pmap
    . initialized buffer for new readproc i/f
    . eliminated now obsolete free() call
   ps
    . added width aware supgrp support
    . initialized buffers for new readproc i/f
    . eliminated now obsolete free() calls
   top
    . added supgrp support as variable width
    . eliminated now obsolete free() calls
    . expoilted library freeproc function
    . corrected -h|v args text & spacing
    . updated some c comments
 Documentation changes
   ps.1
    . added supgid and supgrp
   top.1
    . added supgid and supgrp
    . addition of above required renumbering
      many fields in section 3a. DESCRIPTIONS
This commit is contained in:
Craig Small 2011-08-01 21:28:46 +10:00
parent 3f59ff5a16
commit 3ef4823f90
11 changed files with 306 additions and 217 deletions

2
pmap.c
View File

@ -358,11 +358,11 @@ int main(int argc, char *argv[]){
discover_shm_minor();
memset(&p, '\0', sizeof(p));
pidlist[count] = 0; // old libproc interface is zero-terminated
PT = openproc(PROC_FILLSTAT|PROC_FILLARG|PROC_PID, pidlist);
while(readproc(PT, &p)){
ret |= one_proc(&p);
if(p.cmdline) free((void*)*p.cmdline);
count--;
}
closeproc(PT);

View File

@ -7,7 +7,7 @@ global:
readproc; readtask; readproctab; readproctab2; look_up_our_self; escape_command;
escape_str; escape_strlist; escaped_copy; read_cmdline;
openproc; closeproc;
openproc; closeproc; freeproc;
tty_to_dev; dev_to_tty; open_psdb_message; open_psdb; lookup_wchan;
display_version; procps_version; linux_version_code;
Hertz; smp_num_cpus; have_privs; getbtime;

View File

@ -38,6 +38,7 @@ extern void __cyg_profile_func_enter(void*,void*);
#define LEAVE(x)
#endif
#ifndef SIGNAL_STRING
// convert hex string to unsigned long long
static unsigned long long unhex(const char *restrict cp){
unsigned long long ull = 0;
@ -48,9 +49,21 @@ static unsigned long long unhex(const char *restrict cp){
}
return ull;
}
#endif
static int task_dir_missing;
// free any additional dynamically acquired storage associated with a proc_t
// ( and if it's to be reused, refresh it otherwise destroy it )
static inline void free_acquired (proc_t *p, int reuse) {
if (p->environ) free((void*)*p->environ);
if (p->cmdline) free((void*)*p->cmdline);
if (p->cgroup) free((void*)*p->cgroup);
if (p->supgid) free(p->supgid);
if (p->supgrp) free(p->supgrp);
memset(p, reuse ? '\0' : '\xff', sizeof(*p));
}
///////////////////////////////////////////////////////////////////////////
typedef struct status_table_struct {
@ -71,12 +84,18 @@ typedef struct status_table_struct {
#define NUL {"", 0, 0},
// Derived from:
// gperf -7 --language=ANSI-C --key-positions=1,3,4 -C -n -c sml.gperf
// gperf -7 --language=ANSI-C --key-positions=1,3,4 -C -n -c <if-not-piped>
//
// Suggested method:
// Grep this file for "case_", then strip those down to the name.
// (leave the colon and newline) So "Pid:\n" and "Threads:\n"
// would be lines in the file. (no quote, no escape, etc.)
// Eliminate duplicates (due to #ifs), the ' case_' prefix and
// any c comments. Leave the colon and newline so that "Pid:\n",
// "Threads:\n", etc. would be lines, but no quote, no escape, etc.
//
// After a pipe through gperf, insert the resulting 'asso_values'
// into our 'assoc' array. Then convert the gperf 'wordlist' array
// into our 'table' array by wrapping the string literals within
// the F macro and replacing empty strings with the NUL define.
//
// In the status_table_struct watch out for name size (grrr, expanding)
// and the number of entries (we mask with 63 for now). The table
@ -169,17 +188,6 @@ static void status2proc(char *S, proc_t *restrict P, int is_proc){
ENTER(0x220);
P->vm_size = 0;
P->vm_lock = 0;
P->vm_rss = 0;
P->vm_data = 0;
P->vm_stack= 0;
P->vm_exe = 0;
P->vm_lib = 0;
P->vm_swap = 0;
P->nlwp = 0;
P->signal[0] = '\0'; // so we can detect it as missing for very old kernels
goto base;
for(;;){
@ -209,8 +217,8 @@ ENTER(0x220);
goto *entry.addr;
#endif
case_Name:{
unsigned u = 0;
case_Name:
{ unsigned u = 0;
while(u < sizeof P->cmd - 1u){
int c = *S++;
if(unlikely(c=='\n')) break;
@ -316,12 +324,25 @@ ENTER(0x220);
case_VmSwap: // Linux 2.6.34
P->vm_swap = strtol(S,&S,10);
continue;
case_Groups:
{ int j = strchr(S, '\n') - S; // currently lines end space + \n
if (j) {
P->supgid = xmalloc(j+1); // +1 in case space disappears
memcpy(P->supgid, S, j);
if (unlikely(' ' != P->supgid[--j])) ++j;
P->supgid[j] = '\0'; // whack the space or the newline
for ( ; j; j--)
if (' ' == P->supgid[j])
P->supgid[j] = ',';
} else
P->supgid = strdup("-");
continue;
}
case_CapBnd:
case_CapEff:
case_CapInh:
case_CapPrm:
case_FDSize:
case_Groups:
case_SigQ:
case_VmHWM: // 2005, peak VmRSS unless VmRSS is bigger
case_VmPTE:
@ -332,20 +353,20 @@ ENTER(0x220);
#if 0
// recent kernels supply per-tgid pending signals
if(is_proc && *ShdPnd){
memcpy(P->signal, ShdPnd, 16);
P->signal[16] = '\0';
memcpy(P->signal, ShdPnd, 16);
P->signal[16] = '\0';
}
#endif
// recent kernels supply per-tgid pending signals
#ifdef SIGNAL_STRING
if(!is_proc || !P->signal[0]){
memcpy(P->signal, P->_sigpnd, 16);
P->signal[16] = '\0';
memcpy(P->signal, P->_sigpnd, 16);
P->signal[16] = '\0';
}
#else
if(!is_proc || !have_process_pending){
P->signal = P->_sigpnd;
if(!is_proc){
P->signal = P->_sigpnd;
}
#endif
@ -354,18 +375,36 @@ ENTER(0x220);
// Only 2.6.0 and above have "Threads" (nlwp) info.
if(Threads){
P->nlwp = Threads;
P->tgid = Tgid; // the POSIX PID value
P->tid = Pid; // the thread ID
P->nlwp = Threads;
P->tgid = Tgid; // the POSIX PID value
P->tid = Pid; // the thread ID
}else{
P->nlwp = 1;
P->tgid = Pid;
P->tid = Pid;
P->nlwp = 1;
P->tgid = Pid;
P->tid = Pid;
}
LEAVE(0x220);
}
static void supgrps_from_supgids (proc_t *p) {
char *g, *s;
int t;
if (!p->supgid || '-' == *p->supgid) {
p->supgrp = strdup("-");
return;
}
s = p->supgid;
t = 0;
do {
if (',' == *s) ++s;
g = group_from_gid((uid_t)strtol(s, &s, 10));
p->supgrp = realloc(p->supgrp, P_G_SZ+t+2);
t += snprintf(p->supgrp+t, P_G_SZ+2, "%s%s", t ? "," : "", g);
} while (*s);
}
///////////////////////////////////////////////////////////////////////
#ifdef OOMEM_ENABLE
static void oomscore2proc(const char* S, proc_t *restrict P)
@ -521,7 +560,7 @@ static char** file2strvec(const char* directory, const char* what) {
q = ret = (char**) (endbuf+align); /* ==> free(*ret) to dealloc */
*q++ = p = rbuf; /* point ptrs to the strings */
endbuf--; /* do not traverse final NUL */
while (++p < endbuf)
while (++p < endbuf)
if (!*p) /* NUL char implies that */
*q++ = p+1; /* next string -> next char */
@ -652,35 +691,37 @@ int read_cmdline(char *restrict const dst, unsigned sz, unsigned pid) {
// The pid (tgid? tid?) is already in p, and a path to it in path, with some
// room to spare.
static proc_t* simple_readproc(PROCTAB *restrict const PT, proc_t *restrict const p) {
static struct stat sb; // stat() buffer
static char sbuf[1024]; // buffer for stat,statm
static struct stat sb; // stat() buffer
static char sbuf[1024]; // buffer for stat,statm,status
char *restrict const path = PT->path;
unsigned flags = PT->flags;
if (unlikely(stat(path, &sb) == -1)) /* no such dirent (anymore) */
goto next_proc;
if (unlikely(stat(path, &sb) == -1)) /* no such dirent (anymore) */
goto next_proc;
if ((flags & PROC_UID) && !XinLN(uid_t, sb.st_uid, PT->uids, PT->nuid))
goto next_proc; /* not one of the requested uids */
goto next_proc; /* not one of the requested uids */
p->euid = sb.st_uid; /* need a way to get real uid */
p->egid = sb.st_gid; /* need a way to get real gid */
p->euid = sb.st_uid; /* need a way to get real uid */
p->egid = sb.st_gid; /* need a way to get real gid */
if (flags & PROC_FILLSTAT) { /* read, parse /proc/#/stat */
if (unlikely( file2str(path, "stat", sbuf, sizeof sbuf) == -1 ))
goto next_proc; /* error reading /proc/#/stat */
stat2proc(sbuf, p); /* parse /proc/#/stat */
if (flags & PROC_FILLSTAT) { // read /proc/#/stat
if (unlikely( file2str(path, "stat", sbuf, sizeof sbuf) == -1 ))
goto next_proc;
stat2proc(sbuf, p);
}
if (unlikely(flags & PROC_FILLMEM)) { /* read, parse /proc/#/statm */
if (likely( file2str(path, "statm", sbuf, sizeof sbuf) != -1 ))
statm2proc(sbuf, p); /* ignore statm errors here */
} /* statm fields just zero */
if (flags & PROC_FILLMEM) { // read /proc/#/statm
if (likely(file2str(path, "statm", sbuf, sizeof sbuf) != -1 ))
statm2proc(sbuf, p);
}
if (flags & PROC_FILLSTATUS) { /* read, parse /proc/#/status */
if (likely( file2str(path, "status", sbuf, sizeof sbuf) != -1 )){
status2proc(sbuf, p, 1);
}
if (flags & PROC_FILLSTATUS) { // read /proc/#/status
if (likely( file2str(path, "status", sbuf, sizeof sbuf) != -1 )){
status2proc(sbuf, p, 1);
if (flags & PROC_FILLSUPGRP)
supgrps_from_supgids(p);
}
}
// if multithreaded, some values are crap
@ -688,17 +729,17 @@ static proc_t* simple_readproc(PROCTAB *restrict const PT, proc_t *restrict cons
p->wchan = (KLONG)~0ull;
}
/* some number->text resolving which is time consuming and kind of insane */
/* some number->text resolving which is time consuming */
if (flags & PROC_FILLUSR){
memcpy(p->euser, user_from_uid(p->euid), sizeof p->euser);
memcpy(p->euser, user_from_uid(p->euid), sizeof p->euser);
if(flags & PROC_FILLSTATUS) {
memcpy(p->ruser, user_from_uid(p->ruid), sizeof p->ruser);
memcpy(p->suser, user_from_uid(p->suid), sizeof p->suser);
memcpy(p->fuser, user_from_uid(p->fuid), sizeof p->fuser);
memcpy(p->ruser, user_from_uid(p->ruid), sizeof p->ruser);
memcpy(p->suser, user_from_uid(p->suid), sizeof p->suser);
memcpy(p->fuser, user_from_uid(p->fuid), sizeof p->fuser);
}
}
/* some number->text resolving which is time consuming and kind of insane */
/* some number->text resolving which is time consuming */
if (flags & PROC_FILLGRP){
memcpy(p->egroup, group_from_gid(p->egid), sizeof p->egroup);
if(flags & PROC_FILLSTATUS) {
@ -708,12 +749,12 @@ static proc_t* simple_readproc(PROCTAB *restrict const PT, proc_t *restrict cons
}
}
if (unlikely(flags & PROC_FILLENV)) /* read /proc/#/environ */
if (unlikely(flags & PROC_FILLENV)) // read /proc/#/environ
p->environ = file2strvec(path, "environ");
else
p->environ = NULL;
if (flags & (PROC_FILLCOM|PROC_FILLARG)) { /* read /proc/#/cmdline */
if (flags & (PROC_FILLCOM|PROC_FILLARG)) { // read /proc/#/cmdline
if (flags & PROC_EDITCMDLCVT)
fill_cmdline_cvt(p);
else
@ -721,7 +762,7 @@ static proc_t* simple_readproc(PROCTAB *restrict const PT, proc_t *restrict cons
} else
p->cmdline = NULL;
if ((flags & PROC_FILLCGROUP) /* read /proc/#/cgroup, if possible */
if ((flags & PROC_FILLCGROUP) // read /proc/#/cgroup
&& linux_version_code >= LINUX_VERSION(2,6,24)) {
if (flags & PROC_EDITCGRPCVT)
fill_cgroup_cvt(p);
@ -750,55 +791,55 @@ next_proc:
// t is the POSIX thread (task group member, generally not the leader)
// path is a path to the task, with some room to spare.
static proc_t* simple_readtask(PROCTAB *restrict const PT, const proc_t *restrict const p, proc_t *restrict const t, char *restrict const path) {
static struct stat sb; // stat() buffer
static char sbuf[1024]; // buffer for stat,statm
static struct stat sb; // stat() buffer
static char sbuf[1024]; // buffer for stat,statm,status
unsigned flags = PT->flags;
//printf("hhh\n");
if (unlikely(stat(path, &sb) == -1)) /* no such dirent (anymore) */
goto next_task;
if (unlikely(stat(path, &sb) == -1)) /* no such dirent (anymore) */
goto next_task;
// if ((flags & PROC_UID) && !XinLN(uid_t, sb.st_uid, PT->uids, PT->nuid))
// goto next_task; /* not one of the requested uids */
// goto next_task; /* not one of the requested uids */
t->euid = sb.st_uid; /* need a way to get real uid */
t->egid = sb.st_gid; /* need a way to get real gid */
t->euid = sb.st_uid; /* need a way to get real uid */
t->egid = sb.st_gid; /* need a way to get real gid */
//printf("iii\n");
if (flags & PROC_FILLSTAT) { /* read, parse /proc/#/stat */
if (unlikely( file2str(path, "stat", sbuf, sizeof sbuf) == -1 ))
goto next_task; /* error reading /proc/#/stat */
stat2proc(sbuf, t); /* parse /proc/#/stat */
if (flags & PROC_FILLSTAT) { // read /proc/#/task/#/stat
if (unlikely( file2str(path, "stat", sbuf, sizeof sbuf) == -1 ))
goto next_task;
stat2proc(sbuf, t);
}
if (unlikely(flags & PROC_FILLMEM)) { /* read, parse /proc/#/statm */
#if 0
if (likely( file2str(path, "statm", sbuf, sizeof sbuf) != -1 ))
statm2proc(sbuf, t); /* ignore statm errors here */
if (flags & PROC_FILLMEM) { // read /proc/#/task/#statm
#if 1
if (likely(file2str(path, "statm", sbuf, sizeof sbuf) != -1 ))
statm2proc(sbuf, t);
#else
t->size = p->size;
t->resident = p->resident;
t->share = p->share;
t->trs = p->trs;
t->lrs = p->lrs;
t->drs = p->drs;
t->dt = p->dt;
t->size = p->size;
t->resident = p->resident;
t->share = p->share;
t->trs = p->trs;
t->lrs = p->lrs;
t->drs = p->drs;
t->dt = p->dt;
#endif
} /* statm fields just zero */
}
if (flags & PROC_FILLSTATUS) { /* read, parse /proc/#/status */
if (flags & PROC_FILLSTATUS) { // read /proc/#/task/#/status
if (likely( file2str(path, "status", sbuf, sizeof sbuf) != -1 )){
status2proc(sbuf, t, 0);
if (flags & PROC_FILLSUPGRP)
supgrps_from_supgids(t);
}
}
/* some number->text resolving which is time consuming */
if (flags & PROC_FILLUSR){
memcpy(t->euser, user_from_uid(t->euid), sizeof t->euser);
memcpy(t->euser, user_from_uid(t->euid), sizeof t->euser);
if(flags & PROC_FILLSTATUS) {
memcpy(t->ruser, user_from_uid(t->ruid), sizeof t->ruser);
memcpy(t->suser, user_from_uid(t->suid), sizeof t->suser);
memcpy(t->fuser, user_from_uid(t->fuid), sizeof t->fuser);
memcpy(t->ruser, user_from_uid(t->ruid), sizeof t->ruser);
memcpy(t->suser, user_from_uid(t->suid), sizeof t->suser);
memcpy(t->fuser, user_from_uid(t->fuid), sizeof t->fuser);
}
}
@ -812,21 +853,42 @@ static proc_t* simple_readtask(PROCTAB *restrict const PT, const proc_t *restric
}
}
#if 0
if ((flags & PROC_FILLCOM) || (flags & PROC_FILLARG)) /* read+parse /proc/#/cmdline */
t->cmdline = file2strvec(path, "cmdline");
else
t->cmdline = NULL;
if (unlikely(flags & PROC_FILLENV)) /* read+parse /proc/#/environ */
t->environ = file2strvec(path, "environ");
#if 1 // begin active ------------------------
if (unlikely(flags & PROC_FILLENV)) // read /proc/#/task/#/environ
t->environ = file2strvec(path, "environ");
else
t->environ = NULL;
#else
if (flags & (PROC_FILLCOM|PROC_FILLARG)) { // read /proc/#/task/#/cmdline
if (flags & PROC_EDITCMDLCVT)
fill_cmdline_cvt(t);
else
t->cmdline = file2strvec(path, "cmdline");
} else
t->cmdline = NULL;
if ((flags & PROC_FILLCGROUP) // read /proc/#/task/#/cgroup
&& linux_version_code >= LINUX_VERSION(2,6,24)) {
if (flags & PROC_EDITCGRPCVT)
fill_cgroup_cvt(t);
else
t->cgroup = file2strvec(path, "cgroup");
} else
t->cgroup = NULL;
#else // end active --------------------------
t->cmdline = p->cmdline; // better not free these until done with all threads!
t->environ = p->environ;
t->cgroup = p->cgroup;
t->supgid = p->supgid;
t->supgrp = p->supgrp;
#error we DO NOT BURDEN library users with the above insanity ANYMORE !
#endif // end inactive ------------------------
#ifdef OOMEM_ENABLE
t->oom_score = p->oom_score;
t->oom_adj = p->oom_adj;
#endif
t->cgroup = p->cgroup;
t->ppid = p->ppid; // ought to put the per-task ppid somewhere
return t;
@ -916,7 +978,8 @@ proc_t* readproc(PROCTAB *restrict const PT, proc_t *restrict p) {
// }
saved_p = p;
if(!p) p = xcalloc(p, sizeof *p); /* passed buf or alloced mem */
if(!p) p = xcalloc(NULL, sizeof *p);
else free_acquired(p, 1);
for(;;){
// fills in the path, plus p->tid and p->tgid
@ -944,20 +1007,25 @@ proc_t* readtask(PROCTAB *restrict const PT, const proc_t *restrict const p, pro
proc_t *saved_t;
saved_t = t;
if(!t) t = xcalloc(t, sizeof *t); /* passed buf or alloced mem */
if(!t) t = xcalloc(NULL, sizeof *t);
else free_acquired(t, 1);
// 1. got to fake a thread for old kernels
// 2. for single-threaded processes, this is faster (but must patch up stuff that differs!)
if(task_dir_missing || p->nlwp < 2){
if(task_dir_missing) {
if(PT->did_fake) goto out;
PT->did_fake=1;
memcpy(t,p,sizeof(proc_t));
// use the per-task pending, not per-tgid pending
#ifdef SIGNAL_STRING
memcpy(&t->signal, &t->_sigpnd, sizeof t->signal);
memcpy(&t->signal, &t->_sigpnd, sizeof t->signal);
#else
t->signal = t->_sigpnd;
t->signal = t->_sigpnd;
#endif
t->environ = NULL;
t->cmdline = vectorize_this_str("n/a");
t->cgroup = NULL;
t->supgid = NULL;
t->supgrp = NULL;
return t;
}
@ -1026,19 +1094,12 @@ void closeproc(PROCTAB* PT) {
}
}
// deallocate the space allocated by readproc if the passed rbuf was NULL
// deallocate space allocated by readproc
void freeproc(proc_t* p) {
if (!p) /* in case p is NULL */
return;
/* ptrs are after strings to avoid copying memory when building them. */
/* so free is called on the address of the address of strvec[0]. */
if (p->cmdline)
free((void*)*p->cmdline);
if (p->environ)
free((void*)*p->environ);
if (p->cgroup)
free((void*)*p->cgroup);
free(p);
if (p) {
free_acquired(p, 0);
free(p);
}
}
@ -1115,6 +1176,7 @@ proc_data_t *readproctab2(int(*want_proc)(proc_t *buf), int(*want_task)(proc_t *
n_alloc = n_alloc*5/4+30; // grow by over 25%
data = realloc(data,sizeof(proc_t)*n_alloc);
//if(!data) return NULL;
memset(data+n_used, 0, sizeof(proc_t)*(n_alloc-n_used));
}
if(n_proc_alloc == n_proc){
//proc_t **old = ptab;
@ -1133,8 +1195,8 @@ proc_data_t *readproctab2(int(*want_proc)(proc_t *buf), int(*want_task)(proc_t *
proc_t *old = data;
n_alloc = n_alloc*5/4+30; // grow by over 25%
data = realloc(data,sizeof(proc_t)*n_alloc);
// have to move tmp too
tmp = data+(tmp-old);
// have to move tmp too
tmp = data+(tmp-old);
//if(!data) return NULL;
}
if(n_task_alloc == n_task){

View File

@ -113,7 +113,9 @@ typedef struct proc_t {
char
**environ, // (special) environment string vector (/proc/#/environ)
**cmdline, // (special) command line string vector (/proc/#/cmdline)
**cgroup; // (special) cgroup string vector (/proc/#/cgroup)
**cgroup, // (special) cgroup string vector (/proc/#/cgroup)
*supgid, // status supplementary gids as comma delimited str
*supgrp; // supp grp names as comma delimited str, derived from supgid
char
// Be compatible: Digital allows 16 and NT allows 14 ???
euser[P_G_SZ], // stat(),status effective user name
@ -180,7 +182,7 @@ typedef struct PROCTAB {
unsigned pathlen; // length of string in the above (w/o '\0')
} PROCTAB;
// initialize a PROCTAB structure holding needed call-to-call persistent data
// Initialize a PROCTAB structure holding needed call-to-call persistent data
extern PROCTAB* openproc(int flags, ... /* pid_t*|uid_t*|dev_t*|char* [, int n] */ );
typedef struct proc_data_t {
@ -198,13 +200,21 @@ extern proc_data_t *readproctab2(int(*want_proc)(proc_t *buf), int(*want_task)(p
// table subset satisfying the constraints of flags and the optional PID list.
// Free allocated memory with exit(). Access via tab[N]->member. The pointer
// list is NULL terminated.
extern proc_t** readproctab(int flags, ... /* same as openproc */ );
// clean-up open files, etc from the openproc()
// Clean-up open files, etc from the openproc()
extern void closeproc(PROCTAB* PT);
// retrieve the next process matching the criteria set by the openproc()
// Retrieve the next process or task matching the criteria set by the openproc().
//
// Note: When NULL is used as the readproc 'p' or readtask 't' parameter,
// the library will allocate the necessary proc_t storage.
//
// Alternately, you may provide your own reuseable buffer address
// in which case that buffer *MUST* be initialized to zero one time
// only before first use. Thereafter, the library will manage such
// a passed proc_t, freeing any additional acquired memory associated
// with the previous process or thread.
extern proc_t* readproc(PROCTAB *restrict const PT, proc_t *restrict p);
extern proc_t* readtask(PROCTAB *restrict const PT, const proc_t *restrict const p, proc_t *restrict t);
@ -213,11 +223,10 @@ extern int read_cmdline(char *restrict const dst, unsigned sz, unsigned pid);
extern void look_up_our_self(proc_t *p);
// deallocate space allocated by readproc
// Deallocate space allocated by readproc
extern void freeproc(proc_t* p);
//fill out a proc_t for a single task
// Fill out a proc_t for a single task
extern proc_t * get_proc_stats(pid_t pid, proc_t *p);
// openproc/readproctab:
@ -244,7 +253,8 @@ extern proc_t * get_proc_stats(pid_t pid, proc_t *p);
#define PROC_FILLWCHAN 0x0080 // look up WCHAN name
#define PROC_FILLARG 0x0100 // alloc and fill in `cmdline'
#define PROC_FILLCGROUP 0x0200 // alloc and fill in `cgroup`
#define PROC_FILLOOM 0x0400 // alloc and fill in oom_score, oom_adj
#define PROC_FILLSUPGRP 0x0400 // resolve supplementary group id -> group name
#define PROC_FILLOOM 0x0800 // alloc and fill in oom_score, oom_adj
#define PROC_LOOSE_TASKS 0x2000 // threat threads as if they were processes

View File

@ -947,6 +947,9 @@ out:
void cpuinfo (void) {
// ought to count CPUs in /proc/stat instead of relying
// on glibc, which foolishly tries to parse /proc/cpuinfo
// note: that may have been the case but now /proc/stat
// is the default source. parsing of /proc/cpuinfo
// only occurs if the open on /proc/stat fails
//
// SourceForge has an old Alpha running Linux 2.2.20 that
// appears to have a non-SMP kernel on a 2-way SMP box.

View File

@ -327,61 +327,46 @@ static int want_this_proc_pcpu(proc_t *buf){
/***** just display */
static void simple_spew(void){
proc_t buf;
static proc_t buf, buf2; // static avoids memset
PROCTAB* ptp;
ptp = openproc(needs_for_format | needs_for_sort | needs_for_select | needs_for_threads);
if(!ptp) {
fprintf(stderr, "Error: can not access /proc.\n");
exit(1);
}
memset(&buf, '#', sizeof(proc_t));
switch(thread_flags & (TF_show_proc|TF_loose_tasks|TF_show_task)){
case TF_show_proc: // normal non-thread output
while(readproc(ptp,&buf)){
if(want_this_proc(&buf)){
show_one_proc(&buf, proc_format_list);
}
if(buf.cmdline) free((void*)*buf.cmdline); // ought to reuse
if(buf.environ) free((void*)*buf.environ); // ought to reuse
if(buf.cgroup) free((void*)*buf.cgroup);
}
break;
case TF_show_proc|TF_loose_tasks: // H option
while(readproc(ptp,&buf)){
proc_t buf2;
// must still have the process allocated
while(readtask(ptp,&buf,&buf2)){
if(!want_this_proc(&buf)) continue;
show_one_proc(&buf2, task_format_list);
}
if(buf.cmdline) free((void*)*buf.cmdline); // ought to reuse
if(buf.environ) free((void*)*buf.environ); // ought to reuse
if(buf.cgroup) free((void*)*buf.cgroup);
}
break;
case TF_show_proc|TF_show_task: // m and -m options
while(readproc(ptp,&buf)){
if(want_this_proc(&buf)){
proc_t buf2;
show_one_proc(&buf, proc_format_list);
// must still have the process allocated
while(readtask(ptp,&buf,&buf2)) show_one_proc(&buf2, task_format_list);
}
if(buf.cmdline) free((void*)*buf.cmdline); // ought to reuse
if(buf.environ) free((void*)*buf.environ); // ought to reuse
if(buf.cgroup) free((void*)*buf.cgroup);
}
break;
case TF_show_task: // -L and -T options
while(readproc(ptp,&buf)){
if(want_this_proc(&buf)){
proc_t buf2;
// must still have the process allocated
while(readtask(ptp,&buf,&buf2)) show_one_proc(&buf2, task_format_list);
}
if(buf.cmdline) free((void*)*buf.cmdline); // ought to reuse
if(buf.environ) free((void*)*buf.environ); // ought to reuse
if(buf.cgroup) free((void*)*buf.cgroup);
}
break;
}
@ -438,16 +423,10 @@ static void show_proc_array(PROCTAB *restrict ptp, int n){
while(n--){
if(thread_flags & TF_show_proc) show_one_proc(*p, proc_format_list);
if(thread_flags & TF_show_task){
proc_t buf2;
static proc_t buf2; // static avoids memset
// must still have the process allocated
while(readtask(ptp,*p,&buf2)) show_one_proc(&buf2, task_format_list);
// must not attempt to free cmdline and environ
}
/* no point freeing any of this -- won't need more mem */
// if((*p)->cmdline) free((void*)*(*p)->cmdline);
// if((*p)->environ) free((void*)*(*p)->environ);
// memset(*p, '%', sizeof(proc_t)); /* debug */
// free(*p);
p++;
}
}
@ -464,9 +443,6 @@ static void show_tree(const int self, const int n, const int level, const int ha
forest_prefix[level] = '\0';
}
show_one_proc(processes[self],format_list); /* first show self */
/* no point freeing any of this -- won't need more mem */
// if(processes[self]->cmdline) free((void*)*processes[self]->cmdline);
// if(processes[self]->environ) free((void*)*processes[self]->environ);
for(;;){ /* look for children */
if(i >= n) return; /* no children */
if(processes[i]->ppid == processes[self]->XXXID) break;

View File

@ -38,7 +38,7 @@
*
* Table 5 could go in a file with the output functions.
*/
#include <ctype.h>
#include <fcntl.h>
#include <grp.h>
@ -360,7 +360,7 @@ static int pr_argcom(char *restrict const outbuf, const proc_t *restrict const p
static int pr_cgroup(char *restrict const outbuf,const proc_t *restrict const pp) {
int rightward = max_rightward;
if(pp->cgroup) {
escaped_copy(outbuf, *pp->cgroup, OUTBUF_SIZE, &rightward);
return max_rightward-rightward;
@ -373,7 +373,7 @@ static int pr_cgroup(char *restrict const outbuf,const proc_t *restrict const pp
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;
@ -381,7 +381,7 @@ static int pr_fname(char *restrict const outbuf, const proc_t *restrict const pp
}
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;
@ -1022,10 +1022,10 @@ static int do_pr_name(char *restrict const outbuf, const char *restrict const na
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 */
}
@ -1072,11 +1072,23 @@ static int pr_nlwp(char *restrict const outbuf, const proc_t *restrict const pp)
static int pr_sess(char *restrict const outbuf, const proc_t *restrict const pp){
return snprintf(outbuf, COLWID, "%u", pp->session);
}
static int pr_supgid(char *restrict const outbuf, const proc_t *restrict const pp){
int rightward = max_rightward;
escaped_copy(outbuf, pp->supgid ? pp->supgid : "n/a", OUTBUF_SIZE, &rightward);
return max_rightward-rightward;
}
static int pr_supgrp(char *restrict const outbuf, const proc_t *restrict const pp){
int rightward = max_rightward;
escaped_copy(outbuf, pp->supgrp ? pp->supgrp : "n/a", OUTBUF_SIZE, &rightward);
return max_rightward-rightward;
}
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);
@ -1241,7 +1253,9 @@ static int pr_t_left2(char *restrict const outbuf, const proc_t *restrict const
#define GRP PROC_FILLGRP /* gid_t -> group names */
#define WCH PROC_FILLWCHAN /* do WCHAN lookup */
#define SGRP PROC_FILLSTATUS | PROC_FILLSUPGRP /* supgid -> supgrp (names) */
#define CGRP PROC_FILLCGROUP | PROC_EDITCGRPCVT /* read cgroup */
/* TODO
* pull out annoying BSD aliases into another table (to macro table?)
* add sorting functions here (to unify names)
@ -1437,6 +1451,8 @@ static const format_struct format_array[] = {
{"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},
{"supgid", "SUPGID", pr_supgid, sr_nop, 20, 0, LNX, PO|UNLIMITED},
{"supgrp", "SUPGRP", pr_supgrp, sr_nop, 40,SGRP, LNX, PO|UNLIMITED},
{"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},
@ -1797,14 +1813,14 @@ void show_one_proc(const proc_t *restrict const p, const format_node *restrict f
}
}
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;
@ -1869,7 +1885,7 @@ void show_one_proc(const proc_t *restrict const p, const format_node *restrict f
/* 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. */

12
ps/ps.1
View File

@ -1321,6 +1321,18 @@ suid SUID T{
saved user\ ID. (alias\ \fBsvuid\fR).
T}
supgid SUPGID T{
group ids of supplementary groups, if any.
See
.BR getgroups (2).
T}
supgrp SUPGRP T{
group names of supplementary groups, if any.
See
.BR getgroups (2).
T}
suser SUSER T{
saved user name. This will be the textual user\ ID,
if\ it can be obtained and the field width permits,

46
top.1
View File

@ -435,7 +435,7 @@ Many different hierarchies of cgroups can exist simultaneously on a system
and each hierarchy is attached to one or more subsystems.
A subsystem represents a single resource.
\*(NT The 'CGROUPS' field/column, unlike most columns, is not fixed-width.
\*(NT The 'CGROUPS' field, unlike most columns, is not fixed-width.
When displayed, it plus any other variable width columns will be allocated
all remaining screen width (up to the maximum \*(WX characters).
@ -461,7 +461,7 @@ fit in this field's current width.
That width depends upon other fields selected, their order and the current
screen width.
\*(NT The 'COMMAND' field/column, unlike most columns, is not fixed-width.
\*(NT The 'COMMAND' field, unlike most columns, is not fixed-width.
When displayed, it plus any other variable width columns will be allocated
all remaining screen width (up to the maximum \*(WX characters).
@ -621,15 +621,35 @@ login shell.
The\fI saved\fR user ID.
.TP 4
27.\fB SUSER \*(Em Saved User Name \fR
27.\fB SUPGIDS \*(Em Supplementary Group IDs \fR
The IDs of any supplementary group(s) established at login or
inherited from a task's parent.
They are displayed in a comma delimited list.
\*(NT The 'SUPGIDS' field, unlike most columns, is not fixed-width.
When displayed, it plus any other variable width columns will be allocated
all remaining screen width (up to the maximum \*(WX characters).
.TP 4
28.\fB SUPGRPS \*(Em Supplementary Group Names \fR
The names of any supplementary group(s) established at login or
inherited from a task's parent.
They are displayed in a comma delimited list.
\*(NT The 'SUPGRPS' field, unlike most columns, is not fixed-width.
When displayed, it plus any other variable width columns will be allocated
all remaining screen width (up to the maximum \*(WX characters).
.TP 4
29.\fB SUSER \*(Em Saved User Name \fR
The\fI saved\fR user name.
.TP 4
28.\fB SWAP \*(Em Swapped Size (kb) \fR
30.\fB SWAP \*(Em Swapped Size (kb) \fR
The non-resident portion of a task's address space.
.TP 4
29.\fB TIME \*(Em \*(PU Time \fR
31.\fB TIME \*(Em \*(PU Time \fR
Total \*(PU time the task has used since it started.
When 'Cumulative mode' is \*O, each process is listed with the \*(Pu
time that it and its dead children have used.
@ -637,19 +657,19 @@ You toggle 'Cumulative mode' with 'S', which is both a \*(CO and an \*(CI.
\*(XC 'S' \*(CI for additional information regarding this mode.
.TP 4
30.\fB TIME+ \*(Em \*(PU Time, hundredths \fR
32.\fB TIME+ \*(Em \*(PU Time, hundredths \fR
The same as 'TIME', but reflecting more granularity through hundredths
of a second.
.TP 4
31.\fB TPGID \*(Em Tty Process Group Id \fR
33.\fB TPGID \*(Em Tty Process Group Id \fR
The process group ID of the foreground process for the connected tty,
or -1 if a process is not connected to a terminal.
By convention, this value equals the process ID (\*(Xa PID) of the
the process group leader (\*(Xa PGRP).
.TP 4
32.\fB TTY \*(Em Controlling Tty \fR
34.\fB TTY \*(Em Controlling Tty \fR
The name of the controlling terminal.
This is usually the device (serial port, pty, etc.) from which the
process was started, and which it uses for input or output.
@ -657,21 +677,21 @@ However, a task need not be associated with a terminal, in which case
you'll see '?' displayed.
.TP 4
33.\fB UID \*(Em User Id \fR
35.\fB UID \*(Em User Id \fR
The\fI effective\fR user ID of the task's owner.
.TP 4
34.\fB USER \*(Em User Name \fR
36.\fB USER \*(Em User Name \fR
The\fI effective\fR user name of the task's owner.
.TP 4
35.\fB VIRT \*(Em Virtual Memory Size (kb) \fR
37.\fB VIRT \*(Em Virtual Memory Size (kb) \fR
The total amount of \*(MV used by the task.
It includes all code, data and shared libraries plus pages that have been
swapped out and pages that have been mapped but not used.
.TP 4
36.\fB WCHAN \*(Em Sleeping in Function \fR
38.\fB WCHAN \*(Em Sleeping in Function \fR
Depending on the availability of the kernel link map ('System.map'), this
field will show the name or the address of the kernel function in which the
task is currently sleeping.
@ -682,7 +702,7 @@ By displaying this field, \*(We's own working set could be increased by over
Should that occur, your only means of reducing that overhead will be to stop
and restart \*(We.
\*(NT The 'WCHAN' field/column, unlike most columns, is not fixed-width.
\*(NT The 'WCHAN' field, unlike most columns, is not fixed-width.
When displayed, it plus any other variable width columns will be allocated
all remaining screen width (up to the maximum \*(WX characters).

59
top.c
View File

@ -216,6 +216,8 @@ SCB_NUMx(PID, tid)
SCB_NUMx(PPD, ppid)
SCB_NUMx(PRI, priority)
SCB_NUM1(RES, resident) // also serves MEM !
SCB_STRX(SGD, supgid)
SCB_STRS(SGN, supgrp)
SCB_NUM1(SHR, share)
SCB_NUM1(SID, session)
SCB_NUMx(STA, state)
@ -1164,6 +1166,7 @@ static inline int user_matched (WIN_t *q, const proc_t *p) {
#define L_EUSER PROC_FILLUSR
#define L_OUSER PROC_FILLSTATUS | PROC_FILLUSR
#define L_EGROUP PROC_FILLSTATUS | PROC_FILLGRP
#define L_SUPGRP PROC_FILLSTATUS | PROC_FILLSUPGRP
// make 'none' non-zero (used to be important to Frames_libflags)
#define L_NONE PROC_SPARE_1
// from either 'stat' or 'status' (preferred), via bits not otherwise used
@ -1231,8 +1234,10 @@ static FLD_t Fieldstab[] = {
#else
{ "Flags ", "%08lx ", -1, -1, SF(FLG), L_stat, "Task Flags <sched.h>" },
#endif
// next entry's like P_CMD/P_WCH, and '.head' must be same length -- they share varcolsz
{ "CGROUPS ", NULL, -1, -1, SF(CGR), L_CGROUP, "Control Groups" }
// next 3 entries as P_CMD/P_WCH: '.head' must be same length -- they share varcolsz
{ "CGROUPS ", NULL, -1, -1, SF(CGR), L_CGROUP, "Control Groups" },
{ "SUPGIDS ", NULL, -1, -1, SF(SGD), L_status, "Supp Groups IDs" },
{ "SUPGRPS ", NULL, -1, -1, SF(SGN), L_SUPGRP, "Supp Groups Names" }
#ifdef OOMEM_ENABLE
#define L_oom PROC_FILLOOM
,{ "Adj ", "%3d ", -1, -1, SF(OOA), L_oom, "oom_adjustment (2^X)" }
@ -1506,15 +1511,15 @@ static void calibrate_fields (void) {
* ( xPRFX has pos 2 & 10 for 'extending' when at minimums )
*
* The first 4 screen rows are reserved for explanatory text.
* Thus, with our current 36 fields, a maximum of 6 columns and
* Thus, with our current 38 fields, a maximum of 6 columns and
* 1 space between columns, a tty will still remain useable under
* these extremes:
* rows cols displayed
* ---- ---- ------------------
* 10 66 xPRFX only
* 10 198 full xPRFX + xSUFX
* 22 22 xPRFX only
* 22 66 full xPRFX + xSUFX
* 11 66 xPRFX only (w/ room for +4)
* 11 198 full xPRFX + xSUFX (w/ room for +4)
* 23 22 xPRFX only
* 23 66 full xPRFX + xSUFX
* ( if not, the user deserves our most cryptic messages )
*/
static void display_fields (int focus, int extend) {
@ -1904,13 +1909,11 @@ static void prochlp (proc_t *this) {
static proc_t **procs_refresh (proc_t **ppt) {
#define PTRsz sizeof(proc_t *)
#define ENTsz sizeof(proc_t)
static int threadshown = 0; // thread hack optimization
static unsigned savmax = 0; // first time, Bypass: (i)
proc_t *ptask = (proc_t *)-1; // first time, Force: (ii)
unsigned curmax = 0; // every time (jeeze)
PROCTAB* PT;
proc_t *pthrd; // for thread hack
unsigned i; // ditto
prochlp(NULL); // prep for a new frame
if (NULL == (PT = openproc(Frames_libflags, Monpids)))
@ -1919,17 +1922,6 @@ static proc_t **procs_refresh (proc_t **ppt) {
// i) Allocated Chunks: *Existing* table; refresh + reuse
if (!Thread_mode) {
while (curmax < savmax) {
if (ppt[curmax]->cmdline || ppt[curmax]->cgroup) {
if (threadshown) { // skip if never used (see note below)
for (i = curmax + 1; i < savmax; i++) {
if (ppt[i]->cmdline == ppt[curmax]->cmdline) ppt[i]->cmdline = NULL;
if (ppt[i]->cgroup == ppt[curmax]->cgroup) ppt[i]->cgroup = NULL;
}
}
if (ppt[curmax]->cmdline) free(*ppt[curmax]->cmdline);
if (ppt[curmax]->cgroup) free(*ppt[curmax]->cgroup);
ppt[curmax]->cmdline = ppt[curmax]->cgroup = NULL;
}
if (!(ptask = readproc(PT, ppt[curmax]))) break;
prochlp(ptask); // tally & complete this proc_t
++curmax;
@ -1939,23 +1931,11 @@ static proc_t **procs_refresh (proc_t **ppt) {
while (curmax < savmax) {
if (!(ptask = readproc(PT, NULL))) break;
while (curmax < savmax) {
if (ppt[curmax]->cmdline || ppt[curmax]->cgroup) {
/* note: threads share some of the same storage, so we must look
through the rest of our table for duplicate ref's... */
for (i = curmax + 1; i < savmax; i++) {
if (ppt[i]->cmdline == ppt[curmax]->cmdline) ppt[i]->cmdline = NULL;
if (ppt[i]->cgroup == ppt[curmax]->cgroup) ppt[i]->cgroup = NULL;
} /* ...but free only once ! */
if (ppt[curmax]->cmdline) free(*ppt[curmax]->cmdline);
if (ppt[curmax]->cgroup) free(*ppt[curmax]->cgroup);
ppt[curmax]->cmdline = ppt[curmax]->cgroup = NULL;
}
if (!(pthrd = readtask(PT, ptask, ppt[curmax]))) break;
threadshown = 1;
prochlp(pthrd); // tally & complete this thread
++curmax;
}
free(ptask); // readproc's proc_t not needed
freeproc(ptask); // readproc's proc_t not needed
}
}
@ -1977,11 +1957,10 @@ static proc_t **procs_refresh (proc_t **ppt) {
for (;;) {
ppt = alloc_r(ppt, (curmax + 1) * PTRsz);
if (!(pthrd = readtask(PT, ptask, NULL))) break;
threadshown = 1;
prochlp(pthrd); // tally & complete this thread
ppt[curmax++] = pthrd;
}
free(ptask); // readproc's proc_t not needed
freeproc(ptask); // readproc's proc_t not needed
}
}
}
@ -2191,7 +2170,7 @@ static void parse_args (char **args) {
. bunched args are actually handled properly and none are ignored
. we tolerate NO whitespace and NO switches -- maybe too tolerant? */
static const char usage_str[] =
" -hv | -bcHiSs -d delay -n iters -u|U user | -p pid[,pid] -w [cols]";
" -hv | -bcHiSs -d delay -n limit -u|U user | -p pid[,pid] -w [cols]";
static const char sel_error[] = "conflicting process selections (U/p/u)";
static const char numbs_str[] = "+,-.0123456789";
float tmp_delay = MAXFLOAT;
@ -2230,7 +2209,7 @@ static void parse_args (char **args) {
break;
case 'h':
case 'v': case 'V':
fprintf(stdout, "\t%s\nusage:\t%s%s\n", procps_version, Myname, usage_str);
fprintf(stdout, "\t%s\nusage:\t%s%s", procps_version, Myname, usage_str);
bye_bye(NULL);
case 'i':
TOGw(Curwin, Show_IDLEPS);
@ -3319,6 +3298,12 @@ static void task_show (const WIN_t *q, const proc_t *p) {
case P_RES:
makeCOL(scale_num(pages2K(p->resident), w, s));
break;
case P_SGD:
makeVAR(p->supgid ? p->supgid : "n/a");
break;
case P_SGN:
makeVAR(p->supgrp ? p->supgrp : "n/a");
break;
case P_SHR:
makeCOL(scale_num(pages2K(p->share), w, s));
break;

5
top.h
View File

@ -129,6 +129,7 @@ enum pflag {
P_MEM, P_VRT, P_SWP, P_RES, P_COD, P_DAT, P_SHR,
P_FL1, P_FL2, P_DRT,
P_STA, P_CMD, P_WCH, P_FLG, P_CGR,
P_SGD, P_SGN,
#ifdef OOMEM_ENABLE
P_OOA, P_OOM,
#endif
@ -367,6 +368,10 @@ typedef struct WIN_t {
if (!(*P)->v || !(*Q)->v) return SORT_eq; \
return Frame_srtflg * STRSORTCMP((*Q)->v[0], (*P)->v[0]); } \
return Frame_srtflg * STRSORTCMP((*Q)->s, (*P)->s); }
#define SCB_STRX(f,s) \
int strverscmp(const char *s1, const char *s2); \
static int SCB_NAME(f) (const proc_t **P, const proc_t **Q) { \
return Frame_srtflg * strverscmp((*Q)->s, (*P)->s); }
/*
* The following two macros are used to 'inline' those portions of the