--- /dev/null
+*.dsc
+*.tar.?z
+
--- /dev/null
+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
--- /dev/null
+This package is built using git, debhelper9 and quilt. It is source
+format 3.0 (quilt).
--- /dev/null
+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
--- /dev/null
+# /etc/default/atop
+# this file is no longer used and will be removed in a future release
--- /dev/null
+debian/atop.wrapper usr/share/atop
+debian/atop.service /lib/systemd/system
+debian/atopacct.service /lib/systemd/system
--- /dev/null
+#!/bin/bash
+
+DAEMON="$1"
+OUTFILE="$2"
+shift 2
+
+exec $DAEMON "$@" >$OUTFILE 2>&1
--- /dev/null
+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
+
--- /dev/null
+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>
--- /dev/null
+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'.
--- /dev/null
+usr/bin
+usr/share/man/man1
+usr/share/atop
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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]
--- /dev/null
+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
--- /dev/null
+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) ]; \
--- /dev/null
+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
--- /dev/null
+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
+ ;;
+
+ *)
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
+
--- /dev/null
+--- 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
--- /dev/null
+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
+
+ ##########################################################################
+
--- /dev/null
+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)
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+#!/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#
--- /dev/null
+#! /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
+
--- /dev/null
+#!/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#
--- /dev/null
+#!/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
--- /dev/null
+3.0 (quilt)
--- /dev/null
+# 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
--- /dev/null
+#!/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
--- /dev/null
+Gerlof Langeveld <gerlof.langeveld@atoptool.nl>
--- /dev/null
+ 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.
--- /dev/null
+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
--- /dev/null
+# 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
--- /dev/null
+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
--- /dev/null
+/*
+** 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;
+ }
+}
--- /dev/null
+/*
+** 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 */
+};
--- /dev/null
+#!/bin/bash
+
+case "$1" in
+ pre) /usr/bin/systemctl stop atop
+ exit 0
+ ;;
+ post) /usr/bin/systemctl start atop
+ exit 0
+ ;;
+ *) exit 1
+ ;;
+esac
--- /dev/null
+/*
+** 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);
+ }
+}
--- /dev/null
+# daily restart of atop at midnight
+0 0 * * * root systemctl restart atop
--- /dev/null
+# daily restart of atop at midnight
+0 0 * * * root /usr/share/atop/atop.daily&
--- /dev/null
+#!/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
--- /dev/null
+/*
+** 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 *);
--- /dev/null
+#!/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
--- /dev/null
+[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
--- /dev/null
+#!/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
--- /dev/null
+[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
--- /dev/null
+/*
+** 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;
+}
--- /dev/null
+/*
+** 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
--- /dev/null
+/*
+** 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);
--- /dev/null
+/*
+** 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;
+}
--- /dev/null
+/*
+** 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(ðcmd, 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 *)ðcmd;
+
+ ethcmd.cmd = ETHTOOL_GSET;
+
+ if ( ioctl(sockfd, SIOCETHTOOL, &ifreq) == 0)
+ {
+ ifprops[i].type = 'e'; // type ethernet
+ ifprops[i].speed = ethtool_cmd_speed(ðcmd);
+
+ 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);
+}
--- /dev/null
+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);
--- /dev/null
+.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
--- /dev/null
+.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)
--- /dev/null
+.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
--- /dev/null
+.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)
--- /dev/null
+#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)
--- /dev/null
+#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
+};
--- /dev/null
+/*
+** 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;
+}
--- /dev/null
+#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;
+}
--- /dev/null
+/*
+** 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;
+};
--- /dev/null
+/*
+** 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');
+ }
+}
--- /dev/null
+int parsedef(char *);
+char parseout(time_t, int,
+ struct devtstat *, struct sstat *,
+ int, unsigned int, char);
--- /dev/null
+/*
+** 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);
+}
--- /dev/null
+/*
+** 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);
--- /dev/null
+/*
+** 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
--- /dev/null
+/*
+** 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 *);
--- /dev/null
+/*
+** 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 */
+}
--- /dev/null
+# 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
+}
--- /dev/null
+# 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
+}
--- /dev/null
+/*
+** 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);
+}
--- /dev/null
+/*
+** 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;
+ }
+ }
+}
--- /dev/null
+/*
+** 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);
--- /dev/null
+/*
+** 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);
+}
--- /dev/null
+/*
+** 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);
--- /dev/null
+/*
+** 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 };
--- /dev/null
+/*
+** 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};
--- /dev/null
+/*
+** 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);
+}
--- /dev/null
+/* 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);
+}
--- /dev/null
+#define ATOPVERS "2.3.0"
+#define ATOPDATE "2017/03/25 09:59:59"