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);
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;
#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;
}
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 {
#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
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(;;){
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;
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:
#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
// 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)
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 */
// 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
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) {
}
}
- 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
} 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);
// 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);
}
}
}
}
-#if 0
- if ((flags & PROC_FILLCOM) || (flags & PROC_FILLARG)) /* read+parse /proc/#/cmdline */
- t->cmdline = file2strvec(path, "cmdline");
+#if 1 // begin active ------------------------
+ if (unlikely(flags & PROC_FILLENV)) // read /proc/#/task/#/environ
+ t->environ = file2strvec(path, "environ");
else
+ t->environ = NULL;
+
+ 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 (unlikely(flags & PROC_FILLENV)) /* read+parse /proc/#/environ */
- t->environ = file2strvec(path, "environ");
+ 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->environ = NULL;
-#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;
// }
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
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;
}
}
}
-// 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);
+ }
}
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;
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){
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
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 {
// 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);
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:
#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
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.
/***** 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;
}
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++;
}
}
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;
*
* Table 5 could go in a file with the output functions.
*/
-
+
#include <ctype.h>
#include <fcntl.h>
#include <grp.h>
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;
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;
}
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;
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 */
}
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);
#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)
{"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},
}
}
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;
/* 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. */
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,
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).
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).
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.
\*(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.
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.
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).
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)
#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
#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)" }
* ( 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) {
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)))
// 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;
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
}
}
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
}
}
}
. 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;
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);
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;
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
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