]> granicus.if.org Git - procps-ng/commitdiff
supgid/supgrp support, improved library interface
authorCraig Small <csmall@enc.com.au>
Mon, 1 Aug 2011 11:28:46 +0000 (21:28 +1000)
committerCraig Small <csmall@enc.com.au>
Mon, 1 Aug 2011 11:28:46 +0000 (21:28 +1000)
 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

pmap.c
proc/library.map
proc/readproc.c
proc/readproc.h
proc/sysinfo.c
ps/display.c
ps/output.c
ps/ps.1
top.1
top.c
top.h

diff --git a/pmap.c b/pmap.c
index 6b865fc524603404906b0f73ddfb12c6b6eb7914..24a4e42734673b4656b81587cbba52a7207ca963 100644 (file)
--- a/pmap.c
+++ b/pmap.c
@@ -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);
index d09c6e1d0981e173a4c3a2e6ba6a7e03f5b58b16..fe31dacc335dbc4edebc37de03e2cb5c406b269f 100644 (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;
index f71c514c56b6da96fb928b5f1f9fe4f330ffd3a6..d30de7422080b971085918c1a09fd51aff77b5de 100644 (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");
+#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;
@@ -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){
index bf554be2e6ca7022562c4eb3ee2729a9d0068b70..2d94afaab52cde23457e86aec51b7b3a84cecd03 100644 (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
 
index 40487aa92007df81aa7d4af724fa1183c5eac740..c5f3298b3bc8a10edf979bff62372e95f403c1cd 100644 (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.
index 035a88405e7d63b562d3e2546b89ccfee10440ae..acec0e00ff0dc22a3b807ea4f67516d2a91919a1 100644 (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;
index 6063c903297dec8d959d265aa46bec574872bd44..46baa08a19b7eb2490fca53b8fa0124641551e04 100644 (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. */
diff --git a/ps/ps.1 b/ps/ps.1
index 6ce6e0f218b7955b2dd57dc45677f62b6fb936a2..18c90c56879fc010cbf891c23a3e14dd42dab6f2 100644 (file)
--- a/ps/ps.1
+++ b/ps/ps.1
@@ -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,
diff --git a/top.1 b/top.1
index e88eec220ac5c3b9a62b9eacd73bc2177c90f4df..1e4f87eb9a8abfebd150bfc05488fadb45f452fe 100644 (file)
--- a/top.1
+++ b/top.1
@@ -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).
 
diff --git a/top.c b/top.c
index 2a0002802b59bf352fbda53a9b996bee2f61c7d3..9487ed7b95845f8681ca986e0a214d08f0a0cc8f 100644 (file)
--- a/top.c
+++ b/top.c
@@ -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;
diff --git a/top.h b/top.h
index cba3dea69c5e158f72dc6d38dea857e2b089b767..34522922765dc2ce4decf0473005e8dbafc03cac 100644 (file)
--- a/top.h
+++ b/top.h
@@ -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