"Fossies" - the Fresh Open Source Software Archive

Member "atop-2.8.1/showgeneric.c" (7 Jan 2023, 75877 Bytes) of package /linux/misc/atop-2.8.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "showgeneric.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 2.7.1_vs_2.8.0.

    1 /*
    2 ** ATOP - System & Process Monitor 
    3 **
    4 ** The program 'atop' offers the possibility to view the activity of
    5 ** the system on system-level as well as process-level.
    6 ** 
    7 ** This source-file contains the print-functions to visualize the calculated
    8 ** figures.
    9 ** ==========================================================================
   10 ** Author:      Gerlof Langeveld
   11 ** E-mail:      gerlof.langeveld@atoptool.nl
   12 ** Date:        November 1996
   13 ** LINUX-port:  June 2000
   14 ** --------------------------------------------------------------------------
   15 ** Copyright (C) 2000-2010 Gerlof Langeveld
   16 **
   17 ** This program is free software; you can redistribute it and/or modify it
   18 ** under the terms of the GNU General Public License as published by the
   19 ** Free Software Foundation; either version 2, or (at your option) any
   20 ** later version.
   21 **
   22 ** This program is distributed in the hope that it will be useful, but
   23 ** WITHOUT ANY WARRANTY; without even the implied warranty of
   24 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
   25 ** See the GNU General Public License for more details.
   26 **
   27 ** You should have received a copy of the GNU General Public License
   28 ** along with this program; if not, write to the Free Software
   29 ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   30 ** --------------------------------------------------------------------------
   31 */
   32 
   33 #include <sys/types.h>
   34 #include <sys/param.h>
   35 #include <sys/stat.h>
   36 #include <sys/utsname.h>
   37 #include <signal.h>
   38 #include <ctype.h>
   39 #include <time.h>
   40 #include <stdio.h>
   41 #include <stdlib.h>
   42 #include <errno.h>
   43 #include <fcntl.h>
   44 #include <string.h>
   45 #include <stdarg.h>
   46 #include <curses.h>
   47 #include <pwd.h>
   48 #include <grp.h>
   49 #include <regex.h>
   50 #include <locale.h>
   51 #include <unistd.h>
   52 
   53 #include "atop.h"
   54 #include "photoproc.h"
   55 #include "photosyst.h"
   56 #include "showgeneric.h"
   57 #include "showlinux.h"
   58 
   59 int getresuid(uid_t *ruid, uid_t *euid, uid_t *suid);
   60 
   61 static struct pselection procsel = {"", {USERSTUB, }, {0,},
   62                                     "", 0, { 0, },  "", 0, { 0, }, "", "" };
   63 static struct sselection syssel;
   64 
   65 static void showhelp(int);
   66 static int  paused;         /* boolean: currently in pause-mode     */
   67 static int  fixedhead;  /* boolean: fixate header-lines         */
   68 static int  sysnosort;  /* boolean: suppress sort of resources  */
   69 static int  threadsort; /* boolean: sort threads per process    */
   70 static int  avgval;     /* boolean: average values i.s.o. total */
   71 static int  suppressexit;   /* boolean: suppress exited processes   */
   72 
   73 static char showtype  = MPROCGEN;
   74 static char showorder = MSORTCPU;
   75 
   76 static int  maxcpulines = 999;  /* maximum cpu       lines          */
   77 static int  maxgpulines = 999;  /* maximum gpu       lines          */
   78 static int  maxdsklines = 999;  /* maximum disk      lines          */
   79 static int  maxmddlines = 999;  /* maximum MDD       lines          */
   80 static int  maxlvmlines = 999;  /* maximum LVM       lines          */
   81 static int  maxintlines = 999;  /* maximum interface lines          */
   82 static int  maxifblines = 999;  /* maximum infinibnd lines          */
   83 static int  maxnfslines = 999;  /* maximum nfs mount lines          */
   84 static int  maxcontlines = 999; /* maximum container lines          */
   85 static int  maxnumalines = 999; /* maximum numa      lines          */
   86 static int  maxllclines = 999;  /* maximum llc       lines          */
   87 
   88 static short    colorinfo   = COLOR_GREEN;
   89 static short    coloralmost = COLOR_CYAN;
   90 static short    colorcrit   = COLOR_RED;
   91 static short    colorthread = COLOR_YELLOW;
   92 
   93 static int  cumusers(struct tstat **, struct tstat *, int);
   94 static int  cumprogs(struct tstat **, struct tstat *, int);
   95 static int  cumconts(struct tstat **, struct tstat *, int);
   96 static void accumulate(struct tstat *, struct tstat *);
   97 
   98 static int  procsuppress(struct tstat *, struct pselection *);
   99 static void limitedlines(void);
  100 static long getnumval(char *, long, int);
  101 static void generic_init(void);
  102 
  103 
  104 static int  (*procsort[])(const void *, const void *) = {
  105             [MSORTCPU&0x1f]=compcpu, 
  106             [MSORTMEM&0x1f]=compmem, 
  107             [MSORTDSK&0x1f]=compdsk, 
  108             [MSORTNET&0x1f]=compnet, 
  109             [MSORTGPU&0x1f]=compgpu, 
  110 };
  111 
  112 extern proc_printpair ownprocs[];
  113 
  114 /*
  115 ** global: incremented by -> key and decremented by <- key
  116 */
  117 int startoffset;
  118 
  119 /*
  120 ** print the deviation-counters on process- and system-level
  121 */
  122 char
  123 generic_samp(time_t curtime, int nsecs,
  124            struct devtstat *devtstat, struct sstat *sstat, 
  125            int nexit, unsigned int noverflow, char flag)
  126 {
  127     static int  callnr = 0;
  128     char        *p;
  129 
  130 
  131     register int    i, curline, statline, nproc;
  132     int     firstproc = 0, plistsz, alistsz, killpid, killsig;
  133     int     lastchar;
  134     char        format1[16], format2[16], branchtime[32];
  135     char        *statmsg = NULL, statbuf[80], genline[80];
  136     char         *lastsortp, curorder, autoorder;
  137     char        buf[33];
  138     struct passwd   *pwd;
  139     struct syscap   syscap;
  140 
  141     /*
  142     ** curlist points to the active list of tstat-pointers that
  143     ** should be displayed; ncurlist indicates the number of entries in
  144     ** this list
  145     */
  146     struct tstat    **curlist;
  147     int     ncurlist;
  148 
  149     /*
  150     ** tXcumlist is a list of tstat-structs holding one entry
  151     ** per accumulated (per user or per program) group of processes
  152     **
  153     ** Xcumlist contains the pointers to all structs in tXcumlist
  154     ** 
  155     ** these lists will only be allocated 'lazy'
  156     ** only when accumulation is requested
  157     */
  158     struct tstat    *tpcumlist = 0;     // per program accumulation
  159     struct tstat    **pcumlist = 0;
  160     int     npcum      = 0;
  161     char        plastorder = 0;
  162 
  163     struct tstat    *tucumlist = 0;     // per user accumulation
  164     struct tstat    **ucumlist = 0;
  165     int     nucum      = 0;
  166     char        ulastorder = 0;
  167 
  168     struct tstat    *tccumlist = 0;     // per container accumulation
  169     struct tstat    **ccumlist = 0;
  170     int     nccum      = 0;
  171     char        clastorder = 0;
  172 
  173     /*
  174     ** tsklist contains the pointers to all structs in tstat
  175     ** sorted on process with the related threads immediately
  176     ** following the process
  177     **
  178     ** this list will be allocated 'lazy'
  179     */
  180     struct tstat    **tsklist  = 0;
  181     int     ntsk       = 0;
  182     char        tlastorder = 0;
  183     char        zipagain   = 0;
  184     char        tdeviate   = 0;
  185 
  186     /*
  187     ** sellist contains the pointers to the structs in tstat
  188     ** that are currently selected on basis of a particular
  189     ** username (regexp), program name (regexp), container name
  190     ** or suppressed exited procs
  191     **
  192     ** this list will be allocated 'lazy'
  193     */
  194     struct tstat    **sellist  = 0;
  195     int     nsel       = 0;
  196     char        slastorder = 0;
  197 
  198     char        threadallowed = 0;
  199 
  200 
  201     if (callnr == 0)    /* first call? */
  202         generic_init();
  203 
  204     callnr++;
  205 
  206     startoffset = 0;
  207 
  208     /*
  209     ** compute the total capacity of this system for the 
  210     ** four main resources
  211     */
  212     totalcap(&syscap, sstat, devtstat->procactive, devtstat->nprocactive);
  213 
  214     /*
  215     ** sort per-cpu             statistics on busy percentage
  216     ** sort per-logical-volume      statistics on busy percentage
  217     ** sort per-multiple-device     statistics on busy percentage
  218     ** sort per-disk            statistics on busy percentage
  219     ** sort per-interface       statistics on busy percentage (if known)
  220     */
  221     if (!sysnosort)
  222     {
  223         if (sstat->cpu.nrcpu > 1 && maxcpulines > 0)
  224             qsort(sstat->cpu.cpu, sstat->cpu.nrcpu,
  225                        sizeof sstat->cpu.cpu[0], cpucompar);
  226 
  227         if (sstat->gpu.nrgpus > 1 && maxgpulines > 0)
  228             qsort(sstat->gpu.gpu, sstat->gpu.nrgpus,
  229                        sizeof sstat->gpu.gpu[0], gpucompar);
  230 
  231         if (sstat->dsk.nlvm > 1 && maxlvmlines > 0)
  232             qsort(sstat->dsk.lvm, sstat->dsk.nlvm,
  233                    sizeof sstat->dsk.lvm[0], diskcompar);
  234 
  235         if (sstat->dsk.nmdd > 1 && maxmddlines > 0)
  236             qsort(sstat->dsk.mdd, sstat->dsk.nmdd,
  237                    sizeof sstat->dsk.mdd[0], diskcompar);
  238 
  239         if (sstat->dsk.ndsk > 1 && maxdsklines > 0)
  240             qsort(sstat->dsk.dsk, sstat->dsk.ndsk,
  241                    sizeof sstat->dsk.dsk[0], diskcompar);
  242 
  243         if (sstat->intf.nrintf > 1 && maxintlines > 0)
  244             qsort(sstat->intf.intf, sstat->intf.nrintf,
  245                    sizeof sstat->intf.intf[0], intfcompar);
  246 
  247         if (sstat->ifb.nrports > 1 && maxifblines > 0)
  248             qsort(sstat->ifb.ifb, sstat->ifb.nrports,
  249                    sizeof sstat->ifb.ifb[0], ifbcompar);
  250 
  251         if (sstat->nfs.nfsmounts.nrmounts > 1 && maxnfslines > 0)
  252             qsort(sstat->nfs.nfsmounts.nfsmnt,
  253                       sstat->nfs.nfsmounts.nrmounts,
  254                   sizeof sstat->nfs.nfsmounts.nfsmnt[0],
  255                 nfsmcompar);
  256 
  257         if (sstat->cfs.nrcontainer > 1 && maxcontlines > 0)
  258             qsort(sstat->cfs.cont, sstat->cfs.nrcontainer,
  259                    sizeof sstat->cfs.cont[0], contcompar);
  260 
  261         if (sstat->memnuma.nrnuma > 1 && maxnumalines > 0)
  262             qsort(sstat->memnuma.numa, sstat->memnuma.nrnuma,
  263                    sizeof sstat->memnuma.numa[0], memnumacompar);
  264 
  265         if (sstat->cpunuma.nrnuma > 1 && maxnumalines > 0)
  266             qsort(sstat->cpunuma.numa, sstat->cpunuma.nrnuma,
  267                    sizeof sstat->cpunuma.numa[0], cpunumacompar);
  268 
  269         if (sstat->llc.nrllcs > 1 && maxllclines > 0)
  270             qsort(sstat->llc.perllc, sstat->llc.nrllcs,
  271                 sizeof sstat->llc.perllc[0], llccompar);
  272     }
  273 
  274     /*
  275     ** loop in which the system resources and the list of active
  276     ** processes are shown; the loop will be preempted by receiving
  277     ** a timer-signal or when the trigger-button is pressed.
  278     */
  279     while (1)
  280     {
  281         curline = 1;
  282         genline[0] = '\0';
  283 
  284             /*
  285             ** prepare screen or file output for new sample
  286             */
  287             if (screen)
  288                     werase(stdscr);
  289             else
  290                     printf("\n\n");
  291 
  292             /*
  293             ** print general headerlines
  294             */
  295             convdate(curtime, format1);       /* date to ascii string   */
  296             convtime(curtime, format2);       /* time to ascii string   */
  297 
  298         if (screen)
  299             attron(A_REVERSE);
  300 
  301                 int seclen  = val2elapstr(nsecs, buf);
  302                 int lenavail    = (screen ? COLS : linelen) -
  303                         52 - seclen - utsnodenamelen;
  304                 int len1    = lenavail / 3;
  305                 int len2    = lenavail - len1 - len1; 
  306 
  307         printg("ATOP - %s%*s%s  %s%*s%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%"
  308                "*s%s elapsed", 
  309             utsname.nodename, len1, "", 
  310             format1, format2, len1, "",
  311             threadview                    ? MTHREAD    : '-',
  312             threadsort                    ? MTHRSORT   : '-',
  313             fixedhead             ? MSYSFIXED  : '-',
  314             sysnosort             ? MSYSNOSORT : '-',
  315             deviatonly            ? '-'        : MALLPROC,
  316             usecolors             ? '-'        : MCOLORS,
  317             avgval                ? MAVGVAL    : '-',
  318             calcpss                   ? MCALCPSS   : '-',
  319             getwchan                  ? MGETWCHAN  : '-',
  320             suppressexit              ? MSUPEXITS  : '-',
  321             procsel.userid[0] != USERSTUB ? MSELUSER   : '-',
  322             procsel.prognamesz        ? MSELPROC   : '-',
  323             procsel.container[0]          ? MSELCONT   : '-',
  324             procsel.pid[0] != 0       ? MSELPID    : '-',
  325             procsel.argnamesz         ? MSELARG    : '-',
  326             procsel.states[0]         ? MSELSTATE  : '-',
  327             syssel.lvmnamesz +
  328             syssel.dsknamesz +
  329             syssel.itfnamesz          ? MSELSYS    : '-',
  330             len2, "", buf);
  331 
  332         if (screen)
  333             attroff(A_REVERSE);
  334                 else
  335                         printg("\n");
  336 
  337         /*
  338         ** print cumulative system- and user-time for all processes
  339         */
  340         pricumproc(sstat, devtstat, nexit, noverflow, avgval, nsecs);
  341 
  342         if (noverflow)
  343         {
  344             snprintf(statbuf, sizeof statbuf, 
  345                      "Only %d exited processes handled "
  346                      "-- %u skipped!", nexit, noverflow);
  347 
  348             statmsg = statbuf;
  349         }
  350 
  351         curline=2;
  352 
  353         /*
  354         ** print other lines of system-wide statistics
  355         */
  356         if (showorder == MSORTAUTO)
  357             autoorder = MSORTCPU;
  358         else
  359             autoorder = showorder;
  360 
  361         curline = prisyst(sstat, curline, nsecs, avgval,
  362                           fixedhead, &syssel, &autoorder,
  363                           maxcpulines, maxgpulines, maxdsklines,
  364                   maxmddlines, maxlvmlines,
  365                           maxintlines, maxifblines, maxnfslines,
  366                           maxcontlines, maxnumalines, maxllclines);
  367 
  368         /*
  369         ** if system-wide statistics do not fit,
  370         ** limit the number of variable resource lines
  371         ** and try again
  372         */
  373         if (screen && curline+2 > LINES)
  374         {
  375             curline = 2;
  376 
  377             move(curline, 0);
  378             clrtobot();
  379             move(curline, 0);
  380 
  381             limitedlines();
  382             
  383             curline = prisyst(sstat, curline, nsecs, avgval,
  384                     fixedhead,  &syssel, &autoorder,
  385                     maxcpulines, maxgpulines,
  386                     maxdsklines, maxmddlines,
  387                                 maxlvmlines, maxintlines,
  388                     maxifblines, maxnfslines,
  389                                 maxcontlines, maxnumalines,
  390                     maxllclines);
  391 
  392             /*
  393             ** if system-wide statistics still do not fit,
  394             ** the window is really to small
  395             */
  396             if (curline+2 > LINES)
  397             {
  398                 endwin();   // finish curses interface
  399 
  400                 fprintf(stderr,
  401                       "Not enough screen-lines available "
  402                       "(need at least %d lines)\n", curline+2);
  403                 fprintf(stderr, "Please resize window....\n");
  404 
  405                 cleanstop(1);
  406             }
  407             else
  408             {
  409                 statmsg = "Number of variable resources"
  410                           " limited to fit in this window";
  411             }
  412         }
  413 
  414         statline = curline;
  415 
  416         if (screen)
  417                     move(curline, 0);
  418 
  419         if (statmsg)
  420         {
  421             if (screen)
  422             {
  423                 clrtoeol();
  424                 if (usecolors)
  425                     attron(COLOR_PAIR(COLORINFO));
  426             }
  427 
  428             printg(statmsg);
  429 
  430             if (screen)
  431             {
  432                 if (usecolors)
  433                     attroff(COLOR_PAIR(COLORINFO));
  434             }
  435 
  436             statmsg = NULL;
  437         }
  438         else
  439         {
  440             if (flag&RRBOOT)
  441             {
  442                 char *initmsg = "*** System and Process Activity since Boot ***";
  443                 char *viewmsg;
  444 
  445                 if (rawreadflag)
  446                 {
  447                     viewmsg   = "Rawfile view";
  448                 }
  449                 else
  450                 {
  451                     uid_t ruid, euid, suid;
  452 
  453                     getresuid(&ruid, &euid, &suid);
  454 
  455                     if (suid == 0)
  456                     {
  457                         viewmsg   = "Unrestricted view (privileged)";
  458                     }
  459                     else
  460                     {
  461                         viewmsg   = "Restricted view (unprivileged)";
  462                     }
  463                 }
  464 
  465                 if (screen)
  466                 {
  467                     if (usecolors)
  468                         attron(COLOR_PAIR(COLORINFO));
  469 
  470                     attron(A_BLINK);
  471                 }
  472 
  473                     printg(initmsg);
  474 
  475                 if (screen)
  476                 {
  477                     if (usecolors)
  478                         attroff(COLOR_PAIR(COLORINFO));
  479                     attroff(A_BLINK);
  480 
  481                     printg("%*s", COLS - strlen(initmsg)
  482                                        - strlen(viewmsg),
  483                                          " ");
  484                 }
  485                 else
  486                 {
  487                     printg("%*s", 80 - strlen(initmsg)
  488                                      - strlen(viewmsg),
  489                                        " ");
  490                 }
  491 
  492                 if (screen)
  493                 {
  494                     if (usecolors)
  495                         attron(COLOR_PAIR(COLORALMOST));
  496 
  497                     attron(A_BLINK);
  498                 }
  499 
  500                     printg(viewmsg);
  501 
  502                 if (screen)
  503                 {
  504                     if (usecolors)
  505                         attroff(COLOR_PAIR(COLORALMOST));
  506                     attroff(A_BLINK);
  507                 }
  508 
  509             }
  510         }
  511 
  512         /*
  513         ** select the required list with tasks to be shown
  514         **
  515         ** if cumulative figures required, accumulate resource
  516         ** consumption of all processes in the current list
  517         */
  518         switch (showtype)
  519         {
  520            case MCUMUSER:
  521             threadallowed = 0;
  522 
  523             if (ucumlist)   /* previous list still available? */
  524             {
  525                                 free(ucumlist);
  526                                 free(tucumlist);
  527                 ulastorder = 0;
  528             }
  529 
  530             if (deviatonly)
  531                 nproc = devtstat->nprocactive;
  532             else
  533                 nproc = devtstat->nprocall;
  534 
  535             /*
  536             ** allocate space for new (temporary) list with
  537             ** one entry per user (list has worst-case size)
  538             */
  539             tucumlist = calloc(sizeof(struct tstat),    nproc);
  540             ucumlist  = malloc(sizeof(struct tstat *) * nproc);
  541 
  542             ptrverify(tucumlist,
  543                     "Malloc failed for %d ucum procs\n", nproc);
  544             ptrverify(ucumlist,
  545                     "Malloc failed for %d ucum ptrs\n",  nproc);
  546 
  547             for (i=0; i < nproc; i++)
  548             {
  549                 /* fill pointers */
  550                 ucumlist[i] = tucumlist+i;
  551             }
  552 
  553             nucum = cumusers(deviatonly ?
  554                         devtstat->procactive :
  555                         devtstat->procall,
  556                         tucumlist, nproc);
  557 
  558             curlist   = ucumlist;
  559             ncurlist  = nucum;
  560             lastsortp = &ulastorder;
  561             break;
  562 
  563            case MCUMPROC:
  564             threadallowed = 0;
  565 
  566             if (pcumlist)   /* previous list still available? */
  567             {
  568                                 free(pcumlist);
  569                                 free(tpcumlist);
  570                 plastorder = 0;
  571             }
  572 
  573             if (deviatonly)
  574                 nproc = devtstat->nprocactive;
  575             else
  576                 nproc = devtstat->nprocall;
  577 
  578             /*
  579             ** allocate space for new (temporary) list with
  580             ** one entry per program (list has worst-case size)
  581             */
  582             tpcumlist = calloc(sizeof(struct tstat),    nproc);
  583             pcumlist  = malloc(sizeof(struct tstat *) * nproc);
  584 
  585             ptrverify(tpcumlist,
  586                     "Malloc failed for %d pcum procs\n", nproc);
  587             ptrverify(pcumlist,
  588                     "Malloc failed for %d pcum ptrs\n",  nproc);
  589 
  590             for (i=0; i < nproc; i++)
  591             {   
  592                 /* fill pointers */
  593                 pcumlist[i] = tpcumlist+i;
  594             }
  595 
  596             npcum = cumprogs(deviatonly ?
  597                         devtstat->procactive :
  598                         devtstat->procall,
  599                         tpcumlist, nproc);
  600 
  601             curlist   = pcumlist;
  602             ncurlist  = npcum;
  603             lastsortp = &plastorder;
  604             break;
  605 
  606            case MCUMCONT:
  607             threadallowed = 0;
  608 
  609             if (ccumlist)   /* previous list still available? */
  610             {
  611                                 free(ccumlist);
  612                                 free(tccumlist);
  613                 clastorder = 0;
  614             }
  615 
  616             if (deviatonly)
  617                 nproc = devtstat->nprocactive;
  618             else
  619                 nproc = devtstat->nprocall;
  620 
  621             /*
  622             ** allocate space for new (temporary) list with
  623             ** one entry per user (list has worst-case size)
  624             */
  625             tccumlist = calloc(sizeof(struct tstat),    nproc);
  626             ccumlist  = malloc(sizeof(struct tstat *) * nproc);
  627 
  628             ptrverify(tccumlist,
  629                     "Malloc failed for %d ccum procs\n", nproc);
  630             ptrverify(ccumlist,
  631                     "Malloc failed for %d ccum ptrs\n",  nproc);
  632 
  633             for (i=0; i < nproc; i++)
  634             {
  635                 /* fill pointers */
  636                 ccumlist[i] = tccumlist+i;
  637             }
  638 
  639             nccum = cumconts(deviatonly ?
  640                         devtstat->procactive :
  641                         devtstat->procall,
  642                         tccumlist, nproc);
  643 
  644             curlist   = ccumlist;
  645             ncurlist  = nccum;
  646             lastsortp = &clastorder;
  647             break;
  648 
  649            default:
  650             threadallowed = 1;
  651 
  652             if (deviatonly && showtype  != MPROCMEM &&
  653                               showorder != MSORTMEM   )
  654             {
  655                 curlist   = devtstat->procactive;
  656                 ncurlist  = devtstat->nprocactive;
  657             }
  658             else
  659             {
  660                 curlist   = devtstat->procall;
  661                 ncurlist  = devtstat->nprocall;
  662             }
  663 
  664             lastsortp = &tlastorder;
  665 
  666             if ( procsel.userid[0] == USERSTUB &&
  667                 !procsel.prognamesz            &&
  668                 !procsel.container[0]          &&
  669                 !procsel.states[0]             &&
  670                 !procsel.argnamesz             &&
  671                 !procsel.pid[0]                &&
  672                 !suppressexit                    )
  673                 /* no selection wanted */
  674                 break;
  675 
  676             /*
  677             ** selection specified for tasks:
  678             ** create new (worst case) pointer list if needed
  679             */
  680             if (sellist)    // remove previous list if needed
  681                 free(sellist);
  682 
  683             sellist = malloc(sizeof(struct tstat *) * ncurlist);
  684 
  685             ptrverify(sellist,
  686                    "Malloc failed for %d select ptrs\n", ncurlist);
  687 
  688             for (i=nsel=0; i < ncurlist; i++)
  689             {
  690                 if (procsuppress(*(curlist+i), &procsel))
  691                     continue;
  692 
  693                 if (curlist[i]->gen.state == 'E' &&
  694                     suppressexit                   )
  695                     continue;
  696 
  697                 sellist[nsel++] = curlist[i]; 
  698             }
  699 
  700             curlist    = sellist;
  701             ncurlist   = nsel;
  702             tlastorder = 0; /* new sort and zip normal view */
  703             slastorder = 0; /* new sort and zip now         */
  704             lastsortp  = &slastorder;
  705         }
  706 
  707         /*
  708         ** sort the list in required order 
  709         ** (default CPU-consumption) and print the list
  710         */
  711         if (showorder == MSORTAUTO)
  712             curorder = autoorder;
  713         else
  714             curorder = showorder;
  715 
  716         /*
  717         ** determine size of list to be displayed
  718         */
  719         if (screen)
  720             plistsz = LINES-curline-2;
  721         else
  722             if (threadview && threadallowed)
  723                 plistsz = devtstat->ntaskactive;
  724             else
  725                 plistsz = ncurlist;
  726 
  727 
  728         if (ncurlist > 0 && plistsz > 0)
  729         {
  730             /*
  731             ** if sorting order is changed, sort again
  732             */
  733             if (*lastsortp != curorder)
  734             {
  735                 qsort(curlist, ncurlist,
  736                         sizeof(struct tstat *),
  737                         procsort[(int)curorder&0x1f]);
  738 
  739                 *lastsortp = curorder;
  740 
  741                 zipagain = 1;
  742             }
  743 
  744             if (threadview && threadallowed)
  745             {
  746                 int ntotal, j, t;
  747 
  748                 if (deviatonly && showtype  != MPROCMEM &&
  749                                       showorder != MSORTMEM   )
  750                     ntotal = devtstat->ntaskactive;
  751                 else
  752                     ntotal = devtstat->ntaskall;
  753 
  754                 /*
  755                 ** check if existing pointer list still usable
  756                 ** if not, allocate new pointer list to be able
  757                 ** to zip process list with references to threads
  758                 */
  759                 if (!tsklist || ntsk != ntotal ||
  760                             tdeviate != deviatonly)
  761                 {
  762                     if (tsklist)
  763                         free(tsklist);  // remove current
  764 
  765                     tsklist = malloc(sizeof(struct tstat *)
  766                                     * ntotal);
  767 
  768                     ptrverify(tsklist,
  769                              "Malloc failed for %d taskptrs\n",
  770                              ntotal);
  771 
  772                     ntsk     = ntotal;
  773                     tdeviate = deviatonly;
  774 
  775                     zipagain = 1;
  776                 }
  777                 else
  778                     j = ntotal;
  779 
  780                 /*
  781                 ** zip process list with thread list
  782                 */
  783                 if (zipagain)
  784                 {
  785                     struct tstat *tall = devtstat->taskall;
  786                     struct tstat *pcur;
  787                     long int     n;
  788 
  789                     for (i=j=0; i < ncurlist; i++)
  790                     {
  791                         pcur = curlist[i];
  792 
  793                         tsklist[j++] = pcur; // take process
  794 
  795                         n = j; // start index of threads
  796 
  797                         for (t = pcur - tall + 1;
  798                              t < devtstat->ntaskall &&
  799                          pcur->gen.tgid     &&
  800                              pcur->gen.tgid == 
  801                                 (tall+t)->gen.tgid;
  802                              t++)
  803                         {
  804                         if (deviatonly &&
  805                             showtype  != MPROCMEM &&
  806                                 showorder != MSORTMEM   )
  807                         {
  808                           if (!(tall+t)->gen.wasinactive)
  809                           {
  810                             tsklist[j++] = tall+t;
  811                           }
  812                         }
  813                         else
  814                             tsklist[j++] = tall+t;
  815                         }
  816 
  817                             if (threadsort && j-n > 0 &&
  818                             curorder != MSORTMEM)
  819                         {
  820                         qsort(&tsklist[n], j-n,
  821                                   sizeof(struct tstat *),
  822                                   procsort[(int)curorder&0x1f]);
  823                         }
  824                     }
  825 
  826                     zipagain = 0;
  827                 }
  828 
  829                 curlist  = tsklist;
  830                 ncurlist = j;
  831             }
  832 
  833             /*
  834             ** print the header
  835             ** first determine the column-header for the current
  836             ** sorting order of processes
  837             */
  838             if (screen)
  839             {
  840                 attron(A_REVERSE);
  841                                 move(curline+1, 0);
  842                         }
  843 
  844             priphead(firstproc/plistsz+1, (ncurlist-1)/plistsz+1,
  845                         &showtype, &curorder,
  846                     showorder == MSORTAUTO ? 1 : 0,
  847                     sstat->cpu.nrcpu);
  848 
  849             if (screen)
  850             {
  851                 attroff(A_REVERSE);
  852                 clrtobot();
  853             }
  854 
  855             /*
  856             ** print the list
  857             */
  858             priproc(curlist, firstproc, ncurlist, curline+2,
  859                     firstproc/plistsz+1, (ncurlist-1)/plistsz+1,
  860                     showtype, curorder, &syscap, nsecs, avgval);
  861         }
  862 
  863         alistsz = ncurlist; /* preserve size of active list */
  864 
  865         /*
  866         ** in case of writing to a terminal, the user can also enter
  867         ** a character to switch options, etc
  868         */
  869         if (screen)
  870         {
  871             /*
  872             ** show blinking pause-indication if necessary
  873             */
  874             if (paused)
  875             {
  876                 move(statline, COLS-6);
  877                 attron(A_BLINK);
  878                 attron(A_REVERSE);
  879                 printw("PAUSED");
  880                 attroff(A_REVERSE);
  881                 attroff(A_BLINK);
  882             }
  883 
  884             /*
  885             ** await input-character or interval-timer expiration
  886             */
  887             switch ( (lastchar = mvgetch(statline, 0)) )
  888             {
  889                /*
  890                ** timer expired
  891                */
  892                case ERR:
  893                case 0:
  894                 timeout(0);
  895                 (void) getch();
  896                 timeout(-1);
  897                 if (tpcumlist) free(tpcumlist);
  898                 if (pcumlist)  free(pcumlist);
  899                 if (tucumlist) free(tucumlist);
  900                 if (ucumlist)  free(ucumlist);
  901                 if (tccumlist) free(tccumlist);
  902                 if (ccumlist)  free(ccumlist);
  903                 if (tsklist)   free(tsklist);
  904                 if (sellist)   free(sellist);
  905 
  906                 return lastchar;    
  907 
  908                /*
  909                ** stop it
  910                */
  911                case MQUIT:
  912                 move(LINES-1, 0);
  913                 clrtoeol();
  914                 refresh();
  915                 cleanstop(0);
  916 
  917                /*
  918                ** manual trigger for next sample
  919                */
  920                case MSAMPNEXT:
  921                 if (paused)
  922                     break;
  923 
  924                 getalarm(0);
  925 
  926                 if (tpcumlist) free(tpcumlist);
  927                 if (pcumlist)  free(pcumlist);
  928                 if (tucumlist) free(tucumlist);
  929                 if (ucumlist)  free(ucumlist);
  930                 if (tccumlist) free(tccumlist);
  931                 if (ccumlist)  free(ccumlist);
  932                 if (tsklist)   free(tsklist);
  933                 if (sellist)   free(sellist);
  934 
  935                 return lastchar;
  936 
  937                /*
  938                ** manual trigger for previous sample
  939                */
  940                case MSAMPPREV:
  941                 if (!rawreadflag)
  942                 {
  943                     statmsg = "Only allowed when viewing "
  944                               "raw file!";
  945                     beep();
  946                     break;
  947                 }
  948 
  949                 if (paused)
  950                     break;
  951 
  952                 if (tpcumlist) free(tpcumlist);
  953                 if (pcumlist)  free(pcumlist);
  954                 if (tucumlist) free(tucumlist);
  955                 if (ucumlist)  free(ucumlist);
  956                 if (tccumlist) free(tccumlist);
  957                 if (ccumlist)  free(ccumlist);
  958                 if (tsklist)   free(tsklist);
  959                 if (sellist)   free(sellist);
  960 
  961                 return lastchar;
  962 
  963                            /*
  964                ** branch to certain time stamp
  965                            */
  966                            case MSAMPBRANCH:
  967                                 if (!rawreadflag)
  968                                 {
  969                                         statmsg = "Only allowed when viewing "
  970                                                   "raw file!";
  971                                         beep();
  972                                         break;
  973                                 }
  974 
  975                                 if (paused)
  976                                         break;
  977 
  978                                 echo();
  979                                 move(statline, 0);
  980                                 clrtoeol();
  981                                 printw("Enter new time "
  982                        "(format [YYYYMMDD]hhmm): ");
  983 
  984                                 branchtime[0] = '\0';
  985                                 scanw("%31s\n", branchtime);
  986                                 noecho();
  987 
  988                 begintime = cursortime;
  989 
  990                                 if ( !getbranchtime(branchtime, &begintime) )
  991                                 {
  992                                         move(statline, 0);
  993                                         clrtoeol();
  994                                         statmsg = "Wrong time format!";
  995                                         beep();
  996                     begintime = 0;
  997                                         break;
  998                                 }
  999 
 1000                 if (tpcumlist) free(tpcumlist);
 1001                 if (pcumlist)  free(pcumlist);
 1002                 if (tucumlist) free(tucumlist);
 1003                 if (ucumlist)  free(ucumlist);
 1004                 if (tccumlist) free(tccumlist);
 1005                 if (ccumlist)  free(ccumlist);
 1006                 if (tsklist)   free(tsklist);
 1007                 if (sellist)   free(sellist);
 1008 
 1009                                 return lastchar;
 1010 
 1011                /*
 1012                ** sort order automatically depending on
 1013                ** most busy resource
 1014                */
 1015                case MSORTAUTO:
 1016                 showorder = MSORTAUTO;
 1017                 firstproc = 0;
 1018                 break;
 1019 
 1020                /*
 1021                ** sort in cpu-activity order
 1022                */
 1023                case MSORTCPU:
 1024                 showorder = MSORTCPU;
 1025                 firstproc = 0;
 1026                 break;
 1027 
 1028                /*
 1029                ** sort in memory-consumption order
 1030                */
 1031                case MSORTMEM:
 1032                 showorder = MSORTMEM;
 1033                 firstproc = 0;
 1034                 break;
 1035 
 1036                /*
 1037                ** sort in disk-activity order
 1038                */
 1039                case MSORTDSK:
 1040                 if ( !(supportflags & IOSTAT) )
 1041                 {
 1042                     statmsg = "No disk-activity figures "
 1043                               "available; request ignored!";
 1044                     break;
 1045                 }
 1046                 showorder = MSORTDSK;
 1047                 firstproc = 0;
 1048                 break;
 1049 
 1050                /*
 1051                ** sort in network-activity order
 1052                */
 1053                case MSORTNET:
 1054                 if ( !(supportflags & NETATOP) )
 1055                 {
 1056                     statmsg = "Kernel module 'netatop' not "
 1057                               "active or no root privs; "
 1058                               "request ignored!";
 1059                     break;
 1060                 }
 1061                 showorder = MSORTNET;
 1062                 firstproc = 0;
 1063                 break;
 1064 
 1065                /*
 1066                ** sort in gpu-activity order
 1067                */
 1068                case MSORTGPU:
 1069                 if ( !(supportflags & GPUSTAT) )
 1070                 {
 1071                     statmsg = "No GPU activity figures "
 1072                               "available; request ignored!";
 1073                     break;
 1074                 }
 1075                 showorder = MSORTGPU;
 1076                 firstproc = 0;
 1077                 break;
 1078 
 1079                /*
 1080                ** general figures per process
 1081                */
 1082                case MPROCGEN:
 1083                 showtype  = MPROCGEN;
 1084 
 1085                 if (showorder != MSORTAUTO)
 1086                     showorder = MSORTCPU;
 1087 
 1088                 firstproc = 0;
 1089                 break;
 1090 
 1091                /*
 1092                ** memory-specific figures per process
 1093                */
 1094                case MPROCMEM:
 1095                 showtype  = MPROCMEM;
 1096 
 1097                 if (showorder != MSORTAUTO)
 1098                     showorder = MSORTMEM;
 1099 
 1100                 firstproc = 0;
 1101                 break;
 1102 
 1103                /*
 1104                ** disk-specific figures per process
 1105                */
 1106                case MPROCDSK:
 1107                 if ( !(supportflags & IOSTAT) )
 1108                 {
 1109                     statmsg = "No disk-activity figures "
 1110                               "available; request ignored!";
 1111                     break;
 1112                 }
 1113 
 1114                 showtype  = MPROCDSK;
 1115 
 1116                 if (showorder != MSORTAUTO)
 1117                     showorder = MSORTDSK;
 1118 
 1119                 firstproc = 0;
 1120                 break;
 1121 
 1122                /*
 1123                ** network-specific figures per process
 1124                */
 1125                case MPROCNET:
 1126                 if ( !(supportflags & NETATOP) )
 1127                 {
 1128                     statmsg = "Kernel module 'netatop' not "
 1129                               "active or no root privs; "
 1130                               "request ignored!";
 1131                     break;
 1132                 }
 1133 
 1134                 showtype  = MPROCNET;
 1135 
 1136                 if (showorder != MSORTAUTO)
 1137                     showorder = MSORTNET;
 1138 
 1139                 firstproc = 0;
 1140                 break;
 1141 
 1142                /*
 1143                ** GPU-specific figures per process
 1144                */
 1145                case MPROCGPU:
 1146                 if ( !(supportflags & GPUSTAT) )
 1147                 {
 1148                     statmsg = "No GPU activity figures "
 1149                               "available (atopgpud might "
 1150                               "not be running); "
 1151                               "request ignored!";
 1152                     break;
 1153                 }
 1154 
 1155                 showtype  = MPROCGPU;
 1156 
 1157                 if (showorder != MSORTAUTO)
 1158                     showorder = MSORTGPU;
 1159 
 1160                 firstproc = 0;
 1161                 break;
 1162 
 1163                /*
 1164                ** various info per process
 1165                */
 1166                case MPROCVAR:
 1167                 showtype  = MPROCVAR;
 1168                 firstproc = 0;
 1169                 break;
 1170 
 1171                /*
 1172                ** command line per process
 1173                */
 1174                case MPROCARG:
 1175                 showtype  = MPROCARG;
 1176                 firstproc = 0;
 1177                 break;
 1178 
 1179                /*
 1180                ** cgroup v2 info per process
 1181                */
 1182                case MPROCCGR:
 1183                 if ( !(supportflags & CGROUPV2) )
 1184                 {
 1185                     statmsg = "No cgroup v2 figures "
 1186                               "available; request ignored!";
 1187                     break;
 1188                 }
 1189 
 1190                 showtype  = MPROCCGR;
 1191                 firstproc = 0;
 1192                 break;
 1193 
 1194                /*
 1195                ** own defined output per process
 1196                */
 1197                case MPROCOWN:
 1198                 if (! ownprocs[0].f)
 1199                 {
 1200                     statmsg = "Own process line is not "
 1201                               "configured in rc-file; "
 1202                               "request ignored";
 1203                     break;
 1204                 }
 1205 
 1206                 showtype  = MPROCOWN;
 1207                 firstproc = 0;
 1208                 break;
 1209 
 1210                /*
 1211                ** scheduling-values per process
 1212                */
 1213                case MPROCSCH:
 1214                 showtype  = MPROCSCH;
 1215 
 1216                 if (showorder != MSORTAUTO)
 1217                     showorder = MSORTCPU;
 1218 
 1219                 firstproc = 0;
 1220                 break;
 1221 
 1222                /*
 1223                ** accumulated resource consumption per user
 1224                */
 1225                case MCUMUSER:
 1226                 statmsg = "Consumption per user; use 'a' to "
 1227                           "toggle between all/active processes";
 1228 
 1229                 showtype  = MCUMUSER;
 1230                 firstproc = 0;
 1231                 break;
 1232 
 1233                /*
 1234                ** accumulated resource consumption per program
 1235                */
 1236                case MCUMPROC:
 1237                 statmsg = "Consumption per program; use 'a' to "
 1238                           "toggle between all/active processes";
 1239 
 1240                 showtype  = MCUMPROC;
 1241                 firstproc = 0;
 1242                 break;
 1243 
 1244                /*
 1245                ** accumulated resource consumption per container
 1246                */
 1247                case MCUMCONT:
 1248                 statmsg = "Consumption per container; use 'a' to "
 1249                           "toggle between all/active processes";
 1250 
 1251                 showtype  = MCUMCONT;
 1252                 firstproc = 0;
 1253                 break;
 1254 
 1255                /*
 1256                ** help wanted?
 1257                */
 1258                case MHELP1:
 1259                case MHELP2:
 1260                 alarm(0);   /* stop the clock         */
 1261 
 1262                 move(1, 0);
 1263                 clrtobot(); /* blank the screen */
 1264                 refresh();
 1265 
 1266                 showhelp(2);
 1267 
 1268                 move(statline, 0);
 1269 
 1270                 if (interval && !paused && !rawreadflag)
 1271                     alarm(3); /* force new sample     */
 1272 
 1273                 firstproc = 0;
 1274                 break;
 1275 
 1276                /*
 1277                ** send signal to process
 1278                */
 1279                case MKILLPROC:
 1280                 if (rawreadflag)
 1281                 {
 1282                     statmsg = "Not possible when viewing "
 1283                               "raw file!";
 1284                     beep();
 1285                     break;
 1286                 }
 1287 
 1288                 alarm(0);   /* stop the clock */
 1289 
 1290                 killpid = getnumval("Pid of process: ",
 1291                              0, statline);
 1292 
 1293                 switch (killpid)
 1294                 {
 1295                    case 0:
 1296                    case -1:
 1297                     break;
 1298 
 1299                    case 1:
 1300                     statmsg = "Sending signal to pid 1 not "
 1301                               "allowed!";
 1302                     beep();
 1303                     break;
 1304 
 1305                    default:
 1306                     clrtoeol();
 1307                     killsig = getnumval("Signal [%d]: ",
 1308                              15, statline);
 1309 
 1310                     if ( kill(killpid, killsig) == -1)
 1311                     {
 1312                         statmsg = "Not possible to "
 1313                              "send signal to this pid!";
 1314                         beep();
 1315                     }
 1316                 }
 1317 
 1318                 if (!paused)
 1319                     alarm(3); /* set short timer */
 1320 
 1321                 firstproc = 0;
 1322                 break;
 1323 
 1324                /*
 1325                ** change interval timeout
 1326                */
 1327                case MINTERVAL:
 1328                 if (rawreadflag)
 1329                 {
 1330                     statmsg = "Not possible when viewing "
 1331                               "raw file!";
 1332                     beep();
 1333                     break;
 1334                 }
 1335 
 1336                 alarm(0);   /* stop the clock */
 1337 
 1338                 interval = getnumval("New interval in seconds "
 1339                              "(now %d): ",
 1340                              interval, statline);
 1341 
 1342                 if (interval)
 1343                 {
 1344                     if (!paused)
 1345                         alarm(3); /* set short timer */
 1346                 }
 1347                 else
 1348                 {
 1349                     statmsg = "No timer set; waiting for "
 1350                               "manual trigger ('t').....";
 1351                 }
 1352 
 1353                 firstproc = 0;
 1354                 break;
 1355 
 1356                /*
 1357                ** focus on specific user
 1358                */
 1359                case MSELUSER:
 1360                 alarm(0);   /* stop the clock */
 1361                 echo();
 1362 
 1363                 move(statline, 0);
 1364                 clrtoeol();
 1365                 printw("Username as regular expression "
 1366                        "(enter=all users): ");
 1367 
 1368                 procsel.username[0] = '\0';
 1369                 scanw("%255s\n", procsel.username);
 1370 
 1371                 noecho();
 1372 
 1373                 if (procsel.username[0]) /* data entered ? */
 1374                 {
 1375                     regex_t     userregex;
 1376                     int     u = 0;
 1377 
 1378                     if ( regcomp(&userregex,
 1379                         procsel.username, REG_NOSUB))
 1380                     {
 1381                         statmsg = "Invalid regular "
 1382                                   "expression!";
 1383                         beep();
 1384 
 1385                         procsel.username[0] = '\0';
 1386                     }
 1387                     else
 1388                     {
 1389                         while ( (pwd = getpwent()))
 1390                         {
 1391                             if (regexec(&userregex,
 1392                                 pwd->pw_name, 0,
 1393                                 NULL, 0))
 1394                                 continue;
 1395 
 1396                             if (u < MAXUSERSEL-1)
 1397                             {
 1398                               procsel.userid[u] =
 1399                                 pwd->pw_uid;
 1400                               u++;
 1401                             }
 1402                         }
 1403                         endpwent();
 1404 
 1405                         procsel.userid[u] = USERSTUB;
 1406 
 1407                         if (u == 0)
 1408                         {
 1409                             /*
 1410                             ** possibly a numerical
 1411                             ** value specified?
 1412                             */
 1413                             if (numeric(
 1414                                  procsel.username))
 1415                             {
 1416                              procsel.userid[0] =
 1417                              atoi(procsel.username);
 1418                              procsel.userid[1] =
 1419                                 USERSTUB;
 1420                             }
 1421                             else
 1422                             {
 1423                                  statmsg =
 1424                                 "No user-names "
 1425                                     "match this "
 1426                                 "pattern!";
 1427                                  beep();
 1428                             }
 1429                         }
 1430                     }
 1431                 }
 1432                 else
 1433                 {
 1434                     procsel.userid[0] = USERSTUB;
 1435                 }
 1436 
 1437                 if (interval && !paused && !rawreadflag)
 1438                     alarm(3);  /* set short timer */
 1439 
 1440                 firstproc = 0;
 1441                 break;
 1442 
 1443                /*
 1444                ** focus on specific process-name
 1445                */
 1446                case MSELPROC:
 1447                 alarm(0);   /* stop the clock */
 1448                 echo();
 1449 
 1450                 move(statline, 0);
 1451                 clrtoeol();
 1452                 printw("Process-name as regular "
 1453                        "expression (enter=no regex): ");
 1454 
 1455                 procsel.prognamesz  = 0;
 1456                 procsel.progname[0] = '\0';
 1457 
 1458                 scanw("%63s\n", procsel.progname);
 1459                 procsel.prognamesz = strlen(procsel.progname);
 1460 
 1461                 if (procsel.prognamesz)
 1462                 {
 1463                     if (regcomp(&procsel.progregex,
 1464                              procsel.progname, REG_NOSUB))
 1465                     {
 1466                         statmsg = "Invalid regular "
 1467                                   "expression!";
 1468                         beep();
 1469 
 1470                         procsel.prognamesz  = 0;
 1471                         procsel.progname[0] = '\0';
 1472                     }
 1473                 }
 1474 
 1475                 noecho();
 1476 
 1477                 move(statline, 0);
 1478 
 1479                 if (interval && !paused && !rawreadflag)
 1480                     alarm(3);  /* set short timer */
 1481 
 1482                 firstproc = 0;
 1483                 break;
 1484 
 1485                /*
 1486                ** focus on specific container id
 1487                */
 1488                case MSELCONT:
 1489                 alarm(0);   /* stop the clock */
 1490                 echo();
 1491 
 1492                 move(statline, 0);
 1493                 clrtoeol();
 1494                 printw("Containerid 12 postitions "
 1495                        "(enter=all, "
 1496                        "'host'=host processes): ");
 1497 
 1498                 procsel.container[0]  = '\0';
 1499                 scanw("%15s", procsel.container);
 1500                 procsel.container[12] = '\0';
 1501 
 1502                 switch (strlen(procsel.container))
 1503                 {
 1504                                    case 0:
 1505                     break;  // enter key pressed
 1506 
 1507                    case 4:  // host?
 1508                     if (strcmp(procsel.container, "host"))
 1509                     {
 1510                         statmsg="Invalid containerid!";
 1511                         beep();
 1512                         procsel.container[0] = '\0';
 1513                     }
 1514                     else
 1515                     {
 1516                         procsel.container[0] = 'H';
 1517                         procsel.container[1] = '\0';
 1518                     }
 1519                     break;
 1520 
 1521                    case 12: // container id
 1522                     (void)strtol(procsel.container, &p, 16);
 1523 
 1524                     if (*p)
 1525                     {
 1526                         statmsg ="Containerid not hex!";
 1527                         beep();
 1528                         procsel.container[0] = '\0';
 1529                     }
 1530                     break;
 1531 
 1532                    default:
 1533                     statmsg = "Invalid containerid!";
 1534                     beep();
 1535 
 1536                     procsel.container[0] = '\0';
 1537                 }
 1538 
 1539                 noecho();
 1540 
 1541                 move(statline, 0);
 1542 
 1543                 if (interval && !paused && !rawreadflag)
 1544                     alarm(3);  /* set short timer */
 1545 
 1546                 firstproc = 0;
 1547                 break;
 1548 
 1549                /*
 1550                ** focus on specific PIDs
 1551                */
 1552                case MSELPID:
 1553                 alarm(0);   /* stop the clock */
 1554                 echo();
 1555 
 1556                 move(statline, 0);
 1557                 clrtoeol();
 1558                 printw("Comma-separated PIDs of processes "
 1559                                  "(enter=no selection): ");
 1560 
 1561                 scanw("%79s\n", genline);
 1562 
 1563                 int  id = 0;
 1564 
 1565                 char *pidp = strtok(genline, ",");
 1566 
 1567                 while (pidp)
 1568                 {
 1569                     char *ep;
 1570 
 1571                     if (id >= MAXPID-1)
 1572                     {
 1573                         procsel.pid[id] = 0;    // stub
 1574 
 1575                         statmsg = "Maximum number of"
 1576                                   "PIDs reached!";
 1577                         beep();
 1578                         break;
 1579                     }
 1580 
 1581                     procsel.pid[id] = strtol(pidp, &ep, 10);
 1582 
 1583                     if (*ep)
 1584                     {
 1585                         statmsg = "Non-numerical PID!";
 1586                         beep();
 1587                         procsel.pid[0]  = 0;  // stub
 1588                         break;
 1589                     }
 1590 
 1591                     id++;
 1592                     pidp = strtok(NULL, ",");
 1593                 }
 1594 
 1595                 procsel.pid[id] = 0;    // stub
 1596 
 1597                 noecho();
 1598 
 1599                 move(statline, 0);
 1600 
 1601                 if (interval && !paused && !rawreadflag)
 1602                     alarm(3);  /* set short timer */
 1603 
 1604                 firstproc = 0;
 1605                 break;
 1606 
 1607                /*
 1608                ** focus on specific process/thread state
 1609                */
 1610                case MSELSTATE:
 1611                 alarm(0);   /* stop the clock */
 1612                 echo();
 1613 
 1614                 move(statline, 0);
 1615                 clrtoeol();
 1616 
 1617                 /* Linux fs/proc/array.c - task_state_array */
 1618                 printw("Comma-separated process/thread states "
 1619                        "(R|S|D|I|T|t|X|Z|P): ");
 1620 
 1621                 memset(procsel.states, 0, sizeof procsel.states);
 1622 
 1623                 scanw("%15s\n", genline);
 1624 
 1625                 char *sp = strtok(genline, ",");
 1626 
 1627                 while (sp && *sp)
 1628                 {
 1629                     if (isspace(*sp))
 1630                     {
 1631                         sp++;
 1632                         continue;
 1633                     }
 1634 
 1635                     if (strlen(sp) > 1)
 1636                     {
 1637                         statmsg = "Invalid state!";
 1638                         memset(procsel.states, 0,
 1639                             sizeof procsel.states);
 1640                         break;
 1641                     }
 1642 
 1643                     int needed = 0;
 1644 
 1645                     switch (*sp)
 1646                     {
 1647                         case 'R': /* running */
 1648                         case 'S': /* sleeping */
 1649                         case 'D': /* disk sleep */
 1650                         case 'I': /* idle */
 1651                         case 'T': /* stopped */
 1652                         case 't': /* tracing stop */
 1653                         case 'X': /* dead */
 1654                         case 'Z': /* zombie */
 1655                         case 'P': /* parked */
 1656                             if (!strchr(procsel.states, *sp))
 1657                                 needed = 1;
 1658                             break;
 1659                         default:
 1660                             statmsg = "Invalid state!";
 1661                             memset(procsel.states,
 1662                                 0, sizeof procsel.states);
 1663                             beep();
 1664                             break;
 1665                     }
 1666 
 1667                     if (needed)
 1668                         procsel.states[strlen(procsel.states)] = *sp;
 1669 
 1670                     sp = strtok(NULL, ",");
 1671                 }
 1672 
 1673                 noecho();
 1674 
 1675                 move(statline, 0);
 1676 
 1677                 if (interval && !paused && !rawreadflag)
 1678                     alarm(3);  /* set short timer */
 1679 
 1680                 firstproc = 0;
 1681                 break;
 1682 
 1683                /*
 1684                ** focus on specific command line arguments
 1685                */
 1686                case MSELARG:
 1687                 alarm(0);   /* stop the clock */
 1688                 echo();
 1689 
 1690                 move(statline, 0);
 1691                 clrtoeol();
 1692                 printw("Command line string as regular "
 1693                        "expression (enter=no regex): ");
 1694 
 1695                 procsel.argnamesz  = 0;
 1696                 procsel.argname[0] = '\0';
 1697 
 1698                 scanw("%63s\n", procsel.argname);
 1699                 procsel.argnamesz = strlen(procsel.argname);
 1700 
 1701                 if (procsel.argnamesz)
 1702                 {
 1703                     if (regcomp(&procsel.argregex,
 1704                              procsel.argname, REG_NOSUB))
 1705                     {
 1706                         statmsg = "Invalid regular "
 1707                                   "expression!";
 1708                         beep();
 1709 
 1710                         procsel.argnamesz  = 0;
 1711                         procsel.argname[0] = '\0';
 1712                     }
 1713                 }
 1714 
 1715                 noecho();
 1716 
 1717                 move(statline, 0);
 1718 
 1719                 if (interval && !paused && !rawreadflag)
 1720                     alarm(3);  /* set short timer */
 1721 
 1722                 firstproc = 0;
 1723                 break;
 1724 
 1725                /*
 1726                ** focus on specific system resource
 1727                */
 1728                case MSELSYS:
 1729                 alarm(0);   /* stop the clock */
 1730                 echo();
 1731 
 1732                 move(statline, 0);
 1733                 clrtoeol();
 1734                 printw("Logical volume name as regular "
 1735                        "expression (enter=no specific name): ");
 1736 
 1737                 syssel.lvmnamesz  = 0;
 1738                 syssel.lvmname[0] = '\0';
 1739 
 1740                 scanw("%63s\n", syssel.lvmname);
 1741                 syssel.lvmnamesz = strlen(syssel.lvmname);
 1742 
 1743                 if (syssel.lvmnamesz)
 1744                 {
 1745                     if (regcomp(&syssel.lvmregex,
 1746                              syssel.lvmname, REG_NOSUB))
 1747                     {
 1748                         statmsg = "Invalid regular "
 1749                                   "expression!";
 1750                         beep();
 1751 
 1752                         syssel.lvmnamesz  = 0;
 1753                         syssel.lvmname[0] = '\0';
 1754                     }
 1755                 }
 1756 
 1757                 move(statline, 0);
 1758                 clrtoeol();
 1759                 printw("Disk name as regular "
 1760                        "expression (enter=no specific name): ");
 1761 
 1762                 syssel.dsknamesz  = 0;
 1763                 syssel.dskname[0] = '\0';
 1764 
 1765                 scanw("%63s\n", syssel.dskname);
 1766                 syssel.dsknamesz = strlen(syssel.dskname);
 1767 
 1768                 if (syssel.dsknamesz)
 1769                 {
 1770                     if (regcomp(&syssel.dskregex,
 1771                              syssel.dskname, REG_NOSUB))
 1772                     {
 1773                         statmsg = "Invalid regular "
 1774                                   "expression!";
 1775                         beep();
 1776 
 1777                         syssel.dsknamesz  = 0;
 1778                         syssel.dskname[0] = '\0';
 1779                     }
 1780                 }
 1781 
 1782                 move(statline, 0);
 1783                 clrtoeol();
 1784                 printw("Interface name as regular "
 1785                        "expression (enter=no specific name): ");
 1786 
 1787                 syssel.itfnamesz  = 0;
 1788                 syssel.itfname[0] = '\0';
 1789 
 1790                 scanw("%63s\n", syssel.itfname);
 1791                 syssel.itfnamesz = strlen(syssel.itfname);
 1792 
 1793                 if (syssel.itfnamesz)
 1794                 {
 1795                     if (regcomp(&syssel.itfregex,
 1796                              syssel.itfname, REG_NOSUB))
 1797                     {
 1798                         statmsg = "Invalid regular "
 1799                                   "expression!";
 1800                         beep();
 1801 
 1802                         syssel.itfnamesz  = 0;
 1803                         syssel.itfname[0] = '\0';
 1804                     }
 1805                 }
 1806 
 1807                 noecho();
 1808 
 1809                 move(statline, 0);
 1810 
 1811                 if (interval && !paused && !rawreadflag)
 1812                     alarm(3);  /* set short timer */
 1813 
 1814                 firstproc = 0;
 1815                 break;
 1816 
 1817                /*
 1818                ** toggle pause-state
 1819                */
 1820                case MPAUSE:
 1821                 if (paused)
 1822                 {
 1823                     paused=0;
 1824                     clrtoeol();
 1825                     refresh();
 1826 
 1827                     if (!rawreadflag)
 1828                         alarm(1);
 1829                 }
 1830                 else
 1831                 {
 1832                     paused=1;
 1833                     clrtoeol();
 1834                     refresh();
 1835                     alarm(0);   /* stop the clock */
 1836                 }
 1837                 break;
 1838 
 1839                /*
 1840                ** toggle between modified processes and
 1841                ** all processes
 1842                */
 1843                case MALLPROC:
 1844                 if (deviatonly)
 1845                 {
 1846                     deviatonly=0;
 1847                     statmsg = "All processes/threads will be "
 1848                               "shown/accumulated...";
 1849                 }
 1850                 else
 1851                 {
 1852                     deviatonly=1;
 1853                     statmsg = "Only active processes/threads "
 1854                               "will be shown/accumulated...";
 1855                 }
 1856 
 1857                 tlastorder = 0;
 1858                 firstproc  = 0;
 1859                 break;
 1860 
 1861                /*
 1862                ** toggle average or total values
 1863                */
 1864                case MAVGVAL:
 1865                 if (avgval)
 1866                     avgval=0;
 1867                 else
 1868                     avgval=1;
 1869                 break;
 1870 
 1871                /*
 1872                ** system-statistics lines:
 1873                **            toggle fixed or variable
 1874                */
 1875                case MSYSFIXED:
 1876                 if (fixedhead)
 1877                 {
 1878                     fixedhead=0;
 1879                     statmsg = "Only active system-resources"
 1880                               " will be shown ......";
 1881                 }
 1882                 else
 1883                 {
 1884                     fixedhead=1;
 1885                     statmsg = "Also inactive "
 1886                       "system-resources will be shown.....";
 1887                 }
 1888 
 1889                 firstproc = 0;
 1890                 break;
 1891 
 1892                /*
 1893                ** system-statistics lines:
 1894                **            toggle fixed or variable
 1895                */
 1896                case MSYSNOSORT:
 1897                 if (sysnosort)
 1898                 {
 1899                     sysnosort=0;
 1900                     statmsg = "System resources will be "
 1901                               "sorted on utilization...";
 1902                 }
 1903                 else
 1904                 {
 1905                     sysnosort=1;
 1906                     statmsg = "System resources will not "
 1907                               "be sorted on utilization...";
 1908                 }
 1909 
 1910                 firstproc = 0;
 1911                 break;
 1912 
 1913                /*
 1914                ** per-thread view wanted with sorting on
 1915                ** process level
 1916                */
 1917                case MTHREAD:
 1918                 if (threadview)
 1919                 {
 1920                     threadview = 0;
 1921                     statmsg    = "Thread view disabled";
 1922                     firstproc  = 0;
 1923                 }
 1924                 else
 1925                 {
 1926                     threadview = 1;
 1927                     statmsg    = "Thread view enabled";
 1928                     firstproc  = 0;
 1929                 }
 1930                 break;
 1931 
 1932                /*
 1933                ** sorting on thread level as well (threadview)
 1934                */
 1935                case MTHRSORT:
 1936                 if (threadsort)
 1937                 {
 1938                     threadsort = 0;
 1939                     statmsg    = "Thread sorting disabled for thread view";
 1940                     firstproc  = 0;
 1941                 }
 1942                 else
 1943                 {
 1944                     threadsort = 1;
 1945                     statmsg    = "Thread sorting enabled for thread view";
 1946                     firstproc  = 0;
 1947                 }
 1948                 break;
 1949 
 1950                /*
 1951                ** per-process PSS calculation wanted 
 1952                */
 1953                case MCALCPSS:
 1954                 if (calcpss)
 1955                 {
 1956                     calcpss    = 0;
 1957                     statmsg    = "PSIZE gathering disabled";
 1958                 }
 1959                 else
 1960                 {
 1961                     calcpss    = 1;
 1962                     statmsg    = "PSIZE gathering enabled";
 1963                 }
 1964                 break;
 1965 
 1966                /*
 1967                ** per-thread WCHAN definition 
 1968                */
 1969                case MGETWCHAN:
 1970                 if (getwchan)
 1971                 {
 1972                     getwchan   = 0;
 1973                     statmsg    = "WCHAN gathering disabled";
 1974                 }
 1975                 else
 1976                 {
 1977                     getwchan   = 1;
 1978                     statmsg    = "WCHAN gathering enabled";
 1979                 }
 1980                 break;
 1981 
 1982                /*
 1983                ** suppression of exited processes in output
 1984                */
 1985                case MSUPEXITS:
 1986                 if (suppressexit)
 1987                 {
 1988                     suppressexit = 0;
 1989                     statmsg      = "Exited processes will "
 1990                                    "be shown/accumulated";
 1991                     firstproc    = 0;
 1992                 }
 1993                 else
 1994                 {
 1995                     suppressexit = 1;
 1996                     statmsg      = "Exited processes will "
 1997                                  "not be shown/accumulated";
 1998                     firstproc    = 0;
 1999                 }
 2000                 break;
 2001 
 2002                /*
 2003                ** screen lines:
 2004                **            toggle for colors
 2005                */
 2006                case MCOLORS:
 2007                 if (usecolors)
 2008                 {
 2009                     usecolors=0;
 2010                     statmsg = "No colors will be used...";
 2011                 }
 2012                 else
 2013                 {
 2014                     if (screen && has_colors())
 2015                     {
 2016                         usecolors=1;
 2017                         statmsg =
 2018                            "Colors will be used...";
 2019                     }
 2020                     else
 2021                     {
 2022                         statmsg="No colors supported!";
 2023                     }
 2024                 }
 2025 
 2026                 firstproc = 0;
 2027                 break;
 2028 
 2029                /*
 2030                ** system-statistics lines:
 2031                **            toggle no or all active disk
 2032                */
 2033                case MSYSLIMIT:
 2034                 alarm(0);   /* stop the clock */
 2035 
 2036                 maxcpulines =
 2037                   getnumval("Maximum lines for per-cpu "
 2038                             "statistics (now %d): ",
 2039                             maxcpulines, statline);
 2040 
 2041                 maxgpulines =
 2042                   getnumval("Maximum lines for per-gpu "
 2043                             "statistics (now %d): ",
 2044                             maxgpulines, statline);
 2045 
 2046                 if (sstat->dsk.nlvm > 0)
 2047                 {
 2048                     maxlvmlines =
 2049                       getnumval("Maximum lines for LVM "
 2050                             "statistics (now %d): ",
 2051                             maxlvmlines, statline);
 2052                 }
 2053 
 2054                 if (sstat->dsk.nmdd > 0)
 2055                 {
 2056                     maxmddlines =
 2057                       getnumval("Maximum lines for MD "
 2058                         "device statistics (now %d): ",
 2059                             maxmddlines, statline);
 2060                 }
 2061 
 2062                 maxdsklines =
 2063                   getnumval("Maximum lines for disk "
 2064                             "statistics (now %d): ",
 2065                             maxdsklines, statline);
 2066 
 2067                 maxintlines =
 2068                   getnumval("Maximum lines for interface "
 2069                             "statistics (now %d): ",
 2070                         maxintlines, statline);
 2071 
 2072                 maxifblines =
 2073                   getnumval("Maximum lines for infiniband "
 2074                             "port statistics (now %d): ",
 2075                         maxifblines, statline);
 2076 
 2077                 maxnfslines =
 2078                   getnumval("Maximum lines for NFS mount "
 2079                             "statistics (now %d): ",
 2080                         maxnfslines, statline);
 2081 
 2082                 maxcontlines =
 2083                   getnumval("Maximum lines for container "
 2084                             "statistics (now %d): ",
 2085                         maxcontlines, statline);
 2086 
 2087                 maxnumalines =
 2088                   getnumval("Maximum lines for numa "
 2089                             "statistics (now %d): ",
 2090                         maxnumalines, statline);
 2091 
 2092                 maxllclines =
 2093                   getnumval("Maximum lines for LLC "
 2094                             "statistics (now %d): ",
 2095                         maxllclines, statline);
 2096 
 2097                 if (interval && !paused && !rawreadflag)
 2098                     alarm(3);  /* set short timer */
 2099 
 2100                 firstproc = 0;
 2101                 break;
 2102 
 2103                /*
 2104                ** reset statistics 
 2105                */
 2106                case MRESET:
 2107                 getalarm(0);    /* restart the clock */
 2108                 paused = 0;
 2109 
 2110                 if (tpcumlist) free(tpcumlist);
 2111                 if (pcumlist)  free(pcumlist);
 2112                 if (tucumlist) free(tucumlist);
 2113                 if (ucumlist)  free(ucumlist);
 2114                 if (tccumlist) free(tccumlist);
 2115                 if (ccumlist)  free(ccumlist);
 2116                 if (tsklist)   free(tsklist);
 2117                 if (sellist)   free(sellist);
 2118 
 2119                 return lastchar;
 2120 
 2121                /*
 2122                ** show version info
 2123                */
 2124                case MVERSION:
 2125                 statmsg = getstrvers();
 2126                 break;
 2127 
 2128                /*
 2129                ** handle redraw request
 2130                */
 2131                case MREDRAW:
 2132                                 wclear(stdscr);
 2133                 break;
 2134 
 2135                /*
 2136                ** handle arrow right for command line
 2137                */
 2138                case KEY_RIGHT:
 2139                 startoffset++;
 2140                 break;
 2141 
 2142                /*
 2143                ** handle arrow left for command line
 2144                */
 2145                case KEY_LEFT:
 2146                 if (startoffset > 0)
 2147                     startoffset--;
 2148                 break;
 2149 
 2150                /*
 2151                ** handle arrow down to go one line down
 2152                */
 2153                case KEY_DOWN:
 2154                 if (firstproc < alistsz-1)
 2155                     firstproc += 1;
 2156                 break;
 2157 
 2158                /*
 2159                ** handle arrow up to go one line up
 2160                */
 2161                case KEY_UP: 
 2162                 if (firstproc > 0)
 2163                     firstproc -= 1;
 2164                 break;
 2165 
 2166                /*
 2167                ** handle forward
 2168                */
 2169                case KEY_NPAGE:
 2170                case MLISTFW:
 2171                 if (alistsz-firstproc > plistsz)
 2172                     firstproc += plistsz;
 2173                 break;
 2174 
 2175                /*
 2176                ** handle backward
 2177                */
 2178                case KEY_PPAGE:
 2179                case MLISTBW:
 2180                 if (firstproc >= plistsz)
 2181                     firstproc -= plistsz;
 2182                 else
 2183                     firstproc = 0;
 2184                 break;
 2185 
 2186                /*
 2187                ** handle screen resize
 2188                */
 2189                case KEY_RESIZE:
 2190                 snprintf(statbuf, sizeof statbuf, 
 2191                     "Window resized to %dx%d...",
 2192                             COLS, LINES);
 2193                 statmsg = statbuf;
 2194 
 2195                 timeout(0);
 2196                 (void) getch();
 2197                 timeout(-1);
 2198                 break;
 2199 
 2200                /*
 2201                ** unknown key-stroke
 2202                */
 2203                default:
 2204                     beep();
 2205             }
 2206         }
 2207         else    /* no screen */
 2208         {
 2209             if (tpcumlist) free(tpcumlist);
 2210             if (pcumlist)  free(pcumlist);
 2211             if (tucumlist) free(tucumlist);
 2212             if (ucumlist)  free(ucumlist);
 2213             if (tccumlist) free(tccumlist);
 2214             if (ccumlist)  free(ccumlist);
 2215             if (tsklist)   free(tsklist);
 2216             if (sellist)   free(sellist);
 2217 
 2218             return '\0';
 2219         }
 2220     }
 2221 }
 2222 
 2223 /*
 2224 ** accumulate all processes per user in new list
 2225 */
 2226 static int
 2227 cumusers(struct tstat **curprocs, struct tstat *curusers, int numprocs)
 2228 {
 2229     register int    i, numusers;
 2230 
 2231     /*
 2232     ** sort list of active processes in order of uid (increasing)
 2233     */
 2234     qsort(curprocs, numprocs, sizeof(struct tstat *), compusr);
 2235 
 2236     /*
 2237     ** accumulate all processes per user in the new list
 2238     */
 2239     for (numusers=i=0; i < numprocs; i++, curprocs++)
 2240     {
 2241         if (procsuppress(*curprocs, &procsel))
 2242             continue;
 2243 
 2244         if ((*curprocs)->gen.state == 'E' && suppressexit)
 2245             continue;
 2246  
 2247         if ( curusers->gen.ruid != (*curprocs)->gen.ruid )
 2248         {
 2249             if (curusers->gen.pid)
 2250             {
 2251                 numusers++;
 2252                 curusers++;
 2253             }
 2254             curusers->gen.ruid = (*curprocs)->gen.ruid;
 2255         }
 2256 
 2257         accumulate(*curprocs, curusers);
 2258     }
 2259 
 2260     if (curusers->gen.pid)
 2261         numusers++;
 2262 
 2263     return numusers;
 2264 }
 2265 
 2266 
 2267 /*
 2268 ** accumulate all processes with the same name (i.e. same program)
 2269 ** into a new list
 2270 */
 2271 static int
 2272 cumprogs(struct tstat **curprocs, struct tstat *curprogs, int numprocs)
 2273 {
 2274     register int    i, numprogs;
 2275 
 2276     /*
 2277     ** sort list of active processes in order of process-name
 2278     */
 2279     qsort(curprocs, numprocs, sizeof(struct tstat *), compnam);
 2280 
 2281     /*
 2282     ** accumulate all processes with same name in the new list
 2283     */
 2284     for (numprogs=i=0; i < numprocs; i++, curprocs++)
 2285     {
 2286         if (procsuppress(*curprocs, &procsel))
 2287             continue;
 2288 
 2289         if ((*curprocs)->gen.state == 'E' && suppressexit)
 2290             continue;
 2291 
 2292         if ( strcmp(curprogs->gen.name, (*curprocs)->gen.name) != 0)
 2293         {
 2294             if (curprogs->gen.pid)
 2295             {
 2296                 numprogs++;
 2297                 curprogs++;
 2298             }
 2299             strcpy(curprogs->gen.name, (*curprocs)->gen.name);
 2300         }
 2301 
 2302         accumulate(*curprocs, curprogs);
 2303     }
 2304 
 2305     if (curprogs->gen.pid)
 2306         numprogs++;
 2307 
 2308     return numprogs;
 2309 }
 2310 
 2311 /*
 2312 ** accumulate all processes per container in new list
 2313 */
 2314 static int
 2315 cumconts(struct tstat **curprocs, struct tstat *curconts, int numprocs)
 2316 {
 2317     register int    i, numconts;
 2318 
 2319     /*
 2320     ** sort list of active processes in order of container (increasing)
 2321     */
 2322     qsort(curprocs, numprocs, sizeof(struct tstat *), compcon);
 2323 
 2324     /*
 2325     ** accumulate all processes per container in the new list
 2326     */
 2327     for (numconts=i=0; i < numprocs; i++, curprocs++)
 2328     {
 2329         if (procsuppress(*curprocs, &procsel))
 2330             continue;
 2331 
 2332         if ((*curprocs)->gen.state == 'E' && suppressexit)
 2333             continue;
 2334  
 2335         if ( strcmp(curconts->gen.container,
 2336                          (*curprocs)->gen.container) != 0)
 2337         {
 2338             if (curconts->gen.pid)
 2339             {
 2340                 numconts++;
 2341                 curconts++;
 2342             }
 2343             strcpy(curconts->gen.container,
 2344                 (*curprocs)->gen.container);
 2345         }
 2346 
 2347         accumulate(*curprocs, curconts);
 2348     }
 2349 
 2350     if (curconts->gen.pid)
 2351         numconts++;
 2352 
 2353     return numconts;
 2354 }
 2355 
 2356 
 2357 /*
 2358 ** accumulate relevant counters from individual task to
 2359 ** combined task
 2360 */
 2361 static void
 2362 accumulate(struct tstat *curproc, struct tstat *curstat)
 2363 {
 2364     count_t     nett_wsz;
 2365 
 2366     curstat->gen.pid++;     /* misuse as counter */
 2367 
 2368     curstat->gen.isproc  = 1;
 2369     curstat->gen.nthr   += curproc->gen.nthr;
 2370     curstat->cpu.utime  += curproc->cpu.utime;
 2371     curstat->cpu.stime  += curproc->cpu.stime;
 2372 
 2373     if (curproc->dsk.wsz > curproc->dsk.cwsz)
 2374                 nett_wsz = curproc->dsk.wsz -curproc->dsk.cwsz;
 2375     else
 2376         nett_wsz = 0;
 2377 
 2378     curstat->dsk.rio    += curproc->dsk.rsz;
 2379     curstat->dsk.wio    += nett_wsz;
 2380 
 2381     curstat->dsk.rsz     = curstat->dsk.rio;
 2382     curstat->dsk.wsz     = curstat->dsk.wio;
 2383 
 2384     curstat->net.tcpsnd += curproc->net.tcpsnd;
 2385     curstat->net.tcprcv += curproc->net.tcprcv;
 2386     curstat->net.udpsnd += curproc->net.udpsnd;
 2387     curstat->net.udprcv += curproc->net.udprcv;
 2388 
 2389     curstat->net.tcpssz += curproc->net.tcpssz;
 2390     curstat->net.tcprsz += curproc->net.tcprsz;
 2391     curstat->net.udpssz += curproc->net.udpssz;
 2392     curstat->net.udprsz += curproc->net.udprsz;
 2393 
 2394     if (curproc->gen.state != 'E')
 2395     {
 2396         if (curstat->mem.pmem != -1)
 2397         {
 2398             if  (curproc->mem.pmem != -1)  // no errors?
 2399                 curstat->mem.pmem += curproc->mem.pmem;
 2400             else
 2401                 curstat->mem.pmem  = -1;
 2402         }
 2403 
 2404         curstat->mem.vmem   += curproc->mem.vmem;
 2405         curstat->mem.rmem   += curproc->mem.rmem;
 2406         curstat->mem.vlibs  += curproc->mem.vlibs;
 2407         curstat->mem.vdata  += curproc->mem.vdata;
 2408         curstat->mem.vstack += curproc->mem.vstack;
 2409         curstat->mem.vswap  += curproc->mem.vswap;
 2410         curstat->mem.vlock  += curproc->mem.vlock;
 2411         curstat->mem.rgrow  += curproc->mem.rgrow;
 2412         curstat->mem.vgrow  += curproc->mem.vgrow;
 2413 
 2414         if (curproc->gpu.state)     // GPU is use?
 2415         {
 2416             int i;
 2417 
 2418             curstat->gpu.state = 'A';
 2419 
 2420             if (curproc->gpu.gpubusy == -1)
 2421                 curstat->gpu.gpubusy  = -1;
 2422             else
 2423                 curstat->gpu.gpubusy += curproc->gpu.gpubusy;
 2424 
 2425             if (curproc->gpu.membusy == -1)
 2426                 curstat->gpu.membusy  = -1;
 2427             else
 2428                 curstat->gpu.membusy += curproc->gpu.membusy;
 2429 
 2430             curstat->gpu.memnow  += curproc->gpu.memnow;
 2431             curstat->gpu.gpulist |= curproc->gpu.gpulist;
 2432             curstat->gpu.nrgpus   = 0;
 2433 
 2434             for (i=0; i < MAXGPU; i++)
 2435             {
 2436                 if (curstat->gpu.gpulist & 1<<i)
 2437                     curstat->gpu.nrgpus++;
 2438             }
 2439         }
 2440     }
 2441 }
 2442 
 2443 
 2444 /*
 2445 ** function that checks if the current process is selected or suppressed;
 2446 ** returns 1 (suppress) or 0 (do not suppress)
 2447 */
 2448 static int
 2449 procsuppress(struct tstat *curstat, struct pselection *sel)
 2450 {
 2451     /*
 2452     ** check if only processes of a particular user
 2453     ** should be shown
 2454     */
 2455     if (sel->userid[0] != USERSTUB)
 2456     {
 2457         int     u = 0;
 2458 
 2459         while (sel->userid[u] != USERSTUB)
 2460         {
 2461             if (sel->userid[u] == curstat->gen.ruid)
 2462                 break;
 2463             u++;
 2464         }
 2465 
 2466         if (sel->userid[u] != curstat->gen.ruid)
 2467             return 1;
 2468     }
 2469 
 2470     /*
 2471     ** check if only processes with particular PIDs
 2472     ** should be shown
 2473     */
 2474     if (sel->pid[0])
 2475     {
 2476         int i = 0;
 2477 
 2478         while (sel->pid[i])
 2479         {
 2480             if (sel->pid[i] == curstat->gen.pid)
 2481                 break;
 2482             i++;
 2483         }
 2484 
 2485         if (sel->pid[i] != curstat->gen.pid)
 2486             return 1;
 2487     }
 2488 
 2489     /*
 2490     ** check if only processes with a particular name
 2491     ** should be shown
 2492     */
 2493     if (sel->prognamesz &&
 2494         regexec(&(sel->progregex), curstat->gen.name, 0, NULL, 0))
 2495         return 1;
 2496 
 2497     /*
 2498     ** check if only processes with a particular command line string
 2499     ** should be shown
 2500     */
 2501     if (sel->argnamesz)
 2502     {
 2503         if (curstat->gen.cmdline[0])
 2504         {
 2505             if (regexec(&(sel->argregex), curstat->gen.cmdline,
 2506                                 0, NULL, 0))
 2507                 return 1;
 2508         }
 2509         else
 2510         {
 2511             if (regexec(&(sel->argregex), curstat->gen.name,
 2512                                 0, NULL, 0))
 2513                 return 1;
 2514         }
 2515     }
 2516 
 2517     /*
 2518     ** check if only processes related to a particular container
 2519     ** should be shown (container 'H' stands for native host processes)
 2520     */
 2521     if (sel->container[0])
 2522     {
 2523         if (sel->container[0] == 'H')   // only host processes
 2524         {
 2525             if (curstat->gen.container[0])
 2526                 return 1;
 2527         }
 2528         else
 2529         {
 2530             if (memcmp(sel->container, curstat->gen.container, 12))
 2531                 return 1;
 2532         }
 2533     }
 2534 
 2535     /*
 2536     ** check if only processes in specific states should be shown 
 2537     */
 2538     if (sel->states[0])
 2539     {
 2540         if (strchr(sel->states, curstat->gen.state) == NULL)
 2541             return 1;
 2542     }
 2543 
 2544     return 0;
 2545 }
 2546 
 2547 
 2548 static void
 2549 limitedlines(void)
 2550 {
 2551     if (maxcpulines == 999)     // default?
 2552         maxcpulines  = 0;
 2553 
 2554     if (maxgpulines == 999)     // default?
 2555         maxgpulines  = 2;
 2556 
 2557     if (maxdsklines == 999)     // default?
 2558         maxdsklines  = 3;
 2559 
 2560     if (maxmddlines == 999)     // default?
 2561         maxmddlines  = 3;
 2562 
 2563     if (maxlvmlines == 999)     // default?
 2564         maxlvmlines  = 4;
 2565 
 2566     if (maxintlines == 999)     // default?
 2567         maxintlines  = 2;
 2568 
 2569     if (maxifblines == 999)     // default?
 2570         maxifblines  = 2;
 2571 
 2572     if (maxnfslines == 999)     // default?
 2573         maxnfslines  = 2;
 2574 
 2575     if (maxcontlines == 999)    // default?
 2576         maxcontlines = 1;
 2577 
 2578     if (maxnumalines == 999)    // default?
 2579         maxnumalines = 0;
 2580 
 2581     if (maxllclines  == 999)    // default?
 2582         maxllclines = 0;
 2583 }
 2584 
 2585 /*
 2586 ** get a numerical value from the user and verify 
 2587 */
 2588 static long
 2589 getnumval(char *ask, long valuenow, int statline)
 2590 {
 2591     char numval[16];
 2592     long retval;
 2593 
 2594     echo();
 2595     move(statline, 0);
 2596     clrtoeol();
 2597     printw(ask, valuenow);
 2598 
 2599     numval[0] = 0;
 2600     scanw("%15s", numval);
 2601 
 2602     move(statline, 0);
 2603     noecho();
 2604 
 2605     if (numval[0])  /* data entered ? */
 2606     {
 2607         if ( numeric(numval) )
 2608         {
 2609             retval = atol(numval);
 2610         }
 2611         else
 2612         {
 2613             beep();
 2614             clrtoeol();
 2615             printw("Value not numeric (current value kept)!");
 2616             refresh();
 2617             sleep(2);
 2618             retval = valuenow;
 2619         }
 2620     }
 2621     else
 2622     {
 2623         retval = valuenow;
 2624     }
 2625 
 2626     return retval;
 2627 }
 2628 
 2629 /*
 2630 ** generic print-function which checks if printf should be used
 2631 ** (to file or pipe) or curses (to screen)
 2632 */
 2633 void
 2634 printg(const char *format, ...)
 2635 {
 2636     va_list args;
 2637 
 2638     va_start(args, format);
 2639 
 2640     if (screen)
 2641         vw_printw(stdscr, (char *) format, args);
 2642     else
 2643         vprintf(format, args);
 2644 
 2645     va_end  (args);
 2646 }
 2647 
 2648 /*
 2649 ** initialize generic sample output functions
 2650 */
 2651 static void
 2652 generic_init(void)
 2653 {
 2654     int i;
 2655 
 2656     /*
 2657     ** check if default sort order and/or showtype are overruled
 2658     ** by command-line flags
 2659     */
 2660     for (i=0; flaglist[i]; i++)
 2661     {
 2662         switch (flaglist[i])
 2663         {
 2664            case MSORTAUTO:
 2665             showorder = MSORTAUTO;
 2666             break;
 2667 
 2668            case MSORTCPU:
 2669             showorder = MSORTCPU;
 2670             break;
 2671 
 2672            case MSORTGPU:
 2673             showorder = MSORTGPU;
 2674             break;
 2675 
 2676            case MSORTMEM:
 2677             showorder = MSORTMEM;
 2678             break;
 2679 
 2680            case MSORTDSK:
 2681             showorder = MSORTDSK;
 2682             break;
 2683 
 2684            case MSORTNET:
 2685             showorder = MSORTNET;
 2686             break;
 2687 
 2688            case MPROCGEN:
 2689             showtype  = MPROCGEN;
 2690             showorder = MSORTCPU;
 2691             break;
 2692 
 2693            case MPROCGPU:
 2694             showtype  = MPROCGPU;
 2695             showorder = MSORTGPU;
 2696             break;
 2697 
 2698            case MPROCMEM:
 2699             showtype  = MPROCMEM;
 2700             showorder = MSORTMEM;
 2701             break;
 2702 
 2703            case MPROCSCH:
 2704             showtype  = MPROCSCH;
 2705             showorder = MSORTCPU;
 2706             break;
 2707 
 2708            case MPROCDSK:
 2709             if ( !(supportflags & IOSTAT) )
 2710             {
 2711                 fprintf(stderr,
 2712                     "No disk-activity figures "
 2713                         "available; request ignored\n");
 2714                 sleep(3);
 2715                 break;
 2716             }
 2717 
 2718             showtype  = MPROCDSK;
 2719             showorder = MSORTDSK;
 2720             break;
 2721 
 2722            case MPROCNET:
 2723             if ( !(supportflags & NETATOP) )
 2724             {
 2725                 fprintf(stderr, "Kernel module 'netatop' not "
 2726                               "active; request ignored!\n");
 2727                 sleep(3);
 2728                 break;
 2729             }
 2730 
 2731             showtype  = MPROCNET;
 2732             showorder = MSORTNET;
 2733             break;
 2734 
 2735            case MPROCVAR:
 2736             showtype  = MPROCVAR;
 2737             break;
 2738 
 2739            case MPROCARG:
 2740             showtype  = MPROCARG;
 2741             break;
 2742 
 2743            case MPROCCGR:
 2744             if ( !(supportflags & CGROUPV2) )
 2745             {
 2746                 fprintf(stderr, "No cgroup v2 details "
 2747                           "available; request ignored!\n");
 2748                 sleep(3);
 2749                 break;
 2750             }
 2751 
 2752             showtype  = MPROCCGR;
 2753             break;
 2754 
 2755            case MPROCOWN:
 2756             showtype  = MPROCOWN;
 2757             break;
 2758 
 2759            case MAVGVAL:
 2760             if (avgval)
 2761                 avgval=0;
 2762             else
 2763                 avgval=1;
 2764             break;
 2765 
 2766            case MCUMUSER:
 2767             showtype  = MCUMUSER;
 2768             break;
 2769 
 2770            case MCUMPROC:
 2771             showtype  = MCUMPROC;
 2772             break;
 2773 
 2774            case MCUMCONT:
 2775             showtype  = MCUMCONT;
 2776             break;
 2777 
 2778            case MSYSFIXED:
 2779             if (fixedhead)
 2780                 fixedhead=0;
 2781             else
 2782                 fixedhead=1;
 2783             break;
 2784 
 2785            case MSYSNOSORT:
 2786             if (sysnosort)
 2787                 sysnosort=0;
 2788             else
 2789                 sysnosort=1;
 2790             break;
 2791 
 2792            case MTHREAD:
 2793             if (threadview)
 2794                 threadview = 0;
 2795             else
 2796                 threadview = 1;
 2797             break;
 2798 
 2799            case MTHRSORT:
 2800             if (threadsort)
 2801                 threadsort = 0;
 2802             else
 2803                 threadsort = 1;
 2804             break;
 2805 
 2806            case MCALCPSS:
 2807             if (calcpss)
 2808                 calcpss = 0;
 2809             else
 2810                 calcpss = 1;
 2811             break;
 2812 
 2813            case MGETWCHAN:
 2814             if (getwchan)
 2815                 getwchan = 0;
 2816             else
 2817                 getwchan = 1;
 2818             break;
 2819 
 2820            case MSUPEXITS:
 2821             if (suppressexit)
 2822                 suppressexit = 0;
 2823             else
 2824                 suppressexit = 1;
 2825             break;
 2826 
 2827            case MCOLORS:
 2828             if (usecolors)
 2829                 usecolors=0;
 2830             else
 2831                 usecolors=1;
 2832             break;
 2833 
 2834            case MSYSLIMIT:
 2835             limitedlines();
 2836             break;
 2837 
 2838            default:
 2839             prusage("atop");
 2840         }
 2841     }
 2842 
 2843         /*
 2844         ** set stdout output on line-basis
 2845         */
 2846         setvbuf(stdout, (char *)0, _IOLBF, BUFSIZ);
 2847 
 2848         /*
 2849         ** check if STDOUT is related to a tty or
 2850         ** something else (file, pipe)
 2851         */
 2852         if ( isatty(fileno(stdout)) )
 2853                 screen = 1;
 2854         else
 2855                 screen = 0;
 2856 
 2857         /*
 2858         ** install catch-routine to finish in a controlled way
 2859     ** and activate cbreak-mode
 2860         */
 2861         if (screen)
 2862     {
 2863         /*
 2864         ** if stdin is not connected to a tty (might be redirected
 2865         ** to pipe or file), close it and duplicate stdout (tty)
 2866         ** to stdin
 2867         */
 2868             if ( !isatty(fileno(stdin)) )
 2869         {
 2870             (void) dup2(fileno(stdout), fileno(stdin));
 2871         }
 2872 
 2873         /*
 2874         ** initialize screen-handling via curses
 2875         */
 2876         setlocale(LC_ALL, "");
 2877         setlocale(LC_NUMERIC, "C");
 2878 
 2879         initscr();
 2880         cbreak();
 2881         noecho();
 2882         keypad(stdscr, TRUE);
 2883 
 2884         if (COLS  < 30)
 2885         {
 2886             endwin();   // finish curses interface
 2887 
 2888             fprintf(stderr, "Not enough columns available\n"
 2889                             "(need at least %d columns)\n", 30);
 2890             fprintf(stderr, "Please resize window....\n");
 2891 
 2892             cleanstop(1);
 2893         }
 2894 
 2895         if (has_colors())
 2896         {
 2897             use_default_colors();
 2898             start_color();
 2899 
 2900             init_pair(COLORINFO,   colorinfo,   -1);
 2901             init_pair(COLORALMOST, coloralmost, -1);
 2902             init_pair(COLORCRIT,   colorcrit,   -1);
 2903             init_pair(COLORTHR,    colorthread, -1);
 2904         }
 2905         else
 2906         {
 2907             usecolors = 0;
 2908         }
 2909     }
 2910 
 2911     signal(SIGINT,   cleanstop);
 2912     signal(SIGTERM,  cleanstop);
 2913 }
 2914 
 2915 /*
 2916 ** show help information in interactive mode
 2917 */
 2918 static struct helptext {
 2919     char *helpline;
 2920     char helparg;
 2921 } helptext[] = {
 2922     {"Figures shown for active processes:\n",       ' '},
 2923     {"\t'%c'  - generic info (default)\n",          MPROCGEN},
 2924     {"\t'%c'  - memory details\n",              MPROCMEM},
 2925     {"\t'%c'  - disk details\n",                MPROCDSK},
 2926     {"\t'%c'  - network details\n",             MPROCNET},
 2927     {"\t'%c'  - cgroups v2 details\n",          MPROCCGR},
 2928     {"\t'%c'  - scheduling and thread-group info\n",    MPROCSCH},
 2929     {"\t'%c'  - GPU details\n",             MPROCGPU},
 2930     {"\t'%c'  - various info (ppid, user/group, date/time, status, "
 2931      "exitcode)\n", MPROCVAR},
 2932     {"\t'%c'  - full command line per process\n",       MPROCARG},
 2933     {"\t'%c'  - cgroup v2 info per process\n",      MPROCCGR},
 2934     {"\t'%c'  - use own output line definition\n",      MPROCOWN},
 2935     {"\n",                          ' '},
 2936     {"Sort list of processes in order of:\n",       ' '},
 2937     {"\t'%c'  - cpu activity\n",                MSORTCPU},
 2938     {"\t'%c'  - memory consumption\n",          MSORTMEM},
 2939     {"\t'%c'  - disk activity\n",               MSORTDSK},
 2940     {"\t'%c'  - network activity\n",            MSORTNET},
 2941     {"\t'%c'  - GPU activity\n",                MSORTGPU},
 2942     {"\t'%c'  - most active system resource (auto mode)\n", MSORTAUTO},
 2943     {"\n",                          ' '},
 2944     {"Accumulated figures:\n",              ' '},
 2945     {"\t'%c'  - total resource consumption per user\n",     MCUMUSER},
 2946     {"\t'%c'  - total resource consumption per program (i.e. same "
 2947      "process name)\n",                 MCUMPROC},
 2948     {"\t'%c'  - total resource consumption per container\n",MCUMCONT},
 2949     {"\n",                          ' '},
 2950     {"Process selections (keys shown in header line):\n",   ' '},
 2951     {"\t'%c'  - focus on specific user name           "
 2952                                   "(regular expression)\n", MSELUSER},
 2953     {"\t'%c'  - focus on specific program name        "
 2954                                   "(regular expression)\n", MSELPROC},
 2955     {"\t'%c'  - focus on specific container id (CID)\n",    MSELCONT},
 2956     {"\t'%c'  - focus on specific command line string "
 2957                                   "(regular expression)\n", MSELARG},
 2958     {"\t'%c'  - focus on specific process id (PID)\n",      MSELPID},
 2959     {"\t'%c'  - focus on specific process/thread state(s)\n", MSELSTATE},
 2960     {"\n",                          ' '},
 2961     {"System resource selections (keys shown in header line):\n",' '},
 2962     {"\t'%c'  - focus on specific system resources    "
 2963                                   "(regular expression)\n", MSELSYS},
 2964     {"\n",                                ' '},
 2965     {"Screen-handling:\n",                        ' '},
 2966     {"\t^L   - redraw the screen                       \n",       ' '},
 2967     {"\tPgDn - show next page in the process list (or ^F)\n",     ' '},
 2968     {"\tArDn - arrow-down for next line in process list\n",       ' '},
 2969     {"\tPgUp - show previous page in the process list (or ^B)\n", ' '},
 2970     {"\tArUp   arrow-up for previous line in process list\n",     ' '},
 2971     {"\n",                          ' '},
 2972     {"\tArRt - arrow-right for next character in full command line\n", ' '},
 2973     {"\tArLt - arrow-left  for previous character in full command line\n",
 2974                                     ' '},
 2975     {"\n",                          ' '},
 2976     {"Presentation (keys shown in header line):\n",     ' '},
 2977     {"\t'%c'  - show threads within process (thread view)      (toggle)\n",
 2978                                 MTHREAD},
 2979     {"\t'%c'  - sort threads (when combined with thread view)  (toggle)\n",
 2980                                 MTHRSORT},
 2981     {"\t'%c'  - show all processes (default: active processes) (toggle)\n",
 2982                                 MALLPROC},
 2983     {"\t'%c'  - show fixed number of header lines              (toggle)\n",
 2984                                 MSYSFIXED},
 2985     {"\t'%c'  - suppress sorting system resources              (toggle)\n",
 2986                                 MSYSNOSORT},
 2987     {"\t'%c'  - suppress exited processes in output            (toggle)\n",
 2988                                 MSUPEXITS},
 2989     {"\t'%c'  - no colors to indicate high occupation          (toggle)\n",
 2990                                 MCOLORS},
 2991     {"\t'%c'  - show average-per-second i.s.o. total values    (toggle)\n",
 2992                                 MAVGVAL},
 2993     {"\t'%c'  - calculate proportional set size (PSIZE)        (toggle)\n",
 2994                                 MCALCPSS},
 2995     {"\t'%c'  - determine WCHAN per thread                     (toggle)\n",
 2996                                 MGETWCHAN},
 2997     {"\n",                          ' '},
 2998     {"Raw file viewing:\n",                 ' '},
 2999     {"\t'%c'  - show next     sample in raw file\n",    MSAMPNEXT},
 3000     {"\t'%c'  - show previous sample in raw file\n",    MSAMPPREV},
 3001     {"\t'%c'  - branch to certain time in raw file\n",  MSAMPBRANCH},
 3002     {"\t'%c'  - rewind to begin of raw file\n",     MRESET},
 3003     {"\n",                          ' '},
 3004     {"Miscellaneous commands:\n",               ' '},
 3005     {"\t'%c'  - change interval timer (0 = only manual trigger)\n",
 3006                                 MINTERVAL},
 3007     {"\t'%c'  - manual trigger to force next sample\n", MSAMPNEXT},
 3008     {"\t'%c'  - reset counters to boot time values\n",  MRESET},
 3009     {"\t'%c'  - pause button to freeze current sample (toggle)\n",
 3010                                 MPAUSE},
 3011     {"\n",                          ' '},
 3012     {"\t'%c'  - limited lines for per-cpu, disk and interface resources\n",
 3013                                 MSYSLIMIT},
 3014     {"\t'%c'  - kill a process (i.e. send a signal)\n", MKILLPROC},
 3015     {"\n",                          ' '},
 3016     {"\t'%c'  - version information\n",         MVERSION},
 3017     {"\t'%c'  - help information\n",            MHELP1},
 3018     {"\t'%c'  - help information\n",            MHELP2},
 3019     {"\t'%c'  - quit this program\n",           MQUIT},
 3020 };
 3021 
 3022 static int helplines = sizeof(helptext)/sizeof(struct helptext);
 3023 
 3024 static void
 3025 showhelp(int helpline)
 3026 {
 3027     int winlines = LINES-helpline, shown, tobeshown=1, i;
 3028     WINDOW  *helpwin;
 3029 
 3030     /*
 3031     ** create a new window for the help-info in which scrolling is
 3032     ** allowed
 3033     */
 3034     helpwin = newwin(winlines, COLS, helpline, 0);
 3035     scrollok(helpwin, 1);
 3036 
 3037     /*
 3038     ** show help-lines 
 3039     */
 3040     for (i=0, shown=0; i < helplines; i++, shown++)
 3041     {
 3042         wprintw(helpwin, helptext[i].helpline, helptext[i].helparg);
 3043 
 3044         /*
 3045         ** when the window is full, start paging interactively
 3046         */
 3047         if (i >= winlines-2 && shown >= tobeshown)
 3048         {
 3049             wmove    (helpwin, winlines-1, 0);
 3050             wclrtoeol(helpwin);
 3051             wprintw  (helpwin, "Press 'q' to leave help, " 
 3052                     "space for next page or "
 3053                     "other key for next line... ");
 3054 
 3055             switch (wgetch(helpwin))
 3056             {
 3057                case 'q':
 3058                 delwin(helpwin);
 3059                 return;
 3060                case ' ':
 3061                 shown = 0;
 3062                 tobeshown = winlines-1;
 3063                 break;
 3064                default:
 3065                 shown = 0;
 3066                 tobeshown = 1;
 3067             }
 3068 
 3069             wmove  (helpwin, winlines-1, 0);
 3070         }
 3071     }
 3072 
 3073     wmove    (helpwin, winlines-1, 0);
 3074     wclrtoeol(helpwin);
 3075     wprintw  (helpwin, "End of help - press 'q' to leave help... ");
 3076         while ( wgetch(helpwin) != 'q' );
 3077     delwin   (helpwin);
 3078 }
 3079 
 3080 /*
 3081 ** function to be called to print error-messages
 3082 */
 3083 void
 3084 generic_error(const char *format, ...)
 3085 {
 3086     va_list args;
 3087 
 3088     va_start(args, format);
 3089     vfprintf(stderr, format, args);
 3090     va_end  (args);
 3091 }
 3092 
 3093 /*
 3094 ** function to be called when the program stops
 3095 */
 3096 void
 3097 generic_end(void)
 3098 {
 3099     endwin();
 3100 }
 3101 
 3102 /*
 3103 ** function to be called when usage-info is required
 3104 */
 3105 void
 3106 generic_usage(void)
 3107 {
 3108     printf("\t  -%c  show fixed number of lines with system statistics\n",
 3109             MSYSFIXED);
 3110     printf("\t  -%c  suppress sorting of system resources\n",
 3111             MSYSNOSORT);
 3112     printf("\t  -%c  suppress exited processes in output\n",
 3113             MSUPEXITS);
 3114     printf("\t  -%c  show limited number of lines for certain resources\n",
 3115             MSYSLIMIT);
 3116     printf("\t  -%c  show threads within process\n", MTHREAD);
 3117     printf("\t  -%c  sort threads (when combined with '%c')\n", MTHRSORT, MTHREAD);
 3118     printf("\t  -%c  show average-per-second i.s.o. total values\n\n",
 3119             MAVGVAL);
 3120     printf("\t  -%c  no colors in case of high occupation\n",
 3121             MCOLORS);
 3122     printf("\t  -%c  show general process-info (default)\n",
 3123             MPROCGEN);
 3124     printf("\t  -%c  show memory-related process-info\n",
 3125             MPROCMEM);
 3126     printf("\t  -%c  show disk-related process-info\n",
 3127             MPROCDSK);
 3128     printf("\t  -%c  show network-related process-info\n",
 3129             MPROCNET);
 3130     printf("\t  -%c  show scheduling-related process-info\n",
 3131             MPROCSCH);
 3132     printf("\t  -%c  show various process-info (ppid, user/group, "
 3133                      "date/time)\n", MPROCVAR);
 3134     printf("\t  -%c  show command line per process\n",
 3135             MPROCARG);
 3136     printf("\t  -%c  show cgroup v2 info per process\n",
 3137             MPROCCGR);
 3138     printf("\t  -%c  show own defined process-info\n",
 3139             MPROCOWN);
 3140     printf("\t  -%c  show cumulated process-info per user\n",
 3141             MCUMUSER);
 3142     printf("\t  -%c  show cumulated process-info per program "
 3143                     "(i.e. same name)\n",
 3144             MCUMPROC);
 3145     printf("\t  -%c  show cumulated process-info per container\n\n",
 3146             MCUMCONT);
 3147     printf("\t  -%c  sort processes in order of cpu consumption "
 3148                     "(default)\n",
 3149             MSORTCPU);
 3150     printf("\t  -%c  sort processes in order of memory consumption\n",
 3151             MSORTMEM);
 3152     printf("\t  -%c  sort processes in order of disk activity\n",
 3153             MSORTDSK);
 3154     printf("\t  -%c  sort processes in order of network activity\n",
 3155             MSORTNET);
 3156     printf("\t  -%c  sort processes in order of GPU activity\n",
 3157             MSORTGPU);
 3158     printf("\t  -%c  sort processes in order of most active resource "
 3159                         "(auto mode)\n",
 3160             MSORTAUTO);
 3161 }
 3162 
 3163 /*
 3164 ** functions to handle a particular tag in the /etc/atoprc and .atoprc file
 3165 */
 3166 void
 3167 do_username(char *name, char *val)
 3168 {
 3169     struct passwd   *pwd;
 3170 
 3171     strncpy(procsel.username, val, sizeof procsel.username -1);
 3172     procsel.username[sizeof procsel.username -1] = 0;
 3173 
 3174     if (procsel.username[0])
 3175     {
 3176         regex_t     userregex;
 3177         int     u = 0;
 3178 
 3179         if (regcomp(&userregex, procsel.username, REG_NOSUB))
 3180         {
 3181             fprintf(stderr,
 3182                 "atoprc - %s: invalid regular expression %s\n",
 3183                 name, val);
 3184             exit(1);
 3185         }
 3186 
 3187         while ( (pwd = getpwent()))
 3188         {
 3189             if (regexec(&userregex, pwd->pw_name, 0, NULL, 0))
 3190                 continue;
 3191 
 3192             if (u < MAXUSERSEL-1)
 3193             {
 3194                 procsel.userid[u] = pwd->pw_uid;
 3195                 u++;
 3196             }
 3197         }
 3198         endpwent();
 3199 
 3200         procsel.userid[u] = USERSTUB;
 3201 
 3202         if (u == 0)
 3203         {
 3204             /*
 3205             ** possibly a numerical value has been specified
 3206             */
 3207             if (numeric(procsel.username))
 3208             {
 3209                  procsel.userid[0] = atoi(procsel.username);
 3210                  procsel.userid[1] = USERSTUB;
 3211             }
 3212             else
 3213             {
 3214                 fprintf(stderr,
 3215                         "atoprc - %s: user-names matching %s "
 3216                                         "do not exist\n", name, val);
 3217                 exit(1);
 3218             }
 3219         }
 3220     }
 3221     else
 3222     {
 3223         procsel.userid[0] = USERSTUB;
 3224     }
 3225 }
 3226 
 3227 void
 3228 do_procname(char *name, char *val)
 3229 {
 3230     strncpy(procsel.progname, val, sizeof procsel.progname -1);
 3231     procsel.prognamesz = strlen(procsel.progname);
 3232 
 3233     if (procsel.prognamesz)
 3234     {
 3235         if (regcomp(&procsel.progregex, procsel.progname, REG_NOSUB))
 3236         {
 3237             fprintf(stderr,
 3238                 "atoprc - %s: invalid regular expression %s\n",
 3239                 name, val);
 3240             exit(1);
 3241         }
 3242     }
 3243 }
 3244 
 3245 extern int get_posval(char *name, char *val);
 3246 
 3247 
 3248 void
 3249 do_maxcpu(char *name, char *val)
 3250 {
 3251     maxcpulines = get_posval(name, val);
 3252 }
 3253 
 3254 void
 3255 do_maxgpu(char *name, char *val)
 3256 {
 3257     maxgpulines = get_posval(name, val);
 3258 }
 3259 
 3260 void
 3261 do_maxdisk(char *name, char *val)
 3262 {
 3263     maxdsklines = get_posval(name, val);
 3264 }
 3265 
 3266 void
 3267 do_maxmdd(char *name, char *val)
 3268 {
 3269     maxmddlines = get_posval(name, val);
 3270 }
 3271 
 3272 void
 3273 do_maxlvm(char *name, char *val)
 3274 {
 3275     maxlvmlines = get_posval(name, val);
 3276 }
 3277 
 3278 void
 3279 do_maxintf(char *name, char *val)
 3280 {
 3281     maxintlines = get_posval(name, val);
 3282 }
 3283 
 3284 void
 3285 do_maxifb(char *name, char *val)
 3286 {
 3287     maxifblines = get_posval(name, val);
 3288 }
 3289 
 3290 void
 3291 do_maxnfsm(char *name, char *val)
 3292 {
 3293     maxnfslines = get_posval(name, val);
 3294 }
 3295 
 3296 void
 3297 do_maxcont(char *name, char *val)
 3298 {
 3299     maxcontlines = get_posval(name, val);
 3300 }
 3301 
 3302 void
 3303 do_maxnuma(char *name, char *val)
 3304 {
 3305     maxnumalines = get_posval(name, val);
 3306 }
 3307 
 3308 void
 3309 do_maxllc(char *name, char *val)
 3310 {
 3311     maxllclines = get_posval(name, val);
 3312 }
 3313 
 3314 struct colmap {
 3315     char    *colname;
 3316     short   colval;
 3317 } colormap[] = {
 3318     { "red",    COLOR_RED,  },
 3319     { "green",  COLOR_GREEN,    },
 3320     { "yellow", COLOR_YELLOW,   },
 3321     { "blue",   COLOR_BLUE, },
 3322     { "magenta",    COLOR_MAGENTA,  },
 3323     { "cyan",   COLOR_CYAN, },
 3324     { "black",  COLOR_BLACK,    },
 3325     { "white",  COLOR_WHITE,    },
 3326 };
 3327 static short
 3328 modify_color(char *colorname)
 3329 {
 3330     int i;
 3331 
 3332     for (i=0; i < sizeof colormap/sizeof colormap[0]; i++)
 3333     {
 3334         if ( strcmp(colorname, colormap[i].colname) == 0)
 3335             return colormap[i].colval;
 3336     }
 3337 
 3338     // required color not found
 3339     fprintf(stderr, "atoprc - invalid color used: %s\n", colorname);
 3340     fprintf(stderr, "supported colors:");
 3341     for (i=0; i < sizeof colormap/sizeof colormap[0]; i++)
 3342         fprintf(stderr, " %s", colormap[i].colname);
 3343     fprintf(stderr, "\n");
 3344 
 3345     exit(1);
 3346 }
 3347 
 3348 
 3349 void
 3350 do_colinfo(char *name, char *val)
 3351 {
 3352     colorinfo = modify_color(val);
 3353 }
 3354 
 3355 void
 3356 do_colalmost(char *name, char *val)
 3357 {
 3358     coloralmost = modify_color(val);
 3359 }
 3360 
 3361 void
 3362 do_colcrit(char *name, char *val)
 3363 {
 3364     colorcrit = modify_color(val);
 3365 }
 3366 
 3367 void
 3368 do_colthread(char *name, char *val)
 3369 {
 3370     colorthread = modify_color(val);
 3371 }
 3372 
 3373 void
 3374 do_flags(char *name, char *val)
 3375 {
 3376     int i;
 3377 
 3378     for (i=0; val[i]; i++)
 3379     {
 3380         switch (val[i])
 3381         {
 3382            case '-':
 3383             break;
 3384 
 3385            case MSORTCPU:
 3386             showorder = MSORTCPU;
 3387             break;
 3388 
 3389            case MSORTGPU:
 3390             showorder = MSORTGPU;
 3391             break;
 3392 
 3393            case MSORTMEM:
 3394             showorder = MSORTMEM;
 3395             break;
 3396 
 3397            case MSORTDSK:
 3398             showorder = MSORTDSK;
 3399             break;
 3400 
 3401            case MSORTNET:
 3402             showorder = MSORTNET;
 3403             break;
 3404 
 3405            case MSORTAUTO:
 3406             showorder = MSORTAUTO;
 3407             break;
 3408 
 3409            case MPROCGEN:
 3410             showtype  = MPROCGEN;
 3411             showorder = MSORTCPU;
 3412             break;
 3413 
 3414            case MPROCGPU:
 3415             showtype  = MPROCGPU;
 3416             showorder = MSORTGPU;
 3417             break;
 3418 
 3419            case MPROCMEM:
 3420             showtype  = MPROCMEM;
 3421             showorder = MSORTMEM;
 3422             break;
 3423 
 3424            case MPROCDSK:
 3425             showtype  = MPROCDSK;
 3426             showorder = MSORTDSK;
 3427             break;
 3428 
 3429            case MPROCNET:
 3430             showtype  = MPROCNET;
 3431             showorder = MSORTNET;
 3432             break;
 3433 
 3434            case MPROCVAR:
 3435             showtype  = MPROCVAR;
 3436             break;
 3437 
 3438            case MPROCSCH:
 3439             showtype  = MPROCSCH;
 3440             showorder = MSORTCPU;
 3441             break;
 3442 
 3443            case MPROCARG:
 3444             showtype  = MPROCARG;
 3445             break;
 3446 
 3447            case MPROCCGR:
 3448             showtype  = MPROCCGR;
 3449             break;
 3450 
 3451            case MPROCOWN:
 3452             showtype  = MPROCOWN;
 3453             break;
 3454 
 3455            case MCUMUSER:
 3456             showtype  = MCUMUSER;
 3457             break;
 3458 
 3459            case MCUMPROC:
 3460             showtype  = MCUMPROC;
 3461             break;
 3462 
 3463            case MCUMCONT:
 3464             showtype  = MCUMCONT;
 3465             break;
 3466 
 3467            case MALLPROC:
 3468             deviatonly = 0;
 3469             break;
 3470 
 3471            case MAVGVAL:
 3472             avgval=1;
 3473             break;
 3474 
 3475            case MSYSFIXED:
 3476             fixedhead = 1;
 3477             break;
 3478 
 3479            case MSYSNOSORT:
 3480             sysnosort = 1;
 3481             break;
 3482 
 3483            case MTHREAD:
 3484             threadview = 1;
 3485             break;
 3486 
 3487            case MTHRSORT:
 3488             threadsort = 1;
 3489             break;
 3490 
 3491            case MCOLORS:
 3492             usecolors = 0;
 3493             break;
 3494 
 3495            case MCALCPSS:
 3496             calcpss = 1;
 3497             break;
 3498 
 3499            case MGETWCHAN:
 3500             getwchan = 1;
 3501             break;
 3502 
 3503            case MSUPEXITS:
 3504             suppressexit = 1;
 3505             break;
 3506         }
 3507     }
 3508 }