]> review.fuel-infra Code Review - packages/trusty/atop.git/commitdiff
atop-2.3.0 31/40231/1
authorDmitry Teselkin <dteselkin@mirantis.com>
Tue, 22 Jan 2019 13:00:01 +0000 (16:00 +0300)
committerDmitry Teselkin <dteselkin@mirantis.com>
Tue, 22 Jan 2019 13:00:01 +0000 (16:00 +0300)
Change-Id: I0155650be349c632bdd68ca26c0d2a7e15881787
Related-Prod: PROD-14364

87 files changed:
.gitignore [new file with mode: 0644]
debian/NEWS [new file with mode: 0644]
debian/README.source [new file with mode: 0644]
debian/atop.cron.d [new file with mode: 0644]
debian/atop.default [new file with mode: 0644]
debian/atop.install [new file with mode: 0644]
debian/atop.wrapper [new file with mode: 0755]
debian/changelog [new file with mode: 0644]
debian/compat [new file with mode: 0644]
debian/control [new file with mode: 0644]
debian/copyright [new file with mode: 0644]
debian/dirs [new file with mode: 0644]
debian/docs [new file with mode: 0644]
debian/patches/atop-daily [new file with mode: 0644]
debian/patches/atop-pm [new file with mode: 0644]
debian/patches/atopacct.service [new file with mode: 0644]
debian/patches/dependency-on-remote-fs [new file with mode: 0644]
debian/patches/dh_installinit [new file with mode: 0644]
debian/patches/dh_systemd_enable [new file with mode: 0644]
debian/patches/force-reload [new file with mode: 0644]
debian/patches/init-script-lsb-headers [new file with mode: 0644]
debian/patches/logrotate-nomail [new file with mode: 0644]
debian/patches/lsb-init-functions [new file with mode: 0644]
debian/patches/makefile-clean [new file with mode: 0644]
debian/patches/no-files-in-var-log [new file with mode: 0644]
debian/patches/no-version-symlinks [new file with mode: 0644]
debian/patches/series [new file with mode: 0644]
debian/patches/systemd-path [new file with mode: 0644]
debian/patches/var-run [new file with mode: 0644]
debian/postinst [new file with mode: 0644]
debian/postrm [new file with mode: 0644]
debian/preinst [new file with mode: 0644]
debian/rules [new file with mode: 0755]
debian/source/format [new file with mode: 0644]
debian/watch [new file with mode: 0644]
sources/45atoppm [new file with mode: 0755]
sources/AUTHOR [new file with mode: 0644]
sources/COPYING [new file with mode: 0644]
sources/ChangeLog [new file with mode: 0644]
sources/Makefile [new file with mode: 0644]
sources/README [new file with mode: 0644]
sources/acctproc.c [new file with mode: 0644]
sources/acctproc.h [new file with mode: 0644]
sources/atop-pm.sh [new file with mode: 0755]
sources/atop.c [new file with mode: 0644]
sources/atop.cronsystemd [new file with mode: 0644]
sources/atop.cronsysv [new file with mode: 0644]
sources/atop.daily [new file with mode: 0755]
sources/atop.h [new file with mode: 0644]
sources/atop.init [new file with mode: 0755]
sources/atop.service [new file with mode: 0644]
sources/atopacct.init [new file with mode: 0755]
sources/atopacct.service [new file with mode: 0644]
sources/atopacctd.c [new file with mode: 0644]
sources/atopacctd.h [new file with mode: 0644]
sources/atopsar.c [new file with mode: 0644]
sources/deviate.c [new file with mode: 0644]
sources/ifprop.c [new file with mode: 0644]
sources/ifprop.h [new file with mode: 0644]
sources/man/atop.1 [new file with mode: 0644]
sources/man/atopacctd.8 [new file with mode: 0644]
sources/man/atoprc.5 [new file with mode: 0644]
sources/man/atopsar.1 [new file with mode: 0644]
sources/netatop.h [new file with mode: 0644]
sources/netatopd.h [new file with mode: 0644]
sources/netatopif.c [new file with mode: 0644]
sources/netlink.c [new file with mode: 0644]
sources/netstats.h [new file with mode: 0644]
sources/parseable.c [new file with mode: 0644]
sources/parseable.h [new file with mode: 0644]
sources/photoproc.c [new file with mode: 0644]
sources/photoproc.h [new file with mode: 0644]
sources/photosyst.c [new file with mode: 0644]
sources/photosyst.h [new file with mode: 0644]
sources/procdbase.c [new file with mode: 0644]
sources/psaccs_atop [new file with mode: 0644]
sources/psaccu_atop [new file with mode: 0644]
sources/rawlog.c [new file with mode: 0644]
sources/showgeneric.c [new file with mode: 0644]
sources/showgeneric.h [new file with mode: 0644]
sources/showlinux.c [new file with mode: 0644]
sources/showlinux.h [new file with mode: 0644]
sources/showprocs.c [new file with mode: 0644]
sources/showsys.c [new file with mode: 0644]
sources/various.c [new file with mode: 0644]
sources/version.c [new file with mode: 0644]
sources/version.h [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..e8ed04d
--- /dev/null
@@ -0,0 +1,3 @@
+*.dsc
+*.tar.?z
+
diff --git a/debian/NEWS b/debian/NEWS
new file mode 100644 (file)
index 0000000..e84edf8
--- /dev/null
@@ -0,0 +1,105 @@
+atop (2.2.6-3) unstable; urgency=medium
+
+  atop does some really weird things with logrotate, which cause
+  undocumented behavior in logrotate versions < 3.11. As Debian stretch
+  has 3.11, this should not be an issue when updating between stable
+  versions.
+  
+  If you have been using unstable or testing, and you get weird
+  messages from logrotate, it might be worth looking in /var/log/atop
+  for weird log files, and removing all dummy_* files other than
+  dummy_after and dummy_before.
+
+ -- Marc Haber <mh+debian-packages@zugschlus.de>  Wed, 18 Jan 2017 19:39:45 +0100
+
+atop (2.2.6-2) unstable; urgency=medium
+
+  PID file /run/atopacctd.pid not readable (yet?) after start
+  
+  This is an issue when upgrading from one of the experimental atop
+  versions that have never been in Debian unstable. I therefore don't
+  consider it a bug. The issue is already reported (#842136, #849138).
+  Please don't file additional bug reports for this issue.
+  
+  Here is an explanation from upstream (quoting Gerlof with his kind
+  permission):
+  
+  |(…), I think that this caused by the fact that the previous run of
+  |atopacctd did not terminate properly. That causes the fact that
+  |files and directories are not removed by atopacctd, giving problems
+  |with the next activation.
+
+  |Obviously, it is possble to remove these files and directories
+  |*during* the next activation (instead of terminating with an error
+  |message). However, I don't want to do that since the existence of
+  |the /run/pacct_shadow.d directory might also indicate that the
+  |atopacctd daemon is already running (then a second incarnation
+  |should refuse to start).
+
+  |So when the issue is solved that atopacctd refuses to terminate (…),
+  |the issue of the existing /run/pacct_shadow.d directory should also
+  |be irrelevant IMHO.
+  
+  When atopacctd receives signal 2, 3 or 15, it finishes and destroys
+  the files/directories it has created. If you encounter the situation
+  that atopacctd does not start because it complains about pidfile
+  and/or /run/pacct_* directories/files, please send SIGTERM to any
+  atopacctd process that may still hang around, and if the
+  files/directories are still there without the corresponding atopacctd
+  process, please remove them manually and then try starting atopacctd
+  again.
+
+ -- Marc Haber <mh+debian-packages@zugschlus.de>  Tue, 27 Dec 2016 12:00:00 +0100
+
+atop (2.2.6-2) unstable; urgency=medium
+
+  KERNEL ISSUES WITH PROCESS ACCOUNTING
+
+  Newer upstream kernels have two issues with process accounting.
+  These were isolated and debugged in #833997
+
+  1) Sometimes process accounting does not work at all¹. Atopacctd tries to
+  work around this issue, by retrying to initialize process accounting
+  several times.
+
+  2) When using the NETLINK interface, the command TASKSTATS_CMD_GET
+  consequently returns -EINVAL². Atopacctd needs NETLINK to be able to
+  be triggered that some process in the system has finished. In this way
+  atopacctd can be in a blocking state as long as no processes terminate.
+  When atopacctd detects this condition it thus switches into a polling
+  state to periodically try if it can read process accounting records as
+  a workaround. This issue has to do with cpumask and you can work-around
+  it by building a kernel that has CONFIG_NR_CPUS configured to exactly
+  the amount of CPUs (logical CPUs) in the system the kernel runs on. You
+  can find this kernel option under "Processor type and features" -->
+  "Maximum number of CPUs".
+
+  [1] Bug 190271 - process accounting sometimes does not work
+  https://bugzilla.kernel.org/show_bug.cgi?id=190271
+
+  [2] Bug 190711 - Process accounting: Using the NETLINK interface,
+  the command TASKSTATS_CMD_GET returns -EINVAL
+  https://bugzilla.kernel.org/show_bug.cgi?id=190711
+
+  Linux kernel mailing list thread:
+
+  [REGRESSION] Two issues that prevent process accounting (taskstats) from
+  working correctly
+  https://lkml.org/lkml/2016/12/19/182
+  
+  There are also Debian bug reports for those two issues, #848682 and
+  #848683
+
+ -- Marc Haber <mh+debian-packages@zugschlus.de>  Tue, 27 Dec 2016 12:00:00 +0100
+
+atop (2.2.3-1~exp1) experimental; urgency=low
+
+  * atop's log file format in /var/log has changed. Old log files will
+    be moved away on upgrade so that the new atop will actually start.
+  * currently the restart of atop does not work because in some
+    circumstances, atop won't read its own log file. Symptom is that
+    atop doesn't run after restart and /var/log/atop/daily.log says
+    incompatible log format. Upstream has been informed of that. I am
+    uploading to experimental to get preliminary comments from users.
+
+ -- Marc Haber <mh+debian-packages@zugschlus.de>  Mon, 08 Aug 2016 10:58:40 +0200
diff --git a/debian/README.source b/debian/README.source
new file mode 100644 (file)
index 0000000..1297928
--- /dev/null
@@ -0,0 +1,2 @@
+This package is built using git, debhelper9 and quilt. It is source
+format 3.0 (quilt).
diff --git a/debian/atop.cron.d b/debian/atop.cron.d
new file mode 100644 (file)
index 0000000..6400ac4
--- /dev/null
@@ -0,0 +1,4 @@
+PATH=/bin:/usr/bin:/sbin:/usr/sbin
+
+# daily restart of atop at midnight
+0 0 * * * root if [ -d "/run/systemd/system" ]; then systemctl restart atop; else /usr/share/atop/atop.daily \& ; fi
diff --git a/debian/atop.default b/debian/atop.default
new file mode 100644 (file)
index 0000000..0602db3
--- /dev/null
@@ -0,0 +1,2 @@
+# /etc/default/atop
+# this file is no longer used and will be removed in a future release
diff --git a/debian/atop.install b/debian/atop.install
new file mode 100644 (file)
index 0000000..404846b
--- /dev/null
@@ -0,0 +1,3 @@
+debian/atop.wrapper usr/share/atop
+debian/atop.service /lib/systemd/system
+debian/atopacct.service /lib/systemd/system
diff --git a/debian/atop.wrapper b/debian/atop.wrapper
new file mode 100755 (executable)
index 0000000..eba4c59
--- /dev/null
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+DAEMON="$1"
+OUTFILE="$2"
+shift 2
+
+exec $DAEMON "$@" >$OUTFILE 2>&1
diff --git a/debian/changelog b/debian/changelog
new file mode 100644 (file)
index 0000000..4da7ffa
--- /dev/null
@@ -0,0 +1,369 @@
+atop (2.3.0-1.1~u14.04+mcp) mcp; urgency=medium
+
+  * Rebuild for Ubuntu 14.04 (PROD-14364)
+    http://archive.ubuntu.com/ubuntu/pool/universe/a/atop/atop_2.3.0-1.dsc
+
+ -- Dmitry Teselkin <oscore-eng@mirantis.com>  Tue, 22 Jan 2019 15:57:42 +0300
+
+atop (2.3.0-1) unstable; urgency=medium
+
+  * new upstream release
+  * new git URLs
+  * Maintainer address on packages.debian.org
+  * fix Makefile to allow consecutive clean
+  * Standards-Version: 4.1.2 (no changes necessary)
+
+ -- Marc Haber <mh+debian-packages@zugschlus.de>  Sat, 30 Dec 2017 15:36:37 +0000
+
+atop (2.2.6-5) unstable; urgency=medium
+
+  * install systemd unit and initscript in dh_auto_install.
+    Thanks to Adrian Bunk and nthykier (Closes: #881479)
+  * get rid of dh-systemd dependency, bump debhelper dependency
+  * bump debhelper level to 10
+
+ -- Marc Haber <mh+debian-packages@zugschlus.de>  Fri, 17 Nov 2017 18:52:51 +0100
+
+atop (2.2.6-4) unstable; urgency=medium
+
+  * remove versioned dependency on initscripts. See also #852314.
+    Thanks to Stephen Kitt (Closes: 852239)
+
+ -- Marc Haber <mh+debian-packages@zugschlus.de>  Wed, 25 Jan 2017 20:45:37 +0100
+
+atop (2.2.6-3) unstable; urgency=medium
+
+  * use nocompress in logrotate files, workaround for logrotate < 3.11.
+    Thanks to Yuri D'Elia (Closes: 851138)
+  * add reference to #848682 and #484683 to NEWS.
+    Thanks to Martin Steigerwald (Closes: 833997)
+
+ -- Marc Haber <mh+debian-packages@zugschlus.de>  Thu, 19 Jan 2017 13:11:18 +0000
+
+atop (2.2.6-2) unstable; urgency=medium
+
+  * upload to unstable (finally)
+  * add NEWS.Debian explanation for #842136 and #849138
+
+ -- Marc Haber <mh+debian-packages@zugschlus.de>  Tue, 27 Dec 2016 13:01:24 +0100
+
+atop (2.2.6-1~exp1) experimental; urgency=medium
+
+  * New upstream version 2.2.6
+    * This is 2.2.5 plus a (yet unreleased) upstream change to
+      implement a polling mode in case NETLINK refuses with
+      error EINVAL, working around kernel bug 190711.
+    * 2.2.6 upstream version used in coordination with Upstream,
+      orig.tar.gz created by Debian.
+    * This hopefully takes care of installation issues.
+      Thanks to Vincent Lefevre (Closes: 848657)
+      Thanks to Yuri D'Elia (Closes: 848582)
+
+ -- Marc Haber <mh+debian-packages@zugschlus.de>  Tue, 20 Dec 2016 10:33:24 +0100
+
+atop (2.2.5-1~exp1) experimental; urgency=medium
+
+  * New upstream version 2.2.5
+    * This cannot possibly go without saying big THANKS to Upstream.
+      Your help was great!
+    * Fix c/p Error in man atop(1). 
+      Thanks to Alexandre Kieffer (Closes: #840645)
+    * Take care that atopacctd terminates upon receipt of SIGTERM
+      signal. Thanks to Vincent Lefevre (Closes: #834226)
+  * install atop-pm to /etc/systemd/system-sleep (Closes: 834039)
+  * Architecture: linux-any
+  * fix path typo in cron.d.
+    Thanks to Sam Morris (Closes: #846954)
+
+ -- Marc Haber <mh+debian-packages@zugschlus.de>  Tue, 25 Oct 2016 10:46:20 +0200
+
+atop (2.2.4-1~exp1) experimental; urgency=medium
+
+  * New upstream version 2.2.4
+    * this is an upstream bugfix release
+    * adapt patches
+  * move "lock" files to /run/lock.
+    Thanks to Vincent Lefevre (Closes: #833955)
+  * check for systemd via /run/systemd/system.
+    Thanks to Sam Morris (Closes: #840407)
+  * set nomail in logrotate files (Closes: #833982)
+  * fix wrong URL to X-Vcs-Browser
+
+ -- Marc Haber <mh+debian-packages@zugschlus.de>  Wed, 10 Aug 2016 11:07:16 +0200
+
+atop (2.2.3-1~exp1) experimental; urgency=low
+
+  * New upstream version 2.2.3
+    * adapt patches
+    * fix no-version-symlinks patch
+    * add var-run patch to replace /var/run with /run
+    * create dummy files in /var/log/atop in postinst
+    * install atop.daily to /usr/share instead of /etc
+    * move away old log file if upgrading (incompatible format)
+    * unfortunately this version is not suitable for unstable since it
+      sometimes does not cleanly restart when restarted during the day
+  * init scripts:
+    * remove debian specific init script
+    * use upstream's init scripts, install them using debhelper.
+      (Closes: #730167)
+    * add dependency on $remote_fs to init scripts
+    * implement force-reload in init scripts
+    * fix swapped lsb headers (Short-Description and Description)
+    * call lsb/init-functions in init scripts
+  * systemd units
+    * use upstream's systemd units, install them using debhelper
+    * do not depend on syslog.target (lintian)
+    * put systemd unit in /lib/systemd
+    * have systemd unit start atopaccd from /usr/sbin
+  * packaging:
+    * do call both systemdinstall and sysvinstall. order matters.
+    * remove grossly outdated README.Debian and NEWS
+    * document in atop.default that the file is no longer used
+    * recommend cron | cron-daemon.
+      Thanks to Alexandre Detiste (Closes: #798233)
+    * fix project homepage in debian/control (Closes: #740359)
+    * versioned depends on initscripts (/run)
+    * Standards-Version: 3.9.8 (no changes necessary)
+    * fix watch file
+  * new Debian cron.d script (cross of upstream's two cron.d scripts)
+
+ -- Marc Haber <mh+debian-packages@zugschlus.de>  Tue, 09 Aug 2016 20:54:27 +0200
+
+atop (1.27.3-1) experimental; urgency=low
+
+  * New upstream version 1.27.3
+    * will not exceed 50 MB of memory allocation. Closes: #575547
+  * upstream is no longer repacked
+  * refresh patches
+  * remove virtio-disks patch (applied upstream)
+  * remove patch logfiles (applied upstream)
+  * fix wrongly escaped quote in atoprc.5 man page
+
+ -- Marc Haber <mh+debian-packages@zugschlus.de>  Wed, 25 Jul 2012 22:17:28 +0200
+
+atop (1.26-2) unstable; urgency=low
+
+  * fix typo in bug number in changelog. Sorry, sur5r
+  * add patch to handle vd* disks as well (Closes: 640527)
+  * remove set -e from init script, /etc/init.d/skeleton doesn't have it
+  * Depend on lsb-base (>= 3.2-14) as we use log_* functions
+  * streamlinie init script with current skeleton, add default file.
+    Thanks to Vincent Lefevre (Closes: 675443)
+  * add DEP-3 headers to patches
+  * Roll back Debian patches breaking a feature:
+    * Now, only use mkstemp to create temp file
+    * Roll back Debian change: use upstream cronjob to rotate logs
+      (Closes: 611616)
+  * merge daily cron job and init script, debianize process
+  * use a wrapper to obtain correct redirection
+  * add cron to Recommends
+  * remove atop binary in clean target
+
+ -- Marc Haber <mh+debian-packages@zugschlus.de>  Tue, 22 May 2012 18:50:19 +0200
+
+atop (1.26-1) unstable; urgency=low
+
+  * New upstream version 1.26 (Closes: #569175)
+    * repack source: remove binary that was contained in the source package
+  * use dh level 9, source format 3.0 (quilt)
+    * roll back Makefile to original state
+    * roll /var/run patch into a quilt patch
+    * roll logfile mkstemp patch into a quilt patch
+    * add README.source
+  * disable most functions of Makefile
+    * disable process accounting (no kernel patch)
+    * don't install package's init script
+    * disable upstream's cron job
+    * disable versioned symlinks
+    * disable chkconfig call from Makefile
+  * postinst: remove path from logrotate, move DEBHELPER last, set -e
+  * new init script
+    * Thanks to Peter Eisentraut for a patch that didn't get used
+      (Closes: #529003)
+  * put proper Copyright statement in debian/copyright
+  * debian/control
+    * add ${misc:Depends}
+    * Standards-Version: 3.9.3 (no further changes necessary)
+    * versioned build dep on debhelper
+    * add Vcs-Lines
+  * Import sources to alioth/collab-maint
+  * New Maintainer (cleared with mia@qa) (Closes: #646744)
+
+ -- Marc Haber <mh+debian-packages@zugschlus.de>  Sun, 20 May 2012 21:07:13 +0000
+
+atop (1.23-1.1) unstable; urgency=high
+
+  * Non-maintainer upload.
+  * Fix CVE-2011-XXXX: Insecure use of a temporary files rawlog.c and
+    acctproc.c (Closes: #622794)
+
+ -- Jonathan Wiltshire <jmw@debian.org>  Mon, 01 Aug 2011 15:35:16 +0100
+
+atop (1.23-1) unstable; urgency=low
+
+  * fixed typo (Closes: #467447)
+  * last release was built as native package
+
+ -- Edelhard Becker <edelhard@debian.org>  Fri, 07 Mar 2008 22:07:27 +0100
+
+atop (1.23) unstable; urgency=low
+
+  * new upstream release
+  * bump standards to 3.7.3 (no changes)
+  * moved homepage from description to control field
+
+ -- Edelhard Becker <edelhard@debian.org>  Fri, 07 Mar 2008 16:16:15 +0100
+
+atop (1.22) unstable; urgency=low
+
+  * new upstream release
+
+ -- Edelhard Becker <edelhard@debian.org>  Tue, 20 Nov 2007 17:44:47 +0100
+
+atop (1.21-1) unstable; urgency=low
+
+  * new upstream release
+  * made rules and init.d script lintian clean
+
+ -- Edelhard Becker <edelhard@debian.org>  Fri, 31 Aug 2007 15:55:33 +0200
+
+atop (1.20-2) unstable; urgency=low
+
+  * do not install upstream's crontab file
+
+ -- Edelhard Becker <edelhard@debian.org>  Thu, 03 May 2007 00:12:59 +0200
+
+atop (1.20-1) unstable; urgency=low
+
+  * New upstream release (Closes: #411133, #372935)
+  * include upstream Changelog (Closes: #340093)
+  * bumped standards to 3.7.2
+  * applied patch to cooperate with sa from acct package, thanks Dominique
+    (Closes: #420676)
+  * restart daemon by logrotate via invoke-rc.d (Closes: #393545)
+
+ -- Edelhard Becker <edelhard@debian.org>  Wed, 02 May 2007 02:30:09 +0200
+
+atop (1.16-2) unstable; urgency=low
+
+  * depend on logrotate (Closes: #362893), thanks Tommi
+
+ -- Edelhard Becker <edelhard@debian.org>  Mon, 17 Apr 2006 15:00:20 +0200
+
+atop (1.16-1) unstable; urgency=low
+
+  * New upstream release. Most notable upstream changes:
+
+     - New line with system-level counters called PAG (paging-related counters
+       like scan, stall, swin and swout).                   
+     - For network-interfaces the effective line-speed in bits-per-second for
+       incoming and outgoing traffic is shown now (so Kbps/Mbps/Gbps).       
+     - New function '1': show averages *per second* where applicable.
+     - Improved output that is easier to parse (fixed number of fields per
+       line).     
+
+ -- Edelhard Becker <edelhard@debian.org>  Fri,  7 Apr 2006 11:59:46 +0200
+
+atop (1.15-2) unstable; urgency=low
+
+  * remove log files on purge (Closes: #359083), thanks Justin
+
+ -- Edelhard Becker <edelhard@debian.org>  Tue, 28 Mar 2006 16:29:27 +0200
+
+atop (1.15-1) unstable; urgency=low
+
+  * New upstream release
+
+ -- Edelhard Becker <edelhard@debian.org>  Mon,  7 Nov 2005 23:08:01 +0100
+
+atop (1.14.99-1) unstable; urgency=low
+
+  * New upstream release (upcoming 1.15 preview)
+  * removed /etc/atop/ stuff (not necessary) and the corresponding section
+    from the man page
+  * removed /etc/rc.d/ stuff (not necessary)
+
+ -- Edelhard Becker <edelhard@debian.org>  Sun, 30 Oct 2005 11:56:20 +0100
+
+atop (1.14-1) unstable; urgency=low
+
+  * New upstream release
+  * added URL of the new website where appropriate (copyright, control, README
+    and watch)
+
+ -- Edelhard Becker <edelhard@debian.org>  Sat, 18 Dec 2004 14:01:42 +0100
+
+atop (1.13-1) unstable; urgency=low
+
+  * New upstream release (added recognition of thread-groups and scheduling
+    information)
+  * fixed missing options in manpage and help-screen
+
+ -- Edelhard Becker <edelhard@debian.org>  Fri, 24 Sep 2004 11:09:04 +0200
+
+atop (1.12-1) unstable; urgency=low
+
+  * New upstream release
+
+ -- Edelhard Becker <edelhard@debian.org>  Wed,  2 Jun 2004 10:44:36 +0200
+
+atop (1.10-2) unstable; urgency=low
+
+  * changed default for raw data filename to /var/log/atop.1 to reflect the
+    Debian layout (Closes: #217387); adapted man page
+  * bumped policy to 3.6.1 (no changes needed)
+
+ -- Edelhard Becker <edelhard@debian.org>  Fri, 24 Oct 2003 14:48:11 +0200
+
+atop (1.10-1) unstable; urgency=low
+
+  * New upstream release
+  * increased storage for a number of resources to allow larger values (like
+    reported in #197626)
+  * new commands (see /usr/share/doc/atop/NEWS.Debian.gz for details)
+  * upstream replaced sys/acct.h by linux/acct.h (Closes: #198890)
+  * trailing `pidof atop` removed from init.d/atop (Closes: #199330)
+
+ -- Edelhard Becker <edelhard@debian.org>  Wed,  9 Jul 2003 15:57:14 +0200
+
+atop (1.9-6) unstable; urgency=low
+
+  * changed some 'long's to 'long long's to prevent overflows at 2GB
+    (Closes: #197626)
+  * bumped standards to 3.5.10
+
+ -- Edelhard Becker <edelhard@debian.org>  Mon, 16 Jun 2003 21:23:21 +0200
+
+atop (1.9-5) unstable; urgency=low
+
+  * changed Architecture to any (Closes: #192620)
+  * logrotate now should work - seems that atop needs more time after stopping
+    via start-stop-daemon than logrotate waits
+  * changed maintainer e-mail to my debian account
+  * force inclusion of orig.tar.gz via dpkg-buildpackage -sa
+
+ -- Edelhard Becker <edelhard@debian.org>  Mon, 26 May 2003 18:30:02 +0200
+
+atop (1.9-4) unstable; urgency=low
+
+  * improved logrotate (again)
+  * new upload should include atop-1.9.orig.tar.gz (Closes: #192344)
+
+ -- Edelhard Becker <becker@edelhard.de>  Thu,  8 May 2003 12:38:51 +0200
+
+atop (1.9-2) unstable; urgency=low
+
+  * added -oknodo to start-stop-deamon to allow smooth upgrade
+  * improved upgrade / logrotate handling
+
+ -- Edelhard Becker <becker@edelhard.de>  Fri,  2 May 2003 16:56:19 +0200
+
+atop (1.9-1) unstable; urgency=low
+
+  * Initial Release.
+  * removed atop.{daily,24hours,cron}, rely on init.d/atop instead
+  * add support for logrotate
+  * Initial upload (Closes: #190729)
+
+ -- Edelhard Becker <becker@edelhard.de>  Fri, 25 Apr 2003 14:27:39 +0200
+
diff --git a/debian/compat b/debian/compat
new file mode 100644 (file)
index 0000000..f599e28
--- /dev/null
@@ -0,0 +1 @@
+10
diff --git a/debian/control b/debian/control
new file mode 100644 (file)
index 0000000..f6c5eab
--- /dev/null
@@ -0,0 +1,28 @@
+Source: atop
+Section: admin
+Priority: optional
+Maintainer: OSCORE Packaging Team <oscore-eng@mirantis.com>
+XSBC-Original-Maintainer: Marc Haber <atop@packages.debian.org>
+Uploaders: Marc Haber <mh+debian-packages@zugschlus.de>
+Build-Depends: debhelper (>= 9), zlib1g-dev, libncurses5-dev
+Standards-Version: 4.1.2
+Homepage: http://atoptool.nl/
+Vcs-Git: https://salsa.debian.org/debian/atop.git
+Vcs-Browser: https://salsa.debian.org/debian/atop
+
+Package: atop
+Architecture: linux-any
+Depends: ${shlibs:Depends}, ${misc:Depends}, lsb-base (>= 3.2-14)
+Recommends: cron | cron-daemon
+Description: Monitor for system resources and process activity
+ Atop is an ASCII full-screen performance monitor, similar to the top command,
+ but atop only shows the active system-resources and processes, and only shows
+ the deviations since the previous interval.  At regular intervals, it shows
+ system-level activity related to the CPU, memory, swap, disks and network
+ layers, and it shows for every active process the CPU utilization in system
+ and user mode, the virtual and resident memory growth, priority, username,
+ state, and exit code. The process level activity is also shown for processes
+ which finished during the last interval, to get a complete overview about the
+ consumers of things such as CPU time.
+ .
+  Author: Gerlof Langeveld <gerlof@ATComputing.nl>
diff --git a/debian/copyright b/debian/copyright
new file mode 100644 (file)
index 0000000..27673de
--- /dev/null
@@ -0,0 +1,13 @@
+This package was debianized by Edelhard Becker <becker@edelhard.de> on
+Thu, 13 Feb 2003 21:28:07 +0100.
+
+It was downloaded from ftp://ftp.ATComputing.nl/pub/tools/linux
+New homepage and downloads via http://www.atconsultancy.nl/atop/home.html
+
+Upstream Author: Gerlof Langeveld <gerlof@ATComputing.nl>
+
+Copyright (C) 2000-2010 Gerlof Langeveld, AT Computing (gerlof@ATComputing.nl)
+
+You are free to distribute this software under the terms of the GNU General
+Public License.  On Debian systems, the complete text of the GNU General Public
+License can be found in the file `/usr/share/common-licenses/GPL'.
diff --git a/debian/dirs b/debian/dirs
new file mode 100644 (file)
index 0000000..819d141
--- /dev/null
@@ -0,0 +1,3 @@
+usr/bin
+usr/share/man/man1
+usr/share/atop
diff --git a/debian/docs b/debian/docs
new file mode 100644 (file)
index 0000000..e845566
--- /dev/null
@@ -0,0 +1 @@
+README
diff --git a/debian/patches/atop-daily b/debian/patches/atop-daily
new file mode 100644 (file)
index 0000000..324ead7
--- /dev/null
@@ -0,0 +1,13 @@
+Description: More explanation in atop.daily
+Author: Marc Haber <mh+debian-packages@zugschlus.de>
+Forwarded: no
+Last-Update: 2016-08-07
+--- a/atop.daily
++++ b/atop.daily
+@@ -1,4 +1,6 @@
+ #!/bin/bash
++# this is called - on sysvinit systems - at midnight by cron
++# on systemd systems this is called from the systemd unit
+ CURDAY=`date +%Y%m%d`
+ LOGPATH=/var/log/atop
diff --git a/debian/patches/atop-pm b/debian/patches/atop-pm
new file mode 100644 (file)
index 0000000..3ba007e
--- /dev/null
@@ -0,0 +1,43 @@
+Description: install atop-pm to /etc/systemd/system-sleep/atop-pm
+Author: Marc Haber <mh+debian-packages@zugschlus.de>
+Forwarded: no
+Last-Update: 2016-10-25
+--- a/Makefile
++++ b/Makefile
+@@ -17,7 +17,7 @@ CRNPATH  = /etc/cron.d
+ ROTPATH  = /etc/logrotate.d
+ PMPATH1  = /usr/lib/pm-utils/sleep.d
+ PMPATH2  = /usr/lib64/pm-utils/sleep.d
+-PMPATHD  = /usr/lib/systemd/system-sleep
++PMPATHD  = /lib/systemd/system-sleep
+ CFLAGS  += -O2 -I. -Wall       # -DHTTPSTATS
+ OBJMOD0  = version.o
+@@ -62,8 +62,8 @@ systemdinstall:      genericinstall
+               then    mkdir -p $(DESTDIR)$(PMPATHD); fi
+               #
+               cp atop.cronsystemd   $(DESTDIR)$(CRNPATH)/atop
+-              cp atop-pm.sh         $(DESTDIR)$(PMPATHD)
+-              chmod 0711            $(DESTDIR)$(PMPATHD)/atop-pm.sh
++              cp atop-pm.sh         $(DESTDIR)$(PMPATHD)/atop-pm
++              chmod 0711            $(DESTDIR)$(PMPATHD)/atop-pm
+               #
+               # only when making on target system:
+               #
+--- a/atop-pm.sh
++++ b/atop-pm.sh
+@@ -1,10 +1,12 @@
+ #!/bin/bash
++PATH=/sbin:/usr/sbin:/bin:/usr/bin
++
+ case "$1" in
+-      pre)    /usr/bin/systemctl stop atop
++      pre)    systemctl stop atop
+               exit 0
+               ;;
+-      post)   /usr/bin/systemctl start atop
++      post)   systemctl start atop
+               exit 0
+               ;;
+       *)      exit 1
diff --git a/debian/patches/atopacct.service b/debian/patches/atopacct.service
new file mode 100644 (file)
index 0000000..a8dd1c0
--- /dev/null
@@ -0,0 +1,14 @@
+Description: don't depend on obsolete syslog target (syslog is socket activated)
+Author: Marc Haber <mh+debian-packages@zugschlus.de>
+Forwarded: no
+Last-Update: 2016-08-07
+--- a/atopacct.service
++++ b/atopacct.service
+@@ -2,7 +2,6 @@
+ Description=Atop process accounting daemon
+ Documentation=man:atopacctd(8)
+ Conflicts=psacct.service
+-After=syslog.target
+ Before=atop.service
+ [Service]
diff --git a/debian/patches/dependency-on-remote-fs b/debian/patches/dependency-on-remote-fs
new file mode 100644 (file)
index 0000000..3dc39f0
--- /dev/null
@@ -0,0 +1,30 @@
+Description: If init script uses files from /usr, $remote_fs is needed as Dependency
+Author: Marc Haber <mh+debian-packages@zugschlus.de>
+Forwarded: via web form
+Last-Update: 2016-08-07
+--- a/atop.init
++++ b/atop.init
+@@ -7,8 +7,8 @@
+ #
+ ### BEGIN INIT INFO
+ # Provides:             atop
+-# Required-Start:     $local_fs
+-# Required-Stop:      $local_fs
++# Required-Start:     $local_fs $remote_fs
++# Required-Stop:      $local_fs $remote_fs
+ # Default-Start:      2 3 4 5
+ # Default-Stop:               0 1 6
+ # Short-Description: Advanced system and process activity monitor
+--- a/atopacct.init
++++ b/atopacct.init
+@@ -7,8 +7,8 @@
+ #
+ ### BEGIN INIT INFO
+ # Provides:             atopacct
+-# Required-Start:     $local_fs
+-# Required-Stop:      $local_fs
++# Required-Start:     $local_fs $remote_fs
++# Required-Stop:      $local_fs $remote_fs
+ # Default-Start:      2 3 4 5
+ # Default-Stop:               0 1 6
+ # Description:          This daemon switches on process accounting and
diff --git a/debian/patches/dh_installinit b/debian/patches/dh_installinit
new file mode 100644 (file)
index 0000000..fffb436
--- /dev/null
@@ -0,0 +1,15 @@
+Description: do not install init scripts directly
+Author: Marc Haber <mh+debian-packages@zugschlus.de>
+Forwarded: via web form
+Last-Update: 2016-08-07
+--- a/Makefile
++++ b/Makefile
+@@ -82,8 +82,6 @@ sysvinstall: genericinstall
+               if [ ! -d $(DESTDIR)$(INIPATH) ];                       \
+               then    mkdir -p  $(DESTDIR)$(INIPATH); fi
+               #
+-              cp atop.init      $(DESTDIR)$(INIPATH)/atop
+-              cp atopacct.init  $(DESTDIR)$(INIPATH)/atopacct
+               cp atop.cronsysv  $(DESTDIR)$(CRNPATH)/atop
+               #
+               if [   -d $(DESTDIR)$(PMPATH1) ];                       \
diff --git a/debian/patches/dh_systemd_enable b/debian/patches/dh_systemd_enable
new file mode 100644 (file)
index 0000000..f5a8dad
--- /dev/null
@@ -0,0 +1,17 @@
+Description: do not install atop.service and atopacct.service
+Author: Marc Haber <mh+debian-packages@zugschlus.de>
+Forwarded: via web form
+Last-Update: 2016-08-07
+--- a/Makefile
++++ b/Makefile
+@@ -61,10 +61,6 @@ systemdinstall:     genericinstall
+               if [ ! -d $(DESTDIR)$(PMPATHD) ];                       \
+               then    mkdir -p $(DESTDIR)$(PMPATHD); fi
+               #
+-              cp atop.service       $(DESTDIR)$(SYSDPATH)
+-              chmod 0644            $(DESTDIR)$(SYSDPATH)/atop.service
+-              cp atopacct.service   $(DESTDIR)$(SYSDPATH)
+-              chmod 0644            $(DESTDIR)$(SYSDPATH)/atopacct.service
+               cp atop.cronsystemd   $(DESTDIR)$(CRNPATH)/atop
+               cp atop-pm.sh         $(DESTDIR)$(PMPATHD)
+               chmod 0711            $(DESTDIR)$(PMPATHD)/atop-pm.sh
diff --git a/debian/patches/force-reload b/debian/patches/force-reload
new file mode 100644 (file)
index 0000000..d8c4eb2
--- /dev/null
@@ -0,0 +1,28 @@
+Description: force-reload is required, and restart|force-reload should not be a no-op
+Author: Marc Haber <mh+debian-packages@zugschlus.de>
+Forwarded: via web form
+Last-Update: 2016-08-07
+--- a/atop.init
++++ b/atop.init
+@@ -71,7 +71,7 @@ case "$1" in
+       /usr/share/atop/atop.daily&
+       ;;
+-  restart)
++  restart|force-reload)
+       /usr/share/atop/atop.daily&
+       ;;
+--- a/atopacct.init
++++ b/atopacct.init
+@@ -77,7 +77,9 @@ case "$1" in
+   reload)
+       ;;
+-  restart)
++  restart|force-reload)
++      $0 stop
++        $0 start
+       ;;
+   *)
diff --git a/debian/patches/init-script-lsb-headers b/debian/patches/init-script-lsb-headers
new file mode 100644 (file)
index 0000000..6505ea8
--- /dev/null
@@ -0,0 +1,21 @@
+Description: Short-Description and Description were swapped
+Author: Marc Haber <mh+debian-packages@zugschlus.de>
+Forwarded: via web form as general request to review Debian patches
+Last-Update: 2016-08-07
+--- a/atopacct.init
++++ b/atopacct.init
+@@ -11,10 +11,10 @@
+ # Required-Stop:      $local_fs
+ # Default-Start:      2 3 4 5
+ # Default-Stop:               0 1 6
+-# Short-Description: This daemon switches on process accounting and
+-#                    transfers the process accounting records 'realtime'
+-#                    to small shadow files to avoid huge disk space usage
+-# Description: Process accounting control
++# Description:          This daemon switches on process accounting and
++#                       transfers the process accounting records 'realtime'
++#                       to small shadow files to avoid huge disk space usage
++# Short-Description:    Process accounting control
+ ### END INIT INFO
+ . /lib/lsb/init-functions
diff --git a/debian/patches/logrotate-nomail b/debian/patches/logrotate-nomail
new file mode 100644 (file)
index 0000000..5b5894e
--- /dev/null
@@ -0,0 +1,26 @@
+Description: set nomail in logrotate files
+Author: Marc Haber <mh+debian-packages@zugschlus.de>
+Forwarded: no
+Last-Update: 2016-08-11
+--- a/psaccs_atop
++++ b/psaccs_atop
+@@ -3,6 +3,8 @@
+     missingok
+     daily
+     rotate 0
++    nomail
++    nocompress
+     ifempty
+     create 0600 root root
+     postrotate
+--- a/psaccu_atop
++++ b/psaccu_atop
+@@ -3,6 +3,8 @@
+     missingok
+     daily
+     rotate 0
++    nomail
++    nocompress
+     ifempty
+     create 0600 root root
+     postrotate
diff --git a/debian/patches/lsb-init-functions b/debian/patches/lsb-init-functions
new file mode 100644 (file)
index 0000000..2797f55
--- /dev/null
@@ -0,0 +1,26 @@
+Description: call isb/init-functions for systemd compatibility
+Author: Marc Haber <mh+debian-packages@zugschlus.de>
+Forwarded: no
+Last-Update: 2016-08-07
+--- a/atop.init
++++ b/atop.init
+@@ -15,6 +15,8 @@
+ # Description: Advanced system and process activity monitor
+ ### END INIT INFO
++. /lib/lsb/init-functions
++
+ # Check existance of binaries 
+ [ -f /usr/bin/atop ] || exit 0
+--- a/atopacct.init
++++ b/atopacct.init
+@@ -17,6 +17,8 @@
+ # Description: Process accounting control
+ ### END INIT INFO
++. /lib/lsb/init-functions
++
+ # Check existance of binaries 
+ [ -f /usr/sbin/atopacctd ] || exit 0
diff --git a/debian/patches/makefile-clean b/debian/patches/makefile-clean
new file mode 100644 (file)
index 0000000..42de7db
--- /dev/null
@@ -0,0 +1,12 @@
+--- a/Makefile
++++ b/Makefile
+@@ -44,8 +44,7 @@ netlink.o:   netlink.c
+               $(CC) -I. -Wall -c netlink.c
+ clean:
+-              rm -f *.o
+-              rm atop atopsar atopacctd
++              rm -f *.o atop atopsar atopacctd
+ distr:
+               rm -f *.o atop
diff --git a/debian/patches/no-files-in-var-log b/debian/patches/no-files-in-var-log
new file mode 100644 (file)
index 0000000..1e417c0
--- /dev/null
@@ -0,0 +1,15 @@
+Description: don't create dummy files in log dir on package build
+Author: Marc Haber <mh+debian-packages@zugschlus.de>
+Forwarded: no
+Last-Update: 2016-08-07
+--- a/Makefile
++++ b/Makefile
+@@ -149,8 +149,6 @@ genericinstall:    atop atopacctd
+               cp man/atopacctd.8      $(DESTDIR)$(MAN8PATH)
+               cp psaccs_atop          $(DESTDIR)$(ROTPATH)/psaccs_atop
+               cp psaccu_atop          $(DESTDIR)$(ROTPATH)/psaccu_atop
+-              touch                   $(DESTDIR)$(LOGPATH)/dummy_before
+-              touch                   $(DESTDIR)$(LOGPATH)/dummy_after
+ ##########################################################################
diff --git a/debian/patches/no-version-symlinks b/debian/patches/no-version-symlinks
new file mode 100644 (file)
index 0000000..a4fbea3
--- /dev/null
@@ -0,0 +1,15 @@
+Description: do not symlink atop(sar)-$(VERS)
+Author: Marc Haber <mh+debian-packages@zugschlus.de>
+Forwarded: via web form
+Last-Update: 2016-08-07
+--- a/Makefile
++++ b/Makefile
+@@ -139,8 +139,6 @@ genericinstall:    atop atopacctd
+               cp atopacctd            $(DESTDIR)$(SBINPATH)/atopacctd
+               chown root              $(DESTDIR)$(SBINPATH)/atopacctd
+               chmod 0700              $(DESTDIR)$(SBINPATH)/atopacctd
+-              cp atop                 $(DESTDIR)$(BINPATH)/atop-$(VERS)
+-              ln -sf atop-$(VERS)     $(DESTDIR)$(BINPATH)/atopsar-$(VERS)
+               cp atop.daily           $(DESTDIR)$(SCRPATH)
+               chmod 0711              $(DESTDIR)$(SCRPATH)/atop.daily
+               cp man/atop.1           $(DESTDIR)$(MAN1PATH)
diff --git a/debian/patches/series b/debian/patches/series
new file mode 100644 (file)
index 0000000..5932dfc
--- /dev/null
@@ -0,0 +1,15 @@
+dh_systemd_enable
+dh_installinit
+lsb-init-functions
+systemd-path
+atopacct.service
+init-script-lsb-headers
+force-reload
+dependency-on-remote-fs
+no-files-in-var-log
+no-version-symlinks
+var-run
+atop-daily
+logrotate-nomail
+atop-pm
+makefile-clean
diff --git a/debian/patches/systemd-path b/debian/patches/systemd-path
new file mode 100644 (file)
index 0000000..930c341
--- /dev/null
@@ -0,0 +1,15 @@
+Description: put system unit in /lib/systemd
+Author: Marc Haber <mh+debian-packages@zugschlus.de>
+Forwarded: no
+Last-Update: 2016-08-07
+--- a/Makefile
++++ b/Makefile
+@@ -12,7 +12,7 @@ MAN1PATH = /usr/share/man/man1
+ MAN5PATH = /usr/share/man/man5
+ MAN8PATH = /usr/share/man/man8
+ INIPATH  = /etc/init.d
+-SYSDPATH = /usr/lib/systemd/system
++SYSDPATH = /lib/systemd/system
+ CRNPATH  = /etc/cron.d
+ ROTPATH  = /etc/logrotate.d
+ PMPATH1  = /usr/lib/pm-utils/sleep.d
diff --git a/debian/patches/var-run b/debian/patches/var-run
new file mode 100644 (file)
index 0000000..f4bca72
--- /dev/null
@@ -0,0 +1,217 @@
+Description: replace /var/run with /run
+Author: Marc Haber <mh+debian-packages@zugschlus.de>
+Forwarded: via web form
+Last-Update: 2016-08-07
+--- a/45atoppm
++++ b/45atoppm
+@@ -4,7 +4,7 @@
+ LOGPATH=/var/log/atop
+ BINPATH=/usr/bin
+-PIDFILE=/var/run/atop.pid
++PIDFILE=/run/atop.pid
+ INTERVAL=600          # interval 10 minutes
+ CURDAY=`date +%Y%m%d` # current date in same format
+--- a/atop.daily
++++ b/atop.daily
+@@ -3,7 +3,7 @@
+ CURDAY=`date +%Y%m%d`
+ LOGPATH=/var/log/atop
+ BINPATH=/usr/bin
+-PIDFILE=/var/run/atop.pid
++PIDFILE=/run/atop.pid
+ INTERVAL=600                          # interval 10 minutes
+ # verify if atop still runs for daily logging
+--- a/atop.init
++++ b/atop.init
+@@ -20,7 +20,7 @@
+ # Check existance of binaries 
+ [ -f /usr/bin/atop ] || exit 0
+-PIDFILE=/var/run/atop.pid
++PIDFILE=/run/atop.pid
+ RETVAL=0
+ # See how we were called.
+@@ -35,7 +35,7 @@ case "$1" in
+               # Start atop
+               /usr/share/atop/atop.daily&
+       fi
+-      touch /var/lock/subsys/atop
++      touch /run/lock/atop
+       ;;
+   stop)
+@@ -61,7 +61,7 @@ case "$1" in
+               rm $PIDFILE
+       fi
+-      rm /var/lock/subsys/atop
++      rm /run/lock/atop
+       ;;
+   status)
+--- a/atopacct.init
++++ b/atopacct.init
+@@ -54,11 +54,11 @@ case "$1" in
+               :
+       else
+               # Start atopacctd
+-              rm -rf /var/run/pacct_shadow.d 2> /dev/null
++              rm -rf /run/pacct_shadow.d 2> /dev/null
+               /usr/sbin/atopacctd
+       fi
+-      touch /var/lock/subsys/atopacctd
++      touch /run/lock/atopacctd
+       ;;
+   stop)
+@@ -68,7 +68,7 @@ case "$1" in
+       then
+               kill $(ps -e | grep atopacctd$ | sed 's/^ *//' | cut -d' ' -f1)
+       fi
+-      rm /var/lock/subsys/atopacctd
++      rm /run/lock/atopacctd
+       ;;
+   status)
+--- a/atopacct.service
++++ b/atopacct.service
+@@ -6,7 +6,7 @@ Before=atop.service
+ [Service]
+ Type=forking
+-PIDFile=/var/run/atopacctd.pid
++PIDFile=/run/atopacctd.pid
+ ExecStart=/usr/sbin/atopacctd
+ [Install]
+--- a/atopacctd.c
++++ b/atopacctd.c
+@@ -24,7 +24,7 @@
+ **   any more. As soon as at least one client is activated again, the
+ **   atopacctd daemon start writing shadow files again.
+ **
+-** The directory /var/run is used as the default top-directory. An
++** The directory /run is used as the default top-directory. An
+ ** alternative top-directory can be specified as command line argument
+ ** (in that case, also modify /etc/atoprc to inform atop as a client).
+ ** Below this top-directory the source file pacct_source is created and
+--- a/atopacctd.h
++++ b/atopacctd.h
+@@ -7,7 +7,7 @@
+ /*
+ ** name of the PID file
+ */
+-#define       PIDFILE         "/var/run/atopacctd.pid"
++#define       PIDFILE         "/run/atopacctd.pid"
+ /*
+ ** directory containing the source accounting file and
+@@ -15,7 +15,7 @@
+ ** this directory can be overruled by a command line parameter (atopacctd)
+ ** or by a keyword in the /etc/atoprc file (atop)
+ */
+-#define       PACCTDIR        "/var/run"
++#define       PACCTDIR        "/run"
+ /*
+ ** accounting file (source file to which kernel writes records)
+--- a/man/atop.1
++++ b/man/atop.1
+@@ -2028,7 +2028,7 @@ View the contents of the standard logfil
+ .SH FILES
+ .PP
+ .TP 5
+-.B /var/run/pacct_shadow.d/
++.B /run/pacct_shadow.d/
+ Directory containing the process accounting shadow files that are
+ used by
+ .I atop
+@@ -2069,7 +2069,7 @@ All binary system and process level data
+ in compressed format. 
+ .PP
+ .TP 5
+-.BI /var/run/netatop.log
++.BI /run/netatop.log
+ File that contains the netpertask structs containing the network
+ counters of exited processes. These structs are written by the
+ .I netatopd
+--- a/man/atopacctd.8
++++ b/man/atopacctd.8
+@@ -70,7 +70,7 @@ any more. As soon as at least one client
+ daemon continues writing shadow files.
+ .PP
+ The directory
+-.B /var/run
++.B /run
+ is used as the default topdirectory.
+ Below this top-directory, the source file
+ .B pacct_source
+@@ -121,17 +121,17 @@ daemon.
+ .SH FILES
+ .PP
+ .TP 5
+-.B /var/run/pacct_source
++.B /run/pacct_source
+ Regular file to which the kernel writes the process accounting records.
+ This file will be regularly truncated.
+ .PP
+ .TP 5
+-.B /var/run/pacct_shadow.d/current
++.B /run/pacct_shadow.d/current
+ Regular file containing the sequence number of the current shadow file
+ and the maximum number of records per shadow file.
+ .PP
+ .TP 5
+-.B /var/run/pacct_shadow.d/N.paf
++.B /run/pacct_shadow.d/N.paf
+ Regular files containing the process accounting records that have
+ been copied transparently from the source file (N represents a 10-digit
+ sequence number).
+--- a/man/atoprc.5
++++ b/man/atoprc.5
+@@ -197,7 +197,7 @@ daemon. In this directory, the daemon cr
+ .B pacct_shadow.d
+ in which files will be written containing the process accounting records.
+ The default topdirectory is
+-.B /var/run
++.B /run
+ and this option only has to be specified when the
+ .B atopacctd
+ daemon is started with an alternative topdirectory as command line argument.
+--- a/netatopd.h
++++ b/netatopd.h
+@@ -1,6 +1,6 @@
+ #define SEMAKEY         1541961
+-#define NETEXITFILE     "/var/run/netatop.log"
++#define NETEXITFILE     "/run/netatop.log"
+ #define MYMAGIC         (unsigned int) 0xfeedb0b0
+ struct naheader {
+--- a/psaccs_atop
++++ b/psaccs_atop
+@@ -24,7 +24,7 @@
+                   # stop atop daemon before accounting file
+                   # is rotated
+                   #
+-                  PIDFILE=/var/run/atop.pid
++                  PIDFILE=/run/atop.pid
+                   if [ -e $PIDFILE ] && \
+                          ps -p `cat $PIDFILE` | grep 'atop$' > /dev/null
+--- a/psaccu_atop
++++ b/psaccu_atop
+@@ -10,7 +10,7 @@
+       then
+           # if the atop daemon does not run, restart it after
+           # accounting file is rotated
+-          PIDFILE=/var/run/atop.pid
++          PIDFILE=/run/atop.pid
+           if [ -e $PIDFILE ] && \
+                       ps -p `cat $PIDFILE` | grep 'atop$' > /dev/null
diff --git a/debian/postinst b/debian/postinst
new file mode 100644 (file)
index 0000000..99f61d8
--- /dev/null
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+set -e
+
+if dpkg-maintscript-helper supports rm_conffile; then
+       dpkg-maintscript-helper rm_conffile \
+               /etc/logrotate.d/atop 1.26-2~ -- "$@"
+fi
+
+case "$1" in
+    configure)
+        touch /var/log/atop/dummy_after /var/log/atop/dummy_before
+        if dpkg --compare-versions "$2" lt-nl 2; then
+            # updating from a pre-2.0 version which won't read the old log file format
+            # move away old log file format
+            DATE="$(date +%Y%m%d)"
+            if [ -e "/var/log/atop/atop_${DATE}" ]; then
+                mv "/var/log/atop/atop_${DATE}" "/var/log/atop/atop_${DATE}_pre_2_0"
+            fi
+        fi
+        ;;
+esac
+
+#DEBHELPER#
diff --git a/debian/postrm b/debian/postrm
new file mode 100644 (file)
index 0000000..74fe19b
--- /dev/null
@@ -0,0 +1,16 @@
+#! /bin/bash
+
+#DEBHELPER#
+
+set -e
+
+if dpkg-maintscript-helper supports rm_conffile; then
+       dpkg-maintscript-helper rm_conffile \
+               /etc/logrotate.d/atop 1.26-2~ -- "$@"
+fi
+
+if [ "$1" = "purge" ]
+then
+       rm -rf /var/log/atop
+fi
+
diff --git a/debian/preinst b/debian/preinst
new file mode 100644 (file)
index 0000000..db95cb3
--- /dev/null
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+set -e
+
+if dpkg-maintscript-helper supports rm_conffile; then
+       dpkg-maintscript-helper rm_conffile \
+               /etc/logrotate.d/atop 1.26-2~ -- "$@"
+fi
+
+#DEBHELPER#
diff --git a/debian/rules b/debian/rules
new file mode 100755 (executable)
index 0000000..28613df
--- /dev/null
@@ -0,0 +1,30 @@
+#!/usr/bin/make -f
+
+%:
+       dh $@
+
+override_dh_auto_clean:
+       dh_auto_clean
+       rm -f debian/atop.service debian/atopacct.service debian/atop.init debian/atopacct.init
+       rm -f atop
+
+override_dh_systemd_enable:
+       dh_systemd_enable atop.service
+       dh_systemd_enable atopacct.service
+
+override_dh_systemd_start:
+       dh_systemd_start atop.service
+       dh_systemd_start atopacct.service
+
+override_dh_installinit:
+       dh_installinit --name=atop
+       dh_installinit --name=atopacct
+
+override_dh_auto_install:
+       dh_auto_install
+       make sysvinstall DESTDIR=$(shell pwd)/debian/atop
+       make systemdinstall DESTDIR=$(shell pwd)/debian/atop
+       cp atop.service debian/atop.service
+       cp atopacct.service debian/atopacct.service
+       cp atop.init debian/atop.init
+       cp atopacct.init debian/atopacct.init
diff --git a/debian/source/format b/debian/source/format
new file mode 100644 (file)
index 0000000..163aaf8
--- /dev/null
@@ -0,0 +1 @@
+3.0 (quilt)
diff --git a/debian/watch b/debian/watch
new file mode 100644 (file)
index 0000000..9c5754a
--- /dev/null
@@ -0,0 +1,10 @@
+# Example watch control file for uscan
+# Rename this file to "watch" and then you can run the "uscan" command
+# to check for upstream updates and more.
+# Site                 Directory       Pattern                 Version Script
+version=2
+# the old ftp site
+#ftp://ftp.atcomputing.nl/pub/tools/linux/atop-(.*)\.tar\.gz   debian  uupdate
+# the new website
+#http://www.atconsultancy.nl/atop/download.html packages/atop-(.*)\.tar\.gz
+http://www.atoptool.nl/downloadatop.php download/atop-(.*)\.tar\.gz
diff --git a/sources/45atoppm b/sources/45atoppm
new file mode 100755 (executable)
index 0000000..231979d
--- /dev/null
@@ -0,0 +1,73 @@
+#!/bin/sh
+
+. "${PM_FUNCTIONS}"
+
+LOGPATH=/var/log/atop
+BINPATH=/usr/bin
+PIDFILE=/var/run/atop.pid
+INTERVAL=600           # interval 10 minutes
+CURDAY=`date +%Y%m%d`  # current date in same format
+
+# If the system suspends, one final sample will be taken for the logfile
+#
+suspend_atop()
+{
+       if [ -e $PIDFILE ] && ps -p `cat $PIDFILE` | grep 'atop$' > /dev/null
+       then
+               kill -USR2 `cat $PIDFILE`       # final sample and terminate
+
+               CNT=0
+
+               while ps -p `cat $PIDFILE` > /dev/null
+               do
+                       let CNT+=1
+
+                       if [ $CNT -gt 5 ]
+                       then
+                               break;
+                       fi
+
+                       sleep 1
+               done
+       fi
+}
+
+# If the system resumes, a new atop will be started (similar to boot)
+#
+resume_atop()
+{
+       # in case atop is running, stop it
+       #
+       if [ -e $PIDFILE ] && ps -p `cat $PIDFILE` | grep 'atop$' > /dev/null
+       then
+               kill -TERM `cat $PIDFILE`
+               rm $PIDFILE
+               sleep 1
+       fi
+
+       # start atop
+       #
+       $BINPATH/atop -R -w $LOGPATH/atop_$CURDAY $INTERVAL \
+                                        > $LOGPATH/daily.log 2>&1 &
+       echo $! > $PIDFILE
+       
+       # delete logfiles older than four weeks
+       #
+       (
+               sleep 3;
+               find $LOGPATH -name 'atop_*' -mtime +28 -exec rm {} \;
+       )&
+
+       exit 0
+}
+
+case "$1" in
+       hibernate|suspend)
+               suspend_atop
+               ;;
+       thaw|resume)
+               resume_atop
+               ;;
+       *)      exit $NA
+               ;;
+esac
diff --git a/sources/AUTHOR b/sources/AUTHOR
new file mode 100644 (file)
index 0000000..c34b373
--- /dev/null
@@ -0,0 +1 @@
+Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
diff --git a/sources/COPYING b/sources/COPYING
new file mode 100644 (file)
index 0000000..5b6e7c6
--- /dev/null
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/sources/ChangeLog b/sources/ChangeLog
new file mode 100644 (file)
index 0000000..29374e2
--- /dev/null
@@ -0,0 +1,2370 @@
+commit 8ce799fdd3bb50978c00735cc72bbeb2c70d6844
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri Mar 24 08:54:05 2017 +0100
+
+    Adapt man page for showing full process list for memory sorting
+
+M      man/atop.1
+
+commit 3105ce8d46562cb48081cb92eaee0afbda05166c
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri Mar 24 08:35:17 2017 +0100
+
+    Show full process list when sorting on memory is active
+
+M      showgeneric.c
+
+commit 35869466fc30f318ac404454c322bad039033251
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri Mar 17 16:32:30 2017 +0100
+
+    Solved segmentation fault
+    
+    Segmentation fault occurred in case of a selection
+    (e.g. on user) in combination with switching from
+    active processes to all processes.
+
+M      atop.h
+M      photoproc.c
+M      rawlog.c
+M      showgeneric.c
+M      showprocs.c
+
+commit eaeb7c500820cccc7bf25606a86c4843cd70ad31
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri Mar 17 11:25:46 2017 +0100
+
+    Type in man page
+
+M      man/atop.1
+
+commit 630d66700ca38ddda24d1797636d498e1e600866
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Thu Mar 9 08:30:57 2017 +0100
+
+    Corrected accumulated disk writes
+    
+    When accumulating disk writes for programs, users or
+    containers, values were accumulated twice per process.
+
+M      showgeneric.c
+
+commit 989185c62c3a023e80bb9b2a5bb2e5563cda1651
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Tue Mar 7 14:10:33 2017 +0100
+
+    Determine containerid per process generically
+    
+    The containerid of a process is determined via
+    the file /proc/PID/cpuset that sometimes contains
+    ".../docker-CONTAINERID" (on e.g. Redhat/CentOS)
+    and sometimes "/docker/CONTAINERID" (e.g. Ubuntu).
+    Both layouts should be supported.
+
+M      photoproc.c
+
+commit 6678463533947894e06b178c84213c17a5faae98
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Tue Mar 7 10:23:21 2017 +0100
+
+    Code optimizations releated to Docker containers
+
+M      photoproc.c
+M      showlinux.c
+
+commit fae417d905f6425035df0d3a4c99ddfa61b54e60
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Mon Mar 6 19:20:37 2017 +0100
+
+    Smaal correction on the man page description of CID.
+
+M      man/atop.1
+
+commit ef95f26bdad331b441fbeae2148b0352889256fd
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Mon Mar 6 15:51:47 2017 +0100
+
+    Introduce Docker container statistics
+    
+    Maintain the Docker container id (12 positions) per
+    process and per thread. This container id will be shown
+    in various lists as CID, but only if at least one Docker
+    container is present.
+    Furthermore, it is possible to select all processes
+    and threads that are related to a specific container id
+    (keystroke 'J') and to accumulate the resource consumption
+    for all processes/threads per container (keystroke 'j').
+
+M      atop.h
+M      man/atop.1
+M      parseable.c
+M      photoproc.c
+M      photoproc.h
+M      showgeneric.c
+M      showgeneric.h
+M      showlinux.c
+M      showlinux.h
+M      showprocs.c
+
+commit 0dc5a116cdbe8b1ccd6a0c3784e23a0e7e7abe41
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri Feb 24 15:55:28 2017 +0100
+
+    Solved segmentation fault in case of threadview
+    
+    Sometimes a process is considered to be inactive while one of its
+    threads appears to be active. This situation could lead to a
+    segmentation fault in case of threadview of only active tasks.
+    It has been solved by marking the inactive process as active when
+    later on a thread is discovered that appears to be active.
+
+M      deviate.c
+
+commit b4811a24801d8c35b64ab3c1b919418e934bb957
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri Feb 24 13:16:06 2017 +0100
+
+    Determine maximum number of digits in PID/TID
+    
+    Since the maximum PID number may vary (/proc/sys/kernel/pid_max),
+    the width of PID/PPID/TID/... columns should be determined dynamically.
+
+M      showlinux.c
+M      showprocs.c
+
+commit eb30f1d229cb5ac2c4b3ffe639c1efc8cbf70b72
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri Feb 24 09:58:40 2017 +0100
+
+    Add dependency between version.c and version.h
+
+M      Makefile
+
+commit fc0a5d5f15d5ffcf2203f95d290ce4e046ad1840
+Merge: f498827 0aa208a
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Thu Feb 23 13:57:46 2017 +0100
+
+    Polling mode when NETLINK refuses with error EINVAL
+    
+    Due to kernel bug 190711, the NETLINK interface gives EINVAL when
+    it is activated. Since atopacctd misses a trigger then to read the
+    process accounting records when processes in the system terminate,
+    a polling loop has to be used to verify the existance of new
+    process accounting records.
+
+commit f49882721e5dbb065038ba74ae44b85457f50645
+Merge: 17b2911 51d6285
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Thu Feb 23 13:53:53 2017 +0100
+
+    Retry activation of process accounting after failure
+    
+    According to kernel bug 190271 (process accounting sometimes
+    does not work), the kernel sometimes returns SUCCESS to switching
+    on process accounting while it does not work at all.
+
+commit 17b2911b172eaeafe9f0b8e0bd7e721d1766b81e
+Merge: e402891 e743f1b
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Thu Feb 23 13:52:43 2017 +0100
+
+    Modified version numbering (without dashes)
+
+commit e4028912e77cb1b092cae20173b4c3dea34971fe
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Thu Feb 23 13:34:09 2017 +0100
+
+    Modify the date in the man pages
+
+M      man/atop.1
+M      man/atopacctd.8
+M      man/atoprc.5
+M      man/atopsar.1
+
+commit eb10042167d1d3b72049619c36deeaf7f272d4fe
+Merge: c873d6c c8e2e3f
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Thu Feb 23 13:29:45 2017 +0100
+
+    Improved error handling on the NETLINK interface
+    
+    Properly check the error values that are returned via the
+    field nlmsg_type.
+
+commit c873d6c1eddcbfb3bd84a350684d7d7163aae377
+Merge: 5ad6f1d 2b6aab6
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Thu Feb 23 13:27:49 2017 +0100
+
+    Also show xvd... disk drives without a digit at the end of the name
+
+commit 5ad6f1dffae71cae39685e2883a8a178a40a6531
+Merge: 78c478a 51b4a83
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Thu Feb 23 13:25:06 2017 +0100
+
+    Atopacctd should terminate upon receipt of SIGTERM signal
+    
+    When activating a signal catcher via the signal() function,
+    the SA_RESTART flag is implicitly set. This means that a
+    pending system call is automatically restarted after the
+    received signal is handled.  Instead, the sigaction() system
+    call is used now to activate a signal catcher avoiding system
+    calls to be restarted automatically.
+
+commit 78c478a56f4281ce5e2d28d0fa9822ad4b7d6a37
+Merge: 46bc6d7 2a6f792
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Thu Feb 23 13:21:03 2017 +0100
+
+    Improved activation and deactivation of process accounting
+    
+    When atopacctd is started, it forks itself for proper daemonizing
+    purposes. The first process no longer exits immediately, but is
+    killed by the child process at the moment that atopacctd has
+    finished the initialization phase.
+
+commit 46bc6d7da1d47eeb73f55497e562019b59c527f7
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Wed Feb 22 15:52:15 2017 +0100
+
+    Script atop.daily located in /usr/share/atop instead of /etc/atop.d
+
+M      Makefile
+M      atop.cronsysv
+M      atop.init
+M      atop.service
+M      atop.specsystemd
+M      atop.specsysv
+M      man/atop.1
+M      psaccu_atop
+
+commit 0aa208a0af7bb384010f7332b3131c0884edcdd8
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Mon Dec 19 19:17:28 2016 +0100
+
+    Implemented polling mode in case NETLINK refuses with error EINVAL.
+    
+    Due to kernel bug 190711, the NETLINK interface gives EINVAL when
+    it is activated. Since atopacctd misses a trigger then to read
+    the process accounting records when processes in the system terminate,
+    a polling loop has to be used to verify the existance of new
+    process accounting records.
+
+M      README
+M      atopacctd.c
+
+commit 51d6285f97b2adf8f0efbea7121f4eb2a4b4f75d
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Mon Dec 19 16:05:18 2016 +0100
+
+    Retry activation of process accounting if it appears not to work.
+    
+    According to kernel bug 190271 (process accounting sometimes does not work),
+    the kernel sometimes returns SUCCESS to switching on process accounting
+    while it does not work at all.
+
+M      atopacctd.c
+
+commit e743f1b749d3fccb95bcef6028c238560a81d805
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Thu Dec 15 10:13:48 2016 +0100
+
+    Modified version numbering.
+
+M      mkdistr
+
+commit 96f0613201da8cb7e8ebd1120dd18f83abc83536
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Thu Dec 15 09:12:15 2016 +0100
+
+    Adapt date in man-pages.
+
+M      man/atop.1
+M      man/atopacctd.8
+M      man/atoprc.5
+M      man/atopsar.1
+
+commit c8e2e3f80fc46463c3e2c798b8a4d2a106ac98a5
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Thu Dec 15 09:11:25 2016 +0100
+
+    Improved error handling on the NETLINK interface.
+
+M      atopacctd.c
+M      netlink.c
+
+commit 2b6aab6454c39903a5ead4c3d5d819393ec1cea1
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Wed Nov 16 14:42:49 2016 +0100
+
+    Allow xvd... disk drives without a digit at the end of the name.
+
+M      photosyst.c
+
+commit 51b4a83b2babf9ca944da892092e5559d9588594
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Thu Oct 27 22:05:43 2016 +0200
+
+    Take care that atopacctd terminates upon receipt of SIGTERM signal.
+    When activating a signal catcher via the signal() function, the SA_RESTART
+    flag is implicitly set. This means that a pending system call is
+    automatically restarted after the received signal is handled.
+    Instead, the sigaction system call is used now to activate a signal catcher
+    avoiding system calls to be restarted automatically.
+
+M      atopacctd.c
+
+commit f0516657b3c7261c336f1b6e869afd9baa43d197
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Mon Oct 24 16:13:46 2016 +0200
+
+    Changed 'rotate 1' into 'rotate 0' (no need for another dummy file).
+
+M      psaccs_atop
+M      psaccu_atop
+
+commit 7617354131f9a9bb1503b89b82cc8ca939c0f472
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Mon Oct 24 16:12:35 2016 +0200
+
+    Changed 'rotate 1' into 'rotate 0' (no need for another dummy file).
+
+M      psaccs_atop
+M      psaccu_atop
+
+commit 2a6f792ad71410a75ca633ff38cb52a678cc05ed
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri Oct 14 21:34:08 2016 +0200
+
+    Improved activation and deactivation of process accounting.
+
+M      atopacctd.c
+
+commit 6484680418827937064c2aba11619369e189d94d
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Sat Oct 8 15:23:43 2016 +0200
+
+    Script atop.daily located in /usr/share/atop instead of /etc/atop.d
+
+M      Makefile
+M      atop.cronsysv
+M      atop.init
+M      atop.service
+M      atop.specsystemd
+M      atop.specsysv
+M      psaccu_atop
+
+commit 101c0139e1a703479f7fb779bc258cf199eaa9fb
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Sat Oct 8 14:31:52 2016 +0200
+
+    Raw logging: Determine features per sample
+    Define per sample whether or not process accounting is active and
+    whether or not disk I/O statitics are maintained.
+    In previous versions, this was determined for the entire log file
+    which implied that it was impossible to append to an existing logfile
+    when one of these features had changed.
+
+M      atop.h
+M      rawlog.c
+
+commit 36b9b09ac15519187717481ac79da25e66bce8eb
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Sat Oct 8 14:21:42 2016 +0200
+
+    Moved accounting file from /tmp to /var/cache.
+
+M      acctproc.c
+M      man/atop.1
+
+commit a44bafe1895515e92881225079d56bb04053c9cf
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Sat Oct 8 14:18:13 2016 +0200
+
+    Added -r y... flag to usage message.
+
+M      atopsar.c
+
+commit 2f7369f6911cc0fe6bb79dfa11ea845ebb036ea0
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Sat Oct 8 14:16:29 2016 +0200
+
+    The flag -V has been added to the help-output.
+
+M      atop.c
+
+commit a31b5a516e0dcfa5352e1047defbd6abf3c22444
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Sat Oct 8 14:12:13 2016 +0200
+
+    Modified column name SNETBW to BANDWO.
+    Modified column name RNETBW to BANDWI.
+
+M      showlinux.c
+M      showlinux.h
+M      showprocs.c
+
+commit 5b21d88014fe4e53a5ab8f01d03ae00a2de56a18
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Sat Oct 8 14:05:15 2016 +0200
+
+    Cosmetic changes in distribution script.
+
+M      mkdistr
+
+commit b96168f473ca8a69884d900464cfb5f641e18d26
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Sat Oct 8 14:03:55 2016 +0200
+
+    Cosmetic change.
+
+M      various.c
+
+commit 90a3f939d5db6a41e1352f847f8719a89e5ddf78
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Sat Oct 8 14:03:35 2016 +0200
+
+    Recalculate length of nodename when reading raw logfile.
+
+M      rawlog.c
+
+commit 3fd998d0f2e43e57ff8d5d22b747efb14c1da554
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Sat Oct 8 14:03:18 2016 +0200
+
+    Remove include of termio.h (not needed at all).
+
+M      showgeneric.c
+M      showlinux.c
+M      showprocs.c
+M      showsys.c
+
+commit 2820b1144ce403c7917cd2e09b05e78d2c9dbc07
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Sun Sep 18 14:36:54 2016 +0200
+
+    Moved accounting file from /tmp to /var/cache.
+
+M      acctproc.c
+M      man/atop.1
+
+commit e5a1be40373ebedf5bffede8968304fd3e0e8a18
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Sat Sep 17 10:12:29 2016 +0200
+
+    Raw logging: dDetermine features per sample
+    
+    Define per sample whether or not process accounting is active and
+    whether or not disk I/O statitics are maintained.
+    In previous versions, this was determined for the entire log file
+    which implied that it was impossible to append to an existing logfile
+    when one of these features had changed.
+
+M      atop.h
+M      rawlog.c
+
+commit fec3f38c9b08459dea1a86fcdef56e97d211c1b6
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Tue Sep 13 18:50:31 2016 +0200
+
+    Added -r y... flag to usage message.
+
+M      atopsar.c
+
+commit e721b7194acb0f3de893d22e733f96c93172b5b7
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Thu Sep 1 19:06:44 2016 +0200
+
+    Improved performance data gathering:
+    All process and thread related data is stored now
+    by function deviattask(), independent if the user
+    wants to view all data. In this way, immediate
+    reaction is garanteed for the 'a' key which allows
+    the user to view all processes/threads or to view
+    only the active processes/threads (previously,
+    the 'a' key was only effective after taking a new
+    sample).
+    Together with this modification, the behaviour of
+    the 'm' key (memory view) has changed by showing
+    automatically all processes instead of only the
+    active processes. After all, when viewing the memory
+    consumption of the processes, it is not relevant
+    whether or not that process has been active in the
+    last interval.
+
+M      45atoppm
+M      atop.c
+M      atop.daily
+M      atop.h
+M      atopsar.c
+M      deviate.c
+M      man/atop.1
+M      parseable.c
+M      parseable.h
+M      photoproc.h
+M      rawlog.c
+M      showgeneric.c
+M      showgeneric.h
+M      showlinux.c
+
+commit 9f1bfee3f0babf88e86c8258bad977885eef279e
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Thu Aug 11 15:55:50 2016 +0200
+
+    The flag -V has been added to the help-output.
+
+M      atop.c
+
+commit ba0cdc1474cebcbdac52b3ee9cc8516b33eeb0e8
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Thu Aug 11 15:55:18 2016 +0200
+
+    With 'make clean' also the executable files are removed now.
+
+M      Makefile
+
+commit 057c27d5f7b5e2b9ab06a91bed831d14dd7fe16b
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Mon Mar 7 12:16:06 2016 +0100
+
+    Modified column name SNETBW to BANDWO.
+    Modified column name RNETBW to BANDWI.
+
+M      showlinux.c
+M      showlinux.h
+M      showprocs.c
+
+commit 835d9a22dd88756709d75104550b206147c4151f
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Mon Mar 7 12:15:31 2016 +0100
+
+    Cosmetic change in distribution script.
+
+M      mkdistr
+
+commit d600eac843b87cdfb91a6e1807d3aff5915dfdea
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Wed Jan 13 08:42:58 2016 +0100
+
+    Cosmetic fix.
+
+M      various.c
+
+commit c2370bda4689217cefe60d1760d29fe1397436a4
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Mon Nov 30 15:26:21 2015 +0100
+
+    Recalculate length of nodename when reading raw logfile.
+
+M      rawlog.c
+
+commit a2306c0e1c83f5a123ce521a34d9efaa2297c26d
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Mon Nov 30 15:24:17 2015 +0100
+
+    Remove include of termio.h (not needed at all).
+
+M      showgeneric.c
+M      showlinux.c
+M      showprocs.c
+M      showsys.c
+
+commit 1c72c7dfe0843087440598e4df9dc844c2eff1eb
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Tue Nov 24 10:21:49 2015 +0100
+
+    The top-3 of CPU, disk and network consuming processes now only shows
+    processes that really used capacity of that resource. Implicitly, a
+    segmentation fault bug has been solved that occurred when less than
+    3 processes were available anyhow.
+
+M      atopsar.c
+
+commit 1c5cac40d0bec5ea9b9625a8c61a4ad38899aad9
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Tue Nov 24 08:59:37 2015 +0100
+
+    Column that show interface speed is shifted to the right, to keep
+    input and output packets for all layers aligned.
+
+M      showlinux.c
+
+commit 29012c89cacd30aa3c0f83e9f4c92b720ceda901
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Tue Aug 4 19:11:36 2015 +0200
+
+    Execpath has been changed to /usr/sbin instead of /sbin
+    (wrong directory location).
+
+M      atopacct.service
+
+commit 0f502b2633bb00a340daf8ff0ac3506d486a1e09
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Tue Aug 4 19:08:25 2015 +0200
+
+    Bug solution:
+    When starting atop via sudo, atop runs into
+    the stopped state. Caused by earlier modification
+    to make atop a process group leader (needed for systemd).
+    Problem solved by making atop only a process group leader
+    when it doesn't operate interactively.
+
+M      atop.c
+
+commit c0062f3e5a2604b3e8f0723e50c0cea0931d3e01
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Thu Jun 25 12:54:30 2015 +0200
+
+    Modified handling of release number.
+
+M      mkdistr
+
+commit 8becf63a08ba68ece7543512426cffa36e5c9c01
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Thu Jun 25 12:53:44 2015 +0200
+
+    Modified handling of release number.
+
+M      atop.specsystemd
+M      atop.specsysv
+
+commit c213a40de6884129877b1519400de362a64b1f37
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Thu Jun 25 12:53:14 2015 +0200
+
+    Added some blank fields for interfaces.
+
+M      showlinux.c
+
+commit 67f5b44cbd2ef5ad3a59e09a6e192af489300a52
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Tue Jun 23 16:55:30 2015 +0200
+
+    Minor bug-fixes.
+
+M      man/atop.1
+M      showgeneric.c
+
+commit 6f0105972808ffd852b833ed81c222f259bc762a
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Tue Jun 23 16:32:16 2015 +0200
+
+    New selection possibilities for processes:
+    Key 'I' can be used to specify one or more PIDs of processes
+    to be selected.
+    Key '/' can be used to specify a command line search string for processes
+    to be selected.
+
+M      man/atop.1
+M      showgeneric.c
+M      showgeneric.h
+
+commit d8f641ecca0ac117c2eacd33cc6517944461a0af
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Tue Jun 23 14:44:49 2015 +0200
+
+    Bugfix for buffer overflow: snprintf instead of sprintf (credits: Nathan Scott).
+
+M      showsys.c
+
+commit 8b82505a8125cf5b351b6de841cf6cd35740b133
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Tue Jun 9 12:30:36 2015 +0200
+
+    Fine-tune statistics about data transfer to/from NFS servers.
+
+M      atopsar.c
+M      man/atop.1
+M      man/atopsar.1
+
+commit 7c2b2cb6615a20e7e130a2fe00d4d029e0f5f3fa
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Tue Jun 9 11:03:10 2015 +0200
+
+    Bug solutions and small modifications for nfs4 mountstats.
+
+M      atop.h
+M      deviate.c
+M      man/atop.1
+M      photosyst.c
+M      showlinux.c
+M      showprocs.c
+M      showsys.c
+M      various.c
+
+commit e3d0e1f03ac0cc48c5add560377933a3abf8ddbe
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Mon Jun 8 17:31:58 2015 +0200
+
+    Added statistics per mounted NFS filesystem (label NFM).
+
+M      atop.c
+M      atop.h
+M      atopsar.c
+M      deviate.c
+M      man/atop.1
+M      man/atoprc.5
+M      man/atopsar.1
+M      parseable.c
+M      photosyst.c
+M      photosyst.h
+M      showgeneric.c
+M      showgeneric.h
+M      showlinux.c
+M      showlinux.h
+M      showsys.c
+
+commit 314a64d9c62b27747d23c6f85011411076482f51
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Mon Jun 8 10:01:05 2015 +0200
+
+    Solved various small bugs found by clang-analyzer.
+
+M      atopsar.c
+M      parseable.c
+M      rawlog.c
+M      showlinux.c
+M      various.c
+
+commit 9b621bcac274df21012a42a2d131143e5857a47a
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri May 29 08:42:05 2015 +0200
+
+    Added buffers for formatting HTTPSTATS-specific output.
+
+M      showlinux.c
+
+commit 66e4b0326d8663b8cc26cde42c9d1232e1742714
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri May 22 09:58:22 2015 +0200
+
+    Accept 'xvd[a-z]' disks with a number behind it (they always seem to
+    end with a number).
+
+M      photosyst.c
+
+commit 7d5f789ddbbaea7ae0e4d2c3f78a3f735ca3a36f
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Mon May 18 08:58:41 2015 +0200
+
+    Rebuild of lost spec file for systemd.
+
+M      atop.specsystemd
+
+commit 94f3da8d2740aaa3abfa7974afa900d985b8a9be
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Mon May 18 08:55:18 2015 +0200
+
+    Maintain current speed (rate) for wireless interfaces.
+
+M      atopsar.c
+M      deviate.c
+M      ifprop.c
+M      ifprop.h
+M      man/atop.1
+M      photosyst.h
+M      showlinux.c
+M      showlinux.h
+M      showsys.c
+
+commit 4ae902d49e3207c70153db2bbb8a3eed775c7c4a
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Sat May 9 18:01:12 2015 +0200
+
+    Support systemV and systemd RPMs.
+
+M      atop.specsystemd
+M      atop.specsysv
+M      mkdistr
+
+commit b7b704f4406c92d9c5cda53a4c72bb37afeea744
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Sat May 9 17:58:00 2015 +0200
+
+    Added defined value SPEED_UNKNOWN for older releases.
+
+M      ifprop.c
+
+commit ab95f1a444a5e796641012bf3aa85a70f8056760
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Sat May 9 16:27:17 2015 +0200
+
+    Support of systemv and systemd RPMs.
+
+M      mkdistr
+
+commit 0f9e3cfeb31450ced24375631c6130e9fef60ae8
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Sat May 9 16:21:31 2015 +0200
+
+    Support for OpenVZ containers.
+
+M      atop.c
+M      atop.h
+D      atop.spec
+M      deviate.c
+M      man/atop.1
+M      man/atoprc.5
+M      parseable.c
+M      photoproc.c
+M      photoproc.h
+M      photosyst.c
+M      photosyst.h
+M      showgeneric.c
+M      showgeneric.h
+M      showlinux.c
+M      showlinux.h
+M      showprocs.c
+M      showsys.c
+
+commit b77825747f165a7c3bd76353d8f428676732cedd
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Sat May 9 16:20:24 2015 +0200
+
+    Separate RPM spec files for SystemV init and systemd.
+
+A      atop.specsystemd
+A      atop.specsysv
+
+commit 8f0148d9946344fe9b8a5eeee5a20588e0b149dd
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Tue Apr 7 13:02:58 2015 +0200
+
+    Addition of NFS (server/client) statistics on system level.
+
+M      Makefile
+M      atopsar.c
+M      deviate.c
+M      man/atop.1
+M      man/atopsar.1
+M      parseable.c
+M      photosyst.c
+M      photosyst.h
+M      showlinux.c
+M      showlinux.h
+M      showsys.c
+
+commit 6e133e74acd987e915d7edeb4dab281ed8a055e2
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Tue Mar 24 15:52:11 2015 +0100
+
+    Add NFS client and NFS server statistics.
+
+M      atopsar.c
+M      deviate.c
+M      man/atop.1
+M      man/atopacctd.8
+M      man/atoprc.5
+M      man/atopsar.1
+M      parseable.c
+M      photosyst.c
+M      photosyst.h
+M      showlinux.c
+M      showlinux.h
+M      showsys.c
+
+commit 6da40d213f14865356956b196475c080be1c79e6
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Tue Mar 24 08:36:51 2015 +0100
+
+    Minor modification related to systemd-based install.
+
+M      Makefile
+
+commit 04276882956a196e872fca17fb1dd73289eb3d1f
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Wed Feb 25 15:26:55 2015 +0100
+
+    Prepare atop and atopacct for use on systemd-based systems.
+
+M      Makefile
+M      README
+A      atop-pm.sh
+M      atop.service
+M      atopacct.init
+A      atopacct.service
+M      atopacctd.c
+M      atopacctd.h
+M      mkdistr
+
+commit 1e2fc1c2275d71114a62fe95ca7363a6d9d1cd8a
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Wed Feb 25 15:20:05 2015 +0100
+
+    Avoid negative utilization counters for disks and logical volumes.
+    These negative values might cause buffer overflows within atop.
+
+M      showsys.c
+M      various.c
+
+commit 2c5327f1a0a6797383785e6893b46f39d3a67c85
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Tue Feb 24 09:34:52 2015 +0100
+
+    Avoid that process accounting is reactivated too frequently
+    (and unnecessary).
+
+M      atopacctd.c
+
+commit cfda21540a2a9b6545259ed612f8afc22b2218b8
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Tue Feb 24 09:32:41 2015 +0100
+
+    Added creation of the SBINPATH directory for atopacctd.
+
+M      Makefile
+
+commit 688f446ab5fb81dc6c44c065f956913682d1b03f
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Mon Feb 2 08:32:57 2015 +0100
+
+    Prepare systemd-based installation.
+
+A      atop.cronsystemd
+A      atop.cronsysv
+
+commit ab8db5f44f1b4edff624c4698849e391588e0cbd
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Mon Feb 2 08:31:33 2015 +0100
+
+    Prepare systemd-based installation.
+
+M      Makefile
+M      atop.c
+D      atop.cron
+M      atop.daily
+M      atop.init
+M      atop.service
+M      mkdistr
+M      psaccs_atop
+M      psaccu_atop
+
+commit a5e0d6d0c758ce8ecc3ba6f8df2c30df8af64cc2
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Mon Dec 8 09:38:47 2014 +0100
+
+    Define proper line speed for network interfaces faster than 1 Gbit/sec.
+
+M      ifprop.c
+
+commit d18f067446deb39933b21ba7b2874ea63cb47e71
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri Oct 17 14:31:27 2014 +0200
+
+    Minor change.
+
+M      mkdistr
+
+commit 52e9c4a0f05f76ab81d46dc1f42ac69f5e123ae5
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri Oct 17 14:28:10 2014 +0200
+
+    Minor correction in man-page.
+
+M      man/atopacctd.8
+
+commit c18d1354898e8871f75dedc8e8427694606b3832
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Thu Aug 28 13:52:57 2014 +0200
+
+    Correction of typo's in the man pages, including a wrong counter name
+    in the output of atop ('udpie' for UDP input errors).
+
+M      Makefile
+M      man/atop.1
+M      man/atopacctd.8
+M      man/atoprc.5
+M      man/atopsar.1
+M      showsys.c
+
+commit 4e739738da3aa5931cc0f66c7d402dea50ca6f6f
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Thu Aug 28 11:51:13 2014 +0200
+
+    OpenVZ support: virtual environment identifier per process (envID).
+
+M      photoproc.c
+M      photoproc.h
+M      showlinux.c
+M      showlinux.h
+M      showprocs.c
+
+commit 3bd1bab7bd6fb0ec110922afbb169264a335a74f
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri Jul 25 15:31:42 2014 +0200
+
+    Activate atopacctd before activating the atop daemon.
+
+M      atop.spec
+
+commit 0d02d0565b6d0df1c22be3237d145b7e0b7e68eb
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri Jul 25 14:58:19 2014 +0200
+
+    Improved help screen handling: use space bar to get next page.
+
+M      showgeneric.c
+
+commit 5cebf4101c11d4680e57250635c6148b104bf4f9
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri Jul 25 13:48:37 2014 +0200
+
+    Bug solution: when the screen-width is larger than the total number of
+    columns to be printed, empty columns are added, however one column too many.
+
+M      showsys.c
+
+commit b25ef4ec59240c58e929c93d17d42c91cf70121e
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri Jul 25 13:48:09 2014 +0200
+
+    Removed useless empty column for memory statistics.
+
+M      showlinux.c
+
+commit 38163c1230c5e58890e783dd1cb29e14640e6099
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri Jul 25 13:47:36 2014 +0200
+
+    Cosmetic change.
+
+M      atopacctd.c
+
+commit 636fa8df11c99b3d6468f4c13c830c29ba79638d
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Thu Jul 24 16:07:01 2014 +0200
+
+    Detect if process accounting has been switched off by some other
+    program and switch it on again.
+
+M      atopacctd.c
+M      atopacctd.h
+
+commit 268750adfc4e4a00050501d9c70d56436b4bb0a6
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Sat Jun 28 11:15:17 2014 +0200
+
+    Add a warning about the aggregation of disk transfer to the parent process
+    for the RDDSK and WRDSK counters.
+
+M      man/atop.1
+
+commit 1396164f8a5c880dc14658cc69a685babf040739
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri Jun 20 14:51:53 2014 +0200
+
+    Add automatic start of atopacctd daemon.
+
+M      Makefile
+
+commit 0dcc489d0ded0b2d2241cd9a625fd5206ddb7cd6
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri Jun 20 14:06:08 2014 +0200
+
+    Add man page of atopacctd.
+
+M      atop.spec
+M      mkdistr
+
+commit 5295539f7b7ba1e725d20caf92eb9d28e1dd87ce
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri Jun 20 13:52:16 2014 +0200
+
+    Add man page for the atopacct daemon.
+
+A      man/atopacctd.8
+
+commit 027c748ffcb1a4ee926a3c14f2241342a2e6c5bd
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri Jun 20 13:49:14 2014 +0200
+
+    Modify the man pages and installation procedures for the introduction
+    of the atopacctd daemon.
+
+M      Makefile
+M      atop.spec
+M      atopacctd.c
+M      man/atop.1
+M      man/atoprc.5
+M      man/atopsar.1
+
+commit 366622d86d3716661ad36b86bbe833bc9ce29496
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Wed Jun 18 13:24:49 2014 +0200
+
+    Introduce the 'G' key to suppress showing/accumulating exited processes
+    in the output.
+
+M      man/atop.1
+M      man/atoprc.5
+M      showgeneric.c
+M      showgeneric.h
+
+commit a7668eb60ebda954dd8223e4a501b21afdea4cec
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri Jun 13 07:55:33 2014 +0200
+
+    Support CPU frequencies for systems with Intel P-state driver.
+    Credits: Sjon Hortensius
+
+M      photosyst.c
+
+commit 8e269cc3a8df55852490b8b99148b0f973e3d334
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri Jun 13 07:51:55 2014 +0200
+
+    Cosmetic change.
+
+M      atopacctd.c
+
+commit f12727d632052e6a82149d6e4804e354f2caae54
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri Jun 13 07:50:25 2014 +0200
+
+    Remove rc-scripts with old sequence numbers (if any).
+
+M      Makefile
+
+commit 7cec49a8d095e4d520ffe7f21499a7996137fc78
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Sat Jun 7 12:47:29 2014 +0200
+
+    Doubled the %-signs to get the proper date string.
+
+M      atop.service
+
+commit 9b699e1c0aea4842609f53ba0df061b568aa6d13
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Sat Jun 7 12:11:52 2014 +0200
+
+    Source file netlink.c compiled without -O2 due to a strict-aliasing
+    warning.
+
+M      Makefile
+
+commit c1963ca5c51ccc8dba9866238376d49ffece962a
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Sat Jun 7 12:09:34 2014 +0200
+
+    Source file netlink.c compiled without -O2 due to a strict-aliasing
+    warning.
+
+D      atopacctd
+
+commit f2f369b2c79117aa03ce393e9cb54eab3bc36009
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Sat Jun 7 11:29:36 2014 +0200
+
+    Rename variables to clearly distinghuish between the two
+    semaphore groups.
+
+M      acctproc.c
+
+commit 35ded9cabe93b4f6a126711d31dbe3fdfd3d3ebe
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Sat Jun 7 11:28:46 2014 +0200
+
+    Add the -R flag to gather PSIZE info in the background.
+
+M      45atoppm
+M      atop.service
+
+commit d2a545c15bf4e3099d4391f24d20b079a5759872
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri Jun 6 08:49:35 2014 +0200
+
+    Introduction of new daemon atopacctd. This daemon switches on
+    process accounting and transfers every accounting record to an
+    accounting shadow file. The source accounting file will regularly
+    be truncated, while the shadow files are written with a limited
+    size in a queued way. Non-used shadow files are deleted regularly.
+
+A      atopacct.init
+A      atopacctd
+A      atopacctd.c
+A      atopacctd.h
+A      netlink.c
+
+commit b3909dd3243a7011ee605ed387692dcb0d8e17de
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri Jun 6 08:44:54 2014 +0200
+
+    Introduction of new daemon atopacctd. This daemon switches on
+    process accounting and transfers every accounting record to
+    an accounting shadow file. The source accounting file will
+    regularly be truncated, while the shadow files are written
+    with a limited size in a queued way. Non-used shadow files
+    are deleted regularly.
+
+M      Makefile
+M      acctproc.c
+M      acctproc.h
+M      atop.c
+M      atop.init
+M      atop.spec
+M      man/atoprc.5
+M      showgeneric.c
+
+commit 65abbee8fe9f4ea4ec7c875e897d1aa3a5b2763d
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Thu May 29 13:24:55 2014 +0200
+
+    Add the -R flag for monitoring in the background.
+
+M      atop.service
+
+commit 295585730cbbe322d0dd556c45413e2ab0737099
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Wed May 28 16:59:54 2014 +0200
+
+    Usage message of atop and atopsar extended with reference
+    to the man-page.
+
+M      atop.c
+M      atopsar.c
+
+commit 7df1f797fa27f5dd53298f7511bb36811dc51e14
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Wed May 28 16:46:00 2014 +0200
+
+    Added a function prototype for priline to verify the calling
+    (cause of earlier problems).
+
+M      atopsar.c
+
+commit b495b78b8b120bcf266440f6ed1f0e133573e76b
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Wed May 28 14:29:02 2014 +0200
+
+    Bug-solution: segmentation fault when one of the process names
+    in the system contains a newline.
+
+M      photoproc.c
+
+commit 216c4ccc9e26d1d195985f8b2c45c4008492a341
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Wed May 28 13:54:36 2014 +0200
+
+    Introduce configurable colors: in the atoprc file, colors can
+    be defined for information messages (default green), threads
+    (default yellow), almost critical resources (default cyan) and
+    critical resources (default red).
+
+M      atop.c
+M      man/atop.1
+M      man/atoprc.5
+M      man/atopsar.1
+M      showgeneric.c
+M      showgeneric.h
+M      showsys.c
+
+commit fd2bb81e07dbb1ac97a559e7099e717e05b80642
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Wed May 28 12:17:08 2014 +0200
+
+    Cosmetic change to message.
+
+M      showgeneric.c
+
+commit dc1ef5d83b72095536cfabd53adcfcfc8ec37a01
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Wed May 28 12:15:56 2014 +0200
+
+    Solve floating point exception in case that the process accouting
+    file was not open.
+
+M      acctproc.c
+
+commit a94a6170476406d1ca85aa018ea71eea6d41f5bd
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Wed May 28 11:27:05 2014 +0200
+
+    Limit the names of network interfaces to six characters max.
+
+M      atopsar.c
+
+commit 19826d8ae098b21cf733cf4e69bfb1936cddc7dd
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Wed May 28 11:01:13 2014 +0200
+
+    Modified names for RPM files.
+
+M      mkdistr
+
+commit b71978ea7ec610305a239529345e816e1323a02b
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Wed May 28 10:19:16 2014 +0200
+
+    Added Requires and BuildRequires.
+
+M      atop.spec
+
+commit f6d0eae38d1e9f093d15fb6fa74d353e59efd05d
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Wed May 28 10:18:35 2014 +0200
+
+    Cosmetic change.
+
+M      Makefile
+
+commit 318890acda8a7cab6bba829fa3276e774f1bd4e0
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Sun May 25 22:01:54 2014 +0200
+
+    Correction for PSIZE calculation:
+    regain and drop root privileges.
+
+M      photoproc.c
+
+commit 01be030273991089b6ded31d6a28855bf874d7be
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Sat May 24 14:10:55 2014 +0200
+
+    Introduction of proportional memory size (PSIZE) per process.
+    For the resident memory parts used by a process that are shared with
+    other processes, only a proportional part (shared memory part divided
+    by the number of sharers) is accounted to the process. Since the
+    gathering of this value is rather time-consuming (reading the
+    smaps file of every process), it is optional ('R' key or '-R' flag).
+
+M      atop.c
+M      atop.daily
+M      atop.h
+M      deviate.c
+M      man/atop.1
+M      parseable.c
+M      photoproc.c
+M      photoproc.h
+M      showgeneric.c
+M      showgeneric.h
+M      showlinux.c
+M      showlinux.h
+M      showprocs.c
+
+commit 899cdbd18447ce29f901d43531a1702416ab22ad
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Sat May 24 11:02:18 2014 +0200
+
+    Increased number of LVMs and disks supported.
+
+M      photosyst.h
+
+commit e2f7fb5bc45510b2ec14269e71a0b8ca7ae5a454
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri May 23 23:33:40 2014 +0200
+
+    Resize message shows new dimensions.
+
+M      showgeneric.c
+
+commit 0f54b4c1ebca739cda28ae5793475b06e90e0b88
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri May 23 15:39:46 2014 +0200
+
+    The guest percentage that is shown for CPUs, is not a separate
+    category that should be totalized with the other categories. In fact,
+    it overlaps with the user mode percentage. So if one of the virtual
+    machines executes a looping process, on the KVM hypervisor the
+    user percentage and guest percentage should both show 100% instead
+    of both 50%.
+
+M      atopsar.c
+M      deviate.c
+M      man/atop.1
+M      man/atopsar.1
+M      procdbase.c
+M      showlinux.c
+
+commit 3149aa8f4720e615148179ae8cc5146b8c3f5433
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri May 23 13:28:20 2014 +0200
+
+    Useless buffer formatting removed.
+
+M      atopsar.c
+
+commit e96c1fff1c7a7ca292f3aedaaa28b92d2e7b9629
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri May 23 13:19:21 2014 +0200
+
+    Removed double disk entry for 'vd...' disks.
+
+M      photosyst.c
+
+commit 239bd26a925d54d184248f00df35586d4d8d441d
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Sun Jan 26 15:42:31 2014 +0100
+
+    Solve segmentation vioalation with atopser (wrong number of
+    arguments passed to priline function).
+
+M      atopsar.c
+
+commit 3829e7382f348489f61f01b4e19fb4fb12bc36ef
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Sun Jan 26 15:37:12 2014 +0100
+
+    Solve segmentation violation when making summaries:
+    Extra parameter added to priline-function call.
+
+M      atopsar.c
+M      man/atop.1
+M      rawlog.c
+
+commit fbce79c76ddb6a38a2bc22b44a95edcf9cf78a36
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Sat Dec 7 12:17:07 2013 +0100
+
+    When process accounting is switched on and a child process
+    has been created and terminated, it still might occur that
+    no process accounting record has been written (kernel failure?).
+    This situation resulted in a Floating Point Exception signal.
+
+M      acctproc.c
+
+commit 2c7fe393518d861ff2fac3b38888389dd13c9b67
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Sat Oct 26 13:07:18 2013 +0200
+
+    Add a space between two columns, that were show without white space.
+
+M      parseable.c
+
+commit 948581cce702ffdf93d8e632c6ef12aa3ff6a31b
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Sat May 11 16:58:05 2013 +0200
+
+    Add new counter for memory that is claimed by the vmware balloon
+    driver (to be withdrawn from this virtual machine in favour of
+    other virtual machines).
+
+M      deviate.c
+M      man/atop.1
+M      parseable.c
+M      photosyst.c
+M      photosyst.h
+M      showlinux.c
+M      showlinux.h
+M      showsys.c
+
+commit 3a82bbeda1627a8a133f64f4434edaeefe413759
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Wed Apr 24 10:47:39 2013 +0200
+
+    Support for huge pages (total and in use).
+
+M      deviate.c
+M      man/atop.1
+M      photosyst.c
+M      photosyst.h
+M      showlinux.c
+M      showlinux.h
+M      showsys.c
+
+commit 22295853af2f10701f6da3243b75f55fc2eb5f6d
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Tue Apr 23 14:42:24 2013 +0200
+
+    Documentation improvements.
+
+M      man/atop.1
+M      showgeneric.c
+
+commit 05266573ea93a9866121909b4bd61856103e9a28
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Tue Apr 2 10:59:24 2013 +0200
+
+    Documentation improved concerning top-3 of processes (atopsar)
+    and the netatop module (atop).
+
+M      man/atop.1
+M      man/atopsar.1
+
+commit 9be21d695f218120e501d459ae1bdf112b4dbba5
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Thu Jan 10 13:54:08 2013 +0100
+
+    Cosmetic change: rename some variables to ease future maintenance.
+
+M      atop.c
+M      deviate.c
+M      photoproc.h
+
+commit ffd892d16dc60950a637db45bf2af692a3d2a91e
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Wed Dec 19 15:45:40 2012 +0100
+
+    Under certain circumstances, the number of exited processes might
+    be less than requested, so correct the counter nrexit.
+
+M      acctproc.c
+M      atop.c
+
+commit ba35522fbfb1c4fdb90d0dc68b0ac50e95cae73f
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Tue Dec 11 23:16:36 2012 +0100
+
+    Solution for the following bug:
+    When process accounting type 2 is used by the kernel (i.e.
+    no PID is the accounting record), atop crashes when thread
+    view is enabled.
+    Credits: Alex Samorukov
+
+M      showgeneric.c
+
+commit c34bb60f74c25c3da24ab485ff6d41b8435609b1
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Wed Nov 21 21:50:55 2012 +0100
+
+    Corrected value 'curproc' in -P report.
+
+M      atopsar.c
+
+commit 321e5288151db22e643569f8b71709d1f5da6bf4
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Mon Nov 19 09:26:32 2012 +0100
+
+    Maintain total number of bytes transferred for accumulated
+    per-user/per-program view (key 'u'/'p') to show proper
+    NET-percentage and get proper sorting order.
+
+M      showgeneric.c
+
+commit cc2824f244ae33c3628dc5bc8a96bf1e64088bb7
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri Nov 16 22:47:29 2012 +0100
+
+    Extended the number of positions for the RNET/SNET counters
+    from 4 to 5, and shrinked SYSCPU/USRCPU from 7 to 6.
+
+M      showprocs.c
+M      showsys.c
+M      various.c
+
+commit 57fb7e206e36e131093c7d0c4cbffe0f07d4bb20
+Merge: 55b51fe 8f5f6cc
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri Nov 16 21:42:32 2012 +0100
+
+    Merge branch '2.0.1'
+    
+    Conflicts:
+       mkdistr
+       mkversion
+       version.h
+
+commit 8f5f6ccc03b9b8ebaf8b2d65a18d3b56cb58c483
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri Nov 16 19:27:04 2012 +0100
+
+    Version update
+
+M      version.h
+
+commit 8c17df29c0d6dc82bec807cc2af8c50d69a2fbdb
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri Nov 16 19:23:20 2012 +0100
+
+    Release modification.
+
+M      version.h
+
+commit b5f5f9c05343c0b818b2afef291fd27d35aa690c
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Wed Nov 7 08:54:04 2012 +0100
+
+    Required modifications for distribution.
+
+M      atop.spec
+M      mkdistr
+M      mkversion
+M      version.h
+
+commit e4f27ff6ba3585b82155af29665c017e146e5c75
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Tue Nov 6 08:19:44 2012 +0100
+
+    Remark about netatop in README and solved rendering problem in atoprc.5
+
+M      README
+M      man/atoprc.5
+
+commit 41696c91407d65454769d8c0c94d65b410791963
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri Nov 2 11:14:52 2012 +0100
+
+    Sequence regainprivs-fopen-dropprivs-fread-... does not work for
+    files under /proc, so the /proc/PID/io files could not be accessed
+    when atop is not started by a root-user. Sequence has been changed
+    to regainprivs-fopen-fread-...-dropprivs.
+
+M      photoproc.c
+M      various.c
+
+commit 55b51feda3e2ffa450adb72203ecd9afc3474d30
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri Nov 2 11:08:40 2012 +0100
+
+    Sequence regainprivs-fopen-dropprivs-fread-... does not work for
+    files under /proc, so the /proc/PID/io files could not be accessed
+    when atop is not started by a root-user. Sequence has been changed
+    to regainprivs-fopen-fread-...-dropprivs.
+
+M      photoproc.c
+M      various.c
+
+commit 8e75554bb8f98269912ebb85a457df3fab35a54f
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Sat Oct 27 11:59:24 2012 +0200
+
+    Adapt to correct month.
+
+M      man/atop.1
+M      man/atoprc.5
+M      man/atopsar.1
+
+commit 1a256253c472ccca37ee1dd93ac7d6d1f3fd7e48
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Sat Oct 27 11:53:49 2012 +0200
+
+    Bug fixes.
+
+M      mkdistr
+M      mkversion
+M      version.h
+
+commit 6f61e0e5dbb3f48fd10d52b34eaac6ca49e19b31
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Thu Oct 25 17:22:39 2012 +0200
+
+    Added update-rc.d
+
+M      Makefile
+
+commit 643941fd3ff2055fcdcb979cdeeab62cef5771c8
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Wed Oct 24 13:12:21 2012 +0200
+
+    Dynamically switch per sample to show network stats or not. In this
+    way a running atop (e.g. the daily atop running in the background)
+    immediately reacts on loading or unloading of the netatop module.
+
+M      atop.c
+M      atop.h
+M      deviate.c
+M      man/atop.1
+M      netatopif.c
+M      photoproc.c
+M      rawlog.c
+M      showgeneric.c
+M      showgeneric.h
+M      showlinux.c
+
+commit f36eb4be1d7e26ed233bb101613df1a6363f8891
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Mon Oct 22 09:27:14 2012 +0200
+
+    Consider Shmem value in /proc/meminfo.
+
+M      man/atop.1
+M      showlinux.c
+
+commit fc06911932186159fd190f53379462a1a5b05147
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Mon Oct 22 09:22:42 2012 +0200
+
+    Add counter shmem as total shared memory including tmpfs.
+    Som miscellaneous cosmetic changes.
+
+M      acctproc.c
+M      deviate.c
+M      man/atop.1
+M      man/atoprc.5
+M      man/atopsar.1
+M      photosyst.c
+M      photosyst.h
+M      showlinux.c
+M      showlinux.h
+M      showsys.c
+
+commit 1e3c18350475135d11c300801647b5ea8aa6b204
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Sat Oct 20 17:06:33 2012 +0200
+
+    Modified formula to determine if memory is critically low:
+    the page cache counter implies the resident shared memory (and that
+    should not be subtracted).
+
+M      showlinux.c
+
+commit 58c0aae74341a337397859854dfcf826b21ca3ef
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Sat Oct 20 16:59:30 2012 +0200
+
+    Gather and display the value SReclaimable from /proc/meminfo
+    to give more adequate coloring of the MEM-line.
+
+M      deviate.c
+M      man/atop.1
+M      parseable.c
+M      photosyst.c
+M      photosyst.h
+M      showlinux.c
+M      showlinux.h
+M      showsys.c
+
+commit 213f3e1160046000858edd33c59952be802d5058
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Sun Oct 14 15:07:48 2012 +0200
+
+    Get rid of warnings from ignored return values (with (void))
+
+M      acctproc.c
+M      atop.c
+M      atop.h
+M      atopsar.c
+M      photoproc.c
+M      photosyst.c
+M      rawlog.c
+M      various.c
+
+commit a7a17cfc03f6e4b82840bce4d61bbcdd20db52e0
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Sun Oct 14 13:29:05 2012 +0200
+
+    Cosmetic change.
+
+M      mkdistr
+
+commit f683dfb6c76ff6df2d68031e19d37532843509f9
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Sun Oct 14 13:23:01 2012 +0200
+
+    Various modifications to cooperate properly with the netatop kernel
+    module and the netatopd daemon.
+    Update of man-page.
+    Simplified the generation of the version number.
+
+M      Makefile
+A      atopsar
+M      deviate.c
+M      man/atop.1
+M      mkversion
+M      netatopif.c
+M      photosyst.c
+M      showlinux.c
+D      version.SKEL
+A      version.h
+
+commit 6807de7c036f1f375b7f80a33525b6eff02b03f8
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Sat Sep 15 12:15:31 2012 +0200
+
+    Boot time is determined now with a high resolution clock to be able
+    to determine the start time of a process more precise (needed to make
+    a better match with process accounting/netatop).
+
+M      Makefile
+M      atop.c
+M      atop.h
+M      deviate.c
+M      photoproc.c
+M      photosyst.c
+M      procdbase.c
+M      various.c
+
+commit 6526ea3da42807c7c94a055c5d90252899f8b059
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri Sep 14 23:18:06 2012 +0200
+
+    Further cooperation implemented between atop and netatop/netatopd.
+
+M      Makefile
+M      acctproc.c
+M      atop.c
+M      atop.h
+D      atopnetif.c
+M      atopsar.c
+M      deviate.c
+A      netatopd.h
+A      netatopif.c
+M      parseable.c
+M      photoproc.c
+M      photoproc.h
+M      showgeneric.c
+M      showlinux.c
+M      showprocs.c
+M      various.c
+
+commit cc8cd6572d7541dfc82d0df09690b5b93bcdcf25
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri Sep 14 23:06:54 2012 +0200
+
+    When pid is in the process accounting record, no further check on
+    the btime (begin-time) is required.
+
+M      procdbase.c
+
+commit 129895d44071290750f13ce08df7cf80f534136a
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Wed Aug 29 09:03:46 2012 +0200
+
+    Remove source code related to the kernel patches.
+    Insert new code to support the kernel module 'netatop'.
+    Add shared memory figures to MEM line.
+
+M      Makefile
+M      atop.c
+D      atopnet.h
+M      atopnetif.c
+M      atopsar.c
+M      deviate.c
+M      man/atop.1
+A      netatop.h
+M      parseable.c
+M      photoproc.c
+M      photosyst.c
+M      photosyst.h
+M      showgeneric.c
+M      showlinux.c
+M      showlinux.h
+M      showprocs.c
+M      showsys.c
+
+commit 73bffb6f37e02d5272fbfeb64ada43773fd585e6
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Wed Aug 8 16:53:07 2012 +0200
+
+    WRDSK is normalized with WCANCEL to determine how much real disk transfer
+    is done per process.
+
+M      showgeneric.c
+M      showlinux.c
+M      showlinux.h
+M      showprocs.c
+
+commit 361f1d2c877d6316b6339239d81c94aadde62617
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Wed Aug 8 16:01:12 2012 +0200
+
+    Removed unused counters for raw network sends/receives.
+
+M      acctproc.c
+M      atopsar.c
+M      deviate.c
+M      parseable.c
+M      photoproc.c
+M      photoproc.h
+M      showgeneric.c
+M      showlinux.c
+M      showlinux.h
+M      showprocs.c
+
+commit e11e259fd35c7be4fcab96d48a818ca222b7fa51
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Wed Aug 8 15:49:20 2012 +0200
+
+    Remove code for kernel patches and introduce kernel module 'atopnet'.
+
+M      Makefile
+M      acctproc.c
+M      atop.c
+M      atop.h
+A      atopnet.h
+A      atopnetif.c
+M      atopsar.c
+M      deviate.c
+M      parseable.c
+M      photoproc.c
+M      showgeneric.c
+M      showlinux.c
+M      showlinux.h
+M      showprocs.c
+
+commit 8c9de7a781247b2591d00fc9e5c9a4f3674b0491
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Mon Jul 23 11:12:33 2012 +0200
+
+    Removed unused variable 'columnprefix'.
+
+M      Makefile
+M      showlinux.c
+
+commit eb5da90ccb0246ba9040c158c82589c9e5fffe39
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Mon Jul 23 11:04:45 2012 +0200
+
+    Minor corrections.
+
+M      Makefile
+M      mkdistr
+
+commit 9c0c2287ed60619dc2a688b2e4242c7ded582026
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Sun Jul 22 11:30:51 2012 +0200
+
+    Improved RPM handling.
+
+M      Makefile
+M      mkdistr
+
+commit b1bb45f1d9a61fcd81cebf417cccadac68ddde67
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri Jul 20 15:19:35 2012 +0200
+
+    Add service file for systemd without installing.
+
+A      atop.service
+M      mkdistr
+
+commit 409be5867b31d37f138447110d234b2325902cdf
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri Jul 20 15:18:10 2012 +0200
+
+    Corrected typo in help messages.
+
+M      showgeneric.c
+
+commit 4ea9f1ab0c500d47ae74544f90353c6190b8e317
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri Jul 20 12:09:17 2012 +0200
+
+    Modified description.
+
+M      atop.spec
+M      man/atop.1
+M      man/atopsar.1
+
+commit a98a0d1ad85aaa082fda535cbb9844a0a4591034
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri Jul 20 12:02:17 2012 +0200
+
+    ChangeLog removed (not needed any more when using git).
+
+D      ChangeLog
+
+commit af0f1fad5438374912c167f284653b87c62a3142
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri Jul 20 12:01:06 2012 +0200
+
+    Added LSB header.
+
+M      atop.init
+
+commit d5a9b854674ae451636815e539c4c056365708b2
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri Jul 20 10:12:31 2012 +0200
+
+    Clarified comments about the use of coloring.
+
+M      showsys.c
+
+commit 1b38444596097776f88415091d27efee4f2dee9f
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri Jul 20 10:01:38 2012 +0200
+
+    Solved division-by-zero when maxfreq=0 and
+    more precise coloring of concerning CPU-values when CPU overloaded.
+
+M      showsys.c
+
+commit 826caafb1aaf892e0692d4315a324093ece2da32
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri Jul 20 10:00:53 2012 +0200
+
+    Correct typo's in description of frequency scaling.
+
+M      man/atop.1
+
+commit f266aafb596880c8fe8bcf6f4ae662889803b7c3
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Thu Jul 19 08:24:33 2012 +0200
+
+    If setuid-root is set for atop, root-privileges are regained when reading
+    /proc/pid/io (nowadays only readable for root) to obtain the disk stats
+    per process.
+
+M      photoproc.c
+
+commit 9e77a3bccfa345eb8a4a7b1a417343f80a5ae792
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Thu Jul 19 07:51:43 2012 +0200
+
+    Email address updated in README and ChangeLog added to package.
+
+M      README
+M      mkdistr
+
+commit 1ee760110035966d444f057cdaf2f0784244b4c8
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Wed Jul 18 11:37:29 2012 +0200
+
+    Added EMC Power device recognition.
+
+M      photosyst.c
+
+commit 4aa9c986f8bb469ad38e46db46d7bc1766f4cea0
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Wed Jul 18 09:25:42 2012 +0200
+
+    Better recognition of version of process accounting file.
+
+M      acctproc.c
+
+commit 9c8dcc365631a38df69ddc98a94b28ca71666679
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Wed Jul 18 09:21:36 2012 +0200
+
+    Improved error handling when not enough columns or lines.
+
+M      man/atop.1
+M      man/atopsar.1
+M      showgeneric.c
+
+commit ab6f072b5b85df575c40fd68583052833c09bd48
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Tue Jul 17 14:29:08 2012 +0200
+
+    Suppress sorting of system resources by key F or flag -F (toggle).
+
+M      man/atop.1
+M      man/atoprc.5
+M      showgeneric.c
+M      showgeneric.h
+
+commit 3b0922c3c371ffecab00f6df72bc8b8852ae8e15
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Tue Jul 17 13:29:45 2012 +0200
+
+    Give clearer error message if the format of the rawlog is incompatible.
+
+M      rawlog.c
+
+commit 7c17a309ef97a59a8ee1f5a593f48eeac9d46bc8
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Tue Jul 17 12:57:25 2012 +0200
+
+    Create unique /tmp name for decompressed raw file.
+
+M      man/atop.1
+M      rawlog.c
+
+commit 0f693c1f651c02ecbfbedf49bafdc879c2479184
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Tue Jul 17 11:28:20 2012 +0200
+
+    Numerous cosmetic changes and man-page updates.
+
+M      acctproc.c
+M      deviate.c
+M      man/atop.1
+M      man/atoprc.5
+M      man/atopsar.1
+M      photosyst.c
+M      photosyst.h
+M      showlinux.c
+M      showlinux.h
+M      showprocs.c
+M      showsys.c
+
+commit 2a637eaa2ba734c1ae2cdc2d6caab8bc66bf7782
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Fri Jun 15 10:41:45 2012 +0200
+
+    Improved screen handling.
+    Limited maximum file-size for process accounting file (200 MiB).
+
+M      acctproc.h
+M      showgeneric.c
+
+commit 86e9448c2cd8e0f3bc1b138dfc98b6701f0afa2c
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Thu Jun 14 17:09:15 2012 +0200
+
+    Improved handling of process accounting:
+    - Per sample not more than 50 MiB are allowed for process accounting records,
+      which means about 72000 processes. This avoids atop to explode in memory
+      when a huge amount of processes died within one sample.
+    - When only one atop-process is busy and it detects that the process accounting
+      file exceeds .5 GiB, it will truncate the accounting file to zero.
+
+M      acctproc.c
+M      acctproc.h
+M      atop.c
+M      atop.h
+M      atopsar.c
+M      deviate.c
+M      mkdistr
+M      parseable.c
+M      parseable.h
+M      photoproc.h
+M      rawlog.c
+M      showgeneric.c
+M      showgeneric.h
+M      showlinux.c
+M      showlinux.h
+M      showsys.c
+
+commit 0178cdeba8f654816c082118c046978c94869343
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Wed Jun 13 15:43:55 2012 +0200
+
+    Cosmetical changes.
+
+M      atop.c
+M      showsys.c
+
+commit 428524129548ede14d535a8c2c23ff5e0af2d54c
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Wed Jun 13 13:12:39 2012 +0200
+
+    New key 'S' to make selections of system resources like logical volumes,
+    disks and network interfaces (regular expression).
+
+M      man/atop.1
+M      showgeneric.c
+M      showgeneric.h
+M      showlinux.c
+
+commit 3fff5cfc8e01fceb1e69ff067e73a5d86ae12c48
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Wed Jun 13 12:16:54 2012 +0200
+
+    Use of arrow-keys and PgUp/PgDown for vertical scroll.
+    Use of -> and <- keys for horizontal scroll of command line.
+    Increment of maximum number of system resources (e.g. MAXCPU).
+
+M      photoproc.h
+M      photosyst.h
+M      showgeneric.c
+M      showprocs.c
+
+commit 7671b76506743c0a9d82f77674788a045ea0a427
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Sat Jun 9 12:28:58 2012 +0200
+
+    Improved generation of new version.
+
+A      atop.spec
+M      mkdistr
+
+commit 2c87df01b06c588ce205c1dcdf8bd30889e27b1b
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Sat Jun 9 11:07:03 2012 +0200
+
+    Support statistics for virtio disks (vd*).
+
+M      photosyst.c
+
+commit c0c9238f3667d4916cad4eeb16baa861de7e4527
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Sat Jun 9 11:04:22 2012 +0200
+
+    Uniform handling of failing mallocs.
+
+M      atop.c
+M      atop.h
+M      atopsar.c
+M      deviate.c
+M      mkdistr
+M      photoproc.h
+M      procdbase.c
+M      rawlog.c
+M      showgeneric.c
+M      various.c
+
+commit 0682920a7b41e2007dac1470996d945e55b0fc21
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Tue Jun 5 15:44:04 2012 +0200
+
+    Fetching and displaying of individual threads.
+
+M      acctproc.c
+M      acctproc.h
+M      atop.c
+M      atop.h
+M      atopsar.c
+M      deviate.c
+M      man/atop.1
+M      parseable.c
+M      parseable.h
+M      photoproc.c
+M      photoproc.h
+M      procdbase.c
+M      rawlog.c
+M      showgeneric.c
+M      showgeneric.h
+M      showlinux.c
+M      showlinux.h
+M      showprocs.c
+M      showsys.c
+
+commit 10f54f1caa904b98f270a92d5fdcafe259d88773
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Mon May 28 11:23:43 2012 +0200
+
+    Increase maximum number of disks, lvm's, interfaces and CPUs.
+
+M      photosyst.c
+M      photosyst.h
+
+commit 27fe64e140a62220a0e7fee04431be4756c0047d
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Mon May 28 11:22:49 2012 +0200
+
+    Show message instead of #exits when process accounting could not
+    be switched on.
+
+M      showsys.c
+
+commit 90b7a438c226fd0ce4e8f8680ece4b67ea5a87a8
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Mon May 28 11:21:53 2012 +0200
+
+    Proper handling of lock added.
+
+M      atop.init
+
+commit 30165361059b75acbae534b21eee2465727f05f7
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Mon Mar 5 09:06:57 2012 +0100
+
+    Add various details about the memory usage of processes, like
+    stack size, data size, shared library size and size used on swap.
+
+M      acctproc.c
+M      deviate.c
+M      man/atop.1
+M      parseable.c
+M      photoproc.c
+M      photoproc.h
+M      photosyst.h
+M      showgeneric.c
+M      showlinux.c
+M      showlinux.h
+M      showprocs.c
+
+commit 5c65b96129124fcdd9105d90d11bc46a46b0ec9a
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Tue Sep 13 10:20:22 2011 +0200
+
+    Show new value about swap space usage per process.
+
+M      acctproc.c
+M      deviate.c
+M      man/atop.1
+M      parseable.c
+M      photoproc.c
+M      photoproc.h
+M      showgeneric.c
+M      showlinux.c
+M      showlinux.h
+M      showprocs.c
+
+commit 25ffd8313a499b480f38e493eb50bf4c5a8bec1b
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Tue Sep 13 07:52:15 2011 +0200
+
+    Color vmlim and vmcom adapted.
+
+M      showsys.c
+
+commit eff004b034980224bff10baf14eee598fdd5f470
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Wed Sep 7 15:34:12 2011 +0200
+
+    Colors are shown now for separate system-level counters instead
+    of for the entire line with counters.
+
+M      showgeneric.c
+M      showgeneric.h
+M      showlinux.c
+M      showlinux.h
+M      showsys.c
+
+commit 2358e3fe0445973c78226d25aa13da02f1dd61d9
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Tue Sep 6 09:54:42 2011 +0200
+
+    Git-based determination of the version of atop.
+
+A      mkdistr
+A      mkversion
+A      version.SKEL
+D      version.c
+
+commit 87852e1b1b2d87b26c222fe7abec024570465a48
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Tue Sep 6 08:59:36 2011 +0200
+
+    Enhanced security: improved dropping of root privileges.
+
+M      acctproc.c
+M      atop.c
+M      atop.h
+M      atopsar.c
+M      deviate.c
+M      rawlog.c
+M      various.c
+
+commit 1f0c1bb1b9ad52abfc62147465ccf61e25f5a7f8
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Mon Sep 5 15:59:44 2011 +0200
+
+    Avoid error messages during startup of atop about not being able to
+    activate process accounting (e.g. when starting without root privs).
+    The reason of the error will now be displayed instead of the '#exit' counter.
+
+M      acctproc.c
+M      atop.c
+M      atop.h
+M      man/atop.1
+M      showsys.c
+
+commit fb9ed3f5079344a93cdc29bd415710ef753fcbf8
+Author: Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
+Date:   Mon Sep 5 14:04:31 2011 +0200
+
+    Initial version
+
+A      45atoppm
+A      AUTHOR
+A      COPYING
+A      ChangeLog
+A      Makefile
+A      README
+A      acctproc.c
+A      acctproc.h
+A      atop.c
+A      atop.cron
+A      atop.daily
+A      atop.h
+A      atop.init
+A      atopsar.c
+A      deviate.c
+A      ifprop.c
+A      ifprop.h
+A      man/atop.1
+A      man/atoprc.5
+A      man/atopsar.1
+A      netstats.h
+A      parseable.c
+A      parseable.h
+A      photoproc.c
+A      photoproc.h
+A      photosyst.c
+A      photosyst.h
+A      procdbase.c
+A      psaccs_atop
+A      psaccu_atop
+A      rawlog.c
+A      showgeneric.c
+A      showgeneric.h
+A      showlinux.c
+A      showlinux.h
+A      showprocs.c
+A      showsys.c
+A      various.c
+A      version.c
diff --git a/sources/Makefile b/sources/Makefile
new file mode 100644 (file)
index 0000000..24cb089
--- /dev/null
@@ -0,0 +1,181 @@
+# Makefile for System & Process Monitor ATOP (Linux version)
+#
+# Gerlof Langeveld - gerlof.langeveld@atoptool.nl
+#
+DESTDIR  =
+
+BINPATH  = /usr/bin
+SBINPATH = /usr/sbin
+SCRPATH  = /usr/share/atop
+LOGPATH  = /var/log/atop
+MAN1PATH = /usr/share/man/man1
+MAN5PATH = /usr/share/man/man5
+MAN8PATH = /usr/share/man/man8
+INIPATH  = /etc/init.d
+SYSDPATH = /usr/lib/systemd/system
+CRNPATH  = /etc/cron.d
+ROTPATH  = /etc/logrotate.d
+PMPATH1  = /usr/lib/pm-utils/sleep.d
+PMPATH2  = /usr/lib64/pm-utils/sleep.d
+PMPATHD  = /usr/lib/systemd/system-sleep
+
+CFLAGS  += -O2 -I. -Wall        # -DHTTPSTATS
+OBJMOD0  = version.o
+OBJMOD1  = various.o  deviate.o   procdbase.o
+OBJMOD2  = acctproc.o photoproc.o photosyst.o  rawlog.o ifprop.o parseable.o
+OBJMOD3  = showgeneric.o          showlinux.o  showsys.o showprocs.o
+OBJMOD4  = atopsar.o  netatopif.o
+ALLMODS  = $(OBJMOD0) $(OBJMOD1) $(OBJMOD2) $(OBJMOD3) $(OBJMOD4)
+
+VERS     = $(shell ./atop -V 2>/dev/null| sed -e 's/^[^ ]* //' -e 's/ .*//')
+
+all:           atop atopsar atopacctd
+
+atop:          atop.o    $(ALLMODS) Makefile
+               $(CC) atop.o $(ALLMODS) -o atop -lncurses -lz -lm -lrt $(LDFLAGS)
+
+atopsar:       atop
+               ln -sf atop atopsar
+
+atopacctd:     atopacctd.o netlink.o
+               $(CC) atopacctd.o netlink.o -o atopacctd $(LDFLAGS)
+
+netlink.o:     netlink.c
+               $(CC) -I. -Wall -c netlink.c
+
+clean:
+               rm -f *.o
+               rm atop atopsar atopacctd
+
+distr:
+               rm -f *.o atop
+               tar czvf /tmp/atop.tar.gz *
+
+
+install:
+               @echo Choose either \'make systemdinstall\' or \'make sysvinstall\'
+
+systemdinstall:        genericinstall
+               if [ ! -d $(DESTDIR)$(SYSDPATH) ];                      \
+               then    mkdir -p $(DESTDIR)$(SYSDPATH); fi
+               if [ ! -d $(DESTDIR)$(PMPATHD) ];                       \
+               then    mkdir -p $(DESTDIR)$(PMPATHD); fi
+               #
+               cp atop.service       $(DESTDIR)$(SYSDPATH)
+               chmod 0644            $(DESTDIR)$(SYSDPATH)/atop.service
+               cp atopacct.service   $(DESTDIR)$(SYSDPATH)
+               chmod 0644            $(DESTDIR)$(SYSDPATH)/atopacct.service
+               cp atop.cronsystemd   $(DESTDIR)$(CRNPATH)/atop
+               cp atop-pm.sh         $(DESTDIR)$(PMPATHD)
+               chmod 0711            $(DESTDIR)$(PMPATHD)/atop-pm.sh
+               #
+               # only when making on target system:
+               #
+               if [ -z "$(DESTDIR)" -a -f /bin/systemctl ];            \
+               then    /bin/systemctl stop    atop     2> /dev/null;   \
+                       /bin/systemctl disable atop     2> /dev/null;   \
+                       /bin/systemctl stop    atopacct 2> /dev/null;   \
+                       /bin/systemctl disable atopacct 2> /dev/null;   \
+                       /bin/systemctl enable  atopacct;                \
+                       /bin/systemctl start   atopacct;                \
+                       /bin/systemctl enable  atop;                    \
+                       /bin/systemctl start   atop;                    \
+               fi
+
+sysvinstall:   genericinstall
+               if [ ! -d $(DESTDIR)$(INIPATH) ];                       \
+               then    mkdir -p  $(DESTDIR)$(INIPATH); fi
+               #
+               cp atop.init      $(DESTDIR)$(INIPATH)/atop
+               cp atopacct.init  $(DESTDIR)$(INIPATH)/atopacct
+               cp atop.cronsysv  $(DESTDIR)$(CRNPATH)/atop
+               #
+               if [   -d $(DESTDIR)$(PMPATH1) ];                       \
+               then    cp 45atoppm $(DESTDIR)$(PMPATH1);               \
+                       chmod 0711  $(DESTDIR)$(PMPATH1)/45atoppm;      \
+               fi
+               if [ -d $(DESTDIR)$(PMPATH2) ];                         \
+               then    cp 45atoppm $(DESTDIR)$(PMPATH2);               \
+                       chmod 0711  $(DESTDIR)$(PMPATH2)/45atoppm;      \
+               fi
+               #
+               #
+               # only when making on target system:
+               #
+               if [ -z "$(DESTDIR)" -a -f /sbin/chkconfig ];           \
+               then    /sbin/chkconfig --del atop     2> /dev/null;    \
+                       /sbin/chkconfig --add atop;                     \
+                       /sbin/chkconfig --del atopacct 2> /dev/null;    \
+                       /sbin/chkconfig --add atopacct;                 \
+               fi
+               if [ -z "$(DESTDIR)" -a -f /usr/sbin/update-rc.d ];     \
+               then    update-rc.d atop defaults;                      \
+                       update-rc.d atopacct defaults;                  \
+               fi
+               if [ -z "$(DESTDIR)" -a -f /sbin/service ];             \
+               then    /sbin/service atopacct start;                   \
+                       sleep 2;                                        \
+                       /sbin/service atop     start;                   \
+               fi
+
+genericinstall:        atop atopacctd
+               if [ ! -d $(DESTDIR)$(LOGPATH) ];               \
+               then    mkdir -p $(DESTDIR)$(LOGPATH); fi
+               if [ ! -d $(DESTDIR)$(BINPATH) ];               \
+               then    mkdir -p $(DESTDIR)$(BINPATH); fi
+               if [ ! -d $(DESTDIR)$(SBINPATH) ];              \
+               then mkdir -p $(DESTDIR)$(SBINPATH); fi
+               if [ ! -d $(DESTDIR)$(SCRPATH) ];               \
+               then    mkdir -p $(DESTDIR)$(SCRPATH); fi       
+               if [ ! -d $(DESTDIR)$(MAN1PATH) ];              \
+               then    mkdir -p $(DESTDIR)$(MAN1PATH); fi
+               if [ ! -d $(DESTDIR)$(MAN5PATH) ];              \
+               then    mkdir -p $(DESTDIR)$(MAN5PATH); fi
+               if [ ! -d $(DESTDIR)$(MAN8PATH) ];              \
+               then    mkdir -p $(DESTDIR)$(MAN8PATH); fi
+               if [ ! -d $(DESTDIR)$(CRNPATH) ];               \
+               then    mkdir -p $(DESTDIR)$(CRNPATH);  fi
+               if [ ! -d $(DESTDIR)$(ROTPATH) ];               \
+               then    mkdir -p $(DESTDIR)$(ROTPATH);  fi
+               #
+               cp atop                 $(DESTDIR)$(BINPATH)/atop
+               chown root              $(DESTDIR)$(BINPATH)/atop
+               chmod 04711             $(DESTDIR)$(BINPATH)/atop
+               ln -sf atop             $(DESTDIR)$(BINPATH)/atopsar
+               cp atopacctd            $(DESTDIR)$(SBINPATH)/atopacctd
+               chown root              $(DESTDIR)$(SBINPATH)/atopacctd
+               chmod 0700              $(DESTDIR)$(SBINPATH)/atopacctd
+               cp atop                 $(DESTDIR)$(BINPATH)/atop-$(VERS)
+               ln -sf atop-$(VERS)     $(DESTDIR)$(BINPATH)/atopsar-$(VERS)
+               cp atop.daily           $(DESTDIR)$(SCRPATH)
+               chmod 0711              $(DESTDIR)$(SCRPATH)/atop.daily
+               cp man/atop.1           $(DESTDIR)$(MAN1PATH)
+               cp man/atopsar.1        $(DESTDIR)$(MAN1PATH)
+               cp man/atoprc.5         $(DESTDIR)$(MAN5PATH)
+               cp man/atopacctd.8      $(DESTDIR)$(MAN8PATH)
+               cp psaccs_atop          $(DESTDIR)$(ROTPATH)/psaccs_atop
+               cp psaccu_atop          $(DESTDIR)$(ROTPATH)/psaccu_atop
+               touch                   $(DESTDIR)$(LOGPATH)/dummy_before
+               touch                   $(DESTDIR)$(LOGPATH)/dummy_after
+
+##########################################################################
+
+atop.o:                atop.h  photoproc.h photosyst.h  acctproc.h showgeneric.h
+atopsar.o:     atop.h  photoproc.h photosyst.h                           
+rawlog.o:      atop.h  photoproc.h photosyst.h             showgeneric.h
+various.o:     atop.h                           acctproc.h
+ifprop.o:      atop.h              photosyst.h             ifprop.h
+parseable.o:   atop.h  photoproc.h photosyst.h             parseable.h
+deviate.o:     atop.h  photoproc.h photosyst.h
+procdbase.o:   atop.h  photoproc.h
+acctproc.o:    atop.h  photoproc.h atopacctd.h  acctproc.h netatop.h
+netatopif.o:   atop.h  photoproc.h              netatopd.h netatop.h
+photoproc.o:   atop.h  photoproc.h
+photosyst.o:   atop.h              photosyst.h
+showgeneric.o: atop.h  photoproc.h photosyst.h  showgeneric.h showlinux.h
+showlinux.o:   atop.h  photoproc.h photosyst.h  showgeneric.h showlinux.h
+showsys.o:     atop.h  photoproc.h photosyst.h  showgeneric.h 
+showprocs.o:   atop.h  photoproc.h photosyst.h  showgeneric.h showlinux.h
+version.o:     version.h
+
+atopacctd.o:   atop.h  photoproc.h acctproc.h   version.h     atopacctd.h
diff --git a/sources/README b/sources/README
new file mode 100644 (file)
index 0000000..58f689f
--- /dev/null
@@ -0,0 +1,59 @@
+INSTALLING AND USING ATOP
+
+For interactive use, it is sufficient to install ATOP with the command
+(as root):
+
+       make systemdinstall
+
+               or
+
+       make sysvinstall
+
+For automatic logging in compressed binary format, see the
+description in the manual-page.
+
+The kernel module 'netatop' can be downloaded and installed separately
+from www.atoptool.nl/downloadnetatop.php
+This module is optional and can be used to gather network statistics
+per process/thread as described in www.atoptool.nl/netatop.php
+
+
+KERNEL ISSUES WITH PROCESS ACCOUNTING
+-------------------------------------
+
+Newer upstream kernels (e.g. 4.8 and 4.9)have two issuess
+with process accounting:
+
+1) Sometimes process accounting does not work at all¹. Atopacctd tries to
+work around this issue, by retrying to initialize process accounting
+several times.
+
+2) When using the NETLINK inface, the command TASKSTATS_CMD_GET
+consequently returns -EINVAL². Atopacctd needs NETLINK to be able to
+be triggered that some process in the system has finished. In this way,
+atopacctd can be in a blocking state as long as no processes terminate.
+When atopacctd detects that NETLINK fails, it switches into a polling
+mode to periodically try if it can read process accounting records as
+a workaround. This issue has to do with cpumasks and you can work-around
+it by building a kernel that has CONFIG_NR_CPUS configured to exactly
+the amount of CPUs (logical CPUs) in the system the kernel runs on. You
+can find this kernel option under "Processor type and features" -->
+"Maximum number of CPUs".
+
+
+[1] Bug 190271 - process accounting sometimes does not work
+https://bugzilla.kernel.org/show_bug.cgi?id=190271
+
+[2] Bug 190711 - Process accounting: Using the NETLINK interface,
+the command TASKSTATS_CMD_GET returns -EINVAL
+https://bugzilla.kernel.org/show_bug.cgi?id=190711
+
+Linux kernel mailing list thread:
+
+[REGRESSION] Two issues that prevent process accounting (taskstats) from
+working correctly
+https://lkml.org/lkml/2016/12/19/182
+
+
+Gerlof Langeveld
+gerlof.langeveld@atoptool.nl
diff --git a/sources/acctproc.c b/sources/acctproc.c
new file mode 100644 (file)
index 0000000..e7ae34d
--- /dev/null
@@ -0,0 +1,1104 @@
+/*
+** ATOP - System & Process Monitor
+**
+** The program 'atop' offers the possibility to view the activity of
+** the system on system-level as well as process-level.
+**
+** This source-file contains functions to manipulate with process-accounting
+** features (switch it on/off and read the process-accounting records).
+** ================================================================
+** Author:      Gerlof Langeveld
+** E-mail:      gerlof.langeveld@atoptool.nl
+** Date:        November 1996
+** LINUX-port:  June 2000
+**
+** This program is free software; you can redistribute it and/or modify it
+** under the terms of the GNU General Public License as published by the
+** Free Software Foundation; either version 2, or (at your option) any
+** later version.
+**
+** This program is distributed in the hope that it will be useful, but
+** WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+** See the GNU General Public License for more details.
+**
+** $Log: acctproc.c,v $
+** Revision 1.28  2010/04/23 12:20:19  gerlof
+** Modified mail-address in header.
+**
+** Revision 1.27  2009/12/12 10:12:01  gerlof
+** Register and display end date and end time for process.
+**
+** Revision 1.26  2008/03/06 08:37:25  gerlof
+** Register/show ppid of a process.
+**
+** Revision 1.25  2008/01/14 09:22:23  gerlof
+** Support for environment variable ATOPACCT to specify the name of a
+** particular process accouting file.
+**
+** Revision 1.24  2007/08/17 08:50:26  gerlof
+** Verify if private accounting used before switching off accounting.
+**
+** Revision 1.23  2007/03/20 13:01:51  gerlof
+** Introduction of variable supportflags.
+**
+** Revision 1.22  2007/02/13 09:14:49  gerlof
+** New boolean introduced to indicate if accounting is active.
+**
+** Revision 1.21  2006/02/07 06:46:15  gerlof
+** Removed swap-counter.
+**
+** Revision 1.20  2005/11/04 14:17:10  gerlof
+** Improved recognition of certain version process accounting file.
+**
+** Revision 1.19  2005/10/31 12:44:58  gerlof
+** Support account-record version 3 (used by Mandriva).
+**
+** Revision 1.18  2005/10/31 08:55:05  root
+** *** empty log message ***
+**
+** Revision 1.17  2004/12/14 15:05:00  gerlof
+** Implementation of patch-recognition for disk and network-statistics.
+**
+** Revision 1.16  2004/06/01 11:57:22  gerlof
+** Consider other standard accounting-files, i.e. /var/account/pacct.
+**
+** Revision 1.15  2004/05/06 09:48:21  gerlof
+** Ported to kernel-version 2.6.
+**
+** Revision 1.14  2003/07/07 09:26:15  gerlof
+** Cleanup code (-Wall proof).
+**
+** Revision 1.13  2003/07/02 06:43:11  gerlof
+** Modified include-file sys/acct.h to linux/acct.h to make it
+** work on Alpha-based systems as well.
+**
+** Revision 1.12  2003/06/27 12:31:24  gerlof
+** Adapt long to long long.
+**
+** Revision 1.11  2003/04/03 08:32:58  gerlof
+** Cosmetic changes.
+**
+** Revision 1.10  2003/01/14 09:01:45  gerlof
+** Small cosmetic changes.
+**
+** Revision 1.9  2002/10/03 11:12:03  gerlof
+** Modify (effective) uid/gid to real uid/gid.
+**
+** Revision 1.8  2002/07/24 11:11:12  gerlof
+** Redesigned to ease porting to other UNIX-platforms.
+**
+** Revision 1.7  2002/07/11 07:28:29  root
+** Some additions related to secure accounting file handling
+**
+** Revision 1.6  2002/07/08 09:14:54  root
+** Modified secure handling of accounting file
+** (inspired by Tobias Rittweiler).
+**
+** Revision 1.5  2001/11/07 09:16:55  gerlof
+** Allow users to run atop without process-accounting switched on.
+**
+** Revision 1.4  2001/10/16 06:15:52  gerlof
+** Partly redesigned.
+**
+** Revision 1.3  2001/10/04 13:02:24  gerlof
+** Redesign of the way to determine how many atop's are using process-accounting
+** on basis of semaphores.
+**
+** Revision 1.2  2001/10/04 08:46:46  gerlof
+** Improved verification of kernel-symbol addresses
+**
+** Revision 1.1  2001/10/02 10:38:35  gerlof
+** Initial revision
+**
+*/
+
+static const char rcsid[] = "$Id: acctproc.c,v 1.28 2010/04/23 12:20:19 gerlof Exp $";
+
+#define        _FILE_OFFSET_BITS       64
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#include <sys/wait.h>
+
+#include "atop.h"
+#include "photoproc.h"
+#include "acctproc.h"
+#include "atopacctd.h"
+
+#define        ACCTDIR         "/var/cache/atop.d"
+#define        ACCTFILE        "atop.acct"
+#define        ACCTENV         "ATOPACCT"
+
+static char    acctatop;       /* boolean: atop's own accounting busy ?  */
+static off_t   acctsize;       /* previous size of account file          */
+static int     acctrecsz;      /* size of account record                 */
+static int     acctversion;    /* version of account record layout       */
+static int     acctfd = -1;    /* fd of account file (-1 = not open)     */
+
+static long    curshadowseq;   /* current shadow file sequence number    */
+static long    maxshadowrec;   /* number of records per shadow file      */
+
+static char    *pacctdir = PACCTDIR;
+
+static count_t         acctexp (comp_t  ct);
+static int     acctvers(int);
+static void    acctrestarttrial();
+static void    switchshadow(void);
+
+struct pacctadm {
+       char            *name;
+       struct stat     stat;
+} pacctadm[] = {
+       { "/var/log/pacct",             {0, }, },
+       { "/var/account/pacct",         {0, }, }
+};
+
+/*
+** Semaphore-handling
+**
+**  A semaphore-group with two semaphores is created
+**
+**     0 - Binary semaphore (mutex) to get access to active atop-counter
+**
+**     1 - Active atop-counter (inverted).
+**         This semaphore is initialized at some high value and is
+**         decremented by every atop-incarnation which uses the private
+**         accounting-file and incremented as soon as such atop stops again.
+**         If an atop-incarnation stops and it appears to be the last one
+**         using the private accounting-file, accounting is stopped
+**         and the file removed
+**        (Yes, I know: it's not proper usage of the semaphore-principle).
+*/ 
+#define        ATOPACCTKEY     3121959
+#define        ATOPACCTTOT     100
+
+struct sembuf  semclaim = {0, -1, SEM_UNDO},
+               semrelse = {0, +1, SEM_UNDO},
+               semdecre = {1, -1, SEM_UNDO},
+               semincre = {1, +1, SEM_UNDO};
+
+/*
+** switch on the process-accounting mechanism
+**
+** return value:
+**    0 -     activated (success)
+**    1 - not activated: unreadable accounting file
+**    2 - not activated: empty environment variable ATOPACCT
+**    3 - not activated: no access to semaphore group
+**    4 - not activated: impossible to create own accounting file
+**    5 - not activated: no root privileges
+*/
+int
+acctswon(void)
+{
+       int                     i, j, sematopid, sempacctpubid;
+       static ushort           vals[] = {1, ATOPACCTTOT};
+       union {ushort *array;}  arg = {vals};
+       struct stat             statbuf;
+       char                    *ep;
+
+       /*
+       ** when a particular environment variable is present, atop should
+       ** use a specific accounting-file (as defined by the environment
+       ** variable) or should use no process accounting at all (when
+       ** contents of environment variable is empty)
+       */
+       if ( (ep = getenv(ACCTENV)) )
+       {
+               /*
+               ** environment variable exists
+               */
+               if (*ep)
+               {
+                       /*
+                       ** open active account file with the specified name
+                       */
+                       if (! droprootprivs() )
+                               cleanstop(42);
+
+                       if ( (acctfd = open(ep, O_RDONLY) ) == -1)
+                               return 1;
+
+                       if ( !acctvers(acctfd) )
+                       {
+                               (void) close(acctfd);
+                               acctfd = -1;
+                               return 1;
+                       }
+
+                       supportflags |= ACCTACTIVE;
+                       return 0;
+               }
+               else
+               {
+                       /*
+                       ** no contents
+                       */
+                       return 2;
+               }
+       }
+
+       /*
+       ** when the atopacctd daemon is active on this system,
+       ** it should be the preferred way to read the accounting records
+       */
+       if ( (sempacctpubid = semget(PACCTPUBKEY, 0, 0)) != -1)
+       {
+               FILE                    *cfp;
+               char                    shadowpath[128];
+               struct flock            flock;
+
+               if (! droprootprivs() )
+                       cleanstop(42);
+
+               (void) semop(sempacctpubid, &semclaim, 1);
+
+               snprintf(shadowpath, sizeof shadowpath, "%s/%s/%s",
+                                       pacctdir, PACCTSHADOWD, PACCTSHADOWC);
+
+               if ( (cfp = fopen(shadowpath, "r")) )
+               {
+                       if (fscanf(cfp, "%ld/%lu",
+                                       &curshadowseq, &maxshadowrec) == 2)
+                       {
+                               fclose(cfp);
+
+                               snprintf(shadowpath, sizeof shadowpath,
+                                               PACCTSHADOWF, pacctdir,
+                                               PACCTSHADOWD, curshadowseq);
+
+                               if ( (acctfd = open(shadowpath, O_RDONLY))!=-1)
+                               {
+                                       if ( !acctvers(acctfd) )
+                                       {
+                                               int maxcnt = 40;
+
+                                               if ( fork() == 0 )
+                                                       exit(0);
+
+                                               (void) wait((int *) 0);
+
+                                               while ( !acctvers(acctfd) &&
+                                                                     --maxcnt)
+                                                       usleep(50000);
+                                               
+                                               if (!acctversion)
+                                               {
+                                                       (void) close(acctfd);
+                                                       acctfd = -1;
+       
+                                                       semop(sempacctpubid,
+                                                               &semrelse, 1);
+
+                                                       regainrootprivs();
+                                                       return 1;
+                                               }
+                                       }
+
+                                       /*
+                                       ** set read lock on current shadow file
+                                       */
+                                       flock.l_type    = F_RDLCK;
+                                       flock.l_whence  = SEEK_SET;
+                                       flock.l_start   = 0;
+                                       flock.l_len     = 1;
+
+                                       if ( fcntl(acctfd, F_SETLK, &flock)
+                                                                       != -1)
+                                       {
+                                               supportflags |= ACCTACTIVE;
+                                               regainrootprivs();
+                                               return 0;
+                                       }
+
+                                               (void) close(acctfd);
+                               }
+                       }
+                       else
+                       {
+                               fclose(cfp);
+                               maxshadowrec = 0;
+                       }
+               }
+
+               (void) semop(sempacctpubid, &semrelse, 1);
+       }
+
+       /*
+       ** check if process accounting is already switched on 
+       ** for one of the standard accounting-files; in that case we
+       ** open this file and get our info from here ....
+       */
+       for (i=0, j=0; i < sizeof pacctadm/sizeof pacctadm[0]; i++)
+       {
+               if ( stat(pacctadm[i].name, &pacctadm[i].stat) == 0)
+                       j++;
+       }
+
+       if (j)
+       {
+               /*
+               ** at least one file is present; check if it is really in use
+               ** at this moment for accounting by forking a child-process
+               ** which immediately finishes to force a new accounting-record
+               */
+               if ( fork() == 0 )
+                       exit(0);        /* let the child finish */
+
+               (void) wait((int *) 0); /* let the parent wait  */
+
+               for (i=0; i < sizeof pacctadm/sizeof pacctadm[0]; i++)
+               {
+                       if ( stat(pacctadm[i].name, &statbuf) == 0)
+                       {
+                               /*
+                               ** accounting-file has grown?
+                               */
+                               if (statbuf.st_size > pacctadm[i].stat.st_size)
+                               {
+                                       /*
+                                       ** open active account file
+                                       */
+                                       if ( (acctfd = open(pacctadm[i].name,
+                                                           O_RDONLY) ) == -1)
+                                               return 1;
+
+                                       if ( !acctvers(acctfd) )
+                                       {
+                                               (void) close(acctfd);
+                                               acctfd = -1;
+                                               return 1;
+                                       }
+
+                                       supportflags |= ACCTACTIVE;
+                                       return 0;
+                               }
+                       }
+               }
+       }
+
+       /*
+       ** process-accounting is not yet switched on in a standard way;
+       ** check if another atop has switched on accounting already
+       **
+       ** first try to create a semaphore group exclusively; if this succeeds,
+       ** this is the first atop-incarnation since boot and the semaphore group
+       ** should be initialized`
+       */
+       if ( (sematopid = semget(ATOPACCTKEY, 2, 0600|IPC_CREAT|IPC_EXCL)) >= 0)
+               (void) semctl(sematopid, 0, SETALL, arg);
+       else
+               sematopid = semget(ATOPACCTKEY, 0, 0);
+
+       /*
+       ** check if we got access to the semaphore group
+       */
+       if (sematopid == -1)
+               return 3;
+
+       /*
+       ** the semaphore group is opened now; claim exclusive rights
+       */
+       (void) semop(sematopid, &semclaim, 1);
+
+       /*
+       ** are we the first to use the accounting-mechanism ?
+       */
+       if (semctl(sematopid, 1, GETVAL, 0) == ATOPACCTTOT)
+       {
+               /*
+               ** create a new separate directory below /tmp
+               ** for the accounting file;
+               ** if this directory exists (e.g. previous atop-run killed)
+               ** it will be cleaned and newly created
+               */
+               if ( mkdir(ACCTDIR, 0700) == -1)
+               {
+                       if (errno == EEXIST)
+                       {
+                               (void) acct(0);
+                               (void) unlink(ACCTDIR "/" ACCTFILE);
+                               (void) rmdir(ACCTDIR);
+                       }
+
+                       if ( mkdir(ACCTDIR, 0700) == -1)
+                       {
+                               /*
+                               ** persistent failure
+                               */
+                               (void) semop(sematopid, &semrelse, 1);
+                               return 4;
+                       }
+               }
+
+               /*
+               ** create accounting file in new directory
+               */
+               (void) close( creat(ACCTDIR "/" ACCTFILE, 0600) );
+
+               /*
+               ** switch on accounting
+               */
+               if ( acct(ACCTDIR "/" ACCTFILE) < 0)
+               {
+                       (void) unlink(ACCTDIR "/" ACCTFILE);
+                       (void) rmdir(ACCTDIR);
+                       (void) semop(sematopid, &semrelse, 1);
+
+                       return 5;
+               }
+       }
+
+       /*
+       ** accounting is switched on now;
+       ** open accounting-file
+       */
+       if ( (acctfd = open(ACCTDIR "/" ACCTFILE, O_RDONLY) ) < 0)
+       {
+               (void) acct(0);
+               (void) unlink(ACCTDIR "/" ACCTFILE);
+               (void) rmdir(ACCTDIR);
+
+               (void) semop(sematopid, &semrelse, 1);
+               return 1;
+       }
+
+       /*
+       ** accounting successfully switched on
+       */
+       (void) semop(sematopid, &semdecre, 1);
+       (void) semop(sematopid, &semrelse, 1);
+
+       acctatop = 1;
+
+       /*
+       ** determine version of accounting-record
+       */ 
+       (void) fstat(acctfd, &statbuf);
+
+       if (statbuf.st_size == 0)       /* no acct record written yet */
+       {
+               /*
+               ** let's write one record
+               */
+               if ( fork() == 0 )
+                       exit(0);        /* let the child finish */
+
+               (void) wait((int *) 0); /* let the parent wait  */
+       }
+
+       if ( !acctvers(acctfd) )
+       {
+               (void) acct(0);
+               (void) close(acctfd);
+               (void) unlink(ACCTDIR "/" ACCTFILE);
+               (void) rmdir(ACCTDIR);
+
+               acctfd = -1;
+               return 1;
+
+       }
+
+       supportflags |= ACCTACTIVE;
+       return 0;
+}
+
+/*
+** determine the version of the accounting-record layout/length
+** and reposition the seek-pointer to the end of the accounting file
+*/
+static int
+acctvers(int fd)
+{
+       struct acct     tmprec;
+
+       /*
+       ** read first record from accounting file to verify
+       ** the second byte (always contains version number)
+       */
+       if ( read(fd, &tmprec, sizeof tmprec) < sizeof tmprec)
+               return 0;
+
+       switch (tmprec.ac_version & 0x0f)
+       {
+          case 2:
+               acctrecsz     = sizeof(struct acct);
+               acctversion   = 2;
+               break;
+
+          case 3:
+               acctrecsz     = sizeof(struct acct_v3);
+               acctversion   = 3;
+               break;
+
+          default:
+               fprintf(stderr, "Unknown format of process accounting file\n");
+               cleanstop(8);
+       }
+
+       /*
+       ** accounting successfully switched on
+       */
+       acctsize = acctprocnt() * acctrecsz;
+
+       /*
+       ** reposition to actual file-size
+       */
+       (void) lseek(fd, acctsize, SEEK_SET);
+
+       return 1;
+}
+
+/*
+** switch off the process-accounting mechanism
+*/
+void
+acctswoff(void)
+{
+       int             sematopid;
+       struct stat     before, after;
+
+       /*
+       ** if accounting not supported, skip call
+       */
+       if (acctfd == -1)
+               return;
+
+       /*
+       ** our private accounting-file in use?
+       */
+       if (acctatop)
+       {
+               acctatop = 0;
+
+               /*
+               ** claim the semaphore to get exclusive rights for
+               ** the accounting-manipulation
+               */
+               sematopid = semget(ATOPACCTKEY, 0, 0);
+
+               (void) semop(sematopid, &semclaim, 1);
+               (void) semop(sematopid, &semincre, 1);
+
+               /*
+               ** check if we were the last user of accounting
+               */
+               if (semctl(sematopid, 1, GETVAL, 0) == ATOPACCTTOT)
+               {
+                       /*
+                       ** switch off private accounting
+                       **
+                       ** verify if private accounting is still in use to
+                       ** avoid that we switch off process accounting that
+                       ** has been activated manually in the meantime
+                       */
+                       (void) fstat(acctfd, &before);
+
+                       if ( fork() == 0 )      /* create a child and   */
+                               exit(0);        /* let the child finish */
+
+                       (void) wait((int *) 0); /* let the parent wait  */
+
+                       (void) fstat(acctfd, &after);
+
+                       if (after.st_size > before.st_size)
+                       {
+                               /*
+                               ** remove the directory and file
+                               */
+                               regainrootprivs(); /* get root privs again */
+
+                               (void) acct(0);
+                               (void) unlink(ACCTDIR "/" ACCTFILE);
+                               (void) rmdir(ACCTDIR);
+
+                               if (! droprootprivs() )
+                                       cleanstop(42);
+                       }
+               }
+
+               (void) semop(sematopid, &semrelse, 1);
+       }
+
+       /*
+       ** anyhow close the accounting-file again
+       */
+       (void) close(acctfd);   /* close account file again */
+       acctfd = -1;
+
+       supportflags &= ~ACCTACTIVE;
+}
+
+/*
+** get the number of exited processes written
+** in the process-account file since the previous sample
+*/
+unsigned long
+acctprocnt(void)
+{
+       struct stat     statacc;
+
+       /*
+       ** if accounting not supported, skip call
+       */
+       if (acctfd == -1)
+               return 0;
+
+       /*
+       ** determine the current size of the accounting file
+       */
+       (void) fstat(acctfd, &statacc);
+
+       /*
+       ** handle atopacctd-based process accounting on bases of
+       ** fixed-chunk shadow files
+       */
+       if (maxshadowrec)
+       {
+               unsigned long   numrecs = 0;
+               long            newseq;
+               char            shadowpath[128];
+               FILE            *cfp;
+
+               /*
+               ** verify how many new processes are added to the current
+               ** shadow file
+               */
+               numrecs = (statacc.st_size - acctsize) / acctrecsz;
+
+               /*
+               ** verify if subsequent shadow files are involved
+               ** (i.e. if the current file is full)
+               */
+               if (statacc.st_size / acctrecsz < maxshadowrec)
+                       return numrecs;
+
+               /*
+               ** more shadow files available
+               ** get current shadow file
+               */      
+               snprintf(shadowpath, sizeof shadowpath, "%s/%s/%s",
+                                       pacctdir, PACCTSHADOWD, PACCTSHADOWC);
+
+               if ( (cfp = fopen(shadowpath, "r")) )
+               {
+                       if (fscanf(cfp, "%ld", &newseq) == 1)
+                       {
+                               fclose(cfp);
+                       }
+                       else
+                       {
+                               fclose(cfp);
+                               return numrecs;
+                       }
+               }
+               else
+               {
+                       return numrecs;
+               }
+
+               if (newseq == curshadowseq)
+                       return numrecs;
+
+               snprintf(shadowpath, sizeof shadowpath, PACCTSHADOWF,
+                                       pacctdir, PACCTSHADOWD, newseq);
+
+               /*
+               ** determine the size of newest shadow file
+               */
+               (void) stat(shadowpath, &statacc);
+
+               numrecs += ((newseq - curshadowseq - 1) * maxshadowrec) +
+                          (statacc.st_size / acctrecsz);
+
+               return numrecs;
+       }
+       else
+       /*
+       ** classic process accounting on bases of directly opened
+       ** process accounting file
+       */
+       {
+               if (acctsize > statacc.st_size) /* accounting reset? */
+               {
+                       /*
+                       ** reposition to start of file
+                       */
+                       (void) lseek(acctfd, 0, SEEK_SET);
+                       acctsize = 0;
+               }
+
+               return (statacc.st_size - acctsize) / acctrecsz;
+       }
+}
+
+/*
+** reposition the seek offset in the process accounting file to skip
+** processes that have not been read
+*/
+void
+acctrepos(unsigned int noverflow)
+{
+       /*
+       ** if accounting not supported, skip call
+       */
+       if (acctfd == -1)
+               return;
+
+       if (maxshadowrec)
+       {
+               int     i;
+               off_t   virtoffset  = acctsize + noverflow * acctrecsz;
+               off_t   maxshadowsz = maxshadowrec * acctrecsz;
+               long    switches    = virtoffset / maxshadowsz;
+               acctsize            = virtoffset % maxshadowsz;
+
+               for (i=0; i < switches; i++)
+                       switchshadow();
+
+               (void) lseek(acctfd, acctsize, SEEK_SET);
+       }
+       else
+       {
+               /*
+               ** just reposition to skip superfluous records
+               */
+               (void) lseek(acctfd, noverflow * acctrecsz, SEEK_CUR);
+               acctsize   += noverflow * acctrecsz;
+       }
+}
+
+
+/*
+** read the process records from the process accounting file,
+** that are written since the previous cycle
+*/
+int
+acctphotoproc(struct tstat *accproc, int nrprocs)
+{
+       register int            nrexit;
+       register struct tstat   *api;
+       struct acct             acctrec;
+       struct acct_v3          acctrec_v3;
+       struct stat             statacc;
+
+       /*
+       ** if accounting not supported, skip call
+       */
+       if (acctfd == -1)
+               return 0;
+
+       /*
+       ** determine the size of the (current) account file
+       ** and the current offset within that file
+       */
+       (void) fstat(acctfd, &statacc);
+
+       /*
+       ** check all exited processes in accounting file
+       */
+       for  (nrexit=0, api=accproc; nrexit < nrprocs;
+                               nrexit++, api++, acctsize += acctrecsz)
+       {
+               /*
+               ** in case of shadow accounting files, we might have to
+               ** switch from the current accouting file to the next
+               */
+               if (maxshadowrec && acctsize >= statacc.st_size)
+               {
+                       switchshadow();
+
+                       /*
+                       ** determine the size of the new (current) account file
+                       ** and initialize the current offset within that file
+                       */
+                       (void) fstat(acctfd, &statacc);
+
+                       acctsize  = 0;
+               }
+
+               /*
+               ** read the next record
+               */
+               switch (acctversion)
+               {
+                  case 2:
+                       if ( read(acctfd, &acctrec, acctrecsz) < acctrecsz )
+                               break;  /* unexpected end of account file */
+
+                       /*
+                       ** fill process info from accounting-record
+                       */
+                       api->gen.state  = 'E';
+                       api->gen.nthr   = 1;
+                       api->gen.isproc = 1;
+                       api->gen.pid    = 0;
+                       api->gen.tgid   = 0;
+                       api->gen.ppid   = 0;
+                       api->gen.excode = acctrec.ac_exitcode;
+                       api->gen.ruid   = acctrec.ac_uid16;
+                       api->gen.rgid   = acctrec.ac_gid16;
+                       api->gen.btime  = acctrec.ac_btime;
+                       api->gen.elaps  = acctrec.ac_etime;
+                       api->cpu.stime  = acctexp(acctrec.ac_stime);
+                       api->cpu.utime  = acctexp(acctrec.ac_utime);
+                       api->mem.minflt = acctexp(acctrec.ac_minflt);
+                       api->mem.majflt = acctexp(acctrec.ac_majflt);
+                       api->dsk.rio    = acctexp(acctrec.ac_rw);
+
+                       strcpy(api->gen.name, acctrec.ac_comm);
+                       break;
+
+                  case 3:
+                       if ( read(acctfd, &acctrec_v3, acctrecsz) < acctrecsz )
+                               break;  /* unexpected end of account file */
+
+                       /*
+                       ** fill process info from accounting-record
+                       */
+                       api->gen.state  = 'E';
+                       api->gen.pid    = acctrec_v3.ac_pid;
+                       api->gen.tgid   = acctrec_v3.ac_pid;
+                       api->gen.ppid   = acctrec_v3.ac_ppid;
+                       api->gen.nthr   = 1;
+                       api->gen.isproc = 1;
+                       api->gen.excode = acctrec_v3.ac_exitcode;
+                       api->gen.ruid   = acctrec_v3.ac_uid;
+                       api->gen.rgid   = acctrec_v3.ac_gid;
+                       api->gen.btime  = acctrec_v3.ac_btime;
+                       api->gen.elaps  = acctrec_v3.ac_etime;
+                       api->cpu.stime  = acctexp(acctrec_v3.ac_stime);
+                       api->cpu.utime  = acctexp(acctrec_v3.ac_utime);
+                       api->mem.minflt = acctexp(acctrec_v3.ac_minflt);
+                       api->mem.majflt = acctexp(acctrec_v3.ac_majflt);
+                       api->dsk.rio    = acctexp(acctrec_v3.ac_rw);
+
+                       strcpy(api->gen.name, acctrec_v3.ac_comm);
+                       break;
+               }
+       }
+
+       if (acctsize > ACCTMAXFILESZ && !maxshadowrec)
+               acctrestarttrial();
+
+       return nrexit;
+}
+
+/*
+** when the size of the private accounting file exceeds a certain limit,
+** it might be useful to stop process accounting, truncate the
+** process accounting file to zero and start process accounting again
+**
+** this will only be done if this atop process is the only one
+** that is currently using the accounting file
+*/
+static void
+acctrestarttrial()
+{
+       struct stat     statacc;
+       int             sematopid;
+
+       /*
+       ** not private accounting-file in use?
+       */
+       if (!acctatop)
+               return;         // do not restart
+
+       /*
+       ** still remaining records in accounting file that are
+       ** written between the moment that the number of exited
+       ** processes was counted and the moment that all processes
+       ** were read
+       */
+       (void) fstat(acctfd, &statacc);
+
+       if (acctsize != statacc.st_size)
+               return;         // do not restart
+
+       /*
+       ** claim the semaphore to get exclusive rights for
+       ** the accounting-manipulation
+       */
+       sematopid = semget(ATOPACCTKEY, 0, 0);
+
+       (void) semop(sematopid, &semclaim, 1);
+
+       /*
+       ** check if there are more users of accounting file
+       */
+       if (semctl(sematopid, 1, GETVAL, 0) < ATOPACCTTOT-1)
+       {
+               (void) semop(sematopid, &semrelse, 1);
+               return;         // do not restart
+       }
+
+       /*
+       ** restart is possible
+       **
+       ** - switch off accounting
+       ** - truncate the file
+       ** - switch on accounting
+       */
+       regainrootprivs();      // get root privs again 
+
+       (void) acct(0);         // switch off accounting
+
+       if ( truncate(ACCTDIR "/" ACCTFILE, 0) == 0)
+               (void) lseek(acctfd, 0, SEEK_SET);
+
+       (void) acct(ACCTDIR "/" ACCTFILE);
+
+       if (! droprootprivs() )
+               cleanstop(42);
+
+       acctsize = 0;
+
+       (void) semop(sematopid, &semrelse, 1);
+}
+
+/*
+** expand the special compression-methods
+*/
+static count_t
+acctexp(comp_t ct)
+{
+        count_t        exp;
+        count_t val;
+
+        exp = (ct >> 13) & 0x7;                /* obtain  3 bits base-8 exponent */
+        val =  ct & 0x1fff;            /* obtain 13 bits mantissa        */
+
+        while (exp-- > 0)
+                val <<= 3;
+
+        return val;
+}
+
+/*
+** switch to the next accounting shadow file
+*/
+static void
+switchshadow(void)
+{
+       int             tmpfd;
+       char            shadowpath[128];
+       struct flock    flock;
+
+       /*
+       ** determine path name of new shadow file
+       */
+       curshadowseq++;
+
+       snprintf(shadowpath, sizeof shadowpath, PACCTSHADOWF,
+                                       pacctdir, PACCTSHADOWD, curshadowseq);
+
+       /*
+       ** open new shadow file, while the previous is also kept open
+       ** (to keep the read lock until a read lock is set for the new
+       ** shadow file)
+       */
+       if ( (tmpfd = open(shadowpath, O_RDONLY)) != -1)
+       {
+               /*
+               ** set read lock on new shadow file
+               */
+               flock.l_type    = F_RDLCK;
+               flock.l_whence  = SEEK_SET;
+               flock.l_start   = 0;
+               flock.l_len     = 1;
+
+                if ( fcntl(tmpfd, F_SETLK, &flock) != -1)
+               {
+                       (void) close(acctfd);   // implicit release read lock
+                       acctfd = tmpfd;
+                       return;
+               }
+               else
+               {
+                       (void) close(tmpfd);
+               }
+       }
+}
+
+/*
+** handle the option 'pacctdir' in the /etc/atoprc file
+*/
+void
+do_pacctdir(char *tagname, char *tagvalue)
+{
+       char            shadowpath[128];
+       struct stat     dirstat;
+
+       /*
+       ** copy the directory pathname to an own buffer
+       */
+       if ( (pacctdir = malloc(strlen(tagvalue)+1)) == NULL )
+       {
+               perror("malloc pacctdir");
+               exit(11);
+       }
+
+       strcpy(pacctdir, tagvalue);
+
+       /*
+       ** verify if the atopacctd daemon is active
+       */
+       if ( semget(PACCTPUBKEY, 0, 0) == -1)
+       {
+               fprintf(stderr, "Warning: option '%s' specified "
+                               "while atopacctd not running!\n", tagname);
+               sleep(2);
+               return;
+       }
+
+       /*
+       ** verify that the topdirectory exists
+       */
+        if ( stat(pacctdir, &dirstat) == -1 )
+        {
+               fprintf(stderr, "Warning: option '%s' specified - ", tagname);
+               perror(pacctdir);
+                sleep(2);
+               return;
+        }
+
+        if (! S_ISDIR(dirstat.st_mode) )
+        {
+               fprintf(stderr, "Warning: option '%s' specified - ", tagname);
+                fprintf(stderr, "%s not a directory\n", pacctdir);
+                sleep(2);
+               return;
+        }
+
+       /*
+       ** verify that the topdirectory contains the required subdirectory
+       */
+       snprintf(shadowpath, sizeof shadowpath, "%s/%s",
+                                               pacctdir, PACCTSHADOWD);
+
+        if ( stat(shadowpath, &dirstat) == -1 )
+        {
+               fprintf(stderr, "Warning: option '%s' specified - ", tagname);
+               perror(shadowpath);
+                sleep(2);
+               return;
+        }
+
+        if (! S_ISDIR(dirstat.st_mode) )
+        {
+               fprintf(stderr, "Warning: option '%s' specified - ", tagname);
+                fprintf(stderr, "%s not a directory\n", shadowpath);
+                sleep(2);
+               return;
+        }
+}
diff --git a/sources/acctproc.h b/sources/acctproc.h
new file mode 100644 (file)
index 0000000..66c3a0d
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+** ATOP - System & Process Monitor
+**
+** The program 'atop' offers the possibility to view the activity of
+** the system on system-level as well as process-level.
+**
+** Include-file for process-accounting functions.
+** ================================================================
+** Author:      Gerlof Langeveld
+** E-mail:      gerlof.langeveld@atoptool.nl
+** Date:        November 1996
+** LINUX-port:  June 2000
+**
+** This program is free software; you can redistribute it and/or modify it
+** under the terms of the GNU General Public License as published by the
+** Free Software Foundation; either version 2, or (at your option) any
+** later version.
+**
+** This program is distributed in the hope that it will be useful, but
+** WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+** See the GNU General Public License for more details.
+*/
+int            acctswon(void);
+void           acctswoff(void);
+unsigned long  acctprocnt(void);
+int            acctphotoproc(struct tstat *, int);
+void           acctrepos(unsigned int);
+
+/*
+** maximum number of records to be read from process accounting file
+** for one sample, to avoid that atop explodes and introduces OOM killing ....
+**
+** the maximum is based on a limit of 50 MiB extra memory (approx. 70000 procs)
+*/
+#define MAXACCTPROCS   (50*1024*1024/sizeof(struct tstat))
+
+/*
+** preferred maximum size of process accounting file (200 MiB)
+*/
+#define ACCTMAXFILESZ  (200*1024*1024)
+
+/*
+** alternative layout of accounting record if kernel-patch
+** has been installed
+*/
+#include <linux/types.h>
+typedef __u16   comp_t;
+typedef __u32   comp2_t;
+#define ACCT_COMM      16
+
+struct acct_atop
+{
+       char            ac_flag;                /* Flags */
+       char            ac_version;             /* Always set to ACCT_VERSION */
+       __u32           ac_pid;                 /* Process ID */
+       __u32           ac_ppid;                /* Parent Process ID */
+       __u16           ac_uid16;               /* LSB of Real User ID */
+       __u16           ac_gid16;               /* LSB of Real Group ID */
+       __u16           ac_tty;                 /* Control Terminal */
+       __u32           ac_btime;               /* Process Creation Time */
+       comp_t          ac_utime;               /* User Time */
+       comp_t          ac_stime;               /* System Time */
+       comp_t          ac_etime;               /* Elapsed Time */
+       comp_t          ac_mem;                 /* Virtual  Memory */
+       comp_t          ac_rss;                 /* Resident Memory */
+       comp_t          ac_io;                  /* Chars Transferred */
+       comp_t          ac_rw;                  /* Blocks Read or Written */
+       comp_t          ac_bread;               /* Blocks Read */
+       comp_t          ac_bwrite;              /* Blocks Written */
+       comp2_t         ac_dskrsz;              /* Cum. blocks read */
+       comp2_t         ac_dskwsz;              /* Cum. blocks written */
+       comp_t          ac_tcpsnd;              /* TCP send requests */
+       comp_t          ac_tcprcv;              /* TCP recv requests */
+       comp2_t         ac_tcpssz;              /* TCP cum. length   */
+       comp2_t         ac_tcprsz;              /* TCP cum. length   */
+       comp_t          ac_udpsnd;              /* UDP send requests */
+       comp_t          ac_udprcv;              /* UDP recv requests */
+       comp2_t         ac_udpssz;              /* UDP cum. length   */
+       comp2_t         ac_udprsz;              /* UDP cum. length   */
+       comp_t          ac_rawsnd;              /* RAW send requests */
+       comp_t          ac_rawrcv;              /* RAW recv requests */
+       comp_t          ac_minflt;              /* Minor Pagefaults */
+       comp_t          ac_majflt;              /* Major Pagefaults */
+       comp_t          ac_swaps;               /* Number of Swaps */
+/* m68k had no padding here. */
+#if !defined(CONFIG_M68K) || !defined(__KERNEL__)
+       __u16           ac_ahz;                 /* AHZ */
+#endif
+       __u32           ac_exitcode;            /* Exitcode */
+       char            ac_comm[ACCT_COMM + 1]; /* Command Name */
+       __u8            ac_etime_hi;            /* Elapsed Time MSB */
+       __u16           ac_etime_lo;            /* Elapsed Time LSB */
+       __u32           ac_uid;                 /* Real User ID */
+       __u32           ac_gid;                 /* Real Group ID */
+};
+
+/*
+** default layout of accounting record
+** (copied from /usr/src/linux/include/linux/acct.h)
+*/
+
+struct acct
+{
+       char            ac_flag;                /* Flags */
+       char            ac_version;             /* Always set to ACCT_VERSION */
+       /* for binary compatibility back until 2.0 */
+       __u16           ac_uid16;               /* LSB of Real User ID */
+       __u16           ac_gid16;               /* LSB of Real Group ID */
+       __u16           ac_tty;                 /* Control Terminal */
+       __u32           ac_btime;               /* Process Creation Time */
+       comp_t          ac_utime;               /* User Time */
+       comp_t          ac_stime;               /* System Time */
+       comp_t          ac_etime;               /* Elapsed Time */
+       comp_t          ac_mem;                 /* Average Memory Usage */
+       comp_t          ac_io;                  /* Chars Transferred */
+       comp_t          ac_rw;                  /* Blocks Read or Written */
+       comp_t          ac_minflt;              /* Minor Pagefaults */
+       comp_t          ac_majflt;              /* Major Pagefaults */
+       comp_t          ac_swaps;               /* Number of Swaps */
+/* m68k had no padding here. */
+#if !defined(CONFIG_M68K) || !defined(__KERNEL__)
+       __u16           ac_ahz;                 /* AHZ */
+#endif
+       __u32           ac_exitcode;            /* Exitcode */
+       char            ac_comm[ACCT_COMM + 1]; /* Command Name */
+       __u8            ac_etime_hi;            /* Elapsed Time MSB */
+       __u16           ac_etime_lo;            /* Elapsed Time LSB */
+       __u32           ac_uid;                 /* Real User ID */
+       __u32           ac_gid;                 /* Real Group ID */
+};
+
+struct acct_v3
+{
+       char            ac_flag;                /* Flags */
+       char            ac_version;             /* Always set to ACCT_VERSION */
+       __u16           ac_tty;                 /* Control Terminal */
+       __u32           ac_exitcode;            /* Exitcode */
+       __u32           ac_uid;                 /* Real User ID */
+       __u32           ac_gid;                 /* Real Group ID */
+       __u32           ac_pid;                 /* Process ID */
+       __u32           ac_ppid;                /* Parent Process ID */
+       __u32           ac_btime;               /* Process Creation Time */
+#ifdef __KERNEL__
+       __u32           ac_etime;               /* Elapsed Time */
+#else
+       float           ac_etime;               /* Elapsed Time */
+#endif
+       comp_t          ac_utime;               /* User Time */
+       comp_t          ac_stime;               /* System Time */
+       comp_t          ac_mem;                 /* Average Memory Usage */
+       comp_t          ac_io;                  /* Chars Transferred */
+       comp_t          ac_rw;                  /* Blocks Read or Written */
+       comp_t          ac_minflt;              /* Minor Pagefaults */
+       comp_t          ac_majflt;              /* Major Pagefaults */
+       comp_t          ac_swaps;               /* Number of Swaps */
+       char            ac_comm[ACCT_COMM];     /* Command Name */
+};
diff --git a/sources/atop-pm.sh b/sources/atop-pm.sh
new file mode 100755 (executable)
index 0000000..7f41a86
--- /dev/null
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+case "$1" in
+       pre)    /usr/bin/systemctl stop atop
+               exit 0
+               ;;
+       post)   /usr/bin/systemctl start atop
+               exit 0
+               ;;
+       *)      exit 1
+               ;;
+esac
diff --git a/sources/atop.c b/sources/atop.c
new file mode 100644 (file)
index 0000000..30d8d1f
--- /dev/null
@@ -0,0 +1,1161 @@
+/*
+** ATOP - System & Process Monitor
+**
+** The program 'atop' offers the possibility to view the activity of 
+** the system on system-level as well as process-level.
+**
+** This source-file contains the main-function, which verifies the
+** calling-parameters and takes care of initialization. 
+** The engine-function drives the main sample-loop in which after the
+** indicated interval-time a snapshot is taken of the system-level and
+** process-level counters and the deviations are calculated and
+** visualized for the user.
+** ==========================================================================
+** Author:      Gerlof Langeveld
+** E-mail:      gerlof.langeveld@atoptool.nl
+** Date:        November 1996
+** Linux-port:  June 2000
+** Modified:   May 2001 - Ported to kernel 2.4
+** --------------------------------------------------------------------------
+** Copyright (C) 2000-2012 Gerlof Langeveld
+**
+** This program is free software; you can redistribute it and/or modify it
+** under the terms of the GNU General Public License as published by the
+** Free Software Foundation; either version 2, or (at your option) any
+** later version.
+**
+** This program is distributed in the hope that it will be useful, but
+** WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+** See the GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+** --------------------------------------------------------------------------
+**
+** After initialization, the main-function calls the ENGINE.
+** For every cycle (so after another interval) the ENGINE calls various 
+** functions as shown below:
+**
+** +---------------------------------------------------------------------+
+** |                           E  N  G  I  N  E                          |
+** |                                                                     |
+** |                                                                     |
+** |    _____________________await interval-timer_____________________   |
+** |   |                                                              ^  |
+** |   |      ________       ________      ________      ________     |  |
+** |   |     ^        |     ^        |    ^        |    ^        |    |  |
+** +---|-----|--------|-----|--------|----|--------|----|--------|----|--+
+**     |     |        |     |        |    |        |    |        |    |
+**  +--V-----|--+  +--V-----|--+  +--V----|--+  +--V----|--+  +--V----|-+  
+**  |           |  |           |  |          |  |          |  |         |
+**  | photosyst |  | photoproc |  |   acct   |  | deviate  |  |  print  |
+**  |           |  |           |  |photoproc |  |  ...syst |  |         |
+**  |           |  |           |  |          |  |  ...proc |  |         |
+**  +-----------+  +-----------+  +----------+  +----------+  +---------+  
+**        ^              ^             ^              ^            |
+**        |              |             |              |            |
+**        |              |             |              V            V 
+**      ______       _________     __________     ________     _________
+**     /      \     /         \   /          \   /        \   /         \
+**      /proc          /proc       accounting       task       screen or
+**                                    file        database        file
+**     \______/     \_________/   \__________/   \________/   \_________/
+**
+**    -        photosyst()
+**     Takes a snapshot of the counters related to resource-usage on
+**     system-level (cpu, disk, memory, network).
+**     This code is UNIX-flavor dependent; in case of Linux the counters
+**     are retrieved from /proc.
+**
+**    -        photoproc()
+**     Takes a snapshot of the counters related to resource-usage of
+**     tasks which are currently active. For this purpose the whole
+**     task-list is read.
+**     This code is UNIX-flavor dependent; in case of Linux the counters
+**     are retrieved from /proc.
+**
+**    -        acctphotoproc()
+**     Takes a snapshot of the counters related to resource-usage of
+**     tasks which have been finished during the last interval.
+**     For this purpose all new records in the accounting-file are read.
+**
+** When all counters have been gathered, functions are called to calculate
+** the difference between the current counter-values and the counter-values
+** of the previous cycle. These functions operate on the system-level
+** as well as on the task-level counters. 
+** These differences are stored in a new structure(-table). 
+**
+**    -        deviatsyst()
+**     Calculates the differences between the current system-level
+**     counters and the corresponding counters of the previous cycle.
+**
+**    -        deviattask()
+**     Calculates the differences between the current task-level
+**     counters and the corresponding counters of the previous cycle.
+**     The per-task counters of the previous cycle are stored in the
+**     task-database; this "database" is implemented as a linked list
+**     of taskinfo structures in memory (so no disk-accesses needed).
+**     Within this linked list hash-buckets are maintained for fast searches.
+**     The entire task-database is handled via a set of well-defined 
+**     functions from which the name starts with "pdb_..." (see the
+**     source-file procdbase.c).
+**     The processes which have been finished during the last cycle
+**     are also treated by deviattask() in order to calculate what their
+**     resource-usage was before they finished.
+**
+** All information is ready to be visualized now.
+** There is a structure which holds the start-address of the
+** visualization-function to be called. Initially this structure contains
+** the address of the generic visualization-function ("generic_samp"), but
+** these addresses can be modified in the main-function depending on particular
+** flags. In this way various representation-layers (ASCII, graphical, ...)
+** can be linked with 'atop'; the one to use can eventually be chosen
+** at runtime. 
+**
+** $Log: atop.c,v $
+** Revision 1.49  2010/10/23 14:01:00  gerlof
+** Show counters for total number of running and sleep (S and D) threads.
+**
+** Revision 1.48  2010/10/23 08:18:15  gerlof
+** Catch signal SIGUSR2 to take a final sample and stop.
+** Needed for improved of suspend/hibernate.
+**
+** Revision 1.47  2010/04/23 12:20:19  gerlof
+** Modified mail-address in header.
+**
+** Revision 1.46  2010/04/23 09:57:28  gerlof
+** Version (flag -V) handled earlier after startup.
+**
+** Revision 1.45  2010/04/17 17:19:41  gerlof
+** Allow modifying the layout of the columns in the system lines.
+**
+** Revision 1.44  2010/04/16 13:00:23  gerlof
+** Automatically start another version of atop if the logfile to
+** be read has not been created by the current version.
+**
+** Revision 1.43  2010/03/04 10:51:10  gerlof
+** Support I/O-statistics on logical volumes and MD devices.
+**
+** Revision 1.42  2009/12/31 11:33:33  gerlof
+** Sanity-check to bypass kernel-bug showing 497 days of CPU-consumption.
+**
+** Revision 1.41  2009/12/17 10:51:31  gerlof
+** Allow own defined process line with key 'o' and a definition
+** in the atoprc file.
+**
+** Revision 1.40  2009/12/17 08:15:15  gerlof
+** Introduce branch-key to go to specific time in raw file.
+**
+** Revision 1.39  2009/12/10 13:34:32  gerlof
+** Cosmetical changes.
+**
+** Revision 1.38  2009/12/10 11:55:38  gerlof
+** Introduce -L flag for line length.
+**
+** Revision 1.37  2009/12/10 10:43:33  gerlof
+** Correct calculation of node name.
+**
+** Revision 1.36  2009/12/10 09:19:06  gerlof
+** Various changes related to redesign of user-interface.
+** Made by JC van Winkel.
+**
+** Revision 1.35  2009/11/27 15:11:55  gerlof
+** *** empty log message ***
+**
+** Revision 1.34  2009/11/27 15:07:25  gerlof
+** Give up root-priviliges at a earlier stage.
+**
+** Revision 1.33  2009/11/27 14:01:01  gerlof
+** Introduce system-wide configuration file /etc/atoprc
+**
+** Revision 1.32  2008/01/07 10:16:13  gerlof
+** Implement summaries for atopsar.
+**
+** Revision 1.31  2007/11/06 09:16:05  gerlof
+** Add keyword atopsarflags to configuration-file ~/.atoprc
+**
+** Revision 1.30  2007/08/16 11:58:35  gerlof
+** Add support for atopsar reporting.
+**
+** Revision 1.29  2007/03/20 13:01:36  gerlof
+** Introduction of variable supportflags.
+**
+** Revision 1.28  2007/03/20 12:13:00  gerlof
+** Be sure that all tstat struct's are initialized with binary zeroes.
+**
+** Revision 1.27  2007/02/19 11:55:04  gerlof
+** Bug-fix: flag -S was not recognized any more.
+**
+** Revision 1.26  2007/02/13 10:34:20  gerlof
+** Support parseable output with flag -P
+**
+** Revision 1.25  2007/01/26 12:10:40  gerlof
+** Add configuration-value 'swoutcritsec'.
+**
+** Revision 1.24  2007/01/18 10:29:22  gerlof
+** Improved syntax-checking for ~/.atoprc file.
+** Support for network-interface busy-percentage.
+**
+** Revision 1.23  2006/02/07 08:27:04  gerlof
+** Cosmetic changes.
+**
+** Revision 1.22  2005/10/28 09:50:29  gerlof
+** All flags/subcommands are defined as macro's.
+**
+** Revision 1.21  2005/10/21 09:48:48  gerlof
+** Per-user accumulation of resource consumption.
+**
+** Revision 1.20  2004/12/14 15:05:38  gerlof
+** Implementation of patch-recognition for disk and network-statistics.
+**
+** Revision 1.19  2004/10/26 13:42:49  gerlof
+** Also lock current physical pages in memory.
+**
+** Revision 1.18  2004/09/15 08:23:42  gerlof
+** Set resource limit for locked memory to infinite, because
+** in certain environments it is set to 32K (causes atop-malloc's
+** to fail).
+**
+** Revision 1.17  2004/05/06 09:45:44  gerlof
+** Ported to kernel-version 2.6.
+**
+** Revision 1.16  2003/07/07 09:18:22  gerlof
+** Cleanup code (-Wall proof).
+**
+** Revision 1.15  2003/07/03 11:16:14  gerlof
+** Implemented subcommand `r' (reset).
+**
+** Revision 1.14  2003/06/30 11:29:12  gerlof
+** Handle configuration file ~/.atoprc
+**
+** Revision 1.13  2003/01/14 09:01:10  gerlof
+** Explicit clearing of malloced space for exited processes.
+**
+** Revision 1.12  2002/10/30 13:44:51  gerlof
+** Generate notification for statistics since boot.
+**
+** Revision 1.11  2002/10/08 11:34:52  gerlof
+** Modified storage of raw filename.
+**
+** Revision 1.10  2002/09/26 13:51:47  gerlof
+** Limit header lines by not showing disks.
+**
+** Revision 1.9  2002/09/17 10:42:00  gerlof
+** Copy functions rawread() and rawwrite() to separate source-file rawlog.c
+**
+** Revision 1.8  2002/08/30 07:49:35  gerlof
+** Implement possibility to store and retrieve atop-data in raw format.
+**
+** Revision 1.7  2002/08/27 12:09:16  gerlof
+** Allow raw data file to be written and to be read (with compression).
+**
+** Revision 1.6  2002/07/24 11:12:07  gerlof
+** Redesigned to ease porting to other UNIX-platforms.
+**
+** Revision 1.5  2002/07/11 09:15:53  root
+** *** empty log message ***
+**
+** Revision 1.4  2002/07/08 09:20:45  root
+** Bug solution: flag list overflow.
+**
+** Revision 1.3  2001/11/07 09:17:41  gerlof
+** Use /proc instead of /dev/kmem for process-level statistics.
+**
+** Revision 1.2  2001/10/04 13:03:15  gerlof
+** Separate kopen() function called i.s.o. implicit with first kmem-read
+**
+** Revision 1.1  2001/10/02 10:43:19  gerlof
+** Initial revision
+**
+*/
+
+static const char rcsid[] = "$Id: atop.c,v 1.49 2010/10/23 14:01:00 gerlof Exp $";
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <sys/utsname.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <regex.h>
+
+#include "atop.h"
+#include "acctproc.h"
+#include "ifprop.h"
+#include "photoproc.h"
+#include "photosyst.h"
+#include "showgeneric.h"
+#include "parseable.h"
+
+#define        allflags  "ab:cde:fghijklmnopqrstuvwxyz1ABCDEFGHIJKL:MNOP:QRSTUVWXYZ"
+#define        PROCCHUNK       100     /* process-entries for future expansion  */
+#define        MAXFL           64      /* maximum number of command-line flags  */
+
+/*
+** declaration of global variables
+*/
+struct utsname utsname;
+int            utsnodenamelen;
+time_t                 pretime;        /* timing info                          */
+time_t                 curtime;        /* timing info                          */
+unsigned long  interval = 10;
+unsigned long  sampcnt;
+char           screen;
+int            linelen  = 80;
+char           acctreason;     /* accounting not active (return val)   */
+char           rawname[RAWNAMESZ];
+char           rawreadflag;
+unsigned int   begintime, endtime;
+char           flaglist[MAXFL];
+char           deviatonly = 1;
+char           usecolors  = 1;  /* boolean: colors for high occupation  */
+char           threadview = 0;  /* boolean: show individual threads     */
+char           calcpss    = 0;  /* boolean: read/calculate process PSS  */
+
+unsigned short hertz;
+unsigned int   pagesize;
+int            osrel;
+int            osvers;
+int            ossub;
+
+int            supportflags;   /* supported features                   */
+char           **argvp;
+
+
+struct visualize vis = {generic_samp, generic_error,
+                       generic_end,  generic_usage};
+
+/*
+** argument values
+*/
+static char            awaittrigger;   /* boolean: awaiting trigger */
+static unsigned int    nsamples = 0xffffffff;
+static char            midnightflag;
+static char            rawwriteflag;
+
+/*
+** interpretation of defaults-file /etc/atoprc and $HOME/.atop
+*/
+static void            readrc(char *, int);
+
+void do_flags(char *, char *);
+void do_interval(char *, char *);
+void do_linelength(char *, char *);
+void do_username(char *, char *);
+void do_procname(char *, char *);
+void do_maxcpu(char *, char *);
+void do_maxdisk(char *, char *);
+void do_maxmdd(char *, char *);
+void do_maxlvm(char *, char *);
+void do_maxintf(char *, char *);
+void do_maxnfsm(char *, char *);
+void do_maxcont(char *, char *);
+void do_colinfo(char *, char *);
+void do_colalmost(char *, char *);
+void do_colcrit(char *, char *);
+void do_colthread(char *, char *);
+void do_ownsysprcline(char *, char *);
+void do_ownallcpuline(char *, char *);
+void do_ownindivcpuline(char *, char *);
+void do_owncplline(char *, char *);
+void do_ownmemline(char *, char *);
+void do_ownswpline(char *, char *);
+void do_ownpagline(char *, char *);
+void do_owndskline(char *, char *);
+void do_ownnettransportline(char *, char *);
+void do_ownnetnetline(char *, char *);
+void do_ownnetinterfaceline(char *, char *);
+void do_ownprocline(char *, char *);
+void do_cpucritperc(char *, char *);
+void do_memcritperc(char *, char *);
+void do_swpcritperc(char *, char *);
+void do_dskcritperc(char *, char *);
+void do_netcritperc(char *, char *);
+void do_swoutcritsec(char *, char *);
+void do_almostcrit(char *, char *);
+void do_atopsarflags(char *, char *);
+void do_pacctdir(char *, char *);
+
+static struct {
+       char    *tag;
+       void    (*func)(char *, char *);
+       int     sysonly;
+} manrc[] = {
+       {       "flags",                do_flags,               0, },
+       {       "interval",             do_interval,            0, },
+       {       "linelen",              do_linelength,          0, },
+       {       "username",             do_username,            0, },
+       {       "procname",             do_procname,            0, },
+       {       "maxlinecpu",           do_maxcpu,              0, },
+       {       "maxlinedisk",          do_maxdisk,             0, },
+       {       "maxlinemdd",           do_maxmdd,              0, },
+       {       "maxlinelvm",           do_maxlvm,              0, },
+       {       "maxlineintf",          do_maxintf,             0, },
+       {       "maxlinenfsm",          do_maxnfsm,             0, },
+       {       "maxlinecont",          do_maxcont,             0, },
+       {       "colorinfo",            do_colinfo,             0, },
+       {       "coloralmost",          do_colalmost,           0, },
+       {       "colorcritical",        do_colcrit,             0, },
+       {       "colorthread",          do_colthread,           0, },
+       {       "ownallcpuline",        do_ownallcpuline,       0, },
+       {       "ownonecpuline",        do_ownindivcpuline,     0, },
+       {       "owncplline",           do_owncplline,          0, },
+       {       "ownmemline",           do_ownmemline,          0, },
+       {       "ownswpline",           do_ownswpline,          0, },
+       {       "ownpagline",           do_ownpagline,          0, },
+       {       "owndskline",           do_owndskline,          0, },
+       {       "ownnettrline",         do_ownnettransportline, 0, },
+       {       "ownnetnetline",        do_ownnetnetline,       0, },
+       {       "ownnetifline",         do_ownnetinterfaceline, 0, },
+       {       "ownprocline",          do_ownprocline,         0, },
+       {       "ownsysprcline",        do_ownsysprcline,       0, },
+       {       "owndskline",           do_owndskline,          0, },
+       {       "cpucritperc",          do_cpucritperc,         0, },
+       {       "memcritperc",          do_memcritperc,         0, },
+       {       "swpcritperc",          do_swpcritperc,         0, },
+       {       "dskcritperc",          do_dskcritperc,         0, },
+       {       "netcritperc",          do_netcritperc,         0, },
+       {       "swoutcritsec",         do_swoutcritsec,        0, },
+       {       "almostcrit",           do_almostcrit,          0, },
+       {       "atopsarflags",         do_atopsarflags,        0, },
+       {       "pacctdir",             do_pacctdir,            1, },
+};
+
+/*
+** internal prototypes
+*/
+static void    engine(void);
+
+int
+main(int argc, char *argv[])
+{
+       register int    i;
+       int             c;
+       char            *p;
+       struct rlimit   rlim;
+
+       /*
+       ** since priviliged actions will be done later on, at this stage
+       ** the root-priviliges are dropped by switching effective user-id
+       ** to real user-id (security reasons)
+       */
+        if (! droprootprivs() )
+       {
+               fprintf(stderr, "not possible to drop root privs\n");
+                exit(42);
+       }
+
+       /*
+       ** preserve command arguments to allow restart of other version
+       */
+       argvp = argv;
+
+       /*
+       ** read defaults-files /etc/atoprc en $HOME/.atoprc (if any)
+       */
+       readrc("/etc/atoprc", 1);
+
+       if ( (p = getenv("HOME")) )
+       {
+               char path[1024];
+
+               snprintf(path, sizeof path, "%s/.atoprc", p);
+
+               readrc(path, 0);
+       }
+
+       /*
+       ** check if we are supposed to behave as 'atopsar'
+       ** i.e. system statistics only
+       */
+       if ( (p = strrchr(argv[0], '/')))
+               p++;
+       else
+               p = argv[0];
+
+       if ( memcmp(p, "atopsar", 7) == 0)
+               return atopsar(argc, argv);
+
+       /* 
+       ** interpret command-line arguments & flags 
+       */
+       if (argc > 1)
+       {
+               /* 
+               ** gather all flags for visualization-functions
+               **
+               ** generic flags will be handled here;
+               ** unrecognized flags are passed to the print-routines
+               */
+               i = 0;
+
+               while (i < MAXFL-1 && (c=getopt(argc, argv, allflags)) != EOF)
+               {
+                       switch (c)
+                       {
+                          case '?':            /* usage wanted ?             */
+                               prusage(argv[0]);
+                               break;
+
+                          case 'V':            /* version wanted ?           */
+                               printf("%s\n", getstrvers());
+                               exit(0);
+
+                          case 'w':            /* writing of raw data ?      */
+                               rawwriteflag++;
+                               if (optind >= argc)
+                                       prusage(argv[0]);
+
+                               strncpy(rawname, argv[optind++], RAWNAMESZ-1);
+                               vis.show_samp = rawwrite;
+                               break;
+
+                          case 'r':            /* reading of raw data ?      */
+                               if (optind < argc && *(argv[optind]) != '-')
+                                       strncpy(rawname, argv[optind++],
+                                                       RAWNAMESZ-1);
+
+                               rawreadflag++;
+                               break;
+
+                          case 'S':            /* midnight limit ?           */
+                               midnightflag++;
+                               break;
+
+                           case 'a':           /* all processes per sample ? */
+                               deviatonly = 0;
+                               break;
+
+                           case 'R':           /* all processes per sample ? */
+                               calcpss = 1;
+                               break;
+
+                           case 'b':           /* begin time ?               */
+                               if ( !hhmm2secs(optarg, &begintime) )
+                                       prusage(argv[0]);
+                               break;
+
+                           case 'e':           /* end   time ?               */
+                               if ( !hhmm2secs(optarg, &endtime) )
+                                       prusage(argv[0]);
+                               break;
+
+                           case 'P':           /* parseable output?          */
+                               if ( !parsedef(optarg) )
+                                       prusage(argv[0]);
+
+                               vis.show_samp = parseout;
+                               break;
+
+                           case 'L':           /* line length                */
+                               if ( !numeric(optarg) )
+                                       prusage(argv[0]);
+
+                               linelen = atoi(optarg);
+                               break;
+
+                          default:             /* gather other flags */
+                               flaglist[i++] = c;
+                       }
+               }
+
+               /*
+               ** get optional interval-value and optional number of samples   
+               */
+                       if (optind < argc && optind < MAXFL)
+               {
+                       if (!numeric(argv[optind]))
+                               prusage(argv[0]);
+       
+                       interval = atoi(argv[optind]);
+       
+                       optind++;
+       
+                       if (optind < argc)
+                       {
+                               if (!numeric(argv[optind]) )
+                                       prusage(argv[0]);
+
+                               if ( (nsamples = atoi(argv[optind])) < 1)
+                                       prusage(argv[0]);
+                       }
+               }
+       }
+
+       /*
+       ** determine the name of this node (without domain-name)
+       ** and the kernel-version
+       */
+       (void) uname(&utsname);
+
+       if ( (p = strchr(utsname.nodename, '.')) )
+               *p = '\0';
+
+       utsnodenamelen = strlen(utsname.nodename);
+
+       sscanf(utsname.release, "%d.%d.%d", &osrel, &osvers, &ossub);
+
+       /*
+       ** determine the clock rate and memory page size for this machine
+       */
+       hertz           = sysconf(_SC_CLK_TCK);
+       pagesize        = sysconf(_SC_PAGESIZE);
+
+       /*
+       ** check if raw data from a file must be viewed
+       */
+       if (rawreadflag)
+       {
+               rawread();
+               cleanstop(0);
+       }
+
+       /*
+       ** determine start-time for gathering current statistics
+       */
+       curtime = getboot() / hertz;
+
+       /*
+       ** be sure to be leader of an own process group when
+       ** running as a daemon (or at least: when not interactive);
+       ** needed for systemd
+       */
+       if (rawwriteflag)
+               (void) setpgid(0, 0);
+
+       /*
+       ** catch signals for proper close-down
+       */
+       signal(SIGHUP,  cleanstop);
+       signal(SIGTERM, cleanstop);
+
+       /*
+       ** regain the root-priviliges that we dropped at the beginning
+       ** to do some priviliged work
+       */
+       regainrootprivs();
+
+       /*
+       ** lock ATOP in memory to get reliable samples (also when
+       ** memory is low);
+       ** ignored if not running under superuser priviliges!
+       */
+       rlim.rlim_cur   = RLIM_INFINITY;
+       rlim.rlim_max   = RLIM_INFINITY;
+       (void) setrlimit(RLIMIT_MEMLOCK, &rlim);
+
+       (void) mlockall(MCL_CURRENT|MCL_FUTURE);
+
+       /*
+       ** increment CPU scheduling-priority to get reliable samples (also
+       ** during heavy CPU load);
+       ** ignored if not running under superuser priviliges!
+       */
+       if ( nice(-20) == -1)
+               ;
+
+       /*
+       ** switch-on the process-accounting mechanism to register the
+       ** (remaining) resource-usage by processes which have finished
+       */
+       acctreason = acctswon();
+
+       /*
+       ** determine properties (like speed) of all interfaces
+       */
+       initifprop();
+
+       /*
+       ** open socket to the IP layer to issue getsockopt() calls later on
+       */
+       netatop_ipopen();
+       
+       /*
+       ** since priviliged activities are finished now, there is no
+       ** need to keep running under root-priviliges, so switch
+       ** effective user-id to real user-id
+       */
+        if (! droprootprivs() )
+               cleanstop(42);
+
+       /*
+       ** start the engine now .....
+       */
+       engine();
+
+       cleanstop(0);
+
+       return 0;       /* never reached */
+}
+
+/*
+** The engine() drives the main-loop of the program
+*/
+static void
+engine(void)
+{
+       struct sigaction        sigact;
+       static time_t           timelimit;
+       void                    getusr1(int), getusr2(int);
+
+       /*
+       ** reserve space for system-level statistics
+       */
+       static struct sstat     *cursstat; /* current   */
+       static struct sstat     *presstat; /* previous  */
+       static struct sstat     *devsstat; /* deviation */
+       static struct sstat     *hlpsstat;
+
+       /*
+       ** reserve space for task-level statistics
+       */
+       static struct tstat     *curtpres;      /* current present list      */
+       static int               curtlen;       /* size of present list      */
+       struct tstat            *curpexit;      /* exited process list       */
+
+       static struct devtstat  devtstat;       /* deviation info            */
+
+       unsigned int            ntaskpres;      /* number of tasks present   */
+       unsigned int            nprocexit;      /* number of exited procs    */
+       unsigned int            nprocexitnet;   /* number of exited procs    */
+                                               /* via netatopd daemon       */
+
+       unsigned int            noverflow;
+
+       /*
+       ** initialization: allocate required memory dynamically
+       */
+       cursstat = calloc(1, sizeof(struct sstat));
+       presstat = calloc(1, sizeof(struct sstat));
+       devsstat = calloc(1, sizeof(struct sstat));
+
+       curtlen  = countprocs() * 3 / 2;        /* add 50% for threads */
+       curtpres = calloc(curtlen, sizeof(struct tstat));
+
+       ptrverify(cursstat, "Malloc failed for current sysstats\n");
+       ptrverify(presstat, "Malloc failed for prev    sysstats\n");
+       ptrverify(devsstat, "Malloc failed for deviate sysstats\n");
+       ptrverify(curtpres, "Malloc failed for %d procstats\n", curtlen);
+
+       /*
+       ** install the signal-handler for ALARM, USR1 and USR2 (triggers
+       * for the next sample)
+       */
+       memset(&sigact, 0, sizeof sigact);
+       sigact.sa_handler = getusr1;
+       sigaction(SIGUSR1, &sigact, (struct sigaction *)0);
+
+       memset(&sigact, 0, sizeof sigact);
+       sigact.sa_handler = getusr2;
+       sigaction(SIGUSR2, &sigact, (struct sigaction *)0);
+
+       memset(&sigact, 0, sizeof sigact);
+       sigact.sa_handler = getalarm;
+       sigaction(SIGALRM, &sigact, (struct sigaction *)0);
+
+       if (interval > 0)
+               alarm(interval);
+
+       if (midnightflag)
+       {
+               time_t          timenow = time(0);
+               struct tm       *tp = localtime(&timenow);
+
+               tp->tm_hour = 23;
+               tp->tm_min  = 59;
+               tp->tm_sec  = 59;
+
+               timelimit = mktime(tp);
+       }
+
+       /*
+       ** MAIN-LOOP:
+       **    - Wait for the requested number of seconds or for other trigger
+       **
+       **    - System-level counters
+       **              get current counters
+       **              calculate the differences with the previous sample
+       **
+       **    - Process-level counters
+       **              get current counters from running & exited processes
+       **              calculate the differences with the previous sample
+       **
+       **    - Call the print-function to visualize the differences
+       */
+       for (sampcnt=0; sampcnt < nsamples; sampcnt++)
+       {
+               char    lastcmd;
+
+               /*
+               ** if the limit-flag is specified:
+               **  check if the next sample is expected before midnight;
+               **  if not, stop atop now 
+               */
+               if (midnightflag && (curtime+interval) > timelimit)
+                       break;
+
+               /*
+               ** wait for alarm-signal to arrive (except first sample)
+               ** or wait for SIGUSR1/SIGUSR2
+               */
+               if (sampcnt > 0 && awaittrigger)
+                       pause();
+
+               awaittrigger = 1;
+
+               /*
+               ** gather time info for this sample
+               */
+               pretime  = curtime;
+               curtime  = time(0);             /* seconds since 1-1-1970 */
+
+               /*
+               ** take a snapshot of the current system-level statistics 
+               ** and calculate the deviations (i.e. calculate the activity
+               ** during the last sample)
+               */
+               hlpsstat = cursstat;    /* swap current/prev. stats */
+               cursstat = presstat;
+               presstat = hlpsstat;
+
+               photosyst(cursstat);    /* obtain new counters      */
+
+               deviatsyst(cursstat, presstat, devsstat,
+                               curtime-pretime > 0 ? curtime-pretime : 1);
+
+               /*
+               ** take a snapshot of the current task-level statistics 
+               ** and calculate the deviations (i.e. calculate the activity
+               ** during the last sample)
+               **
+               ** first register active tasks
+               **  --> atop malloc's a minimal amount of space which is
+               **      only extended when needed
+               */
+               memset(curtpres, 0, curtlen * sizeof(struct tstat));
+
+               while ( (ntaskpres = photoproc(curtpres, curtlen)) == curtlen)
+               {
+                       curtlen += PROCCHUNK;
+
+                       curtpres = realloc(curtpres,
+                                       curtlen * sizeof(struct tstat));
+
+                       ptrverify(curtpres,
+                                 "Realloc failed for %d tasks\n", curtlen);
+
+                       memset(curtpres, 0, curtlen * sizeof(struct tstat));
+               }
+
+               /*
+               ** register processes that exited during last sample;
+               ** first determine how many processes exited
+               **
+               ** the number of exited processes is limited to avoid
+               ** that atop explodes in memory and introduces OOM killing
+               */
+               nprocexit = acctprocnt();       /* number of exited processes */
+
+               if (nprocexit > MAXACCTPROCS)
+               {
+                       noverflow = nprocexit - MAXACCTPROCS;
+                       nprocexit = MAXACCTPROCS;
+               }
+               else
+                       noverflow = 0;
+
+               /*
+               ** determine how many processes have been exited
+               ** for the netatop module (only processes that have
+               ** used the network)
+               */
+               if (nprocexit > 0 && (supportflags & NETATOPD))
+                       nprocexitnet = netatop_exitstore();
+               else
+                       nprocexitnet = 0;
+
+               /*
+               ** reserve space for the exited processes and read them
+               */
+               if (nprocexit > 0)
+               {
+                       curpexit = malloc(nprocexit * sizeof(struct tstat));
+
+                       ptrverify(curpexit,
+                                 "Malloc failed for %d exited processes\n",
+                                 nprocexit);
+
+                       memset(curpexit, 0, nprocexit * sizeof(struct tstat));
+
+                       nprocexit = acctphotoproc(curpexit, nprocexit);
+
+                       /*
+                       ** reposition offset in accounting file when not
+                       ** all exited processes have been read (i.e. skip
+                       ** those processes)
+                       */
+                       if (noverflow)
+                               acctrepos(noverflow);
+               }
+               else
+               {
+                       curpexit    = NULL;
+               }
+
+               /*
+               ** calculate deviations
+               */
+               deviattask(curtpres,  ntaskpres, curpexit,  nprocexit,
+                                     &devtstat, devsstat);
+
+               /*
+               ** activate the installed print-function to visualize
+               ** the deviations
+               */
+               lastcmd = (vis.show_samp)( curtime,
+                                    curtime-pretime > 0 ? curtime-pretime : 1,
+                                    &devtstat, devsstat, 
+                                    nprocexit, noverflow, sampcnt==0);
+
+               /*
+               ** release dynamically allocated memory
+               */
+               if (nprocexit > 0)
+                       free(curpexit);
+
+               if (nprocexitnet > 0)
+                       netatop_exiterase();
+
+               if (lastcmd == 'r')     /* reset requested ? */
+               {
+                       sampcnt = -1;
+
+                       curtime = getboot() / hertz;    // reset current time
+
+                       /* set current (will be 'previous') counters to 0 */
+                       memset(cursstat, 0,           sizeof(struct sstat));
+                       memset(curtpres, 0, curtlen * sizeof(struct tstat));
+
+                       /* remove all tasks in database */
+                       pdb_makeresidue();
+                       pdb_cleanresidue();
+               }
+       } /* end of main-loop */
+}
+
+/*
+** print usage of this command
+*/
+void
+prusage(char *myname)
+{
+       printf("Usage: %s [-flags] [interval [samples]]\n",
+                                       myname);
+       printf("\t\tor\n");
+       printf("Usage: %s -w  file  [-S] [-%c] [interval [samples]]\n",
+                                       myname, MALLPROC);
+       printf("       %s -r [file] [-b hh:mm] [-e hh:mm] [-flags]\n",
+                                       myname);
+       printf("\n");
+       printf("\tgeneric flags:\n");
+       printf("\t  -%c  show version information\n", MVERSION);
+       printf("\t  -%c  show or log all processes (i.s.o. active processes "
+                       "only)\n", MALLPROC);
+       printf("\t  -%c  calculate proportional set size (PSS) per process\n", 
+                       MCALCPSS);
+       printf("\t  -P  generate parseable output for specified label(s)\n");
+       printf("\t  -L  alternate line length (default 80) in case of "
+                       "non-screen output\n");
+
+       (*vis.show_usage)();
+
+       printf("\n");
+       printf("\tspecific flags for raw logfiles:\n");
+       printf("\t  -w  write raw data to   file (compressed)\n");
+       printf("\t  -r  read  raw data from file (compressed)\n");
+       printf("\t      special file: y[y...] for yesterday (repeated)\n");
+       printf("\t  -S  finish atop automatically before midnight "
+                       "(i.s.o. #samples)\n");
+       printf("\t  -b  begin showing data from specified time\n");
+       printf("\t  -e  finish showing data after specified time\n");
+       printf("\n");
+       printf("\tinterval: number of seconds   (minimum 0)\n");
+       printf("\tsamples:  number of intervals (minimum 1)\n");
+       printf("\n");
+       printf("If the interval-value is zero, a new sample can be\n");
+       printf("forced manually by sending signal USR1"
+                       " (kill -USR1 pid_atop)\n");
+       printf("or with the keystroke '%c' in interactive mode.\n", MSAMPNEXT);
+       printf("\n");
+       printf("Please refer to the man-page of 'atop' for more details.\n");
+
+       cleanstop(1);
+}
+
+/*
+** handler for ALRM-signal
+*/
+void
+getalarm(int sig)
+{
+       awaittrigger=0;
+
+       if (interval > 0)
+               alarm(interval);        /* restart the timer */
+}
+
+/*
+** handler for USR1-signal
+*/
+void
+getusr1(int sig)
+{
+       awaittrigger=0;
+}
+
+/*
+** handler for USR2-signal
+*/
+void
+getusr2(int sig)
+{
+       awaittrigger=0;
+       nsamples = sampcnt;     // force stop after next sample
+}
+
+/*
+** functions to handle a particular tag in the .atoprc file
+*/
+extern int get_posval(char *name, char *val);
+
+void
+do_interval(char *name, char *val)
+{
+       interval = get_posval(name, val);
+}
+
+void
+do_linelength(char *name, char *val)
+{
+       linelen = get_posval(name, val);
+}
+
+/*
+** read RC-file and modify defaults accordingly
+*/
+static void
+readrc(char *path, int syslevel)
+{
+       int     i, nr, line=0, errorcnt = 0;
+
+       /*
+       ** check if this file is readable with the user's
+       ** *real uid/gid* with syscall access()
+       */
+       if ( access(path, R_OK) == 0)
+       {
+               FILE    *fp;
+               char    linebuf[256], tagname[20], tagvalue[256];
+
+               fp = fopen(path, "r");
+
+               while ( fgets(linebuf, sizeof linebuf, fp) )
+               {
+                       line++;
+
+                       i = strlen(linebuf);
+
+                       if (i > 0 && linebuf[i-1] == '\n')
+                               linebuf[i-1] = 0;
+
+                       nr = sscanf(linebuf, "%19s %255[^#]",
+                                               tagname, tagvalue);
+
+                       switch (nr)
+                       {
+                          case 0:
+                               continue;
+
+                          case 1:
+                               if (tagname[0] == '#')
+                                       continue;
+
+                               fprintf(stderr,
+                                       "%s: syntax error line "
+                                       "%d (no value specified)\n",
+                                       path, line);
+
+                               cleanstop(1);
+                               break;          /* not reached */
+
+                          default:
+                               if (tagname[0] == '#')
+                                       continue;
+                               
+                               if (tagvalue[0] != '#')
+                                       break;
+
+                               fprintf(stderr,
+                                       "%s: syntax error line "
+                                       "%d (no value specified)\n",
+                                       path, line);
+
+                               cleanstop(1);
+                       }
+
+                       /*
+                       ** tag name and tag value found
+                       ** try to recognize tag name
+                       */
+                       for (i=0; i < sizeof manrc/sizeof manrc[0]; i++)
+                       {
+                               if ( strcmp(tagname, manrc[i].tag) == 0)
+                               {
+                                       if (manrc[i].sysonly && !syslevel)
+                                       {
+                                               fprintf(stderr,
+                                                  "%s: warning at line %2d "
+                                                  "- tag name %s not allowed "
+                                                  "in private atoprc\n",
+                                                       path, line, tagname);
+
+                                               errorcnt++;
+                                               break;
+                                       }
+
+                                       manrc[i].func(tagname, tagvalue);
+                                       break;
+                               }
+                       }
+
+                       /*
+                       ** tag name not recognized
+                       */
+                       if (i == sizeof manrc/sizeof manrc[0])
+                       {
+                               fprintf(stderr,
+                                       "%s: warning at line %2d "
+                                       "- tag name %s not valid\n",
+                                       path, line, tagname);
+
+                               errorcnt++;
+                       }
+               }
+
+               if (errorcnt)
+                       sleep(2);
+
+               fclose(fp);
+       }
+}
diff --git a/sources/atop.cronsystemd b/sources/atop.cronsystemd
new file mode 100644 (file)
index 0000000..61ef090
--- /dev/null
@@ -0,0 +1,2 @@
+# daily restart of atop at midnight
+0 0 * * * root systemctl restart atop
diff --git a/sources/atop.cronsysv b/sources/atop.cronsysv
new file mode 100644 (file)
index 0000000..da27499
--- /dev/null
@@ -0,0 +1,2 @@
+# daily restart of atop at midnight
+0 0 * * * root /usr/share/atop/atop.daily&
diff --git a/sources/atop.daily b/sources/atop.daily
new file mode 100755 (executable)
index 0000000..32a94d8
--- /dev/null
@@ -0,0 +1,41 @@
+#!/bin/bash
+
+CURDAY=`date +%Y%m%d`
+LOGPATH=/var/log/atop
+BINPATH=/usr/bin
+PIDFILE=/var/run/atop.pid
+INTERVAL=600                           # interval 10 minutes
+
+# verify if atop still runs for daily logging
+#
+if [ -e $PIDFILE ] && ps -p `cat $PIDFILE` | grep 'atop$' > /dev/null
+then
+       kill -USR2 `cat $PIDFILE`       # final sample and terminate
+
+       CNT=0
+
+       while ps -p `cat $PIDFILE` > /dev/null
+       do
+               let CNT+=1
+
+               if [ $CNT -gt 5 ]
+               then
+                       break;
+               fi
+
+               sleep 1
+       done
+
+       rm $PIDFILE
+fi
+
+# delete logfiles older than four weeks
+# start a child shell that activates another child shell in
+# the background to avoid a zombie
+#
+( (sleep 3; find $LOGPATH -name 'atop_*' -mtime +28 -exec rm {} \;)& )
+
+# activate atop with interval of 10 minutes, replacing the current shell
+#
+echo $$ > $PIDFILE
+exec $BINPATH/atop -R -w $LOGPATH/atop_$CURDAY $INTERVAL > $LOGPATH/daily.log 2>&1
diff --git a/sources/atop.h b/sources/atop.h
new file mode 100644 (file)
index 0000000..e3cd73f
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+** ATOP - System & Process Monitor
+**
+** The program 'atop' offers the possibility to view the activity of
+** the system on system-level as well as process-level.
+**
+** Include-file describing miscellaneous constants and function-prototypes.
+** ================================================================
+** Author:      Gerlof Langeveld
+** E-mail:      gerlof.langeveld@atoptool.nl
+** Date:        November 1996
+** LINUX-port:  June 2000
+**
+** This program is free software; you can redistribute it and/or modify it
+** under the terms of the GNU General Public License as published by the
+** Free Software Foundation; either version 2, or (at your option) any
+** later version.
+**
+** This program is distributed in the hope that it will be useful, but
+** WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+** See the GNU General Public License for more details.
+*/
+#define        EQ              0
+#define SECSDAY                86400
+#define RAWNAMESZ      256
+
+/*
+** memory-size formatting possibilities
+*/
+#define        ANYFORMAT       0
+#define        KBFORMAT        1
+#define        MBFORMAT        2
+#define        GBFORMAT        3
+#define        TBFORMAT        4
+#define        PBFORMAT        5
+#define        OVFORMAT        9
+
+typedef        long long       count_t;
+
+struct tstat;
+struct devtstat;
+struct sstat;
+struct netpertask;
+
+/* 
+** miscellaneous flags
+*/
+#define RRBOOT         0x0001
+#define RRLAST         0x0002
+#define RRNETATOP      0x0004
+#define RRNETATOPD     0x0008
+#define RRACCTACTIVE   0x0010
+#define RRIOSTAT       0x0020
+#define RRDOCKSTAT     0x0040
+
+struct visualize {
+       char    (*show_samp)  (time_t, int,
+                       struct devtstat *, struct sstat *,
+                       int, unsigned int, char);
+       void    (*show_error) (const char *, ...);
+       void    (*show_end)   (void);
+       void    (*show_usage) (void);
+};
+
+/*
+** external values
+*/
+extern struct utsname   utsname;
+extern int              utsnodenamelen;
+extern time_t          pretime;
+extern time_t          curtime;
+extern unsigned long    interval;
+extern unsigned long   sampcnt;
+extern char            screen;
+extern int             linelen;
+extern char            acctreason;
+extern char            deviatonly;
+extern char            usecolors;
+extern char            threadview;
+extern char            calcpss;
+extern char            rawname[];
+extern char            rawreadflag;
+extern unsigned int    begintime, endtime;
+extern char            flaglist[];
+extern struct visualize vis;
+
+extern int             osrel;
+extern int             osvers;
+extern int             ossub;
+
+extern unsigned short  hertz;
+extern unsigned int    pagesize;
+
+extern int             supportflags;
+
+extern int             cpubadness;
+extern int             membadness;
+extern int             swpbadness;
+extern int             dskbadness;
+extern int             netbadness;
+extern int             pagbadness;
+extern int             almostcrit;
+
+/*
+** bit-values for supportflags
+*/
+#define        ACCTACTIVE      0x00000001
+#define        IOSTAT          0x00000004
+#define        NETATOP         0x00000010
+#define        NETATOPD        0x00000020
+#define        DOCKSTAT        0x00000040
+
+/*
+** in rawlog file, the four least significant bits 
+** are moved to the per-sample flags and therefor dummy
+** in the support flags of the general header
+*/
+#define        RAWLOGNG        (ACCTACTIVE|IOSTAT|NETATOP|NETATOPD)
+
+/*
+** structure containing the start-addresses of functions for visualization
+*/
+char           generic_samp (time_t, int,
+                           struct devtstat *, struct sstat *,
+                           int, unsigned int, char);
+void           generic_error(const char *, ...);
+void           generic_end  (void);
+void           generic_usage(void);
+
+/*
+** miscellaneous prototypes
+*/
+int            atopsar(int, char *[]);
+char                   *convtime(time_t, char *);
+char                   *convdate(time_t, char *);
+int            hhmm2secs(char *, unsigned int *);
+int            daysecs(time_t);
+
+char                   *val2valstr(count_t, char *, int, int, int);
+char                   *val2memstr(count_t, char *, int, int, int);
+char           *val2cpustr(count_t, char *);
+char            *val2Hzstr(count_t, char *);
+int             val2elapstr(int, char *);
+
+int            compcpu(const void *, const void *);
+int            compdsk(const void *, const void *);
+int            compmem(const void *, const void *);
+int            compnet(const void *, const void *);
+int            compusr(const void *, const void *);
+int            compnam(const void *, const void *);
+int            compcon(const void *, const void *);
+
+int            cpucompar (const void *, const void *);
+int            diskcompar(const void *, const void *);
+int            intfcompar(const void *, const void *);
+int            nfsmcompar(const void *, const void *);
+int            contcompar(const void *, const void *);
+
+count_t                subcount(count_t, count_t);
+void           rawread(void);
+char           rawwrite (time_t, int,
+                           struct devtstat *, struct sstat *,
+                           int, unsigned int, char);
+
+int            numeric(char *);
+void           getalarm(int);
+unsigned long long     getboot(void);
+char           *getstrvers(void);
+unsigned short         getnumvers(void);
+void           ptrverify(const void *, const char *, ...);
+void           cleanstop(int);
+void           prusage(char *);
+
+int            droprootprivs(void);
+void           regainrootprivs(void);
+FILE           *fopen_tryroot(const char *, const char *);
+
+void           netatop_ipopen(void);
+void           netatop_probe(void);
+void           netatop_signoff(void);
+void           netatop_gettask(pid_t, char, struct tstat *);
+unsigned int   netatop_exitstore(void);
+void           netatop_exiterase(void);
+void           netatop_exithash(char);
+void           netatop_exitfind(unsigned long, struct tstat *, struct tstat *);
diff --git a/sources/atop.init b/sources/atop.init
new file mode 100755 (executable)
index 0000000..ea545b8
--- /dev/null
@@ -0,0 +1,81 @@
+#!/bin/sh
+#
+# atop         Startup script for the Atop process logging in background
+#
+# chkconfig: 2345 96 4
+# description: Advanced system and process activity monitor
+#
+### BEGIN INIT INFO
+# Provides:             atop
+# Required-Start:      $local_fs
+# Required-Stop:       $local_fs
+# Default-Start:       2 3 4 5
+# Default-Stop:                0 1 6
+# Short-Description: Advanced system and process activity monitor
+# Description: Advanced system and process activity monitor
+### END INIT INFO
+
+# Check existance of binaries 
+[ -f /usr/bin/atop ] || exit 0
+
+PIDFILE=/var/run/atop.pid
+RETVAL=0
+
+# See how we were called.
+case "$1" in
+  start)
+       # Check if atop runs already
+       #
+       if [ -e $PIDFILE ] && ps -p `cat $PIDFILE` | grep 'atop$' > /dev/null
+       then
+               :
+       else
+               # Start atop
+               /usr/share/atop/atop.daily&
+       fi
+       touch /var/lock/subsys/atop
+       ;;
+
+  stop)
+       # Check if atop runs
+       #
+       if [ -e $PIDFILE ] && ps -p `cat $PIDFILE` | grep 'atop$' > /dev/null
+       then
+               kill -USR2 `cat $PIDFILE`       # final sample and terminate
+
+               CNT=0
+
+               while ps -p `cat $PIDFILE` > /dev/null
+               do
+                       let CNT+=1
+
+                       if [ $CNT -gt 5 ]
+                       then
+                               break;
+                       fi
+
+                       sleep 1
+               done
+
+               rm $PIDFILE
+       fi
+       rm /var/lock/subsys/atop
+       ;;
+
+  status)
+       ;;
+
+  reload)
+       /usr/share/atop/atop.daily&
+       ;;
+
+  restart)
+       /usr/share/atop/atop.daily&
+       ;;
+
+  *)
+       echo "Usage: $0 [start|stop|status|reload|restart]"
+       exit 1
+esac
+
+exit $RETVAL
diff --git a/sources/atop.service b/sources/atop.service
new file mode 100644 (file)
index 0000000..4d07c57
--- /dev/null
@@ -0,0 +1,12 @@
+[Unit]
+Description=Atop advanced performance monitor
+Documentation=man:atop(1)
+
+[Service]
+Type=simple
+ExecStart=/usr/share/atop/atop.daily
+KillSignal=SIGUSR2
+#ExecStopPost=/usr/bin/sleep 3
+
+[Install]
+WantedBy=multi-user.target
diff --git a/sources/atopacct.init b/sources/atopacct.init
new file mode 100755 (executable)
index 0000000..1aea1b6
--- /dev/null
@@ -0,0 +1,86 @@
+#!/bin/sh
+#
+# atopacctd    Startup script for the atopacctd daemon 
+#
+# chkconfig: 2345 91 9
+# description: Process accounting control
+#
+### BEGIN INIT INFO
+# Provides:             atopacct
+# Required-Start:      $local_fs
+# Required-Stop:       $local_fs
+# Default-Start:       2 3 4 5
+# Default-Stop:                0 1 6
+# Short-Description: This daemon switches on process accounting and
+#                    transfers the process accounting records 'realtime'
+#                    to small shadow files to avoid huge disk space usage
+# Description: Process accounting control
+### END INIT INFO
+
+# Check existance of binaries 
+[ -f /usr/sbin/atopacctd ] || exit 0
+
+RETVAL=0
+
+# See how we were called.
+case "$1" in
+  start)
+       # Check if process accounting already in use via psacct
+       #
+       for PACCTFILE in /var/account/pacct /var/log/pacct
+       do
+               if [ -f "$PACCTFILE" ]  # file exists?
+               then
+                       BEFORSIZE=$(ls -lL "$PACCTFILE" | awk '{print $5}')
+                       AFTERSIZE=$(ls -lL "$PACCTFILE" | awk '{print $5}')
+
+                       # verify if accounting file grows, so is in use
+                       #
+                       if [ $BEFORSIZE -lt $AFTERSIZE ]
+                       then
+                          echo Process accounting already used by psacct! >&2
+                          exit $RETVAL         # do not start atopacctd
+                       fi
+               fi
+       done
+
+
+       # Check if atopacctd runs already
+       #
+       if ps -e | grep -q atopacctd$
+       then
+               :
+       else
+               # Start atopacctd
+               rm -rf /var/run/pacct_shadow.d 2> /dev/null
+               /usr/sbin/atopacctd
+       fi
+
+       touch /var/lock/subsys/atopacctd
+       ;;
+
+  stop)
+       # Check if atopacctd runs
+       #
+       if ps -e | grep -q atopacctd$
+       then
+               kill $(ps -e | grep atopacctd$ | sed 's/^ *//' | cut -d' ' -f1)
+       fi
+       rm /var/lock/subsys/atopacctd
+       ;;
+
+  status)
+       ;;
+
+  reload)
+       ;;
+
+  restart)
+       ;;
+
+  *)
+       echo "Usage: $0 [start|stop]"
+       exit 1
+esac
+
+exit $RETVAL
diff --git a/sources/atopacct.service b/sources/atopacct.service
new file mode 100644 (file)
index 0000000..1f51ec4
--- /dev/null
@@ -0,0 +1,14 @@
+[Unit]
+Description=Atop process accounting daemon
+Documentation=man:atopacctd(8)
+Conflicts=psacct.service
+After=syslog.target
+Before=atop.service
+
+[Service]
+Type=forking
+PIDFile=/var/run/atopacctd.pid
+ExecStart=/usr/sbin/atopacctd
+
+[Install]
+WantedBy=multi-user.target
diff --git a/sources/atopacctd.c b/sources/atopacctd.c
new file mode 100644 (file)
index 0000000..fc4b3d5
--- /dev/null
@@ -0,0 +1,1040 @@
+/*
+** The atopacctd daemon switches on the process accounting feature
+** in the kernel and let the process accounting records be written to
+** a file, called the source file. After process accounting is active,
+** the atopacctd daemon transfers every process accounting record that
+** is available in the source file to a shadow file.
+** Client processes (like atop) can read the shadow file instead of
+** the original process accounting source file.
+**
+** This approach has the following advantages:
+**
+** - The atopacctd daemon keeps the source file to a limited size
+**   by truncating it regularly.
+**
+** - The atopacct daemon takes care that a shadow file has a limited size.
+**   As soon as the current shadow file reaches its maximum size, the
+**   atopacctd daemon creates a subsequent shadow file. For this reason,
+**   the name of a shadow file contains a sequence number.
+**   Shadow files that are not used by client processes any more, are
+**   automatically removed by the atopacctd daemon.
+**
+** - When no client processes are active (any more), all shadow files
+**   will be deleted and no records will be transferred to shadow files
+**   any more. As soon as at least one client is activated again, the
+**   atopacctd daemon start writing shadow files again.
+**
+** The directory /var/run is used as the default top-directory. An
+** alternative top-directory can be specified as command line argument
+** (in that case, also modify /etc/atoprc to inform atop as a client).
+** Below this top-directory the source file pacct_source is created and
+** the subdirectory pacct_shadow.d as a 'container' for the shadow files.
+** ----------------------------------------------------------------------
+** Copyright (C) 2014    Gerlof Langeveld (gerlof.langeveld@atoptool.nl)
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License version 2 as
+** published by the Free Software Foundation.
+*/
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <string.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/mman.h>
+#include <sys/statvfs.h>
+#include <sys/wait.h>
+
+#include "atop.h"
+#include "photoproc.h"
+#include "acctproc.h"
+#include "version.h"
+#include "atopacctd.h"
+
+#define        RETRYCNT        10      // # retries to read account record
+#define        RETRYMS         25      // timeout (millisec) to read account record
+#define        NORECINTERVAL   3600    // no-record-available interval (seconds)
+
+#define PACCTSEC       3       // timeout (sec) to retry switch on pacct
+#define POLLSEC                1       // timeout (sec) when NETLINK fails
+
+#define GCINTERVAL      60      // garbage collection interval (seconds)
+
+/*
+** Semaphore-handling
+**
+** Two semaphore groups are created with one semaphore each.
+**
+** The private semaphore (group) specifies the number of atopacctd processes
+** running (to be sure that only one daemon is active at the time).
+**
+** The public semaphore (group) reflects the number of processes using
+** the process accounting shadow files, i.e. the number of clients. This
+** semaphore starts at a high value and should be decremented whenever a
+** client starts using the shadow files and incremented again whenever that
+** client terminates.
+*/
+static int             semprv;
+static int             sempub;
+#define SEMTOTAL        100
+#define        NUMCLIENTS      (SEMTOTAL - semctl(sempub, 0, GETVAL, 0))
+
+static char            atopacctdversion[] = ATOPVERS;
+static char            atopacctddate[]    = ATOPDATE;
+
+static unsigned long   maxshadowrec = MAXSHADOWREC;
+static char            *pacctdir    = PACCTDIR;
+
+static char            cleanup_and_go = 0;
+
+/*
+** function prototypes
+*/
+static int     awaitprocterm(int, int, int, char *, int *,
+                               unsigned long *, unsigned long *);
+static int     swonpacct(int, char *);
+static int     createshadow(long);
+static int     pass2shadow(int, char *, int);
+static void    gcshadows(unsigned long *, unsigned long);
+static void    setcurrent(long);
+static int     acctsize(struct acct *);
+static void    cleanup(int);
+
+
+int            netlink_open(void);             // from netlink.c
+int            netlink_recv(int, int);         // from netlink.c
+
+
+int
+main(int argc, char *argv[])
+{
+       int                     i, nfd, afd, sfd;
+       int                     parentpid;
+       struct stat             dirstat;
+       struct rlimit           rlim;
+       FILE                    *pidf;
+
+       struct sembuf           semincr = {0, +1, SEM_UNDO};
+
+       char                    shadowdir[128], shadowpath[128];
+       char                    accountpath[128];
+       unsigned long           oldshadow = 0, curshadow = 0;
+       int                     shadowbusy = 0;
+       time_t                  gclast = time(0);
+
+       struct sigaction        sigcleanup;
+
+       /*
+       ** argument passed?
+       */
+       if (argc == 2)
+       {
+               /*
+               ** version number required (flag -v or -V)?
+               */
+               if (*argv[1] == '-')    // flag?
+               {
+                       if ( *(argv[1]+1) == 'v' || *(argv[1]+1) == 'V')
+                       {
+                               printf("%s  <gerlof.langeveld@atoptool.nl>\n",
+                                                               getstrvers());
+                               return 0;
+                       }
+                       else
+                       {
+                               fprintf(stderr,
+                                       "Usage: atopacctd [-v|topdirectory]\n"
+                                       "Default topdirectory: %s\n", PACCTDIR);
+                               exit(1);
+                       }
+               }
+
+               /*
+               ** if first argument is not a flag, it should be the name
+               ** of an alternative top directory (to be validated later on)
+               */
+               pacctdir = argv[1];
+       }
+       else
+       {
+               if (argc != 1)
+               {
+                       fprintf(stderr,
+                               "Usage: atopacctd [-v|topdirectory]\n"
+                               "Default topdirectory: %s\n", PACCTDIR);
+                       exit(1);
+               }
+       }
+
+       /*
+       ** verify if we are running with the right privileges
+       */
+       if (geteuid() != 0)
+       {
+               fprintf(stderr, "Root privileges are needed!\n");
+               exit(1);
+       }
+
+       /*
+       ** verify that the top directory is not world-writable
+       ** and owned by root
+       */
+       if ( stat(pacctdir, &dirstat) == -1 )
+       {
+               perror(pacctdir);
+               fprintf(stderr, "Usage: atopacctd [-v|topdirectory]\n"
+                               "Default topdirectory: %s\n", PACCTDIR);
+               exit(2);
+       }
+
+       if (! S_ISDIR(dirstat.st_mode) )
+       {
+               fprintf(stderr, "atopacctd: %s is not a directory\n", pacctdir);
+               exit(2);
+       }
+
+       if (dirstat.st_uid != 0)
+       {
+               fprintf(stderr,
+                       "atopacctd: directory %s must be owned by root\n",
+                       pacctdir);
+               exit(2);
+       }
+
+       if (dirstat.st_mode & (S_IWGRP|S_IWOTH))
+       {
+               fprintf(stderr,
+                       "atopacctd: directory %s may not be writable "
+                       "for group/others\n", pacctdir);
+               exit(2);
+       }
+
+       /*
+       ** create the semaphore groups and initialize the semaphores;
+       ** if the private semaphore already exists, verify if another
+       ** atopacctd daemon is already running
+       */
+       if ( (semprv = semget(PACCTPRVKEY, 0, 0)) >= 0) // exists?
+       {
+               if ( semctl(semprv, 0, GETVAL, 0) > 0)
+               {
+                       fprintf(stderr, "atopacctd is already running!\n");
+                       exit(3);
+               }
+       }
+       else
+       {
+               if ( (semprv = semget(PACCTPRVKEY, 1,
+                                       0600|IPC_CREAT|IPC_EXCL)) >= 0)
+               {
+                       (void) semctl(semprv, 0, SETVAL, 0);
+               }
+               else
+               {
+                       perror("cannot create private semaphore");
+                       exit(3);
+               }
+       }
+
+       if ( (sempub = semget(PACCTPUBKEY, 0, 0)) == -1)        // not existing?
+       {
+               if ( (sempub = semget(PACCTPUBKEY, 1,
+                                               0666|IPC_CREAT|IPC_EXCL)) >= 0)
+               {
+                       (void) semctl(sempub, 0, SETVAL, SEMTOTAL);
+               }
+               else
+               {
+                       perror("cannot create public semaphore");
+                       exit(3);
+               }
+       }
+
+       /*
+       ** prepare cleanup signal handler
+       */
+       memset(&sigcleanup, 0, sizeof sigcleanup);
+       sigcleanup.sa_handler   = cleanup;
+       sigemptyset(&sigcleanup.sa_mask);
+
+       /*
+       ** daemonize this process
+       ** i.e. be sure that the daemon is no session leader (any more)
+       ** and get rid of a possible bad context that might have been
+       ** inherited from ancestors
+       */
+       parentpid = getpid();           // to be killed when initialized
+       (void) sigaction(SIGTERM, &sigcleanup, (struct sigaction *)0);
+
+       if ( fork() )                   // implicitly switch to background
+       {
+               /*
+               ** parent after forking first child:
+               ** wait for signal 15 from child before terminating
+               ** because systemd expects parent to terminate whenever
+               ** service is up and running
+               */
+               pause();                // wait for signal from child
+               exit(0);                // finish parent
+       }
+
+       setsid();                       // become session leader to lose ctty
+
+       if ( fork() )                   // finish parent; continue in child
+               exit(0);                // --> no session leader, no ctty
+
+       getrlimit(RLIMIT_NOFILE, &rlim);
+
+       for (i=0; i < rlim.rlim_cur; i++)       // close all files, but
+       {
+               if (i != 2)                     // do not close stderr 
+                       close(i);
+       }
+
+       umask(022);
+
+       (void) chdir("/tmp");                   // go to a safe place
+
+       /*
+       ** increase semaphore to define that atopacctd is running
+       */
+       if ( semop(semprv, &semincr, 1) == -1)
+               {
+               perror("cannot increment private semaphore");
+               kill(parentpid, SIGTERM);
+               exit(4);
+       }
+
+       /*
+       ** create source accounting file to which the kernel can write
+       ** its records
+       */
+       snprintf(accountpath, sizeof accountpath, "%s/%s", pacctdir, PACCTORIG);
+
+       (void) unlink(accountpath);
+
+       if ( (afd = creat(accountpath, 0600)) == -1)
+               {
+               perror(accountpath);
+               kill(parentpid, SIGTERM);
+               exit(5);
+       }
+
+       (void) close(afd);
+
+       /*
+       ** open the accounting file for read
+       */
+       if ( (afd = open(accountpath, O_RDONLY)) == -1)
+               {
+               perror(accountpath);
+               kill(parentpid, SIGTERM);
+               exit(5);
+       }
+
+       /*
+       ** create directory to store the shadow files
+       */
+       snprintf(shadowdir, sizeof shadowdir, "%s/%s", pacctdir, PACCTSHADOWD);
+
+       if ( mkdir(shadowdir, 0755) == -1)
+               {
+               perror(shadowdir);
+               kill(parentpid, SIGTERM);
+               exit(5);
+       }
+
+       sfd = createshadow(curshadow);
+       setcurrent(curshadow);
+
+       /*
+       ** open syslog interface 
+       */
+       openlog("atopacctd", LOG_PID, LOG_DAEMON);
+       syslog(LOG_INFO, "%s  <gerlof.langeveld@atoptool.nl>", getstrvers());
+
+       /*
+       ** raise priority (be sure the nice value becomes -20,
+       ** independent of the current nice value)
+       */
+       (void) nice(-39);
+
+       /*
+       ** connect to NETLINK socket of kernel to be triggered
+       ** when processes have finished
+       */
+       if ( (nfd = netlink_open()) == -1)
+       {
+               (void) unlink(accountpath);
+               kill(parentpid, SIGTERM);
+               exit(5);
+       }
+
+       /*
+       ** switch on accounting - inital
+       */
+       if ( swonpacct(afd, accountpath) == -1)
+       {
+               (void) unlink(accountpath);
+               kill(parentpid, SIGTERM);
+               exit(6);
+       }
+
+       syslog(LOG_INFO, "accounting to %s", accountpath);
+
+       /*
+       ** signal handling
+       */
+       (void) signal(SIGHUP, SIG_IGN);
+
+       (void) sigaction(SIGINT,  &sigcleanup, (struct sigaction *)0);
+       (void) sigaction(SIGQUIT, &sigcleanup, (struct sigaction *)0);
+       (void) sigaction(SIGTERM, &sigcleanup, (struct sigaction *)0);
+
+       /*
+       ** create PID file
+       */
+       if ( (pidf = fopen(PIDFILE, "w")) )
+       {
+               fprintf(pidf, "%d\n", getpid());
+               fclose(pidf);
+       }
+
+       /*
+       ** terminate parent: service  initialized
+       */
+       kill(parentpid, SIGTERM);
+
+       /*
+       ** main loop
+       */
+       while (! cleanup_and_go)
+       {
+               int     state;
+
+               /*
+               ** await termination of (at least) one process and
+               ** copy the process accounting record(s)
+               */
+               state = awaitprocterm(nfd, afd, sfd, accountpath,
+                                       &shadowbusy, &oldshadow, &curshadow);
+
+               if (state == -1)        // irrecoverable error?
+                       break;
+
+               /*
+               ** verify if garbage collection is needed
+               ** i.e. removal of shadow files that are not in use any more
+               */
+               if ( shadowbusy && time(0) > gclast + GCINTERVAL )
+               {
+                       gcshadows(&oldshadow, curshadow);
+                       gclast = time(0);
+               }
+       }
+
+       /*
+       ** cleanup and terminate
+       */
+       (void) acct((char *) 0);        // disable process accounting
+       (void) unlink(accountpath);     // remove source file
+
+       for (; oldshadow <= curshadow; oldshadow++)     // remove shadow files
+       {
+               /*
+               ** assemble path name of shadow file (starting by oldest)
+               */
+               snprintf(shadowpath, sizeof shadowpath, PACCTSHADOWF,
+                                       pacctdir, PACCTSHADOWD, oldshadow);
+
+               (void) unlink(shadowpath);
+       }
+
+       snprintf(shadowpath, sizeof shadowpath, "%s/%s/%s",
+                       pacctdir, PACCTSHADOWD, PACCTSHADOWC);
+
+       (void) unlink(shadowpath);      // remove file 'current'
+       (void) rmdir(shadowdir);        // remove shadow.d directory
+
+       if (cleanup_and_go)
+       {
+               syslog(LOG_NOTICE, "Terminated by signal %d\n", cleanup_and_go);
+
+               if (cleanup_and_go == SIGTERM)
+                       return 0;
+               else
+                       return cleanup_and_go + 128;
+       }
+       else
+       {
+               syslog(LOG_NOTICE, "Terminated!\n");
+               return 13;
+       }
+}
+
+
+/*
+** wait for at least one process termination and copy process accounting
+** record(s) from the source process accounting file to the current
+** shadow accounting file
+**
+** return code:        0 - no process accounting record read
+**              1 - at least one process accounting record read
+**             -1 - irrecoverable failure
+*/
+static int
+awaitprocterm(int nfd, int afd, int sfd, char *accountpath,
+       int *shadowbusyp, unsigned long *oldshadowp, unsigned long *curshadowp)
+{
+       static int                      arecsize, netlinkactive = 1;
+       static unsigned long long       atotsize, stotsize, maxshadowsz;
+       static time_t                   reclast;
+       struct timespec                 retrytimer = {0, RETRYMS/2*1000000};
+       int                             retrycount = RETRYCNT;
+       int                             asz, rv, ssz;
+       char                            abuf[16000];
+       int                             partsz, remsz;
+
+       /*
+       ** neutral state:
+       **
+       ** wait for info from NETLINK indicating that at least
+       ** one process has finished; the real contents of the
+       ** NETLINK message is ignored, it is only used as trigger
+       ** that something can be read from the process accounting file
+       **
+       ** unfortunately it is not possible to use inotify() on the
+       ** source file as a trigger that a new accounting record
+       ** has been written (does not work if the kernel itself
+       ** writes to the file)
+       **
+       ** when the NETLINK interface fails due to kernel bug 190711,
+       ** we switch to polling mode:
+       **      wait for timer and verify if process accounting
+       **      records are available (repeatedly); ugly but the only
+       **      thing we can do if we can't use NETLINK
+       */
+       if (netlinkactive)
+       {
+               rv = netlink_recv(nfd, 0);
+
+               if (rv == 0)            // EOF?
+               {
+                       syslog(LOG_ERR, "unexpected EOF on NETLINK\n");
+                       perror("unexpected EOF on NETLINK\n");
+                       return -1;
+               }
+
+               if (rv < 0)             // failure?
+               {
+                       switch (-rv)
+                       {
+                          // acceptable errors that might indicate that
+                          // processes have terminated
+                          case EINTR:
+                          case ENOMEM:
+                          case ENOBUFS:
+                               break;
+       
+                          default:
+                               syslog(LOG_ERR,
+                                       "unexpected error on NETLINK: %s\n",
+                                       strerror(-rv));
+                               fprintf(stderr,
+                                       "unexpected error on NETLINK: %s\n",
+                                               strerror(-rv));
+
+                               if (-rv == EINVAL)
+                               {
+                                       syslog(LOG_ERR,
+                                       "(see ATOP README about kernel bug 190711)\n");
+                                       fprintf(stderr,
+                                       "(see ATOP README about kernel bug 190711)\n");
+                               }
+
+                               syslog(LOG_ERR,
+                                       "switching to polling mode\n");
+                               fprintf(stderr,
+                                       "switching to polling mode\n");
+
+                               netlinkactive = 0;      // polling mode wanted
+
+                               return 0;
+                       }
+               }
+
+               /*
+               ** get rid of all other waiting finished processes via netlink
+               ** before handling the process accounting record(s)
+               */
+               while ( netlink_recv(nfd, MSG_DONTWAIT) > 0 );
+       }
+       else    // POLLING MODE
+       {
+               sleep(POLLSEC);
+               retrycount = 1;
+       }
+
+       /*
+       ** read new process accounting record(s)
+       ** such record(s) may not immediately be available (timing matter),
+       ** so some retries might be necessary 
+       */
+       while ((asz = read(afd, abuf, sizeof abuf)) == 0 && --retrycount)
+       {
+               nanosleep(&retrytimer, (struct timespec *)0);
+               retrytimer.tv_nsec = RETRYMS*1000000;
+       }
+
+       switch (asz)
+       {
+          case 0:              // EOF (no records available)?
+               if (time(0) > reclast + NORECINTERVAL && reclast)
+               {
+                       syslog(LOG_WARNING, "reactivate process accounting\n");
+
+                       if (truncate(accountpath, 0) != -1)
+                       {
+                               lseek(afd, 0, SEEK_SET);
+                               atotsize = swonpacct(afd, accountpath);
+                       }
+
+                       reclast = time(0);
+               }
+
+               return 0;       // wait for NETLINK again
+
+          case -1:             // failure?
+               syslog(LOG_ERR, "%s - unexpected read error: %s\n",
+                                       accountpath, strerror(errno));
+               return -1;
+       }
+
+       reclast = time(0);
+
+       /*
+       ** only once: determine the size of an accounting
+       ** record and calculate the maximum size for each
+       ** shadow file
+       */
+       if (!arecsize)
+       {
+               arecsize = acctsize((struct acct *)abuf);
+
+               if (arecsize)
+               {
+                       maxshadowsz = maxshadowrec * arecsize;
+               }
+               else
+               {
+                       syslog(LOG_ERR,
+                              "cannot determine size of account record\n");
+                       return -1;
+               }
+       }
+
+       /*
+       ** truncate process accounting file regularly
+       */
+       atotsize += asz;        // maintain current size
+
+       if (atotsize >= MAXORIGSZ)
+       {
+               if (truncate(accountpath, 0) != -1)
+               {
+                       lseek(afd, 0, SEEK_SET);
+                       atotsize = 0;
+               }
+       }
+
+       /*
+       ** determine if any client is using the shadow
+       ** accounting files; if not, verify if clients
+       ** have been using the shadow files till now and
+       ** cleanup has to be performed
+       */
+       if (NUMCLIENTS == 0)
+       {
+               /*
+               ** did last client just disappeared?
+               */
+               if (*shadowbusyp)
+               {
+                       /*
+                       ** remove all shadow files
+                       */
+                       gcshadows(oldshadowp, (*curshadowp)+1);
+                       *oldshadowp = 0;
+                       *curshadowp = 0;
+                       stotsize    = 0;
+
+                       /*
+                       ** create new file with sequence 0
+                       */
+                       (void) close(sfd);
+
+                       sfd = createshadow(*curshadowp);
+                       setcurrent(*curshadowp);
+
+                       *shadowbusyp = 0;
+               }
+
+               return 1;
+       }
+
+       *shadowbusyp = 1;
+
+       /*
+       ** transfer process accounting data to shadow file
+       ** but take care to fill a shadow file exactly
+       ** to its maximum and not more...
+       */
+       if (stotsize + asz <= maxshadowsz)      // would fit?
+       {
+               /*
+               ** pass all data
+               */
+               if ( (ssz = pass2shadow(sfd, abuf, asz)) > 0)
+                       stotsize += ssz;
+
+               remsz  = 0;
+               partsz = 0;
+       }
+       else
+       {
+               /*
+               ** calculate the remainder that has to
+               ** be written to the next shadow file
+               */
+               partsz = maxshadowsz - stotsize;
+               remsz  = asz - partsz;
+
+               /*
+               ** transfer first part of the data to current
+               ** shadow file
+               */
+               if ( (ssz = pass2shadow(sfd, abuf, partsz)) > 0)
+                       stotsize += ssz;
+       }
+
+       /*
+       ** verify if current shadow file has reached its
+       ** maximum size; if so, switch to next sequence number
+       ** and write remaining data (if any)
+       */
+       if (stotsize >= maxshadowsz)
+       {
+               close(sfd);
+
+               sfd = createshadow(++(*curshadowp));
+               setcurrent(*curshadowp);
+
+               stotsize = 0;
+
+               /*
+               ** transfer remaining part of the data
+               */
+               if (remsz)
+               {
+                       if ( (ssz = pass2shadow(sfd, abuf+partsz, remsz)) > 0)
+                               stotsize += ssz;
+               }
+       }
+
+       return 1;
+}
+
+
+/*
+** create first shadow file with requested sequence number
+*/
+static int
+createshadow(long seq)
+{
+       int     sfd;
+       char    shadowpath[128];
+
+       snprintf(shadowpath, sizeof shadowpath, PACCTSHADOWF,
+                               pacctdir,  PACCTSHADOWD, seq);
+
+       /*
+       ** open the shadow file for write
+       */
+       if ( (sfd = creat(shadowpath, 0644)) == -1)
+       {
+               perror(shadowpath);
+               exit(5);
+       }
+
+       return sfd;
+}
+
+/*
+** transfer process accounting data to shadow file
+*/
+static int
+pass2shadow(int sfd, char *sbuf, int ssz)
+{
+       static unsigned long long       nrskipped;
+       struct statvfs                  statvfs;
+
+       /*
+       ** check if the filesystem is not filled for more than 95%
+       */
+       if ( fstatvfs(sfd, &statvfs) != -1)
+       {
+               if (statvfs.f_blocks == 0                       ||
+                   statvfs.f_bfree * 100 / statvfs.f_blocks < 5  )
+               {
+                       if (nrskipped == 0)     // first skip?
+                       {
+                               syslog(LOG_WARNING,
+                                       "Filesystem > 95%% full; "
+                                       "pacct writing skipped\n");
+                       }
+
+                       nrskipped++;
+
+                       return 0;
+               }
+       }
+
+       /*
+       ** there is enough space in the filesystem (again)
+       ** verify if writing has been suspended due to lack of space (if so,
+       ** write a log message)
+       */
+       if (nrskipped)
+       {
+               syslog(LOG_WARNING, "Pacct writing continued (%llu skipped)\n",
+                                                               nrskipped);
+               nrskipped = 0;
+       }
+
+       /*
+       ** transfer process accounting record(s) to shadow file
+       */
+       if ( write(sfd, sbuf, ssz) == -1 )
+       {
+               syslog(LOG_ERR, "Unexpected write error to shadow file: %s\n",
+                                                       strerror(errno));
+               exit(7);
+       }
+
+       return ssz;
+}
+
+
+/*
+** switch on the process accounting mechanism
+**   first parameter:  file descriptor of open accounting file
+**   second parameter: name of accounting file
+**   return value:     -1 in case of permanent failure,
+**                     otherwise number of bytes read from accounting file
+*/
+static int
+swonpacct(int afd, char *accountpath)
+{
+       int  n, acctokay = 0;
+       char abuf[4096];
+
+       /*
+       ** due to kernel bug 190271 (process accounting sometimes
+       ** does not work), we verify if process accounting really
+       ** works after switching it on. If not, we keep retrying
+       ** for a while.
+       */
+       while (! acctokay)
+       {
+               int  maxcnt = 40;
+
+               /*
+               ** switch on process accounting
+               */
+               if ( acct(accountpath) == -1)
+               {
+                       perror("cannot switch on process accounting");
+                       return -1;
+               }
+
+               /*
+               ** try if process accounting works by spawning a
+               ** child process that immediately finishes (should
+               ** result in a process accounting record)
+               */
+               if ( fork() == 0 )      
+                       exit(0);
+
+               (void) wait((int *)0);          // wait for child to finish
+
+               while ( (n = read(afd, abuf, sizeof abuf)) <= 0 && --maxcnt)
+                       usleep(50000);
+
+               if (n > 0)                      // process accounting works
+               {
+                       acctokay = 1;
+               }
+               else
+               {
+                       syslog(LOG_ERR,
+                          "Retrying to switch on process accounting\n");
+                       syslog(LOG_ERR,
+                          "(see ATOP README about kernel bug 190271)\n");
+
+                       acct((char *)0);        // switch off process accounting
+                       sleep(PACCTSEC);        // wait a while before retrying
+               }
+       }
+
+       return n;
+}
+
+
+/*
+** remove old shadow files not being in use any more
+**
+** When a reading process (atop) opens a shadow file,
+** it places a read lock on the first byte of that file.
+** More then one read lock is allowed on that first byte
+** (in case of more atop incarnations).
+** If at least one read lock exists, a write lock (to be
+** tried here) will fail which means that the file is still
+** in use by at least one reader.
+*/
+static void
+gcshadows(unsigned long *oldshadowp, unsigned long curshadow)
+{
+       struct flock    flock;
+       int             tmpsfd;
+       char            shadowpath[128];
+
+       /*
+       ** fill flock structure: write lock on first byte
+       */
+       flock.l_type    = F_WRLCK;
+       flock.l_whence  = SEEK_SET;
+       flock.l_start   = 0;
+       flock.l_len     = 1;
+       
+       for (; *oldshadowp < curshadow; (*oldshadowp)++)
+       {
+               /*
+               ** assemble path name of shadow file (starting by oldest)
+               */
+               snprintf(shadowpath, sizeof shadowpath, PACCTSHADOWF,
+                                       pacctdir, PACCTSHADOWD, *oldshadowp);
+
+               /*
+               ** try to open oldest existing file for write
+               ** and verify if it is in use
+               */
+               if ( (tmpsfd = open(shadowpath, O_WRONLY)) == -1)
+                       break;
+
+               if ( fcntl(tmpsfd, F_SETLK, &flock) == -1)  // no-wait trial
+               {
+                       close(tmpsfd);
+                       break;          // setting lock failed, so still in use
+               }
+
+               /*
+               ** lock successfully set, so file is unused: remove file;
+               ** closing the file implicitly removes the succeeded lock
+               */
+               (void) close(tmpsfd);
+               (void) unlink(shadowpath);
+       }
+}
+
+/*
+** write sequence number of current (newest) file
+*/
+static void
+setcurrent(long curshadow)
+{
+       static int      cfd = -1;
+       char            currentpath[128], currentdata[128];
+       int             len;
+
+       /*
+       ** assemble file name of currency file and open (only once)
+       */
+       if (cfd == -1)
+       {
+               snprintf(currentpath, sizeof currentpath, "%s/%s/%s",
+                       pacctdir, PACCTSHADOWD, PACCTSHADOWC);
+
+               if ( (cfd = creat(currentpath, 0644)) == -1)
+               {
+                       syslog(LOG_ERR, "Could not create currency file: %s\n",
+                                                       strerror(errno));
+                       return;
+               }
+       }
+
+       /*
+       ** assemble ASCII string to be written: seqnumber/maxrecords
+       */
+       len = snprintf(currentdata, sizeof currentdata, "%ld/%lu",
+                                       curshadow, maxshadowrec);
+
+       /*
+       ** wipe currency file and write new assembled string
+       */
+       (void) ftruncate(cfd, 0);
+       (void) lseek(cfd, 0, SEEK_SET);
+       (void) write(cfd, currentdata, len);
+}
+
+/*
+** determine the size of an accounting record
+*/
+static int
+acctsize(struct acct *parec)
+{
+       switch (parec->ac_version & 0x0f)
+       {
+          case 2:
+               return sizeof(struct acct);
+
+          case 3:
+               return sizeof(struct acct_v3);
+
+          default:
+               return 0;
+       }
+}
+
+/*
+** generate version number and date
+*/
+char *
+getstrvers(void)
+{
+       static char vers[256];
+
+       snprintf(vers, sizeof vers, "Version: %s - %s",
+               atopacctdversion, atopacctddate);
+
+       return vers;
+}
+
+/*
+** signal catcher:
+**    set flag to be verified in main loop to cleanup and terminate
+*/
+void
+cleanup(int sig)
+{
+       cleanup_and_go = sig;
+}
diff --git a/sources/atopacctd.h b/sources/atopacctd.h
new file mode 100644 (file)
index 0000000..6c26376
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+** keys to access the semaphores
+*/
+#define PACCTPUBKEY    1071980         // # clients using shadow files
+#define PACCTPRVKEY    (PACCTPUBKEY-1) // # atopacctd daemons busy (max. 1)
+
+/*
+** name of the PID file
+*/
+#define        PIDFILE         "/var/run/atopacctd.pid"
+
+/*
+** directory containing the source accounting file and
+** the subdirectory (PACCTSHADOWD) containing the shadow file(s)
+** this directory can be overruled by a command line parameter (atopacctd)
+** or by a keyword in the /etc/atoprc file (atop)
+*/
+#define        PACCTDIR        "/var/run"
+
+/*
+** accounting file (source file to which kernel writes records)
+*/
+#define PACCTORIG      "pacct_source"          // file name in PACCTDIR
+
+#define MAXORIGSZ      (1024*1024)             // maximum size of source file
+
+/*
+** file and directory names for shadow files
+*/
+#define PACCTSHADOWD   "pacct_shadow.d"        // directory name in PACCTDIR
+#define PACCTSHADOWF   "%s/%s/%010ld.paf"      // file name of shadow file
+#define PACCTSHADOWC   "current"               // file containining current
+                                               // sequence and MAXSHADOWREC
+
+#define MAXSHADOWREC   10000   // number of accounting records per shadow file
diff --git a/sources/atopsar.c b/sources/atopsar.c
new file mode 100644 (file)
index 0000000..5582e77
--- /dev/null
@@ -0,0 +1,2558 @@
+/*
+** ATOP - System & Process Monitor
+**
+** The program 'atop'/'atopsar' offers the possibility to view the activity of 
+** the system on system-level as well as process-level.
+**
+** This source-file contains the 'atopsar'-functionality, that makes use
+** of the 'atop'-framework.
+** ==========================================================================
+** Author:      Gerlof Langeveld
+** E-mail:      gerlof.langeveld@atoptool.nl
+** Date:        July 2007
+** --------------------------------------------------------------------------
+** Copyright (C) 2007-2010 Gerlof Langeveld
+**
+** This program is free software; you can redistribute it and/or modify it
+** under the terms of the GNU General Public License as published by the
+** Free Software Foundation; either version 2, or (at your option) any
+** later version.
+**
+** This program is distributed in the hope that it will be useful, but
+** WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+** See the GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+** --------------------------------------------------------------------------
+*/
+
+static const char rcsid[] = "$Id: atopsar.c,v 1.28 2010/11/26 06:19:43 gerlof Exp $";
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <sys/utsname.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <regex.h>
+#include <sys/ioctl.h>
+
+#include "atop.h"
+#include "ifprop.h"
+#include "photosyst.h"
+#include "photoproc.h"
+
+#define        MAXFL   64      /* maximum number of command-line flags  */
+
+
+/*
+** color definitions
+*/
+#define        COLSETHEAD      "\033[30;43m"   /* black on yellow      */
+#define        COLSETMED       "\033[36m"      /* cyan                 */
+#define        COLSETHIGH      "\033[31m"      /* red                  */
+#define        COLRESET        "\033[00m"      /* reset any color      */
+
+/*
+** miscellaneous values
+*/
+static unsigned int    nsamples = 9999999;
+static char            stampalways;
+static char            usemarkers;
+static char            allresources;
+static int             numreports;
+static time_t          saved_begintime;
+static unsigned int    repeathead = 9999999;
+static unsigned int    summarycnt = 1;
+static char            *datemsg = "-------------------------- analysis "
+                                  "date: %s --------------------------\n";
+
+/*
+** structure definition for print-functions
+*/
+struct pridef { 
+       char    wanted;         /* selected option (boolean)              */
+       char    *cntcat;        /* used categories of counters            */
+       char    flag;           /* flag on command line                   */
+       void    (*prihead)();   /* print header of list                   */
+       int     (*priline)(struct sstat *, struct tstat *, struct tstat **,
+                          int, time_t, time_t, time_t,
+                          int, int, int, char *,
+                          int, int, int, int, int, int);
+                               /* print counters per line (excl. time)   */
+       char    *about;         /* statistics about what                  */
+};
+
+extern struct pridef   pridef[];      /* table of print-functions        */
+extern int             pricnt;        /* total number of print-functions */
+
+static time_t          daylim;        /* last second of day in epoch     */
+static int             prinow;        /* current selection               */
+static char            coloron;       /* boolean: colors active now      */
+
+/*
+** local prototypes
+*/
+static void    engine(void);
+static void    pratopsaruse(char *);
+static void    reportlive(time_t, int, struct sstat *);
+static char     reportraw (time_t, int,
+                            struct devtstat *, struct sstat *,
+                            int, unsigned int, char);
+
+static void    reportheader(struct utsname *, time_t);
+static time_t  daylimit(time_t);
+
+int
+atopsar(int argc, char *argv[])
+{
+       register int    i, c;
+       struct rlimit   rlim;
+       char            *p, *flaglist;
+
+       usecolors = 't';
+
+       /* 
+       ** interpret command-line arguments & flags 
+       */
+       if (argc > 1)
+       {
+               /* 
+               ** gather all flags for the print-functions
+               */
+               flaglist = malloc(pricnt+32);
+
+               ptrverify(flaglist, "Malloc failed for %d flags\n", pricnt+32);
+
+               for (i=0; i < pricnt; i++)
+                       flaglist[i] = pridef[i].flag;
+
+               flaglist[i] = 0;
+
+               /*
+               ** add generic flags
+               */
+               strcat(flaglist, "b:e:SxCMHr:R:aA");
+
+               while ((c=getopt(argc, argv, flaglist)) != EOF)
+               {
+                       switch (c)
+                       {
+                          case '?':            /* usage wanted ?        */
+                               pratopsaruse(argv[0]);
+                               break;
+
+                           case 'b':           /* begin time ?          */
+                               if ( !hhmm2secs(optarg, &begintime) )
+                                       pratopsaruse(argv[0]);
+
+                               saved_begintime = begintime;
+                               break;
+
+                           case 'e':           /* end   time ?          */
+                               if ( !hhmm2secs(optarg, &endtime) )
+                                       pratopsaruse(argv[0]);
+                               break;
+
+                          case 'r':            /* reading of file data ? */
+                               strncpy(rawname, optarg, RAWNAMESZ-1);
+                               rawreadflag++;
+                               break;
+
+                          case 'R':            /* summarize samples */
+                               if (!numeric(optarg))
+                                       pratopsaruse(argv[0]);
+
+                               summarycnt = atoi(optarg);
+
+                               if (summarycnt < 1)
+                                       pratopsaruse(argv[0]);
+                               break;
+
+                          case 'S':            /* timestamp on every line */
+                               stampalways = 1;
+                               break;
+
+                          case 'x':            /* never use colors        */
+                               usecolors = 0;
+                               break;
+
+                          case 'C':            /* always use colors       */
+                               usecolors = 'a';
+                               break;
+
+                          case 'M':            /* markers for overload    */
+                               usemarkers = 1;
+                               break;
+
+                          case 'H':            /* repeat headers          */
+                               repeathead = 23;        /* define default  */
+
+                               if (isatty(1))
+                               {
+                                       struct winsize wsz;
+
+                                       if ( ioctl(1, TIOCGWINSZ, &wsz) != -1)
+                                               repeathead = wsz.ws_row - 1;
+                               }
+                               break;
+
+                          case 'a':            /* every interval all units */
+                               allresources = 1;
+                               break;
+
+                          case 'A':            /* all reports wanted ?  */
+                               for (i=0; i < pricnt; i++)
+                                       pridef[i].wanted = 1;
+
+                               numreports = pricnt;
+                               break;
+
+                          default:             /* gather report-flags    */
+                               for (i=0; i < pricnt; i++)
+                               {
+                                       if (pridef[i].flag   == c && 
+                                           pridef[i].wanted == 0   )
+                                       {
+                                               pridef[i].wanted = 1;
+                                               numreports++;
+                                               break;
+                                       }
+                               }
+
+                               if (i == pricnt)
+                                       pratopsaruse(argv[0]);
+                       }
+               }
+
+               free(flaglist);
+
+               /*
+               ** get optional interval-value and
+               ** optional number of samples   
+               */
+               if (optind < argc && optind < MAXFL)
+               {
+                       if (rawreadflag)
+                               pratopsaruse(argv[0]);
+
+                       if (!numeric(argv[optind]))
+                               pratopsaruse(argv[0]);
+       
+                       interval = atoi(argv[optind]);
+       
+                       optind++;
+       
+                       if (optind < argc)
+                       {
+                               if (!numeric(argv[optind]) )
+                                       pratopsaruse(argv[0]);
+
+                               if ( (nsamples = atoi(argv[optind])) < 1)
+                                       pratopsaruse(argv[0]);
+                       }
+               }
+               else    /* if no interval specified, read from logfile */
+               {
+                       rawreadflag++;
+               }
+       }
+       else    /* if no flags specified at all, read from logfile */
+       {
+               rawreadflag++;
+       }
+
+       /*
+       ** if no report-flags have been specified, take the first
+       ** option in the print-list as default
+       */
+       if (numreports == 0)
+       {
+               pridef[0].wanted = 1;
+               numreports       = 1;
+       }
+
+       /*
+       ** set stdout output on line-basis
+       */
+       setvbuf(stdout, (char *)0, _IOLBF, BUFSIZ);
+
+       /*
+       ** if only use colors when the output is directed to a tty,
+       ** be sure that output is directed to a tty
+       */
+       if (usecolors == 't')
+       {
+               if (! isatty(1) )
+                       usecolors = 0;
+       }
+
+       /*
+       ** check if raw data from a file must be viewed
+       */
+       if (rawreadflag)
+       {
+               /*
+               ** select own reportraw-function to be called
+               ** by the rawread function
+               */
+               vis.show_samp  = reportraw;
+
+               for (i=0; i < pricnt; i++)
+               {
+                       if ( pridef[i].wanted )
+                       {
+                               prinow    = i;
+                               daylim    = 0;
+                               begintime = saved_begintime;
+                               rawread();
+                               printf("\n");
+                       }
+               }
+
+               cleanstop(0);
+       }
+
+       /*
+       ** live data must be gathered
+       **
+       ** determine the name of this node (without domain-name)
+       ** and the kernel-version
+       */
+       (void) uname(&utsname);
+
+       if ( (p = strchr(utsname.nodename, '.')) )
+               *p = '\0';
+
+       sscanf(utsname.release, "%d.%d.%d", &osrel, &osvers, &ossub);
+
+       /*
+       ** determine the clock rate and memory page size for this machine
+       */
+       hertz           = sysconf(_SC_CLK_TCK);
+       pagesize        = sysconf(_SC_PAGESIZE);
+
+       /*
+       ** determine start-time for gathering current statistics
+       */
+       curtime = time(0);
+
+        /*
+       ** regain the root-priviliges that we dropped at the beginning
+       ** to do some priviliged work now
+       */
+        regainrootprivs();
+
+       /*
+       ** lock in memory to get reliable samples (also when
+       ** memory is low);
+       ** ignored if not running under superuser privileges!
+       */
+       rlim.rlim_cur   = RLIM_INFINITY;
+       rlim.rlim_max   = RLIM_INFINITY;
+       (void) setrlimit(RLIMIT_MEMLOCK, &rlim);
+       (void) mlockall(MCL_CURRENT|MCL_FUTURE);
+
+       /*
+       ** increment CPU scheduling-priority to get reliable samples (also
+       ** during heavy CPU load);
+       ** ignored if not running under superuser privileges!
+       */
+       if ( nice(-20) == -1)
+               ;
+       /*
+       ** determine properties (like speed) of all interfaces
+       */
+       initifprop();
+
+       /*
+       ** since privileged activities are finished now, there is no
+       ** need to keep running under root-privileges, so switch
+       ** effective user-id to real user-id
+       */
+       if (! droprootprivs() )
+               cleanstop(42);
+
+       /*
+       ** start live reporting
+       */
+       engine();
+
+       return 0;
+}
+
+/*
+** engine() that drives the main-loop for atopsar
+*/
+static void
+engine(void)
+{
+       struct sigaction        sigact;
+       void                    getusr1(int);
+
+       /*
+       ** reserve space for system-level statistics
+       */
+       static struct sstat     *cursstat; /* current   */
+       static struct sstat     *presstat; /* previous  */
+       static struct sstat     *devsstat; /* deviation */
+       static struct sstat     *hlpsstat;
+
+       /*
+       ** initialization: allocate required memory dynamically
+       */
+       cursstat = calloc(1, sizeof(struct sstat));
+       presstat = calloc(1, sizeof(struct sstat));
+       devsstat = calloc(1, sizeof(struct sstat));
+
+       ptrverify(cursstat,  "Malloc failed for current sysstats\n");
+       ptrverify(presstat,  "Malloc failed for prev    sysstats\n");
+       ptrverify(devsstat,  "Malloc failed for deviate sysstats\n");
+
+       /*
+       ** install the signal-handler for ALARM and SIGUSR1 (both triggers
+       ** for the next sample)
+       */
+       memset(&sigact, 0, sizeof sigact);
+       sigact.sa_handler = getusr1;
+       sigaction(SIGUSR1, &sigact, (struct sigaction *)0);
+
+       memset(&sigact, 0, sizeof sigact);
+       sigact.sa_handler = getalarm;
+       sigaction(SIGALRM, &sigact, (struct sigaction *)0);
+
+       if (interval > 0)
+               alarm(interval);
+
+       /*
+       ** print overall report header
+       */
+       reportheader(&utsname, time(0));
+
+       /*
+       ** MAIN-LOOP:
+       **    - Wait for the requested number of seconds or for other trigger
+       **
+       **    - System-level counters
+       **              get current counters
+       **              calculate the differences with the previous sample
+       **
+       **    - Call the print-function to visualize the differences
+       */
+       for (sampcnt=0; sampcnt < nsamples+1; sampcnt++)
+       {
+               /*
+               ** wait for alarm-signal to arrive or
+               ** wait for SIGUSR1 in case of an interval of 0.
+               */
+               if (sampcnt > 0)
+                       pause();
+
+               /*
+               ** gather time info for this sample
+               */
+               pretime  = curtime;
+               curtime  = time(0);             /* seconds since 1-1-1970 */
+
+               /*
+               ** take a snapshot of the current system-level statistics 
+               ** and calculate the deviations (i.e. calculate the activity
+               ** during the last sample)
+               */
+               hlpsstat = cursstat;    /* swap current/prev. stats */
+               cursstat = presstat;
+               presstat = hlpsstat;
+
+               photosyst(cursstat);    /* obtain new counters      */
+
+               deviatsyst(cursstat, presstat, devsstat,
+                                curtime-pretime > 0 ? curtime-pretime : 1);
+
+               /*
+               ** activate the report-function to visualize the deviations
+               */
+               reportlive(curtime,
+                       curtime-pretime > 0 ? curtime-pretime : 1, devsstat);
+       } /* end of main-loop */
+}
+
+/*
+** report function to print a new sample in case of live measurements
+*/
+static void
+reportlive(time_t curtime, int numsecs, struct sstat *ss)
+{
+       char                    timebuf[16], datebuf[16];
+       int                     i, nr = numreports, rv;
+       static unsigned int     curline, headline;
+
+       /*
+       ** printing more reports needs another way of handling compared
+       ** to printing one report
+       */
+       if (numreports > 1)
+       {
+               /*
+               ** skip first sample
+               */
+               if (sampcnt == 0)
+                       return;
+
+               printf(datemsg, convdate(curtime, datebuf));
+
+               for (i=0; i < pricnt && nr > 0; i++)
+               {
+                       if ( !pridef[i].wanted )
+                               continue;
+
+                       nr--;
+
+                       /*
+                       ** print header-line
+                       */
+                       printf("\n");
+
+                       if (usecolors)
+                               printf(COLSETHEAD);
+
+                       printf("%s  ", convtime(curtime-numsecs, timebuf));
+       
+                       (pridef[i].prihead)(osvers, osrel, ossub);
+       
+                       if (usecolors)
+                               printf(COLRESET);
+
+                       printf("\n");
+
+                       /*
+                       ** print line with statistical counters
+                       */
+                       printf("%s  ", convtime(curtime, timebuf));
+       
+                       if ( !(pridef[i].priline)(ss, (struct tstat *)0, 0, 0,
+                               numsecs, numsecs*hertz, hertz,
+                               osvers, osrel, ossub,
+                               stampalways ? timebuf : "        ",
+                               0, 0, 0, 0, 0, 0) )
+                       {
+                               /*
+                               ** print line has failed;
+                               ** do not call function again
+                               */
+                               pridef[i].wanted = 0;
+       
+                               if (--numreports == 0)
+                                       cleanstop(1);
+                       }
+               }
+
+               printf("\n");
+       }
+       else            /* just one report to be printed */
+       {
+               /*
+               ** search required report
+               */
+               for (i=0; i < pricnt; i++)
+                       if ( pridef[i].wanted )
+                               break;
+
+               /*
+               ** verify if we have passed midnight of some day
+               */
+               if (curtime > daylim)
+               {
+                       printf(datemsg, convdate(curtime, datebuf));
+                       daylim = daylimit(curtime);
+                       curline++;
+               }
+
+               /*
+               ** print first header
+               */
+               if (sampcnt == 0)
+               {
+                       /*
+                       ** print header-line
+                       */
+                       printf("\n");
+
+                       if (usecolors)
+                               printf(COLSETHEAD);
+
+                       printf("%s  ", convtime(curtime, timebuf));
+       
+                       (pridef[i].prihead)(osvers, osrel, ossub);
+
+                       if (usecolors)
+                               printf(COLRESET);
+
+                       printf("\n");
+
+                       curline+=2;
+
+                       headline = repeathead;
+                       return;
+               }
+
+               /*
+               ** print line with statistical counters
+               */
+               printf("%s  ", convtime(curtime, timebuf));
+       
+               if ( !(rv = (pridef[i].priline)(ss, (struct tstat *)0, 0, 0,
+                                       numsecs, numsecs*hertz, hertz,
+                                       osvers, osrel, ossub, 
+                                       stampalways ? timebuf : "        ",
+                                       0, 0, 0, 0, 0, 0) ) )
+               {
+                       /*
+                       ** print line has failed;
+                       ** do not call function again
+                       */
+                       cleanstop(1);
+               }
+
+               curline+=rv;
+
+               if (curline >= headline)
+               {
+                       headline = curline + repeathead;
+
+                       /*
+                       ** print header-line
+                       */
+                       printf("\n");
+
+                       if (usecolors)
+                               printf(COLSETHEAD);
+
+                       printf("%s  ", convtime(curtime, timebuf));
+       
+                       (pridef[i].prihead)(osvers, osrel, ossub);
+
+                       if (usecolors)
+                               printf(COLRESET);
+
+                       printf("\n");
+
+                       curline+=2;
+               }
+       }
+}
+
+/*
+** report function to print a new sample in case of logged measurements
+*/
+static char
+reportraw(time_t curtime, int numsecs,
+               struct devtstat *devtstat, struct sstat *sstat,
+               int nexit, unsigned int noverflow, char flags)
+{
+       static char             firstcall = 1;
+       char                    timebuf[16], datebuf[16];
+       unsigned int            rv;
+       static unsigned int     curline, headline, sampsum,
+                               totalsec, totalexit, lastnpres,
+                               lastntrun, lastntslpi, lastntslpu, lastnzomb;
+       static time_t           lasttime;
+       static struct sstat     totsyst;
+
+       /*
+       ** is this function still wanted?
+       */
+       if ( ! pridef[prinow].wanted )
+               return '\0';
+
+       /*
+       ** when this is first call to this function,
+       ** print overall header with system information
+       */
+       if (firstcall)
+       {
+               reportheader(&utsname, time(0));
+               firstcall = 0;
+       }
+
+       /*
+       ** verify if we have passed midnight
+       */
+       if (curtime > daylim)
+       {
+               printf(datemsg, convdate(curtime, datebuf));
+               daylim = daylimit(curtime);
+               curline++;
+       }
+
+       /*
+       ** when this is the first record for a new report,
+       ** initialize various variables
+       */
+       if (sampcnt == 1)
+       {
+               /*
+               ** initialize variables for new report
+               */
+               pretime = curtime;
+
+               curline   = 1;
+               headline  = 0;
+
+               sampsum   = summarycnt + 1;
+               totalsec  = 0;
+               totalexit = 0;
+               memset(&totsyst, 0, sizeof totsyst);
+
+               return '\0';
+       }
+
+       /*
+       ** check if a (new) report header needs to be printed
+       */
+       if (curline >= headline)
+       {
+               headline = curline + repeathead;
+
+               /*
+               ** print header-line
+               */
+               printf("\n");
+
+               if (usecolors)
+                       printf(COLSETHEAD);
+
+               printf("%s  ", convtime(pretime, timebuf));
+
+               (pridef[prinow].prihead)(osvers, osrel, ossub);
+
+               if (usecolors)
+                       printf(COLRESET);
+
+               printf("\n");
+
+               curline+=2;
+       }
+
+       /*
+       ** when current record contains log-restart indicator,
+       ** print message and reinitialize variables
+       */
+       if (flags & RRBOOT)
+       {
+               /*
+               ** when accumulating counters, print results upto
+               ** the *previous* record
+               */
+               if (summarycnt > 1 && sampcnt <= sampsum && totalsec)
+               {
+                       printf("%s  ", convtime(lasttime, timebuf));
+
+                       rv = (pridef[prinow].priline)(&totsyst,
+                               (struct tstat *)0, 0, 0,
+                               totalsec, totalsec*hertz, hertz,
+                               osvers, osrel, ossub,
+                               stampalways ? timebuf : "        ",
+                               lastnpres, lastntrun, lastntslpi, lastntslpu,
+                               totalexit, lastnzomb);
+
+                       if (rv == 0)
+                       {
+                               curline++;
+                               pridef[prinow].wanted = 0; /* not call again */
+
+                               if (--numreports == 0)
+                                       cleanstop(1);
+                       }
+                       else
+                       {
+                               curline += rv;
+                       }
+               }
+
+               /*
+               ** print restart-line in case of logging restarted
+               */
+               printf("%s  ", convtime(curtime, timebuf));
+
+               printf("......................... logging restarted "
+                      ".........................\n");
+
+               pretime = curtime;
+               curline++;
+
+               /*
+               ** reinitialize variables
+               */
+               sampsum   = summarycnt + sampcnt;
+               totalsec  = 0;
+               totalexit = 0;
+               memset(&totsyst, 0, sizeof totsyst);
+
+               return '\0';
+       }
+
+       /*
+       ** when no accumulation is required,
+       ** just print current sample
+       */
+       if (summarycnt == 1)
+       {
+               printf("%s  ", convtime(curtime, timebuf));
+
+               rv = (pridef[prinow].priline) (sstat, devtstat->taskall,
+                               devtstat->procall, devtstat->nprocall,
+                               numsecs, numsecs*hertz, hertz,
+                               osvers, osrel, ossub,
+                               stampalways ? timebuf : "        ",
+                               devtstat->ntaskall, devtstat->totrun,
+                               devtstat->totslpi, devtstat->totslpu,
+                               nexit, devtstat->totzombie);
+
+               if (rv == 0)
+               {
+                       curline++;
+                       pridef[prinow].wanted = 0; /* not call again */
+
+                       if (--numreports == 0)
+                               cleanstop(1);
+               }
+               else
+               {
+                       curline += rv;
+               }
+       }
+       else            /* accumulation is required */
+       {
+               char  *cp = pridef[prinow].cntcat;
+
+               /*
+               ** maintain totals per category
+               */
+               while (*cp)
+               {
+                       totalsyst(*cp, sstat, &totsyst);
+                       cp++;
+               }
+
+               totalsec  += numsecs;
+               totalexit += nexit;
+
+               /*
+               ** remember some values in case the next record
+               ** contains the log-restart indicator
+               */
+               lasttime   = curtime;
+               lastnpres  = devtstat->nprocall;
+               lastntrun  = devtstat->totrun;
+               lastntslpi = devtstat->totslpi;
+               lastntslpu = devtstat->totslpu;
+               lastnzomb  = devtstat->totzombie;
+
+               /*
+               ** print line only if needed
+               */
+               if (sampcnt >= sampsum || ( (flags&RRLAST) && totalsec) )
+               {
+                       /*
+                       ** print output line for required report
+                       */
+                       printf("%s  ", convtime(curtime, timebuf));
+
+                       rv = (pridef[prinow].priline) (&totsyst,
+                                       (struct tstat *)0, 0, 0,
+                                       totalsec, totalsec*hertz, hertz,
+                                       osvers, osrel, ossub,
+                                       stampalways ? timebuf : "        ",
+                                       devtstat->ntaskall, devtstat->totrun,
+                                       devtstat->totslpi, devtstat->totslpu,
+                                       totalexit, devtstat->totzombie);
+
+                       if (rv == 0)
+                       {
+                               curline++;
+                               pridef[prinow].wanted = 0; /* not call again */
+
+                               if (--numreports == 0)
+                                       cleanstop(1);
+                       }
+                       else
+                       {
+                               curline += rv;
+                       }
+
+                       sampsum   = summarycnt + sampcnt;
+                       totalsec  = 0;
+                       totalexit = 0;
+                       memset(&totsyst, 0, sizeof totsyst);
+               }
+               else
+               {
+                       rv = 1;
+               }
+       }
+
+       if (!rv)
+       {
+               /*
+               ** print for line has failed;
+               ** never call this function again
+               */
+               pridef[prinow].wanted = 0;
+
+               if (--numreports == 0)
+                       cleanstop(1);
+       }
+
+       pretime = curtime;
+
+       return '\0';
+}
+
+/*
+** print overall header
+*/
+static void
+reportheader(struct utsname *uname, time_t mtime)
+{
+        char            cdate[16];
+
+        printf("\n%s  %s  %s  %s  %s\n\n",
+                uname->nodename,
+                uname->release,
+                uname->version,
+                uname->machine,
+               convdate(mtime, cdate));
+}
+
+/*
+** print usage of atopsar command
+*/
+void
+pratopsaruse(char *myname)
+{
+       int     i;
+
+       fprintf(stderr,
+               "Usage: %s [-flags] [-r file|date|y...] [-R cnt] [-b hh:mm] [-e hh:mm]\n",
+                                                               myname);
+       fprintf(stderr, "\t\tor\n");
+       fprintf(stderr,
+               "Usage: %s [-flags] interval [samples]\n", myname);
+       fprintf(stderr, "\n");
+       fprintf(stderr,
+               "\tToday's atop logfile is used by default!\n");
+       fprintf(stderr, "\n");
+       fprintf(stderr,
+               "\tGeneric flags:\n");
+       fprintf(stderr,
+               "\t  -r  read statistical data from specific atop logfile\n");
+       fprintf(stderr,
+               "\t      (pathname, or date in format YYYYMMDD, or y[y..])\n");
+       fprintf(stderr,
+               "\t  -R  summarize <cnt> samples into one sample\n");
+       fprintf(stderr,
+               "\t  -b  begin  showing data from  specified time\n");
+       fprintf(stderr,
+               "\t  -e  finish showing data after specified time\n");
+       fprintf(stderr,
+               "\t  -S  print timestamp on every line in case of more "
+               "resources\n");
+       fprintf(stderr,
+               "\t  -x  never  use colors to indicate overload"
+               " (default: only if tty)\n");
+       fprintf(stderr,
+               "\t  -C  always use colors to indicate overload"
+               " (default: only if tty)\n");
+       fprintf(stderr,
+               "\t  -M  use markers to indicate overload "
+               "(* = critical, + = almost)\n");
+       fprintf(stderr,
+               "\t  -H  repeat report headers "
+               "(in case of tty: depending on screen lines)\n");
+       fprintf(stderr,
+               "\t  -a  print all resources, even when inactive\n");
+       fprintf(stderr, "\n");
+       fprintf(stderr,
+               "\tSpecific flags to select reports:\n");
+       fprintf(stderr,
+               "\t  -A  print all available reports\n");
+
+       for (i=0; i < pricnt; i++)
+               fprintf(stderr,
+               "\t  -%c  %s\n", pridef[i].flag, pridef[i].about);
+
+       fprintf(stderr, "\n");
+       fprintf(stderr,
+                "Please refer to the man-page of 'atopsar' "
+               "for more details.\n");
+
+
+       cleanstop(1);
+}
+
+/*
+** calculate the epoch-value for the last second
+** of the day given a certain epoch
+*/
+static time_t
+daylimit(time_t timval)
+{
+       struct tm  *tp = localtime(&timval);
+
+       tp->tm_hour = 23;
+       tp->tm_min  = 59;
+       tp->tm_sec  = 59;
+
+       return mktime(tp);
+}
+
+/*
+** function to be called before printing a statistics line
+** to switch on colors when necessary
+*/
+static void
+preprint(unsigned int badness)
+{
+       if (usecolors)
+       {
+               if (badness >= 100)
+               {
+                       coloron = 1;
+                       printf(COLSETHIGH);
+               }
+               else
+               {
+                       if (almostcrit && badness >= almostcrit)
+                       {
+                               coloron = 1;
+                               printf(COLSETMED);
+                       }
+               }
+       }
+}
+
+/*
+** function to be called after printing a statistics line
+** to switch off colors when necessary and print a line feed
+*/
+static void
+postprint(unsigned int badness)
+{
+       if (coloron)
+       {
+               coloron = 0;
+               printf(COLRESET);
+       }
+
+       if (usemarkers)
+       {
+               if (badness >= 100)
+               {
+                       printf(" *");
+               }
+               else
+               {
+                       if (almostcrit && badness >= almostcrit)
+                               printf(" +");
+               }
+       }
+
+       printf("\n");
+}
+
+/*
+** function the handle the default flags for atopsar as
+** read from the file ~/.atoprc
+*/
+void
+do_atopsarflags(char *val)
+{
+       int     i, j;
+
+       for (i=0; val[i]; i++)
+       {
+               switch (val[i])
+               {
+                  case '-':
+                       break;
+
+                  case 'S':            /* timestamp on every line */
+                       stampalways = 1;
+                       break;
+
+                  case 'x':            /* always colors for overload */
+                       usecolors = 0;
+                       break;
+
+                  case 'C':            /* always colors for overload */
+                       usecolors = 'a';
+                       break;
+
+                  case 'M':            /* markers for overload    */
+                       usemarkers = 1;
+                       break;
+
+                  case 'H':            /* repeat headers          */
+                       repeathead = 23;        /* define default  */
+
+                       if (isatty(1))
+                       {
+                               struct winsize wsz;
+
+                               if ( ioctl(1, TIOCGWINSZ, &wsz) != -1)
+                                       repeathead = wsz.ws_row - 1;
+                       }
+                       break;
+
+                  case 'a':            /* every interval all units */
+                       allresources = 1;
+                       break;
+
+                  case 'A':            /* all reports wanted ?  */
+                       for (j=0; j < pricnt; j++)
+                               pridef[j].wanted = 1;
+
+                       numreports = pricnt;
+                       break;
+
+                  default:             /* gather report-flags    */
+                       for (j=0; j < pricnt; j++)
+                       {
+                               if (pridef[j].flag   == val[i] && 
+                                   pridef[j].wanted == 0        )
+                               {
+                                       pridef[j].wanted = 1;
+                                       numreports++;
+                                       break;
+                               }
+                       }
+               }
+       }
+}
+
+/**************************************************************************/
+/*                 Functions to print statistics                          */
+/**************************************************************************/
+/*
+** CPU statistics
+*/
+static void
+cpuhead(int osvers, int osrel, int ossub)
+{
+       printf("cpu  %%usr %%nice %%sys %%irq %%softirq  %%steal %%guest "
+              " %%wait %%idle  _cpu_");
+}
+
+static int
+cpuline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc,
+        time_t deltasec, time_t deltatic, time_t hz,
+        int osvers, int osrel, int ossub, char *tstamp,
+        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
+{
+       register int    i, nlines = 1;
+       count_t         cputot;
+       unsigned int    badness;
+
+       /*
+       ** print overall statistics
+       */
+        cputot = ss->cpu.all.stime + ss->cpu.all.utime +
+                 ss->cpu.all.ntime + ss->cpu.all.itime +
+                 ss->cpu.all.wtime + ss->cpu.all.Itime +
+                 ss->cpu.all.Stime + ss->cpu.all.steal;
+
+       if (cputot == 0)
+               cputot = 1;     /* avoid divide-by-zero */
+
+       if (cpubadness)
+               badness = ((cputot - ss->cpu.all.itime - ss->cpu.all.wtime) *
+                            100.0 / cputot) * 100 / cpubadness;
+       else
+               badness = 0;
+
+       preprint(badness);
+
+       printf("all %5.0lf %5.0lf %4.0lf %4.0lf %8.0lf %7.0f %6.0f %6.0lf %5.0lf",
+                (double) (ss->cpu.all.utime * 100.0) / cputot * ss->cpu.nrcpu,
+                (double) (ss->cpu.all.ntime * 100.0) / cputot * ss->cpu.nrcpu,
+                (double) (ss->cpu.all.stime * 100.0) / cputot * ss->cpu.nrcpu,
+                (double) (ss->cpu.all.Itime * 100.0) / cputot * ss->cpu.nrcpu,
+                (double) (ss->cpu.all.Stime * 100.0) / cputot * ss->cpu.nrcpu,
+                (double) (ss->cpu.all.steal * 100.0) / cputot * ss->cpu.nrcpu,
+                (double) (ss->cpu.all.guest * 100.0) / cputot * ss->cpu.nrcpu,
+                (double) (ss->cpu.all.wtime * 100.0) / cputot * ss->cpu.nrcpu,
+                (double) (ss->cpu.all.itime * 100.0) / cputot * ss->cpu.nrcpu);
+
+       postprint(badness);
+
+       /*
+       ** print per-cpu statistics
+       */
+       if (ss->cpu.nrcpu > 1)
+       {
+               for (i=0; i < ss->cpu.nrcpu; i++)
+               {
+                       cputot = ss->cpu.cpu[i].stime + ss->cpu.cpu[i].utime +
+                                ss->cpu.cpu[i].ntime + ss->cpu.cpu[i].itime +
+                                ss->cpu.cpu[i].wtime + ss->cpu.cpu[i].Itime +
+                                ss->cpu.cpu[i].Stime + ss->cpu.cpu[i].steal;
+
+                       if (cputot == 0)
+                               cputot = 1;     /* avoid divide-by-zero */
+
+                       if (cpubadness)
+                               badness = ((cputot - ss->cpu.cpu[i].itime -
+                                           ss->cpu.cpu[i].wtime) * 100.0 /
+                                           cputot) * 100 / cpubadness;
+                       else
+                               badness = 0;
+
+                       printf("%s ", tstamp);
+
+                       preprint(badness);
+
+                       printf("%4d %5.0lf %5.0lf %4.0lf %4.0lf %8.0lf "
+                              "%7.0f %6.0lf %6.0lf %5.0lf",
+                            i,
+                            (double)(ss->cpu.cpu[i].utime * 100.0) / cputot,
+                            (double)(ss->cpu.cpu[i].ntime * 100.0) / cputot,
+                            (double)(ss->cpu.cpu[i].stime * 100.0) / cputot,
+                            (double)(ss->cpu.cpu[i].Itime * 100.0) / cputot,
+                            (double)(ss->cpu.cpu[i].Stime * 100.0) / cputot,
+                            (double)(ss->cpu.cpu[i].steal * 100.0) / cputot,
+                            (double)(ss->cpu.cpu[i].guest * 100.0) / cputot,
+                            (double)(ss->cpu.cpu[i].wtime * 100.0) / cputot,
+                            (double)(ss->cpu.cpu[i].itime * 100.0) / cputot);
+
+                       postprint(badness);
+
+                       nlines++;
+               }
+       }
+
+       return nlines;
+}
+
+/*
+** other processor statistics
+*/
+static void
+prochead(int osvers, int osrel, int ossub)
+{
+       printf("pswch/s devintr/s  clones/s  loadavg1 loadavg5 loadavg15  "
+              "     _load_");
+}
+
+static int
+procline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc,
+        time_t deltasec, time_t deltatic, time_t hz,
+        int osvers, int osrel, int ossub, char *tstamp,
+        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
+{
+       printf("%7.0lf %9.0lf %9.2lf  %8.2lf %8.2lf  %8.2lf\n",
+               (double)ss->cpu.csw    / deltasec,
+               (double)ss->cpu.devint / deltasec,
+               (double)ss->cpu.nprocs / deltasec,
+               ss->cpu.lavg1, ss->cpu.lavg5, ss->cpu.lavg15);
+       return 1;
+}
+
+/*
+** process statistics
+*/
+static void
+taskhead(int osvers, int osrel, int ossub)
+{
+       printf("clones/s pexit/s  curproc curzomb    thrrun thrslpi thrslpu "
+              "_procthr_");
+}
+
+static int
+taskline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc,
+        time_t deltasec, time_t deltatic, time_t hz,
+        int osvers, int osrel, int ossub, char *tstamp,
+        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
+{
+       if (ppres == 0)
+       {
+               printf("report not available for live measurements.....\n");
+               return 0;
+       }
+
+       if (ts)         /* process statistics available */
+       {
+               printf("%8.2lf %7.2lf  %7d %7d    %6d %7d %7d\n",
+                       (double)ss->cpu.nprocs / deltasec,
+                       (double)pexit          / deltasec,
+                       nactproc-pexit, pzombie, ntrun, ntslpi, ntslpu);
+       }
+       else
+       {
+               printf("%8.2lf %7.2lf  %7d %7d\n",
+                       (double)ss->cpu.nprocs / deltasec,
+                       (double)pexit          / deltasec,
+                       nactproc-pexit, pzombie);
+       }
+
+       return 1;
+}
+
+/*
+** memory- & swap-usage
+*/
+static void
+memhead(int osvers, int osrel, int ossub)
+{
+       printf("memtotal memfree buffers cached dirty slabmem"
+              "  swptotal swpfree _mem_"             );
+}
+
+static int
+memline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc,
+        time_t deltasec, time_t deltatic, time_t hz,
+        int osvers, int osrel, int ossub, char *tstamp,
+        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
+{
+       unsigned int    mbadness, sbadness;
+
+       if (membadness)
+               mbadness = ((ss->mem.physmem  - ss->mem.freemem -
+                            ss->mem.cachemem - ss->mem.buffermem)
+                                      * 100.0 / ss->mem.physmem) 
+                                      * 100   / membadness;
+       else
+               mbadness = 0;
+
+        if (swpbadness)
+               sbadness = ((ss->mem.totswap - ss->mem.freeswap)
+                                      * 100.0 / ss->mem.totswap)
+                                       * 100   / swpbadness;
+        else
+                sbadness = 0;
+
+       preprint(mbadness >= sbadness ? mbadness : sbadness);
+
+       printf("%7lldM %6lldM %6lldM %5lldM %4lldM %6lldM  %7lldM %6lldM",
+               ss->mem.physmem   * (pagesize / 1024) /1024,
+               ss->mem.freemem   * (pagesize / 1024) /1024,
+               ss->mem.buffermem * (pagesize / 1024) /1024,
+               ss->mem.cachemem  * (pagesize / 1024) /1024,
+               ss->mem.cachedrt  * (pagesize / 1024) /1024,
+               ss->mem.slabmem   * (pagesize / 1024) /1024,
+               ss->mem.totswap   * (pagesize / 1024) /1024,
+               ss->mem.freeswap  * (pagesize / 1024) /1024);
+
+       postprint(mbadness >= sbadness ? mbadness : sbadness);
+
+       return 1;
+}
+
+/*
+** swapping statistics
+*/
+static void
+swaphead(int osvers, int osrel, int ossub)
+{
+       printf("pagescan/s   swapin/s  swapout/s      "
+              "  commitspc  commitlim   _swap_");
+}
+
+static int
+swapline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc,
+        time_t deltasec, time_t deltatic, time_t hz,
+        int osvers, int osrel, int ossub, char *tstamp,
+        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
+{
+       unsigned int    badness;
+
+       if (membadness)
+                badness = (ss->mem.swouts / deltasec * pagbadness)
+                               * 100 / membadness;
+       else
+               badness = 0;
+
+       /*
+       ** take care that this line is anyhow colored for
+       ** 'almost critical' in case of swapouts > 1 per second
+       */
+       if (ss->mem.swouts / deltasec > 0  &&
+           pagbadness && almostcrit && badness < almostcrit)
+               badness = almostcrit;
+
+       if (ss->mem.commitlim && ss->mem.committed > ss->mem.commitlim)
+               badness = 100;         /* force colored output */
+
+       preprint(badness);
+
+       printf("%10.2lf  %9.2lf  %9.2lf       %9lluM %9lluM",
+               (double)ss->mem.pgscans / deltasec,
+               (double)ss->mem.swins   / deltasec,
+               (double)ss->mem.swouts  / deltasec,
+                       ss->mem.committed * (pagesize / 1024) / 1024,
+                       ss->mem.commitlim * (pagesize / 1024) / 1024);
+
+       postprint(badness);
+
+       return 1;
+}
+
+/*
+** disk statistics
+*/
+static void
+lvmhead(int osvers, int osrel, int ossub)
+{
+       printf("disk           busy read/s KB/read  "
+              "writ/s KB/writ avque avserv _lvm_");
+}
+
+static void
+mddhead(int osvers, int osrel, int ossub)
+{
+       printf("disk           busy read/s KB/read  "
+              "writ/s KB/writ avque avserv _mdd_");
+}
+
+static void
+dskhead(int osvers, int osrel, int ossub)
+{
+       printf("disk           busy read/s KB/read  "
+              "writ/s KB/writ avque avserv _dsk_");
+}
+
+static int
+gendskline(struct sstat *ss, char *tstamp, char selector)
+{
+       static char     firstcall = 1;
+       register int    i, nlines = 0, nunit = 0;
+       count_t         mstot, iotot;
+       struct perdsk   *dp;
+       unsigned int    badness;
+
+       switch (selector)
+       {
+          case 'l':
+               dp      = ss->dsk.lvm;
+               nunit   = ss->dsk.nlvm;
+               break;
+
+          case 'm':
+               dp      = ss->dsk.mdd;
+               nunit   = ss->dsk.nmdd;
+               break;
+
+          case 'd':
+               dp      = ss->dsk.dsk;
+               nunit   = ss->dsk.ndsk;
+               break;
+
+          default:
+               return 0;
+       }
+
+        mstot  = (ss->cpu.all.stime + ss->cpu.all.utime +
+                  ss->cpu.all.ntime + ss->cpu.all.itime +
+                  ss->cpu.all.wtime + ss->cpu.all.Itime +
+                  ss->cpu.all.Stime + ss->cpu.all.steal  )
+                               * (count_t)1000 / hertz / ss->cpu.nrcpu;
+
+       for (i=0; i < nunit; i++, dp++)
+       {
+               char    *pn;
+               int     len;
+
+               if ( (iotot = dp->nread + dp->nwrite) == 0 &&
+                    !firstcall && !allresources             )
+                       continue;       /* no activity on this disk */
+
+               /*
+               ** disk was active during last interval; print info
+               */
+               if (nlines++)
+                       printf("%s  ", tstamp);
+
+               if (dskbadness)
+                       badness = (dp->io_ms * 100.0 / mstot) * 100/dskbadness;
+                else
+                       badness = 0;
+
+               preprint(badness);
+
+               if ( (len = strlen(dp->name)) > 14)
+                       pn = dp->name + len - 14;
+               else
+                       pn = dp->name;
+
+               printf("%-14s %3.0lf%% %6.1lf %7.1lf %7.1lf %7.1lf "
+                      "%5.1lf %6.2lf ms",
+                       pn,
+                       mstot ? (double)dp->io_ms  *  100.0 / mstot   : 0.0,
+                       mstot ? (double)dp->nread  * 1000.0 / mstot   : 0.0,
+                       dp->nread  ?
+                               (double)dp->nrsect / dp->nread / 2.0  : 0.0,
+                       mstot ? (double)dp->nwrite * 1000.0 / mstot   : 0.0,
+                       dp->nwrite ?
+                               (double)dp->nwsect / dp->nwrite / 2.0 : 0.0,
+                       dp->io_ms  ? (double)dp->avque / dp->io_ms    : 0.0,
+                       iotot ? (double)dp->io_ms  / iotot            : 0.0);
+
+               postprint(badness);
+       }
+
+       if (nlines == 0)
+       {
+               printf("\n");
+               nlines++;
+       }
+
+       firstcall = 0;
+
+       return nlines;
+}
+
+static int
+lvmline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc,
+        time_t deltasec, time_t deltatic, time_t hz,
+        int osvers, int osrel, int ossub, char *tstamp,
+        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
+{
+       return gendskline(ss, tstamp, 'l');
+}
+
+static int
+mddline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc,
+        time_t deltasec, time_t deltatic, time_t hz,
+        int osvers, int osrel, int ossub, char *tstamp,
+        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
+{
+       return gendskline(ss, tstamp, 'm');
+}
+
+static int
+dskline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc,
+        time_t deltasec, time_t deltatic, time_t hz,
+        int osvers, int osrel, int ossub, char *tstamp,
+        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
+{
+       return gendskline(ss, tstamp, 'd');
+}
+
+/*
+** NFS client statistics
+*/
+static void
+nfmhead(int osvers, int osrel, int ossub)
+{
+       printf("mounted_device                          physread/s  physwrit/s"
+               "  _nfm_");
+}
+
+static int
+nfmline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc,
+        time_t deltasec, time_t deltatic, time_t hz,
+        int osvers, int osrel, int ossub, char *tstamp,
+        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
+{
+       static char     firstcall = 1;
+       register long   i, nlines = 0;
+       char            *pn, state;
+       int             len;
+
+       for (i=0; i < ss->nfs.nfsmounts.nrmounts; i++)  /* per NFS mount */
+       {
+               /*
+               ** print for the first sample all mounts that
+               ** are found; afterwards print only the mounts
+               ** that were really active during the interval
+               */
+               if (firstcall                                  ||
+                   allresources                               ||
+                   ss->nfs.nfsmounts.nfsmnt[i].age < deltasec ||
+                   ss->nfs.nfsmounts.nfsmnt[i].bytestotread   ||
+                   ss->nfs.nfsmounts.nfsmnt[i].bytestotwrite    )
+               {
+                       if (nlines++)
+                               printf("%s  ", tstamp);
+
+                       if ( (len = strlen(ss->nfs.nfsmounts.nfsmnt[i].mountdev)) > 38)
+                               pn = ss->nfs.nfsmounts.nfsmnt[i].mountdev + len - 38;
+                       else
+                               pn = ss->nfs.nfsmounts.nfsmnt[i].mountdev;
+
+                       if (ss->nfs.nfsmounts.nfsmnt[i].age < deltasec)
+                               state = 'M';
+                       else
+                               state = ' ';
+
+                       printf("%-38s %10.3lfK %10.3lfK    %c\n", 
+                           pn,
+                           (double)ss->nfs.nfsmounts.nfsmnt[i].bytestotread  /
+                                                               1024 / deltasec,
+                           (double)ss->nfs.nfsmounts.nfsmnt[i].bytestotwrite /
+                                                               1024 / deltasec,
+                           state);
+               }
+       }
+
+       if (nlines == 0)
+       {
+               printf("\n");
+               nlines++;
+       }
+
+       firstcall= 0;
+       return nlines;
+}
+
+static void
+nfchead(int osvers, int osrel, int ossub)
+{
+       printf("     rpc/s   rpcread/s  rpcwrite/s  retrans/s  autrefresh/s   "
+               "  _nfc_");
+}
+
+static int
+nfcline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc,
+        time_t deltasec, time_t deltatic, time_t hz,
+        int osvers, int osrel, int ossub, char *tstamp,
+        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
+{
+       printf("%10.2lf  %10.2lf  %10.2lf %10.2lf  %12.2lf\n",
+               (double)ss->nfs.client.rpccnt        / deltasec,
+               (double)ss->nfs.client.rpcread       / deltasec,
+               (double)ss->nfs.client.rpcwrite      / deltasec,
+               (double)ss->nfs.client.rpcretrans    / deltasec,
+               (double)ss->nfs.client.rpcautrefresh / deltasec);
+
+       return 1;
+}
+
+static void
+nfshead(int osvers, int osrel, int ossub)
+{
+       printf("  rpc/s  rpcread/s rpcwrite/s MBcr/s  MBcw/s  "
+               "nettcp/s netudp/s _nfs_");
+}
+
+static int
+nfsline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc,
+        time_t deltasec, time_t deltatic, time_t hz,
+        int osvers, int osrel, int ossub, char *tstamp,
+        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
+{
+       printf("%7.2lf %10.2lf %10.2lf %6.2lf %7.2lf %9.2lf %8.2lf\n",
+               (double)ss->nfs.server.rpccnt    / deltasec,
+               (double)ss->nfs.server.rpcread   / deltasec,
+               (double)ss->nfs.server.rpcwrite  / deltasec,
+               (double)ss->nfs.server.nrbytes / 1024.0 / 1024.0 / deltasec,
+               (double)ss->nfs.server.nwbytes / 1024.0 / 1024.0 / deltasec,
+               (double)ss->nfs.server.nettcpcnt / deltasec,
+               (double)ss->nfs.server.netudpcnt / deltasec);
+
+       return 1;
+}
+
+/*
+** network-interface statistics
+*/
+static void
+ifhead(int osvers, int osrel, int ossub)
+{
+       printf("interf busy ipack/s opack/s iKbyte/s oKbyte/s "
+              "imbps ombps maxmbps_if_");
+}
+
+static int
+ifline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc,
+        time_t deltasec, time_t deltatic, time_t hz,
+        int osvers, int osrel, int ossub, char *tstamp,
+        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
+{
+       static char     firstcall = 1;
+       register long   i, nlines = 0;
+       double          busy;
+       char            busyval[16], dupval;
+       unsigned int    badness;
+       char            *pn;
+       int             len;
+
+       for (i=0; i < ss->intf.nrintf; i++)     /* per interface */
+       {
+               count_t ival, oval;
+
+               /*
+               ** print for the first sample all interfaces which
+               ** are found; afterwards print only the interfaces
+               ** which were really active during the interval
+               */
+               if (!firstcall && !allresources &&
+                   !ss->intf.intf[i].rpack && !ss->intf.intf[i].spack)
+                       continue;
+
+               /*
+               ** convert byte-transfers to bit-transfers     (*       8)
+               ** convert bit-transfers  to megabit-transfers (/ 1000000)
+               ** per second
+               */
+               ival = ss->intf.intf[i].rbyte/125000/deltasec;
+               oval = ss->intf.intf[i].sbyte/125000/deltasec;
+
+               /*
+               ** calculate busy-percentage for interface
+               */
+               if (ss->intf.intf[i].speed)     /* speed known? */
+               {
+                       if (ss->intf.intf[i].duplex)
+                               busy = (ival > oval ? ival*100 : oval*100) /
+                                       ss->intf.intf[i].speed;
+                       else
+                               busy = (ival + oval) * 100 /
+                                       ss->intf.intf[i].speed;
+
+                       // especially with wireless, the speed might have
+                       // dropped temporarily to a very low value (snapshot)
+                       // it might be better to take the speed of the
+                       // previous sample
+                       if (busy > 100 && ss->intf.intf[i].speed <
+                                               ss->intf.intf[i].speedp )
+                       {
+                               ss->intf.intf[i].speed =
+                                       ss->intf.intf[i].speedp;
+
+                               if (ss->intf.intf[i].duplex)
+                                       busy = (ival > oval ?
+                                               ival*100 : oval*100) /
+                                               ss->intf.intf[i].speed;
+                               else
+                                       busy = (ival + oval) * 100 /
+                                               ss->intf.intf[i].speed;
+                       }
+
+                       snprintf(busyval, sizeof busyval,
+                                               "%3.0lf%%", busy);
+               }
+               else
+               {
+                       strcpy(busyval, "?"); /* speed unknown */
+                       busy = 0;
+               }
+
+               if (nlines++)
+                       printf("%s  ", tstamp);
+
+               if (ss->intf.intf[i].speed)
+               {
+                       if (ss->intf.intf[i].duplex)
+                               dupval = 'f';
+                       else
+                               dupval = 'h';
+               }
+               else
+               {
+                       dupval = ' ';
+               }
+
+               if (netbadness)
+                       badness = busy * 100 / netbadness;
+               else
+                       badness = 0;
+
+               if ( (len = strlen(ss->intf.intf[i].name)) > 6)
+                       pn = ss->intf.intf[i].name + len - 6;
+               else
+                       pn = ss->intf.intf[i].name;
+
+               preprint(badness);
+
+               printf("%-6s %4s %7.1lf %7.1lf %8.0lf %8.0lf "
+                      "%5lld %5lld %7ld %c", 
+                       pn, busyval,
+                       (double)ss->intf.intf[i].rpack / deltasec,
+                       (double)ss->intf.intf[i].spack / deltasec,
+                       (double)ss->intf.intf[i].rbyte / 1024 / deltasec,
+                       (double)ss->intf.intf[i].sbyte / 1024 / deltasec,
+                       ival, oval,
+                       ss->intf.intf[i].speed, dupval);
+
+               postprint(badness);
+       }
+
+       if (nlines == 0)
+       {
+               printf("\n");
+               nlines++;
+       }
+
+       firstcall = 0;
+       return nlines;
+}
+
+static void
+IFhead(int osvers, int osrel, int ossub)
+{
+       printf("interf ierr/s oerr/s coll/s idrop/s odrop/s "
+              "iframe/s ocarrier/s  _if_");
+}
+
+static int
+IFline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc,
+        time_t deltasec, time_t deltatic, time_t hz,
+        int osvers, int osrel, int ossub, char *tstamp,
+        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
+{
+       static char     firstcall = 1;
+       register long   i, nlines = 0;
+       char            *pn;
+       int             len;
+
+       for (i=0; i < ss->intf.nrintf; i++)     /* per interface */
+       {
+               /*
+               ** print for the first sample all interfaces which
+               ** are found; afterwards print only the interfaces
+               ** which were really active during the interval
+               */
+               if (!firstcall && !allresources &&
+                   !ss->intf.intf[i].rpack && !ss->intf.intf[i].spack)
+                       continue;
+
+               if (nlines++)
+                       printf("%s  ", tstamp);
+
+               if ( (len = strlen(ss->intf.intf[i].name)) > 6)
+                       pn = ss->intf.intf[i].name + len - 6;
+               else
+                       pn = ss->intf.intf[i].name;
+
+               printf("%-6s %6.2lf %6.2lf %6.2lf %7.2lf %7.2lf "
+                      "%8.2lf %10.2lf\n", 
+                       pn,
+                       (double)ss->intf.intf[i].rerrs    / deltasec,
+                       (double)ss->intf.intf[i].serrs    / deltasec,
+                       (double)ss->intf.intf[i].scollis  / deltasec,
+                       (double)ss->intf.intf[i].rdrop    / deltasec,
+                       (double)ss->intf.intf[i].sdrop    / deltasec,
+                       (double)ss->intf.intf[i].rframe   / deltasec,
+                       (double)ss->intf.intf[i].scarrier / deltasec);
+       }
+
+       if (nlines == 0)
+       {
+               printf("\n");
+               nlines++;
+       }
+
+       firstcall= 0;
+       return nlines;
+}
+
+/*
+** IP version 4 statistics
+*/
+static void
+ipv4head(int osvers, int osrel, int ossub)
+{
+       printf("inrecv/s outreq/s indeliver/s forward/s "
+              "reasmok/s fragcreat/s  _ipv4_");
+}
+
+static int
+ipv4line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc,
+        time_t deltasec, time_t deltatic, time_t hz,
+        int osvers, int osrel, int ossub, char *tstamp,
+        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
+{
+       printf("%8.1lf %8.1lf %11.1lf %9.1lf %9.1lf %11.1lf\n", 
+               (double)ss->net.ipv4.InReceives  / deltasec,
+               (double)ss->net.ipv4.OutRequests / deltasec,
+               (double)ss->net.ipv4.InDelivers  / deltasec,
+               (double)ss->net.ipv4.Forwarding  / deltasec,
+               (double)ss->net.ipv4.ReasmOKs    / deltasec,
+               (double)ss->net.ipv4.FragCreates / deltasec);
+       return 1;
+}
+
+static void
+IPv4head(int osvers, int osrel, int ossub)
+{
+       printf("in: dsc/s hder/s ader/s unkp/s ratim/s rfail/s "
+              "out: dsc/s nrt/s_ipv4_");
+}
+
+static int
+IPv4line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc,
+        time_t deltasec, time_t deltatic, time_t hz,
+        int osvers, int osrel, int ossub, char *tstamp,
+        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
+{
+       printf("    %5.1lf %6.1lf %6.1lf %6.1lf %7.1lf %7.1lf  "
+              "    %5.1lf %5.1lf\n", 
+               (double)ss->net.ipv4.InDiscards      / deltasec,
+               (double)ss->net.ipv4.InHdrErrors     / deltasec,
+               (double)ss->net.ipv4.InAddrErrors    / deltasec,
+               (double)ss->net.ipv4.InUnknownProtos / deltasec,
+               (double)ss->net.ipv4.ReasmTimeout    / deltasec,
+               (double)ss->net.ipv4.ReasmFails      / deltasec,
+               (double)ss->net.ipv4.OutDiscards     / deltasec,
+               (double)ss->net.ipv4.OutNoRoutes     / deltasec);
+       return 1;
+}
+
+/*
+** ICMP version 4 statistics
+*/
+static void
+icmpv4head(int osvers, int osrel, int ossub)
+{
+       printf("intot/s outtot/s  inecho/s inerep/s  "
+              "otecho/s oterep/s       _icmpv4_"   );
+}
+
+static int
+icmpv4line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc,
+        time_t deltasec, time_t deltatic, time_t hz,
+        int osvers, int osrel, int ossub, char *tstamp,
+        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
+{
+       printf("%7.1lf %8.1lf  %8.2lf %8.2lf  %8.2lf %8.2lf\n", 
+               (double)ss->net.icmpv4.InMsgs      / deltasec, 
+               (double)ss->net.icmpv4.OutMsgs     / deltasec, 
+               (double)ss->net.icmpv4.InEchos     / deltasec, 
+               (double)ss->net.icmpv4.OutEchos    / deltasec, 
+               (double)ss->net.icmpv4.InEchoReps  / deltasec, 
+               (double)ss->net.icmpv4.OutEchoReps / deltasec);
+       return 1;
+}
+
+static void
+ICMPv4head(int osvers, int osrel, int ossub)
+{
+       printf("ierr/s isq/s ird/s idu/s ite/s "
+              "oerr/s osq/s ord/s odu/s ote/s_icmpv4_");
+}
+
+static int
+ICMPv4line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc,
+        time_t deltasec, time_t deltatic, time_t hz,
+        int osvers, int osrel, int ossub, char *tstamp,
+        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
+{
+       printf("%6.2lf %5.2lf %5.2lf %5.2lf %5.2lf "
+              "%6.2lf %5.2lf %5.2lf %5.2lf %5.2lf\n", 
+               (double)ss->net.icmpv4.InErrors        / deltasec,
+               (double)ss->net.icmpv4.InSrcQuenchs    / deltasec,
+               (double)ss->net.icmpv4.InRedirects     / deltasec,
+               (double)ss->net.icmpv4.InDestUnreachs  / deltasec,
+               (double)ss->net.icmpv4.InTimeExcds     / deltasec,
+               (double)ss->net.icmpv4.OutErrors       / deltasec,
+               (double)ss->net.icmpv4.OutSrcQuenchs   / deltasec,
+               (double)ss->net.icmpv4.OutRedirects    / deltasec,
+               (double)ss->net.icmpv4.OutDestUnreachs / deltasec,
+               (double)ss->net.icmpv4.OutTimeExcds    / deltasec);
+       return 1;
+}
+
+/*
+** UDP version 4 statistics
+*/
+static void
+udpv4head(int osvers, int osrel, int ossub)
+{
+       printf("indgram/s outdgram/s   inerr/s  noport/s    "
+              "                  _udpv4_");
+}
+
+static int
+udpv4line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc,
+        time_t deltasec, time_t deltatic, time_t hz,
+        int osvers, int osrel, int ossub, char *tstamp,
+        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
+{
+       printf("%9.1lf %10.1lf   %7.2lf %9.2lf\n",
+               (double)ss->net.udpv4.InDatagrams  / deltasec,
+               (double)ss->net.udpv4.OutDatagrams / deltasec,
+               (double)ss->net.udpv4.InErrors     / deltasec,
+               (double)ss->net.udpv4.NoPorts      / deltasec);
+       return 1;
+}
+
+/*
+** IP version 6 statistics
+*/
+static void
+ipv6head(int osvers, int osrel, int ossub)
+{
+       printf("inrecv/s outreq/s inmc/s outmc/s indeliv/s "
+              "reasmok/s fragcre/s _ipv6_");
+}
+
+static int
+ipv6line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc,
+        time_t deltasec, time_t deltatic, time_t hz,
+        int osvers, int osrel, int ossub, char *tstamp,
+        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
+{
+       printf("%8.1lf %8.1lf %6.1lf %7.1lf %9.1lf %9.1lf %9.1lf\n", 
+               (double)ss->net.ipv6.Ip6InReceives   / deltasec,
+               (double)ss->net.ipv6.Ip6OutRequests  / deltasec,
+               (double)ss->net.ipv6.Ip6InMcastPkts  / deltasec,
+               (double)ss->net.ipv6.Ip6OutMcastPkts / deltasec,
+               (double)ss->net.ipv6.Ip6InDelivers   / deltasec,
+               (double)ss->net.ipv6.Ip6ReasmOKs     / deltasec,
+               (double)ss->net.ipv6.Ip6FragCreates  / deltasec);
+       return 1;
+}
+
+static void
+IPv6head(int osvers, int osrel, int ossub)
+{
+       printf("in: dsc/s hder/s ader/s unkp/s ratim/s rfail/s "
+              "out: dsc/s nrt/s_ipv6_");
+}
+
+static int
+IPv6line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc,
+        time_t deltasec, time_t deltatic, time_t hz,
+        int osvers, int osrel, int ossub, char *tstamp,
+        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
+{
+       printf("    %5.1lf %6.1lf %6.1lf %6.1lf %7.1lf %7.1lf  "
+              "    %5.1lf %5.1lf\n", 
+               (double)ss->net.ipv6.Ip6InDiscards      / deltasec,
+               (double)ss->net.ipv6.Ip6InHdrErrors     / deltasec,
+               (double)ss->net.ipv6.Ip6InAddrErrors    / deltasec,
+               (double)ss->net.ipv6.Ip6InUnknownProtos / deltasec,
+               (double)ss->net.ipv6.Ip6ReasmTimeout    / deltasec,
+               (double)ss->net.ipv6.Ip6ReasmFails      / deltasec,
+               (double)ss->net.ipv6.Ip6OutDiscards  / deltasec,
+               (double)ss->net.ipv6.Ip6OutNoRoutes     / deltasec);
+       return 1;
+}
+
+/*
+** ICMP version 6 statistics
+*/
+static void
+icmpv6head(int osvers, int osrel, int ossub)
+{
+       printf("intot/s outtot/s inerr/s innsol/s innadv/s "
+              "otnsol/s otnadv/s  _icmp6_"   );
+}
+
+static int
+icmpv6line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc,
+        time_t deltasec, time_t deltatic, time_t hz,
+        int osvers, int osrel, int ossub, char *tstamp,
+        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
+{
+       printf("%7.1lf %8.1lf %7.2lf %8.2lf %8.2lf %8.2lf %8.2lf\n", 
+               (double)ss->net.icmpv6.Icmp6InMsgs                  / deltasec, 
+               (double)ss->net.icmpv6.Icmp6OutMsgs                 / deltasec, 
+               (double)ss->net.icmpv6.Icmp6InErrors                / deltasec, 
+               (double)ss->net.icmpv6.Icmp6InNeighborSolicits      / deltasec, 
+               (double)ss->net.icmpv6.Icmp6InNeighborAdvertisements/ deltasec, 
+               (double)ss->net.icmpv6.Icmp6OutNeighborSolicits     / deltasec, 
+               (double)ss->net.icmpv6.Icmp6OutNeighborAdvertisements
+                                                               /deltasec);
+       return 1;
+}
+
+static void
+ICMPv6head(int osvers, int osrel, int ossub)
+{
+       printf("iecho/s ierep/s oerep/s idu/s odu/s ird/s ord/s ite/s "
+              "ote/s  _icmpv6_");
+}
+
+static int
+ICMPv6line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc,
+        time_t deltasec, time_t deltatic, time_t hz,
+        int osvers, int osrel, int ossub, char *tstamp,
+        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
+{
+       printf("%7.2lf %7.2lf %7.2lf %5.2lf %5.2lf "
+              "%5.2lf %5.2lf %5.2lf %5.2lf\n", 
+               (double)ss->net.icmpv6.Icmp6InEchos         / deltasec,
+               (double)ss->net.icmpv6.Icmp6InEchoReplies   / deltasec,
+               (double)ss->net.icmpv6.Icmp6OutEchoReplies  / deltasec,
+               (double)ss->net.icmpv6.Icmp6InDestUnreachs  / deltasec,
+               (double)ss->net.icmpv6.Icmp6OutDestUnreachs / deltasec,
+               (double)ss->net.icmpv6.Icmp6InRedirects     / deltasec,
+               (double)ss->net.icmpv6.Icmp6OutRedirects    / deltasec,
+               (double)ss->net.icmpv6.Icmp6InTimeExcds     / deltasec,
+               (double)ss->net.icmpv6.Icmp6OutTimeExcds    / deltasec);
+       return 1;
+}
+
+/*
+** UDP version 6 statistics
+*/
+static void
+udpv6head(int osvers, int osrel, int ossub)
+{
+       printf("indgram/s outdgram/s   inerr/s  noport/s    "
+              "                  _udpv6_");
+}
+
+static int
+udpv6line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc,
+        time_t deltasec, time_t deltatic, time_t hz,
+        int osvers, int osrel, int ossub, char *tstamp,
+        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
+{
+       printf("%9.1lf %10.1lf   %7.2lf %9.2lf\n",
+               (double)ss->net.udpv6.Udp6InDatagrams  / deltasec,
+               (double)ss->net.udpv6.Udp6OutDatagrams / deltasec,
+               (double)ss->net.udpv6.Udp6InErrors     / deltasec,
+               (double)ss->net.udpv6.Udp6NoPorts      / deltasec);
+       return 1;
+}
+
+/*
+** TCP statistics
+*/
+static void
+tcphead(int osvers, int osrel, int ossub)
+{
+       printf("insegs/s outsegs/s  actopen/s pasopen/s  "
+              "nowopen                _tcp_");
+}
+
+static int
+tcpline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc,
+        time_t deltasec, time_t deltatic, time_t hz,
+        int osvers, int osrel, int ossub, char *tstamp,
+        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
+{
+       printf("%8.1lf %9.1lf  %9.1lf %9.1lf  %7lld\n",
+               (double)ss->net.tcp.InSegs       / deltasec,
+               (double)ss->net.tcp.OutSegs      / deltasec,
+               (double)ss->net.tcp.ActiveOpens  / deltasec,
+               (double)ss->net.tcp.PassiveOpens / deltasec,
+                       ss->net.tcp.CurrEstab);
+       return 1;
+}
+
+static void
+TCPhead(int osvers, int osrel, int ossub)
+{
+       printf("inerr/s  retrans/s  attfail/s  "
+              "estabreset/s  outreset/s         _tcp_");
+}
+
+static int
+TCPline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc,
+        time_t deltasec, time_t deltatic, time_t hz,
+        int osvers, int osrel, int ossub, char *tstamp,
+        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
+{
+       printf("%7.1lf  %9.1lf  %9.1lf  %12.1lf  %10.1lf\n",
+               (double)ss->net.tcp.InErrs       / deltasec,
+               (double)ss->net.tcp.RetransSegs  / deltasec,
+               (double)ss->net.tcp.AttemptFails / deltasec,
+               (double)ss->net.tcp.EstabResets  / deltasec,
+               (double)ss->net.tcp.OutRsts      / deltasec);
+       return 1;
+}
+
+#if    HTTPSTATS
+static void
+httphead(int osvers, int osrel, int ossub)
+{
+       printf("requests/s  Kbytes/s  bytes/req    "
+              "idleworkers busyworkers     _http_");
+}
+
+static int
+httpline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc,
+        time_t deltasec, time_t deltatic, time_t hz,
+        int osvers, int osrel, int ossub, char *tstamp,
+        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
+{
+       printf("%10.2lf  %8.2lf  %9.2lf    %11d %11d\n",
+               (double)ss->www.accesses      / deltasec,
+               (double)ss->www.totkbytes     / deltasec,
+               ss->www.accesses ? 
+                   (double)ss->www.totkbytes*1024/ss->www.accesses : 0,
+                       ss->www.iworkers,
+                       ss->www.bworkers);
+
+       return 1;
+}
+#endif
+
+/*
+** per-process statistics: top-3 processor consumers
+*/
+static void
+topchead(int osvers, int osrel, int ossub)
+{
+       printf("  pid command  cpu%% |   pid command  cpu%% | "
+              "  pid command  cpu%%_top3_");
+}
+
+static int
+topcline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc,
+        time_t deltasec, time_t deltatic, time_t hz,
+        int osvers, int osrel, int ossub, char *tstamp,
+        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
+{
+       count_t availcpu;
+
+       if (!ts)
+       {
+               printf("report not available.....\n");
+               return 0;
+       }
+
+       /*
+       ** sort process list in cpu order
+       */
+       qsort(ps, nactproc, sizeof(struct tstat *), compcpu);
+
+       availcpu  = ss->cpu.all.stime + ss->cpu.all.utime +
+                   ss->cpu.all.ntime + ss->cpu.all.itime +
+                   ss->cpu.all.wtime + ss->cpu.all.Itime +
+                   ss->cpu.all.Stime + ss->cpu.all.steal;
+
+       availcpu /= ss->cpu.nrcpu;
+
+       if (availcpu == 0)
+               availcpu = 1;   /* avoid divide-by-zero */
+
+       if (nactproc >= 1 && (ps[0])->cpu.stime + (ps[0])->cpu.utime > 0)
+           printf("%5d %-8.8s %3.0lf%% | ",
+             (ps[0])->gen.pid, (ps[0])->gen.name,
+             (double)((ps[0])->cpu.stime + (ps[0])->cpu.utime)*100.0/availcpu);
+        else
+           printf("%19s | ", " ");
+
+       if (nactproc >= 2 && (ps[1])->cpu.stime + (ps[1])->cpu.utime > 0)
+           printf("%5d %-8.8s %3.0lf%% | ",
+             (ps[1])->gen.pid, (ps[1])->gen.name,
+             (double)((ps[1])->cpu.stime + (ps[1])->cpu.utime)*100.0/availcpu);
+        else
+           printf("%19s | ", " ");
+
+       if (nactproc >= 3 && (ps[2])->cpu.stime + (ps[2])->cpu.utime > 0)
+           printf("%5d %-8.8s %3.0lf%%\n",
+             (ps[2])->gen.pid, (ps[2])->gen.name,
+             (double)((ps[2])->cpu.stime + (ps[2])->cpu.utime)*100.0/availcpu);
+        else
+           printf("%19s\n", " ");
+
+
+       return 1;
+}
+
+/*
+** per-process statistics: top-3 memory consumers
+*/
+static void
+topmhead(int osvers, int osrel, int ossub)
+{
+       printf("  pid command  mem%% |   pid command  mem%% | "
+              "  pid command  mem%%_top3_");
+}
+
+static int
+topmline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc,
+        time_t deltasec, time_t deltatic, time_t hz,
+        int osvers, int osrel, int ossub, char *tstamp,
+        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
+{
+       count_t         availmem;
+
+       if (!ts)
+       {
+               printf("report not available.....\n");
+               return 0;
+       }
+
+       /*
+       ** sort process list in memory order
+       */
+       qsort(ps, nactproc, sizeof(struct tstat *), compmem);
+
+       availmem  = ss->mem.physmem * pagesize/1024;
+
+        if (nactproc >= 1)
+           printf("%5d %-8.8s %3.0lf%% | ",
+             (ps[0])->gen.pid, (ps[0])->gen.name,
+             (double)(ps[0])->mem.rmem * 100.0 / availmem);
+        else
+           printf("%19s | ", " ");
+
+        if (nactproc >= 2)
+           printf("%5d %-8.8s %3.0lf%% | ",
+             (ps[1])->gen.pid, (ps[1])->gen.name,
+             (double)(ps[1])->mem.rmem * 100.0 / availmem);
+        else
+           printf("%19s | ", " ");
+
+        if (nactproc >= 3)
+           printf("%5d %-8.8s %3.0lf%%\n",
+             (ps[2])->gen.pid, (ps[2])->gen.name,
+             (double)(ps[2])->mem.rmem * 100.0 / availmem);
+        else
+           printf("%19s\n", " ");
+
+
+       return 1;
+}
+
+/*
+** per-process statistics: top-3 disk consumers
+*/
+static void
+topdhead(int osvers, int osrel, int ossub)
+{
+       printf("  pid command  dsk%% |   pid command  dsk%% | "
+              "  pid command  dsk%%_top3_");
+}
+
+static int
+topdline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc,
+        time_t deltasec, time_t deltatic, time_t hz,
+        int osvers, int osrel, int ossub, char *tstamp,
+        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
+{
+       int             i;
+       count_t         availdsk;
+
+       if (!ts)
+       {
+               printf("report not available.....\n");
+               return 0;
+       }
+
+       if ( !(supportflags & IOSTAT) )
+       {
+               printf("no per-process disk counters available.....\n");
+               return 0;
+       }
+
+       /*
+       ** determine total disk accesses for all processes
+       */
+       for (i=0, availdsk=0; i < nactproc; i++)
+       {
+               availdsk += (ps[i])->dsk.rio + (ps[i])->dsk.wio;
+       }
+
+       if (availdsk == 0)
+               availdsk = 1;
+
+       /*
+       ** sort process list in disk order
+       */
+       qsort(ps, nactproc, sizeof(struct tstat *), compdsk);
+
+        if (nactproc >= 1 && (ps[0])->dsk.rio + (ps[0])->dsk.wio > 0)
+           printf("%5d %-8.8s %3.0lf%% | ",
+             (ps[0])->gen.pid, (ps[0])->gen.name,
+             (double)((ps[0])->dsk.rio+(ps[0])->dsk.wio) *100.0/availdsk);
+        else
+           printf("%19s | ", " ");
+
+        if (nactproc >= 2 && (ps[1])->dsk.rio + (ps[1])->dsk.wio > 0)
+           printf("%5d %-8.8s %3.0lf%% | ",
+             (ps[1])->gen.pid, (ps[1])->gen.name,
+             (double)((ps[1])->dsk.rio+(ps[1])->dsk.wio) *100.0/availdsk);
+        else
+           printf("%19s | ", " ");
+
+        if (nactproc >= 3 && (ps[2])->dsk.rio + (ps[2])->dsk.wio > 0)
+           printf("%5d %-8.8s %3.0lf%%\n",
+             (ps[2])->gen.pid, (ps[2])->gen.name,
+             (double)((ps[2])->dsk.rio+(ps[2])->dsk.wio) *100.0/availdsk);
+        else
+           printf("%19s\n", " ");
+
+
+       return 1;
+}
+
+/*
+** per-process statistics: top-3 network consumers
+*/
+static void
+topnhead(int osvers, int osrel, int ossub)
+{
+       printf("  pid command  net%% |   pid command  net%% | "
+              "  pid command  net%%_top3_");
+}
+
+static int
+topnline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc,
+        time_t deltasec, time_t deltatic, time_t hz,
+        int osvers, int osrel, int ossub, char *tstamp,
+        int ppres,  int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie)
+{
+       int             i;
+       count_t         availnet;
+       count_t         totbytes;
+
+       if (!ts)
+       {
+               printf("report not available.....\n");
+               return 0;
+       }
+
+       if ( !(supportflags & NETATOP) )
+       {
+               printf("no per-process network counters available.....\n");
+               return 0;
+       }
+
+       /*
+       ** determine total network accesses for all processes
+       */
+       for (i=0, availnet=0; i < nactproc; i++)
+       {
+               availnet += (*(ps+i))->net.tcpssz + (*(ps+i))->net.tcprsz +
+                           (*(ps+i))->net.udpssz + (*(ps+i))->net.udprsz;
+       }
+
+       if (availnet == 0)
+               availnet = 1;
+
+       /*
+       ** sort process list in network order
+       */
+       qsort(ps, nactproc, sizeof(struct tstat *), compnet);
+
+        if (nactproc >= 1)
+       {
+               totbytes = (ps[0])->net.tcpssz + (ps[0])->net.tcprsz +
+                          (ps[0])->net.udpssz + (ps[0])->net.udprsz;
+
+               if (totbytes > 0)
+                       printf("%5d %-8.8s %3.0lf%% | ",
+                               (ps[0])->gen.pid, (ps[0])->gen.name,
+                               (double)totbytes * 100.0 / availnet);
+               else
+                       printf("%19s | ", " ");
+       }
+        else
+               printf("%19s | ", " ");
+
+        if (nactproc >= 2)
+       {
+               totbytes = (ps[1])->net.tcpssz + (ps[1])->net.tcprsz +
+                          (ps[1])->net.udpssz + (ps[1])->net.udprsz;
+
+               if (totbytes > 0)
+                       printf("%5d %-8.8s %3.0lf%% | ",
+                               (ps[1])->gen.pid, (ps[1])->gen.name,
+                               (double)totbytes * 100.0 / availnet);
+               else
+                       printf("%19s | ", " ");
+       }
+        else
+               printf("%19s | ", " ");
+
+        if (nactproc >= 3)
+       {
+               totbytes = (ps[2])->net.tcpssz + (ps[2])->net.tcprsz +
+                          (ps[2])->net.udpssz + (ps[2])->net.udprsz;
+
+               if (totbytes > 0)
+                       printf("%5d %-8.8s %3.0lf%%\n",
+                               (ps[2])->gen.pid, (ps[2])->gen.name,
+                               (double)totbytes * 100.0 / availnet);
+               else
+                       printf("%19s\n", " ");
+       }
+        else
+               printf("%19s\n", " ");
+
+
+       return 1;
+}
+
+/*********************************************************************/
+/* Function definition table.                                        */
+/*                                                                   */
+/* The layout of this table is as follows:                           */
+/*     Column 1:                                                     */
+/*        Boolean which indicates if the specified function is       */
+/*        active during a run of 'atopsar'. When started,            */
+/*        this boolean will be defined 'true' for all entries for    */
+/*        which the command-line flag has been specified. Initially  */
+/*        this column should contain 0 (false), unless this function */
+/*        is always required.                                        */
+/*        If no flags are specified for 'atopsar', the first entry   */
+/*        in this table is defined active (default flag).            */
+/*                                                                   */
+/*     Column 2:                                                     */
+/*        Categories of counters used by this function.              */
+/*           c = cpu  counters,    m = memory  counters,             */
+/*           d = disk counters,    n = network counters              */
+/*                                                                   */
+/*     Column 3:                                                     */
+/*        Flag which can be used as command-line argument to         */
+/*        select the function defined in this table-entry. Be sure   */
+/*        that a unique character is choosen.                        */
+/*        Notice that certain flags are reserved!                    */
+/*                                                                   */
+/*     Column 4:                                                     */
+/*        Entry-point of the 'printhead' function.                   */
+/*                                                                   */
+/*     Column 5:                                                     */
+/*        Entry-point of the 'printline' function.                   */
+/*                                                                   */
+/*     Column 6:                                                     */
+/*        Information about the statistics shown by the function     */
+/*        specified by the table-entry. This text is printed as      */
+/*        command-usage.                                             */
+/*********************************************************************/
+struct pridef pridef[] =
+{
+   {0,  "c",  'c',  cpuhead,   cpuline,        "cpu utilization",        },
+   {0,  "c",  'p',  prochead,  procline,       "process(or) load",       },
+   {0,  "c",  'P',  taskhead,  taskline,       "processes & threads",    },
+   {0,  "m",  'm',  memhead,   memline,        "memory & swapspace",     },
+   {0,  "m",  's',  swaphead,  swapline,       "swap rate",              },
+   {0,  "cd", 'l',  lvmhead,   lvmline,        "logical volume activity", },
+   {0,  "cd", 'f',  mddhead,   mddline,        "multiple device activity",},
+   {0,  "cd", 'd',  dskhead,   dskline,        "disk activity",          },
+   {0,  "n",  'n',  nfmhead,   nfmline,        "NFS client mounts",      },
+   {0,  "n",  'j',  nfchead,   nfcline,        "NFS client activity",    },
+   {0,  "n",  'J',  nfshead,   nfsline,        "NFS server activity",    },
+   {0,  "n",  'i',  ifhead,    ifline,         "net-interf (general)",   },
+   {0,  "n",  'I',  IFhead,    IFline,         "net-interf (errors)",    },
+   {0,  "n",  'w',  ipv4head,  ipv4line,       "ip   v4    (general)",   },
+   {0,  "n",  'W',  IPv4head,  IPv4line,       "ip   v4    (errors)",    },
+   {0,  "n",  'y',  icmpv4head,        icmpv4line,     "icmp v4    (general)",   },
+   {0,  "n",  'Y',  ICMPv4head,        ICMPv4line,     "icmp v4    (per type)",  },
+   {0,  "n",  'u',  udpv4head, udpv4line,      "udp  v4",                },
+   {0,  "n",  'z',  ipv6head,  ipv6line,       "ip   v6    (general)",   },
+   {0,  "n",  'Z',  IPv6head,  IPv6line,       "ip   v6    (errors)",    },
+   {0,  "n",  'k',  icmpv6head,        icmpv6line,     "icmp v6    (general)",   },
+   {0,  "n",  'K',  ICMPv6head,        ICMPv6line,     "icmp v6    (per type)",  },
+   {0,  "n",  'U',  udpv6head, udpv6line,      "udp  v6",                },
+   {0,  "n",  't',  tcphead,   tcpline,        "tcp        (general)",   },
+   {0,  "n",  'T',  TCPhead,   TCPline,        "tcp        (errors)",    },
+#if    HTTPSTATS
+   {0,  "n",  'h',  httphead,  httpline,       "HTTP activity",          },
+#endif
+   {0,  "",   'O',  topchead,  topcline,       "top-3 processes cpu",    },
+   {0,  "",   'G',  topmhead,  topmline,       "top-3 processes memory", },
+   {0,  "",   'D',  topdhead,  topdline,       "top-3 processes disk",   },
+   {0,  "",   'N',  topnhead,  topnline,       "top-3 processes network",},
+};
+
+int    pricnt = sizeof(pridef)/sizeof(struct pridef);
diff --git a/sources/deviate.c b/sources/deviate.c
new file mode 100644 (file)
index 0000000..b7f66b4
--- /dev/null
@@ -0,0 +1,1620 @@
+/*
+** ATOP - System & Process Monitor
+**
+** The program 'atop' offers the possibility to view the activity of
+** the system on system-level as well as process-level.
+**
+** This source-file contains functions to calculate the differences for
+** the system-level and process-level counters since the previous sample.
+** ==========================================================================
+** Author:      Gerlof Langeveld
+** E-mail:      gerlof.langeveld@atoptool.nl
+** Date:        November 1996
+** LINUX-port:  June 2000
+** --------------------------------------------------------------------------
+** Copyright (C) 2000-2010 Gerlof Langeveld
+**
+** This program is free software; you can redistribute it and/or modify it
+** under the terms of the GNU General Public License as published by the
+** Free Software Foundation; either version 2, or (at your option) any
+** later version.
+**
+** This program is distributed in the hope that it will be useful, but
+** WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+** See the GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+** --------------------------------------------------------------------------
+**
+** $Log: deviate.c,v $
+** Revision 1.45  2010/10/23 14:02:03  gerlof
+** Show counters for total number of running and sleep (S and D) threads.
+**
+** Revision 1.44  2010/05/18 19:19:43  gerlof
+** Introduce CPU frequency and scaling (JC van Winkel).
+**
+** Revision 1.43  2010/04/23 12:19:35  gerlof
+** Modified mail-address in header.
+**
+** Revision 1.42  2010/03/04 10:52:08  gerlof
+** Support I/O-statistics on logical volumes and MD devices.
+**
+** Revision 1.41  2009/12/31 11:34:21  gerlof
+** Sanity-check to bypass kernel-bug showing 497 days of CPU-consumption.
+**
+** Revision 1.40  2009/12/17 11:58:25  gerlof
+** Gather and display new counters: dirty cache and guest cpu usage.
+**
+** Revision 1.39  2008/02/25 14:51:18  gerlof
+** Experimental code for HTTP-statistics.
+**
+** Revision 1.38  2008/01/07 11:33:43  gerlof
+** Cosmetic changes.
+**
+** Revision 1.37  2008/01/07 10:17:24  gerlof
+** Implement possibility to make summaries.
+**
+** Revision 1.36  2007/11/05 12:13:16  gerlof
+** Match processes not only on pid, but also on start time.
+**
+** Revision 1.35  2007/11/05 11:42:47  gerlof
+** Bug-solution for new-process indicator on 64-bits machines.
+**
+** Revision 1.34  2007/08/17 09:44:59  gerlof
+** Experimental: gather info about HTTP statistics.
+**
+** Revision 1.33  2007/08/16 11:59:32  gerlof
+** Add support for atopsar reporting.
+** Concerns addition of lots of counters.
+**
+** Revision 1.32  2007/07/03 09:01:07  gerlof
+** Support Apache-statistics.
+**
+** Revision 1.31  2007/03/20 13:02:03  gerlof
+** Introduction of variable supportflags.
+**
+** Revision 1.30  2007/03/20 11:18:57  gerlof
+** Add counter for cancelled writes.
+**
+** Revision 1.29  2007/02/13 09:21:04  gerlof
+** Removed external declarations.
+**
+** Revision 1.28  2007/01/22 08:28:18  gerlof
+** Support steal-time from /proc/stat.
+**
+** Revision 1.27  2007/01/18 10:43:18  gerlof
+** Support for network-interface busy-percentage (speed and duplex).
+**
+** Revision 1.26  2006/11/13 13:47:26  gerlof
+** Implement load-average counters, context-switches and interrupts.
+**
+** Revision 1.25  2006/02/07 06:45:33  gerlof
+** Removed swap-counter.
+**
+** Revision 1.24  2006/01/30 09:13:33  gerlof
+** Extend memory counters (a.o. page scans).
+**
+** Revision 1.23  2005/10/31 12:45:29  gerlof
+** Support account-record version 3 (used by Mandriva).
+**
+** Revision 1.22  2005/10/21 09:49:38  gerlof
+** Per-user accumulation of resource consumption.
+**
+** Revision 1.21  2004/12/14 15:05:47  gerlof
+** Implementation of patch-recognition for disk and network-statistics.
+**
+** Revision 1.20  2004/10/28 08:30:51  gerlof
+** New counter: vm committed space
+**
+** Revision 1.19  2004/09/24 10:02:01  gerlof
+** Wrong cpu-numbers for system level statistics.
+**
+** Revision 1.18  2004/09/02 10:49:18  gerlof
+** Added sleep-average to process-info.
+**
+** Revision 1.17  2004/08/31 13:27:04  gerlof
+** Add new info for threading.
+**
+** Revision 1.16  2004/05/07 05:49:40  gerlof
+** *** empty log message ***
+**
+** Revision 1.15  2004/05/06 09:46:55  gerlof
+** Ported to kernel-version 2.6.
+**
+** Revision 1.14  2003/07/07 09:26:33  gerlof
+** Cleanup code (-Wall proof).
+**
+** Revision 1.13  2003/07/03 11:17:49  gerlof
+** Corrected calculations for exited processes.
+**
+** Revision 1.12  2003/06/30 11:30:57  gerlof
+** Enlarge counters to 'long long'.
+**
+** Revision 1.11  2003/06/24 06:21:12  gerlof
+** Limit number of system resource lines.
+**
+** Revision 1.10  2003/01/24 14:20:16  gerlof
+** If possible, also show commandline when process has exited.
+**
+** Revision 1.9  2002/09/16 08:58:08  gerlof
+** Add indicator for newly created processes.
+**
+** Revision 1.8  2002/08/27 04:47:46  gerlof
+** Minor comment updates.
+**
+** Revision 1.7  2002/07/24 11:12:20  gerlof
+** Redesigned to ease porting to other UNIX-platforms.
+**
+** Revision 1.6  2002/07/10 04:59:37  root
+** Counters pin/pout renamed to swin/swout (Linux conventions).
+**
+** Revision 1.5  2002/01/22 13:39:20  gerlof
+** Support for number of cpu's.
+**
+** Revision 1.4  2001/11/22 08:33:10  gerlof
+** Add priority per process.
+**
+** Revision 1.3  2001/11/07 09:18:22  gerlof
+** Use /proc instead of /dev/kmem for process-level statistics.
+**
+** Revision 1.2  2001/10/03 08:58:41  gerlof
+** Improved subtraction which is overflow-proof
+**
+** Revision 1.1  2001/10/02 10:43:23  gerlof
+** Initial revision
+**
+*/
+
+static const char rcsid[] = "$Id: deviate.c,v 1.45 2010/10/23 14:02:03 gerlof Exp $";
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <time.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <memory.h>
+#include <string.h>
+
+#include "atop.h"
+#include "ifprop.h"
+#include "photoproc.h"
+#include "photosyst.h"
+
+#define        MAX32BITVAL     0x100000000LL
+
+static void calcdiff(struct tstat *, struct tstat *, struct tstat *,
+                                                       char, count_t);
+
+/*
+** calculate the process-activity during the last sample
+*/
+void
+deviattask(struct tstat    *curtpres, int ntaskpres,
+           struct tstat    *curpexit, int nprocexit,
+          struct devtstat *devtstat,
+          struct sstat    *devsstat)
+{
+       register int            c, d, pall=0, pact=0;
+       register struct tstat   *curstat, *devstat, *thisproc;
+       struct tstat            prestat;
+       struct pinfo            *pinfo;
+       count_t                 totusedcpu;
+       char                    hashtype = 'p';
+
+       /*
+       ** needed for sanity check later on...
+       */
+       totusedcpu      = devsstat->cpu.all.stime + devsstat->cpu.all.utime +
+                         devsstat->cpu.all.ntime + devsstat->cpu.all.itime +
+                         devsstat->cpu.all.wtime + devsstat->cpu.all.Itime +
+                         devsstat->cpu.all.Stime + devsstat->cpu.all.steal;
+
+       /*
+       ** make new list of all tasks in the task-database;
+       ** after handling all task, the left-overs are tasks
+       ** that have disappeared since the previous sample
+       */
+       pdb_makeresidue();
+
+       /*
+       ** remove allocated lists of previous sample and initialize counters
+       */
+       if (devtstat->taskall)
+               free(devtstat->taskall);
+
+       if (devtstat->procall)
+               free(devtstat->procall);
+
+       if (devtstat->procactive)
+               free(devtstat->procactive);
+
+       memset(devtstat, 0, sizeof *devtstat);
+
+       /*
+       ** create list for the sample deviations of all tasks
+       */
+       devtstat->ntaskall = ntaskpres + nprocexit;
+       devtstat->taskall  = malloc(devtstat->ntaskall * sizeof(struct tstat));
+
+       ptrverify(devtstat->taskall, "Malloc failed for %d deviated tasks\n",
+                                  devtstat->ntaskall);
+
+       /*
+       ** calculate deviations per present task
+       */
+       for (c=0, thisproc = devtstat->taskall; c < ntaskpres; c++)
+       {
+               char    newtask = 0;
+
+               curstat = curtpres+c;
+               devstat = devtstat->taskall+c;
+
+               if (curstat->gen.isproc)
+               {
+                       thisproc = devstat;     // remember last process seen
+
+                       devtstat->nprocall++;
+
+                       if (curstat->gen.state == 'Z')
+                       {
+                               devtstat->totzombie++;
+                       }
+                       else
+                       {
+                               devtstat->totrun   += curstat->gen.nthrrun;
+                               devtstat->totslpi  += curstat->gen.nthrslpi;
+                               devtstat->totslpu  += curstat->gen.nthrslpu;
+                       }
+               }
+
+               /*
+               ** get previous figures from task-database
+               */
+               if ( pdb_gettask(curstat->gen.pid, curstat->gen.isproc,
+                                curstat->gen.btime, &pinfo))
+               {
+                       /*
+                       ** task already present in the previous sample
+                       **
+                       ** save stats from previous sample (to use for
+                       ** further calculations) and store new statistics
+                       ** in task-database
+                       */
+                       if (memcmp(curstat, &pinfo->tstat, 
+                                                  sizeof(struct tstat)) == EQ)
+                       {
+                               /*
+                               ** no activity for task
+                               */
+                               curstat->gen.wasinactive = 1;
+                       }
+                       else
+                       {
+                               /*
+                               ** save the values of the previous sample
+                               ** and overwrite the previous sample in
+                               ** the database with the current sample
+                               */
+                               prestat         = pinfo->tstat;
+                               pinfo->tstat    = *curstat;
+
+                               curstat->gen.wasinactive = 0;
+
+                               devtstat->ntaskactive++;
+
+                               if (curstat->gen.isproc)
+                               {
+                                       devtstat->nprocactive++;
+                               }
+                               else
+                               {
+                                       if (thisproc->gen.wasinactive)
+                                       {
+                                               thisproc->gen.wasinactive = 0;
+                                               devtstat->ntaskactive++;
+                                               devtstat->nprocactive++;
+                                       }
+                               }
+                       }
+               }
+               else
+               {
+                       /*
+                       ** new task which must have been started during
+                       ** last interval
+                       */
+                       memset(&prestat, 0, sizeof(prestat));
+
+                       curstat->gen.wasinactive = 0;
+                       devtstat->ntaskactive++;
+
+                       if (curstat->gen.isproc)
+                       {
+                               devtstat->nprocactive++;
+                       }
+                       else
+                       {
+                               if (thisproc->gen.wasinactive)
+                               {
+                                       thisproc->gen.wasinactive = 0;
+                                       devtstat->ntaskactive++;
+                                       devtstat->nprocactive++;
+                               }
+                       }
+
+                       /*
+                       ** create new task struct
+                       */
+                       pinfo = calloc(1, sizeof(struct pinfo));
+
+                       ptrverify(pinfo, "Malloc failed for new pinfo\n");
+
+                       pinfo->tstat = *curstat;
+
+                       /*
+                       ** add new task to task-database
+                       */
+                       pdb_addtask(curstat->gen.pid, pinfo);
+
+                       newtask = 1;
+               }
+
+               /*
+               ** do the difference calculations
+               */
+               calcdiff(devstat, curstat, &prestat, newtask, totusedcpu);
+       }
+
+       /*
+       ** calculate deviations per exited process
+       */
+       if (nprocexit > 0 && supportflags&NETATOPD)
+       {
+               if (curpexit->gen.pid)
+                       hashtype = 'p';
+               else
+                       hashtype = 'b';
+
+               netatop_exithash(hashtype);
+       }
+
+       for (d=c, c=0; c < nprocexit; c++)
+       {
+               /*
+               ** check if this process has been started AND
+               ** finished since previous sample;
+               ** if so, it has no use to check if there is still 
+               ** existing info present in the process-database
+               */
+               curstat = curpexit+c;
+               curstat->gen.wasinactive = 0;
+
+               devtstat->nprocall++;
+               devtstat->nprocactive++;
+               devtstat->ntaskactive++;
+
+               if (curstat->gen.pid)   /* acctrecord contains pid? */
+               {
+                       if ( pdb_gettask(curstat->gen.pid, 1,
+                                        curstat->gen.btime, &pinfo))
+                                       prestat = pinfo->tstat;
+                               else
+                                       memset(&prestat, 0, sizeof(prestat));
+               }
+               else
+               {
+                       if ( curstat->gen.btime > pretime )
+                       {
+                               /*
+                               ** process-start and -finish in same interval
+                               */
+                               memset(&prestat, 0, sizeof(prestat));
+                       }
+                       else
+                       {
+                               /*
+                               ** process must be known in process-database;
+                               ** try to match one of the remaining processes
+                               ** against this exited one
+                               */
+                               if ( pdb_srchresidue(curstat, &pinfo) )
+                                       prestat = pinfo->tstat;
+                               else
+                                       memset(&prestat, 0, sizeof(prestat));
+                       }
+               }
+
+               /*
+               ** now do the calculations
+               */
+               devstat = devtstat->taskall+d;
+               memset(devstat, 0, sizeof *devstat);
+
+               devstat->gen = curstat->gen;
+
+               if ( curstat->gen.pid == 0 )
+                       devstat->gen.pid    = prestat.gen.pid;
+
+               if (!prestat.gen.pid)
+                       devstat->gen.excode |= ~(INT_MAX);
+
+               strcpy(devstat->gen.cmdline, prestat.gen.cmdline);
+
+               /*
+               ** due to the strange exponent-type storage of values
+               ** in the process accounting record, the resource-value
+               ** in the exit-record might have been smaller than the
+               ** stored value of the last registered sample; in that
+               ** case the deviation should be set to zero
+               */
+               if (curstat->cpu.stime > prestat.cpu.stime)
+                       devstat->cpu.stime  = curstat->cpu.stime -
+                                             prestat.cpu.stime;
+
+               if (curstat->cpu.utime > prestat.cpu.utime)
+                       devstat->cpu.utime  = curstat->cpu.utime -
+                                             prestat.cpu.utime;
+
+               if (curstat->mem.minflt > prestat.mem.minflt)
+                       devstat->mem.minflt = curstat->mem.minflt - 
+                                             prestat.mem.minflt;
+
+               if (curstat->mem.majflt > prestat.mem.majflt)
+                       devstat->mem.majflt = curstat->mem.majflt -
+                                             prestat.mem.majflt;
+
+               if (curstat->dsk.rio > (prestat.dsk.rio + prestat.dsk.wio))
+                       devstat->dsk.rio    = curstat->dsk.rio  -
+                                             prestat.dsk.rio   -
+                                             prestat.dsk.wio;
+
+               /*
+               ** try to match the network counters of netatop
+               */
+               if (supportflags & NETATOPD)
+               {
+                       unsigned long   val = (hashtype == 'p' ?
+                                               curstat->gen.pid :
+                                               curstat->gen.btime);
+
+                       netatop_exitfind(val, devstat, &prestat);
+               }
+
+               if (prestat.gen.pid > 0)
+                       pdb_deltask(prestat.gen.pid, prestat.gen.isproc);
+
+               d++;
+       }
+
+       /*
+       ** remove unused entries from RESIDUE chain
+       */
+       pdb_cleanresidue();
+
+       /*
+       ** create and fill other pointer lists
+       */
+       devtstat->procall    = malloc(devtstat->nprocall *
+                                               sizeof(struct tstat *));
+       devtstat->procactive = malloc(devtstat->nprocactive *
+                                               sizeof(struct tstat *));
+
+       ptrverify(devtstat->procall, "Malloc failed for %d processes\n",
+                                  devtstat->nprocall);
+
+       ptrverify(devtstat->procactive, "Malloc failed for %d active procs\n",
+                                  devtstat->nprocactive);
+
+
+        for (c=0, thisproc=devstat=devtstat->taskall; c < devtstat->ntaskall;
+                                                               c++, devstat++)
+        {
+               if (devstat->gen.isproc)
+               {
+                       devtstat->procall[pall++] = devstat;
+
+                       if (! devstat->gen.wasinactive)
+                                       devtstat->procactive[pact++] = devstat;
+               }
+        }
+}
+
+/*
+** calculate the differences between the current sample and
+** the previous sample for a task
+*/
+static void
+calcdiff(struct tstat *devstat, struct tstat *curstat, struct tstat *prestat,
+                                             char newtask, count_t totusedcpu)
+{
+       /*
+       ** for inactive tasks, set all counters to zero
+       */
+       if (curstat->gen.wasinactive)
+               memset(devstat, 0, sizeof *devstat);
+
+       /*
+       ** copy all static values from the current task settings
+       */
+       devstat->gen          = curstat->gen;
+
+       if (newtask)
+               devstat->gen.excode |= ~(INT_MAX);
+
+       devstat->cpu.nice     = curstat->cpu.nice;
+       devstat->cpu.prio     = curstat->cpu.prio;
+       devstat->cpu.rtprio   = curstat->cpu.rtprio;
+       devstat->cpu.policy   = curstat->cpu.policy;
+       devstat->cpu.curcpu   = curstat->cpu.curcpu;
+       devstat->cpu.sleepavg = curstat->cpu.sleepavg;
+
+       devstat->mem.vexec    = curstat->mem.vexec;
+       devstat->mem.vmem     = curstat->mem.vmem;
+       devstat->mem.rmem     = curstat->mem.rmem;
+       devstat->mem.pmem     = curstat->mem.pmem;
+       devstat->mem.vdata    = curstat->mem.vdata;
+       devstat->mem.vstack   = curstat->mem.vstack;
+       devstat->mem.vlibs    = curstat->mem.vlibs;
+       devstat->mem.vswap    = curstat->mem.vswap;
+
+       /*
+       ** for inactive tasks, only the static values had to be copied, while
+       ** all use counters have been set to zero
+       */
+       if (curstat->gen.wasinactive)
+               return;
+
+       devstat->cpu.stime  = 
+               subcount(curstat->cpu.stime, prestat->cpu.stime);
+       devstat->cpu.utime  =
+               subcount(curstat->cpu.utime, prestat->cpu.utime);
+
+       /*
+       ** particular kernel versions sometimes supply a smaller
+       ** amount for consumed CPU-ticks than a previous sample;
+       ** with unsigned calculations this results in 497 days of
+       ** CPU-consumption so a sanity-check is needed here...
+       */
+       if (devstat->cpu.stime > totusedcpu)
+               devstat->cpu.stime = 1;
+
+       if (devstat->cpu.utime > totusedcpu)
+               devstat->cpu.utime = 1;
+
+       /*
+       ** do further calculations
+       */
+       devstat->dsk.rio    =
+               subcount(curstat->dsk.rio, prestat->dsk.rio);
+       devstat->dsk.rsz    =
+               subcount(curstat->dsk.rsz, prestat->dsk.rsz);
+       devstat->dsk.wio    =
+               subcount(curstat->dsk.wio, prestat->dsk.wio);
+       devstat->dsk.wsz    =
+               subcount(curstat->dsk.wsz, prestat->dsk.wsz);
+       devstat->dsk.cwsz   =
+               subcount(curstat->dsk.cwsz, prestat->dsk.cwsz);
+
+       devstat->mem.vgrow  = curstat->mem.vmem   - prestat->mem.vmem;
+       devstat->mem.rgrow  = curstat->mem.rmem   - prestat->mem.rmem;
+
+       devstat->mem.minflt = 
+               subcount(curstat->mem.minflt, prestat->mem.minflt);
+       devstat->mem.majflt =
+               subcount(curstat->mem.majflt, prestat->mem.majflt);
+
+       /*
+       ** network counters: due to an unload/load of the netatop module,
+       ** previous counters might be larger than the current
+       */
+       if (curstat->net.tcpsnd >= prestat->net.tcpsnd)
+               devstat->net.tcpsnd =
+                       subcount(curstat->net.tcpsnd, prestat->net.tcpsnd);
+       else
+               devstat->net.tcpsnd = curstat->net.tcpsnd;
+
+       if (curstat->net.tcpssz >= prestat->net.tcpssz)
+               devstat->net.tcpssz =
+                       subcount(curstat->net.tcpssz, prestat->net.tcpssz);
+       else
+               devstat->net.tcpssz = curstat->net.tcpssz;
+
+       if (curstat->net.tcprcv >= prestat->net.tcprcv)
+               devstat->net.tcprcv =
+                       subcount(curstat->net.tcprcv, prestat->net.tcprcv);
+       else
+               devstat->net.tcprcv = curstat->net.tcprcv;
+
+       if (curstat->net.tcprsz >= prestat->net.tcprsz)
+               devstat->net.tcprsz =
+                       subcount(curstat->net.tcprsz, prestat->net.tcprsz);
+       else
+               devstat->net.tcprsz = curstat->net.tcprsz;
+
+       if (curstat->net.udpsnd >= prestat->net.udpsnd)
+               devstat->net.udpsnd =
+                       subcount(curstat->net.udpsnd, prestat->net.udpsnd);
+       else
+               devstat->net.udpsnd = curstat->net.udpsnd;
+
+       if (curstat->net.udpssz >= prestat->net.udpssz)
+               devstat->net.udpssz =
+                       subcount(curstat->net.udpssz, prestat->net.udpssz);
+       else
+               devstat->net.udpssz = curstat->net.udpssz;
+
+       if (curstat->net.udprcv >= prestat->net.udprcv)
+               devstat->net.udprcv =
+                       subcount(curstat->net.udprcv, prestat->net.udprcv);
+       else
+               devstat->net.udprcv = curstat->net.udprcv;
+
+       if (curstat->net.udprsz >= prestat->net.udprsz)
+               devstat->net.udprsz =
+                       subcount(curstat->net.udprsz, prestat->net.udprsz);
+       else
+               devstat->net.udprsz = curstat->net.udprsz;
+}
+
+/*
+** calculate the system-activity during the last sample
+*/
+void
+deviatsyst(struct sstat *cur, struct sstat *pre, struct sstat *dev,
+                                                       long interval)
+{
+       register int    i, j;
+       count_t         *cdev, *ccur, *cpre;
+       struct ifprop   ifprop;
+
+       dev->cpu.nrcpu     = cur->cpu.nrcpu;
+       dev->cpu.devint    = subcount(cur->cpu.devint, pre->cpu.devint);
+       dev->cpu.csw       = subcount(cur->cpu.csw,    pre->cpu.csw);
+       dev->cpu.nprocs    = subcount(cur->cpu.nprocs, pre->cpu.nprocs);
+
+       dev->cpu.all.stime = subcount(cur->cpu.all.stime, pre->cpu.all.stime);
+       dev->cpu.all.utime = subcount(cur->cpu.all.utime, pre->cpu.all.utime);
+       dev->cpu.all.ntime = subcount(cur->cpu.all.ntime, pre->cpu.all.ntime);
+       dev->cpu.all.itime = subcount(cur->cpu.all.itime, pre->cpu.all.itime);
+       dev->cpu.all.wtime = subcount(cur->cpu.all.wtime, pre->cpu.all.wtime);
+       dev->cpu.all.Itime = subcount(cur->cpu.all.Itime, pre->cpu.all.Itime);
+       dev->cpu.all.Stime = subcount(cur->cpu.all.Stime, pre->cpu.all.Stime);
+
+       dev->cpu.all.steal = subcount(cur->cpu.all.steal, pre->cpu.all.steal);
+       dev->cpu.all.guest = subcount(cur->cpu.all.guest, pre->cpu.all.guest);
+
+       for (i=0; i < dev->cpu.nrcpu; i++)
+       {
+               count_t         ticks;
+
+               dev->cpu.cpu[i].cpunr = cur->cpu.cpu[i].cpunr;
+               dev->cpu.cpu[i].stime = subcount(cur->cpu.cpu[i].stime,
+                                                pre->cpu.cpu[i].stime);
+               dev->cpu.cpu[i].utime = subcount(cur->cpu.cpu[i].utime,
+                                                pre->cpu.cpu[i].utime);
+               dev->cpu.cpu[i].ntime = subcount(cur->cpu.cpu[i].ntime,
+                                                pre->cpu.cpu[i].ntime);
+               dev->cpu.cpu[i].itime = subcount(cur->cpu.cpu[i].itime,
+                                                pre->cpu.cpu[i].itime);
+               dev->cpu.cpu[i].wtime = subcount(cur->cpu.cpu[i].wtime,
+                                                pre->cpu.cpu[i].wtime);
+               dev->cpu.cpu[i].Itime = subcount(cur->cpu.cpu[i].Itime,
+                                                pre->cpu.cpu[i].Itime);
+               dev->cpu.cpu[i].Stime = subcount(cur->cpu.cpu[i].Stime,
+                                                pre->cpu.cpu[i].Stime);
+
+               dev->cpu.cpu[i].steal = subcount(cur->cpu.cpu[i].steal,
+                                                pre->cpu.cpu[i].steal);
+               dev->cpu.cpu[i].guest = subcount(cur->cpu.cpu[i].guest,
+                                                pre->cpu.cpu[i].guest);
+
+               ticks                 = cur->cpu.cpu[i].freqcnt.ticks;
+
+               dev->cpu.cpu[i].freqcnt.maxfreq = 
+                                       cur->cpu.cpu[i].freqcnt.maxfreq;
+               dev->cpu.cpu[i].freqcnt.cnt = ticks ?
+                                       subcount(cur->cpu.cpu[i].freqcnt.cnt,
+                                                pre->cpu.cpu[i].freqcnt.cnt)
+                                              : cur->cpu.cpu[i].freqcnt.cnt;
+
+               dev->cpu.cpu[i].freqcnt.ticks = ticks ?
+                                       subcount(cur->cpu.cpu[i].freqcnt.ticks,
+                                                pre->cpu.cpu[i].freqcnt.ticks)
+                                              : cur->cpu.cpu[i].freqcnt.ticks;
+       }
+
+       dev->cpu.lavg1          = cur->cpu.lavg1;
+       dev->cpu.lavg5          = cur->cpu.lavg5;
+       dev->cpu.lavg15         = cur->cpu.lavg15;
+
+       dev->mem.physmem        = cur->mem.physmem;
+       dev->mem.freemem        = cur->mem.freemem;
+       dev->mem.buffermem      = cur->mem.buffermem;
+       dev->mem.slabmem        = cur->mem.slabmem;
+       dev->mem.slabreclaim    = cur->mem.slabreclaim;
+       dev->mem.committed      = cur->mem.committed;
+       dev->mem.commitlim      = cur->mem.commitlim;
+       dev->mem.cachemem       = cur->mem.cachemem;
+       dev->mem.cachedrt       = cur->mem.cachedrt;
+       dev->mem.totswap        = cur->mem.totswap;
+       dev->mem.freeswap       = cur->mem.freeswap;
+
+       dev->mem.shmem          = cur->mem.shmem;
+       dev->mem.shmrss         = cur->mem.shmrss;
+       dev->mem.shmswp         = cur->mem.shmswp;
+
+       dev->mem.tothugepage    = cur->mem.tothugepage;
+       dev->mem.freehugepage   = cur->mem.freehugepage;
+       dev->mem.hugepagesz     = cur->mem.hugepagesz;
+
+       dev->mem.vmwballoon     = cur->mem.vmwballoon;
+
+       dev->mem.swouts         = subcount(cur->mem.swouts,  pre->mem.swouts);
+       dev->mem.swins          = subcount(cur->mem.swins,   pre->mem.swins);
+       dev->mem.pgscans        = subcount(cur->mem.pgscans, pre->mem.pgscans);
+       dev->mem.pgsteal        = subcount(cur->mem.pgsteal, pre->mem.pgsteal);
+       dev->mem.allocstall     = subcount(cur->mem.allocstall,
+                                                        pre->mem.allocstall);
+
+       /*
+       ** structures with network-related counters are considered
+       ** as tables of frequency-counters that have to be subtracted;
+       ** values that do not represent a frequency are corrected afterwards
+       */
+       for (cdev = (count_t *)&dev->net.ipv4,
+            ccur = (count_t *)&cur->net.ipv4,
+            cpre = (count_t *)&pre->net.ipv4,
+            i    = 0;
+               i < (sizeof dev->net.ipv4 / sizeof(count_t));
+                       cdev++, ccur++, cpre++, i++)
+                               *cdev = *ccur - *cpre;
+       
+       dev->net.ipv4.Forwarding = cur->net.ipv4.Forwarding;
+       dev->net.ipv4.DefaultTTL = cur->net.ipv4.DefaultTTL;
+
+               /* ------------- */
+
+       for (cdev = (count_t *)&dev->net.icmpv4,
+            ccur = (count_t *)&cur->net.icmpv4,
+            cpre = (count_t *)&pre->net.icmpv4,
+            i    = 0;
+               i < (sizeof dev->net.icmpv4 / sizeof(count_t));
+                       cdev++, ccur++, cpre++, i++)
+                               *cdev = *ccur - *cpre;
+
+               /* ------------- */
+
+       for (cdev = (count_t *)&dev->net.udpv4,
+            ccur = (count_t *)&cur->net.udpv4,
+            cpre = (count_t *)&pre->net.udpv4,
+            i    = 0;
+               i < (sizeof dev->net.udpv4 / sizeof(count_t));
+                       cdev++, ccur++, cpre++, i++)
+                               *cdev = *ccur - *cpre;
+
+               /* ------------- */
+
+       for (cdev = (count_t *)&dev->net.ipv6,
+            ccur = (count_t *)&cur->net.ipv6,
+            cpre = (count_t *)&pre->net.ipv6,
+            i    = 0;
+               i < (sizeof dev->net.ipv6 / sizeof(count_t));
+                       cdev++, ccur++, cpre++, i++)
+                               *cdev = *ccur - *cpre;
+
+               /* ------------- */
+
+       for (cdev = (count_t *)&dev->net.icmpv6,
+            ccur = (count_t *)&cur->net.icmpv6,
+            cpre = (count_t *)&pre->net.icmpv6,
+            i    = 0;
+               i < (sizeof dev->net.icmpv6 / sizeof(count_t));
+                       cdev++, ccur++, cpre++, i++)
+                               *cdev = *ccur - *cpre;
+
+               /* ------------- */
+
+       for (cdev = (count_t *)&dev->net.udpv6,
+            ccur = (count_t *)&cur->net.udpv6,
+            cpre = (count_t *)&pre->net.udpv6,
+            i    = 0;
+               i < (sizeof dev->net.udpv6 / sizeof(count_t));
+                       cdev++, ccur++, cpre++, i++)
+                               *cdev = *ccur - *cpre;
+
+               /* ------------- */
+
+       for (cdev = (count_t *)&dev->net.tcp,
+            ccur = (count_t *)&cur->net.tcp,
+            cpre = (count_t *)&pre->net.tcp,
+            i    = 0;
+               i < (sizeof dev->net.tcp / sizeof(count_t));
+                       cdev++, ccur++, cpre++, i++)
+                               *cdev = *ccur - *cpre;
+
+       dev->net.tcp.RtoAlgorithm = cur->net.tcp.RtoAlgorithm;
+       dev->net.tcp.RtoMin       = cur->net.tcp.RtoMin;
+       dev->net.tcp.RtoMax       = cur->net.tcp.RtoMax;
+       dev->net.tcp.MaxConn      = cur->net.tcp.MaxConn;
+       dev->net.tcp.CurrEstab    = cur->net.tcp.CurrEstab;
+
+       /*
+       ** calculate deviations for interfaces
+       **
+       ** refresh all interface properties
+       */
+        regainrootprivs();     /* get root privileges      */
+
+       initifprop();           /* refresh interface info   */
+
+       if (! droprootprivs())  /* drop setuid-root privs   */
+               cleanstop(42);
+
+       for (i=0; cur->intf.intf[i].name[0]; i++)
+       {
+               strcpy(ifprop.name, cur->intf.intf[i].name);
+
+               getifprop(&ifprop);
+
+               cur->intf.intf[i].type   = ifprop.type;
+               cur->intf.intf[i].speed  = ifprop.speed;
+               cur->intf.intf[i].speedp = ifprop.speed;
+               cur->intf.intf[i].duplex = ifprop.fullduplex;
+       }
+
+       if (pre->intf.intf[0].name[0] == '\0')  /* first sample? */
+       {
+               for (i=0; cur->intf.intf[i].name[0]; i++)
+               {
+                       strcpy(pre->intf.intf[i].name, cur->intf.intf[i].name);
+
+                       pre->intf.intf[i].type   = cur->intf.intf[i].type;
+                       pre->intf.intf[i].speed  = cur->intf.intf[i].speed;
+                       pre->intf.intf[i].speedp = cur->intf.intf[i].speedp;
+                       pre->intf.intf[i].duplex = cur->intf.intf[i].duplex;
+               }
+       }
+       
+       for (i=0, j=0; cur->intf.intf[i].name[0]; i++, j++)
+       {
+               /*
+               ** be sure that we have the same interface
+               ** (interfaces could have been added or removed since
+               ** previous sample)
+               */
+               if (strcmp(cur->intf.intf[i].name, pre->intf.intf[j].name) != 0)
+               {
+                       // try to resync
+                       for (j=0; pre->intf.intf[j].name[0]; j++)
+                       {
+                               if (strcmp(cur->intf.intf[i].name,
+                                          pre->intf.intf[j].name) == 0)
+                                       break;
+                       }
+
+                       // resync not succeeded?
+                       if (! pre->intf.intf[j].name[0])
+                       {
+                               memcpy(&dev->intf.intf[i],
+                                      &cur->intf.intf[i],
+                                      sizeof cur->intf.intf[i]);
+
+                               j = 0;
+                               continue;
+                       }
+               }
+
+               /*
+               ** calculate interface deviations for this sample
+               */
+               strcpy(dev->intf.intf[i].name, cur->intf.intf[i].name);
+
+               dev->intf.intf[i].rbyte = subcount(cur->intf.intf[i].rbyte,
+                                                  pre->intf.intf[j].rbyte);
+               dev->intf.intf[i].rpack = subcount(cur->intf.intf[i].rpack,
+                                                  pre->intf.intf[j].rpack);
+               dev->intf.intf[i].rerrs = subcount(cur->intf.intf[i].rerrs,
+                                                  pre->intf.intf[j].rerrs);
+               dev->intf.intf[i].rdrop = subcount(cur->intf.intf[i].rdrop,
+                                                  pre->intf.intf[j].rdrop);
+               dev->intf.intf[i].rfifo = subcount(cur->intf.intf[i].rfifo,
+                                                  pre->intf.intf[j].rfifo);
+               dev->intf.intf[i].rframe= subcount(cur->intf.intf[i].rframe,
+                                                  pre->intf.intf[j].rframe);
+               dev->intf.intf[i].rcompr= subcount(cur->intf.intf[i].rcompr,
+                                                  pre->intf.intf[j].rcompr);
+               dev->intf.intf[i].rmultic=subcount(cur->intf.intf[i].rmultic,
+                                                  pre->intf.intf[j].rmultic);
+
+               dev->intf.intf[i].sbyte = subcount(cur->intf.intf[i].sbyte,
+                                                  pre->intf.intf[j].sbyte);
+               dev->intf.intf[i].spack = subcount(cur->intf.intf[i].spack,
+                                                  pre->intf.intf[j].spack);
+               dev->intf.intf[i].serrs = subcount(cur->intf.intf[i].serrs,
+                                                  pre->intf.intf[j].serrs);
+               dev->intf.intf[i].sdrop = subcount(cur->intf.intf[i].sdrop,
+                                                  pre->intf.intf[j].sdrop);
+               dev->intf.intf[i].sfifo = subcount(cur->intf.intf[i].sfifo,
+                                                  pre->intf.intf[j].sfifo);
+               dev->intf.intf[i].scollis= subcount(cur->intf.intf[i].scollis,
+                                                  pre->intf.intf[j].scollis);
+               dev->intf.intf[i].scarrier= subcount(cur->intf.intf[i].scarrier,
+                                                  pre->intf.intf[j].scarrier);
+               dev->intf.intf[i].scompr= subcount(cur->intf.intf[i].scompr,
+                                                  pre->intf.intf[j].scompr);
+
+               dev->intf.intf[i].type          = cur->intf.intf[i].type;
+               dev->intf.intf[i].duplex        = cur->intf.intf[i].duplex;
+               dev->intf.intf[i].speed         = cur->intf.intf[i].speed;
+               dev->intf.intf[i].speedp        = pre->intf.intf[j].speed;
+
+               cur->intf.intf[i].speedp        = pre->intf.intf[j].speed;
+       }
+
+       dev->intf.intf[i].name[0] = '\0';
+       dev->intf.nrintf = i;
+
+       /*
+       ** calculate deviations for disks
+       */
+       for (i=j=0; cur->dsk.dsk[i].name[0]; i++)
+       {
+               int     realj = j;
+
+               /*
+               ** check if disk has been added or removed since
+               ** previous interval
+               */
+               if ( strcmp(cur->dsk.dsk[i].name, pre->dsk.dsk[j].name) != 0)
+               {
+                       for (j++; pre->dsk.dsk[j].name[0]; j++)
+                       {
+                               if ( strcmp(cur->dsk.dsk[i].name,
+                                               pre->dsk.dsk[j].name) == 0)
+                                       break;
+                       }
+
+                       /*
+                       ** either the corresponding entry has been found
+                       ** in the case that a disk has been removed, or
+                       ** an empty entry has been found (all counters
+                       ** on zero) in the case that a disk has been added
+                       ** during the last sample
+                       */
+               }
+
+               strcpy(dev->dsk.dsk[i].name, cur->dsk.dsk[i].name);
+
+               dev->dsk.dsk[i].nread  = subcount(cur->dsk.dsk[i].nread,
+                                                 pre->dsk.dsk[j].nread);
+               dev->dsk.dsk[i].nwrite = subcount(cur->dsk.dsk[i].nwrite,
+                                                 pre->dsk.dsk[j].nwrite);
+               dev->dsk.dsk[i].nrsect = subcount(cur->dsk.dsk[i].nrsect,
+                                                 pre->dsk.dsk[j].nrsect);
+               dev->dsk.dsk[i].nwsect = subcount(cur->dsk.dsk[i].nwsect,
+                                                 pre->dsk.dsk[j].nwsect);
+               dev->dsk.dsk[i].io_ms  = subcount(cur->dsk.dsk[i].io_ms,
+                                                 pre->dsk.dsk[j].io_ms);
+               dev->dsk.dsk[i].avque  = subcount(cur->dsk.dsk[i].avque,
+                                                 pre->dsk.dsk[j].avque);
+
+               /*
+               ** determine new j
+               */
+               if (pre->dsk.dsk[j].name)       // existing matching entry
+                       j++;
+               else
+                       j = realj;              // empty entry: stick to old j
+       }
+
+       dev->dsk.dsk[i].name[0] = '\0';
+       dev->dsk.ndsk = i;
+
+       /*
+       ** calculate deviations for multiple devices
+       */
+       for (i=j=0; cur->dsk.mdd[i].name[0]; i++)
+       {
+               int     realj = j;
+
+               /*
+               ** check if md has been added or removed since
+               ** previous interval
+               */
+               if ( strcmp(cur->dsk.mdd[i].name, pre->dsk.mdd[j].name) != 0)
+               {
+                       for (j++; pre->dsk.mdd[j].name[0]; j++)
+                       {
+                               if ( strcmp(cur->dsk.mdd[i].name,
+                                               pre->dsk.mdd[j].name) == 0)
+                                       break;
+                       }
+
+                       /*
+                       ** either the corresponding entry has been found
+                       ** in the case that a md has been removed, or
+                       ** an empty entry has been found (all counters
+                       ** on zero) in the case that a md has been added
+                       ** during the last sample
+                       */
+               }
+
+               strcpy(dev->dsk.mdd[i].name, cur->dsk.mdd[i].name);
+
+               dev->dsk.mdd[i].nread  = subcount(cur->dsk.mdd[i].nread,
+                                                 pre->dsk.mdd[j].nread);
+               dev->dsk.mdd[i].nwrite = subcount(cur->dsk.mdd[i].nwrite,
+                                                 pre->dsk.mdd[j].nwrite);
+               dev->dsk.mdd[i].nrsect = subcount(cur->dsk.mdd[i].nrsect,
+                                                 pre->dsk.mdd[j].nrsect);
+               dev->dsk.mdd[i].nwsect = subcount(cur->dsk.mdd[i].nwsect,
+                                                 pre->dsk.mdd[j].nwsect);
+               dev->dsk.mdd[i].io_ms  = subcount(cur->dsk.mdd[i].io_ms,
+                                                 pre->dsk.mdd[j].io_ms);
+               dev->dsk.mdd[i].avque  = subcount(cur->dsk.mdd[i].avque,
+                                                 pre->dsk.mdd[j].avque);
+
+               /*
+               ** determine new j
+               */
+               if (pre->dsk.mdd[j].name)       // existing matching entry
+                       j++;
+               else
+                       j = realj;              // empty entry: stick to old j
+       }
+
+       dev->dsk.mdd[i].name[0] = '\0';
+       dev->dsk.nmdd = i;
+
+       /*
+       ** calculate deviations for LVM logical volumes
+       */
+       for (i=j=0; cur->dsk.lvm[i].name[0]; i++)
+       {
+               int     realj = j;
+
+               /*
+               ** check if logical volume has been added or removed since
+               ** previous interval
+               */
+               if ( strcmp(cur->dsk.lvm[i].name, pre->dsk.lvm[j].name) != 0)
+               {
+                       for (j++; pre->dsk.lvm[j].name[0]; j++)
+                       {
+                               if ( strcmp(cur->dsk.lvm[i].name,
+                                               pre->dsk.lvm[j].name) == 0)
+                                       break;
+                       }
+
+                       /*
+                       ** either the corresponding entry has been found
+                       ** in the case that a logical volume has been removed,
+                       ** or an empty entry has been found (all counters
+                       ** on zero) in the case that a logical volume has
+                       ** been added during the last sample
+                       */
+               }
+
+               strcpy(dev->dsk.lvm[i].name, cur->dsk.lvm[i].name);
+
+               dev->dsk.lvm[i].nread  = subcount(cur->dsk.lvm[i].nread,
+                                                 pre->dsk.lvm[j].nread);
+               dev->dsk.lvm[i].nwrite = subcount(cur->dsk.lvm[i].nwrite,
+                                                 pre->dsk.lvm[j].nwrite);
+               dev->dsk.lvm[i].nrsect = subcount(cur->dsk.lvm[i].nrsect,
+                                                 pre->dsk.lvm[j].nrsect);
+               dev->dsk.lvm[i].nwsect = subcount(cur->dsk.lvm[i].nwsect,
+                                                 pre->dsk.lvm[j].nwsect);
+               dev->dsk.lvm[i].io_ms  = subcount(cur->dsk.lvm[i].io_ms,
+                                                 pre->dsk.lvm[j].io_ms);
+               dev->dsk.lvm[i].avque  = subcount(cur->dsk.lvm[i].avque,
+                                                 pre->dsk.lvm[j].avque);
+
+               /*
+               ** determine new j
+               */
+               if (pre->dsk.lvm[j].name)       // existing matching entry
+                       j++;
+               else
+                       j = realj;              // empty entry: stick to old j
+       }
+
+       dev->dsk.lvm[i].name[0] = '\0';
+       dev->dsk.nlvm = i;
+
+       /*
+       ** calculate deviations for NFS
+       */
+       dev->nfs.server.netcnt    = subcount(cur->nfs.server.netcnt,
+                                            pre->nfs.server.netcnt);
+       dev->nfs.server.netudpcnt = subcount(cur->nfs.server.netudpcnt,
+                                            pre->nfs.server.netudpcnt);
+       dev->nfs.server.nettcpcnt = subcount(cur->nfs.server.nettcpcnt,
+                                            pre->nfs.server.nettcpcnt);
+       dev->nfs.server.nettcpcon = subcount(cur->nfs.server.nettcpcon,
+                                            pre->nfs.server.nettcpcon);
+
+       dev->nfs.server.rpccnt    = subcount(cur->nfs.server.rpccnt,
+                                            pre->nfs.server.rpccnt);
+       dev->nfs.server.rpcread   = subcount(cur->nfs.server.rpcread,
+                                            pre->nfs.server.rpcread);
+       dev->nfs.server.rpcwrite  = subcount(cur->nfs.server.rpcwrite,
+                                            pre->nfs.server.rpcwrite);
+       dev->nfs.server.rpcbadfmt = subcount(cur->nfs.server.rpcbadfmt,
+                                            pre->nfs.server.rpcbadfmt);
+       dev->nfs.server.rpcbadaut = subcount(cur->nfs.server.rpcbadaut,
+                                            pre->nfs.server.rpcbadaut);
+       dev->nfs.server.rpcbadcln = subcount(cur->nfs.server.rpcbadcln,
+                                            pre->nfs.server.rpcbadcln);
+       
+       dev->nfs.server.rchits    = subcount(cur->nfs.server.rchits,
+                                            pre->nfs.server.rchits);
+       dev->nfs.server.rcmiss    = subcount(cur->nfs.server.rcmiss,
+                                            pre->nfs.server.rcmiss);
+       dev->nfs.server.rcnoca    = subcount(cur->nfs.server.rcnoca,
+                                            pre->nfs.server.rcnoca);
+
+       dev->nfs.server.nrbytes   = subcount(cur->nfs.server.nrbytes,
+                                            pre->nfs.server.nrbytes);
+       dev->nfs.server.nwbytes   = subcount(cur->nfs.server.nwbytes,
+                                            pre->nfs.server.nwbytes);
+
+       dev->nfs.client.rpccnt        = subcount(cur->nfs.client.rpccnt,
+                                                pre->nfs.client.rpccnt);
+       dev->nfs.client.rpcread       = subcount(cur->nfs.client.rpcread,
+                                                pre->nfs.client.rpcread);
+       dev->nfs.client.rpcwrite      = subcount(cur->nfs.client.rpcwrite,
+                                                pre->nfs.client.rpcwrite);
+       dev->nfs.client.rpcretrans    = subcount(cur->nfs.client.rpcretrans,
+                                                pre->nfs.client.rpcretrans);
+       dev->nfs.client.rpcautrefresh = subcount(cur->nfs.client.rpcautrefresh,
+                                                pre->nfs.client.rpcautrefresh);
+
+
+       for (i=j=0; i < cur->nfs.nfsmounts.nrmounts; i++, j++)
+       {
+               /*
+               ** check if nfsmounts have been added or removed since
+               ** previous interval
+               */
+               if ( strcmp(cur->nfs.nfsmounts.nfsmnt[i].mountdev,
+                           pre->nfs.nfsmounts.nfsmnt[j].mountdev) != 0)
+               {
+                       for (j=0; j < pre->nfs.nfsmounts.nrmounts; j++)
+                       {
+                           if ( strcmp(cur->nfs.nfsmounts.nfsmnt[i].mountdev,
+                                       pre->nfs.nfsmounts.nfsmnt[j].mountdev)
+                                                                       == 0)
+                                       break;
+                       }
+
+                       /*
+                       ** either the corresponding entry has been found
+                       ** in the case that a container has been removed,
+                       ** or an empty entry has been found (all counters
+                       ** on zero) in the case that a container has
+                       ** been added during the last sample
+                       */
+               }
+
+               strcpy(dev->nfs.nfsmounts.nfsmnt[i].mountdev,
+                      cur->nfs.nfsmounts.nfsmnt[i].mountdev);
+
+                dev->nfs.nfsmounts.nfsmnt[i].age = 
+                                    cur->nfs.nfsmounts.nfsmnt[i].age;
+
+               if (dev->nfs.nfsmounts.nfsmnt[i].age <= interval)
+                       memset(&(pre->nfs.nfsmounts.nfsmnt[j]), 0, 
+                                       sizeof(struct pernfsmount));
+
+                dev->nfs.nfsmounts.nfsmnt[i].bytesread = 
+                          subcount(cur->nfs.nfsmounts.nfsmnt[i].bytesread,
+                                   pre->nfs.nfsmounts.nfsmnt[j].bytesread);
+
+                dev->nfs.nfsmounts.nfsmnt[i].byteswrite = 
+                          subcount(cur->nfs.nfsmounts.nfsmnt[i].byteswrite,
+                                   pre->nfs.nfsmounts.nfsmnt[j].byteswrite);
+
+                dev->nfs.nfsmounts.nfsmnt[i].bytesdread = 
+                          subcount(cur->nfs.nfsmounts.nfsmnt[i].bytesdread,
+                                   pre->nfs.nfsmounts.nfsmnt[j].bytesdread);
+
+                dev->nfs.nfsmounts.nfsmnt[i].bytesdwrite = 
+                          subcount(cur->nfs.nfsmounts.nfsmnt[i].bytesdwrite,
+                                   pre->nfs.nfsmounts.nfsmnt[j].bytesdwrite);
+
+                dev->nfs.nfsmounts.nfsmnt[i].bytestotread = 
+                          subcount(cur->nfs.nfsmounts.nfsmnt[i].bytestotread,
+                                   pre->nfs.nfsmounts.nfsmnt[j].bytestotread);
+
+                dev->nfs.nfsmounts.nfsmnt[i].bytestotwrite = 
+                          subcount(cur->nfs.nfsmounts.nfsmnt[i].bytestotwrite,
+                                   pre->nfs.nfsmounts.nfsmnt[j].bytestotwrite);
+
+                dev->nfs.nfsmounts.nfsmnt[i].pagesmread = 
+                          subcount(cur->nfs.nfsmounts.nfsmnt[i].pagesmread,
+                                   pre->nfs.nfsmounts.nfsmnt[j].pagesmread);
+
+                dev->nfs.nfsmounts.nfsmnt[i].pagesmwrite = 
+                          subcount(cur->nfs.nfsmounts.nfsmnt[i].pagesmwrite,
+                                   pre->nfs.nfsmounts.nfsmnt[j].pagesmwrite);
+       }
+
+       dev->nfs.nfsmounts.nrmounts = cur->nfs.nfsmounts.nrmounts;
+
+       /*
+       ** calculate deviations for containers
+       */
+       for (i=j=0; i < cur->cfs.nrcontainer; i++, j++)
+       {
+               /*
+               ** check if containers have been added or removed since
+               ** previous interval
+               */
+               if (cur->cfs.cont[i].ctid != pre->cfs.cont[j].ctid)
+               {
+                       for (j=0; j < pre->cfs.nrcontainer; j++)
+                       {
+                               if (cur->cfs.cont[i].ctid ==
+                                               pre->cfs.cont[j].ctid)
+                                       break;
+                       }
+
+                       /*
+                       ** either the corresponding entry has been found
+                       ** in the case that a container has been removed,
+                       ** or an empty entry has been found (all counters
+                       ** on zero) in the case that a container has
+                       ** been added during the last sample
+                       */
+               }
+
+               dev->cfs.cont[i].ctid    = cur->cfs.cont[i].ctid;
+               dev->cfs.cont[i].numproc = cur->cfs.cont[i].numproc;
+
+               dev->cfs.cont[i].system  = subcount(cur->cfs.cont[i].system,
+                                                   pre->cfs.cont[i].system);
+               dev->cfs.cont[i].user    = subcount(cur->cfs.cont[i].user,
+                                                   pre->cfs.cont[i].user);
+               dev->cfs.cont[i].nice    = subcount(cur->cfs.cont[i].nice,
+                                                   pre->cfs.cont[i].nice);
+               dev->cfs.cont[i].uptime  = subcount(cur->cfs.cont[i].uptime,
+                                                   pre->cfs.cont[i].uptime);
+
+               dev->cfs.cont[i].physpages = cur->cfs.cont[i].physpages;
+       }
+
+       dev->cfs.nrcontainer = cur->cfs.nrcontainer;
+
+       /*
+       ** application-specific counters
+       */
+#if    HTTPSTATS
+       if (cur->www.uptime >= pre->www.uptime)
+       {
+               dev->www.accesses  = subcount(cur->www.accesses,
+                                              pre->www.accesses);
+               dev->www.totkbytes = subcount(cur->www.totkbytes,
+                                              pre->www.totkbytes);
+       }
+       else
+       {
+               dev->www.accesses  = cur->www.accesses;
+               dev->www.totkbytes = cur->www.totkbytes;
+       }
+
+       dev->www.bworkers  = cur->www.bworkers;
+       dev->www.iworkers  = cur->www.iworkers;
+#endif
+}
+
+/*
+** add the values of a new sample to a structure holding the totals
+** for the indicated category (c=cpu, m=memory, d=disk, n=network).
+*/
+void
+totalsyst(char category, struct sstat *new, struct sstat *tot)
+{
+       register int    i;
+       count_t         *ctot, *cnew;
+
+       switch (category)
+       {
+          case 'c':    /* accumulate cpu-related counters */
+               tot->cpu.nrcpu      = new->cpu.nrcpu;
+               tot->cpu.devint    += new->cpu.devint;
+               tot->cpu.csw       += new->cpu.csw;
+               tot->cpu.nprocs    += new->cpu.nprocs;
+
+               tot->cpu.all.stime += new->cpu.all.stime;
+               tot->cpu.all.utime += new->cpu.all.utime;
+               tot->cpu.all.ntime += new->cpu.all.ntime;
+               tot->cpu.all.itime += new->cpu.all.itime;
+               tot->cpu.all.wtime += new->cpu.all.wtime;
+               tot->cpu.all.Itime += new->cpu.all.Itime;
+               tot->cpu.all.Stime += new->cpu.all.Stime;
+               tot->cpu.all.steal += new->cpu.all.steal;
+               tot->cpu.all.guest += new->cpu.all.guest;
+
+               if (new->cpu.nrcpu == 1)
+               {
+                       tot->cpu.cpu[0] = tot->cpu.all;
+               }
+               else
+               {
+                       for (i=0; i < new->cpu.nrcpu; i++)
+                       {
+                               tot->cpu.cpu[i].cpunr  = new->cpu.cpu[i].cpunr;
+                               tot->cpu.cpu[i].stime += new->cpu.cpu[i].stime;
+                               tot->cpu.cpu[i].utime += new->cpu.cpu[i].utime;
+                               tot->cpu.cpu[i].ntime += new->cpu.cpu[i].ntime;
+                               tot->cpu.cpu[i].itime += new->cpu.cpu[i].itime;
+                               tot->cpu.cpu[i].wtime += new->cpu.cpu[i].wtime;
+                               tot->cpu.cpu[i].Itime += new->cpu.cpu[i].Itime;
+                               tot->cpu.cpu[i].Stime += new->cpu.cpu[i].Stime;
+                               tot->cpu.cpu[i].steal += new->cpu.cpu[i].steal;
+                               tot->cpu.cpu[i].guest += new->cpu.cpu[i].guest;
+                       }
+               }
+
+               tot->cpu.lavg1   = new->cpu.lavg1;
+               tot->cpu.lavg5   = new->cpu.lavg5;
+               tot->cpu.lavg15  = new->cpu.lavg15;
+               break;
+
+          case 'm':    /* accumulate memory-related counters */
+               tot->mem.physmem         = new->mem.physmem;
+               tot->mem.freemem         = new->mem.freemem;
+               tot->mem.buffermem       = new->mem.buffermem;
+               tot->mem.slabmem         = new->mem.slabmem;
+               tot->mem.slabreclaim     = new->mem.slabreclaim;
+               tot->mem.committed       = new->mem.committed;
+               tot->mem.commitlim       = new->mem.commitlim;
+               tot->mem.cachemem        = new->mem.cachemem;
+               tot->mem.cachedrt        = new->mem.cachedrt;
+               tot->mem.totswap         = new->mem.totswap;
+               tot->mem.freeswap        = new->mem.freeswap;
+
+               tot->mem.shmem           = new->mem.shmem;
+               tot->mem.shmrss          = new->mem.shmrss;
+               tot->mem.shmswp          = new->mem.shmswp;
+
+               tot->mem.swouts         += new->mem.swouts;
+               tot->mem.swins          += new->mem.swins;
+               tot->mem.pgscans        += new->mem.pgscans;
+               tot->mem.allocstall     += new->mem.allocstall;
+               break;
+
+          case 'n':    /* accumulate network-related counters */
+               tot->nfs.server.rpccnt     += new->nfs.server.rpccnt;
+               tot->nfs.server.rpcread    += new->nfs.server.rpcread;
+               tot->nfs.server.rpcwrite   += new->nfs.server.rpcwrite;
+               tot->nfs.server.rpcbadfmt  += new->nfs.server.rpcbadfmt;
+               tot->nfs.server.rpcbadaut  += new->nfs.server.rpcbadaut;
+               tot->nfs.server.rpcbadcln  += new->nfs.server.rpcbadcln;
+
+               tot->nfs.server.netcnt     += new->nfs.server.netcnt;
+               tot->nfs.server.nettcpcnt  += new->nfs.server.nettcpcnt;
+               tot->nfs.server.netudpcnt  += new->nfs.server.netudpcnt;
+               tot->nfs.server.nettcpcon  += new->nfs.server.nettcpcon;
+
+               tot->nfs.server.rchits     += new->nfs.server.rchits;
+               tot->nfs.server.rcmiss     += new->nfs.server.rcmiss;
+               tot->nfs.server.rcnoca     += new->nfs.server.rcnoca;
+
+               tot->nfs.server.nrbytes    += new->nfs.server.nrbytes;
+               tot->nfs.server.nwbytes    += new->nfs.server.nwbytes;
+
+               tot->nfs.client.rpccnt        += new->nfs.client.rpccnt;
+               tot->nfs.client.rpcread       += new->nfs.client.rpcread;
+               tot->nfs.client.rpcwrite      += new->nfs.client.rpcwrite;
+               tot->nfs.client.rpcretrans    += new->nfs.client.rpcretrans;
+               tot->nfs.client.rpcautrefresh += new->nfs.client.rpcautrefresh;
+
+               /*
+               ** other structures with network counters are considered
+               ** as tables of frequency-counters that will be accumulated;
+               ** values that do not represent a frequency are corrected
+               ** afterwards
+               */
+               for (ctot = (count_t *)&tot->net.ipv4,
+                    cnew = (count_t *)&new->net.ipv4, i = 0;
+                       i < (sizeof tot->net.ipv4 / sizeof(count_t));
+                               ctot++, cnew++, i++)
+                                       *ctot += *cnew;
+
+               tot->net.ipv4.Forwarding = new->net.ipv4.Forwarding;
+               tot->net.ipv4.DefaultTTL = new->net.ipv4.DefaultTTL;
+       
+                       /* ------------- */
+       
+               for (ctot = (count_t *)&tot->net.icmpv4,
+                    cnew = (count_t *)&new->net.icmpv4, i = 0;
+                       i < (sizeof tot->net.icmpv4 / sizeof(count_t));
+                               ctot++, cnew++, i++)
+                                       *ctot += *cnew;
+       
+                       /* ------------- */
+       
+               for (ctot = (count_t *)&tot->net.udpv4,
+                    cnew = (count_t *)&new->net.udpv4, i = 0;
+                       i < (sizeof tot->net.udpv4 / sizeof(count_t));
+                               ctot++, cnew++, i++)
+                                       *ctot += *cnew;
+       
+                       /* ------------- */
+       
+               for (ctot = (count_t *)&tot->net.ipv6,
+                    cnew = (count_t *)&new->net.ipv6, i = 0;
+                       i < (sizeof tot->net.ipv6 / sizeof(count_t));
+                               ctot++, cnew++, i++)
+                                       *ctot += *cnew;
+       
+                       /* ------------- */
+       
+               for (ctot = (count_t *)&tot->net.icmpv6,
+                    cnew = (count_t *)&new->net.icmpv6, i = 0;
+                       i < (sizeof tot->net.icmpv6 / sizeof(count_t));
+                               ctot++, cnew++, i++)
+                                       *ctot += *cnew;
+       
+                       /* ------------- */
+       
+               for (ctot = (count_t *)&tot->net.udpv6,
+                    cnew = (count_t *)&new->net.udpv6, i = 0;
+                       i < (sizeof tot->net.udpv6 / sizeof(count_t));
+                               ctot++, cnew++, i++)
+                                       *ctot += *cnew;
+       
+                       /* ------------- */
+       
+               for (ctot = (count_t *)&tot->net.tcp,
+                    cnew = (count_t *)&new->net.tcp, i = 0;
+                       i < (sizeof tot->net.tcp / sizeof(count_t));
+                               ctot++, cnew++, i++)
+                                       *ctot += *cnew;
+       
+               tot->net.tcp.RtoAlgorithm = new->net.tcp.RtoAlgorithm;
+               tot->net.tcp.RtoMin       = new->net.tcp.RtoMin;
+               tot->net.tcp.RtoMax       = new->net.tcp.RtoMax;
+               tot->net.tcp.MaxConn      = new->net.tcp.MaxConn;
+               tot->net.tcp.CurrEstab    = new->net.tcp.CurrEstab;
+       
+               for (i=0; new->intf.intf[i].name[0]; i++)
+               {
+                       /*
+                       ** check if an interface has been added or removed;
+                       ** in that case, zero all counters
+                       */
+                       if (strcmp(new->intf.intf[i].name,
+                                  tot->intf.intf[i].name) != 0)
+                       {
+                               tot->intf.intf[i].rbyte    = 0;
+                               tot->intf.intf[i].rpack    = 0;
+                               tot->intf.intf[i].rerrs    = 0;
+                               tot->intf.intf[i].rdrop    = 0;
+                               tot->intf.intf[i].rfifo    = 0;
+                               tot->intf.intf[i].rframe   = 0;
+                               tot->intf.intf[i].rcompr   = 0;
+                               tot->intf.intf[i].rmultic  = 0;
+       
+                               tot->intf.intf[i].sbyte    = 0;
+                               tot->intf.intf[i].spack    = 0;
+                               tot->intf.intf[i].serrs    = 0;
+                               tot->intf.intf[i].sdrop    = 0;
+                               tot->intf.intf[i].sfifo    = 0;
+                               tot->intf.intf[i].scollis  = 0;
+                               tot->intf.intf[i].scarrier = 0;
+                               tot->intf.intf[i].scompr   = 0;
+                       }
+       
+                       /*
+                       ** accumulate counters for this sample
+                       */
+                       strcpy(tot->intf.intf[i].name, new->intf.intf[i].name);
+       
+                       tot->intf.intf[i].rbyte   += new->intf.intf[i].rbyte;
+                       tot->intf.intf[i].rpack   += new->intf.intf[i].rpack;
+                       tot->intf.intf[i].rerrs   += new->intf.intf[i].rerrs;
+                       tot->intf.intf[i].rdrop   += new->intf.intf[i].rdrop;
+                       tot->intf.intf[i].rfifo   += new->intf.intf[i].rfifo;
+                       tot->intf.intf[i].rframe  += new->intf.intf[i].rframe;
+                       tot->intf.intf[i].rcompr  += new->intf.intf[i].rcompr;
+                       tot->intf.intf[i].rmultic += new->intf.intf[i].rmultic;
+       
+                       tot->intf.intf[i].sbyte   += new->intf.intf[i].sbyte;
+                       tot->intf.intf[i].spack   += new->intf.intf[i].spack;
+                       tot->intf.intf[i].serrs   += new->intf.intf[i].serrs;
+                       tot->intf.intf[i].sdrop   += new->intf.intf[i].sdrop;
+                       tot->intf.intf[i].sfifo   += new->intf.intf[i].sfifo;
+                       tot->intf.intf[i].scollis += new->intf.intf[i].scollis;
+                       tot->intf.intf[i].scarrier+= new->intf.intf[i].scarrier;
+                       tot->intf.intf[i].scompr  += new->intf.intf[i].scompr;
+       
+                       tot->intf.intf[i].type     = new->intf.intf[i].type;
+                       tot->intf.intf[i].speed    = new->intf.intf[i].speed;
+                       tot->intf.intf[i].duplex   = new->intf.intf[i].duplex;
+               }
+       
+               tot->intf.intf[i].name[0] = '\0';
+               tot->intf.nrintf          = i;
+
+#if            HTTPSTATS
+               tot->www.accesses  += new->www.accesses;
+               tot->www.totkbytes += new->www.totkbytes;
+               tot->www.bworkers   = new->www.bworkers;
+               tot->www.iworkers   = new->www.iworkers;
+#endif
+               break;
+
+          case 'd':    /* accumulate disk-related counters */
+               for (i=0; new->dsk.dsk[i].name[0]; i++)
+               {
+                       strcpy(tot->dsk.dsk[i].name, new->dsk.dsk[i].name);
+       
+                       tot->dsk.dsk[i].nread  += new->dsk.dsk[i].nread;
+                       tot->dsk.dsk[i].nwrite += new->dsk.dsk[i].nwrite;
+                       tot->dsk.dsk[i].nrsect += new->dsk.dsk[i].nrsect;
+                       tot->dsk.dsk[i].nwsect += new->dsk.dsk[i].nwsect;
+                       tot->dsk.dsk[i].io_ms  += new->dsk.dsk[i].io_ms;
+                       tot->dsk.dsk[i].avque  += new->dsk.dsk[i].avque;
+               }
+       
+               tot->dsk.dsk[i].name[0] = '\0';
+               tot->dsk.ndsk = i;
+
+               for (i=0; new->dsk.lvm[i].name[0]; i++)
+               {
+                       strcpy(tot->dsk.lvm[i].name, new->dsk.lvm[i].name);
+       
+                       tot->dsk.lvm[i].nread  += new->dsk.lvm[i].nread;
+                       tot->dsk.lvm[i].nwrite += new->dsk.lvm[i].nwrite;
+                       tot->dsk.lvm[i].nrsect += new->dsk.lvm[i].nrsect;
+                       tot->dsk.lvm[i].nwsect += new->dsk.lvm[i].nwsect;
+                       tot->dsk.lvm[i].io_ms  += new->dsk.lvm[i].io_ms;
+                       tot->dsk.lvm[i].avque  += new->dsk.lvm[i].avque;
+               }
+       
+               tot->dsk.lvm[i].name[0] = '\0';
+               tot->dsk.nlvm = i;
+
+               for (i=0; new->dsk.mdd[i].name[0]; i++)
+               {
+                       strcpy(tot->dsk.mdd[i].name, new->dsk.mdd[i].name);
+       
+                       tot->dsk.mdd[i].nread  += new->dsk.mdd[i].nread;
+                       tot->dsk.mdd[i].nwrite += new->dsk.mdd[i].nwrite;
+                       tot->dsk.mdd[i].nrsect += new->dsk.mdd[i].nrsect;
+                       tot->dsk.mdd[i].nwsect += new->dsk.mdd[i].nwsect;
+                       tot->dsk.mdd[i].io_ms  += new->dsk.mdd[i].io_ms;
+                       tot->dsk.mdd[i].avque  += new->dsk.mdd[i].avque;
+               }
+       
+               tot->dsk.mdd[i].name[0] = '\0';
+               tot->dsk.nmdd = i;
+               break;
+       }
+}
+
+
+/*
+** Generic function to subtract two counters taking into 
+** account the possibility of overflow of a 32-bit kernel-counter.
+*/
+count_t
+subcount(count_t newval, count_t oldval)
+{
+       if (newval >= oldval)
+               return newval - oldval;
+       else
+               return MAX32BITVAL + newval - oldval;
+}
diff --git a/sources/ifprop.c b/sources/ifprop.c
new file mode 100644 (file)
index 0000000..62e1c26
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+** ATOP - System & Process Monitor
+**
+** The program 'atop' offers the possibility to view the activity of
+** the system on system-level as well as process-level.
+**
+** ==========================================================================
+** Author:      Gerlof Langeveld
+** E-mail:      gerlof.langeveld@atoptool.nl
+** Date:        January 2007
+** --------------------------------------------------------------------------
+** Copyright (C) 2007-2010 Gerlof Langeveld
+**
+** This program is free software; you can redistribute it and/or modify it
+** under the terms of the GNU General Public License as published by the
+** Free Software Foundation; either version 2, or (at your option) any
+** later version.
+**
+** This program is distributed in the hope that it will be useful, but
+** WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+** See the GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+** --------------------------------------------------------------------------
+**
+** $Id: ifprop.c,v 1.5 2010/04/23 12:19:35 gerlof Exp $
+** $Log: ifprop.c,v $
+** Revision 1.5  2010/04/23 12:19:35  gerlof
+** Modified mail-address in header.
+**
+** Revision 1.4  2007/02/13 10:34:06  gerlof
+** Removal of external declarations.
+**
+*/
+#include <sys/types.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <linux/sockios.h>
+#include <linux/if.h>
+#include <linux/in.h>
+
+typedef __u64  u64;
+typedef __u32  u32;
+typedef __u16  u16;
+typedef __u8   u8;
+#include <linux/ethtool.h>
+#include <linux/wireless.h>
+
+#ifndef        SPEED_UNKNOWN
+#define        SPEED_UNKNOWN   -1
+#endif
+
+#include "atop.h"
+#include "ifprop.h"
+#include "photosyst.h"
+
+static struct ifprop   ifprops[MAXINTF];
+
+/*
+** function searches for the properties of a particular interface
+** the interface name should be filled in the struct ifprop before
+** calling this function
+**
+** return value reflects true or false
+*/
+int
+getifprop(struct ifprop *ifp)
+{
+       register int    i;
+
+       for (i=0; ifprops[i].name[0]; i++)
+       {
+               if (strcmp(ifp->name, ifprops[i].name) == 0)
+               {
+                       *ifp = ifprops[i];
+                       return 1;
+               }
+       }
+
+       ifp->type       = '?';
+       ifp->speed      = 0;
+       ifp->fullduplex = 0;
+
+       return 0;
+}
+
+/*
+** function stores properties of all interfaces in a static
+** table to be queried later on
+**
+** this function should be called with superuser privileges!
+*/
+void
+initifprop(void)
+{
+       FILE                    *fp;
+       char                    *cp, linebuf[2048];
+       int                     i=0, sockfd;
+       struct ethtool_cmd      ethcmd;
+       struct ifreq            ifreq;
+       struct iwreq            iwreq;
+
+       /*
+       ** open /proc/net/dev to obtain all interface names and open
+       ** a socket to determine the properties for each interface
+       */
+       if ( (fp = fopen("/proc/net/dev", "r")) == NULL)
+               return;
+
+       if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+       {
+               fclose(fp);
+               return;
+       }
+
+       /*
+       ** read every name and obtain properties
+       */
+       while ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
+       {
+               /*
+               ** skip lines containing a '|' symbol (headers)
+               */
+               if ( strchr(linebuf, '|') != NULL)
+                       continue;
+
+               if ( (cp = strchr(linebuf, ':')) != NULL)
+                       *cp = ' ';    /* subst ':' by space */
+
+               sscanf(linebuf, "%15s", ifprops[i].name);
+
+               /*
+               ** determine properties of ethernet interface
+               */
+               memset(&ifreq,  0, sizeof ifreq);
+               memset(&ethcmd, 0, sizeof ethcmd);
+
+               strncpy((void *)&ifreq.ifr_ifrn.ifrn_name, ifprops[i].name,
+                               sizeof ifreq.ifr_ifrn.ifrn_name-1);
+
+               ifreq.ifr_ifru.ifru_data = (void *)&ethcmd;
+
+               ethcmd.cmd = ETHTOOL_GSET;
+
+               if ( ioctl(sockfd, SIOCETHTOOL, &ifreq) == 0) 
+               {
+                       ifprops[i].type  = 'e'; // type ethernet
+                       ifprops[i].speed = ethtool_cmd_speed(&ethcmd);
+
+                       if (ifprops[i].speed == (u32)SPEED_UNKNOWN)
+                               ifprops[i].speed = 0;
+
+                       switch (ethcmd.duplex)
+                       {
+                          case DUPLEX_FULL:
+                               ifprops[i].fullduplex   = 1;
+                               break;
+                          default:
+                               ifprops[i].fullduplex   = 0;
+                       }
+
+                       if (++i >= MAXINTF-1)
+                               break;
+                       else
+                               continue;
+               }
+
+               /*
+               ** determine properties of wireless interface
+               */
+               memset(&iwreq,  0, sizeof iwreq);
+
+               strncpy(iwreq.ifr_ifrn.ifrn_name, ifprops[i].name,
+                               sizeof iwreq.ifr_ifrn.ifrn_name-1);
+
+               if ( ioctl(sockfd, SIOCGIWRATE, &iwreq) == 0) 
+               {
+                       ifprops[i].type       = 'w';    // type wireless
+                       ifprops[i].fullduplex = 0;
+
+                       ifprops[i].speed =
+                               (iwreq.u.bitrate.value + 500000) / 1000000;
+
+                       if (++i >= MAXINTF-1)
+                               break;
+                       else
+                               continue;
+               }
+
+               ifprops[i].type       = '?';    // type unknown
+               ifprops[i].fullduplex = 0;
+               ifprops[i].speed      = 0;
+
+               if (++i >= MAXINTF-1)
+                       break;
+       }
+
+       close(sockfd);
+       fclose(fp);
+}
diff --git a/sources/ifprop.h b/sources/ifprop.h
new file mode 100644 (file)
index 0000000..718f755
--- /dev/null
@@ -0,0 +1,10 @@
+struct ifprop  {
+       char            type;           /* type: 'e' (ethernet)         */
+                                       /*       'w' (wireless)         */
+       char            name[31];       /* name of interface            */
+       long int        speed;          /* in megabits per second       */
+       char            fullduplex;     /* boolean                      */
+};
+
+int    getifprop(struct ifprop *);
+void   initifprop(void);
diff --git a/sources/man/atop.1 b/sources/man/atop.1
new file mode 100644 (file)
index 0000000..d75c84a
--- /dev/null
@@ -0,0 +1,2091 @@
+.TH ATOP 1 "March 2017" "Linux"
+.SH NAME
+.B atop 
+- Advanced System & Process Monitor
+.SH SYNOPSIS
+Interactive Usage:
+.P
+.B atop
+[\-g|\-m|\-d|\-n|\-u|\-p|\-s|\-c|\-v|\-o|\-y] [\-C|\-M|\-D|\-N|\-A] [\-afFG1xR] [\-L linelen] [\-Plabel[,label]...]
+[
+.I interval
+[
+.I samples
+]]
+.P
+Writing and reading raw logfiles:
+.P
+.B atop
+\-w
+.I rawfile
+[\-a] [\-S]
+[
+.I interval
+[
+.I samples
+]]
+.br
+.B atop
+\-r [
+.I rawfile
+] [\-b 
+.I hh:mm
+] [\-e
+.I hh:mm
+] [\-g|\-m|\-d|\-n|\-u|\-p|\-s|\-c|\-v|\-o|\-y] [\-C|\-M|\-D|\-N|\-A] [\-fFG1xR] [\-L linelen] [\-Plabel[,label]...]
+.SH DESCRIPTION
+The program
+.I atop
+is an interactive monitor to view the load on a Linux system.
+It shows the occupation of the most critical hardware resources 
+(from a performance point of view) on system level, i.e. cpu, memory, disk
+and network.
+.br
+It also shows which processes are responsible for the indicated
+load with respect to cpu and memory load on process level.
+Disk load is shown per process if "storage accounting" is active in the kernel.
+Network load is shown per process if the kernel module `netatop'
+has been installed.
+.PP
+Every
+.I interval
+(default: 10 seconds) information is shown about the resource occupation
+on system level (cpu, memory, disks and network layers), followed
+by a list of processes which have been active during the last interval
+(note that all processes that were unchanged during the last interval
+are not shown, unless the key 'a' has been pressed or unless sorting on
+memory occupation is done).
+If the list of active processes does not entirely fit on
+the screen, only the top of the list is shown (sorted in order of activity).
+.br
+The intervals are repeated till the number of
+.I samples
+(specified as command argument) is reached, or till the key 'q' is pressed
+in interactive mode.
+.PP
+When 
+.I atop
+is started, it checks whether the standard output channel is connected to a
+screen, or to a file/pipe. In the first case it produces screen control 
+codes (via the ncurses library) and behaves interactively; in the second case
+it produces flat ASCII-output.
+.PP
+In interactive mode, the output of 
+.I atop
+scales dynamically to the current dimensions of the screen/window.
+.br
+If the window is resized horizontally, columns will be added or removed
+automatically. For this purpose, every column has a particular weight. The
+columns with the highest weights that fit within the current width will
+be shown.
+.br
+If the window is resized vertically, lines of the process/thread list 
+will be added or removed automatically.
+.PP
+Furthermore in interactive mode the output of 
+.I atop
+can be controlled by pressing particular keys.
+However it is also possible to specify such key as
+.B flag
+on the command line. In that case
+.I atop
+switches to the indicated mode on beforehand; this mode can 
+be modified again interactively. Specifying such key as flag is especially
+useful when running
+.I atop
+with output to a pipe or file (non-interactively).
+These flags are the same as the keys that can be pressed in interactive
+mode (see section INTERACTIVE COMMANDS).
+.br
+Additional flags are available to support storage of atop-data in raw 
+format (see section RAW DATA STORAGE).
+.SH PROCESS ACCOUNTING
+With every interval,
+.I atop
+reads the kernel administration to obtain information about all
+running processes.
+However, it is likely that during the interval also processes have terminated.
+These processes might have consumed system resources during
+this interval as well before they terminated.
+Therefor,
+.I atop
+tries to read the process accounting records that contain the accounting
+information of terminated processes and report these processes too.
+Only when the process accounting mechanism in the kernel is activated,
+the kernel writes such process accounting record to a file
+for every process that terminates.
+.PP
+There are various ways for
+.I atop
+to get access to the process accounting records (tried in this order):
+.PP
+.TP 4
+1.
+When the environment variable ATOPACCT is set,
+it specifies the name of the process accounting file.
+In that case, process accounting for this file
+should have been activated on beforehand.
+Before opening this file for reading,
+.I atop
+drops its root privileges (if any).
+.br
+When this environment variable is present but its
+contents is empty, process accounting will not be used at all.
+.PP
+.TP 4
+2.
+.B This is the preferred way of handling process accounting records!
+.br
+When the
+.I atopacctd
+daemon is active, it has activated the process accounting mechanism in
+the kernel and transfers to original accounting records to shadow files.
+In that case,
+.I atop
+drops its root privileges and opens the current shadow file for reading.
+.br
+This way is preferred, because the
+.I atopacctd
+daemon maintains full control of the sizes of the original process
+accounting file (written by the kernel) and the shadow files (read by the
+.I atop
+processes). For further information, refer to the
+.B atopacctd
+man page.
+.PP
+.TP 4
+3.
+When the
+.I atopacctd
+daemon is not active, 
+.I atop
+verifies if the process accounting mechanism has been switched on
+via the separate
+.B psacct
+package. In that case, the file
+.B /var/account/pacct
+is in use as process accounting file and 
+.I atop
+opens this file for reading.
+.PP
+.TP 4
+4.
+As a last possibility,
+.I atop
+itself tries to activate the process accounting mechanism (requires root
+privileges) using the file
+.B /var/cache/atop.d/atop.acct
+(to be written by the kernel, to be read by
+.I atop
+itself). Process accounting remains active as long as
+at least one
+.I atop
+process is alive.
+Whenever the last
+.I atop
+process stops (either by pressing `q' or by `kill \-15'), it deactivates the
+process accounting mechanism again. Therefor you should never terminate
+.I atop
+by `kill \-9', because then it has no chance to stop process accounting.
+As a result, the accounting file may consume a lot of 
+disk space after a while.
+.br
+To avoid that the process accounting file consumes too much disk space,
+.I atop
+verifies at the end of every sample if the size of the process accounting
+file exceeds 200 MiB and if this
+.I atop
+process is the only one that is currently using the file.
+In that case the file is truncated to a size of zero.
+
+Notice that root-privileges are required to switch on/off process accounting
+in the kernel. You can start
+.I atop
+as a root user or specify setuid-root privileges to the executable file.
+In the latter case,
+.I atop
+switches on process accounting and drops the root-privileges again.
+.br
+If
+.I atop
+does not run with root-privileges, it does not show information
+about finished processes.
+It indicates this situation with the message
+message `no procacct` in the top-right corner (instead of the counter that
+shows the number of exited processes).
+.PP
+When during one interval a lot of processes have finished,
+.I atop
+might grow tremendously in memory when reading all process accounting
+records at the end of the interval. To avoid such excessive growth,
+.I atop
+will never read more than 50 MiB with process information from the
+process accounting file per interval (approx. 70000 finished processes).
+In interactive mode a warning is given whenever processes have been skipped
+for this reason.
+.PP
+.SH COLORS
+For the resource consumption on system level,
+.I atop
+uses colors to indicate that a critical occupation percentage has
+been (almost) reached. 
+A critical occupation percentage means that is likely that this load
+causes a noticeable negative performance influence for applications using
+this resource. The critical percentage depends on the type of resource:
+e.g. the performance influence of a disk with a busy percentage of 80%
+might be more noticeable for applications/user than a CPU with a busy
+percentage of 90%.
+.br
+Currently
+.I atop
+uses the following default values to calculate a weighted percentage
+per resource:
+.PP
+.TP 5
+.B \ Processor
+A busy percentage of 90% or higher is considered `critical'.
+.TP 5
+.B \ Disk
+A busy percentage of 70% or higher is considered `critical'.
+.TP 5
+.B \ Network
+A busy percentage of 90% or higher for the load of an interface is
+considered `critical'.
+.TP 5
+.B \ Memory
+An occupation percentage of 90% is considered `critical'.
+Notice that this occupation percentage is the accumulated memory
+consumption of the kernel (including slab) and all processes; the
+memory for the page cache (`cache' and `buff' in the MEM-line) and the
+reclaimable part of the slab (`slrec`) is not implied!
+.br
+If the number of pages swapped out (`swout' in the PAG-line) is larger
+than 10 per second, the memory resource is considered `critical'.
+A value of at least 1 per second is considered `almost critical'.
+.br
+If the committed virtual memory exceeds the limit (`vmcom' and `vmlim'
+in the SWP-line), the SWP-line is colored due to overcommitting the system.
+.TP 5
+.B \ Swap
+An occupation percentage of 80% is considered `critical'
+because swap space might be completely exhausted in the near future;
+it is not critical from a performance point-of-view.
+.PP
+These default values can be modified in the configuration file
+(see separate man-page of atoprc).
+.PP
+When a resource exceeds its critical occupation percentage, the concerning
+values in the screen line are colored red by default.
+.br
+When a resource exceeded (default) 80% of its critical percentage
+(so it is almost critical), the concerning values in the screen line
+are colored cyan by default. This `almost critical percentage' (one value
+for all resources) can be modified in the configuration file
+(see separate man-page of atoprc).
+.br
+The default colors red and cyan can be modified in the configuration file
+as well (see separate man-page of atoprc).
+.PP
+With the key 'x' (or flag \-x), the use of colors can be suppressed.
+.SH NETATOP MODULE
+Per-process and per-thread network activity can be measured by the
+.I netatop
+kernel module. You can download this kernel module from the website
+(mentioned at the end of this manual page) and install it on your
+system if the kernel version is 2.6.24 or newer.
+.br
+When
+.I atop
+gathers counters for a new interval, it verifies if the
+.I netatop
+module is currently active. If so,
+.I atop
+obtains the relevant network counters from this module and shows
+the number of sent and received packets per process/thread in the generic
+screen. Besides, detailed counters can be requested by
+pressing the `n' key.
+.br
+When the
+.I netatopd
+daemon is running as well,
+.I atop
+also reads the network counters of exited processes that are logged
+by this daemon (comparable with process accounting).
+.PP
+More information about the optional
+.I netatop
+kernel module and the
+.I netatopd
+daemon can be found in the concerning man-pages and on the website
+mentioned at the end of this manual page.
+.SH INTERACTIVE COMMANDS
+When running
+.I atop
+interactively (no output redirection), keys can be pressed to control the
+output. In general, lower case keys can be used to show other information for
+the active processes and upper case keys can be used to influence the
+sort order of the active process/thread list.
+.PP
+.TP 5
+.B g
+Show generic output (default).
+
+Per process the following fields are shown in case of a window-width
+of 80 positions:
+process-id, cpu consumption during
+the last interval in system and user mode, the virtual and resident
+memory growth of the process.
+
+The subsequent columns depend on the used kernel:
+.br
+When the kernel supports "storage accounting" (>= 2.6.20), the data
+transfer for read/write on disk, the status and exit code are
+shown for each process.
+When the kernel does not support
+"storage accounting", the username, number of threads in the
+thread group, the status and exit code are shown.
+.br
+When the kernel module 'netatop' is loaded, the data transfer for send/receive
+of network packets is shown for each process.
+.br
+The last columns contain the state, the occupation percentage for the
+chosen resource (default: cpu) and the process name.
+
+When more than 80 positions are available, other information is added.
+.PP
+.TP 5
+.B m
+Show memory related output.
+
+Per process the following fields are shown in case of a window-width
+of 80 positions:
+process-id, minor and major
+memory faults, size of virtual shared text, total virtual 
+process size, total resident process size, virtual and resident growth during
+last interval, memory occupation percentage and process name.
+
+When more than 80 positions are available, other information is added.
+
+For memory consumption, always all processes are shown (also the processes
+that were not active during the interval).
+.PP
+.TP 5
+.B d
+Show disk-related output.
+
+When "storage accounting" is active in the kernel, the following
+fields are shown:
+process-id, amount of data read from disk, amount of data written to disk,
+amount of data that was written but has been withdrawn again (WCANCL),
+disk occupation percentage and process name.
+.PP
+.TP 5
+.B n
+Show network related output.
+
+Per process the following fields are shown in case of a window-width
+of 80 positions:
+process-id, thread-id,
+total bandwidth for received packets,
+total bandwidth for sent packets,
+number of received TCP packets with the average size per packet (in bytes),
+number of sent TCP packets with the average size per packet (in bytes),
+number of received UDP packets with the average size per packet (in bytes),
+number of sent UDP packets with the average size per packet (in bytes),
+the network occupation percentage and process name.
+.br
+This information can only be shown when kernel module `netatop' is installed.
+
+When more than 80 positions are available, other information is added.
+.PP
+.TP 5
+.B s
+Show scheduling characteristics.
+
+Per process the following fields are shown in case of a window-width
+of 80 positions:
+process-id,
+number of threads in state 'running' (R),
+number of threads in state 'interruptible sleeping' (S),
+number of threads in state 'uninterruptible sleeping' (D),
+scheduling policy (normal timesharing, realtime round-robin, realtime fifo),
+nice value, priority, realtime priority, current processor,
+status, exit code, state, the occupation percentage for the chosen
+resource and the process name.
+
+When more than 80 positions are available, other information is added.
+.PP
+.TP 5
+.B v
+Show various process characteristics.
+
+Per process the following fields are shown in case of a window-width
+of 80 positions:
+process-id, user name and group,
+start date and time, status (e.g. exit code if the process has finished),
+state, the occupation percentage for the chosen resource and the process name.
+
+When more than 80 positions are available, other information is added.
+.PP
+.TP 5
+.B c
+Show the command line of the process.
+
+Per process the following fields are shown: process-id,
+the occupation percentage for the chosen resource and the
+command line including arguments.
+.PP
+.TP 5
+.B o
+Show the user-defined line of the process.
+
+In the configuration file the keyword
+.I ownprocline
+can be specified with the description of a user-defined output-line.
+.br
+Refer to the man-page of
+.B atoprc
+for a detailed description.
+.PP
+.TP 5
+.B y
+Show the individual threads within a process (toggle).
+
+Single-threaded processes are still shown as one line.
+.br
+For multi-threaded processes, one line represents the process
+while additional lines show the activity
+per individual thread (in a different color). Depending on
+the option 'a' (all or active toggle), all threads are shown
+or only the threads that were active during the last interval.
+.br
+Whether this key is active or not can be seen in the header line.
+.PP
+.TP 5
+.B u
+Show the process activity accumulated per user.
+
+Per user the following fields are shown: number of processes active
+or terminated during last interval (or in total if combined with command `a'),
+accumulated cpu consumption during last interval in system and user mode,
+the current virtual and resident memory space consumed by active processes
+(or all processes of the user if combined with command `a'). 
+.br
+When "storage accounting" is active in the kernel,
+the accumulated read and write throughput on disk is shown.
+When the kernel module `netatop' has been installed,
+the number of received and sent network packets are shown.
+.br
+The last columns contain the accumulated occupation percentage for the
+chosen resource (default: cpu) and the user name.
+.PP
+.TP 5
+.B p
+Show the process activity accumulated per program (i.e. process name).
+
+Per program the following fields are shown: number of processes active
+or terminated during last interval (or in total if combined with command `a'),
+accumulated cpu consumption during last interval in system and user mode,
+the current virtual and resident memory space consumed by active processes
+(or all processes of the user if combined with command `a'). 
+.br
+When "storage accounting" is active in the kernel,
+the accumulated read and write throughput on disk is shown.
+When the kernel module `netatop' has been installed,
+the number of received and sent network packets are shown.
+.br
+The last columns contain the accumulated occupation percentage for the
+chosen resource (default: cpu) and the program name.
+.PP
+.TP 5
+.B j
+Show the process activity accumulated per Docker container.
+
+Per container the following fields are shown: number of processes active
+or terminated during last interval (or in total if combined with command `a'),
+accumulated cpu consumption during last interval in system and user mode,
+the current virtual and resident memory space consumed by active processes
+(or all processes of the user if combined with command `a'). 
+.br
+When "storage accounting" is active in the kernel,
+the accumulated read and write throughput on disk is shown.
+When the kernel module `netatop' has been installed,
+the number of received and sent network packets are shown.
+.br
+The last columns contain the accumulated occupation percentage for the
+chosen resource (default: cpu) and the Docker container id (CID).
+.PP
+.TP 5
+.B C 
+Sort the current list in the order of cpu consumption (default).
+The one-but-last column changes to ``CPU''.
+.PP
+.TP 5
+.B M 
+Sort the current list in the order of resident memory consumption.
+The one-but-last column changes to ``MEM''. In case of sorting on memory,
+the full process list will be shown (not only the active processes).
+.PP
+.TP 5
+.B D
+Sort the current list in the order of disk accesses issued.
+The one-but-last column changes to ``DSK''.
+.PP
+.TP 5
+.B N
+Sort the current list in the order of network bandwidth (received
+and transmitted).
+The one-but-last column changes to ``NET''.
+.PP
+.TP 5
+.B A
+Sort the current list automatically in the order of the most busy
+system resource during this interval.
+The one-but-last column shows either ``ACPU'', ``AMEM'', ``ADSK'' or ``ANET''
+(the preceding 'A' indicates automatic sorting-order).
+The most busy resource is determined by comparing the weighted
+busy-percentages of the system resources, as described earlier in
+the section COLORS.
+.br
+This option remains valid until
+another sorting-order is explicitly selected again.
+.br
+A sorting-order for disk is only possible when "storage accounting" is active.
+A sorting-order for network is only possible when the kernel module `netatop'
+is loaded.
+.PP
+Miscellaneous interactive commands:
+.PP
+.TP 5
+.B ?
+Request for help information (also the key 'h' can be pressed).
+.PP
+.TP 5
+.B V
+Request for version information (version number and date).
+.PP
+.TP 5
+.B R
+Gather and calculate the proportional set size of processes (toggle).
+Gathering of all values that are needed to calculate the PSIZE of a process
+is a relatively time-consuming task, so this key should only be active when
+analyzing the resident memory consumption of processes.
+.PP
+.TP 5
+.B x
+Suppress colors to highlight critical resources (toggle).
+.br
+Whether this key is active or not can be seen in the header line.
+.PP
+.TP 5
+.B z
+The pause key can be used to freeze the current situation in order to
+investigate the output on the screen. While 
+.I atop
+is paused, the keys described above can be pressed to show other
+information about the current list of processes.
+Whenever the pause key is pressed again,
+atop will continue with a next sample.
+.PP
+.TP 5
+.B i
+Modify the interval timer (default: 10 seconds). If an interval timer of 0 is
+entered, the interval timer is switched off. In that case a new sample can
+only be triggered manually by pressing the key 't'.
+.PP
+.TP 5
+.B t
+Trigger a new sample manually. This key can be pressed if the current sample
+should be finished before the timer has exceeded, or if no timer is set at all
+(interval timer defined as 0). In the latter case
+.I atop
+can be used as a stopwatch to measure the load being caused by a
+particular application transaction, without knowing on beforehand how many
+seconds this transaction will last.
+
+When viewing the contents of a raw file, this key can be used to show the
+next sample from the file.
+.PP
+.TP 5
+.B T
+When viewing the contents of a raw file, this key can be used to show the
+previous sample from the file.
+.PP
+.TP 5
+.B b
+When viewing the contents of a raw file, this key can be used to branch
+to a certain timestamp within the file (either forward or backward).
+.PP
+.TP 5
+.B r
+Reset all counters to zero to see the system and process activity since
+boot again.
+
+When viewing the contents of a raw file, this key can be used to rewind
+to the beginning of the file again.
+.PP
+.TP 5
+.B U
+Specify a search string for specific user names as a regular expression.
+From now on, only (active) processes will be shown from a user which matches
+the regular expression.
+The system statistics are still system wide.
+If the Enter-key is pressed without specifying a name, (active)
+processes of all users will be shown again.
+.br
+Whether this key is active or not can be seen in the header line.
+.PP
+.TP 5
+.B I
+Specify a list with one or more PIDs to be selected.
+From now on, only processes will be shown with a PID which matches
+one of the given list.
+The system statistics are still system wide.
+If the Enter-key is pressed without specifying a PID, all (active)
+processes will be shown again.
+.br
+Whether this key is active or not can be seen in the header line.
+.PP
+.TP 5
+.B P
+Specify a search string for specific process names as a regular expression.
+From now on, only processes will be shown with a name which matches the
+regular expression.
+The system statistics are still system wide.
+If the Enter-key is pressed without specifying a name, all (active)
+processes will be shown again.
+.br
+Whether this key is active or not can be seen in the header line.
+.PP
+.TP 5
+.B /
+Specify a specific command line search string as a regular expression.
+From now on, only processes will be shown with a command line which
+matches the regular expression.
+The system statistics are still system wide.
+If the Enter-key is pressed without specifying a string, all (active)
+processes will be shown again.
+.br
+Whether this key is active or not can be seen in the header line.
+.PP
+.TP 5
+.B J
+Specify a Docker container id of 12 (hexadecimal) characters.
+From now on, only processes will be shown that run in that specific
+Docker container (CID).
+The system statistics are still system wide.
+If the Enter-key is pressed without specifying a container id,
+all (active) processes will be shown again.
+.br
+Whether this key is active or not can be seen in the header line.
+.PP
+.TP 5
+.B S
+Specify search strings for specific logical volume names,
+specific disk names and specific network interface names. All
+search strings are interpreted as a regular expressions.
+From now on, only those system resources are shown that match
+the concerning regular expression.
+If the Enter-key is pressed without specifying a search string, all (active)
+system resources of that type will be shown again.
+.br
+Whether this key is active or not can be seen in the header line.
+.PP
+.TP 5
+.B a
+The `all/active' key can be used to toggle between only showing/accumulating
+the processes that were active during the last interval (default) or
+showing/accumulating all processes.
+.br
+Whether this key is active or not can be seen in the header line.
+.PP
+.TP 5
+.B G
+By default, 
+.I atop
+shows/accumulates the processes that are alive and the processes
+that are exited during the last interval. With this key (toggle),
+showing/accumulating the processes that are exited can be suppressed.
+.br
+Whether this key is active or not can be seen in the header line.
+.PP
+.TP 5
+.B f
+Show a fixed (maximum) number of header lines for system resources (toggle).
+By default only the lines are shown about system resources (CPUs, paging,
+logical volumes, disks, network interfaces) that really have been active
+during the last interval.
+With this key you can force
+.I atop
+to show lines of inactive resources as well.
+.br
+Whether this key is active or not can be seen in the header line.
+.PP
+.TP 5
+.B F
+Suppress sorting of system resources (toggle).
+By default system resources (CPUs, logical volumes, disks,
+network interfaces) are sorted on utilization.
+.br
+Whether this key is active or not can be seen in the header line.
+.PP
+.TP 5
+.B 1
+Show relevant counters as an average per second (in the format `..../s')
+instead of as a total during the interval (toggle).
+.br
+Whether this key is active or not can be seen in the header line.
+.PP
+.TP 5
+.B l
+Limit the number of system level lines for the counters per-cpu,
+the active disks and the network interfaces.
+By default lines are shown of all CPUs, disks and network interfaces
+which have been active during the last interval.
+Limiting these lines can be useful on systems with huge number CPUs,
+disks or interfaces in order to be able to run
+.I atop
+on a screen/window with e.g. only 24 lines.
+.br
+For all mentioned resources the maximum number of lines can be specified
+interactively. When using the flag
+.B -l
+the maximum number of per-cpu lines is set to 0,
+the maximum number of disk lines to 5 and
+the maximum number of interface lines to 3.
+These values can be modified again in interactive mode.
+.PP
+.TP 5
+.B k
+Send a signal to an active process (a.k.a. kill a process).
+.PP
+.TP 5
+.B q
+Quit the program.
+.PP
+.TP 5
+.B PgDn
+Show the next page of the process/thread list.
+.br
+With the arrow-down key the list can be scrolled downwards with single lines.
+.PP
+.TP 5
+.B ^F
+Show the next page of the process/thread list (forward).
+.br
+With the arrow-down key the list can be scrolled downwards with single lines.
+.PP
+.TP 5
+.B PgUp
+Show the previous page of the process/thread list.
+.br
+With the arrow-up key the list can be scrolled upwards with single lines.
+.PP
+.TP 5
+.B ^B
+Show the previous page of the process/thread list (backward).
+.br
+With the arrow-up key the list can be scrolled upwards with single lines.
+.PP
+.TP 5
+.B ^L
+Redraw the screen.
+.SH RAW DATA STORAGE
+In order to store system and process level statistics for long-term
+analysis (e.g. to check the system load and the active processes running
+yesterday between 3:00 and 4:00 PM),
+.I atop
+can store the system and process level statistics in
+compressed binary format in a raw file with the flag
+.B -w
+followed by the filename.
+If this file already exists and is recognized as a raw data file,
+.I atop
+will append new samples to the file (starting with a sample which reflects
+the activity since boot); if the file does not exist, it will be created.
+.br
+All processes/threads are stored in the raw file. 
+.br
+The interval (default: 10 seconds) and number of samples (default: infinite)
+can be passed as last arguments. Instead of the number of samples, the flag
+.B -S
+can be used to indicate that
+.I atop
+should finish anyhow before midnight.
+.PP
+A raw file can be read and visualized again with the flag
+.B -r
+followed by the filename. If no filename is specified, the file
+.BI /var/log/atop/atop_ YYYYMMDD
+is opened for input (where
+.I YYYYMMDD
+are digits representing the current date).
+If a filename is specified in the format YYYYMMDD (representing any valid
+date), the file
+.BI /var/log/atop/atop_ YYYYMMDD
+is opened.
+If a filename with the symbolic name
+.BI y
+is specified, yesterday's daily logfile is opened
+(this can be repeated so 'yyyy' indicates the logfile of four days ago). 
+.br
+The samples from the file can be viewed interactively by using the key 't'
+to show the next sample, the key 'T' to show the previous sample, the
+key 'b' to branch to a particular time or the key 'r' to rewind to
+the begin of the file.
+.br
+When output is redirected to a file or pipe,
+.B atop
+prints all samples in plain ASCII. The default line length is 80 characters
+in that case; with the flag
+.B -L
+followed by an alternate line length, more (or less) columns will be shown.
+.br
+With the flag
+.B -b
+(begin time) and/or
+.B -e
+(end time) followed by a time argument of the form HH:MM,
+a certain time period within the raw file can be selected.
+.PP
+When
+.B atop
+is installed, the script
+.B atop.daily
+is stored in the
+.I /usr/share/atop
+directory.
+This scripts takes care that
+.B atop
+is activated every day at midnight to write compressed binary data to the file
+.BI /var/log/atop/atop_ YYYYMMDD
+with an interval of 10 minutes.
+.br
+Furthermore the script removes all raw files which are older than four weeks.
+.br
+The script is activated via the
+.B cron
+daemon using the file
+.I /etc/cron.d/atop
+with the contents
+.br
+.B \ \ \ \ \ \ \ \ 0 0 * * * root /usr/share/atop/atop.daily
+.PP
+When the package
+.B psacct
+is installed, the process accounting is automatically restarted via the
+.B logrotate
+mechanism. The file
+.B /etc/logrotate.d/psaccs_atop
+takes care that
+.B atop
+is finished just before the rotation of the process accounting file
+and the file
+.B /etc/logrotate.d/psaccu_atop
+takes care that
+.B atop
+is restarted again after the rotation.
+When the package
+.B psacct
+is not installed, these logrotate-files have no effect.
+.SH OUTPUT DESCRIPTION
+The first sample shows the system level activity since boot
+(the elapsed time in the header shows the time since boot).
+Note that particular counters could have reached their maximum
+value (several times) and started by zero again,
+so do not rely on these figures.
+.PP
+For every sample
+.I atop
+first shows the lines related to system level activity. If a particular 
+system resource has not been used during the interval, the entire line
+related to this resource is suppressed. So the number of system level lines
+may vary for each sample.
+.br
+After that a list is shown of processes which have been active during the last
+interval. This list is by default sorted on cpu consumption, but this order
+can be changed by the keys which are previously described.
+.PP
+If values have to be shown by
+.I atop
+which do not fit in the column width,
+another format is used. If e.g. a cpu-consumption of 233216 milliseconds
+should be shown in a column width of 4 positions, it is shown as `233s'
+(in seconds).
+For large memory figures, another unit is chosen if the value does not fit
+(Mb instead of Kb, Gb instead of Mb, Tb instead of Gb, ...).
+For other values, a kind of exponent notation is used (value 123456789
+shown in a column of 5 positions gives 123e6).
+.SH OUTPUT DESCRIPTION - SYSTEM LEVEL
+The system level information consists of the following output lines:
+.PP
+.TP 5
+.B PRC
+Process and thread level totals.
+.br
+This line contains the total cpu time consumed 
+in system mode (`sys') and in user mode (`user'),
+the total number of processes present at this moment (`#proc'),
+the total number of threads present at this moment in state `running' (`#trun'),
+`sleeping interruptible' (`#tslpi') and `sleeping uninterruptible' (`#tslpu'),
+the number of zombie processes (`#zombie'),
+the number of clone system calls (`clones'), and
+the number of processes that ended during the interval
+(`#exit') when process accounting is used. Instead of `#exit` the last
+column may indicate that process accounting could not be activated
+(`no procacct`).
+.br
+If the screen-width does not allow all of these counters,
+only a relevant subset is shown.
+.PP
+.TP 5
+.B CPU
+CPU utilization.
+.br
+At least one line is shown for the total occupation of all CPUs together.
+.br
+In case of a multi-processor system, an additional line is shown
+for every individual processor (with `cpu' in lower case),
+sorted on activity. Inactive CPUs will not be shown by default.
+The lines showing the per-cpu occupation contain the cpu number in
+the last field.
+
+Every line contains the percentage of cpu time spent in 
+kernel mode by all active processes (`sys'), 
+the percentage of cpu time consumed in user mode (`user') for all
+active processes (including processes running with a nice value larger than
+zero), the percentage of cpu time spent for interrupt handling (`irq')
+including softirq, the percentage of unused cpu time while no processes
+were waiting for disk-I/O (`idle'), and
+the percentage of unused cpu time while at least one process was waiting
+for disk-I/O (`wait').
+.br
+In case of per-cpu occupation, the last column shows the cpu number and
+the wait percentage (`w') for that cpu.
+The number of lines showing the per-cpu occupation can be limited.
+
+For virtual machines the steal-percentage is shown (`steal'), reflecting
+the percentage of cpu time stolen by other virtual machines
+running on the same hardware.
+.br
+For physical machines hosting one or more virtual machines,
+the guest-percentage is shown (`guest'), reflecting
+the percentage of cpu time used by the virtual machines. Notice that
+this percentage overlaps the user-percentage.
+
+In case of frequency-scaling, all previously mentioned CPU-percentages
+are relative to the used scaling of the CPU during the interval.
+If a CPU has been active for e.g. 50% in user mode during the interval
+while the frequency-scaling of that CPU was 40%, only 20% of the full
+capacity of the CPU has been used in user mode.
+.br
+In case that the kernel module `cpufreq_stats' is active
+(after issueing `modprobe cpufreq_stats'), the
+.I average
+frequency (`avgf') and the
+.I average
+scaling percentage (`avgscal') is shown. Otherwise the
+.I current
+frequency (`curf') and the
+.I current
+scaling percentage (`curscal') is shown at the moment that the sample
+is taken.
+
+If the screen-width does not allow all of these counters,
+only a relevant subset is shown.
+.PP
+.TP 5
+.B CPL
+CPU load information.
+.br
+This line contains the load average figures reflecting the number
+of threads that are available to run on a CPU (i.e. part of the runqueue)
+or that are waiting for disk I/O. These figures are averaged over
+1 (`avg1'), 5 (`avg5') and 15 (`avg15') minutes.
+.br
+Furthermore the number of context switches (`csw'), the number
+of serviced interrupts (`intr') and the number of available CPUs are shown.
+
+If the screen-width does not allow all of these counters,
+only a relevant subset is shown.
+.PP
+.TP 5
+.B MEM
+Memory occupation.
+.br
+This line contains the total amount of physical memory
+(`tot'), the amount of memory which is currently free (`free'),
+the amount of memory in use as page cache including
+the total resident shared memory (`cache'), the amount of memory within the
+page cache that has to be flushed to disk (`dirty'), the amount
+of memory used for filesystem meta data (`buff'), the amount of
+memory being used for kernel mallocs (`slab'), the amount of
+slab memory that is reclaimable (`slrec'), the resident size of shared
+memory including tmpfs (`shmem`), the resident size of shared memory (`shrss`)
+the amount of shared memory that is currently swapped (`shswp`),
+the amount of memory that is currently claimed by vmware's
+balloon driver (`vmbal`),
+the amount of memory that is claimed for huge pages (`hptot`),
+and the amount of huge page memory that is really in use (`hpuse`).
+
+If the screen-width does not allow all of these counters,
+only a relevant subset is shown.
+.PP
+.TP 5
+.B SWP
+Swap occupation and overcommit info.
+.br
+This line contains the total amount of swap space on disk (`tot') and
+the amount of free swap space (`free').
+.br
+Furthermore the committed virtual memory space (`vmcom') and the maximum 
+limit of the committed space (`vmlim', which is by default swap size
+plus 50% of memory size) is shown.
+The committed space is the reserved virtual space for all allocations of
+private memory space for processes. The kernel only verifies whether the
+committed space exceeds the limit if strict overcommit handling is
+configured (vm.overcommit_memory is 2).
+.PP
+.TP 5
+.B PAG
+Paging frequency.
+.br
+This line contains the number of scanned pages (`scan') due to the fact
+that free memory drops below a particular threshold and the number
+times that the kernel tries to reclaim pages due to an urgent need (`stall').
+.br
+Also the number of memory pages the system read from swap space (`swin')
+and the number of memory pages the system wrote to swap space (`swout')
+are shown.
+.PP
+.TP 5
+.B LVM/MDD/DSK
+Logical volume/multiple device/disk utilization.
+.br
+Per active unit one line is produced, sorted on unit activity.
+Such line shows the name (e.g. VolGroup00-lvtmp for a logical volume or
+sda for a hard disk), the busy percentage i.e. the portion of time that the
+unit was busy handling requests (`busy'), the number of read requests issued
+(`read'), the number of write requests issued (`write'),
+the number of KiBytes per read (`KiB/r'), 
+the number of KiBytes per write (`KiB/w'), 
+the number of MiBytes per second throughput for reads (`MBr/s'), 
+the number of MiBytes per second throughput for writes (`MBw/s'), 
+the average queue depth (`avq')
+and the average number of milliseconds needed by a request (`avio')
+for seek, latency and data transfer.
+.br
+If the screen-width does not allow all of these counters,
+only a relevant subset is shown.
+
+The number of lines showing the units can be limited per class (LVM, MDD or
+DSK) with the 'l' key or statically (see separate man-page of atoprc).
+By specifying the value 0 for a particular class, no lines will be
+shown any more for that class.
+.PP
+.TP 5
+.B NFM
+Network Filesystem (NFS) mount at the client side.
+.br
+For each NFS-mounted filesystem, a line is shown that contains 
+the mounted server directory, the name of the server (`srv'),
+the total number of bytes physically read from the server (`read') and
+the total number of bytes physically written to the server (`write').
+Data transfer is subdivided in
+the number of bytes read via normal read() system calls (`nread'),
+the number of bytes written via normal read() system calls (`nwrit'),
+the number of bytes read via direct I/O (`dread'),
+the number of bytes written via direct I/O (`dwrit'),
+the number of bytes read via memory mapped I/O pages (`mread'), and
+the number of bytes written via memory mapped I/O pages (`mwrit').
+.PP
+.TP 5
+.B NFC
+Network Filesystem (NFS) client side counters.
+.br
+This line contains the number of RPC calls issues by local processes 
+(`rpc'), the number of read RPC calls (`read`) and
+write RPC calls (`rpwrite') issued to the NFS server,
+the number of RPC calls being retransmitted (`retxmit')
+and the number of authorization refreshes (`autref').
+.PP
+.TP 5
+.B NFS
+Network Filesystem (NFS) server side counters.
+.br
+This line contains the number of RPC calls received from 
+NFS clients (`rpc'),
+the number of read RPC calls received (`cread`),
+the number of write RPC calls received (`cwrit'),
+the number of network requests handled via TCP (`nettcp'), 
+the number of network requests handled via UDP (`netudp'),
+the number of Megabytes/second returned to read requests by clients (`MBcr/s`),
+the number of Megabytes/second passed in write requests by clients (`MBcw/s`),
+the number of reply cache hits (`rchits'),
+the number of reply cache misses (`rcmiss') and
+the number of uncached requests (`rcnoca').
+Furthermore some error counters indicating the number of requests
+with a bad format (`badfmt') or a bad authorization (`badaut'), and a
+counter indicating the number of bad clients (`badcln').
+and the number of authorization refreshes (`autref').
+.PP
+.TP 5
+.B NET
+Network utilization (TCP/IP). 
+.br
+One line is shown for activity of the transport layer (TCP and UDP), one line
+for the IP layer and one line per active interface.
+.br
+For the transport layer,
+counters are shown concerning the number of received TCP segments
+including those received in error (`tcpi'),
+the number of transmitted TCP segments excluding
+those containing only retransmitted octets (`tcpo'),
+the number of UDP datagrams received (`udpi'),
+the number of UDP datagrams transmitted (`udpo'),
+the number of active TCP opens (`tcpao'),
+the number of passive TCP opens (`tcppo'),
+the number of TCP output retransmissions (`tcprs'),
+the number of TCP input errors (`tcpie'),
+the number of TCP output resets (`tcpor'),
+the number of UDP no ports (`udpnp'), and
+the number of UDP input errors (`udpie').
+.br
+If the screen-width does not allow all of these counters,
+only a relevant subset is shown.
+.br
+These counters are related to IPv4 and IPv6 combined.
+
+For the IP layer, counters are shown concerning the number of IP datagrams
+received from interfaces, including those received in error (`ipi'),
+the number of IP datagrams that local higher-layer protocols offered for
+transmission (`ipo'), the number of received IP datagrams which were
+forwarded to other interfaces (`ipfrw'), the number of IP datagrams which
+were delivered to local higher-layer protocols (`deliv'),
+the number of received ICMP datagrams (`icmpi'), and
+the number of transmitted ICMP datagrams (`icmpo').
+.br
+If the screen-width does not allow all of these counters,
+only a relevant subset is shown.
+.br
+These counters are related to IPv4 and IPv6 combined.
+
+For every active network interface one line is shown,
+sorted on the interface activity.
+Such line shows the name of the interface and its busy percentage
+in the first column.
+The busy percentage for half duplex is determined by comparing the
+interface speed with the number of bits transmitted and received
+per second; for full duplex the interface speed is compared with the
+highest of either the transmitted or the received bits.
+When the interface speed can not be determined (e.g. for the loopback
+interface), `---' is shown instead of the percentage.
+.br
+Furthermore the number of received packets (`pcki'),
+the number of transmitted packets (`pcko'),
+the line speed of the interface (`sp'),
+the effective amount of bits received per second (`si'),
+the effective amount of bits transmitted per second (`so'),
+the number of collisions (`coll'),
+the number of received multicast packets (`mlti'),
+the number of errors while receiving a packet (`erri'),
+the number of errors while transmitting a packet (`erro'),
+the number of received packets dropped (`drpi'), and
+the number of transmitted packets dropped (`drpo').
+.br
+If the screen-width does not allow all of these counters,
+only a relevant subset is shown.
+.br
+The number of lines showing the network interfaces can be limited.
+.SH OUTPUT DESCRIPTION - PROCESS LEVEL
+Following the system level information, the processes are shown from which the
+resource utilization has changed during the last interval. These processes
+might have used cpu time or issued disk or network requests. However a process
+is also shown if part of it has been paged out due to lack of memory (while
+the process itself was in sleep state).
+.PP
+Per process the following fields may be shown (in alphabetical order),
+depending on the current output mode as described in the section
+INTERACTIVE COMMANDS and depending on the current width of your window:
+.PP
+.TP 9
+.B AVGRSZ
+The average size of one read-action on disk.
+.PP
+.TP 9
+.B AVGWSZ
+The average size of one write-action on disk.
+.PP
+.TP 9
+.B BANDWI
+Total bandwidth for received TCP and UDP packets consumed by this process
+(bits-per-second).
+This value can be compared with the value `si'
+on interface level (used bandwidth per interface).
+.br
+This information will only be shown when the kernel module `netatop'
+is loaded.
+.PP
+.TP 9
+.B BANDWO
+Total bandwidth for sent TCP and UDP packets consumed by this process
+(bits-per-second).
+This value can be compared with the value `so'
+on interface level (used bandwidth per interface).
+.br
+This information will only be shown when the kernel module `netatop'
+is loaded.
+.PP
+.TP 9
+.B CID
+Container ID (Docker) of 12 hexadecimal digits, referring to the container
+in which the process/thread is running.
+If a process has been started and finished during the last
+interval, a `?' is shown because the container ID is not part of
+the standard process accounting record.
+.PP
+.TP 9
+.B CMD
+The name of the process.
+This name can be surrounded by "less/greater than" 
+signs (`<name>') which means that the process has finished during the last
+interval.
+.br
+Behind the abbreviation `CMD' in the header line, the current page number and
+the total number of pages of the process/thread list are shown.
+.PP
+.TP 9
+.B COMMAND-LINE
+The full command line of the process (including arguments). If the length of
+the command line exceeds the length of the screen line, the arrow 
+keys -> and <- can be used for horizontal scroll.
+.br
+Behind the verb `COMMAND-LINE' in the header line, the current page number
+and the total number of pages of the process/thread list are shown.
+.PP
+.TP 9
+.B CPU
+The occupation percentage of this process related to the available capacity
+for this resource on system level.
+.PP
+.TP 9
+.B CPUNR
+The identification of the CPU the (main) thread is running on
+or has recently been running on.  
+.PP
+.TP 9
+.B CTID
+Container ID (OpenVZ).
+If a process has been started and finished during the last
+interval, a `?' is shown because the container ID is not part of
+the standard process accounting record.
+.PP
+.TP 9
+.B DSK
+The occupation percentage of this process related to the total load that
+is produced by all processes (i.e. total disk accesses
+by all processes during the last interval).
+.br
+This information is shown when per process "storage accounting" is active
+in the kernel.
+.PP
+.TP 9
+.B EGID
+Effective group-id under which this process executes.
+.PP
+.TP 9
+.B ENDATE
+Date that the process has been finished. If the process is still running,
+this field shows `active'.
+.PP
+.TP 9
+.B ENTIME
+Time that the process has been finished. If the process is still running,
+this field shows `active'.
+.PP
+.TP 9
+.B ENVID
+Virtual environment identified (OpenVZ only).
+.PP
+.TP 9
+.B EUID
+Effective user-id under which this process executes.
+.PP
+.TP 9
+.B EXC
+The exit code of a terminated process (second position of column `ST' is E)
+or the fatal signal number (second position of column `ST' is S or C).
+.PP
+.TP 9
+.B FSGID
+Filesystem group-id under which this process executes.
+.PP
+.TP 9
+.B FSUID
+Filesystem user-id under which this process executes.
+.PP
+.TP 9
+.B MAJFLT
+The number of page faults issued by this process that have been solved
+by creating/loading the requested memory page.
+.PP
+.TP 9
+.B MEM
+The occupation percentage of this process related to the available capacity
+for this resource on system level.
+.PP
+.TP 9
+.B MINFLT
+The number of page faults issued by this process that have been solved
+by reclaiming the requested memory page from the free list of pages.
+.PP
+.TP 9
+.B NET
+The occupation percentage of this process related to the total load that
+is produced by all processes (i.e. consumed network bandwidth
+of all processes during the last interval).
+.br
+This information will only be shown when kernel module `netatop' is loaded.
+.PP
+.TP 9
+.B NICE
+The more or less static priority that can be given to a process on a
+scale from -20 (high priority) to +19 (low priority).
+.PP
+.TP 9
+.B NPROCS
+The number of active and terminated processes accumulated for this user
+or program.
+.PP
+.TP 9
+.B PID
+Process-id.
+If a process has been started and finished during the last
+interval, a `?' is shown because the process-id is not part of
+the standard process accounting record.
+.PP
+.TP 9
+.B POLI
+The policies 'norm' (normal, which is SCHED_OTHER), 'btch' (batch)
+and 'idle' refer to timesharing processes.
+The policies 'fifo' (SCHED_FIFO) and 'rr' (round robin, which is SCHED_RR)
+refer to realtime processes.
+.PP
+.TP 9
+.B PPID
+Parent process-id.
+If a process has been started and finished during the last
+interval, value 0 is shown because the parent process-id is not part of
+the standard process accounting record.
+.PP
+.TP 9
+.B PRI
+The process' priority ranges from 0 (highest priority) to 139 (lowest
+priority). Priority 0 to 99 are used for realtime processes (fixed
+priority independent of their behavior) and priority 100 to 139 for
+timesharing processes (variable priority depending on their recent
+CPU consumption and the nice value).
+.PP
+.TP 9
+.B PSIZE
+The proportional memory size of this process (or user).
+.br
+Every process shares resident memory with other processes. E.g. when a
+particular program is started several times, the code pages (text) are
+only loaded once in memory and shared by all incarnations. Also the code
+of shared libraries is shared by all processes using that shared library,
+as well as shared memory and memory-mapped files.
+For the PSIZE calculation of a process, the resident memory of a process
+that is shared with other processes is divided by the number of sharers.
+This means, that every process is accounted for a proportional part of
+that memory. Accumulating the PSIZE values of all processes in the
+system gives a reliable impression of the total resident memory consumed
+by all processes.
+.br
+Since gathering of all values that are needed to calculate the PSIZE is a
+relatively time-consuming task, the 'R' key (or '-R' flag) should
+be active. Gathering these values also requires superuser privileges
+(otherwise '?K' is shown in the output).
+.br
+If a process has finished during the last interval, no value is shown
+since the proportional memory size is not part of the standard
+process accounting record.
+.PP
+.TP 9
+.B RDDSK 
+When the kernel maintains standard io statistics (>= 2.6.20):
+.br
+The read data transfer issued physically on disk (so reading from the
+disk cache is not accounted for).
+.br
+Unfortunately, the kernel aggregates the
+data tranfer of a process to the data transfer of its parent process when
+terminating, so you might see transfers for (parent) processes like
+cron, bash or init, that are not really issued by them.
+.PP
+.TP 9
+.B RGID
+The real group-id under which the process executes. 
+.PP
+.TP 9
+.B RGROW 
+The amount of resident memory that the process has grown during the last
+interval. A resident growth can be caused by touching memory pages which
+were not physically created/loaded before (load-on-demand). 
+Note that a resident growth can also be negative e.g. when part of the process
+is paged out due to lack of memory or when the process frees dynamically 
+allocated memory.
+For a process which started during the last interval, the resident growth
+reflects the total resident size of the process at that moment.
+.br
+If a process has finished during the last interval, no value is shown
+since resident memory occupation is not part of the standard
+process accounting record.
+.PP
+.TP 9
+.B RNET 
+The number of TCP- and UDP packets received by this process.
+This information will only be shown when kernel module `netatop' is installed.
+.br
+If a process has finished during the last interval, no value is shown
+since network counters are not part of the standard process accounting record.
+.PP
+.TP 9
+.B RSIZE
+The total resident memory usage consumed by this process (or user).
+Notice that the RSIZE of a process includes all resident memory used
+by that process, even if certain memory parts are shared with other processes
+(see also the explanation of PSIZE).
+.br
+If a process has finished during the last interval, no value is shown
+since resident memory occupation is not part of the standard
+process accounting record.
+.PP
+.TP 9
+.B RTPR
+Realtime priority according the POSIX standard.
+Value can be 0 for a timesharing process (policy 'norm', 'btch' or 'idle')
+or ranges from 1 (lowest) till 99 (highest) for a realtime process
+(policy 'rr' or 'fifo').
+.PP
+.TP 9
+.B RUID
+The real user-id under which the process executes. 
+.PP
+.TP 9
+.B S
+The current state of the (main) thread: `R' for running
+(currently processing or in the runqueue), `S' for sleeping interruptible
+(wait for an event to occur), 
+`D' for sleeping non-interruptible, `Z' for zombie (waiting to be synchronized
+with its parent process), `T' for stopped (suspended or traced), `W' for
+swapping, and `E' (exit) for processes which have finished during the last
+interval.
+.PP
+.TP 9
+.B SGID
+The saved group-id of the process.
+.PP
+.TP 9
+.B SNET 
+The number of TCP and UDP packets transmitted by this process.
+This information will only be shown when the kernel module `netatop'
+is loaded.
+.PP
+.TP 9
+.B ST
+The status of a process.
+.br
+The first position indicates if the process has been
+started during the last interval (the value 
+.I N
+means 'new process').
+
+The second position indicates if the process has been
+finished during the last interval.
+.br
+The value
+.I E
+means 'exit' on the process' own initiative; the exit code is displayed
+in the column `EXC'.
+.br
+The value
+.I S
+means that the process has been terminated unvoluntarily 
+by a signal; the signal number is displayed in the in the column `EXC'.
+.br
+The value
+.I C
+means that the process has been terminated unvoluntarily 
+by a signal, producing a core dump in its current directory;
+the signal number is displayed in the column `EXC'.
+.PP
+.TP 9
+.B STDATE
+The start date of the process.
+.PP
+.TP 9
+.B STTIME
+The start time of the process.
+.PP
+.TP 9
+.B SUID
+The saved user-id of the process.
+.PP
+.TP 9
+.B SWAPSZ
+The swap space consumed by this process (or user).
+.PP
+.TP 9
+.B SYSCPU
+CPU time consumption of this process in system mode (kernel mode), usually
+due to system call handling.
+.PP
+.TP 9
+.B TCPRASZ
+The average size of a received TCP buffer in bytes. 
+This information will only be shown when the kernel module `netatop'
+is loaded.
+.PP
+.TP 9
+.B TCPRCV
+The number of TCP packets received for this process.
+This information will only be shown when the kernel module `netatop'
+is loaded.
+.PP
+.TP 9
+.B TCPSASZ
+The average size of a transmitted TCP buffer in bytes.
+This information will only be shown when the kernel module `netatop'
+is loaded.
+.PP
+.TP 9
+.B TCPSND
+The number of TCP packets transmitted for this process.
+This information will only be shown when the kernel module `netatop'
+is loaded.
+.PP
+.TP 9
+.B THR
+Total number of threads within this process.
+All related threads are contained in a thread group, represented by
+.I atop
+as one line or as a separate line when the 'y' key (or -y flag) is active.
+
+On Linux 2.4 systems it is hardly possible to determine
+which threads (i.e. processes) are related to the same thread group.
+Every thread is represented by
+.I atop
+as a separate line.
+.PP
+.TP 9
+.B TID
+Thread-id.
+All threads within a process run with the same PID but with a
+different TID. This value is shown for individual threads in
+multi-threaded processes (when using the key 'y').
+.PP
+.TP 9
+.B TRUN
+Number of threads within this process that are in the state 'running' (R).
+.PP
+.TP 9
+.B TSLPI
+Number of threads within this process that are in the
+state 'interruptible sleeping' (S).
+.PP
+.TP 9
+.B TSLPU
+Number of threads within this process that are in the
+state 'uninterruptible sleeping' (D).
+.PP
+.TP 9
+.B UDPRASZ
+The average size of a received UDP packet in bytes.
+This information will only be shown when the kernel module `netatop'
+is loaded.
+.PP
+.TP 9
+.B UDPRCV
+The number of UDP packets received by this process.
+This information will only be shown when the kernel module `netatop'
+is loaded.
+.PP
+.TP 9
+.B UDPSASZ
+The average size of a transmitted UDP packets in bytes.
+This information will only be shown when the kernel module `netatop'
+is loaded.
+.PP
+.TP 9
+.B UDPSND
+The number of UDP packets transmitted by this process.
+This information will only be shown when the kernel module `netatop'
+is loaded.
+.PP
+.TP 9
+.B USRCPU
+CPU time consumption of this process in user mode, due to processing the
+own program text.
+.PP
+.TP 9
+.B VDATA
+The virtual memory size of the private data used by this process
+(including heap and shared library data).
+.PP
+.TP 9
+.B VGROW 
+The amount of virtual memory that the process has grown during the last
+interval. A virtual growth can be caused by e.g. issueing a malloc()
+or attaching a shared memory segment. Note that a virtual growth can also
+be negative by e.g. issueing a free() or detaching a shared memory segment.
+For a process which started during the last interval, the virtual growth
+reflects the total virtual size of the process at that moment.
+.br
+If a process has finished during the last interval, no value is shown
+since virtual memory occupation is not part of the standard
+process accounting record.
+.PP
+.TP 9
+.B VPID
+Virtual process-id (within an OpenVZ container).
+If a process has been started and finished during the last
+interval, a `?' is shown because the virtual process-id is not part of
+the standard process accounting record.
+.PP
+.TP 9
+.B VSIZE
+The total virtual memory usage consumed by this process (or user).
+.br
+If a process has finished during the last interval, no value is shown
+since virtual memory occupation is not part of the standard
+process accounting record.
+.PP
+.TP 9
+.B VSLIBS
+The virtual memory size of the (shared) text of all shared libraries used
+by this process.
+.PP
+.TP 9
+.B VSTACK
+The virtual memory size of the (private) stack used by this process
+.PP
+.TP 9
+.B VSTEXT
+The virtual memory size of the (shared) text of the executable program.
+.PP
+.TP 9
+.B WRDSK 
+When the kernel maintains standard io statistics (>= 2.6.20):
+.br
+The write data transfer issued physically on disk (so writing to the
+disk cache is not accounted for).
+This counter is maintained for the application process that writes its
+data to the cache (assuming that this data is physically transferred
+to disk later on). Notice that disk I/O needed for swapping is
+not taken into account.
+.br
+Unfortunately, the kernel aggregates the
+data tranfer of a process to the data transfer of its parent process when
+terminating, so you might see transfers for (parent) processes like
+cron, bash or init, that are not really issued by them.
+.PP
+.TP 9
+.B WCANCL
+When the kernel maintains standard io statistics (>= 2.6.20):
+.br
+The write data transfer previously accounted for this process
+or another process that has been cancelled.
+Suppose that a process writes new data to a file and that data is removed
+again before the cache buffers have been flushed to disk.
+Then the original process shows the written data as WRDSK, while
+the process that removes/truncates the file shows
+the unflushed removed data as WCANCL.
+.SH PARSEABLE OUTPUT
+With the flag
+.B -P
+followed by a list of one or more labels (comma-separated),
+parseable output is produced for each sample.
+The labels that can be specified for system-level statistics
+correspond to the labels (first verb of each line)
+that can be found in the interactive output:
+"CPU", "cpu" "CPL" "MEM", "SWP", "PAG", "LVM", "MDD", "DSK", "NFM",
+"NFC", "NFS" and "NET".
+.br
+For process-level statistics special labels are introduced:
+"PRG" (general), "PRC" (cpu), "PRM" (memory), "PRD" (disk, only if
+"storage accounting" is active) and "PRN" (network, only if
+the kernel module 'netatop' has been installed). 
+.br
+With the label "ALL", all system and process level statistics are shown.
+.PP
+For every interval all requested lines are shown whereafter
+.B atop
+shows a line just containing the label "SEP" as a separator before the
+lines for the next sample are generated.
+.br
+When a sample contains the values since boot,
+.B atop
+shows a line just containing the label "RESET" before the
+lines for this sample are generated.
+.PP
+The first part of each output-line consists of the following six fields:
+.B label
+(the name of the label),
+.B host
+(the name of this machine),
+.B epoch
+(the time of this interval as number of seconds since 1-1-1970),
+.B date
+(date of this interval in format YYYY/MM/DD),
+.B time
+(time of this interval in format HH:MM:SS), and
+.B interval
+(number of seconds elapsed for this interval).
+.PP
+The subsequent fields of each output-line depend on the label:
+.PP
+.TP 9
+.B CPU
+Subsequent fields:
+total number of clock-ticks per second for this machine,
+number of processors,
+consumption for all CPUs in system mode (clock-ticks),
+consumption for all CPUs in user mode (clock-ticks),
+consumption for all CPUs in user mode for niced processes (clock-ticks),
+consumption for all CPUs in idle mode (clock-ticks),
+consumption for all CPUs in wait mode (clock-ticks),
+consumption for all CPUs in irq mode (clock-ticks),
+consumption for all CPUs in softirq mode (clock-ticks),
+consumption for all CPUs in steal mode (clock-ticks),
+consumption for all CPUs in guest mode (clock-ticks) overlapping user mode,
+frequency of all CPUs and frequency percentage of all CPUs.
+.TP 9
+.B cpu
+Subsequent fields:
+total number of clock-ticks per second for this machine,
+processor-number,
+consumption for this CPU in system mode (clock-ticks),
+consumption for this CPU in user mode (clock-ticks),
+consumption for this CPU in user mode for niced processes (clock-ticks),
+consumption for this CPU in idle mode (clock-ticks),
+consumption for this CPU in wait mode (clock-ticks),
+consumption for this CPU in irq mode (clock-ticks),
+consumption for this CPU in softirq mode (clock-ticks),
+consumption for this CPU in steal mode (clock-ticks),
+consumption for this CPU in guest mode (clock-ticks) overlapping user mode,
+frequency of this CPU and frequency percentage of this CPU.
+.TP 9
+.B CPL
+Subsequent fields:
+number of processors,
+load average for last minute,
+load average for last five minutes,
+load average for last fifteen minutes,
+number of context-switches, and
+number of device interrupts.
+.TP 9
+.B MEM
+Subsequent fields:
+page size for this machine (in bytes),
+size of physical memory (pages),
+size of free memory (pages),
+size of page cache (pages),
+size of buffer cache (pages),
+size of slab (pages),
+dirty pages in cache (pages),
+reclaimable part of slab (pages),
+total size of vmware's balloon pages (pages),
+total size of shared memory (pages),
+size of resident shared memory (pages),
+size of swapped shared memory (pages),
+huge page size (in bytes),
+total size of huge pages (huge pages), and
+size of free huge pages (huge pages).
+.TP 9
+.B SWP
+Subsequent fields:
+page size for this machine (in bytes),
+size of swap (pages),
+size of free swap (pages),
+0 (future use),
+size of committed space (pages), and
+limit for committed space (pages).
+.TP 9
+.B PAG
+Subsequent fields:
+page size for this machine (in bytes),
+number of page scans,
+number of allocstalls,
+0 (future use),
+number of swapins, and 
+number of swapouts.
+.TP 9
+.B LVM/MDD/DSK
+For every logical volume/multiple device/hard disk one line is shown.
+.br
+Subsequent fields:
+name,
+number of milliseconds spent for I/O,
+number of reads issued,
+number of sectors transferred for reads,
+number of writes issued,
+and number of sectors transferred for write.
+.TP 9
+.B NFM
+Subsequent fields:
+mounted NFS filesystem,
+total number of bytes read,
+total number of bytes written,
+number of bytes read by normal system calls,
+number of bytes written by normal system calls,
+number of bytes read by direct I/O,
+number of bytes written by direct I/O,
+number of pages read by memory-mapped I/O, and
+number of pages written by memory-mapped I/O.
+.TP 9
+.B NFC
+Subsequent fields:
+number of transmitted RPCs,
+number of transmitted read RPCs,
+number of transmitted write RPCs,
+number of RPC retransmissions, and
+number of authorization refreshes.
+.TP 9
+.B NFS
+Subsequent fields:
+number of handled RPCs,
+number of received read RPCs,
+number of received write RPCs,
+number of bytes read by clients,
+number of bytes written by clients,
+number of RPCs with bad format,
+number of RPCs with bad authorization,
+number of RPCs from bad client,
+total number of handled network requests,
+number of handled network requests via TCP,
+number of handled network requests via UDP,
+number of handled TCP connections,
+number of hits on reply cache,
+number of misses on reply cache, and
+number of uncached requests.
+.TP 9
+.B NET
+First one line is produced for the upper layers of the TCP/IP stack.
+.br
+Subsequent fields:
+the verb "upper",
+number of packets received by TCP,
+number of packets transmitted by TCP,
+number of packets received by UDP,
+number of packets transmitted by UDP,
+number of packets received by IP,
+number of packets transmitted by IP,
+number of packets delivered to higher layers by IP, and
+number of packets forwarded by IP.
+
+Next one line is shown for every interface.
+.br
+Subsequent fields:
+name of the interface,
+number of packets received by the interface,
+number of bytes received by the interface,
+number of packets transmitted by the interface,
+number of bytes transmitted by the interface,
+interface speed, and
+duplex mode (0=half, 1=full).
+.TP 9
+.B PRG
+For every process one line is shown.
+.br
+Subsequent fields:
+PID (unique ID of task), name (between brackets), state,
+real uid, real gid, TGID (group number of related tasks/threads),
+total number of threads,
+exit code, start time (epoch),
+full command line (between brackets), PPID,
+number of threads in state 'running' (R),
+number of threads in state 'interruptible sleeping' (S),
+number of threads in state 'uninterruptible sleeping' (D),
+effective uid, effective gid,
+saved uid, saved gid,
+filesystem uid, filesystem gid, elapsed time (hertz),
+is_process (y/n), OpenVZ  virtual pid (VPID), OpenVZ container id (CTID)
+and Docker container id (CID).
+.TP 9
+.B PRC
+For every process one line is shown.
+.br
+Subsequent fields:
+PID, name (between brackets), state,
+total number of clock-ticks per second for this machine,
+CPU-consumption in user mode (clockticks),
+CPU-consumption in system mode (clockticks),
+nice value, priority, realtime priority,
+scheduling policy, current CPU, sleep average,
+TGID (group number of related tasks/threads) and is_process (y/n).
+.TP 9
+.B PRM
+For every process one line is shown.
+.br
+Subsequent fields:
+PID, name (between brackets), state,
+page size for this machine (in bytes),
+virtual memory size (Kbytes),
+resident memory size (Kbytes),
+shared text memory size (Kbytes),
+virtual memory growth (Kbytes),
+resident memory growth (Kbytes),
+number of minor page faults,
+number of major page faults,
+virtual library exec size (Kbytes),
+virtual data size (Kbytes),
+virtual stack size (Kbytes),
+swap space used (Kbytes),
+TGID (group number of related tasks/threads), is_process (y/n) and
+proportional set size (Kbytes) if in 'R' option is specified.
+.TP 9
+.B PRD
+For every process one line is shown.
+.br
+Subsequent fields:
+PID, name (between brackets), state,
+obsoleted kernel patch installed ('n'),
+standard io statistics used ('y' or 'n'),
+number of reads on disk,
+cumulative number of sectors read,
+number of writes on disk, 
+cumulative number of sectors written,
+cancelled number of written sectors,
+TGID (group number of related tasks/threads) and is_process (y/n).
+.br
+If the standard I/O statistics (>= 2.6.20) are not used,
+the disk I/O counters per process are not relevant.
+The counters 'number of reads on disk' and 'number of writes on disk' are
+obsoleted anyhow.
+.TP 9
+.B PRN
+For every process one line is shown.
+.br
+Subsequent fields:
+PID, name (between brackets), state,
+kernel module 'netatop' loaded ('y' or 'n'),
+number of TCP-packets transmitted,
+cumulative size of TCP-packets transmitted,
+number of TCP-packets received,
+cumulative size of TCP-packets received,
+number of UDP-packets transmitted,
+cumulative size of UDP-packets transmitted,
+number of UDP-packets received,
+cumulative size of UDP-packets transmitted,
+number of raw packets transmitted (obsolete, always 0),
+number of raw packets received (obsolete, always 0),
+TGID (group number of related tasks/threads) and is_process (y/n).
+.br
+If the kernel module is not active, the network I/O counters
+per process are not relevant.
+.SH EXAMPLES
+To monitor the current system load interactively with an interval of 5 seconds:
+.PP
+.TP 12
+.B \  atop 5
+.PP
+To monitor the system load and write it to a file (in plain ASCII)
+with an interval of one minute during half an hour with active
+processes sorted on memory consumption:
+.PP
+.TP 12
+.B \  atop -M 60 30 > /log/atop.mem
+.PP
+Store information about the system and process activity in binary compressed
+form to a file with an interval of ten minutes during an hour:
+.PP
+.TP 12
+.B \  atop -w /tmp/atop.raw 600 6
+.PP
+View the contents of this file interactively:
+.PP
+.B \  atop -r /tmp/atop.raw
+.PP
+View the processor and disk utilization of this file in parseable format:
+.PP
+.B \  atop -PCPU,DSK -r /tmp/atop.raw
+.PP
+View the contents of today's standard logfile interactively:
+.PP
+.B \  atop -r
+.PP
+View the contents of the standard logfile of the day before yesterday
+interactively:
+.PP
+.B \  atop -r yy
+.PP
+View the contents of the standard logfile of 2014, June 7 from
+02:00 PM onwards interactively:
+.PP
+.B \  atop -r 20140607 -b 14:00
+.PP
+.SH FILES
+.PP
+.TP 5
+.B /var/run/pacct_shadow.d/
+Directory containing the process accounting shadow files that are
+used by
+.I atop
+when the
+.I atopacctd
+daemon is active.
+.PP
+.TP 5
+.B /var/cache/atop.d/atop.acct
+File in which the kernel writes the accounting records when
+.I atop
+itself has activated the process accounting mechanism.
+.PP
+.TP 5
+.B /etc/atoprc
+Configuration file containing system-wide default values.
+See related man-page.
+.PP
+.TP 5
+.B ~/.atoprc
+Configuration file containing personal default values.
+See related man-page.
+.PP
+.TP 5
+.BI /var/log/atop/atop_ YYYYMMDD
+Raw file, where
+.I YYYYMMDD
+are digits representing the current date.
+This name is used by the script
+.B atop.daily
+as default name for the output file, and by
+.B atop
+as default name for the input file when using the
+.B -r
+flag.
+.br
+All binary system and process level data in this file has been stored
+in compressed format. 
+.PP
+.TP 5
+.BI /var/run/netatop.log
+File that contains the netpertask structs containing the network
+counters of exited processes. These structs are written by the
+.I netatopd
+daemon and read by
+.I atop
+after reading the standard process accounting records.
+.SH SEE ALSO
+.B atopsar(1),
+.B atoprc(5),
+.B atopacctd(8),
+.B netatop(4),
+.B netatopd(8),
+.B logrotate(8)
+.br
+.B http://www.atoptool.nl
+.SH AUTHOR
+Gerlof Langeveld (gerlof.langeveld@atoptool.nl)
+.br
+JC van Winkel
diff --git a/sources/man/atopacctd.8 b/sources/man/atopacctd.8
new file mode 100644 (file)
index 0000000..d6a8f69
--- /dev/null
@@ -0,0 +1,147 @@
+.TH ATOPACCTD 8 "March 2017" "Linux"
+.SH NAME
+.B atopacctd
+- process accounting daemon
+.SH SYNOPSIS
+.P
+.B atopacctd
+[-v | topdirectory]
+.P
+.SH DESCRIPTION
+The
+.I atopacctd
+daemon switches on the process accounting feature in the kernel
+and let the process accounting records be written to a file,
+called the source file from now.
+After process accounting is activated, the
+.I atopacctd
+daemon transfers every process accounting record that is available
+in the source file to a shadow file.
+Client processes (like
+.I atop
+processes) will read the shadow files instead of the
+process accounting source file.
+.br
+In this way, the
+.I atopacctd
+daemon operates as a 'layer' between the process accounting file
+that is written by the kernel and the shadow accounting files that are read by
+.I atop
+processes.
+.PP
+This approach has the following advantages:
+.PP
+.TP 3
+.B o
+The
+.I atopacctd
+daemon takes care that the source file is kept to a limited size.
+As soon as its maximum size is reached, it is truncated to a size
+of zero again (this is not noticed by the
+.I atop
+processes).
+.PP
+.TP 3
+.B o
+The
+.I atopacct
+daemon takes care that a shadow file is kept to a limited size.
+As soon as the current shadow file reaches this maximum size, the
+.I atopacctd
+daemon creates a new (subsequent) shadow file.
+While client processes still have the possibility to read the previous
+shadow file(s), the
+.I atopacctd
+daemon continues writing accounting records to the newest (current)
+shadow file.
+For this reason, the name of a shadow file consists of a 10-digit
+sequence number followed by the extension '.paf' (process acounting file).
+Old shadow files that are not used by client processes any more, are
+automatically removed by the garbage collector in the
+.I atopacctd
+daemon.
+.PP
+.TP 3
+.B o
+When no client processes are active (any more), all shadow files
+will be deleted and no records will be transferred to a shadow file
+any more. As soon as at least one client is activate again, the
+.I atopacctd
+daemon continues writing shadow files.
+.PP
+The directory
+.B /var/run
+is used as the default topdirectory.
+Below this top-directory, the source file
+.B pacct_source
+is created to which the kernel writes the process accounting records.
+.br
+Furthermore, the subdirectory
+.B pacct_shadow.d
+is created as a 'container' for the shadow files.
+Apart from the shadow files, also the file
+.B current
+is maintained in this subdirectory, containing the sequence number of the
+current (newest) shadow file and the maximum number of records that will be
+written in each shadow file.
+.PP
+An alternative topdirectory can be specified as command line argument.
+When an alternative topdirectory is defined, also modify the
+configuration file
+.B /etc/atoprc
+to inform
+.I atop
+clients about this alternative location (see the 
+.B atoprc
+man page).
+Such alternative topdirectory should be owned by root and may not be
+writable for the group or others (security reasons).
+.PP
+Notice that the kernel suspends writing process accounting records
+when the free space of the filesystem on which the process accounting file
+resides drops below 2%. Writing is resumed when the free space is 4% or more.
+These lowwater and highwater percentages can be configured via the
+.B /proc/sys/kernel/acct
+pseudo-file.
+.br
+The
+.I atopacctd
+daemon suspends transferring process accounting records to shadow files
+when the free space of the filesystem on which the process accounting file
+resides drops below 5%. Transfer is resumed when the free space is 5% or more.
+Log messages are generated via syslog when writing to the current shadow
+file is suspended or resumed.
+.PP
+The
+.B -v
+flag can be used to verify the version of the
+.I atopacctd
+daemon.
+.PP
+.SH FILES
+.PP
+.TP 5
+.B /var/run/pacct_source
+Regular file to which the kernel writes the process accounting records.
+This file will be regularly truncated.
+.PP
+.TP 5
+.B /var/run/pacct_shadow.d/current
+Regular file containing the sequence number of the current shadow file
+and the maximum number of records per shadow file.
+.PP
+.TP 5
+.B /var/run/pacct_shadow.d/N.paf
+Regular files containing the process accounting records that have
+been copied transparently from the source file (N represents a 10-digit
+sequence number).
+.SH SEE ALSO
+.B atop(1),
+.B atopsar(1),
+.B atoprc(5),
+.B netatop(4),
+.B netatopd(8)
+.br
+.B http://www.atoptool.nl
+.SH AUTHOR
+Gerlof Langeveld (gerlof.langeveld@atoptool.nl)
diff --git a/sources/man/atoprc.5 b/sources/man/atoprc.5
new file mode 100644 (file)
index 0000000..6efcde3
--- /dev/null
@@ -0,0 +1,403 @@
+.TH ATOPRC 5 "March 2017" "Linux"
+.SH NAME
+.B atoprc
+- atop/atopsar related rcfile
+.SH DESCRIPTION
+This manual page documents the rcfile of the
+.I atop
+and
+.I atopsar
+commands.
+These commands can be used to monitor the system and process load on a
+Linux system.
+.PP
+The atoprc file contains the default settings. These settings are read
+during startup, first from the system-wide rcfile
+.I /etc/atoprc
+and after that from the user-specific rcfile
+.I ~/.atoprc
+(so system-wide settings can be overruled by an individual user).
+The options in both rcfiles are identical.
+.PP
+.SH OPTIONS
+.PP
+The rcfile contains keyword-value pairs, one on every line (blank lines
+and lines starting with a #-sign are ignored).
+.br
+The following keywords can be specified:
+.PP
+.TP 4
+.B flags
+A list of default flags for
+.B atop
+can be defined here. The flags which are allowed
+are 'g', 'm', 'd', 'n', 'u', 'p', 's', 'c', 'v', 'C', 'M', 'D', 'N', 'A',
+\&'a', 'y', 'f', 'F', 'G', 'R', '1' and 'x'.
+.PP
+.TP 4
+.B interval
+The default interval value in seconds.
+.PP
+.TP 4
+.B linelen
+The length of a screen line when sending output to a file or pipe (default 80).
+.PP
+.TP 4
+.B username
+The default regular expression for the users for which active
+processes will be shown.
+.PP
+.TP 4
+.B procname
+The default regular expression for the process names to be shown.
+.PP
+.TP 4
+.B maxlinecpu
+The maximum number of active CPU's that will be shown.
+.PP
+.TP 4
+.B maxlinelvm
+The maximum number of active logical volumes that will be shown.
+.PP
+.TP 4
+.B maxlinemdd
+The maximum number of active multiple devices that will be shown.
+.PP
+.TP 4
+.B maxlinedisk
+The maximum number of active disks that will be shown.
+.PP
+.TP 4
+.B maxlinenfsm
+The maximum number of NFS mounts that will be shown on an NFS client.
+.PP
+.TP 4
+.B maxlineintf
+The maximum number of active network interfaces that will be shown.
+.PP
+.TP 4
+.B maxlinecont
+The maximum number of active containers that will be shown.
+.PP
+.TP 4
+.B cpucritperc
+The busy percentage considered critical for a processor
+(see section COLORS in the man-page of the
+.I atop
+command).
+This percentage is used to determine 
+a weighted percentage for line coloring and sorting of active processes.
+When this value is zero, no line coloring or automatic sorting is performed
+for this resource.
+.PP
+.TP 4
+.B dskcritperc
+The busy percentage considered critical for a disk
+(see section COLORS in the man-page of the
+.I atop
+command).
+This percentage is used to determine 
+a weighted percentage for line coloring and sorting of active processes.
+When this value is zero, no line coloring or automatic sorting is performed
+for this resource.
+.PP
+.TP 4
+.B netcritperc
+The busy percentage considered critical for a network interface
+(see section COLORS in the man-page of the
+.I atop
+command).
+This percentage is used to determine 
+a weighted percentage for line coloring and sorting of active processes.
+When this value is zero, no line coloring or automatic sorting is performed
+for this resource.
+.PP
+.TP 4
+.B memcritperc
+The percentage considered critical for memory utilization
+(see section COLORS in the man-page of the
+.I atop
+command).
+This percentage is used to determine 
+a weighted percentage for line coloring and sorting of active processes.
+When this value is zero, no line coloring or automatic sorting is performed
+for this resource.
+.PP
+.TP 4
+.B swpcritperc
+The occupation percentage considered critical for swap space
+(see section COLORS in the man-page of the
+.I atop
+command).
+This percentage is used to determine 
+a weighted percentage for line coloring and sorting of active processes.
+When this value is zero, no line coloring or automatic sorting is performed
+for this resource.
+.PP
+.TP 4
+.B swoutcritsec
+The number of pages swapped out per second considered critical for 
+for memory utilization
+(see section COLORS in the man-page of the
+.I atop
+command).
+This threshold is used in combination with 'memcritperc' to determine a
+weighted percentage for line coloring and sorting of active processes.
+When this value is zero, no line coloring or automatic sorting is performed
+for this resource.
+.PP
+.TP 4
+.B almostcrit
+A percentage of the critical percentage to determine if the resource
+is almost critical
+(see section COLORS in the man-page of the
+.I atop
+command).
+When this value is zero, no line coloring for `almost critical' is
+performed.
+.PP
+.TP 4
+.B colorinfo
+Definition of color name for information messages (default: green).
+.br
+Allowed colors are: red green yellow blue magenta cyan black white.
+.PP
+.TP 4
+.B colorthread
+Definition of color name for thread-specific lines when using
+the 'y' option (default: yellow).
+.br
+Allowed colors are: red green yellow blue magenta cyan black white.
+.PP
+.TP 4
+.B coloralmost
+Definition of color name for almost critical resources (default: cyan).
+.br
+Allowed colors are: red green yellow blue magenta cyan black white.
+.PP
+.TP 4
+.B colorcritical
+Definition of color name for critical resources (default: red).
+.br
+Allowed colors are: red green yellow blue magenta cyan black white.
+.PP
+.TP 4
+.B atopsarflags
+A list of default flags for
+.B atopsar
+can be defined here. The flags that are allowed
+are 'S', 'x', 'C', 'M', 'H', 'a', 'A' and the flags to select
+one or more specific reports.
+.PP
+.TP 4
+.B pacctdir
+The name of the topdirectory used by the
+.B atopacctd
+daemon. In this directory, the daemon creates a subdirectory 
+.B pacct_shadow.d
+in which files will be written containing the process accounting records.
+The default topdirectory is
+.B /var/run
+and this option only has to be specified when the
+.B atopacctd
+daemon is started with an alternative topdirectory as command line argument.
+.br
+This option can only be specified in the 
+.B /etc/atoprc
+file (on system level)!
+.PP
+An example of the
+.B /etc/atoprc
+or
+.B ~/.atoprc
+file:
+.TP 8
+\ 
+.br
+flags\ \ \ \ \ \ \ \ \ Aaf
+.br
+interval\ \ \ \ \ \ 5
+.br
+username
+.br
+procname
+.br
+maxlinecpu\ \ \ \ 4
+.br
+maxlinedisk\ \ \ 10
+.br
+maxlineintf\ \ \ 5
+.br
+cpucritperc\ \ \ 80
+.br
+almostcrit\ \ \ \ 90
+.br
+atopsarflags\ \ CMH
+.br
+ownprocline\ \ \ PID:50 VGROW:40 RGROW:45 COMMAND-LINE:50
+.br
+ownpagline\ \ \ \ PAGSCAN:3 BLANKBOX:0 PAGSWIN:3 PAGSWOUT:7
+.PP
+The keywords 'ownprocline' and 'ownpagline' are explained in the
+subsequent section.
+.SH OWN DEFINITION OF OUTPUT LINE
+Via the rcfile it is possible to define the layout of the output lines
+yourself, i.e. you can define the layout of one line with process information
+with the keyword 'ownprocline' (to be selected with the key 'o'
+or the flag \-o) and you can redefine all lines with system information.
+.PP
+The layout of an output-line can be defined as follows
+(notice that this should be specified as one line in the rcfile):
+.PP
+\ \ \ keyword\ \ \ <columnid>:<prio> [<columnid>:<prio> ...]
+.PP
+The
+.B columnid
+is the symbolic name of a column that should shown at this position
+in the output line.
+.br
+The
+.B prio
+is a positive integer value that determines which columns have precedence
+whenever not all specified columns fit into the current screen-width.
+The higher value, the higher priority.
+.br
+The column-specifications should be separated by a space. The order
+in which columns have been specified is the order in which they will be
+shown, with respect to their priority (columns that do not fit, will be
+dropped dynamically).
+.PP
+A special columnid for system lines is 'BLANKBOX'. This indicates
+that an empty column is required at this position. Also this
+special columnid is followed by a priority (usually low). 
+.PP
+The following definition can be specified for process information:
+.PP
+.TP 4
+.B ownprocline
+The columnid's are the names of the columns that are shown in the
+normal output of the process-related lines that are shown by
+.I atop
+such as 'PID', 'CMD', 'S', ....
+The only exception is the special columnid 'SORTITEM' that is used to
+show one of the columns CPU%/DSK%/MEM%/NET%, depending on the chosen
+sort-criterium.
+.br
+An example of a user-defined process line:
+.PP
+.TP 8
+\ 
+ownprocline\ \ \ PID:20 PPID:10 SYSCPU:15 USRCPU:15
+VGROW:14 VSIZE:12 RGROW:14 RSIZE:12 ST:8 EXC:7 S:11 SORTITEM:18 CMD:20
+.PP
+The following definitions are used internally by
+.I atop
+as the default system lines (you can redefine each of them in the
+rcfile as one line):
+.PP
+.TP 4
+.B ownsysprcline
+Redefinition of line labeled with 'PRC':
+.PP
+.TP 8
+\ 
+ownsysprcline\ \ \ PRCSYS:8 PRCUSER:8 BLANKBOX:0 PRCNPROC:7 PRCNZOMBIE:5 PRCCLONES:4 BLANKBOX:0 PRCNNEXIT:6
+.PP
+.TP 4
+.B ownallcpuline
+Redefinition of line labeled with 'CPU' for total CPU-utilization:
+.PP
+.TP 8
+\ 
+ownallcpuline\ \ \ CPUSYS:8 CPUUSER:7 CPUIRQ:4 BLANKBOX:0 CPUIDLE:5 CPUWAIT:6 BLANKBOX:0 CPUSTEAL:1 CPUGUEST:3
+.PP
+.TP 4
+.B ownonecpuline
+Redefinition of line labeled with 'CPU' for utilization of one CPU:
+.PP
+.TP 8
+\ 
+ownonecpuline\ \ \ CPUISYS:8 CPUIUSER:7 CPUIIRQ:4 BLANKBOX:0 CPUIIDLE:5 CPUIWAIT:6 BLANKBOX:0 CPUISTEAL:1 CPUIGUEST:3
+.PP
+.TP 4
+.B owncplline
+Redefinition of line labeled with 'CPL':
+.PP
+.TP 8
+\ 
+owncplline\ \ \ CPLAVG1:4 CPLAVG5:3 CPLAVG15:2 BLANKBOX:0 CPLCSW:6 CPLINTR:5 BLANKBOX:0 CPLNUMCPU:1
+.PP
+.TP 4
+.B ownmemline
+Redefinition of line labeled with 'MEM':
+.PP
+.TP 8
+\ 
+ownmemline\ \ \ MEMTOT:2 MEMFREE:5 MEMCACHE:3 MEMDIRTY:1 MEMBUFFER:3 MEMSLAB:3 BLANKBOX:0 BLANKBOX:0 BLANKBOX:0 BLANKBOX:0
+.PP
+.TP 4
+.B ownswpline
+Redefinition of line labeled with 'SWP':
+.PP
+.TP 8
+\ 
+ownswpline\ \ \ SWPTOT:3 SWPFREE:4 BLANKBOX:0 BLANKBOX:0 BLANKBOX:0 BLANKBOX:0 BLANKBOX:0 BLANKBOX:0 SWPCOMMITTED:5 SWPCOMMITLIM:6
+.PP
+.TP 4
+.B ownpagline
+Redefinition of line labeled with 'PAG':
+.PP
+.TP 8
+\ 
+ownpagline\ \ \ PAGSCAN:3 PAGSTALL:1 BLANKBOX:0 PAGSWIN:4 PAGSWOUT:3
+.PP
+.TP 4
+.B owndskline
+Redefinition of lines labeled with 'LVM', 'MDD' and 'DSK':
+.PP
+.TP 8
+\ 
+owndskline\ \ \ DSKNAME:8 DSKBUSY:7 DSKNREAD:6 DSKNWRITE:6 DSKKBPERRD:4 DSKKBPERWR:4 DSKMBPERSECRD:5 DSKMBPERSECWR:5 DSKAVQUEUE:1 DSKAVIO:5
+.PP
+.TP 4
+.B ownnettrline
+Redefinition of line labeled with 'NET' for transport:
+.PP
+.TP 8
+\ 
+ownnettrline\ \ \ NETTRANSPORT:9 NETTCPI:8 NETTCPO:8 NETUDPI:8 NETUDPO:8 NETTCPACTOPEN:6 NETTCPPASVOPEN:5 NETTCPRETRANS:4 NETTCPINERR:3 NETTCPORESET:20 NETUDPNOPORT:1 NETUDPINERR:3
+.PP
+.TP 4
+.B ownnetnetline
+Redefinition of line labeled with 'NET' for network:
+.PP
+.TP 8
+\ 
+ownnetnetline\ \ \ NETNETWORK:5 NETIPI:4 NETIPO:4 NETIPFRW:4 NETIPDELIV:4 BLANKBOX:0 BLANKBOX:0 BLANKBOX:0 NETICMPIN:1 NETICMPOUT:1
+.PP
+.TP 4
+.B ownnetifline
+Redefinition of line labeled with 'NET' for interfaces:
+.PP
+.TP 8
+\ 
+ownnetifline\ \ \ NETNAME:8 NETPCKI:7 NETPCKO:7 NETSPEEDIN:6 NETSPEEDOUT:6 NETCOLLIS:3 NETMULTICASTIN:2 NETRCVERR:5 NETSNDERR:5 NETRCVDROP:4 NETSNDDROP:4
+.PP
+The lines above are shown in the order as shown by
+.I atop
+in combination with the
+.B -f
+flag (in a very wide window you should be able to see all of the columns).
+.SH SEE ALSO
+.B atop(1),
+.B atopsar(1),
+.B atopacctd(8),
+.B netatop(4),
+.B netatopd(8),
+.B logrotate(8)
+.br
+.B http://www.atoptool.nl
+.SH AUTHOR
+Gerlof Langeveld (gerlof.langeveld@atoptool.nl)
+.br
+JC van Winkel
diff --git a/sources/man/atopsar.1 b/sources/man/atopsar.1
new file mode 100644 (file)
index 0000000..4f4e1e0
--- /dev/null
@@ -0,0 +1,1151 @@
+.TH ATOPSAR 1 "March 2017" "Linux"
+.SH NAME
+.B atopsar
+- Advanced System Activity Report (atop related)
+.SH SYNOPSIS
+.P
+.B atopsar
+[\-flags...]
+[\-r
+.I file|date
+] [\-R
+.I cnt
+] [\-b
+.I hh:mm
+] [\-e
+.I hh:mm
+]
+.br
+.B atopsar
+[\-flags...]
+.I interval
+[
+.I samples
+]
+.P
+.SH DESCRIPTION
+The program
+.I atopsar
+can be used to report statistics on system level.
+.PP
+In the first synopsis line (no sampling interval specified),
+.I atopsar
+extracts data from a raw logfile that has been recorded previously by
+the program
+.I atop
+(option
+.B -w 
+of the
+.I atop
+program).
+.br
+You can specify the name of the logfile with the 
+.B -r
+option of the
+.I atopsar
+program.
+When a daily logfile of
+.I atop
+is used, named
+.B /var/log/atop/atop_YYYYMMDD
+(where YYYYMMDD reflects the date),
+the required date of the form YYYYMMDD can be specified with the
+.B -r
+option instead of the filename, or
+the symbolic name 'y' can be used for yesterday's daily logfile
+(this can be repeated so 'yyyy' indicates the logfile of four days ago).
+If the
+.B -r
+option is not specified at all, today's daily logfile is used by default.
+.br
+The starting and ending times of the report can be defined using the
+options
+.B -b
+and
+.B -e
+followed by a time argument of the form hh:mm.
+.PP
+In the second synopsis line,
+.B atopsar
+reads actual activity counters from the kernel with the specified
+.I interval
+(in seconds) and the specified number of
+.I samples
+(optionally).
+When
+.B atopsar
+is activated in this way it immediately sends the output for every requested
+report to standard output.
+If only one type of report is requested, the header is printed
+once and after every
+.I interval
+seconds the statistical counters are shown for that period.
+If several reports are requested, a header is printed per sample
+followed by the statistical counters for that period.
+.PP
+Some generic flags can be specified to influence the behaviour of the
+.B atopsar
+program:
+.PP
+.TP 5
+.B -S
+By default the timestamp at the beginning of a line is suppressed if more
+lines are shown for one interval. With this flag a timestamp is
+given for every output-line (easier for post-processing).
+.PP
+.TP 5
+.B -a
+By default certain resources as disks and network interfaces are only
+shown when they were active during the interval.
+With this flag all resources of a given type are shown, even if
+they were inactive during the interval.
+.PP
+.TP 5
+.B -x
+By default
+.B atopsar
+only uses colors if output is directed to a terminal (window).
+These colors might indicate that a critical occupation percentage has
+been reached (red) or has been almost reached (cyan) for a particular
+resource.
+See the man-page of
+.B atop
+for a detailed description of this feature (section COLORS).
+.br
+With the flag 
+.B -x
+the use of colors is suppressed unconditionally.
+.PP
+.TP 5
+.B -C
+By default
+.B atopsar
+only uses colors if output is directed to a terminal (window).
+These colors might indicate that a critical occupation percentage has
+been reached (red) or has been almost reached (cyan) for a particular
+resource.
+See the man-page of
+.B atop
+for a detailed description of this feature (section COLORS).
+.br
+With the flag 
+.B -C
+colors will always be used, even if output is not directed to a terminal.
+.PP
+.TP 5
+.B -M
+Use markers at the end of a line to indicate that a critical occupation
+percentage has been reached ('*') or has been almost reached ('+')
+for particular resources. The marker '*' is similar to the color red
+and the marker '+' to the color cyan. See the man-page of
+.B atop
+for a detailed description of these colors (section COLORS).
+.PP
+.TP 5
+.B -H
+Repeat the header line within a report for every
+.I N
+detail lines. The value of
+.I N 
+is determined dynamically in case of output to a tty/window (depending
+on the number of lines); for output to a file or pipe this value is 23.
+.PP
+.TP 5
+.B -R
+Summarize
+.I cnt
+samples into one sample. When the logfile contains e.g. samples of 10 minutes,
+the use of the flag '\-R 6' shows a report with one sample for every hour.
+.PP
+Other flags are used to define which reports are required:
+.PP
+.TP 5
+.B -A
+Show all possible reports.
+.PP
+.TP 5
+.B -c
+Report about CPU utilization (in total and per cpu).
+.PP
+.TP 5
+.B -p
+Report about processor-related matters, like load-averages and
+hardware interrupts.
+.PP
+.TP 5
+.B -P
+Report about processes.
+.PP
+.TP 5
+.B -m
+Current memory- and swap-occupation.
+.PP
+.TP 5
+.B -s
+Report about paging- and swapping-activity, and overcommitment.
+.PP
+.TP 5
+.B -l
+Report about utilization of logical volumes.
+.PP
+.TP 5
+.B -f
+Report about utilization of multiple devices.
+.PP
+.TP 5
+.B -d
+Report about utilization of disks.
+.PP
+.TP 5
+.B -n
+Report about NFS mounted filesystems on NFS client.
+.PP
+.TP 5
+.B -j
+Report about NFS client activity.
+.PP
+.TP 5
+.B -J
+Report about NFS server activity.
+.PP
+.TP 5
+.B -i
+Report about the network interfaces.
+.PP
+.TP 5
+.B -I
+Report about errors for network-interfaces.
+.PP
+.TP 5
+.B -w
+Report about IP version 4 network traffic.
+.PP
+.TP 5
+.B -W
+Report about errors for IP version 4 traffic.
+.PP
+.TP 5
+.B -y
+General report about ICMP version 4 layer activity.
+.PP
+.TP 5
+.B -Y
+Per-type report about ICMP version 4 layer activity.
+.PP
+.TP 5
+.B -u
+Report about UDP version 4 network traffic.
+.PP
+.TP 5
+.B -z
+Report about IP version 6 network traffic.
+.PP
+.TP 5
+.B -Z
+Report about errors for IP version 6 traffic.
+.PP
+.TP 5
+.B -k
+General report about ICMP version 6 layer activity.
+.PP
+.TP 5
+.B -K
+Per-type report about ICMP version 6 layer activity.
+.PP
+.TP 5
+.B -U
+Report about UDP version 6 network traffic.
+.PP
+.TP 5
+.B -t
+Report about TCP network traffic.
+.PP
+.TP 5
+.B -T
+Report about errors for TCP-traffic.
+.PP
+.TP 5
+.B -O
+Report about top-3 processes consuming most processor capacity.
+This report is only available when using a log file (not when specifying
+an interval).
+.PP
+.TP 5
+.B -G
+Report about top-3 processes consuming most resident memory.
+This report is only available when using a log file (not when specifying
+an interval).
+.PP
+.TP 5
+.B -D
+Report about top-3 processes issueing most disk transfers.
+This report is only available when using a log file (not when specifying
+an interval).
+.PP
+.TP 5
+.B -N
+Report about top-3 processes issueing most IPv4/IPv6 socket transfers.
+This report is only available when using a log file (not when specifying
+an interval).
+.SH OUTPUT DESCRIPTION
+Depending on the requested report, a number of columns with
+output values are produced.
+The values are mostly presented as a number of events per second.
+.PP
+The output for the flag
+.B -c
+contains the following columns per cpu:
+.TP 12
+.B usr%
+Percentage of cpu-time consumed in user mode (program text) for all
+active processes running with a nice value of zero (default) or a
+negative nice value (which means a higher priority than usual).
+The cpu consumption in user mode of processes with a nice value larger
+than zero (lower priority) is indicated in the nice%-column.
+.TP 12
+.B nice%
+Percentage of cpu time consumed in user mode (i.e. program text) for all
+processes running witn a nice value larger than zero (which means with a
+lower priority than average).
+.TP 12
+.B sys%
+Percentage of cpu time consumed in system mode (kernel text) for all
+active processes. A high percentage usually indicates a lot of system calls
+being issued.
+.TP 12
+.B irq%
+Percentage of cpu time consumed for handling of device interrupts.
+.TP 12
+.B softirq%
+Percentage of cpu time consumed for soft interrupt handling.
+.TP 12
+.B steal%
+Percentage of cpu time stolen by other virtual machines
+running on the same hardware.
+.TP 12
+.B guest%
+Percentage of cpu time used by other virtual machines
+running on the same hardware (overlaps with usr%/nice%).
+.TP 12
+.B wait%
+Percentage of unused cpu time while
+at least one of the processes in wait-state awaits completion of disk I/O.
+.TP 12
+.B idle%
+Percentage of unused cpu time because all processes are in a wait-state
+but not waiting for disk-I/O.
+.PP
+The output for the flag
+.B -p
+contains the following values:
+.TP 12
+.B pswch/s
+Number of process switches (also called context switches) per second on this
+cpu. A process switch occurs at the moment that an active thread (i.e.
+the thread using a cpu) enters a wait state or has used its time slice
+completely; another thread will then be chosen to use the cpu.
+.TP 12
+.B devintr/s
+Number of hardware interrupts handled per second on this cpu.
+.TP 12
+.B  clones/s
+The number of new threads started per second.
+.TP 12
+.B loadavg1
+Load average reflecting the average number of threads in the runqueue
+or in non-interruptible wait state (usually waiting for disk or tape I/O)
+during the last minute.
+.TP 12
+.B loadavg5
+Load average reflecting the average number of threads in the runqueue
+or in non-interruptible wait state (usually waiting for disk or tape I/O)
+during the last 5 minutes.
+.TP 12
+.B loadavg15
+Load average reflecting the average number of threads in the runqueue
+or in non-interruptible wait state (usually waiting for disk or tape I/O)
+during the last 15 minutes.
+.PP
+The output for the flag
+.B -P
+contains information about the processes and threads:
+.TP 12
+.B clones/s
+The number of new threads started per second.
+.TP 12
+.B pexit/s
+.TP 12
+.B curproc
+Total number of processes present in the system.
+.TP 12
+.B curzomb
+Number of zombie processes present in the system.
+.TP 12
+.B thrrun
+Total number of threads present in the system in state 'running'.
+.TP 12
+.B thrslpi
+Total number of threads present in the system in
+state 'interruptible sleeping'.
+.TP 12
+.B thrslpu
+Total number of threads present in the system in
+state 'uninterruptible sleeping'.
+.PP
+The output for the flag
+.B -m
+contains information about the memory- and swap-utilization:
+.TP 12
+.B memtotal
+Total usable main memory size.
+.TP 12
+.B memfree
+Available main memory size at this moment (snapshot).
+.TP 12
+.B buffers
+Main memory used at this moment to cache metadata-blocks (snapshot).
+.TP 12
+.B cached
+Main memory used at this moment to cache data-blocks (snapshot).
+.TP 12
+.B dirty
+Amount of memory in the page cache that still has to be flushed to disk
+at this moment (snapshot).
+.TP 12
+.B slabmem
+Main memory used at this moment for dynamically allocated memory
+by the kernel (snapshot).
+.TP 12
+.B swptotal
+Total swap space size at this moment (snapshot).
+.TP 12
+.B swpfree
+Available swap space at this moment (snapshot).
+.PP
+The output for the flag
+.B -s
+contains information about the frequency of swapping:
+.TP 12
+.B  pagescan/s
+Number of scanned pages per second due to the fact
+that free memory drops below a particular threshold.
+.TP 12
+.B  swapin/s
+The number of memory-pages the system read from the swap-device per second.
+.TP 12
+.B  swapout/s
+The number of memory-pages the system wrote to the swap-device per second.
+.TP 12
+.B  commitspc
+The committed virtual memory space i.e.
+the reserved virtual space for all allocations of
+private memory space for processes.
+.TP 12
+.B  commitlim
+The maximum limit for the committed space, which is by default swap size
+plus 50% of memory size.
+The kernel only verifies whether the committed space exceeds the limit
+if strict overcommit handling is configured (vm.overcommit_memory is 2).
+.PP
+The output for the flags
+.B -l
+(LVM),
+.B -f
+(MD), and
+.B -d 
+(hard disk) contains the following columns per active unit:
+.TP 12
+.B disk
+Name.
+.TP 12
+.B busy
+Busy-percentage of the unit (i.e. the portion of time that the
+device was busy handling requests).
+.TP 12
+.B read/s
+Number of read-requests issued per second on this unit.
+.TP 12
+.B KB/read
+Average number of Kbytes transferred per read-request for this unit.
+.TP 12
+.B writ/s
+Number of write-requests issued per second on this unit.
+.TP 12
+.B KB/writ
+Average number of Kbytes transferred per write-request for this unit.
+.TP 12
+.B avque
+Average number of requests outstanding in the queue during the time
+that the unit is busy.
+.TP 12
+.B avserv
+Average number of milliseconds needed by a request on this unit
+(seek, latency and data-transfer).
+.PP
+The output for the flag
+.B -n
+contains information about activity on NFS mounted filesystems (client):
+.TP 12
+.B mounted_device
+Mounted device containing server name and server directory being mounted.
+.TP 12
+.B physread/s
+Kilobytes data physically read from the NFS server by processes running
+on the NFS client.
+.TP 12
+.B KBwrite/s
+Kilobytes data physically written to the NFS server by processes running
+on the NFS client.
+.br
+When the NFS filesystem was mounted during the interval, the state 'M' is
+shown.
+.PP
+The output for the flag
+.B -j
+contains information about NFS client activity:
+.TP 12
+.B rpc/s
+Number of RPC calls per second issued to NFS server(s).
+.TP 12
+.B rpcread/s
+Number of read RPC calls per second issued to NFS server(s).
+.TP 12
+.B rpcwrite/s
+Number of write RPC calls per second issued to NFS server(s).
+.TP 12
+.B retrans/s
+Number of retransmitted RPC calls per second.
+.TP 12
+.B autrefresh/s
+Number of authorization refreshes per second.
+.PP
+The output for the flag
+.B -J
+contains information about NFS server activity:
+.TP 12
+.B rpc/s
+Number of RPC calls per second received from NFS client(s).
+.TP 12
+.B rpcread/s
+Number of read RPC calls per second received from NFS client(s).
+.TP 12
+.B rpcwrite/s
+Number of write RPC calls per second received from NFS client(s).
+.TP 12
+.B MBcr/s
+Number of Megabytes per second returned to read requests by clients.
+.TP 12
+.B MBcw/s
+Number of Megabytes per second passed in write requests by clients.
+.TP 12
+.B nettcp/s
+Number of requests per second handled via TCP.
+.TP 12
+.B netudp/s
+Number of requests per second handled via UDP.
+.PP
+The output for the flag
+.B -i
+provides information about utilization of network interfaces:
+.TP 12
+.B interf
+Name of interface.
+.TP 12
+.B busy
+Busy percentage for this interface.
+If the linespeed of this interface could not be determined
+(for virtual interfaces or in case that
+.B atop
+or
+.B atopsar
+had no root-privileges), a question mark is shown.
+.TP 12
+.B ipack/s
+Number of packets received from this interface per second.
+.TP 12
+.B opack/s
+Number of packets transmitted to this interface per second.
+.TP 12
+.B iKbyte/s
+Number of Kbytes received from this interface per second.
+.TP 12
+.B oKbyte/s
+Number of Kbytes transmitted via this interface per second.
+.TP 12
+.B imbps/s
+Effective number of megabits received per second.
+.TP 12
+.B ombps/s
+Effective number of megabits transmitted per second.
+.TP 12
+.B maxmbps/s
+Linespeed as number of megabits per second.
+If the linespeed could not be determined (for virtual interfaces
+or in case that
+.B atop
+or
+.B atopsar
+had no root-privileges), value 0 is shown.
+.br
+The linespeed is followed by the indication 'f' (full duplex)
+or 'h' (half duplex).
+.PP
+The output for the flag
+.B -I
+provides information about the failures that were detected for
+network interfaces:
+.TP 12
+.B interf
+Name of interface.
+.TP 12
+.B ierr/s
+Number of bad packets received from this interface per second.
+.TP 12
+.B oerr/s
+Number of times that packet transmission to this interface failed per second.
+.TP 12
+.B coll/s
+Number of collisions encountered per second while transmitting packets.
+.TP 12
+.B idrop/s
+Number of received packets dropped per second due to lack of buffer-space
+in the local system.
+.TP 12
+.B odrop/s
+Number of transmitted packets dropped per second due to lack of buffer-space
+in the local system.
+.TP 12
+.B iframe/s
+Number of frame alignment-errors encountered per second on received packets.
+.TP 12
+.B ocarrier/s
+Number of carrier-errors encountered per second on transmitted packets.
+.PP
+The output for the flag
+.B -w
+provides information about the utilization of the IPv4-layer
+(formal SNMP-names between brackets):
+.TP 12
+.B inrecv/s
+Number of IP datagrams received from interfaces per second, including
+those received in error (ipInReceives).
+.TP 12
+.B outreq/s
+Number of IP datagrams that local higher-layer protocols
+supplied to IP in requests for transmission per second (ipOutRequests).
+.TP 12
+.B indeliver/s
+Number of received IP datagrams that have been successfully delivered to
+higher protocol-layers per second (ipInDelivers).
+.TP 12
+.B forward/s
+Number of received IP datagrams per second for which this entity was not
+their final IP destination, as a result of which an attempt was made to
+forward (ipForwDatagrams).
+.TP 12
+.B reasmok/s
+Number of IP datagrams successfully reassembled per second (ipReasmOKs).
+.TP 12
+.B fragcreat/s
+Number of IP datagram fragments generated per second at this entity
+(ipFragCreates).
+.PP
+The output for the flag
+.B -W
+provides information about the failures that were detected in
+the IPv4-layer (formal SNMP-names between brackets):
+.TP 12
+.B in: dsc/s
+Number of input IP datagrams per second for which no problems were encountered
+to prevent their continued processing but that were discarded, e.g. for lack
+of buffer space (ipInDiscards).
+.TP 12
+.B in: hder/s
+Number of input IP datagrams per second discarded due to errors
+in the IP header (ipInHdrErrors).
+.TP 12
+.B in: ader/s
+Number of input IP datagrams per second discarded because the IP address
+in the destination field was not valid to be received by this entity
+(ipInAddrErrors).
+.TP 12
+.B in: unkp/s
+Number of inbound packets per second that were discarded because of an
+unknown or unsupported protocol (ipInUnknownProtos).
+.TP 12
+.B in: ratim/s
+Number of timeout-situations per second while other fragments were
+expected for successful reassembly (ipReasmTimeout).
+.TP 12
+.B in: rfail/s
+Number of failures detected per second by the IP reassembly algorithm
+(ipReasmFails).
+.TP 12
+.B out: dsc/s
+Number of output IP datagrams per second for which no problems were
+encountered to prevent their continued processing but that were
+discarded, e.g. for lack of buffer space (ipOutDiscards).
+.TP 12
+.B out: nrt/s
+Number of IP datagrams per second discarded because no route could be found
+(ipOutNoRoutes).
+.PP
+The output for the flag
+.B -y
+provides information about the general utilization of the ICMPv4-layer and
+some information per type of ICMP-message
+(formal SNMP-names between brackets):
+.TP 12
+.B intot/s
+Number of ICMP messages (any type) received per second at this entity
+(icmpInMsgs).
+.TP 12
+.B outtot/s
+Number of ICMP messages (any type) transmitted per second from this entity
+(icmpOutMsgs).
+.TP 12
+.B inecho/s
+Number of ICMP Echo (request) messages received per second
+(icmpInEchos).
+.TP 12
+.B inerep/s
+Number of ICMP Echo-Reply messages received per second
+(icmpInEchoReps).
+.TP 12
+.B otecho/s
+Number of ICMP Echo (request) messages transmitted per second
+(icmpOutEchos).
+.TP 12
+.B oterep/s
+Number of ICMP Echo-Reply messages transmitted per second
+(icmpOutEchoReps).
+.PP
+The output for the flag
+.B -Y
+provides information about other types of ICMPv4-messages
+(formal SNMP-names between brackets):
+.TP 12
+.B ierr/s
+Number of ICMP messages received per second but determined to have
+ICMP-specific errors (icmpInErrors).
+.TP 12
+.B isq/s
+Number of ICMP Source Quench messages received per second
+(icmpInSrcQuenchs).
+.TP 12
+.B ird/s
+Number of ICMP Redirect messages received per second
+(icmpInRedirects).
+.TP 12
+.B idu/s
+Number of ICMP Destination Unreachable messages received per second
+(icmpInDestUnreachs).
+.TP 12
+.B ite/s
+Number of ICMP Time Exceeded messages received per second
+(icmpOutTimeExcds).
+.TP 12
+.B oerr/s
+Number of ICMP messages transmitted per second but determined to have
+ICMP-specific errors (icmpOutErrors).
+.TP 12
+.B osq/s
+Number of ICMP Source Quench messages transmitted per second
+(icmpOutSrcQuenchs).
+.TP 12
+.B ord/s
+Number of ICMP Redirect messages transmitted per second
+(icmpOutRedirects).
+.TP 12
+.B odu/s
+Number of ICMP Destination Unreachable messages transmitted per second
+(icmpOutDestUnreachs).
+.TP 12
+.B ote/s
+Number of ICMP Time Exceeded messages transmitted per second
+(icmpOutTimeExcds).
+.PP
+The output for the flag
+.B -u
+provides information about the utilization of the UDPv4-layer
+(formal SNMP-names between brackets):
+.TP 12
+.B indgram/s
+Number of UDP datagrams per second delivered to UDP users (udpInDatagrams).
+.TP 12
+.B outdgram/s
+Number of UDP datagrams transmitted per second from this entity
+(udpOutDatagrams).
+.TP 12
+.B inerr/s
+Number of received UDP datagrams per second that could not be delivered
+for reasons other than the lack of an application at the destination port
+(udpInErrors).
+.TP 12
+.B noport/s
+Number of received UDP datagrams per second for which there was
+no application at the destination port (udpNoPorts).
+.PP
+The output for the flag
+.B -z
+provides information about the utilization of the IPv6-layer
+(formal SNMP-names between brackets):
+.TP 12
+.B inrecv/s
+Number of input IPv6-datagrams received from interfaces per second, including
+those received in error (ipv6IfStatsInReceives).
+.TP 12
+.B outreq/s
+Number of IPv6-datagrams per second that local higher-layer protocols
+supplied to IP in requests for transmission (ipv6IfStatsOutRequests).
+This counter does not include any forwarded datagrams.
+.TP 12
+.B inmc/s
+Number of multicast packets per second that have been received by the
+interface (ipv6IfStatsInMcastPkts).
+.TP 12
+.B outmc/s
+Number of multicast packets per second that have been transmitted to the
+interface (ipv6IfStatsOutMcastPkts).
+.TP 12
+.B indeliv/s
+Number of IP datagrams successfully delivered per second to
+IPv6 user-protocols, including ICMP (ipv6IfStatsInDelivers).
+.TP 12
+.B reasmok/s
+Number of IPv6 datagrams successfully reassembled per second
+(ipv6IfStatsReasmOKs).
+.TP 12
+.B fragcre/s
+Number of IPv6 datagram fragments generated per second at this entity
+(ipv6IfStatsOutFragCreates).
+.PP
+The output for the flag
+.B -Z
+provides information about the failures that were detected in the IPv6-layer
+(formal SNMP-names between brackets):
+.TP 12
+.B in: dsc/s
+Number of input IPv6 datagrams per second for which no problems
+were encountered to prevent their continued processing but that
+were discarded, e.g. for lack of buffer space (ipv6IfStatsInDiscards).
+.TP 12
+.B in: hder/s
+Number of input datagrams per second discarded due to errors in the
+IPv6 header (ipv6IfStatsInHdrErrors).
+.TP 12
+.B in: ader/s
+Number of input datagrams per second discarded because the IPv6 address
+in the destination field was not valid to be received by this entity
+(ipv6IfStatsInAddrErrors).
+.TP 12
+.B in: unkp/s
+Number of locally-addressed datagrams per second that were discarded because
+of an unknown or unsupported protocol (ipv6IfStatsInUnknownProtos).
+.TP 12
+.B in: ratim/s
+Number of timeout-situations per second while other IPv6 fragments were
+expected for successful reassembly (ipv6ReasmTimeout).
+.TP 12
+.B in: rfail/s
+Number of failures detected per second by the IPv6 reassembly-algorithm
+(ipv6IfStatsReasmFails).
+.TP 12
+.B out: dsc/s
+Number of output IPv6 datagrams per second for which no problems
+were encountered to prevent their continued processing but that
+were discarded, e.g. for lack of buffer space (ipv6IfStatsOutDiscards).
+.TP 12
+.B out: nrt/s
+Number of IPv6 datagrams per second discarded because no route could be found
+(ipv6IfStatsInNoRoutes).
+.PP
+The output for the flag
+.B -k
+provides information about the general utilization of the ICMPv6-layer and
+some information per type of ICMP-message
+(formal SNMP-names between brackets):
+.TP 12
+.B intot/s
+Number of ICMPv6 messages (any type) received per second at the interface
+(ipv6IfIcmpInMsgs).
+.TP 12
+.B outtot/s
+Number of ICMPv6 messages (any type) transmitted per second from this entity
+(ipv6IfIcmpOutMsgs).
+.TP 12
+.B inerr/s
+Number of ICMPv6 messages received per second that had ICMP-specific
+errors, such as bad ICMP checksums, bad length, etc (ipv6IfIcmpInErrors).
+.TP 12
+.B innsol/s
+Number of ICMP Neighbor Solicit messages received per second
+(ipv6IfIcmpInNeighborSolicits).
+.TP 12
+.B innadv/s
+Number of ICMP Neighbor Advertisement messages received per second
+(ipv6IfIcmpInNeighborAdvertisements).
+.TP 12
+.B otnsol/s
+Number of ICMP Neighbor Solicit messages transmitted per second
+(ipv6IfIcmpOutNeighborSolicits).
+.TP 12
+.B otnadv/s
+Number of ICMP Neighbor Advertisement messages transmitted per second
+(ipv6IfIcmpOutNeighborAdvertisements).
+.PP
+The output for the flag
+.B -K
+provides information about other types of ICMPv6-messages
+(formal SNMP-names between brackets):
+.TP 12
+.B iecho/s
+Number of ICMP Echo (request) messages received per second
+(ipv6IfIcmpInEchos).
+.TP 12
+.B ierep/s
+Number of ICMP Echo-Reply messages received per second
+(ipv6IfIcmpInEchoReplies).
+.TP 12
+.B oerep/s
+Number of ICMP Echo-Reply messages transmitted per second
+(ipv6IfIcmpOutEchoReplies).
+.TP 12
+.B idu/s
+Number of ICMP Destination Unreachable messages received per second
+(ipv6IfIcmpInDestUnreachs).
+.TP 12
+.B odu/s
+Number of ICMP Destination Unreachable messages transmitted per second
+(ipv6IfIcmpOutDestUnreachs).
+.TP 12
+.B ird/s
+Number of ICMP Redirect messages received per second
+(ipv6IfIcmpInRedirects).
+.TP 12
+.B ord/s
+Number of ICMP Redirect messages transmitted per second
+(ipv6IfIcmpOutRedirect).
+.TP 12
+.B ite/s
+Number of ICMP Time Exceeded messages received per second
+(ipv6IfIcmpInTimeExcds).
+.TP 12
+.B ote/s
+Number of ICMP Time Exceeded messages transmitted per second
+(ipv6IfIcmpOutTimeExcds).
+.PP
+The output for the flag
+.B -U
+provides information about the utilization of the UDPv6-layer
+(formal SNMP-names between brackets):
+.TP 12
+.B indgram/s
+Number of UDPv6 datagrams per second delivered to UDP users (udpInDatagrams),
+.TP 12
+.B outdgram/s
+Number of UDPv6 datagrams transmitted per second from this entity
+(udpOutDatagrams),
+.TP 12
+.B inerr/s
+Number of received UDPv6 datagrams per second that could not be delivered
+for reasons other than the lack of an application at the destination port
+(udpInErrors).
+.TP 12
+.B noport/s
+Number of received UDPv6 datagrams per second for which there was
+no application at the destination port (udpNoPorts).
+.PP
+The output for the flag
+.B -t
+provides information about the utilization of the TCP-layer
+(formal SNMP-names between brackets):
+.TP 12
+.B insegs/s
+Number of received segments per second, including those received in error
+(tcpInSegs).
+.TP 12
+.B outsegs/s
+Number of transmitted segments per second, excluding those containing only
+retransmitted octets (tcpOutSegs).
+.TP 12
+.B actopen/s
+Number of active opens per second that have been supported by this entity
+(tcpActiveOpens).
+.TP 12
+.B pasopen/s
+Number of passive opens per second that have been supported by this entity
+(tcpPassiveOpens).
+.TP 12
+.B nowopen
+Number of connections currently open (snapshot), for which the state
+is either ESTABLISHED or CLOSE-WAIT (tcpCurrEstab).
+.PP
+The output for the flag
+.B -T
+provides information about the failures that were detected in the TCP-layer
+(formal SNMP-names between brackets):
+.TP 12
+.B inerr/s
+Number of received segments per second received in error (tcpInErrs).
+.TP 12
+.B retrans/s
+Number of retransmitted segments per second (tcpRetransSegs).
+.TP 12
+.B attfail/s
+Number of failed connection attempts per second that have occurred at this
+entity (tcpAttemptFails).
+.TP 12
+.B estabreset/s
+Number of resets per second that have occurred at this entity
+(tcpEstabResets).
+.TP 12
+.B outreset/s
+Number of transmitted segments per second containing the RST flag
+(tcpOutRsts).
+.PP
+The output for the flag
+.B -O
+provides information about the top-3 of processes with the highest
+processor consumption:
+.TP 12
+.B pid
+Process-id (if zero, the process has exited while the
+pid could not be determined).
+.TP 12
+.B command
+The name of the process.
+.TP 12
+.B cpu%
+The percentage of cpu-capacity being consumed.
+This value can exceed 100% for a multithreaded process running on
+a multiprocessor machine.
+.PP
+The output for the flag
+.B -G
+provides information about the top-3 of processes with the highest
+memory consumption:
+.TP 12
+.B pid
+Process-id (if zero, the process has exited while the
+pid could not be determined).
+.TP 12
+.B command
+The name of the process.
+.TP 12
+.B mem%
+The percentage of resident memory-utilization by this process.
+.PP
+The output for the flag
+.B -D
+provides information about the top-3 of processes that issue
+the most read and write accesses to disk:
+.TP 12
+.B pid
+Process-id (if zero, the process has exited while the
+pid could not be determined).
+.TP 12
+.B command
+The name of the process.
+.TP 12
+.B dsk%
+The percentage of read and write accesses related to the total
+number of read and write accesses issued on disk by all processes,
+so a high percentage does not imply a high disk load on system level.
+.PP
+The output for the flag
+.B -N
+provides information about the top-3 of processes that issue
+the most socket transfers for IPv4/IPv6:
+.TP 12
+.B pid
+Process-id (if zero, the process has exited while the
+pid could not be determined).
+.TP 12
+.B command
+The name of the process.
+.TP 12
+.B net%
+The percentage of socket transfers related to the total
+number of transfers issued by all processes,
+so a high percentage does not imply a high network load on system level.
+.SH EXAMPLES
+To see today's cpu-activity so far 
+(supposed that
+.B atop
+is logging in the background):
+.PP
+.TP 12
+.B \  atopsar
+.PP
+To see the memory occupation for June 5, 2012 between 10:00 and 12:30
+(supposed that
+.B atop
+has been logging daily in the background):
+.PP
+.TP 12
+.B \  atopsar -m -r /var/log/atop_20120605 -b 10:00 -e 12:30
+.br
+\ 
+.br
+    or
+.TP 12
+.B \  atopsar -m -r 20120605 -b 10:00 -e 12:30
+.br
+\ 
+.br
+    or, suppose it is June 8, 2012 at this moment
+.TP 12
+.B \  atopsar -m -r yyy -b 10:00 -e 12:30
+.PP
+Write a logfile with
+.B atop
+to record the system behaviour for 30 minutes
+(30 samples of one minute) and produce all available reports
+afterwards:
+.PP
+.TP 12
+.B \  atop       -w /tmp/atoplog 60 30
+.TP 12
+.B \  atopsar -A -r /tmp/atoplog
+.PP
+To watch TCP activity evolve for ten minutes (10 samples with sixty seconds
+interval):
+.PP
+.TP 12
+.B \  atopsar -t 60 10
+.PP
+To watch the header-lines ('_' as last character) of all reports with only 
+the detail-lines showing critical resource consumption (marker '*' or '+'
+as last character):
+.PP
+.TP 12
+.B \  atopsar -AM | grep '[_*+]$'
+.PP
+.SH FILES
+.PP
+.TP 5
+.B /etc/atoprc
+Configuration file containing system-wide default values (mainly flags).
+See related man-page.
+.PP
+.TP 5
+.B ~/.atoprc
+Configuration file containing personal default values (mainly flags).
+See related man-page.
+.PP
+.TP 5
+.BI /var/log/atop/atop_ YYYYMMDD
+Daily data file, where
+.I YYYYMMDD
+are digits representing the date.
+.SH SEE ALSO
+.B atop(1),
+.B atoprc(5),
+.B atopacctd(8),
+.B netatop(4),
+.B netatopd(8)
+.br
+.B http://www.atoptool.nl
+.SH AUTHOR
+Gerlof Langeveld (gerlof.langeveld@atoptool.nl)
diff --git a/sources/netatop.h b/sources/netatop.h
new file mode 100644 (file)
index 0000000..a14e6fa
--- /dev/null
@@ -0,0 +1,47 @@
+#define        COMLEN  16
+
+struct taskcount {
+       unsigned long long      tcpsndpacks;
+       unsigned long long      tcpsndbytes;
+       unsigned long long      tcprcvpacks;
+       unsigned long long      tcprcvbytes;
+
+       unsigned long long      udpsndpacks;
+       unsigned long long      udpsndbytes;
+       unsigned long long      udprcvpacks;
+       unsigned long long      udprcvbytes;
+
+       /* space for future extensions */
+};
+
+struct netpertask {
+       pid_t                   id;     // tgid or tid (depending on command)
+       unsigned long           btime;
+       char                    command[COMLEN];
+
+       struct taskcount        tc;
+};
+
+
+/*
+** getsocktop commands
+*/
+#define NETATOP_BASE_CTL       15661
+
+// just probe if the netatop module is active
+#define NETATOP_PROBE          (NETATOP_BASE_CTL)
+
+// force garbage collection to make finished processes available
+#define NETATOP_FORCE_GC       (NETATOP_BASE_CTL+1)
+
+// wait until all finished processes are read (blocks until done)
+#define NETATOP_EMPTY_EXIT     (NETATOP_BASE_CTL+2)
+
+// get info for finished process (blocks until available)
+#define NETATOP_GETCNT_EXIT    (NETATOP_BASE_CTL+3)
+
+// get counters for thread group (i.e. process):  input is 'id' (pid)
+#define NETATOP_GETCNT_TGID    (NETATOP_BASE_CTL+4)
+
+// get counters for thread:  input is 'id' (tid)
+#define NETATOP_GETCNT_PID     (NETATOP_BASE_CTL+5)
diff --git a/sources/netatopd.h b/sources/netatopd.h
new file mode 100644 (file)
index 0000000..c1ba8e0
--- /dev/null
@@ -0,0 +1,12 @@
+#define SEMAKEY         1541961
+
+#define NETEXITFILE     "/var/run/netatop.log"
+#define MYMAGIC         (unsigned int) 0xfeedb0b0
+
+struct naheader {
+        u_int32_t      magic;  // magic number MYMAGIC
+        u_int32_t      curseq; // sequence number of last netpertask
+        u_int16_t      hdrlen; // length of this header
+        u_int16_t      ntplen; // length of netpertask structure
+        pid_t          mypid;  // PID of netatopd itself
+};
diff --git a/sources/netatopif.c b/sources/netatopif.c
new file mode 100644 (file)
index 0000000..30c3cb3
--- /dev/null
@@ -0,0 +1,531 @@
+/*
+** ATOP - System & Process Monitor
+**
+** The program 'atop' offers the possibility to view the activity of
+** the system on system-level as well as process-level.
+**
+** This source-file contains functions to interface with the netatop
+** module in the kernel. That module keeps track of network activity
+** per process and thread.
+** ================================================================
+** Author:      Gerlof Langeveld
+** E-mail:      gerlof.langeveld@atoptool.nl
+** Date:        August/September 2012
+**
+** This program is free software; you can redistribute it and/or modify it
+** under the terms of the GNU General Public License as published by the
+** Free Software Foundation; either version 2, or (at your option) any
+** later version.
+**
+** This program is distributed in the hope that it will be useful, but
+** WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+** See the GNU General Public License for more details.
+*/
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <zlib.h>
+#include <sys/mman.h>
+
+#include "atop.h"
+#include "photoproc.h"
+
+#include "netatop.h"
+#include "netatopd.h"
+
+static int             netsock   = -1;
+static int             netexitfd = -1;
+static struct naheader *nahp;
+static int             semid     = -1;
+static unsigned long   lastseq;
+
+/*
+** storage of last exited tasks read from exitfile
+** every exitstore struct is registered in hash buckets,
+** by its pid or by its begintime
+*/
+struct exitstore {
+       struct exitstore  *next;
+       unsigned char      isused;
+       struct netpertask  npt;
+};
+
+#define NHASH          1024    // must be power of two!
+#define HASHCALC(x)    ((x)&(NHASH-1))
+
+static struct exitstore        *esbucket[NHASH];
+
+static struct exitstore *exitall;
+static int              exitnum;
+static char             exithash;
+
+static void    fill_networkcnt(struct tstat *, struct tstat *,
+                               struct exitstore *);
+
+/*
+** open a raw socket to the IP layer (root privs required)
+*/
+void
+netatop_ipopen(void)
+{
+       netsock = socket(PF_INET, SOCK_RAW, IPPROTO_RAW);
+}
+
+/*
+** check if at this moment the netatop kernel module is loaded and
+** the netatopd daemon is active
+*/
+void
+netatop_probe(void)
+{
+       struct sembuf           semdecr = {1, -1, SEM_UNDO};
+       socklen_t               sl = 0;
+       struct stat             exstat;
+
+       /*
+       ** check if IP socket is open
+       */
+       if (netsock == -1)
+               return;
+
+       /*
+       ** probe if the netatop module is active
+       */
+       if ( getsockopt(netsock, SOL_IP, NETATOP_PROBE, NULL, &sl) != 0)
+       {
+               supportflags &= ~NETATOP;
+               supportflags &= ~NETATOPD;
+               return;
+       }
+
+       // set appropriate support flag
+       supportflags |= NETATOP;
+
+       /*
+       ** check if the netatopd daemon is active to register exited tasks
+       ** and decrement semaphore to indicate that we want to subscribe
+       */
+       if (semid == -1)
+       {
+               if ( (semid = semget(SEMAKEY, 0, 0))    == -1 ||
+                             semop(semid, &semdecr, 1) == -1   )
+               {
+                       supportflags &= ~NETATOPD;
+                       return;
+               }
+       }
+
+       if (semctl(semid, 0, GETVAL, 0) != 1)
+       {
+               supportflags &= ~NETATOPD;
+               return;
+       }
+
+       /*
+       ** check if exitfile still open and not removed by netatopd
+       */
+       if (netexitfd != -1)
+       {
+               if ( fstat(netexitfd, &exstat) == 0 && 
+                    exstat.st_nlink > 0              ) // not removed
+               {
+                       supportflags |= NETATOPD;
+                       return;
+               }
+               else
+               {
+                       (void) close(netexitfd);
+
+                       if (nahp)
+                               munmap(nahp, sizeof *nahp);
+
+                       netexitfd = -1;
+                       nahp = NULL;
+               }
+       }
+
+       /*
+       ** open file with compressed stats of exited tasks
+       ** and (re)mmap the start record, mainly to obtain current sequence
+       */
+       if (netexitfd == -1)
+       {
+               if ( (netexitfd = open(NETEXITFILE, O_RDONLY, 0)) == -1)
+               {
+                       supportflags &= ~NETATOPD;
+                               return;
+               }
+       }
+
+       if ( (nahp = mmap((void *)0, sizeof *nahp, PROT_READ, MAP_SHARED,
+                                               netexitfd, 0)) == (void *) -1)
+       {
+               (void) close(netexitfd);
+               netexitfd = -1;
+               nahp = NULL;
+               supportflags &= ~NETATOPD;
+               return;
+       }
+
+       /*
+       ** if this is a new incarnation of the netatopd daemon,
+       ** position seek pointer on first task that is relevant to us
+       ** and remember last sequence number to know where to start
+       */
+       (void) lseek(netexitfd, 0, SEEK_END);
+
+       lastseq     = nahp->curseq;
+
+       // set appropriate support flag
+       supportflags |= NETATOPD;
+}
+
+void
+netatop_signoff(void)
+{
+       struct sembuf   semincr = {1, +1, SEM_UNDO};
+
+       if (netsock == -1 || nahp == NULL)
+               return;
+
+       if (supportflags & NETATOPD)
+       {
+               regainrootprivs();
+               
+               (void) semop(semid, &semincr, 1);
+
+               kill(nahp->mypid, SIGHUP);
+
+                       if (! droprootprivs())
+                               cleanstop(42);
+
+               (void) munmap(nahp, sizeof *nahp);
+               (void) close(netexitfd);
+       }
+}
+
+/*
+** read network counters for one existing task
+** (type 'g' for thread group or type 't' for thread)
+*/
+void
+netatop_gettask(pid_t id, char type, struct tstat *tp)
+{
+       struct netpertask       npt;
+       socklen_t               socklen = sizeof npt;
+       int                     cmd = (type == 'g' ?
+                                   NETATOP_GETCNT_TGID : NETATOP_GETCNT_PID);
+
+       /*
+       ** if kernel module netatop not active on this system, skip call
+       */
+       if (!(supportflags & NETATOP) ) {
+               memset(&tp->net, 0, sizeof tp->net);
+               return;
+       }
+
+       /*
+       ** get statistics of this process/thread
+       */
+       npt.id  = id;
+
+        regainrootprivs();
+
+       if (getsockopt(netsock, SOL_IP, cmd, &npt, &socklen) != 0) {
+               memset(&tp->net, 0, sizeof tp->net);
+
+               if (! droprootprivs())
+                       cleanstop(42);
+
+               if (errno == ENOPROTOOPT || errno == EPERM)
+               {
+                       supportflags &= ~NETATOP;
+                       supportflags &= ~NETATOPD;
+                       close(netsock);
+                       netsock = -1;
+               }
+
+               return;
+       }
+
+               if (! droprootprivs())
+                       cleanstop(42);
+
+       /*
+       ** statistics available: fill counters
+       */
+       tp->net.tcpsnd = npt.tc.tcpsndpacks;
+       tp->net.tcprcv = npt.tc.tcprcvpacks;
+       tp->net.tcpssz = npt.tc.tcpsndbytes;
+       tp->net.tcprsz = npt.tc.tcprcvbytes;
+
+       tp->net.udpsnd = npt.tc.udpsndpacks;
+       tp->net.udprcv = npt.tc.udprcvpacks;
+       tp->net.udpssz = npt.tc.udpsndbytes;
+       tp->net.udprsz = npt.tc.udprcvbytes;
+}
+
+/*
+** read all exited processes that have been added to the exitfile
+** and store them into memory
+*/
+unsigned int
+netatop_exitstore(void)
+{
+       socklen_t               socklen = 0, nexitnet, sz, nr=0;
+       unsigned long           uncomplen;
+       unsigned char           nextsize;
+       unsigned char           readbuf[nahp->ntplen+100];
+       unsigned char           databuf[nahp->ntplen];
+       struct netpertask       *tmp = (struct netpertask *)databuf;
+       struct exitstore        *esp;
+
+        regainrootprivs();
+
+       /*
+       ** force garbage collection:
+       **   netatop module builds new list of exited processes that
+       **   can be read by netatopd and written to exitfile
+       */
+       if (getsockopt(netsock, SOL_IP, NETATOP_FORCE_GC, NULL, &socklen)!=0) {
+               if (! droprootprivs())
+                       cleanstop(42);
+
+               if (errno == ENOPROTOOPT || errno == EPERM)
+               {
+                       supportflags &= ~NETATOP;
+                       supportflags &= ~NETATOPD;
+                       close(netsock);
+                       netsock = -1;
+               }
+
+               return 0;
+       }
+
+       /*
+       ** wait until list of exited processes is read by netatopd
+       ** and available to be read by atop
+       */
+       if (getsockopt(netsock, SOL_IP, NETATOP_EMPTY_EXIT, 0, &socklen) !=0) {
+               if (! droprootprivs())
+                       cleanstop(42);
+
+               if (errno == ENOPROTOOPT || errno == EPERM)
+               {
+                       supportflags &= ~NETATOP;
+                       supportflags &= ~NETATOPD;
+                       close(netsock);
+                       netsock = -1;
+               }
+
+               return 0;
+       }
+
+               if (! droprootprivs())
+                       cleanstop(42);
+
+       /*
+       ** verify how many exited processes are available to be read
+       ** from the exitfile
+       */
+       nexitnet = nahp->curseq - lastseq;
+       lastseq  = nahp->curseq;
+
+       /*
+       ** allocate storage for all exited processes
+       */
+       exitall = malloc(nexitnet * sizeof(struct exitstore));
+
+       ptrverify(exitall, "Malloc failed for %d exited netprocs\n", nexitnet);
+
+       memset(exitall, 0, nexitnet * sizeof(struct exitstore));
+
+       esp = exitall;
+
+       /*
+       ** read next byte from exitfile that specifies the length
+       ** of the next record
+       */
+       if ( read(netexitfd, &nextsize, 1) != 1) 
+               return 0;
+
+       /*
+       ** read the next record and (if possible) the byte specifying
+       ** the size of the next record
+       */
+       while ( (sz = read(netexitfd, readbuf, nextsize+1)) >= nextsize)
+       {
+               /*
+               ** decompress record and store it
+               */
+               uncomplen = nahp->ntplen;
+
+               if (nahp->ntplen <= sizeof(struct netpertask))
+               {
+                       (void) uncompress((Byte *)&(esp->npt), &uncomplen,
+                                                       readbuf, nextsize);
+               }
+               else
+               {
+                       (void) uncompress((Byte *)databuf, &uncomplen,
+                                                       readbuf, nextsize);
+                       esp->npt = *tmp;
+               }
+
+               esp++;
+               nr++;
+
+               /*
+               ** check if we have read all records
+               */
+               if (nr == nexitnet)
+               {
+                       /*
+                       ** if we have read one byte too many:
+                       ** reposition seek pointer 
+                       */
+                       if (sz > nextsize)
+                               (void) lseek(netexitfd, -1, SEEK_CUR);
+
+                       break;
+               }
+
+               /*
+               ** prepare reading next record
+               */
+               if (sz > nextsize)
+                       nextsize = readbuf[nextsize];
+               else
+                       break;  // unexpected: more requested than available
+       }
+
+       exitnum = nr;
+
+       return nr;
+}
+
+/*
+** remove all stored exited processes from the hash bucket list
+*/
+void
+netatop_exiterase(void)
+{
+       free(exitall);
+       memset(esbucket, 0, sizeof esbucket);
+       exitnum = 0;
+}
+
+/*
+** add all stored tasks to a hash bucket, either
+** by pid (argument 'p') or by begintime (argument 'b')
+*/
+void
+netatop_exithash(char hashtype)
+{
+       int                     i, h;
+       struct exitstore        *esp;
+
+       for (i=0, esp=exitall; i < exitnum; i++, esp++)
+       {
+               if (hashtype == 'p')
+                       h = HASHCALC(esp->npt.id);
+               else
+                       h = HASHCALC(esp->npt.btime);
+
+               esp->next   = esbucket[h];
+               esbucket[h] = esp;
+       }
+
+       exithash = hashtype;
+}
+
+/*
+** search for relevant exited network task and
+** update counters in tstat struct
+*/
+void
+netatop_exitfind(unsigned long key, struct tstat *dev, struct tstat *pre)
+{
+       int                     h = HASHCALC(key);
+       struct exitstore        *esp;
+
+       /*
+       ** if bucket empty, forget about it
+       */
+       if ( (esp = esbucket[h]) == NULL)
+               return;
+
+       /*
+       ** search thru hash bucket list
+       */
+       for (; esp; esp=esp->next)
+       {
+               switch (exithash)
+               {
+                  case 'p':            // search by PID
+                       if (key != esp->npt.id)
+                               continue;
+
+                       /*
+                       ** correct PID found
+                       */
+                       fill_networkcnt(dev, pre, esp);
+                       break;
+
+                  case 'b':            // search by begintime
+                       if (esp->isused)
+                               continue;
+
+                       if (key != esp->npt.btime)
+                               continue;
+
+                       /*
+                       ** btime is okay; additional checks required
+                       */
+                       if ( strcmp(esp->npt.command, pre->gen.name) != 0)
+                               continue;
+
+                       if (esp->npt.tc.tcpsndpacks < pre->net.tcpsnd   ||
+                           esp->npt.tc.tcpsndbytes < pre->net.tcpssz   ||
+                           esp->npt.tc.tcprcvpacks < pre->net.tcprcv   ||
+                           esp->npt.tc.tcprcvbytes < pre->net.tcprsz   ||
+                           esp->npt.tc.udpsndpacks < pre->net.udpsnd   ||
+                           esp->npt.tc.udpsndbytes < pre->net.udpssz   ||
+                           esp->npt.tc.udprcvpacks < pre->net.udprcv   ||
+                           esp->npt.tc.udprcvbytes < pre->net.udprsz     )
+                               continue;
+
+                       esp->isused = 1;
+                       fill_networkcnt(dev, pre, esp);
+                       break;
+               }
+       }
+}
+
+static void
+fill_networkcnt(struct tstat *dev, struct tstat *pre, struct exitstore *esp)
+{
+       dev->net.tcpsnd = esp->npt.tc.tcpsndpacks - pre->net.tcpsnd;
+       dev->net.tcpssz = esp->npt.tc.tcpsndbytes - pre->net.tcpssz;
+       dev->net.tcprcv = esp->npt.tc.tcprcvpacks - pre->net.tcprcv;
+       dev->net.tcprsz = esp->npt.tc.tcprcvbytes - pre->net.tcprsz;
+
+       dev->net.udpsnd = esp->npt.tc.udpsndpacks - pre->net.udpsnd;
+       dev->net.udpssz = esp->npt.tc.udpsndbytes - pre->net.udpssz;
+       dev->net.udprcv = esp->npt.tc.udprcvpacks - pre->net.udprcv;
+       dev->net.udprsz = esp->npt.tc.udprcvbytes - pre->net.udprsz;
+}
diff --git a/sources/netlink.c b/sources/netlink.c
new file mode 100644 (file)
index 0000000..6f84978
--- /dev/null
@@ -0,0 +1,253 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <poll.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <signal.h>
+
+#include <linux/genetlink.h>
+#include <linux/taskstats.h>
+
+/*
+** generic macro's
+*/
+#define GENLMSG_DATA(glh)      ((void *)(NLMSG_DATA(glh) + GENL_HDRLEN))
+#define GENLMSG_PAYLOAD(glh)   (NLMSG_PAYLOAD(glh, 0)    - GENL_HDRLEN)
+#define NLA_DATA(na)           ((void *)((char*)(na)     + NLA_HDRLEN))
+#define NLA_PAYLOAD(len)       (len                      - NLA_HDRLEN)
+
+/*
+** function prototypes
+*/
+static int     nlsock_open(void);
+static int     nlsock_sendcmd(int, __u16, __u32, __u8, __u16, void *, int);
+static int     nlsock_getfam(int);
+static int     getnumcpu(void);
+
+/*
+** message format to communicate with NETLINK
+*/
+struct msgtemplate {
+       struct nlmsghdr         n;
+       struct genlmsghdr       g;
+       char                    buf[2048];
+};
+
+int
+netlink_open(void)
+{
+       int     nlsock, famid;
+       char    cpudef[64];
+
+       /*
+       ** open the netlink socket
+       */
+       nlsock = nlsock_open();
+
+       /*
+       ** get the family id for the TASKSTATS family
+       */
+       famid = nlsock_getfam(nlsock);
+
+       /*
+       ** determine maximum number of CPU's for this system
+       ** and specify mask to register all cpu's
+       */
+       sprintf(cpudef, "0-%d", getnumcpu() -1);
+
+       /*
+       ** indicate to listen for processes from all CPU's
+       */
+       if (nlsock_sendcmd(nlsock, famid, getpid(), TASKSTATS_CMD_GET,
+               TASKSTATS_CMD_ATTR_REGISTER_CPUMASK,
+               &cpudef, strlen(cpudef)+1) == -1)
+       {
+               fprintf(stderr, "register cpumask failed\n");
+               return -1;
+       }
+
+       return nlsock;
+}
+
+
+int
+netlink_recv(int nlsock, int flags)
+{
+       int                     len;
+        struct msgtemplate     msg;
+
+        if ( (len = recv(nlsock, &msg, sizeof msg, flags)) == -1)
+               return -errno;          // negative: errno
+
+       if  (msg.n.nlmsg_type == NLMSG_ERROR || !NLMSG_OK(&msg.n, len))
+       {
+               struct nlmsgerr *err = NLMSG_DATA(&msg);
+
+               return err->error;      // negative: errno
+       }
+
+       return len;                     // 0 or positive value
+}
+
+static int
+nlsock_getfam(int nlsock)
+{
+       int                     len;
+        struct nlattr          *nlattr;
+        struct msgtemplate     msg;
+
+        nlsock_sendcmd(nlsock, GENL_ID_CTRL, getpid(),
+                       CTRL_CMD_GETFAMILY,
+                        CTRL_ATTR_FAMILY_NAME,
+                       TASKSTATS_GENL_NAME, sizeof TASKSTATS_GENL_NAME);
+
+        if ( (len = recv(nlsock, &msg, sizeof msg, 0)) == -1)
+       {
+               perror("receive NETLINK family");
+               exit(1);
+       }
+
+       if  (msg.n.nlmsg_type == NLMSG_ERROR || !NLMSG_OK(&msg.n, len))
+       {
+               struct nlmsgerr *err = NLMSG_DATA(&msg);
+
+               fprintf(stderr, "receive NETLINK family, errno %d\n",
+                                err->error);
+
+               exit(1);
+       }
+
+        nlattr = (struct nlattr *) GENLMSG_DATA(&msg);
+       nlattr = (struct nlattr *) ((char *) nlattr +
+                                       NLA_ALIGN(nlattr->nla_len));
+
+       if (nlattr->nla_type != CTRL_ATTR_FAMILY_ID)
+       {
+               fprintf(stderr, "unexpected family id\n");
+               exit(1);
+       }
+
+       return *(__u16 *) NLA_DATA(nlattr);
+}
+
+
+static int
+nlsock_open(void)
+{
+       int                     nlsock, rcvsz = 256*1024;
+       struct sockaddr_nl      nlsockaddr;
+
+       if ( (nlsock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC) ) == -1)
+       {
+               perror("open NETLINK socket");
+               exit(1);
+       }
+
+       if (setsockopt(nlsock, SOL_SOCKET, SO_RCVBUF, &rcvsz, sizeof rcvsz)
+                                                                       == -1)
+       {
+               perror("set length receive buffer");
+               exit(1);
+       }
+
+       memset(&nlsockaddr, 0, sizeof nlsockaddr);
+       nlsockaddr.nl_family = AF_NETLINK;
+
+       if (bind(nlsock, (struct sockaddr *) &nlsockaddr, sizeof nlsockaddr)
+                                                                       == -1)
+       {
+               perror("bind NETLINK socket");
+               close(nlsock);
+               exit(1);
+       }
+
+       return nlsock;
+}
+
+static int
+nlsock_sendcmd(int nlsock, __u16 nlmsg_type,
+       __u32 nlmsg_pid, __u8 genl_cmd,
+       __u16 nla_type, void *nla_data, int nla_len)
+{
+       struct nlattr           *na;
+       struct sockaddr_nl      nlsockaddr;
+       int                     rv, buflen;
+       char                    *buf;
+       struct msgtemplate      msg;
+
+       msg.n.nlmsg_len         = NLMSG_LENGTH(GENL_HDRLEN);
+       msg.n.nlmsg_type        = nlmsg_type;
+       msg.n.nlmsg_flags       = NLM_F_REQUEST;
+       msg.n.nlmsg_seq         = 0;
+       msg.n.nlmsg_pid         = nlmsg_pid;
+       msg.g.cmd               = genl_cmd;
+       msg.g.version           = 0x1;
+       na                      = (struct nlattr *) GENLMSG_DATA(&msg);
+       na->nla_type            = nla_type;
+       na->nla_len             = nla_len + 1 + NLA_HDRLEN;
+
+       memcpy(NLA_DATA(na), nla_data, nla_len);
+       msg.n.nlmsg_len         += NLMSG_ALIGN(na->nla_len);
+
+       buf                     = (char *) &msg;
+       buflen                  = msg.n.nlmsg_len ;
+
+       memset(&nlsockaddr, 0, sizeof(nlsockaddr));
+       nlsockaddr.nl_family = AF_NETLINK;
+
+       while ((rv = sendto(nlsock, buf, buflen, 0,
+               (struct sockaddr *) &nlsockaddr, sizeof(nlsockaddr))) < buflen)
+       {
+               if (rv == -1)
+               {
+                       if (errno != EAGAIN)
+                       {
+                               perror("sendto NETLINK");
+                               return -1;
+                       }
+               }
+               else
+               {
+                       buf     += rv;
+                       buflen  -= rv;
+               }
+       }
+
+       return 0;
+}
+
+static int
+getnumcpu(void)
+{
+       FILE    *fp;
+       char    linebuf[4096], label[256];
+       int     cpunum, maxcpu = 0;
+
+       if ( (fp = fopen("/proc/stat", "r")) != NULL)
+       {
+               while ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
+               {
+                       sscanf(linebuf, "%255s", label);
+
+                       if ( strncmp("cpu", label, 3) == 0 && strlen(label) >3)
+                       {
+                               cpunum = atoi(&label[3]);
+
+                               if (maxcpu < cpunum)
+                                       maxcpu = cpunum;
+                       }
+
+                       if ( strncmp("int", label, 3) == 0)
+                               break;
+               }
+
+               fclose(fp);
+       }
+
+       return maxcpu+1;
+}
diff --git a/sources/netstats.h b/sources/netstats.h
new file mode 100644 (file)
index 0000000..5f5676f
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+** structures defined from the output of /proc/net/snmp and /proc/net/snmp6
+*/
+struct ipv4_stats {
+       count_t Forwarding;
+       count_t DefaultTTL;
+       count_t InReceives;
+       count_t InHdrErrors;
+       count_t InAddrErrors;
+       count_t ForwDatagrams;
+       count_t InUnknownProtos;
+       count_t InDiscards;
+       count_t InDelivers;
+       count_t OutRequests;
+       count_t OutDiscards;
+       count_t OutNoRoutes;
+       count_t ReasmTimeout;
+       count_t ReasmReqds;
+       count_t ReasmOKs;
+       count_t ReasmFails;
+       count_t FragOKs;
+       count_t FragFails;
+       count_t FragCreates;
+};
+
+struct icmpv4_stats {
+       count_t InMsgs;
+       count_t InErrors;
+       count_t InDestUnreachs;
+       count_t InTimeExcds;
+       count_t InParmProbs;
+       count_t InSrcQuenchs;
+       count_t InRedirects;
+       count_t InEchos;
+       count_t InEchoReps;
+       count_t InTimestamps;
+       count_t InTimestampReps;
+       count_t InAddrMasks;
+       count_t InAddrMaskReps;
+       count_t OutMsgs;
+       count_t OutErrors;
+       count_t OutDestUnreachs;
+       count_t OutTimeExcds;
+       count_t OutParmProbs;
+       count_t OutSrcQuenchs;
+       count_t OutRedirects;
+       count_t OutEchos;
+       count_t OutEchoReps;
+       count_t OutTimestamps;
+       count_t OutTimestampReps;
+       count_t OutAddrMasks;
+       count_t OutAddrMaskReps;
+};
+
+struct udpv4_stats {
+       count_t InDatagrams;
+       count_t NoPorts;
+       count_t InErrors;
+       count_t OutDatagrams;
+};
+
+struct tcp_stats {
+       count_t RtoAlgorithm;
+       count_t RtoMin;
+       count_t RtoMax;
+       count_t MaxConn;
+       count_t ActiveOpens;
+       count_t PassiveOpens;
+       count_t AttemptFails;
+       count_t EstabResets;
+       count_t CurrEstab;
+       count_t InSegs;
+       count_t OutSegs;
+       count_t RetransSegs;
+       count_t InErrs;
+       count_t OutRsts;
+};
+
+struct ipv6_stats {
+       count_t Ip6InReceives;
+       count_t Ip6InHdrErrors;
+       count_t Ip6InTooBigErrors;
+       count_t Ip6InNoRoutes;
+       count_t Ip6InAddrErrors;
+       count_t Ip6InUnknownProtos;
+       count_t Ip6InTruncatedPkts;
+       count_t Ip6InDiscards;
+       count_t Ip6InDelivers;
+       count_t Ip6OutForwDatagrams;
+       count_t Ip6OutRequests;
+       count_t Ip6OutDiscards;
+       count_t Ip6OutNoRoutes;
+       count_t Ip6ReasmTimeout;
+       count_t Ip6ReasmReqds;
+       count_t Ip6ReasmOKs;
+       count_t Ip6ReasmFails;
+       count_t Ip6FragOKs;
+       count_t Ip6FragFails;
+       count_t Ip6FragCreates;
+       count_t Ip6InMcastPkts;
+       count_t Ip6OutMcastPkts;
+};
+
+struct icmpv6_stats {
+       count_t Icmp6InMsgs;
+       count_t Icmp6InErrors;
+       count_t Icmp6InDestUnreachs;
+       count_t Icmp6InPktTooBigs;
+       count_t Icmp6InTimeExcds;
+       count_t Icmp6InParmProblems;
+       count_t Icmp6InEchos;
+       count_t Icmp6InEchoReplies;
+       count_t Icmp6InGroupMembQueries;
+       count_t Icmp6InGroupMembResponses;
+       count_t Icmp6InGroupMembReductions;
+       count_t Icmp6InRouterSolicits;
+       count_t Icmp6InRouterAdvertisements;
+       count_t Icmp6InNeighborSolicits;
+       count_t Icmp6InNeighborAdvertisements;
+       count_t Icmp6InRedirects;
+       count_t Icmp6OutMsgs;
+       count_t Icmp6OutDestUnreachs;
+       count_t Icmp6OutPktTooBigs;
+       count_t Icmp6OutTimeExcds;
+       count_t Icmp6OutParmProblems;
+       count_t Icmp6OutEchoReplies;
+       count_t Icmp6OutRouterSolicits;
+       count_t Icmp6OutNeighborSolicits;
+       count_t Icmp6OutNeighborAdvertisements;
+       count_t Icmp6OutRedirects;
+       count_t Icmp6OutGroupMembResponses;
+       count_t Icmp6OutGroupMembReductions;
+};
+
+struct udpv6_stats {
+       count_t Udp6InDatagrams;
+       count_t Udp6NoPorts;
+       count_t Udp6InErrors;
+       count_t Udp6OutDatagrams;
+};
diff --git a/sources/parseable.c b/sources/parseable.c
new file mode 100644 (file)
index 0000000..6ba1e95
--- /dev/null
@@ -0,0 +1,716 @@
+/*
+** ATOP - System & Process Monitor
+**
+** The program 'atop' offers the possibility to view the activity of
+** the system on system-level as well as process-level.
+**
+** ==========================================================================
+** Author:      Gerlof Langeveld
+** E-mail:      gerlof.langeveld@atoptool.nl
+** Date:        February 2007
+** --------------------------------------------------------------------------
+** Copyright (C) 2007-2010 Gerlof Langeveld
+**
+** This program is free software; you can redistribute it and/or modify it
+** under the terms of the GNU General Public License as published by the
+** Free Software Foundation; either version 2, or (at your option) any
+** later version.
+**
+** This program is distributed in the hope that it will be useful, but
+** WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+** See the GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+** --------------------------------------------------------------------------
+**
+** $Id: parseable.c,v 1.13 2010/10/23 14:02:19 gerlof Exp $
+** $Log: parseable.c,v $
+** Revision 1.13  2010/10/23 14:02:19  gerlof
+** Show counters for total number of running and sleep (S and D) threads.
+**
+** Revision 1.12  2010/05/18 19:20:55  gerlof
+** Introduce CPU frequency and scaling (JC van Winkel).
+**
+** Revision 1.11  2010/04/23 12:19:35  gerlof
+** Modified mail-address in header.
+**
+** Revision 1.10  2010/03/04 10:52:21  gerlof
+** Support I/O-statistics on logical volumes and MD devices.
+**
+** Revision 1.9  2010/01/08 14:46:42  gerlof
+** Added label RESET in case of a sample with values since boot.
+**
+** Revision 1.8  2009/12/19 22:32:14  gerlof
+** Add new counters to parseable output.
+**
+** Revision 1.7  2008/03/06 09:08:29  gerlof
+** Bug-solution regarding parseable output of PPID.
+**
+** Revision 1.6  2008/03/06 08:38:03  gerlof
+** Register/show ppid of a process.
+**
+** Revision 1.5  2008/01/18 08:03:40  gerlof
+** Show information about the number of threads in state 'running',
+** 'interruptible sleeping' and 'non-interruptible sleeping'.
+**
+** Revision 1.4  2007/12/11 13:33:12  gerlof
+** Cosmetic change.
+**
+** Revision 1.3  2007/08/16 12:00:11  gerlof
+** Add support for atopsar reporting.
+** Concerns networking-counters that have been changed.
+**
+** Revision 1.2  2007/03/20 13:01:12  gerlof
+** Introduction of variable supportflags.
+**
+** Revision 1.1  2007/02/19 11:55:43  gerlof
+** Initial revision
+**
+*/
+#include <sys/types.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/utsname.h>
+
+#include "atop.h"
+#include "photosyst.h"
+#include "photoproc.h"
+#include "parseable.h"
+
+void   print_CPU();
+void   print_cpu();
+void   print_CPL();
+void   print_MEM();
+void   print_SWP();
+void   print_PAG();
+void   print_LVM();
+void   print_MDD();
+void   print_DSK();
+void   print_NFM();
+void   print_NFC();
+void   print_NFS();
+void   print_NET();
+
+void   print_PRG();
+void   print_PRC();
+void   print_PRM();
+void   print_PRD();
+void   print_PRN();
+
+/*
+** table with possible labels and the corresponding
+** print-function for parseable output.
+*/
+struct labeldef {
+       char    *label;
+       int     valid;
+       void    (*prifunc)(char *, struct sstat *, struct tstat *, int);
+};
+
+static struct labeldef labeldef[] = {
+       { "CPU",        0,      print_CPU },
+       { "cpu",        0,      print_cpu },
+       { "CPL",        0,      print_CPL },
+       { "MEM",        0,      print_MEM },
+       { "SWP",        0,      print_SWP },
+       { "PAG",        0,      print_PAG },
+       { "LVM",        0,      print_LVM },
+       { "MDD",        0,      print_MDD },
+       { "DSK",        0,      print_DSK },
+       { "NFM",        0,      print_NFM },
+       { "NFC",        0,      print_NFC },
+       { "NFS",        0,      print_NFS },
+       { "NET",        0,      print_NET },
+
+       { "PRG",        0,      print_PRG },
+       { "PRC",        0,      print_PRC },
+       { "PRM",        0,      print_PRM },
+       { "PRD",        0,      print_PRD },
+       { "PRN",        0,      print_PRN },
+};
+
+static int     numlabels = sizeof labeldef/sizeof(struct labeldef);
+
+/*
+** analyse the parse-definition string that has been
+** passed as argument with the flag -P
+*/
+int
+parsedef(char *pd)
+{
+       register int    i;
+       char            *p, *ep = pd + strlen(pd);
+
+       /*
+       ** check if string passed bahind -P is not another flag
+       */
+       if (*pd == '-')
+       {
+               fprintf(stderr, "flag -P should be followed by label list\n");
+               return 0;
+       }
+
+       /*
+       ** check list of comma-separated labels 
+       */
+       while (pd < ep)
+       {
+               /*
+               ** exchange comma by null-byte
+               */
+               if ( (p = strchr(pd, ',')) )
+                       *p = 0; 
+               else
+                       p  = ep-1;
+
+               /*
+               ** check if the next label exists
+               */
+               for (i=0; i < numlabels; i++)
+               {
+                       if ( strcmp(labeldef[i].label, pd) == 0)
+                       {
+                               labeldef[i].valid = 1;
+                               break;
+                       }
+               }
+
+               /*
+               ** non-existing label has been used
+               */
+               if (i == numlabels)
+               {
+                       /*
+                       ** check if special label 'ALL' has been used
+                       */
+                       if ( strcmp("ALL", pd) == 0)
+                       {
+                               for (i=0; i < numlabels; i++)
+                                       labeldef[i].valid = 1;
+                               break;
+                       }
+                       else
+                       {
+                               fprintf(stderr, "label %s not found\n", pd);
+                               return 0;
+                       }
+               }
+
+               pd = p+1;
+       }
+
+       setbuf(stdout, (char *)0);
+
+       return 1;
+}
+
+/*
+** produce parseable output for an interval
+*/
+char
+parseout(time_t curtime, int numsecs,
+         struct devtstat *devtstat, struct sstat *sstat,
+         int nexit, unsigned int noverflow, char flag)
+{
+       register int    i;
+       char            datestr[32], timestr[32], header[256];
+
+       /*
+       ** print reset-label for sample-values since boot
+       */
+       if (flag&RRBOOT)
+               printf("RESET\n");
+
+       /*
+       ** search all labels which are selected before
+       */
+       for (i=0; i < numlabels; i++)
+       {
+               if (labeldef[i].valid)
+               {
+                       /*
+                       ** prepare generic columns
+                       */
+                       convdate(curtime, datestr);
+                       convtime(curtime, timestr);
+
+                       snprintf(header, sizeof header, "%s %s %ld %s %s %d",
+                               labeldef[i].label,
+                               utsname.nodename,
+                               curtime,
+                               datestr, timestr, numsecs);
+
+                       /*
+                       ** call a selected print-function
+                       */
+                       (labeldef[i].prifunc)(header, sstat,
+                               devtstat->taskall, devtstat->ntaskall);
+               }
+       }
+
+       /*
+       ** print separator
+       */
+       printf("SEP\n");
+
+       return '\0';
+}
+
+/*
+** print functions for system-level statistics
+*/
+void
+calc_freqscale(count_t maxfreq, count_t cnt, count_t ticks, 
+               count_t *freq, int *freqperc)
+{
+        // if ticks != 0, do full calcs
+        if (maxfreq && ticks) 
+        {
+            *freq=cnt/ticks;
+            *freqperc=100* *freq / maxfreq;
+        } 
+        else if (maxfreq)   // max frequency is known so % can be calculated
+        {
+            *freq=cnt;
+            *freqperc=100*cnt/maxfreq;
+        }
+        else if (cnt)       // no max known, set % to 100
+        {
+            *freq=cnt;
+            *freqperc=100;
+        }
+        else                // nothing is known: set freq to 0, % to 100
+        {
+            *freq=0;
+            *freqperc=100;
+        }
+}
+
+
+void
+print_CPU(char *hp, struct sstat *ss, struct tstat *ps, int nact)
+{
+        count_t maxfreq=0;
+        count_t cnt=0;
+        count_t ticks=0;
+        count_t freq;
+        int freqperc;
+        int i;
+
+        // calculate average clock frequency
+       for (i=0; i < ss->cpu.nrcpu; i++)
+        {
+                cnt    += ss->cpu.cpu[i].freqcnt.cnt;
+                ticks  += ss->cpu.cpu[i].freqcnt.ticks;
+        }
+        maxfreq = ss->cpu.cpu[0].freqcnt.maxfreq;
+        calc_freqscale(maxfreq, cnt, ticks, &freq, &freqperc);
+
+       printf("%s %u %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %d\n",
+                       hp,
+                       hertz,
+                       ss->cpu.nrcpu,
+                       ss->cpu.all.stime,
+                       ss->cpu.all.utime,
+                       ss->cpu.all.ntime,
+                       ss->cpu.all.itime,
+                       ss->cpu.all.wtime,
+                       ss->cpu.all.Itime,
+                       ss->cpu.all.Stime,
+                       ss->cpu.all.steal,
+                       ss->cpu.all.guest,
+                        freq,
+                        freqperc
+                        );
+}
+
+void
+print_cpu(char *hp, struct sstat *ss, struct tstat *ps, int nact)
+{
+       register int i;
+        count_t maxfreq=0;
+        count_t cnt=0;
+        count_t ticks=0;
+        count_t freq;
+        int freqperc;
+
+       for (i=0; i < ss->cpu.nrcpu; i++)
+       {
+                cnt    = ss->cpu.cpu[i].freqcnt.cnt;
+                ticks  = ss->cpu.cpu[i].freqcnt.ticks;
+                maxfreq= ss->cpu.cpu[0].freqcnt.maxfreq;
+
+                calc_freqscale(maxfreq, cnt, ticks, &freq, &freqperc);
+
+               printf("%s %u %d %lld %lld %lld "
+                      "%lld %lld %lld %lld %lld %lld %lld %d\n",
+                       hp, hertz, i,
+                       ss->cpu.cpu[i].stime,
+                       ss->cpu.cpu[i].utime,
+                       ss->cpu.cpu[i].ntime,
+                       ss->cpu.cpu[i].itime,
+                       ss->cpu.cpu[i].wtime,
+                       ss->cpu.cpu[i].Itime,
+                       ss->cpu.cpu[i].Stime,
+                       ss->cpu.cpu[i].steal,
+                       ss->cpu.cpu[i].guest,
+                        freq,
+                        freqperc);
+       }
+}
+
+void
+print_CPL(char *hp, struct sstat *ss, struct tstat *ps, int nact)
+{
+       printf("%s %lld %.2f %.2f %.2f %lld %lld\n",
+                       hp,
+                       ss->cpu.nrcpu,
+                       ss->cpu.lavg1,
+                       ss->cpu.lavg5,
+                       ss->cpu.lavg15,
+                       ss->cpu.csw,
+                       ss->cpu.devint);
+}
+
+void
+print_MEM(char *hp, struct sstat *ss, struct tstat *ps, int nact)
+{
+       printf( "%s %u %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld\n",
+                       hp,
+                       pagesize,
+                       ss->mem.physmem,
+                       ss->mem.freemem,
+                       ss->mem.cachemem,
+                       ss->mem.buffermem,
+                       ss->mem.slabmem,
+                       ss->mem.cachedrt,
+                       ss->mem.slabreclaim,
+                       ss->mem.vmwballoon,
+                       ss->mem.shmem,
+                       ss->mem.shmrss,
+                       ss->mem.shmswp,
+                       ss->mem.hugepagesz,
+                       ss->mem.tothugepage,
+                       ss->mem.freehugepage);
+}
+
+void
+print_SWP(char *hp, struct sstat *ss, struct tstat *ps, int nact)
+{
+       printf( "%s %u %lld %lld %lld %lld %lld\n",
+                       hp,
+                       pagesize,
+                       ss->mem.totswap,
+                       ss->mem.freeswap,
+                       (long long)0,
+                       ss->mem.committed,
+                       ss->mem.commitlim);
+}
+
+void
+print_PAG(char *hp, struct sstat *ss, struct tstat *ps, int nact)
+{
+       printf( "%s %u %lld %lld %lld %lld %lld\n",
+                       hp,
+                       pagesize,
+                       ss->mem.pgscans,
+                       ss->mem.allocstall,
+                       (long long)0,
+                       ss->mem.swins,
+                       ss->mem.swouts);
+}
+
+void
+print_LVM(char *hp, struct sstat *ss, struct tstat *ps, int nact)
+{
+       register int    i;
+
+        for (i=0; ss->dsk.lvm[i].name[0]; i++)
+       {
+               printf( "%s %s %lld %lld %lld %lld %lld\n",
+                       hp,
+                       ss->dsk.lvm[i].name,
+                       ss->dsk.lvm[i].io_ms,
+                       ss->dsk.lvm[i].nread,
+                       ss->dsk.lvm[i].nrsect,
+                       ss->dsk.lvm[i].nwrite,
+                       ss->dsk.lvm[i].nwsect);
+       }
+}
+
+void
+print_MDD(char *hp, struct sstat *ss, struct tstat *ps, int nact)
+{
+       register int    i;
+
+        for (i=0; ss->dsk.mdd[i].name[0]; i++)
+       {
+               printf( "%s %s %lld %lld %lld %lld %lld\n",
+                       hp,
+                       ss->dsk.mdd[i].name,
+                       ss->dsk.mdd[i].io_ms,
+                       ss->dsk.mdd[i].nread,
+                       ss->dsk.mdd[i].nrsect,
+                       ss->dsk.mdd[i].nwrite,
+                       ss->dsk.mdd[i].nwsect);
+       }
+}
+
+void
+print_DSK(char *hp, struct sstat *ss, struct tstat *ps, int nact)
+{
+       register int    i;
+
+        for (i=0; ss->dsk.dsk[i].name[0]; i++)
+       {
+               printf( "%s %s %lld %lld %lld %lld %lld\n",
+                       hp,
+                       ss->dsk.dsk[i].name,
+                       ss->dsk.dsk[i].io_ms,
+                       ss->dsk.dsk[i].nread,
+                       ss->dsk.dsk[i].nrsect,
+                       ss->dsk.dsk[i].nwrite,
+                       ss->dsk.dsk[i].nwsect);
+       }
+}
+
+void
+print_NFM(char *hp, struct sstat *ss, struct tstat *ps, int nact)
+{
+       register int    i;
+
+        for (i=0; i < ss->nfs.nfsmounts.nrmounts; i++)
+       {
+               printf("%s %s %lld %lld %lld %lld %lld %lld %lld %lld\n",
+                       hp,
+                       ss->nfs.nfsmounts.nfsmnt[i].mountdev,
+                       ss->nfs.nfsmounts.nfsmnt[i].bytestotread,
+                       ss->nfs.nfsmounts.nfsmnt[i].bytestotread,
+                       ss->nfs.nfsmounts.nfsmnt[i].bytesread,
+                       ss->nfs.nfsmounts.nfsmnt[i].byteswrite,
+                       ss->nfs.nfsmounts.nfsmnt[i].bytesdread,
+                       ss->nfs.nfsmounts.nfsmnt[i].bytesdwrite,
+                       ss->nfs.nfsmounts.nfsmnt[i].pagesmread,
+                       ss->nfs.nfsmounts.nfsmnt[i].pagesmwrite);
+       }
+}
+
+void
+print_NFC(char *hp, struct sstat *ss, struct tstat *ps, int nact)
+{
+       printf( "%s %lld %lld %lld %lld %lld\n",
+                       hp,
+                       ss->nfs.client.rpccnt,
+                       ss->nfs.client.rpcread,
+                       ss->nfs.client.rpcwrite,
+                       ss->nfs.client.rpcretrans,
+                       ss->nfs.client.rpcautrefresh);
+}
+
+void
+print_NFS(char *hp, struct sstat *ss, struct tstat *ps, int nact)
+{
+       printf( "%s %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld "
+               "%lld %lld %lld %lld %lld\n",
+                       hp,
+                       ss->nfs.server.rpccnt,
+                       ss->nfs.client.rpcread,
+                       ss->nfs.client.rpcwrite,
+                       ss->nfs.server.nrbytes,
+                       ss->nfs.server.nwbytes,
+                       ss->nfs.server.rpcbadfmt,
+                       ss->nfs.server.rpcbadaut,
+                       ss->nfs.server.rpcbadcln,
+                       ss->nfs.server.netcnt,
+                       ss->nfs.server.nettcpcnt,
+                       ss->nfs.server.netudpcnt,
+                       ss->nfs.server.nettcpcon,
+                       ss->nfs.server.rchits,
+                       ss->nfs.server.rcmiss,
+                       ss->nfs.server.rcnoca);
+}
+
+void
+print_NET(char *hp, struct sstat *ss, struct tstat *ps, int nact)
+{
+       register int    i;
+
+       printf( "%s %s %lld %lld %lld %lld %lld %lld %lld %lld\n",
+                       hp,
+                       "upper",
+                       ss->net.tcp.InSegs,
+                       ss->net.tcp.OutSegs,
+                               ss->net.udpv4.InDatagrams +
+                               ss->net.udpv6.Udp6InDatagrams,
+                               ss->net.udpv4.OutDatagrams +
+                               ss->net.udpv6.Udp6OutDatagrams,
+                               ss->net.ipv4.InReceives  +
+                               ss->net.ipv6.Ip6InReceives,
+                               ss->net.ipv4.OutRequests +
+                               ss->net.ipv6.Ip6OutRequests,
+                               ss->net.ipv4.InDelivers +
+                                       ss->net.ipv6.Ip6InDelivers,
+                               ss->net.ipv4.ForwDatagrams +
+                                       ss->net.ipv6.Ip6OutForwDatagrams);
+
+       for (i=0; ss->intf.intf[i].name[0]; i++)
+       {
+               printf( "%s %s %lld %lld %lld %lld %ld %d\n",
+                       hp,
+                       ss->intf.intf[i].name,
+                       ss->intf.intf[i].rpack,
+                       ss->intf.intf[i].rbyte,
+                       ss->intf.intf[i].spack,
+                       ss->intf.intf[i].sbyte,
+                       ss->intf.intf[i].speed,
+                       ss->intf.intf[i].duplex);
+       }
+}
+
+/*
+** print functions for process-level statistics
+*/
+void
+print_PRG(char *hp, struct sstat *ss, struct tstat *ps, int nact)
+{
+       register int i;
+
+       for (i=0; i < nact; i++, ps++)
+       {
+               printf("%s %d (%s) %c %d %d %d %d %d %ld (%s) %d %d %d %d "
+                      "%d %d %d %d %d %d %ld %c %d %d %s\n",
+                       hp,
+                       ps->gen.pid,
+                       ps->gen.name,
+                       ps->gen.state,
+                       ps->gen.ruid,
+                       ps->gen.rgid,
+                       ps->gen.tgid,
+                       ps->gen.nthr,
+                       ps->gen.excode,
+                       ps->gen.btime,
+                       ps->gen.cmdline,
+                       ps->gen.ppid,
+                       ps->gen.nthrrun,
+                       ps->gen.nthrslpi,
+                       ps->gen.nthrslpu,
+                       ps->gen.euid,
+                       ps->gen.egid,
+                       ps->gen.suid,
+                       ps->gen.sgid,
+                       ps->gen.fsuid,
+                       ps->gen.fsgid,
+                       ps->gen.elaps,
+                       ps->gen.isproc ? 'y':'n',
+                       ps->gen.vpid,
+                       ps->gen.ctid,
+                       ps->gen.container[0] ? ps->gen.container:"-");
+       }
+}
+
+void
+print_PRC(char *hp, struct sstat *ss, struct tstat *ps, int nact)
+{
+       register int i;
+
+       for (i=0; i < nact; i++, ps++)
+       {
+               printf("%s %d (%s) %c %u %lld %lld %d %d %d %d %d %d %d %c\n",
+                               hp,
+                               ps->gen.pid,
+                               ps->gen.name,
+                               ps->gen.state,
+                               hertz,
+                               ps->cpu.utime,
+                               ps->cpu.stime,
+                               ps->cpu.nice,
+                               ps->cpu.prio,
+                               ps->cpu.rtprio,
+                               ps->cpu.policy,
+                               ps->cpu.curcpu,
+                               ps->cpu.sleepavg,
+                               ps->gen.tgid,
+                               ps->gen.isproc ? 'y':'n');
+       }
+}
+
+void
+print_PRM(char *hp, struct sstat *ss, struct tstat *ps, int nact)
+{
+       register int i;
+
+       for (i=0; i < nact; i++, ps++)
+       {
+               printf("%s %d (%s) %c %u %lld %lld %lld %lld %lld %lld "
+                      "%lld %lld %lld %lld %lld %d %c %lld\n",
+                               hp,
+                               ps->gen.pid,
+                               ps->gen.name,
+                               ps->gen.state,
+                               pagesize,
+                               ps->mem.vmem,
+                               ps->mem.rmem,
+                               ps->mem.vexec,
+                               ps->mem.vgrow,
+                               ps->mem.rgrow,
+                               ps->mem.minflt,
+                               ps->mem.majflt,
+                               ps->mem.vlibs,
+                               ps->mem.vdata,
+                               ps->mem.vstack,
+                               ps->mem.vswap,
+                               ps->gen.tgid,
+                               ps->gen.isproc ? 'y':'n',
+                               ps->mem.pmem == (unsigned long long)-1LL ?
+                                                               0:ps->mem.pmem);
+       }
+}
+
+void
+print_PRD(char *hp, struct sstat *ss, struct tstat *ps, int nact)
+{
+       register int i;
+
+       for (i=0; i < nact; i++, ps++)
+       {
+               printf("%s %d (%s) %c %c %c %lld %lld %lld %lld %lld %d n %c\n",
+                               hp,
+                               ps->gen.pid,
+                               ps->gen.name,
+                               ps->gen.state,
+                               'n',
+                               supportflags & IOSTAT ? 'y' : 'n',
+                               ps->dsk.rio, ps->dsk.rsz,
+                               ps->dsk.wio, ps->dsk.wsz, ps->dsk.cwsz,
+                               ps->gen.tgid,
+                               ps->gen.isproc ? 'y':'n');
+       }
+}
+
+void
+print_PRN(char *hp, struct sstat *ss, struct tstat *ps, int nact)
+{
+       register int i;
+
+       for (i=0; i < nact; i++, ps++)
+       {
+               printf("%s %d (%s) %c %c %lld %lld %lld %lld %lld %lld "
+                      "%lld %lld %d %d %d %c\n",
+                               hp,
+                               ps->gen.pid,
+                               ps->gen.name,
+                               ps->gen.state,
+                               supportflags & NETATOP ? 'y' : 'n',
+                               ps->net.tcpsnd, ps->net.tcpssz,
+                               ps->net.tcprcv, ps->net.tcprsz,
+                               ps->net.udpsnd, ps->net.udpssz,
+                               ps->net.udprcv, ps->net.udprsz,
+                               0,              0,
+                               ps->gen.tgid,   ps->gen.isproc ? 'y':'n');
+       }
+}
diff --git a/sources/parseable.h b/sources/parseable.h
new file mode 100644 (file)
index 0000000..4d592cd
--- /dev/null
@@ -0,0 +1,4 @@
+int    parsedef(char *);
+char   parseout(time_t, int,
+               struct devtstat *, struct sstat *,
+               int, unsigned int, char);
diff --git a/sources/photoproc.c b/sources/photoproc.c
new file mode 100644 (file)
index 0000000..dc9a0c9
--- /dev/null
@@ -0,0 +1,815 @@
+/*
+** ATOP - System & Process Monitor 
+** 
+** The program 'atop' offers the possibility to view the activity of
+** the system on system-level as well as process-/thread-level.
+** 
+** This source-file contains functions to read the process-administration
+** of every running process from kernel-space and extract the required
+** activity-counters.
+** ==========================================================================
+** Author:      Gerlof Langeveld
+** E-mail:      gerlof.langeveld@atoptool.nl
+** Date:        November 1996
+** LINUX-port:  June 2000
+** --------------------------------------------------------------------------
+** Copyright (C) 2000-2010 Gerlof Langeveld
+**
+** This program is free software; you can redistribute it and/or modify it
+** under the terms of the GNU General Public License as published by the
+** Free Software Foundation; either version 2, or (at your option) any
+** later version.
+**
+** This program is distributed in the hope that it will be useful, but
+** WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+** See the GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+** --------------------------------------------------------------------------
+**
+** $Log: photoproc.c,v $
+** Revision 1.33  2010/04/23 12:19:35  gerlof
+** Modified mail-address in header.
+**
+** Revision 1.32  2009/11/27 13:44:00  gerlof
+** euid, suid, fsuid, egid, sgid and fsgid also registered.
+**
+** Revision 1.31  2008/03/06 08:38:14  gerlof
+** Register/show ppid of a process.
+**
+** Revision 1.30  2008/01/18 07:36:29  gerlof
+** Gather information about the state of the individual threads.
+**
+** Revision 1.29  2007/11/05 12:26:10  gerlof
+** Detect disappearing /proc/stat file  when process exits
+** (credits: Rene Rebe).
+**
+** Revision 1.28  2007/03/27 10:53:59  gerlof
+** Bug-solution: only allow IOSTAT when patches are not installed.
+**
+** Revision 1.27  2007/03/21 14:21:37  gerlof
+** Handle io counters maintained from 2.6.20
+**
+** Revision 1.26  2007/02/13 10:32:34  gerlof
+** Removal of external declarations.
+**
+** Revision 1.25  2007/01/15 09:00:14  gerlof
+** Add new function to count actual number of processes.
+**
+** Revision 1.24  2006/02/07 06:47:35  gerlof
+** Removed swap-counter.
+**
+** Revision 1.23  2005/10/21 09:49:57  gerlof
+** Per-user accumulation of resource consumption.
+**
+** Revision 1.22  2004/12/14 15:05:58  gerlof
+** Implementation of patch-recognition for disk and network-statistics.
+**
+** Revision 1.21  2004/09/23 09:07:49  gerlof
+** Solved segmentation fault by checking tval.
+**
+** Revision 1.20  2004/09/08 06:01:01  gerlof
+** Correct the priority of a process by adding 100 (the kernel
+** subtracts 100 when showing the value via /proc).
+**
+** Revision 1.19  2004/09/02 10:49:45  gerlof
+** Added sleep-average to process-info.
+**
+** Revision 1.18  2004/08/31 09:51:36  gerlof
+** Gather information about underlying threads.
+**
+** Revision 1.17  2003/07/07 09:26:59  gerlof
+** Cleanup code (-Wall proof).
+**
+** Revision 1.16  2003/06/30 11:30:43  gerlof
+** Enlarge counters to 'long long'.
+**
+** Revision 1.15  2003/02/06 12:09:23  gerlof
+** Exchange tab-character in command-line by space.
+**
+** Revision 1.14  2003/01/24 14:19:39  gerlof
+** Exchange newline byte in command-line by space.
+**
+** Revision 1.13  2003/01/17 14:21:41  root
+** Change-directory to /proc to optimize opening /proc-files
+** via relative path-names i.s.o. absolute path-names.
+**
+** Revision 1.12  2003/01/17 07:31:29  gerlof
+** Store the full command-line for every process.
+**
+** Revision 1.11  2003/01/06 13:03:09  gerlof
+** Improved command-name parsing (command-names containing a close-bracket
+** were not parsed correctly).
+**
+** Revision 1.10  2002/10/03 11:12:39  gerlof
+** Modify (effective) uid/gid to real uid/gid.
+**
+** Revision 1.9  2002/07/24 11:13:31  gerlof
+** Changed to ease porting to other UNIX-platforms.
+**
+** Revision 1.8  2002/07/08 09:27:45  gerlof
+** Avoid buffer overflow during sprintf by using snprintf.
+**
+** Revision 1.7  2002/01/22 13:39:53  gerlof
+** Support for number of cpu's.
+**
+** Revision 1.6  2001/11/22 08:33:43  gerlof
+** Add priority per process.
+**
+** Revision 1.5  2001/11/13 08:26:15  gerlof
+** Small bug-fixes.
+**
+** Revision 1.4  2001/11/07 09:18:43  gerlof
+** Use /proc instead of /dev/kmem for process-level statistics.
+**
+** Revision 1.3  2001/10/04 13:57:34  gerlof
+** Explicit include of sched.h (i.s.o. linux/sched.h via linux/mm.h).
+**
+** Revision 1.2  2001/10/04 08:47:26  gerlof
+** Improved verification of kernel-symbol addresses
+**
+** Revision 1.1  2001/10/02 10:43:29  gerlof
+** Initial revision
+**
+*/
+
+static const char rcsid[] = "$Id: photoproc.c,v 1.33 2010/04/23 12:19:35 gerlof Exp $";
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <time.h>
+
+#include "atop.h"
+#include "photoproc.h"
+
+#define        SCANSTAT        "%c   %d   %*d  %*d  %*d  %*d  "        \
+                       "%*d  %lld %*d  %lld %*d  %lld "        \
+                       "%lld %*d  %*d  %d   %d   %*d  "        \
+                       "%*d  %ld %lld %lld %*d  %*d  " \
+                       "%*d  %*d  %*d  %*d  %*d  %*d  "        \
+                       "%*d  %*d  %*d  %*d  %*d  %*d  "        \
+                       "%d   %d   %d "
+
+/* ATOP-extension line of /proc/pid/stat */
+#define ATOPSTAT       "%lld %llu %lld %llu %lld %llu %lld %llu "      \
+                       "%lld %llu %lld %llu %lld %lld"
+
+static int     procstat(struct tstat *, unsigned long long, char);
+static int     procstatus(struct tstat *);
+static int     procio(struct tstat *);
+static int     proccont(struct tstat *);
+static void    proccmd(struct tstat *);
+static void    procsmaps(struct tstat *);
+
+int
+photoproc(struct tstat *tasklist, int maxtask)
+{
+       static int                      firstcall = 1;
+       static unsigned long long       bootepoch;
+
+       register struct tstat   *curtask;
+
+       FILE            *fp;
+       DIR             *dirp;
+       struct dirent   *entp;
+       char            origdir[1024], dockstat=0;
+       int             tval=0;
+
+       /*
+       ** one-time initialization stuff
+       */
+       if (firstcall)
+       {
+               /*
+               ** check if this kernel offers io-statistics per task
+               */
+               regainrootprivs();
+
+               if ( (fp = fopen("/proc/1/io", "r")) )
+               {
+                       supportflags |= IOSTAT;
+                       fclose(fp);
+               }
+
+               if (! droprootprivs())
+                       cleanstop(42);
+
+               /*
+               ** find epoch time of boot moment
+               */
+               bootepoch = getboot();
+
+               firstcall = 0;
+       }
+
+       /*
+       ** probe if the netatop module and (optionally) the
+       ** netatopd daemon are active
+       */
+       regainrootprivs();
+
+       netatop_probe();
+
+       if (! droprootprivs())
+               cleanstop(42);
+
+       /*
+       ** read all subdirectory-names below the /proc directory
+       */
+       if ( getcwd(origdir, sizeof origdir) == NULL)
+               cleanstop(53);
+
+       if ( chdir("/proc") == -1)
+               cleanstop(53);
+
+       dirp = opendir(".");
+
+       while ( (entp = readdir(dirp)) && tval < maxtask )
+       {
+               /*
+               ** skip non-numerical names
+               */
+               if (!isdigit(entp->d_name[0]))
+                       continue;
+
+               /*
+               ** change to the process' subdirectory
+               */
+               if ( chdir(entp->d_name) != 0 )
+                       continue;
+
+               /*
+               ** gather process-level information
+               */
+               curtask = tasklist+tval;
+
+               if ( !procstat(curtask, bootepoch, 1)) /* from /proc/pid/stat */
+               {
+                       if ( chdir("..") == -1);
+                       continue;
+               }
+
+               if ( !procstatus(curtask) )     /* from /proc/pid/status  */
+               {
+                       if ( chdir("..") == -1);
+                       continue;
+               }
+
+               if ( !procio(curtask) )         /* from /proc/pid/io      */
+               {
+                       if ( chdir("..") == -1);
+                       continue;
+               }
+
+               proccmd(curtask);               /* from /proc/pid/cmdline */
+               dockstat += proccont(curtask);  /* from /proc/pid/cpuset  */
+
+               /*
+               ** reading the smaps file for every process with every sample
+               ** is a really 'expensive' from a CPU consumption point-of-view,
+               ** so gathering this info is optional
+               */
+               if (calcpss)
+                       procsmaps(curtask);     /* from /proc/pid/smaps */
+
+               // read network stats from netatop
+               netatop_gettask(curtask->gen.tgid, 'g', curtask);
+
+               tval++;         /* increment for process-level info */
+
+               /*
+               ** if needed (when number of threads is larger than 1):
+               **   read and fill new entries with thread-level info
+               */
+               if (curtask->gen.nthr > 1)
+               {
+                       DIR             *dirtask;
+                       struct dirent   *tent;
+
+                       curtask->gen.nthrrun  = 0;
+                       curtask->gen.nthrslpi = 0;
+                       curtask->gen.nthrslpu = 0;
+                       
+                       /*
+                       ** open underlying task directory
+                       */
+                       if ( chdir("task") == 0 )
+                       {
+                               dirtask = opendir(".");
+       
+                               while ((tent=readdir(dirtask)) && tval<maxtask)
+                               {
+                                       struct tstat *curthr = tasklist+tval;
+
+                                       /*
+                                       ** change to the thread's subdirectory
+                                       */
+                                       if ( tent->d_name[0] == '.'  ||
+                                            chdir(tent->d_name) != 0 )
+                                               continue;
+
+                                       if ( !procstat(curthr, bootepoch, 0))
+                                       {
+                                               if ( chdir("..") == -1);
+                                               continue;
+                                       }
+                       
+                                       if ( !procstatus(curthr) )
+                                       {
+                                               if ( chdir("..") == -1);
+                                               continue;
+                                       }
+
+                                       if ( !procio(curthr) )
+                                       {
+                                               if ( chdir("..") == -1);
+                                               continue;
+                                       }
+
+                                       strcpy(curthr->gen.container,
+                                                       curtask->gen.container);
+
+                                       switch (curthr->gen.state)
+                                       {
+                                          case 'R':
+                                               curtask->gen.nthrrun  += 1;
+                                               break;
+                                          case 'S':
+                                               curtask->gen.nthrslpi += 1;
+                                               break;
+                                          case 'D':
+                                               curtask->gen.nthrslpu += 1;
+                                               break;
+                                       }
+
+                                       curthr->gen.nthr = 1;
+
+                                       // read network stats from netatop
+                                       netatop_gettask(curthr->gen.pid, 't',
+                                                                       curthr);
+
+                                       // all stats read now
+                                       tval++;     /* increment thread-level */
+                                       if ( chdir("..") == -1); /* thread */
+                               }
+
+                               closedir(dirtask);
+                               if ( chdir("..") == -1); /* leave task */
+                       }
+               }
+
+               if ( chdir("..") == -1); /* leave process-level directry */
+       }
+
+       closedir(dirp);
+
+       if ( chdir(origdir) == -1)
+               cleanstop(53);
+
+       if (dockstat)
+               supportflags |= DOCKSTAT;
+       else
+               supportflags &= ~DOCKSTAT;
+
+       return tval;
+}
+
+/*
+** count number of processes currently running
+*/
+unsigned int
+countprocs(void)
+{
+       unsigned int    nr=0;
+       DIR             *dirp;
+       struct dirent   *entp;
+       char            origdir[1024];
+
+       if ( getcwd(origdir, sizeof origdir) == NULL)
+               cleanstop(53);
+
+       if ( chdir("/proc") == -1)
+               cleanstop(53);
+
+       dirp = opendir(".");
+
+       while ( (entp = readdir(dirp)) )
+       {
+               /*
+               ** count subdirectory-names starting with a digit
+               */
+               if (isdigit(entp->d_name[0]))
+                       nr++;
+       }
+
+       closedir(dirp);
+
+       if ( chdir(origdir) == -1)
+               cleanstop(53);
+
+       return nr;
+}
+
+/*
+** open file "stat" and obtain required info
+*/
+static int
+procstat(struct tstat *curtask, unsigned long long bootepoch, char isproc)
+{
+       FILE    *fp;
+       int     nr;
+       char    line[4096], *p, *cmdhead, *cmdtail;
+
+       if ( (fp = fopen("stat", "r")) == NULL)
+               return 0;
+
+       if ( (nr = fread(line, 1, sizeof line-1, fp)) == 0)
+       {
+               fclose(fp);
+               return 0;
+       }
+
+       line[nr] = '\0';        // terminate string
+
+       /*
+       ** fetch command name
+       */
+       cmdhead = strchr (line, '(');
+       cmdtail = strrchr(line, ')');
+
+       if (!cmdhead || !cmdtail || cmdtail < cmdhead) // parsing failed?
+       {
+               fclose(fp);
+               return 0;
+       }
+
+       if ( (nr = cmdtail-cmdhead-1) > PNAMLEN)
+               nr = PNAMLEN;
+
+       p = curtask->gen.name;
+
+       memcpy(p, cmdhead+1, nr);
+       *(p+nr) = 0;
+
+       while ( (p = strchr(p, '\n')) != NULL)
+       {
+               *p = '?';
+               p++;
+       }
+
+       /*
+       ** fetch other values
+       */
+       curtask->gen.isproc = isproc;
+       curtask->cpu.rtprio  = 0;
+       curtask->cpu.policy  = 0;
+       curtask->gen.excode  = 0;
+
+       sscanf(line, "%d", &(curtask->gen.pid));  /* fetch pid */
+
+       nr = sscanf(cmdtail+2, SCANSTAT,
+               &(curtask->gen.state),  &(curtask->gen.ppid),
+               &(curtask->mem.minflt), &(curtask->mem.majflt),
+               &(curtask->cpu.utime),  &(curtask->cpu.stime),
+               &(curtask->cpu.prio),   &(curtask->cpu.nice),
+               &(curtask->gen.btime),
+               &(curtask->mem.vmem),   &(curtask->mem.rmem),
+               &(curtask->cpu.curcpu), &(curtask->cpu.rtprio),
+               &(curtask->cpu.policy));
+
+       if (nr < 12)            /* parsing failed? */
+       {
+               fclose(fp);
+               return 0;
+       }
+
+       /*
+       ** normalization
+       */
+       curtask->gen.btime   = (curtask->gen.btime+bootepoch)/hertz;
+       curtask->cpu.prio   += 100;     /* was subtracted by kernel */
+       curtask->mem.vmem   /= 1024;
+       curtask->mem.rmem   *= pagesize/1024;
+
+       fclose(fp);
+
+       switch (curtask->gen.state)
+       {
+          case 'R':
+               curtask->gen.nthrrun  = 1;
+               break;
+          case 'S':
+               curtask->gen.nthrslpi = 1;
+               break;
+          case 'D':
+               curtask->gen.nthrslpu = 1;
+               break;
+       }
+
+       return 1;
+}
+
+/*
+** open file "status" and obtain required info
+*/
+static int
+procstatus(struct tstat *curtask)
+{
+       FILE    *fp;
+       char    line[4096];
+
+       if ( (fp = fopen("status", "r")) == NULL)
+               return 0;
+
+       curtask->gen.nthr     = 1;      /* for compat with 2.4 */
+       curtask->cpu.sleepavg = 0;      /* for compat with 2.4 */
+       curtask->mem.vgrow    = 0;      /* calculated later */
+       curtask->mem.rgrow    = 0;      /* calculated later */
+
+       while (fgets(line, sizeof line, fp))
+       {
+               if (memcmp(line, "Tgid:", 5) ==0)
+               {
+                       sscanf(line, "Tgid: %d", &(curtask->gen.tgid));
+                       continue;
+               }
+
+               if (memcmp(line, "Pid:", 4) ==0)
+               {
+                       sscanf(line, "Pid: %d", &(curtask->gen.pid));
+                       continue;
+               }
+
+               if (memcmp(line, "SleepAVG:", 9)==0)
+               {
+                       sscanf(line, "SleepAVG: %d%%",
+                               &(curtask->cpu.sleepavg));
+                       continue;
+               }
+
+               if (memcmp(line, "Uid:", 4)==0)
+               {
+                       sscanf(line, "Uid: %d %d %d %d",
+                               &(curtask->gen.ruid), &(curtask->gen.euid),
+                               &(curtask->gen.suid), &(curtask->gen.fsuid));
+                       continue;
+               }
+
+               if (memcmp(line, "Gid:", 4)==0)
+               {
+                       sscanf(line, "Gid: %d %d %d %d",
+                               &(curtask->gen.rgid), &(curtask->gen.egid),
+                               &(curtask->gen.sgid), &(curtask->gen.fsgid));
+                       continue;
+               }
+
+               if (memcmp(line, "envID:", 6) ==0)
+               {
+                       sscanf(line, "envID: %d", &(curtask->gen.ctid));
+                       continue;
+               }
+
+               if (memcmp(line, "VPid:", 5) ==0)
+               {
+                       sscanf(line, "VPid: %d", &(curtask->gen.vpid));
+                       continue;
+               }
+
+               if (memcmp(line, "Threads:", 8)==0)
+               {
+                       sscanf(line, "Threads: %d", &(curtask->gen.nthr));
+                       continue;
+               }
+
+               if (memcmp(line, "VmData:", 7)==0)
+               {
+                       sscanf(line, "VmData: %lld", &(curtask->mem.vdata));
+                       continue;
+               }
+
+               if (memcmp(line, "VmStk:", 6)==0)
+               {
+                       sscanf(line, "VmStk: %lld", &(curtask->mem.vstack));
+                       continue;
+               }
+
+               if (memcmp(line, "VmExe:", 6)==0)
+               {
+                       sscanf(line, "VmExe: %lld", &(curtask->mem.vexec));
+                       continue;
+               }
+
+               if (memcmp(line, "VmLib:", 6)==0)
+               {
+                       sscanf(line, "VmLib: %lld", &(curtask->mem.vlibs));
+                       continue;
+               }
+
+               if (memcmp(line, "VmSwap:", 7)==0)
+               {
+                       sscanf(line, "VmSwap: %lld", &(curtask->mem.vswap));
+                       continue;
+               }
+
+               if (memcmp(line, "SigQ:", 5)==0)
+                       break;
+       }
+
+       fclose(fp);
+       return 1;
+}
+
+/*
+** open file "io" (>= 2.6.20) and obtain required info
+*/
+#define        IO_READ         "read_bytes:"
+#define        IO_WRITE        "write_bytes:"
+#define        IO_CWRITE       "cancelled_write_bytes:"
+static int
+procio(struct tstat *curtask)
+{
+       FILE    *fp;
+       char    line[4096];
+       count_t dskrsz=0, dskwsz=0, dskcwsz=0;
+
+       if (supportflags & IOSTAT)
+       {
+               regainrootprivs();
+
+               if ( (fp = fopen("io", "r")) )
+               {
+                       while (fgets(line, sizeof line, fp))
+                       {
+                               if (memcmp(line, IO_READ,
+                                               sizeof IO_READ -1) == 0)
+                               {
+                                       sscanf(line, "%*s %llu", &dskrsz);
+                                       dskrsz /= 512;          // in sectors
+                                       continue;
+                               }
+
+                               if (memcmp(line, IO_WRITE,
+                                               sizeof IO_WRITE -1) == 0)
+                               {
+                                       sscanf(line, "%*s %llu", &dskwsz);
+                                       dskwsz /= 512;          // in sectors
+                                       continue;
+                               }
+
+                               if (memcmp(line, IO_CWRITE,
+                                               sizeof IO_CWRITE -1) == 0)
+                               {
+                                       sscanf(line, "%*s %llu", &dskcwsz);
+                                       dskcwsz /= 512;         // in sectors
+                                       continue;
+                               }
+                       }
+
+                       fclose(fp);
+
+                       curtask->dsk.rsz        = dskrsz;
+                       curtask->dsk.rio        = dskrsz;  // to enable sort
+                       curtask->dsk.wsz        = dskwsz;
+                       curtask->dsk.wio        = dskwsz;  // to enable sort
+                       curtask->dsk.cwsz       = dskcwsz;
+               }
+
+               if (! droprootprivs())
+                       cleanstop(42);
+       }
+
+       return 1;
+}
+
+/*
+** store the full command line; the command-line may contain:
+**    - null-bytes as a separator between the arguments
+**    - newlines (e.g. arguments for awk or sed)
+**    - tabs (e.g. arguments for awk or sed)
+** these special bytes will be converted to spaces
+*/
+static void
+proccmd(struct tstat *curtask)
+{
+       FILE            *fp;
+       register int    i, nr;
+
+       memset(curtask->gen.cmdline, 0, CMDLEN+1);
+
+       if ( (fp = fopen("cmdline", "r")) != NULL)
+       {
+               register char *p = curtask->gen.cmdline;
+
+               nr = fread(p, 1, CMDLEN, fp);
+               fclose(fp);
+
+               if (nr >= 0)    /* anything read ? */
+               {
+                       for (i=0; i < nr-1; i++, p++)
+                       {
+                               switch (*p)
+                               {
+                                  case '\0':
+                                  case '\n':
+                                  case '\t':
+                                       *p = ' ';
+                               }
+                       }
+               }
+       }
+}
+
+
+/*
+** store the Docker container ID, retrieved from the cpuset
+** that could look like this:
+**    /system.slice/docker-af78216c2a230f1aa5dce56cbf[SNAP].scope (e.g. CentOS)
+**    /docker/af78216c2a230f1aa5dce56cbf[SNAP]   (e.g. openSUSE and Ubuntu))
+*/
+#define        CIDPREFIX       "docker"
+#define        CIDSIZE         12
+
+static int
+proccont(struct tstat *curtask)
+{
+       FILE            *fp;
+       char            line[80];
+
+       if ( (fp = fopen("cpuset", "r")) != NULL)
+       {
+               register char *p;
+
+               if ( fgets(line, sizeof line, fp) )
+               {
+                       // default string (so if not used) is "/"
+                       if (line[1] && (p = strstr(line, CIDPREFIX)) )
+                       {
+                               memcpy(curtask->gen.container,
+                                       p + sizeof CIDPREFIX, CIDSIZE);
+
+                               fclose(fp);
+                               return 1;
+                       }
+               }
+
+               fclose(fp);
+       }
+
+       return 0;
+}
+
+
+/*
+** open file "smaps" and obtain required info
+*/
+static void
+procsmaps(struct tstat *curtask)
+{
+       FILE    *fp;
+       char    line[4096];
+       count_t pssval;
+
+
+       /*
+       ** open the file (always succeeds, even if no root privs)
+       */
+       regainrootprivs();
+
+       if ( (fp = fopen("smaps", "r")) )
+       {
+               curtask->mem.pmem = 0;
+
+               while (fgets(line, sizeof line, fp))
+               {
+                       if (memcmp(line, "Pss:", 4) != 0)
+                               continue;
+
+                       // PSS line found to be accumulated
+                       sscanf(line, "Pss: %llu", &pssval);
+                       curtask->mem.pmem += pssval;
+               }
+
+               /*
+               ** verify if fgets returned NULL due to error i.s.o. EOF
+               */
+               if (ferror(fp))
+                       curtask->mem.pmem = (unsigned long long)-1LL;
+
+               fclose(fp);
+       }
+       else
+       {
+               curtask->mem.pmem = (unsigned long long)-1LL;
+       }
+
+       if (! droprootprivs())
+               cleanstop(42);
+}
diff --git a/sources/photoproc.h b/sources/photoproc.h
new file mode 100644 (file)
index 0000000..2351934
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+** ATOP - System & Process Monitor
+**
+** The program 'atop' offers the possibility to view the activity of
+** the system on system-level as well as process-level.
+**
+** Include-file describing process-level counters maintained and functions
+** to access the process-database.
+** ================================================================
+** Author:      Gerlof Langeveld
+** E-mail:      gerlof.langeveld@atoptool.nl
+** Date:        November 1996
+** LINUX-port:  June 2000
+**
+** This program is free software; you can redistribute it and/or modify it
+** under the terms of the GNU General Public License as published by the
+** Free Software Foundation; either version 2, or (at your option) any
+** later version.
+**
+** This program is distributed in the hope that it will be useful, but
+** WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+** See the GNU General Public License for more details.
+*/
+
+#define        PNAMLEN         15
+#define        CMDLEN          255
+
+/* 
+** structure containing only relevant process-info extracted 
+** from kernel's process-administration
+*/
+struct tstat {
+       /* GENERAL TASK INFO                                    */
+       struct gen {
+               int     tgid;           /* threadgroup identification   */
+               int     pid;            /* process identification       */
+               int     ppid;           /* parent process identification*/
+               int     ruid;           /* real  user  identification   */
+               int     euid;           /* eff.  user  identification   */
+               int     suid;           /* saved user  identification   */
+               int     fsuid;          /* fs    user  identification   */
+               int     rgid;           /* real  group identification   */
+               int     egid;           /* eff.  group identification   */
+               int     sgid;           /* saved group identification   */
+               int     fsgid;          /* fs    group identification   */
+               int     nthr;           /* number of threads in tgroup  */
+               char    name[PNAMLEN+1];/* process name string          */
+               char    isproc;         /* boolean: process level?      */
+               char    state;          /* process state ('E' = exited) */
+               int     excode;         /* process exit status          */
+               time_t  btime;          /* process start time (epoch)   */
+               time_t  elaps;          /* process elaps time (hertz)   */
+               char    cmdline[CMDLEN+1];/* command-line string        */
+               int     nthrslpi;       /* # threads in state 'S'       */
+               int     nthrslpu;       /* # threads in state 'D'       */
+               int     nthrrun;        /* # threads in state 'R'       */
+
+               int     ctid;           /* OpenVZ container ID          */
+               int     vpid;           /* OpenVZ virtual PID           */
+
+               int     wasinactive;    /* boolean: task inactive       */
+
+               char    container[16];  /* Docker container id (12 pos) */
+       } gen;
+
+       /* CPU STATISTICS                                               */
+       struct cpu {
+               count_t utime;          /* time user   text (ticks)     */
+               count_t stime;          /* time system text (ticks)     */
+               int     nice;           /* nice value                   */
+               int     prio;           /* priority                     */
+               int     rtprio;         /* realtime priority            */
+               int     policy;         /* scheduling policy            */
+               int     curcpu;         /* current processor            */
+               int     sleepavg;       /* sleep average percentage     */
+               int     ifuture[4];     /* reserved for future use      */
+               count_t cfuture[4];     /* reserved for future use      */
+       } cpu;
+
+       /* DISK STATISTICS                                              */
+       struct dsk {
+               count_t rio;            /* number of read requests      */
+               count_t rsz;            /* cumulative # sectors read    */
+               count_t wio;            /* number of write requests     */
+               count_t wsz;            /* cumulative # sectors written */
+               count_t cwsz;           /* cumulative # written sectors */
+                                       /* being cancelled              */
+               count_t cfuture[4];     /* reserved for future use      */
+       } dsk;
+
+       /* MEMORY STATISTICS                                            */
+       struct mem {
+               count_t minflt;         /* number of page-reclaims      */
+               count_t majflt;         /* number of page-faults        */
+               count_t vexec;          /* virtmem execfile (Kb)        */
+               count_t vmem;           /* virtual  memory  (Kb)        */
+               count_t rmem;           /* resident memory  (Kb)        */
+               count_t pmem;           /* resident memory  (Kb)        */
+               count_t vgrow;          /* virtual  growth  (Kb)        */
+               count_t rgrow;          /* resident growth  (Kb)        */
+               count_t vdata;          /* virtmem data     (Kb)        */
+               count_t vstack;         /* virtmem stack    (Kb)        */
+               count_t vlibs;          /* virtmem libexec  (Kb)        */
+               count_t vswap;          /* swap space used  (Kb)        */
+               count_t cfuture[4];     /* reserved for future use      */
+       } mem;
+
+       /* NETWORK STATISTICS                                           */
+       struct net {
+               count_t tcpsnd;         /* number of TCP-packets sent   */
+               count_t tcpssz;         /* cumulative size packets sent */
+               count_t tcprcv;         /* number of TCP-packets recved */
+               count_t tcprsz;         /* cumulative size packets rcvd */
+               count_t udpsnd;         /* number of UDP-packets sent   */
+               count_t udpssz;         /* cumulative size packets sent */
+               count_t udprcv;         /* number of UDP-packets recved */
+               count_t udprsz;         /* cumulative size packets sent */
+               count_t avail1;         /* */
+               count_t avail2;         /* */
+               count_t cfuture[4];     /* reserved for future use      */
+       } net;
+};
+
+struct pinfo {
+       struct pinfo    *phnext;        /* next process in hash    chain */
+       struct pinfo    *prnext;        /* next process in residue chain */
+       struct pinfo    *prprev;        /* prev process in residue chain */
+
+       struct tstat    tstat;          /* per-process statistics        */
+};
+
+/*
+** structure to maintains all deviation info related to one sample
+*/
+struct devtstat {
+        struct tstat     *taskall;
+        struct tstat    **procall;
+        struct tstat    **procactive;
+
+       unsigned long   ntaskall;
+        unsigned long  ntaskactive;
+       unsigned long   nprocall;
+       unsigned long   nprocactive;
+
+        unsigned long   totrun, totslpi, totslpu, totzombie;
+};
+
+/*
+** prototypes of process-database functions
+*/
+int            pdb_gettask(int, char, time_t, struct pinfo **);
+void           pdb_addtask(int, struct pinfo *);
+int            pdb_deltask(int, char);
+int            pdb_makeresidue(void);
+int            pdb_cleanresidue(void);
+int            pdb_srchresidue(struct tstat *, struct pinfo **);
+
+/*
+** prototypes for raw process-statistics functions
+*/
+struct netpertask;
+
+void           deviattask(struct tstat *, int,
+                          struct tstat *, int, 
+                          struct devtstat *, struct sstat *);
+
+int            photoproc(struct tstat *, int);
+unsigned int   countprocs(void);
diff --git a/sources/photosyst.c b/sources/photosyst.c
new file mode 100644 (file)
index 0000000..38828ec
--- /dev/null
@@ -0,0 +1,1754 @@
+/*
+** ATOP - System & Process Monitor 
+** 
+** The program 'atop' offers the possibility to view the activity of
+** the system on system-level as well as process-level.
+** 
+** This source-file contains functions to read all relevant system-level
+** figures.
+** ==========================================================================
+** Author:      Gerlof Langeveld
+** E-mail:      gerlof.langeveld@atoptool.nl
+** Date:        November 1996
+** LINUX-port:  June 2000
+** --------------------------------------------------------------------------
+** Copyright (C) 2000-2012 Gerlof Langeveld
+**
+** This program is free software; you can redistribute it and/or modify it
+** under the terms of the GNU General Public License as published by the
+** Free Software Foundation; either version 2, or (at your option) any
+** later version.
+**
+** This program is distributed in the hope that it will be useful, but
+** WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+** See the GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+** --------------------------------------------------------------------------
+**
+** $Log: photosyst.c,v $
+** Revision 1.38  2010/11/19 07:40:40  gerlof
+** Support of virtual disk vd... (kvm).
+**
+** Revision 1.37  2010/11/14 06:42:18  gerlof
+** After opening /proc/cpuinfo, the file descriptor was not closed any more.
+**
+** Revision 1.36  2010/10/23 14:09:50  gerlof
+** Add support for mmcblk disks (MMC/SD cardreaders)
+** Credits: Anssi Hannula
+**
+** Revision 1.35  2010/05/18 19:20:30  gerlof
+** Introduce CPU frequency and scaling (JC van Winkel).
+**
+** Revision 1.34  2010/04/23 12:19:35  gerlof
+** Modified mail-address in header.
+**
+** Revision 1.33  2010/03/04 10:58:05  gerlof
+** Added recognition of device-type /dev/fio...
+**
+** Revision 1.32  2010/03/04 10:52:47  gerlof
+** Support I/O-statistics on logical volumes and MD devices.
+**
+** Revision 1.31  2009/12/17 11:59:16  gerlof
+** Gather and display new counters: dirty cache and guest cpu usage.
+**
+** Revision 1.30  2008/02/25 13:47:00  gerlof
+** Experimental code for HTTP statistics.
+**
+** Revision 1.29  2007/08/17 09:45:44  gerlof
+** Experimental: gather info about HTTP statistics.
+**
+** Revision 1.28  2007/08/16 12:00:49  gerlof
+** Add support for atopsar reporting.
+** Gather more counters, mainly related to networking.
+**
+** Revision 1.27  2007/07/03 09:01:56  gerlof
+** Support Apache-statistics.
+**
+** Revision 1.26  2007/02/13 10:32:28  gerlof
+** Removal of external declarations.
+**
+** Revision 1.25  2007/02/13 09:29:57  gerlof
+** Removal of external declarations.
+**
+** Revision 1.24  2007/01/22 14:57:12  gerlof
+** Support of special disks used by virtual machines.
+**
+** Revision 1.23  2007/01/22 08:28:50  gerlof
+** Support steal-time from /proc/stat.
+**
+** Revision 1.22  2006/11/13 13:48:20  gerlof
+** Implement load-average counters, context-switches and interrupts.
+**
+** Revision 1.21  2006/01/30 09:14:16  gerlof
+** Extend memory counters (a.o. page scans).
+**
+** Revision 1.20  2005/10/21 09:50:08  gerlof
+** Per-user accumulation of resource consumption.
+**
+** Revision 1.19  2004/10/28 08:31:23  gerlof
+** New counter: vm committed space
+**
+** Revision 1.18  2004/09/24 10:02:35  gerlof
+** Wrong cpu-numbers for system level statistics.
+**
+** Revision 1.17  2004/05/07 05:27:37  gerlof
+** Recognize new disk-names and support of diskname-modification.
+**
+** Revision 1.16  2004/05/06 09:53:31  gerlof
+** Skip statistics of ram-disks.
+**
+** Revision 1.15  2004/05/06 09:46:14  gerlof
+** Ported to kernel-version 2.6.
+**
+** Revision 1.14  2003/07/08 13:53:21  gerlof
+** Cleanup code.
+**
+** Revision 1.13  2003/07/07 09:27:06  gerlof
+** Cleanup code (-Wall proof).
+**
+** Revision 1.12  2003/06/30 11:30:37  gerlof
+** Enlarge counters to 'long long'.
+**
+** Revision 1.11  2003/06/24 06:21:40  gerlof
+** Limit number of system resource lines.
+**
+** Revision 1.10  2003/01/17 14:23:05  root
+** Change-directory to /proc to optimize opening /proc-files
+** via relative path-names i.s.o. absolute path-names.
+**
+** Revision 1.9  2003/01/14 07:50:26  gerlof
+** Consider IPv6 counters on IP and UDP level (add them to the IPv4 counters).
+**
+** Revision 1.8  2002/07/24 11:13:38  gerlof
+** Changed to ease porting to other UNIX-platforms.
+**
+** Revision 1.7  2002/07/11 09:12:41  root
+** Parsing of /proc/meminfo made 2.5 proof.
+**
+** Revision 1.6  2002/07/10 05:00:21  root
+** Counters pin/pout renamed to swin/swout (Linux conventions).
+**
+** Revision 1.5  2002/07/08 09:31:11  gerlof
+** *** empty log message ***
+**
+** Revision 1.4  2002/07/02 07:36:45  gerlof
+** *** empty log message ***
+**
+** Revision 1.3  2002/07/02 07:08:36  gerlof
+** Recognize more disk driver types via regular expressions
+**
+** Revision 1.2  2002/01/22 13:40:11  gerlof
+** Support for number of cpu's.
+**
+** Revision 1.1  2001/10/02 10:43:31  gerlof
+** Initial revision
+**
+*/
+
+static const char rcsid[] = "$Id: photosyst.c,v 1.38 2010/11/19 07:40:40 gerlof Exp $";
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <regex.h>
+#include <sys/stat.h>
+#include <sys/times.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <signal.h>
+#include <string.h>
+#include <dirent.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+// #define     _GNU_SOURCE
+#include <sys/ipc.h>
+#include <sys/shm.h>
+
+#include "atop.h"
+#include "photosyst.h"
+
+#define        MAXCNT  64
+
+/* return value of isdisk() */
+#define        NONTYPE 0
+#define        DSKTYPE 1
+#define        MDDTYPE 2
+#define        LVMTYPE 3
+
+static int     isdisk(unsigned int, unsigned int,
+                       char *, struct perdsk *, int);
+
+static struct ipv6_stats       ipv6_tmp;
+static struct icmpv6_stats     icmpv6_tmp;
+static struct udpv6_stats      udpv6_tmp;
+
+struct v6tab {
+       char    *nam;
+       count_t *val;
+};
+
+static struct v6tab            v6tab[] = {
+    {"Ip6InReceives",          &ipv6_tmp.Ip6InReceives,                     },
+    {"Ip6InHdrErrors",         &ipv6_tmp.Ip6InHdrErrors,                    },
+    {"Ip6InTooBigErrors",      &ipv6_tmp.Ip6InTooBigErrors,                 },
+    {"Ip6InNoRoutes",          &ipv6_tmp.Ip6InNoRoutes,                     },
+    {"Ip6InAddrErrors",                &ipv6_tmp.Ip6InAddrErrors,                   },
+    {"Ip6InUnknownProtos",     &ipv6_tmp.Ip6InUnknownProtos,                },
+    {"Ip6InTruncatedPkts",     &ipv6_tmp.Ip6InTruncatedPkts,                },
+    {"Ip6InDiscards",          &ipv6_tmp.Ip6InDiscards,                     },
+    {"Ip6InDelivers",          &ipv6_tmp.Ip6InDelivers,                     },
+    {"Ip6OutForwDatagrams",    &ipv6_tmp.Ip6OutForwDatagrams,               },
+    {"Ip6OutRequests",         &ipv6_tmp.Ip6OutRequests,                    },
+    {"Ip6OutDiscards",         &ipv6_tmp.Ip6OutDiscards,                    },
+    {"Ip6OutNoRoutes",         &ipv6_tmp.Ip6OutNoRoutes,                    },
+    {"Ip6ReasmTimeout",                &ipv6_tmp.Ip6ReasmTimeout,                   },
+    {"Ip6ReasmReqds",          &ipv6_tmp.Ip6ReasmReqds,                     },
+    {"Ip6ReasmOKs",            &ipv6_tmp.Ip6ReasmOKs,                       },
+    {"Ip6ReasmFails",          &ipv6_tmp.Ip6ReasmFails,                     },
+    {"Ip6FragOKs",             &ipv6_tmp.Ip6FragOKs,                        },
+    {"Ip6FragFails",           &ipv6_tmp.Ip6FragFails,                      },
+    {"Ip6FragCreates",         &ipv6_tmp.Ip6FragCreates,                    },
+    {"Ip6InMcastPkts",         &ipv6_tmp.Ip6InMcastPkts,                    },
+    {"Ip6OutMcastPkts",                &ipv6_tmp.Ip6OutMcastPkts,                   },
+    {"Icmp6InMsgs",            &icmpv6_tmp.Icmp6InMsgs,                     },
+    {"Icmp6InErrors",          &icmpv6_tmp.Icmp6InErrors,                   },
+    {"Icmp6InDestUnreachs",    &icmpv6_tmp.Icmp6InDestUnreachs,             },
+    {"Icmp6InPktTooBigs",      &icmpv6_tmp.Icmp6InPktTooBigs,               },
+    {"Icmp6InTimeExcds",       &icmpv6_tmp.Icmp6InTimeExcds,                },
+    {"Icmp6InParmProblems",    &icmpv6_tmp.Icmp6InParmProblems,             },
+    {"Icmp6InEchos",           &icmpv6_tmp.Icmp6InEchos,                    },
+    {"Icmp6InEchoReplies",     &icmpv6_tmp.Icmp6InEchoReplies,              },
+    {"Icmp6InGroupMembQueries",        &icmpv6_tmp.Icmp6InGroupMembQueries,         },
+    {"Icmp6InGroupMembResponses",
+                               &icmpv6_tmp.Icmp6InGroupMembResponses,       },
+    {"Icmp6InGroupMembReductions",
+                               &icmpv6_tmp.Icmp6InGroupMembReductions,      },
+    {"Icmp6InRouterSolicits",  &icmpv6_tmp.Icmp6InRouterSolicits,           },
+    {"Icmp6InRouterAdvertisements",
+                               &icmpv6_tmp.Icmp6InRouterAdvertisements,     },
+    {"Icmp6InNeighborSolicits",        &icmpv6_tmp.Icmp6InNeighborSolicits,         },
+    {"Icmp6InNeighborAdvertisements",
+                               &icmpv6_tmp.Icmp6InNeighborAdvertisements,   },
+    {"Icmp6InRedirects",       &icmpv6_tmp.Icmp6InRedirects,                },
+    {"Icmp6OutMsgs",           &icmpv6_tmp.Icmp6OutMsgs,                    },
+    {"Icmp6OutDestUnreachs",   &icmpv6_tmp.Icmp6OutDestUnreachs,            },
+    {"Icmp6OutPktTooBigs",     &icmpv6_tmp.Icmp6OutPktTooBigs,              },
+    {"Icmp6OutTimeExcds",      &icmpv6_tmp.Icmp6OutTimeExcds,               },
+    {"Icmp6OutParmProblems",   &icmpv6_tmp.Icmp6OutParmProblems,            },
+    {"Icmp6OutEchoReplies",    &icmpv6_tmp.Icmp6OutEchoReplies,             },
+    {"Icmp6OutRouterSolicits", &icmpv6_tmp.Icmp6OutRouterSolicits,          },
+    {"Icmp6OutNeighborSolicits",&icmpv6_tmp.Icmp6OutNeighborSolicits,        },
+    {"Icmp6OutNeighborAdvertisements",
+                               &icmpv6_tmp.Icmp6OutNeighborAdvertisements,  },
+    {"Icmp6OutRedirects",      &icmpv6_tmp.Icmp6OutRedirects,               },
+    {"Icmp6OutGroupMembResponses",
+                               &icmpv6_tmp.Icmp6OutGroupMembResponses,      },
+    {"Icmp6OutGroupMembReductions",
+                               &icmpv6_tmp.Icmp6OutGroupMembReductions,     },
+
+    {"Udp6InDatagrams",                &udpv6_tmp.Udp6InDatagrams,                   },
+    {"Udp6NoPorts",            &udpv6_tmp.Udp6NoPorts,                       },
+    {"Udp6InErrors",           &udpv6_tmp.Udp6InErrors,                      },
+    {"Udp6OutDatagrams",       &udpv6_tmp.Udp6OutDatagrams,                  },
+};
+
+static int     v6tab_entries = sizeof(v6tab)/sizeof(struct v6tab);
+
+void
+photosyst(struct sstat *si)
+{
+       register int    i, nr;
+       count_t         cnts[MAXCNT];
+       float           lavg1, lavg5, lavg15;
+       FILE            *fp;
+       char            linebuf[1024], nam[64], origdir[1024];
+       static char     part_stats = 1; /* per-partition statistics ? */
+       unsigned int    major, minor;
+       struct shm_info shminfo;
+#if    HTTPSTATS
+       static int      wwwvalid = 1;
+#endif
+
+       memset(si, 0, sizeof(struct sstat));
+
+       if ( getcwd(origdir, sizeof origdir) == NULL)
+               cleanstop(53);
+
+       if ( chdir("/proc") == -1)
+               cleanstop(53);
+
+       /*
+       ** gather various general statistics from the file /proc/stat and
+       ** store them in binary form
+       */
+       if ( (fp = fopen("stat", "r")) != NULL)
+       {
+               while ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
+               {
+                       nr = sscanf(linebuf,
+                                   "%s   %lld %lld %lld %lld %lld %lld %lld "
+                                   "%lld %lld %lld %lld %lld %lld %lld %lld ",
+                               nam,
+                               &cnts[0],  &cnts[1],  &cnts[2],  &cnts[3],
+                               &cnts[4],  &cnts[5],  &cnts[6],  &cnts[7],
+                               &cnts[8],  &cnts[9],  &cnts[10], &cnts[11],
+                               &cnts[12], &cnts[13], &cnts[14]);
+
+                       if (nr < 2)             /* headerline ? --> skip */
+                               continue;
+
+                       if ( strcmp("cpu", nam) == EQ)
+                       {
+                               si->cpu.all.utime       = cnts[0];
+                               si->cpu.all.ntime       = cnts[1];
+                               si->cpu.all.stime       = cnts[2];
+                               si->cpu.all.itime       = cnts[3];
+
+                               if (nr > 5)     /* 2.6 kernel? */
+                               {
+                                       si->cpu.all.wtime       = cnts[4];
+                                       si->cpu.all.Itime       = cnts[5];
+                                       si->cpu.all.Stime       = cnts[6];
+
+                                       if (nr > 8)     /* steal support */
+                                               si->cpu.all.steal = cnts[7];
+
+                                       if (nr > 9)     /* guest support */
+                                               si->cpu.all.guest = cnts[8];
+                               }
+                               continue;
+                       }
+
+                       if ( strncmp("cpu", nam, 3) == EQ)
+                       {
+                               i = atoi(&nam[3]);
+
+                               if (i >= MAXCPU)
+                               {
+                                       fprintf(stderr,
+                                               "cpu %s exceeds maximum of %d\n",
+                                               nam, MAXCPU);
+                                       continue;
+                               }
+
+                               si->cpu.cpu[i].cpunr    = i;
+                               si->cpu.cpu[i].utime    = cnts[0];
+                               si->cpu.cpu[i].ntime    = cnts[1];
+                               si->cpu.cpu[i].stime    = cnts[2];
+                               si->cpu.cpu[i].itime    = cnts[3];
+
+                               if (nr > 5)     /* 2.6 kernel? */
+                               {
+                                       si->cpu.cpu[i].wtime    = cnts[4];
+                                       si->cpu.cpu[i].Itime    = cnts[5];
+                                       si->cpu.cpu[i].Stime    = cnts[6];
+
+                                       if (nr > 8)     /* steal support */
+                                               si->cpu.cpu[i].steal = cnts[7];
+
+                                       if (nr > 9)     /* guest support */
+                                               si->cpu.cpu[i].guest = cnts[8];
+                               }
+
+                               si->cpu.nrcpu++;
+                               continue;
+                       }
+
+                       if ( strcmp("ctxt", nam) == EQ)
+                       {
+                               si->cpu.csw     = cnts[0];
+                               continue;
+                       }
+
+                       if ( strcmp("intr", nam) == EQ)
+                       {
+                               si->cpu.devint  = cnts[0];
+                               continue;
+                       }
+
+                       if ( strcmp("processes", nam) == EQ)
+                       {
+                               si->cpu.nprocs  = cnts[0];
+                               continue;
+                       }
+
+                       if ( strcmp("swap", nam) == EQ)   /* < 2.6 */
+                       {
+                               si->mem.swins   = cnts[0];
+                               si->mem.swouts  = cnts[1];
+                               continue;
+                       }
+               }
+
+               fclose(fp);
+
+               if (si->cpu.nrcpu == 0)
+                       si->cpu.nrcpu = 1;
+       }
+
+       /*
+       ** gather loadaverage values from the file /proc/loadavg and
+       ** store them in binary form
+       */
+       if ( (fp = fopen("loadavg", "r")) != NULL)
+       {
+               if ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
+               {
+                       if ( sscanf(linebuf, "%f %f %f",
+                               &lavg1, &lavg5, &lavg15) == 3)
+                       {
+                               si->cpu.lavg1   = lavg1;
+                               si->cpu.lavg5   = lavg5;
+                               si->cpu.lavg15  = lavg15;
+                       }
+               }
+
+               fclose(fp);
+       }
+
+       /*
+       ** gather frequency scaling info.
+        ** sources (in order of preference): 
+        **     /sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state
+        ** or
+        **     /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq
+        **     /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq
+        **
+       ** store them in binary form
+       */
+        static char fn[80];
+        int didone=0;
+
+        for (i = 0; i < si->cpu.nrcpu; ++i)
+        {
+                long long f=0;
+
+                sprintf(fn,
+                   "/sys/devices/system/cpu/cpu%d/cpufreq/stats/time_in_state",
+                   i);
+
+                if ((fp=fopen(fn, "r")) != 0)
+                {
+                        long long hits=0;
+                        long long maxfreq=0;
+                        long long cnt=0;
+                        long long sum=0;
+
+                        while (fscanf(fp, "%lld %lld", &f, &cnt) == 2)
+                        {
+                                f      /= 1000;
+                                sum    += (f*cnt);
+                                hits   += cnt;
+
+                                if (f > maxfreq)
+                                       maxfreq=f;
+                               didone=1;
+                        }
+
+                       si->cpu.cpu[i].freqcnt.maxfreq  = maxfreq;
+                       si->cpu.cpu[i].freqcnt.cnt      = sum;
+                       si->cpu.cpu[i].freqcnt.ticks    = hits;
+
+                        fclose(fp);
+                }
+               else
+               {    // governor statistics not available
+                     sprintf(fn,  
+                      "/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq",
+                     i);
+
+                        if ((fp=fopen(fn, "r")) != 0)
+                        {
+                                if (fscanf(fp, "%lld", &f) == 1)
+                                {
+                                       // convert KHz to MHz
+                                       si->cpu.cpu[i].freqcnt.maxfreq =f/1000; 
+                                }
+
+                                fclose(fp);
+                        }
+                        else 
+                        {
+                               si->cpu.cpu[i].freqcnt.maxfreq=0;
+                        }
+
+                       sprintf(fn,  
+                       "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq",
+                      i);
+        
+                        if ((fp=fopen(fn, "r")) != 0)
+                        {
+                                if (fscanf(fp, "%lld", &f) == 1)
+                                {
+                                       // convert KHz to MHz
+                                        si->cpu.cpu[i].freqcnt.cnt   = f/1000;
+                                        si->cpu.cpu[i].freqcnt.ticks = 0;
+                                        didone=1;
+                                }
+
+                                fclose(fp);
+                        }
+                        else
+                        {
+                                si->cpu.cpu[i].freqcnt.cnt     = 0;
+                                si->cpu.cpu[i].freqcnt.ticks   = 0;
+                        }
+                }
+        } // for all CPUs
+
+        if (!didone)     // did not get processor freq statistics.
+                         // use /proc/cpuinfo
+        {
+               if ( (fp = fopen("cpuinfo", "r")) != NULL)
+                {
+                        // get information from the lines
+                        // processor\t: 0
+                        // cpu MHz\t\t: 800.000
+                        
+                        int cpuno=-1;
+
+                       while ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
+                        {
+                                if (memcmp(linebuf, "processor", 9)== EQ)
+                                       sscanf(linebuf, "%*s %*s %d", &cpuno);
+
+                                if (memcmp(linebuf, "cpu MHz", 7) == EQ)
+                               {
+                                        if (cpuno >= 0 && cpuno < si->cpu.nrcpu)
+                                       {
+                                               sscanf(linebuf,
+                                                       "%*s %*s %*s %lld",
+                                                       &(si->cpu.cpu[cpuno].freqcnt.cnt));
+                                       }
+                                }
+                        }
+
+                       fclose(fp);
+                }
+
+        }
+
+       /*
+       ** gather virtual memory statistics from the file /proc/vmstat and
+       ** store them in binary form (>= kernel 2.6)
+       */
+       if ( (fp = fopen("vmstat", "r")) != NULL)
+       {
+               while ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
+               {
+                       nr = sscanf(linebuf, "%s %lld", nam, &cnts[0]);
+
+                       if (nr < 2)             /* headerline ? --> skip */
+                               continue;
+
+                       if ( strcmp("pswpin", nam) == EQ)
+                       {
+                               si->mem.swins   = cnts[0];
+                               continue;
+                       }
+
+                       if ( strcmp("pswpout", nam) == EQ)
+                       {
+                               si->mem.swouts  = cnts[0];
+                               continue;
+                       }
+
+                       if ( strncmp("pgscan_", nam, 7) == EQ)
+                       {
+                               si->mem.pgscans += cnts[0];
+                               continue;
+                       }
+
+                       if ( strncmp("pgsteal_", nam, 8) == EQ)
+                       {
+                               si->mem.pgsteal += cnts[0];
+                               continue;
+                       }
+
+                       if ( strcmp("allocstall", nam) == EQ)
+                       {
+                               si->mem.allocstall = cnts[0];
+                               continue;
+                       }
+               }
+
+               fclose(fp);
+       }
+
+       /*
+       ** gather memory-related statistics from the file /proc/meminfo and
+       ** store them in binary form
+       **
+       ** in the file /proc/meminfo a 2.4 kernel starts with two lines
+       ** headed by the strings "Mem:" and "Swap:" containing all required
+       ** fields, except proper value for page cache
+        ** if these lines are present we try to skip parsing the rest
+       ** of the lines; if these lines are not present we should get the
+       ** required field from other lines
+       */
+       si->mem.physmem         = (count_t)-1; 
+       si->mem.freemem         = (count_t)-1;
+       si->mem.buffermem       = (count_t)-1;
+       si->mem.cachemem        = (count_t)-1;
+       si->mem.slabmem         = (count_t) 0;
+       si->mem.slabreclaim     = (count_t) 0;
+       si->mem.shmem           = (count_t) 0;
+       si->mem.totswap         = (count_t)-1;
+       si->mem.freeswap        = (count_t)-1;
+       si->mem.committed       = (count_t) 0;
+
+       if ( (fp = fopen("meminfo", "r")) != NULL)
+       {
+               while ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
+               {
+                       nr = sscanf(linebuf,
+                               "%s %lld %lld %lld %lld %lld %lld %lld "
+                               "%lld %lld %lld\n",
+                               nam,
+                               &cnts[0],  &cnts[1],  &cnts[2],  &cnts[3],
+                               &cnts[4],  &cnts[5],  &cnts[6],  &cnts[7],
+                               &cnts[8],  &cnts[9]);
+
+                       if (nr < 2)             /* headerline ? --> skip */
+                               continue;
+
+                       if ( strcmp("Mem:", nam) == EQ)
+                       {
+                               si->mem.physmem         = cnts[0] / pagesize; 
+                               si->mem.freemem         = cnts[2] / pagesize;
+                               si->mem.buffermem       = cnts[4] / pagesize;
+                       }
+                       else    if ( strcmp("Swap:", nam) == EQ)
+                               {
+                                       si->mem.totswap  = cnts[0] / pagesize;
+                                       si->mem.freeswap = cnts[2] / pagesize;
+                               }
+                       else    if (strcmp("Cached:", nam) == EQ)
+                               {
+                                       if (si->mem.cachemem  == (count_t)-1)
+                                       {
+                                               si->mem.cachemem  =
+                                                       cnts[0]*1024/pagesize;
+                                       }
+                               }
+                       else    if (strcmp("Dirty:", nam) == EQ)
+                               {
+                                       si->mem.cachedrt  =
+                                                       cnts[0]*1024/pagesize;
+                               }
+                       else    if (strcmp("MemTotal:", nam) == EQ)
+                               {
+                                       if (si->mem.physmem  == (count_t)-1)
+                                       {
+                                               si->mem.physmem  =
+                                                       cnts[0]*1024/pagesize;
+                                       }
+                               }
+                       else    if (strcmp("MemFree:", nam) == EQ)
+                               {
+                                       if (si->mem.freemem  == (count_t)-1)
+                                       {
+                                               si->mem.freemem  =
+                                                       cnts[0]*1024/pagesize;
+                                       }
+                               }
+                       else    if (strcmp("Buffers:", nam) == EQ)
+                               {
+                                       if (si->mem.buffermem  == (count_t)-1)
+                                       {
+                                               si->mem.buffermem  =
+                                                       cnts[0]*1024/pagesize;
+                                       }
+                               }
+                       else    if (strcmp("Shmem:", nam) == EQ)
+                               {
+                                       si->mem.shmem = cnts[0]*1024/pagesize;
+                               }
+                       else    if (strcmp("SwapTotal:", nam) == EQ)
+                               {
+                                       if (si->mem.totswap  == (count_t)-1)
+                                       {
+                                               si->mem.totswap  =
+                                                       cnts[0]*1024/pagesize;
+                                       }
+                               }
+                       else    if (strcmp("SwapFree:", nam) == EQ)
+                               {
+                                       if (si->mem.freeswap  == (count_t)-1)
+                                       {
+                                               si->mem.freeswap  =
+                                                       cnts[0]*1024/pagesize;
+                                       }
+                               }
+                       else    if (strcmp("Slab:", nam) == EQ)
+                               {
+                                       si->mem.slabmem = cnts[0]*1024/pagesize;
+                               }
+                       else    if (strcmp("SReclaimable:", nam) == EQ)
+                               {
+                                       si->mem.slabreclaim = cnts[0]*1024/
+                                                               pagesize;
+                               }
+                       else    if (strcmp("Committed_AS:", nam) == EQ)
+                               {
+                                       si->mem.committed = cnts[0]*1024/
+                                                               pagesize;
+                               }
+                       else    if (strcmp("CommitLimit:", nam) == EQ)
+                               {
+                                       si->mem.commitlim = cnts[0]*1024/
+                                                               pagesize;
+                               }
+                       else    if (strcmp("HugePages_Total:", nam) == EQ)
+                               {
+                                       si->mem.tothugepage = cnts[0];
+                               }
+                       else    if (strcmp("HugePages_Free:", nam) == EQ)
+                               {
+                                       si->mem.freehugepage = cnts[0];
+                               }
+                       else    if (strcmp("Hugepagesize:", nam) == EQ)
+                               {
+                                       si->mem.hugepagesz = cnts[0]*1024;
+                               }
+               }
+
+               fclose(fp);
+       }
+
+       /*
+       ** gather vmware-related statistics from /proc/vmmemctl
+       ** (only present if balloon driver enabled)
+       */ 
+       si->mem.vmwballoon = (count_t) 0;
+
+       if ( (fp = fopen("vmmemctl", "r")) != NULL)
+       {
+               while ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
+               {
+                       nr = sscanf(linebuf, "%s %lld ", nam, &cnts[0]);
+
+                       if ( strcmp("current:", nam) == EQ)
+                       {
+                               si->mem.vmwballoon = cnts[0];
+                               break;
+                       }
+               }
+
+               fclose(fp);
+       }
+
+       /*
+       ** gather network-related statistics
+       **      - interface stats from the file /proc/net/dev
+       **      - IPv4      stats from the file /proc/net/snmp
+       **      - IPv6      stats from the file /proc/net/snmp6
+       */
+
+       /*
+       ** interface statistics
+       */
+       if ( (fp = fopen("net/dev", "r")) != NULL)
+       {
+               char *cp;
+
+               i = 0;
+
+               while ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
+               {
+                       if ( (cp = strchr(linebuf, ':')) != NULL)
+                               *cp = ' ';      /* substitute ':' by space */
+
+                       nr = sscanf(linebuf,
+                                    "%15s %lld %lld %lld %lld %lld %lld %lld "
+                                    "%lld %lld %lld %lld %lld %lld %lld %lld "
+                                    "%lld\n",
+                                 si->intf.intf[i].name,
+                               &(si->intf.intf[i].rbyte),
+                               &(si->intf.intf[i].rpack),
+                               &(si->intf.intf[i].rerrs),
+                               &(si->intf.intf[i].rdrop),
+                               &(si->intf.intf[i].rfifo),
+                               &(si->intf.intf[i].rframe),
+                               &(si->intf.intf[i].rcompr),
+                               &(si->intf.intf[i].rmultic),
+                               &(si->intf.intf[i].sbyte),
+                               &(si->intf.intf[i].spack),
+                               &(si->intf.intf[i].serrs),
+                               &(si->intf.intf[i].sdrop),
+                               &(si->intf.intf[i].sfifo),
+                               &(si->intf.intf[i].scollis),
+                               &(si->intf.intf[i].scarrier),
+                               &(si->intf.intf[i].scompr));
+
+                       if (nr == 17)   /* skip header & lines without stats */
+                       {
+                               if (++i >= MAXINTF-1)
+                                       break;
+                       }
+               }
+
+               si->intf.intf[i].name[0] = '\0'; /* set terminator for table */
+               si->intf.nrintf = i;
+
+               fclose(fp);
+       }
+
+       /*
+       ** IP version 4 statistics
+       */
+       if ( (fp = fopen("net/snmp", "r")) != NULL)
+       {
+               while ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
+               {
+                       nr = sscanf(linebuf,
+                        "%s   %lld %lld %lld %lld %lld %lld %lld %lld %lld "
+                        "%lld %lld %lld %lld %lld %lld %lld %lld %lld %lld "
+                        "%lld %lld %lld %lld %lld %lld %lld %lld %lld %lld "
+                        "%lld %lld %lld %lld %lld %lld %lld %lld %lld %lld "
+                        "%lld\n",
+                               nam,
+                               &cnts[0],  &cnts[1],  &cnts[2],  &cnts[3],
+                               &cnts[4],  &cnts[5],  &cnts[6],  &cnts[7],
+                               &cnts[8],  &cnts[9],  &cnts[10], &cnts[11],
+                               &cnts[12], &cnts[13], &cnts[14], &cnts[15],
+                               &cnts[16], &cnts[17], &cnts[18], &cnts[19],
+                               &cnts[20], &cnts[21], &cnts[22], &cnts[23],
+                               &cnts[24], &cnts[25], &cnts[26], &cnts[27],
+                               &cnts[28], &cnts[29], &cnts[30], &cnts[31],
+                               &cnts[32], &cnts[33], &cnts[34], &cnts[35],
+                               &cnts[36], &cnts[37], &cnts[38], &cnts[39]);
+
+                       if (nr < 2)             /* headerline ? --> skip */
+                               continue;
+
+                       if ( strcmp("Ip:", nam) == 0)
+                       {
+                               memcpy(&si->net.ipv4, cnts,
+                                               sizeof si->net.ipv4);
+                               continue;
+                       }
+       
+                       if ( strcmp("Icmp:", nam) == 0)
+                       {
+                               memcpy(&si->net.icmpv4, cnts,
+                                               sizeof si->net.icmpv4);
+                               continue;
+                       }
+       
+                       if ( strcmp("Tcp:", nam) == 0)
+                       {
+                               memcpy(&si->net.tcp, cnts,
+                                               sizeof si->net.tcp);
+                               continue;
+                       }
+       
+                       if ( strcmp("Udp:", nam) == 0)
+                       {
+                               memcpy(&si->net.udpv4, cnts,
+                                               sizeof si->net.udpv4);
+                               continue;
+                       }
+               }
+       
+               fclose(fp);
+       }
+
+       /*
+       ** IP version 6 statistics
+       */
+       memset(&ipv6_tmp,   0, sizeof ipv6_tmp);
+       memset(&icmpv6_tmp, 0, sizeof icmpv6_tmp);
+       memset(&udpv6_tmp,  0, sizeof udpv6_tmp);
+
+       if ( (fp = fopen("net/snmp6", "r")) != NULL)
+       {
+               count_t countval;
+               int     cur = 0;
+
+               /*
+               ** one name-value pair per line
+               */
+               while ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
+               {
+                       nr = sscanf(linebuf, "%s %lld", nam, &countval);
+
+                       if (nr < 2)             /* unexpected line ? --> skip */
+                               continue;
+
+                       if (strcmp(v6tab[cur].nam, nam) == 0)
+                       {
+                               *(v6tab[cur].val) = countval;
+                       }
+                       else
+                       {
+                               for (cur=0; cur < v6tab_entries; cur++)
+                                       if (strcmp(v6tab[cur].nam, nam) == 0)
+                                               break;
+
+                               if (cur < v6tab_entries) /* found ? */
+                                       *(v6tab[cur].val) = countval;
+                       }
+
+                       if (++cur >= v6tab_entries)
+                               cur = 0;
+               }
+
+               memcpy(&si->net.ipv6,   &ipv6_tmp,   sizeof ipv6_tmp);
+               memcpy(&si->net.icmpv6, &icmpv6_tmp, sizeof icmpv6_tmp);
+               memcpy(&si->net.udpv6,  &udpv6_tmp,  sizeof udpv6_tmp);
+
+               fclose(fp);
+       }
+
+       /*
+       ** check if extended partition-statistics are provided < kernel 2.6
+       */
+       if ( part_stats && (fp = fopen("partitions", "r")) != NULL)
+       {
+               char diskname[256];
+
+               i = 0;
+
+               while ( fgets(linebuf, sizeof(linebuf), fp) )
+               {
+                       nr = sscanf(linebuf,
+                             "%*d %*d %*d %255s %lld %*d %lld %*d "
+                             "%lld %*d %lld %*d %*d %lld %lld",
+                               diskname,
+                               &(si->dsk.dsk[i].nread),
+                               &(si->dsk.dsk[i].nrsect),
+                               &(si->dsk.dsk[i].nwrite),
+                               &(si->dsk.dsk[i].nwsect),
+                               &(si->dsk.dsk[i].io_ms),
+                               &(si->dsk.dsk[i].avque) );
+
+                       /*
+                       ** check if this line concerns the entire disk
+                       ** or just one of the partitions of a disk (to be
+                       ** skipped)
+                       */
+                       if (nr == 7)    /* full stats-line ? */
+                       {
+                               if ( isdisk(0, 0, diskname,
+                                                &(si->dsk.dsk[i]),
+                                                MAXDKNAM) != DSKTYPE)
+                                      continue;
+                       
+                               if (++i >= MAXDSK-1)
+                                       break;
+                       }
+               }
+
+               si->dsk.dsk[i].name[0] = '\0'; /* set terminator for table */
+               si->dsk.ndsk = i;
+
+               fclose(fp);
+
+               if (i == 0)
+                       part_stats = 0; /* do not try again for next cycles */
+       }
+
+
+       /*
+       ** check if disk-statistics are provided (kernel 2.6 onwards)
+       */
+       if ( (fp = fopen("diskstats", "r")) != NULL)
+       {
+               char            diskname[256];
+               struct perdsk   tmpdsk;
+
+               si->dsk.ndsk = 0;
+               si->dsk.nmdd = 0;
+               si->dsk.nlvm = 0;
+
+               while ( fgets(linebuf, sizeof(linebuf), fp) )
+               {
+                       nr = sscanf(linebuf,
+                             "%d %d %255s %lld %*d %lld %*d "
+                             "%lld %*d %lld %*d %*d %lld %lld",
+                               &major, &minor, diskname,
+                               &tmpdsk.nread,  &tmpdsk.nrsect,
+                               &tmpdsk.nwrite, &tmpdsk.nwsect,
+                               &tmpdsk.io_ms,  &tmpdsk.avque );
+
+                       /*
+                       ** check if this line concerns the entire disk
+                       ** or just one of the partitions of a disk (to be
+                       ** skipped)
+                       */
+                       if (nr == 9)    /* full stats-line ? */
+                       {
+                               switch ( isdisk(major, minor, diskname,
+                                                        &tmpdsk, MAXDKNAM) )
+                               {
+                                  case NONTYPE:
+                                      continue;
+
+                                  case DSKTYPE:
+                                       if (si->dsk.ndsk < MAXDSK-1)
+                                         si->dsk.dsk[si->dsk.ndsk++] = tmpdsk;
+                                       break;
+
+                                  case MDDTYPE:
+                                       if (si->dsk.nmdd < MAXMDD-1)
+                                         si->dsk.mdd[si->dsk.nmdd++] = tmpdsk;
+                                       break;
+
+                                  case LVMTYPE:
+                                       if (si->dsk.nlvm < MAXLVM-1)
+                                         si->dsk.lvm[si->dsk.nlvm++] = tmpdsk;
+                                       break;
+                               }
+                       }
+               }
+
+               /*
+               ** set terminator for table
+               */
+               si->dsk.dsk[si->dsk.ndsk].name[0] = '\0';
+               si->dsk.mdd[si->dsk.nmdd].name[0] = '\0';
+               si->dsk.lvm[si->dsk.nlvm].name[0] = '\0'; 
+
+               fclose(fp);
+       }
+
+       /*
+       ** get information about the shared memory statistics
+       */
+       if ( shmctl(0, SHM_INFO, (struct shmid_ds *)&shminfo) != -1)
+       {
+               si->mem.shmrss = shminfo.shm_rss;
+               si->mem.shmswp = shminfo.shm_swp;
+       }
+
+       /*
+       ** NFS server statistics
+       */
+       if ( (fp = fopen("net/rpc/nfsd", "r")) != NULL)
+       {
+               char    label[32];
+               count_t cnt[40];
+
+               /*
+               ** every line starts with a small label,
+               ** followed by upto 60 counters
+               */
+               while ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
+               {
+                       memset(cnt, 0, sizeof cnt);
+
+                       nr = sscanf(linebuf, "%31s %lld %lld %lld %lld %lld"
+                                                 "%lld %lld %lld %lld %lld"
+                                                 "%lld %lld %lld %lld %lld"
+                                                 "%lld %lld %lld %lld %lld"
+                                                 "%lld %lld %lld %lld %lld"
+                                                 "%lld %lld %lld %lld %lld"
+                                                 "%lld %lld %lld %lld %lld"
+                                                 "%lld %lld %lld %lld %lld",
+                                   label,
+                                   &cnt[0],  &cnt[1],  &cnt[2],  &cnt[3],
+                                   &cnt[4],  &cnt[5],  &cnt[6],  &cnt[7],
+                                   &cnt[8],  &cnt[9],  &cnt[10], &cnt[11],
+                                   &cnt[12], &cnt[13], &cnt[14], &cnt[15],
+                                   &cnt[16], &cnt[17], &cnt[18], &cnt[19],
+                                   &cnt[20], &cnt[21], &cnt[22], &cnt[23],
+                                   &cnt[24], &cnt[25], &cnt[26], &cnt[27],
+                                   &cnt[28], &cnt[29], &cnt[30], &cnt[31],
+                                   &cnt[32], &cnt[33], &cnt[34], &cnt[35],
+                                   &cnt[36], &cnt[37], &cnt[38], &cnt[39]);
+
+                       if (nr < 2)             // unexpected empty line ?
+                               continue;
+
+                       if (strcmp(label, "rc") == 0)
+                       {
+                               si->nfs.server.rchits = cnt[0];
+                               si->nfs.server.rcmiss = cnt[1];
+                               si->nfs.server.rcnoca = cnt[2];
+
+                               continue;
+                       }
+
+                       if (strcmp(label, "io") == 0)
+                       {
+                               si->nfs.server.nrbytes = cnt[0];
+                               si->nfs.server.nwbytes = cnt[1];
+
+                               continue;
+                       }
+
+                       if (strcmp(label, "net") == 0)
+                       {
+                               si->nfs.server.netcnt    = cnt[0];
+                               si->nfs.server.netudpcnt = cnt[1];
+                               si->nfs.server.nettcpcnt = cnt[2];
+                               si->nfs.server.nettcpcon = cnt[3];
+
+                               continue;
+                       }
+
+                       if (strcmp(label, "rpc") == 0)
+                       {
+                               si->nfs.server.rpccnt    = cnt[0];
+                               si->nfs.server.rpcbadfmt = cnt[1];
+                               si->nfs.server.rpcbadaut = cnt[2];
+                               si->nfs.server.rpcbadcln = cnt[3];
+
+                               continue;
+                       }
+                       //
+                       // first counter behind 'proc..' is number of
+                       // counters that follow
+                       if (strcmp(label, "proc2") == 0)
+                       {
+                               si->nfs.server.rpcread  += cnt[7]; // offset+1
+                               si->nfs.server.rpcwrite += cnt[9]; // offset+1
+                               continue;
+                       }
+                       if (strcmp(label, "proc3") == 0)
+                       {
+                               si->nfs.server.rpcread  += cnt[7]; // offset+1
+                               si->nfs.server.rpcwrite += cnt[8]; // offset+1
+                               continue;
+                       }
+                       if (strcmp(label, "proc4ops") == 0)
+                       {
+                               si->nfs.server.rpcread  += cnt[26]; // offset+1
+                               si->nfs.server.rpcwrite += cnt[39]; // offset+1
+                               continue;
+                       }
+               }
+
+               fclose(fp);
+       }
+
+       /*
+       ** NFS client statistics
+       */
+       if ( (fp = fopen("net/rpc/nfs", "r")) != NULL)
+       {
+               char    label[32];
+               count_t cnt[10];
+
+               /*
+               ** every line starts with a small label,
+               ** followed by counters
+               */
+               while ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
+               {
+                       memset(cnt, 0, sizeof cnt);
+
+                       nr = sscanf(linebuf, "%31s %lld %lld %lld %lld %lld"
+                                                 "%lld %lld %lld %lld %lld",
+                                   label,
+                                   &cnt[0], &cnt[1], &cnt[2], &cnt[3],
+                                   &cnt[4], &cnt[5], &cnt[6], &cnt[7],
+                                   &cnt[8], &cnt[9]);
+
+                       if (nr < 2)             // unexpected empty line ?
+                               continue;
+
+                       if (strcmp(label, "rpc") == 0)
+                       {
+                               si->nfs.client.rpccnt        = cnt[0];
+                               si->nfs.client.rpcretrans    = cnt[1];
+                               si->nfs.client.rpcautrefresh = cnt[2];
+                               continue;
+                       }
+
+                       // first counter behind 'proc..' is number of
+                       // counters that follow
+                       if (strcmp(label, "proc2") == 0)
+                       {
+                               si->nfs.client.rpcread  += cnt[7]; // offset+1
+                               si->nfs.client.rpcwrite += cnt[9]; // offset+1
+                               continue;
+                       }
+                       if (strcmp(label, "proc3") == 0)
+                       {
+                               si->nfs.client.rpcread  += cnt[7]; // offset+1
+                               si->nfs.client.rpcwrite += cnt[8]; // offset+1
+                               continue;
+                       }
+                       if (strcmp(label, "proc4") == 0)
+                       {
+                               si->nfs.client.rpcread  += cnt[2]; // offset+1
+                               si->nfs.client.rpcwrite += cnt[3]; // offset+1
+                               continue;
+                       }
+               }
+
+               fclose(fp);
+       }
+
+       /*
+       ** NFS client: per-mount statistics
+       */
+       regainrootprivs();
+
+       if ( (fp = fopen("self/mountstats", "r")) != NULL)
+       {
+               char    mountdev[128], fstype[32], label[32];
+                count_t        cnt[8];
+
+               i = 0;
+
+               while ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
+               {
+                       // if 'device' line, just remember the mounted device
+                       if (sscanf(linebuf,
+                               "device %127s mounted on %*s with fstype %31s",
+                               mountdev, fstype) == 2)
+                       {
+                               continue;
+                       }
+
+                       if (memcmp(fstype, "nfs", 3) != 0)
+                               continue;
+
+                       // this is line with NFS client stats
+                       nr = sscanf(linebuf,
+                                "%31s %lld %lld %lld %lld %lld %lld %lld %lld",
+                               label, &cnt[0], &cnt[1], &cnt[2], &cnt[3],
+                                      &cnt[4], &cnt[5], &cnt[6], &cnt[7]);
+
+                       if (nr >= 2 )
+                       {
+                               if (strcmp(label, "age:") == 0)
+                               {
+                                   strcpy(si->nfs.nfsmounts.nfsmnt[i].mountdev,
+                                          mountdev);
+
+                                   si->nfs.nfsmounts.nfsmnt[i].age = cnt[0];
+                               }
+
+                               if (strcmp(label, "bytes:") == 0)
+                               {
+                                   si->nfs.nfsmounts.nfsmnt[i].bytesread =
+                                                                       cnt[0];
+                                   si->nfs.nfsmounts.nfsmnt[i].byteswrite =
+                                                                       cnt[1];
+                                   si->nfs.nfsmounts.nfsmnt[i].bytesdread =
+                                                                       cnt[2];
+                                   si->nfs.nfsmounts.nfsmnt[i].bytesdwrite =
+                                                                       cnt[3];
+                                   si->nfs.nfsmounts.nfsmnt[i].bytestotread =
+                                                                       cnt[4];
+                                   si->nfs.nfsmounts.nfsmnt[i].bytestotwrite =
+                                                                       cnt[5];
+                                   si->nfs.nfsmounts.nfsmnt[i].pagesmread =
+                                                                       cnt[6];
+                                   si->nfs.nfsmounts.nfsmnt[i].pagesmwrite =
+                                                                       cnt[7];
+
+                                   if (++i >= MAXNFSMOUNT-1)
+                                       break;
+                               }
+                       }
+               }
+
+               si->nfs.nfsmounts.nrmounts = i;
+
+               fclose(fp);
+       }
+
+       if (! droprootprivs())
+               cleanstop(42);
+
+       /*
+       ** Container statistics (if any)
+       */
+       if ( (fp = fopen("user_beancounters", "r")) != NULL)
+       {
+               unsigned long   ctid;
+               char            label[32];
+               count_t         cnt;
+
+               i = -1;
+
+               /*
+               ** lines introducing a new container have an extra
+               ** field with the container id at the beginning.
+               */
+               while ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
+               {
+                       nr = sscanf(linebuf, "%lu: %31s %lld",
+                                               &ctid, label, &cnt);
+
+                       if (nr == 3)            // new container ?
+                       {
+                               if (++i >= MAXCONTAINER)
+                                       break;
+
+                               si->cfs.cont[i].ctid = ctid;
+                       }
+                       else
+                       {
+                               nr = sscanf(linebuf, "%31s %lld", label, &cnt);
+
+                               if (nr != 2)
+                                       continue;
+                       }
+
+                       if (i == -1)    // no container defined yet
+                               continue;
+
+                       if (strcmp(label, "numproc") == 0)
+                       {
+                               si->cfs.cont[i].numproc = cnt;
+                               continue;
+                       }
+
+                       if (strcmp(label, "physpages") == 0)
+                       {
+                               si->cfs.cont[i].physpages = cnt;
+                               continue;
+                       }
+               }
+
+               fclose(fp);
+
+               si->cfs.nrcontainer = i+1;
+
+               if ( (fp = fopen("vz/vestat", "r")) != NULL)
+               {
+                       unsigned long   ctid;
+                       count_t         cnt[8];
+
+                       /*
+                       ** relevant lines start with container id
+                       */
+                       while ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
+                       {
+                               nr = sscanf(linebuf, "%lu  %lld %lld %lld %lld"
+                                                    "%lld %lld %lld %lld %lld",
+                                       &ctid,
+                                       &cnt[0], &cnt[1], &cnt[2], &cnt[3],
+                                       &cnt[4], &cnt[5], &cnt[6], &cnt[7],
+                                       &cnt[8]);
+
+                               if (nr < 9)     // irrelevant contents
+                                       continue;
+
+                               // relevant stats: search for containerid
+                               for (i=0; i < si->cfs.nrcontainer; i++)
+                               {
+                                       if (si->cfs.cont[i].ctid == ctid)
+                                               break;
+                               }
+
+                               if (i >= si->cfs.nrcontainer)
+                                       continue;       // container not found
+
+                               si->cfs.cont[i].user   = cnt[0];
+                               si->cfs.cont[i].nice   = cnt[1];
+                               si->cfs.cont[i].system = cnt[2];
+                               si->cfs.cont[i].uptime = cnt[3];
+                       }
+
+                       fclose(fp);
+               }
+       }
+
+       /*
+       ** return to original directory
+       */
+       if ( chdir(origdir) == -1)
+               cleanstop(53);
+
+       /*
+       ** fetch application-specific counters
+       */
+#if    HTTPSTATS
+       if ( wwwvalid)
+               wwwvalid = getwwwstat(80, &(si->www));
+#endif
+}
+
+/*
+** set of subroutines to determine which disks should be monitored
+** and to translate name strings into (shorter) name strings
+*/
+static void
+nullmodname(unsigned int major, unsigned int minor,
+               char *curname, struct perdsk *px, int maxlen)
+{
+       strncpy(px->name, curname, maxlen-1);
+       *(px->name+maxlen-1) = 0;
+}
+
+static void
+abbrevname1(unsigned int major, unsigned int minor,
+               char *curname, struct perdsk *px, int maxlen)
+{
+       char    cutype[128];
+       int     hostnum, busnum, targetnum, lunnum;
+
+       sscanf(curname, "%[^/]/host%d/bus%d/target%d/lun%d",
+                       cutype, &hostnum, &busnum, &targetnum, &lunnum);
+
+       snprintf(px->name, maxlen, "%c-h%db%dt%d", 
+                       cutype[0], hostnum, busnum, targetnum);
+}
+
+/*
+** recognize LVM logical volumes
+*/
+#define        NUMDMHASH       64
+#define        DMHASH(x,y)     (((x)+(y))%NUMDMHASH)   
+#define        MAPDIR          "/dev/mapper"
+
+struct devmap {
+       unsigned int    major;
+       unsigned int    minor;
+       char            name[MAXDKNAM];
+       struct devmap   *next;
+};
+
+static void
+lvmmapname(unsigned int major, unsigned int minor,
+               char *curname, struct perdsk *px, int maxlen)
+{
+       static int              firstcall = 1;
+       static struct devmap    *devmaps[NUMDMHASH], *dmp;
+       int                     hashix;
+
+       /*
+       ** setup a list of major-minor numbers of dm-devices with their
+       ** corresponding name
+       */
+       if (firstcall)
+       {
+               DIR             *dirp;
+               struct dirent   *dentry;
+               struct stat     statbuf;
+               char            path[64];
+
+               if ( (dirp = opendir(MAPDIR)) )
+               {
+                       /*
+                       ** read every directory-entry and search for
+                       ** block devices
+                       */
+                       while ( (dentry = readdir(dirp)) )
+                       {
+                               snprintf(path, sizeof path, "%s/%s", 
+                                               MAPDIR, dentry->d_name);
+
+                               if ( stat(path, &statbuf) == -1 )
+                                       continue;
+
+                               if ( ! S_ISBLK(statbuf.st_mode) )
+                                       continue;
+                               /*
+                               ** allocate struct to store name
+                               */
+                               if ( !(dmp = malloc(sizeof (struct devmap))))
+                                       continue;
+
+                               /*
+                               ** store info in hash list
+                               */
+                               strncpy(dmp->name, dentry->d_name, MAXDKNAM);
+                               dmp->name[MAXDKNAM-1] = 0;
+                               dmp->major      = major(statbuf.st_rdev);
+                               dmp->minor      = minor(statbuf.st_rdev);
+
+                               hashix = DMHASH(dmp->major, dmp->minor);
+
+                               dmp->next       = devmaps[hashix];
+
+                               devmaps[hashix] = dmp;
+                       }
+
+                       closedir(dirp);
+               }
+
+               firstcall = 0;
+       }
+
+       /*
+       ** find info in hash list
+       */
+       hashix  = DMHASH(major, minor);
+       dmp     = devmaps[hashix];
+
+       while (dmp)
+       {
+               if (dmp->major == major && dmp->minor == minor)
+               {
+                       /*
+                       ** info found in hash list; fill proper name
+                       */
+                       strncpy(px->name, dmp->name, maxlen-1);
+                       *(px->name+maxlen-1) = 0;
+                       return;
+               }
+
+               dmp = dmp->next;
+       }
+
+       /*
+       ** info not found in hash list; fill original name
+       */
+       strncpy(px->name, curname, maxlen-1);
+       *(px->name+maxlen-1) = 0;
+}
+
+/*
+** this table is used in the function isdisk()
+**
+** table contains the names (in regexp format) of disks
+** to be recognized, together with a function to modify
+** the name-strings (i.e. to abbreviate long strings);
+** some frequently found names (like 'loop' and 'ram')
+** are also recognized to skip them as fast as possible
+*/
+static struct {
+       char    *regexp;
+       regex_t compreg;
+       void    (*modname)(unsigned int, unsigned int,
+                               char *, struct perdsk *, int);
+       int     retval;
+} validdisk[] = {
+       { "^ram[0-9][0-9]*$",                   {0},  (void *)0,   NONTYPE, },
+       { "^loop[0-9][0-9]*$",                  {0},  (void *)0,   NONTYPE, },
+       { "^sd[a-z][a-z]*$",                    {0},  nullmodname, DSKTYPE, },
+       { "^dm-[0-9][0-9]*$",                   {0},  lvmmapname,  LVMTYPE, },
+       { "^md[0-9][0-9]*$",                    {0},  nullmodname, MDDTYPE, },
+       { "^vd[a-z][a-z]*$",                    {0},  nullmodname, DSKTYPE, },
+       { "^hd[a-z]$",                          {0},  nullmodname, DSKTYPE, },
+       { "^rd/c[0-9][0-9]*d[0-9][0-9]*$",      {0},  nullmodname, DSKTYPE, },
+       { "^cciss/c[0-9][0-9]*d[0-9][0-9]*$",   {0},  nullmodname, DSKTYPE, },
+       { "^fio[a-z][a-z]*$",                   {0},  nullmodname, DSKTYPE, },
+       { "/host.*/bus.*/target.*/lun.*/disc",  {0},  abbrevname1, DSKTYPE, },
+       { "^xvd[a-z][a-z]*[0-9]*$",             {0},  nullmodname, DSKTYPE, },
+       { "^dasd[a-z][a-z]*$",                  {0},  nullmodname, DSKTYPE, },
+       { "^mmcblk[0-9][0-9]*$",                {0},  nullmodname, DSKTYPE, },
+       { "^emcpower[a-z][a-z]*$",              {0},  nullmodname, DSKTYPE, },
+};
+
+static int
+isdisk(unsigned int major, unsigned int minor,
+           char *curname, struct perdsk *px, int maxlen)
+{
+       static int      firstcall = 1;
+       register int    i;
+
+       if (firstcall)          /* compile the regular expressions */
+       {
+               for (i=0; i < sizeof validdisk/sizeof validdisk[0]; i++)
+                       regcomp(&validdisk[i].compreg, validdisk[i].regexp,
+                                                               REG_NOSUB);
+               firstcall = 0;
+       }
+
+       /*
+       ** try to recognize one of the compiled regular expressions
+       */
+       for (i=0; i < sizeof validdisk/sizeof validdisk[0]; i++)
+       {
+               if (regexec(&validdisk[i].compreg, curname, 0, NULL, 0) == 0)
+               {
+                       /*
+                       ** name-string recognized; modify name-string
+                       */
+                       if (validdisk[i].retval != NONTYPE)
+                               (*validdisk[i].modname)(major, minor,
+                                               curname, px, maxlen);
+
+                       return validdisk[i].retval;
+               }
+       }
+
+       return NONTYPE;
+}
+
+/*
+** LINUX SPECIFIC:
+** Determine boot-time of this system (as number of jiffies since 1-1-1970).
+*/
+unsigned long long
+getbootlinux(long hertz)
+{
+       int                     cpid;
+       char                    tmpbuf[1280];
+       FILE                    *fp;
+       unsigned long           startticks;
+       unsigned long long      bootjiffies = 0;
+       struct timespec         ts;
+
+       /*
+       ** dirty hack to get the boottime, since the
+       ** Linux 2.6 kernel (2.6.5) does not return a proper
+       ** boottime-value with the times() system call   :-(
+       */
+       if ( (cpid = fork()) == 0 )
+       {
+               /*
+               ** child just waiting to be killed by parent
+               */
+               pause();
+       }
+       else
+       {
+               /*
+               ** parent determines start-time (in jiffies since boot) 
+               ** of the child and calculates the boottime in jiffies
+               ** since 1-1-1970
+               */
+               (void) clock_gettime(CLOCK_REALTIME, &ts);      // get current
+               bootjiffies = 1LL * ts.tv_sec  * hertz +
+                             1LL * ts.tv_nsec * hertz / 1000000000LL;
+
+               snprintf(tmpbuf, sizeof tmpbuf, "/proc/%d/stat", cpid);
+
+               if ( (fp = fopen(tmpbuf, "r")) != NULL)
+               {
+                       if ( fscanf(fp, "%*d (%*[^)]) %*c %*d %*d %*d %*d "
+                                       "%*d %*d %*d %*d %*d %*d %*d %*d "
+                                       "%*d %*d %*d %*d %*d %*d %lu",
+                                       &startticks) == 1)
+                       {
+                               bootjiffies -= startticks;
+                       }
+
+                       fclose(fp);
+               }
+
+               /*
+               ** kill the child and get rid of the zombie
+               */
+               kill(cpid, SIGKILL);
+               (void) wait((int *)0);
+       }
+
+       return bootjiffies;
+}
+
+#if    HTTPSTATS
+/*
+** retrieve statistics from local HTTP daemons
+** via http://localhost/server-status?auto
+*/
+int
+getwwwstat(unsigned short port, struct wwwstat *wp)
+{
+       int                     sockfd, tobefound;
+       FILE                    *sockfp;
+       struct sockaddr_in      sockname;
+       char                    linebuf[4096];
+       char                    label[512];
+       long long               value;
+
+       memset(wp, 0, sizeof *wp);
+
+       /*
+       ** allocate a socket and connect to the local HTTP daemon
+       */
+       if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
+               return 0;
+
+       sockname.sin_family             = AF_INET;
+       sockname.sin_addr.s_addr        = htonl(INADDR_LOOPBACK);
+       sockname.sin_port               = htons(port);
+
+       if ( connect(sockfd, (struct sockaddr *) &sockname,
+                                               sizeof sockname) == -1)
+       {
+               close(sockfd);
+               return 0;
+       }
+
+       /*
+       ** write a GET-request for /server-status
+       */
+       if ( write(sockfd, HTTPREQ, sizeof HTTPREQ) < sizeof HTTPREQ)
+       {
+               close(sockfd);
+               return 0;
+       }
+
+       /*
+       ** remap socket descriptor to a stream to allow stdio calls
+       */
+       sockfp = fdopen(sockfd, "r+");
+
+       /*
+       ** read response line by line
+       */
+       tobefound = 5;          /* number of values to be searched */
+
+       while ( fgets(linebuf, sizeof linebuf, sockfp) && tobefound)
+       {
+               /*
+               ** handle line containing status code
+               */
+               if ( strncmp(linebuf, "HTTP/", 5) == 0)
+               {
+                       sscanf(linebuf, "%511s %lld %*s\n", label, &value);
+
+                       if (value != 200)       /* HTTP-request okay? */
+                       {
+                               fclose(sockfp);
+                               close(sockfd);
+                               return 0;
+                       }
+
+                       continue;
+               }
+
+               /*
+               ** decode line and search for the required counters
+               */
+               if (sscanf(linebuf, "%511[^:]: %lld\n", label, &value) == 2)
+               {
+                       if ( strcmp(label, "Total Accesses") == 0)
+                       {
+                               wp->accesses = value;
+                               tobefound--;
+                       }
+
+                       if ( strcmp(label, "Total kBytes") == 0)
+                       {
+                               wp->totkbytes = value;
+                               tobefound--;
+                       }
+
+                       if ( strcmp(label, "Uptime") == 0)
+                       {
+                               wp->uptime = value;
+                               tobefound--;
+                       }
+
+                       if ( strcmp(label, "BusyWorkers") == 0)
+                       {
+                               wp->bworkers = value;
+                               tobefound--;
+                       }
+
+                       if ( strcmp(label, "IdleWorkers") == 0)
+                       {
+                               wp->iworkers = value;
+                               tobefound--;
+                       }
+               }
+       }
+
+       fclose(sockfp);
+       close(sockfd);
+
+       return 1;
+}
+#endif
diff --git a/sources/photosyst.h b/sources/photosyst.h
new file mode 100644 (file)
index 0000000..a7c69b4
--- /dev/null
@@ -0,0 +1,299 @@
+/*
+** ATOP - System & Process Monitor
+**
+** The program 'atop' offers the possibility to view the activity of
+** the system on system-level as well as process-level.
+**
+** Include-file describing system-level counters maintained.
+** ================================================================
+** Author:      Gerlof Langeveld
+** E-mail:      gerlof.langeveld@atoptool.nl
+** Date:        November 1996
+** LINUX-port:  June 2000
+**
+** This program is free software; you can redistribute it and/or modify it
+** under the terms of the GNU General Public License as published by the
+** Free Software Foundation; either version 2, or (at your option) any
+** later version.
+**
+** This program is distributed in the hope that it will be useful, but
+** WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+** See the GNU General Public License for more details.
+*/
+#include "netstats.h"
+
+#define        MAXCPU          2048
+#define        MAXDSK          1024
+#define        MAXLVM          2048
+#define        MAXMDD          256
+#define        MAXINTF         128
+#define        MAXCONTAINER    128
+#define        MAXNFSMOUNT     64
+
+#define        MAXDKNAM        32
+
+/************************************************************************/
+
+struct memstat {
+       count_t physmem;        // number of physical pages
+       count_t freemem;        // number of free     pages
+       count_t buffermem;      // number of buffer   pages
+       count_t slabmem;        // number of slab     pages
+       count_t cachemem;       // number of cache    pages
+       count_t cachedrt;       // number of cache    pages (dirty)
+
+       count_t totswap;        // number of pages in swap
+       count_t freeswap;       // number of free swap pages
+
+       count_t pgscans;        // number of page scans
+       count_t pgsteal;        // number of page steals
+       count_t allocstall;     // try to free pages forced
+       count_t swouts;         // number of pages swapped out
+       count_t swins;          // number of pages swapped in
+
+       count_t commitlim;      // commit limit in pages
+       count_t committed;      // number of reserved pages
+
+       count_t shmem;          // tot shmem incl. tmpfs (pages)
+       count_t shmrss;         // resident shared memory (pages)
+       count_t shmswp;         // swapped shared memory (pages)
+
+       count_t slabreclaim;    // reclaimable slab (pages)
+
+       count_t tothugepage;    // total huge pages (huge pages)
+       count_t freehugepage;   // free  huge pages (huge pages)
+       count_t hugepagesz;     // huge page size (bytes)
+
+       count_t vmwballoon;     // vmware claimed balloon pages
+
+       count_t cfuture[8];     // reserved for future use
+};
+
+/************************************************************************/
+
+struct netstat {
+       struct ipv4_stats       ipv4;
+       struct icmpv4_stats     icmpv4;
+       struct udpv4_stats      udpv4;
+
+       struct ipv6_stats       ipv6;
+       struct icmpv6_stats     icmpv6;
+       struct udpv6_stats      udpv6;
+
+       struct tcp_stats        tcp;
+};
+
+/************************************************************************/
+
+struct freqcnt {
+        count_t maxfreq;/* frequency in MHz                    */
+        count_t cnt;    /* number of clock ticks times state   */
+        count_t ticks;  /* number of total clock ticks         */
+                        /* if zero, cnt is actul freq          */
+};
+
+struct percpu {
+       int             cpunr;
+       count_t         stime;  /* system  time in clock ticks          */
+       count_t         utime;  /* user    time in clock ticks          */
+       count_t         ntime;  /* nice    time in clock ticks          */
+       count_t         itime;  /* idle    time in clock ticks          */
+       count_t         wtime;  /* iowait  time in clock ticks          */
+       count_t         Itime;  /* irq     time in clock ticks          */
+       count_t         Stime;  /* softirq time in clock ticks          */
+       count_t         steal;  /* steal   time in clock ticks          */
+       count_t         guest;  /* guest   time in clock ticks          */
+        struct freqcnt freqcnt;/* frequency scaling info               */
+       count_t         cfuture[4];     /* reserved for future use      */
+};
+
+struct cpustat {
+       count_t nrcpu;  /* number of cpu's                      */
+       count_t devint; /* number of device interrupts          */
+       count_t csw;    /* number of context switches           */
+       count_t nprocs; /* number of processes started          */
+       float   lavg1;  /* load average last    minute          */
+       float   lavg5;  /* load average last  5 minutes         */
+       float   lavg15; /* load average last 15 minutes         */
+       count_t cfuture[4];     /* reserved for future use      */
+
+       struct percpu   all;
+       struct percpu   cpu[MAXCPU];
+};
+
+/************************************************************************/
+
+struct perdsk {
+        char   name[MAXDKNAM]; /* empty string for last        */
+        count_t        nread;  /* number of read  transfers            */
+        count_t        nrsect; /* number of sectors read               */
+        count_t        nwrite; /* number of write transfers            */
+        count_t        nwsect; /* number of sectors written            */
+        count_t        io_ms;  /* number of millisecs spent for I/O    */
+        count_t        avque;  /* average queue length                 */
+       count_t cfuture[4];     /* reserved for future use      */
+};
+
+struct dskstat {
+       int             ndsk;   /* number of physical disks     */
+       int             nmdd;   /* number of md volumes         */
+       int             nlvm;   /* number of logical volumes    */
+       struct perdsk   dsk[MAXDSK];
+       struct perdsk   mdd[MAXMDD];
+       struct perdsk   lvm[MAXLVM];
+};
+
+/************************************************************************/
+
+struct perintf {
+        char   name[16];       /* empty string for last        */
+
+        count_t        rbyte;  /* number of read bytes                 */
+        count_t        rpack;  /* number of read packets               */
+       count_t rerrs;  /* receive errors                       */
+       count_t rdrop;  /* receive drops                        */
+       count_t rfifo;  /* receive fifo                         */
+       count_t rframe; /* receive framing errors               */
+       count_t rcompr; /* receive compressed                   */
+       count_t rmultic;/* receive multicast                    */
+       count_t rfuture[4];     /* reserved for future use      */
+
+        count_t        sbyte;  /* number of written bytes              */
+        count_t        spack;  /* number of written packets            */
+       count_t serrs;  /* transmit errors                      */
+       count_t sdrop;  /* transmit drops                       */
+       count_t sfifo;  /* transmit fifo                        */
+       count_t scollis;/* collisions                           */
+       count_t scarrier;/* transmit carrier                    */
+       count_t scompr; /* transmit compressed                  */
+       count_t sfuture[4];     /* reserved for future use      */
+
+       char    type;   /* interface type ('e'/'w'/'?')         */
+       long    speed;  /* interface speed in megabits/second   */
+       long    speedp; /* previous interface speed             */
+       char    duplex; /* full duplex (boolean)                */
+       count_t cfuture[4];     /* reserved for future use      */
+};
+
+struct intfstat {
+       int             nrintf;
+       struct perintf  intf[MAXINTF];
+};
+
+/************************************************************************/
+
+struct  pernfsmount {
+        char   mountdev[128];          /* mountdevice                  */
+        count_t        age;                    /* number of seconds mounted    */
+       
+       count_t bytesread;              /* via normal reads             */
+       count_t byteswrite;             /* via normal writes            */
+       count_t bytesdread;             /* via direct reads             */
+       count_t bytesdwrite;            /* via direct writes            */
+       count_t bytestotread;           /* via reads                    */
+       count_t bytestotwrite;          /* via writes                   */
+       count_t pagesmread;             /* via mmap  reads              */
+       count_t pagesmwrite;            /* via mmap  writes             */
+
+       count_t future[8];
+};
+
+struct nfsstat {
+       struct {
+               count_t netcnt;
+               count_t netudpcnt;
+               count_t nettcpcnt;
+               count_t nettcpcon;
+
+               count_t rpccnt;
+               count_t rpcbadfmt;
+               count_t rpcbadaut;
+               count_t rpcbadcln;
+
+               count_t rpcread;
+               count_t rpcwrite;
+
+               count_t rchits;         /* repcache hits        */
+               count_t rcmiss;         /* repcache misses      */
+               count_t rcnoca;         /* uncached requests    */
+
+               count_t nrbytes;        /* read bytes           */
+               count_t nwbytes;        /* written bytes        */
+
+               count_t future[8];
+       } server;
+
+       struct {
+               count_t rpccnt;
+               count_t rpcretrans;
+               count_t rpcautrefresh;
+
+               count_t rpcread;
+               count_t rpcwrite;
+
+               count_t future[8];
+       } client;
+
+       struct {
+               int                     nrmounts;
+                       struct pernfsmount      nfsmnt[MAXNFSMOUNT];
+       } nfsmounts;
+};
+
+/************************************************************************/
+
+struct  percontainer {
+        unsigned long  ctid;           /* container id                 */
+        unsigned long  numproc;        /* number of processes          */
+
+        count_t system;        /* */
+        count_t user;                  /* */
+        count_t nice;                  /* */
+        count_t uptime;        /* */
+
+        count_t physpages;     /* */
+};
+
+struct contstat {
+        int                    nrcontainer;
+        struct percontainer    cont[MAXCONTAINER];
+};
+
+/************************************************************************/
+/*
+** experimental stuff for access to local HTTP daemons
+*/
+#define        HTTPREQ "GET /server-status?auto HTTP/1.1\nHost: localhost\n\n"
+
+struct wwwstat {
+       count_t accesses;       /* total number of HTTP-requests        */
+       count_t totkbytes;      /* total kbytes transfer for HTTP-req   */
+       count_t uptime;         /* number of seconds since startup      */
+       int     bworkers;       /* number of busy httpd-daemons         */
+       int     iworkers;       /* number of idle httpd-daemons         */
+};
+
+#if    HTTPSTATS
+int    getwwwstat(unsigned short, struct wwwstat *);
+#endif
+/************************************************************************/
+
+struct sstat {
+       struct cpustat  cpu;
+       struct memstat  mem;
+       struct netstat  net;
+       struct intfstat intf;
+       struct dskstat  dsk;
+       struct nfsstat  nfs;
+       struct contstat cfs;
+
+       struct wwwstat  www;
+};
+
+/*
+** prototypes
+*/
+void   photosyst (struct sstat *);
+void   deviatsyst(struct sstat *, struct sstat *, struct sstat *, long);
+void   totalsyst (char,           struct sstat *, struct sstat *);
diff --git a/sources/procdbase.c b/sources/procdbase.c
new file mode 100644 (file)
index 0000000..9cab347
--- /dev/null
@@ -0,0 +1,364 @@
+/*
+** ATOP - System & Process Monitor 
+** 
+** The program 'atop' offers the possibility to view the activity of
+** the system on system-level as well as process-level.
+** 
+** This source-file contains all functions required to manipulate the
+** process-database. This database is implemented as a linked list of
+** all running processes, needed to remember the process-counters from
+** the previous sample.
+** ==========================================================================
+** Author:      Gerlof Langeveld
+** E-mail:      gerlof.langeveld@atoptool.nl
+** Date:        November 1996
+** LINUX-port:  June 2000
+** --------------------------------------------------------------------------
+** Copyright (C) 2000-2012 Gerlof Langeveld
+**
+** This program is free software; you can redistribute it and/or modify it
+** under the terms of the GNU General Public License as published by the
+** Free Software Foundation; either version 2, or (at your option) any
+** later version.
+**
+** This program is distributed in the hope that it will be useful, but
+** WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+** See the GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+** --------------------------------------------------------------------------
+**
+** $Log: procdbase.c,v $
+** Revision 1.8  2010/04/23 12:19:35  gerlof
+** Modified mail-address in header.
+**
+** Revision 1.7  2007/11/05 12:12:31  gerlof
+** Match processes not only on pid, but also on start time.
+**
+** Revision 1.6  2005/10/21 09:50:19  gerlof
+** Per-user accumulation of resource consumption.
+**
+** Revision 1.5  2003/07/07 09:26:40  gerlof
+** Cleanup code (-Wall proof).
+**
+** Revision 1.4  2002/10/03 11:19:58  gerlof
+** Modify (effective) uid/gid to real uid/gid.
+**
+** Revision 1.3  2002/07/24 11:13:50  gerlof
+** Changed to ease porting to other UNIX-platforms.
+**
+** Revision 1.2  2002/07/08 09:29:07  root
+** Call to calloc i.s.o. malloc + memset.
+**
+** Revision 1.1  2001/10/02 10:43:33  gerlof
+** Initial revision
+**
+*/
+
+static const char rcsid[] = "$Id: procdbase.c,v 1.8 2010/04/23 12:19:35 gerlof Exp $";
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <malloc.h>
+
+#include "atop.h"
+#include "photoproc.h"
+
+/*****************************************************************************/
+#define        NPHASH  256             /* number of hash queues for process dbase   */
+                               /* MUST be a power of 2 !!!                  */
+
+       /* hash buckets for getting process-info     */
+       /* for a given PID                           */
+static struct pinfo    *phash[NPHASH];
+
+       /* cyclic list of all processes, to detect   */
+       /* which processes were not referred         */
+static struct pinfo    presidue;
+/*****************************************************************************/
+
+
+/*
+** search process database for the given PID
+*/
+int
+pdb_gettask(int pid, char isproc, time_t btime, struct pinfo **pinfopp)
+{
+       register struct pinfo   *pp;
+
+       pp = phash[pid&(NPHASH-1)];     /* get proper hash bucket       */
+
+       /*
+       ** scan all entries in hash Q
+       */
+       while (pp)
+       {
+               /*
+               ** if this is required PID, unchain it from the RESIDUE-list
+               ** and return info
+               */
+               if (pp->tstat.gen.pid    == pid    && 
+                   pp->tstat.gen.isproc == isproc   )
+               {
+                       int diff = pp->tstat.gen.btime - btime;
+
+                       /*
+                       ** with longer intervals, the same PID might be
+                       ** found more than once, so also check the start
+                       ** time of the task
+                       */
+                       if (diff > 1 || diff < -1)
+                       {
+                               pp = pp->phnext;
+                               continue;
+                       }
+
+                       if (pp->prnext)         /* if part of RESIDUE-list   */
+                       {
+                               (pp->prnext)->prprev = pp->prprev; /* unchain */
+                               (pp->prprev)->prnext = pp->prnext;
+                       }
+
+                       pp->prnext = NULL;
+                       pp->prprev = NULL;
+
+                       *pinfopp = pp;
+
+                       return 1;
+               }
+
+               pp = pp->phnext;
+       }
+
+       /*
+       ** end of list; PID not found
+       */
+       return 0;
+}
+
+/*
+** add new process-info structure to the process database
+*/
+void
+pdb_addtask(int pid, struct pinfo *pinfop)
+{
+       register int i  = pid&(NPHASH-1);
+
+       pinfop->phnext  = phash[i];
+       phash[i]        = pinfop;
+}
+
+/*
+** delete a process from the process database
+*/
+int
+pdb_deltask(int pid, char isproc)
+{
+       register struct pinfo   *pp, *ppp;
+
+       pp = phash[pid&(NPHASH-1)];     /* get proper hash bucket       */
+
+       /*
+       ** check first entry in hash Q
+       */
+       if (pp->tstat.gen.pid == pid && pp->tstat.gen.isproc == isproc)
+       {
+               phash[pid&(NPHASH-1)] = pp->phnext;
+
+               if ( pp->prnext )       /* still part of RESIDUE-list ? */
+               {
+                       (pp->prprev)->prnext = pp->prnext;
+                       (pp->prnext)->prprev = pp->prprev;      /* unchain */
+               }
+
+               /*
+               ** remove process-info from process-database
+               */
+               free(pp);
+
+               return 1;
+       }
+
+       /*
+       ** scan other entries of hash-list
+       */
+       ppp     = pp;
+       pp      = pp->phnext;
+
+       while (pp)
+       {
+               /*
+               ** if this is wanted PID, unchain it from the RESIDUE-list
+               ** and return info
+               */
+               if (pp->tstat.gen.pid == pid && pp->tstat.gen.isproc == isproc)
+               {
+                       ppp->phnext = pp->phnext;
+
+                       if ( pp->prnext )       /* part of RESIDUE-list ? */
+                       {
+                               (pp->prnext)->prprev = pp->prprev;
+                               (pp->prprev)->prnext = pp->prnext;
+                       }
+
+                       /*
+                       ** remove process-info from process-database
+                       */
+                       free(pp);
+
+                       return 1;
+               }
+
+               ppp     = pp;
+               pp      = pp->phnext;
+       }
+
+       return 0;       /* PID not found */
+}
+
+/*
+** Chain all process-info structures into the RESIDUE-list;
+** every process-info struct which is referenced later on by pdb_gettask(),
+** will be removed from this list again. After that, the remaining
+** (unreferred) process-info structs can be easily discovered and
+** eventually removed.
+*/
+int
+pdb_makeresidue(void)
+{
+       register struct pinfo   *pp, *pr;
+       register int            i;
+
+       /*
+       ** prepare RESIDUE-list anchor
+       */
+       pr = &presidue;
+
+       pr->prnext      = pr;
+       pr->prprev      = pr;
+
+       /*
+       ** check all entries in hash list
+       */
+       for (i=0; i < NPHASH; i++)
+       {
+               if (!phash[i])
+                       continue;       /* empty hash bucket */
+
+               pp = phash[i];          /* get start of list */
+
+               while (pp)              /* all entries in hash list     */
+               {
+                       pp->prnext              = pr->prnext;
+                       pr->prnext              = pp;
+
+                        pp->prprev             = (pp->prnext)->prprev;
+                       (pp->prnext)->prprev    = pp;
+
+                       pp = pp->phnext;        /* get next of hash list */
+               }
+       }
+
+       /*
+       ** all entries chained in doubly-linked RESIDUE-list
+       */
+       return 1;
+}
+
+/*
+** remove all remaining entries in RESIDUE-list
+*/
+int
+pdb_cleanresidue(void)
+{
+       register struct pinfo   *pr;
+       register int            pid;
+        char                   isproc;
+
+       /*
+       ** start at RESIDUE-list anchor and delete all entries
+       */
+       pr = presidue.prnext;
+
+       while (pr != &presidue)
+       {
+               pid    = pr->tstat.gen.pid;
+               isproc = pr->tstat.gen.isproc;
+
+               pr  = pr->prnext;       /* MUST be done before deletion */
+
+               pdb_deltask(pid, isproc);
+       }
+
+       return 1;
+}
+
+/*
+** search in the RESIDUE-list for process-info which may fit to the
+** given process-info, for which the PID is not known
+*/
+int
+pdb_srchresidue(struct tstat *tstatp, struct pinfo **pinfopp)
+{
+       register struct pinfo   *pr, *prmin=NULL;
+       register long           btimediff;
+
+       /*
+       ** start at RESIDUE-list anchor and search through
+       ** all remaining entries
+       */
+       pr = presidue.prnext;
+
+       while (pr != &presidue) /* still entries left ? */
+       {
+               /*
+               ** check if this entry matches searched info
+               */
+               if (    pr->tstat.gen.ruid   == tstatp->gen.ruid        && 
+                       pr->tstat.gen.rgid   == tstatp->gen.rgid        && 
+                       strcmp(pr->tstat.gen.name, tstatp->gen.name) == EQ  )
+               {
+                       /*
+                       ** check if the start-time of the process is exactly
+                       ** the same ----> then we have a match;
+                       ** however sometimes the start-time may deviate a
+                       ** second although it IS the process we are looking
+                       ** for (depending on the rounding of the boot-time),
+                       ** so if we don't find the exact match, we will check
+                       ** later on if we found an almost-exact match
+                       */
+                       btimediff = pr->tstat.gen.btime - tstatp->gen.btime;
+
+                       if (btimediff == 0)     /* gotcha !! */
+                       {
+                               *pinfopp = pr;
+                               return 1;
+                       }
+
+                       if ((btimediff== -1 || btimediff== 1) && prmin== NULL)
+                               prmin = pr;     /* remember this process */
+               }
+
+               pr = pr->prnext;
+       }
+
+       /*
+       ** nothing found that matched exactly;
+       ** do we remember a process that matched almost exactly?
+       */
+       if (prmin)
+       {
+               *pinfopp = prmin;
+               return 1;
+       }
+
+       return 0;       /* even not almost */
+}
diff --git a/sources/psaccs_atop b/sources/psaccs_atop
new file mode 100644 (file)
index 0000000..56a9f3a
--- /dev/null
@@ -0,0 +1,40 @@
+# Logrotate file to take action before psacct is rotated
+/var/log/atop/dummy_before {
+    missingok
+    daily
+    rotate 0
+    ifempty
+    create 0600 root root
+    postrotate
+       # check if process accounting is installed
+       #
+       if [ -e /etc/logrotate.d/psacct ]
+       then
+           # check if process accounting is actually in use
+           #
+           ACCTFILE=`awk '$2 == "{" {print $1}' /etc/logrotate.d/psacct`
+
+           if [ -f "$ACCTFILE" ]
+           then
+               ACCTSIZE1=`ls -l "$ACCTFILE" | awk '{print $5}'`
+               ACCTSIZE2=`ls -l "$ACCTFILE" | awk '{print $5}'`
+
+               if [ $ACCTSIZE1 -lt $ACCTSIZE2 ]
+               then
+                   # stop atop daemon before accounting file
+                   # is rotated
+                   #
+                   PIDFILE=/var/run/atop.pid
+
+                   if [ -e $PIDFILE ] && \
+                          ps -p `cat $PIDFILE` | grep 'atop$' > /dev/null
+                   then
+                       kill -USR2 `cat $PIDFILE` # take final sample
+                       rm $PIDFILE
+                       sleep 1
+                   fi
+               fi
+           fi
+       fi
+    endscript
+}
diff --git a/sources/psaccu_atop b/sources/psaccu_atop
new file mode 100644 (file)
index 0000000..768c970
--- /dev/null
@@ -0,0 +1,24 @@
+# Logrotate file to take action after psacct is rotated
+/var/log/atop/dummy_after {
+    missingok
+    daily
+    rotate 0
+    ifempty
+    create 0600 root root
+    postrotate
+       if [ -e /etc/logrotate.d/psacct ]
+       then
+           # if the atop daemon does not run, restart it after
+           # accounting file is rotated
+           PIDFILE=/var/run/atop.pid
+
+           if [ -e $PIDFILE ] && \
+                       ps -p `cat $PIDFILE` | grep 'atop$' > /dev/null
+           then
+               :
+           else
+               /usr/share/atop/atop.daily&
+           fi
+       fi
+    endscript
+}     
diff --git a/sources/rawlog.c b/sources/rawlog.c
new file mode 100644 (file)
index 0000000..0f6a47e
--- /dev/null
@@ -0,0 +1,970 @@
+/*
+** ATOP - System & Process Monitor
+**
+** The program 'atop' offers the possibility to view the activity of 
+** the system on system-level as well as process-level.
+** ==========================================================================
+** Author:      Gerlof Langeveld
+** E-mail:      gerlof.langeveld@atoptool.nl
+** Date:        September 2002
+** --------------------------------------------------------------------------
+** Copyright (C) 2000-2010 Gerlof Langeveld
+**
+** This program is free software; you can redistribute it and/or modify it
+** under the terms of the GNU General Public License as published by the
+** Free Software Foundation; either version 2, or (at your option) any
+** later version.
+**
+** This program is distributed in the hope that it will be useful, but
+** WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+** See the GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+** --------------------------------------------------------------------------
+*/
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <ctype.h>
+#include <sys/utsname.h>
+#include <string.h>
+#include <regex.h>
+#include <zlib.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <unistd.h>
+
+#include "atop.h"
+#include "showgeneric.h"
+#include "photoproc.h"
+#include "photosyst.h"
+
+#define        BASEPATH        "/var/log/atop"  
+
+/*
+** structure which describes the raw file contents
+**
+** layout raw file:    rawheader
+**
+**                     rawrecord                           \
+**                     compressed system-level statistics   | sample 1
+**                     compressed process-level statistics /
+**
+**                     rawrecord                           \
+**                     compressed system-level statistics   | sample 2
+**                     compressed process-level statistics /
+**
+** etcetera .....
+*/
+#define        MYMAGIC         (unsigned int) 0xfeedbeef
+
+struct rawheader {
+       unsigned int    magic;
+
+       unsigned short  aversion;       /* creator atop version with MSB */
+       unsigned short  future1;        /* can be reused                 */
+       unsigned short  future2;        /* can be reused                 */
+       unsigned short  rawheadlen;     /* length of struct rawheader    */
+       unsigned short  rawreclen;      /* length of struct rawrecord    */
+       unsigned short  hertz;          /* clock interrupts per second   */
+       unsigned short  sfuture[6];     /* future use                    */
+       unsigned int    sstatlen;       /* length of struct sstat        */
+       unsigned int    tstatlen;       /* length of struct tstat        */
+       struct utsname  utsname;        /* info about this system        */
+       char            cfuture[8];     /* future use                    */
+
+       unsigned int    pagesize;       /* size of memory page (bytes)   */
+       int             supportflags;   /* used features                 */
+       int             osrel;          /* OS release number             */
+       int             osvers;         /* OS version number             */
+       int             ossub;          /* OS version subnumber          */
+       int             ifuture[6];     /* future use                    */
+};
+
+struct rawrecord {
+       time_t          curtime;        /* current time (epoch)         */
+
+       unsigned short  flags;          /* various flags                */
+       unsigned short  sfuture[3];     /* future use                   */
+
+       unsigned int    scomplen;       /* length of compressed sstat   */
+       unsigned int    pcomplen;       /* length of compressed tstat's */
+       unsigned int    interval;       /* interval (number of seconds) */
+       unsigned int    ndeviat;        /* number of tasks in list      */
+       unsigned int    nactproc;       /* number of processes in list  */
+       unsigned int    ntask;          /* total number of tasks        */
+       unsigned int    totproc;        /* total number of processes    */
+       unsigned int    totrun;         /* number of running  threads   */
+       unsigned int    totslpi;        /* number of sleeping threads(S)*/
+       unsigned int    totslpu;        /* number of sleeping threads(D)*/
+       unsigned int    totzomb;        /* number of zombie processes   */
+       unsigned int    nexit;          /* number of exited processes   */
+       unsigned int    noverflow;      /* number of overflow processes */
+       unsigned int    ifuture[6];     /* future use                   */
+};
+
+static int     getrawrec  (int, struct rawrecord *, int);
+static int     getrawsstat(int, struct sstat *, int);
+static int     getrawtstat(int, struct tstat *, int, int);
+static int     rawwopen(void);
+static int     lookslikedatetome(char *);
+static void    testcompval(int, char *);
+static void    try_other_version(int, int);
+
+/*
+** write a raw record to file
+** (file is opened/created during the first call)
+*/
+char
+rawwrite(time_t curtime, int numsecs, 
+         struct devtstat *devtstat, struct sstat *sstat,
+         int nexit, unsigned int noverflow, char flag)
+{
+       static int              rawfd = -1;
+       struct rawrecord        rr;
+       int                     rv;
+       struct stat             filestat;
+
+       Byte                    scompbuf[sizeof(struct sstat)], *pcompbuf;
+       unsigned long           scomplen = sizeof scompbuf;
+       unsigned long           pcomplen = sizeof(struct tstat) *
+                                               devtstat->ntaskall;
+
+       /*
+       ** first call:
+       **      take care that the log file is opened
+       */
+       if (rawfd == -1)
+               rawfd = rawwopen();
+
+       /*
+       ** register current size of file in order to "roll back"
+       ** writes that have been done while not *all* writes could
+       ** succeed, e.g. when file system full
+       */
+       (void) fstat(rawfd, &filestat);
+
+       /*
+       ** compress system- and process-level statistics
+       */
+       rv = compress(scompbuf, &scomplen,
+                               (Byte *)sstat, (unsigned long)sizeof *sstat);
+
+       testcompval(rv, "compress");
+
+       pcompbuf = malloc(pcomplen);
+
+       ptrverify(pcompbuf, "Malloc failed for compression buffer\n");
+
+       rv = compress(pcompbuf, &pcomplen, (Byte *)devtstat->taskall,
+                                               (unsigned long)pcomplen);
+
+       testcompval(rv, "compress");
+
+       /*
+       ** fill record header and write to file
+       */
+       memset(&rr, 0, sizeof rr);
+
+       rr.curtime      = curtime;
+       rr.interval     = numsecs;
+       rr.flags        = 0;
+       rr.ndeviat      = devtstat->ntaskall;
+       rr.nactproc     = devtstat->nprocactive;
+       rr.ntask        = devtstat->ntaskall;
+       rr.nexit        = nexit;
+       rr.noverflow    = noverflow;
+       rr.totproc      = devtstat->nprocall;
+       rr.totrun       = devtstat->totrun;
+       rr.totslpi      = devtstat->totslpi;
+       rr.totslpu      = devtstat->totslpu;
+       rr.totzomb      = devtstat->totzombie;
+       rr.scomplen     = scomplen;
+       rr.pcomplen     = pcomplen;
+
+       if (flag&RRBOOT)
+               rr.flags |= RRBOOT;
+
+       if (supportflags & ACCTACTIVE)
+               rr.flags |= RRACCTACTIVE;
+
+       if (supportflags & IOSTAT)
+               rr.flags |= RRIOSTAT;
+
+       if (supportflags & NETATOP)
+               rr.flags |= RRNETATOP;
+
+       if (supportflags & NETATOPD)
+               rr.flags |= RRNETATOPD;
+
+       if (supportflags & DOCKSTAT)
+               rr.flags |= RRDOCKSTAT;
+
+       if ( write(rawfd, &rr, sizeof rr) == -1)
+       {
+               fprintf(stderr, "%s - ", rawname);
+               perror("write raw record");
+               if ( ftruncate(rawfd, filestat.st_size) == -1)
+                       cleanstop(8);
+               cleanstop(7);
+       }
+
+       /*
+       ** write compressed system status structure to file
+       */
+       if ( write(rawfd, scompbuf, scomplen) == -1)
+       {
+               fprintf(stderr, "%s - ", rawname);
+               perror("write raw status record");
+               if ( ftruncate(rawfd, filestat.st_size) == -1)
+                       cleanstop(8);
+               cleanstop(7);
+       }
+
+       /*
+       ** write compressed list of process status structures to file
+       */
+       if ( write(rawfd, pcompbuf, pcomplen) == -1)
+       {
+               fprintf(stderr, "%s - ", rawname);
+               perror("write raw process record");
+               if ( ftruncate(rawfd, filestat.st_size) == -1)
+                       cleanstop(8);
+               cleanstop(7);
+       }
+
+       free(pcompbuf);
+
+       return '\0';
+}
+
+
+/*
+** open a raw file for writing
+**
+** if the raw file exists already:
+**    - read and validate the header record (be sure it is an atop-file)
+**    - seek to the end of the file
+**
+** if the raw file does not yet exist:
+**    - create the raw file
+**    - write a header record
+**
+** return the filedescriptor of the raw file
+*/
+static int
+rawwopen()
+{
+       struct rawheader        rh;
+       int                     fd;
+
+       /*
+       ** check if the file exists already
+       */
+       if ( (fd = open(rawname, O_RDWR)) >= 0)
+       {
+               /*
+               ** read and verify header record
+               */
+               if ( read(fd, &rh, sizeof rh) < sizeof rh)
+               {
+                       fprintf(stderr, "%s - cannot read header\n", rawname);
+                       cleanstop(7);
+               }
+
+               if (rh.magic != MYMAGIC)
+               {
+                       fprintf(stderr,
+                               "file %s exists but does not contain raw "
+                               "atop output (wrong magic number)\n", rawname);
+
+                       cleanstop(7);
+               }
+
+               if ( rh.sstatlen        != sizeof(struct sstat)         ||
+                    rh.tstatlen        != sizeof(struct tstat)         ||
+                    rh.rawheadlen      != sizeof(struct rawheader)     ||
+                    rh.rawreclen       != sizeof(struct rawrecord)       )
+               {
+                       fprintf(stderr,
+                               "existing file %s has incompatible header\n",
+                               rawname);
+
+                       if (rh.aversion & 0x8000 &&
+                          (rh.aversion & 0x7fff) != getnumvers())
+                       {
+                               fprintf(stderr,
+                                       "(created by version %d.%d - "
+                                       "current version %d.%d)\n",
+                                       (rh.aversion >> 8) & 0x7f,
+                                        rh.aversion & 0xff,
+                                        getnumvers() >> 8,
+                                        getnumvers() & 0x7f);
+                       }
+
+                       cleanstop(7);
+               }
+
+               (void) lseek(fd, (off_t) 0, SEEK_END);
+
+               return fd;
+       }
+
+       /*
+       ** file does not exist (or can not be opened)
+       */
+       if ( (fd = creat(rawname, 0666)) == -1)
+       {
+               fprintf(stderr, "%s - ", rawname);
+               perror("create raw file");
+               cleanstop(7);
+       }
+
+       memset(&rh, 0, sizeof rh);
+
+       rh.magic        = MYMAGIC;
+       rh.aversion     = getnumvers() | 0x8000;
+       rh.sstatlen     = sizeof(struct sstat);
+       rh.tstatlen     = sizeof(struct tstat);
+       rh.rawheadlen   = sizeof(struct rawheader);
+       rh.rawreclen    = sizeof(struct rawrecord);
+       rh.supportflags = supportflags | RAWLOGNG;
+       rh.osrel        = osrel;
+       rh.osvers       = osvers;
+       rh.ossub        = ossub;
+       rh.hertz        = hertz;
+       rh.pagesize     = pagesize;
+
+       memcpy(&rh.utsname, &utsname, sizeof rh.utsname);
+
+       if ( write(fd, &rh, sizeof rh) == -1)
+       {
+               fprintf(stderr, "%s - ", rawname);
+               perror("write raw header");
+               cleanstop(7);
+       }
+
+       return fd;
+}
+
+/*
+** read the contents of a raw file
+*/
+#define        OFFCHUNK        256
+
+void
+rawread(void)
+{
+       int                     i, j, rawfd, len;
+       char                    *py;
+       struct rawheader        rh;
+       struct rawrecord        rr;
+       struct sstat            sstat;
+       static struct devtstat  devtstat;
+
+       struct stat             filestat;
+
+       /*
+       ** variables to maintain the offsets of the raw records
+       ** to be able to see previous samples again
+       */
+       off_t                   *offlist;
+       unsigned int            offsize = 0;
+       unsigned int            offcur  = 0;
+       char                    lastcmd = 'X', flags;
+
+       time_t                  timenow;
+       struct tm               *tp;
+
+       switch ( len = strlen(rawname) )
+       {
+          /*
+          ** if no filename is specified, assemble the name of the raw file
+          */
+          case 0:
+               timenow = time(0);
+               tp      = localtime(&timenow);
+
+               snprintf(rawname, RAWNAMESZ, "%s/atop_%04d%02d%02d",
+                       BASEPATH, 
+                       tp->tm_year+1900,
+                       tp->tm_mon+1,
+                       tp->tm_mday);
+
+               break;
+
+          /*
+          ** if date specified as filename in format YYYYMMDD, assemble
+          ** the full pathname of the raw file
+          */
+          case 8:
+               if ( access(rawname, F_OK) == 0) 
+                       break;          /* existing file */
+
+               if (lookslikedatetome(rawname))
+               {
+                       char    savedname[RAWNAMESZ];
+
+                       strncpy(savedname, rawname, RAWNAMESZ-1);
+
+                       snprintf(rawname, RAWNAMESZ, "%s/atop_%s",
+                               BASEPATH, 
+                               savedname);
+                       break;
+               }
+
+          /*
+          ** if one or more 'y' (yesterday) characters are used and that
+          ** string is not known as an existing file, the standard logfile
+          ** is shown from N days ago (N is determined by the number
+          ** of y's).
+          */
+          default:
+               if ( access(rawname, F_OK) == 0) 
+                       break;          /* existing file */
+
+               /*
+               ** make a string existing of y's to compare with
+               */
+               py = malloc(len+1);
+
+               ptrverify(py, "Malloc failed for 'yes' sequence\n");
+
+               memset(py, 'y', len);
+               *(py+len) = '\0';
+
+               if ( strcmp(rawname, py) == 0 )
+               {
+                       timenow  = time(0);
+                       timenow -= len*3600*24;
+                       tp       = localtime(&timenow);
+
+                       snprintf(rawname, RAWNAMESZ, "%s/atop_%04d%02d%02d",
+                               BASEPATH, 
+                               tp->tm_year+1900,
+                               tp->tm_mon+1,
+                               tp->tm_mday);
+               }
+
+               free(py);
+       }
+
+       /*
+       ** open raw file
+       */
+       if ( (rawfd = open(rawname, O_RDONLY)) == -1)
+       {
+               char    command[512], tmpname1[RAWNAMESZ], tmpname2[RAWNAMESZ];
+
+               /*
+               ** check if a compressed raw file is present
+               */
+               snprintf(tmpname1, sizeof tmpname1, "%s.gz", rawname);
+
+               if ( access(tmpname1, F_OK|R_OK) == -1)
+               {
+                       fprintf(stderr, "%s - ", rawname);
+                       perror("open raw file");
+                       cleanstop(7);
+               }
+
+               /*
+               ** compressed raw file to be decompressed via gunzip
+               */
+               fprintf(stderr, "Decompressing logfile ....\n");
+               snprintf(tmpname2, sizeof tmpname2, "/tmp/atopwrkXXXXXX");
+               rawfd = mkstemp(tmpname2);
+               if (rawfd == -1)
+               {
+                       fprintf(stderr, "%s - ", rawname);
+                       perror("creating decompression temp file");
+                       cleanstop(7);
+               }
+
+               snprintf(command,  sizeof command, "gunzip -c %s > %s",
+                                                       tmpname1, tmpname2);
+               const int system_res = system (command);
+               unlink(tmpname2);
+
+               if (system_res)
+               {
+                       fprintf(stderr, "%s - gunzip failed", rawname);
+                       cleanstop(7);
+               }
+       }
+
+       /*
+       ** read the raw header and verify the magic
+       */
+       if ( read(rawfd, &rh, sizeof rh) < sizeof rh)
+       {
+               fprintf(stderr, "can not read raw file header\n");
+               cleanstop(7);
+       }
+
+       if (rh.magic != MYMAGIC)
+       {
+               fprintf(stderr, "file %s does not contain raw atop/atopsar "
+                               "output (wrong magic number)\n", rawname);
+               cleanstop(7);
+       }
+
+       /*
+       ** magic okay, but file-layout might have been modified
+       */
+       if (rh.sstatlen   != sizeof(struct sstat)               ||
+           rh.tstatlen   != sizeof(struct tstat)               ||
+           rh.rawheadlen != sizeof(struct rawheader)           ||
+           rh.rawreclen  != sizeof(struct rawrecord)             )
+       {
+               fprintf(stderr,
+                       "raw file %s has incompatible format\n", rawname);
+
+               if (rh.aversion & 0x8000 &&
+                          (rh.aversion & 0x7fff) != getnumvers())
+               {
+                       fprintf(stderr,
+                               "(created by version %d.%d - "
+                               "current version %d.%d)\n",
+                               (rh.aversion >> 8) & 0x7f,
+                                rh.aversion       & 0xff,
+                                getnumvers() >> 8,
+                                getnumvers() & 0x7f);
+               }
+               else
+               {
+                       fprintf(stderr,
+                               "(files from other system architectures might"
+                               " be binary incompatible)\n");
+               }
+
+               close(rawfd);
+
+               if (((rh.aversion >> 8) & 0x7f) != (getnumvers()   >> 8) ||
+                    (rh.aversion       & 0xff) != (getnumvers() & 0x7f)   )
+               {
+                       try_other_version((rh.aversion >> 8) & 0x7f,
+                                          rh.aversion       & 0xff);
+               }
+
+               cleanstop(7);
+       }
+
+       memcpy(&utsname, &rh.utsname, sizeof utsname);
+       utsnodenamelen = strlen(utsname.nodename);
+
+       supportflags = rh.supportflags;
+       osrel        = rh.osrel;
+       osvers       = rh.osvers;
+       ossub        = rh.ossub;
+       interval     = 0;
+
+       if (rh.hertz)
+               hertz    = rh.hertz;
+
+       if (rh.pagesize)
+               pagesize = rh.pagesize;
+
+       /*
+       ** allocate a list for backtracking of rawrecord-offsets
+       */
+       offlist = malloc(sizeof(off_t) * OFFCHUNK);
+
+       ptrverify(offlist, "Malloc failed for backtrack list\n");
+
+       offsize = OFFCHUNK;
+
+       *offlist = lseek(rawfd, 0, SEEK_CUR);
+       offcur   = 1;
+
+       /*
+       ** read a raw record header until end-of-file
+       */
+       sampcnt = 0;
+
+       while (lastcmd && lastcmd != 'q')
+       {
+               while ( getrawrec(rawfd, &rr, rh.rawreclen) == rh.rawreclen)
+               {
+                       unsigned int    secsinday = daysecs(rr.curtime);
+                       unsigned int    k, l;
+
+                       /*
+                       ** store the offset of the raw record in the offset list
+                       ** in case of offset list overflow, extend the list
+                       */
+                       *(offlist+offcur) = lseek(rawfd, 0, SEEK_CUR) -
+                                                               rh.rawreclen;
+
+                       if ( ++offcur >= offsize )
+                       {
+                               offlist = realloc(offlist,
+                                            (offsize+OFFCHUNK)*sizeof(off_t));
+
+                               ptrverify(offlist,
+                                       "Realloc failed for backtrack list\n");
+
+                               offsize+= OFFCHUNK;
+                       }
+       
+                       /*
+                       ** check if this sample is within the time-range
+                       ** specified with the -b and -e flags (if any)
+                       */
+                       if ( (begintime && begintime > secsinday) )
+                       {
+                               lastcmd = 1;
+                               lseek(rawfd, rr.scomplen+rr.pcomplen, SEEK_CUR);
+                               continue;
+                       }
+
+                       begintime = 0;  // allow earlier times from now on
+
+                       if ( (endtime && endtime < secsinday) )
+                       {
+                               free(offlist);
+                               close(rawfd);
+                               return;
+                       }
+
+                       /*
+                       ** allocate space, read compressed system-level
+                       ** statistics and decompress
+                       */
+                       if ( !getrawsstat(rawfd, &sstat, rr.scomplen) )
+                               cleanstop(7);
+
+                       /*
+                       ** allocate space, read compressed process-level
+                       ** statistics and decompress
+                       */
+                       devtstat.taskall    = malloc(sizeof(struct tstat  )
+                                                               * rr.ndeviat);
+
+                       if (rr.totproc < rr.nactproc)   // compat old raw files
+                               devtstat.procall = malloc(sizeof(struct tstat *)
+                                                               * rr.nactproc);
+                       else
+                               devtstat.procall = malloc(sizeof(struct tstat *)
+                                                               * rr.totproc);
+
+                       devtstat.procactive = malloc(sizeof(struct tstat *) * rr.nactproc);
+
+                       ptrverify(devtstat.taskall,
+                                 "Malloc failed for %d stored tasks\n",
+                                 rr.ndeviat);
+
+                       ptrverify(devtstat.procall,
+                                 "Malloc failed for total %d processes\n",
+                                 rr.totproc);
+
+                       ptrverify(devtstat.procactive,
+                                 "Malloc failed for %d active processes\n",
+                                 rr.nactproc);
+
+                       if ( !getrawtstat(rawfd, devtstat.taskall,
+                                               rr.pcomplen, rr.ndeviat) )
+                               cleanstop(7);
+
+
+                       for (i=j=k=l=0; i < rr.ndeviat; i++)
+                       {
+                               if ( (devtstat.taskall+i)->gen.isproc)
+                               {
+                                       devtstat.procall[j++] = devtstat.taskall+i;
+
+                                       if (! (devtstat.taskall+i)->gen.wasinactive)
+                                               devtstat.procactive[k++] = devtstat.taskall+i;
+                               }
+
+                               if (! (devtstat.taskall+i)->gen.wasinactive)
+                                       l++;
+                       }
+
+                       devtstat.ntaskall       = i;
+                       devtstat.nprocall       = j;
+                       devtstat.nprocactive    = k;
+                       devtstat.ntaskactive    = l;
+
+                       devtstat.totrun         = rr.totrun;
+                       devtstat.totslpi        = rr.totslpi;
+                       devtstat.totslpu        = rr.totslpu;
+                       devtstat.totzombie      = rr.totzomb;
+
+                       /*
+                       ** activate the installed print-function to visualize
+                       ** the system- and process-level statistics
+                       */
+                       sampcnt++;
+
+                       if ( (rh.supportflags & RAWLOGNG) == RAWLOGNG)
+                       {
+                               if (rr.flags & RRACCTACTIVE)
+                                       supportflags |=  ACCTACTIVE;
+                               else
+                                       supportflags &= ~ACCTACTIVE;
+
+                               if (rr.flags & RRIOSTAT)
+                                       supportflags |=  IOSTAT;
+                               else
+                                       supportflags &= ~IOSTAT;
+                       }
+
+                       if (rr.flags & RRNETATOP)
+                               supportflags |=  NETATOP;
+                       else
+                               supportflags &= ~NETATOP;
+
+                       if (rr.flags & RRNETATOPD)
+                               supportflags |=  NETATOPD;
+                       else
+                               supportflags &= ~NETATOPD;
+
+                       if (rr.flags & RRDOCKSTAT)
+                               supportflags |=  DOCKSTAT;
+                       else
+                               supportflags &= ~DOCKSTAT;
+
+                       flags = rr.flags & RRBOOT;
+
+                       (void) fstat(rawfd, &filestat);
+
+                       if ( filestat.st_size -
+                            lseek(rawfd, (off_t)0, SEEK_CUR) <= rh.rawreclen)
+                               flags |= RRLAST;
+
+                       lastcmd = (vis.show_samp)(rr.curtime, rr.interval,
+                                    &devtstat, &sstat,
+                                    rr.nexit, rr.noverflow, flags);
+
+                       free(devtstat.taskall);
+                       free(devtstat.procall);
+                       free(devtstat.procactive);
+       
+                       switch (lastcmd)
+                       {
+                          case MSAMPPREV:
+                               if (offcur >= 2)
+                                       offcur-= 2;
+                               else
+                                       offcur = 0;
+       
+                               lseek(rawfd, *(offlist+offcur), SEEK_SET);
+                               break;
+
+                          case MRESET:
+                               lseek(rawfd, *offlist, SEEK_SET);
+                               offcur = 1;
+                               break;
+
+                          case MSAMPBRANCH:
+                               if (begintime && begintime < secsinday)
+                               {
+                                       lseek(rawfd, *offlist, SEEK_SET);
+                                       offcur = 1;
+                               }
+                       }
+               }
+
+               begintime = 0;  // allow earlier times from now on
+
+               if (offcur >= 1)
+                       offcur--;
+
+               lseek(rawfd, *(offlist+offcur), SEEK_SET);
+       }
+
+       free(offlist);
+
+       close(rawfd);
+}
+
+/*
+** read the next raw record from the raw logfile
+*/
+static int
+getrawrec(int rawfd, struct rawrecord *prr, int rrlen)
+{
+       return read(rawfd, prr, rrlen);
+}
+
+/*
+** read the system-level statistics from the current offset
+*/
+static int
+getrawsstat(int rawfd, struct sstat *sp, int complen)
+{
+       Byte            *compbuf;
+       unsigned long   uncomplen = sizeof(struct sstat);
+       int             rv;
+
+       if ( (compbuf = malloc(complen)) == NULL)
+
+       ptrverify(compbuf, "Malloc failed for reading compressed sysstats\n");
+
+       if ( read(rawfd, compbuf, complen) < complen)
+       {
+               free(compbuf);
+               return 0;
+       }
+
+       rv = uncompress((Byte *)sp, &uncomplen, compbuf, complen);
+
+       testcompval(rv, "uncompress");
+
+       free(compbuf);
+
+       return 1;
+}
+
+/*
+** read the process-level statistics from the current offset
+*/
+static int
+getrawtstat(int rawfd, struct tstat *pp, int complen, int ndeviat)
+{
+       Byte            *compbuf;
+       unsigned long   uncomplen = sizeof(struct tstat) * ndeviat;
+       int             rv;
+
+       compbuf = malloc(complen);
+
+       ptrverify(compbuf, "Malloc failed for reading compressed procstats\n");
+
+       if ( read(rawfd, compbuf, complen) < complen)
+       {
+               free(compbuf);
+               return 0;
+       }
+
+       rv = uncompress((Byte *)pp, &uncomplen, compbuf, complen);
+
+       testcompval(rv, "uncompress");
+
+       free(compbuf);
+
+       return 1;
+}
+
+/* 
+** verify if a particular ascii-string is in the format yyyymmdd
+*/
+static int
+lookslikedatetome(char *p)
+{
+       register int    i;
+
+       for (i=0; i < 8; i++)
+               if ( !isdigit(*(p+i)) )
+                       return 0;
+
+       if (*p != '2')
+               return 0;       /* adapt this in the year 3000 */
+
+       if ( *(p+4) > '1')
+               return 0;
+
+       if ( *(p+6) > '3')
+               return 0;
+
+       return 1;       /* yes, looks like a date to me */
+}
+
+static void
+testcompval(int rv, char *func)
+{
+       switch (rv)
+       {
+          case Z_OK:
+          case Z_STREAM_END:
+          case Z_NEED_DICT:
+               break;
+
+          case Z_MEM_ERROR:
+               fprintf(stderr, "atop/atopsar - "
+                       "%s: failed due to lack of memory\n", func);
+               cleanstop(7);
+
+          case Z_BUF_ERROR:
+               fprintf(stderr, "atop/atopsar - "
+                       "%s: failed due to lack of room in buffer\n", func);
+               cleanstop(7);
+
+          case Z_DATA_ERROR:
+               fprintf(stderr, "atop/atopsar - "
+                       "%s: failed due to corrupted/incomplete data\n", func);
+               cleanstop(7);
+
+          default:
+               fprintf(stderr, "atop/atopsar - "
+                       "%s: unexpected error %d\n", func, rv);
+               cleanstop(7);
+       }
+}
+
+/*
+** try to activate another atop- or atopsar-version
+** to read this logfile
+*/
+static void
+try_other_version(int majorversion, int minorversion)
+{
+       char            tmpbuf[1024], *p;
+       extern char     **argvp;
+       int             fds;
+       struct rlimit   rlimit;
+       int             setresuid(uid_t, uid_t, uid_t);
+
+       /*
+       ** prepare name of executable file
+       ** the current pathname (if any) is stripped off
+       */
+       snprintf(tmpbuf, sizeof tmpbuf, "%s-%d.%d",
+               (p = strrchr(*argvp, '/')) ? p+1 : *argvp,
+                       majorversion, minorversion);
+
+       fprintf(stderr, "trying to activate %s....\n", tmpbuf);
+
+       /*
+       ** be sure no open file descriptors are passed
+       ** except stdin, stdout en stderr
+       */
+       (void) getrlimit(RLIMIT_NOFILE, &rlimit);
+
+       for (fds=3; fds < rlimit.rlim_cur; fds++)
+               close(fds);
+
+       /*
+       ** be absolutely sure not to pass setuid-root privileges
+       ** to the loaded program; errno EAGAIN and ENOMEM are not
+       ** acceptable!
+       */
+       if ( setresuid(getuid(), getuid(), getuid()) == -1 && errno != EPERM)
+       {
+               fprintf(stderr, "not possible to drop root-privileges!\n");
+               exit(1);
+       }
+
+       /*
+       ** load alternative executable image
+       ** at this moment the saved-uid might still be set
+       ** to 'root' but this is reset at the moment of exec
+       */
+       (void) execvp(tmpbuf, argvp);
+
+       /*
+       ** point of no return, except when exec failed
+       */
+       fprintf(stderr, "activation of %s failed!\n", tmpbuf);
+}
diff --git a/sources/showgeneric.c b/sources/showgeneric.c
new file mode 100644 (file)
index 0000000..0668b03
--- /dev/null
@@ -0,0 +1,3287 @@
+/*
+** ATOP - System & Process Monitor 
+**
+** The program 'atop' offers the possibility to view the activity of
+** the system on system-level as well as process-level.
+** 
+** This source-file contains the print-functions to visualize the calculated
+** figures.
+** ==========================================================================
+** Author:      Gerlof Langeveld
+** E-mail:      gerlof.langeveld@atoptool.nl
+** Date:        November 1996
+** LINUX-port:  June 2000
+** --------------------------------------------------------------------------
+** Copyright (C) 2000-2010 Gerlof Langeveld
+**
+** This program is free software; you can redistribute it and/or modify it
+** under the terms of the GNU General Public License as published by the
+** Free Software Foundation; either version 2, or (at your option) any
+** later version.
+**
+** This program is distributed in the hope that it will be useful, but
+** WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+** See the GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+** --------------------------------------------------------------------------
+** 
+** $Log: showgeneric.c,v $
+** Revision 1.71  2010/10/25 19:08:32  gerlof
+** When the number of lines is too small for the system-level
+** lines, limit the number of variable resources automatically
+** to a minimum.
+**
+** Revision 1.70  2010/10/23 14:04:05  gerlof
+** Counters for total number of running and sleep threads (JC van Winkel).
+**
+** Revision 1.69  2010/04/23 12:19:35  gerlof
+** Modified mail-address in header.
+**
+** Revision 1.68  2010/04/23 09:58:11  gerlof
+** Version (flag -V) handled earlier after startup.
+**
+** Revision 1.67  2010/04/23 07:57:32  gerlof
+** Proper sorting of processes when switching from single process view
+** to cumulative view (key 'u' or 'p') and vice versa.
+**
+** Revision 1.66  2010/04/17 17:20:26  gerlof
+** Allow modifying the layout of the columns in the system lines.
+**
+** Revision 1.65  2010/03/16 21:13:38  gerlof
+** Program and user selection can be combined with program and user
+** accumulation.
+**
+** Revision 1.64  2010/03/16 20:18:46  gerlof
+** Show in header-line if user selections and program selection are active.
+**
+** Revision 1.63  2010/03/16 20:08:51  gerlof
+** Performance improvement: only sort system-resources once per interval.
+**
+** Revision 1.62  2010/03/04 10:53:01  gerlof
+** Support I/O-statistics on logical volumes and MD devices.
+**
+** Revision 1.61  2009/12/17 10:55:07  gerlof
+** *** empty log message ***
+**
+** Revision 1.60  2009/12/17 10:50:30  gerlof
+** Allow own defined process line with key 'o' and a definition
+** in the atoprc file.
+**
+** Revision 1.59  2009/12/17 09:03:26  gerlof
+** Center message "....since boot" in status line on first screen.
+**
+** Revision 1.58  2009/12/17 08:55:15  gerlof
+** Show messages on status line in color to draw attention.
+**
+** Revision 1.57  2009/12/17 08:16:14  gerlof
+** Introduce branch-key to go to specific time in raw file.
+**
+** Revision 1.56  2009/12/12 09:06:39  gerlof
+** Corrected cumulated disk I/O per user/program (JC van Winkel).
+**
+** Revision 1.55  2009/12/10 13:34:44  gerlof
+** Show which toggle-keys are active in the header line.
+**
+** Revision 1.54  2009/12/10 11:55:03  gerlof
+** Introduce system-wide /etc/atoprc
+**
+** Revision 1.53  2009/12/10 09:53:08  gerlof
+** Improved display of header-line (JC van Winkel).
+**
+** Revision 1.52  2008/03/06 10:14:01  gerlof
+** Modified help-messages.
+**
+** Revision 1.51  2008/02/25 13:47:21  gerlof
+** Bug-solution: segmentation-fault in case of invalid regular expression.
+**
+** Revision 1.50  2008/01/07 11:33:58  gerlof
+** Cosmetic changes.
+**
+** Revision 1.49  2008/01/07 10:18:24  gerlof
+** Implement possibility to make summaries with atopsar.
+**
+** Revision 1.48  2007/03/22 10:12:17  gerlof
+** Support for io counters (>= kernel 2.6.20).
+**
+** Revision 1.47  2007/03/21 14:22:34  gerlof
+** Handle io counters maintained from 2.6.20
+**
+** Revision 1.46  2007/03/20 11:13:15  gerlof
+** Cosmetic changes.
+**
+** Revision 1.45  2007/03/09 12:39:59  gerlof
+** Do not allow 'N' and 'D' when kernel-patch is not installed.
+**
+** Revision 1.44  2007/02/13 10:31:53  gerlof
+** Removal of external declarations.
+**
+** Revision 1.43  2007/01/18 10:41:45  gerlof
+** Add support for colors.
+** Add support for automatic determination of most critical resource.
+**
+** Revision 1.42  2006/11/13 13:48:36  gerlof
+** Implement load-average counters, context-switches and interrupts.
+**
+** Revision 1.41  2006/04/03 05:42:35  gerlof
+** *** empty log message ***
+**
+** Revision 1.40  2006/02/07 08:28:26  gerlof
+** Improve screen-handling (less flashing) by exchanging clear()
+** by werase() (contribution Folkert van Heusden).
+**
+** Revision 1.39  2005/11/04 14:16:16  gerlof
+** Minor bug-solutions.
+**
+** Revision 1.38  2005/10/28 09:52:03  gerlof
+** All flags/subcommands are defined as macro's.
+** Subcommand 'p' has been changed to 'z' (pause).
+**
+** Revision 1.37  2005/10/24 06:12:17  gerlof
+** Flag -L modified into -l.
+**
+** Revision 1.36  2005/10/21 09:50:46  gerlof
+** Per-user accumulation of resource consumption.
+** Possibility to send signal to process.
+**
+** Revision 1.35  2004/12/14 15:06:41  gerlof
+** Implementation of patch-recognition for disk and network-statistics.
+**
+** Revision 1.34  2004/09/27 11:01:13  gerlof
+** Corrected usage-info as suggested by Edelhard Becker.
+**
+** Revision 1.33  2004/09/13 09:19:14  gerlof
+** Modify subcommands (former 's' -> 'v', 'v' -> 'V', new 's').
+**
+** Revision 1.32  2004/08/31 09:52:47  root
+** information about underlying threads.
+**
+** Revision 1.31  2004/06/01 11:57:58  gerlof
+** Regular expressions for selections on process-name and user-name.
+**
+** Revision 1.30  2003/07/07 09:27:24  gerlof
+** Cleanup code (-Wall proof).
+**
+** Revision 1.29  2003/07/03 11:16:42  gerlof
+** Implemented subcommand `r' (reset).
+**
+** Revision 1.28  2003/06/30 11:29:49  gerlof
+** Handle configuration file ~/.atoprc
+**
+** Revision 1.27  2003/06/24 06:21:57  gerlof
+** Limit number of system resource lines.
+**
+** Revision 1.26  2003/02/07 10:19:18  gerlof
+** Possibility to show the version number and date.
+**
+** Revision 1.25  2003/01/17 07:32:16  gerlof
+** Show the full command-line per process (option 'c').
+**
+** Revision 1.24  2002/10/30 13:47:20  gerlof
+** Generate notification for statistics since boot.
+**
+** Revision 1.23  2002/10/08 12:00:30  gerlof
+** *** empty log message ***
+**
+** Revision 1.22  2002/09/26 14:17:39  gerlof
+** No beep when resizing the window.
+**
+** Revision 1.21  2002/09/26 13:52:26  gerlof
+** Limit header lines by not showing disks.
+**
+** Revision 1.20  2002/09/18 07:15:59  gerlof
+** Modified viewflag to rawreadflag.
+**
+** Revision 1.19  2002/09/17 13:17:39  gerlof
+** Allow key 'T' to be pressed to view previous sample in raw file.
+**
+** Revision 1.18  2002/08/30 07:11:50  gerlof
+** Minor changes to support viewing of raw atop data.
+**
+** Revision 1.17  2002/08/27 12:10:12  gerlof
+** Allow raw data file to be written and to be read (with compression).
+**
+** Revision 1.16  2002/07/24 11:12:46  gerlof
+** Changed to ease porting to other UNIX-platforms.
+**
+** Revision 1.15  2002/07/11 09:12:05  root
+** Some minor updates.
+**
+** Revision 1.14  2002/07/10 05:00:37  root
+** Counters pin/pout renamed to swin/swout (Linux conventions).
+**
+** Revision 1.13  2002/07/08 09:29:49  root
+** Limitation for username and groupname (8 characters truncate).
+**
+** Revision 1.12  2002/07/02 07:14:02  gerlof
+** More positions for the name of the disk-unit in the DSK-line.
+**
+** Revision 1.11  2002/01/22 13:40:42  gerlof
+** Support for number of cpu's.
+** Check if the window is large enough for the system-statistics.
+**
+** Revision 1.10  2001/11/30 09:09:36  gerlof
+** Cosmetic chnage.
+**
+** Revision 1.9  2001/11/29 10:41:44  gerlof
+** *** empty log message ***
+**
+** Revision 1.8  2001/11/29 10:38:16  gerlof
+** Exit-code correctly printed.
+**
+** Revision 1.7  2001/11/26 11:18:45  gerlof
+** Modified generic output in case that the kernel-patch is not installed.
+**
+** Revision 1.6  2001/11/13 08:24:50  gerlof
+** Show blank columns for sockets and disk I/O when no kernel-patch installed.
+**
+** Revision 1.5  2001/11/07 09:19:28  gerlof
+** Use /proc instead of /dev/kmem for process-level statistics.
+**
+** Revision 1.4  2001/10/05 13:46:32  gerlof
+** Implemented paging through the process-list
+**
+** Revision 1.3  2001/10/04 08:47:27  gerlof
+** Improved handling of error-messages
+**
+** Revision 1.2  2001/10/03 08:57:53  gerlof
+** Improved help-screen shown in scrollable window
+**
+** Revision 1.1  2001/10/02 10:43:34  gerlof
+** Initial revision
+**
+*/
+
+static const char rcsid[] = "$Id: showgeneric.c,v 1.71 2010/10/25 19:08:32 gerlof Exp $";
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+#include <signal.h>
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <curses.h>
+#include <pwd.h>
+#include <grp.h>
+#include <regex.h>
+
+#include "atop.h"
+#include "photoproc.h"
+#include "photosyst.h"
+#include "showgeneric.h"
+#include "showlinux.h"
+
+static struct pselection procsel = {"", {USERSTUB, }, {0,},
+                                    "", 0, { 0, },  "", 0, { 0, } };
+static struct sselection syssel;
+
+static void    showhelp(int);
+static int     paused;         /* boolean: currently in pause-mode     */
+static int     fixedhead;      /* boolean: fixate header-lines         */
+static int     sysnosort;      /* boolean: suppress sort of resources  */
+static int     avgval;         /* boolean: average values i.s.o. total */
+static int     suppressexit;   /* boolean: suppress exited processes   */
+
+static char    showtype  = MPROCGEN;
+static char    showorder = MSORTCPU;
+
+static int     maxcpulines = 999;  /* maximum cpu       lines          */
+static int     maxdsklines = 999;  /* maximum disk      lines          */
+static int     maxmddlines = 999;  /* maximum MDD       lines          */
+static int     maxlvmlines = 999;  /* maximum LVM       lines          */
+static int     maxintlines = 999;  /* maximum interface lines          */
+static int     maxnfslines = 999;  /* maximum nfs mount lines          */
+static int     maxcontlines = 999; /* maximum container lines          */
+
+static short   colorinfo   = COLOR_GREEN;
+static short   coloralmost = COLOR_CYAN;
+static short   colorcrit   = COLOR_RED;
+static short   colorthread = COLOR_YELLOW;
+
+static int     cumusers(struct tstat **, struct tstat *, int);
+static int     cumprogs(struct tstat **, struct tstat *, int);
+static int     cumconts(struct tstat **, struct tstat *, int);
+static void    accumulate(struct tstat *, struct tstat *);
+
+static int     procsuppress(struct tstat *, struct pselection *);
+static void    limitedlines(void);
+static long    getnumval(char *, long, int);
+static void    generic_init(void);
+
+
+static int     (*procsort[])(const void *, const void *) = {
+                       [MSORTCPU&0x1f]=compcpu, 
+                       [MSORTMEM&0x1f]=compmem, 
+                       [MSORTDSK&0x1f]=compdsk, 
+                       [MSORTNET&0x1f]=compnet, 
+};
+
+extern proc_printpair ownprocs[];
+
+/*
+** global: incremented by -> key and decremented by <- key
+*/
+int    startoffset;
+
+/*
+** print the deviation-counters on process- and system-level
+*/
+char
+generic_samp(time_t curtime, int nsecs,
+           struct devtstat *devtstat, struct sstat *sstat, 
+           int nexit, unsigned int noverflow, char flag)
+{
+       static int      callnr = 0;
+       char            *p;
+
+
+       register int    i, curline, statline, nproc;
+       int             firstproc = 0, plistsz, alistsz, killpid, killsig;
+       int             lastchar;
+       char            format1[16], format2[16], hhmm[16];
+       char            *statmsg = NULL, statbuf[80], genline[80];
+       char             *lastsortp, curorder, autoorder;
+       char            buf[33];
+       struct passwd   *pwd;
+       struct syscap   syscap;
+
+       /*
+       ** curlist points to the active list of tstat-pointers that
+       ** should be displayed; ncurlist indicates the number of entries in
+       ** this list
+       */
+       struct tstat    **curlist;
+       int             ncurlist;
+
+       /*
+       ** tXcumlist is a list of tstat-structs holding one entry
+       ** per accumulated (per user or per program) group of processes
+       **
+       ** Xcumlist contains the pointers to all structs in tXcumlist
+       ** 
+       ** these lists will only be allocated 'lazy'
+       ** only when accumulation is requested
+       */
+       struct tstat    *tpcumlist = 0;         // per program accumulation
+       struct tstat    **pcumlist = 0;
+       int             npcum      = 0;
+       char            plastorder = 0;
+
+       struct tstat    *tucumlist = 0;         // per user accumulation
+       struct tstat    **ucumlist = 0;
+       int             nucum      = 0;
+       char            ulastorder = 0;
+
+       struct tstat    *tccumlist = 0;         // per container accumulation
+       struct tstat    **ccumlist = 0;
+       int             nccum      = 0;
+       char            clastorder = 0;
+
+       /*
+       ** tsklist contains the pointers to all structs in tstat
+       ** sorted on process with the related threads immediately
+       ** following the process
+       **
+       ** this list will be allocated 'lazy'
+       */
+       struct tstat    **tsklist  = 0;
+       int             ntsk       = 0;
+       char            tlastorder = 0;
+       char            zipagain   = 0;
+       char            tdeviate   = 0;
+
+       /*
+       ** sellist contains the pointers to the structs in tstat
+       ** that are currently selected on basis of a particular
+       ** username (regexp), program name (regexp), container name
+       ** or suppressed exited procs
+       **
+       ** this list will be allocated 'lazy'
+       */
+       struct tstat    **sellist  = 0;
+       int             nsel       = 0;
+       char            slastorder = 0;
+
+       char            threadallowed = 0;
+
+
+       if (callnr == 0)        /* first call? */
+               generic_init();
+
+       callnr++;
+
+       startoffset = 0;
+
+       /*
+       ** compute the total capacity of this system for the 
+       ** four main resources
+       */
+       totalcap(&syscap, sstat, devtstat->procactive, devtstat->nprocactive);
+
+       /*
+       ** sort per-cpu                 statistics on busy percentage
+       ** sort per-logical-volume      statistics on busy percentage
+       ** sort per-multiple-device     statistics on busy percentage
+       ** sort per-disk                statistics on busy percentage
+       ** sort per-interface           statistics on busy percentage (if known)
+       */
+       if (!sysnosort)
+       {
+               if (sstat->cpu.nrcpu > 1 && maxcpulines > 0)
+                       qsort(sstat->cpu.cpu, sstat->cpu.nrcpu,
+                              sizeof sstat->cpu.cpu[0], cpucompar);
+
+               if (sstat->dsk.nlvm > 1 && maxlvmlines > 0)
+                       qsort(sstat->dsk.lvm, sstat->dsk.nlvm,
+                              sizeof sstat->dsk.lvm[0], diskcompar);
+
+               if (sstat->dsk.nmdd > 1 && maxmddlines > 0)
+                       qsort(sstat->dsk.mdd, sstat->dsk.nmdd,
+                              sizeof sstat->dsk.mdd[0], diskcompar);
+
+               if (sstat->dsk.ndsk > 1 && maxdsklines > 0)
+                       qsort(sstat->dsk.dsk, sstat->dsk.ndsk,
+                              sizeof sstat->dsk.dsk[0], diskcompar);
+
+               if (sstat->intf.nrintf > 1 && maxintlines > 0)
+                       qsort(sstat->intf.intf, sstat->intf.nrintf,
+                              sizeof sstat->intf.intf[0], intfcompar);
+
+               if (sstat->nfs.nfsmounts.nrmounts > 1 && maxnfslines > 0)
+                       qsort(sstat->nfs.nfsmounts.nfsmnt,
+                             sstat->nfs.nfsmounts.nrmounts,
+                             sizeof sstat->nfs.nfsmounts.nfsmnt[0],
+                               nfsmcompar);
+
+               if (sstat->cfs.nrcontainer > 1 && maxcontlines > 0)
+                       qsort(sstat->cfs.cont, sstat->cfs.nrcontainer,
+                              sizeof sstat->cfs.cont[0], contcompar);
+       }
+
+       /*
+       ** loop in which the system resources and the list of active
+       ** processes are shown; the loop will be preempted by receiving
+       ** a timer-signal or when the trigger-button is pressed.
+       */
+       while (1)
+       {
+               curline = 1;
+
+               /*
+                       ** prepare screen or file output for new sample
+               */
+               if (screen)
+                               werase(stdscr);
+               else
+                       printf("\n\n");
+
+               /*
+               ** print general headerlines
+               */
+               convdate(curtime, format1);       /* date to ascii string   */
+               convtime(curtime, format2);       /* time to ascii string   */
+
+               if (screen)
+                       attron(A_REVERSE);
+
+                int seclen     = val2elapstr(nsecs, buf);
+                int lenavail   = (screen ? COLS : linelen) -
+                                               49 - seclen - utsnodenamelen;
+                int len1       = lenavail / 3;
+                int len2       = lenavail - len1 - len1; 
+
+               printg("ATOP - %s%*s%s  %s%*s%c%c%c%c%c%c%c%c%c%c%c%c%c%c%*s%s"
+                      " elapsed", 
+                       utsname.nodename, len1, "", 
+                       format1, format2, len1, "",
+                       threadview                    ? MTHREAD    : '-',
+                       fixedhead                     ? MSYSFIXED  : '-',
+                       sysnosort                     ? MSYSNOSORT : '-',
+                       deviatonly                    ? '-'        : MALLPROC,
+                       usecolors                     ? '-'        : MCOLORS,
+                       avgval                        ? MAVGVAL    : '-',
+                       calcpss                       ? MCALCPSS   : '-',
+                       suppressexit                  ? MSUPEXITS  : '-',
+                       procsel.userid[0] != USERSTUB ? MSELUSER   : '-',
+                       procsel.prognamesz            ? MSELPROC   : '-',
+                       procsel.container[0]          ? MSELCONT   : '-',
+                       procsel.pid[0] != 0           ? MSELPID    : '-',
+                       procsel.argnamesz             ? MSELARG    : '-',
+                       syssel.lvmnamesz +
+                       syssel.dsknamesz +
+                       syssel.itfnamesz              ? MSELSYS    : '-',
+                       len2, "", buf);
+
+               if (screen)
+                       attroff(A_REVERSE);
+                else
+                        printg("\n");
+
+               /*
+               ** print cumulative system- and user-time for all processes
+               */
+               pricumproc(sstat, devtstat, nexit, noverflow, avgval, nsecs);
+
+               if (noverflow)
+               {
+                       snprintf(statbuf, sizeof statbuf, 
+                                "Only %d exited processes handled "
+                                "-- %u skipped!", nexit, noverflow);
+
+                       statmsg = statbuf;
+               }
+
+               curline=2;
+
+               /*
+               ** print other lines of system-wide statistics
+               */
+               if (showorder == MSORTAUTO)
+                       autoorder = MSORTCPU;
+               else
+                       autoorder = showorder;
+
+               curline = prisyst(sstat, curline, nsecs, avgval,
+                                 fixedhead, &syssel, &autoorder,
+                                 maxcpulines, maxdsklines, maxmddlines,
+                                 maxlvmlines, maxintlines, maxnfslines,
+                                 maxcontlines);
+
+               /*
+               ** if system-wide statistics do not fit,
+               ** limit the number of variable resource lines
+               ** and try again
+               */
+               if (screen && curline+2 > LINES)
+               {
+                       curline = 2;
+
+                       move(curline, 0);
+                       clrtobot();
+                       move(curline, 0);
+
+                       limitedlines();
+                       
+                       curline = prisyst(sstat, curline, nsecs, avgval,
+                                       fixedhead,  &syssel, &autoorder,
+                                       maxcpulines, maxdsklines, maxmddlines,
+                                       maxlvmlines, maxintlines, maxnfslines,
+                                       maxcontlines);
+
+                       /*
+                       ** if system-wide statistics still do not fit,
+                       ** the window is really to small
+                       */
+                       if (curline+2 > LINES)
+                       {
+                               endwin();       // finish curses interface
+
+                               fprintf(stderr,
+                                     "Not enough screen-lines available "
+                                     "(need at least %d lines)\n", curline+2);
+                               fprintf(stderr, "Please resize window....\n");
+
+                               cleanstop(1);
+                       }
+                       else
+                       {
+                               statmsg = "Number of variable resources"
+                                         " limited to fit in this window";
+                       }
+               }
+
+               statline = curline;
+
+               if (screen)
+                       move(curline, 0);
+
+               if (statmsg)
+               {
+                       if (screen)
+                       {
+                               clrtoeol();
+                               if (usecolors)
+                                       attron(COLOR_PAIR(COLORINFO));
+                       }
+
+                       printg(statmsg);
+
+                       if (screen)
+                       {
+                               if (usecolors)
+                                       attroff(COLOR_PAIR(COLORINFO));
+                       }
+
+                       statmsg = NULL;
+               }
+               else
+               {
+                       if (flag&RRBOOT)
+                       {
+                               if (screen)
+                               {
+                                       if (usecolors)
+                                               attron(COLOR_PAIR(COLORINFO));
+
+                                       attron(A_BLINK);
+
+                                       printg("%*s", (COLS-45)/2, " ");
+                               }
+                               else
+                               {
+                                       printg("                   ");
+                               }
+
+                                       printg("*** system and process activity "
+                                      "since boot ***");
+
+                               if (screen)
+                               {
+                                       if (usecolors)
+                                               attroff(COLOR_PAIR(COLORINFO));
+                                       attroff(A_BLINK);
+                               }
+                       }
+               }
+
+               /*
+               ** select the required list with tasks to be shown
+               **
+               ** if cumulative figures required, accumulate resource
+               ** consumption of all processes in the current list
+               */
+               switch (showtype)
+               {
+                  case MCUMUSER:
+                       threadallowed = 0;
+
+                       if (ucumlist)   /* previous list still available? */
+                       {
+                                free(ucumlist);
+                                free(tucumlist);
+                               ulastorder = 0;
+                       }
+
+                       if (deviatonly)
+                               nproc = devtstat->nprocactive;
+                       else
+                               nproc = devtstat->nprocall;
+
+                       /*
+                       ** allocate space for new (temporary) list with
+                       ** one entry per user (list has worst-case size)
+                       */
+                       tucumlist = calloc(sizeof(struct tstat),    nproc);
+                       ucumlist  = malloc(sizeof(struct tstat *) * nproc);
+
+                       ptrverify(tucumlist,
+                               "Malloc failed for %d ucum procs\n", nproc);
+                       ptrverify(ucumlist,
+                               "Malloc failed for %d ucum ptrs\n",  nproc);
+
+                       for (i=0; i < nproc; i++)
+                       {
+                               /* fill pointers */
+                               ucumlist[i] = tucumlist+i;
+                       }
+
+                       nucum = cumusers(deviatonly ?
+                                               devtstat->procactive :
+                                               devtstat->procall,
+                                               tucumlist, nproc);
+
+                       curlist   = ucumlist;
+                       ncurlist  = nucum;
+                       lastsortp = &ulastorder;
+                       break;
+
+                  case MCUMPROC:
+                       threadallowed = 0;
+
+                       if (pcumlist)   /* previous list still available? */
+                       {
+                                free(pcumlist);
+                                free(tpcumlist);
+                               plastorder = 0;
+                       }
+
+                       if (deviatonly)
+                               nproc = devtstat->nprocactive;
+                       else
+                               nproc = devtstat->nprocall;
+
+                       /*
+                       ** allocate space for new (temporary) list with
+                       ** one entry per program (list has worst-case size)
+                       */
+                       tpcumlist = calloc(sizeof(struct tstat),    nproc);
+                       pcumlist  = malloc(sizeof(struct tstat *) * nproc);
+
+                       ptrverify(tpcumlist,
+                               "Malloc failed for %d pcum procs\n", nproc);
+                       ptrverify(pcumlist,
+                               "Malloc failed for %d pcum ptrs\n",  nproc);
+
+                       for (i=0; i < nproc; i++)
+                       {       
+                               /* fill pointers */
+                               pcumlist[i] = tpcumlist+i;
+                       }
+
+                       npcum = cumprogs(deviatonly ?
+                                               devtstat->procactive :
+                                               devtstat->procall,
+                                               tpcumlist, nproc);
+
+                       curlist   = pcumlist;
+                       ncurlist  = npcum;
+                       lastsortp = &plastorder;
+                       break;
+
+                  case MCUMCONT:
+                       threadallowed = 0;
+
+                       if (ccumlist)   /* previous list still available? */
+                       {
+                                free(ccumlist);
+                                free(tccumlist);
+                               clastorder = 0;
+                       }
+
+                       if (deviatonly)
+                               nproc = devtstat->nprocactive;
+                       else
+                               nproc = devtstat->nprocall;
+
+                       /*
+                       ** allocate space for new (temporary) list with
+                       ** one entry per user (list has worst-case size)
+                       */
+                       tccumlist = calloc(sizeof(struct tstat),    nproc);
+                       ccumlist  = malloc(sizeof(struct tstat *) * nproc);
+
+                       ptrverify(tccumlist,
+                               "Malloc failed for %d ccum procs\n", nproc);
+                       ptrverify(ccumlist,
+                               "Malloc failed for %d ccum ptrs\n",  nproc);
+
+                       for (i=0; i < nproc; i++)
+                       {
+                               /* fill pointers */
+                               ccumlist[i] = tccumlist+i;
+                       }
+
+                       nccum = cumconts(deviatonly ?
+                                               devtstat->procactive :
+                                               devtstat->procall,
+                                               tccumlist, nproc);
+
+                       curlist   = ccumlist;
+                       ncurlist  = nccum;
+                       lastsortp = &clastorder;
+                       break;
+
+                  default:
+                       threadallowed = 1;
+
+                       if (deviatonly && showtype  != MPROCMEM &&
+                                         showorder != MSORTMEM   )
+                       {
+                               curlist   = devtstat->procactive;
+                               ncurlist  = devtstat->nprocactive;
+                       }
+                       else
+                       {
+                               curlist   = devtstat->procall;
+                               ncurlist  = devtstat->nprocall;
+                       }
+
+                       lastsortp = &tlastorder;
+
+                       if ( procsel.userid[0] == USERSTUB &&
+                           !procsel.prognamesz            &&
+                           !procsel.container[0]          &&
+                           !procsel.argnamesz             &&
+                           !procsel.pid[0]                &&
+                           !suppressexit                    )
+                               /* no selection wanted */
+                               break;
+
+                       /*
+                       ** selection specified for tasks:
+                       ** create new (worst case) pointer list if needed
+                       */
+                       if (sellist)    // remove previous list if needed
+                               free(sellist);
+
+                       sellist = malloc(sizeof(struct tstat *) * ncurlist);
+
+                       ptrverify(sellist,
+                              "Malloc failed for %d select ptrs\n", ncurlist);
+
+                       for (i=nsel=0; i < ncurlist; i++)
+                       {
+                               if (procsuppress(*(curlist+i), &procsel))
+                                       continue;
+
+                               if (curlist[i]->gen.state == 'E' &&
+                                   suppressexit                   )
+                                       continue;
+
+                               sellist[nsel++] = curlist[i]; 
+                       }
+
+                       curlist    = sellist;
+                       ncurlist   = nsel;
+                       tlastorder = 0; /* new sort and zip normal view */
+                       slastorder = 0; /* new sort and zip now         */
+                       lastsortp  = &slastorder;
+               }
+
+               /*
+               ** sort the list in required order 
+               ** (default CPU-consumption) and print the list
+               */
+               if (showorder == MSORTAUTO)
+                       curorder = autoorder;
+               else
+                       curorder = showorder;
+
+               /*
+               ** determine size of list to be displayed
+               */
+               if (screen)
+                       plistsz = LINES-curline-2;
+               else
+                       if (threadview && threadallowed)
+                               plistsz = devtstat->ntaskactive;
+                       else
+                               plistsz = ncurlist;
+
+
+               if (ncurlist > 0 && plistsz > 0)
+               {
+                       /*
+                       ** if sorting order is changed, sort again
+                       */
+                       if (*lastsortp != curorder)
+                       {
+                               qsort(curlist, ncurlist,
+                                       sizeof(struct tstat *),
+                                       procsort[(int)curorder&0x1f]);
+
+                               *lastsortp = curorder;
+
+                               zipagain = 1;
+                       }
+
+                       if (threadview && threadallowed)
+                       {
+                               int ntotal, j, t;
+
+                               if (deviatonly && showtype  != MPROCMEM &&
+                                                 showorder != MSORTMEM   )
+                                       ntotal = devtstat->ntaskactive;
+                               else
+                                       ntotal = devtstat->ntaskall;
+
+                               /*
+                               ** check if existing pointer list still usable
+                               ** if not, allocate new pointer list to be able
+                               ** to zip process list with references to threads
+                               */
+                               if (!tsklist || ntsk != ntotal ||
+                                                       tdeviate != deviatonly)
+                               {
+                                       if (tsklist)
+                                               free(tsklist);  // remove current
+
+                                       tsklist = malloc(sizeof(struct tstat *)
+                                                                   * ntotal);
+
+                                       ptrverify(tsklist,
+                                            "Malloc failed for %d taskptrs\n",
+                                            ntotal);
+
+                                       ntsk     = ntotal;
+                                       tdeviate = deviatonly;
+
+                                       zipagain = 1;
+                               }
+                               else
+                                       j = ntotal;
+
+                               if (zipagain)
+                               {
+                                       struct tstat *tall = devtstat->taskall;
+                                       struct tstat *pcur;
+
+                                       for (i=j=0; i < ncurlist; i++)
+                                       {
+                                           pcur = curlist[i];
+
+                                           tsklist[j++] = pcur;
+
+                                           for (t = pcur - tall + 1;
+                                                t < devtstat->ntaskall &&
+                                                pcur->gen.tgid         &&
+                                                pcur->gen.tgid == 
+                                                   (tall+t)->gen.tgid;
+                                                t++)
+                                           {
+                                               if (deviatonly &&
+                                                       showtype  != MPROCMEM &&
+                                                       showorder != MSORTMEM   )
+                                               {
+                                                 if (!(tall+t)->gen.wasinactive)
+                                                 {
+                                                       tsklist[j++] = tall+t;
+                                                 }
+                                               }
+                                               else
+                                                       tsklist[j++] = tall+t;
+                                           }
+                                       }
+
+                                       zipagain = 0;
+                               }
+
+                               curlist  = tsklist;
+                               ncurlist = j;
+                       }
+
+                       /*
+                       ** print the header
+                       ** first determine the column-header for the current
+                       ** sorting order of processes
+                       */
+                       if (screen)
+                       {
+                               attron(A_REVERSE);
+                                move(curline+1, 0);
+                        }
+
+                       priphead(firstproc/plistsz+1, (ncurlist-1)/plistsz+1,
+                                       &showtype, &curorder,
+                                       showorder == MSORTAUTO ? 1 : 0);
+
+                       if (screen)
+                       {
+                               attroff(A_REVERSE);
+                               clrtobot();
+                       }
+
+                       /*
+                       ** print the list
+                       */
+                       priproc(curlist, firstproc, ncurlist, curline+2,
+                               firstproc/plistsz+1, (ncurlist-1)/plistsz+1,
+                               showtype, curorder, &syscap, nsecs, avgval);
+               }
+
+               alistsz = ncurlist;     /* preserve size of active list */
+
+               /*
+               ** in case of writing to a terminal, the user can also enter
+               ** a character to switch options, etc
+               */
+               if (screen)
+               {
+                       /*
+                       ** show blinking pause-indication if necessary
+                       */
+                       if (paused)
+                       {
+                               move(statline, COLS-6);
+                               attron(A_BLINK);
+                               attron(A_REVERSE);
+                               printw("PAUSED");
+                               attroff(A_REVERSE);
+                               attroff(A_BLINK);
+                       }
+
+                       /*
+                       ** await input-character or interval-timer expiration
+                       */
+                       switch ( (lastchar = mvgetch(statline, 0)) )
+                       {
+                          /*
+                          ** timer expired
+                          */
+                          case ERR:
+                          case 0:
+                               timeout(0);
+                               (void) getch();
+                               timeout(-1);
+                               if (tpcumlist) free(tpcumlist);
+                               if (pcumlist)  free(pcumlist);
+                               if (tucumlist) free(tucumlist);
+                               if (ucumlist)  free(ucumlist);
+                               if (tccumlist) free(tccumlist);
+                               if (ccumlist)  free(ccumlist);
+                               if (tsklist)   free(tsklist);
+                               if (sellist)   free(sellist);
+
+                               return lastchar;        
+
+                          /*
+                          ** stop it
+                          */
+                          case MQUIT:
+                               move(LINES-1, 0);
+                               clrtoeol();
+                               refresh();
+                               cleanstop(0);
+
+                          /*
+                          ** manual trigger for next sample
+                          */
+                          case MSAMPNEXT:
+                               if (paused)
+                                       break;
+
+                               getalarm(0);
+
+                               if (tpcumlist) free(tpcumlist);
+                               if (pcumlist)  free(pcumlist);
+                               if (tucumlist) free(tucumlist);
+                               if (ucumlist)  free(ucumlist);
+                               if (tccumlist) free(tccumlist);
+                               if (ccumlist)  free(ccumlist);
+                               if (tsklist)   free(tsklist);
+                               if (sellist)   free(sellist);
+
+                               return lastchar;
+
+                          /*
+                          ** manual trigger for previous sample
+                          */
+                          case MSAMPPREV:
+                               if (!rawreadflag)
+                               {
+                                       statmsg = "Only allowed when viewing "
+                                                 "raw file!";
+                                       beep();
+                                       break;
+                               }
+
+                               if (paused)
+                                       break;
+
+                               if (tpcumlist) free(tpcumlist);
+                               if (pcumlist)  free(pcumlist);
+                               if (tucumlist) free(tucumlist);
+                               if (ucumlist)  free(ucumlist);
+                               if (tccumlist) free(tccumlist);
+                               if (ccumlist)  free(ccumlist);
+                               if (tsklist)   free(tsklist);
+                               if (sellist)   free(sellist);
+
+                               return lastchar;
+
+                           /*
+                          ** branch to certain time stamp
+                           */
+                           case MSAMPBRANCH:
+                                if (!rawreadflag)
+                                {
+                                        statmsg = "Only allowed when viewing "
+                                                  "raw file!";
+                                        beep();
+                                        break;
+                                }
+
+                                if (paused)
+                                        break;
+
+                                echo();
+                                move(statline, 0);
+                                clrtoeol();
+                                printw("Enter new time (format hh:mm): ");
+
+                                hhmm[0] = '\0';
+                                scanw("%15s\n", hhmm);
+                                noecho();
+
+                                if ( !hhmm2secs(hhmm, &begintime) )
+                                {
+                                        move(statline, 0);
+                                        clrtoeol();
+                                        statmsg = "Wrong time format!";
+                                        beep();
+                                        begintime = 0;
+                                        break;
+                                }
+
+                               if (tpcumlist) free(tpcumlist);
+                               if (pcumlist)  free(pcumlist);
+                               if (tucumlist) free(tucumlist);
+                               if (ucumlist)  free(ucumlist);
+                               if (tccumlist) free(tccumlist);
+                               if (ccumlist)  free(ccumlist);
+                               if (tsklist)   free(tsklist);
+                               if (sellist)   free(sellist);
+
+                                return lastchar;
+
+                          /*
+                          ** sort order automatically depending on
+                          ** most busy resource
+                          */
+                          case MSORTAUTO:
+                               showorder = MSORTAUTO;
+                               firstproc = 0;
+                               break;
+
+                          /*
+                          ** sort in cpu-activity order
+                          */
+                          case MSORTCPU:
+                               showorder = MSORTCPU;
+                               firstproc = 0;
+                               break;
+
+                          /*
+                          ** sort in memory-consumption order
+                          */
+                          case MSORTMEM:
+                               showorder = MSORTMEM;
+                               firstproc = 0;
+                               break;
+
+                          /*
+                          ** sort in disk-activity order
+                          */
+                          case MSORTDSK:
+                               if ( !(supportflags & IOSTAT) )
+                               {
+                                       statmsg = "No disk-activity figures "
+                                                 "available; request ignored!";
+                                       break;
+                               }
+                               showorder = MSORTDSK;
+                               firstproc = 0;
+                               break;
+
+                          /*
+                          ** sort in network-activity order
+                          */
+                          case MSORTNET:
+                               if ( !(supportflags & NETATOP) )
+                               {
+                                       statmsg = "Kernel module 'netatop' not "
+                                                 "active or no root privs; "
+                                                 "request ignored!";
+                                       break;
+                               }
+                               showorder = MSORTNET;
+                               firstproc = 0;
+                               break;
+
+                          /*
+                          ** general figures per process
+                          */
+                          case MPROCGEN:
+                               showtype  = MPROCGEN;
+
+                               if (showorder != MSORTAUTO)
+                                       showorder = MSORTCPU;
+
+                               firstproc = 0;
+                               break;
+
+                          /*
+                          ** memory-specific figures per process
+                          */
+                          case MPROCMEM:
+                               showtype  = MPROCMEM;
+
+                               if (showorder != MSORTAUTO)
+                                       showorder = MSORTMEM;
+
+                               firstproc = 0;
+                               break;
+
+                          /*
+                          ** disk-specific figures per process
+                          */
+                          case MPROCDSK:
+                               if ( !(supportflags & IOSTAT) )
+                               {
+                                       statmsg = "No disk-activity figures "
+                                                 "available; request ignored!";
+                                       break;
+                               }
+
+                               showtype  = MPROCDSK;
+
+                               if (showorder != MSORTAUTO)
+                                       showorder = MSORTDSK;
+
+                               firstproc = 0;
+                               break;
+
+                          /*
+                          ** network-specific figures per process
+                          */
+                          case MPROCNET:
+                               if ( !(supportflags & NETATOP) )
+                               {
+                                       statmsg = "Kernel module 'netatop' not "
+                                                 "active or no root privs; "
+                                                 "request ignored!";
+                                       break;
+                               }
+
+                               showtype  = MPROCNET;
+
+                               if (showorder != MSORTAUTO)
+                                       showorder = MSORTNET;
+
+                               firstproc = 0;
+                               break;
+
+                          /*
+                          ** various info per process
+                          */
+                          case MPROCVAR:
+                               showtype  = MPROCVAR;
+                               firstproc = 0;
+                               break;
+
+                          /*
+                          ** command line per process
+                          */
+                          case MPROCARG:
+                               showtype  = MPROCARG;
+                               firstproc = 0;
+                               break;
+
+                          /*
+                          ** own defined output per process
+                          */
+                          case MPROCOWN:
+                               if (! ownprocs[0].f)
+                               {
+                                       statmsg = "Own process line is not "
+                                                 "configured in rc-file; "
+                                                 "request ignored";
+                                       break;
+                               }
+
+                               showtype  = MPROCOWN;
+                               firstproc = 0;
+                               break;
+
+                          /*
+                          ** scheduling-values per process
+                          */
+                          case MPROCSCH:
+                               showtype  = MPROCSCH;
+
+                               if (showorder != MSORTAUTO)
+                                       showorder = MSORTCPU;
+
+                               firstproc = 0;
+                               break;
+
+                          /*
+                          ** accumulated resource consumption per user
+                          */
+                          case MCUMUSER:
+                               statmsg = "Consumption per user; use 'a' to "
+                                         "toggle between all/active processes";
+
+                               showtype  = MCUMUSER;
+                               firstproc = 0;
+                               break;
+
+                          /*
+                          ** accumulated resource consumption per program
+                          */
+                          case MCUMPROC:
+                               statmsg = "Consumption per program; use 'a' to "
+                                         "toggle between all/active processes";
+
+                               showtype  = MCUMPROC;
+                               firstproc = 0;
+                               break;
+
+                          /*
+                          ** accumulated resource consumption per container
+                          */
+                          case MCUMCONT:
+                               statmsg = "Consumption per container; use 'a' to "
+                                         "toggle between all/active processes";
+
+                               showtype  = MCUMCONT;
+                               firstproc = 0;
+                               break;
+
+                          /*
+                          ** help wanted?
+                          */
+                          case MHELP1:
+                          case MHELP2:
+                               alarm(0);       /* stop the clock         */
+
+                               move(1, 0);
+                               clrtobot();     /* blank the screen */
+                               refresh();
+
+                               showhelp(2);
+
+                               move(statline, 0);
+
+                               if (interval && !paused && !rawreadflag)
+                                       alarm(3); /* force new sample     */
+
+                               firstproc = 0;
+                               break;
+
+                          /*
+                          ** send signal to process
+                          */
+                          case MKILLPROC:
+                               if (rawreadflag)
+                               {
+                                       statmsg = "Not possible when viewing "
+                                                 "raw file!";
+                                       beep();
+                                       break;
+                               }
+
+                               alarm(0);       /* stop the clock */
+
+                               killpid = getnumval("Pid of process: ",
+                                                    0, statline);
+
+                               switch (killpid)
+                               {
+                                  case 0:
+                                  case -1:
+                                       break;
+
+                                  case 1:
+                                       statmsg = "Sending signal to pid 1 not "
+                                                 "allowed!";
+                                       beep();
+                                       break;
+
+                                  default:
+                                       clrtoeol();
+                                       killsig = getnumval("Signal [%d]: ",
+                                                    15, statline);
+
+                                       if ( kill(killpid, killsig) == -1)
+                                       {
+                                               statmsg = "Not possible to "
+                                                    "send signal to this pid!";
+                                               beep();
+                                       }
+                               }
+
+                               if (!paused)
+                                       alarm(3); /* set short timer */
+
+                               firstproc = 0;
+                               break;
+
+                          /*
+                          ** change interval timeout
+                          */
+                          case MINTERVAL:
+                               if (rawreadflag)
+                               {
+                                       statmsg = "Not possible when viewing "
+                                                 "raw file!";
+                                       beep();
+                                       break;
+                               }
+
+                               alarm(0);       /* stop the clock */
+
+                               interval = getnumval("New interval in seconds "
+                                                    "(now %d): ",
+                                                    interval, statline);
+
+                               if (interval)
+                               {
+                                       if (!paused)
+                                               alarm(3); /* set short timer */
+                               }
+                               else
+                               {
+                                       statmsg = "No timer set; waiting for "
+                                                 "manual trigger ('t').....";
+                               }
+
+                               firstproc = 0;
+                               break;
+
+                          /*
+                          ** focus on specific user
+                          */
+                          case MSELUSER:
+                               alarm(0);       /* stop the clock */
+                               echo();
+
+                               move(statline, 0);
+                               clrtoeol();
+                               printw("Username as regular expression "
+                                      "(enter=all users): ");
+
+                               procsel.username[0] = '\0';
+                               scanw("%255s\n", procsel.username);
+
+                               noecho();
+
+                               if (procsel.username[0]) /* data entered ? */
+                               {
+                                       regex_t         userregex;
+                                       int             u = 0;
+
+                                       if ( regcomp(&userregex,
+                                               procsel.username, REG_NOSUB))
+                                       {
+                                               statmsg = "Invalid regular "
+                                                         "expression!";
+                                               beep();
+
+                                               procsel.username[0] = '\0';
+                                       }
+                                       else
+                                       {
+                                               while ( (pwd = getpwent()))
+                                               {
+                                                       if (regexec(&userregex,
+                                                           pwd->pw_name, 0,
+                                                           NULL, 0))
+                                                               continue;
+
+                                                       if (u < MAXUSERSEL-1)
+                                                       {
+                                                         procsel.userid[u] =
+                                                               pwd->pw_uid;
+                                                         u++;
+                                                       }
+                                               }
+                                               endpwent();
+
+                                               procsel.userid[u] = USERSTUB;
+
+                                               if (u == 0)
+                                               {
+                                                       /*
+                                                       ** possibly a numerical
+                                                       ** value specified?
+                                                       */
+                                                       if (numeric(
+                                                            procsel.username))
+                                                       {
+                                                        procsel.userid[0] =
+                                                        atoi(procsel.username);
+                                                        procsel.userid[1] =
+                                                               USERSTUB;
+                                                       }
+                                                       else
+                                                       {
+                                                            statmsg =
+                                                               "No user-names "
+                                                               "match this "
+                                                               "pattern!";
+                                                            beep();
+                                                       }
+                                               }
+                                       }
+                               }
+                               else
+                               {
+                                       procsel.userid[0] = USERSTUB;
+                               }
+
+                               if (interval && !paused && !rawreadflag)
+                                       alarm(3);  /* set short timer */
+
+                               firstproc = 0;
+                               break;
+
+                          /*
+                          ** focus on specific process-name
+                          */
+                          case MSELPROC:
+                               alarm(0);       /* stop the clock */
+                               echo();
+
+                               move(statline, 0);
+                               clrtoeol();
+                               printw("Process-name as regular "
+                                      "expression (enter=no regex): ");
+
+                               procsel.prognamesz  = 0;
+                               procsel.progname[0] = '\0';
+
+                               scanw("%63s\n", procsel.progname);
+                               procsel.prognamesz = strlen(procsel.progname);
+
+                               if (procsel.prognamesz)
+                               {
+                                       if (regcomp(&procsel.progregex,
+                                                procsel.progname, REG_NOSUB))
+                                       {
+                                               statmsg = "Invalid regular "
+                                                         "expression!";
+                                               beep();
+
+                                               procsel.prognamesz  = 0;
+                                               procsel.progname[0] = '\0';
+                                       }
+                               }
+
+                               noecho();
+
+                               move(statline, 0);
+
+                               if (interval && !paused && !rawreadflag)
+                                       alarm(3);  /* set short timer */
+
+                               firstproc = 0;
+                               break;
+
+                          /*
+                          ** focus on specific container id
+                          */
+                          case MSELCONT:
+                               alarm(0);       /* stop the clock */
+                               echo();
+
+                               move(statline, 0);
+                               clrtoeol();
+                               printw("Containerid 12 postitions "
+                                      "(enter=all, "
+                                      "'host'=host processes): ");
+
+                               procsel.container[0]  = '\0';
+                               scanw("%15s", procsel.container);
+                               procsel.container[12] = '\0';
+
+                               switch (strlen(procsel.container))
+                               {
+                                   case 0:
+                                       break;  // enter key pressed
+
+                                  case 4:      // host?
+                                       if (strcmp(procsel.container, "host"))
+                                       {
+                                               statmsg="Invalid containerid!";
+                                               beep();
+                                               procsel.container[0] = '\0';
+                                       }
+                                       else
+                                       {
+                                               procsel.container[0] = 'H';
+                                               procsel.container[1] = '\0';
+                                       }
+                                       break;
+
+                                  case 12:     // container id
+                                       (void)strtol(procsel.container, &p, 16);
+
+                                       if (*p)
+                                       {
+                                               statmsg ="Containerid not hex!";
+                                               beep();
+                                               procsel.container[0] = '\0';
+                                       }
+                                       break;
+
+                                  default:
+                                       statmsg = "Invalid containerid!";
+                                       beep();
+
+                                       procsel.container[0] = '\0';
+                               }
+
+                               noecho();
+
+                               move(statline, 0);
+
+                               if (interval && !paused && !rawreadflag)
+                                       alarm(3);  /* set short timer */
+
+                               firstproc = 0;
+                               break;
+
+                          /*
+                          ** focus on specific PIDs
+                          */
+                          case MSELPID:
+                               alarm(0);       /* stop the clock */
+                               echo();
+
+                               move(statline, 0);
+                               clrtoeol();
+                               printw("Comma-separated PIDs of processes "
+                                                "(enter=no selection): ");
+
+                               scanw("%79s\n", genline);
+
+                               int  id = 0;
+
+                               char *pidp = strtok(genline, ",");
+
+                               while (pidp)
+                               {
+                                       char *ep;
+
+                                       if (id >= MAXPID-1)
+                                       {
+                                               procsel.pid[id] = 0;    // stub
+
+                                               statmsg = "Maximum number of"
+                                                         "PIDs reached!";
+                                               beep();
+                                               break;
+                                       }
+
+                                       procsel.pid[id] = strtol(pidp, &ep, 10);
+
+                                       if (*ep)
+                                       {
+                                               statmsg = "Non-numerical PID!";
+                                               beep();
+                                               procsel.pid[0]  = 0;  // stub
+                                               break;
+                                       }
+
+                                       id++;
+                                       pidp = strtok(NULL, ",");
+                               }
+
+                               procsel.pid[id] = 0;    // stub
+
+                               noecho();
+
+                               move(statline, 0);
+
+                               if (interval && !paused && !rawreadflag)
+                                       alarm(3);  /* set short timer */
+
+                               firstproc = 0;
+                               break;
+
+
+                          /*
+                          ** focus on specific command line arguments
+                          */
+                          case MSELARG:
+                               alarm(0);       /* stop the clock */
+                               echo();
+
+                               move(statline, 0);
+                               clrtoeol();
+                               printw("Command line string as regular "
+                                      "expression (enter=no regex): ");
+
+                               procsel.argnamesz  = 0;
+                               procsel.argname[0] = '\0';
+
+                               scanw("%63s\n", procsel.argname);
+                               procsel.argnamesz = strlen(procsel.argname);
+
+                               if (procsel.argnamesz)
+                               {
+                                       if (regcomp(&procsel.argregex,
+                                                procsel.argname, REG_NOSUB))
+                                       {
+                                               statmsg = "Invalid regular "
+                                                         "expression!";
+                                               beep();
+
+                                               procsel.argnamesz  = 0;
+                                               procsel.argname[0] = '\0';
+                                       }
+                               }
+
+                               noecho();
+
+                               move(statline, 0);
+
+                               if (interval && !paused && !rawreadflag)
+                                       alarm(3);  /* set short timer */
+
+                               firstproc = 0;
+                               break;
+
+                          /*
+                          ** focus on specific system resource
+                          */
+                          case MSELSYS:
+                               alarm(0);       /* stop the clock */
+                               echo();
+
+                               move(statline, 0);
+                               clrtoeol();
+                               printw("Logical volume name as regular "
+                                      "expression (enter=no specific name): ");
+
+                               syssel.lvmnamesz  = 0;
+                               syssel.lvmname[0] = '\0';
+
+                               scanw("%63s\n", syssel.lvmname);
+                               syssel.lvmnamesz = strlen(syssel.lvmname);
+
+                               if (syssel.lvmnamesz)
+                               {
+                                       if (regcomp(&syssel.lvmregex,
+                                                syssel.lvmname, REG_NOSUB))
+                                       {
+                                               statmsg = "Invalid regular "
+                                                         "expression!";
+                                               beep();
+
+                                               syssel.lvmnamesz  = 0;
+                                               syssel.lvmname[0] = '\0';
+                                       }
+                               }
+
+                               move(statline, 0);
+                               clrtoeol();
+                               printw("Disk name as regular "
+                                      "expression (enter=no specific name): ");
+
+                               syssel.dsknamesz  = 0;
+                               syssel.dskname[0] = '\0';
+
+                               scanw("%63s\n", syssel.dskname);
+                               syssel.dsknamesz = strlen(syssel.dskname);
+
+                               if (syssel.dsknamesz)
+                               {
+                                       if (regcomp(&syssel.dskregex,
+                                                syssel.dskname, REG_NOSUB))
+                                       {
+                                               statmsg = "Invalid regular "
+                                                         "expression!";
+                                               beep();
+
+                                               syssel.dsknamesz  = 0;
+                                               syssel.dskname[0] = '\0';
+                                       }
+                               }
+
+                               move(statline, 0);
+                               clrtoeol();
+                               printw("Interface name as regular "
+                                      "expression (enter=no specific name): ");
+
+                               syssel.itfnamesz  = 0;
+                               syssel.itfname[0] = '\0';
+
+                               scanw("%63s\n", syssel.itfname);
+                               syssel.itfnamesz = strlen(syssel.itfname);
+
+                               if (syssel.itfnamesz)
+                               {
+                                       if (regcomp(&syssel.itfregex,
+                                                syssel.itfname, REG_NOSUB))
+                                       {
+                                               statmsg = "Invalid regular "
+                                                         "expression!";
+                                               beep();
+
+                                               syssel.itfnamesz  = 0;
+                                               syssel.itfname[0] = '\0';
+                                       }
+                               }
+
+                               noecho();
+
+                               move(statline, 0);
+
+                               if (interval && !paused && !rawreadflag)
+                                       alarm(3);  /* set short timer */
+
+                               firstproc = 0;
+                               break;
+
+                          /*
+                          ** toggle pause-state
+                          */
+                          case MPAUSE:
+                               if (paused)
+                               {
+                                       paused=0;
+                                       clrtoeol();
+                                       refresh();
+
+                                       if (!rawreadflag)
+                                               alarm(1);
+                               }
+                               else
+                               {
+                                       paused=1;
+                                       clrtoeol();
+                                       refresh();
+                                       alarm(0);       /* stop the clock */
+                               }
+                               break;
+
+                          /*
+                          ** toggle between modified processes and
+                          ** all processes
+                          */
+                          case MALLPROC:
+                               if (deviatonly)
+                               {
+                                       deviatonly=0;
+                                       statmsg = "All processes/threads will be "
+                                                 "shown/accumulated...";
+                               }
+                               else
+                               {
+                                       deviatonly=1;
+                                       statmsg = "Only active processes/threads "
+                                                 "will be shown/accumulated...";
+                               }
+
+                               tlastorder = 0;
+                               firstproc  = 0;
+                               break;
+
+                          /*
+                          ** toggle average or total values
+                          */
+                          case MAVGVAL:
+                               if (avgval)
+                                       avgval=0;
+                               else
+                                       avgval=1;
+                               break;
+
+                          /*
+                          ** system-statistics lines:
+                          **            toggle fixed or variable
+                          */
+                          case MSYSFIXED:
+                               if (fixedhead)
+                               {
+                                       fixedhead=0;
+                                       statmsg = "Only active system-resources"
+                                                 " will be shown ......";
+                               }
+                               else
+                               {
+                                       fixedhead=1;
+                                       statmsg = "Also inactive "
+                                         "system-resources will be shown.....";
+                               }
+
+                               firstproc = 0;
+                               break;
+
+                          /*
+                          ** system-statistics lines:
+                          **            toggle fixed or variable
+                          */
+                          case MSYSNOSORT:
+                               if (sysnosort)
+                               {
+                                       sysnosort=0;
+                                       statmsg = "System resources will be "
+                                                 "sorted on utilization...";
+                               }
+                               else
+                               {
+                                       sysnosort=1;
+                                       statmsg = "System resources will not "
+                                                 "be sorted on utilization...";
+                               }
+
+                               firstproc = 0;
+                               break;
+
+                          /*
+                          ** per-thread view wanted with sorting on
+                          ** process level or thread level
+                          */
+                          case MTHREAD:
+                               if (threadview)
+                               {
+                                       threadview = 0;
+                                       statmsg    = "Thread view disabled";
+                                       firstproc  = 0;
+                               }
+                               else
+                               {
+                                       threadview = 1;
+                                       statmsg    = "Thread view enabled";
+                                       firstproc  = 0;
+                               }
+                               break;
+
+                          /*
+                          ** per-process PSS calculation wanted 
+                          */
+                          case MCALCPSS:
+                               if (calcpss)
+                               {
+                                       calcpss    = 0;
+                                       statmsg    = "PSIZE gathering disabled";
+                               }
+                               else
+                               {
+                                       calcpss    = 1;
+                                       statmsg    = "PSIZE gathering enabled";
+                               }
+                               break;
+
+                          /*
+                          ** suppression of exited processes in output
+                          */
+                          case MSUPEXITS:
+                               if (suppressexit)
+                               {
+                                       suppressexit = 0;
+                                       statmsg      = "Exited processes will "
+                                                      "be shown/accumulated";
+                                       firstproc    = 0;
+                               }
+                               else
+                               {
+                                       suppressexit = 1;
+                                       statmsg      = "Exited processes will "
+                                                    "not be shown/accumulated";
+                                       firstproc    = 0;
+                               }
+                               break;
+
+                          /*
+                          ** screen lines:
+                          **            toggle for colors
+                          */
+                          case MCOLORS:
+                               if (usecolors)
+                               {
+                                       usecolors=0;
+                                       statmsg = "No colors will be used...";
+                               }
+                               else
+                               {
+                                       if (screen && has_colors())
+                                       {
+                                               usecolors=1;
+                                               statmsg =
+                                                  "Colors will be used...";
+                                       }
+                                       else
+                                       {
+                                               statmsg="No colors supported!";
+                                       }
+                               }
+
+                               firstproc = 0;
+                               break;
+
+                          /*
+                          ** system-statistics lines:
+                          **            toggle no or all active disk
+                          */
+                          case MSYSLIMIT:
+                               alarm(0);       /* stop the clock */
+
+                               maxcpulines =
+                                 getnumval("Maximum lines for per-cpu "
+                                           "statistics (now %d): ",
+                                           maxcpulines, statline);
+
+                               if (sstat->dsk.nlvm > 0)
+                               {
+                                       maxlvmlines =
+                                         getnumval("Maximum lines for LVM "
+                                           "statistics (now %d): ",
+                                           maxlvmlines, statline);
+                               }
+
+                               if (sstat->dsk.nmdd > 0)
+                               {
+                                       maxmddlines =
+                                         getnumval("Maximum lines for MD "
+                                           "device statistics (now %d): ",
+                                           maxmddlines, statline);
+                               }
+
+                               maxdsklines =
+                                 getnumval("Maximum lines for disk "
+                                           "statistics (now %d): ",
+                                           maxdsklines, statline);
+
+                               maxintlines =
+                                 getnumval("Maximum lines for interface "
+                                           "statistics (now %d): ",
+                                           maxintlines, statline);
+
+                               maxnfslines =
+                                 getnumval("Maximum lines for NFS mount "
+                                           "statistics (now %d): ",
+                                           maxnfslines, statline);
+
+                               maxcontlines =
+                                 getnumval("Maximum lines for container "
+                                           "statistics (now %d): ",
+                                           maxcontlines, statline);
+
+                               if (interval && !paused && !rawreadflag)
+                                       alarm(3);  /* set short timer */
+
+                               firstproc = 0;
+                               break;
+
+                          /*
+                          ** reset statistics 
+                          */
+                          case MRESET:
+                               getalarm(0);    /* restart the clock */
+                               paused = 0;
+
+                               if (tpcumlist) free(tpcumlist);
+                               if (pcumlist)  free(pcumlist);
+                               if (tucumlist) free(tucumlist);
+                               if (ucumlist)  free(ucumlist);
+                               if (tccumlist) free(tccumlist);
+                               if (ccumlist)  free(ccumlist);
+                               if (tsklist)   free(tsklist);
+                               if (sellist)   free(sellist);
+
+                               return lastchar;
+
+                          /*
+                          ** show version info
+                          */
+                          case MVERSION:
+                               statmsg = getstrvers();
+                               break;
+
+                          /*
+                          ** handle redraw request
+                          */
+                          case MREDRAW:
+                                wclear(stdscr);
+                               break;
+
+                          /*
+                          ** handle arrow right for command line
+                          */
+                          case KEY_RIGHT:
+                               startoffset++;
+                               break;
+
+                          /*
+                          ** handle arrow left for command line
+                          */
+                          case KEY_LEFT:
+                               if (startoffset > 0)
+                                       startoffset--;
+                               break;
+
+                          /*
+                          ** handle arrow down to go one line down
+                          */
+                          case KEY_DOWN:
+                               if (firstproc < alistsz-1)
+                                       firstproc += 1;
+                               break;
+
+                          /*
+                          ** handle arrow up to go one line up
+                          */
+                          case KEY_UP: 
+                               if (firstproc > 0)
+                                       firstproc -= 1;
+                               break;
+
+                          /*
+                          ** handle forward
+                          */
+                          case KEY_NPAGE:
+                          case MLISTFW:
+                               if (alistsz-firstproc > plistsz)
+                                       firstproc += plistsz;
+                               break;
+
+                          /*
+                          ** handle backward
+                          */
+                          case KEY_PPAGE:
+                          case MLISTBW:
+                               if (firstproc >= plistsz)
+                                       firstproc -= plistsz;
+                               else
+                                       firstproc = 0;
+                               break;
+
+                          /*
+                          ** handle screen resize
+                          */
+                          case KEY_RESIZE:
+                               snprintf(statbuf, sizeof statbuf, 
+                                       "Window resized to %dx%d...",
+                                               COLS, LINES);
+                               statmsg = statbuf;
+
+                               timeout(0);
+                               (void) getch();
+                               timeout(-1);
+                               break;
+
+                          /*
+                          ** unknown key-stroke
+                          */
+                          default:
+                               beep();
+                       }
+               }
+               else    /* no screen */
+               {
+                       if (tpcumlist) free(tpcumlist);
+                       if (pcumlist)  free(pcumlist);
+                       if (tucumlist) free(tucumlist);
+                       if (ucumlist)  free(ucumlist);
+                       if (tccumlist) free(tccumlist);
+                       if (ccumlist)  free(ccumlist);
+                       if (tsklist)   free(tsklist);
+                       if (sellist)   free(sellist);
+
+                       return '\0';
+               }
+       }
+}
+
+/*
+** accumulate all processes per user in new list
+*/
+static int
+cumusers(struct tstat **curprocs, struct tstat *curusers, int numprocs)
+{
+       register int    i, numusers;
+
+       /*
+       ** sort list of active processes in order of uid (increasing)
+       */
+       qsort(curprocs, numprocs, sizeof(struct tstat *), compusr);
+
+       /*
+       ** accumulate all processes per user in the new list
+       */
+       for (numusers=i=0; i < numprocs; i++, curprocs++)
+       {
+               if (procsuppress(*curprocs, &procsel))
+                       continue;
+
+               if ((*curprocs)->gen.state == 'E' && suppressexit)
+                       continue;
+               if ( curusers->gen.ruid != (*curprocs)->gen.ruid )
+               {
+                       if (curusers->gen.pid)
+                       {
+                               numusers++;
+                               curusers++;
+                       }
+                       curusers->gen.ruid = (*curprocs)->gen.ruid;
+               }
+
+               accumulate(*curprocs, curusers);
+       }
+
+       if (curusers->gen.pid)
+               numusers++;
+
+       return numusers;
+}
+
+
+/*
+** accumulate all processes with the same name (i.e. same program)
+** into a new list
+*/
+static int
+cumprogs(struct tstat **curprocs, struct tstat *curprogs, int numprocs)
+{
+       register int    i, numprogs;
+
+       /*
+       ** sort list of active processes in order of process-name
+       */
+       qsort(curprocs, numprocs, sizeof(struct tstat *), compnam);
+
+       /*
+       ** accumulate all processes with same name in the new list
+       */
+       for (numprogs=i=0; i < numprocs; i++, curprocs++)
+       {
+               if (procsuppress(*curprocs, &procsel))
+                       continue;
+
+               if ((*curprocs)->gen.state == 'E' && suppressexit)
+                       continue;
+
+               if ( strcmp(curprogs->gen.name, (*curprocs)->gen.name) != 0)
+               {
+                       if (curprogs->gen.pid)
+                       {
+                               numprogs++;
+                               curprogs++;
+                       }
+                       strcpy(curprogs->gen.name, (*curprocs)->gen.name);
+               }
+
+               accumulate(*curprocs, curprogs);
+       }
+
+       if (curprogs->gen.pid)
+               numprogs++;
+
+       return numprogs;
+}
+
+/*
+** accumulate all processes per container in new list
+*/
+static int
+cumconts(struct tstat **curprocs, struct tstat *curconts, int numprocs)
+{
+       register int    i, numconts;
+
+       /*
+       ** sort list of active processes in order of container (increasing)
+       */
+       qsort(curprocs, numprocs, sizeof(struct tstat *), compcon);
+
+       /*
+       ** accumulate all processes per container in the new list
+       */
+       for (numconts=i=0; i < numprocs; i++, curprocs++)
+       {
+               if (procsuppress(*curprocs, &procsel))
+                       continue;
+
+               if ((*curprocs)->gen.state == 'E' && suppressexit)
+                       continue;
+               if ( strcmp(curconts->gen.container,
+                         (*curprocs)->gen.container) != 0)
+               {
+                       if (curconts->gen.pid)
+                       {
+                               numconts++;
+                               curconts++;
+                       }
+                       strcpy(curconts->gen.container,
+                           (*curprocs)->gen.container);
+               }
+
+               accumulate(*curprocs, curconts);
+       }
+
+       if (curconts->gen.pid)
+               numconts++;
+
+       return numconts;
+}
+
+
+/*
+** accumulate relevant counters from individual task to
+** combined task
+*/
+static void
+accumulate(struct tstat *curproc, struct tstat *curstat)
+{
+       count_t         nett_wsz;
+
+       curstat->gen.pid++;             /* misuse as counter */
+
+       curstat->gen.isproc  = 1;
+       curstat->gen.nthr   += curproc->gen.nthr;
+       curstat->cpu.utime  += curproc->cpu.utime;
+       curstat->cpu.stime  += curproc->cpu.stime;
+
+       if (curproc->dsk.wsz > curproc->dsk.cwsz)
+                       nett_wsz = curproc->dsk.wsz -curproc->dsk.cwsz;
+       else
+               nett_wsz = 0;
+
+       curstat->dsk.rio    += curproc->dsk.rsz;
+       curstat->dsk.wio    += nett_wsz;
+
+       curstat->dsk.rsz     = curstat->dsk.rio;
+       curstat->dsk.wsz     = curstat->dsk.wio;
+
+       curstat->net.tcpsnd += curproc->net.tcpsnd;
+       curstat->net.tcprcv += curproc->net.tcprcv;
+       curstat->net.udpsnd += curproc->net.udpsnd;
+       curstat->net.udprcv += curproc->net.udprcv;
+
+       curstat->net.tcpssz += curproc->net.tcpssz;
+       curstat->net.tcprsz += curproc->net.tcprsz;
+       curstat->net.udpssz += curproc->net.udpssz;
+       curstat->net.udprsz += curproc->net.udprsz;
+
+       if (curproc->gen.state != 'E')
+       {
+               if (curstat->mem.pmem != -1)
+               {
+                       if  (curproc->mem.pmem != -1)  // no errors?
+                               curstat->mem.pmem += curproc->mem.pmem;
+                       else
+                               curstat->mem.pmem  = -1;
+               }
+
+               curstat->mem.vmem   += curproc->mem.vmem;
+               curstat->mem.rmem   += curproc->mem.rmem;
+               curstat->mem.vlibs  += curproc->mem.vlibs;
+               curstat->mem.vdata  += curproc->mem.vdata;
+               curstat->mem.vstack += curproc->mem.vstack;
+               curstat->mem.vswap  += curproc->mem.vswap;
+               curstat->mem.rgrow  += curproc->mem.rgrow;
+               curstat->mem.vgrow  += curproc->mem.vgrow;
+       }
+}
+
+
+/*
+** function that checks if the current process is selected or suppressed;
+** returns 1 (suppress) or 0 (do not suppress)
+*/
+static int
+procsuppress(struct tstat *curstat, struct pselection *sel)
+{
+       /*
+       ** check if only processes of a particular user
+       ** should be shown
+       */
+       if (sel->userid[0] != USERSTUB)
+       {
+               int     u = 0;
+
+               while (sel->userid[u] != USERSTUB)
+               {
+                       if (sel->userid[u] == curstat->gen.ruid)
+                               break;
+                       u++;
+               }
+
+               if (sel->userid[u] != curstat->gen.ruid)
+                       return 1;
+       }
+
+       /*
+       ** check if only processes with particular PIDs
+       ** should be shown
+       */
+       if (sel->pid[0])
+       {
+               int i = 0;
+
+               while (sel->pid[i])
+               {
+                       if (sel->pid[i] == curstat->gen.pid)
+                               break;
+                       i++;
+               }
+
+               if (sel->pid[i] != curstat->gen.pid)
+                       return 1;
+       }
+
+       /*
+       ** check if only processes with a particular name
+       ** should be shown
+       */
+       if (sel->prognamesz &&
+           regexec(&(sel->progregex), curstat->gen.name, 0, NULL, 0))
+               return 1;
+
+       /*
+       ** check if only processes with a particular command line string
+       ** should be shown
+       */
+       if (sel->argnamesz)
+       {
+               if (curstat->gen.cmdline[0])
+               {
+                       if (regexec(&(sel->argregex), curstat->gen.cmdline,
+                                                               0, NULL, 0))
+                               return 1;
+               }
+               else
+               {
+                       if (regexec(&(sel->argregex), curstat->gen.name,
+                                                               0, NULL, 0))
+                               return 1;
+               }
+       }
+
+       /*
+       ** check if only processes related to a particular container
+       ** should be shown (container 'H' stands for native host processes)
+       */
+       if (sel->container[0])
+       {
+               if (sel->container[0] == 'H')   // only host processes
+               {
+                       if (curstat->gen.container[0])
+                               return 1;
+               }
+               else
+               {
+                       if (memcmp(sel->container, curstat->gen.container, 12))
+                               return 1;
+               }
+       }
+
+       return 0;
+}
+
+
+static void
+limitedlines(void)
+{
+       maxcpulines  = 0;
+       maxdsklines  = 3;
+       maxmddlines  = 3;
+       maxlvmlines  = 4;
+       maxintlines  = 2;
+       maxnfslines  = 2;
+       maxcontlines = 0;
+}
+
+/*
+** get a numerical value from the user and verify 
+*/
+static long
+getnumval(char *ask, long valuenow, int statline)
+{
+       char numval[16];
+       long retval;
+
+       echo();
+       move(statline, 0);
+       clrtoeol();
+       printw(ask, valuenow);
+
+       numval[0] = 0;
+       scanw("%15s", numval);
+
+       move(statline, 0);
+       noecho();
+
+       if (numval[0])  /* data entered ? */
+       {
+               if ( numeric(numval) )
+               {
+                       retval = atol(numval);
+               }
+               else
+               {
+                       beep();
+                       clrtoeol();
+                       printw("Value not numeric (current value kept)!");
+                       refresh();
+                       sleep(2);
+                       retval = valuenow;
+               }
+       }
+       else
+       {
+               retval = valuenow;
+       }
+
+       return retval;
+}
+
+/*
+** generic print-function which checks if printf should be used
+** (to file or pipe) or curses (to screen)
+*/
+void
+printg(const char *format, ...)
+{
+       va_list args;
+
+       va_start(args, format);
+
+       if (screen)
+               vwprintw(stdscr, (char *) format, args);
+       else
+               vprintf(format, args);
+
+       va_end  (args);
+}
+
+/*
+** initialize generic sample output functions
+*/
+static void
+generic_init(void)
+{
+       int i;
+
+       /*
+       ** check if default sort order and/or showtype are overruled
+       ** by command-line flags
+       */
+       for (i=0; flaglist[i]; i++)
+       {
+               switch (flaglist[i])
+               {
+                  case MSORTAUTO:
+                       showorder = MSORTAUTO;
+                       break;
+
+                  case MSORTCPU:
+                       showorder = MSORTCPU;
+                       break;
+
+                  case MSORTMEM:
+                       showorder = MSORTMEM;
+                       break;
+
+                  case MSORTDSK:
+                       showorder = MSORTDSK;
+                       break;
+
+                  case MSORTNET:
+                       showorder = MSORTNET;
+                       break;
+
+                  case MPROCGEN:
+                       showtype  = MPROCGEN;
+                       showorder = MSORTCPU;
+                       break;
+
+                  case MPROCMEM:
+                       showtype  = MPROCMEM;
+                       showorder = MSORTMEM;
+                       break;
+
+                  case MPROCSCH:
+                       showtype  = MPROCSCH;
+                       showorder = MSORTCPU;
+                       break;
+
+                  case MPROCDSK:
+                       if ( !(supportflags & IOSTAT) )
+                       {
+                               fprintf(stderr,
+                                       "No disk-activity figures "
+                                       "available; request ignored\n");
+                               sleep(3);
+                               break;
+                       }
+
+                       showtype  = MPROCDSK;
+                       showorder = MSORTDSK;
+                       break;
+
+                  case MPROCNET:
+                       if ( !(supportflags & NETATOP) )
+                       {
+                               fprintf(stderr, "Kernel module 'netatop' not "
+                                                 "active; request ignored!");
+                               sleep(3);
+                               break;
+                       }
+
+                       showtype  = MPROCNET;
+                       showorder = MSORTNET;
+                       break;
+
+                  case MPROCVAR:
+                       showtype  = MPROCVAR;
+                       break;
+
+                  case MPROCARG:
+                       showtype  = MPROCARG;
+                       break;
+
+                  case MPROCOWN:
+                       showtype  = MPROCOWN;
+                       break;
+
+                  case MAVGVAL:
+                       if (avgval)
+                               avgval=0;
+                       else
+                               avgval=1;
+                       break;
+
+                  case MCUMUSER:
+                       showtype  = MCUMUSER;
+                       break;
+
+                  case MCUMPROC:
+                       showtype  = MCUMPROC;
+                       break;
+
+                  case MCUMCONT:
+                       showtype  = MCUMCONT;
+                       break;
+
+                  case MSYSFIXED:
+                       if (fixedhead)
+                               fixedhead=0;
+                       else
+                               fixedhead=1;
+                       break;
+
+                  case MSYSNOSORT:
+                       if (sysnosort)
+                               sysnosort=0;
+                       else
+                               sysnosort=1;
+                       break;
+
+                  case MTHREAD:
+                       if (threadview)
+                               threadview = 0;
+                       else
+                               threadview = 1;
+                       break;
+
+                  case MCALCPSS:
+                       if (calcpss)
+                               calcpss = 0;
+                       else
+                               calcpss = 1;
+                       break;
+
+                  case MSUPEXITS:
+                       if (suppressexit)
+                               suppressexit = 0;
+                       else
+                               suppressexit = 1;
+                       break;
+
+                  case MCOLORS:
+                       if (usecolors)
+                               usecolors=0;
+                       else
+                               usecolors=1;
+                       break;
+
+                  case MSYSLIMIT:
+                       limitedlines();
+                       break;
+
+                  default:
+                       prusage("atop");
+               }
+       }
+
+               /*
+               ** set stdout output on line-basis
+               */
+               setvbuf(stdout, (char *)0, _IOLBF, BUFSIZ);
+
+               /*
+               ** check if STDOUT is related to a tty or
+               ** something else (file, pipe)
+               */
+               if ( isatty(1) )
+                       screen = 1;
+               else
+               screen = 0;
+
+               /*
+               ** install catch-routine to finish in a controlled way
+       ** and activate cbreak-mode
+               */
+               if (screen)
+       {
+               /*
+               ** initialize screen-handling via curses
+               */
+               initscr();
+               cbreak();
+               noecho();
+               keypad(stdscr, TRUE);
+
+               if (COLS  < 30)
+               {
+                       endwin();       // finish curses interface
+
+                       fprintf(stderr, "Not enough columns available\n"
+                                       "(need at least %d columns)\n", 30);
+                       fprintf(stderr, "Please resize window....\n");
+
+                       cleanstop(1);
+               }
+
+               if (has_colors())
+               {
+                       use_default_colors();
+                       start_color();
+
+                       init_pair(COLORINFO,   colorinfo,   -1);
+                       init_pair(COLORALMOST, coloralmost, -1);
+                       init_pair(COLORCRIT,   colorcrit,   -1);
+                       init_pair(COLORTHR,    colorthread, -1);
+               }
+               else
+               {
+                       usecolors = 0;
+               }
+       }
+
+       signal(SIGINT,   cleanstop);
+       signal(SIGTERM,  cleanstop);
+}
+
+/*
+** show help information in interactive mode
+*/
+static struct helptext {
+       char *helpline;
+       char helparg;
+} helptext[] = {
+       {"Figures shown for active processes:\n",               ' '},
+       {"\t'%c'  - generic info (default)\n",                  MPROCGEN},
+       {"\t'%c'  - memory details\n",                          MPROCMEM},
+       {"\t'%c'  - disk details\n",                            MPROCDSK},
+       {"\t'%c'  - network details\n",                         MPROCNET},
+       {"\t'%c'  - scheduling and thread-group info\n",        MPROCSCH},
+       {"\t'%c'  - various info (ppid, user/group, date/time, status, "
+        "exitcode)\n", MPROCVAR},
+       {"\t'%c'  - full command line per process\n",           MPROCARG},
+       {"\t'%c'  - use own output line definition\n",          MPROCOWN},
+       {"\n",                                                  ' '},
+       {"Sort list of processes in order of:\n",               ' '},
+       {"\t'%c'  - cpu activity\n",                            MSORTCPU},
+       {"\t'%c'  - memory consumption\n",                      MSORTMEM},
+       {"\t'%c'  - disk activity\n",                           MSORTDSK},
+       {"\t'%c'  - network activity\n",                        MSORTNET},
+       {"\t'%c'  - most active system resource (auto mode)\n", MSORTAUTO},
+       {"\n",                                                  ' '},
+       {"Accumulated figures:\n",                              ' '},
+       {"\t'%c'  - total resource consumption per user\n",     MCUMUSER},
+       {"\t'%c'  - total resource consumption per program (i.e. same "
+        "process name)\n",                                     MCUMPROC},
+       {"\t'%c'  - total resource consumption per container\n",MCUMCONT},
+       {"\n",                                                  ' '},
+       {"Process selections (keys shown in header line):\n",   ' '},
+       {"\t'%c'  - focus on specific user name           "
+                                     "(regular expression)\n", MSELUSER},
+       {"\t'%c'  - focus on specific program name        "
+                                     "(regular expression)\n", MSELPROC},
+       {"\t'%c'  - focus on specific contained id (CID)\n",    MSELCONT},
+       {"\t'%c'  - focus on specific command line string "
+                                     "(regular expression)\n", MSELARG},
+       {"\t'%c'  - focus on specific process id (PID)\n",      MSELPID},
+       {"\n",                                                  ' '},
+       {"System resource selections (keys shown in header line):\n",' '},
+       {"\t'%c'  - focus on specific system resources    "
+                                     "(regular expression)\n", MSELSYS},
+       {"\n",                                                        ' '},
+       {"Screen-handling:\n",                                        ' '},
+       {"\t^L   - redraw the screen                       \n",       ' '},
+       {"\tPgDn - show next page in the process list (or ^F)\n",     ' '},
+       {"\tArDn - arrow-down for next line in process list\n",       ' '},
+       {"\tPgUp - show previous page in the process list (or ^B)\n", ' '},
+       {"\tArUp   arrow-up for previous line in process list\n",     ' '},
+       {"\n",                                                  ' '},
+       {"\tArRt - arrow-right for next character in full command line\n", ' '},
+       {"\tArLt - arrow-left  for previous character in full command line\n",
+                                                                       ' '},
+       {"\n",                                                  ' '},
+       {"Presentation (keys shown in header line):\n",         ' '},
+       {"\t'%c'  - show individual threads                        (toggle)\n",
+                                                               MTHREAD},
+       {"\t'%c'  - show all processes (default: active processes) (toggle)\n",
+                                                               MALLPROC},
+       {"\t'%c'  - show fixed number of header lines              (toggle)\n",
+                                                               MSYSFIXED},
+       {"\t'%c'  - suppress sorting system resources              (toggle)\n",
+                                                               MSYSNOSORT},
+       {"\t'%c'  - suppress exited processes in output            (toggle)\n",
+                                                               MSUPEXITS},
+       {"\t'%c'  - no colors to indicate high occupation          (toggle)\n",
+                                                               MCOLORS},
+       {"\t'%c'  - show average-per-second i.s.o. total values    (toggle)\n",
+                                                               MAVGVAL},
+       {"\t'%c'  - calculate proportional set size (PSIZE)        (toggle)\n",
+                                                               MCALCPSS},
+       {"\n",                                                  ' '},
+       {"Raw file viewing:\n",                                 ' '},
+       {"\t'%c'  - show next     sample in raw file\n",        MSAMPNEXT},
+       {"\t'%c'  - show previous sample in raw file\n",        MSAMPPREV},
+       {"\t'%c'  - branch to certain time in raw file\n",      MSAMPBRANCH},
+       {"\t'%c'  - rewind to begin of raw file\n",             MRESET},
+       {"\n",                                                  ' '},
+       {"Miscellaneous commands:\n",                           ' '},
+       {"\t'%c'  - change interval timer (0 = only manual trigger)\n",
+                                                               MINTERVAL},
+       {"\t'%c'  - manual trigger to force next sample\n",     MSAMPNEXT},
+       {"\t'%c'  - reset counters to boot time values\n",      MRESET},
+       {"\t'%c'  - pause button to freeze current sample (toggle)\n",
+                                                               MPAUSE},
+       {"\n",                                                  ' '},
+       {"\t'%c'  - limited lines for per-cpu, disk and interface resources\n",
+                                                               MSYSLIMIT},
+       {"\t'%c'  - kill a process (i.e. send a signal)\n",     MKILLPROC},
+       {"\n",                                                  ' '},
+       {"\t'%c'  - version information\n",                     MVERSION},
+       {"\t'%c'  - help information\n",                        MHELP1},
+       {"\t'%c'  - help information\n",                        MHELP2},
+       {"\t'%c'  - quit this program\n",                       MQUIT},
+};
+
+static int helplines = sizeof(helptext)/sizeof(struct helptext);
+
+static void
+showhelp(int helpline)
+{
+       int     winlines = LINES-helpline, shown, tobeshown=1, i;
+       WINDOW  *helpwin;
+
+       /*
+       ** create a new window for the help-info in which scrolling is
+       ** allowed
+       */
+       helpwin = newwin(winlines, COLS, helpline, 0);
+       scrollok(helpwin, 1);
+
+       /*
+       ** show help-lines 
+       */
+       for (i=0, shown=0; i < helplines; i++, shown++)
+       {
+               wprintw(helpwin, helptext[i].helpline, helptext[i].helparg);
+
+               /*
+               ** when the window is full, start paging interactively
+               */
+               if (i >= winlines-2 && shown >= tobeshown)
+               {
+                       wmove    (helpwin, winlines-1, 0);
+                       wclrtoeol(helpwin);
+                       wprintw  (helpwin, "Press 'q' to leave help, " 
+                                       "space for next page or "
+                                       "other key for next line... ");
+
+                       switch (wgetch(helpwin))
+                       {
+                          case 'q':
+                               delwin(helpwin);
+                               return;
+                          case ' ':
+                               shown = 0;
+                               tobeshown = winlines-1;
+                               break;
+                          default:
+                               shown = 0;
+                               tobeshown = 1;
+                       }
+
+                       wmove  (helpwin, winlines-1, 0);
+               }
+       }
+
+       wmove    (helpwin, winlines-1, 0);
+       wclrtoeol(helpwin);
+       wprintw  (helpwin, "End of help - press 'q' to leave help... ");
+        while ( wgetch(helpwin) != 'q' );
+       delwin   (helpwin);
+}
+
+/*
+** function to be called to print error-messages
+*/
+void
+generic_error(const char *format, ...)
+{
+       va_list args;
+
+       va_start(args, format);
+       vfprintf(stderr, format, args);
+       va_end  (args);
+}
+
+/*
+** function to be called when the program stops
+*/
+void
+generic_end(void)
+{
+       endwin();
+}
+
+/*
+** function to be called when usage-info is required
+*/
+void
+generic_usage(void)
+{
+       printf("\t  -%c  show fixed number of lines with system statistics\n",
+                       MSYSFIXED);
+       printf("\t  -%c  suppress sorting of system resources\n",
+                       MSYSNOSORT);
+       printf("\t  -%c  suppress exited processes in output\n",
+                       MSUPEXITS);
+       printf("\t  -%c  show limited number of lines for certain resources\n",
+                       MSYSLIMIT);
+       printf("\t  -%c  show individual threads\n", MTHREAD);
+       printf("\t  -%c  show average-per-second i.s.o. total values\n\n",
+                       MAVGVAL);
+       printf("\t  -%c  no colors in case of high occupation\n",
+                       MCOLORS);
+       printf("\t  -%c  show general process-info (default)\n",
+                       MPROCGEN);
+       printf("\t  -%c  show memory-related process-info\n",
+                       MPROCMEM);
+       printf("\t  -%c  show disk-related process-info\n",
+                       MPROCDSK);
+       printf("\t  -%c  show network-related process-info\n",
+                       MPROCNET);
+       printf("\t  -%c  show scheduling-related process-info\n",
+                       MPROCSCH);
+       printf("\t  -%c  show various process-info (ppid, user/group, "
+                        "date/time)\n", MPROCVAR);
+       printf("\t  -%c  show command line per process\n",
+                       MPROCARG);
+       printf("\t  -%c  show own defined process-info\n",
+                       MPROCOWN);
+       printf("\t  -%c  show cumulated process-info per user\n",
+                       MCUMUSER);
+       printf("\t  -%c  show cumulated process-info per program "
+                       "(i.e. same name)\n",
+                       MCUMPROC);
+       printf("\t  -%c  show cumulated process-info per container\n\n",
+                       MCUMCONT);
+       printf("\t  -%c  sort processes in order of cpu-consumption "
+                       "(default)\n",
+                       MSORTCPU);
+       printf("\t  -%c  sort processes in order of memory-consumption\n",
+                       MSORTMEM);
+       printf("\t  -%c  sort processes in order of disk-activity\n",
+                       MSORTDSK);
+       printf("\t  -%c  sort processes in order of network-activity\n",
+                       MSORTNET);
+       printf("\t  -%c  sort processes in order of most active resource "
+                        "(auto mode)\n",
+                       MSORTAUTO);
+}
+
+/*
+** functions to handle a particular tag in the /etc/atoprc and .atoprc file
+*/
+void
+do_username(char *name, char *val)
+{
+       struct passwd   *pwd;
+
+       strncpy(procsel.username, val, sizeof procsel.username -1);
+       procsel.username[sizeof procsel.username -1] = 0;
+
+       if (procsel.username[0])
+       {
+               regex_t         userregex;
+               int             u = 0;
+
+               if (regcomp(&userregex, procsel.username, REG_NOSUB))
+               {
+                       fprintf(stderr,
+                               "atoprc - %s: invalid regular expression %s\n",
+                               name, val);
+                       exit(1);
+               }
+
+               while ( (pwd = getpwent()))
+               {
+                       if (regexec(&userregex, pwd->pw_name, 0, NULL, 0))
+                               continue;
+
+                       if (u < MAXUSERSEL-1)
+                       {
+                               procsel.userid[u] = pwd->pw_uid;
+                               u++;
+                       }
+               }
+               endpwent();
+
+               procsel.userid[u] = USERSTUB;
+
+               if (u == 0)
+               {
+                       /*
+                       ** possibly a numerical value has been specified
+                       */
+                       if (numeric(procsel.username))
+                       {
+                            procsel.userid[0] = atoi(procsel.username);
+                            procsel.userid[1] = USERSTUB;
+                       }
+                       else
+                       {
+                               fprintf(stderr,
+                                       "atoprc - %s: user-names matching %s "
+                                        "do not exist\n", name, val);
+                               exit(1);
+                       }
+               }
+       }
+       else
+       {
+               procsel.userid[0] = USERSTUB;
+       }
+}
+
+void
+do_procname(char *name, char *val)
+{
+       strncpy(procsel.progname, val, sizeof procsel.progname -1);
+       procsel.prognamesz = strlen(procsel.progname);
+
+       if (procsel.prognamesz)
+       {
+               if (regcomp(&procsel.progregex, procsel.progname, REG_NOSUB))
+               {
+                       fprintf(stderr,
+                               "atoprc - %s: invalid regular expression %s\n",
+                               name, val);
+                       exit(1);
+               }
+       }
+}
+
+extern int get_posval(char *name, char *val);
+
+
+void
+do_maxcpu(char *name, char *val)
+{
+       maxcpulines = get_posval(name, val);
+}
+
+void
+do_maxdisk(char *name, char *val)
+{
+       maxdsklines = get_posval(name, val);
+}
+
+void
+do_maxmdd(char *name, char *val)
+{
+       maxmddlines = get_posval(name, val);
+}
+
+void
+do_maxlvm(char *name, char *val)
+{
+       maxlvmlines = get_posval(name, val);
+}
+
+void
+do_maxintf(char *name, char *val)
+{
+       maxintlines = get_posval(name, val);
+}
+
+void
+do_maxnfsm(char *name, char *val)
+{
+       maxnfslines = get_posval(name, val);
+}
+
+void
+do_maxcont(char *name, char *val)
+{
+       maxcontlines = get_posval(name, val);
+}
+
+
+struct colmap {
+       char    *colname;
+       short   colval;
+} colormap[] = {
+       { "red",        COLOR_RED,      },
+       { "green",      COLOR_GREEN,    },
+       { "yellow",     COLOR_YELLOW,   },
+       { "blue",       COLOR_BLUE,     },
+       { "magenta",    COLOR_MAGENTA,  },
+       { "cyan",       COLOR_CYAN,     },
+       { "black",      COLOR_BLACK,    },
+       { "white",      COLOR_WHITE,    },
+};
+static short
+modify_color(char *colorname)
+{
+       int i;
+
+       for (i=0; i < sizeof colormap/sizeof colormap[0]; i++)
+       {
+               if ( strcmp(colorname, colormap[i].colname) == 0)
+                       return colormap[i].colval;
+       }
+
+       // required color not found
+       fprintf(stderr, "atoprc - invalid color used: %s\n", colorname);
+       fprintf(stderr, "supported colors:");
+       for (i=0; i < sizeof colormap/sizeof colormap[0]; i++)
+               fprintf(stderr, " %s", colormap[i].colname);
+       fprintf(stderr, "\n");
+
+       exit(1);
+}
+
+
+void
+do_colinfo(char *name, char *val)
+{
+       colorinfo = modify_color(val);
+}
+
+void
+do_colalmost(char *name, char *val)
+{
+       coloralmost = modify_color(val);
+}
+
+void
+do_colcrit(char *name, char *val)
+{
+       colorcrit = modify_color(val);
+}
+
+void
+do_colthread(char *name, char *val)
+{
+       colorthread = modify_color(val);
+}
+
+void
+do_flags(char *name, char *val)
+{
+       int     i;
+
+       for (i=0; val[i]; i++)
+       {
+               switch (val[i])
+               {
+                  case '-':
+                       break;
+
+                  case MSORTCPU:
+                       showorder = MSORTCPU;
+                       break;
+
+                  case MSORTMEM:
+                       showorder = MSORTMEM;
+                       break;
+
+                  case MSORTDSK:
+                       showorder = MSORTDSK;
+                       break;
+
+                  case MSORTNET:
+                       showorder = MSORTNET;
+                       break;
+
+                  case MSORTAUTO:
+                       showorder = MSORTAUTO;
+                       break;
+
+                  case MPROCGEN:
+                       showtype  = MPROCGEN;
+                       showorder = MSORTCPU;
+                       break;
+
+                  case MPROCMEM:
+                       showtype  = MPROCMEM;
+                       showorder = MSORTMEM;
+                       break;
+
+                  case MPROCDSK:
+                       showtype  = MPROCDSK;
+                       showorder = MSORTDSK;
+                       break;
+
+                  case MPROCNET:
+                       showtype  = MPROCNET;
+                       showorder = MSORTNET;
+                       break;
+
+                  case MPROCVAR:
+                       showtype  = MPROCVAR;
+                       break;
+
+                  case MPROCSCH:
+                       showtype  = MPROCSCH;
+                       showorder = MSORTCPU;
+                       break;
+
+                  case MPROCARG:
+                       showtype  = MPROCARG;
+                       break;
+
+                  case MPROCOWN:
+                       showtype  = MPROCOWN;
+                       break;
+
+                  case MCUMUSER:
+                       showtype  = MCUMUSER;
+                       break;
+
+                  case MCUMPROC:
+                       showtype  = MCUMPROC;
+                       break;
+
+                  case MCUMCONT:
+                       showtype  = MCUMCONT;
+                       break;
+
+                  case MALLPROC:
+                       deviatonly = 0;
+                       break;
+
+                  case MAVGVAL:
+                       avgval=1;
+                       break;
+
+                  case MSYSFIXED:
+                       fixedhead = 1;
+                       break;
+
+                  case MSYSNOSORT:
+                       sysnosort = 1;
+                       break;
+
+                  case MTHREAD:
+                       threadview = 1;
+                       break;
+
+                  case MCOLORS:
+                       usecolors = 0;
+                       break;
+
+                  case MCALCPSS:
+                       calcpss = 1;
+                       break;
+
+                  case MSUPEXITS:
+                       suppressexit = 1;
+                       break;
+               }
+       }
+}
diff --git a/sources/showgeneric.h b/sources/showgeneric.h
new file mode 100644 (file)
index 0000000..77141a8
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+** ATOP - System & Process Monitor
+**
+** The program 'atop' offers the possibility to view the activity of
+** the system on system-level as well as process-level.
+**
+** Include-file describing prototypes and structures for visualization
+** of counters.
+** ================================================================
+** Author:      Gerlof Langeveld
+** E-mail:      gerlof.langeveld@atoptool.nl
+** Date:        July 2002
+**
+** This program is free software; you can redistribute it and/or modify it
+** under the terms of the GNU General Public License as published by the
+** Free Software Foundation; either version 2, or (at your option) any
+** later version.
+**
+** This program is distributed in the hope that it will be useful, but
+** WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+** See the GNU General Public License for more details.
+*/
+#define USERSTUB       9999999
+#define MAXUSERSEL     64
+#define MAXPID         32
+
+struct syscap {
+       int     nrcpu;
+       count_t availcpu;
+       count_t availmem;
+       count_t availdsk;
+       count_t availnet;
+};
+
+struct pselection {
+       char    username[256];
+       uid_t   userid[MAXUSERSEL];
+
+       pid_t   pid[MAXPID];
+
+       char    progname[64];
+       int     prognamesz;
+       regex_t progregex;
+
+       char    argname[64];
+       int     argnamesz;
+       regex_t argregex;
+
+       char    container[16];
+};
+
+struct sselection {
+       char    lvmname[64];    // logical volume selection
+       int     lvmnamesz;
+       regex_t lvmregex;
+
+       char    dskname[64];    // disk selection
+       int     dsknamesz;
+       regex_t dskregex;
+
+       char    itfname[64];    // network interface selection
+       int     itfnamesz;
+       regex_t itfregex;
+};
+
+/*
+** color names
+*/
+#define        COLORINFO       2
+#define        COLORALMOST     3
+#define        COLORCRIT       4
+#define        COLORTHR        5
+
+/*
+** list with keystrokes/flags
+*/
+#define        MPROCGEN        'g'
+#define        MPROCMEM        'm'
+#define        MPROCDSK        'd'
+#define        MPROCNET        'n'
+#define        MPROCSCH        's'
+#define        MPROCVAR        'v'
+#define        MPROCARG        'c'
+#define MPROCOWN       'o'
+
+#define        MCUMUSER        'u'
+#define        MCUMPROC        'p'
+#define        MCUMCONT        'j'
+
+#define        MSORTCPU        'C'
+#define        MSORTDSK        'D'
+#define        MSORTMEM        'M'
+#define        MSORTNET        'N'
+#define        MSORTAUTO       'A'
+
+#define        MTHREAD         'y'
+#define        MCALCPSS        'R'
+#define        MSUPEXITS       'G'
+#define        MCOLORS         'x'
+#define        MSYSFIXED       'f'
+#define        MSYSNOSORT      'F'
+#define        MSYSLIMIT       'l'
+
+#define        MSELUSER        'U'
+#define        MSELPROC        'P'
+#define        MSELCONT        'J'
+#define        MSELPID         'I'
+#define        MSELARG         '/'
+#define        MSELSYS         'S'
+
+#define        MALLPROC        'a'
+#define        MKILLPROC       'k'
+#define        MLISTFW         0x06
+#define        MLISTBW         0x02
+#define MREDRAW         0x0c
+#define        MINTERVAL       'i'
+#define        MPAUSE          'z'
+#define        MQUIT           'q'
+#define        MRESET          'r'
+#define        MSAMPNEXT       't'
+#define        MSAMPPREV       'T'
+#define        MSAMPBRANCH     'b'
+#define        MVERSION        'V'
+#define        MAVGVAL         '1'
+#define        MHELP1          '?'
+#define        MHELP2          'h'
+
+/*
+** general function prototypes
+*/
+void   totalcap   (struct syscap *, struct sstat *, struct tstat **, int);
+void   pricumproc (struct sstat *,  struct devtstat *,
+                               int, unsigned int, int, int);
+
+void   showgenproc(struct tstat *, double, int, int);
+void   showmemproc(struct tstat *, double, int, int);
+void   showdskproc(struct tstat *, double, int, int);
+void   shownetproc(struct tstat *, double, int, int);
+void   showvarproc(struct tstat *, double, int, int);
+void   showschproc(struct tstat *, double, int, int);
+void   showtotproc(struct tstat *, double, int, int);
+void   showcmdproc(struct tstat *, double, int, int);
+
+void   printg     (const char *, ...);
+int    prisyst(struct sstat  *, int, int, int, int, struct sselection *,
+                       char *, int, int, int, int, int, int, int);
+int    priproc(struct tstat  **, int, int, int, int, int, char, char,
+               struct syscap *, int, int);
+void   priphead(int, int, char *, char *, char);
diff --git a/sources/showlinux.c b/sources/showlinux.c
new file mode 100644 (file)
index 0000000..9fe4ea3
--- /dev/null
@@ -0,0 +1,2371 @@
+/*
+** ATOP - System & Process Monitor 
+**
+** The program 'atop' offers the possibility to view the activity of
+** the system on system-level as well as process-level.
+**
+** This source-file contains the Linux-specific functions to calculate
+** figures to be visualized.
+** ==========================================================================
+** Author:      Gerlof Langeveld
+**              Original version.
+** E-mail:      gerlof.langeveld@atoptool.nl
+** Date:        July 2002
+**
+** Author:     JC van Winkel - AT Computing, Nijmegen, Holland
+**              Complete redesign.
+** E-mail:      jc@ATComputing.nl
+** Date:        November 2009
+** --------------------------------------------------------------------------
+** Copyright (C) 2009-2010 JC van Winkel
+**
+** This program is free software; you can redistribute it and/or modify it
+** under the terms of the GNU General Public License as published by the
+** Free Software Foundation; either version 2, or (at your option) any
+** later version.
+**
+** This program is distributed in the hope that it will be useful, but
+** WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+** See the GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+** --------------------------------------------------------------------------
+**
+** $Log: showlinux.c,v $
+** Revision 1.70  2010/10/23 14:04:12  gerlof
+** Counters for total number of running and sleep threads (JC van Winkel).
+**
+** Revision 1.69  2010/05/18 19:20:08  gerlof
+** Introduce CPU frequency and scaling (JC van Winkel).
+**
+** Revision 1.68  2010/04/23 12:19:35  gerlof
+** Modified mail-address in header.
+**
+** Revision 1.67  2010/04/17 17:20:33  gerlof
+** Allow modifying the layout of the columns in the system lines.
+**
+** Revision 1.66  2010/03/16 21:14:46  gerlof
+** Program and user selection can be combined with program and user
+** accumulation.
+**
+** Revision 1.65  2010/03/04 10:53:26  gerlof
+** Support I/O-statistics on logical volumes and MD devices.
+**
+** Revision 1.64  2010/01/18 18:06:28  gerlof
+** Modified priorities for system-level columns.
+**
+** Revision 1.63  2010/01/16 12:54:33  gerlof
+** Corrected order of columns.
+**
+** Revision 1.62  2010/01/16 11:38:02  gerlof
+** Corrected counters for patched kernels (JC van Winkel).
+**
+** Revision 1.61  2010/01/08 11:25:56  gerlof
+** Corrected column-width and priorities of network-stats.
+**
+** Revision 1.60  2010/01/03 18:27:19  gerlof
+** *** empty log message ***
+**
+** Revision 1.59  2009/12/19 21:01:28  gerlof
+** Improved syntax checking for ownprocline keyword (JC van Winkel).
+**
+** Revision 1.58  2009/12/17 11:59:28  gerlof
+** Gather and display new counters: dirty cache and guest cpu usage.
+**
+** Revision 1.57  2009/12/17 10:51:19  gerlof
+** Allow own defined process line with key 'o' and a definition
+** in the atoprc file.
+**
+** Revision 1.56  2009/12/17 09:13:19  gerlof
+** Reformatted some fields for better grouping of info.
+**
+** Revision 1.55  2009/12/12 10:11:18  gerlof
+** Register and display end date and end time for process.
+**
+** Revision 1.54  2009/12/12 09:06:48  gerlof
+** \Corrected cumulated disk I/O per user/program (JC van Winkel).
+**
+** Revision 1.53  2009/12/10 14:02:39  gerlof
+** Add EUID, SUID and FSUID (and similar for GID's).
+**
+** Revision 1.52  2009/12/10 11:56:34  gerlof
+** Various bug-solutions.
+**
+** Revision 1.51  2009/12/10 10:08:01  gerlof
+** Major redesign for improved user interface (variable number of columns).
+** Made by JC van Winkel.
+**
+** Revision 1.49  2008/03/06 08:38:28  gerlof
+** Register/show ppid of a process.
+**
+** Revision 1.48  2008/01/18 07:37:05  gerlof
+** Show information about the state of the individual threads
+** in the scheduling report shown with keystroke 's'.
+**
+** Revision 1.47  2008/01/07 11:34:18  gerlof
+** Correct the sort-order of network-interfaces (on busy-percentage).
+**
+** Revision 1.46  2007/11/07 09:23:29  gerlof
+** Modified format for avg1, avg5 and avg15 (CPL) when counters too large.
+**
+** Revision 1.45  2007/11/05 11:43:25  gerlof
+** Bug-solution for new-process indicator on 64-bits machines.
+**
+** Revision 1.44  2007/11/05 10:57:56  gerlof
+** Bug-solution for huge exit code on 64-bits machines.
+**
+** Revision 1.43  2007/08/17 09:45:57  gerlof
+** Experimental: gather info about HTTP statistics.
+**
+** Revision 1.42  2007/08/16 12:02:04  gerlof
+** Add support for atopsar reporting.
+** Concerns modification of networking-counters.
+**
+** Revision 1.41  2007/07/04 10:18:16  gerlof
+** Bug-solution for division by zero.
+**
+** Revision 1.40  2007/07/03 09:02:29  gerlof
+** Support Apache-statistics.
+**
+** Revision 1.39  2007/03/22 10:12:54  gerlof
+** Support for io counters (>= kernel 2.6.20).
+**
+** Revision 1.38  2007/03/21 14:22:24  gerlof
+** Handle io counters maintained from 2.6.20
+**
+** Revision 1.37  2007/02/13 10:36:09  gerlof
+** Removal of external declarations.
+** Use of hertz variable instead of HZ.
+**
+** Revision 1.36  2007/01/26 12:11:07  gerlof
+** Add configuration-value 'swoutcritsec'.
+**
+** Revision 1.35  2007/01/26 10:25:42  gerlof
+** Introduce steal percentage for virtual machines.
+** Correct bug: when one interface is colored all subsequent interfaces
+**              are colored.
+**
+** Revision 1.34  2007/01/18 10:58:45  gerlof
+** Only check for committed limit if it is not zero.
+**
+** Revision 1.33  2007/01/18 10:37:09  gerlof
+** Add support for colors.
+** Add support for automatic determination of most critical resource.
+** Add support for parsing of new arguments in ~/.atoprc
+**
+** Revision 1.32  2006/11/13 13:48:46  gerlof
+** Implement load-average counters, context-switches and interrupts.
+**
+** Revision 1.31  2006/02/07 08:38:49  gerlof
+** Swapped the zombie counter and exit counter in the PRC-line.
+**
+** Revision 1.30  2006/02/07 08:30:07  gerlof
+** Add possibility to show counters per second.
+** Ease parsing of output-lines by fixed number of columns per line.
+**
+** Revision 1.29  2006/01/30 09:24:12  gerlof
+** PRC-line: 'exits' modified to 'exit' to save space.
+**
+** Revision 1.28  2006/01/30 09:14:26  gerlof
+** Extend memory counters (a.o. page scans).
+**
+** Revision 1.27  2005/11/04 14:16:45  gerlof
+** Minor bug-solutions.
+**
+** Revision 1.26  2005/10/28 09:51:29  gerlof
+** All flags/subcommands are defined as macro's.
+** Subcommand 'p' has been changed to 'z' (pause).
+**
+** Revision 1.25  2005/10/21 09:51:11  gerlof
+** Per-user accumulation of resource consumption.
+**
+** Revision 1.24  2004/12/14 15:06:48  gerlof
+** Implementation of patch-recognition for disk and network-statistics.
+**
+** Revision 1.23  2004/10/28 08:31:41  gerlof
+** New counter: vm committed space
+**
+** Revision 1.22  2004/09/24 10:02:46  gerlof
+** Wrong cpu-numbers for system level statistics.
+**
+** Revision 1.21  2004/09/23 08:21:10  gerlof
+** Added wait-percentage per cpu.
+**
+** Revision 1.20  2004/09/23 07:37:34  gerlof
+** Consistent handling of CPU percentages on system-level and process-level.
+**
+** Revision 1.19  2004/09/13 09:20:21  gerlof
+** Modify subcommands (former 's' -> 'v', 'v' -> 'V', new 's').
+**
+** Revision 1.18  2004/09/02 10:55:21  root
+** Added sleep-average to process-info.
+**
+** Revision 1.17  2004/08/31 09:53:31  gerlof
+** Show information about underlying threads.
+**
+** Revision 1.16  2004/06/01 11:58:34  gerlof
+** Regular expressions for selections on process-name and user-name.
+**
+** Revision 1.15  2004/05/06 09:47:59  gerlof
+** Ported to kernel-version 2.6.
+**
+** Revision 1.14  2003/07/07 09:27:34  gerlof
+** Cleanup code (-Wall proof).
+**
+** Revision 1.13  2003/07/03 12:04:25  gerlof
+** Minor bug fixes.
+**
+** Revision 1.12  2003/06/30 11:29:57  gerlof
+** Enlarge counters to 'long long'.
+**
+** Revision 1.11  2003/06/24 06:22:10  gerlof
+** Limit number of system resource lines.
+**
+** Revision 1.10  2003/02/07 10:43:22  gerlof
+** Solved a division-by-zero bug for process-percentage.
+**
+** Revision 1.9  2003/01/24 14:20:57  gerlof
+** If possible, also show commandline when process has exited.
+**
+** Revision 1.8  2003/01/17 07:32:49  gerlof
+** Show the full command-line per process (option 'c').
+**
+** Revision 1.7  2002/10/04 10:05:54  gerlof
+** Bug-solution: New process indicator in static output set when needed.
+**
+** Revision 1.6  2002/10/03 11:14:42  gerlof
+** Modify (effective) uid/gid to real uid/gid.
+**
+** Revision 1.5  2002/09/26 13:52:51  gerlof
+** Limit header lines by not showing disks.
+** Limit header lines by not showing disks.
+**
+** Revision 1.4  2002/09/16 08:59:13  gerlof
+** Change field EXCODE to STATUS for support of indicator of newly created
+** processes.
+**
+** Revision 1.3  2002/09/02 08:42:44  gerlof
+** Bug-solution: blank line after header when more than 999 screens of
+** process-list information.
+**
+** Revision 1.2  2002/08/30 07:11:20  gerlof
+** Minor changes in the header-line of the process list.
+**
+** Revision 1.1  2002/07/24 11:14:16  gerlof
+** Initial revision
+**
+**
+** Initial
+**
+*/
+
+static const char rcsid[] = "$Id: showlinux.c,v 1.70 2010/10/23 14:04:12 gerlof Exp $";
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <curses.h>
+#include <pwd.h>
+#include <grp.h>
+#include <regex.h>
+
+#include "atop.h"
+#include "photoproc.h"
+#include "photosyst.h"
+#include "showgeneric.h"
+#include "showlinux.h"
+
+static void    make_proc_dynamicgen(void);
+
+/*
+** critical percentages for occupation-percentage;
+** these defaults can be overruled via the config-file
+*/
+int   cpubadness = 90;        /* percentage           */
+int   membadness = 90;        /* percentage           */
+int   swpbadness = 80;        /* percentage           */
+int   dskbadness = 70;        /* percentage           */
+int   netbadness = 90;        /* percentage           */
+int   pagbadness = 10;        /* number per second    */
+
+int   almostcrit = 80;        /* percentage           */
+
+/*
+ * tables with all sys_printdefs
+ */
+sys_printdef *prcsyspdefs[] = {
+       &syspdef_PRCSYS,
+       &syspdef_PRCUSER,
+       &syspdef_PRCNPROC,
+        &syspdef_PRCNRUNNING,
+        &syspdef_PRCNSLEEPING,
+        &syspdef_PRCNDSLEEPING,
+       &syspdef_PRCNZOMBIE,
+       &syspdef_PRCCLONES,
+       &syspdef_PRCNNEXIT,
+       &syspdef_BLANKBOX,
+        0
+};
+sys_printdef *cpusyspdefs[] = {
+       &syspdef_CPUSYS,
+       &syspdef_CPUUSER,
+       &syspdef_CPUIRQ,
+       &syspdef_CPUIDLE,
+       &syspdef_CPUWAIT,
+       &syspdef_BLANKBOX,
+       &syspdef_CPUFREQ,
+       &syspdef_CPUSCALE,
+       &syspdef_CPUSTEAL,
+       &syspdef_CPUGUEST,
+       &syspdef_BLANKBOX,
+        0
+};
+sys_printdef *cpisyspdefs[] = {
+       &syspdef_CPUISYS,
+       &syspdef_CPUIUSER,
+       &syspdef_CPUIIRQ,
+       &syspdef_CPUIIDLE,
+       &syspdef_CPUIWAIT,
+       &syspdef_BLANKBOX,
+       &syspdef_CPUIFREQ,
+       &syspdef_CPUISCALE,
+       &syspdef_CPUISTEAL,
+       &syspdef_CPUIGUEST,
+       &syspdef_BLANKBOX,
+        0
+};
+sys_printdef *cplsyspdefs[] = {
+       &syspdef_CPLAVG1,
+       &syspdef_CPLAVG5,
+       &syspdef_CPLAVG15,
+       &syspdef_CPLCSW,
+       &syspdef_CPLNUMCPU,
+       &syspdef_CPLINTR,
+       &syspdef_BLANKBOX,
+        0
+};
+sys_printdef *memsyspdefs[] = {
+       &syspdef_MEMTOT,
+       &syspdef_MEMFREE,
+       &syspdef_MEMCACHE,
+       &syspdef_MEMDIRTY,
+       &syspdef_MEMBUFFER,
+       &syspdef_MEMSLAB,
+       &syspdef_RECSLAB,
+       &syspdef_BLANKBOX,
+       &syspdef_SHMEM,
+       &syspdef_SHMRSS,
+       &syspdef_SHMSWP,
+       &syspdef_BLANKBOX,
+       &syspdef_VMWBAL,
+       &syspdef_BLANKBOX,
+       &syspdef_HUPTOT,
+       &syspdef_HUPUSE,
+        0
+};
+sys_printdef *swpsyspdefs[] = {
+       &syspdef_SWPTOT,
+       &syspdef_SWPFREE,
+       &syspdef_SWPCOMMITTED,
+       &syspdef_SWPCOMMITLIM,
+       &syspdef_BLANKBOX,
+        0
+};
+sys_printdef *pagsyspdefs[] = {
+       &syspdef_PAGSCAN,
+       &syspdef_PAGSTEAL,
+       &syspdef_PAGSTALL,
+       &syspdef_PAGSWIN,
+       &syspdef_PAGSWOUT,
+       &syspdef_BLANKBOX,
+        0
+};
+sys_printdef *contsyspdefs[] = {
+       &syspdef_CONTNAME,
+       &syspdef_CONTNPROC,
+       &syspdef_CONTCPU,
+       &syspdef_CONTMEM,
+       &syspdef_BLANKBOX,
+       0
+};
+sys_printdef *dsksyspdefs[] = {
+       &syspdef_DSKNAME,
+       &syspdef_DSKBUSY,
+       &syspdef_DSKNREAD,
+       &syspdef_DSKNWRITE,
+       &syspdef_DSKMBPERSECWR,
+       &syspdef_DSKMBPERSECRD,
+       &syspdef_DSKKBPERWR,
+       &syspdef_DSKKBPERRD,
+       &syspdef_DSKAVQUEUE,
+       &syspdef_DSKAVIO,
+       &syspdef_BLANKBOX,
+        0
+};
+sys_printdef *nfsmntsyspdefs[] = {
+       &syspdef_NFMPATH,
+       &syspdef_NFMSERVER,
+       &syspdef_NFMTOTREAD,
+       &syspdef_NFMTOTWRITE,
+       &syspdef_NFMNREAD,
+       &syspdef_NFMNWRITE,
+       &syspdef_NFMDREAD,
+       &syspdef_NFMDWRITE,
+       &syspdef_NFMMREAD,
+       &syspdef_NFMMWRITE,
+       &syspdef_BLANKBOX,
+        0
+};
+sys_printdef *nfcsyspdefs[] = {
+       &syspdef_NFCRPCCNT,
+       &syspdef_NFCRPCREAD,
+       &syspdef_NFCRPCWRITE,
+       &syspdef_NFCRPCRET,
+       &syspdef_NFCRPCARF,
+       &syspdef_BLANKBOX,
+        0
+};
+sys_printdef *nfssyspdefs[] = {
+       &syspdef_NFSRPCCNT,
+       &syspdef_NFSRPCREAD,
+       &syspdef_NFSRPCWRITE,
+       &syspdef_NFSNRBYTES,
+       &syspdef_NFSNWBYTES,
+       &syspdef_NFSNETTCP,
+       &syspdef_NFSNETUDP,
+       &syspdef_NFSBADFMT,
+       &syspdef_NFSBADAUT,
+       &syspdef_NFSBADCLN,
+       &syspdef_NFSRCHITS,
+       &syspdef_NFSRCMISS,
+       &syspdef_NFSRCNOCA,
+       &syspdef_BLANKBOX,
+        0
+};
+sys_printdef *nettranssyspdefs[] = {
+       &syspdef_NETTRANSPORT,
+       &syspdef_NETTCPI,
+       &syspdef_NETTCPO,
+       &syspdef_NETUDPI,
+       &syspdef_NETUDPO,
+       &syspdef_NETTCPACTOPEN,
+       &syspdef_NETTCPPASVOPEN,
+       &syspdef_NETTCPRETRANS,
+       &syspdef_NETTCPINERR,
+       &syspdef_NETTCPORESET,
+       &syspdef_NETUDPNOPORT,
+       &syspdef_NETUDPINERR,
+       &syspdef_BLANKBOX,
+        0
+};
+sys_printdef *netnetsyspdefs[] = {
+       &syspdef_NETNETWORK,
+       &syspdef_NETIPI,
+       &syspdef_NETIPO,
+       &syspdef_NETIPFRW,
+       &syspdef_NETIPDELIV,
+       &syspdef_NETICMPIN,
+       &syspdef_NETICMPOUT,
+       &syspdef_BLANKBOX,
+        0
+};
+sys_printdef *netintfsyspdefs[] = {
+       &syspdef_NETNAME,
+       &syspdef_NETPCKI,
+       &syspdef_NETPCKO,
+       &syspdef_NETSPEEDMAX,
+       &syspdef_NETSPEEDIN,
+       &syspdef_NETSPEEDOUT,
+       &syspdef_NETCOLLIS,
+       &syspdef_NETMULTICASTIN,
+       &syspdef_NETRCVERR,
+       &syspdef_NETSNDERR,
+       &syspdef_NETRCVDROP,
+       &syspdef_NETSNDDROP,
+       &syspdef_BLANKBOX,
+        0
+};
+
+
+/*
+ * table with all proc_printdefs
+ */
+proc_printdef *allprocpdefs[]= 
+{
+       &procprt_PID,
+       &procprt_TID,
+       &procprt_PPID,
+       &procprt_SYSCPU,
+       &procprt_USRCPU,
+       &procprt_VGROW,
+       &procprt_RGROW,
+       &procprt_MINFLT,
+       &procprt_MAJFLT,
+       &procprt_VSTEXT,
+       &procprt_VSIZE,
+       &procprt_RSIZE,
+       &procprt_PSIZE,
+       &procprt_VSLIBS,
+       &procprt_VDATA,
+       &procprt_VSTACK,
+       &procprt_SWAPSZ,
+       &procprt_CMD,
+       &procprt_RUID,
+       &procprt_EUID,
+       &procprt_SUID,
+       &procprt_FSUID,
+       &procprt_RGID,
+       &procprt_EGID,
+       &procprt_SGID,
+       &procprt_FSGID,
+       &procprt_CTID,
+       &procprt_VPID,
+       &procprt_CID,
+       &procprt_STDATE,
+       &procprt_STTIME,
+       &procprt_ENDATE,
+       &procprt_ENTIME,
+       &procprt_THR,
+       &procprt_TRUN,
+       &procprt_TSLPI,
+       &procprt_TSLPU,
+       &procprt_POLI,
+       &procprt_NICE,
+       &procprt_PRI,
+       &procprt_RTPR,
+       &procprt_CURCPU,
+       &procprt_ST,
+       &procprt_EXC,
+       &procprt_S,
+       &procprt_COMMAND_LINE,
+       &procprt_NPROCS,
+       &procprt_RDDSK,
+       &procprt_WRDSK,
+       &procprt_CWRDSK,
+       &procprt_WCANCEL,
+       &procprt_TCPRCV,
+       &procprt_TCPRASZ,
+       &procprt_TCPSND,
+       &procprt_TCPSASZ,
+       &procprt_UDPRCV,
+       &procprt_UDPRASZ,
+       &procprt_UDPSND,
+       &procprt_UDPSASZ,
+       &procprt_RNET,
+       &procprt_SNET,
+       &procprt_BANDWI,
+       &procprt_BANDWO,
+       &procprt_SORTITEM,
+        0
+};
+
+/*
+ * table with all proc_printdefs with PID/TID width to be initialized
+ */
+proc_printdef *idprocpdefs[]= 
+{
+       &procprt_PID,
+       &procprt_TID,
+       &procprt_PPID,
+       &procprt_VPID,
+       0
+};
+
+
+/***************************************************************/
+/*
+ * output definitions for process data
+ * these should be user configurable
+ */
+proc_printpair userprocs[MAXITEMS];
+proc_printpair memprocs[MAXITEMS];
+proc_printpair schedprocs[MAXITEMS];
+proc_printpair genprocs[MAXITEMS];
+proc_printpair dskprocs[MAXITEMS];
+proc_printpair netprocs[MAXITEMS];
+proc_printpair varprocs[MAXITEMS];
+proc_printpair cmdprocs[MAXITEMS];
+proc_printpair ownprocs[MAXITEMS];
+proc_printpair totusers[MAXITEMS];
+proc_printpair totprocs[MAXITEMS];
+proc_printpair totconts[MAXITEMS];
+
+
+/*****************************************************************/
+/*
+ * output definitions for system data
+ * these should be user configurable
+ */
+sys_printpair sysprcline[MAXITEMS];
+sys_printpair allcpuline[MAXITEMS];
+sys_printpair indivcpuline[MAXITEMS];
+sys_printpair cplline[MAXITEMS];
+sys_printpair memline[MAXITEMS];
+sys_printpair swpline[MAXITEMS];
+sys_printpair pagline[MAXITEMS];
+sys_printpair contline[MAXITEMS];
+sys_printpair dskline[MAXITEMS];
+sys_printpair nettransportline[MAXITEMS];
+sys_printpair netnetline[MAXITEMS];
+sys_printpair netinterfaceline[MAXITEMS];
+sys_printpair nfsmountline[MAXITEMS];
+sys_printpair nfcline[MAXITEMS];
+sys_printpair nfsline[MAXITEMS];
+
+typedef struct {
+        const char *name;
+        int        prio;
+} name_prio;
+
+/*
+** make an string,int pair array from a string.  chop based on spaces/tabs
+** example: input: "ABCD:3  EFG:1   QWE:16"
+**         output: { { "ABCD", 3 }, {"EFG", 1},  { "QWE", 16}, { 0, 0 }  }
+*/
+static void
+makeargv(char *line, const char *linename, name_prio *vec) 
+{
+        int   i=0;
+        char *p=line;
+        char *name=0;
+        char *prio=0;
+
+        // find pair and scan it
+        while (*p && i<MAXITEMS-1) 
+        {
+                // skip initial spaces
+                while (*p && (*p==' ' || *p=='\t'))
+                {
+                        ++p;
+                }
+                if (! *p) 
+                {
+                        break;
+                }
+                name=p;
+                // found a new word; let's chop!
+                while (*p && *p !=':')
+                {
+                        ++p;
+                }
+                if (*p==':')
+                {
+                        *p=0;
+                }
+                else
+                {
+                       fprintf(stderr,
+                       "atoprc - %s: no name:prio pair for "
+                        "`%s'\n", name, linename);
+                        cleanstop(1);
+                }
+
+                /* now get number */
+                p++;
+                prio=p;
+                errno = 0;    /* To distinguish success/failure after call */
+
+                long lprio=strtol(p, &p, 10);
+
+                if (prio==p || errno == ERANGE || lprio >= INT_MAX || lprio <0)
+                {
+                       fprintf(stderr,
+                       "atoprc - %s: item `%s` has "
+                        "invalid priority `", linename, name);
+                        while (*prio && *prio !=' ') {
+                            fputc(*prio, stderr);
+                            prio++;
+                        }
+                        fprintf(stderr, "'\n");
+                        cleanstop(1);
+                }
+                vec[i].name=name;
+                vec[i].prio=lprio;
+
+                ++i;
+        }
+                
+        vec[i].name=0;
+}
+
+
+/*
+ * make_sys_prints: make array of sys_printpairs
+ * input: string, sys_printpair array, maxentries
+ */
+void
+make_sys_prints(sys_printpair *ar, int maxn, const char *pairs, 
+                sys_printdef *permissables[], const char *linename)
+{
+        name_prio items[MAXITEMS];
+        int n=strlen(pairs);
+
+        char str[n+1];
+        strcpy(str, pairs);
+
+        makeargv(str, linename, items);
+
+        int i;
+        for(i=0; items[i].name && i<maxn-1; ++i) 
+        {
+                const char *name=items[i].name;
+                int j;
+                for (j=0; permissables[j] != 0; ++j)
+                {
+                        if (strcmp(permissables[j]->configname, name)==0)
+                        {
+                                ar[i].f=permissables[j];
+                                ar[i].prio=items[i].prio;
+                                break;
+                        }
+                }
+                if (permissables[j]==0)
+                {
+                        fprintf(stderr,
+                               "atoprc - own system line: item %s invalid in %s line!\n",
+                               name, linename);
+                        cleanstop(1);
+                }
+        }
+        ar[i].f=0;
+        ar[i].prio=0;
+}
+
+
+
+/*
+ * init_proc_prints: determine width of columns that are
+ *                   dependent of dynamic values 
+ */
+void 
+init_proc_prints()
+{
+       int     i, numdigits = 5;
+       char    linebuf[64];
+       FILE    *fp;
+
+       /*
+       ** determine maximum number of digits for PID/TID
+       */
+       if ( (fp = fopen("/proc/sys/kernel/pid_max", "r")) != NULL)
+       {
+                if ( fgets(linebuf, sizeof(linebuf), fp) != NULL)
+                {
+                        numdigits = strlen(linebuf) - 1;
+                }
+
+                fclose(fp);
+        }
+
+       /*
+       ** fill number of digits for various PID/TID columns
+       ** and reformat header to new width
+       */
+       for (i=0; idprocpdefs[i] != 0; i++)
+       {
+               idprocpdefs[i]->width = numdigits;
+
+               if ( strlen(idprocpdefs[i]->head) < numdigits)
+               {
+                       char *p = malloc(numdigits) + 1;
+
+                       sprintf(p, "%*s", numdigits, idprocpdefs[i]->head);
+                       idprocpdefs[i]->head = p;
+               }
+       }
+}
+
+/*
+ * make_proc_prints: make array of proc_printpairs
+ * input: string, proc_printpair array, maxentries
+ */
+void 
+make_proc_prints(proc_printpair *ar, int maxn, const char *pairs, 
+const char *linename)
+{
+        name_prio items[MAXITEMS];
+        int n=strlen(pairs);
+
+        char str[n+1];
+        strcpy(str, pairs);
+
+        makeargv(str, linename, items);
+
+        int i;
+        for(i=0; items[i].name && i<maxn-1; ++i) 
+        {
+                const char *name=items[i].name;
+                int j;
+                for (j=0; allprocpdefs[j] != 0; ++j)
+                {
+                        if (strcmp(allprocpdefs[j]->configname, name)==0)
+                        {
+                                ar[i].f=allprocpdefs[j];
+                                ar[i].prio=items[i].prio;
+                                break;
+                        }
+                }
+                if (allprocpdefs[j]==0)
+                {
+                        fprintf(stderr,
+                               "atoprc - ownprocline: item %s invalid!\n",
+                               name);
+                        cleanstop(1);
+                }
+        }
+        ar[i].f=0;
+        ar[i].prio=0;
+}
+
+/*
+** calculate the total consumption on system-level for the 
+** four main resources
+*/
+void
+totalcap(struct syscap *psc, struct sstat *sstat,
+                             struct tstat **proclist, int nactproc)
+{
+        register int    i;
+
+        psc->nrcpu      = sstat->cpu.nrcpu;
+        psc->availcpu   = sstat->cpu.all.stime +
+                          sstat->cpu.all.utime +
+                          sstat->cpu.all.ntime +
+                          sstat->cpu.all.itime +
+                          sstat->cpu.all.wtime +
+                          sstat->cpu.all.Itime +
+                          sstat->cpu.all.Stime +
+                          sstat->cpu.all.steal;
+
+        psc->availmem   = sstat->mem.physmem * pagesize/1024;
+
+       /*
+       ** calculate total transfer issued by the active processes
+       ** for disk and for network
+       */
+       for (psc->availnet=psc->availdsk=0, i=0; i < nactproc; i++) 
+       {
+               struct tstat    *curstat = *(proclist+i);
+               count_t         nett_wsz;
+
+               psc->availnet += curstat->net.tcpssz;
+               psc->availnet += curstat->net.tcprsz;
+               psc->availnet += curstat->net.udpssz;
+               psc->availnet += curstat->net.udprsz;
+
+               if (curstat->dsk.wsz > curstat->dsk.cwsz)
+                       nett_wsz = curstat->dsk.wsz -
+                                  curstat->dsk.cwsz;
+               else
+                       nett_wsz = 0;
+
+               psc->availdsk += curstat->dsk.rsz;
+               psc->availdsk += nett_wsz;
+       }
+}
+
+/*
+** calculate cumulative system- and user-time for all active processes
+*/
+void
+pricumproc(struct sstat *sstat, struct devtstat *devtstat,
+           int nexit, unsigned int noverflow, int avgval, int nsecs)
+{
+
+        static int firsttime=1;
+
+        if (firsttime)
+        {
+                firsttime=0;
+
+                if (sysprcline[0].f == 0)
+                {
+                    make_sys_prints(sysprcline, MAXITEMS,
+                        "PRCSYS:8 "
+                        "PRCUSER:8 "
+                       "BLANKBOX:0 "
+                        "PRCNPROC:7 "
+                        "PRCNRUNNING:5 "
+                        "PRCNSLEEPING:5 "
+                        "PRCNDSLEEPING:5 "
+                        "PRCNZOMBIE:5 "
+                        "PRCCLONES:4 "
+                       "BLANKBOX:0 "
+                        "PRCNNEXIT:6", prcsyspdefs, "builtin sysprcline");
+                }
+                if (allcpuline[0].f == 0)
+                {
+                    make_sys_prints(allcpuline, MAXITEMS,
+                       "CPUSYS:9 "
+                       "CPUUSER:8 "
+                       "CPUIRQ:5 "
+                       "BLANKBOX:0 "
+                       "CPUIDLE:6 "
+                       "CPUWAIT:6 "
+                       "BLANKBOX:0 "
+                        "CPUSTEAL:2 "
+                        "CPUGUEST:3 "
+                        "CPUFREQ:4 "
+                        "CPUSCALE:4 ", cpusyspdefs, "builtin allcpuline");
+                }
+
+                if (indivcpuline[0].f == 0)
+                {
+                    make_sys_prints(indivcpuline, MAXITEMS,
+                       "CPUISYS:9 "
+                        "CPUIUSER:8 "
+                       "CPUIIRQ:5 "
+                       "BLANKBOX:0 "
+                       "CPUIIDLE:6 "
+                       "CPUIWAIT:6 "
+                       "BLANKBOX:0 "
+                        "CPUISTEAL:2 "
+                        "CPUIGUEST:3 "
+                        "CPUIFREQ:4 "
+                        "CPUISCALE:4 ", cpisyspdefs, "builtin indivcpuline");
+                }
+
+                if (cplline[0].f == 0)
+                {
+                    make_sys_prints(cplline, MAXITEMS,
+                       "CPLAVG1:4 "
+                       "CPLAVG5:3 "
+                       "CPLAVG15:2 "
+                       "BLANKBOX:0 "
+                       "CPLCSW:6 "
+                       "CPLINTR:5 "
+                       "BLANKBOX:0 "
+                       "CPLNUMCPU:1", cplsyspdefs, "builtin cplline");
+                }
+
+                if (memline[0].f == 0)
+                {
+                    make_sys_prints(memline, MAXITEMS,
+                       "MEMTOT:6 "
+                       "MEMFREE:7 "
+                       "MEMCACHE:5 "
+                       "MEMDIRTY:3 "
+                       "MEMBUFFER:5 "
+                       "MEMSLAB:5 "
+                       "RECSLAB:2 "
+                       "BLANKBOX:0 "
+                       "SHMEM:4 "
+                       "SHMRSS:3 "
+                       "SHMSWP:1 "
+                       "BLANKBOX:0 "
+                       "VMWBAL:4 "
+                       "BLANKBOX:0 "
+                       "HUPTOT:4 "
+                       "HUPUSE:3 ", memsyspdefs, "builtin memline");
+                }
+                if (swpline[0].f == 0)
+                {
+                    make_sys_prints(swpline, MAXITEMS,
+                       "SWPTOT:3 "
+                       "SWPFREE:4 "
+                       "BLANKBOX:0 "
+                       "BLANKBOX:0 "
+                       "BLANKBOX:0 "
+                       "BLANKBOX:0 "
+                       "BLANKBOX:0 "
+                       "BLANKBOX:0 "
+                       "SWPCOMMITTED:5 "
+                       "SWPCOMMITLIM:6", swpsyspdefs, "builtin swpline");
+                }
+                if (pagline[0].f == 0)
+                {
+                    make_sys_prints(pagline, MAXITEMS,
+                       "PAGSCAN:3 "
+                       "PAGSTEAL:3 "
+                       "PAGSTALL:1 "
+                       "BLANKBOX:0 "
+                       "BLANKBOX:0 "
+                       "BLANKBOX:0 "
+                       "BLANKBOX:0 "
+                       "BLANKBOX:0 "
+                       "PAGSWIN:3 "
+                       "PAGSWOUT:4", pagsyspdefs, "builtin pagline");
+                }
+                if (contline[0].f == 0)
+                {
+                    make_sys_prints(contline, MAXITEMS,
+                       "CONTNAME:8 "
+                       "CONTNPROC:7 "
+                       "CONTCPU:6 "
+                       "CONTMEM:6 "
+                       "BLANKBOX:0 "
+                       "BLANKBOX:0 ", contsyspdefs, "builtin contline");
+                }
+                if (dskline[0].f == 0)
+                {
+                    make_sys_prints(dskline, MAXITEMS,
+                       "DSKNAME:8 "
+                       "DSKBUSY:7 "
+                       "DSKNREAD:6 "
+                       "DSKNWRITE:6 "
+                       "DSKKBPERRD:4 "
+                       "DSKKBPERWR:4 "
+                        "DSKMBPERSECRD:5 "
+                        "DSKMBPERSECWR:5 "
+                       "DSKAVQUEUE:1 "
+                       "DSKAVIO:5", dsksyspdefs, "builtin dskline");
+                }
+                if (nfsmountline[0].f == 0)
+                {
+                    make_sys_prints(nfsmountline, MAXITEMS,
+                       "NFMPATH:8 "
+                       "NFMSERVER:8 "
+                       "NFMTOTREAD:8 "
+                       "NFMTOTWRITE:8 "
+                       "BLANKBOX:0 "
+                       "NFMNREAD:7 "
+                       "NFMNWRITE:6 "
+                       "BLANKBOX:0 "
+                       "NFMDREAD:5 "
+                       "NFMDWRITE:4 "
+                       "BLANKBOX:0 "
+                       "NFMMREAD:3 "
+                       "NFMMWRITE:2 "
+                       "BLANKBOX:0 "
+                        "BLANKBOX:0", nfsmntsyspdefs, "builtin nfsmountline");
+                }
+                if (nfcline[0].f == 0)
+                {
+                    make_sys_prints(nfcline, MAXITEMS,
+                       "NFCRPCCNT:8 "
+                       "NFCRPCREAD:7 "
+                       "NFCRPCWRITE:7 "
+                       "NFCRPCRET:5 "
+                       "NFCRPCARF:5 "
+                       "BLANKBOX:0 "
+                       "BLANKBOX:0 "
+                       "BLANKBOX:0 "
+                       "BLANKBOX:0 "
+                       "BLANKBOX:0 ", nfcsyspdefs, "builtin nfcline");
+               }
+                if (nfsline[0].f == 0)
+                {
+                    make_sys_prints(nfsline, MAXITEMS,
+                       "NFSRPCCNT:8 "
+                       "NFSRPCREAD:6 "
+                       "NFSRPCWRITE:6 "
+                       "BLANKBOX:0 "
+                       "NFSNRBYTES:7 "
+                       "NFSNWBYTES:7 "
+                       "BLANKBOX:0 "
+                       "NFSNETTCP:5 "
+                       "NFSNETUDP:5 "
+                       "BLANKBOX:0 "
+                       "NFSRCHITS:3 "
+                       "NFSRCMISS:2 "
+                       "NFSRCNOCA:1 "
+                       "BLANKBOX:0 "
+                       "NFSBADFMT:4 "
+                       "NFSBADAUT:4 "
+                       "NFSBADCLN:4 ", nfssyspdefs, "builtin nfsline");
+               }
+                if (nettransportline[0].f == 0)
+                {
+                    make_sys_prints(nettransportline, MAXITEMS,
+                       "NETTRANSPORT:9 "
+                       "NETTCPI:8 "
+                       "NETTCPO:8 "
+                       "NETUDPI:8 "
+                       "NETUDPO:8 "
+                        "NETTCPACTOPEN:6 "
+                        "NETTCPPASVOPEN:5 "
+                        "NETTCPRETRANS:4 "
+                        "NETTCPINERR:3 "
+                        "NETTCPORESET:2 "
+                        "NETUDPNOPORT:1 "
+                        "NETUDPINERR:3", nettranssyspdefs, "builtin nettransportline");
+                }
+                if (netnetline[0].f == 0)
+                {
+                    make_sys_prints(netnetline, MAXITEMS,
+                        "NETNETWORK:5 "
+                        "NETIPI:4 "
+                        "NETIPO:4 "
+                        "NETIPFRW:4 "
+                        "NETIPDELIV:4 "
+                       "BLANKBOX:0 "
+                       "BLANKBOX:0 "
+                       "BLANKBOX:0 "
+                        "NETICMPIN:1 "
+                        "NETICMPOUT:1 ", netnetsyspdefs, "builtin netnetline");
+                }
+                if (netinterfaceline[0].f == 0)
+                {
+                    make_sys_prints(netinterfaceline, MAXITEMS,
+                       "NETNAME:8 "
+                       "BLANKBOX:0 "
+                       "NETPCKI:7 "
+                       "NETPCKO:7 "
+                       "BLANKBOX:0 "
+                       "NETSPEEDMAX:5 "
+                       "NETSPEEDIN:6 "
+                       "NETSPEEDOUT:6 "
+                       "BLANKBOX:0 "
+                        "NETCOLLIS:2 "
+                        "NETMULTICASTIN:2 "
+                        "NETRCVERR:4 "
+                        "NETSNDERR:4 "
+                        "NETRCVDROP:3 "
+                        "NETSNDDROP:3", netintfsyspdefs, "builtin netinterfaceline");
+                }
+        }  // firsttime
+
+
+        int     i;
+        extraparam extra;
+
+
+        for (i=0, extra.totut=extra.totst=0; i < devtstat->nprocactive; i++)
+        {
+               struct tstat *curstat = *(devtstat->procactive+i);
+
+                extra.totut    += curstat->cpu.utime;
+                extra.totst    += curstat->cpu.stime;
+        }
+
+        extra.nproc    = devtstat->nprocall;
+       extra.ntrun     = devtstat->totrun;
+       extra.ntslpi    = devtstat->totslpi;
+       extra.ntslpu    = devtstat->totslpu;
+        extra.nzomb    = devtstat->totzombie;
+        extra.nexit    = nexit;
+        extra.noverflow        = noverflow;
+        extra.avgval   = avgval;
+        extra.nsecs    = nsecs;
+
+        move(1, 0);
+        showsysline(sysprcline, sstat, &extra, "PRC", 0);
+}
+
+/*
+** print the header for the process list
+*/
+void
+priphead(int curlist, int totlist, char *showtype, char *showorder,
+                                                       char autosort)
+{
+        static int      firsttime=1;
+        static int      prev_supportflags = -1, prev_threadview = -1;
+
+       /*
+       ** determine once the layout of all per-process reports
+       ** except for the generic report (might change dynamically)
+       */
+        if (firsttime) 
+        {
+               init_proc_prints();
+
+                make_proc_prints(memprocs, MAXITEMS, 
+                        "PID:10 TID:3 MINFLT:2 MAJFLT:2 VSTEXT:4 VSLIBS:4 "
+                       "VDATA:4 VSTACK:4 VSIZE:6 RSIZE:7 PSIZE:5 "
+                        "VGROW:7 RGROW:8 SWAPSZ:5 RUID:1 EUID:0 "
+                        "SORTITEM:9 CMD:10", 
+                        "built-in memprocs");
+
+                make_proc_prints(schedprocs, MAXITEMS, 
+                        "PID:10 TID:6 CID:5 VPID:4 CTID:4 TRUN:7 TSLPI:7 "
+                       "TSLPU:7 POLI:8 NICE:9 PRI:9 RTPR:9 CPUNR:8 ST:8 "
+                       "EXC:8 S:8 SORTITEM:10 CMD:10", 
+                        "built-in schedprocs");
+
+                make_proc_prints(dskprocs, MAXITEMS, 
+                        "PID:10 TID:4 RDDSK:9 "
+                        "WRDSK:9 WCANCL:8 "
+                        "SORTITEM:10 CMD:10", 
+                        "built-in dskprocs");
+
+                make_proc_prints(netprocs, MAXITEMS, 
+                        "PID:10 TID:6 "
+                       "TCPRCV:9 TCPRASZ:4 TCPSND:9 TCPSASZ:4 "
+                       "UDPRCV:8 UDPRASZ:3 UDPSND:8 UDPSASZ:3 "
+                       "BANDWI:10 BANDWO:10 "
+                        "SORTITEM:10 CMD:10", 
+                        "built-in netprocs");
+
+                make_proc_prints(varprocs, MAXITEMS,
+                        "PID:10 TID:4 PPID:9 CID:2 VPID:1 CTID:1 "
+                       "RUID:8 RGID:8 EUID:5 EGID:4 "
+                       "SUID:3 SGID:2 FSUID:3 FSGID:2 "
+                        "STDATE:7 STTIME:7 ENDATE:5 ENTIME:5 "
+                       "ST:6 EXC:6 S:6 SORTITEM:10 CMD:10", 
+                        "built-in varprocs");
+
+                make_proc_prints(cmdprocs, MAXITEMS,
+                        "PID:10 TID:4 S:8 SORTITEM:10 COMMAND-LINE:10", 
+                        "built-in cmdprocs");
+
+                make_proc_prints(totusers, MAXITEMS, 
+                        "NPROCS:10 SYSCPU:9 USRCPU:9 VSIZE:6 "
+                        "RSIZE:8 PSIZE:8 SWAPSZ:5 RDDSK:7 CWRDSK:7 "
+                       "RNET:6 SNET:6 SORTITEM:10 RUID:10", 
+                        "built-in totusers");
+
+                make_proc_prints(totprocs, MAXITEMS, 
+                        "NPROCS:10 SYSCPU:9 USRCPU:9 VSIZE:6 "
+                        "RSIZE:8 PSIZE:8 SWAPSZ:5 RDDSK:7 CWRDSK:7 "
+                       "RNET:6 SNET:6 SORTITEM:10 CMD:10", 
+                        "built-in totprocs");
+
+                make_proc_prints(totconts, MAXITEMS, 
+                        "NPROCS:10 SYSCPU:9 USRCPU:9 VSIZE:6 "
+                        "RSIZE:8 PSIZE:8 SWAPSZ:5 RDDSK:7 CWRDSK:7 "
+                       "RNET:6 SNET:6 SORTITEM:10 CID:10", 
+                        "built-in totconts");
+        }
+
+       /*
+       ** update the generic report if needed
+       */
+       if (prev_supportflags != supportflags || prev_threadview != threadview)
+       {
+               make_proc_dynamicgen();
+
+               prev_supportflags = supportflags;
+               prev_threadview   = threadview;
+
+               if (*showtype == MPROCNET && !(supportflags&NETATOP) )
+               {
+                       *showtype  = MPROCGEN;
+                       *showorder = MSORTCPU;
+               }
+       }
+
+        /*
+        ** print the header line
+        */
+        switch (*showtype)
+        {
+           case MPROCGEN:
+                showhdrline(genprocs, curlist, totlist, *showorder, autosort);
+                break;
+
+           case MPROCMEM:
+                showhdrline(memprocs, curlist, totlist, *showorder, autosort);
+                break;
+
+           case MPROCDSK:
+                showhdrline(dskprocs, curlist, totlist, *showorder, autosort);
+                break;
+
+           case MPROCNET:
+                showhdrline(netprocs, curlist, totlist, *showorder, autosort);
+                break;
+
+           case MPROCVAR:
+                showhdrline(varprocs, curlist, totlist, *showorder, autosort);
+                break;
+
+           case MPROCARG:
+                showhdrline(cmdprocs, curlist, totlist, *showorder, autosort);
+                break;
+
+           case MPROCOWN:
+                showhdrline(ownprocs, curlist, totlist, *showorder, autosort);
+                break;
+
+           case MPROCSCH:
+                showhdrline(schedprocs, curlist, totlist, *showorder, autosort);
+                break;
+
+           case MCUMUSER:
+                showhdrline(totusers, curlist, totlist, *showorder, autosort);
+                break;
+
+           case MCUMPROC:
+                showhdrline(totprocs, curlist, totlist, *showorder, autosort);
+                break;
+
+           case MCUMCONT:
+                showhdrline(totconts, curlist, totlist, *showorder, autosort);
+                break;
+        }
+}
+
+/*
+** assemble the layout of the generic line,
+** depending on the supported features (like
+** I/O stats, network stats) and current view
+*/
+#define        FORMPID "PID:10 "
+#define        FORMTID "TID:6 "
+#define        FORMCID "CID:5 "
+#define        FORMCPU "SYSCPU:9 USRCPU:9 "
+#define FORMMEM        "VGROW:8 RGROW:8 "
+#define FORMDSK        "RDDSK:7 CWRDSK:7 "
+#define FORMNET        "RNET:6 SNET:6 "
+#define FORMMSC        "RUID:3 EUID:2 ST:4 EXC:4 THR:4 S:4 CPUNR:4 "
+#define FORMEND        "SORTITEM:10 CMD:10"
+
+static void
+make_proc_dynamicgen()
+{
+       char format[300], *p = format;
+
+       memcpy(p, FORMPID, sizeof FORMPID -1);
+       p += sizeof FORMPID -1;
+
+       if (threadview)
+       {
+               memcpy(p, FORMTID, sizeof FORMTID -1);
+               p += sizeof FORMTID -1;
+       }
+
+       if (supportflags & DOCKSTAT)
+       {
+               memcpy(p, FORMCID, sizeof FORMCID -1);
+               p += sizeof FORMCID -1;
+       }
+
+       memcpy(p, FORMCPU, sizeof FORMCPU -1);
+       p += sizeof FORMCPU -1;
+
+       memcpy(p, FORMMEM, sizeof FORMMEM -1);
+       p += sizeof FORMMEM -1;
+
+       if (supportflags & IOSTAT)
+       {
+               memcpy(p, FORMDSK, sizeof FORMDSK -1);
+               p += sizeof FORMDSK -1;
+       }
+
+       if (supportflags & NETATOP)
+       {
+               memcpy(p, FORMNET, sizeof FORMNET -1);
+               p += sizeof FORMNET -1;
+       }
+
+       memcpy(p, FORMMSC, sizeof FORMMSC -1);
+       p += sizeof FORMMSC -1;
+
+       memcpy(p, FORMEND, sizeof FORMEND);
+       p += sizeof FORMEND;
+
+       make_proc_prints(genprocs, MAXITEMS, format, "built-in genprocs");
+}
+
+/*
+** print the list of processes from the deviation-list
+*/
+int
+priproc(struct tstat **proclist, int firstproc, int lastproc, int curline,
+        int curlist, int totlist, char showtype, char showorder,
+        struct syscap *sb, int nsecs, int avgval)
+{
+        register int            i;
+        register struct tstat   *curstat;
+        double                  perc;
+
+        /*
+        ** print info per actual process and maintain totals
+        */
+        for (i=firstproc; i < lastproc; i++)
+        {
+                curstat = *(proclist+i);
+
+                if (screen && curline >= LINES) /* screen filled entirely ? */
+                        break;
+
+                /*
+                ** calculate occupation-percentage of this process
+                ** depending on selected resource
+                */
+                switch (showorder) 
+                {
+                   case MSORTCPU:
+                        perc = 0.0;
+
+                        if (sb->availcpu)
+                        {
+                                perc = (double)(curstat->cpu.stime +
+                                                curstat->cpu.utime  ) * 100 /
+                                                (sb->availcpu / sb->nrcpu);
+
+                                if (perc > 100.0 * sb->nrcpu)
+                                        perc = 100.0 * sb->nrcpu;
+
+                                if (perc > 100.0 * curstat->gen.nthr)
+                                        perc = 100.0 * curstat->gen.nthr;
+                        }
+                        break;
+
+                   case MSORTMEM:
+                        perc = 0.0;
+
+                        if (sb->availmem)
+                        {
+                                perc = (double)curstat->mem.rmem *
+                                               100.0 / sb->availmem;
+
+                                if (perc > 100.0)
+                                        perc = 100.0;
+                        }
+                        break;
+
+                   case MSORTDSK:
+                        perc = 0.0;
+
+                       if (sb->availdsk)
+                        {
+                               count_t nett_wsz;
+
+
+                               if (curstat->dsk.wsz > curstat->dsk.cwsz)
+                                       nett_wsz = curstat->dsk.wsz -
+                                                  curstat->dsk.cwsz;
+                               else
+                                       nett_wsz = 0;
+
+                               perc = (double)(curstat->dsk.rsz + nett_wsz) *
+                                               100.0 / sb->availdsk;
+
+                               if (perc > 100.0)
+                                       perc = 100.0;
+                        }
+                        break;
+
+                   case MSORTNET:
+                        perc = 0.0;
+
+                        if (sb->availnet)
+                        {
+                                perc = (double)(curstat->net.tcpssz +
+                                                curstat->net.tcprsz +
+                                                curstat->net.udpssz +
+                                                curstat->net.udprsz  ) *
+                                                100.0 / sb->availnet;
+
+                                if (perc > 100.0)
+                                        perc = 100.0;
+                        }
+                        break;
+
+                   default:
+                        perc = 0.0;
+                }
+
+                /*
+                ** now do the formatting of output
+                */
+                if (screen) {
+                        move(curline,0);
+                }
+
+                switch (showtype)
+                {
+                   case MPROCGEN:
+                        showprocline(genprocs, curstat, perc, nsecs, avgval);
+                        break;
+
+                   case MPROCMEM:
+                        showprocline(memprocs, curstat, perc, nsecs, avgval);
+                        break;
+
+                   case MPROCDSK:
+                        showprocline(dskprocs, curstat, perc, nsecs, avgval);
+                        break;
+
+                   case MPROCNET:
+                        showprocline(netprocs, curstat, perc, nsecs, avgval);
+                        break;
+
+                   case MPROCVAR:
+                        showprocline(varprocs, curstat, perc, nsecs, avgval);
+                        break;
+
+                   case MPROCARG:
+                        showprocline(cmdprocs, curstat, perc, nsecs, avgval);
+                        break;
+
+                   case MPROCOWN:
+                        showprocline(ownprocs, curstat, perc, nsecs, avgval);
+                        break;
+
+                   case MPROCSCH:
+                        showprocline(schedprocs, curstat, perc, nsecs, avgval);
+                        break;
+
+                   case MCUMUSER:
+                        showprocline(totusers, curstat, perc, nsecs, avgval);
+                        break;
+
+                   case MCUMPROC:
+                        showprocline(totprocs, curstat, perc, nsecs, avgval);
+                        break;
+
+                   case MCUMCONT:
+                        showprocline(totconts, curstat, perc, nsecs, avgval);
+                        break;
+                }
+
+                curline++;
+        }
+
+        return curline;
+}
+
+
+/*
+** print the system-wide statistics
+*/
+static void    pridisklike(extraparam *, struct perdsk *, char *,
+                     char *, int, unsigned int *, int *, int, regex_t *);
+
+int
+prisyst(struct sstat *sstat, int curline, int nsecs, int avgval,
+        int fixedhead, struct sselection *selp, char *highorderp,
+        int maxcpulines, int maxdsklines, int maxmddlines,
+       int maxlvmlines, int maxintlines, int maxnfslines, int maxcontlines)
+{
+        extraparam      extra;
+        int             lin;
+        count_t         busy;
+        unsigned int    badness, highbadness=0;
+
+        extra.nsecs    = nsecs;
+        extra.avgval   = avgval;
+
+        /*
+        ** CPU statistics
+        */
+        extra.cputot = sstat->cpu.all.stime + sstat->cpu.all.utime +
+                       sstat->cpu.all.ntime + sstat->cpu.all.itime +
+                       sstat->cpu.all.wtime + sstat->cpu.all.Itime +
+                       sstat->cpu.all.Stime + sstat->cpu.all.steal;
+
+        busy   = (extra.cputot - sstat->cpu.all.itime - sstat->cpu.all.wtime)
+                                * 100.0 / extra.cputot;
+
+        if (cpubadness)
+                badness = busy * 100 / cpubadness;
+        else
+                badness = 0;
+
+        if (highbadness < badness)
+        {
+                highbadness = badness;
+                *highorderp = MSORTCPU;
+        }
+
+        if (extra.cputot == 0)
+                extra.cputot = 1;             /* avoid divide-by-zero */
+
+        extra.percputot = extra.cputot / sstat->cpu.nrcpu;
+
+        if (extra.percputot == 0)
+                extra.percputot = 1;          /* avoid divide-by-zero */
+
+       if (screen)
+              move(curline, 0);
+
+        showsysline(allcpuline, sstat, &extra, "CPU", badness);
+        curline++;
+
+        if (sstat->cpu.nrcpu > 1)
+        {
+                for (extra.index=lin=0;
+                    extra.index < sstat->cpu.nrcpu && lin < maxcpulines;
+                    extra.index++)
+                {
+                        extra.percputot =  sstat->cpu.cpu[extra.index].stime +
+                                     sstat->cpu.cpu[extra.index].utime +
+                                     sstat->cpu.cpu[extra.index].ntime +
+                                     sstat->cpu.cpu[extra.index].itime +
+                                     sstat->cpu.cpu[extra.index].wtime +
+                                     sstat->cpu.cpu[extra.index].Itime +
+                                     sstat->cpu.cpu[extra.index].Stime +
+                                     sstat->cpu.cpu[extra.index].steal;
+
+                        if (extra.percputot ==
+                               (sstat->cpu.cpu[extra.index].itime +
+                                 sstat->cpu.cpu[extra.index].wtime  ) &&
+                                 !fixedhead                             )
+                                continue;       /* inactive cpu */
+
+                        busy   = (extra.percputot -
+                                       sstat->cpu.cpu[extra.index].itime -
+                                        sstat->cpu.cpu[extra.index].wtime)
+                                                  * 100.0 / extra.percputot;
+
+                        if (cpubadness)
+                                badness = busy * 100 / cpubadness;
+                        else
+                                badness = 0;
+
+                        if (highbadness < badness)
+                        {
+                                highbadness = badness;
+                                *highorderp = MSORTCPU;
+                        }
+
+                        if (extra.percputot == 0)
+                                extra.percputot = 1; /* avoid divide-by-zero */
+
+
+                        move(curline, 0);
+                        showsysline(indivcpuline, sstat, &extra, "cpu",
+                                                               badness);
+                        curline++;
+                        lin++;
+                }
+        }
+
+        /*
+        ** other CPU-related statistics
+        */
+       if (screen)
+            move(curline, 0);
+
+        showsysline(cplline, sstat, &extra, "CPL", 0);
+        curline++;
+
+        /*
+        ** MEMORY statistics
+        */
+        busy   = (sstat->mem.physmem - sstat->mem.freemem
+                                     - sstat->mem.cachemem
+                                     - sstat->mem.buffermem
+                                     - sstat->mem.slabreclaim
+                                     + sstat->mem.shmem)
+                                                * 100.0 / sstat->mem.physmem;
+
+        if (membadness)
+                badness = busy * 100 / membadness;
+        else
+                badness = 0;
+
+        if (highbadness < badness)
+        {
+                highbadness = badness;
+                *highorderp = MSORTMEM;
+        }
+
+       if (screen)
+               move(curline, 0);
+
+        showsysline(memline, sstat, &extra, "MEM", badness);
+        curline++;
+
+        /*
+        ** SWAP statistics
+        */
+        busy   = (sstat->mem.totswap - sstat->mem.freeswap)
+                                * 100.0 / sstat->mem.totswap;
+
+        if (swpbadness)
+        {
+                badness = busy * 100 / swpbadness;
+        }
+        else
+        {
+                badness = 0;
+        }
+
+        if (highbadness < badness)
+        {
+                highbadness = badness;
+                *highorderp = MSORTMEM;
+        }
+
+       if (screen)
+               move(curline, 0);
+
+        showsysline(swpline, sstat, &extra, "SWP", badness);
+        curline++;
+
+        /*
+        ** PAGING statistics
+        */
+        if (fixedhead             ||
+            sstat->mem.pgscans    ||
+            sstat->mem.pgsteal    ||
+            sstat->mem.allocstall ||
+            sstat->mem.swins      ||
+            sstat->mem.swouts       )
+        {
+                busy = sstat->mem.swouts / nsecs * pagbadness;
+
+                if (busy > 100)
+                        busy = 100;
+
+                if (membadness)
+                        badness = busy * 100 / membadness;
+                else
+                        badness = 0;
+
+                if (highbadness < badness)
+                {
+                        highbadness = badness;
+                        *highorderp = MSORTMEM;
+                }
+
+                /*
+                ** take care that this line is anyhow colored for
+                ** 'almost critical' in case of swapouts > 1 per second
+                */
+                if (sstat->mem.swouts / nsecs > 0  &&
+                    pagbadness && almostcrit && badness < almostcrit)
+                        badness = almostcrit;
+
+               if (screen)
+                       move(curline, 0);
+
+                showsysline(pagline, sstat, &extra,"PAG", badness);
+                curline++;
+        }
+
+       /*
+       ** Container statistics (if any)
+       */
+        for (extra.index=0, lin=0;
+            extra.index < sstat->cfs.nrcontainer && lin < maxcontlines;
+             extra.index++)
+       {
+               if (fixedhead                           ||
+                   sstat->cfs.cont[extra.index].system ||
+                   sstat->cfs.cont[extra.index].user   ||
+                   sstat->cfs.cont[extra.index].nice     )
+               {
+                       if (screen)
+                               move(curline, 0);
+
+                        showsysline(contline, sstat, &extra, "CON", 0);
+                        curline++;
+                        lin++;
+               }
+       }
+
+        /*
+        ** DISK statistics
+        */
+        extra.mstot = extra.cputot * 1000 / hertz / sstat->cpu.nrcpu; 
+
+       pridisklike(&extra, sstat->dsk.lvm, "LVM", highorderp, maxlvmlines,
+                       &highbadness, &curline, fixedhead,
+                       selp->lvmnamesz ? &(selp->lvmregex) : (void *) 0);
+
+       pridisklike(&extra, sstat->dsk.mdd, "MDD", highorderp, maxmddlines,
+                       &highbadness, &curline, fixedhead, (void *) 0);
+
+       pridisklike(&extra, sstat->dsk.dsk, "DSK", highorderp, maxdsklines,
+                       &highbadness, &curline, fixedhead,
+                       selp->dsknamesz ? &(selp->dskregex) : (void *) 0);
+
+        /*
+        ** NFS server and client statistics
+        */
+       for (extra.index=0, lin=0;
+            extra.index < sstat->nfs.nfsmounts.nrmounts && lin < maxnfslines;
+                                                               extra.index++)
+       {
+               int i = extra.index;
+
+               if ( (sstat->nfs.nfsmounts.nfsmnt[i].bytesread     +
+                     sstat->nfs.nfsmounts.nfsmnt[i].byteswrite    +
+                     sstat->nfs.nfsmounts.nfsmnt[i].bytesdread    +
+                     sstat->nfs.nfsmounts.nfsmnt[i].bytesdwrite   +
+                     sstat->nfs.nfsmounts.nfsmnt[i].bytestotread  +
+                     sstat->nfs.nfsmounts.nfsmnt[i].bytestotwrite +
+                     sstat->nfs.nfsmounts.nfsmnt[i].pagesmread    +
+                     sstat->nfs.nfsmounts.nfsmnt[i].pagesmwrite    ) ||
+                     sstat->nfs.nfsmounts.nfsmnt[i].age < nsecs      ||
+                     fixedhead                                         )
+               {
+                       if (screen)
+                               move(curline, 0);
+
+                        showsysline(nfsmountline, sstat, &extra, "NFM", 0);
+                        curline++;
+                        lin++;
+               }
+       }
+
+        if (sstat->nfs.client.rpccnt || fixedhead )
+        {
+               if (screen)
+                       move(curline, 0);
+
+                showsysline(nfcline, sstat, &extra, "NFC", 0);
+                curline++;
+        }
+
+        if (sstat->nfs.server.rpccnt || fixedhead )
+        {
+               if (screen)
+                       move(curline, 0);
+
+                showsysline(nfsline, sstat, &extra, "NFS", 0);
+                curline++;
+        }
+
+        /*
+        ** NET statistics
+        */
+        if (sstat->net.tcp.InSegs             ||
+            sstat->net.tcp.OutSegs            ||
+            sstat->net.udpv4.InDatagrams      ||
+            sstat->net.udpv6.Udp6InDatagrams  ||
+            sstat->net.udpv4.OutDatagrams     ||
+            sstat->net.udpv6.Udp6OutDatagrams ||
+            fixedhead )
+        {
+               if (screen)
+                       move(curline, 0);
+
+                showsysline(nettransportline, sstat, &extra, "NET", 0);
+                curline++;
+        }
+
+        if (sstat->net.ipv4.InReceives ||
+            sstat->net.ipv6.Ip6InReceives ||
+            sstat->net.ipv4.OutRequests ||
+            sstat->net.ipv6.Ip6OutRequests ||
+            fixedhead )
+        {
+               if (screen)
+                       move(curline, 0);
+
+                showsysline(netnetline, sstat, &extra, "NET", 0);
+                curline++;
+        }
+
+        for (extra.index=0, lin=0;
+            sstat->intf.intf[extra.index].name[0] && lin < maxintlines;
+             extra.index++)
+        {
+                if (sstat->intf.intf[extra.index].rpack ||
+                    sstat->intf.intf[extra.index].spack || fixedhead)
+                {
+                       if (selp->itfnamesz && regexec(&(selp->itfregex),
+                              sstat->intf.intf[extra.index].name, 0, NULL, 0))
+                               continue;       // suppress (not selected)
+
+                        /*
+                        ** calculate busy-percentage for interface
+                        */
+
+                       count_t ival, oval;
+
+                        /*
+                        ** convert byte-transfers to bit-transfers     (*    8)
+                        ** convert bit-transfers  to kilobit-transfers (/ 1000)
+                        ** per second
+                        */
+                        ival    = sstat->intf.intf[extra.index].rbyte/125/nsecs;
+                        oval    = sstat->intf.intf[extra.index].sbyte/125/nsecs;
+
+                       /* speed known? */
+                        if (sstat->intf.intf[extra.index].speed) 
+                        {
+                                if (sstat->intf.intf[extra.index].duplex)
+                                       busy = (ival > oval ? ival : oval) /
+                                          (sstat->intf.intf[extra.index].speed
+                                                                       *10);
+                                else
+                                       busy = (ival + oval) /
+                                           (sstat->intf.intf[extra.index].speed
+                                                                       *10);
+
+                        }
+                        else
+                        {
+                                busy = 0;
+                        }
+
+                        if (netbadness)
+                                badness = busy * 100 / netbadness;
+                        else
+                                badness = 0;
+
+                        if (highbadness < badness && (supportflags & NETATOP) )
+                        {
+                                highbadness = badness;
+                                *highorderp = MSORTNET;
+                        }
+
+                       if (screen)
+                               move(curline, 0);
+
+                        showsysline(netinterfaceline, sstat, &extra, 
+                                                       "NET", badness);
+                        curline++;
+                        lin++;
+                }
+        }
+
+        /*
+        ** application statistics
+        **
+        ** WWW: notice that we cause one access ourselves by fetching
+        **      the statistical counters
+        */
+#if     HTTPSTATS
+        if (sstat->www.accesses > 1 || fixedhead )
+        {
+               char format1[8], format2[8], format3[8], format4[8], format5[8];
+
+               if (screen)
+                               move(curline, 0);
+
+                printg("WWW | reqs  %s | totKB %s | byt/rq %s | iwork %s |"
+                       " bwork %s |",
+                        val2valstr(sstat->www.accesses,  format1, 6,
+                                                         avgval, nsecs),
+                        val2valstr(sstat->www.totkbytes, format2, 6,
+                                                         avgval, nsecs),
+                        val2valstr(sstat->www.accesses ?
+                            sstat->www.totkbytes*1024/sstat->www.accesses : 0,
+                                                         format3, 5, 0, 0),
+                        val2valstr(sstat->www.iworkers,  format4, 6, 0, 0),
+                        val2valstr(sstat->www.bworkers,  format5, 6, 0, 0) );
+                if (!screen) 
+                {
+                        printg("\n");
+                }
+                curline++;
+        }
+#endif
+
+        /*
+        ** if the system is hardly loaded, still CPU-ordering of
+        ** processes is most interesting (instead of memory)
+        */
+        if (highbadness < 70 && *highorderp == MSORTMEM)
+                *highorderp = MSORTCPU;
+
+        return curline;
+}
+
+/*
+** handle all instances of a specific disk-like device
+*/
+static void
+pridisklike(extraparam *ep, struct perdsk *dp, char *lp, char *highorderp,
+       int maxlines, unsigned int *highbadp, int *curlinp, int fixedhead,
+       regex_t *rep)
+{
+       int             lin;
+        count_t         busy;
+        unsigned int    badness;
+
+        for (ep->perdsk = dp, ep->index=0, lin=0;
+            ep->perdsk[ep->index].name[0] && lin < maxlines; ep->index++)
+        {
+               if (rep && regexec(rep, ep->perdsk[ep->index].name, 0, NULL, 0))
+                       continue;       // suppress (not selected)
+
+                ep->iotot =  ep->perdsk[ep->index].nread +
+                            ep->perdsk[ep->index].nwrite;
+
+                busy        = (double)(ep->perdsk[ep->index].io_ms *
+                                               100.0 / ep->mstot);
+
+                if (dskbadness)
+                        badness = busy * 100 / dskbadness;
+                else
+                        badness = 0;
+
+                if (*highbadp < badness && (supportflags & IOSTAT) )
+                {
+                        *highbadp      = badness;
+                        *highorderp    = MSORTDSK;
+                }
+
+                if (ep->iotot || fixedhead)
+                {
+                        move(*curlinp, 0);
+                        showsysline(dskline, 0, ep, lp, badness);
+                        (*curlinp)++;
+                        lin++;
+                }
+        }
+}
+
+
+/*
+** sort-functions
+*/
+int
+compcpu(const void *a, const void *b)
+{
+        register count_t acpu = (*(struct tstat **)a)->cpu.stime +
+                                (*(struct tstat **)a)->cpu.utime;
+        register count_t bcpu = (*(struct tstat **)b)->cpu.stime +
+                                (*(struct tstat **)b)->cpu.utime;
+
+        if (acpu < bcpu) return  1;
+        if (acpu > bcpu) return -1;
+                         return compmem(a, b);
+}
+
+int
+compdsk(const void *a, const void *b)
+{
+       struct tstat    *ta = *(struct tstat **)a;
+       struct tstat    *tb = *(struct tstat **)b;
+
+        count_t        adsk;
+        count_t bdsk;
+
+       if (ta->dsk.wsz > ta->dsk.cwsz)
+               adsk = ta->dsk.rio + ta->dsk.wsz - ta->dsk.cwsz;
+       else
+               adsk = ta->dsk.rio;
+
+       if (tb->dsk.wsz > tb->dsk.cwsz)
+               bdsk = tb->dsk.rio + tb->dsk.wsz - tb->dsk.cwsz;
+       else
+               bdsk = tb->dsk.rio;
+
+        if (adsk < bdsk) return  1;
+        if (adsk > bdsk) return -1;
+                         return compcpu(a, b);
+}
+
+int
+compmem(const void *a, const void *b)
+{
+        register count_t amem = (*(struct tstat **)a)->mem.rmem;
+        register count_t bmem = (*(struct tstat **)b)->mem.rmem;
+
+        if (amem < bmem) return  1;
+        if (amem > bmem) return -1;
+                         return  0;
+}
+
+int
+compnet(const void *a, const void *b)
+{
+        register count_t anet = (*(struct tstat **)a)->net.tcpssz +
+                                (*(struct tstat **)a)->net.tcprsz +
+                                (*(struct tstat **)a)->net.udpssz +
+                                (*(struct tstat **)a)->net.udprsz  ;
+        register count_t bnet = (*(struct tstat **)b)->net.tcpssz +
+                                (*(struct tstat **)b)->net.tcprsz +
+                                (*(struct tstat **)b)->net.udpssz +
+                                (*(struct tstat **)b)->net.udprsz  ;
+
+        if (anet < bnet) return  1;
+        if (anet > bnet) return -1;
+                         return compcpu(a, b);
+}
+
+int
+compusr(const void *a, const void *b)
+{
+        register int uida = (*(struct tstat **)a)->gen.ruid;
+        register int uidb = (*(struct tstat **)b)->gen.ruid;
+
+        if (uida > uidb) return  1;
+        if (uida < uidb) return -1;
+                         return  0;
+}
+
+int
+compnam(const void *a, const void *b)
+{
+        register char *nama = (*(struct tstat **)a)->gen.name;
+        register char *namb = (*(struct tstat **)b)->gen.name;
+
+        return strcmp(nama, namb);
+}
+
+int
+compcon(const void *a, const void *b)
+{
+        register char *containera = (*(struct tstat **)a)->gen.container;
+        register char *containerb = (*(struct tstat **)b)->gen.container;
+
+       return strcmp(containera, containerb);
+}
+
+int
+cpucompar(const void *a, const void *b)
+{
+        register count_t aidle = ((struct percpu *)a)->itime +
+                                 ((struct percpu *)a)->wtime;
+        register count_t bidle = ((struct percpu *)b)->itime +
+                                 ((struct percpu *)b)->wtime;
+
+        if (aidle < bidle) return -1;
+        if (aidle > bidle) return  1;
+                           return  0;
+}
+
+int
+diskcompar(const void *a, const void *b)
+{
+        register count_t amsio = ((struct perdsk *)a)->io_ms;
+        register count_t bmsio = ((struct perdsk *)b)->io_ms;
+
+        if (amsio < bmsio) return  1;
+        if (amsio > bmsio) return -1;
+                           return  0;
+}
+
+int
+intfcompar(const void *a, const void *b)
+{
+        register count_t afactor=0, bfactor=0;
+
+        count_t aspeed         = ((struct perintf *)a)->speed;
+        count_t bspeed         = ((struct perintf *)b)->speed;
+        char    aduplex        = ((struct perintf *)a)->duplex;
+        char    bduplex        = ((struct perintf *)b)->duplex;
+        count_t arbyte         = ((struct perintf *)a)->rbyte;
+        count_t brbyte         = ((struct perintf *)b)->rbyte;
+        count_t asbyte         = ((struct perintf *)a)->sbyte;
+        count_t bsbyte         = ((struct perintf *)b)->sbyte;
+
+
+        /*
+        ** if speed of first interface known, calculate busy factor
+        */
+        if (aspeed)
+        {
+                if (aduplex)
+                        afactor = (arbyte > asbyte ? arbyte : asbyte) 
+                                                                * 10 / aspeed;
+                else
+                        afactor = (arbyte + asbyte)             * 10 / aspeed;
+        }
+
+        /*
+        ** if speed of second interface known, calculate busy factor
+        */
+        if (bspeed)
+        {
+                if (bduplex)
+                        bfactor = (brbyte > bsbyte ? brbyte : bsbyte)
+                                                                * 10 / bspeed;
+                else
+                        bfactor = (brbyte + bsbyte)             * 10 / bspeed;
+        }
+
+        /*
+        ** compare interfaces
+        */
+        if (aspeed && bspeed)
+        {
+                if (afactor < bfactor)  return  1;
+                if (afactor > bfactor)  return -1;
+                                        return  0;
+        }
+
+        if (!aspeed && !bspeed)
+        {
+                if ((arbyte + asbyte) < (brbyte + bsbyte))      return  1;
+                if ((arbyte + asbyte) > (brbyte + bsbyte))      return -1;
+                                                                return  0;
+        }
+
+        if (aspeed)
+                return -1;
+        else
+                return  1;
+}
+
+int
+nfsmcompar(const void *a, const void *b)
+{
+        const struct pernfsmount *na = a;
+        const struct pernfsmount *nb = b;
+
+        register count_t aused = na->bytesread    + na->byteswrite    + 
+                                na->bytesdread   + na->bytesdwrite   +
+                                na->bytestotread + na->bytestotwrite +
+                                 na->pagesmread   + na->pagesmwrite;
+        register count_t bused = nb->bytesread    + nb->byteswrite    + 
+                                nb->bytesdread   + nb->bytesdwrite   +
+                                nb->bytestotread + nb->bytestotwrite +
+                                 nb->pagesmread   + nb->pagesmwrite;
+
+        if (aused < bused) return  1;
+        if (aused > bused) return -1;
+                           return  0;
+}
+
+int
+contcompar(const void *a, const void *b)
+{
+        const struct percontainer *ca = a;
+        const struct percontainer *cb = b;
+
+        register count_t aused = ca->system + ca->user + ca->nice;
+        register count_t bused = cb->system + cb->user + cb->nice;
+
+        if (aused < bused) return  1;
+        if (aused > bused) return -1;
+                           return  0;
+}
+
+/*
+** handle modifications from the /etc/atoprc and ~/.atoprc file
+*/
+int
+get_posval(char *name, char *val)
+{
+        int     value = atoi(val);
+
+        if ( !numeric(val))
+        {
+                fprintf(stderr, "atoprc: %s value %s not a (positive) numeric\n", 
+                name, val);
+                exit(1);
+        }
+
+        if (value < 0)
+        {
+                fprintf(stderr,
+                        "atoprc: %s value %d not positive\n", 
+                        name, value);
+                exit(1);
+        }
+
+        return value;
+}
+
+int
+get_perc(char *name, char *val)
+{
+        int     value = get_posval(name, val);
+
+        if (value < 0 || value > 100)
+        {
+                fprintf(stderr, "atoprc: %s value %d not in range 0-100\n", 
+                        name, value);
+                exit(1);
+        }
+
+        return value;
+}
+
+void
+do_cpucritperc(char *name, char *val)
+{
+        cpubadness = get_perc(name, val);
+}
+
+void
+do_memcritperc(char *name, char *val)
+{
+        membadness = get_perc(name, val);
+}
+
+void
+do_swpcritperc(char *name, char *val)
+{
+        swpbadness = get_perc(name, val);
+}
+
+void
+do_dskcritperc(char *name, char *val)
+{
+        dskbadness = get_perc(name, val);
+}
+
+void
+do_netcritperc(char *name, char *val)
+{
+        netbadness = get_perc(name, val);
+}
+
+void
+do_swoutcritsec(char *name, char *val)
+{
+        pagbadness = get_posval(name, val);
+}
+
+void
+do_almostcrit(char *name, char *val)
+{
+        almostcrit = get_perc(name, val);
+}
+
+void
+do_ownsysprcline(char *name, char *val)
+{
+        make_sys_prints(sysprcline, MAXITEMS, val, prcsyspdefs, name);
+}
+
+void
+do_ownallcpuline(char *name, char *val)
+{
+        make_sys_prints(allcpuline, MAXITEMS, val, cpusyspdefs, name);
+}
+
+void
+do_ownindivcpuline(char *name, char *val)
+{
+        make_sys_prints(indivcpuline, MAXITEMS, val, cpisyspdefs, name);
+}
+
+void
+do_owncplline(char *name, char *val)
+{
+        make_sys_prints(cplline, MAXITEMS, val, cplsyspdefs, name);
+}
+
+void
+do_ownmemline(char *name, char *val)
+{
+        make_sys_prints(memline, MAXITEMS, val, memsyspdefs, name);
+}
+
+void
+do_ownswpline(char *name, char *val)
+{
+        make_sys_prints(swpline, MAXITEMS, val, swpsyspdefs, name);
+}
+
+void
+do_ownpagline(char *name, char *val)
+{
+        make_sys_prints(pagline, MAXITEMS, val, pagsyspdefs, name);
+}
+
+void
+do_owndskline(char *name, char *val)
+{
+        make_sys_prints(dskline, MAXITEMS, val, dsksyspdefs, name);
+}
+
+void
+do_ownnettransportline(char *name, char *val)
+{
+        make_sys_prints(nettransportline, MAXITEMS, val, nettranssyspdefs, name);
+}
+
+void
+do_ownnetnetline(char *name, char *val)
+{
+        make_sys_prints(netnetline, MAXITEMS, val, netnetsyspdefs, name);
+}
+
+void
+do_ownnetinterfaceline(char *name, char *val)
+{
+        make_sys_prints(netinterfaceline, MAXITEMS, val, netintfsyspdefs, name);
+}
+
+void
+do_ownprocline(char *name, char *val)
+{
+       make_proc_prints(ownprocs, MAXITEMS, val, name);
+}
diff --git a/sources/showlinux.h b/sources/showlinux.h
new file mode 100644 (file)
index 0000000..dae7e37
--- /dev/null
@@ -0,0 +1,365 @@
+/*
+** ATOP - System & Process Monitor 
+**
+** The program 'atop' offers the possibility to view the activity of
+** the system on system-level as well as process-level.
+**
+** This source-file contains the Linux-specific functions to calculate
+** figures to be visualized.
+** ==========================================================================
+** Author:      JC van Winkel - AT Computing, Nijmegen, Holland
+** E-mail:      jc@ATComputing.nl
+** Date:        November 2009
+** --------------------------------------------------------------------------
+** Copyright (C) 2009  JC van Winkel
+**
+** This program is free software; you can redistribute it and/or modify it
+** under the terms of the GNU General Public License as published by the
+** Free Software Foundation; either version 2, or (at your option) any
+** later version.
+**
+** This program is distributed in the hope that it will be useful, but
+** WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+** See the GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+** --------------------------------------------------------------------------
+**
+** $Log: showlinux.h,v $
+** Initial revision
+**
+** Initial
+**
+*/
+#define MAXITEMS 80    /* The maximum number of items per line */
+
+/*
+ * structure for extra parameters for system related data
+*/
+typedef struct {
+        count_t                totut;
+        count_t                totst;
+        int            nact; 
+        int            nproc;
+        int            ntrun;
+        int            ntslpi;
+        int            ntslpu;
+        int            nzomb;
+        int            nexit;
+        int            noverflow;
+        int            avgval;
+        int            nsecs;
+        count_t                mstot;
+        count_t                iotot;
+       struct perdsk   *perdsk;
+        int            index;
+        count_t                cputot;
+        count_t                percputot;
+} extraparam;
+
+/***************************************************************/
+/*
+ * structure for system print-list
+*/
+typedef struct {
+        char *configname;                          // name as used to 
+                                                   // config print line
+        char* (*doconvert)(void *, void *, int, int *); // ptr to convert func
+} sys_printdef;
+
+
+/*
+ * structure for system print-list with priority
+ * in case of leck of screen space, lowest priority items will be
+ * removed first 
+*/
+typedef struct
+{
+        sys_printdef    *f;
+        int             prio;
+} sys_printpair;
+
+
+
+/*
+** structure for process print-list
+*/
+typedef struct 
+{
+        char *head;                      // column header
+        char *configname;                // name as used to config print line
+        char *(*doactiveconvert)(struct tstat *,int,int); 
+                                         // pointer to conv function
+                                         // for active process
+        char *(*doexitconvert)  (struct tstat *,int,int);   
+                                         // pointer to conv function
+                                         // for exited process
+        int  width;                      // required width
+        int  varwidth;                   // width may grow (eg cmd params)
+} proc_printdef;
+
+
+typedef struct 
+{
+        proc_printdef   *f;
+        int             prio;
+} proc_printpair;
+
+void showsysline(sys_printpair* elemptr, 
+                 struct sstat* sstat, extraparam *extra,
+                 char *labeltext, unsigned int badness);
+
+
+void showhdrline(proc_printpair* elemptr, int curlist, int totlist, 
+                  char showorder, char autosort);
+void showprocline(proc_printpair* elemptr, struct tstat *curstat, 
+                  double perc, int nsecs, int avgval);
+
+extern sys_printdef *prcsyspdefs[];
+extern sys_printdef *cpusyspdefs[];
+extern sys_printdef *cpisyspdefs[];
+extern sys_printdef *cplsyspdefs[];
+extern sys_printdef *memsyspdefs[];
+extern sys_printdef *swpsyspdefs[];
+extern sys_printdef *pagsyspdefs[];
+extern sys_printdef *dsksyspdefs[];
+extern sys_printdef *nettranssyspdefs[];
+extern sys_printdef *netnetsyspdefs[];
+extern sys_printdef *netintfsyspdefs[];
+
+extern sys_printdef syspdef_PRCSYS;
+extern sys_printdef syspdef_PRCUSER;
+extern sys_printdef syspdef_PRCNPROC;
+extern sys_printdef syspdef_PRCNRUNNING;
+extern sys_printdef syspdef_PRCNSLEEPING;
+extern sys_printdef syspdef_PRCNDSLEEPING;
+extern sys_printdef syspdef_PRCNZOMBIE;
+extern sys_printdef syspdef_PRCCLONES;
+extern sys_printdef syspdef_PRCNNEXIT;
+extern sys_printdef syspdef_CPUSYS;
+extern sys_printdef syspdef_CPUUSER;
+extern sys_printdef syspdef_CPUIRQ;
+extern sys_printdef syspdef_CPUIDLE;
+extern sys_printdef syspdef_CPUWAIT;
+extern sys_printdef syspdef_CPUISYS;
+extern sys_printdef syspdef_CPUIUSER;
+extern sys_printdef syspdef_CPUIIRQ;
+extern sys_printdef syspdef_CPUIIDLE;
+extern sys_printdef syspdef_CPUIWAIT;
+extern sys_printdef syspdef_CPUISTEAL;
+extern sys_printdef syspdef_CPUIFREQ;
+extern sys_printdef syspdef_CPUFREQ;
+extern sys_printdef syspdef_CPUSCALE;
+extern sys_printdef syspdef_CPUISCALE;
+extern sys_printdef syspdef_CPUSTEAL;
+extern sys_printdef syspdef_CPUISTEAL;
+extern sys_printdef syspdef_CPUGUEST;
+extern sys_printdef syspdef_CPUIGUEST;
+extern sys_printdef syspdef_CPLAVG1;
+extern sys_printdef syspdef_CPLAVG5;
+extern sys_printdef syspdef_CPLAVG15;
+extern sys_printdef syspdef_CPLCSW;
+extern sys_printdef syspdef_CPLNUMCPU;
+extern sys_printdef syspdef_CPLINTR;
+extern sys_printdef syspdef_MEMTOT;
+extern sys_printdef syspdef_MEMFREE;
+extern sys_printdef syspdef_MEMCACHE;
+extern sys_printdef syspdef_MEMDIRTY;
+extern sys_printdef syspdef_MEMBUFFER;
+extern sys_printdef syspdef_MEMSLAB;
+extern sys_printdef syspdef_RECSLAB;
+extern sys_printdef syspdef_SHMEM;
+extern sys_printdef syspdef_SHMRSS;
+extern sys_printdef syspdef_SHMSWP;
+extern sys_printdef syspdef_VMWBAL;
+extern sys_printdef syspdef_HUPTOT;
+extern sys_printdef syspdef_HUPUSE;
+extern sys_printdef syspdef_SWPTOT;
+extern sys_printdef syspdef_SWPFREE;
+extern sys_printdef syspdef_SWPCOMMITTED;
+extern sys_printdef syspdef_SWPCOMMITLIM;
+extern sys_printdef syspdef_PAGSCAN;
+extern sys_printdef syspdef_PAGSTEAL;
+extern sys_printdef syspdef_PAGSTALL;
+extern sys_printdef syspdef_PAGSWIN;
+extern sys_printdef syspdef_PAGSWOUT;
+extern sys_printdef syspdef_CONTNAME;
+extern sys_printdef syspdef_CONTNPROC;
+extern sys_printdef syspdef_CONTCPU;
+extern sys_printdef syspdef_CONTMEM;
+extern sys_printdef syspdef_DSKNAME;
+extern sys_printdef syspdef_DSKBUSY;
+extern sys_printdef syspdef_DSKNREAD;
+extern sys_printdef syspdef_DSKNWRITE;
+extern sys_printdef syspdef_DSKMBPERSECWR;
+extern sys_printdef syspdef_DSKMBPERSECRD;
+extern sys_printdef syspdef_DSKKBPERWR;
+extern sys_printdef syspdef_DSKKBPERRD;
+extern sys_printdef syspdef_DSKAVQUEUE;
+extern sys_printdef syspdef_DSKAVIO;
+extern sys_printdef syspdef_NETTRANSPORT;
+extern sys_printdef syspdef_NETTCPI;
+extern sys_printdef syspdef_NETTCPO;
+extern sys_printdef syspdef_NETTCPACTOPEN;
+extern sys_printdef syspdef_NETTCPPASVOPEN;
+extern sys_printdef syspdef_NETTCPRETRANS;
+extern sys_printdef syspdef_NETTCPINERR;
+extern sys_printdef syspdef_NETTCPORESET;
+extern sys_printdef syspdef_NETUDPNOPORT;
+extern sys_printdef syspdef_NETUDPINERR;
+extern sys_printdef syspdef_NETUDPI;
+extern sys_printdef syspdef_NETUDPO;
+extern sys_printdef syspdef_NETNETWORK;
+extern sys_printdef syspdef_NETIPI;
+extern sys_printdef syspdef_NETIPO;
+extern sys_printdef syspdef_NETIPFRW;
+extern sys_printdef syspdef_NETIPDELIV;
+extern sys_printdef syspdef_NETICMPIN;
+extern sys_printdef syspdef_NETICMPOUT;
+extern sys_printdef syspdef_NETNAME;
+extern sys_printdef syspdef_NETPCKI;
+extern sys_printdef syspdef_NETPCKO;
+extern sys_printdef syspdef_NETSPEEDMAX;
+extern sys_printdef syspdef_NETSPEEDIN;
+extern sys_printdef syspdef_NETSPEEDOUT;
+extern sys_printdef syspdef_NETCOLLIS;
+extern sys_printdef syspdef_NETMULTICASTIN;
+extern sys_printdef syspdef_NETRCVERR;
+extern sys_printdef syspdef_NETSNDERR;
+extern sys_printdef syspdef_NETRCVDROP;
+extern sys_printdef syspdef_NETSNDDROP;
+extern sys_printdef syspdef_NFMSERVER;
+extern sys_printdef syspdef_NFMPATH;
+extern sys_printdef syspdef_NFMTOTREAD;
+extern sys_printdef syspdef_NFMTOTWRITE;
+extern sys_printdef syspdef_NFMNREAD;
+extern sys_printdef syspdef_NFMNWRITE;
+extern sys_printdef syspdef_NFMDREAD;
+extern sys_printdef syspdef_NFMDWRITE;
+extern sys_printdef syspdef_NFMMREAD;
+extern sys_printdef syspdef_NFMMWRITE;
+extern sys_printdef syspdef_NFCRPCCNT;
+extern sys_printdef syspdef_NFCRPCREAD;
+extern sys_printdef syspdef_NFCRPCWRITE;
+extern sys_printdef syspdef_NFCRPCRET;
+extern sys_printdef syspdef_NFCRPCARF;
+extern sys_printdef syspdef_NFSRPCCNT;
+extern sys_printdef syspdef_NFSRPCREAD;
+extern sys_printdef syspdef_NFSRPCWRITE;
+extern sys_printdef syspdef_NFSBADFMT;
+extern sys_printdef syspdef_NFSBADAUT;
+extern sys_printdef syspdef_NFSBADCLN;
+extern sys_printdef syspdef_NFSNETTCP;
+extern sys_printdef syspdef_NFSNETUDP;
+extern sys_printdef syspdef_NFSNRBYTES;
+extern sys_printdef syspdef_NFSNWBYTES;
+extern sys_printdef syspdef_NFSRCHITS;
+extern sys_printdef syspdef_NFSRCMISS;
+extern sys_printdef syspdef_NFSRCNOCA;
+extern sys_printdef syspdef_BLANKBOX;
+
+
+/*
+** functions that print ???? for unavailable data
+*/
+char *procprt_NOTAVAIL_4(struct tstat *curstat, int avgval, int nsecs);
+char *procprt_NOTAVAIL_5(struct tstat *curstat, int avgval, int nsecs);
+char *procprt_NOTAVAIL_6(struct tstat *curstat, int avgval, int nsecs);
+char *procprt_NOTAVAIL_7(struct tstat *curstat, int avgval, int nsecs);
+
+extern proc_printdef *allprocpdefs[];
+extern proc_printdef procprt_PID;
+extern proc_printdef procprt_TID;
+extern proc_printdef procprt_PPID;
+extern proc_printdef procprt_SYSCPU;
+extern proc_printdef procprt_USRCPU;
+extern proc_printdef procprt_VGROW;
+extern proc_printdef procprt_RGROW;
+extern proc_printdef procprt_MINFLT;
+extern proc_printdef procprt_MAJFLT;
+extern proc_printdef procprt_VSTEXT;
+extern proc_printdef procprt_VSIZE;
+extern proc_printdef procprt_RSIZE;
+extern proc_printdef procprt_PSIZE;
+extern proc_printdef procprt_VSLIBS;
+extern proc_printdef procprt_VDATA;
+extern proc_printdef procprt_VSTACK;
+extern proc_printdef procprt_SWAPSZ;
+extern proc_printdef procprt_CMD;
+extern proc_printdef procprt_RUID;
+extern proc_printdef procprt_EUID;
+extern proc_printdef procprt_SUID;
+extern proc_printdef procprt_FSUID;
+extern proc_printdef procprt_RGID;
+extern proc_printdef procprt_EGID;
+extern proc_printdef procprt_SGID;
+extern proc_printdef procprt_FSGID;
+extern proc_printdef procprt_CTID;
+extern proc_printdef procprt_VPID;
+extern proc_printdef procprt_CID;
+extern proc_printdef procprt_STDATE;
+extern proc_printdef procprt_STTIME;
+extern proc_printdef procprt_ENDATE;
+extern proc_printdef procprt_ENTIME;
+extern proc_printdef procprt_THR;
+extern proc_printdef procprt_TRUN;
+extern proc_printdef procprt_TSLPI;
+extern proc_printdef procprt_TSLPU;
+extern proc_printdef procprt_POLI;
+extern proc_printdef procprt_NICE;
+extern proc_printdef procprt_PRI;
+extern proc_printdef procprt_RTPR;
+extern proc_printdef procprt_CURCPU;
+extern proc_printdef procprt_ST;
+extern proc_printdef procprt_EXC;
+extern proc_printdef procprt_S;
+extern proc_printdef procprt_COMMAND_LINE;
+extern proc_printdef procprt_NPROCS;
+extern proc_printdef procprt_RDDSK;
+extern proc_printdef procprt_WRDSK;
+extern proc_printdef procprt_CWRDSK;
+extern proc_printdef procprt_WCANCEL;
+extern proc_printdef procprt_TCPRCV;
+extern proc_printdef procprt_TCPRASZ;
+extern proc_printdef procprt_TCPSND;
+extern proc_printdef procprt_TCPSASZ;
+extern proc_printdef procprt_UDPRCV;
+extern proc_printdef procprt_UDPRASZ;
+extern proc_printdef procprt_UDPSND;
+extern proc_printdef procprt_UDPSASZ;
+extern proc_printdef procprt_RNET;
+extern proc_printdef procprt_SNET;
+extern proc_printdef procprt_BANDWI;
+extern proc_printdef procprt_BANDWO;
+extern proc_printdef procprt_SORTITEM;
+
+
+
+//extern char *procprt_NRDDSK_ae(struct tstat *, int, int);
+//extern char *procprt_NWRDSK_a(struct tstat *, int, int);
+//extern char *procprt_NRDDSK_e(struct tstat *, int, int);
+//extern char *procprt_NWRDSK_e(struct tstat *, int, int);
+
+extern char *procprt_SNET_a(struct tstat *, int, int);
+extern char *procprt_SNET_e(struct tstat *, int, int);
+extern char *procprt_RNET_a(struct tstat *, int, int);
+extern char *procprt_RNET_e(struct tstat *, int, int);
+extern char *procprt_TCPSND_a(struct tstat *, int, int);
+extern char *procprt_TCPRCV_a(struct tstat *, int, int);
+extern char *procprt_UDPSND_a(struct tstat *, int, int);
+extern char *procprt_UDPRCV_a(struct tstat *, int, int);
+extern char *procprt_TCPSASZ_a(struct tstat *, int, int);
+extern char *procprt_TCPRASZ_a(struct tstat *, int, int);
+extern char *procprt_UDPSASZ_a(struct tstat *, int, int);
+extern char *procprt_UDPRASZ_a(struct tstat *, int, int);
+extern char *procprt_TCPSND_e(struct tstat *, int, int);
+extern char *procprt_TCPRCV_e(struct tstat *, int, int);
+extern char *procprt_UDPSND_e(struct tstat *, int, int);
+extern char *procprt_UDPRCV_e(struct tstat *, int, int);
+extern char *procprt_TCPSASZ_e(struct tstat *, int, int);
+extern char *procprt_TCPRASZ_e(struct tstat *, int, int);
+extern char *procprt_UDPSASZ_e(struct tstat *, int, int);
+extern char *procprt_UDPRASZ_e(struct tstat *, int, int);
diff --git a/sources/showprocs.c b/sources/showprocs.c
new file mode 100644 (file)
index 0000000..79841de
--- /dev/null
@@ -0,0 +1,1915 @@
+/*
+** ATOP - System & Process Monitor 
+**
+** The program 'atop' offers the possibility to view the activity of
+** the system on system-level as well as process-level.
+**
+** This source-file contains the Linux-specific functions to calculate
+** figures to be visualized.
+** ==========================================================================
+** Author:      JC van Winkel - AT Computing, Nijmegen, Holland
+** E-mail:      jc@ATComputing.nl
+** Date:        November 2009
+** --------------------------------------------------------------------------
+** Copyright (C) 2009 JC van Winkel
+**
+** This program is free software; you can redistribute it and/or modify it
+** under the terms of the GNU General Public License as published by the
+** Free Software Foundation; either version 2, or (at your option) any
+** later version.
+**
+** This program is distributed in the hope that it will be useful, but
+** WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+** See the GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+** --------------------------------------------------------------------------
+**
+** $Log: showprocs.c,v $
+** Revision 1.15  2011/09/05 11:44:16  gerlof
+** *** empty log message ***
+**
+** Revision 1.14  2010/12/01 09:05:38  gerlof
+** Added a dash in the column PPID for exited processes.
+**
+** Revision 1.13  2010/11/12 06:11:58  gerlof
+** Sometimes segmentation-fault on particular CPU-types
+** due to memcpy i.s.o. memmove when moving memory in overlap.
+**
+** Revision 1.12  2010/04/23 14:06:42  gerlof
+** Added special routined for uid/gid not available for exited processes.
+**
+** Revision 1.11  2010/01/16 12:54:08  gerlof
+** Minor change for CPUNR.
+**
+** Revision 1.10  2010/01/16 11:37:25  gerlof
+** Corrected counters for patched kernels (JC van Winkel).
+**
+** Revision 1.9  2010/01/08 13:44:47  gerlof
+** Added policies batch, iso and idle for scheduling class.
+**
+** Revision 1.8  2010/01/08 11:25:13  gerlof
+** Corrected column-width and priorities of network-stats.
+**
+** Revision 1.7  2010/01/03 18:26:53  gerlof
+** Consistent naming of columns for process-related info.
+**
+** Revision 1.6  2009/12/19 21:03:21  gerlof
+** Alignment of CMD column (JC van Winkel).
+**
+** Revision 1.5  2009/12/12 10:11:49  gerlof
+** Register and display end date and end time for process.
+**
+** Revision 1.4  2009/12/12 09:05:56  gerlof
+** Corrected cumulated disk I/O per user/program (JC van Winkel).
+**
+** Revision 1.3  2009/12/10 14:01:52  gerlof
+** Add EUID, SUID and FSUID (and similar for GID's).
+**
+** Revision 1.2  2009/12/10 11:56:22  gerlof
+** Various bug-solutions.
+**
+** Revision 1.1  2009/12/10 09:31:23  gerlof
+** Initial revision
+**
+** Initial revision
+**
+**
+** Initial
+**
+*/
+
+static const char rcsid[] = "$Id: showprocs.c,v 1.15 2011/09/05 11:44:16 gerlof Exp $";
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <curses.h>
+#include <pwd.h>
+#include <grp.h>
+#include <regex.h>
+
+#include "atop.h"
+#include "photoproc.h"
+#include "photosyst.h"
+#include "showgeneric.h"
+#include "showlinux.h"
+
+static void    format_bandw(char *, count_t);
+
+static char     *columnhead[] = {
+       [MSORTCPU]= "CPU", [MSORTMEM]= "MEM",
+       [MSORTDSK]= "DSK", [MSORTNET]= "NET",
+};
+
+
+/***************************************************************/
+static int *colspacings;     // ugly static var, 
+                             // but saves a lot of recomputations
+                             // points to table with intercolumn 
+                             // spacings
+static proc_printpair newelems[MAXITEMS];
+                             // ugly static var,
+                             // but saves a lot of recomputations
+                             // contains the actual list of items to
+                             // be printed
+                             //
+                             //
+/***************************************************************/
+/*
+ * gettotwidth: calculate the sum of widths and number of columns
+ * Also copys the printpair elements to the static array newelems
+ * for later removal of lower priority elements.
+ * Params:
+ * elemptr: the array of what to print
+ * nitems: (ref) returns the number of printitems in the array
+ * sumwidth: (ref) returns the total width of the printitems in the array
+ * varwidth: (ref) returns the number of variable width items in the array
+ */
+void
+gettotwidth(proc_printpair* elemptr, int *nitems, int *sumwidth, int* varwidth) 
+{
+        int i;
+        int col;
+        int varw=0;
+
+        for (i=0, col=0; elemptr[i].f!=0; ++i) 
+        {
+                col += (elemptr[i].f->varwidth ? 0 : elemptr[i].f->width);
+                varw += elemptr[i].f->varwidth;
+                newelems[i]=elemptr[i];    // copy element
+        }
+        newelems[i].f=0;
+        *nitems=i;
+        *sumwidth=col;
+        *varwidth=varw;
+}
+
+
+
+/***************************************************************/
+/*
+ * getspacings: determine how much extra space there is for
+ * inter-column space.
+ * returns an int array this number of spaces to add after each column
+ * also removes items from the newelems array if the available width
+ * is lower than what is needed.  The lowest priority columns are
+ * removed first.
+ *
+ * Note: this function is only to be called when screen is true.
+ */
+int *
+getspacings(proc_printpair* elemptr) 
+{
+        static int spacings[MAXITEMS];
+
+        int col=0;
+        int nitems;
+        int varwidth=0;
+        int j;
+        int maxw=screen ? COLS : linelen;    // for non screen: 80 columns max
+
+        // get width etc; copy elemptr array to static newelms
+        gettotwidth(elemptr, &nitems, &col, &varwidth);
+
+        /* cases:
+         *   1) nitems==1: just one column, no spacing needed.  Done
+         *
+         *   2) total width is more than COLS: remove low prio columns
+         *      2a)  a varwidth column: no spacing needed
+         *      2b)  total width is less than COLS: compute inter spacing
+         */
+
+        if (nitems==1)          // no inter column spacing if 1 column
+        {
+                spacings[0]=0;
+                return spacings;
+        }
+
+        // Check if available width is less than required.
+        // If so, delete columns to make things fit
+        // space required:
+        // width + (nitems-1) * 1 space + 12 for a varwidth column.
+        while (col + nitems-1+ 12*varwidth > maxw)  
+        {    
+                int lowestprio=999999;
+                int lowestprio_index=-1;
+                int i;
+                for (i=0; i<nitems; ++i) 
+                {
+                        if (newelems[i].prio < lowestprio) 
+                        {
+                                lowestprio=newelems[i].prio;
+                                lowestprio_index=i;
+                        }
+                }
+                
+                // lowest priority item found, remove from newelems;
+                col -= newelems[lowestprio_index].f->width;
+                varwidth -= newelems[lowestprio_index].f->varwidth;
+                memmove(newelems+lowestprio_index, 
+                        newelems+lowestprio_index+1, 
+                        (nitems-lowestprio_index)* sizeof(proc_printpair));   
+                       // also copies final 0 entry
+                nitems--;
+        }
+
+
+        /* if there is a var width column, handle that separately */
+        if (varwidth) 
+        {
+                for (j=0; j<nitems; ++j) 
+                {
+                        spacings[j]=1;
+                        if (elemptr[j].f->varwidth)
+                        {
+                                elemptr[j].f->width=maxw-col-(nitems-1);
+                                // only nitems-1 in-between spaces
+                                // needed
+                        }
+
+                }
+                return spacings;
+        }
+
+
+        /* fixed columns, spread whitespace over columns */
+        double over=(0.0+maxw-col)/(nitems-1);
+        double todo=over;
+
+        for (j=0; j<nitems-1; ++j)   // last column gets no space appended
+        {
+                spacings[j]=(int)todo+0.5;
+                todo-=spacings[j];
+                todo+=over;
+        }
+        spacings[j]=0;
+        return spacings;
+}
+
+
+/*
+ * showhdrline: show header line for processes.
+ * if in interactive mode, also add a page numer
+ * if in interactive mode, columns are aligned to fill out rows
+ */
+void
+showhdrline(proc_printpair* elemptr, int curlist, int totlist, 
+                  char showorder, char autosort) 
+{
+        proc_printpair curelem;
+
+        char *chead="";
+        char *autoindic="";
+        int order=showorder;
+        int col=0;
+        int allign;
+        char pagindic[10];
+        int pagindiclen;
+        int n=0;
+        int bufsz;
+        int maxw=screen ? COLS : linelen;    // for non screen: 80 columns max
+
+        colspacings=getspacings(elemptr);
+        bufsz=maxw+1;
+
+        elemptr=newelems;     // point to adjusted array
+        char buf[bufsz+2];    // long live dynamically sized auto arrays...
+        
+        if (!screen) 
+        {
+                printg("\n");
+        }
+
+        while ((curelem=*elemptr).f!=0) 
+        {
+                if (curelem.f->head==0)     // empty header==special: SORTITEM
+                {
+                        chead=columnhead[order];
+                        autoindic= autosort ? "A" : " ";
+                } 
+                else 
+                {
+                        chead=curelem.f->head;
+                        autoindic="";
+                }
+
+                if (screen)
+                {
+                        col += sprintf(buf+col, "%s%s%*s", autoindic, chead,
+                              colspacings[n], "");
+                }
+                else
+                {
+                        col += sprintf(buf+col, "%s%s ", autoindic, chead);
+                }
+                              
+                elemptr++;
+                n++;
+        }
+
+        if (screen)   // add page number, eat from last header if needed...
+        {
+                pagindiclen=sprintf(pagindic,"%d/%d", curlist, totlist);
+                allign=COLS-col-pagindiclen;    // extra spaces needed
+            
+                if (allign >= 0)     // allign by adding spaces
+                {
+                        sprintf(buf+col, "%*s", allign+pagindiclen, pagindic);
+                }
+                else
+                {    // allign by removing from the right
+                        sprintf(buf+col+allign, "%s", pagindic);
+                }
+        }
+
+        printg("%s", buf);
+
+        if (!screen) 
+        {
+                printg("\n");
+        }
+}
+
+
+
+/***************************************************************/
+/*
+ * showprocline: show line for processes.
+ * if in interactive mode, columns are aligned to fill out rows
+ * params:
+ *     elemptr: pointer to array of print definition structs ptrs
+ *     curstat: the process to print
+ *     perc: the sort order used
+ *     nsecs: number of seconds elapsed between previous and this sample
+ *     avgval: is averaging out per second needed?
+ */
+void
+showprocline(proc_printpair* elemptr, struct tstat *curstat, 
+                            double perc, int nsecs, int avgval) 
+{
+        proc_printpair curelem;
+        
+        elemptr=newelems;      // point to static array
+        int n=0;
+
+       if (screen && threadview)
+       {
+               if (usecolors && !curstat->gen.isproc)
+               {
+                       attron(COLOR_PAIR(COLORTHR));
+               }
+               else
+               {
+                       if (!usecolors && curstat->gen.isproc)
+                               attron(A_BOLD);
+               }
+       }
+
+        while ((curelem=*elemptr).f!=0) 
+        {
+                // what to print?  SORTITEM, or active process or
+                // exited process?
+
+                if (curelem.f->head==0)                // empty string=sortitem
+                {
+                        printg("%3.0lf%%", perc);    // cannot pass perc
+                }
+                else if (curstat->gen.state != 'E')  // active process
+                {
+                        printg("%s", curelem.f->doactiveconvert(curstat, 
+                                                        avgval, nsecs));
+                }
+                else                                 // exited process
+                {
+                        printg("%s", curelem.f->doexitconvert(curstat, 
+                                                        avgval, nsecs));
+                }
+
+                if (screen)
+                {
+                        printg("%*s",colspacings[n], "");
+                }
+                else
+                {
+                        printg(" ");
+                }
+
+                elemptr++;
+                n++;
+        }
+
+       if (screen && threadview)
+       {
+               if (usecolors && !curstat->gen.isproc)
+               {
+                       attroff(COLOR_PAIR(COLORTHR));
+               }
+               else
+               {
+                       if (!usecolors && curstat->gen.isproc)
+                               attroff(A_BOLD);
+               }
+       }
+
+        if (!screen) 
+        {
+                printg("\n");
+        }
+}
+
+
+/*******************************************************************/
+/* PROCESS PRINT FUNCTIONS */
+/***************************************************************/
+char *
+procprt_NOTAVAIL_4(struct tstat *curstat, int avgval, int nsecs)
+{
+        return "   ?";
+}
+
+char *
+procprt_NOTAVAIL_5(struct tstat *curstat, int avgval, int nsecs)
+{
+        return "    ?";
+}
+
+char *
+procprt_NOTAVAIL_6(struct tstat *curstat, int avgval, int nsecs)
+{
+        return "     ?";
+}
+
+char *
+procprt_NOTAVAIL_7(struct tstat *curstat, int avgval, int nsecs)
+{
+        return "      ?";
+}
+/***************************************************************/
+char *
+procprt_TID_ae(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[64];
+
+       if (curstat->gen.isproc)
+               sprintf(buf, "%*s", procprt_TID.width, "-");
+       else
+               sprintf(buf, "%*d", procprt_TID.width, curstat->gen.pid);
+        return buf;
+}
+
+proc_printdef procprt_TID = 
+   { "TID", "TID", procprt_TID_ae, procprt_TID_ae, 5}; //DYNAMIC WIDTH!
+/***************************************************************/
+char *
+procprt_PID_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[64];
+
+        sprintf(buf, "%*d", procprt_PID.width, curstat->gen.tgid);
+        return buf;
+}
+
+char *
+procprt_PID_e(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[64];
+
+        if (curstat->gen.pid == 0)
+               sprintf(buf, "%*s", procprt_PID.width, "?");
+       else
+               sprintf(buf, "%*d", procprt_PID.width, curstat->gen.tgid);
+        return buf;
+}
+
+proc_printdef procprt_PID = 
+   { "PID", "PID", procprt_PID_a, procprt_PID_e, 5}; //DYNAMIC WIDTH!
+/***************************************************************/
+char *
+procprt_PPID_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[64];
+
+        sprintf(buf, "%*d", procprt_PPID.width, curstat->gen.ppid);
+        return buf;
+}
+
+char *
+procprt_PPID_e(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[64];
+
+       sprintf(buf, "%*s", procprt_PPID.width, "-");
+        return buf;
+}
+
+proc_printdef procprt_PPID = 
+   { "PPID", "PPID", procprt_PPID_a, procprt_PPID_e, 5 }; //DYNAMIC WIDTH!
+/***************************************************************/
+char *
+procprt_VPID_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[64];
+
+        sprintf(buf, "%*d", procprt_VPID.width, curstat->gen.vpid);
+        return buf;
+}
+
+char *
+procprt_VPID_e(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[64];
+
+       sprintf(buf, "%*s", procprt_VPID.width, "-");
+        return buf;
+}
+
+proc_printdef procprt_VPID = 
+   { "VPID", "VPID", procprt_VPID_a, procprt_VPID_e, 5 }; //DYNAMIC WIDTH!
+/***************************************************************/
+char *
+procprt_CTID_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[32];
+
+        sprintf(buf, "%5d", curstat->gen.ctid);
+        return buf;
+}
+
+char *
+procprt_CTID_e(struct tstat *curstat, int avgval, int nsecs)
+{
+        return "    -";
+}
+
+proc_printdef procprt_CTID = 
+   { " CTID", "CTID", procprt_CTID_a, procprt_CTID_e, 5 };
+/***************************************************************/
+char *
+procprt_CID_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[64];
+
+       if (curstat->gen.container[0])
+               sprintf(buf, "%-12s", curstat->gen.container);
+       else
+               sprintf(buf, "%-12s", "host--------");
+
+        return buf;
+}
+
+char *
+procprt_CID_e(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[64];
+
+       if (curstat->gen.container[0])
+               sprintf(buf, "%-12s", curstat->gen.container);
+       else
+               sprintf(buf, "%-12s", "?");
+
+        return buf;
+}
+
+proc_printdef procprt_CID = 
+   { "CID         ", "CID", procprt_CID_a, procprt_CID_e, 12};
+/***************************************************************/
+char *
+procprt_SYSCPU_ae(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[10];
+
+        val2cpustr(curstat->cpu.stime*1000/hertz, buf);
+        return buf;
+}
+
+proc_printdef procprt_SYSCPU = 
+   { "SYSCPU", "SYSCPU", procprt_SYSCPU_ae, procprt_SYSCPU_ae, 6 };
+/***************************************************************/
+char *
+procprt_USRCPU_ae(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[10];
+
+        val2cpustr(curstat->cpu.utime*1000/hertz, buf);
+        return buf;
+}
+
+proc_printdef procprt_USRCPU = 
+   { "USRCPU", "USRCPU", procprt_USRCPU_ae, procprt_USRCPU_ae, 6 };
+/***************************************************************/
+char *
+procprt_VGROW_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[10];
+
+        val2memstr(curstat->mem.vgrow*1024, buf, KBFORMAT, 0, 0);
+        return buf;
+}
+
+char *
+procprt_VGROW_e(struct tstat *curstat, int avgval, int nsecs)
+{
+        return "    0K";
+}
+
+proc_printdef procprt_VGROW = 
+   { " VGROW", "VGROW", procprt_VGROW_a, procprt_VGROW_e, 6 };
+/***************************************************************/
+char *
+procprt_RGROW_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[10];
+
+        val2memstr(curstat->mem.rgrow*1024, buf, KBFORMAT, 0, 0);
+        return buf;
+}
+
+char *
+procprt_RGROW_e(struct tstat *curstat, int avgval, int nsecs)
+{
+        return "    0K";
+}
+
+proc_printdef procprt_RGROW = 
+   { " RGROW", "RGROW", procprt_RGROW_a, procprt_RGROW_e, 6 };
+/***************************************************************/
+char *
+procprt_MINFLT_ae(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[10];
+
+        val2valstr(curstat->mem.minflt, buf, 6, avgval, nsecs);
+        return buf;
+}
+
+proc_printdef procprt_MINFLT = 
+   { "MINFLT", "MINFLT", procprt_MINFLT_ae, procprt_MINFLT_ae, 6 };
+/***************************************************************/
+char *
+procprt_MAJFLT_ae(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[10];
+
+        val2valstr(curstat->mem.majflt, buf, 6, avgval, nsecs);
+        return buf;
+}
+
+proc_printdef procprt_MAJFLT = 
+   { "MAJFLT", "MAJFLT", procprt_MAJFLT_ae, procprt_MAJFLT_ae, 6 };
+/***************************************************************/
+char *
+procprt_VSTEXT_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[10];
+
+        val2memstr(curstat->mem.vexec*1024, buf, KBFORMAT, 0, 0);
+        return buf;
+}
+
+char *
+procprt_VSTEXT_e(struct tstat *curstat, int avgval, int nsecs)
+{
+        return "    0K";
+}
+
+proc_printdef procprt_VSTEXT = 
+   { "VSTEXT", "VSTEXT", procprt_VSTEXT_a, procprt_VSTEXT_e, 6 };
+/***************************************************************/
+char *
+procprt_VSIZE_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[10];
+
+        val2memstr(curstat->mem.vmem*1024, buf, KBFORMAT, 0, 0);
+        return buf;
+}
+
+char *
+procprt_VSIZE_e(struct tstat *curstat, int avgval, int nsecs)
+{
+        return "    0K";
+}
+
+proc_printdef procprt_VSIZE = 
+   { " VSIZE", "VSIZE", procprt_VSIZE_a, procprt_VSIZE_e, 6 };
+/***************************************************************/
+char *
+procprt_RSIZE_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[10];
+
+        val2memstr(curstat->mem.rmem*1024, buf, KBFORMAT, 0, 0);
+        return buf;
+}
+
+char *
+procprt_RSIZE_e(struct tstat *curstat, int avgval, int nsecs)
+{
+        return "    0K";
+}
+
+proc_printdef procprt_RSIZE = 
+   { " RSIZE", "RSIZE", procprt_RSIZE_a, procprt_RSIZE_e, 6 };
+/***************************************************************/
+char *
+procprt_PSIZE_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[10];
+
+       if (curstat->mem.pmem == (unsigned long long)-1LL)      
+               return "    ?K";
+
+               val2memstr(curstat->mem.pmem*1024, buf, KBFORMAT, 0, 0);
+        return buf;
+}
+
+char *
+procprt_PSIZE_e(struct tstat *curstat, int avgval, int nsecs)
+{
+        return "    0K";
+}
+
+proc_printdef procprt_PSIZE = 
+   { " PSIZE", "PSIZE", procprt_PSIZE_a, procprt_PSIZE_e, 6 };
+/***************************************************************/
+char *
+procprt_VSLIBS_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[10];
+
+        val2memstr(curstat->mem.vlibs*1024, buf, KBFORMAT, 0, 0);
+        return buf;
+}
+
+char *
+procprt_VSLIBS_e(struct tstat *curstat, int avgval, int nsecs)
+{
+        return "    0K";
+}
+
+proc_printdef procprt_VSLIBS = 
+   { "VSLIBS", "VSLIBS", procprt_VSLIBS_a, procprt_VSLIBS_e, 6 };
+/***************************************************************/
+char *
+procprt_VDATA_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[10];
+
+        val2memstr(curstat->mem.vdata*1024, buf, KBFORMAT, 0, 0);
+        return buf;
+}
+
+char *
+procprt_VDATA_e(struct tstat *curstat, int avgval, int nsecs)
+{
+        return "    0K";
+}
+
+proc_printdef procprt_VDATA = 
+   { " VDATA", "VDATA", procprt_VDATA_a, procprt_VDATA_e, 6 };
+/***************************************************************/
+char *
+procprt_VSTACK_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[10];
+
+        val2memstr(curstat->mem.vstack*1024, buf, KBFORMAT, 0, 0);
+        return buf;
+}
+
+char *
+procprt_VSTACK_e(struct tstat *curstat, int avgval, int nsecs)
+{
+        return "    0K";
+}
+
+proc_printdef procprt_VSTACK = 
+   { "VSTACK", "VSTACK", procprt_VSTACK_a, procprt_VSTACK_e, 6 };
+/***************************************************************/
+char *
+procprt_SWAPSZ_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[10];
+
+        val2memstr(curstat->mem.vswap*1024, buf, KBFORMAT, 0, 0);
+        return buf;
+}
+
+char *
+procprt_SWAPSZ_e(struct tstat *curstat, int avgval, int nsecs)
+{
+        return "    0K";
+}
+
+proc_printdef procprt_SWAPSZ = 
+   { "SWAPSZ", "SWAPSZ", procprt_SWAPSZ_a, procprt_SWAPSZ_e, 6 };
+/***************************************************************/
+char *
+procprt_CMD_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[15];
+
+        sprintf(buf, "%-14.14s", curstat->gen.name);
+        return buf;
+}
+
+char *
+procprt_CMD_e(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[15]="<";
+        char        helpbuf[15];
+
+        sprintf(helpbuf, "<%.12s>",  curstat->gen.name);
+        sprintf(buf,     "%-14.14s", helpbuf);
+        return buf;
+}
+
+proc_printdef procprt_CMD = 
+   { "CMD           ", "CMD", procprt_CMD_a, procprt_CMD_e, 14 };
+/***************************************************************/
+char *
+procprt_RUID_ae(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[9];
+        struct passwd   *pwd;
+
+        if ( (pwd = getpwuid(curstat->gen.ruid)) )
+        {
+                        sprintf(buf, "%-8.8s", pwd->pw_name);
+        } 
+        else 
+        {
+                        snprintf(buf, sizeof buf, "%-8d", curstat->gen.ruid);
+        }
+        return buf;
+}
+
+proc_printdef procprt_RUID = 
+   { "RUID    ", "RUID", procprt_RUID_ae, procprt_RUID_ae, 8 };
+/***************************************************************/
+char *
+procprt_EUID_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[9];
+        struct passwd   *pwd;
+
+        if ( (pwd = getpwuid(curstat->gen.euid)) )
+        {
+                        sprintf(buf, "%-8.8s", pwd->pw_name);
+        } 
+        else 
+        {
+                        snprintf(buf, sizeof buf, "%-8d", curstat->gen.euid);
+        }
+        return buf;
+}
+
+char *
+procprt_EUID_e(struct tstat *curstat, int avgval, int nsecs)
+{
+       return "-       ";
+}
+
+proc_printdef procprt_EUID = 
+   { "EUID    ", "EUID", procprt_EUID_a, procprt_EUID_e, 8 };
+/***************************************************************/
+char *
+procprt_SUID_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[9];
+        struct passwd   *pwd;
+
+        if ( (pwd = getpwuid(curstat->gen.suid)) )
+        {
+                        sprintf(buf, "%-8.8s", pwd->pw_name);
+        } 
+        else 
+        {
+                        snprintf(buf, sizeof buf, "%-8d", curstat->gen.suid);
+        }
+        return buf;
+}
+
+char *
+procprt_SUID_e(struct tstat *curstat, int avgval, int nsecs)
+{
+       return "-       ";
+}
+
+proc_printdef procprt_SUID = 
+   { "SUID    ", "SUID", procprt_SUID_a, procprt_SUID_e, 8 };
+/***************************************************************/
+char *
+procprt_FSUID_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[9];
+        struct passwd   *pwd;
+
+        if ( (pwd = getpwuid(curstat->gen.fsuid)) )
+        {
+                        sprintf(buf, "%-8.8s", pwd->pw_name);
+        } 
+        else 
+        {
+                        snprintf(buf, sizeof buf, "%-8d", curstat->gen.fsuid);
+        }
+        return buf;
+}
+
+char *
+procprt_FSUID_e(struct tstat *curstat, int avgval, int nsecs)
+{
+       return "-       ";
+}
+
+proc_printdef procprt_FSUID = 
+   { "FSUID   ", "FSUID", procprt_FSUID_a, procprt_FSUID_e, 8 };
+/***************************************************************/
+char *
+procprt_RGID_ae(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[10];
+        struct group    *grp;
+        char *groupname;
+        char grname[16];
+
+        if ( (grp = getgrgid(curstat->gen.rgid)) )
+        {
+                        groupname = grp->gr_name;
+        }
+        else
+        {
+                        snprintf(grname, sizeof grname, "%d",curstat->gen.rgid);
+                        groupname = grname;
+        }
+
+        sprintf(buf, "%-8.8s", groupname);
+        return buf;
+}
+
+proc_printdef procprt_RGID = 
+   { "RGID    ", "RGID", procprt_RGID_ae, procprt_RGID_ae, 8 };
+/***************************************************************/
+char *
+procprt_EGID_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[10];
+        struct group    *grp;
+        char *groupname;
+        char grname[16];
+
+        if ( (grp = getgrgid(curstat->gen.egid)) )
+        {
+                        groupname = grp->gr_name;
+        }
+        else
+        {
+                        snprintf(grname, sizeof grname, "%d",curstat->gen.egid);
+                        groupname = grname;
+        }
+
+        sprintf(buf, "%-8.8s", groupname);
+        return buf;
+}
+
+char *
+procprt_EGID_e(struct tstat *curstat, int avgval, int nsecs)
+{
+       return "-       ";
+}
+
+proc_printdef procprt_EGID = 
+   { "EGID    ", "EGID", procprt_EGID_a, procprt_EGID_e, 8 };
+/***************************************************************/
+char *
+procprt_SGID_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[10];
+        struct group    *grp;
+        char *groupname;
+        char grname[16];
+
+        if ( (grp = getgrgid(curstat->gen.sgid)) )
+        {
+                        groupname = grp->gr_name;
+        }
+        else
+        {
+                        snprintf(grname, sizeof grname, "%d",curstat->gen.sgid);
+                        groupname = grname;
+        }
+
+        sprintf(buf, "%-8.8s", groupname);
+        return buf;
+}
+
+char *
+procprt_SGID_e(struct tstat *curstat, int avgval, int nsecs)
+{
+       return "-       ";
+}
+
+proc_printdef procprt_SGID = 
+   { "SGID    ", "SGID", procprt_SGID_a, procprt_SGID_e, 8 };
+/***************************************************************/
+char *
+procprt_FSGID_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[10];
+        struct group    *grp;
+        char *groupname;
+        char grname[16];
+
+        if ( (grp = getgrgid(curstat->gen.fsgid)) )
+        {
+                        groupname = grp->gr_name;
+        }
+        else
+        {
+                        snprintf(grname, sizeof grname,"%d",curstat->gen.fsgid);
+                        groupname = grname;
+        }
+
+        sprintf(buf, "%-8.8s", groupname);
+        return buf;
+}
+
+char *
+procprt_FSGID_e(struct tstat *curstat, int avgval, int nsecs)
+{
+       return "-       ";
+}
+
+proc_printdef procprt_FSGID = 
+   { "FSGID   ", "FSGID", procprt_FSGID_a, procprt_FSGID_e, 8 };
+/***************************************************************/
+char *
+procprt_STDATE_ae(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[11];
+
+        convdate(curstat->gen.btime, buf);
+        return buf;
+}
+
+proc_printdef procprt_STDATE = 
+   { "  STDATE  ", "STDATE", procprt_STDATE_ae, procprt_STDATE_ae, 10 };
+/***************************************************************/
+char *
+procprt_STTIME_ae(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[9];
+
+        convtime(curstat->gen.btime, buf);
+        return buf;
+}
+
+proc_printdef procprt_STTIME = 
+   { " STTIME ", "STTIME", procprt_STTIME_ae, procprt_STTIME_ae, 8 };
+/***************************************************************/
+char *
+procprt_ENDATE_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[11];
+
+       strcpy(buf, "  active  ");
+
+        return buf;
+}
+
+char *
+procprt_ENDATE_e(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[11];
+
+        convdate(curstat->gen.btime + curstat->gen.elaps/hertz, buf);
+
+        return buf;
+}
+
+proc_printdef procprt_ENDATE = 
+   { "  ENDATE  ", "ENDATE", procprt_ENDATE_a, procprt_ENDATE_e, 10 };
+/***************************************************************/
+char *
+procprt_ENTIME_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[9];
+
+       strcpy(buf, " active ");
+
+        return buf;
+}
+
+char *
+procprt_ENTIME_e(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[9];
+
+        convtime(curstat->gen.btime + curstat->gen.elaps/hertz, buf);
+
+        return buf;
+}
+
+proc_printdef procprt_ENTIME = 
+   { " ENTIME ", "ENTIME", procprt_ENTIME_a, procprt_ENTIME_e, 8 };
+/***************************************************************/
+char *
+procprt_THR_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[15];
+
+        sprintf(buf, "%4d", curstat->gen.nthr);
+        return buf;
+}
+
+char *
+procprt_THR_e(struct tstat *curstat, int avgval, int nsecs)
+{
+        return "   0";
+}
+
+proc_printdef procprt_THR = 
+   { " THR", "THR", procprt_THR_a, procprt_THR_e, 4 };
+/***************************************************************/
+char *
+procprt_TRUN_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[15];
+
+        sprintf(buf, "%4d", curstat->gen.nthrrun);
+        return buf;
+}
+
+char *
+procprt_TRUN_e(struct tstat *curstat, int avgval, int nsecs)
+{
+        return "   0";
+}
+
+proc_printdef procprt_TRUN = 
+   { "TRUN", "TRUN", procprt_TRUN_a, procprt_TRUN_e, 4 };
+/***************************************************************/
+char *
+procprt_TSLPI_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[15];
+
+        sprintf(buf, "%5d", curstat->gen.nthrslpi);
+        return buf;
+}
+
+char *
+procprt_TSLPI_e(struct tstat *curstat, int avgval, int nsecs)
+{
+        return "    0";
+}
+
+proc_printdef procprt_TSLPI = 
+   { "TSLPI", "TSLPI", procprt_TSLPI_a, procprt_TSLPI_e, 5 };
+/***************************************************************/
+char *
+procprt_TSLPU_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[15];
+
+        sprintf(buf, "%5d", curstat->gen.nthrslpu);
+        return buf;
+}
+
+char *
+procprt_TSLPU_e(struct tstat *curstat, int avgval, int nsecs)
+{
+        return "    0";
+}
+
+proc_printdef procprt_TSLPU = 
+   { "TSLPU", "TSLPU", procprt_TSLPU_a, procprt_TSLPU_e, 5 };
+/***************************************************************/
+#define SCHED_NORMAL   0
+#define SCHED_FIFO     1
+#define SCHED_RR       2
+#define SCHED_BATCH    3
+#define SCHED_ISO      4
+#define SCHED_IDLE     5
+
+char *
+procprt_POLI_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        switch (curstat->cpu.policy)
+        {
+                case SCHED_NORMAL:
+                        return "norm";
+                        break;
+                case SCHED_FIFO:
+                        return "fifo";
+                        break;
+                case SCHED_RR:
+                        return "rr  ";
+                        break;
+                case SCHED_BATCH:
+                        return "btch";
+                        break;
+                case SCHED_ISO:
+                        return "iso ";
+                        break;
+                case SCHED_IDLE:
+                        return "idle";
+                        break;
+        }
+        return "?   ";
+}
+
+char *
+procprt_POLI_e(struct tstat *curstat, int avgval, int nsecs)
+{
+        return "-   ";
+}
+
+proc_printdef procprt_POLI = 
+   { "POLI", "POLI", procprt_POLI_a, procprt_POLI_e, 4 };
+/***************************************************************/
+char *
+procprt_NICE_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[15];
+
+        sprintf(buf, "%4d", curstat->cpu.nice);
+        return buf;
+}
+
+char *
+procprt_NICE_e(struct tstat *curstat, int avgval, int nsecs)
+{
+        return "   -";
+}
+
+proc_printdef procprt_NICE = 
+   { "NICE", "NICE", procprt_NICE_a, procprt_NICE_e, 4 };
+/***************************************************************/
+char *
+procprt_PRI_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[15];
+
+        sprintf(buf, "%3d", curstat->cpu.prio);
+        return buf;
+}
+
+char *
+procprt_PRI_e(struct tstat *curstat, int avgval, int nsecs)
+{
+        return "  -";
+}
+
+proc_printdef procprt_PRI = 
+   { "PRI", "PRI", procprt_PRI_a, procprt_PRI_e, 3 };
+/***************************************************************/
+char *
+procprt_RTPR_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[15];
+
+        sprintf(buf, "%4d", curstat->cpu.rtprio);
+        return buf;
+}
+
+char *
+procprt_RTPR_e(struct tstat *curstat, int avgval, int nsecs)
+{
+        return "   -";
+}
+
+proc_printdef procprt_RTPR = 
+   { "RTPR", "RTPR", procprt_RTPR_a, procprt_RTPR_e, 4 };
+/***************************************************************/
+char *
+procprt_CURCPU_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[15];
+
+        sprintf(buf, "%5d", curstat->cpu.curcpu);
+        return buf;
+}
+
+char *
+procprt_CURCPU_e(struct tstat *curstat, int avgval, int nsecs)
+{
+        return "    -";
+}
+
+proc_printdef procprt_CURCPU = 
+   { "CPUNR", "CPUNR", procprt_CURCPU_a, procprt_CURCPU_e, 5 };
+/***************************************************************/
+char *
+procprt_ST_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[3]="--";
+        if (curstat->gen.excode & ~(INT_MAX))
+        {
+                buf[0]='N';
+        } 
+        else
+        { 
+                buf[0]='-';
+        }
+        return buf;
+}
+
+char *
+procprt_ST_e(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[3];
+        if (curstat->gen.excode & ~(INT_MAX))
+        {
+                buf[0]='N';
+        } 
+        else 
+        { 
+                buf[0]='-';
+        }
+        if (curstat->gen.excode & 0xff) 
+        {
+                if (curstat->gen.excode & 0x80)
+                        buf[1] = 'C';
+                else
+                        buf[1] = 'S';
+        } 
+        else 
+        {
+                buf[1] = 'E';
+        }
+        return buf;
+}
+
+proc_printdef procprt_ST = 
+   { "ST", "ST", procprt_ST_a, procprt_ST_e, 2 };
+/***************************************************************/
+char *
+procprt_EXC_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        return "  -";
+}
+
+char *
+procprt_EXC_e(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[4];
+
+
+        sprintf(buf, "%3d", 
+                 curstat->gen.excode & 0xff ?
+                          curstat->gen.excode & 0x7f : 
+                          (curstat->gen.excode>>8) & 0xff);
+        return buf;
+}
+
+
+proc_printdef procprt_EXC = 
+   { "EXC", "EXC", procprt_EXC_a, procprt_EXC_e, 3 };
+/***************************************************************/
+char *
+procprt_S_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[2]="E";
+
+        buf[0]=curstat->gen.state;
+        return buf;
+}
+
+char *
+procprt_S_e(struct tstat *curstat, int avgval, int nsecs)
+{
+        return "E";
+
+}
+
+proc_printdef procprt_S = 
+   { "S", "S", procprt_S_a, procprt_S_e, 1 };
+
+/***************************************************************/
+char *
+procprt_COMMAND_LINE_ae(struct tstat *curstat, int avgval, int nsecs)
+{
+        extern proc_printdef procprt_COMMAND_LINE;
+        extern int     startoffset;    // influenced by -> and <- keys
+
+        static char    buf[CMDLEN+1];
+
+       char    *pline     = curstat->gen.cmdline[0] ?
+                               curstat->gen.cmdline : curstat->gen.name;
+
+        int    curwidth   = procprt_COMMAND_LINE.width <= CMDLEN ?
+                               procprt_COMMAND_LINE.width : CMDLEN;
+
+        int    cmdlen     = strlen(pline);
+        int    curoffset  = startoffset <= cmdlen ? startoffset : cmdlen;
+
+        if (screen) 
+                sprintf(buf, "%-*.*s", curwidth, curwidth, pline+curoffset);
+        else
+                sprintf(buf, "%.*s", CMDLEN, pline+curoffset);
+
+        return buf;
+}
+
+proc_printdef procprt_COMMAND_LINE = 
+       { "COMMAND-LINE (horizontal scroll with <- and -> keys)",
+       "COMMAND-LINE", 
+        procprt_COMMAND_LINE_ae, procprt_COMMAND_LINE_ae, 0, 1 };
+/***************************************************************/
+char *
+procprt_NPROCS_ae(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[10];
+
+        val2valstr(curstat->gen.pid, buf, 6, 0, 0); // pid abused as proc counter
+        return buf;
+}
+
+proc_printdef procprt_NPROCS = 
+   { "NPROCS", "NPROCS", procprt_NPROCS_ae, procprt_NPROCS_ae, 6 };
+/***************************************************************/
+char *
+procprt_RDDSK_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[10];
+        val2memstr(curstat->dsk.rsz*512, buf, KBFORMAT, avgval, nsecs);
+
+        return buf;
+}
+
+char *
+procprt_RDDSK_e(struct tstat *curstat, int avgval, int nsecs)
+{
+        return "     -";
+}
+
+proc_printdef procprt_RDDSK = 
+   { " RDDSK", "RDDSK", procprt_RDDSK_a, procprt_RDDSK_e, 6 };
+/***************************************************************/
+char *
+procprt_WRDSK_a(struct tstat *curstat, int avgval, int nsecs) 
+{
+        static char buf[10];
+
+        val2memstr(curstat->dsk.wsz*512, buf, KBFORMAT, avgval, nsecs);
+
+        return buf;
+}
+
+char *
+procprt_WRDSK_e(struct tstat *curstat, int avgval, int nsecs)
+{
+        return "     -";
+}
+
+proc_printdef procprt_WRDSK = 
+   { " WRDSK", "WRDSK", procprt_WRDSK_a, procprt_WRDSK_e, 6 };
+/***************************************************************/
+char *
+procprt_CWRDSK_a(struct tstat *curstat, int avgval, int nsecs) 
+{
+       count_t nett_wsz;
+        static char buf[10];
+
+       if (curstat->dsk.wsz > curstat->dsk.cwsz)
+               nett_wsz = curstat->dsk.wsz - curstat->dsk.cwsz;
+       else
+               nett_wsz = 0;
+
+        val2memstr(nett_wsz*512, buf, KBFORMAT, avgval, nsecs);
+
+        return buf;
+}
+
+proc_printdef procprt_CWRDSK = 
+   {" WRDSK", "CWRDSK", procprt_CWRDSK_a, procprt_WRDSK_e, 6 };
+/***************************************************************/
+char *
+procprt_WCANCEL_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[10];
+        val2memstr(curstat->dsk.cwsz*512, buf, KBFORMAT, avgval, nsecs);
+
+        return buf;
+}
+
+char *
+procprt_WCANCEL_e(struct tstat *curstat, int avgval, int nsecs)
+{
+        return "     -";
+}
+
+proc_printdef procprt_WCANCEL = 
+   {"WCANCL", "WCANCL", procprt_WCANCEL_a, procprt_WCANCEL_e, 6};
+/***************************************************************/
+char *
+procprt_TCPRCV_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[10];
+        
+        val2valstr(curstat->net.tcprcv, buf, 6, avgval, nsecs);
+
+        return buf;
+}
+
+char *
+procprt_TCPRCV_e(struct tstat *curstat, int avgval, int nsecs) 
+{      
+       if (supportflags & NETATOPD)
+       {
+               static char buf[10];
+        
+               val2valstr(curstat->net.tcprcv, buf, 6, avgval, nsecs);
+
+               return buf;
+       }
+       else
+               return "     -";
+}
+
+
+proc_printdef procprt_TCPRCV = 
+   { "TCPRCV", "TCPRCV", procprt_TCPRCV_a, procprt_TCPRCV_e, 6 };
+/***************************************************************/
+char *
+procprt_TCPRASZ_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[10];
+        
+        int avgtcpr = curstat->net.tcprcv ?
+                                  curstat->net.tcprsz / curstat->net.tcprcv : 0;
+
+        val2valstr(avgtcpr, buf, 7, 0, 0);
+        return buf;
+}
+
+char *
+procprt_TCPRASZ_e(struct tstat *curstat, int avgval, int nsecs)
+{
+       if (supportflags & NETATOPD)
+       {
+               static char buf[10];
+        
+               int avgtcpr = curstat->net.tcprcv ?
+                                  curstat->net.tcprsz / curstat->net.tcprcv : 0;
+
+               val2valstr(avgtcpr, buf, 7, 0, 0);
+               return buf;
+       }
+       else
+               return "      -";
+}
+
+proc_printdef procprt_TCPRASZ = 
+   { "TCPRASZ", "TCPRASZ", procprt_TCPRASZ_a, procprt_TCPRASZ_e, 7 };
+/***************************************************************/
+char *
+procprt_TCPSND_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[10];
+        
+        val2valstr(curstat->net.tcpsnd, buf, 6, avgval, nsecs);
+
+        return buf;
+}
+
+char *
+procprt_TCPSND_e(struct tstat *curstat, int avgval, int nsecs)
+{
+       if (supportflags & NETATOPD)
+       {
+               static char buf[10];
+        
+               val2valstr(curstat->net.tcpsnd, buf, 6, avgval, nsecs);
+
+               return buf;
+       }
+       else
+               return "     -";
+}
+
+proc_printdef procprt_TCPSND = 
+   { "TCPSND", "TCPSND", procprt_TCPSND_a, procprt_TCPSND_e, 6 };
+/***************************************************************/
+char *
+procprt_TCPSASZ_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[10];
+        
+        int avgtcps = curstat->net.tcpsnd ?
+                                  curstat->net.tcpssz / curstat->net.tcpsnd : 0;
+
+        val2valstr(avgtcps, buf, 7, 0, 0);
+        return buf;
+}
+
+char *
+procprt_TCPSASZ_e(struct tstat *curstat, int avgval, int nsecs)
+{
+       if (supportflags & NETATOPD)
+       {
+               static char buf[10];
+        
+               int avgtcps = curstat->net.tcpsnd ?
+                                  curstat->net.tcpssz / curstat->net.tcpsnd : 0;
+
+               val2valstr(avgtcps, buf, 7, 0, 0);
+               return buf;
+       }
+       else
+               return "      -";
+}
+
+
+proc_printdef procprt_TCPSASZ = 
+   { "TCPSASZ", "TCPSASZ", procprt_TCPSASZ_a, procprt_TCPSASZ_e, 7 };
+/***************************************************************/
+char *
+procprt_UDPRCV_a(struct tstat *curstat, int avgval, int nsecs)        
+{
+        static char buf[10];
+        
+        val2valstr(curstat->net.udprcv, buf, 6, avgval, nsecs);
+
+        return buf;
+}
+
+char *
+procprt_UDPRCV_e(struct tstat *curstat, int avgval, int nsecs) 
+{
+       if (supportflags & NETATOPD)
+       {
+               static char buf[10];
+        
+               val2valstr(curstat->net.udprcv, buf, 6, avgval, nsecs);
+
+               return buf;
+       }
+       else
+               return "     -";
+}
+
+
+proc_printdef procprt_UDPRCV = 
+   { "UDPRCV", "UDPRCV", procprt_UDPRCV_a, procprt_UDPRCV_e, 6 };
+/***************************************************************/
+char *
+procprt_UDPRASZ_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[10];
+        
+        int avgudpr = curstat->net.udprcv ?
+                          curstat->net.udprsz / curstat->net.udprcv : 0;
+
+        val2valstr(avgudpr, buf, 7, 0, 0);
+        return buf;
+}
+
+char *
+procprt_UDPRASZ_e(struct tstat *curstat, int avgval, int nsecs)
+{
+       if (supportflags & NETATOPD)
+       {
+               static char buf[10];
+        
+               int avgudpr = curstat->net.udprcv ?
+                          curstat->net.udprsz / curstat->net.udprcv : 0;
+
+               val2valstr(avgudpr, buf, 7, 0, 0);
+               return buf;
+       }
+       else
+               return "      -";
+}
+
+
+proc_printdef procprt_UDPRASZ = 
+   { "UDPRASZ", "UDPRASZ", procprt_UDPRASZ_a, procprt_UDPRASZ_e, 7 };
+/***************************************************************/
+char *
+procprt_UDPSND_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[10];
+        
+        val2valstr(curstat->net.udpsnd, buf, 6, avgval, nsecs);
+
+        return buf;
+}
+
+char *
+procprt_UDPSND_e(struct tstat *curstat, int avgval, int nsecs)
+{
+       if (supportflags & NETATOPD)
+       {
+               static char buf[10];
+        
+               val2valstr(curstat->net.udpsnd, buf, 6, avgval, nsecs);
+
+               return buf;
+       }
+       else
+               return "     -";
+}
+
+proc_printdef procprt_UDPSND = 
+   { "UDPSND", "UDPSND", procprt_UDPSND_a, procprt_UDPSND_e, 6 };
+/***************************************************************/
+char *
+procprt_UDPSASZ_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[10];
+        
+        int avgudps = curstat->net.udpsnd ?
+                                  curstat->net.udpssz / curstat->net.udpsnd : 0;
+
+        val2valstr(avgudps, buf, 7, 0, 0);
+        return buf;
+}
+
+char *
+procprt_UDPSASZ_e(struct tstat *curstat, int avgval, int nsecs)
+{
+       if (supportflags & NETATOPD)
+       {
+               static char buf[10];
+        
+               int avgudps = curstat->net.udpsnd ?
+                                  curstat->net.udpssz / curstat->net.udpsnd : 0;
+
+               val2valstr(avgudps, buf, 7, 0, 0);
+               return buf;
+       }
+       else
+               return "      -";
+}
+
+
+proc_printdef procprt_UDPSASZ = 
+   { "UDPSASZ", "UDPSASZ", procprt_UDPSASZ_a, procprt_UDPSASZ_e, 7 };
+/***************************************************************/
+char *
+procprt_RNET_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[10];
+        
+        val2valstr(curstat->net.tcprcv + curstat->net.udprcv ,
+                                       buf, 5, avgval, nsecs);
+
+        return buf;
+}
+
+char *
+procprt_RNET_e(struct tstat *curstat, int avgval, int nsecs)
+{
+       if (supportflags & NETATOPD)
+       {
+               static char buf[10];
+               val2valstr(curstat->net.tcprcv + curstat->net.udprcv ,
+                                       buf, 5, avgval, nsecs);
+
+                       return buf;
+       }
+       else
+               return "    -";
+}
+
+proc_printdef procprt_RNET = 
+   { " RNET", "RNET", procprt_RNET_a, procprt_RNET_e, 5 };
+/***************************************************************/
+char *
+procprt_SNET_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[10];
+        
+        val2valstr(curstat->net.tcpsnd + curstat->net.udpsnd,
+                                       buf, 5, avgval, nsecs);
+        return buf;
+}
+
+char *
+procprt_SNET_e(struct tstat *curstat, int avgval, int nsecs)
+{
+       if (supportflags & NETATOPD)
+       {
+               static char buf[10];
+        
+                       val2valstr(curstat->net.tcpsnd + curstat->net.udpsnd,
+                                       buf, 5, avgval, nsecs);
+               return buf;
+       }
+       else
+               return "    -";
+}
+
+proc_printdef procprt_SNET = 
+   { " SNET", "SNET", procprt_SNET_a, procprt_SNET_e, 5 };
+/***************************************************************/
+char *
+procprt_BANDWI_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[16];
+       count_t     rkbps = (curstat->net.tcprsz+curstat->net.udprsz)/125/nsecs;
+
+       format_bandw(buf, rkbps);
+        return buf;
+}
+
+char *
+procprt_BANDWI_e(struct tstat *curstat, int avgval, int nsecs)
+{
+       if (supportflags & NETATOPD)
+       {
+               static char buf[16];
+               count_t     rkbps = (curstat->net.tcprsz + curstat->net.udprsz)
+                                                               /125/nsecs;
+
+               format_bandw(buf, rkbps);
+               return buf;
+       }
+       else
+               return "        -";
+}
+
+proc_printdef procprt_BANDWI = 
+   { "   BANDWI", "BANDWI", procprt_BANDWI_a, procprt_BANDWI_e, 9};
+/***************************************************************/
+char *
+procprt_BANDWO_a(struct tstat *curstat, int avgval, int nsecs)
+{
+        static char buf[16];
+       count_t     skbps = (curstat->net.tcpssz+curstat->net.udpssz)/125/nsecs;
+
+       format_bandw(buf, skbps);
+        return buf;
+}
+
+char *
+procprt_BANDWO_e(struct tstat *curstat, int avgval, int nsecs)
+{
+       if (supportflags & NETATOPD)
+       {
+               static char buf[16];
+               count_t     skbps = (curstat->net.tcpssz + curstat->net.udpssz)
+                                                               /125/nsecs;
+
+               format_bandw(buf, skbps);
+                       return buf;
+       }
+       else
+               return "        -";
+}
+
+proc_printdef procprt_BANDWO = 
+   { "   BANDWO", "BANDWO", procprt_BANDWO_a, procprt_BANDWO_e, 9};
+/***************************************************************/
+static void
+format_bandw(char *buf, count_t kbps)
+{
+       char        c;
+
+       if (kbps < 10000)
+       {
+                c='K';
+        }
+        else if (kbps < (count_t)10000 * 1000)
+        {
+                kbps/=1000;
+                c = 'M';
+        }
+        else if (kbps < (count_t)10000 * 1000 * 1000)
+        {
+                kbps/=1000 * 1000;
+                c = 'G';
+        }
+        else
+        {
+                kbps = kbps / 1000 / 1000 / 1000;
+                c = 'T';
+        }
+
+        sprintf(buf, "%4lld %cbps", kbps, c);
+}
+/***************************************************************/
+char *
+procprt_SORTITEM_ae(struct tstat *curstat, int avgval, int nsecs)
+{
+        return "";   // dummy function
+}
+
+proc_printdef procprt_SORTITEM = 
+   { 0, "SORTITEM", procprt_SORTITEM_ae, procprt_SORTITEM_ae, 4 };
diff --git a/sources/showsys.c b/sources/showsys.c
new file mode 100644 (file)
index 0000000..db51154
--- /dev/null
@@ -0,0 +1,2374 @@
+/*
+** ATOP - System & Process Monitor 
+**
+** The program 'atop' offers the possibility to view the activity of
+** the system on system-level as well as process-level.
+**
+** This source-file contains the Linux-specific functions to calculate
+** figures to be visualized.
+** ==========================================================================
+** Author:      JC van Winkel - AT Computing, Nijmegen, Holland
+** E-mail:      jc@ATComputing.nl
+** Date:        November 2009
+** --------------------------------------------------------------------------
+** Copyright (C) 2009 JC van Winkel
+**
+** This program is free software; you can redistribute it and/or modify it
+** under the terms of the GNU General Public License as published by the
+** Free Software Foundation; either version 2, or (at your option) any
+** later version.
+**
+** This program is distributed in the hope that it will be useful, but
+** WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+** See the GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+** --------------------------------------------------------------------------
+**
+** $Log: showsys.c,v $
+** Revision 1.10  2010/11/12 06:06:43  gerlof
+** Sometimes segmentation-fault on particular CPU-types
+** due to memcpy i.s.o. memmove when moving memory in overlap.
+**
+** Revision 1.9  2010/10/23 14:04:20  gerlof
+** Counters for total number of running and sleep threads (JC van Winkel).
+**
+** Revision 1.8  2010/05/18 19:20:02  gerlof
+** Introduce CPU frequency and scaling (JC van Winkel).
+**
+** Revision 1.7  2010/04/23 08:16:58  gerlof
+** Field 'avque' modified to 'avq' to be able to show higher values
+** (especially on LVM-level).
+**
+** Revision 1.6  2010/03/04 10:53:37  gerlof
+** Support I/O-statistics on logical volumes and MD devices.
+**
+** Revision 1.5  2009/12/17 11:59:36  gerlof
+** Gather and display new counters: dirty cache and guest cpu usage.
+**
+** Revision 1.4  2009/12/17 08:53:03  gerlof
+** If no coclors wanted, use bold display for critical resources.
+**
+** Revision 1.3  2009/12/17 07:33:05  gerlof
+** Scale system-statistics properly when modifying window size (JC van Winkel).
+**
+** Revision 1.2  2009/12/10 11:56:08  gerlof
+** Various bug-solutions.
+**
+** Revision 1.1  2009/12/10 09:46:16  gerlof
+** Initial revision
+**
+** Initial revision
+**
+**
+** Initial
+**
+*/
+
+static const char rcsid[] = "XXXXXX";
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <curses.h>
+#include <pwd.h>
+#include <grp.h>
+#include <regex.h>
+
+#include "atop.h"
+#include "photoproc.h"
+#include "photosyst.h"
+#include "showgeneric.h"
+#include "showlinux.h"
+
+/*******************************************************************/
+/*
+** print the label of a system-statistics line and switch on
+** colors if needed 
+*/
+static int
+syscolorlabel(char *labeltext, unsigned int badness)
+{
+        if (screen)
+        {
+                if (badness >= 100)
+                {
+                        attron (A_BLINK);
+
+                       if (usecolors)
+                       {
+                               attron(COLOR_PAIR(COLORCRIT));
+                               printg(labeltext);
+                               attroff(COLOR_PAIR(COLORCRIT));
+                       }
+                       else
+                       {
+                               attron(A_BOLD);
+                               printg(labeltext);
+                               attroff(A_BOLD);
+                       }
+
+                        attroff(A_BLINK);
+
+                        return COLORCRIT;
+                }
+
+                if (almostcrit && badness >= almostcrit)
+                {
+                       if (usecolors)
+                       {
+                               attron(COLOR_PAIR(COLORALMOST));
+                               printg(labeltext);
+                               attroff(COLOR_PAIR(COLORALMOST));
+                       }
+                       else
+                       {
+                               attron(A_BOLD);
+                               printg(labeltext);
+                               attroff(A_BOLD);
+                       }
+
+                        return COLORALMOST;
+                }
+        }
+
+        /*
+        ** no colors required or no reason to show colors
+        */
+        printg(labeltext);
+        return 0;
+}
+
+char *sysprt_BLANKBOX(void *p, void *notused, int, int *);
+
+void
+addblanks(double *charslackused, double *charslackover)
+{
+        *charslackused+=*charslackover;
+        while (*charslackused>0.5)
+        {
+                printg(" ");
+                *charslackused-=1;
+        }
+}
+
+/*
+ * showsysline
+ * print an array of sys_printpair things.  If the screen contains to
+ * few character columns, lower priority items are removed
+ *
+ */
+void showsysline(sys_printpair* elemptr, 
+                 struct sstat* sstat, extraparam *extra,
+                 char *labeltext, unsigned int badness)
+{
+        sys_printdef    *curelem;
+        int maxw = screen ? COLS : linelen;
+
+        // every 15-char item is printed as:
+        // >>>> | datadatadata<<<<<
+        //     012345678901234
+        
+        /* how many items will fit on one line? */
+        int avail = (maxw-5)/15;
+
+        syscolorlabel(labeltext, badness);
+
+        /* count number of items */
+#define MAXELEMS 40
+       sys_printpair newelems[MAXELEMS];
+       int nitems;
+
+       for (nitems=0; nitems < MAXELEMS-1 && elemptr[nitems].f != 0; ++nitems)
+               newelems[nitems]=elemptr[nitems];
+
+       newelems[nitems].f=0;
+
+        /* remove lowest priority box to make room as needed */
+        while (nitems > avail)
+        {
+                int lowestprio=999999;
+                int lowestprio_index=-1;
+                int i;
+                for (i=0; i<nitems; ++i) 
+                {
+                        if (newelems[i].prio < lowestprio) 
+                        {
+                                lowestprio=newelems[i].prio;
+                                lowestprio_index=i;
+                        }
+                }
+
+                // lowest priority item found, remove from newelems;
+                memmove(newelems+lowestprio_index, 
+                        newelems+lowestprio_index+1, 
+                        (nitems-lowestprio_index)* sizeof(sys_printpair));   
+                       // also copies final 0 entry
+                nitems--;
+        }
+
+        /* 
+         * ``item shortage'' is used to make entire blank boxes
+         * these boxes are spread out as much as possible
+         * remaining slack is used to add spaces around the vertical
+         * bars
+         */
+        double slackitemsover;
+
+        if (nitems >1)
+        {
+                slackitemsover=(double)(avail-nitems)/(nitems);
+        }
+        else 
+        {
+                slackitemsover=(avail-nitems)/2;
+        }
+
+
+        // charslack: the slack in characters after using as many
+        // items as possible
+        double charslackover = screen ? ((COLS - 5) % 15) : ((linelen - 5) %15);
+
+        // two places per items where blanks can be added
+        charslackover /= (avail * 2);    
+
+        double charslackused=0.0;
+        double itemslackused=0.0;
+        elemptr=newelems;
+
+        while ((curelem=elemptr->f)!=0) 
+        {
+               char    *itemp;
+               int     color;
+
+                printg(" | ");
+                addblanks(&charslackused, &charslackover);
+
+               /*
+               ** by default no color is shown for this field (color = 0)
+               **
+               ** the convert-function can set a color-number (color > 0)
+               ** when a specific color is wanted or the convert-function
+               ** can leave the decision to display with a color to the piece
+               ** of code below (color == -1)
+               */
+               color = 0;
+                itemp = curelem->doconvert(sstat, extra, badness, &color);
+
+               if (screen)
+               {
+                       if (color == -1) // default color wanted
+                       {
+                               color = 0;
+
+                               if (badness >= 100)
+                                               color = COLORCRIT;
+                               else if (almostcrit && badness >= almostcrit)
+                                       color = COLORALMOST;
+                       }
+
+                       if (color)      // after all: has a color been set?
+                       {
+                               if (usecolors)
+                                               attron(COLOR_PAIR(color));
+                               else
+                                               attron(A_BOLD);
+                       }
+               }
+
+                printg("%s", itemp);
+
+               if (color && screen)    // color set for this value?
+               {
+                       if (usecolors)
+                                attroff(COLOR_PAIR(color));
+                        else
+                                attroff(A_BOLD);
+               }
+
+                itemslackused+=slackitemsover;
+                while (itemslackused>0.5)
+                {
+                        addblanks(&charslackused, &charslackover);
+                        printg(" | ");
+                        printg("%s", sysprt_BLANKBOX(0, 0, 0, 0));
+                        addblanks(&charslackused, &charslackover);
+                        itemslackused-=1;
+                }
+
+                elemptr++;
+
+                addblanks(&charslackused, &charslackover);
+        }
+
+        printg(" |");
+
+        if (!screen) 
+        {
+                printg("\n");
+        }
+}
+
+/*******************************************************************/
+/* SYSTEM PRINT FUNCTIONS */
+/*******************************************************************/
+char *
+sysprt_PRCSYS(void *notused, void *q, int badness, int *color)
+{
+        extraparam *as=q;
+        static char buf[15]="sys   ";
+        val2cpustr(as->totst * 1000/hertz, buf+6);
+        return buf;
+}
+
+sys_printdef syspdef_PRCSYS = {"PRCSYS", sysprt_PRCSYS};
+/*******************************************************************/
+char *
+sysprt_PRCUSER(void *notused, void *q, int badness, int *color) 
+{
+        extraparam *as=q;
+        static char buf[15]="user  ";
+        val2cpustr(as->totut * 1000/hertz, buf+6);
+        return buf;
+}
+
+sys_printdef syspdef_PRCUSER = {"PRCUSER", sysprt_PRCUSER};
+/*******************************************************************/
+char *
+sysprt_PRCNPROC(void *notused, void *q, int badness, int *color) 
+{
+        extraparam *as=q;
+        static char buf[15]="#proc     ";
+        val2valstr(as->nproc, buf+6, 6, 0, 0);
+        return buf;
+}
+
+sys_printdef syspdef_PRCNPROC = {"PRCNPROC", sysprt_PRCNPROC};
+/*******************************************************************/
+char *
+sysprt_PRCNRUNNING(void *notused, void *q, int badness, int *color) 
+{
+        extraparam *as=q;
+        static char buf[15]="#trun     ";
+        val2valstr(as->ntrun, buf+6, 6, 0, 0);
+        return buf;
+}
+
+sys_printdef syspdef_PRCNRUNNING = {"PRCNRUNNING", sysprt_PRCNRUNNING};
+/*******************************************************************/
+char *
+sysprt_PRCNSLEEPING(void *notused, void *q, int badness, int *color) 
+{
+        extraparam *as=q;
+        static char buf[15]="#tslpi    ";
+        val2valstr(as->ntslpi, buf+8, 4, 0, 0);
+        return buf;
+}
+
+sys_printdef syspdef_PRCNSLEEPING = {"PRCNSLEEPING", sysprt_PRCNSLEEPING};
+/*******************************************************************/
+char *
+sysprt_PRCNDSLEEPING(void *notused, void *q, int badness, int *color) 
+{
+        extraparam *as=q;
+        static char buf[15]="#tslpu    ";
+        val2valstr(as->ntslpu, buf+8, 4, 0, 0);
+        return buf;
+}
+
+sys_printdef syspdef_PRCNDSLEEPING = {"PRCNDSLEEPING", sysprt_PRCNDSLEEPING};
+/*******************************************************************/
+char *
+sysprt_PRCNZOMBIE(void *notused, void *q, int badness, int *color) 
+{
+        extraparam *as=q;
+        static char buf[15]="#zombie   ";
+
+       if (as->nzomb > 30)
+               *color = COLORALMOST;
+
+       if (as->nzomb > 50)
+               *color = COLORCRIT;
+
+        val2valstr(as->nzomb, buf+8, 4, 0, 0);
+        return buf;
+}
+
+sys_printdef syspdef_PRCNZOMBIE = {"PRCNZOMBIE", sysprt_PRCNZOMBIE};
+/*******************************************************************/
+char *
+sysprt_PRCNNEXIT(void *notused, void *q, int badness, int *color) 
+{
+       static char firstcall = 1;
+
+        extraparam *as=q;
+        static char buf[15]="#exit     ";
+
+        if (supportflags & ACCTACTIVE)
+        {
+               if (as->noverflow)
+               {
+                       *color = COLORCRIT;
+                       buf[6] = '>';
+                       val2valstr(as->nexit, buf+7, 5, as->avgval, as->nsecs);
+               }
+               else
+               {
+                       val2valstr(as->nexit, buf+6, 6, as->avgval, as->nsecs);
+               }
+
+                return buf;
+        }
+        else
+        {
+               if (firstcall)
+               {
+                       *color = COLORCRIT;
+                       firstcall = 0;
+               }
+               else
+               {
+                       *color = COLORINFO;
+               }
+
+               switch (acctreason)
+               {
+                  case 1:
+                       return "no  procacct";  // "no  acctread";
+                  case 2:
+                       return "no  procacct";  // "no  acctwant";
+                  case 3:
+                       return "no  procacct";  // "no  acctsema";
+                  case 4:
+                       return "no  procacct";  // "no acctmkdir";
+                  case 5:
+                       return "no  procacct";  // "no rootprivs";
+                  default:
+                       return "no  procacct";
+               }
+        }
+}
+
+sys_printdef syspdef_PRCNNEXIT = {"PRCNNEXIT", sysprt_PRCNNEXIT};
+/*******************************************************************/
+char *
+sysprt_CPUSYS(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[15];
+       float perc      = (sstat->cpu.all.stime * 100.0) / as->percputot;
+
+       if (perc > 1.0)
+               *color = -1;
+
+        sprintf(buf, "sys  %6.0f%%", perc);
+        return buf;
+}
+
+sys_printdef syspdef_CPUSYS = {"CPUSYS", sysprt_CPUSYS};
+/*******************************************************************/
+char *
+sysprt_CPUUSER(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[15];
+       float perc      = (sstat->cpu.all.utime + sstat->cpu.all.ntime)
+                                        * 100.0 / as->percputot;
+
+       if (perc > 1.0)
+               *color = -1;
+
+        sprintf(buf, "user %6.0f%%", perc);
+        return buf;
+}
+
+sys_printdef syspdef_CPUUSER = {"CPUUSER", sysprt_CPUUSER};
+/*******************************************************************/
+char *
+sysprt_CPUIRQ(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[15];
+        float perc = (sstat->cpu.all.Itime + sstat->cpu.all.Stime)
+                                    * 100.0 / as->percputot;
+
+       if (perc > 1.0)
+               *color = -1;
+
+        sprintf(buf, "irq  %6.0f%%", perc);
+        return buf;
+}
+
+sys_printdef syspdef_CPUIRQ = {"CPUIRQ", sysprt_CPUIRQ};
+/*******************************************************************/
+char *
+sysprt_CPUIDLE(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[15];
+        sprintf(buf, "idle %6.0f%%", 
+                (sstat->cpu.all.itime * 100.0) / as->percputot);
+        return buf;
+}
+
+sys_printdef syspdef_CPUIDLE = {"CPUIDLE", sysprt_CPUIDLE};
+/*******************************************************************/
+char *
+sysprt_CPUWAIT(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[15];
+        sprintf(buf, "wait %6.0f%%", 
+                (sstat->cpu.all.wtime * 100.0) / as->percputot);
+        return buf;
+}
+
+sys_printdef syspdef_CPUWAIT = {"CPUWAIT", sysprt_CPUWAIT};
+/*******************************************************************/
+char *
+sysprt_CPUISYS(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[15];
+       float perc      = sstat->cpu.cpu[as->index].stime * 100.0
+                                                       / as->percputot;
+
+       if (perc > 1.0)
+               *color = -1;
+
+        sprintf(buf, "sys  %6.0f%%", perc);
+        return buf;
+}
+
+sys_printdef syspdef_CPUISYS = {"CPUISYS", sysprt_CPUISYS};
+/*******************************************************************/
+char *
+sysprt_CPUIUSER(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[15];
+       float perc      = (sstat->cpu.cpu[as->index].utime +
+                           sstat->cpu.cpu[as->index].ntime) 
+                          * 100.0 / as->percputot;
+
+       if (perc > 1.0)
+               *color = -1;
+
+        sprintf(buf, "user %6.0f%%", perc);
+        return buf;
+}
+
+sys_printdef syspdef_CPUIUSER = {"CPUIUSER", sysprt_CPUIUSER};
+/*******************************************************************/
+char *
+sysprt_CPUIIRQ(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[15];
+       float perc      = (sstat->cpu.cpu[as->index].Itime +
+                          sstat->cpu.cpu[as->index].Stime)
+                          * 100.0 / as->percputot;
+
+       if (perc > 1.0)
+               *color = -1;
+
+        sprintf(buf, "irq  %6.0f%%", perc);
+        return buf;
+}
+
+sys_printdef syspdef_CPUIIRQ = {"CPUIIRQ", sysprt_CPUIIRQ};
+/*******************************************************************/
+char *
+sysprt_CPUIIDLE(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[15];
+        sprintf(buf, "idle %6.0f%%", 
+                (sstat->cpu.cpu[as->index].itime * 100.0) / as->percputot);
+        return buf;
+}
+
+sys_printdef syspdef_CPUIIDLE = {"CPUIIDLE", sysprt_CPUIIDLE};
+/*******************************************************************/
+char *
+sysprt_CPUIWAIT(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[15];
+        sprintf(buf, "cpu%03d w%3.0f%%", 
+                sstat->cpu.cpu[as->index].cpunr,
+                (sstat->cpu.cpu[as->index].wtime * 100.0) / as->percputot);
+        return buf;
+}
+
+sys_printdef syspdef_CPUIWAIT = {"CPUIWAIT", sysprt_CPUIWAIT};
+/*******************************************************************/
+void dofmt_cpufreq(char *buf, count_t maxfreq, count_t cnt, count_t ticks)
+{
+        // if ticks != 0, do full output
+        if (ticks) 
+        {
+            count_t curfreq    = cnt/ticks;
+            strcpy(buf, "avgf ");
+            val2Hzstr(curfreq, buf+5);
+        } 
+        else if (cnt)       // no max, no %.  if freq is known: print it
+        {
+            strcpy(buf, "curf ");
+            val2Hzstr(cnt, buf+5);
+        }
+        else                // nothing is known: print ?????
+        {
+            strcpy(buf, "curf    ?MHz");
+        }
+}
+
+
+/*
+ * sumscaling: sum scaling info for all processors
+ *
+ */
+void sumscaling(struct sstat *sstat, count_t *maxfreq,
+                               count_t *cnt, count_t *ticks)
+{
+        count_t mymaxfreq = 0;
+        count_t mycnt     = 0;
+        count_t myticks   = 0;
+
+        int n=sstat->cpu.nrcpu;
+        int i;
+
+        for (i=0; i < n; ++i)
+        {
+                mymaxfreq+= sstat->cpu.cpu[i].freqcnt.maxfreq;
+                mycnt    += sstat->cpu.cpu[i].freqcnt.cnt;
+                myticks  += sstat->cpu.cpu[i].freqcnt.ticks;
+        }
+        *maxfreq= mymaxfreq;
+        *cnt    = mycnt;
+        *ticks  = myticks;
+}
+
+
+void dofmt_cpuscale(char *buf, count_t maxfreq, count_t cnt, count_t ticks)
+{
+       if (ticks) 
+       {
+               count_t curfreq = cnt/ticks;
+               int     perc = maxfreq ? 100 * curfreq / maxfreq : 0;
+
+               strcpy(buf, "avgscal ");
+               sprintf(buf+7, "%4d%%", perc);
+        } 
+        else if (maxfreq)   // max frequency is known so % can be calculated
+        {
+               strcpy(buf, "curscal ");
+               sprintf(buf+7, "%4lld%%", 100 * cnt / maxfreq);
+        }
+       else    // nothing is known: print ?????
+       {
+               strcpy(buf, "curscal   ?%");
+       }
+}
+
+/*******************************************************************/
+char *
+sysprt_CPUIFREQ(void *p, void *q, int badness, int *color) 
+{
+
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[15];
+
+        count_t maxfreq        = sstat->cpu.cpu[as->index].freqcnt.maxfreq;
+        count_t cnt    = sstat->cpu.cpu[as->index].freqcnt.cnt;
+        count_t ticks  = sstat->cpu.cpu[as->index].freqcnt.ticks;
+
+        dofmt_cpufreq(buf, maxfreq, cnt, ticks);
+        return buf;
+}
+
+sys_printdef syspdef_CPUIFREQ = {"CPUIFREQ", sysprt_CPUIFREQ};
+/*******************************************************************/
+char *
+sysprt_CPUFREQ(void *p, void *q, int badness, int *color) 
+{
+
+        struct sstat *sstat=p;
+        static char buf[15];
+
+        count_t maxfreq;
+        count_t cnt;
+        count_t ticks;
+        int     n = sstat->cpu.nrcpu;
+
+        sumscaling(sstat, &maxfreq, &cnt, &ticks);
+        dofmt_cpufreq(buf, maxfreq/n, cnt/n, ticks/n);
+        return buf;
+}
+
+sys_printdef syspdef_CPUFREQ = {"CPUFREQ", sysprt_CPUFREQ};
+/*******************************************************************/
+char *
+sysprt_CPUISCALE(void *p, void *q, int badness, int *color) 
+{
+
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[15];
+
+        count_t maxfreq = sstat->cpu.cpu[as->index].freqcnt.maxfreq;
+        count_t cnt     = sstat->cpu.cpu[as->index].freqcnt.cnt;
+        count_t ticks   = sstat->cpu.cpu[as->index].freqcnt.ticks;
+
+        dofmt_cpuscale(buf, maxfreq, cnt, ticks);
+        return buf;
+}
+
+sys_printdef syspdef_CPUISCALE = {"CPUISCALE", sysprt_CPUISCALE};
+/*******************************************************************/
+char *
+sysprt_CPUSCALE(void *p, void *q, int badness, int *color) 
+{
+
+        struct sstat *sstat=p;
+        static char buf[15];
+
+        count_t maxfreq;
+        count_t cnt;
+        count_t ticks;
+        int     n = sstat->cpu.nrcpu;
+
+        sumscaling(sstat, &maxfreq, &cnt, &ticks);
+        dofmt_cpuscale(buf, maxfreq/n, cnt/n, ticks/n);
+        return buf;
+}
+
+sys_printdef syspdef_CPUSCALE = {"CPUSCALE", sysprt_CPUSCALE};
+/*******************************************************************/
+char *
+sysprt_CPUSTEAL(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[15];
+       float perc      = sstat->cpu.all.steal * 100.0 / as->percputot;
+
+       if (perc > 1.0)
+               *color = -1;
+
+        sprintf(buf, "steal %5.0f%%", perc);
+        return buf;
+}
+
+sys_printdef syspdef_CPUSTEAL = {"CPUSTEAL", sysprt_CPUSTEAL};
+/*******************************************************************/
+char *
+sysprt_CPUISTEAL(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[15];
+       float perc      = sstat->cpu.cpu[as->index].steal * 100.0
+                                                       / as->percputot;
+
+       if (perc > 1.0)
+               *color = -1;
+
+        sprintf(buf, "steal %5.0f%%", perc);
+        return buf;
+}
+
+sys_printdef syspdef_CPUISTEAL = {"CPUISTEAL", sysprt_CPUISTEAL};
+/*******************************************************************/
+char *
+sysprt_CPUGUEST(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[15];
+        float perc = sstat->cpu.all.guest * 100.0 / as->percputot;
+
+       if (perc > 1.0)
+               *color = -1;
+
+        sprintf(buf, "guest %5.0f%%", perc);
+        return buf;
+}
+
+sys_printdef syspdef_CPUGUEST = {"CPUGUEST", sysprt_CPUGUEST};
+/*******************************************************************/
+char *
+sysprt_CPUIGUEST(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[15];
+        float perc = sstat->cpu.cpu[as->index].guest * 100.0 / as->percputot;
+
+       if (perc > 1.0)
+               *color = -1;
+
+        sprintf(buf, "guest %5.0f%%", perc);
+        return buf;
+}
+
+sys_printdef syspdef_CPUIGUEST = {"CPUIGUEST", sysprt_CPUIGUEST};
+/*******************************************************************/
+char *
+sysprt_CPLAVG1(void *p, void *notused, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        static char buf[15]="avg1 ";
+
+        if (sstat->cpu.lavg1  > 999.0)
+        {
+                sprintf(buf+5, "%7.0f", sstat->cpu.lavg1);
+        }
+        else
+        {
+                sprintf(buf+5, "%7.2f", sstat->cpu.lavg1);
+        }
+        return buf;
+}
+        
+sys_printdef syspdef_CPLAVG1 = {"CPLAVG1", sysprt_CPLAVG1};
+/*******************************************************************/
+char *
+sysprt_CPLAVG5(void *p, void *notused, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        static char buf[15]="avg5 ";
+
+        if (sstat->cpu.lavg5  > 999.0)
+        {
+                sprintf(buf+5, "%7.0f", sstat->cpu.lavg5);
+        }
+        else
+        {
+                sprintf(buf+5, "%7.2f", sstat->cpu.lavg5);
+        }
+        return buf;
+}
+        
+sys_printdef syspdef_CPLAVG5 = {"CPLAVG5", sysprt_CPLAVG5};
+/*******************************************************************/
+char *
+sysprt_CPLAVG15(void *p, void *notused, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        static char buf[15]="avg15 ";
+
+       if (sstat->cpu.lavg15 > (2 * sstat->cpu.nrcpu) )
+               *color = COLORALMOST;
+
+        if (sstat->cpu.lavg15  > 999.0)
+        {
+                sprintf(buf+6, "%6.0f", sstat->cpu.lavg15);
+        }
+        else
+        {
+                sprintf(buf+6, "%6.2f", sstat->cpu.lavg15);
+        }
+        return buf;
+}
+        
+sys_printdef syspdef_CPLAVG15 = {"CPLAVG15", sysprt_CPLAVG15};
+/*******************************************************************/
+char *
+sysprt_CPLCSW(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="csw    ";
+
+        val2valstr(sstat->cpu.csw, buf+4   , 8,as->avgval,as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_CPLCSW = {"CPLCSW", sysprt_CPLCSW};
+/*******************************************************************/
+char *
+sysprt_PRCCLONES(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="clones ";
+
+        val2valstr(sstat->cpu.nprocs, buf+7   , 5,as->avgval,as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_PRCCLONES = {"PRCCLONES", sysprt_PRCCLONES};
+/*******************************************************************/
+char *
+sysprt_CPLNUMCPU(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="numcpu ";
+
+        val2valstr(sstat->cpu.nrcpu, buf+7   , 5,0,as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_CPLNUMCPU = {"CPLNUMCPU", sysprt_CPLNUMCPU};
+/*******************************************************************/
+char *
+sysprt_CPLINTR(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="intr   ";
+
+        val2valstr(sstat->cpu.devint, buf+5   , 7,as->avgval,as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_CPLINTR = {"CPLINTR", sysprt_CPLINTR};
+/*******************************************************************/
+char *
+sysprt_MEMTOT(void *p, void *notused, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        static char buf[16]="tot   ";
+       *color = -1;
+        val2memstr(sstat->mem.physmem   * pagesize, buf+6, MBFORMAT, 0, 0);
+        return buf;
+}
+
+sys_printdef syspdef_MEMTOT = {"MEMTOT", sysprt_MEMTOT};
+/*******************************************************************/
+char *
+sysprt_MEMFREE(void *p, void *notused, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        static char buf[16]="free  ";
+       *color = -1;
+        val2memstr(sstat->mem.freemem   * pagesize, buf+6, MBFORMAT, 0, 0);
+        return buf;
+}
+
+sys_printdef syspdef_MEMFREE = {"MEMFREE", sysprt_MEMFREE};
+/*******************************************************************/
+char *
+sysprt_MEMCACHE(void *p, void *notused, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        static char buf[16]="cache ";
+       *color = -1;
+        val2memstr(sstat->mem.cachemem   * pagesize, buf+6, MBFORMAT, 0, 0);
+        return buf;
+}
+
+sys_printdef syspdef_MEMCACHE = {"MEMCACHE", sysprt_MEMCACHE};
+/*******************************************************************/
+char *
+sysprt_MEMDIRTY(void *p, void *notused, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        static char buf[16] = "dirty ";
+        val2memstr(sstat->mem.cachedrt   * pagesize, buf+6, MBFORMAT, 0, 0);
+
+        return buf;
+}
+
+sys_printdef syspdef_MEMDIRTY = {"MEMDIRTY", sysprt_MEMDIRTY};
+/*******************************************************************/
+char *
+sysprt_MEMBUFFER(void *p, void *notused, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        static char buf[16]="buff  ";
+       *color = -1;
+        val2memstr(sstat->mem.buffermem   * pagesize, buf+6, MBFORMAT, 0, 0);
+        return buf;
+}
+
+sys_printdef syspdef_MEMBUFFER = {"MEMBUFFER", sysprt_MEMBUFFER};
+/*******************************************************************/
+char *
+sysprt_MEMSLAB(void *p, void *notused, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        static char buf[16]="slab  ";
+       *color = -1;
+        val2memstr(sstat->mem.slabmem   * pagesize, buf+6, MBFORMAT, 0, 0);
+        return buf;
+}
+
+sys_printdef syspdef_MEMSLAB = {"MEMSLAB", sysprt_MEMSLAB};
+/*******************************************************************/
+char *
+sysprt_RECSLAB(void *p, void *notused, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        static char buf[16]="slrec ";
+       *color = -1;
+        val2memstr(sstat->mem.slabreclaim * pagesize, buf+6, MBFORMAT, 0, 0);
+        return buf;
+}
+
+sys_printdef syspdef_RECSLAB = {"RECSLAB", sysprt_RECSLAB};
+/*******************************************************************/
+char *
+sysprt_SHMEM(void *p, void *notused, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        static char buf[16]="shmem  ";
+       *color = -1;
+        val2memstr(sstat->mem.shmem * pagesize, buf+6, MBFORMAT, 0, 0);
+        return buf;
+}
+
+sys_printdef syspdef_SHMEM = {"SHMEM", sysprt_SHMEM};
+/*******************************************************************/
+char *
+sysprt_SHMRSS(void *p, void *notused, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        static char buf[16]="shrss  ";
+       *color = -1;
+        val2memstr(sstat->mem.shmrss * pagesize, buf+6, MBFORMAT, 0, 0);
+        return buf;
+}
+
+sys_printdef syspdef_SHMRSS = {"SHMRSS", sysprt_SHMRSS};
+/*******************************************************************/
+char *
+sysprt_SHMSWP(void *p, void *notused, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        static char buf[16]="shswp  ";
+       *color = -1;
+        val2memstr(sstat->mem.shmswp * pagesize, buf+6, MBFORMAT, 0, 0);
+        return buf;
+}
+
+sys_printdef syspdef_SHMSWP = {"SHMSWP", sysprt_SHMSWP};
+/*******************************************************************/
+char *
+sysprt_HUPTOT(void *p, void *notused, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        static char buf[16]="hptot  ";
+       *color = -1;
+        val2memstr(sstat->mem.tothugepage * sstat->mem.hugepagesz,
+                                               buf+6, MBFORMAT, 0, 0);
+        return buf;
+}
+
+sys_printdef syspdef_HUPTOT = {"HUPTOT", sysprt_HUPTOT};
+/*******************************************************************/
+char *
+sysprt_HUPUSE(void *p, void *notused, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        static char buf[16]="hpuse  ";
+       *color = -1;
+        val2memstr( (sstat->mem.tothugepage - sstat->mem.freehugepage) *
+                               sstat->mem.hugepagesz, buf+6, MBFORMAT, 0, 0);
+        return buf;
+}
+
+sys_printdef syspdef_HUPUSE = {"HUPUSE", sysprt_HUPUSE};
+/*******************************************************************/
+char *
+sysprt_VMWBAL(void *p, void *notused, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        static char buf[16]="vmbal  ";
+       *color = -1;
+        val2memstr(sstat->mem.vmwballoon * pagesize, buf+6, MBFORMAT, 0, 0);
+        return buf;
+}
+
+sys_printdef syspdef_VMWBAL = {"VMWBAL", sysprt_VMWBAL};
+/*******************************************************************/
+char *
+sysprt_SWPTOT(void *p, void *notused, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        static char buf[16]="tot    ";
+       *color = -1;
+        val2memstr(sstat->mem.totswap   * pagesize, buf+6, MBFORMAT, 0, 0);
+        return buf;
+}
+
+sys_printdef syspdef_SWPTOT = {"SWPTOT", sysprt_SWPTOT};
+/*******************************************************************/
+char *
+sysprt_SWPFREE(void *p, void *notused, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        static char buf[16]="free  ";
+       *color = -1;
+        val2memstr(sstat->mem.freeswap   * pagesize, buf+6, MBFORMAT, 0, 0);
+        return buf;
+}
+
+sys_printdef syspdef_SWPFREE = {"SWPFREE", sysprt_SWPFREE};
+/*******************************************************************/
+char *
+sysprt_SWPCOMMITTED(void *p, void *notused, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        static char buf[16]="vmcom  ";
+        val2memstr(sstat->mem.committed   * pagesize, buf+6, MBFORMAT, 0, 0);
+
+       if (sstat->mem.commitlim && sstat->mem.committed > sstat->mem.commitlim)
+               *color = COLORALMOST;
+
+        return buf;
+}
+
+sys_printdef syspdef_SWPCOMMITTED = {"SWPCOMMITTED", sysprt_SWPCOMMITTED};
+/*******************************************************************/
+char *
+sysprt_SWPCOMMITLIM(void *p, void *notused, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        static char buf[16]="vmlim  ";
+        val2memstr(sstat->mem.commitlim   * pagesize, buf+6, MBFORMAT, 0, 0);
+
+       if (sstat->mem.commitlim && sstat->mem.committed > sstat->mem.commitlim)
+               *color = COLORINFO;
+
+        return buf;
+}
+
+sys_printdef syspdef_SWPCOMMITLIM = {"SWPCOMMITLIM", sysprt_SWPCOMMITLIM};
+/*******************************************************************/
+char *
+sysprt_PAGSCAN(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="scan  ";
+        val2valstr(sstat->mem.pgscans, buf+5, 7, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_PAGSCAN = {"PAGSCAN", sysprt_PAGSCAN};
+/*******************************************************************/
+char *
+sysprt_PAGSTEAL(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="steal  ";
+        val2valstr(sstat->mem.pgsteal, buf+ 6, 6, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_PAGSTEAL = {"PAGSTEAL", sysprt_PAGSTEAL};
+/*******************************************************************/
+char *
+sysprt_PAGSTALL(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="stall  ";
+        val2valstr(sstat->mem.allocstall, buf+6, 6, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_PAGSTALL = {"PAGSTALL", sysprt_PAGSTALL};
+/*******************************************************************/
+char *
+sysprt_PAGSWIN(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="swin   ";
+        val2valstr(sstat->mem.swins, buf+5, 7, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_PAGSWIN = {"PAGSWIN", sysprt_PAGSWIN};
+/*******************************************************************/
+char *
+sysprt_PAGSWOUT(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="swout  ";
+       *color = -1;
+        val2valstr(sstat->mem.swouts, buf+6, 6, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_PAGSWOUT = {"PAGSWOUT", sysprt_PAGSWOUT};
+/*******************************************************************/
+char *
+sysprt_CONTNAME(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam     *as=q;
+        static char    buf[32] = "ctid ";
+
+       *color = -1;
+
+        sprintf(buf+5, "%7lu", sstat->cfs.cont[as->index].ctid);
+        return buf;
+}
+
+sys_printdef syspdef_CONTNAME = {"CONTNAME", sysprt_CONTNAME};
+/*******************************************************************/
+char *
+sysprt_CONTNPROC(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam      *as=q;
+        static char buf[16]="nproc  ";
+
+       *color = -1;
+
+        val2valstr(sstat->cfs.cont[as->index].numproc, 
+                         buf+6, 6, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_CONTNPROC = {"CONTNPROC", sysprt_CONTNPROC};
+/*******************************************************************/
+char *
+sysprt_CONTCPU(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam      *as=q;
+        static char buf[16];
+       float  perc;
+
+       count_t used = sstat->cfs.cont[as->index].system + 
+                       sstat->cfs.cont[as->index].user + 
+                       sstat->cfs.cont[as->index].nice;
+
+       *color = -1;
+
+       if (sstat->cfs.cont[as->index].uptime)
+       {
+               perc = used * 100.0 / sstat->cfs.cont[as->index].uptime;
+               sprintf(buf, "cpubusy %3.0f%%", perc);
+       }
+       else
+               sprintf(buf, "cpubusy   ?%%");
+
+        return buf;
+}
+
+sys_printdef syspdef_CONTCPU = {"CONTCPU", sysprt_CONTCPU};
+/*******************************************************************/
+char *
+sysprt_CONTMEM(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam      *as=q;
+        static char buf[16]="mem   ";
+
+       *color = -1;
+
+        val2memstr(sstat->cfs.cont[as->index].physpages * pagesize,
+                                               buf+6, MBFORMAT, 0, 0);
+        return buf;
+}
+
+sys_printdef syspdef_CONTMEM = {"CONTMEM", sysprt_CONTMEM};
+/*******************************************************************/
+char *
+sysprt_DSKNAME(void *p, void *q, int badness, int *color) 
+{
+        extraparam     *as=q;
+        static char    buf[16];
+       char            *pn;
+       int             len;
+
+       *color = -1;
+
+        if ( (len = strlen(as->perdsk[as->index].name)) > 12)
+               pn = as->perdsk[as->index].name + len - 12;
+       else
+               pn = as->perdsk[as->index].name;
+
+        sprintf(buf, "%12.12s", pn);
+        return buf;
+}
+
+sys_printdef syspdef_DSKNAME = {"DSKNAME", sysprt_DSKNAME};
+/*******************************************************************/
+char *
+sysprt_DSKBUSY(void *p, void *q, int badness, int *color) 
+{
+        extraparam     *as=q;
+       double          perc;
+        static char    buf[16]="busy  ";
+
+       *color = -1;
+
+       perc = as->perdsk[as->index].io_ms * 100.0 / as->mstot;
+
+       if (perc >= 0.0 && perc < 1000000.0)
+               sprintf(buf+5, "%6.0lf%%", perc);
+       else
+               sprintf(buf+5, "%6.0lf%%", 999999.0);
+
+        return buf;
+}
+
+sys_printdef syspdef_DSKBUSY = {"DSKBUSY", sysprt_DSKBUSY};
+/*******************************************************************/
+char *
+sysprt_DSKNREAD(void *p, void *q, int badness, int *color) 
+{
+        extraparam     *as=q;
+        static char    buf[16]="read  ";
+
+       *color = -1;
+
+        val2valstr(as->perdsk[as->index].nread >= 0 ?
+                       as->perdsk[as->index].nread : 0,  
+                       buf+5, 7, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_DSKNREAD = {"DSKNREAD", sysprt_DSKNREAD};
+/*******************************************************************/
+char *
+sysprt_DSKNWRITE(void *p, void *q, int badness, int *color) 
+{
+        extraparam     *as=q;
+        static char    buf[16]="write ";
+
+       *color = -1;
+
+        val2valstr(as->perdsk[as->index].nwrite >= 0 ?
+                       as->perdsk[as->index].nwrite : 0,  
+                       buf+6, 6, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_DSKNWRITE = {"DSKNWRITE", sysprt_DSKNWRITE};
+/*******************************************************************/
+char *
+sysprt_DSKKBPERWR(void *p, void *q, int badness, int *color) 
+{
+        extraparam     *as=q;
+        static char    buf[16]="KiB/w ";
+       struct perdsk   *dp = &(as->perdsk[as->index]);
+
+        val2valstr(dp->nwrite > 0 ?  dp->nwsect / dp->nwrite / 2 : 0,
+                   buf+6, 6, 0, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_DSKKBPERWR = {"DSKKBPERWR", sysprt_DSKKBPERWR};
+/*******************************************************************/
+char *
+sysprt_DSKKBPERRD(void *p, void *q, int badness, int *color) 
+{
+        extraparam     *as=q;
+        static char    buf[16]="KiB/r ";
+       struct perdsk   *dp = &(as->perdsk[as->index]);
+
+        val2valstr(dp->nread > 0 ?  dp->nrsect / dp->nread / 2 : 0,
+                   buf+6, 6, 0, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_DSKKBPERRD = {"DSKKBPERRD", sysprt_DSKKBPERRD};
+/*******************************************************************/
+char *
+sysprt_DSKMBPERSECWR(void *p, void *q, int badness, int *color) 
+{
+        extraparam     *as=q;
+        static char    buf[16]="MBw/s ";
+       struct perdsk   *dp = &(as->perdsk[as->index]);
+
+        snprintf(buf+6, sizeof buf-6, "%6.1lf",
+                               dp->nwsect / 2.0 / 1024 / as->nsecs);
+
+        return buf;
+}
+
+sys_printdef syspdef_DSKMBPERSECWR = {"DSKMBPERSECWR", sysprt_DSKMBPERSECWR};
+/*******************************************************************/
+char *
+sysprt_DSKMBPERSECRD(void *p, void *q, int badness, int *color) 
+{
+        extraparam     *as=q;
+        static char    buf[16]="MBr/s ";
+       struct perdsk   *dp = &(as->perdsk[as->index]);
+
+        snprintf(buf+6, sizeof buf-6, "%6.1lf",
+                               dp->nrsect / 2.0 / 1024 / as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_DSKMBPERSECRD = {"DSKMBPERSECRD", sysprt_DSKMBPERSECRD};
+/*******************************************************************/
+char *
+sysprt_DSKAVQUEUE(void *p, void *q, int badness, int *color) 
+{
+        extraparam     *as=q;
+        static char    buf[16]="avq  ";
+       struct perdsk   *dp = &(as->perdsk[as->index]);
+
+       sprintf(buf+4, "%8.2f", dp->io_ms > 0 ?
+                                (double)dp->avque / dp->io_ms : 0.0);
+        return buf;
+}
+
+sys_printdef syspdef_DSKAVQUEUE = {"DSKAVQUEUE", sysprt_DSKAVQUEUE};
+/*******************************************************************/
+char *
+sysprt_DSKAVIO(void *p, void *q, int badness, int *color) 
+{
+        extraparam     *as=q;
+        static char    buf[16]="avio  ";
+        double                 tim = as->iotot > 0 ? 
+                       (double)(as->perdsk[as->index].io_ms)/as->iotot:0.0;
+
+       *color = -1;
+
+        if (tim > 100.0)
+        {
+                sprintf(buf+5, "%4.0lf ms", tim);
+        } 
+        else if (tim > 10.0) 
+        {
+                sprintf(buf+5, "%4.1lf ms", tim);
+        }
+        else 
+        {
+                sprintf(buf+5, "%4.2lf ms", tim);
+        }
+
+        return buf;
+}
+
+sys_printdef syspdef_DSKAVIO = {"DSKAVIO", sysprt_DSKAVIO};
+/*******************************************************************/
+char *
+sysprt_NETTRANSPORT(void *p, void *notused, int badness, int *color) 
+{
+        return "transport   ";
+}
+
+sys_printdef syspdef_NETTRANSPORT = {"NETTRANSPORT", sysprt_NETTRANSPORT};
+/*******************************************************************/
+char *
+sysprt_NETTCPI(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="tcpi   ";
+        val2valstr(sstat->net.tcp.InSegs,  buf+5, 7, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NETTCPI = {"NETTCPI", sysprt_NETTCPI};
+/*******************************************************************/
+char *
+sysprt_NETTCPO(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="tcpo   ";
+        val2valstr(sstat->net.tcp.OutSegs,  buf+5, 7, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NETTCPO = {"NETTCPO", sysprt_NETTCPO};
+/*******************************************************************/
+char *
+sysprt_NETTCPACTOPEN(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="tcpao  ";
+        val2valstr(sstat->net.tcp.ActiveOpens,  buf+6, 6, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NETTCPACTOPEN = {"NETTCPACTOPEN", sysprt_NETTCPACTOPEN};
+/*******************************************************************/
+char *
+sysprt_NETTCPPASVOPEN(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="tcppo  ";
+        val2valstr(sstat->net.tcp.PassiveOpens, buf+6, 6, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NETTCPPASVOPEN = {"NETTCPPASVOPEN", sysprt_NETTCPPASVOPEN};
+/*******************************************************************/
+char *
+sysprt_NETTCPRETRANS(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="tcprs  ";
+        val2valstr(sstat->net.tcp.RetransSegs,  buf+6, 6, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NETTCPRETRANS = {"NETTCPRETRANS", sysprt_NETTCPRETRANS};
+/*******************************************************************/
+char *
+sysprt_NETTCPINERR(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="tcpie  ";
+        val2valstr(sstat->net.tcp.InErrs,  buf+6, 6, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NETTCPINERR = {"NETTCPINERR", sysprt_NETTCPINERR};
+/*******************************************************************/
+char *
+sysprt_NETTCPORESET(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="tcpor  ";
+        val2valstr(sstat->net.tcp.OutRsts,  buf+6, 6, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NETTCPORESET = {"NETTCPORESET", sysprt_NETTCPORESET};
+/*******************************************************************/
+char *
+sysprt_NETUDPNOPORT(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="udpnp  ";
+        val2valstr(sstat->net.udpv4.NoPorts,  buf+6, 6, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NETUDPNOPORT = {"NETUDPNOPORT", sysprt_NETUDPNOPORT};
+/*******************************************************************/
+char *
+sysprt_NETUDPINERR(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="udpie  ";
+        val2valstr(sstat->net.udpv4.InErrors,  buf+6, 6, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NETUDPINERR = {"NETUDPINERR", sysprt_NETUDPINERR};
+/*******************************************************************/
+char *
+sysprt_NETUDPI(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="udpi   ";
+        count_t udpin  = sstat->net.udpv4.InDatagrams  +
+                        sstat->net.udpv6.Udp6InDatagrams;
+        val2valstr(udpin,   buf+5, 7, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NETUDPI = {"NETUDPI", sysprt_NETUDPI};
+/*******************************************************************/
+char *
+sysprt_NETUDPO(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="udpo   ";
+        count_t udpout = sstat->net.udpv4.OutDatagrams +
+                        sstat->net.udpv6.Udp6OutDatagrams;
+        val2valstr(udpout,   buf+5, 7, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NETUDPO = {"NETUDPO", sysprt_NETUDPO};
+/*******************************************************************/
+char *
+sysprt_NETNETWORK(void *p, void *notused, int badness, int *color) 
+{
+        return "network     ";
+}
+
+sys_printdef syspdef_NETNETWORK = {"NETNETWORK", sysprt_NETNETWORK};
+/*******************************************************************/
+char *
+sysprt_NETIPI(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="ipi    ";
+        count_t ipin    = sstat->net.ipv4.InReceives  +
+                        sstat->net.ipv6.Ip6InReceives;
+        val2valstr(ipin, buf+4, 8, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NETIPI = {"NETIPI", sysprt_NETIPI};
+/*******************************************************************/
+char *
+sysprt_NETIPO(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="ipo    ";
+        count_t ipout   = sstat->net.ipv4.OutRequests +
+                        sstat->net.ipv6.Ip6OutRequests;
+        val2valstr(ipout, buf+4, 8, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NETIPO = {"NETIPO", sysprt_NETIPO};
+/*******************************************************************/
+char *
+sysprt_NETIPFRW(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="ipfrw  ";
+        count_t ipfrw   = sstat->net.ipv4.ForwDatagrams +
+                        sstat->net.ipv6.Ip6OutForwDatagrams;
+        val2valstr(ipfrw, buf+6, 6, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NETIPFRW = {"NETIPFRW", sysprt_NETIPFRW};
+/*******************************************************************/
+char *
+sysprt_NETIPDELIV(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="deliv  ";
+        count_t ipindel = sstat->net.ipv4.InDelivers +
+                        sstat->net.ipv6.Ip6InDelivers;
+        val2valstr(ipindel, buf+6, 6, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NETIPDELIV = {"NETIPDELIV", sysprt_NETIPDELIV};
+/*******************************************************************/
+char *
+sysprt_NETICMPIN(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="icmpi  ";
+        count_t icmpin = sstat->net.icmpv4.InMsgs+
+                        sstat->net.icmpv6.Icmp6InMsgs;
+        val2valstr(icmpin , buf+6, 6, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NETICMPIN = {"NETICMPIN", sysprt_NETICMPIN};
+/*******************************************************************/
+char *
+sysprt_NETICMPOUT(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="icmpo  ";
+        count_t icmpin = sstat->net.icmpv4.OutMsgs+
+                        sstat->net.icmpv6.Icmp6OutMsgs;
+        val2valstr(icmpin , buf+6, 6, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NETICMPOUT = {"NETICMPOUT", sysprt_NETICMPOUT};
+/*******************************************************************/
+char *
+sysprt_NETNAME(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        count_t busy;
+        count_t ival = sstat->intf.intf[as->index].rbyte/125/as->nsecs;
+        count_t oval = sstat->intf.intf[as->index].sbyte/125/as->nsecs;
+
+        static char buf[16] = "ethxxxx ----";
+                      //       012345678901
+
+       *color = -1;
+
+        if (sstat->intf.intf[as->index].speed)  /* speed known? */
+        {
+                if (sstat->intf.intf[as->index].duplex)
+                        busy = (ival > oval ? ival : oval) /
+                               (sstat->intf.intf[as->index].speed *10);
+                else
+                        busy = (ival + oval) /
+                               (sstat->intf.intf[as->index].speed *10);
+
+               // especially with wireless, the speed might have dropped
+               // temporarily to a very low value (snapshot)
+               // then it might be better to take the speed of the previous
+               // sample
+               if (busy > 100 && sstat->intf.intf[as->index].speed <
+                                       sstat->intf.intf[as->index].speedp)
+               {
+                       sstat->intf.intf[as->index].speed =
+                               sstat->intf.intf[as->index].speedp;
+
+                       if (sstat->intf.intf[as->index].duplex)
+                               busy = (ival > oval ? ival : oval) /
+                                               (sstat->intf.intf[as->index].speed *10);
+                       else
+                               busy = (ival + oval) /
+                                               (sstat->intf.intf[as->index].speed *10);
+               }
+
+               snprintf(buf, sizeof(buf)-1, "%-7.7s %3lld%%", 
+                                 sstat->intf.intf[as->index].name, busy);
+
+        } 
+        else 
+        {
+                snprintf(buf, sizeof(buf)-1, "%-7.7s ----",
+                               sstat->intf.intf[as->index].name);
+                strcpy(buf+8, "----");
+        } 
+        return buf;
+}
+
+sys_printdef syspdef_NETNAME = {"NETNAME", sysprt_NETNAME};
+/*******************************************************************/
+char *
+sysprt_NETPCKI(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="pcki  ";
+
+       *color = -1;
+
+        val2valstr(sstat->intf.intf[as->index].rpack, 
+                   buf+5, 7, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NETPCKI = {"NETPCKI", sysprt_NETPCKI};
+/*******************************************************************/
+char *
+sysprt_NETPCKO(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="pcko  ";
+
+       *color = -1;
+
+        val2valstr(sstat->intf.intf[as->index].spack, 
+                   buf+5, 7, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NETPCKO = {"NETPCKO", sysprt_NETPCKO};
+/*******************************************************************/
+/*
+** convert byte-transfers to bit-transfers     (*    8)
+** convert bit-transfers  to kilobit-transfers (/ 1000)
+** per second
+*/
+char *makenetspeed(count_t val, int nsecs)
+{
+        char           c;
+        static char    buf[16]="si      ?bps";
+                              // 012345678901
+
+        val=val/125/nsecs;     // convert to Kbps
+
+        if (val < 10000)
+        {
+                c='K';
+        } 
+        else if (val < (count_t)10000 * 1000)
+        {
+                val/=1000;
+                c = 'M';
+        } 
+        else if (val < (count_t)10000 * 1000 * 1000)
+        {
+                val/=1000 * 1000;
+                c = 'G';
+        } 
+        else 
+        {
+                val = val / 1000 / 1000 / 1000;
+                c = 'T';
+        }
+
+        sprintf(buf+3, "%4lld %cbps", val, c);
+
+        return buf;
+}
+/*******************************************************************/
+char *
+sysprt_NETSPEEDMAX(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat = p;
+        extraparam *as = q;
+        static char buf[16];
+        count_t speed = sstat->intf.intf[as->index].speed;
+
+       *color = -1;
+
+       if (speed < 10000)
+       {
+               snprintf(buf, sizeof buf, "sp %4lld Mbps", speed);
+       }
+       else
+       {
+               speed /= 1000;
+               snprintf(buf, sizeof buf, "sp %4lld Gbps", speed);
+       }
+
+        return buf;
+}
+
+sys_printdef syspdef_NETSPEEDMAX = {"NETSPEEDMAX", sysprt_NETSPEEDMAX};
+/*******************************************************************/
+char *
+sysprt_NETSPEEDIN(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+
+       *color = -1;
+
+        char *ps=makenetspeed(sstat->intf.intf[as->index].rbyte,as->nsecs);
+        ps[0]='s';
+        ps[1]='i';
+        return ps;
+}
+
+sys_printdef syspdef_NETSPEEDIN = {"NETSPEEDIN", sysprt_NETSPEEDIN};
+/*******************************************************************/
+char *
+sysprt_NETSPEEDOUT(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+
+       *color = -1;
+
+        char *ps=makenetspeed(sstat->intf.intf[as->index].sbyte,as->nsecs);
+        ps[0]='s';
+        ps[1]='o';
+        return ps;
+}
+
+sys_printdef syspdef_NETSPEEDOUT = {"NETSPEEDOUT", sysprt_NETSPEEDOUT};
+/*******************************************************************/
+char *
+sysprt_NETCOLLIS(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="coll  ";
+        val2valstr(sstat->intf.intf[as->index].scollis, 
+                   buf+5, 7, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NETCOLLIS = {"NETCOLLIS", sysprt_NETCOLLIS};
+/*******************************************************************/
+char *
+sysprt_NETMULTICASTIN(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="mlti ";
+        val2valstr(sstat->intf.intf[as->index].rmultic, 
+                   buf+5, 7, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NETMULTICASTIN = {"NETMULTICASTIN", sysprt_NETMULTICASTIN};
+/*******************************************************************/
+char *
+sysprt_NETRCVERR(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="erri   ";
+        val2valstr(sstat->intf.intf[as->index].rerrs, 
+                   buf+5, 7, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NETRCVERR = {"NETRCVERR", sysprt_NETRCVERR};
+/*******************************************************************/
+char *
+sysprt_NETSNDERR(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="erro   ";
+        val2valstr(sstat->intf.intf[as->index].serrs, 
+                   buf+5, 7, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NETSNDERR = {"NETSNDERR", sysprt_NETSNDERR};
+/*******************************************************************/
+char *
+sysprt_NETRCVDROP(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="drpi   ";
+        val2valstr(sstat->intf.intf[as->index].rdrop,
+                   buf+5, 7, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NETRCVDROP = {"NETRCVDROP", sysprt_NETRCVDROP};
+/*******************************************************************/
+char *
+sysprt_NETSNDDROP(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="drpo   ";
+        val2valstr(sstat->intf.intf[as->index].sdrop,
+                   buf+5, 7, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NETSNDDROP = {"NETSNDDROP", sysprt_NETSNDDROP};
+/*******************************************************************/
+char *
+sysprt_NFMSERVER(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+       static char buf[16] = "srv ";
+        char mntdev[128], *ps;
+
+        memcpy(mntdev, sstat->nfs.nfsmounts.nfsmnt[as->index].mountdev,
+                                                               sizeof mntdev);
+
+       if ( (ps = strchr(mntdev, ':')) )               // colon found?
+               *ps = '\0';
+       else
+               strcpy(mntdev, "?");
+
+       sprintf(buf+4, "%8.8s", mntdev);
+        return buf;
+}
+
+sys_printdef syspdef_NFMSERVER = {"NFMSERVER", sysprt_NFMSERVER};
+/*******************************************************************/
+char *
+sysprt_NFMPATH(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+       static char buf[16];
+        char mntdev[128], *ps;
+       int len;
+
+        memcpy(mntdev, sstat->nfs.nfsmounts.nfsmnt[as->index].mountdev,
+                                                               sizeof mntdev);
+
+       if ( (ps = strchr(mntdev, ':')) )               // colon found?
+               ps++;
+       else
+               ps = mntdev;
+
+       len = strlen(ps);
+        if (len > 12)
+               ps = ps + len - 12;
+
+       sprintf(buf, "%12.12s", ps);
+        return buf;
+}
+
+sys_printdef syspdef_NFMPATH = {"NFMPATH", sysprt_NFMPATH};
+/*******************************************************************/
+char *
+sysprt_NFMTOTREAD(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="read   ";
+
+        val2memstr(sstat->nfs.nfsmounts.nfsmnt[as->index].bytestotread,
+                               buf+6, KBFORMAT, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NFMTOTREAD = {"NFMTOTREAD", sysprt_NFMTOTREAD};
+/*******************************************************************/
+char *
+sysprt_NFMTOTWRITE(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="write   ";
+
+        val2memstr(sstat->nfs.nfsmounts.nfsmnt[as->index].bytestotwrite,
+                               buf+6, KBFORMAT, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NFMTOTWRITE = {"NFMTOTWRITE", sysprt_NFMTOTWRITE};
+/*******************************************************************/
+char *
+sysprt_NFMNREAD(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="nread    ";
+
+        val2memstr(sstat->nfs.nfsmounts.nfsmnt[as->index].bytesread,
+                               buf+6, KBFORMAT, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NFMNREAD = {"NFMNREAD", sysprt_NFMNREAD};
+/*******************************************************************/
+char *
+sysprt_NFMNWRITE(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="nwrit    ";
+
+        val2memstr(sstat->nfs.nfsmounts.nfsmnt[as->index].byteswrite,
+                               buf+6, KBFORMAT, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NFMNWRITE = {"NFMNWRITE", sysprt_NFMNWRITE};
+/*******************************************************************/
+char *
+sysprt_NFMDREAD(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="dread    ";
+
+        val2memstr(sstat->nfs.nfsmounts.nfsmnt[as->index].bytesdread,
+                               buf+6, KBFORMAT, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NFMDREAD = {"NFMDREAD", sysprt_NFMDREAD};
+/*******************************************************************/
+char *
+sysprt_NFMDWRITE(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="dwrit    ";
+
+        val2memstr(sstat->nfs.nfsmounts.nfsmnt[as->index].bytesdwrite,
+                               buf+6, KBFORMAT, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NFMDWRITE = {"NFMDWRITE", sysprt_NFMDWRITE};
+/*******************************************************************/
+char *
+sysprt_NFMMREAD(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="mread    ";
+
+        val2memstr(sstat->nfs.nfsmounts.nfsmnt[as->index].pagesmread *pagesize,
+                               buf+6, KBFORMAT, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NFMMREAD = {"NFMMREAD", sysprt_NFMMREAD};
+/*******************************************************************/
+char *
+sysprt_NFMMWRITE(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="mwrit    ";
+
+        val2memstr(sstat->nfs.nfsmounts.nfsmnt[as->index].pagesmwrite *pagesize,
+                               buf+6, KBFORMAT, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NFMMWRITE = {"NFMMWRITE", sysprt_NFMMWRITE};
+/*******************************************************************/
+char *
+sysprt_NFCRPCCNT(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="rpc   ";
+        val2valstr(sstat->nfs.client.rpccnt,
+                   buf+4, 8, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NFCRPCCNT = {"NFCRPCCNT", sysprt_NFCRPCCNT};
+/*******************************************************************/
+char *
+sysprt_NFCRPCREAD(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="read   ";
+        val2valstr(sstat->nfs.client.rpcread,
+                   buf+5, 7, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NFCRPCREAD = {"NFCRPCREAD", sysprt_NFCRPCREAD};
+/*******************************************************************/
+char *
+sysprt_NFCRPCWRITE(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="write   ";
+        val2valstr(sstat->nfs.client.rpcwrite,
+                   buf+6, 6, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NFCRPCWRITE = {"NFCRPCWRITE", sysprt_NFCRPCWRITE};
+/*******************************************************************/
+char *
+sysprt_NFCRPCRET(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="retxmit ";
+        val2valstr(sstat->nfs.client.rpcretrans,
+                   buf+8, 4, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NFCRPCRET = {"NFCRPCRET", sysprt_NFCRPCRET};
+/*******************************************************************/
+char *
+sysprt_NFCRPCARF(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="autref  ";
+        val2valstr(sstat->nfs.client.rpcautrefresh,
+                   buf+7, 5, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NFCRPCARF = {"NFCRPCARF", sysprt_NFCRPCARF};
+/*******************************************************************/
+char *
+sysprt_NFSRPCCNT(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="rpc   ";
+        val2valstr(sstat->nfs.server.rpccnt,
+                   buf+4, 8, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NFSRPCCNT = {"NFSRPCCNT", sysprt_NFSRPCCNT};
+/*******************************************************************/
+char *
+sysprt_NFSRPCREAD(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="cread   ";
+        val2valstr(sstat->nfs.server.rpcread,
+                   buf+6, 6, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NFSRPCREAD = {"NFSRPCREAD", sysprt_NFSRPCREAD};
+/*******************************************************************/
+char *
+sysprt_NFSRPCWRITE(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="cwrit   ";
+        val2valstr(sstat->nfs.server.rpcwrite,
+                   buf+6, 6, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NFSRPCWRITE = {"NFSRPCWRITE", sysprt_NFSRPCWRITE};
+/*******************************************************************/
+char *
+sysprt_NFSBADFMT(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="badfmt   ";
+        val2valstr(sstat->nfs.server.rpcbadfmt,
+                   buf+7, 5, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NFSBADFMT = {"NFSBADFMT", sysprt_NFSBADFMT};
+/*******************************************************************/
+char *
+sysprt_NFSBADAUT(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="badaut   ";
+        val2valstr(sstat->nfs.server.rpcbadaut,
+                   buf+7, 5, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NFSBADAUT = {"NFSBADAUT", sysprt_NFSBADAUT};
+/*******************************************************************/
+char *
+sysprt_NFSBADCLN(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="badcln   ";
+        val2valstr(sstat->nfs.server.rpcbadcln,
+                   buf+7, 5, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NFSBADCLN = {"NFSBADCLN", sysprt_NFSBADCLN};
+/*******************************************************************/
+char *
+sysprt_NFSNETTCP(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="nettcp   ";
+        val2valstr(sstat->nfs.server.nettcpcnt,
+                   buf+7, 5, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NFSNETTCP = {"NFSNETTCP", sysprt_NFSNETTCP};
+/*******************************************************************/
+char *
+sysprt_NFSNETUDP(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="netudp   ";
+        val2valstr(sstat->nfs.server.netudpcnt,
+                   buf+7, 5, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NFSNETUDP = {"NFSNETUDP", sysprt_NFSNETUDP};
+/*******************************************************************/
+char *
+sysprt_NFSNRBYTES(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam     *as=q;
+        static char    buf[32]="MBcr/s ";
+
+        sprintf(buf+7, "%5.1lf",
+               sstat->nfs.server.nrbytes / 1024.0 / 1024.0 / as->nsecs);
+
+        return buf;
+}
+
+sys_printdef syspdef_NFSNRBYTES = {"NFSNRBYTES", sysprt_NFSNRBYTES};
+/*******************************************************************/
+char *
+sysprt_NFSNWBYTES(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam     *as=q;
+        static char    buf[32]="MBcw/s ";
+
+        sprintf(buf+7, "%5.1lf",
+               sstat->nfs.server.nwbytes / 1024.0 / 1024.0 / as->nsecs);
+
+        return buf;
+}
+
+sys_printdef syspdef_NFSNWBYTES = {"NFSNWBYTES", sysprt_NFSNWBYTES};
+/*******************************************************************/
+char *
+sysprt_NFSRCHITS(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="rchits   ";
+        val2valstr(sstat->nfs.server.rchits,
+                   buf+8, 4, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NFSRCHITS = {"NFSRCHITS", sysprt_NFSRCHITS};
+/*******************************************************************/
+char *
+sysprt_NFSRCMISS(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="rcmiss   ";
+        val2valstr(sstat->nfs.server.rcmiss,
+                   buf+8, 4, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NFSRCMISS = {"NFSRCMISS", sysprt_NFSRCMISS};
+/*******************************************************************/
+char *
+sysprt_NFSRCNOCA(void *p, void *q, int badness, int *color) 
+{
+        struct sstat *sstat=p;
+        extraparam *as=q;
+        static char buf[16]="rcnoca   ";
+        val2valstr(sstat->nfs.server.rcnoca,
+                   buf+8, 4, as->avgval, as->nsecs);
+        return buf;
+}
+
+sys_printdef syspdef_NFSRCNOCA = {"NFSRCNOCA", sysprt_NFSRCNOCA};
+/*******************************************************************/
+char *
+sysprt_BLANKBOX(void *p, void *notused, int badness, int *color) 
+{
+        return "            ";
+}
+
+sys_printdef syspdef_BLANKBOX = {"BLANKBOX", sysprt_BLANKBOX};
diff --git a/sources/various.c b/sources/various.c
new file mode 100644 (file)
index 0000000..7ab64a8
--- /dev/null
@@ -0,0 +1,605 @@
+/*
+** ATOP - System & Process Monitor
+**
+** The program 'atop' offers the possibility to view the activity of
+** the system on system-level as well as process-level.
+**
+** This source-file contains various functions to a.o. format the
+** time-of-day, the cpu-time consumption and the memory-occupation. 
+** ==========================================================================
+** Author:      Gerlof Langeveld
+** E-mail:      gerlof.langeveld@atoptool.nl
+** Date:        November 1996
+** LINUX-port:  June 2000
+** --------------------------------------------------------------------------
+** Copyright (C) 2000-2010 Gerlof Langeveld
+**
+** This program is free software; you can redistribute it and/or modify it
+** under the terms of the GNU General Public License as published by the
+** Free Software Foundation; either version 2, or (at your option) any
+** later version.
+**
+** This program is distributed in the hope that it will be useful, but
+** WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+** See the GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+** --------------------------------------------------------------------------
+**
+** $Log: various.c,v $
+** Revision 1.21  2010/11/12 06:16:16  gerlof
+** Show all parts of timestamp in header line, even when zero.
+**
+** Revision 1.20  2010/05/18 19:21:08  gerlof
+** Introduce CPU frequency and scaling (JC van Winkel).
+**
+** Revision 1.19  2010/04/28 18:21:11  gerlof
+** Cast value larger than 4GB to long long.
+**
+** Revision 1.18  2010/04/23 12:19:35  gerlof
+** Modified mail-address in header.
+**
+** Revision 1.17  2010/03/26 11:52:45  gerlof
+** Introduced unit of Tbytes for memory-usage.
+**
+** Revision 1.16  2009/12/17 08:28:38  gerlof
+** Express CPU-time usage in days and hours for large values.
+**
+** Revision 1.15  2009/12/10 08:50:39  gerlof
+** Introduction of a new function to convert number of seconds
+** to a string indicating days, hours, minutes and seconds.
+**
+** Revision 1.14  2007/02/13 10:32:47  gerlof
+** Removal of external declarations.
+** Removal of function getpagesz().
+**
+** Revision 1.13  2006/02/07 08:27:21  gerlof
+** Add possibility to show counters per second.
+** Modify presentation of CPU-values.
+**
+** Revision 1.12  2005/10/31 12:26:09  gerlof
+** Modified date-format to yyyy/mm/dd.
+**
+** Revision 1.11  2005/10/21 09:51:29  gerlof
+** Per-user accumulation of resource consumption.
+**
+** Revision 1.10  2004/05/06 09:46:24  gerlof
+** Ported to kernel-version 2.6.
+**
+** Revision 1.9  2003/07/07 09:27:46  gerlof
+** Cleanup code (-Wall proof).
+**
+** Revision 1.8  2003/07/03 11:16:59  gerlof
+** Minor bug solutions.
+**
+** Revision 1.7  2003/06/30 11:31:17  gerlof
+** Enlarge counters to 'long long'.
+**
+** Revision 1.6  2003/06/24 06:22:24  gerlof
+** Limit number of system resource lines.
+**
+** Revision 1.5  2002/08/30 07:49:09  gerlof
+** Convert a hh:mm string into a number of seconds since 00:00.
+**
+** Revision 1.4  2002/08/27 12:08:37  gerlof
+** Modified date format (from yyyy/mm/dd to mm/dd/yyyy).
+**
+** Revision 1.3  2002/07/24 11:14:05  gerlof
+** Changed to ease porting to other UNIX-platforms.
+**
+** Revision 1.2  2002/07/11 09:43:36  root
+** Modified HZ into sysconf(_SC_CLK_TCK).
+**
+** Revision 1.1  2001/10/02 10:43:36  gerlof
+** Initial revision
+**
+*/
+
+static const char rcsid[] = "$Id: various.c,v 1.21 2010/11/12 06:16:16 gerlof Exp $";
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/times.h>
+#include <signal.h>
+#include <time.h>
+#include <math.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdarg.h>
+
+#include "atop.h"
+#include "acctproc.h"
+
+/*
+** Function convtime() converts a value (number of seconds since
+** 1-1-1970) to an ascii-string in the format hh:mm:ss, stored in
+** chartim (9 bytes long).
+*/
+char *
+convtime(time_t utime, char *chartim)
+{
+       struct tm       *tt;
+
+       tt = localtime(&utime);
+
+       sprintf(chartim, "%02d:%02d:%02d", tt->tm_hour, tt->tm_min, tt->tm_sec);
+
+       return chartim;
+}
+
+/*
+** Function convdate() converts a value (number of seconds since
+** 1-1-1970) to an ascii-string in the format yyyy/mm/dd, stored in
+** chardat (11 bytes long).
+*/
+char *
+convdate(time_t utime, char *chardat)
+{
+       struct tm       *tt;
+
+       tt = localtime(&utime);
+
+       sprintf(chardat, "%04d/%02d/%02d",
+               tt->tm_year+1900, tt->tm_mon+1, tt->tm_mday);
+
+       return chardat;
+}
+
+
+/*
+** Convert a hh:mm string into a number of seconds since 00:00
+**
+** Return-value:       0 - Wrong input-format
+**                     1 - Success
+*/
+int
+hhmm2secs(char *itim, unsigned int *otim)
+{
+       register int    i;
+       int             hours, minutes;
+
+       /*
+       ** check string syntax
+       */
+       for (i=0; *(itim+i); i++)
+               if ( !isdigit(*(itim+i)) && *(itim+i) != ':' )
+                       return(0);
+
+       sscanf(itim, "%d:%d", &hours, &minutes);
+
+       if ( hours < 0 || hours > 23 || minutes < 0 || minutes > 59 )
+               return(0);
+
+       *otim = (hours * 3600) + (minutes * 60);
+
+       if (*otim >= SECSDAY)
+               *otim = SECSDAY-1;
+
+       return(1);
+}
+
+
+/*
+** Return number of seconds since midnight according local clock time
+**
+** Return-value:        Number of seconds
+*/
+int
+daysecs(time_t itime)
+{
+       struct tm *tt;
+
+       tt = localtime(&itime);
+
+       return( (tt->tm_hour*3600) + (tt->tm_min*60) );
+}
+
+
+/*
+** Function val2valstr() converts a positive value to an ascii-string of a 
+** fixed number of positions; if the value does not fit, it will be formatted
+** to exponent-notation (as short as possible, so not via the standard printf-
+** formatters %f or %e). The offered string should have a length of width+1.
+** The value might even be printed as an average for the interval-time.
+*/
+char *
+val2valstr(count_t value, char *strvalue, int width, int avg, int nsecs)
+{
+       count_t maxval, remain = 0;
+       int     exp     = 0;
+       char    *suffix = "";
+
+       if (avg && nsecs)
+       {
+               value  = (value + (nsecs/2)) / nsecs;     /* rounded value */
+               width  = width - 2;     /* subtract two positions for '/s' */
+               suffix = "/s";
+       }
+
+       if (value < 0)          // no negative value expected
+       {
+               sprintf(strvalue, "%*s%s", width, "?", suffix);
+               return strvalue;
+       }
+
+       maxval = pow(10.0, width) - 1;
+
+       if (value < maxval)
+       {
+               sprintf(strvalue, "%*lld%s", width, value, suffix);
+       }
+       else
+       {
+               if (width < 3)
+               {
+                       /*
+                       ** cannot avoid ignoring width
+                       */
+                       sprintf(strvalue, "%lld%s", value, suffix);
+               }
+               else
+               {
+                       /*
+                       ** calculate new maximum value for the string,
+                       ** calculating space for 'e' (exponent) + one digit
+                       */
+                       width -= 2;
+                       maxval = pow(10.0, width) - 1;
+
+                       while (value > maxval)
+                       {
+                               exp++;
+                               remain = value % 10;
+                               value /= 10;
+                       }
+
+                       if (remain >= 5)
+                               value++;
+
+                       sprintf(strvalue, "%*llde%d%s",
+                                       width, value, exp, suffix);
+               }
+       }
+
+       return strvalue;
+}
+
+#define DAYSECS        (24*60*60)
+#define HOURSECS       (60*60)
+#define MINSECS        (60)
+
+/*
+** Function val2elapstr() converts a value (number of seconds)
+** to an ascii-string of up to max 13 positions in NNNdNNhNNmNNs
+** stored in strvalue (at least 14 positions).
+** returnvalue: number of bytes stored
+*/
+int
+val2elapstr(int value, char *strvalue)
+{
+        char   *p=strvalue;
+
+        if (value >= DAYSECS) 
+        {
+                p+=sprintf(p, "%dd", value/DAYSECS);
+        }
+
+        if (value >= HOURSECS) 
+        {
+                p+=sprintf(p, "%dh", (value%DAYSECS)/HOURSECS);
+        }
+
+        if (value >= MINSECS) 
+        {
+                p+=sprintf(p, "%dm", (value%HOURSECS)/MINSECS);
+        }
+
+        p+=sprintf(p, "%ds", (value%MINSECS));
+
+        return p-strvalue;
+}
+
+
+/*
+** Function val2cpustr() converts a value (number of milliseconds)
+** to an ascii-string of 6 positions in milliseconds or minute-seconds or
+** hours-minutes, stored in strvalue (at least 7 positions).
+*/
+#define        MAXMSEC         (count_t)100000
+#define        MAXSEC          (count_t)6000
+#define        MAXMIN          (count_t)6000
+
+char *
+val2cpustr(count_t value, char *strvalue)
+{
+       if (value < MAXMSEC)
+       {
+               sprintf(strvalue, "%2lld.%02llds", value/1000, value%1000/10);
+       }
+       else
+       {
+               /*
+                       ** millisecs irrelevant; round to seconds
+                       */
+               value = (value + 500) / 1000;
+
+               if (value < MAXSEC) 
+               {
+                               sprintf(strvalue, "%2lldm%02llds", value/60, value%60);
+               }
+               else
+               {
+                       /*
+                       ** seconds irrelevant; round to minutes
+                       */
+                       value = (value + 30) / 60;
+
+                       if (value < MAXMIN) 
+                       {
+                               sprintf(strvalue, "%2lldh%02lldm",
+                                                       value/60, value%60);
+                       }
+                       else
+                       {
+                               /*
+                               ** minutes irrelevant; round to hours
+                               */
+                               value = (value + 30) / 60;
+
+                               sprintf(strvalue, "%2lldd%02lldh",
+                                               value/24, value%24);
+                       }
+               }
+       }
+
+       return strvalue;
+}
+
+/*
+** Function val2Hzstr() converts a value (in MHz) 
+** to an ascii-string.
+** The result-string is placed in the area pointed to strvalue,
+** which should be able to contain at least 8 positions.
+*/
+char *
+val2Hzstr(count_t value, char *strvalue)
+{
+        if (value < 1000)
+        {
+                sprintf(strvalue, "%4lldMHz", value);
+        }
+        else
+        {
+                double fval=value/1000.0;      // fval is double in GHz
+                char prefix='G';
+
+                if (fval >= 1000.0)            // prepare for the future
+                {
+                        prefix='T';        
+                        fval /= 1000.0;
+                }
+                sprintf(strvalue, "%4.2f%cHz", fval, prefix);
+        }
+       return strvalue;
+}
+
+
+/*
+** Function val2memstr() converts a value (number of bytes)
+** to an ascii-string in a specific format (indicated by pformat).
+** The result-string is placed in the area pointed to strvalue,
+** which should be able to contain at least 7 positions.
+*/
+#define        ONEKBYTE        1024
+#define        ONEMBYTE        1048576
+#define        ONEGBYTE        1073741824L
+#define        ONETBYTE        1099511627776LL
+#define        ONEPBYTE        1125899906842624LL
+
+#define        MAXBYTE         1024
+#define        MAXKBYTE        ONEKBYTE*99999L
+#define        MAXMBYTE        ONEMBYTE*999L
+#define        MAXGBYTE        ONEGBYTE*999LL
+#define        MAXTBYTE        ONETBYTE*999LL
+
+char *
+val2memstr(count_t value, char *strvalue, int pformat, int avgval, int nsecs)
+{
+       char    aformat;        /* advised format               */
+       count_t verifyval;
+       char    *suffix = "";
+       int     basewidth = 6;
+
+
+       /*
+       ** notice that the value can be negative, in which case the
+       ** modulo-value should be evaluated and an extra position should
+       ** be reserved for the sign
+       */
+       if (value < 0)
+               verifyval = -value * 10;
+       else
+               verifyval =  value;
+
+       /*
+       ** verify if printed value is required per second (average) or total
+       */
+       if (avgval && nsecs)
+       {
+               value     /= nsecs;
+               verifyval  = verifyval * 100 /nsecs;
+               basewidth -= 2;
+               suffix     = "/s";
+       }
+       
+       /*
+       ** determine which format will be used on bases of the value itself
+       */
+       if (verifyval <= MAXBYTE)       /* bytes ? */
+               aformat = ANYFORMAT;
+       else
+               if (verifyval <= MAXKBYTE)      /* kbytes ? */
+                       aformat = KBFORMAT;
+               else
+                       if (verifyval <= MAXMBYTE)      /* mbytes ? */
+                               aformat = MBFORMAT;
+                       else
+                               if (verifyval <= MAXGBYTE)      /* gbytes ? */
+                                       aformat = GBFORMAT;
+                               else
+                                       if (verifyval <= MAXTBYTE)/* tbytes? */
+                                               aformat = TBFORMAT;/* tbytes! */
+                                       else
+                                               aformat = PBFORMAT;/* pbytes! */
+
+       /*
+       ** check if this is also the preferred format
+       */
+       if (aformat <= pformat)
+               aformat = pformat;
+
+       switch (aformat)
+       {
+          case ANYFORMAT:
+               sprintf(strvalue, "%*lld%s",
+                               basewidth, value, suffix);
+               break;
+
+          case KBFORMAT:
+               sprintf(strvalue, "%*lldK%s",
+                               basewidth-1, value/ONEKBYTE, suffix);
+               break;
+
+          case MBFORMAT:
+               sprintf(strvalue, "%*.1lfM%s",
+                       basewidth-1, (double)((double)value/ONEMBYTE), suffix); 
+               break;
+
+          case GBFORMAT:
+               sprintf(strvalue, "%*.1lfG%s",
+                       basewidth-1, (double)((double)value/ONEGBYTE), suffix);
+               break;
+
+          case TBFORMAT:
+               sprintf(strvalue, "%*.1lfT%s",
+                       basewidth-1, (double)((double)value/ONETBYTE), suffix);
+               break;
+
+          case PBFORMAT:
+               sprintf(strvalue, "%*.1lfP%s",
+                       basewidth-1, (double)((double)value/ONEPBYTE), suffix);
+               break;
+
+          default:
+               sprintf(strvalue, "!TILT!");
+       }
+
+       return strvalue;
+}
+
+
+/*
+** Function numeric() checks if the ascii-string contains 
+** a numeric (positive) value.
+** Returns 1 (true) if so, or 0 (false).
+*/
+int
+numeric(char *ns)
+{
+       register char *s = ns;
+
+       while (*s)
+               if (*s < '0' || *s > '9')
+                       return(0);              /* false */
+               else
+                       s++;
+       return(1);                              /* true  */
+}
+
+
+/*
+** Function getboot() returns the boot-time of this system 
+** as number of jiffies since 1-1-1970.
+*/
+unsigned long long
+getboot(void)
+{
+       static unsigned long long       boottime;
+       unsigned long long              getbootlinux(long);
+
+       if (!boottime)          /* do this only once */
+               boottime = getbootlinux(hertz);
+
+       return boottime;
+}
+
+/*
+** generic pointer verification after malloc
+*/
+void
+ptrverify(const void *ptr, const char *errormsg, ...)
+{
+       va_list args;
+
+        va_start(args, errormsg);
+
+       if (!ptr)
+       {
+               acctswoff();
+               netatop_signoff();
+
+               if (vis.show_end)
+                       (vis.show_end)();
+
+               va_list args;
+               fprintf(stderr, errormsg, args);
+               va_end  (args);
+
+               exit(13);
+       }
+}
+
+/*
+** signal catcher for cleanup before exit
+*/
+void
+cleanstop(int exitcode)
+{
+       acctswoff();
+       netatop_signoff();
+       (vis.show_end)();
+       exit(exitcode);
+}
+
+/*
+** drop the root privileges that might be obtained via setuid-bit
+**
+** this action may only fail with errno EPERM (normal situation when
+** atop has not been started with setuid-root privs); when this
+** action fails with EAGAIN or ENOMEM, atop should not continue
+** without root privs being dropped...
+*/
+int
+droprootprivs(void)
+{
+       if (seteuid( getuid() ) == -1 && errno != EPERM)
+               return 0;       /* false */
+       else
+               return 1;       /* true  */
+}
+
+/*
+** regain the root privileges that might be dropped earlier
+*/
+void
+regainrootprivs(void)
+{
+       seteuid(0);
+}
diff --git a/sources/version.c b/sources/version.c
new file mode 100644 (file)
index 0000000..eea7113
--- /dev/null
@@ -0,0 +1,29 @@
+/* No manual changes in this file */
+#include <stdio.h>
+#include <string.h>
+#include "version.h"
+
+static char atopversion[] = ATOPVERS;
+static char atopdate[]    = ATOPDATE;
+
+char *
+getstrvers(void)
+{
+       static char vers[256];
+
+       snprintf(vers, sizeof vers,
+               "Version: %s - %s     <gerlof.langeveld@atoptool.nl>",
+               atopversion, atopdate);
+
+       return vers;
+}
+
+unsigned short
+getnumvers(void)
+{
+       int     vers1, vers2;
+
+       sscanf(atopversion, "%u.%u", &vers1, &vers2);
+
+       return (unsigned short) ((vers1 << 8) + vers2);
+}
diff --git a/sources/version.h b/sources/version.h
new file mode 100644 (file)
index 0000000..f298461
--- /dev/null
@@ -0,0 +1,2 @@
+#define        ATOPVERS        "2.3.0"
+#define        ATOPDATE        "2017/03/25 09:59:59"