"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 }