From 090ad6f5f7f431374abf2a06cdf588186f25a669 Mon Sep 17 00:00:00 2001 From: Dmitry Teselkin Date: Tue, 22 Jan 2019 16:00:01 +0300 Subject: [PATCH] atop-2.3.0 Change-Id: I0155650be349c632bdd68ca26c0d2a7e15881787 Related-Prod: PROD-14364 --- .gitignore | 3 + debian/NEWS | 105 + debian/README.source | 2 + debian/atop.cron.d | 4 + debian/atop.default | 2 + debian/atop.install | 3 + debian/atop.wrapper | 7 + debian/changelog | 369 +++ debian/compat | 1 + debian/control | 28 + debian/copyright | 13 + debian/dirs | 3 + debian/docs | 1 + debian/patches/atop-daily | 13 + debian/patches/atop-pm | 43 + debian/patches/atopacct.service | 14 + debian/patches/dependency-on-remote-fs | 30 + debian/patches/dh_installinit | 15 + debian/patches/dh_systemd_enable | 17 + debian/patches/force-reload | 28 + debian/patches/init-script-lsb-headers | 21 + debian/patches/logrotate-nomail | 26 + debian/patches/lsb-init-functions | 26 + debian/patches/makefile-clean | 12 + debian/patches/no-files-in-var-log | 15 + debian/patches/no-version-symlinks | 15 + debian/patches/series | 15 + debian/patches/systemd-path | 15 + debian/patches/var-run | 217 ++ debian/postinst | 24 + debian/postrm | 16 + debian/preinst | 10 + debian/rules | 30 + debian/source/format | 1 + debian/watch | 10 + sources/45atoppm | 73 + sources/AUTHOR | 1 + sources/COPYING | 340 +++ sources/ChangeLog | 2370 +++++++++++++++++ sources/Makefile | 181 ++ sources/README | 59 + sources/acctproc.c | 1104 ++++++++ sources/acctproc.h | 158 ++ sources/atop-pm.sh | 12 + sources/atop.c | 1161 +++++++++ sources/atop.cronsystemd | 2 + sources/atop.cronsysv | 2 + sources/atop.daily | 41 + sources/atop.h | 186 ++ sources/atop.init | 81 + sources/atop.service | 12 + sources/atopacct.init | 86 + sources/atopacct.service | 14 + sources/atopacctd.c | 1040 ++++++++ sources/atopacctd.h | 35 + sources/atopsar.c | 2558 ++++++++++++++++++ sources/deviate.c | 1620 ++++++++++++ sources/ifprop.c | 207 ++ sources/ifprop.h | 10 + sources/man/atop.1 | 2091 +++++++++++++++ sources/man/atopacctd.8 | 147 ++ sources/man/atoprc.5 | 403 +++ sources/man/atopsar.1 | 1151 +++++++++ sources/netatop.h | 47 + sources/netatopd.h | 12 + sources/netatopif.c | 531 ++++ sources/netlink.c | 253 ++ sources/netstats.h | 140 + sources/parseable.c | 716 ++++++ sources/parseable.h | 4 + sources/photoproc.c | 815 ++++++ sources/photoproc.h | 169 ++ sources/photosyst.c | 1754 +++++++++++++ sources/photosyst.h | 299 +++ sources/procdbase.c | 364 +++ sources/psaccs_atop | 40 + sources/psaccu_atop | 24 + sources/rawlog.c | 970 +++++++ sources/showgeneric.c | 3287 ++++++++++++++++++++++++ sources/showgeneric.h | 150 ++ sources/showlinux.c | 2371 +++++++++++++++++ sources/showlinux.h | 365 +++ sources/showprocs.c | 1915 ++++++++++++++ sources/showsys.c | 2374 +++++++++++++++++ sources/various.c | 605 +++++ sources/version.c | 29 + sources/version.h | 2 + 87 files changed, 33535 insertions(+) create mode 100644 .gitignore create mode 100644 debian/NEWS create mode 100644 debian/README.source create mode 100644 debian/atop.cron.d create mode 100644 debian/atop.default create mode 100644 debian/atop.install create mode 100755 debian/atop.wrapper create mode 100644 debian/changelog create mode 100644 debian/compat create mode 100644 debian/control create mode 100644 debian/copyright create mode 100644 debian/dirs create mode 100644 debian/docs create mode 100644 debian/patches/atop-daily create mode 100644 debian/patches/atop-pm create mode 100644 debian/patches/atopacct.service create mode 100644 debian/patches/dependency-on-remote-fs create mode 100644 debian/patches/dh_installinit create mode 100644 debian/patches/dh_systemd_enable create mode 100644 debian/patches/force-reload create mode 100644 debian/patches/init-script-lsb-headers create mode 100644 debian/patches/logrotate-nomail create mode 100644 debian/patches/lsb-init-functions create mode 100644 debian/patches/makefile-clean create mode 100644 debian/patches/no-files-in-var-log create mode 100644 debian/patches/no-version-symlinks create mode 100644 debian/patches/series create mode 100644 debian/patches/systemd-path create mode 100644 debian/patches/var-run create mode 100644 debian/postinst create mode 100644 debian/postrm create mode 100644 debian/preinst create mode 100755 debian/rules create mode 100644 debian/source/format create mode 100644 debian/watch create mode 100755 sources/45atoppm create mode 100644 sources/AUTHOR create mode 100644 sources/COPYING create mode 100644 sources/ChangeLog create mode 100644 sources/Makefile create mode 100644 sources/README create mode 100644 sources/acctproc.c create mode 100644 sources/acctproc.h create mode 100755 sources/atop-pm.sh create mode 100644 sources/atop.c create mode 100644 sources/atop.cronsystemd create mode 100644 sources/atop.cronsysv create mode 100755 sources/atop.daily create mode 100644 sources/atop.h create mode 100755 sources/atop.init create mode 100644 sources/atop.service create mode 100755 sources/atopacct.init create mode 100644 sources/atopacct.service create mode 100644 sources/atopacctd.c create mode 100644 sources/atopacctd.h create mode 100644 sources/atopsar.c create mode 100644 sources/deviate.c create mode 100644 sources/ifprop.c create mode 100644 sources/ifprop.h create mode 100644 sources/man/atop.1 create mode 100644 sources/man/atopacctd.8 create mode 100644 sources/man/atoprc.5 create mode 100644 sources/man/atopsar.1 create mode 100644 sources/netatop.h create mode 100644 sources/netatopd.h create mode 100644 sources/netatopif.c create mode 100644 sources/netlink.c create mode 100644 sources/netstats.h create mode 100644 sources/parseable.c create mode 100644 sources/parseable.h create mode 100644 sources/photoproc.c create mode 100644 sources/photoproc.h create mode 100644 sources/photosyst.c create mode 100644 sources/photosyst.h create mode 100644 sources/procdbase.c create mode 100644 sources/psaccs_atop create mode 100644 sources/psaccu_atop create mode 100644 sources/rawlog.c create mode 100644 sources/showgeneric.c create mode 100644 sources/showgeneric.h create mode 100644 sources/showlinux.c create mode 100644 sources/showlinux.h create mode 100644 sources/showprocs.c create mode 100644 sources/showsys.c create mode 100644 sources/various.c create mode 100644 sources/version.c create mode 100644 sources/version.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e8ed04d --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.dsc +*.tar.?z + diff --git a/debian/NEWS b/debian/NEWS new file mode 100644 index 0000000..e84edf8 --- /dev/null +++ b/debian/NEWS @@ -0,0 +1,105 @@ +atop (2.2.6-3) unstable; urgency=medium + + atop does some really weird things with logrotate, which cause + undocumented behavior in logrotate versions < 3.11. As Debian stretch + has 3.11, this should not be an issue when updating between stable + versions. + + If you have been using unstable or testing, and you get weird + messages from logrotate, it might be worth looking in /var/log/atop + for weird log files, and removing all dummy_* files other than + dummy_after and dummy_before. + + -- Marc Haber 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 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 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 Mon, 08 Aug 2016 10:58:40 +0200 diff --git a/debian/README.source b/debian/README.source new file mode 100644 index 0000000..1297928 --- /dev/null +++ b/debian/README.source @@ -0,0 +1,2 @@ +This package is built using git, debhelper9 and quilt. It is source +format 3.0 (quilt). diff --git a/debian/atop.cron.d b/debian/atop.cron.d new file mode 100644 index 0000000..6400ac4 --- /dev/null +++ b/debian/atop.cron.d @@ -0,0 +1,4 @@ +PATH=/bin:/usr/bin:/sbin:/usr/sbin + +# daily restart of atop at midnight +0 0 * * * root if [ -d "/run/systemd/system" ]; then systemctl restart atop; else /usr/share/atop/atop.daily \& ; fi diff --git a/debian/atop.default b/debian/atop.default new file mode 100644 index 0000000..0602db3 --- /dev/null +++ b/debian/atop.default @@ -0,0 +1,2 @@ +# /etc/default/atop +# this file is no longer used and will be removed in a future release diff --git a/debian/atop.install b/debian/atop.install new file mode 100644 index 0000000..404846b --- /dev/null +++ b/debian/atop.install @@ -0,0 +1,3 @@ +debian/atop.wrapper usr/share/atop +debian/atop.service /lib/systemd/system +debian/atopacct.service /lib/systemd/system diff --git a/debian/atop.wrapper b/debian/atop.wrapper new file mode 100755 index 0000000..eba4c59 --- /dev/null +++ b/debian/atop.wrapper @@ -0,0 +1,7 @@ +#!/bin/bash + +DAEMON="$1" +OUTFILE="$2" +shift 2 + +exec $DAEMON "$@" >$OUTFILE 2>&1 diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..4da7ffa --- /dev/null +++ b/debian/changelog @@ -0,0 +1,369 @@ +atop (2.3.0-1.1~u14.04+mcp) mcp; urgency=medium + + * Rebuild for Ubuntu 14.04 (PROD-14364) + http://archive.ubuntu.com/ubuntu/pool/universe/a/atop/atop_2.3.0-1.dsc + + -- Dmitry Teselkin 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 Fri, 07 Mar 2008 16:16:15 +0100 + +atop (1.22) unstable; urgency=low + + * new upstream release + + -- Edelhard Becker 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 Fri, 31 Aug 2007 15:55:33 +0200 + +atop (1.20-2) unstable; urgency=low + + * do not install upstream's crontab file + + -- Edelhard Becker 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 Wed, 02 May 2007 02:30:09 +0200 + +atop (1.16-2) unstable; urgency=low + + * depend on logrotate (Closes: #362893), thanks Tommi + + -- Edelhard Becker 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 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 Tue, 28 Mar 2006 16:29:27 +0200 + +atop (1.15-1) unstable; urgency=low + + * New upstream release + + -- Edelhard Becker 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 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 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 Fri, 24 Sep 2004 11:09:04 +0200 + +atop (1.12-1) unstable; urgency=low + + * New upstream release + + -- Edelhard Becker 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 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 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 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 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 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 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 Fri, 25 Apr 2003 14:27:39 +0200 + diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..f599e28 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +10 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..f6c5eab --- /dev/null +++ b/debian/control @@ -0,0 +1,28 @@ +Source: atop +Section: admin +Priority: optional +Maintainer: OSCORE Packaging Team +XSBC-Original-Maintainer: Marc Haber +Uploaders: Marc Haber +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 diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..27673de --- /dev/null +++ b/debian/copyright @@ -0,0 +1,13 @@ +This package was debianized by Edelhard Becker 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 + +Copyright (C) 2000-2010 Gerlof Langeveld, AT Computing (gerlof@ATComputing.nl) + +You are free to distribute this software under the terms of the GNU General +Public License. On Debian systems, the complete text of the GNU General Public +License can be found in the file `/usr/share/common-licenses/GPL'. diff --git a/debian/dirs b/debian/dirs new file mode 100644 index 0000000..819d141 --- /dev/null +++ b/debian/dirs @@ -0,0 +1,3 @@ +usr/bin +usr/share/man/man1 +usr/share/atop diff --git a/debian/docs b/debian/docs new file mode 100644 index 0000000..e845566 --- /dev/null +++ b/debian/docs @@ -0,0 +1 @@ +README diff --git a/debian/patches/atop-daily b/debian/patches/atop-daily new file mode 100644 index 0000000..324ead7 --- /dev/null +++ b/debian/patches/atop-daily @@ -0,0 +1,13 @@ +Description: More explanation in atop.daily +Author: Marc Haber +Forwarded: no +Last-Update: 2016-08-07 +--- a/atop.daily ++++ b/atop.daily +@@ -1,4 +1,6 @@ + #!/bin/bash ++# this is called - on sysvinit systems - at midnight by cron ++# on systemd systems this is called from the systemd unit + + CURDAY=`date +%Y%m%d` + LOGPATH=/var/log/atop diff --git a/debian/patches/atop-pm b/debian/patches/atop-pm new file mode 100644 index 0000000..3ba007e --- /dev/null +++ b/debian/patches/atop-pm @@ -0,0 +1,43 @@ +Description: install atop-pm to /etc/systemd/system-sleep/atop-pm +Author: Marc Haber +Forwarded: no +Last-Update: 2016-10-25 +--- a/Makefile ++++ b/Makefile +@@ -17,7 +17,7 @@ CRNPATH = /etc/cron.d + ROTPATH = /etc/logrotate.d + PMPATH1 = /usr/lib/pm-utils/sleep.d + PMPATH2 = /usr/lib64/pm-utils/sleep.d +-PMPATHD = /usr/lib/systemd/system-sleep ++PMPATHD = /lib/systemd/system-sleep + + CFLAGS += -O2 -I. -Wall # -DHTTPSTATS + OBJMOD0 = version.o +@@ -62,8 +62,8 @@ systemdinstall: genericinstall + then mkdir -p $(DESTDIR)$(PMPATHD); fi + # + cp atop.cronsystemd $(DESTDIR)$(CRNPATH)/atop +- cp atop-pm.sh $(DESTDIR)$(PMPATHD) +- chmod 0711 $(DESTDIR)$(PMPATHD)/atop-pm.sh ++ cp atop-pm.sh $(DESTDIR)$(PMPATHD)/atop-pm ++ chmod 0711 $(DESTDIR)$(PMPATHD)/atop-pm + # + # only when making on target system: + # +--- a/atop-pm.sh ++++ b/atop-pm.sh +@@ -1,10 +1,12 @@ + #!/bin/bash + ++PATH=/sbin:/usr/sbin:/bin:/usr/bin ++ + case "$1" in +- pre) /usr/bin/systemctl stop atop ++ pre) systemctl stop atop + exit 0 + ;; +- post) /usr/bin/systemctl start atop ++ post) systemctl start atop + exit 0 + ;; + *) exit 1 diff --git a/debian/patches/atopacct.service b/debian/patches/atopacct.service new file mode 100644 index 0000000..a8dd1c0 --- /dev/null +++ b/debian/patches/atopacct.service @@ -0,0 +1,14 @@ +Description: don't depend on obsolete syslog target (syslog is socket activated) +Author: Marc Haber +Forwarded: no +Last-Update: 2016-08-07 +--- a/atopacct.service ++++ b/atopacct.service +@@ -2,7 +2,6 @@ + Description=Atop process accounting daemon + Documentation=man:atopacctd(8) + Conflicts=psacct.service +-After=syslog.target + Before=atop.service + + [Service] diff --git a/debian/patches/dependency-on-remote-fs b/debian/patches/dependency-on-remote-fs new file mode 100644 index 0000000..3dc39f0 --- /dev/null +++ b/debian/patches/dependency-on-remote-fs @@ -0,0 +1,30 @@ +Description: If init script uses files from /usr, $remote_fs is needed as Dependency +Author: Marc Haber +Forwarded: via web form +Last-Update: 2016-08-07 +--- a/atop.init ++++ b/atop.init +@@ -7,8 +7,8 @@ + # + ### BEGIN INIT INFO + # Provides: atop +-# Required-Start: $local_fs +-# Required-Stop: $local_fs ++# Required-Start: $local_fs $remote_fs ++# Required-Stop: $local_fs $remote_fs + # Default-Start: 2 3 4 5 + # Default-Stop: 0 1 6 + # Short-Description: Advanced system and process activity monitor +--- a/atopacct.init ++++ b/atopacct.init +@@ -7,8 +7,8 @@ + # + ### BEGIN INIT INFO + # Provides: atopacct +-# Required-Start: $local_fs +-# Required-Stop: $local_fs ++# Required-Start: $local_fs $remote_fs ++# Required-Stop: $local_fs $remote_fs + # Default-Start: 2 3 4 5 + # Default-Stop: 0 1 6 + # Description: This daemon switches on process accounting and diff --git a/debian/patches/dh_installinit b/debian/patches/dh_installinit new file mode 100644 index 0000000..fffb436 --- /dev/null +++ b/debian/patches/dh_installinit @@ -0,0 +1,15 @@ +Description: do not install init scripts directly +Author: Marc Haber +Forwarded: via web form +Last-Update: 2016-08-07 +--- a/Makefile ++++ b/Makefile +@@ -82,8 +82,6 @@ sysvinstall: genericinstall + if [ ! -d $(DESTDIR)$(INIPATH) ]; \ + then mkdir -p $(DESTDIR)$(INIPATH); fi + # +- cp atop.init $(DESTDIR)$(INIPATH)/atop +- cp atopacct.init $(DESTDIR)$(INIPATH)/atopacct + cp atop.cronsysv $(DESTDIR)$(CRNPATH)/atop + # + if [ -d $(DESTDIR)$(PMPATH1) ]; \ diff --git a/debian/patches/dh_systemd_enable b/debian/patches/dh_systemd_enable new file mode 100644 index 0000000..f5a8dad --- /dev/null +++ b/debian/patches/dh_systemd_enable @@ -0,0 +1,17 @@ +Description: do not install atop.service and atopacct.service +Author: Marc Haber +Forwarded: via web form +Last-Update: 2016-08-07 +--- a/Makefile ++++ b/Makefile +@@ -61,10 +61,6 @@ systemdinstall: genericinstall + if [ ! -d $(DESTDIR)$(PMPATHD) ]; \ + then mkdir -p $(DESTDIR)$(PMPATHD); fi + # +- cp atop.service $(DESTDIR)$(SYSDPATH) +- chmod 0644 $(DESTDIR)$(SYSDPATH)/atop.service +- cp atopacct.service $(DESTDIR)$(SYSDPATH) +- chmod 0644 $(DESTDIR)$(SYSDPATH)/atopacct.service + cp atop.cronsystemd $(DESTDIR)$(CRNPATH)/atop + cp atop-pm.sh $(DESTDIR)$(PMPATHD) + chmod 0711 $(DESTDIR)$(PMPATHD)/atop-pm.sh diff --git a/debian/patches/force-reload b/debian/patches/force-reload new file mode 100644 index 0000000..d8c4eb2 --- /dev/null +++ b/debian/patches/force-reload @@ -0,0 +1,28 @@ +Description: force-reload is required, and restart|force-reload should not be a no-op +Author: Marc Haber +Forwarded: via web form +Last-Update: 2016-08-07 +--- a/atop.init ++++ b/atop.init +@@ -71,7 +71,7 @@ case "$1" in + /usr/share/atop/atop.daily& + ;; + +- restart) ++ restart|force-reload) + /usr/share/atop/atop.daily& + ;; + +--- a/atopacct.init ++++ b/atopacct.init +@@ -77,7 +77,9 @@ case "$1" in + reload) + ;; + +- restart) ++ restart|force-reload) ++ $0 stop ++ $0 start + ;; + + *) diff --git a/debian/patches/init-script-lsb-headers b/debian/patches/init-script-lsb-headers new file mode 100644 index 0000000..6505ea8 --- /dev/null +++ b/debian/patches/init-script-lsb-headers @@ -0,0 +1,21 @@ +Description: Short-Description and Description were swapped +Author: Marc Haber +Forwarded: via web form as general request to review Debian patches +Last-Update: 2016-08-07 +--- a/atopacct.init ++++ b/atopacct.init +@@ -11,10 +11,10 @@ + # Required-Stop: $local_fs + # Default-Start: 2 3 4 5 + # Default-Stop: 0 1 6 +-# Short-Description: This daemon switches on process accounting and +-# transfers the process accounting records 'realtime' +-# to small shadow files to avoid huge disk space usage +-# Description: Process accounting control ++# Description: This daemon switches on process accounting and ++# transfers the process accounting records 'realtime' ++# to small shadow files to avoid huge disk space usage ++# Short-Description: Process accounting control + ### END INIT INFO + + . /lib/lsb/init-functions diff --git a/debian/patches/logrotate-nomail b/debian/patches/logrotate-nomail new file mode 100644 index 0000000..5b5894e --- /dev/null +++ b/debian/patches/logrotate-nomail @@ -0,0 +1,26 @@ +Description: set nomail in logrotate files +Author: Marc Haber +Forwarded: no +Last-Update: 2016-08-11 +--- a/psaccs_atop ++++ b/psaccs_atop +@@ -3,6 +3,8 @@ + missingok + daily + rotate 0 ++ nomail ++ nocompress + ifempty + create 0600 root root + postrotate +--- a/psaccu_atop ++++ b/psaccu_atop +@@ -3,6 +3,8 @@ + missingok + daily + rotate 0 ++ nomail ++ nocompress + ifempty + create 0600 root root + postrotate diff --git a/debian/patches/lsb-init-functions b/debian/patches/lsb-init-functions new file mode 100644 index 0000000..2797f55 --- /dev/null +++ b/debian/patches/lsb-init-functions @@ -0,0 +1,26 @@ +Description: call isb/init-functions for systemd compatibility +Author: Marc Haber +Forwarded: no +Last-Update: 2016-08-07 +--- a/atop.init ++++ b/atop.init +@@ -15,6 +15,8 @@ + # Description: Advanced system and process activity monitor + ### END INIT INFO + ++. /lib/lsb/init-functions ++ + # Check existance of binaries + [ -f /usr/bin/atop ] || exit 0 + +--- a/atopacct.init ++++ b/atopacct.init +@@ -17,6 +17,8 @@ + # Description: Process accounting control + ### END INIT INFO + ++. /lib/lsb/init-functions ++ + # Check existance of binaries + [ -f /usr/sbin/atopacctd ] || exit 0 + diff --git a/debian/patches/makefile-clean b/debian/patches/makefile-clean new file mode 100644 index 0000000..42de7db --- /dev/null +++ b/debian/patches/makefile-clean @@ -0,0 +1,12 @@ +--- a/Makefile ++++ b/Makefile +@@ -44,8 +44,7 @@ netlink.o: netlink.c + $(CC) -I. -Wall -c netlink.c + + clean: +- rm -f *.o +- rm atop atopsar atopacctd ++ rm -f *.o atop atopsar atopacctd + + distr: + rm -f *.o atop diff --git a/debian/patches/no-files-in-var-log b/debian/patches/no-files-in-var-log new file mode 100644 index 0000000..1e417c0 --- /dev/null +++ b/debian/patches/no-files-in-var-log @@ -0,0 +1,15 @@ +Description: don't create dummy files in log dir on package build +Author: Marc Haber +Forwarded: no +Last-Update: 2016-08-07 +--- a/Makefile ++++ b/Makefile +@@ -149,8 +149,6 @@ genericinstall: atop atopacctd + cp man/atopacctd.8 $(DESTDIR)$(MAN8PATH) + cp psaccs_atop $(DESTDIR)$(ROTPATH)/psaccs_atop + cp psaccu_atop $(DESTDIR)$(ROTPATH)/psaccu_atop +- touch $(DESTDIR)$(LOGPATH)/dummy_before +- touch $(DESTDIR)$(LOGPATH)/dummy_after + + ########################################################################## + diff --git a/debian/patches/no-version-symlinks b/debian/patches/no-version-symlinks new file mode 100644 index 0000000..a4fbea3 --- /dev/null +++ b/debian/patches/no-version-symlinks @@ -0,0 +1,15 @@ +Description: do not symlink atop(sar)-$(VERS) +Author: Marc Haber +Forwarded: via web form +Last-Update: 2016-08-07 +--- a/Makefile ++++ b/Makefile +@@ -139,8 +139,6 @@ genericinstall: atop atopacctd + cp atopacctd $(DESTDIR)$(SBINPATH)/atopacctd + chown root $(DESTDIR)$(SBINPATH)/atopacctd + chmod 0700 $(DESTDIR)$(SBINPATH)/atopacctd +- cp atop $(DESTDIR)$(BINPATH)/atop-$(VERS) +- ln -sf atop-$(VERS) $(DESTDIR)$(BINPATH)/atopsar-$(VERS) + cp atop.daily $(DESTDIR)$(SCRPATH) + chmod 0711 $(DESTDIR)$(SCRPATH)/atop.daily + cp man/atop.1 $(DESTDIR)$(MAN1PATH) diff --git a/debian/patches/series b/debian/patches/series new file mode 100644 index 0000000..5932dfc --- /dev/null +++ b/debian/patches/series @@ -0,0 +1,15 @@ +dh_systemd_enable +dh_installinit +lsb-init-functions +systemd-path +atopacct.service +init-script-lsb-headers +force-reload +dependency-on-remote-fs +no-files-in-var-log +no-version-symlinks +var-run +atop-daily +logrotate-nomail +atop-pm +makefile-clean diff --git a/debian/patches/systemd-path b/debian/patches/systemd-path new file mode 100644 index 0000000..930c341 --- /dev/null +++ b/debian/patches/systemd-path @@ -0,0 +1,15 @@ +Description: put system unit in /lib/systemd +Author: Marc Haber +Forwarded: no +Last-Update: 2016-08-07 +--- a/Makefile ++++ b/Makefile +@@ -12,7 +12,7 @@ MAN1PATH = /usr/share/man/man1 + MAN5PATH = /usr/share/man/man5 + MAN8PATH = /usr/share/man/man8 + INIPATH = /etc/init.d +-SYSDPATH = /usr/lib/systemd/system ++SYSDPATH = /lib/systemd/system + CRNPATH = /etc/cron.d + ROTPATH = /etc/logrotate.d + PMPATH1 = /usr/lib/pm-utils/sleep.d diff --git a/debian/patches/var-run b/debian/patches/var-run new file mode 100644 index 0000000..f4bca72 --- /dev/null +++ b/debian/patches/var-run @@ -0,0 +1,217 @@ +Description: replace /var/run with /run +Author: Marc Haber +Forwarded: via web form +Last-Update: 2016-08-07 +--- a/45atoppm ++++ b/45atoppm +@@ -4,7 +4,7 @@ + + LOGPATH=/var/log/atop + BINPATH=/usr/bin +-PIDFILE=/var/run/atop.pid ++PIDFILE=/run/atop.pid + INTERVAL=600 # interval 10 minutes + CURDAY=`date +%Y%m%d` # current date in same format + +--- a/atop.daily ++++ b/atop.daily +@@ -3,7 +3,7 @@ + CURDAY=`date +%Y%m%d` + LOGPATH=/var/log/atop + BINPATH=/usr/bin +-PIDFILE=/var/run/atop.pid ++PIDFILE=/run/atop.pid + INTERVAL=600 # interval 10 minutes + + # verify if atop still runs for daily logging +--- a/atop.init ++++ b/atop.init +@@ -20,7 +20,7 @@ + # Check existance of binaries + [ -f /usr/bin/atop ] || exit 0 + +-PIDFILE=/var/run/atop.pid ++PIDFILE=/run/atop.pid + RETVAL=0 + + # See how we were called. +@@ -35,7 +35,7 @@ case "$1" in + # Start atop + /usr/share/atop/atop.daily& + fi +- touch /var/lock/subsys/atop ++ touch /run/lock/atop + ;; + + stop) +@@ -61,7 +61,7 @@ case "$1" in + + rm $PIDFILE + fi +- rm /var/lock/subsys/atop ++ rm /run/lock/atop + ;; + + status) +--- a/atopacct.init ++++ b/atopacct.init +@@ -54,11 +54,11 @@ case "$1" in + : + else + # Start atopacctd +- rm -rf /var/run/pacct_shadow.d 2> /dev/null ++ rm -rf /run/pacct_shadow.d 2> /dev/null + /usr/sbin/atopacctd + fi + +- touch /var/lock/subsys/atopacctd ++ touch /run/lock/atopacctd + ;; + + stop) +@@ -68,7 +68,7 @@ case "$1" in + then + kill $(ps -e | grep atopacctd$ | sed 's/^ *//' | cut -d' ' -f1) + fi +- rm /var/lock/subsys/atopacctd ++ rm /run/lock/atopacctd + ;; + + status) +--- a/atopacct.service ++++ b/atopacct.service +@@ -6,7 +6,7 @@ Before=atop.service + + [Service] + Type=forking +-PIDFile=/var/run/atopacctd.pid ++PIDFile=/run/atopacctd.pid + ExecStart=/usr/sbin/atopacctd + + [Install] +--- a/atopacctd.c ++++ b/atopacctd.c +@@ -24,7 +24,7 @@ + ** any more. As soon as at least one client is activated again, the + ** atopacctd daemon start writing shadow files again. + ** +-** The directory /var/run is used as the default top-directory. An ++** The directory /run is used as the default top-directory. An + ** alternative top-directory can be specified as command line argument + ** (in that case, also modify /etc/atoprc to inform atop as a client). + ** Below this top-directory the source file pacct_source is created and +--- a/atopacctd.h ++++ b/atopacctd.h +@@ -7,7 +7,7 @@ + /* + ** name of the PID file + */ +-#define PIDFILE "/var/run/atopacctd.pid" ++#define PIDFILE "/run/atopacctd.pid" + + /* + ** directory containing the source accounting file and +@@ -15,7 +15,7 @@ + ** this directory can be overruled by a command line parameter (atopacctd) + ** or by a keyword in the /etc/atoprc file (atop) + */ +-#define PACCTDIR "/var/run" ++#define PACCTDIR "/run" + + /* + ** accounting file (source file to which kernel writes records) +--- a/man/atop.1 ++++ b/man/atop.1 +@@ -2028,7 +2028,7 @@ View the contents of the standard logfil + .SH FILES + .PP + .TP 5 +-.B /var/run/pacct_shadow.d/ ++.B /run/pacct_shadow.d/ + Directory containing the process accounting shadow files that are + used by + .I atop +@@ -2069,7 +2069,7 @@ All binary system and process level data + in compressed format. + .PP + .TP 5 +-.BI /var/run/netatop.log ++.BI /run/netatop.log + File that contains the netpertask structs containing the network + counters of exited processes. These structs are written by the + .I netatopd +--- a/man/atopacctd.8 ++++ b/man/atopacctd.8 +@@ -70,7 +70,7 @@ any more. As soon as at least one client + daemon continues writing shadow files. + .PP + The directory +-.B /var/run ++.B /run + is used as the default topdirectory. + Below this top-directory, the source file + .B pacct_source +@@ -121,17 +121,17 @@ daemon. + .SH FILES + .PP + .TP 5 +-.B /var/run/pacct_source ++.B /run/pacct_source + Regular file to which the kernel writes the process accounting records. + This file will be regularly truncated. + .PP + .TP 5 +-.B /var/run/pacct_shadow.d/current ++.B /run/pacct_shadow.d/current + Regular file containing the sequence number of the current shadow file + and the maximum number of records per shadow file. + .PP + .TP 5 +-.B /var/run/pacct_shadow.d/N.paf ++.B /run/pacct_shadow.d/N.paf + Regular files containing the process accounting records that have + been copied transparently from the source file (N represents a 10-digit + sequence number). +--- a/man/atoprc.5 ++++ b/man/atoprc.5 +@@ -197,7 +197,7 @@ daemon. In this directory, the daemon cr + .B pacct_shadow.d + in which files will be written containing the process accounting records. + The default topdirectory is +-.B /var/run ++.B /run + and this option only has to be specified when the + .B atopacctd + daemon is started with an alternative topdirectory as command line argument. +--- a/netatopd.h ++++ b/netatopd.h +@@ -1,6 +1,6 @@ + #define SEMAKEY 1541961 + +-#define NETEXITFILE "/var/run/netatop.log" ++#define NETEXITFILE "/run/netatop.log" + #define MYMAGIC (unsigned int) 0xfeedb0b0 + + struct naheader { +--- a/psaccs_atop ++++ b/psaccs_atop +@@ -24,7 +24,7 @@ + # stop atop daemon before accounting file + # is rotated + # +- PIDFILE=/var/run/atop.pid ++ PIDFILE=/run/atop.pid + + if [ -e $PIDFILE ] && \ + ps -p `cat $PIDFILE` | grep 'atop$' > /dev/null +--- a/psaccu_atop ++++ b/psaccu_atop +@@ -10,7 +10,7 @@ + then + # if the atop daemon does not run, restart it after + # accounting file is rotated +- PIDFILE=/var/run/atop.pid ++ PIDFILE=/run/atop.pid + + if [ -e $PIDFILE ] && \ + ps -p `cat $PIDFILE` | grep 'atop$' > /dev/null diff --git a/debian/postinst b/debian/postinst new file mode 100644 index 0000000..99f61d8 --- /dev/null +++ b/debian/postinst @@ -0,0 +1,24 @@ +#!/bin/bash + +set -e + +if dpkg-maintscript-helper supports rm_conffile; then + dpkg-maintscript-helper rm_conffile \ + /etc/logrotate.d/atop 1.26-2~ -- "$@" +fi + +case "$1" in + configure) + touch /var/log/atop/dummy_after /var/log/atop/dummy_before + if dpkg --compare-versions "$2" lt-nl 2; then + # updating from a pre-2.0 version which won't read the old log file format + # move away old log file format + DATE="$(date +%Y%m%d)" + if [ -e "/var/log/atop/atop_${DATE}" ]; then + mv "/var/log/atop/atop_${DATE}" "/var/log/atop/atop_${DATE}_pre_2_0" + fi + fi + ;; +esac + +#DEBHELPER# diff --git a/debian/postrm b/debian/postrm new file mode 100644 index 0000000..74fe19b --- /dev/null +++ b/debian/postrm @@ -0,0 +1,16 @@ +#! /bin/bash + +#DEBHELPER# + +set -e + +if dpkg-maintscript-helper supports rm_conffile; then + dpkg-maintscript-helper rm_conffile \ + /etc/logrotate.d/atop 1.26-2~ -- "$@" +fi + +if [ "$1" = "purge" ] +then + rm -rf /var/log/atop +fi + diff --git a/debian/preinst b/debian/preinst new file mode 100644 index 0000000..db95cb3 --- /dev/null +++ b/debian/preinst @@ -0,0 +1,10 @@ +#!/bin/bash + +set -e + +if dpkg-maintscript-helper supports rm_conffile; then + dpkg-maintscript-helper rm_conffile \ + /etc/logrotate.d/atop 1.26-2~ -- "$@" +fi + +#DEBHELPER# diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..28613df --- /dev/null +++ b/debian/rules @@ -0,0 +1,30 @@ +#!/usr/bin/make -f + +%: + dh $@ + +override_dh_auto_clean: + dh_auto_clean + rm -f debian/atop.service debian/atopacct.service debian/atop.init debian/atopacct.init + rm -f atop + +override_dh_systemd_enable: + dh_systemd_enable atop.service + dh_systemd_enable atopacct.service + +override_dh_systemd_start: + dh_systemd_start atop.service + dh_systemd_start atopacct.service + +override_dh_installinit: + dh_installinit --name=atop + dh_installinit --name=atopacct + +override_dh_auto_install: + dh_auto_install + make sysvinstall DESTDIR=$(shell pwd)/debian/atop + make systemdinstall DESTDIR=$(shell pwd)/debian/atop + cp atop.service debian/atop.service + cp atopacct.service debian/atopacct.service + cp atop.init debian/atop.init + cp atopacct.init debian/atopacct.init diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000..163aaf8 --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/debian/watch b/debian/watch new file mode 100644 index 0000000..9c5754a --- /dev/null +++ b/debian/watch @@ -0,0 +1,10 @@ +# Example watch control file for uscan +# Rename this file to "watch" and then you can run the "uscan" command +# to check for upstream updates and more. +# Site Directory Pattern Version Script +version=2 +# the old ftp site +#ftp://ftp.atcomputing.nl/pub/tools/linux/atop-(.*)\.tar\.gz debian uupdate +# the new website +#http://www.atconsultancy.nl/atop/download.html packages/atop-(.*)\.tar\.gz +http://www.atoptool.nl/downloadatop.php download/atop-(.*)\.tar\.gz diff --git a/sources/45atoppm b/sources/45atoppm new file mode 100755 index 0000000..231979d --- /dev/null +++ b/sources/45atoppm @@ -0,0 +1,73 @@ +#!/bin/sh + +. "${PM_FUNCTIONS}" + +LOGPATH=/var/log/atop +BINPATH=/usr/bin +PIDFILE=/var/run/atop.pid +INTERVAL=600 # interval 10 minutes +CURDAY=`date +%Y%m%d` # current date in same format + +# If the system suspends, one final sample will be taken for the logfile +# +suspend_atop() +{ + if [ -e $PIDFILE ] && ps -p `cat $PIDFILE` | grep 'atop$' > /dev/null + then + kill -USR2 `cat $PIDFILE` # final sample and terminate + + CNT=0 + + while ps -p `cat $PIDFILE` > /dev/null + do + let CNT+=1 + + if [ $CNT -gt 5 ] + then + break; + fi + + sleep 1 + done + fi +} + +# If the system resumes, a new atop will be started (similar to boot) +# +resume_atop() +{ + # in case atop is running, stop it + # + if [ -e $PIDFILE ] && ps -p `cat $PIDFILE` | grep 'atop$' > /dev/null + then + kill -TERM `cat $PIDFILE` + rm $PIDFILE + sleep 1 + fi + + # start atop + # + $BINPATH/atop -R -w $LOGPATH/atop_$CURDAY $INTERVAL \ + > $LOGPATH/daily.log 2>&1 & + echo $! > $PIDFILE + + # delete logfiles older than four weeks + # + ( + sleep 3; + find $LOGPATH -name 'atop_*' -mtime +28 -exec rm {} \; + )& + + exit 0 +} + +case "$1" in + hibernate|suspend) + suspend_atop + ;; + thaw|resume) + resume_atop + ;; + *) exit $NA + ;; +esac diff --git a/sources/AUTHOR b/sources/AUTHOR new file mode 100644 index 0000000..c34b373 --- /dev/null +++ b/sources/AUTHOR @@ -0,0 +1 @@ +Gerlof Langeveld diff --git a/sources/COPYING b/sources/COPYING new file mode 100644 index 0000000..5b6e7c6 --- /dev/null +++ b/sources/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + 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.) + +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. + + 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. + + 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 + + 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. + + + Copyright (C) + + 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. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/sources/ChangeLog b/sources/ChangeLog new file mode 100644 index 0000000..29374e2 --- /dev/null +++ b/sources/ChangeLog @@ -0,0 +1,2370 @@ +commit 8ce799fdd3bb50978c00735cc72bbeb2c70d6844 +Author: Gerlof Langeveld +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 +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 +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 +Date: Fri Mar 17 11:25:46 2017 +0100 + + Type in man page + +M man/atop.1 + +commit 630d66700ca38ddda24d1797636d498e1e600866 +Author: Gerlof Langeveld +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +Date: Thu Feb 23 13:52:43 2017 +0100 + + Modified version numbering (without dashes) + +commit e4028912e77cb1b092cae20173b4c3dea34971fe +Author: Gerlof Langeveld +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 +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 +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 +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 +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 +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 +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 +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 +Date: Thu Dec 15 10:13:48 2016 +0100 + + Modified version numbering. + +M mkdistr + +commit 96f0613201da8cb7e8ebd1120dd18f83abc83536 +Author: Gerlof Langeveld +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 +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 +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 +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 +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 +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 +Date: Fri Oct 14 21:34:08 2016 +0200 + + Improved activation and deactivation of process accounting. + +M atopacctd.c + +commit 6484680418827937064c2aba11619369e189d94d +Author: Gerlof Langeveld +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 +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 +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 +Date: Sat Oct 8 14:18:13 2016 +0200 + + Added -r y... flag to usage message. + +M atopsar.c + +commit 2f7369f6911cc0fe6bb79dfa11ea845ebb036ea0 +Author: Gerlof Langeveld +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 +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 +Date: Sat Oct 8 14:05:15 2016 +0200 + + Cosmetic changes in distribution script. + +M mkdistr + +commit b96168f473ca8a69884d900464cfb5f641e18d26 +Author: Gerlof Langeveld +Date: Sat Oct 8 14:03:55 2016 +0200 + + Cosmetic change. + +M various.c + +commit 90a3f939d5db6a41e1352f847f8719a89e5ddf78 +Author: Gerlof Langeveld +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 +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 +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 +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 +Date: Tue Sep 13 18:50:31 2016 +0200 + + Added -r y... flag to usage message. + +M atopsar.c + +commit e721b7194acb0f3de893d22e733f96c93172b5b7 +Author: Gerlof Langeveld +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 +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 +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 +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 +Date: Mon Mar 7 12:15:31 2016 +0100 + + Cosmetic change in distribution script. + +M mkdistr + +commit d600eac843b87cdfb91a6e1807d3aff5915dfdea +Author: Gerlof Langeveld +Date: Wed Jan 13 08:42:58 2016 +0100 + + Cosmetic fix. + +M various.c + +commit c2370bda4689217cefe60d1760d29fe1397436a4 +Author: Gerlof Langeveld +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 +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 +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 +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 +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 +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 +Date: Thu Jun 25 12:54:30 2015 +0200 + + Modified handling of release number. + +M mkdistr + +commit 8becf63a08ba68ece7543512426cffa36e5c9c01 +Author: Gerlof Langeveld +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 +Date: Thu Jun 25 12:53:14 2015 +0200 + + Added some blank fields for interfaces. + +M showlinux.c + +commit 67f5b44cbd2ef5ad3a59e09a6e192af489300a52 +Author: Gerlof Langeveld +Date: Tue Jun 23 16:55:30 2015 +0200 + + Minor bug-fixes. + +M man/atop.1 +M showgeneric.c + +commit 6f0105972808ffd852b833ed81c222f259bc762a +Author: Gerlof Langeveld +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 +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 +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 +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 +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 +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 +Date: Fri May 29 08:42:05 2015 +0200 + + Added buffers for formatting HTTPSTATS-specific output. + +M showlinux.c + +commit 66e4b0326d8663b8cc26cde42c9d1232e1742714 +Author: Gerlof Langeveld +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 +Date: Mon May 18 08:58:41 2015 +0200 + + Rebuild of lost spec file for systemd. + +M atop.specsystemd + +commit 94f3da8d2740aaa3abfa7974afa900d985b8a9be +Author: Gerlof Langeveld +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 +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 +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 +Date: Sat May 9 16:27:17 2015 +0200 + + Support of systemv and systemd RPMs. + +M mkdistr + +commit 0f9e3cfeb31450ced24375631c6130e9fef60ae8 +Author: Gerlof Langeveld +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 +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 +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 +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 +Date: Tue Mar 24 08:36:51 2015 +0100 + + Minor modification related to systemd-based install. + +M Makefile + +commit 04276882956a196e872fca17fb1dd73289eb3d1f +Author: Gerlof Langeveld +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 +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 +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 +Date: Tue Feb 24 09:32:41 2015 +0100 + + Added creation of the SBINPATH directory for atopacctd. + +M Makefile + +commit 688f446ab5fb81dc6c44c065f956913682d1b03f +Author: Gerlof Langeveld +Date: Mon Feb 2 08:32:57 2015 +0100 + + Prepare systemd-based installation. + +A atop.cronsystemd +A atop.cronsysv + +commit ab8db5f44f1b4edff624c4698849e391588e0cbd +Author: Gerlof Langeveld +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 +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 +Date: Fri Oct 17 14:31:27 2014 +0200 + + Minor change. + +M mkdistr + +commit 52e9c4a0f05f76ab81d46dc1f42ac69f5e123ae5 +Author: Gerlof Langeveld +Date: Fri Oct 17 14:28:10 2014 +0200 + + Minor correction in man-page. + +M man/atopacctd.8 + +commit c18d1354898e8871f75dedc8e8427694606b3832 +Author: Gerlof Langeveld +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 +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 +Date: Fri Jul 25 15:31:42 2014 +0200 + + Activate atopacctd before activating the atop daemon. + +M atop.spec + +commit 0d02d0565b6d0df1c22be3237d145b7e0b7e68eb +Author: Gerlof Langeveld +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 +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 +Date: Fri Jul 25 13:48:09 2014 +0200 + + Removed useless empty column for memory statistics. + +M showlinux.c + +commit 38163c1230c5e58890e783dd1cb29e14640e6099 +Author: Gerlof Langeveld +Date: Fri Jul 25 13:47:36 2014 +0200 + + Cosmetic change. + +M atopacctd.c + +commit 636fa8df11c99b3d6468f4c13c830c29ba79638d +Author: Gerlof Langeveld +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 +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 +Date: Fri Jun 20 14:51:53 2014 +0200 + + Add automatic start of atopacctd daemon. + +M Makefile + +commit 0dcc489d0ded0b2d2241cd9a625fd5206ddb7cd6 +Author: Gerlof Langeveld +Date: Fri Jun 20 14:06:08 2014 +0200 + + Add man page of atopacctd. + +M atop.spec +M mkdistr + +commit 5295539f7b7ba1e725d20caf92eb9d28e1dd87ce +Author: Gerlof Langeveld +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 +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 +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 +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 +Date: Fri Jun 13 07:51:55 2014 +0200 + + Cosmetic change. + +M atopacctd.c + +commit f12727d632052e6a82149d6e4804e354f2caae54 +Author: Gerlof Langeveld +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +Date: Wed May 28 12:17:08 2014 +0200 + + Cosmetic change to message. + +M showgeneric.c + +commit dc1ef5d83b72095536cfabd53adcfcfc8ec37a01 +Author: Gerlof Langeveld +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 +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 +Date: Wed May 28 11:01:13 2014 +0200 + + Modified names for RPM files. + +M mkdistr + +commit b71978ea7ec610305a239529345e816e1323a02b +Author: Gerlof Langeveld +Date: Wed May 28 10:19:16 2014 +0200 + + Added Requires and BuildRequires. + +M atop.spec + +commit f6d0eae38d1e9f093d15fb6fa74d353e59efd05d +Author: Gerlof Langeveld +Date: Wed May 28 10:18:35 2014 +0200 + + Cosmetic change. + +M Makefile + +commit 318890acda8a7cab6bba829fa3276e774f1bd4e0 +Author: Gerlof Langeveld +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 +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 +Date: Sat May 24 11:02:18 2014 +0200 + + Increased number of LVMs and disks supported. + +M photosyst.h + +commit e2f7fb5bc45510b2ec14269e71a0b8ca7ae5a454 +Author: Gerlof Langeveld +Date: Fri May 23 23:33:40 2014 +0200 + + Resize message shows new dimensions. + +M showgeneric.c + +commit 0f54b4c1ebca739cda28ae5793475b06e90e0b88 +Author: Gerlof Langeveld +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 +Date: Fri May 23 13:28:20 2014 +0200 + + Useless buffer formatting removed. + +M atopsar.c + +commit e96c1fff1c7a7ca292f3aedaaa28b92d2e7b9629 +Author: Gerlof Langeveld +Date: Fri May 23 13:19:21 2014 +0200 + + Removed double disk entry for 'vd...' disks. + +M photosyst.c + +commit 239bd26a925d54d184248f00df35586d4d8d441d +Author: Gerlof Langeveld +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 +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 +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 +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 +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 +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 +Date: Tue Apr 23 14:42:24 2013 +0200 + + Documentation improvements. + +M man/atop.1 +M showgeneric.c + +commit 05266573ea93a9866121909b4bd61856103e9a28 +Author: Gerlof Langeveld +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 +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 +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 +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 +Date: Wed Nov 21 21:50:55 2012 +0100 + + Corrected value 'curproc' in -P report. + +M atopsar.c + +commit 321e5288151db22e643569f8b71709d1f5da6bf4 +Author: Gerlof Langeveld +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 +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 +Date: Fri Nov 16 21:42:32 2012 +0100 + + Merge branch '2.0.1' + + Conflicts: + mkdistr + mkversion + version.h + +commit 8f5f6ccc03b9b8ebaf8b2d65a18d3b56cb58c483 +Author: Gerlof Langeveld +Date: Fri Nov 16 19:27:04 2012 +0100 + + Version update + +M version.h + +commit 8c17df29c0d6dc82bec807cc2af8c50d69a2fbdb +Author: Gerlof Langeveld +Date: Fri Nov 16 19:23:20 2012 +0100 + + Release modification. + +M version.h + +commit b5f5f9c05343c0b818b2afef291fd27d35aa690c +Author: Gerlof Langeveld +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 +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 +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 +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 +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 +Date: Sat Oct 27 11:53:49 2012 +0200 + + Bug fixes. + +M mkdistr +M mkversion +M version.h + +commit 6f61e0e5dbb3f48fd10d52b34eaac6ca49e19b31 +Author: Gerlof Langeveld +Date: Thu Oct 25 17:22:39 2012 +0200 + + Added update-rc.d + +M Makefile + +commit 643941fd3ff2055fcdcb979cdeeab62cef5771c8 +Author: Gerlof Langeveld +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 +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 +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 +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 +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 +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 +Date: Sun Oct 14 13:29:05 2012 +0200 + + Cosmetic change. + +M mkdistr + +commit f683dfb6c76ff6df2d68031e19d37532843509f9 +Author: Gerlof Langeveld +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 +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 +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 +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 +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 +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 +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 +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 +Date: Mon Jul 23 11:12:33 2012 +0200 + + Removed unused variable 'columnprefix'. + +M Makefile +M showlinux.c + +commit eb5da90ccb0246ba9040c158c82589c9e5fffe39 +Author: Gerlof Langeveld +Date: Mon Jul 23 11:04:45 2012 +0200 + + Minor corrections. + +M Makefile +M mkdistr + +commit 9c0c2287ed60619dc2a688b2e4242c7ded582026 +Author: Gerlof Langeveld +Date: Sun Jul 22 11:30:51 2012 +0200 + + Improved RPM handling. + +M Makefile +M mkdistr + +commit b1bb45f1d9a61fcd81cebf417cccadac68ddde67 +Author: Gerlof Langeveld +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 +Date: Fri Jul 20 15:18:10 2012 +0200 + + Corrected typo in help messages. + +M showgeneric.c + +commit 4ea9f1ab0c500d47ae74544f90353c6190b8e317 +Author: Gerlof Langeveld +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 +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 +Date: Fri Jul 20 12:01:06 2012 +0200 + + Added LSB header. + +M atop.init + +commit d5a9b854674ae451636815e539c4c056365708b2 +Author: Gerlof Langeveld +Date: Fri Jul 20 10:12:31 2012 +0200 + + Clarified comments about the use of coloring. + +M showsys.c + +commit 1b38444596097776f88415091d27efee4f2dee9f +Author: Gerlof Langeveld +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 +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 +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 +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 +Date: Wed Jul 18 11:37:29 2012 +0200 + + Added EMC Power device recognition. + +M photosyst.c + +commit 4aa9c986f8bb469ad38e46db46d7bc1766f4cea0 +Author: Gerlof Langeveld +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 +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 +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 +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 +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 +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 +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 +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 +Date: Wed Jun 13 15:43:55 2012 +0200 + + Cosmetical changes. + +M atop.c +M showsys.c + +commit 428524129548ede14d535a8c2c23ff5e0af2d54c +Author: Gerlof Langeveld +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 +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 +Date: Sat Jun 9 12:28:58 2012 +0200 + + Improved generation of new version. + +A atop.spec +M mkdistr + +commit 2c87df01b06c588ce205c1dcdf8bd30889e27b1b +Author: Gerlof Langeveld +Date: Sat Jun 9 11:07:03 2012 +0200 + + Support statistics for virtio disks (vd*). + +M photosyst.c + +commit c0c9238f3667d4916cad4eeb16baa861de7e4527 +Author: Gerlof Langeveld +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 +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 +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 +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 +Date: Mon May 28 11:21:53 2012 +0200 + + Proper handling of lock added. + +M atop.init + +commit 30165361059b75acbae534b21eee2465727f05f7 +Author: Gerlof Langeveld +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 +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 +Date: Tue Sep 13 07:52:15 2011 +0200 + + Color vmlim and vmcom adapted. + +M showsys.c + +commit eff004b034980224bff10baf14eee598fdd5f470 +Author: Gerlof Langeveld +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 +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 +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 +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 +Date: Mon Sep 5 14:04:31 2011 +0200 + + Initial version + +A 45atoppm +A AUTHOR +A COPYING +A ChangeLog +A Makefile +A README +A acctproc.c +A acctproc.h +A atop.c +A atop.cron +A atop.daily +A atop.h +A atop.init +A atopsar.c +A deviate.c +A ifprop.c +A ifprop.h +A man/atop.1 +A man/atoprc.5 +A man/atopsar.1 +A netstats.h +A parseable.c +A parseable.h +A photoproc.c +A photoproc.h +A photosyst.c +A photosyst.h +A procdbase.c +A psaccs_atop +A psaccu_atop +A rawlog.c +A showgeneric.c +A showgeneric.h +A showlinux.c +A showlinux.h +A showprocs.c +A showsys.c +A various.c +A version.c diff --git a/sources/Makefile b/sources/Makefile new file mode 100644 index 0000000..24cb089 --- /dev/null +++ b/sources/Makefile @@ -0,0 +1,181 @@ +# Makefile for System & Process Monitor ATOP (Linux version) +# +# Gerlof Langeveld - gerlof.langeveld@atoptool.nl +# +DESTDIR = + +BINPATH = /usr/bin +SBINPATH = /usr/sbin +SCRPATH = /usr/share/atop +LOGPATH = /var/log/atop +MAN1PATH = /usr/share/man/man1 +MAN5PATH = /usr/share/man/man5 +MAN8PATH = /usr/share/man/man8 +INIPATH = /etc/init.d +SYSDPATH = /usr/lib/systemd/system +CRNPATH = /etc/cron.d +ROTPATH = /etc/logrotate.d +PMPATH1 = /usr/lib/pm-utils/sleep.d +PMPATH2 = /usr/lib64/pm-utils/sleep.d +PMPATHD = /usr/lib/systemd/system-sleep + +CFLAGS += -O2 -I. -Wall # -DHTTPSTATS +OBJMOD0 = version.o +OBJMOD1 = various.o deviate.o procdbase.o +OBJMOD2 = acctproc.o photoproc.o photosyst.o rawlog.o ifprop.o parseable.o +OBJMOD3 = showgeneric.o showlinux.o showsys.o showprocs.o +OBJMOD4 = atopsar.o netatopif.o +ALLMODS = $(OBJMOD0) $(OBJMOD1) $(OBJMOD2) $(OBJMOD3) $(OBJMOD4) + +VERS = $(shell ./atop -V 2>/dev/null| sed -e 's/^[^ ]* //' -e 's/ .*//') + +all: atop atopsar atopacctd + +atop: atop.o $(ALLMODS) Makefile + $(CC) atop.o $(ALLMODS) -o atop -lncurses -lz -lm -lrt $(LDFLAGS) + +atopsar: atop + ln -sf atop atopsar + +atopacctd: atopacctd.o netlink.o + $(CC) atopacctd.o netlink.o -o atopacctd $(LDFLAGS) + +netlink.o: netlink.c + $(CC) -I. -Wall -c netlink.c + +clean: + rm -f *.o + rm atop atopsar atopacctd + +distr: + rm -f *.o atop + tar czvf /tmp/atop.tar.gz * + + +install: + @echo Choose either \'make systemdinstall\' or \'make sysvinstall\' + +systemdinstall: genericinstall + if [ ! -d $(DESTDIR)$(SYSDPATH) ]; \ + then mkdir -p $(DESTDIR)$(SYSDPATH); fi + if [ ! -d $(DESTDIR)$(PMPATHD) ]; \ + then mkdir -p $(DESTDIR)$(PMPATHD); fi + # + cp atop.service $(DESTDIR)$(SYSDPATH) + chmod 0644 $(DESTDIR)$(SYSDPATH)/atop.service + cp atopacct.service $(DESTDIR)$(SYSDPATH) + chmod 0644 $(DESTDIR)$(SYSDPATH)/atopacct.service + cp atop.cronsystemd $(DESTDIR)$(CRNPATH)/atop + cp atop-pm.sh $(DESTDIR)$(PMPATHD) + chmod 0711 $(DESTDIR)$(PMPATHD)/atop-pm.sh + # + # only when making on target system: + # + if [ -z "$(DESTDIR)" -a -f /bin/systemctl ]; \ + then /bin/systemctl stop atop 2> /dev/null; \ + /bin/systemctl disable atop 2> /dev/null; \ + /bin/systemctl stop atopacct 2> /dev/null; \ + /bin/systemctl disable atopacct 2> /dev/null; \ + /bin/systemctl enable atopacct; \ + /bin/systemctl start atopacct; \ + /bin/systemctl enable atop; \ + /bin/systemctl start atop; \ + fi + +sysvinstall: genericinstall + if [ ! -d $(DESTDIR)$(INIPATH) ]; \ + then mkdir -p $(DESTDIR)$(INIPATH); fi + # + cp atop.init $(DESTDIR)$(INIPATH)/atop + cp atopacct.init $(DESTDIR)$(INIPATH)/atopacct + cp atop.cronsysv $(DESTDIR)$(CRNPATH)/atop + # + if [ -d $(DESTDIR)$(PMPATH1) ]; \ + then cp 45atoppm $(DESTDIR)$(PMPATH1); \ + chmod 0711 $(DESTDIR)$(PMPATH1)/45atoppm; \ + fi + if [ -d $(DESTDIR)$(PMPATH2) ]; \ + then cp 45atoppm $(DESTDIR)$(PMPATH2); \ + chmod 0711 $(DESTDIR)$(PMPATH2)/45atoppm; \ + fi + # + # + # only when making on target system: + # + if [ -z "$(DESTDIR)" -a -f /sbin/chkconfig ]; \ + then /sbin/chkconfig --del atop 2> /dev/null; \ + /sbin/chkconfig --add atop; \ + /sbin/chkconfig --del atopacct 2> /dev/null; \ + /sbin/chkconfig --add atopacct; \ + fi + if [ -z "$(DESTDIR)" -a -f /usr/sbin/update-rc.d ]; \ + then update-rc.d atop defaults; \ + update-rc.d atopacct defaults; \ + fi + if [ -z "$(DESTDIR)" -a -f /sbin/service ]; \ + then /sbin/service atopacct start; \ + sleep 2; \ + /sbin/service atop start; \ + fi + +genericinstall: atop atopacctd + if [ ! -d $(DESTDIR)$(LOGPATH) ]; \ + then mkdir -p $(DESTDIR)$(LOGPATH); fi + if [ ! -d $(DESTDIR)$(BINPATH) ]; \ + then mkdir -p $(DESTDIR)$(BINPATH); fi + if [ ! -d $(DESTDIR)$(SBINPATH) ]; \ + then mkdir -p $(DESTDIR)$(SBINPATH); fi + if [ ! -d $(DESTDIR)$(SCRPATH) ]; \ + then mkdir -p $(DESTDIR)$(SCRPATH); fi + if [ ! -d $(DESTDIR)$(MAN1PATH) ]; \ + then mkdir -p $(DESTDIR)$(MAN1PATH); fi + if [ ! -d $(DESTDIR)$(MAN5PATH) ]; \ + then mkdir -p $(DESTDIR)$(MAN5PATH); fi + if [ ! -d $(DESTDIR)$(MAN8PATH) ]; \ + then mkdir -p $(DESTDIR)$(MAN8PATH); fi + if [ ! -d $(DESTDIR)$(CRNPATH) ]; \ + then mkdir -p $(DESTDIR)$(CRNPATH); fi + if [ ! -d $(DESTDIR)$(ROTPATH) ]; \ + then mkdir -p $(DESTDIR)$(ROTPATH); fi + # + cp atop $(DESTDIR)$(BINPATH)/atop + chown root $(DESTDIR)$(BINPATH)/atop + chmod 04711 $(DESTDIR)$(BINPATH)/atop + ln -sf atop $(DESTDIR)$(BINPATH)/atopsar + cp atopacctd $(DESTDIR)$(SBINPATH)/atopacctd + chown root $(DESTDIR)$(SBINPATH)/atopacctd + chmod 0700 $(DESTDIR)$(SBINPATH)/atopacctd + cp atop $(DESTDIR)$(BINPATH)/atop-$(VERS) + ln -sf atop-$(VERS) $(DESTDIR)$(BINPATH)/atopsar-$(VERS) + cp atop.daily $(DESTDIR)$(SCRPATH) + chmod 0711 $(DESTDIR)$(SCRPATH)/atop.daily + cp man/atop.1 $(DESTDIR)$(MAN1PATH) + cp man/atopsar.1 $(DESTDIR)$(MAN1PATH) + cp man/atoprc.5 $(DESTDIR)$(MAN5PATH) + cp man/atopacctd.8 $(DESTDIR)$(MAN8PATH) + cp psaccs_atop $(DESTDIR)$(ROTPATH)/psaccs_atop + cp psaccu_atop $(DESTDIR)$(ROTPATH)/psaccu_atop + touch $(DESTDIR)$(LOGPATH)/dummy_before + touch $(DESTDIR)$(LOGPATH)/dummy_after + +########################################################################## + +atop.o: atop.h photoproc.h photosyst.h acctproc.h showgeneric.h +atopsar.o: atop.h photoproc.h photosyst.h +rawlog.o: atop.h photoproc.h photosyst.h showgeneric.h +various.o: atop.h acctproc.h +ifprop.o: atop.h photosyst.h ifprop.h +parseable.o: atop.h photoproc.h photosyst.h parseable.h +deviate.o: atop.h photoproc.h photosyst.h +procdbase.o: atop.h photoproc.h +acctproc.o: atop.h photoproc.h atopacctd.h acctproc.h netatop.h +netatopif.o: atop.h photoproc.h netatopd.h netatop.h +photoproc.o: atop.h photoproc.h +photosyst.o: atop.h photosyst.h +showgeneric.o: atop.h photoproc.h photosyst.h showgeneric.h showlinux.h +showlinux.o: atop.h photoproc.h photosyst.h showgeneric.h showlinux.h +showsys.o: atop.h photoproc.h photosyst.h showgeneric.h +showprocs.o: atop.h photoproc.h photosyst.h showgeneric.h showlinux.h +version.o: version.h + +atopacctd.o: atop.h photoproc.h acctproc.h version.h atopacctd.h diff --git a/sources/README b/sources/README new file mode 100644 index 0000000..58f689f --- /dev/null +++ b/sources/README @@ -0,0 +1,59 @@ +INSTALLING AND USING ATOP + +For interactive use, it is sufficient to install ATOP with the command +(as root): + + make systemdinstall + + or + + make sysvinstall + +For automatic logging in compressed binary format, see the +description in the manual-page. + +The kernel module 'netatop' can be downloaded and installed separately +from www.atoptool.nl/downloadnetatop.php +This module is optional and can be used to gather network statistics +per process/thread as described in www.atoptool.nl/netatop.php + + +KERNEL ISSUES WITH PROCESS ACCOUNTING +------------------------------------- + +Newer upstream kernels (e.g. 4.8 and 4.9)have two issuess +with process accounting: + +1) Sometimes process accounting does not work at all¹. Atopacctd tries to +work around this issue, by retrying to initialize process accounting +several times. + +2) When using the NETLINK inface, the command TASKSTATS_CMD_GET +consequently returns -EINVAL². Atopacctd needs NETLINK to be able to +be triggered that some process in the system has finished. In this way, +atopacctd can be in a blocking state as long as no processes terminate. +When atopacctd detects that NETLINK fails, it switches into a polling +mode to periodically try if it can read process accounting records as +a workaround. This issue has to do with cpumasks and you can work-around +it by building a kernel that has CONFIG_NR_CPUS configured to exactly +the amount of CPUs (logical CPUs) in the system the kernel runs on. You +can find this kernel option under "Processor type and features" --> +"Maximum number of CPUs". + + +[1] Bug 190271 - process accounting sometimes does not work +https://bugzilla.kernel.org/show_bug.cgi?id=190271 + +[2] Bug 190711 - Process accounting: Using the NETLINK interface, +the command TASKSTATS_CMD_GET returns -EINVAL +https://bugzilla.kernel.org/show_bug.cgi?id=190711 + +Linux kernel mailing list thread: + +[REGRESSION] Two issues that prevent process accounting (taskstats) from +working correctly +https://lkml.org/lkml/2016/12/19/182 + + +Gerlof Langeveld +gerlof.langeveld@atoptool.nl diff --git a/sources/acctproc.c b/sources/acctproc.c new file mode 100644 index 0000000..e7ae34d --- /dev/null +++ b/sources/acctproc.c @@ -0,0 +1,1104 @@ +/* +** ATOP - System & Process Monitor +** +** The program 'atop' offers the possibility to view the activity of +** the system on system-level as well as process-level. +** +** This source-file contains functions to manipulate with process-accounting +** features (switch it on/off and read the process-accounting records). +** ================================================================ +** Author: Gerlof Langeveld +** E-mail: gerlof.langeveld@atoptool.nl +** Date: November 1996 +** LINUX-port: June 2000 +** +** This program is free software; you can redistribute it and/or modify it +** under the terms of the GNU General Public License as published by the +** Free Software Foundation; either version 2, or (at your option) any +** later version. +** +** This program is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +** See the GNU General Public License for more details. +** +** $Log: acctproc.c,v $ +** Revision 1.28 2010/04/23 12:20:19 gerlof +** Modified mail-address in header. +** +** Revision 1.27 2009/12/12 10:12:01 gerlof +** Register and display end date and end time for process. +** +** Revision 1.26 2008/03/06 08:37:25 gerlof +** Register/show ppid of a process. +** +** Revision 1.25 2008/01/14 09:22:23 gerlof +** Support for environment variable ATOPACCT to specify the name of a +** particular process accouting file. +** +** Revision 1.24 2007/08/17 08:50:26 gerlof +** Verify if private accounting used before switching off accounting. +** +** Revision 1.23 2007/03/20 13:01:51 gerlof +** Introduction of variable supportflags. +** +** Revision 1.22 2007/02/13 09:14:49 gerlof +** New boolean introduced to indicate if accounting is active. +** +** Revision 1.21 2006/02/07 06:46:15 gerlof +** Removed swap-counter. +** +** Revision 1.20 2005/11/04 14:17:10 gerlof +** Improved recognition of certain version process accounting file. +** +** Revision 1.19 2005/10/31 12:44:58 gerlof +** Support account-record version 3 (used by Mandriva). +** +** Revision 1.18 2005/10/31 08:55:05 root +** *** empty log message *** +** +** Revision 1.17 2004/12/14 15:05:00 gerlof +** Implementation of patch-recognition for disk and network-statistics. +** +** Revision 1.16 2004/06/01 11:57:22 gerlof +** Consider other standard accounting-files, i.e. /var/account/pacct. +** +** Revision 1.15 2004/05/06 09:48:21 gerlof +** Ported to kernel-version 2.6. +** +** Revision 1.14 2003/07/07 09:26:15 gerlof +** Cleanup code (-Wall proof). +** +** Revision 1.13 2003/07/02 06:43:11 gerlof +** Modified include-file sys/acct.h to linux/acct.h to make it +** work on Alpha-based systems as well. +** +** Revision 1.12 2003/06/27 12:31:24 gerlof +** Adapt long to long long. +** +** Revision 1.11 2003/04/03 08:32:58 gerlof +** Cosmetic changes. +** +** Revision 1.10 2003/01/14 09:01:45 gerlof +** Small cosmetic changes. +** +** Revision 1.9 2002/10/03 11:12:03 gerlof +** Modify (effective) uid/gid to real uid/gid. +** +** Revision 1.8 2002/07/24 11:11:12 gerlof +** Redesigned to ease porting to other UNIX-platforms. +** +** Revision 1.7 2002/07/11 07:28:29 root +** Some additions related to secure accounting file handling +** +** Revision 1.6 2002/07/08 09:14:54 root +** Modified secure handling of accounting file +** (inspired by Tobias Rittweiler). +** +** Revision 1.5 2001/11/07 09:16:55 gerlof +** Allow users to run atop without process-accounting switched on. +** +** Revision 1.4 2001/10/16 06:15:52 gerlof +** Partly redesigned. +** +** Revision 1.3 2001/10/04 13:02:24 gerlof +** Redesign of the way to determine how many atop's are using process-accounting +** on basis of semaphores. +** +** Revision 1.2 2001/10/04 08:46:46 gerlof +** Improved verification of kernel-symbol addresses +** +** Revision 1.1 2001/10/02 10:38:35 gerlof +** Initial revision +** +*/ + +static const char rcsid[] = "$Id: acctproc.c,v 1.28 2010/04/23 12:20:19 gerlof Exp $"; + +#define _FILE_OFFSET_BITS 64 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "atop.h" +#include "photoproc.h" +#include "acctproc.h" +#include "atopacctd.h" + +#define ACCTDIR "/var/cache/atop.d" +#define ACCTFILE "atop.acct" +#define ACCTENV "ATOPACCT" + +static char acctatop; /* boolean: atop's own accounting busy ? */ +static off_t acctsize; /* previous size of account file */ +static int acctrecsz; /* size of account record */ +static int acctversion; /* version of account record layout */ +static int acctfd = -1; /* fd of account file (-1 = not open) */ + +static long curshadowseq; /* current shadow file sequence number */ +static long maxshadowrec; /* number of records per shadow file */ + +static char *pacctdir = PACCTDIR; + +static count_t acctexp (comp_t ct); +static int acctvers(int); +static void acctrestarttrial(); +static void switchshadow(void); + +struct pacctadm { + char *name; + struct stat stat; +} pacctadm[] = { + { "/var/log/pacct", {0, }, }, + { "/var/account/pacct", {0, }, } +}; + +/* +** Semaphore-handling +** +** A semaphore-group with two semaphores is created +** +** 0 - Binary semaphore (mutex) to get access to active atop-counter +** +** 1 - Active atop-counter (inverted). +** This semaphore is initialized at some high value and is +** decremented by every atop-incarnation which uses the private +** accounting-file and incremented as soon as such atop stops again. +** If an atop-incarnation stops and it appears to be the last one +** using the private accounting-file, accounting is stopped +** and the file removed +** (Yes, I know: it's not proper usage of the semaphore-principle). +*/ +#define ATOPACCTKEY 3121959 +#define ATOPACCTTOT 100 + +struct sembuf semclaim = {0, -1, SEM_UNDO}, + semrelse = {0, +1, SEM_UNDO}, + semdecre = {1, -1, SEM_UNDO}, + semincre = {1, +1, SEM_UNDO}; + +/* +** switch on the process-accounting mechanism +** +** return value: +** 0 - activated (success) +** 1 - not activated: unreadable accounting file +** 2 - not activated: empty environment variable ATOPACCT +** 3 - not activated: no access to semaphore group +** 4 - not activated: impossible to create own accounting file +** 5 - not activated: no root privileges +*/ +int +acctswon(void) +{ + int i, j, sematopid, sempacctpubid; + static ushort vals[] = {1, ATOPACCTTOT}; + union {ushort *array;} arg = {vals}; + struct stat statbuf; + char *ep; + + /* + ** when a particular environment variable is present, atop should + ** use a specific accounting-file (as defined by the environment + ** variable) or should use no process accounting at all (when + ** contents of environment variable is empty) + */ + if ( (ep = getenv(ACCTENV)) ) + { + /* + ** environment variable exists + */ + if (*ep) + { + /* + ** open active account file with the specified name + */ + if (! droprootprivs() ) + cleanstop(42); + + if ( (acctfd = open(ep, O_RDONLY) ) == -1) + return 1; + + if ( !acctvers(acctfd) ) + { + (void) close(acctfd); + acctfd = -1; + return 1; + } + + supportflags |= ACCTACTIVE; + return 0; + } + else + { + /* + ** no contents + */ + return 2; + } + } + + /* + ** when the atopacctd daemon is active on this system, + ** it should be the preferred way to read the accounting records + */ + if ( (sempacctpubid = semget(PACCTPUBKEY, 0, 0)) != -1) + { + FILE *cfp; + char shadowpath[128]; + struct flock flock; + + if (! droprootprivs() ) + cleanstop(42); + + (void) semop(sempacctpubid, &semclaim, 1); + + snprintf(shadowpath, sizeof shadowpath, "%s/%s/%s", + pacctdir, PACCTSHADOWD, PACCTSHADOWC); + + if ( (cfp = fopen(shadowpath, "r")) ) + { + if (fscanf(cfp, "%ld/%lu", + &curshadowseq, &maxshadowrec) == 2) + { + fclose(cfp); + + snprintf(shadowpath, sizeof shadowpath, + PACCTSHADOWF, pacctdir, + PACCTSHADOWD, curshadowseq); + + if ( (acctfd = open(shadowpath, O_RDONLY))!=-1) + { + if ( !acctvers(acctfd) ) + { + int maxcnt = 40; + + if ( fork() == 0 ) + exit(0); + + (void) wait((int *) 0); + + while ( !acctvers(acctfd) && + --maxcnt) + usleep(50000); + + if (!acctversion) + { + (void) close(acctfd); + acctfd = -1; + + semop(sempacctpubid, + &semrelse, 1); + + regainrootprivs(); + return 1; + } + } + + /* + ** set read lock on current shadow file + */ + flock.l_type = F_RDLCK; + flock.l_whence = SEEK_SET; + flock.l_start = 0; + flock.l_len = 1; + + if ( fcntl(acctfd, F_SETLK, &flock) + != -1) + { + supportflags |= ACCTACTIVE; + regainrootprivs(); + return 0; + } + + (void) close(acctfd); + } + } + else + { + fclose(cfp); + maxshadowrec = 0; + } + } + + (void) semop(sempacctpubid, &semrelse, 1); + } + + /* + ** check if process accounting is already switched on + ** for one of the standard accounting-files; in that case we + ** open this file and get our info from here .... + */ + for (i=0, j=0; i < sizeof pacctadm/sizeof pacctadm[0]; i++) + { + if ( stat(pacctadm[i].name, &pacctadm[i].stat) == 0) + j++; + } + + if (j) + { + /* + ** at least one file is present; check if it is really in use + ** at this moment for accounting by forking a child-process + ** which immediately finishes to force a new accounting-record + */ + if ( fork() == 0 ) + exit(0); /* let the child finish */ + + (void) wait((int *) 0); /* let the parent wait */ + + for (i=0; i < sizeof pacctadm/sizeof pacctadm[0]; i++) + { + if ( stat(pacctadm[i].name, &statbuf) == 0) + { + /* + ** accounting-file has grown? + */ + if (statbuf.st_size > pacctadm[i].stat.st_size) + { + /* + ** open active account file + */ + if ( (acctfd = open(pacctadm[i].name, + O_RDONLY) ) == -1) + return 1; + + if ( !acctvers(acctfd) ) + { + (void) close(acctfd); + acctfd = -1; + return 1; + } + + supportflags |= ACCTACTIVE; + return 0; + } + } + } + } + + /* + ** process-accounting is not yet switched on in a standard way; + ** check if another atop has switched on accounting already + ** + ** first try to create a semaphore group exclusively; if this succeeds, + ** this is the first atop-incarnation since boot and the semaphore group + ** should be initialized` + */ + if ( (sematopid = semget(ATOPACCTKEY, 2, 0600|IPC_CREAT|IPC_EXCL)) >= 0) + (void) semctl(sematopid, 0, SETALL, arg); + else + sematopid = semget(ATOPACCTKEY, 0, 0); + + /* + ** check if we got access to the semaphore group + */ + if (sematopid == -1) + return 3; + + /* + ** the semaphore group is opened now; claim exclusive rights + */ + (void) semop(sematopid, &semclaim, 1); + + /* + ** are we the first to use the accounting-mechanism ? + */ + if (semctl(sematopid, 1, GETVAL, 0) == ATOPACCTTOT) + { + /* + ** create a new separate directory below /tmp + ** for the accounting file; + ** if this directory exists (e.g. previous atop-run killed) + ** it will be cleaned and newly created + */ + if ( mkdir(ACCTDIR, 0700) == -1) + { + if (errno == EEXIST) + { + (void) acct(0); + (void) unlink(ACCTDIR "/" ACCTFILE); + (void) rmdir(ACCTDIR); + } + + if ( mkdir(ACCTDIR, 0700) == -1) + { + /* + ** persistent failure + */ + (void) semop(sematopid, &semrelse, 1); + return 4; + } + } + + /* + ** create accounting file in new directory + */ + (void) close( creat(ACCTDIR "/" ACCTFILE, 0600) ); + + /* + ** switch on accounting + */ + if ( acct(ACCTDIR "/" ACCTFILE) < 0) + { + (void) unlink(ACCTDIR "/" ACCTFILE); + (void) rmdir(ACCTDIR); + (void) semop(sematopid, &semrelse, 1); + + return 5; + } + } + + /* + ** accounting is switched on now; + ** open accounting-file + */ + if ( (acctfd = open(ACCTDIR "/" ACCTFILE, O_RDONLY) ) < 0) + { + (void) acct(0); + (void) unlink(ACCTDIR "/" ACCTFILE); + (void) rmdir(ACCTDIR); + + (void) semop(sematopid, &semrelse, 1); + return 1; + } + + /* + ** accounting successfully switched on + */ + (void) semop(sematopid, &semdecre, 1); + (void) semop(sematopid, &semrelse, 1); + + acctatop = 1; + + /* + ** determine version of accounting-record + */ + (void) fstat(acctfd, &statbuf); + + if (statbuf.st_size == 0) /* no acct record written yet */ + { + /* + ** let's write one record + */ + if ( fork() == 0 ) + exit(0); /* let the child finish */ + + (void) wait((int *) 0); /* let the parent wait */ + } + + if ( !acctvers(acctfd) ) + { + (void) acct(0); + (void) close(acctfd); + (void) unlink(ACCTDIR "/" ACCTFILE); + (void) rmdir(ACCTDIR); + + acctfd = -1; + return 1; + + } + + supportflags |= ACCTACTIVE; + return 0; +} + +/* +** determine the version of the accounting-record layout/length +** and reposition the seek-pointer to the end of the accounting file +*/ +static int +acctvers(int fd) +{ + struct acct tmprec; + + /* + ** read first record from accounting file to verify + ** the second byte (always contains version number) + */ + if ( read(fd, &tmprec, sizeof tmprec) < sizeof tmprec) + return 0; + + switch (tmprec.ac_version & 0x0f) + { + case 2: + acctrecsz = sizeof(struct acct); + acctversion = 2; + break; + + case 3: + acctrecsz = sizeof(struct acct_v3); + acctversion = 3; + break; + + default: + fprintf(stderr, "Unknown format of process accounting file\n"); + cleanstop(8); + } + + /* + ** accounting successfully switched on + */ + acctsize = acctprocnt() * acctrecsz; + + /* + ** reposition to actual file-size + */ + (void) lseek(fd, acctsize, SEEK_SET); + + return 1; +} + +/* +** switch off the process-accounting mechanism +*/ +void +acctswoff(void) +{ + int sematopid; + struct stat before, after; + + /* + ** if accounting not supported, skip call + */ + if (acctfd == -1) + return; + + /* + ** our private accounting-file in use? + */ + if (acctatop) + { + acctatop = 0; + + /* + ** claim the semaphore to get exclusive rights for + ** the accounting-manipulation + */ + sematopid = semget(ATOPACCTKEY, 0, 0); + + (void) semop(sematopid, &semclaim, 1); + (void) semop(sematopid, &semincre, 1); + + /* + ** check if we were the last user of accounting + */ + if (semctl(sematopid, 1, GETVAL, 0) == ATOPACCTTOT) + { + /* + ** switch off private accounting + ** + ** verify if private accounting is still in use to + ** avoid that we switch off process accounting that + ** has been activated manually in the meantime + */ + (void) fstat(acctfd, &before); + + if ( fork() == 0 ) /* create a child and */ + exit(0); /* let the child finish */ + + (void) wait((int *) 0); /* let the parent wait */ + + (void) fstat(acctfd, &after); + + if (after.st_size > before.st_size) + { + /* + ** remove the directory and file + */ + regainrootprivs(); /* get root privs again */ + + (void) acct(0); + (void) unlink(ACCTDIR "/" ACCTFILE); + (void) rmdir(ACCTDIR); + + if (! droprootprivs() ) + cleanstop(42); + } + } + + (void) semop(sematopid, &semrelse, 1); + } + + /* + ** anyhow close the accounting-file again + */ + (void) close(acctfd); /* close account file again */ + acctfd = -1; + + supportflags &= ~ACCTACTIVE; +} + +/* +** get the number of exited processes written +** in the process-account file since the previous sample +*/ +unsigned long +acctprocnt(void) +{ + struct stat statacc; + + /* + ** if accounting not supported, skip call + */ + if (acctfd == -1) + return 0; + + /* + ** determine the current size of the accounting file + */ + (void) fstat(acctfd, &statacc); + + /* + ** handle atopacctd-based process accounting on bases of + ** fixed-chunk shadow files + */ + if (maxshadowrec) + { + unsigned long numrecs = 0; + long newseq; + char shadowpath[128]; + FILE *cfp; + + /* + ** verify how many new processes are added to the current + ** shadow file + */ + numrecs = (statacc.st_size - acctsize) / acctrecsz; + + /* + ** verify if subsequent shadow files are involved + ** (i.e. if the current file is full) + */ + if (statacc.st_size / acctrecsz < maxshadowrec) + return numrecs; + + /* + ** more shadow files available + ** get current shadow file + */ + snprintf(shadowpath, sizeof shadowpath, "%s/%s/%s", + pacctdir, PACCTSHADOWD, PACCTSHADOWC); + + if ( (cfp = fopen(shadowpath, "r")) ) + { + if (fscanf(cfp, "%ld", &newseq) == 1) + { + fclose(cfp); + } + else + { + fclose(cfp); + return numrecs; + } + } + else + { + return numrecs; + } + + if (newseq == curshadowseq) + return numrecs; + + snprintf(shadowpath, sizeof shadowpath, PACCTSHADOWF, + pacctdir, PACCTSHADOWD, newseq); + + /* + ** determine the size of newest shadow file + */ + (void) stat(shadowpath, &statacc); + + numrecs += ((newseq - curshadowseq - 1) * maxshadowrec) + + (statacc.st_size / acctrecsz); + + return numrecs; + } + else + /* + ** classic process accounting on bases of directly opened + ** process accounting file + */ + { + if (acctsize > statacc.st_size) /* accounting reset? */ + { + /* + ** reposition to start of file + */ + (void) lseek(acctfd, 0, SEEK_SET); + acctsize = 0; + } + + return (statacc.st_size - acctsize) / acctrecsz; + } +} + +/* +** reposition the seek offset in the process accounting file to skip +** processes that have not been read +*/ +void +acctrepos(unsigned int noverflow) +{ + /* + ** if accounting not supported, skip call + */ + if (acctfd == -1) + return; + + if (maxshadowrec) + { + int i; + off_t virtoffset = acctsize + noverflow * acctrecsz; + off_t maxshadowsz = maxshadowrec * acctrecsz; + long switches = virtoffset / maxshadowsz; + acctsize = virtoffset % maxshadowsz; + + for (i=0; i < switches; i++) + switchshadow(); + + (void) lseek(acctfd, acctsize, SEEK_SET); + } + else + { + /* + ** just reposition to skip superfluous records + */ + (void) lseek(acctfd, noverflow * acctrecsz, SEEK_CUR); + acctsize += noverflow * acctrecsz; + } +} + + +/* +** read the process records from the process accounting file, +** that are written since the previous cycle +*/ +int +acctphotoproc(struct tstat *accproc, int nrprocs) +{ + register int nrexit; + register struct tstat *api; + struct acct acctrec; + struct acct_v3 acctrec_v3; + struct stat statacc; + + /* + ** if accounting not supported, skip call + */ + if (acctfd == -1) + return 0; + + /* + ** determine the size of the (current) account file + ** and the current offset within that file + */ + (void) fstat(acctfd, &statacc); + + /* + ** check all exited processes in accounting file + */ + for (nrexit=0, api=accproc; nrexit < nrprocs; + nrexit++, api++, acctsize += acctrecsz) + { + /* + ** in case of shadow accounting files, we might have to + ** switch from the current accouting file to the next + */ + if (maxshadowrec && acctsize >= statacc.st_size) + { + switchshadow(); + + /* + ** determine the size of the new (current) account file + ** and initialize the current offset within that file + */ + (void) fstat(acctfd, &statacc); + + acctsize = 0; + } + + /* + ** read the next record + */ + switch (acctversion) + { + case 2: + if ( read(acctfd, &acctrec, acctrecsz) < acctrecsz ) + break; /* unexpected end of account file */ + + /* + ** fill process info from accounting-record + */ + api->gen.state = 'E'; + api->gen.nthr = 1; + api->gen.isproc = 1; + api->gen.pid = 0; + api->gen.tgid = 0; + api->gen.ppid = 0; + api->gen.excode = acctrec.ac_exitcode; + api->gen.ruid = acctrec.ac_uid16; + api->gen.rgid = acctrec.ac_gid16; + api->gen.btime = acctrec.ac_btime; + api->gen.elaps = acctrec.ac_etime; + api->cpu.stime = acctexp(acctrec.ac_stime); + api->cpu.utime = acctexp(acctrec.ac_utime); + api->mem.minflt = acctexp(acctrec.ac_minflt); + api->mem.majflt = acctexp(acctrec.ac_majflt); + api->dsk.rio = acctexp(acctrec.ac_rw); + + strcpy(api->gen.name, acctrec.ac_comm); + break; + + case 3: + if ( read(acctfd, &acctrec_v3, acctrecsz) < acctrecsz ) + break; /* unexpected end of account file */ + + /* + ** fill process info from accounting-record + */ + api->gen.state = 'E'; + api->gen.pid = acctrec_v3.ac_pid; + api->gen.tgid = acctrec_v3.ac_pid; + api->gen.ppid = acctrec_v3.ac_ppid; + api->gen.nthr = 1; + api->gen.isproc = 1; + api->gen.excode = acctrec_v3.ac_exitcode; + api->gen.ruid = acctrec_v3.ac_uid; + api->gen.rgid = acctrec_v3.ac_gid; + api->gen.btime = acctrec_v3.ac_btime; + api->gen.elaps = acctrec_v3.ac_etime; + api->cpu.stime = acctexp(acctrec_v3.ac_stime); + api->cpu.utime = acctexp(acctrec_v3.ac_utime); + api->mem.minflt = acctexp(acctrec_v3.ac_minflt); + api->mem.majflt = acctexp(acctrec_v3.ac_majflt); + api->dsk.rio = acctexp(acctrec_v3.ac_rw); + + strcpy(api->gen.name, acctrec_v3.ac_comm); + break; + } + } + + if (acctsize > ACCTMAXFILESZ && !maxshadowrec) + acctrestarttrial(); + + return nrexit; +} + +/* +** when the size of the private accounting file exceeds a certain limit, +** it might be useful to stop process accounting, truncate the +** process accounting file to zero and start process accounting again +** +** this will only be done if this atop process is the only one +** that is currently using the accounting file +*/ +static void +acctrestarttrial() +{ + struct stat statacc; + int sematopid; + + /* + ** not private accounting-file in use? + */ + if (!acctatop) + return; // do not restart + + /* + ** still remaining records in accounting file that are + ** written between the moment that the number of exited + ** processes was counted and the moment that all processes + ** were read + */ + (void) fstat(acctfd, &statacc); + + if (acctsize != statacc.st_size) + return; // do not restart + + /* + ** claim the semaphore to get exclusive rights for + ** the accounting-manipulation + */ + sematopid = semget(ATOPACCTKEY, 0, 0); + + (void) semop(sematopid, &semclaim, 1); + + /* + ** check if there are more users of accounting file + */ + if (semctl(sematopid, 1, GETVAL, 0) < ATOPACCTTOT-1) + { + (void) semop(sematopid, &semrelse, 1); + return; // do not restart + } + + /* + ** restart is possible + ** + ** - switch off accounting + ** - truncate the file + ** - switch on accounting + */ + regainrootprivs(); // get root privs again + + (void) acct(0); // switch off accounting + + if ( truncate(ACCTDIR "/" ACCTFILE, 0) == 0) + (void) lseek(acctfd, 0, SEEK_SET); + + (void) acct(ACCTDIR "/" ACCTFILE); + + if (! droprootprivs() ) + cleanstop(42); + + acctsize = 0; + + (void) semop(sematopid, &semrelse, 1); +} + +/* +** expand the special compression-methods +*/ +static count_t +acctexp(comp_t ct) +{ + count_t exp; + count_t val; + + exp = (ct >> 13) & 0x7; /* obtain 3 bits base-8 exponent */ + val = ct & 0x1fff; /* obtain 13 bits mantissa */ + + while (exp-- > 0) + val <<= 3; + + return val; +} + +/* +** switch to the next accounting shadow file +*/ +static void +switchshadow(void) +{ + int tmpfd; + char shadowpath[128]; + struct flock flock; + + /* + ** determine path name of new shadow file + */ + curshadowseq++; + + snprintf(shadowpath, sizeof shadowpath, PACCTSHADOWF, + pacctdir, PACCTSHADOWD, curshadowseq); + + /* + ** open new shadow file, while the previous is also kept open + ** (to keep the read lock until a read lock is set for the new + ** shadow file) + */ + if ( (tmpfd = open(shadowpath, O_RDONLY)) != -1) + { + /* + ** set read lock on new shadow file + */ + flock.l_type = F_RDLCK; + flock.l_whence = SEEK_SET; + flock.l_start = 0; + flock.l_len = 1; + + if ( fcntl(tmpfd, F_SETLK, &flock) != -1) + { + (void) close(acctfd); // implicit release read lock + acctfd = tmpfd; + return; + } + else + { + (void) close(tmpfd); + } + } +} + +/* +** handle the option 'pacctdir' in the /etc/atoprc file +*/ +void +do_pacctdir(char *tagname, char *tagvalue) +{ + char shadowpath[128]; + struct stat dirstat; + + /* + ** copy the directory pathname to an own buffer + */ + if ( (pacctdir = malloc(strlen(tagvalue)+1)) == NULL ) + { + perror("malloc pacctdir"); + exit(11); + } + + strcpy(pacctdir, tagvalue); + + /* + ** verify if the atopacctd daemon is active + */ + if ( semget(PACCTPUBKEY, 0, 0) == -1) + { + fprintf(stderr, "Warning: option '%s' specified " + "while atopacctd not running!\n", tagname); + sleep(2); + return; + } + + /* + ** verify that the topdirectory exists + */ + if ( stat(pacctdir, &dirstat) == -1 ) + { + fprintf(stderr, "Warning: option '%s' specified - ", tagname); + perror(pacctdir); + sleep(2); + return; + } + + if (! S_ISDIR(dirstat.st_mode) ) + { + fprintf(stderr, "Warning: option '%s' specified - ", tagname); + fprintf(stderr, "%s not a directory\n", pacctdir); + sleep(2); + return; + } + + /* + ** verify that the topdirectory contains the required subdirectory + */ + snprintf(shadowpath, sizeof shadowpath, "%s/%s", + pacctdir, PACCTSHADOWD); + + if ( stat(shadowpath, &dirstat) == -1 ) + { + fprintf(stderr, "Warning: option '%s' specified - ", tagname); + perror(shadowpath); + sleep(2); + return; + } + + if (! S_ISDIR(dirstat.st_mode) ) + { + fprintf(stderr, "Warning: option '%s' specified - ", tagname); + fprintf(stderr, "%s not a directory\n", shadowpath); + sleep(2); + return; + } +} diff --git a/sources/acctproc.h b/sources/acctproc.h new file mode 100644 index 0000000..66c3a0d --- /dev/null +++ b/sources/acctproc.h @@ -0,0 +1,158 @@ +/* +** ATOP - System & Process Monitor +** +** The program 'atop' offers the possibility to view the activity of +** the system on system-level as well as process-level. +** +** Include-file for process-accounting functions. +** ================================================================ +** Author: Gerlof Langeveld +** E-mail: gerlof.langeveld@atoptool.nl +** Date: November 1996 +** LINUX-port: June 2000 +** +** This program is free software; you can redistribute it and/or modify it +** under the terms of the GNU General Public License as published by the +** Free Software Foundation; either version 2, or (at your option) any +** later version. +** +** This program is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +** See the GNU General Public License for more details. +*/ +int acctswon(void); +void acctswoff(void); +unsigned long acctprocnt(void); +int acctphotoproc(struct tstat *, int); +void acctrepos(unsigned int); + +/* +** maximum number of records to be read from process accounting file +** for one sample, to avoid that atop explodes and introduces OOM killing .... +** +** the maximum is based on a limit of 50 MiB extra memory (approx. 70000 procs) +*/ +#define MAXACCTPROCS (50*1024*1024/sizeof(struct tstat)) + +/* +** preferred maximum size of process accounting file (200 MiB) +*/ +#define ACCTMAXFILESZ (200*1024*1024) + +/* +** alternative layout of accounting record if kernel-patch +** has been installed +*/ +#include +typedef __u16 comp_t; +typedef __u32 comp2_t; +#define ACCT_COMM 16 + +struct acct_atop +{ + char ac_flag; /* Flags */ + char ac_version; /* Always set to ACCT_VERSION */ + __u32 ac_pid; /* Process ID */ + __u32 ac_ppid; /* Parent Process ID */ + __u16 ac_uid16; /* LSB of Real User ID */ + __u16 ac_gid16; /* LSB of Real Group ID */ + __u16 ac_tty; /* Control Terminal */ + __u32 ac_btime; /* Process Creation Time */ + comp_t ac_utime; /* User Time */ + comp_t ac_stime; /* System Time */ + comp_t ac_etime; /* Elapsed Time */ + comp_t ac_mem; /* Virtual Memory */ + comp_t ac_rss; /* Resident Memory */ + comp_t ac_io; /* Chars Transferred */ + comp_t ac_rw; /* Blocks Read or Written */ + comp_t ac_bread; /* Blocks Read */ + comp_t ac_bwrite; /* Blocks Written */ + comp2_t ac_dskrsz; /* Cum. blocks read */ + comp2_t ac_dskwsz; /* Cum. blocks written */ + comp_t ac_tcpsnd; /* TCP send requests */ + comp_t ac_tcprcv; /* TCP recv requests */ + comp2_t ac_tcpssz; /* TCP cum. length */ + comp2_t ac_tcprsz; /* TCP cum. length */ + comp_t ac_udpsnd; /* UDP send requests */ + comp_t ac_udprcv; /* UDP recv requests */ + comp2_t ac_udpssz; /* UDP cum. length */ + comp2_t ac_udprsz; /* UDP cum. length */ + comp_t ac_rawsnd; /* RAW send requests */ + comp_t ac_rawrcv; /* RAW recv requests */ + comp_t ac_minflt; /* Minor Pagefaults */ + comp_t ac_majflt; /* Major Pagefaults */ + comp_t ac_swaps; /* Number of Swaps */ +/* m68k had no padding here. */ +#if !defined(CONFIG_M68K) || !defined(__KERNEL__) + __u16 ac_ahz; /* AHZ */ +#endif + __u32 ac_exitcode; /* Exitcode */ + char ac_comm[ACCT_COMM + 1]; /* Command Name */ + __u8 ac_etime_hi; /* Elapsed Time MSB */ + __u16 ac_etime_lo; /* Elapsed Time LSB */ + __u32 ac_uid; /* Real User ID */ + __u32 ac_gid; /* Real Group ID */ +}; + +/* +** default layout of accounting record +** (copied from /usr/src/linux/include/linux/acct.h) +*/ + +struct acct +{ + char ac_flag; /* Flags */ + char ac_version; /* Always set to ACCT_VERSION */ + /* for binary compatibility back until 2.0 */ + __u16 ac_uid16; /* LSB of Real User ID */ + __u16 ac_gid16; /* LSB of Real Group ID */ + __u16 ac_tty; /* Control Terminal */ + __u32 ac_btime; /* Process Creation Time */ + comp_t ac_utime; /* User Time */ + comp_t ac_stime; /* System Time */ + comp_t ac_etime; /* Elapsed Time */ + comp_t ac_mem; /* Average Memory Usage */ + comp_t ac_io; /* Chars Transferred */ + comp_t ac_rw; /* Blocks Read or Written */ + comp_t ac_minflt; /* Minor Pagefaults */ + comp_t ac_majflt; /* Major Pagefaults */ + comp_t ac_swaps; /* Number of Swaps */ +/* m68k had no padding here. */ +#if !defined(CONFIG_M68K) || !defined(__KERNEL__) + __u16 ac_ahz; /* AHZ */ +#endif + __u32 ac_exitcode; /* Exitcode */ + char ac_comm[ACCT_COMM + 1]; /* Command Name */ + __u8 ac_etime_hi; /* Elapsed Time MSB */ + __u16 ac_etime_lo; /* Elapsed Time LSB */ + __u32 ac_uid; /* Real User ID */ + __u32 ac_gid; /* Real Group ID */ +}; + +struct acct_v3 +{ + char ac_flag; /* Flags */ + char ac_version; /* Always set to ACCT_VERSION */ + __u16 ac_tty; /* Control Terminal */ + __u32 ac_exitcode; /* Exitcode */ + __u32 ac_uid; /* Real User ID */ + __u32 ac_gid; /* Real Group ID */ + __u32 ac_pid; /* Process ID */ + __u32 ac_ppid; /* Parent Process ID */ + __u32 ac_btime; /* Process Creation Time */ +#ifdef __KERNEL__ + __u32 ac_etime; /* Elapsed Time */ +#else + float ac_etime; /* Elapsed Time */ +#endif + comp_t ac_utime; /* User Time */ + comp_t ac_stime; /* System Time */ + comp_t ac_mem; /* Average Memory Usage */ + comp_t ac_io; /* Chars Transferred */ + comp_t ac_rw; /* Blocks Read or Written */ + comp_t ac_minflt; /* Minor Pagefaults */ + comp_t ac_majflt; /* Major Pagefaults */ + comp_t ac_swaps; /* Number of Swaps */ + char ac_comm[ACCT_COMM]; /* Command Name */ +}; diff --git a/sources/atop-pm.sh b/sources/atop-pm.sh new file mode 100755 index 0000000..7f41a86 --- /dev/null +++ b/sources/atop-pm.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +case "$1" in + pre) /usr/bin/systemctl stop atop + exit 0 + ;; + post) /usr/bin/systemctl start atop + exit 0 + ;; + *) exit 1 + ;; +esac diff --git a/sources/atop.c b/sources/atop.c new file mode 100644 index 0000000..30d8d1f --- /dev/null +++ b/sources/atop.c @@ -0,0 +1,1161 @@ +/* +** ATOP - System & Process Monitor +** +** The program 'atop' offers the possibility to view the activity of +** the system on system-level as well as process-level. +** +** This source-file contains the main-function, which verifies the +** calling-parameters and takes care of initialization. +** The engine-function drives the main sample-loop in which after the +** indicated interval-time a snapshot is taken of the system-level and +** process-level counters and the deviations are calculated and +** visualized for the user. +** ========================================================================== +** Author: Gerlof Langeveld +** E-mail: gerlof.langeveld@atoptool.nl +** Date: November 1996 +** Linux-port: June 2000 +** Modified: May 2001 - Ported to kernel 2.4 +** -------------------------------------------------------------------------- +** Copyright (C) 2000-2012 Gerlof Langeveld +** +** This program is free software; you can redistribute it and/or modify it +** under the terms of the GNU General Public License as published by the +** Free Software Foundation; either version 2, or (at your option) any +** later version. +** +** This program is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +** See the GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +** -------------------------------------------------------------------------- +** +** After initialization, the main-function calls the ENGINE. +** For every cycle (so after another interval) the ENGINE calls various +** functions as shown below: +** +** +---------------------------------------------------------------------+ +** | E N G I N E | +** | | +** | | +** | _____________________await interval-timer_____________________ | +** | | ^ | +** | | ________ ________ ________ ________ | | +** | | ^ | ^ | ^ | ^ | | | +** +---|-----|--------|-----|--------|----|--------|----|--------|----|--+ +** | | | | | | | | | | +** +--V-----|--+ +--V-----|--+ +--V----|--+ +--V----|--+ +--V----|-+ +** | | | | | | | | | | +** | photosyst | | photoproc | | acct | | deviate | | print | +** | | | | |photoproc | | ...syst | | | +** | | | | | | | ...proc | | | +** +-----------+ +-----------+ +----------+ +----------+ +---------+ +** ^ ^ ^ ^ | +** | | | | | +** | | | V V +** ______ _________ __________ ________ _________ +** / \ / \ / \ / \ / \ +** /proc /proc accounting task screen or +** file database file +** \______/ \_________/ \__________/ \________/ \_________/ +** +** - photosyst() +** Takes a snapshot of the counters related to resource-usage on +** system-level (cpu, disk, memory, network). +** This code is UNIX-flavor dependent; in case of Linux the counters +** are retrieved from /proc. +** +** - photoproc() +** Takes a snapshot of the counters related to resource-usage of +** tasks which are currently active. For this purpose the whole +** task-list is read. +** This code is UNIX-flavor dependent; in case of Linux the counters +** are retrieved from /proc. +** +** - acctphotoproc() +** Takes a snapshot of the counters related to resource-usage of +** tasks which have been finished during the last interval. +** For this purpose all new records in the accounting-file are read. +** +** When all counters have been gathered, functions are called to calculate +** the difference between the current counter-values and the counter-values +** of the previous cycle. These functions operate on the system-level +** as well as on the task-level counters. +** These differences are stored in a new structure(-table). +** +** - deviatsyst() +** Calculates the differences between the current system-level +** counters and the corresponding counters of the previous cycle. +** +** - deviattask() +** Calculates the differences between the current task-level +** counters and the corresponding counters of the previous cycle. +** The per-task counters of the previous cycle are stored in the +** task-database; this "database" is implemented as a linked list +** of taskinfo structures in memory (so no disk-accesses needed). +** Within this linked list hash-buckets are maintained for fast searches. +** The entire task-database is handled via a set of well-defined +** functions from which the name starts with "pdb_..." (see the +** source-file procdbase.c). +** The processes which have been finished during the last cycle +** are also treated by deviattask() in order to calculate what their +** resource-usage was before they finished. +** +** All information is ready to be visualized now. +** There is a structure which holds the start-address of the +** visualization-function to be called. Initially this structure contains +** the address of the generic visualization-function ("generic_samp"), but +** these addresses can be modified in the main-function depending on particular +** flags. In this way various representation-layers (ASCII, graphical, ...) +** can be linked with 'atop'; the one to use can eventually be chosen +** at runtime. +** +** $Log: atop.c,v $ +** Revision 1.49 2010/10/23 14:01:00 gerlof +** Show counters for total number of running and sleep (S and D) threads. +** +** Revision 1.48 2010/10/23 08:18:15 gerlof +** Catch signal SIGUSR2 to take a final sample and stop. +** Needed for improved of suspend/hibernate. +** +** Revision 1.47 2010/04/23 12:20:19 gerlof +** Modified mail-address in header. +** +** Revision 1.46 2010/04/23 09:57:28 gerlof +** Version (flag -V) handled earlier after startup. +** +** Revision 1.45 2010/04/17 17:19:41 gerlof +** Allow modifying the layout of the columns in the system lines. +** +** Revision 1.44 2010/04/16 13:00:23 gerlof +** Automatically start another version of atop if the logfile to +** be read has not been created by the current version. +** +** Revision 1.43 2010/03/04 10:51:10 gerlof +** Support I/O-statistics on logical volumes and MD devices. +** +** Revision 1.42 2009/12/31 11:33:33 gerlof +** Sanity-check to bypass kernel-bug showing 497 days of CPU-consumption. +** +** Revision 1.41 2009/12/17 10:51:31 gerlof +** Allow own defined process line with key 'o' and a definition +** in the atoprc file. +** +** Revision 1.40 2009/12/17 08:15:15 gerlof +** Introduce branch-key to go to specific time in raw file. +** +** Revision 1.39 2009/12/10 13:34:32 gerlof +** Cosmetical changes. +** +** Revision 1.38 2009/12/10 11:55:38 gerlof +** Introduce -L flag for line length. +** +** Revision 1.37 2009/12/10 10:43:33 gerlof +** Correct calculation of node name. +** +** Revision 1.36 2009/12/10 09:19:06 gerlof +** Various changes related to redesign of user-interface. +** Made by JC van Winkel. +** +** Revision 1.35 2009/11/27 15:11:55 gerlof +** *** empty log message *** +** +** Revision 1.34 2009/11/27 15:07:25 gerlof +** Give up root-priviliges at a earlier stage. +** +** Revision 1.33 2009/11/27 14:01:01 gerlof +** Introduce system-wide configuration file /etc/atoprc +** +** Revision 1.32 2008/01/07 10:16:13 gerlof +** Implement summaries for atopsar. +** +** Revision 1.31 2007/11/06 09:16:05 gerlof +** Add keyword atopsarflags to configuration-file ~/.atoprc +** +** Revision 1.30 2007/08/16 11:58:35 gerlof +** Add support for atopsar reporting. +** +** Revision 1.29 2007/03/20 13:01:36 gerlof +** Introduction of variable supportflags. +** +** Revision 1.28 2007/03/20 12:13:00 gerlof +** Be sure that all tstat struct's are initialized with binary zeroes. +** +** Revision 1.27 2007/02/19 11:55:04 gerlof +** Bug-fix: flag -S was not recognized any more. +** +** Revision 1.26 2007/02/13 10:34:20 gerlof +** Support parseable output with flag -P +** +** Revision 1.25 2007/01/26 12:10:40 gerlof +** Add configuration-value 'swoutcritsec'. +** +** Revision 1.24 2007/01/18 10:29:22 gerlof +** Improved syntax-checking for ~/.atoprc file. +** Support for network-interface busy-percentage. +** +** Revision 1.23 2006/02/07 08:27:04 gerlof +** Cosmetic changes. +** +** Revision 1.22 2005/10/28 09:50:29 gerlof +** All flags/subcommands are defined as macro's. +** +** Revision 1.21 2005/10/21 09:48:48 gerlof +** Per-user accumulation of resource consumption. +** +** Revision 1.20 2004/12/14 15:05:38 gerlof +** Implementation of patch-recognition for disk and network-statistics. +** +** Revision 1.19 2004/10/26 13:42:49 gerlof +** Also lock current physical pages in memory. +** +** Revision 1.18 2004/09/15 08:23:42 gerlof +** Set resource limit for locked memory to infinite, because +** in certain environments it is set to 32K (causes atop-malloc's +** to fail). +** +** Revision 1.17 2004/05/06 09:45:44 gerlof +** Ported to kernel-version 2.6. +** +** Revision 1.16 2003/07/07 09:18:22 gerlof +** Cleanup code (-Wall proof). +** +** Revision 1.15 2003/07/03 11:16:14 gerlof +** Implemented subcommand `r' (reset). +** +** Revision 1.14 2003/06/30 11:29:12 gerlof +** Handle configuration file ~/.atoprc +** +** Revision 1.13 2003/01/14 09:01:10 gerlof +** Explicit clearing of malloced space for exited processes. +** +** Revision 1.12 2002/10/30 13:44:51 gerlof +** Generate notification for statistics since boot. +** +** Revision 1.11 2002/10/08 11:34:52 gerlof +** Modified storage of raw filename. +** +** Revision 1.10 2002/09/26 13:51:47 gerlof +** Limit header lines by not showing disks. +** +** Revision 1.9 2002/09/17 10:42:00 gerlof +** Copy functions rawread() and rawwrite() to separate source-file rawlog.c +** +** Revision 1.8 2002/08/30 07:49:35 gerlof +** Implement possibility to store and retrieve atop-data in raw format. +** +** Revision 1.7 2002/08/27 12:09:16 gerlof +** Allow raw data file to be written and to be read (with compression). +** +** Revision 1.6 2002/07/24 11:12:07 gerlof +** Redesigned to ease porting to other UNIX-platforms. +** +** Revision 1.5 2002/07/11 09:15:53 root +** *** empty log message *** +** +** Revision 1.4 2002/07/08 09:20:45 root +** Bug solution: flag list overflow. +** +** Revision 1.3 2001/11/07 09:17:41 gerlof +** Use /proc instead of /dev/kmem for process-level statistics. +** +** Revision 1.2 2001/10/04 13:03:15 gerlof +** Separate kopen() function called i.s.o. implicit with first kmem-read +** +** Revision 1.1 2001/10/02 10:43:19 gerlof +** Initial revision +** +*/ + +static const char rcsid[] = "$Id: atop.c,v 1.49 2010/10/23 14:01:00 gerlof Exp $"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "atop.h" +#include "acctproc.h" +#include "ifprop.h" +#include "photoproc.h" +#include "photosyst.h" +#include "showgeneric.h" +#include "parseable.h" + +#define allflags "ab:cde:fghijklmnopqrstuvwxyz1ABCDEFGHIJKL:MNOP:QRSTUVWXYZ" +#define PROCCHUNK 100 /* process-entries for future expansion */ +#define MAXFL 64 /* maximum number of command-line flags */ + +/* +** declaration of global variables +*/ +struct utsname utsname; +int utsnodenamelen; +time_t pretime; /* timing info */ +time_t curtime; /* timing info */ +unsigned long interval = 10; +unsigned long sampcnt; +char screen; +int linelen = 80; +char acctreason; /* accounting not active (return val) */ +char rawname[RAWNAMESZ]; +char rawreadflag; +unsigned int begintime, endtime; +char flaglist[MAXFL]; +char deviatonly = 1; +char usecolors = 1; /* boolean: colors for high occupation */ +char threadview = 0; /* boolean: show individual threads */ +char calcpss = 0; /* boolean: read/calculate process PSS */ + +unsigned short hertz; +unsigned int pagesize; +int osrel; +int osvers; +int ossub; + +int supportflags; /* supported features */ +char **argvp; + + +struct visualize vis = {generic_samp, generic_error, + generic_end, generic_usage}; + +/* +** argument values +*/ +static char awaittrigger; /* boolean: awaiting trigger */ +static unsigned int nsamples = 0xffffffff; +static char midnightflag; +static char rawwriteflag; + +/* +** interpretation of defaults-file /etc/atoprc and $HOME/.atop +*/ +static void readrc(char *, int); + +void do_flags(char *, char *); +void do_interval(char *, char *); +void do_linelength(char *, char *); +void do_username(char *, char *); +void do_procname(char *, char *); +void do_maxcpu(char *, char *); +void do_maxdisk(char *, char *); +void do_maxmdd(char *, char *); +void do_maxlvm(char *, char *); +void do_maxintf(char *, char *); +void do_maxnfsm(char *, char *); +void do_maxcont(char *, char *); +void do_colinfo(char *, char *); +void do_colalmost(char *, char *); +void do_colcrit(char *, char *); +void do_colthread(char *, char *); +void do_ownsysprcline(char *, char *); +void do_ownallcpuline(char *, char *); +void do_ownindivcpuline(char *, char *); +void do_owncplline(char *, char *); +void do_ownmemline(char *, char *); +void do_ownswpline(char *, char *); +void do_ownpagline(char *, char *); +void do_owndskline(char *, char *); +void do_ownnettransportline(char *, char *); +void do_ownnetnetline(char *, char *); +void do_ownnetinterfaceline(char *, char *); +void do_ownprocline(char *, char *); +void do_cpucritperc(char *, char *); +void do_memcritperc(char *, char *); +void do_swpcritperc(char *, char *); +void do_dskcritperc(char *, char *); +void do_netcritperc(char *, char *); +void do_swoutcritsec(char *, char *); +void do_almostcrit(char *, char *); +void do_atopsarflags(char *, char *); +void do_pacctdir(char *, char *); + +static struct { + char *tag; + void (*func)(char *, char *); + int sysonly; +} manrc[] = { + { "flags", do_flags, 0, }, + { "interval", do_interval, 0, }, + { "linelen", do_linelength, 0, }, + { "username", do_username, 0, }, + { "procname", do_procname, 0, }, + { "maxlinecpu", do_maxcpu, 0, }, + { "maxlinedisk", do_maxdisk, 0, }, + { "maxlinemdd", do_maxmdd, 0, }, + { "maxlinelvm", do_maxlvm, 0, }, + { "maxlineintf", do_maxintf, 0, }, + { "maxlinenfsm", do_maxnfsm, 0, }, + { "maxlinecont", do_maxcont, 0, }, + { "colorinfo", do_colinfo, 0, }, + { "coloralmost", do_colalmost, 0, }, + { "colorcritical", do_colcrit, 0, }, + { "colorthread", do_colthread, 0, }, + { "ownallcpuline", do_ownallcpuline, 0, }, + { "ownonecpuline", do_ownindivcpuline, 0, }, + { "owncplline", do_owncplline, 0, }, + { "ownmemline", do_ownmemline, 0, }, + { "ownswpline", do_ownswpline, 0, }, + { "ownpagline", do_ownpagline, 0, }, + { "owndskline", do_owndskline, 0, }, + { "ownnettrline", do_ownnettransportline, 0, }, + { "ownnetnetline", do_ownnetnetline, 0, }, + { "ownnetifline", do_ownnetinterfaceline, 0, }, + { "ownprocline", do_ownprocline, 0, }, + { "ownsysprcline", do_ownsysprcline, 0, }, + { "owndskline", do_owndskline, 0, }, + { "cpucritperc", do_cpucritperc, 0, }, + { "memcritperc", do_memcritperc, 0, }, + { "swpcritperc", do_swpcritperc, 0, }, + { "dskcritperc", do_dskcritperc, 0, }, + { "netcritperc", do_netcritperc, 0, }, + { "swoutcritsec", do_swoutcritsec, 0, }, + { "almostcrit", do_almostcrit, 0, }, + { "atopsarflags", do_atopsarflags, 0, }, + { "pacctdir", do_pacctdir, 1, }, +}; + +/* +** internal prototypes +*/ +static void engine(void); + +int +main(int argc, char *argv[]) +{ + register int i; + int c; + char *p; + struct rlimit rlim; + + /* + ** since priviliged actions will be done later on, at this stage + ** the root-priviliges are dropped by switching effective user-id + ** to real user-id (security reasons) + */ + if (! droprootprivs() ) + { + fprintf(stderr, "not possible to drop root privs\n"); + exit(42); + } + + /* + ** preserve command arguments to allow restart of other version + */ + argvp = argv; + + /* + ** read defaults-files /etc/atoprc en $HOME/.atoprc (if any) + */ + readrc("/etc/atoprc", 1); + + if ( (p = getenv("HOME")) ) + { + char path[1024]; + + snprintf(path, sizeof path, "%s/.atoprc", p); + + readrc(path, 0); + } + + /* + ** check if we are supposed to behave as 'atopsar' + ** i.e. system statistics only + */ + if ( (p = strrchr(argv[0], '/'))) + p++; + else + p = argv[0]; + + if ( memcmp(p, "atopsar", 7) == 0) + return atopsar(argc, argv); + + /* + ** interpret command-line arguments & flags + */ + if (argc > 1) + { + /* + ** gather all flags for visualization-functions + ** + ** generic flags will be handled here; + ** unrecognized flags are passed to the print-routines + */ + i = 0; + + while (i < MAXFL-1 && (c=getopt(argc, argv, allflags)) != EOF) + { + switch (c) + { + case '?': /* usage wanted ? */ + prusage(argv[0]); + break; + + case 'V': /* version wanted ? */ + printf("%s\n", getstrvers()); + exit(0); + + case 'w': /* writing of raw data ? */ + rawwriteflag++; + if (optind >= argc) + prusage(argv[0]); + + strncpy(rawname, argv[optind++], RAWNAMESZ-1); + vis.show_samp = rawwrite; + break; + + case 'r': /* reading of raw data ? */ + if (optind < argc && *(argv[optind]) != '-') + strncpy(rawname, argv[optind++], + RAWNAMESZ-1); + + rawreadflag++; + break; + + case 'S': /* midnight limit ? */ + midnightflag++; + break; + + case 'a': /* all processes per sample ? */ + deviatonly = 0; + break; + + case 'R': /* all processes per sample ? */ + calcpss = 1; + break; + + case 'b': /* begin time ? */ + if ( !hhmm2secs(optarg, &begintime) ) + prusage(argv[0]); + break; + + case 'e': /* end time ? */ + if ( !hhmm2secs(optarg, &endtime) ) + prusage(argv[0]); + break; + + case 'P': /* parseable output? */ + if ( !parsedef(optarg) ) + prusage(argv[0]); + + vis.show_samp = parseout; + break; + + case 'L': /* line length */ + if ( !numeric(optarg) ) + prusage(argv[0]); + + linelen = atoi(optarg); + break; + + default: /* gather other flags */ + flaglist[i++] = c; + } + } + + /* + ** get optional interval-value and optional number of samples + */ + if (optind < argc && optind < MAXFL) + { + if (!numeric(argv[optind])) + prusage(argv[0]); + + interval = atoi(argv[optind]); + + optind++; + + if (optind < argc) + { + if (!numeric(argv[optind]) ) + prusage(argv[0]); + + if ( (nsamples = atoi(argv[optind])) < 1) + prusage(argv[0]); + } + } + } + + /* + ** determine the name of this node (without domain-name) + ** and the kernel-version + */ + (void) uname(&utsname); + + if ( (p = strchr(utsname.nodename, '.')) ) + *p = '\0'; + + utsnodenamelen = strlen(utsname.nodename); + + sscanf(utsname.release, "%d.%d.%d", &osrel, &osvers, &ossub); + + /* + ** determine the clock rate and memory page size for this machine + */ + hertz = sysconf(_SC_CLK_TCK); + pagesize = sysconf(_SC_PAGESIZE); + + /* + ** check if raw data from a file must be viewed + */ + if (rawreadflag) + { + rawread(); + cleanstop(0); + } + + /* + ** determine start-time for gathering current statistics + */ + curtime = getboot() / hertz; + + /* + ** be sure to be leader of an own process group when + ** running as a daemon (or at least: when not interactive); + ** needed for systemd + */ + if (rawwriteflag) + (void) setpgid(0, 0); + + /* + ** catch signals for proper close-down + */ + signal(SIGHUP, cleanstop); + signal(SIGTERM, cleanstop); + + /* + ** regain the root-priviliges that we dropped at the beginning + ** to do some priviliged work + */ + regainrootprivs(); + + /* + ** lock ATOP in memory to get reliable samples (also when + ** memory is low); + ** ignored if not running under superuser priviliges! + */ + rlim.rlim_cur = RLIM_INFINITY; + rlim.rlim_max = RLIM_INFINITY; + (void) setrlimit(RLIMIT_MEMLOCK, &rlim); + + (void) mlockall(MCL_CURRENT|MCL_FUTURE); + + /* + ** increment CPU scheduling-priority to get reliable samples (also + ** during heavy CPU load); + ** ignored if not running under superuser priviliges! + */ + if ( nice(-20) == -1) + ; + + /* + ** switch-on the process-accounting mechanism to register the + ** (remaining) resource-usage by processes which have finished + */ + acctreason = acctswon(); + + /* + ** determine properties (like speed) of all interfaces + */ + initifprop(); + + /* + ** open socket to the IP layer to issue getsockopt() calls later on + */ + netatop_ipopen(); + + /* + ** since priviliged activities are finished now, there is no + ** need to keep running under root-priviliges, so switch + ** effective user-id to real user-id + */ + if (! droprootprivs() ) + cleanstop(42); + + /* + ** start the engine now ..... + */ + engine(); + + cleanstop(0); + + return 0; /* never reached */ +} + +/* +** The engine() drives the main-loop of the program +*/ +static void +engine(void) +{ + struct sigaction sigact; + static time_t timelimit; + void getusr1(int), getusr2(int); + + /* + ** reserve space for system-level statistics + */ + static struct sstat *cursstat; /* current */ + static struct sstat *presstat; /* previous */ + static struct sstat *devsstat; /* deviation */ + static struct sstat *hlpsstat; + + /* + ** reserve space for task-level statistics + */ + static struct tstat *curtpres; /* current present list */ + static int curtlen; /* size of present list */ + struct tstat *curpexit; /* exited process list */ + + static struct devtstat devtstat; /* deviation info */ + + unsigned int ntaskpres; /* number of tasks present */ + unsigned int nprocexit; /* number of exited procs */ + unsigned int nprocexitnet; /* number of exited procs */ + /* via netatopd daemon */ + + unsigned int noverflow; + + /* + ** initialization: allocate required memory dynamically + */ + cursstat = calloc(1, sizeof(struct sstat)); + presstat = calloc(1, sizeof(struct sstat)); + devsstat = calloc(1, sizeof(struct sstat)); + + curtlen = countprocs() * 3 / 2; /* add 50% for threads */ + curtpres = calloc(curtlen, sizeof(struct tstat)); + + ptrverify(cursstat, "Malloc failed for current sysstats\n"); + ptrverify(presstat, "Malloc failed for prev sysstats\n"); + ptrverify(devsstat, "Malloc failed for deviate sysstats\n"); + ptrverify(curtpres, "Malloc failed for %d procstats\n", curtlen); + + /* + ** install the signal-handler for ALARM, USR1 and USR2 (triggers + * for the next sample) + */ + memset(&sigact, 0, sizeof sigact); + sigact.sa_handler = getusr1; + sigaction(SIGUSR1, &sigact, (struct sigaction *)0); + + memset(&sigact, 0, sizeof sigact); + sigact.sa_handler = getusr2; + sigaction(SIGUSR2, &sigact, (struct sigaction *)0); + + memset(&sigact, 0, sizeof sigact); + sigact.sa_handler = getalarm; + sigaction(SIGALRM, &sigact, (struct sigaction *)0); + + if (interval > 0) + alarm(interval); + + if (midnightflag) + { + time_t timenow = time(0); + struct tm *tp = localtime(&timenow); + + tp->tm_hour = 23; + tp->tm_min = 59; + tp->tm_sec = 59; + + timelimit = mktime(tp); + } + + /* + ** MAIN-LOOP: + ** - Wait for the requested number of seconds or for other trigger + ** + ** - System-level counters + ** get current counters + ** calculate the differences with the previous sample + ** + ** - Process-level counters + ** get current counters from running & exited processes + ** calculate the differences with the previous sample + ** + ** - Call the print-function to visualize the differences + */ + for (sampcnt=0; sampcnt < nsamples; sampcnt++) + { + char lastcmd; + + /* + ** if the limit-flag is specified: + ** check if the next sample is expected before midnight; + ** if not, stop atop now + */ + if (midnightflag && (curtime+interval) > timelimit) + break; + + /* + ** wait for alarm-signal to arrive (except first sample) + ** or wait for SIGUSR1/SIGUSR2 + */ + if (sampcnt > 0 && awaittrigger) + pause(); + + awaittrigger = 1; + + /* + ** gather time info for this sample + */ + pretime = curtime; + curtime = time(0); /* seconds since 1-1-1970 */ + + /* + ** take a snapshot of the current system-level statistics + ** and calculate the deviations (i.e. calculate the activity + ** during the last sample) + */ + hlpsstat = cursstat; /* swap current/prev. stats */ + cursstat = presstat; + presstat = hlpsstat; + + photosyst(cursstat); /* obtain new counters */ + + deviatsyst(cursstat, presstat, devsstat, + curtime-pretime > 0 ? curtime-pretime : 1); + + /* + ** take a snapshot of the current task-level statistics + ** and calculate the deviations (i.e. calculate the activity + ** during the last sample) + ** + ** first register active tasks + ** --> atop malloc's a minimal amount of space which is + ** only extended when needed + */ + memset(curtpres, 0, curtlen * sizeof(struct tstat)); + + while ( (ntaskpres = photoproc(curtpres, curtlen)) == curtlen) + { + curtlen += PROCCHUNK; + + curtpres = realloc(curtpres, + curtlen * sizeof(struct tstat)); + + ptrverify(curtpres, + "Realloc failed for %d tasks\n", curtlen); + + memset(curtpres, 0, curtlen * sizeof(struct tstat)); + } + + /* + ** register processes that exited during last sample; + ** first determine how many processes exited + ** + ** the number of exited processes is limited to avoid + ** that atop explodes in memory and introduces OOM killing + */ + nprocexit = acctprocnt(); /* number of exited processes */ + + if (nprocexit > MAXACCTPROCS) + { + noverflow = nprocexit - MAXACCTPROCS; + nprocexit = MAXACCTPROCS; + } + else + noverflow = 0; + + /* + ** determine how many processes have been exited + ** for the netatop module (only processes that have + ** used the network) + */ + if (nprocexit > 0 && (supportflags & NETATOPD)) + nprocexitnet = netatop_exitstore(); + else + nprocexitnet = 0; + + /* + ** reserve space for the exited processes and read them + */ + if (nprocexit > 0) + { + curpexit = malloc(nprocexit * sizeof(struct tstat)); + + ptrverify(curpexit, + "Malloc failed for %d exited processes\n", + nprocexit); + + memset(curpexit, 0, nprocexit * sizeof(struct tstat)); + + nprocexit = acctphotoproc(curpexit, nprocexit); + + /* + ** reposition offset in accounting file when not + ** all exited processes have been read (i.e. skip + ** those processes) + */ + if (noverflow) + acctrepos(noverflow); + } + else + { + curpexit = NULL; + } + + /* + ** calculate deviations + */ + deviattask(curtpres, ntaskpres, curpexit, nprocexit, + &devtstat, devsstat); + + /* + ** activate the installed print-function to visualize + ** the deviations + */ + lastcmd = (vis.show_samp)( curtime, + curtime-pretime > 0 ? curtime-pretime : 1, + &devtstat, devsstat, + nprocexit, noverflow, sampcnt==0); + + /* + ** release dynamically allocated memory + */ + if (nprocexit > 0) + free(curpexit); + + if (nprocexitnet > 0) + netatop_exiterase(); + + if (lastcmd == 'r') /* reset requested ? */ + { + sampcnt = -1; + + curtime = getboot() / hertz; // reset current time + + /* set current (will be 'previous') counters to 0 */ + memset(cursstat, 0, sizeof(struct sstat)); + memset(curtpres, 0, curtlen * sizeof(struct tstat)); + + /* remove all tasks in database */ + pdb_makeresidue(); + pdb_cleanresidue(); + } + } /* end of main-loop */ +} + +/* +** print usage of this command +*/ +void +prusage(char *myname) +{ + printf("Usage: %s [-flags] [interval [samples]]\n", + myname); + printf("\t\tor\n"); + printf("Usage: %s -w file [-S] [-%c] [interval [samples]]\n", + myname, MALLPROC); + printf(" %s -r [file] [-b hh:mm] [-e hh:mm] [-flags]\n", + myname); + printf("\n"); + printf("\tgeneric flags:\n"); + printf("\t -%c show version information\n", MVERSION); + printf("\t -%c show or log all processes (i.s.o. active processes " + "only)\n", MALLPROC); + printf("\t -%c calculate proportional set size (PSS) per process\n", + MCALCPSS); + printf("\t -P generate parseable output for specified label(s)\n"); + printf("\t -L alternate line length (default 80) in case of " + "non-screen output\n"); + + (*vis.show_usage)(); + + printf("\n"); + printf("\tspecific flags for raw logfiles:\n"); + printf("\t -w write raw data to file (compressed)\n"); + printf("\t -r read raw data from file (compressed)\n"); + printf("\t special file: y[y...] for yesterday (repeated)\n"); + printf("\t -S finish atop automatically before midnight " + "(i.s.o. #samples)\n"); + printf("\t -b begin showing data from specified time\n"); + printf("\t -e finish showing data after specified time\n"); + printf("\n"); + printf("\tinterval: number of seconds (minimum 0)\n"); + printf("\tsamples: number of intervals (minimum 1)\n"); + printf("\n"); + printf("If the interval-value is zero, a new sample can be\n"); + printf("forced manually by sending signal USR1" + " (kill -USR1 pid_atop)\n"); + printf("or with the keystroke '%c' in interactive mode.\n", MSAMPNEXT); + printf("\n"); + printf("Please refer to the man-page of 'atop' for more details.\n"); + + cleanstop(1); +} + +/* +** handler for ALRM-signal +*/ +void +getalarm(int sig) +{ + awaittrigger=0; + + if (interval > 0) + alarm(interval); /* restart the timer */ +} + +/* +** handler for USR1-signal +*/ +void +getusr1(int sig) +{ + awaittrigger=0; +} + +/* +** handler for USR2-signal +*/ +void +getusr2(int sig) +{ + awaittrigger=0; + nsamples = sampcnt; // force stop after next sample +} + +/* +** functions to handle a particular tag in the .atoprc file +*/ +extern int get_posval(char *name, char *val); + +void +do_interval(char *name, char *val) +{ + interval = get_posval(name, val); +} + +void +do_linelength(char *name, char *val) +{ + linelen = get_posval(name, val); +} + +/* +** read RC-file and modify defaults accordingly +*/ +static void +readrc(char *path, int syslevel) +{ + int i, nr, line=0, errorcnt = 0; + + /* + ** check if this file is readable with the user's + ** *real uid/gid* with syscall access() + */ + if ( access(path, R_OK) == 0) + { + FILE *fp; + char linebuf[256], tagname[20], tagvalue[256]; + + fp = fopen(path, "r"); + + while ( fgets(linebuf, sizeof linebuf, fp) ) + { + line++; + + i = strlen(linebuf); + + if (i > 0 && linebuf[i-1] == '\n') + linebuf[i-1] = 0; + + nr = sscanf(linebuf, "%19s %255[^#]", + tagname, tagvalue); + + switch (nr) + { + case 0: + continue; + + case 1: + if (tagname[0] == '#') + continue; + + fprintf(stderr, + "%s: syntax error line " + "%d (no value specified)\n", + path, line); + + cleanstop(1); + break; /* not reached */ + + default: + if (tagname[0] == '#') + continue; + + if (tagvalue[0] != '#') + break; + + fprintf(stderr, + "%s: syntax error line " + "%d (no value specified)\n", + path, line); + + cleanstop(1); + } + + /* + ** tag name and tag value found + ** try to recognize tag name + */ + for (i=0; i < sizeof manrc/sizeof manrc[0]; i++) + { + if ( strcmp(tagname, manrc[i].tag) == 0) + { + if (manrc[i].sysonly && !syslevel) + { + fprintf(stderr, + "%s: warning at line %2d " + "- tag name %s not allowed " + "in private atoprc\n", + path, line, tagname); + + errorcnt++; + break; + } + + manrc[i].func(tagname, tagvalue); + break; + } + } + + /* + ** tag name not recognized + */ + if (i == sizeof manrc/sizeof manrc[0]) + { + fprintf(stderr, + "%s: warning at line %2d " + "- tag name %s not valid\n", + path, line, tagname); + + errorcnt++; + } + } + + if (errorcnt) + sleep(2); + + fclose(fp); + } +} diff --git a/sources/atop.cronsystemd b/sources/atop.cronsystemd new file mode 100644 index 0000000..61ef090 --- /dev/null +++ b/sources/atop.cronsystemd @@ -0,0 +1,2 @@ +# daily restart of atop at midnight +0 0 * * * root systemctl restart atop diff --git a/sources/atop.cronsysv b/sources/atop.cronsysv new file mode 100644 index 0000000..da27499 --- /dev/null +++ b/sources/atop.cronsysv @@ -0,0 +1,2 @@ +# daily restart of atop at midnight +0 0 * * * root /usr/share/atop/atop.daily& diff --git a/sources/atop.daily b/sources/atop.daily new file mode 100755 index 0000000..32a94d8 --- /dev/null +++ b/sources/atop.daily @@ -0,0 +1,41 @@ +#!/bin/bash + +CURDAY=`date +%Y%m%d` +LOGPATH=/var/log/atop +BINPATH=/usr/bin +PIDFILE=/var/run/atop.pid +INTERVAL=600 # interval 10 minutes + +# verify if atop still runs for daily logging +# +if [ -e $PIDFILE ] && ps -p `cat $PIDFILE` | grep 'atop$' > /dev/null +then + kill -USR2 `cat $PIDFILE` # final sample and terminate + + CNT=0 + + while ps -p `cat $PIDFILE` > /dev/null + do + let CNT+=1 + + if [ $CNT -gt 5 ] + then + break; + fi + + sleep 1 + done + + rm $PIDFILE +fi + +# delete logfiles older than four weeks +# start a child shell that activates another child shell in +# the background to avoid a zombie +# +( (sleep 3; find $LOGPATH -name 'atop_*' -mtime +28 -exec rm {} \;)& ) + +# activate atop with interval of 10 minutes, replacing the current shell +# +echo $$ > $PIDFILE +exec $BINPATH/atop -R -w $LOGPATH/atop_$CURDAY $INTERVAL > $LOGPATH/daily.log 2>&1 diff --git a/sources/atop.h b/sources/atop.h new file mode 100644 index 0000000..e3cd73f --- /dev/null +++ b/sources/atop.h @@ -0,0 +1,186 @@ +/* +** ATOP - System & Process Monitor +** +** The program 'atop' offers the possibility to view the activity of +** the system on system-level as well as process-level. +** +** Include-file describing miscellaneous constants and function-prototypes. +** ================================================================ +** Author: Gerlof Langeveld +** E-mail: gerlof.langeveld@atoptool.nl +** Date: November 1996 +** LINUX-port: June 2000 +** +** This program is free software; you can redistribute it and/or modify it +** under the terms of the GNU General Public License as published by the +** Free Software Foundation; either version 2, or (at your option) any +** later version. +** +** This program is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +** See the GNU General Public License for more details. +*/ +#define EQ 0 +#define SECSDAY 86400 +#define RAWNAMESZ 256 + +/* +** memory-size formatting possibilities +*/ +#define ANYFORMAT 0 +#define KBFORMAT 1 +#define MBFORMAT 2 +#define GBFORMAT 3 +#define TBFORMAT 4 +#define PBFORMAT 5 +#define OVFORMAT 9 + +typedef long long count_t; + +struct tstat; +struct devtstat; +struct sstat; +struct netpertask; + +/* +** miscellaneous flags +*/ +#define RRBOOT 0x0001 +#define RRLAST 0x0002 +#define RRNETATOP 0x0004 +#define RRNETATOPD 0x0008 +#define RRACCTACTIVE 0x0010 +#define RRIOSTAT 0x0020 +#define RRDOCKSTAT 0x0040 + +struct visualize { + char (*show_samp) (time_t, int, + struct devtstat *, struct sstat *, + int, unsigned int, char); + void (*show_error) (const char *, ...); + void (*show_end) (void); + void (*show_usage) (void); +}; + +/* +** external values +*/ +extern struct utsname utsname; +extern int utsnodenamelen; +extern time_t pretime; +extern time_t curtime; +extern unsigned long interval; +extern unsigned long sampcnt; +extern char screen; +extern int linelen; +extern char acctreason; +extern char deviatonly; +extern char usecolors; +extern char threadview; +extern char calcpss; +extern char rawname[]; +extern char rawreadflag; +extern unsigned int begintime, endtime; +extern char flaglist[]; +extern struct visualize vis; + +extern int osrel; +extern int osvers; +extern int ossub; + +extern unsigned short hertz; +extern unsigned int pagesize; + +extern int supportflags; + +extern int cpubadness; +extern int membadness; +extern int swpbadness; +extern int dskbadness; +extern int netbadness; +extern int pagbadness; +extern int almostcrit; + +/* +** bit-values for supportflags +*/ +#define ACCTACTIVE 0x00000001 +#define IOSTAT 0x00000004 +#define NETATOP 0x00000010 +#define NETATOPD 0x00000020 +#define DOCKSTAT 0x00000040 + +/* +** in rawlog file, the four least significant bits +** are moved to the per-sample flags and therefor dummy +** in the support flags of the general header +*/ +#define RAWLOGNG (ACCTACTIVE|IOSTAT|NETATOP|NETATOPD) + +/* +** structure containing the start-addresses of functions for visualization +*/ +char generic_samp (time_t, int, + struct devtstat *, struct sstat *, + int, unsigned int, char); +void generic_error(const char *, ...); +void generic_end (void); +void generic_usage(void); + +/* +** miscellaneous prototypes +*/ +int atopsar(int, char *[]); +char *convtime(time_t, char *); +char *convdate(time_t, char *); +int hhmm2secs(char *, unsigned int *); +int daysecs(time_t); + +char *val2valstr(count_t, char *, int, int, int); +char *val2memstr(count_t, char *, int, int, int); +char *val2cpustr(count_t, char *); +char *val2Hzstr(count_t, char *); +int val2elapstr(int, char *); + +int compcpu(const void *, const void *); +int compdsk(const void *, const void *); +int compmem(const void *, const void *); +int compnet(const void *, const void *); +int compusr(const void *, const void *); +int compnam(const void *, const void *); +int compcon(const void *, const void *); + +int cpucompar (const void *, const void *); +int diskcompar(const void *, const void *); +int intfcompar(const void *, const void *); +int nfsmcompar(const void *, const void *); +int contcompar(const void *, const void *); + +count_t subcount(count_t, count_t); +void rawread(void); +char rawwrite (time_t, int, + struct devtstat *, struct sstat *, + int, unsigned int, char); + +int numeric(char *); +void getalarm(int); +unsigned long long getboot(void); +char *getstrvers(void); +unsigned short getnumvers(void); +void ptrverify(const void *, const char *, ...); +void cleanstop(int); +void prusage(char *); + +int droprootprivs(void); +void regainrootprivs(void); +FILE *fopen_tryroot(const char *, const char *); + +void netatop_ipopen(void); +void netatop_probe(void); +void netatop_signoff(void); +void netatop_gettask(pid_t, char, struct tstat *); +unsigned int netatop_exitstore(void); +void netatop_exiterase(void); +void netatop_exithash(char); +void netatop_exitfind(unsigned long, struct tstat *, struct tstat *); diff --git a/sources/atop.init b/sources/atop.init new file mode 100755 index 0000000..ea545b8 --- /dev/null +++ b/sources/atop.init @@ -0,0 +1,81 @@ +#!/bin/sh +# +# atop Startup script for the Atop process logging in background +# +# chkconfig: 2345 96 4 +# description: Advanced system and process activity monitor +# +### BEGIN INIT INFO +# Provides: atop +# Required-Start: $local_fs +# Required-Stop: $local_fs +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Advanced system and process activity monitor +# Description: Advanced system and process activity monitor +### END INIT INFO + +# Check existance of binaries +[ -f /usr/bin/atop ] || exit 0 + +PIDFILE=/var/run/atop.pid +RETVAL=0 + +# See how we were called. +case "$1" in + start) + # Check if atop runs already + # + if [ -e $PIDFILE ] && ps -p `cat $PIDFILE` | grep 'atop$' > /dev/null + then + : + else + # Start atop + /usr/share/atop/atop.daily& + fi + touch /var/lock/subsys/atop + ;; + + stop) + # Check if atop runs + # + if [ -e $PIDFILE ] && ps -p `cat $PIDFILE` | grep 'atop$' > /dev/null + then + kill -USR2 `cat $PIDFILE` # final sample and terminate + + CNT=0 + + while ps -p `cat $PIDFILE` > /dev/null + do + let CNT+=1 + + if [ $CNT -gt 5 ] + then + break; + fi + + sleep 1 + done + + rm $PIDFILE + fi + rm /var/lock/subsys/atop + ;; + + status) + ;; + + reload) + /usr/share/atop/atop.daily& + ;; + + restart) + /usr/share/atop/atop.daily& + ;; + + *) + echo "Usage: $0 [start|stop|status|reload|restart]" + exit 1 +esac + +exit $RETVAL diff --git a/sources/atop.service b/sources/atop.service new file mode 100644 index 0000000..4d07c57 --- /dev/null +++ b/sources/atop.service @@ -0,0 +1,12 @@ +[Unit] +Description=Atop advanced performance monitor +Documentation=man:atop(1) + +[Service] +Type=simple +ExecStart=/usr/share/atop/atop.daily +KillSignal=SIGUSR2 +#ExecStopPost=/usr/bin/sleep 3 + +[Install] +WantedBy=multi-user.target diff --git a/sources/atopacct.init b/sources/atopacct.init new file mode 100755 index 0000000..1aea1b6 --- /dev/null +++ b/sources/atopacct.init @@ -0,0 +1,86 @@ +#!/bin/sh +# +# atopacctd Startup script for the atopacctd daemon +# +# chkconfig: 2345 91 9 +# description: Process accounting control +# +### BEGIN INIT INFO +# Provides: atopacct +# Required-Start: $local_fs +# Required-Stop: $local_fs +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: This daemon switches on process accounting and +# transfers the process accounting records 'realtime' +# to small shadow files to avoid huge disk space usage +# Description: Process accounting control +### END INIT INFO + +# Check existance of binaries +[ -f /usr/sbin/atopacctd ] || exit 0 + +RETVAL=0 + +# See how we were called. +case "$1" in + start) + # Check if process accounting already in use via psacct + # + for PACCTFILE in /var/account/pacct /var/log/pacct + do + if [ -f "$PACCTFILE" ] # file exists? + then + BEFORSIZE=$(ls -lL "$PACCTFILE" | awk '{print $5}') + AFTERSIZE=$(ls -lL "$PACCTFILE" | awk '{print $5}') + + # verify if accounting file grows, so is in use + # + if [ $BEFORSIZE -lt $AFTERSIZE ] + then + echo Process accounting already used by psacct! >&2 + exit $RETVAL # do not start atopacctd + fi + fi + done + + + # Check if atopacctd runs already + # + if ps -e | grep -q atopacctd$ + then + : + else + # Start atopacctd + rm -rf /var/run/pacct_shadow.d 2> /dev/null + /usr/sbin/atopacctd + fi + + touch /var/lock/subsys/atopacctd + ;; + + stop) + # Check if atopacctd runs + # + if ps -e | grep -q atopacctd$ + then + kill $(ps -e | grep atopacctd$ | sed 's/^ *//' | cut -d' ' -f1) + fi + rm /var/lock/subsys/atopacctd + ;; + + status) + ;; + + reload) + ;; + + restart) + ;; + + *) + echo "Usage: $0 [start|stop]" + exit 1 +esac + +exit $RETVAL diff --git a/sources/atopacct.service b/sources/atopacct.service new file mode 100644 index 0000000..1f51ec4 --- /dev/null +++ b/sources/atopacct.service @@ -0,0 +1,14 @@ +[Unit] +Description=Atop process accounting daemon +Documentation=man:atopacctd(8) +Conflicts=psacct.service +After=syslog.target +Before=atop.service + +[Service] +Type=forking +PIDFile=/var/run/atopacctd.pid +ExecStart=/usr/sbin/atopacctd + +[Install] +WantedBy=multi-user.target diff --git a/sources/atopacctd.c b/sources/atopacctd.c new file mode 100644 index 0000000..fc4b3d5 --- /dev/null +++ b/sources/atopacctd.c @@ -0,0 +1,1040 @@ +/* +** The atopacctd daemon switches on the process accounting feature +** in the kernel and let the process accounting records be written to +** a file, called the source file. After process accounting is active, +** the atopacctd daemon transfers every process accounting record that +** is available in the source file to a shadow file. +** Client processes (like atop) can read the shadow file instead of +** the original process accounting source file. +** +** This approach has the following advantages: +** +** - The atopacctd daemon keeps the source file to a limited size +** by truncating it regularly. +** +** - The atopacct daemon takes care that a shadow file has a limited size. +** As soon as the current shadow file reaches its maximum size, the +** atopacctd daemon creates a subsequent shadow file. For this reason, +** the name of a shadow file contains a sequence number. +** Shadow files that are not used by client processes any more, are +** automatically removed by the atopacctd daemon. +** +** - When no client processes are active (any more), all shadow files +** will be deleted and no records will be transferred to shadow files +** any more. As soon as at least one client is activated again, the +** atopacctd daemon start writing shadow files again. +** +** The directory /var/run is used as the default top-directory. An +** alternative top-directory can be specified as command line argument +** (in that case, also modify /etc/atoprc to inform atop as a client). +** Below this top-directory the source file pacct_source is created and +** the subdirectory pacct_shadow.d as a 'container' for the shadow files. +** ---------------------------------------------------------------------- +** Copyright (C) 2014 Gerlof Langeveld (gerlof.langeveld@atoptool.nl) +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License version 2 as +** published by the Free Software Foundation. +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 \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 ", getstrvers()); + + /* + ** raise priority (be sure the nice value becomes -20, + ** independent of the current nice value) + */ + (void) nice(-39); + + /* + ** connect to NETLINK socket of kernel to be triggered + ** when processes have finished + */ + if ( (nfd = netlink_open()) == -1) + { + (void) unlink(accountpath); + kill(parentpid, SIGTERM); + exit(5); + } + + /* + ** switch on accounting - inital + */ + if ( swonpacct(afd, accountpath) == -1) + { + (void) unlink(accountpath); + kill(parentpid, SIGTERM); + exit(6); + } + + syslog(LOG_INFO, "accounting to %s", accountpath); + + /* + ** signal handling + */ + (void) signal(SIGHUP, SIG_IGN); + + (void) sigaction(SIGINT, &sigcleanup, (struct sigaction *)0); + (void) sigaction(SIGQUIT, &sigcleanup, (struct sigaction *)0); + (void) sigaction(SIGTERM, &sigcleanup, (struct sigaction *)0); + + /* + ** create PID file + */ + if ( (pidf = fopen(PIDFILE, "w")) ) + { + fprintf(pidf, "%d\n", getpid()); + fclose(pidf); + } + + /* + ** terminate parent: service initialized + */ + kill(parentpid, SIGTERM); + + /* + ** main loop + */ + while (! cleanup_and_go) + { + int state; + + /* + ** await termination of (at least) one process and + ** copy the process accounting record(s) + */ + state = awaitprocterm(nfd, afd, sfd, accountpath, + &shadowbusy, &oldshadow, &curshadow); + + if (state == -1) // irrecoverable error? + break; + + /* + ** verify if garbage collection is needed + ** i.e. removal of shadow files that are not in use any more + */ + if ( shadowbusy && time(0) > gclast + GCINTERVAL ) + { + gcshadows(&oldshadow, curshadow); + gclast = time(0); + } + } + + /* + ** cleanup and terminate + */ + (void) acct((char *) 0); // disable process accounting + (void) unlink(accountpath); // remove source file + + for (; oldshadow <= curshadow; oldshadow++) // remove shadow files + { + /* + ** assemble path name of shadow file (starting by oldest) + */ + snprintf(shadowpath, sizeof shadowpath, PACCTSHADOWF, + pacctdir, PACCTSHADOWD, oldshadow); + + (void) unlink(shadowpath); + } + + snprintf(shadowpath, sizeof shadowpath, "%s/%s/%s", + pacctdir, PACCTSHADOWD, PACCTSHADOWC); + + (void) unlink(shadowpath); // remove file 'current' + (void) rmdir(shadowdir); // remove shadow.d directory + + if (cleanup_and_go) + { + syslog(LOG_NOTICE, "Terminated by signal %d\n", cleanup_and_go); + + if (cleanup_and_go == SIGTERM) + return 0; + else + return cleanup_and_go + 128; + } + else + { + syslog(LOG_NOTICE, "Terminated!\n"); + return 13; + } +} + + +/* +** wait for at least one process termination and copy process accounting +** record(s) from the source process accounting file to the current +** shadow accounting file +** +** return code: 0 - no process accounting record read +** 1 - at least one process accounting record read +** -1 - irrecoverable failure +*/ +static int +awaitprocterm(int nfd, int afd, int sfd, char *accountpath, + int *shadowbusyp, unsigned long *oldshadowp, unsigned long *curshadowp) +{ + static int arecsize, netlinkactive = 1; + static unsigned long long atotsize, stotsize, maxshadowsz; + static time_t reclast; + struct timespec retrytimer = {0, RETRYMS/2*1000000}; + int retrycount = RETRYCNT; + int asz, rv, ssz; + char abuf[16000]; + int partsz, remsz; + + /* + ** neutral state: + ** + ** wait for info from NETLINK indicating that at least + ** one process has finished; the real contents of the + ** NETLINK message is ignored, it is only used as trigger + ** that something can be read from the process accounting file + ** + ** unfortunately it is not possible to use inotify() on the + ** source file as a trigger that a new accounting record + ** has been written (does not work if the kernel itself + ** writes to the file) + ** + ** when the NETLINK interface fails due to kernel bug 190711, + ** we switch to polling mode: + ** wait for timer and verify if process accounting + ** records are available (repeatedly); ugly but the only + ** thing we can do if we can't use NETLINK + */ + if (netlinkactive) + { + rv = netlink_recv(nfd, 0); + + if (rv == 0) // EOF? + { + syslog(LOG_ERR, "unexpected EOF on NETLINK\n"); + perror("unexpected EOF on NETLINK\n"); + return -1; + } + + if (rv < 0) // failure? + { + switch (-rv) + { + // acceptable errors that might indicate that + // processes have terminated + case EINTR: + case ENOMEM: + case ENOBUFS: + break; + + default: + syslog(LOG_ERR, + "unexpected error on NETLINK: %s\n", + strerror(-rv)); + fprintf(stderr, + "unexpected error on NETLINK: %s\n", + strerror(-rv)); + + if (-rv == EINVAL) + { + syslog(LOG_ERR, + "(see ATOP README about kernel bug 190711)\n"); + fprintf(stderr, + "(see ATOP README about kernel bug 190711)\n"); + } + + syslog(LOG_ERR, + "switching to polling mode\n"); + fprintf(stderr, + "switching to polling mode\n"); + + netlinkactive = 0; // polling mode wanted + + return 0; + } + } + + /* + ** get rid of all other waiting finished processes via netlink + ** before handling the process accounting record(s) + */ + while ( netlink_recv(nfd, MSG_DONTWAIT) > 0 ); + } + else // POLLING MODE + { + sleep(POLLSEC); + retrycount = 1; + } + + /* + ** read new process accounting record(s) + ** such record(s) may not immediately be available (timing matter), + ** so some retries might be necessary + */ + while ((asz = read(afd, abuf, sizeof abuf)) == 0 && --retrycount) + { + nanosleep(&retrytimer, (struct timespec *)0); + retrytimer.tv_nsec = RETRYMS*1000000; + } + + switch (asz) + { + case 0: // EOF (no records available)? + if (time(0) > reclast + NORECINTERVAL && reclast) + { + syslog(LOG_WARNING, "reactivate process accounting\n"); + + if (truncate(accountpath, 0) != -1) + { + lseek(afd, 0, SEEK_SET); + atotsize = swonpacct(afd, accountpath); + } + + reclast = time(0); + } + + return 0; // wait for NETLINK again + + case -1: // failure? + syslog(LOG_ERR, "%s - unexpected read error: %s\n", + accountpath, strerror(errno)); + return -1; + } + + reclast = time(0); + + /* + ** only once: determine the size of an accounting + ** record and calculate the maximum size for each + ** shadow file + */ + if (!arecsize) + { + arecsize = acctsize((struct acct *)abuf); + + if (arecsize) + { + maxshadowsz = maxshadowrec * arecsize; + } + else + { + syslog(LOG_ERR, + "cannot determine size of account record\n"); + return -1; + } + } + + /* + ** truncate process accounting file regularly + */ + atotsize += asz; // maintain current size + + if (atotsize >= MAXORIGSZ) + { + if (truncate(accountpath, 0) != -1) + { + lseek(afd, 0, SEEK_SET); + atotsize = 0; + } + } + + /* + ** determine if any client is using the shadow + ** accounting files; if not, verify if clients + ** have been using the shadow files till now and + ** cleanup has to be performed + */ + if (NUMCLIENTS == 0) + { + /* + ** did last client just disappeared? + */ + if (*shadowbusyp) + { + /* + ** remove all shadow files + */ + gcshadows(oldshadowp, (*curshadowp)+1); + *oldshadowp = 0; + *curshadowp = 0; + stotsize = 0; + + /* + ** create new file with sequence 0 + */ + (void) close(sfd); + + sfd = createshadow(*curshadowp); + setcurrent(*curshadowp); + + *shadowbusyp = 0; + } + + return 1; + } + + *shadowbusyp = 1; + + /* + ** transfer process accounting data to shadow file + ** but take care to fill a shadow file exactly + ** to its maximum and not more... + */ + if (stotsize + asz <= maxshadowsz) // would fit? + { + /* + ** pass all data + */ + if ( (ssz = pass2shadow(sfd, abuf, asz)) > 0) + stotsize += ssz; + + remsz = 0; + partsz = 0; + } + else + { + /* + ** calculate the remainder that has to + ** be written to the next shadow file + */ + partsz = maxshadowsz - stotsize; + remsz = asz - partsz; + + /* + ** transfer first part of the data to current + ** shadow file + */ + if ( (ssz = pass2shadow(sfd, abuf, partsz)) > 0) + stotsize += ssz; + } + + /* + ** verify if current shadow file has reached its + ** maximum size; if so, switch to next sequence number + ** and write remaining data (if any) + */ + if (stotsize >= maxshadowsz) + { + close(sfd); + + sfd = createshadow(++(*curshadowp)); + setcurrent(*curshadowp); + + stotsize = 0; + + /* + ** transfer remaining part of the data + */ + if (remsz) + { + if ( (ssz = pass2shadow(sfd, abuf+partsz, remsz)) > 0) + stotsize += ssz; + } + } + + return 1; +} + + +/* +** create first shadow file with requested sequence number +*/ +static int +createshadow(long seq) +{ + int sfd; + char shadowpath[128]; + + snprintf(shadowpath, sizeof shadowpath, PACCTSHADOWF, + pacctdir, PACCTSHADOWD, seq); + + /* + ** open the shadow file for write + */ + if ( (sfd = creat(shadowpath, 0644)) == -1) + { + perror(shadowpath); + exit(5); + } + + return sfd; +} + +/* +** transfer process accounting data to shadow file +*/ +static int +pass2shadow(int sfd, char *sbuf, int ssz) +{ + static unsigned long long nrskipped; + struct statvfs statvfs; + + /* + ** check if the filesystem is not filled for more than 95% + */ + if ( fstatvfs(sfd, &statvfs) != -1) + { + if (statvfs.f_blocks == 0 || + statvfs.f_bfree * 100 / statvfs.f_blocks < 5 ) + { + if (nrskipped == 0) // first skip? + { + syslog(LOG_WARNING, + "Filesystem > 95%% full; " + "pacct writing skipped\n"); + } + + nrskipped++; + + return 0; + } + } + + /* + ** there is enough space in the filesystem (again) + ** verify if writing has been suspended due to lack of space (if so, + ** write a log message) + */ + if (nrskipped) + { + syslog(LOG_WARNING, "Pacct writing continued (%llu skipped)\n", + nrskipped); + nrskipped = 0; + } + + /* + ** transfer process accounting record(s) to shadow file + */ + if ( write(sfd, sbuf, ssz) == -1 ) + { + syslog(LOG_ERR, "Unexpected write error to shadow file: %s\n", + strerror(errno)); + exit(7); + } + + return ssz; +} + + +/* +** switch on the process accounting mechanism +** first parameter: file descriptor of open accounting file +** second parameter: name of accounting file +** return value: -1 in case of permanent failure, +** otherwise number of bytes read from accounting file +*/ +static int +swonpacct(int afd, char *accountpath) +{ + int n, acctokay = 0; + char abuf[4096]; + + /* + ** due to kernel bug 190271 (process accounting sometimes + ** does not work), we verify if process accounting really + ** works after switching it on. If not, we keep retrying + ** for a while. + */ + while (! acctokay) + { + int maxcnt = 40; + + /* + ** switch on process accounting + */ + if ( acct(accountpath) == -1) + { + perror("cannot switch on process accounting"); + return -1; + } + + /* + ** try if process accounting works by spawning a + ** child process that immediately finishes (should + ** result in a process accounting record) + */ + if ( fork() == 0 ) + exit(0); + + (void) wait((int *)0); // wait for child to finish + + while ( (n = read(afd, abuf, sizeof abuf)) <= 0 && --maxcnt) + usleep(50000); + + if (n > 0) // process accounting works + { + acctokay = 1; + } + else + { + syslog(LOG_ERR, + "Retrying to switch on process accounting\n"); + syslog(LOG_ERR, + "(see ATOP README about kernel bug 190271)\n"); + + acct((char *)0); // switch off process accounting + sleep(PACCTSEC); // wait a while before retrying + } + } + + return n; +} + + +/* +** remove old shadow files not being in use any more +** +** When a reading process (atop) opens a shadow file, +** it places a read lock on the first byte of that file. +** More then one read lock is allowed on that first byte +** (in case of more atop incarnations). +** If at least one read lock exists, a write lock (to be +** tried here) will fail which means that the file is still +** in use by at least one reader. +*/ +static void +gcshadows(unsigned long *oldshadowp, unsigned long curshadow) +{ + struct flock flock; + int tmpsfd; + char shadowpath[128]; + + /* + ** fill flock structure: write lock on first byte + */ + flock.l_type = F_WRLCK; + flock.l_whence = SEEK_SET; + flock.l_start = 0; + flock.l_len = 1; + + for (; *oldshadowp < curshadow; (*oldshadowp)++) + { + /* + ** assemble path name of shadow file (starting by oldest) + */ + snprintf(shadowpath, sizeof shadowpath, PACCTSHADOWF, + pacctdir, PACCTSHADOWD, *oldshadowp); + + /* + ** try to open oldest existing file for write + ** and verify if it is in use + */ + if ( (tmpsfd = open(shadowpath, O_WRONLY)) == -1) + break; + + if ( fcntl(tmpsfd, F_SETLK, &flock) == -1) // no-wait trial + { + close(tmpsfd); + break; // setting lock failed, so still in use + } + + /* + ** lock successfully set, so file is unused: remove file; + ** closing the file implicitly removes the succeeded lock + */ + (void) close(tmpsfd); + (void) unlink(shadowpath); + } +} + +/* +** write sequence number of current (newest) file +*/ +static void +setcurrent(long curshadow) +{ + static int cfd = -1; + char currentpath[128], currentdata[128]; + int len; + + /* + ** assemble file name of currency file and open (only once) + */ + if (cfd == -1) + { + snprintf(currentpath, sizeof currentpath, "%s/%s/%s", + pacctdir, PACCTSHADOWD, PACCTSHADOWC); + + if ( (cfd = creat(currentpath, 0644)) == -1) + { + syslog(LOG_ERR, "Could not create currency file: %s\n", + strerror(errno)); + return; + } + } + + /* + ** assemble ASCII string to be written: seqnumber/maxrecords + */ + len = snprintf(currentdata, sizeof currentdata, "%ld/%lu", + curshadow, maxshadowrec); + + /* + ** wipe currency file and write new assembled string + */ + (void) ftruncate(cfd, 0); + (void) lseek(cfd, 0, SEEK_SET); + (void) write(cfd, currentdata, len); +} + +/* +** determine the size of an accounting record +*/ +static int +acctsize(struct acct *parec) +{ + switch (parec->ac_version & 0x0f) + { + case 2: + return sizeof(struct acct); + + case 3: + return sizeof(struct acct_v3); + + default: + return 0; + } +} + +/* +** generate version number and date +*/ +char * +getstrvers(void) +{ + static char vers[256]; + + snprintf(vers, sizeof vers, "Version: %s - %s", + atopacctdversion, atopacctddate); + + return vers; +} + +/* +** signal catcher: +** set flag to be verified in main loop to cleanup and terminate +*/ +void +cleanup(int sig) +{ + cleanup_and_go = sig; +} diff --git a/sources/atopacctd.h b/sources/atopacctd.h new file mode 100644 index 0000000..6c26376 --- /dev/null +++ b/sources/atopacctd.h @@ -0,0 +1,35 @@ +/* +** keys to access the semaphores +*/ +#define PACCTPUBKEY 1071980 // # clients using shadow files +#define PACCTPRVKEY (PACCTPUBKEY-1) // # atopacctd daemons busy (max. 1) + +/* +** name of the PID file +*/ +#define PIDFILE "/var/run/atopacctd.pid" + +/* +** directory containing the source accounting file and +** the subdirectory (PACCTSHADOWD) containing the shadow file(s) +** this directory can be overruled by a command line parameter (atopacctd) +** or by a keyword in the /etc/atoprc file (atop) +*/ +#define PACCTDIR "/var/run" + +/* +** accounting file (source file to which kernel writes records) +*/ +#define PACCTORIG "pacct_source" // file name in PACCTDIR + +#define MAXORIGSZ (1024*1024) // maximum size of source file + +/* +** file and directory names for shadow files +*/ +#define PACCTSHADOWD "pacct_shadow.d" // directory name in PACCTDIR +#define PACCTSHADOWF "%s/%s/%010ld.paf" // file name of shadow file +#define PACCTSHADOWC "current" // file containining current + // sequence and MAXSHADOWREC + +#define MAXSHADOWREC 10000 // number of accounting records per shadow file diff --git a/sources/atopsar.c b/sources/atopsar.c new file mode 100644 index 0000000..5582e77 --- /dev/null +++ b/sources/atopsar.c @@ -0,0 +1,2558 @@ +/* +** ATOP - System & Process Monitor +** +** The program 'atop'/'atopsar' offers the possibility to view the activity of +** the system on system-level as well as process-level. +** +** This source-file contains the 'atopsar'-functionality, that makes use +** of the 'atop'-framework. +** ========================================================================== +** Author: Gerlof Langeveld +** E-mail: gerlof.langeveld@atoptool.nl +** Date: July 2007 +** -------------------------------------------------------------------------- +** Copyright (C) 2007-2010 Gerlof Langeveld +** +** This program is free software; you can redistribute it and/or modify it +** under the terms of the GNU General Public License as published by the +** Free Software Foundation; either version 2, or (at your option) any +** later version. +** +** This program is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +** See the GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +** -------------------------------------------------------------------------- +*/ + +static const char rcsid[] = "$Id: atopsar.c,v 1.28 2010/11/26 06:19:43 gerlof Exp $"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 samples into one sample\n"); + fprintf(stderr, + "\t -b begin showing data from specified time\n"); + fprintf(stderr, + "\t -e finish showing data after specified time\n"); + fprintf(stderr, + "\t -S print timestamp on every line in case of more " + "resources\n"); + fprintf(stderr, + "\t -x never use colors to indicate overload" + " (default: only if tty)\n"); + fprintf(stderr, + "\t -C always use colors to indicate overload" + " (default: only if tty)\n"); + fprintf(stderr, + "\t -M use markers to indicate overload " + "(* = critical, + = almost)\n"); + fprintf(stderr, + "\t -H repeat report headers " + "(in case of tty: depending on screen lines)\n"); + fprintf(stderr, + "\t -a print all resources, even when inactive\n"); + fprintf(stderr, "\n"); + fprintf(stderr, + "\tSpecific flags to select reports:\n"); + fprintf(stderr, + "\t -A print all available reports\n"); + + for (i=0; i < pricnt; i++) + fprintf(stderr, + "\t -%c %s\n", pridef[i].flag, pridef[i].about); + + fprintf(stderr, "\n"); + fprintf(stderr, + "Please refer to the man-page of 'atopsar' " + "for more details.\n"); + + + cleanstop(1); +} + +/* +** calculate the epoch-value for the last second +** of the day given a certain epoch +*/ +static time_t +daylimit(time_t timval) +{ + struct tm *tp = localtime(&timval); + + tp->tm_hour = 23; + tp->tm_min = 59; + tp->tm_sec = 59; + + return mktime(tp); +} + +/* +** function to be called before printing a statistics line +** to switch on colors when necessary +*/ +static void +preprint(unsigned int badness) +{ + if (usecolors) + { + if (badness >= 100) + { + coloron = 1; + printf(COLSETHIGH); + } + else + { + if (almostcrit && badness >= almostcrit) + { + coloron = 1; + printf(COLSETMED); + } + } + } +} + +/* +** function to be called after printing a statistics line +** to switch off colors when necessary and print a line feed +*/ +static void +postprint(unsigned int badness) +{ + if (coloron) + { + coloron = 0; + printf(COLRESET); + } + + if (usemarkers) + { + if (badness >= 100) + { + printf(" *"); + } + else + { + if (almostcrit && badness >= almostcrit) + printf(" +"); + } + } + + printf("\n"); +} + +/* +** function the handle the default flags for atopsar as +** read from the file ~/.atoprc +*/ +void +do_atopsarflags(char *val) +{ + int i, j; + + for (i=0; val[i]; i++) + { + switch (val[i]) + { + case '-': + break; + + case 'S': /* timestamp on every line */ + stampalways = 1; + break; + + case 'x': /* always colors for overload */ + usecolors = 0; + break; + + case 'C': /* always colors for overload */ + usecolors = 'a'; + break; + + case 'M': /* markers for overload */ + usemarkers = 1; + break; + + case 'H': /* repeat headers */ + repeathead = 23; /* define default */ + + if (isatty(1)) + { + struct winsize wsz; + + if ( ioctl(1, TIOCGWINSZ, &wsz) != -1) + repeathead = wsz.ws_row - 1; + } + break; + + case 'a': /* every interval all units */ + allresources = 1; + break; + + case 'A': /* all reports wanted ? */ + for (j=0; j < pricnt; j++) + pridef[j].wanted = 1; + + numreports = pricnt; + break; + + default: /* gather report-flags */ + for (j=0; j < pricnt; j++) + { + if (pridef[j].flag == val[i] && + pridef[j].wanted == 0 ) + { + pridef[j].wanted = 1; + numreports++; + break; + } + } + } + } +} + +/**************************************************************************/ +/* Functions to print statistics */ +/**************************************************************************/ +/* +** CPU statistics +*/ +static void +cpuhead(int osvers, int osrel, int ossub) +{ + printf("cpu %%usr %%nice %%sys %%irq %%softirq %%steal %%guest " + " %%wait %%idle _cpu_"); +} + +static int +cpuline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, + time_t deltasec, time_t deltatic, time_t hz, + int osvers, int osrel, int ossub, char *tstamp, + int ppres, int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie) +{ + register int i, nlines = 1; + count_t cputot; + unsigned int badness; + + /* + ** print overall statistics + */ + cputot = ss->cpu.all.stime + ss->cpu.all.utime + + ss->cpu.all.ntime + ss->cpu.all.itime + + ss->cpu.all.wtime + ss->cpu.all.Itime + + ss->cpu.all.Stime + ss->cpu.all.steal; + + if (cputot == 0) + cputot = 1; /* avoid divide-by-zero */ + + if (cpubadness) + badness = ((cputot - ss->cpu.all.itime - ss->cpu.all.wtime) * + 100.0 / cputot) * 100 / cpubadness; + else + badness = 0; + + preprint(badness); + + printf("all %5.0lf %5.0lf %4.0lf %4.0lf %8.0lf %7.0f %6.0f %6.0lf %5.0lf", + (double) (ss->cpu.all.utime * 100.0) / cputot * ss->cpu.nrcpu, + (double) (ss->cpu.all.ntime * 100.0) / cputot * ss->cpu.nrcpu, + (double) (ss->cpu.all.stime * 100.0) / cputot * ss->cpu.nrcpu, + (double) (ss->cpu.all.Itime * 100.0) / cputot * ss->cpu.nrcpu, + (double) (ss->cpu.all.Stime * 100.0) / cputot * ss->cpu.nrcpu, + (double) (ss->cpu.all.steal * 100.0) / cputot * ss->cpu.nrcpu, + (double) (ss->cpu.all.guest * 100.0) / cputot * ss->cpu.nrcpu, + (double) (ss->cpu.all.wtime * 100.0) / cputot * ss->cpu.nrcpu, + (double) (ss->cpu.all.itime * 100.0) / cputot * ss->cpu.nrcpu); + + postprint(badness); + + /* + ** print per-cpu statistics + */ + if (ss->cpu.nrcpu > 1) + { + for (i=0; i < ss->cpu.nrcpu; i++) + { + cputot = ss->cpu.cpu[i].stime + ss->cpu.cpu[i].utime + + ss->cpu.cpu[i].ntime + ss->cpu.cpu[i].itime + + ss->cpu.cpu[i].wtime + ss->cpu.cpu[i].Itime + + ss->cpu.cpu[i].Stime + ss->cpu.cpu[i].steal; + + if (cputot == 0) + cputot = 1; /* avoid divide-by-zero */ + + if (cpubadness) + badness = ((cputot - ss->cpu.cpu[i].itime - + ss->cpu.cpu[i].wtime) * 100.0 / + cputot) * 100 / cpubadness; + else + badness = 0; + + printf("%s ", tstamp); + + preprint(badness); + + printf("%4d %5.0lf %5.0lf %4.0lf %4.0lf %8.0lf " + "%7.0f %6.0lf %6.0lf %5.0lf", + i, + (double)(ss->cpu.cpu[i].utime * 100.0) / cputot, + (double)(ss->cpu.cpu[i].ntime * 100.0) / cputot, + (double)(ss->cpu.cpu[i].stime * 100.0) / cputot, + (double)(ss->cpu.cpu[i].Itime * 100.0) / cputot, + (double)(ss->cpu.cpu[i].Stime * 100.0) / cputot, + (double)(ss->cpu.cpu[i].steal * 100.0) / cputot, + (double)(ss->cpu.cpu[i].guest * 100.0) / cputot, + (double)(ss->cpu.cpu[i].wtime * 100.0) / cputot, + (double)(ss->cpu.cpu[i].itime * 100.0) / cputot); + + postprint(badness); + + nlines++; + } + } + + return nlines; +} + +/* +** other processor statistics +*/ +static void +prochead(int osvers, int osrel, int ossub) +{ + printf("pswch/s devintr/s clones/s loadavg1 loadavg5 loadavg15 " + " _load_"); +} + +static int +procline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, + time_t deltasec, time_t deltatic, time_t hz, + int osvers, int osrel, int ossub, char *tstamp, + int ppres, int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie) +{ + printf("%7.0lf %9.0lf %9.2lf %8.2lf %8.2lf %8.2lf\n", + (double)ss->cpu.csw / deltasec, + (double)ss->cpu.devint / deltasec, + (double)ss->cpu.nprocs / deltasec, + ss->cpu.lavg1, ss->cpu.lavg5, ss->cpu.lavg15); + return 1; +} + +/* +** process statistics +*/ +static void +taskhead(int osvers, int osrel, int ossub) +{ + printf("clones/s pexit/s curproc curzomb thrrun thrslpi thrslpu " + "_procthr_"); +} + +static int +taskline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, + time_t deltasec, time_t deltatic, time_t hz, + int osvers, int osrel, int ossub, char *tstamp, + int ppres, int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie) +{ + if (ppres == 0) + { + printf("report not available for live measurements.....\n"); + return 0; + } + + if (ts) /* process statistics available */ + { + printf("%8.2lf %7.2lf %7d %7d %6d %7d %7d\n", + (double)ss->cpu.nprocs / deltasec, + (double)pexit / deltasec, + nactproc-pexit, pzombie, ntrun, ntslpi, ntslpu); + } + else + { + printf("%8.2lf %7.2lf %7d %7d\n", + (double)ss->cpu.nprocs / deltasec, + (double)pexit / deltasec, + nactproc-pexit, pzombie); + } + + return 1; +} + +/* +** memory- & swap-usage +*/ +static void +memhead(int osvers, int osrel, int ossub) +{ + printf("memtotal memfree buffers cached dirty slabmem" + " swptotal swpfree _mem_" ); +} + +static int +memline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, + time_t deltasec, time_t deltatic, time_t hz, + int osvers, int osrel, int ossub, char *tstamp, + int ppres, int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie) +{ + unsigned int mbadness, sbadness; + + if (membadness) + mbadness = ((ss->mem.physmem - ss->mem.freemem - + ss->mem.cachemem - ss->mem.buffermem) + * 100.0 / ss->mem.physmem) + * 100 / membadness; + else + mbadness = 0; + + if (swpbadness) + sbadness = ((ss->mem.totswap - ss->mem.freeswap) + * 100.0 / ss->mem.totswap) + * 100 / swpbadness; + else + sbadness = 0; + + preprint(mbadness >= sbadness ? mbadness : sbadness); + + printf("%7lldM %6lldM %6lldM %5lldM %4lldM %6lldM %7lldM %6lldM", + ss->mem.physmem * (pagesize / 1024) /1024, + ss->mem.freemem * (pagesize / 1024) /1024, + ss->mem.buffermem * (pagesize / 1024) /1024, + ss->mem.cachemem * (pagesize / 1024) /1024, + ss->mem.cachedrt * (pagesize / 1024) /1024, + ss->mem.slabmem * (pagesize / 1024) /1024, + ss->mem.totswap * (pagesize / 1024) /1024, + ss->mem.freeswap * (pagesize / 1024) /1024); + + postprint(mbadness >= sbadness ? mbadness : sbadness); + + return 1; +} + +/* +** swapping statistics +*/ +static void +swaphead(int osvers, int osrel, int ossub) +{ + printf("pagescan/s swapin/s swapout/s " + " commitspc commitlim _swap_"); +} + +static int +swapline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, + time_t deltasec, time_t deltatic, time_t hz, + int osvers, int osrel, int ossub, char *tstamp, + int ppres, int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie) +{ + unsigned int badness; + + if (membadness) + badness = (ss->mem.swouts / deltasec * pagbadness) + * 100 / membadness; + else + badness = 0; + + /* + ** take care that this line is anyhow colored for + ** 'almost critical' in case of swapouts > 1 per second + */ + if (ss->mem.swouts / deltasec > 0 && + pagbadness && almostcrit && badness < almostcrit) + badness = almostcrit; + + if (ss->mem.commitlim && ss->mem.committed > ss->mem.commitlim) + badness = 100; /* force colored output */ + + preprint(badness); + + printf("%10.2lf %9.2lf %9.2lf %9lluM %9lluM", + (double)ss->mem.pgscans / deltasec, + (double)ss->mem.swins / deltasec, + (double)ss->mem.swouts / deltasec, + ss->mem.committed * (pagesize / 1024) / 1024, + ss->mem.commitlim * (pagesize / 1024) / 1024); + + postprint(badness); + + return 1; +} + +/* +** disk statistics +*/ +static void +lvmhead(int osvers, int osrel, int ossub) +{ + printf("disk busy read/s KB/read " + "writ/s KB/writ avque avserv _lvm_"); +} + +static void +mddhead(int osvers, int osrel, int ossub) +{ + printf("disk busy read/s KB/read " + "writ/s KB/writ avque avserv _mdd_"); +} + +static void +dskhead(int osvers, int osrel, int ossub) +{ + printf("disk busy read/s KB/read " + "writ/s KB/writ avque avserv _dsk_"); +} + +static int +gendskline(struct sstat *ss, char *tstamp, char selector) +{ + static char firstcall = 1; + register int i, nlines = 0, nunit = 0; + count_t mstot, iotot; + struct perdsk *dp; + unsigned int badness; + + switch (selector) + { + case 'l': + dp = ss->dsk.lvm; + nunit = ss->dsk.nlvm; + break; + + case 'm': + dp = ss->dsk.mdd; + nunit = ss->dsk.nmdd; + break; + + case 'd': + dp = ss->dsk.dsk; + nunit = ss->dsk.ndsk; + break; + + default: + return 0; + } + + mstot = (ss->cpu.all.stime + ss->cpu.all.utime + + ss->cpu.all.ntime + ss->cpu.all.itime + + ss->cpu.all.wtime + ss->cpu.all.Itime + + ss->cpu.all.Stime + ss->cpu.all.steal ) + * (count_t)1000 / hertz / ss->cpu.nrcpu; + + for (i=0; i < nunit; i++, dp++) + { + char *pn; + int len; + + if ( (iotot = dp->nread + dp->nwrite) == 0 && + !firstcall && !allresources ) + continue; /* no activity on this disk */ + + /* + ** disk was active during last interval; print info + */ + if (nlines++) + printf("%s ", tstamp); + + if (dskbadness) + badness = (dp->io_ms * 100.0 / mstot) * 100/dskbadness; + else + badness = 0; + + preprint(badness); + + if ( (len = strlen(dp->name)) > 14) + pn = dp->name + len - 14; + else + pn = dp->name; + + printf("%-14s %3.0lf%% %6.1lf %7.1lf %7.1lf %7.1lf " + "%5.1lf %6.2lf ms", + pn, + mstot ? (double)dp->io_ms * 100.0 / mstot : 0.0, + mstot ? (double)dp->nread * 1000.0 / mstot : 0.0, + dp->nread ? + (double)dp->nrsect / dp->nread / 2.0 : 0.0, + mstot ? (double)dp->nwrite * 1000.0 / mstot : 0.0, + dp->nwrite ? + (double)dp->nwsect / dp->nwrite / 2.0 : 0.0, + dp->io_ms ? (double)dp->avque / dp->io_ms : 0.0, + iotot ? (double)dp->io_ms / iotot : 0.0); + + postprint(badness); + } + + if (nlines == 0) + { + printf("\n"); + nlines++; + } + + firstcall = 0; + + return nlines; +} + +static int +lvmline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, + time_t deltasec, time_t deltatic, time_t hz, + int osvers, int osrel, int ossub, char *tstamp, + int ppres, int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie) +{ + return gendskline(ss, tstamp, 'l'); +} + +static int +mddline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, + time_t deltasec, time_t deltatic, time_t hz, + int osvers, int osrel, int ossub, char *tstamp, + int ppres, int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie) +{ + return gendskline(ss, tstamp, 'm'); +} + +static int +dskline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, + time_t deltasec, time_t deltatic, time_t hz, + int osvers, int osrel, int ossub, char *tstamp, + int ppres, int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie) +{ + return gendskline(ss, tstamp, 'd'); +} + +/* +** NFS client statistics +*/ +static void +nfmhead(int osvers, int osrel, int ossub) +{ + printf("mounted_device physread/s physwrit/s" + " _nfm_"); +} + +static int +nfmline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, + time_t deltasec, time_t deltatic, time_t hz, + int osvers, int osrel, int ossub, char *tstamp, + int ppres, int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie) +{ + static char firstcall = 1; + register long i, nlines = 0; + char *pn, state; + int len; + + for (i=0; i < ss->nfs.nfsmounts.nrmounts; i++) /* per NFS mount */ + { + /* + ** print for the first sample all mounts that + ** are found; afterwards print only the mounts + ** that were really active during the interval + */ + if (firstcall || + allresources || + ss->nfs.nfsmounts.nfsmnt[i].age < deltasec || + ss->nfs.nfsmounts.nfsmnt[i].bytestotread || + ss->nfs.nfsmounts.nfsmnt[i].bytestotwrite ) + { + if (nlines++) + printf("%s ", tstamp); + + if ( (len = strlen(ss->nfs.nfsmounts.nfsmnt[i].mountdev)) > 38) + pn = ss->nfs.nfsmounts.nfsmnt[i].mountdev + len - 38; + else + pn = ss->nfs.nfsmounts.nfsmnt[i].mountdev; + + if (ss->nfs.nfsmounts.nfsmnt[i].age < deltasec) + state = 'M'; + else + state = ' '; + + printf("%-38s %10.3lfK %10.3lfK %c\n", + pn, + (double)ss->nfs.nfsmounts.nfsmnt[i].bytestotread / + 1024 / deltasec, + (double)ss->nfs.nfsmounts.nfsmnt[i].bytestotwrite / + 1024 / deltasec, + state); + } + } + + if (nlines == 0) + { + printf("\n"); + nlines++; + } + + firstcall= 0; + return nlines; +} + +static void +nfchead(int osvers, int osrel, int ossub) +{ + printf(" rpc/s rpcread/s rpcwrite/s retrans/s autrefresh/s " + " _nfc_"); +} + +static int +nfcline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, + time_t deltasec, time_t deltatic, time_t hz, + int osvers, int osrel, int ossub, char *tstamp, + int ppres, int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie) +{ + printf("%10.2lf %10.2lf %10.2lf %10.2lf %12.2lf\n", + (double)ss->nfs.client.rpccnt / deltasec, + (double)ss->nfs.client.rpcread / deltasec, + (double)ss->nfs.client.rpcwrite / deltasec, + (double)ss->nfs.client.rpcretrans / deltasec, + (double)ss->nfs.client.rpcautrefresh / deltasec); + + return 1; +} + +static void +nfshead(int osvers, int osrel, int ossub) +{ + printf(" rpc/s rpcread/s rpcwrite/s MBcr/s MBcw/s " + "nettcp/s netudp/s _nfs_"); +} + +static int +nfsline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, + time_t deltasec, time_t deltatic, time_t hz, + int osvers, int osrel, int ossub, char *tstamp, + int ppres, int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie) +{ + printf("%7.2lf %10.2lf %10.2lf %6.2lf %7.2lf %9.2lf %8.2lf\n", + (double)ss->nfs.server.rpccnt / deltasec, + (double)ss->nfs.server.rpcread / deltasec, + (double)ss->nfs.server.rpcwrite / deltasec, + (double)ss->nfs.server.nrbytes / 1024.0 / 1024.0 / deltasec, + (double)ss->nfs.server.nwbytes / 1024.0 / 1024.0 / deltasec, + (double)ss->nfs.server.nettcpcnt / deltasec, + (double)ss->nfs.server.netudpcnt / deltasec); + + return 1; +} + +/* +** network-interface statistics +*/ +static void +ifhead(int osvers, int osrel, int ossub) +{ + printf("interf busy ipack/s opack/s iKbyte/s oKbyte/s " + "imbps ombps maxmbps_if_"); +} + +static int +ifline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, + time_t deltasec, time_t deltatic, time_t hz, + int osvers, int osrel, int ossub, char *tstamp, + int ppres, int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie) +{ + static char firstcall = 1; + register long i, nlines = 0; + double busy; + char busyval[16], dupval; + unsigned int badness; + char *pn; + int len; + + for (i=0; i < ss->intf.nrintf; i++) /* per interface */ + { + count_t ival, oval; + + /* + ** print for the first sample all interfaces which + ** are found; afterwards print only the interfaces + ** which were really active during the interval + */ + if (!firstcall && !allresources && + !ss->intf.intf[i].rpack && !ss->intf.intf[i].spack) + continue; + + /* + ** convert byte-transfers to bit-transfers (* 8) + ** convert bit-transfers to megabit-transfers (/ 1000000) + ** per second + */ + ival = ss->intf.intf[i].rbyte/125000/deltasec; + oval = ss->intf.intf[i].sbyte/125000/deltasec; + + /* + ** calculate busy-percentage for interface + */ + if (ss->intf.intf[i].speed) /* speed known? */ + { + if (ss->intf.intf[i].duplex) + busy = (ival > oval ? ival*100 : oval*100) / + ss->intf.intf[i].speed; + else + busy = (ival + oval) * 100 / + ss->intf.intf[i].speed; + + // especially with wireless, the speed might have + // dropped temporarily to a very low value (snapshot) + // it might be better to take the speed of the + // previous sample + if (busy > 100 && ss->intf.intf[i].speed < + ss->intf.intf[i].speedp ) + { + ss->intf.intf[i].speed = + ss->intf.intf[i].speedp; + + if (ss->intf.intf[i].duplex) + busy = (ival > oval ? + ival*100 : oval*100) / + ss->intf.intf[i].speed; + else + busy = (ival + oval) * 100 / + ss->intf.intf[i].speed; + } + + snprintf(busyval, sizeof busyval, + "%3.0lf%%", busy); + } + else + { + strcpy(busyval, "?"); /* speed unknown */ + busy = 0; + } + + if (nlines++) + printf("%s ", tstamp); + + if (ss->intf.intf[i].speed) + { + if (ss->intf.intf[i].duplex) + dupval = 'f'; + else + dupval = 'h'; + } + else + { + dupval = ' '; + } + + if (netbadness) + badness = busy * 100 / netbadness; + else + badness = 0; + + if ( (len = strlen(ss->intf.intf[i].name)) > 6) + pn = ss->intf.intf[i].name + len - 6; + else + pn = ss->intf.intf[i].name; + + preprint(badness); + + printf("%-6s %4s %7.1lf %7.1lf %8.0lf %8.0lf " + "%5lld %5lld %7ld %c", + pn, busyval, + (double)ss->intf.intf[i].rpack / deltasec, + (double)ss->intf.intf[i].spack / deltasec, + (double)ss->intf.intf[i].rbyte / 1024 / deltasec, + (double)ss->intf.intf[i].sbyte / 1024 / deltasec, + ival, oval, + ss->intf.intf[i].speed, dupval); + + postprint(badness); + } + + if (nlines == 0) + { + printf("\n"); + nlines++; + } + + firstcall = 0; + return nlines; +} + +static void +IFhead(int osvers, int osrel, int ossub) +{ + printf("interf ierr/s oerr/s coll/s idrop/s odrop/s " + "iframe/s ocarrier/s _if_"); +} + +static int +IFline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, + time_t deltasec, time_t deltatic, time_t hz, + int osvers, int osrel, int ossub, char *tstamp, + int ppres, int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie) +{ + static char firstcall = 1; + register long i, nlines = 0; + char *pn; + int len; + + for (i=0; i < ss->intf.nrintf; i++) /* per interface */ + { + /* + ** print for the first sample all interfaces which + ** are found; afterwards print only the interfaces + ** which were really active during the interval + */ + if (!firstcall && !allresources && + !ss->intf.intf[i].rpack && !ss->intf.intf[i].spack) + continue; + + if (nlines++) + printf("%s ", tstamp); + + if ( (len = strlen(ss->intf.intf[i].name)) > 6) + pn = ss->intf.intf[i].name + len - 6; + else + pn = ss->intf.intf[i].name; + + printf("%-6s %6.2lf %6.2lf %6.2lf %7.2lf %7.2lf " + "%8.2lf %10.2lf\n", + pn, + (double)ss->intf.intf[i].rerrs / deltasec, + (double)ss->intf.intf[i].serrs / deltasec, + (double)ss->intf.intf[i].scollis / deltasec, + (double)ss->intf.intf[i].rdrop / deltasec, + (double)ss->intf.intf[i].sdrop / deltasec, + (double)ss->intf.intf[i].rframe / deltasec, + (double)ss->intf.intf[i].scarrier / deltasec); + } + + if (nlines == 0) + { + printf("\n"); + nlines++; + } + + firstcall= 0; + return nlines; +} + +/* +** IP version 4 statistics +*/ +static void +ipv4head(int osvers, int osrel, int ossub) +{ + printf("inrecv/s outreq/s indeliver/s forward/s " + "reasmok/s fragcreat/s _ipv4_"); +} + +static int +ipv4line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, + time_t deltasec, time_t deltatic, time_t hz, + int osvers, int osrel, int ossub, char *tstamp, + int ppres, int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie) +{ + printf("%8.1lf %8.1lf %11.1lf %9.1lf %9.1lf %11.1lf\n", + (double)ss->net.ipv4.InReceives / deltasec, + (double)ss->net.ipv4.OutRequests / deltasec, + (double)ss->net.ipv4.InDelivers / deltasec, + (double)ss->net.ipv4.Forwarding / deltasec, + (double)ss->net.ipv4.ReasmOKs / deltasec, + (double)ss->net.ipv4.FragCreates / deltasec); + return 1; +} + +static void +IPv4head(int osvers, int osrel, int ossub) +{ + printf("in: dsc/s hder/s ader/s unkp/s ratim/s rfail/s " + "out: dsc/s nrt/s_ipv4_"); +} + +static int +IPv4line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, + time_t deltasec, time_t deltatic, time_t hz, + int osvers, int osrel, int ossub, char *tstamp, + int ppres, int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie) +{ + printf(" %5.1lf %6.1lf %6.1lf %6.1lf %7.1lf %7.1lf " + " %5.1lf %5.1lf\n", + (double)ss->net.ipv4.InDiscards / deltasec, + (double)ss->net.ipv4.InHdrErrors / deltasec, + (double)ss->net.ipv4.InAddrErrors / deltasec, + (double)ss->net.ipv4.InUnknownProtos / deltasec, + (double)ss->net.ipv4.ReasmTimeout / deltasec, + (double)ss->net.ipv4.ReasmFails / deltasec, + (double)ss->net.ipv4.OutDiscards / deltasec, + (double)ss->net.ipv4.OutNoRoutes / deltasec); + return 1; +} + +/* +** ICMP version 4 statistics +*/ +static void +icmpv4head(int osvers, int osrel, int ossub) +{ + printf("intot/s outtot/s inecho/s inerep/s " + "otecho/s oterep/s _icmpv4_" ); +} + +static int +icmpv4line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, + time_t deltasec, time_t deltatic, time_t hz, + int osvers, int osrel, int ossub, char *tstamp, + int ppres, int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie) +{ + printf("%7.1lf %8.1lf %8.2lf %8.2lf %8.2lf %8.2lf\n", + (double)ss->net.icmpv4.InMsgs / deltasec, + (double)ss->net.icmpv4.OutMsgs / deltasec, + (double)ss->net.icmpv4.InEchos / deltasec, + (double)ss->net.icmpv4.OutEchos / deltasec, + (double)ss->net.icmpv4.InEchoReps / deltasec, + (double)ss->net.icmpv4.OutEchoReps / deltasec); + return 1; +} + +static void +ICMPv4head(int osvers, int osrel, int ossub) +{ + printf("ierr/s isq/s ird/s idu/s ite/s " + "oerr/s osq/s ord/s odu/s ote/s_icmpv4_"); +} + +static int +ICMPv4line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, + time_t deltasec, time_t deltatic, time_t hz, + int osvers, int osrel, int ossub, char *tstamp, + int ppres, int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie) +{ + printf("%6.2lf %5.2lf %5.2lf %5.2lf %5.2lf " + "%6.2lf %5.2lf %5.2lf %5.2lf %5.2lf\n", + (double)ss->net.icmpv4.InErrors / deltasec, + (double)ss->net.icmpv4.InSrcQuenchs / deltasec, + (double)ss->net.icmpv4.InRedirects / deltasec, + (double)ss->net.icmpv4.InDestUnreachs / deltasec, + (double)ss->net.icmpv4.InTimeExcds / deltasec, + (double)ss->net.icmpv4.OutErrors / deltasec, + (double)ss->net.icmpv4.OutSrcQuenchs / deltasec, + (double)ss->net.icmpv4.OutRedirects / deltasec, + (double)ss->net.icmpv4.OutDestUnreachs / deltasec, + (double)ss->net.icmpv4.OutTimeExcds / deltasec); + return 1; +} + +/* +** UDP version 4 statistics +*/ +static void +udpv4head(int osvers, int osrel, int ossub) +{ + printf("indgram/s outdgram/s inerr/s noport/s " + " _udpv4_"); +} + +static int +udpv4line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, + time_t deltasec, time_t deltatic, time_t hz, + int osvers, int osrel, int ossub, char *tstamp, + int ppres, int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie) +{ + printf("%9.1lf %10.1lf %7.2lf %9.2lf\n", + (double)ss->net.udpv4.InDatagrams / deltasec, + (double)ss->net.udpv4.OutDatagrams / deltasec, + (double)ss->net.udpv4.InErrors / deltasec, + (double)ss->net.udpv4.NoPorts / deltasec); + return 1; +} + +/* +** IP version 6 statistics +*/ +static void +ipv6head(int osvers, int osrel, int ossub) +{ + printf("inrecv/s outreq/s inmc/s outmc/s indeliv/s " + "reasmok/s fragcre/s _ipv6_"); +} + +static int +ipv6line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, + time_t deltasec, time_t deltatic, time_t hz, + int osvers, int osrel, int ossub, char *tstamp, + int ppres, int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie) +{ + printf("%8.1lf %8.1lf %6.1lf %7.1lf %9.1lf %9.1lf %9.1lf\n", + (double)ss->net.ipv6.Ip6InReceives / deltasec, + (double)ss->net.ipv6.Ip6OutRequests / deltasec, + (double)ss->net.ipv6.Ip6InMcastPkts / deltasec, + (double)ss->net.ipv6.Ip6OutMcastPkts / deltasec, + (double)ss->net.ipv6.Ip6InDelivers / deltasec, + (double)ss->net.ipv6.Ip6ReasmOKs / deltasec, + (double)ss->net.ipv6.Ip6FragCreates / deltasec); + return 1; +} + +static void +IPv6head(int osvers, int osrel, int ossub) +{ + printf("in: dsc/s hder/s ader/s unkp/s ratim/s rfail/s " + "out: dsc/s nrt/s_ipv6_"); +} + +static int +IPv6line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, + time_t deltasec, time_t deltatic, time_t hz, + int osvers, int osrel, int ossub, char *tstamp, + int ppres, int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie) +{ + printf(" %5.1lf %6.1lf %6.1lf %6.1lf %7.1lf %7.1lf " + " %5.1lf %5.1lf\n", + (double)ss->net.ipv6.Ip6InDiscards / deltasec, + (double)ss->net.ipv6.Ip6InHdrErrors / deltasec, + (double)ss->net.ipv6.Ip6InAddrErrors / deltasec, + (double)ss->net.ipv6.Ip6InUnknownProtos / deltasec, + (double)ss->net.ipv6.Ip6ReasmTimeout / deltasec, + (double)ss->net.ipv6.Ip6ReasmFails / deltasec, + (double)ss->net.ipv6.Ip6OutDiscards / deltasec, + (double)ss->net.ipv6.Ip6OutNoRoutes / deltasec); + return 1; +} + +/* +** ICMP version 6 statistics +*/ +static void +icmpv6head(int osvers, int osrel, int ossub) +{ + printf("intot/s outtot/s inerr/s innsol/s innadv/s " + "otnsol/s otnadv/s _icmp6_" ); +} + +static int +icmpv6line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, + time_t deltasec, time_t deltatic, time_t hz, + int osvers, int osrel, int ossub, char *tstamp, + int ppres, int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie) +{ + printf("%7.1lf %8.1lf %7.2lf %8.2lf %8.2lf %8.2lf %8.2lf\n", + (double)ss->net.icmpv6.Icmp6InMsgs / deltasec, + (double)ss->net.icmpv6.Icmp6OutMsgs / deltasec, + (double)ss->net.icmpv6.Icmp6InErrors / deltasec, + (double)ss->net.icmpv6.Icmp6InNeighborSolicits / deltasec, + (double)ss->net.icmpv6.Icmp6InNeighborAdvertisements/ deltasec, + (double)ss->net.icmpv6.Icmp6OutNeighborSolicits / deltasec, + (double)ss->net.icmpv6.Icmp6OutNeighborAdvertisements + /deltasec); + return 1; +} + +static void +ICMPv6head(int osvers, int osrel, int ossub) +{ + printf("iecho/s ierep/s oerep/s idu/s odu/s ird/s ord/s ite/s " + "ote/s _icmpv6_"); +} + +static int +ICMPv6line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, + time_t deltasec, time_t deltatic, time_t hz, + int osvers, int osrel, int ossub, char *tstamp, + int ppres, int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie) +{ + printf("%7.2lf %7.2lf %7.2lf %5.2lf %5.2lf " + "%5.2lf %5.2lf %5.2lf %5.2lf\n", + (double)ss->net.icmpv6.Icmp6InEchos / deltasec, + (double)ss->net.icmpv6.Icmp6InEchoReplies / deltasec, + (double)ss->net.icmpv6.Icmp6OutEchoReplies / deltasec, + (double)ss->net.icmpv6.Icmp6InDestUnreachs / deltasec, + (double)ss->net.icmpv6.Icmp6OutDestUnreachs / deltasec, + (double)ss->net.icmpv6.Icmp6InRedirects / deltasec, + (double)ss->net.icmpv6.Icmp6OutRedirects / deltasec, + (double)ss->net.icmpv6.Icmp6InTimeExcds / deltasec, + (double)ss->net.icmpv6.Icmp6OutTimeExcds / deltasec); + return 1; +} + +/* +** UDP version 6 statistics +*/ +static void +udpv6head(int osvers, int osrel, int ossub) +{ + printf("indgram/s outdgram/s inerr/s noport/s " + " _udpv6_"); +} + +static int +udpv6line(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, + time_t deltasec, time_t deltatic, time_t hz, + int osvers, int osrel, int ossub, char *tstamp, + int ppres, int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie) +{ + printf("%9.1lf %10.1lf %7.2lf %9.2lf\n", + (double)ss->net.udpv6.Udp6InDatagrams / deltasec, + (double)ss->net.udpv6.Udp6OutDatagrams / deltasec, + (double)ss->net.udpv6.Udp6InErrors / deltasec, + (double)ss->net.udpv6.Udp6NoPorts / deltasec); + return 1; +} + +/* +** TCP statistics +*/ +static void +tcphead(int osvers, int osrel, int ossub) +{ + printf("insegs/s outsegs/s actopen/s pasopen/s " + "nowopen _tcp_"); +} + +static int +tcpline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, + time_t deltasec, time_t deltatic, time_t hz, + int osvers, int osrel, int ossub, char *tstamp, + int ppres, int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie) +{ + printf("%8.1lf %9.1lf %9.1lf %9.1lf %7lld\n", + (double)ss->net.tcp.InSegs / deltasec, + (double)ss->net.tcp.OutSegs / deltasec, + (double)ss->net.tcp.ActiveOpens / deltasec, + (double)ss->net.tcp.PassiveOpens / deltasec, + ss->net.tcp.CurrEstab); + return 1; +} + +static void +TCPhead(int osvers, int osrel, int ossub) +{ + printf("inerr/s retrans/s attfail/s " + "estabreset/s outreset/s _tcp_"); +} + +static int +TCPline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, + time_t deltasec, time_t deltatic, time_t hz, + int osvers, int osrel, int ossub, char *tstamp, + int ppres, int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie) +{ + printf("%7.1lf %9.1lf %9.1lf %12.1lf %10.1lf\n", + (double)ss->net.tcp.InErrs / deltasec, + (double)ss->net.tcp.RetransSegs / deltasec, + (double)ss->net.tcp.AttemptFails / deltasec, + (double)ss->net.tcp.EstabResets / deltasec, + (double)ss->net.tcp.OutRsts / deltasec); + return 1; +} + +#if HTTPSTATS +static void +httphead(int osvers, int osrel, int ossub) +{ + printf("requests/s Kbytes/s bytes/req " + "idleworkers busyworkers _http_"); +} + +static int +httpline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, + time_t deltasec, time_t deltatic, time_t hz, + int osvers, int osrel, int ossub, char *tstamp, + int ppres, int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie) +{ + printf("%10.2lf %8.2lf %9.2lf %11d %11d\n", + (double)ss->www.accesses / deltasec, + (double)ss->www.totkbytes / deltasec, + ss->www.accesses ? + (double)ss->www.totkbytes*1024/ss->www.accesses : 0, + ss->www.iworkers, + ss->www.bworkers); + + return 1; +} +#endif + +/* +** per-process statistics: top-3 processor consumers +*/ +static void +topchead(int osvers, int osrel, int ossub) +{ + printf(" pid command cpu%% | pid command cpu%% | " + " pid command cpu%%_top3_"); +} + +static int +topcline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, + time_t deltasec, time_t deltatic, time_t hz, + int osvers, int osrel, int ossub, char *tstamp, + int ppres, int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie) +{ + count_t availcpu; + + if (!ts) + { + printf("report not available.....\n"); + return 0; + } + + /* + ** sort process list in cpu order + */ + qsort(ps, nactproc, sizeof(struct tstat *), compcpu); + + availcpu = ss->cpu.all.stime + ss->cpu.all.utime + + ss->cpu.all.ntime + ss->cpu.all.itime + + ss->cpu.all.wtime + ss->cpu.all.Itime + + ss->cpu.all.Stime + ss->cpu.all.steal; + + availcpu /= ss->cpu.nrcpu; + + if (availcpu == 0) + availcpu = 1; /* avoid divide-by-zero */ + + if (nactproc >= 1 && (ps[0])->cpu.stime + (ps[0])->cpu.utime > 0) + printf("%5d %-8.8s %3.0lf%% | ", + (ps[0])->gen.pid, (ps[0])->gen.name, + (double)((ps[0])->cpu.stime + (ps[0])->cpu.utime)*100.0/availcpu); + else + printf("%19s | ", " "); + + if (nactproc >= 2 && (ps[1])->cpu.stime + (ps[1])->cpu.utime > 0) + printf("%5d %-8.8s %3.0lf%% | ", + (ps[1])->gen.pid, (ps[1])->gen.name, + (double)((ps[1])->cpu.stime + (ps[1])->cpu.utime)*100.0/availcpu); + else + printf("%19s | ", " "); + + if (nactproc >= 3 && (ps[2])->cpu.stime + (ps[2])->cpu.utime > 0) + printf("%5d %-8.8s %3.0lf%%\n", + (ps[2])->gen.pid, (ps[2])->gen.name, + (double)((ps[2])->cpu.stime + (ps[2])->cpu.utime)*100.0/availcpu); + else + printf("%19s\n", " "); + + + return 1; +} + +/* +** per-process statistics: top-3 memory consumers +*/ +static void +topmhead(int osvers, int osrel, int ossub) +{ + printf(" pid command mem%% | pid command mem%% | " + " pid command mem%%_top3_"); +} + +static int +topmline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, + time_t deltasec, time_t deltatic, time_t hz, + int osvers, int osrel, int ossub, char *tstamp, + int ppres, int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie) +{ + count_t availmem; + + if (!ts) + { + printf("report not available.....\n"); + return 0; + } + + /* + ** sort process list in memory order + */ + qsort(ps, nactproc, sizeof(struct tstat *), compmem); + + availmem = ss->mem.physmem * pagesize/1024; + + if (nactproc >= 1) + printf("%5d %-8.8s %3.0lf%% | ", + (ps[0])->gen.pid, (ps[0])->gen.name, + (double)(ps[0])->mem.rmem * 100.0 / availmem); + else + printf("%19s | ", " "); + + if (nactproc >= 2) + printf("%5d %-8.8s %3.0lf%% | ", + (ps[1])->gen.pid, (ps[1])->gen.name, + (double)(ps[1])->mem.rmem * 100.0 / availmem); + else + printf("%19s | ", " "); + + if (nactproc >= 3) + printf("%5d %-8.8s %3.0lf%%\n", + (ps[2])->gen.pid, (ps[2])->gen.name, + (double)(ps[2])->mem.rmem * 100.0 / availmem); + else + printf("%19s\n", " "); + + + return 1; +} + +/* +** per-process statistics: top-3 disk consumers +*/ +static void +topdhead(int osvers, int osrel, int ossub) +{ + printf(" pid command dsk%% | pid command dsk%% | " + " pid command dsk%%_top3_"); +} + +static int +topdline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, + time_t deltasec, time_t deltatic, time_t hz, + int osvers, int osrel, int ossub, char *tstamp, + int ppres, int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie) +{ + int i; + count_t availdsk; + + if (!ts) + { + printf("report not available.....\n"); + return 0; + } + + if ( !(supportflags & IOSTAT) ) + { + printf("no per-process disk counters available.....\n"); + return 0; + } + + /* + ** determine total disk accesses for all processes + */ + for (i=0, availdsk=0; i < nactproc; i++) + { + availdsk += (ps[i])->dsk.rio + (ps[i])->dsk.wio; + } + + if (availdsk == 0) + availdsk = 1; + + /* + ** sort process list in disk order + */ + qsort(ps, nactproc, sizeof(struct tstat *), compdsk); + + if (nactproc >= 1 && (ps[0])->dsk.rio + (ps[0])->dsk.wio > 0) + printf("%5d %-8.8s %3.0lf%% | ", + (ps[0])->gen.pid, (ps[0])->gen.name, + (double)((ps[0])->dsk.rio+(ps[0])->dsk.wio) *100.0/availdsk); + else + printf("%19s | ", " "); + + if (nactproc >= 2 && (ps[1])->dsk.rio + (ps[1])->dsk.wio > 0) + printf("%5d %-8.8s %3.0lf%% | ", + (ps[1])->gen.pid, (ps[1])->gen.name, + (double)((ps[1])->dsk.rio+(ps[1])->dsk.wio) *100.0/availdsk); + else + printf("%19s | ", " "); + + if (nactproc >= 3 && (ps[2])->dsk.rio + (ps[2])->dsk.wio > 0) + printf("%5d %-8.8s %3.0lf%%\n", + (ps[2])->gen.pid, (ps[2])->gen.name, + (double)((ps[2])->dsk.rio+(ps[2])->dsk.wio) *100.0/availdsk); + else + printf("%19s\n", " "); + + + return 1; +} + +/* +** per-process statistics: top-3 network consumers +*/ +static void +topnhead(int osvers, int osrel, int ossub) +{ + printf(" pid command net%% | pid command net%% | " + " pid command net%%_top3_"); +} + +static int +topnline(struct sstat *ss, struct tstat *ts, struct tstat **ps, int nactproc, + time_t deltasec, time_t deltatic, time_t hz, + int osvers, int osrel, int ossub, char *tstamp, + int ppres, int ntrun, int ntslpi, int ntslpu, int pexit, int pzombie) +{ + int i; + count_t availnet; + count_t totbytes; + + if (!ts) + { + printf("report not available.....\n"); + return 0; + } + + if ( !(supportflags & NETATOP) ) + { + printf("no per-process network counters available.....\n"); + return 0; + } + + /* + ** determine total network accesses for all processes + */ + for (i=0, availnet=0; i < nactproc; i++) + { + availnet += (*(ps+i))->net.tcpssz + (*(ps+i))->net.tcprsz + + (*(ps+i))->net.udpssz + (*(ps+i))->net.udprsz; + } + + if (availnet == 0) + availnet = 1; + + /* + ** sort process list in network order + */ + qsort(ps, nactproc, sizeof(struct tstat *), compnet); + + if (nactproc >= 1) + { + totbytes = (ps[0])->net.tcpssz + (ps[0])->net.tcprsz + + (ps[0])->net.udpssz + (ps[0])->net.udprsz; + + if (totbytes > 0) + printf("%5d %-8.8s %3.0lf%% | ", + (ps[0])->gen.pid, (ps[0])->gen.name, + (double)totbytes * 100.0 / availnet); + else + printf("%19s | ", " "); + } + else + printf("%19s | ", " "); + + if (nactproc >= 2) + { + totbytes = (ps[1])->net.tcpssz + (ps[1])->net.tcprsz + + (ps[1])->net.udpssz + (ps[1])->net.udprsz; + + if (totbytes > 0) + printf("%5d %-8.8s %3.0lf%% | ", + (ps[1])->gen.pid, (ps[1])->gen.name, + (double)totbytes * 100.0 / availnet); + else + printf("%19s | ", " "); + } + else + printf("%19s | ", " "); + + if (nactproc >= 3) + { + totbytes = (ps[2])->net.tcpssz + (ps[2])->net.tcprsz + + (ps[2])->net.udpssz + (ps[2])->net.udprsz; + + if (totbytes > 0) + printf("%5d %-8.8s %3.0lf%%\n", + (ps[2])->gen.pid, (ps[2])->gen.name, + (double)totbytes * 100.0 / availnet); + else + printf("%19s\n", " "); + } + else + printf("%19s\n", " "); + + + return 1; +} + +/*********************************************************************/ +/* Function definition table. */ +/* */ +/* The layout of this table is as follows: */ +/* Column 1: */ +/* Boolean which indicates if the specified function is */ +/* active during a run of 'atopsar'. When started, */ +/* this boolean will be defined 'true' for all entries for */ +/* which the command-line flag has been specified. Initially */ +/* this column should contain 0 (false), unless this function */ +/* is always required. */ +/* If no flags are specified for 'atopsar', the first entry */ +/* in this table is defined active (default flag). */ +/* */ +/* Column 2: */ +/* Categories of counters used by this function. */ +/* c = cpu counters, m = memory counters, */ +/* d = disk counters, n = network counters */ +/* */ +/* Column 3: */ +/* Flag which can be used as command-line argument to */ +/* select the function defined in this table-entry. Be sure */ +/* that a unique character is choosen. */ +/* Notice that certain flags are reserved! */ +/* */ +/* Column 4: */ +/* Entry-point of the 'printhead' function. */ +/* */ +/* Column 5: */ +/* Entry-point of the 'printline' function. */ +/* */ +/* Column 6: */ +/* Information about the statistics shown by the function */ +/* specified by the table-entry. This text is printed as */ +/* command-usage. */ +/*********************************************************************/ +struct pridef pridef[] = +{ + {0, "c", 'c', cpuhead, cpuline, "cpu utilization", }, + {0, "c", 'p', prochead, procline, "process(or) load", }, + {0, "c", 'P', taskhead, taskline, "processes & threads", }, + {0, "m", 'm', memhead, memline, "memory & swapspace", }, + {0, "m", 's', swaphead, swapline, "swap rate", }, + {0, "cd", 'l', lvmhead, lvmline, "logical volume activity", }, + {0, "cd", 'f', mddhead, mddline, "multiple device activity",}, + {0, "cd", 'd', dskhead, dskline, "disk activity", }, + {0, "n", 'n', nfmhead, nfmline, "NFS client mounts", }, + {0, "n", 'j', nfchead, nfcline, "NFS client activity", }, + {0, "n", 'J', nfshead, nfsline, "NFS server activity", }, + {0, "n", 'i', ifhead, ifline, "net-interf (general)", }, + {0, "n", 'I', IFhead, IFline, "net-interf (errors)", }, + {0, "n", 'w', ipv4head, ipv4line, "ip v4 (general)", }, + {0, "n", 'W', IPv4head, IPv4line, "ip v4 (errors)", }, + {0, "n", 'y', icmpv4head, icmpv4line, "icmp v4 (general)", }, + {0, "n", 'Y', ICMPv4head, ICMPv4line, "icmp v4 (per type)", }, + {0, "n", 'u', udpv4head, udpv4line, "udp v4", }, + {0, "n", 'z', ipv6head, ipv6line, "ip v6 (general)", }, + {0, "n", 'Z', IPv6head, IPv6line, "ip v6 (errors)", }, + {0, "n", 'k', icmpv6head, icmpv6line, "icmp v6 (general)", }, + {0, "n", 'K', ICMPv6head, ICMPv6line, "icmp v6 (per type)", }, + {0, "n", 'U', udpv6head, udpv6line, "udp v6", }, + {0, "n", 't', tcphead, tcpline, "tcp (general)", }, + {0, "n", 'T', TCPhead, TCPline, "tcp (errors)", }, +#if HTTPSTATS + {0, "n", 'h', httphead, httpline, "HTTP activity", }, +#endif + {0, "", 'O', topchead, topcline, "top-3 processes cpu", }, + {0, "", 'G', topmhead, topmline, "top-3 processes memory", }, + {0, "", 'D', topdhead, topdline, "top-3 processes disk", }, + {0, "", 'N', topnhead, topnline, "top-3 processes network",}, +}; + +int pricnt = sizeof(pridef)/sizeof(struct pridef); diff --git a/sources/deviate.c b/sources/deviate.c new file mode 100644 index 0000000..b7f66b4 --- /dev/null +++ b/sources/deviate.c @@ -0,0 +1,1620 @@ +/* +** ATOP - System & Process Monitor +** +** The program 'atop' offers the possibility to view the activity of +** the system on system-level as well as process-level. +** +** This source-file contains functions to calculate the differences for +** the system-level and process-level counters since the previous sample. +** ========================================================================== +** Author: Gerlof Langeveld +** E-mail: gerlof.langeveld@atoptool.nl +** Date: November 1996 +** LINUX-port: June 2000 +** -------------------------------------------------------------------------- +** Copyright (C) 2000-2010 Gerlof Langeveld +** +** This program is free software; you can redistribute it and/or modify it +** under the terms of the GNU General Public License as published by the +** Free Software Foundation; either version 2, or (at your option) any +** later version. +** +** This program is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +** See the GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +** -------------------------------------------------------------------------- +** +** $Log: deviate.c,v $ +** Revision 1.45 2010/10/23 14:02:03 gerlof +** Show counters for total number of running and sleep (S and D) threads. +** +** Revision 1.44 2010/05/18 19:19:43 gerlof +** Introduce CPU frequency and scaling (JC van Winkel). +** +** Revision 1.43 2010/04/23 12:19:35 gerlof +** Modified mail-address in header. +** +** Revision 1.42 2010/03/04 10:52:08 gerlof +** Support I/O-statistics on logical volumes and MD devices. +** +** Revision 1.41 2009/12/31 11:34:21 gerlof +** Sanity-check to bypass kernel-bug showing 497 days of CPU-consumption. +** +** Revision 1.40 2009/12/17 11:58:25 gerlof +** Gather and display new counters: dirty cache and guest cpu usage. +** +** Revision 1.39 2008/02/25 14:51:18 gerlof +** Experimental code for HTTP-statistics. +** +** Revision 1.38 2008/01/07 11:33:43 gerlof +** Cosmetic changes. +** +** Revision 1.37 2008/01/07 10:17:24 gerlof +** Implement possibility to make summaries. +** +** Revision 1.36 2007/11/05 12:13:16 gerlof +** Match processes not only on pid, but also on start time. +** +** Revision 1.35 2007/11/05 11:42:47 gerlof +** Bug-solution for new-process indicator on 64-bits machines. +** +** Revision 1.34 2007/08/17 09:44:59 gerlof +** Experimental: gather info about HTTP statistics. +** +** Revision 1.33 2007/08/16 11:59:32 gerlof +** Add support for atopsar reporting. +** Concerns addition of lots of counters. +** +** Revision 1.32 2007/07/03 09:01:07 gerlof +** Support Apache-statistics. +** +** Revision 1.31 2007/03/20 13:02:03 gerlof +** Introduction of variable supportflags. +** +** Revision 1.30 2007/03/20 11:18:57 gerlof +** Add counter for cancelled writes. +** +** Revision 1.29 2007/02/13 09:21:04 gerlof +** Removed external declarations. +** +** Revision 1.28 2007/01/22 08:28:18 gerlof +** Support steal-time from /proc/stat. +** +** Revision 1.27 2007/01/18 10:43:18 gerlof +** Support for network-interface busy-percentage (speed and duplex). +** +** Revision 1.26 2006/11/13 13:47:26 gerlof +** Implement load-average counters, context-switches and interrupts. +** +** Revision 1.25 2006/02/07 06:45:33 gerlof +** Removed swap-counter. +** +** Revision 1.24 2006/01/30 09:13:33 gerlof +** Extend memory counters (a.o. page scans). +** +** Revision 1.23 2005/10/31 12:45:29 gerlof +** Support account-record version 3 (used by Mandriva). +** +** Revision 1.22 2005/10/21 09:49:38 gerlof +** Per-user accumulation of resource consumption. +** +** Revision 1.21 2004/12/14 15:05:47 gerlof +** Implementation of patch-recognition for disk and network-statistics. +** +** Revision 1.20 2004/10/28 08:30:51 gerlof +** New counter: vm committed space +** +** Revision 1.19 2004/09/24 10:02:01 gerlof +** Wrong cpu-numbers for system level statistics. +** +** Revision 1.18 2004/09/02 10:49:18 gerlof +** Added sleep-average to process-info. +** +** Revision 1.17 2004/08/31 13:27:04 gerlof +** Add new info for threading. +** +** Revision 1.16 2004/05/07 05:49:40 gerlof +** *** empty log message *** +** +** Revision 1.15 2004/05/06 09:46:55 gerlof +** Ported to kernel-version 2.6. +** +** Revision 1.14 2003/07/07 09:26:33 gerlof +** Cleanup code (-Wall proof). +** +** Revision 1.13 2003/07/03 11:17:49 gerlof +** Corrected calculations for exited processes. +** +** Revision 1.12 2003/06/30 11:30:57 gerlof +** Enlarge counters to 'long long'. +** +** Revision 1.11 2003/06/24 06:21:12 gerlof +** Limit number of system resource lines. +** +** Revision 1.10 2003/01/24 14:20:16 gerlof +** If possible, also show commandline when process has exited. +** +** Revision 1.9 2002/09/16 08:58:08 gerlof +** Add indicator for newly created processes. +** +** Revision 1.8 2002/08/27 04:47:46 gerlof +** Minor comment updates. +** +** Revision 1.7 2002/07/24 11:12:20 gerlof +** Redesigned to ease porting to other UNIX-platforms. +** +** Revision 1.6 2002/07/10 04:59:37 root +** Counters pin/pout renamed to swin/swout (Linux conventions). +** +** Revision 1.5 2002/01/22 13:39:20 gerlof +** Support for number of cpu's. +** +** Revision 1.4 2001/11/22 08:33:10 gerlof +** Add priority per process. +** +** Revision 1.3 2001/11/07 09:18:22 gerlof +** Use /proc instead of /dev/kmem for process-level statistics. +** +** Revision 1.2 2001/10/03 08:58:41 gerlof +** Improved subtraction which is overflow-proof +** +** Revision 1.1 2001/10/02 10:43:23 gerlof +** Initial revision +** +*/ + +static const char rcsid[] = "$Id: deviate.c,v 1.45 2010/10/23 14:02:03 gerlof Exp $"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "atop.h" +#include "ifprop.h" +#include "photoproc.h" +#include "photosyst.h" + +#define MAX32BITVAL 0x100000000LL + +static void calcdiff(struct tstat *, struct tstat *, struct tstat *, + char, count_t); + +/* +** calculate the process-activity during the last sample +*/ +void +deviattask(struct tstat *curtpres, int ntaskpres, + struct tstat *curpexit, int nprocexit, + struct devtstat *devtstat, + struct sstat *devsstat) +{ + register int c, d, pall=0, pact=0; + register struct tstat *curstat, *devstat, *thisproc; + struct tstat prestat; + struct pinfo *pinfo; + count_t totusedcpu; + char hashtype = 'p'; + + /* + ** needed for sanity check later on... + */ + totusedcpu = devsstat->cpu.all.stime + devsstat->cpu.all.utime + + devsstat->cpu.all.ntime + devsstat->cpu.all.itime + + devsstat->cpu.all.wtime + devsstat->cpu.all.Itime + + devsstat->cpu.all.Stime + devsstat->cpu.all.steal; + + /* + ** make new list of all tasks in the task-database; + ** after handling all task, the left-overs are tasks + ** that have disappeared since the previous sample + */ + pdb_makeresidue(); + + /* + ** remove allocated lists of previous sample and initialize counters + */ + if (devtstat->taskall) + free(devtstat->taskall); + + if (devtstat->procall) + free(devtstat->procall); + + if (devtstat->procactive) + free(devtstat->procactive); + + memset(devtstat, 0, sizeof *devtstat); + + /* + ** create list for the sample deviations of all tasks + */ + devtstat->ntaskall = ntaskpres + nprocexit; + devtstat->taskall = malloc(devtstat->ntaskall * sizeof(struct tstat)); + + ptrverify(devtstat->taskall, "Malloc failed for %d deviated tasks\n", + devtstat->ntaskall); + + /* + ** calculate deviations per present task + */ + for (c=0, thisproc = devtstat->taskall; c < ntaskpres; c++) + { + char newtask = 0; + + curstat = curtpres+c; + devstat = devtstat->taskall+c; + + if (curstat->gen.isproc) + { + thisproc = devstat; // remember last process seen + + devtstat->nprocall++; + + if (curstat->gen.state == 'Z') + { + devtstat->totzombie++; + } + else + { + devtstat->totrun += curstat->gen.nthrrun; + devtstat->totslpi += curstat->gen.nthrslpi; + devtstat->totslpu += curstat->gen.nthrslpu; + } + } + + /* + ** get previous figures from task-database + */ + if ( pdb_gettask(curstat->gen.pid, curstat->gen.isproc, + curstat->gen.btime, &pinfo)) + { + /* + ** task already present in the previous sample + ** + ** save stats from previous sample (to use for + ** further calculations) and store new statistics + ** in task-database + */ + if (memcmp(curstat, &pinfo->tstat, + sizeof(struct tstat)) == EQ) + { + /* + ** no activity for task + */ + curstat->gen.wasinactive = 1; + } + else + { + /* + ** save the values of the previous sample + ** and overwrite the previous sample in + ** the database with the current sample + */ + prestat = pinfo->tstat; + pinfo->tstat = *curstat; + + curstat->gen.wasinactive = 0; + + devtstat->ntaskactive++; + + if (curstat->gen.isproc) + { + devtstat->nprocactive++; + } + else + { + if (thisproc->gen.wasinactive) + { + thisproc->gen.wasinactive = 0; + devtstat->ntaskactive++; + devtstat->nprocactive++; + } + } + } + } + else + { + /* + ** new task which must have been started during + ** last interval + */ + memset(&prestat, 0, sizeof(prestat)); + + curstat->gen.wasinactive = 0; + devtstat->ntaskactive++; + + if (curstat->gen.isproc) + { + devtstat->nprocactive++; + } + else + { + if (thisproc->gen.wasinactive) + { + thisproc->gen.wasinactive = 0; + devtstat->ntaskactive++; + devtstat->nprocactive++; + } + } + + /* + ** create new task struct + */ + pinfo = calloc(1, sizeof(struct pinfo)); + + ptrverify(pinfo, "Malloc failed for new pinfo\n"); + + pinfo->tstat = *curstat; + + /* + ** add new task to task-database + */ + pdb_addtask(curstat->gen.pid, pinfo); + + newtask = 1; + } + + /* + ** do the difference calculations + */ + calcdiff(devstat, curstat, &prestat, newtask, totusedcpu); + } + + /* + ** calculate deviations per exited process + */ + if (nprocexit > 0 && supportflags&NETATOPD) + { + if (curpexit->gen.pid) + hashtype = 'p'; + else + hashtype = 'b'; + + netatop_exithash(hashtype); + } + + for (d=c, c=0; c < nprocexit; c++) + { + /* + ** check if this process has been started AND + ** finished since previous sample; + ** if so, it has no use to check if there is still + ** existing info present in the process-database + */ + curstat = curpexit+c; + curstat->gen.wasinactive = 0; + + devtstat->nprocall++; + devtstat->nprocactive++; + devtstat->ntaskactive++; + + if (curstat->gen.pid) /* acctrecord contains pid? */ + { + if ( pdb_gettask(curstat->gen.pid, 1, + curstat->gen.btime, &pinfo)) + prestat = pinfo->tstat; + else + memset(&prestat, 0, sizeof(prestat)); + } + else + { + if ( curstat->gen.btime > pretime ) + { + /* + ** process-start and -finish in same interval + */ + memset(&prestat, 0, sizeof(prestat)); + } + else + { + /* + ** process must be known in process-database; + ** try to match one of the remaining processes + ** against this exited one + */ + if ( pdb_srchresidue(curstat, &pinfo) ) + prestat = pinfo->tstat; + else + memset(&prestat, 0, sizeof(prestat)); + } + } + + /* + ** now do the calculations + */ + devstat = devtstat->taskall+d; + memset(devstat, 0, sizeof *devstat); + + devstat->gen = curstat->gen; + + if ( curstat->gen.pid == 0 ) + devstat->gen.pid = prestat.gen.pid; + + if (!prestat.gen.pid) + devstat->gen.excode |= ~(INT_MAX); + + strcpy(devstat->gen.cmdline, prestat.gen.cmdline); + + /* + ** due to the strange exponent-type storage of values + ** in the process accounting record, the resource-value + ** in the exit-record might have been smaller than the + ** stored value of the last registered sample; in that + ** case the deviation should be set to zero + */ + if (curstat->cpu.stime > prestat.cpu.stime) + devstat->cpu.stime = curstat->cpu.stime - + prestat.cpu.stime; + + if (curstat->cpu.utime > prestat.cpu.utime) + devstat->cpu.utime = curstat->cpu.utime - + prestat.cpu.utime; + + if (curstat->mem.minflt > prestat.mem.minflt) + devstat->mem.minflt = curstat->mem.minflt - + prestat.mem.minflt; + + if (curstat->mem.majflt > prestat.mem.majflt) + devstat->mem.majflt = curstat->mem.majflt - + prestat.mem.majflt; + + if (curstat->dsk.rio > (prestat.dsk.rio + prestat.dsk.wio)) + devstat->dsk.rio = curstat->dsk.rio - + prestat.dsk.rio - + prestat.dsk.wio; + + /* + ** try to match the network counters of netatop + */ + if (supportflags & NETATOPD) + { + unsigned long val = (hashtype == 'p' ? + curstat->gen.pid : + curstat->gen.btime); + + netatop_exitfind(val, devstat, &prestat); + } + + if (prestat.gen.pid > 0) + pdb_deltask(prestat.gen.pid, prestat.gen.isproc); + + d++; + } + + /* + ** remove unused entries from RESIDUE chain + */ + pdb_cleanresidue(); + + /* + ** create and fill other pointer lists + */ + devtstat->procall = malloc(devtstat->nprocall * + sizeof(struct tstat *)); + devtstat->procactive = malloc(devtstat->nprocactive * + sizeof(struct tstat *)); + + ptrverify(devtstat->procall, "Malloc failed for %d processes\n", + devtstat->nprocall); + + ptrverify(devtstat->procactive, "Malloc failed for %d active procs\n", + devtstat->nprocactive); + + + for (c=0, thisproc=devstat=devtstat->taskall; c < devtstat->ntaskall; + c++, devstat++) + { + if (devstat->gen.isproc) + { + devtstat->procall[pall++] = devstat; + + if (! devstat->gen.wasinactive) + devtstat->procactive[pact++] = devstat; + } + } +} + +/* +** calculate the differences between the current sample and +** the previous sample for a task +*/ +static void +calcdiff(struct tstat *devstat, struct tstat *curstat, struct tstat *prestat, + char newtask, count_t totusedcpu) +{ + /* + ** for inactive tasks, set all counters to zero + */ + if (curstat->gen.wasinactive) + memset(devstat, 0, sizeof *devstat); + + /* + ** copy all static values from the current task settings + */ + devstat->gen = curstat->gen; + + if (newtask) + devstat->gen.excode |= ~(INT_MAX); + + devstat->cpu.nice = curstat->cpu.nice; + devstat->cpu.prio = curstat->cpu.prio; + devstat->cpu.rtprio = curstat->cpu.rtprio; + devstat->cpu.policy = curstat->cpu.policy; + devstat->cpu.curcpu = curstat->cpu.curcpu; + devstat->cpu.sleepavg = curstat->cpu.sleepavg; + + devstat->mem.vexec = curstat->mem.vexec; + devstat->mem.vmem = curstat->mem.vmem; + devstat->mem.rmem = curstat->mem.rmem; + devstat->mem.pmem = curstat->mem.pmem; + devstat->mem.vdata = curstat->mem.vdata; + devstat->mem.vstack = curstat->mem.vstack; + devstat->mem.vlibs = curstat->mem.vlibs; + devstat->mem.vswap = curstat->mem.vswap; + + /* + ** for inactive tasks, only the static values had to be copied, while + ** all use counters have been set to zero + */ + if (curstat->gen.wasinactive) + return; + + devstat->cpu.stime = + subcount(curstat->cpu.stime, prestat->cpu.stime); + devstat->cpu.utime = + subcount(curstat->cpu.utime, prestat->cpu.utime); + + /* + ** particular kernel versions sometimes supply a smaller + ** amount for consumed CPU-ticks than a previous sample; + ** with unsigned calculations this results in 497 days of + ** CPU-consumption so a sanity-check is needed here... + */ + if (devstat->cpu.stime > totusedcpu) + devstat->cpu.stime = 1; + + if (devstat->cpu.utime > totusedcpu) + devstat->cpu.utime = 1; + + /* + ** do further calculations + */ + devstat->dsk.rio = + subcount(curstat->dsk.rio, prestat->dsk.rio); + devstat->dsk.rsz = + subcount(curstat->dsk.rsz, prestat->dsk.rsz); + devstat->dsk.wio = + subcount(curstat->dsk.wio, prestat->dsk.wio); + devstat->dsk.wsz = + subcount(curstat->dsk.wsz, prestat->dsk.wsz); + devstat->dsk.cwsz = + subcount(curstat->dsk.cwsz, prestat->dsk.cwsz); + + devstat->mem.vgrow = curstat->mem.vmem - prestat->mem.vmem; + devstat->mem.rgrow = curstat->mem.rmem - prestat->mem.rmem; + + devstat->mem.minflt = + subcount(curstat->mem.minflt, prestat->mem.minflt); + devstat->mem.majflt = + subcount(curstat->mem.majflt, prestat->mem.majflt); + + /* + ** network counters: due to an unload/load of the netatop module, + ** previous counters might be larger than the current + */ + if (curstat->net.tcpsnd >= prestat->net.tcpsnd) + devstat->net.tcpsnd = + subcount(curstat->net.tcpsnd, prestat->net.tcpsnd); + else + devstat->net.tcpsnd = curstat->net.tcpsnd; + + if (curstat->net.tcpssz >= prestat->net.tcpssz) + devstat->net.tcpssz = + subcount(curstat->net.tcpssz, prestat->net.tcpssz); + else + devstat->net.tcpssz = curstat->net.tcpssz; + + if (curstat->net.tcprcv >= prestat->net.tcprcv) + devstat->net.tcprcv = + subcount(curstat->net.tcprcv, prestat->net.tcprcv); + else + devstat->net.tcprcv = curstat->net.tcprcv; + + if (curstat->net.tcprsz >= prestat->net.tcprsz) + devstat->net.tcprsz = + subcount(curstat->net.tcprsz, prestat->net.tcprsz); + else + devstat->net.tcprsz = curstat->net.tcprsz; + + if (curstat->net.udpsnd >= prestat->net.udpsnd) + devstat->net.udpsnd = + subcount(curstat->net.udpsnd, prestat->net.udpsnd); + else + devstat->net.udpsnd = curstat->net.udpsnd; + + if (curstat->net.udpssz >= prestat->net.udpssz) + devstat->net.udpssz = + subcount(curstat->net.udpssz, prestat->net.udpssz); + else + devstat->net.udpssz = curstat->net.udpssz; + + if (curstat->net.udprcv >= prestat->net.udprcv) + devstat->net.udprcv = + subcount(curstat->net.udprcv, prestat->net.udprcv); + else + devstat->net.udprcv = curstat->net.udprcv; + + if (curstat->net.udprsz >= prestat->net.udprsz) + devstat->net.udprsz = + subcount(curstat->net.udprsz, prestat->net.udprsz); + else + devstat->net.udprsz = curstat->net.udprsz; +} + +/* +** calculate the system-activity during the last sample +*/ +void +deviatsyst(struct sstat *cur, struct sstat *pre, struct sstat *dev, + long interval) +{ + register int i, j; + count_t *cdev, *ccur, *cpre; + struct ifprop ifprop; + + dev->cpu.nrcpu = cur->cpu.nrcpu; + dev->cpu.devint = subcount(cur->cpu.devint, pre->cpu.devint); + dev->cpu.csw = subcount(cur->cpu.csw, pre->cpu.csw); + dev->cpu.nprocs = subcount(cur->cpu.nprocs, pre->cpu.nprocs); + + dev->cpu.all.stime = subcount(cur->cpu.all.stime, pre->cpu.all.stime); + dev->cpu.all.utime = subcount(cur->cpu.all.utime, pre->cpu.all.utime); + dev->cpu.all.ntime = subcount(cur->cpu.all.ntime, pre->cpu.all.ntime); + dev->cpu.all.itime = subcount(cur->cpu.all.itime, pre->cpu.all.itime); + dev->cpu.all.wtime = subcount(cur->cpu.all.wtime, pre->cpu.all.wtime); + dev->cpu.all.Itime = subcount(cur->cpu.all.Itime, pre->cpu.all.Itime); + dev->cpu.all.Stime = subcount(cur->cpu.all.Stime, pre->cpu.all.Stime); + + dev->cpu.all.steal = subcount(cur->cpu.all.steal, pre->cpu.all.steal); + dev->cpu.all.guest = subcount(cur->cpu.all.guest, pre->cpu.all.guest); + + for (i=0; i < dev->cpu.nrcpu; i++) + { + count_t ticks; + + dev->cpu.cpu[i].cpunr = cur->cpu.cpu[i].cpunr; + dev->cpu.cpu[i].stime = subcount(cur->cpu.cpu[i].stime, + pre->cpu.cpu[i].stime); + dev->cpu.cpu[i].utime = subcount(cur->cpu.cpu[i].utime, + pre->cpu.cpu[i].utime); + dev->cpu.cpu[i].ntime = subcount(cur->cpu.cpu[i].ntime, + pre->cpu.cpu[i].ntime); + dev->cpu.cpu[i].itime = subcount(cur->cpu.cpu[i].itime, + pre->cpu.cpu[i].itime); + dev->cpu.cpu[i].wtime = subcount(cur->cpu.cpu[i].wtime, + pre->cpu.cpu[i].wtime); + dev->cpu.cpu[i].Itime = subcount(cur->cpu.cpu[i].Itime, + pre->cpu.cpu[i].Itime); + dev->cpu.cpu[i].Stime = subcount(cur->cpu.cpu[i].Stime, + pre->cpu.cpu[i].Stime); + + dev->cpu.cpu[i].steal = subcount(cur->cpu.cpu[i].steal, + pre->cpu.cpu[i].steal); + dev->cpu.cpu[i].guest = subcount(cur->cpu.cpu[i].guest, + pre->cpu.cpu[i].guest); + + ticks = cur->cpu.cpu[i].freqcnt.ticks; + + dev->cpu.cpu[i].freqcnt.maxfreq = + cur->cpu.cpu[i].freqcnt.maxfreq; + dev->cpu.cpu[i].freqcnt.cnt = ticks ? + subcount(cur->cpu.cpu[i].freqcnt.cnt, + pre->cpu.cpu[i].freqcnt.cnt) + : cur->cpu.cpu[i].freqcnt.cnt; + + dev->cpu.cpu[i].freqcnt.ticks = ticks ? + subcount(cur->cpu.cpu[i].freqcnt.ticks, + pre->cpu.cpu[i].freqcnt.ticks) + : cur->cpu.cpu[i].freqcnt.ticks; + } + + dev->cpu.lavg1 = cur->cpu.lavg1; + dev->cpu.lavg5 = cur->cpu.lavg5; + dev->cpu.lavg15 = cur->cpu.lavg15; + + dev->mem.physmem = cur->mem.physmem; + dev->mem.freemem = cur->mem.freemem; + dev->mem.buffermem = cur->mem.buffermem; + dev->mem.slabmem = cur->mem.slabmem; + dev->mem.slabreclaim = cur->mem.slabreclaim; + dev->mem.committed = cur->mem.committed; + dev->mem.commitlim = cur->mem.commitlim; + dev->mem.cachemem = cur->mem.cachemem; + dev->mem.cachedrt = cur->mem.cachedrt; + dev->mem.totswap = cur->mem.totswap; + dev->mem.freeswap = cur->mem.freeswap; + + dev->mem.shmem = cur->mem.shmem; + dev->mem.shmrss = cur->mem.shmrss; + dev->mem.shmswp = cur->mem.shmswp; + + dev->mem.tothugepage = cur->mem.tothugepage; + dev->mem.freehugepage = cur->mem.freehugepage; + dev->mem.hugepagesz = cur->mem.hugepagesz; + + dev->mem.vmwballoon = cur->mem.vmwballoon; + + dev->mem.swouts = subcount(cur->mem.swouts, pre->mem.swouts); + dev->mem.swins = subcount(cur->mem.swins, pre->mem.swins); + dev->mem.pgscans = subcount(cur->mem.pgscans, pre->mem.pgscans); + dev->mem.pgsteal = subcount(cur->mem.pgsteal, pre->mem.pgsteal); + dev->mem.allocstall = subcount(cur->mem.allocstall, + pre->mem.allocstall); + + /* + ** structures with network-related counters are considered + ** as tables of frequency-counters that have to be subtracted; + ** values that do not represent a frequency are corrected afterwards + */ + for (cdev = (count_t *)&dev->net.ipv4, + ccur = (count_t *)&cur->net.ipv4, + cpre = (count_t *)&pre->net.ipv4, + i = 0; + i < (sizeof dev->net.ipv4 / sizeof(count_t)); + cdev++, ccur++, cpre++, i++) + *cdev = *ccur - *cpre; + + dev->net.ipv4.Forwarding = cur->net.ipv4.Forwarding; + dev->net.ipv4.DefaultTTL = cur->net.ipv4.DefaultTTL; + + /* ------------- */ + + for (cdev = (count_t *)&dev->net.icmpv4, + ccur = (count_t *)&cur->net.icmpv4, + cpre = (count_t *)&pre->net.icmpv4, + i = 0; + i < (sizeof dev->net.icmpv4 / sizeof(count_t)); + cdev++, ccur++, cpre++, i++) + *cdev = *ccur - *cpre; + + /* ------------- */ + + for (cdev = (count_t *)&dev->net.udpv4, + ccur = (count_t *)&cur->net.udpv4, + cpre = (count_t *)&pre->net.udpv4, + i = 0; + i < (sizeof dev->net.udpv4 / sizeof(count_t)); + cdev++, ccur++, cpre++, i++) + *cdev = *ccur - *cpre; + + /* ------------- */ + + for (cdev = (count_t *)&dev->net.ipv6, + ccur = (count_t *)&cur->net.ipv6, + cpre = (count_t *)&pre->net.ipv6, + i = 0; + i < (sizeof dev->net.ipv6 / sizeof(count_t)); + cdev++, ccur++, cpre++, i++) + *cdev = *ccur - *cpre; + + /* ------------- */ + + for (cdev = (count_t *)&dev->net.icmpv6, + ccur = (count_t *)&cur->net.icmpv6, + cpre = (count_t *)&pre->net.icmpv6, + i = 0; + i < (sizeof dev->net.icmpv6 / sizeof(count_t)); + cdev++, ccur++, cpre++, i++) + *cdev = *ccur - *cpre; + + /* ------------- */ + + for (cdev = (count_t *)&dev->net.udpv6, + ccur = (count_t *)&cur->net.udpv6, + cpre = (count_t *)&pre->net.udpv6, + i = 0; + i < (sizeof dev->net.udpv6 / sizeof(count_t)); + cdev++, ccur++, cpre++, i++) + *cdev = *ccur - *cpre; + + /* ------------- */ + + for (cdev = (count_t *)&dev->net.tcp, + ccur = (count_t *)&cur->net.tcp, + cpre = (count_t *)&pre->net.tcp, + i = 0; + i < (sizeof dev->net.tcp / sizeof(count_t)); + cdev++, ccur++, cpre++, i++) + *cdev = *ccur - *cpre; + + dev->net.tcp.RtoAlgorithm = cur->net.tcp.RtoAlgorithm; + dev->net.tcp.RtoMin = cur->net.tcp.RtoMin; + dev->net.tcp.RtoMax = cur->net.tcp.RtoMax; + dev->net.tcp.MaxConn = cur->net.tcp.MaxConn; + dev->net.tcp.CurrEstab = cur->net.tcp.CurrEstab; + + /* + ** calculate deviations for interfaces + ** + ** refresh all interface properties + */ + regainrootprivs(); /* get root privileges */ + + initifprop(); /* refresh interface info */ + + if (! droprootprivs()) /* drop setuid-root privs */ + cleanstop(42); + + for (i=0; cur->intf.intf[i].name[0]; i++) + { + strcpy(ifprop.name, cur->intf.intf[i].name); + + getifprop(&ifprop); + + cur->intf.intf[i].type = ifprop.type; + cur->intf.intf[i].speed = ifprop.speed; + cur->intf.intf[i].speedp = ifprop.speed; + cur->intf.intf[i].duplex = ifprop.fullduplex; + } + + if (pre->intf.intf[0].name[0] == '\0') /* first sample? */ + { + for (i=0; cur->intf.intf[i].name[0]; i++) + { + strcpy(pre->intf.intf[i].name, cur->intf.intf[i].name); + + pre->intf.intf[i].type = cur->intf.intf[i].type; + pre->intf.intf[i].speed = cur->intf.intf[i].speed; + pre->intf.intf[i].speedp = cur->intf.intf[i].speedp; + pre->intf.intf[i].duplex = cur->intf.intf[i].duplex; + } + } + + for (i=0, j=0; cur->intf.intf[i].name[0]; i++, j++) + { + /* + ** be sure that we have the same interface + ** (interfaces could have been added or removed since + ** previous sample) + */ + if (strcmp(cur->intf.intf[i].name, pre->intf.intf[j].name) != 0) + { + // try to resync + for (j=0; pre->intf.intf[j].name[0]; j++) + { + if (strcmp(cur->intf.intf[i].name, + pre->intf.intf[j].name) == 0) + break; + } + + // resync not succeeded? + if (! pre->intf.intf[j].name[0]) + { + memcpy(&dev->intf.intf[i], + &cur->intf.intf[i], + sizeof cur->intf.intf[i]); + + j = 0; + continue; + } + } + + /* + ** calculate interface deviations for this sample + */ + strcpy(dev->intf.intf[i].name, cur->intf.intf[i].name); + + dev->intf.intf[i].rbyte = subcount(cur->intf.intf[i].rbyte, + pre->intf.intf[j].rbyte); + dev->intf.intf[i].rpack = subcount(cur->intf.intf[i].rpack, + pre->intf.intf[j].rpack); + dev->intf.intf[i].rerrs = subcount(cur->intf.intf[i].rerrs, + pre->intf.intf[j].rerrs); + dev->intf.intf[i].rdrop = subcount(cur->intf.intf[i].rdrop, + pre->intf.intf[j].rdrop); + dev->intf.intf[i].rfifo = subcount(cur->intf.intf[i].rfifo, + pre->intf.intf[j].rfifo); + dev->intf.intf[i].rframe= subcount(cur->intf.intf[i].rframe, + pre->intf.intf[j].rframe); + dev->intf.intf[i].rcompr= subcount(cur->intf.intf[i].rcompr, + pre->intf.intf[j].rcompr); + dev->intf.intf[i].rmultic=subcount(cur->intf.intf[i].rmultic, + pre->intf.intf[j].rmultic); + + dev->intf.intf[i].sbyte = subcount(cur->intf.intf[i].sbyte, + pre->intf.intf[j].sbyte); + dev->intf.intf[i].spack = subcount(cur->intf.intf[i].spack, + pre->intf.intf[j].spack); + dev->intf.intf[i].serrs = subcount(cur->intf.intf[i].serrs, + pre->intf.intf[j].serrs); + dev->intf.intf[i].sdrop = subcount(cur->intf.intf[i].sdrop, + pre->intf.intf[j].sdrop); + dev->intf.intf[i].sfifo = subcount(cur->intf.intf[i].sfifo, + pre->intf.intf[j].sfifo); + dev->intf.intf[i].scollis= subcount(cur->intf.intf[i].scollis, + pre->intf.intf[j].scollis); + dev->intf.intf[i].scarrier= subcount(cur->intf.intf[i].scarrier, + pre->intf.intf[j].scarrier); + dev->intf.intf[i].scompr= subcount(cur->intf.intf[i].scompr, + pre->intf.intf[j].scompr); + + dev->intf.intf[i].type = cur->intf.intf[i].type; + dev->intf.intf[i].duplex = cur->intf.intf[i].duplex; + dev->intf.intf[i].speed = cur->intf.intf[i].speed; + dev->intf.intf[i].speedp = pre->intf.intf[j].speed; + + cur->intf.intf[i].speedp = pre->intf.intf[j].speed; + } + + dev->intf.intf[i].name[0] = '\0'; + dev->intf.nrintf = i; + + /* + ** calculate deviations for disks + */ + for (i=j=0; cur->dsk.dsk[i].name[0]; i++) + { + int realj = j; + + /* + ** check if disk has been added or removed since + ** previous interval + */ + if ( strcmp(cur->dsk.dsk[i].name, pre->dsk.dsk[j].name) != 0) + { + for (j++; pre->dsk.dsk[j].name[0]; j++) + { + if ( strcmp(cur->dsk.dsk[i].name, + pre->dsk.dsk[j].name) == 0) + break; + } + + /* + ** either the corresponding entry has been found + ** in the case that a disk has been removed, or + ** an empty entry has been found (all counters + ** on zero) in the case that a disk has been added + ** during the last sample + */ + } + + strcpy(dev->dsk.dsk[i].name, cur->dsk.dsk[i].name); + + dev->dsk.dsk[i].nread = subcount(cur->dsk.dsk[i].nread, + pre->dsk.dsk[j].nread); + dev->dsk.dsk[i].nwrite = subcount(cur->dsk.dsk[i].nwrite, + pre->dsk.dsk[j].nwrite); + dev->dsk.dsk[i].nrsect = subcount(cur->dsk.dsk[i].nrsect, + pre->dsk.dsk[j].nrsect); + dev->dsk.dsk[i].nwsect = subcount(cur->dsk.dsk[i].nwsect, + pre->dsk.dsk[j].nwsect); + dev->dsk.dsk[i].io_ms = subcount(cur->dsk.dsk[i].io_ms, + pre->dsk.dsk[j].io_ms); + dev->dsk.dsk[i].avque = subcount(cur->dsk.dsk[i].avque, + pre->dsk.dsk[j].avque); + + /* + ** determine new j + */ + if (pre->dsk.dsk[j].name) // existing matching entry + j++; + else + j = realj; // empty entry: stick to old j + } + + dev->dsk.dsk[i].name[0] = '\0'; + dev->dsk.ndsk = i; + + /* + ** calculate deviations for multiple devices + */ + for (i=j=0; cur->dsk.mdd[i].name[0]; i++) + { + int realj = j; + + /* + ** check if md has been added or removed since + ** previous interval + */ + if ( strcmp(cur->dsk.mdd[i].name, pre->dsk.mdd[j].name) != 0) + { + for (j++; pre->dsk.mdd[j].name[0]; j++) + { + if ( strcmp(cur->dsk.mdd[i].name, + pre->dsk.mdd[j].name) == 0) + break; + } + + /* + ** either the corresponding entry has been found + ** in the case that a md has been removed, or + ** an empty entry has been found (all counters + ** on zero) in the case that a md has been added + ** during the last sample + */ + } + + strcpy(dev->dsk.mdd[i].name, cur->dsk.mdd[i].name); + + dev->dsk.mdd[i].nread = subcount(cur->dsk.mdd[i].nread, + pre->dsk.mdd[j].nread); + dev->dsk.mdd[i].nwrite = subcount(cur->dsk.mdd[i].nwrite, + pre->dsk.mdd[j].nwrite); + dev->dsk.mdd[i].nrsect = subcount(cur->dsk.mdd[i].nrsect, + pre->dsk.mdd[j].nrsect); + dev->dsk.mdd[i].nwsect = subcount(cur->dsk.mdd[i].nwsect, + pre->dsk.mdd[j].nwsect); + dev->dsk.mdd[i].io_ms = subcount(cur->dsk.mdd[i].io_ms, + pre->dsk.mdd[j].io_ms); + dev->dsk.mdd[i].avque = subcount(cur->dsk.mdd[i].avque, + pre->dsk.mdd[j].avque); + + /* + ** determine new j + */ + if (pre->dsk.mdd[j].name) // existing matching entry + j++; + else + j = realj; // empty entry: stick to old j + } + + dev->dsk.mdd[i].name[0] = '\0'; + dev->dsk.nmdd = i; + + /* + ** calculate deviations for LVM logical volumes + */ + for (i=j=0; cur->dsk.lvm[i].name[0]; i++) + { + int realj = j; + + /* + ** check if logical volume has been added or removed since + ** previous interval + */ + if ( strcmp(cur->dsk.lvm[i].name, pre->dsk.lvm[j].name) != 0) + { + for (j++; pre->dsk.lvm[j].name[0]; j++) + { + if ( strcmp(cur->dsk.lvm[i].name, + pre->dsk.lvm[j].name) == 0) + break; + } + + /* + ** either the corresponding entry has been found + ** in the case that a logical volume has been removed, + ** or an empty entry has been found (all counters + ** on zero) in the case that a logical volume has + ** been added during the last sample + */ + } + + strcpy(dev->dsk.lvm[i].name, cur->dsk.lvm[i].name); + + dev->dsk.lvm[i].nread = subcount(cur->dsk.lvm[i].nread, + pre->dsk.lvm[j].nread); + dev->dsk.lvm[i].nwrite = subcount(cur->dsk.lvm[i].nwrite, + pre->dsk.lvm[j].nwrite); + dev->dsk.lvm[i].nrsect = subcount(cur->dsk.lvm[i].nrsect, + pre->dsk.lvm[j].nrsect); + dev->dsk.lvm[i].nwsect = subcount(cur->dsk.lvm[i].nwsect, + pre->dsk.lvm[j].nwsect); + dev->dsk.lvm[i].io_ms = subcount(cur->dsk.lvm[i].io_ms, + pre->dsk.lvm[j].io_ms); + dev->dsk.lvm[i].avque = subcount(cur->dsk.lvm[i].avque, + pre->dsk.lvm[j].avque); + + /* + ** determine new j + */ + if (pre->dsk.lvm[j].name) // existing matching entry + j++; + else + j = realj; // empty entry: stick to old j + } + + dev->dsk.lvm[i].name[0] = '\0'; + dev->dsk.nlvm = i; + + /* + ** calculate deviations for NFS + */ + dev->nfs.server.netcnt = subcount(cur->nfs.server.netcnt, + pre->nfs.server.netcnt); + dev->nfs.server.netudpcnt = subcount(cur->nfs.server.netudpcnt, + pre->nfs.server.netudpcnt); + dev->nfs.server.nettcpcnt = subcount(cur->nfs.server.nettcpcnt, + pre->nfs.server.nettcpcnt); + dev->nfs.server.nettcpcon = subcount(cur->nfs.server.nettcpcon, + pre->nfs.server.nettcpcon); + + dev->nfs.server.rpccnt = subcount(cur->nfs.server.rpccnt, + pre->nfs.server.rpccnt); + dev->nfs.server.rpcread = subcount(cur->nfs.server.rpcread, + pre->nfs.server.rpcread); + dev->nfs.server.rpcwrite = subcount(cur->nfs.server.rpcwrite, + pre->nfs.server.rpcwrite); + dev->nfs.server.rpcbadfmt = subcount(cur->nfs.server.rpcbadfmt, + pre->nfs.server.rpcbadfmt); + dev->nfs.server.rpcbadaut = subcount(cur->nfs.server.rpcbadaut, + pre->nfs.server.rpcbadaut); + dev->nfs.server.rpcbadcln = subcount(cur->nfs.server.rpcbadcln, + pre->nfs.server.rpcbadcln); + + dev->nfs.server.rchits = subcount(cur->nfs.server.rchits, + pre->nfs.server.rchits); + dev->nfs.server.rcmiss = subcount(cur->nfs.server.rcmiss, + pre->nfs.server.rcmiss); + dev->nfs.server.rcnoca = subcount(cur->nfs.server.rcnoca, + pre->nfs.server.rcnoca); + + dev->nfs.server.nrbytes = subcount(cur->nfs.server.nrbytes, + pre->nfs.server.nrbytes); + dev->nfs.server.nwbytes = subcount(cur->nfs.server.nwbytes, + pre->nfs.server.nwbytes); + + dev->nfs.client.rpccnt = subcount(cur->nfs.client.rpccnt, + pre->nfs.client.rpccnt); + dev->nfs.client.rpcread = subcount(cur->nfs.client.rpcread, + pre->nfs.client.rpcread); + dev->nfs.client.rpcwrite = subcount(cur->nfs.client.rpcwrite, + pre->nfs.client.rpcwrite); + dev->nfs.client.rpcretrans = subcount(cur->nfs.client.rpcretrans, + pre->nfs.client.rpcretrans); + dev->nfs.client.rpcautrefresh = subcount(cur->nfs.client.rpcautrefresh, + pre->nfs.client.rpcautrefresh); + + + for (i=j=0; i < cur->nfs.nfsmounts.nrmounts; i++, j++) + { + /* + ** check if nfsmounts have been added or removed since + ** previous interval + */ + if ( strcmp(cur->nfs.nfsmounts.nfsmnt[i].mountdev, + pre->nfs.nfsmounts.nfsmnt[j].mountdev) != 0) + { + for (j=0; j < pre->nfs.nfsmounts.nrmounts; j++) + { + if ( strcmp(cur->nfs.nfsmounts.nfsmnt[i].mountdev, + pre->nfs.nfsmounts.nfsmnt[j].mountdev) + == 0) + break; + } + + /* + ** either the corresponding entry has been found + ** in the case that a container has been removed, + ** or an empty entry has been found (all counters + ** on zero) in the case that a container has + ** been added during the last sample + */ + } + + strcpy(dev->nfs.nfsmounts.nfsmnt[i].mountdev, + cur->nfs.nfsmounts.nfsmnt[i].mountdev); + + dev->nfs.nfsmounts.nfsmnt[i].age = + cur->nfs.nfsmounts.nfsmnt[i].age; + + if (dev->nfs.nfsmounts.nfsmnt[i].age <= interval) + memset(&(pre->nfs.nfsmounts.nfsmnt[j]), 0, + sizeof(struct pernfsmount)); + + dev->nfs.nfsmounts.nfsmnt[i].bytesread = + subcount(cur->nfs.nfsmounts.nfsmnt[i].bytesread, + pre->nfs.nfsmounts.nfsmnt[j].bytesread); + + dev->nfs.nfsmounts.nfsmnt[i].byteswrite = + subcount(cur->nfs.nfsmounts.nfsmnt[i].byteswrite, + pre->nfs.nfsmounts.nfsmnt[j].byteswrite); + + dev->nfs.nfsmounts.nfsmnt[i].bytesdread = + subcount(cur->nfs.nfsmounts.nfsmnt[i].bytesdread, + pre->nfs.nfsmounts.nfsmnt[j].bytesdread); + + dev->nfs.nfsmounts.nfsmnt[i].bytesdwrite = + subcount(cur->nfs.nfsmounts.nfsmnt[i].bytesdwrite, + pre->nfs.nfsmounts.nfsmnt[j].bytesdwrite); + + dev->nfs.nfsmounts.nfsmnt[i].bytestotread = + subcount(cur->nfs.nfsmounts.nfsmnt[i].bytestotread, + pre->nfs.nfsmounts.nfsmnt[j].bytestotread); + + dev->nfs.nfsmounts.nfsmnt[i].bytestotwrite = + subcount(cur->nfs.nfsmounts.nfsmnt[i].bytestotwrite, + pre->nfs.nfsmounts.nfsmnt[j].bytestotwrite); + + dev->nfs.nfsmounts.nfsmnt[i].pagesmread = + subcount(cur->nfs.nfsmounts.nfsmnt[i].pagesmread, + pre->nfs.nfsmounts.nfsmnt[j].pagesmread); + + dev->nfs.nfsmounts.nfsmnt[i].pagesmwrite = + subcount(cur->nfs.nfsmounts.nfsmnt[i].pagesmwrite, + pre->nfs.nfsmounts.nfsmnt[j].pagesmwrite); + } + + dev->nfs.nfsmounts.nrmounts = cur->nfs.nfsmounts.nrmounts; + + /* + ** calculate deviations for containers + */ + for (i=j=0; i < cur->cfs.nrcontainer; i++, j++) + { + /* + ** check if containers have been added or removed since + ** previous interval + */ + if (cur->cfs.cont[i].ctid != pre->cfs.cont[j].ctid) + { + for (j=0; j < pre->cfs.nrcontainer; j++) + { + if (cur->cfs.cont[i].ctid == + pre->cfs.cont[j].ctid) + break; + } + + /* + ** either the corresponding entry has been found + ** in the case that a container has been removed, + ** or an empty entry has been found (all counters + ** on zero) in the case that a container has + ** been added during the last sample + */ + } + + dev->cfs.cont[i].ctid = cur->cfs.cont[i].ctid; + dev->cfs.cont[i].numproc = cur->cfs.cont[i].numproc; + + dev->cfs.cont[i].system = subcount(cur->cfs.cont[i].system, + pre->cfs.cont[i].system); + dev->cfs.cont[i].user = subcount(cur->cfs.cont[i].user, + pre->cfs.cont[i].user); + dev->cfs.cont[i].nice = subcount(cur->cfs.cont[i].nice, + pre->cfs.cont[i].nice); + dev->cfs.cont[i].uptime = subcount(cur->cfs.cont[i].uptime, + pre->cfs.cont[i].uptime); + + dev->cfs.cont[i].physpages = cur->cfs.cont[i].physpages; + } + + dev->cfs.nrcontainer = cur->cfs.nrcontainer; + + /* + ** application-specific counters + */ +#if HTTPSTATS + if (cur->www.uptime >= pre->www.uptime) + { + dev->www.accesses = subcount(cur->www.accesses, + pre->www.accesses); + dev->www.totkbytes = subcount(cur->www.totkbytes, + pre->www.totkbytes); + } + else + { + dev->www.accesses = cur->www.accesses; + dev->www.totkbytes = cur->www.totkbytes; + } + + dev->www.bworkers = cur->www.bworkers; + dev->www.iworkers = cur->www.iworkers; +#endif +} + +/* +** add the values of a new sample to a structure holding the totals +** for the indicated category (c=cpu, m=memory, d=disk, n=network). +*/ +void +totalsyst(char category, struct sstat *new, struct sstat *tot) +{ + register int i; + count_t *ctot, *cnew; + + switch (category) + { + case 'c': /* accumulate cpu-related counters */ + tot->cpu.nrcpu = new->cpu.nrcpu; + tot->cpu.devint += new->cpu.devint; + tot->cpu.csw += new->cpu.csw; + tot->cpu.nprocs += new->cpu.nprocs; + + tot->cpu.all.stime += new->cpu.all.stime; + tot->cpu.all.utime += new->cpu.all.utime; + tot->cpu.all.ntime += new->cpu.all.ntime; + tot->cpu.all.itime += new->cpu.all.itime; + tot->cpu.all.wtime += new->cpu.all.wtime; + tot->cpu.all.Itime += new->cpu.all.Itime; + tot->cpu.all.Stime += new->cpu.all.Stime; + tot->cpu.all.steal += new->cpu.all.steal; + tot->cpu.all.guest += new->cpu.all.guest; + + if (new->cpu.nrcpu == 1) + { + tot->cpu.cpu[0] = tot->cpu.all; + } + else + { + for (i=0; i < new->cpu.nrcpu; i++) + { + tot->cpu.cpu[i].cpunr = new->cpu.cpu[i].cpunr; + tot->cpu.cpu[i].stime += new->cpu.cpu[i].stime; + tot->cpu.cpu[i].utime += new->cpu.cpu[i].utime; + tot->cpu.cpu[i].ntime += new->cpu.cpu[i].ntime; + tot->cpu.cpu[i].itime += new->cpu.cpu[i].itime; + tot->cpu.cpu[i].wtime += new->cpu.cpu[i].wtime; + tot->cpu.cpu[i].Itime += new->cpu.cpu[i].Itime; + tot->cpu.cpu[i].Stime += new->cpu.cpu[i].Stime; + tot->cpu.cpu[i].steal += new->cpu.cpu[i].steal; + tot->cpu.cpu[i].guest += new->cpu.cpu[i].guest; + } + } + + tot->cpu.lavg1 = new->cpu.lavg1; + tot->cpu.lavg5 = new->cpu.lavg5; + tot->cpu.lavg15 = new->cpu.lavg15; + break; + + case 'm': /* accumulate memory-related counters */ + tot->mem.physmem = new->mem.physmem; + tot->mem.freemem = new->mem.freemem; + tot->mem.buffermem = new->mem.buffermem; + tot->mem.slabmem = new->mem.slabmem; + tot->mem.slabreclaim = new->mem.slabreclaim; + tot->mem.committed = new->mem.committed; + tot->mem.commitlim = new->mem.commitlim; + tot->mem.cachemem = new->mem.cachemem; + tot->mem.cachedrt = new->mem.cachedrt; + tot->mem.totswap = new->mem.totswap; + tot->mem.freeswap = new->mem.freeswap; + + tot->mem.shmem = new->mem.shmem; + tot->mem.shmrss = new->mem.shmrss; + tot->mem.shmswp = new->mem.shmswp; + + tot->mem.swouts += new->mem.swouts; + tot->mem.swins += new->mem.swins; + tot->mem.pgscans += new->mem.pgscans; + tot->mem.allocstall += new->mem.allocstall; + break; + + case 'n': /* accumulate network-related counters */ + tot->nfs.server.rpccnt += new->nfs.server.rpccnt; + tot->nfs.server.rpcread += new->nfs.server.rpcread; + tot->nfs.server.rpcwrite += new->nfs.server.rpcwrite; + tot->nfs.server.rpcbadfmt += new->nfs.server.rpcbadfmt; + tot->nfs.server.rpcbadaut += new->nfs.server.rpcbadaut; + tot->nfs.server.rpcbadcln += new->nfs.server.rpcbadcln; + + tot->nfs.server.netcnt += new->nfs.server.netcnt; + tot->nfs.server.nettcpcnt += new->nfs.server.nettcpcnt; + tot->nfs.server.netudpcnt += new->nfs.server.netudpcnt; + tot->nfs.server.nettcpcon += new->nfs.server.nettcpcon; + + tot->nfs.server.rchits += new->nfs.server.rchits; + tot->nfs.server.rcmiss += new->nfs.server.rcmiss; + tot->nfs.server.rcnoca += new->nfs.server.rcnoca; + + tot->nfs.server.nrbytes += new->nfs.server.nrbytes; + tot->nfs.server.nwbytes += new->nfs.server.nwbytes; + + tot->nfs.client.rpccnt += new->nfs.client.rpccnt; + tot->nfs.client.rpcread += new->nfs.client.rpcread; + tot->nfs.client.rpcwrite += new->nfs.client.rpcwrite; + tot->nfs.client.rpcretrans += new->nfs.client.rpcretrans; + tot->nfs.client.rpcautrefresh += new->nfs.client.rpcautrefresh; + + /* + ** other structures with network counters are considered + ** as tables of frequency-counters that will be accumulated; + ** values that do not represent a frequency are corrected + ** afterwards + */ + for (ctot = (count_t *)&tot->net.ipv4, + cnew = (count_t *)&new->net.ipv4, i = 0; + i < (sizeof tot->net.ipv4 / sizeof(count_t)); + ctot++, cnew++, i++) + *ctot += *cnew; + + tot->net.ipv4.Forwarding = new->net.ipv4.Forwarding; + tot->net.ipv4.DefaultTTL = new->net.ipv4.DefaultTTL; + + /* ------------- */ + + for (ctot = (count_t *)&tot->net.icmpv4, + cnew = (count_t *)&new->net.icmpv4, i = 0; + i < (sizeof tot->net.icmpv4 / sizeof(count_t)); + ctot++, cnew++, i++) + *ctot += *cnew; + + /* ------------- */ + + for (ctot = (count_t *)&tot->net.udpv4, + cnew = (count_t *)&new->net.udpv4, i = 0; + i < (sizeof tot->net.udpv4 / sizeof(count_t)); + ctot++, cnew++, i++) + *ctot += *cnew; + + /* ------------- */ + + for (ctot = (count_t *)&tot->net.ipv6, + cnew = (count_t *)&new->net.ipv6, i = 0; + i < (sizeof tot->net.ipv6 / sizeof(count_t)); + ctot++, cnew++, i++) + *ctot += *cnew; + + /* ------------- */ + + for (ctot = (count_t *)&tot->net.icmpv6, + cnew = (count_t *)&new->net.icmpv6, i = 0; + i < (sizeof tot->net.icmpv6 / sizeof(count_t)); + ctot++, cnew++, i++) + *ctot += *cnew; + + /* ------------- */ + + for (ctot = (count_t *)&tot->net.udpv6, + cnew = (count_t *)&new->net.udpv6, i = 0; + i < (sizeof tot->net.udpv6 / sizeof(count_t)); + ctot++, cnew++, i++) + *ctot += *cnew; + + /* ------------- */ + + for (ctot = (count_t *)&tot->net.tcp, + cnew = (count_t *)&new->net.tcp, i = 0; + i < (sizeof tot->net.tcp / sizeof(count_t)); + ctot++, cnew++, i++) + *ctot += *cnew; + + tot->net.tcp.RtoAlgorithm = new->net.tcp.RtoAlgorithm; + tot->net.tcp.RtoMin = new->net.tcp.RtoMin; + tot->net.tcp.RtoMax = new->net.tcp.RtoMax; + tot->net.tcp.MaxConn = new->net.tcp.MaxConn; + tot->net.tcp.CurrEstab = new->net.tcp.CurrEstab; + + for (i=0; new->intf.intf[i].name[0]; i++) + { + /* + ** check if an interface has been added or removed; + ** in that case, zero all counters + */ + if (strcmp(new->intf.intf[i].name, + tot->intf.intf[i].name) != 0) + { + tot->intf.intf[i].rbyte = 0; + tot->intf.intf[i].rpack = 0; + tot->intf.intf[i].rerrs = 0; + tot->intf.intf[i].rdrop = 0; + tot->intf.intf[i].rfifo = 0; + tot->intf.intf[i].rframe = 0; + tot->intf.intf[i].rcompr = 0; + tot->intf.intf[i].rmultic = 0; + + tot->intf.intf[i].sbyte = 0; + tot->intf.intf[i].spack = 0; + tot->intf.intf[i].serrs = 0; + tot->intf.intf[i].sdrop = 0; + tot->intf.intf[i].sfifo = 0; + tot->intf.intf[i].scollis = 0; + tot->intf.intf[i].scarrier = 0; + tot->intf.intf[i].scompr = 0; + } + + /* + ** accumulate counters for this sample + */ + strcpy(tot->intf.intf[i].name, new->intf.intf[i].name); + + tot->intf.intf[i].rbyte += new->intf.intf[i].rbyte; + tot->intf.intf[i].rpack += new->intf.intf[i].rpack; + tot->intf.intf[i].rerrs += new->intf.intf[i].rerrs; + tot->intf.intf[i].rdrop += new->intf.intf[i].rdrop; + tot->intf.intf[i].rfifo += new->intf.intf[i].rfifo; + tot->intf.intf[i].rframe += new->intf.intf[i].rframe; + tot->intf.intf[i].rcompr += new->intf.intf[i].rcompr; + tot->intf.intf[i].rmultic += new->intf.intf[i].rmultic; + + tot->intf.intf[i].sbyte += new->intf.intf[i].sbyte; + tot->intf.intf[i].spack += new->intf.intf[i].spack; + tot->intf.intf[i].serrs += new->intf.intf[i].serrs; + tot->intf.intf[i].sdrop += new->intf.intf[i].sdrop; + tot->intf.intf[i].sfifo += new->intf.intf[i].sfifo; + tot->intf.intf[i].scollis += new->intf.intf[i].scollis; + tot->intf.intf[i].scarrier+= new->intf.intf[i].scarrier; + tot->intf.intf[i].scompr += new->intf.intf[i].scompr; + + tot->intf.intf[i].type = new->intf.intf[i].type; + tot->intf.intf[i].speed = new->intf.intf[i].speed; + tot->intf.intf[i].duplex = new->intf.intf[i].duplex; + } + + tot->intf.intf[i].name[0] = '\0'; + tot->intf.nrintf = i; + +#if HTTPSTATS + tot->www.accesses += new->www.accesses; + tot->www.totkbytes += new->www.totkbytes; + tot->www.bworkers = new->www.bworkers; + tot->www.iworkers = new->www.iworkers; +#endif + break; + + case 'd': /* accumulate disk-related counters */ + for (i=0; new->dsk.dsk[i].name[0]; i++) + { + strcpy(tot->dsk.dsk[i].name, new->dsk.dsk[i].name); + + tot->dsk.dsk[i].nread += new->dsk.dsk[i].nread; + tot->dsk.dsk[i].nwrite += new->dsk.dsk[i].nwrite; + tot->dsk.dsk[i].nrsect += new->dsk.dsk[i].nrsect; + tot->dsk.dsk[i].nwsect += new->dsk.dsk[i].nwsect; + tot->dsk.dsk[i].io_ms += new->dsk.dsk[i].io_ms; + tot->dsk.dsk[i].avque += new->dsk.dsk[i].avque; + } + + tot->dsk.dsk[i].name[0] = '\0'; + tot->dsk.ndsk = i; + + for (i=0; new->dsk.lvm[i].name[0]; i++) + { + strcpy(tot->dsk.lvm[i].name, new->dsk.lvm[i].name); + + tot->dsk.lvm[i].nread += new->dsk.lvm[i].nread; + tot->dsk.lvm[i].nwrite += new->dsk.lvm[i].nwrite; + tot->dsk.lvm[i].nrsect += new->dsk.lvm[i].nrsect; + tot->dsk.lvm[i].nwsect += new->dsk.lvm[i].nwsect; + tot->dsk.lvm[i].io_ms += new->dsk.lvm[i].io_ms; + tot->dsk.lvm[i].avque += new->dsk.lvm[i].avque; + } + + tot->dsk.lvm[i].name[0] = '\0'; + tot->dsk.nlvm = i; + + for (i=0; new->dsk.mdd[i].name[0]; i++) + { + strcpy(tot->dsk.mdd[i].name, new->dsk.mdd[i].name); + + tot->dsk.mdd[i].nread += new->dsk.mdd[i].nread; + tot->dsk.mdd[i].nwrite += new->dsk.mdd[i].nwrite; + tot->dsk.mdd[i].nrsect += new->dsk.mdd[i].nrsect; + tot->dsk.mdd[i].nwsect += new->dsk.mdd[i].nwsect; + tot->dsk.mdd[i].io_ms += new->dsk.mdd[i].io_ms; + tot->dsk.mdd[i].avque += new->dsk.mdd[i].avque; + } + + tot->dsk.mdd[i].name[0] = '\0'; + tot->dsk.nmdd = i; + break; + } +} + + +/* +** Generic function to subtract two counters taking into +** account the possibility of overflow of a 32-bit kernel-counter. +*/ +count_t +subcount(count_t newval, count_t oldval) +{ + if (newval >= oldval) + return newval - oldval; + else + return MAX32BITVAL + newval - oldval; +} diff --git a/sources/ifprop.c b/sources/ifprop.c new file mode 100644 index 0000000..62e1c26 --- /dev/null +++ b/sources/ifprop.c @@ -0,0 +1,207 @@ +/* +** ATOP - System & Process Monitor +** +** The program 'atop' offers the possibility to view the activity of +** the system on system-level as well as process-level. +** +** ========================================================================== +** Author: Gerlof Langeveld +** E-mail: gerlof.langeveld@atoptool.nl +** Date: January 2007 +** -------------------------------------------------------------------------- +** Copyright (C) 2007-2010 Gerlof Langeveld +** +** This program is free software; you can redistribute it and/or modify it +** under the terms of the GNU General Public License as published by the +** Free Software Foundation; either version 2, or (at your option) any +** later version. +** +** This program is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +** See the GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +** -------------------------------------------------------------------------- +** +** $Id: ifprop.c,v 1.5 2010/04/23 12:19:35 gerlof Exp $ +** $Log: ifprop.c,v $ +** Revision 1.5 2010/04/23 12:19:35 gerlof +** Modified mail-address in header. +** +** Revision 1.4 2007/02/13 10:34:06 gerlof +** Removal of external declarations. +** +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef __u64 u64; +typedef __u32 u32; +typedef __u16 u16; +typedef __u8 u8; +#include +#include + +#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); +} diff --git a/sources/ifprop.h b/sources/ifprop.h new file mode 100644 index 0000000..718f755 --- /dev/null +++ b/sources/ifprop.h @@ -0,0 +1,10 @@ +struct ifprop { + char type; /* type: 'e' (ethernet) */ + /* 'w' (wireless) */ + char name[31]; /* name of interface */ + long int speed; /* in megabits per second */ + char fullduplex; /* boolean */ +}; + +int getifprop(struct ifprop *); +void initifprop(void); diff --git a/sources/man/atop.1 b/sources/man/atop.1 new file mode 100644 index 0000000..d75c84a --- /dev/null +++ b/sources/man/atop.1 @@ -0,0 +1,2091 @@ +.TH ATOP 1 "March 2017" "Linux" +.SH NAME +.B atop +- Advanced System & Process Monitor +.SH SYNOPSIS +Interactive Usage: +.P +.B atop +[\-g|\-m|\-d|\-n|\-u|\-p|\-s|\-c|\-v|\-o|\-y] [\-C|\-M|\-D|\-N|\-A] [\-afFG1xR] [\-L linelen] [\-Plabel[,label]...] +[ +.I interval +[ +.I samples +]] +.P +Writing and reading raw logfiles: +.P +.B atop +\-w +.I rawfile +[\-a] [\-S] +[ +.I interval +[ +.I samples +]] +.br +.B atop +\-r [ +.I rawfile +] [\-b +.I hh:mm +] [\-e +.I hh:mm +] [\-g|\-m|\-d|\-n|\-u|\-p|\-s|\-c|\-v|\-o|\-y] [\-C|\-M|\-D|\-N|\-A] [\-fFG1xR] [\-L linelen] [\-Plabel[,label]...] +.SH DESCRIPTION +The program +.I atop +is an interactive monitor to view the load on a Linux system. +It shows the occupation of the most critical hardware resources +(from a performance point of view) on system level, i.e. cpu, memory, disk +and network. +.br +It also shows which processes are responsible for the indicated +load with respect to cpu and memory load on process level. +Disk load is shown per process if "storage accounting" is active in the kernel. +Network load is shown per process if the kernel module `netatop' +has been installed. +.PP +Every +.I interval +(default: 10 seconds) information is shown about the resource occupation +on system level (cpu, memory, disks and network layers), followed +by a list of processes which have been active during the last interval +(note that all processes that were unchanged during the last interval +are not shown, unless the key 'a' has been pressed or unless sorting on +memory occupation is done). +If the list of active processes does not entirely fit on +the screen, only the top of the list is shown (sorted in order of activity). +.br +The intervals are repeated till the number of +.I samples +(specified as command argument) is reached, or till the key 'q' is pressed +in interactive mode. +.PP +When +.I atop +is started, it checks whether the standard output channel is connected to a +screen, or to a file/pipe. In the first case it produces screen control +codes (via the ncurses library) and behaves interactively; in the second case +it produces flat ASCII-output. +.PP +In interactive mode, the output of +.I atop +scales dynamically to the current dimensions of the screen/window. +.br +If the window is resized horizontally, columns will be added or removed +automatically. For this purpose, every column has a particular weight. The +columns with the highest weights that fit within the current width will +be shown. +.br +If the window is resized vertically, lines of the process/thread list +will be added or removed automatically. +.PP +Furthermore in interactive mode the output of +.I atop +can be controlled by pressing particular keys. +However it is also possible to specify such key as +.B flag +on the command line. In that case +.I atop +switches to the indicated mode on beforehand; this mode can +be modified again interactively. Specifying such key as flag is especially +useful when running +.I atop +with output to a pipe or file (non-interactively). +These flags are the same as the keys that can be pressed in interactive +mode (see section INTERACTIVE COMMANDS). +.br +Additional flags are available to support storage of atop-data in raw +format (see section RAW DATA STORAGE). +.SH PROCESS ACCOUNTING +With every interval, +.I atop +reads the kernel administration to obtain information about all +running processes. +However, it is likely that during the interval also processes have terminated. +These processes might have consumed system resources during +this interval as well before they terminated. +Therefor, +.I atop +tries to read the process accounting records that contain the accounting +information of terminated processes and report these processes too. +Only when the process accounting mechanism in the kernel is activated, +the kernel writes such process accounting record to a file +for every process that terminates. +.PP +There are various ways for +.I atop +to get access to the process accounting records (tried in this order): +.PP +.TP 4 +1. +When the environment variable ATOPACCT is set, +it specifies the name of the process accounting file. +In that case, process accounting for this file +should have been activated on beforehand. +Before opening this file for reading, +.I atop +drops its root privileges (if any). +.br +When this environment variable is present but its +contents is empty, process accounting will not be used at all. +.PP +.TP 4 +2. +.B This is the preferred way of handling process accounting records! +.br +When the +.I atopacctd +daemon is active, it has activated the process accounting mechanism in +the kernel and transfers to original accounting records to shadow files. +In that case, +.I atop +drops its root privileges and opens the current shadow file for reading. +.br +This way is preferred, because the +.I atopacctd +daemon maintains full control of the sizes of the original process +accounting file (written by the kernel) and the shadow files (read by the +.I atop +processes). For further information, refer to the +.B atopacctd +man page. +.PP +.TP 4 +3. +When the +.I atopacctd +daemon is not active, +.I atop +verifies if the process accounting mechanism has been switched on +via the separate +.B psacct +package. In that case, the file +.B /var/account/pacct +is in use as process accounting file and +.I atop +opens this file for reading. +.PP +.TP 4 +4. +As a last possibility, +.I atop +itself tries to activate the process accounting mechanism (requires root +privileges) using the file +.B /var/cache/atop.d/atop.acct +(to be written by the kernel, to be read by +.I atop +itself). Process accounting remains active as long as +at least one +.I atop +process is alive. +Whenever the last +.I atop +process stops (either by pressing `q' or by `kill \-15'), it deactivates the +process accounting mechanism again. Therefor you should never terminate +.I atop +by `kill \-9', because then it has no chance to stop process accounting. +As a result, the accounting file may consume a lot of +disk space after a while. +.br +To avoid that the process accounting file consumes too much disk space, +.I atop +verifies at the end of every sample if the size of the process accounting +file exceeds 200 MiB and if this +.I atop +process is the only one that is currently using the file. +In that case the file is truncated to a size of zero. + +Notice that root-privileges are required to switch on/off process accounting +in the kernel. You can start +.I atop +as a root user or specify setuid-root privileges to the executable file. +In the latter case, +.I atop +switches on process accounting and drops the root-privileges again. +.br +If +.I atop +does not run with root-privileges, it does not show information +about finished processes. +It indicates this situation with the message +message `no procacct` in the top-right corner (instead of the counter that +shows the number of exited processes). +.PP +When during one interval a lot of processes have finished, +.I atop +might grow tremendously in memory when reading all process accounting +records at the end of the interval. To avoid such excessive growth, +.I atop +will never read more than 50 MiB with process information from the +process accounting file per interval (approx. 70000 finished processes). +In interactive mode a warning is given whenever processes have been skipped +for this reason. +.PP +.SH COLORS +For the resource consumption on system level, +.I atop +uses colors to indicate that a critical occupation percentage has +been (almost) reached. +A critical occupation percentage means that is likely that this load +causes a noticeable negative performance influence for applications using +this resource. The critical percentage depends on the type of resource: +e.g. the performance influence of a disk with a busy percentage of 80% +might be more noticeable for applications/user than a CPU with a busy +percentage of 90%. +.br +Currently +.I atop +uses the following default values to calculate a weighted percentage +per resource: +.PP +.TP 5 +.B \ Processor +A busy percentage of 90% or higher is considered `critical'. +.TP 5 +.B \ Disk +A busy percentage of 70% or higher is considered `critical'. +.TP 5 +.B \ Network +A busy percentage of 90% or higher for the load of an interface is +considered `critical'. +.TP 5 +.B \ Memory +An occupation percentage of 90% is considered `critical'. +Notice that this occupation percentage is the accumulated memory +consumption of the kernel (including slab) and all processes; the +memory for the page cache (`cache' and `buff' in the MEM-line) and the +reclaimable part of the slab (`slrec`) is not implied! +.br +If the number of pages swapped out (`swout' in the PAG-line) is larger +than 10 per second, the memory resource is considered `critical'. +A value of at least 1 per second is considered `almost critical'. +.br +If the committed virtual memory exceeds the limit (`vmcom' and `vmlim' +in the SWP-line), the SWP-line is colored due to overcommitting the system. +.TP 5 +.B \ Swap +An occupation percentage of 80% is considered `critical' +because swap space might be completely exhausted in the near future; +it is not critical from a performance point-of-view. +.PP +These default values can be modified in the configuration file +(see separate man-page of atoprc). +.PP +When a resource exceeds its critical occupation percentage, the concerning +values in the screen line are colored red by default. +.br +When a resource exceeded (default) 80% of its critical percentage +(so it is almost critical), the concerning values in the screen line +are colored cyan by default. This `almost critical percentage' (one value +for all resources) can be modified in the configuration file +(see separate man-page of atoprc). +.br +The default colors red and cyan can be modified in the configuration file +as well (see separate man-page of atoprc). +.PP +With the key 'x' (or flag \-x), the use of colors can be suppressed. +.SH NETATOP MODULE +Per-process and per-thread network activity can be measured by the +.I netatop +kernel module. You can download this kernel module from the website +(mentioned at the end of this manual page) and install it on your +system if the kernel version is 2.6.24 or newer. +.br +When +.I atop +gathers counters for a new interval, it verifies if the +.I netatop +module is currently active. If so, +.I atop +obtains the relevant network counters from this module and shows +the number of sent and received packets per process/thread in the generic +screen. Besides, detailed counters can be requested by +pressing the `n' key. +.br +When the +.I netatopd +daemon is running as well, +.I atop +also reads the network counters of exited processes that are logged +by this daemon (comparable with process accounting). +.PP +More information about the optional +.I netatop +kernel module and the +.I netatopd +daemon can be found in the concerning man-pages and on the website +mentioned at the end of this manual page. +.SH INTERACTIVE COMMANDS +When running +.I atop +interactively (no output redirection), keys can be pressed to control the +output. In general, lower case keys can be used to show other information for +the active processes and upper case keys can be used to influence the +sort order of the active process/thread list. +.PP +.TP 5 +.B g +Show generic output (default). + +Per process the following fields are shown in case of a window-width +of 80 positions: +process-id, cpu consumption during +the last interval in system and user mode, the virtual and resident +memory growth of the process. + +The subsequent columns depend on the used kernel: +.br +When the kernel supports "storage accounting" (>= 2.6.20), the data +transfer for read/write on disk, the status and exit code are +shown for each process. +When the kernel does not support +"storage accounting", the username, number of threads in the +thread group, the status and exit code are shown. +.br +When the kernel module 'netatop' is loaded, the data transfer for send/receive +of network packets is shown for each process. +.br +The last columns contain the state, the occupation percentage for the +chosen resource (default: cpu) and the process name. + +When more than 80 positions are available, other information is added. +.PP +.TP 5 +.B m +Show memory related output. + +Per process the following fields are shown in case of a window-width +of 80 positions: +process-id, minor and major +memory faults, size of virtual shared text, total virtual +process size, total resident process size, virtual and resident growth during +last interval, memory occupation percentage and process name. + +When more than 80 positions are available, other information is added. + +For memory consumption, always all processes are shown (also the processes +that were not active during the interval). +.PP +.TP 5 +.B d +Show disk-related output. + +When "storage accounting" is active in the kernel, the following +fields are shown: +process-id, amount of data read from disk, amount of data written to disk, +amount of data that was written but has been withdrawn again (WCANCL), +disk occupation percentage and process name. +.PP +.TP 5 +.B n +Show network related output. + +Per process the following fields are shown in case of a window-width +of 80 positions: +process-id, thread-id, +total bandwidth for received packets, +total bandwidth for sent packets, +number of received TCP packets with the average size per packet (in bytes), +number of sent TCP packets with the average size per packet (in bytes), +number of received UDP packets with the average size per packet (in bytes), +number of sent UDP packets with the average size per packet (in bytes), +the network occupation percentage and process name. +.br +This information can only be shown when kernel module `netatop' is installed. + +When more than 80 positions are available, other information is added. +.PP +.TP 5 +.B s +Show scheduling characteristics. + +Per process the following fields are shown in case of a window-width +of 80 positions: +process-id, +number of threads in state 'running' (R), +number of threads in state 'interruptible sleeping' (S), +number of threads in state 'uninterruptible sleeping' (D), +scheduling policy (normal timesharing, realtime round-robin, realtime fifo), +nice value, priority, realtime priority, current processor, +status, exit code, state, the occupation percentage for the chosen +resource and the process name. + +When more than 80 positions are available, other information is added. +.PP +.TP 5 +.B v +Show various process characteristics. + +Per process the following fields are shown in case of a window-width +of 80 positions: +process-id, user name and group, +start date and time, status (e.g. exit code if the process has finished), +state, the occupation percentage for the chosen resource and the process name. + +When more than 80 positions are available, other information is added. +.PP +.TP 5 +.B c +Show the command line of the process. + +Per process the following fields are shown: process-id, +the occupation percentage for the chosen resource and the +command line including arguments. +.PP +.TP 5 +.B o +Show the user-defined line of the process. + +In the configuration file the keyword +.I ownprocline +can be specified with the description of a user-defined output-line. +.br +Refer to the man-page of +.B atoprc +for a detailed description. +.PP +.TP 5 +.B y +Show the individual threads within a process (toggle). + +Single-threaded processes are still shown as one line. +.br +For multi-threaded processes, one line represents the process +while additional lines show the activity +per individual thread (in a different color). Depending on +the option 'a' (all or active toggle), all threads are shown +or only the threads that were active during the last interval. +.br +Whether this key is active or not can be seen in the header line. +.PP +.TP 5 +.B u +Show the process activity accumulated per user. + +Per user the following fields are shown: number of processes active +or terminated during last interval (or in total if combined with command `a'), +accumulated cpu consumption during last interval in system and user mode, +the current virtual and resident memory space consumed by active processes +(or all processes of the user if combined with command `a'). +.br +When "storage accounting" is active in the kernel, +the accumulated read and write throughput on disk is shown. +When the kernel module `netatop' has been installed, +the number of received and sent network packets are shown. +.br +The last columns contain the accumulated occupation percentage for the +chosen resource (default: cpu) and the user name. +.PP +.TP 5 +.B p +Show the process activity accumulated per program (i.e. process name). + +Per program the following fields are shown: number of processes active +or terminated during last interval (or in total if combined with command `a'), +accumulated cpu consumption during last interval in system and user mode, +the current virtual and resident memory space consumed by active processes +(or all processes of the user if combined with command `a'). +.br +When "storage accounting" is active in the kernel, +the accumulated read and write throughput on disk is shown. +When the kernel module `netatop' has been installed, +the number of received and sent network packets are shown. +.br +The last columns contain the accumulated occupation percentage for the +chosen resource (default: cpu) and the program name. +.PP +.TP 5 +.B j +Show the process activity accumulated per Docker container. + +Per container the following fields are shown: number of processes active +or terminated during last interval (or in total if combined with command `a'), +accumulated cpu consumption during last interval in system and user mode, +the current virtual and resident memory space consumed by active processes +(or all processes of the user if combined with command `a'). +.br +When "storage accounting" is active in the kernel, +the accumulated read and write throughput on disk is shown. +When the kernel module `netatop' has been installed, +the number of received and sent network packets are shown. +.br +The last columns contain the accumulated occupation percentage for the +chosen resource (default: cpu) and the Docker container id (CID). +.PP +.TP 5 +.B C +Sort the current list in the order of cpu consumption (default). +The one-but-last column changes to ``CPU''. +.PP +.TP 5 +.B M +Sort the current list in the order of resident memory consumption. +The one-but-last column changes to ``MEM''. In case of sorting on memory, +the full process list will be shown (not only the active processes). +.PP +.TP 5 +.B D +Sort the current list in the order of disk accesses issued. +The one-but-last column changes to ``DSK''. +.PP +.TP 5 +.B N +Sort the current list in the order of network bandwidth (received +and transmitted). +The one-but-last column changes to ``NET''. +.PP +.TP 5 +.B A +Sort the current list automatically in the order of the most busy +system resource during this interval. +The one-but-last column shows either ``ACPU'', ``AMEM'', ``ADSK'' or ``ANET'' +(the preceding 'A' indicates automatic sorting-order). +The most busy resource is determined by comparing the weighted +busy-percentages of the system resources, as described earlier in +the section COLORS. +.br +This option remains valid until +another sorting-order is explicitly selected again. +.br +A sorting-order for disk is only possible when "storage accounting" is active. +A sorting-order for network is only possible when the kernel module `netatop' +is loaded. +.PP +Miscellaneous interactive commands: +.PP +.TP 5 +.B ? +Request for help information (also the key 'h' can be pressed). +.PP +.TP 5 +.B V +Request for version information (version number and date). +.PP +.TP 5 +.B R +Gather and calculate the proportional set size of processes (toggle). +Gathering of all values that are needed to calculate the PSIZE of a process +is a relatively time-consuming task, so this key should only be active when +analyzing the resident memory consumption of processes. +.PP +.TP 5 +.B x +Suppress colors to highlight critical resources (toggle). +.br +Whether this key is active or not can be seen in the header line. +.PP +.TP 5 +.B z +The pause key can be used to freeze the current situation in order to +investigate the output on the screen. While +.I atop +is paused, the keys described above can be pressed to show other +information about the current list of processes. +Whenever the pause key is pressed again, +atop will continue with a next sample. +.PP +.TP 5 +.B i +Modify the interval timer (default: 10 seconds). If an interval timer of 0 is +entered, the interval timer is switched off. In that case a new sample can +only be triggered manually by pressing the key 't'. +.PP +.TP 5 +.B t +Trigger a new sample manually. This key can be pressed if the current sample +should be finished before the timer has exceeded, or if no timer is set at all +(interval timer defined as 0). In the latter case +.I atop +can be used as a stopwatch to measure the load being caused by a +particular application transaction, without knowing on beforehand how many +seconds this transaction will last. + +When viewing the contents of a raw file, this key can be used to show the +next sample from the file. +.PP +.TP 5 +.B T +When viewing the contents of a raw file, this key can be used to show the +previous sample from the file. +.PP +.TP 5 +.B b +When viewing the contents of a raw file, this key can be used to branch +to a certain timestamp within the file (either forward or backward). +.PP +.TP 5 +.B r +Reset all counters to zero to see the system and process activity since +boot again. + +When viewing the contents of a raw file, this key can be used to rewind +to the beginning of the file again. +.PP +.TP 5 +.B U +Specify a search string for specific user names as a regular expression. +From now on, only (active) processes will be shown from a user which matches +the regular expression. +The system statistics are still system wide. +If the Enter-key is pressed without specifying a name, (active) +processes of all users will be shown again. +.br +Whether this key is active or not can be seen in the header line. +.PP +.TP 5 +.B I +Specify a list with one or more PIDs to be selected. +From now on, only processes will be shown with a PID which matches +one of the given list. +The system statistics are still system wide. +If the Enter-key is pressed without specifying a PID, all (active) +processes will be shown again. +.br +Whether this key is active or not can be seen in the header line. +.PP +.TP 5 +.B P +Specify a search string for specific process names as a regular expression. +From now on, only processes will be shown with a name which matches the +regular expression. +The system statistics are still system wide. +If the Enter-key is pressed without specifying a name, all (active) +processes will be shown again. +.br +Whether this key is active or not can be seen in the header line. +.PP +.TP 5 +.B / +Specify a specific command line search string as a regular expression. +From now on, only processes will be shown with a command line which +matches the regular expression. +The system statistics are still system wide. +If the Enter-key is pressed without specifying a string, all (active) +processes will be shown again. +.br +Whether this key is active or not can be seen in the header line. +.PP +.TP 5 +.B J +Specify a Docker container id of 12 (hexadecimal) characters. +From now on, only processes will be shown that run in that specific +Docker container (CID). +The system statistics are still system wide. +If the Enter-key is pressed without specifying a container id, +all (active) processes will be shown again. +.br +Whether this key is active or not can be seen in the header line. +.PP +.TP 5 +.B S +Specify search strings for specific logical volume names, +specific disk names and specific network interface names. All +search strings are interpreted as a regular expressions. +From now on, only those system resources are shown that match +the concerning regular expression. +If the Enter-key is pressed without specifying a search string, all (active) +system resources of that type will be shown again. +.br +Whether this key is active or not can be seen in the header line. +.PP +.TP 5 +.B a +The `all/active' key can be used to toggle between only showing/accumulating +the processes that were active during the last interval (default) or +showing/accumulating all processes. +.br +Whether this key is active or not can be seen in the header line. +.PP +.TP 5 +.B G +By default, +.I atop +shows/accumulates the processes that are alive and the processes +that are exited during the last interval. With this key (toggle), +showing/accumulating the processes that are exited can be suppressed. +.br +Whether this key is active or not can be seen in the header line. +.PP +.TP 5 +.B f +Show a fixed (maximum) number of header lines for system resources (toggle). +By default only the lines are shown about system resources (CPUs, paging, +logical volumes, disks, network interfaces) that really have been active +during the last interval. +With this key you can force +.I atop +to show lines of inactive resources as well. +.br +Whether this key is active or not can be seen in the header line. +.PP +.TP 5 +.B F +Suppress sorting of system resources (toggle). +By default system resources (CPUs, logical volumes, disks, +network interfaces) are sorted on utilization. +.br +Whether this key is active or not can be seen in the header line. +.PP +.TP 5 +.B 1 +Show relevant counters as an average per second (in the format `..../s') +instead of as a total during the interval (toggle). +.br +Whether this key is active or not can be seen in the header line. +.PP +.TP 5 +.B l +Limit the number of system level lines for the counters per-cpu, +the active disks and the network interfaces. +By default lines are shown of all CPUs, disks and network interfaces +which have been active during the last interval. +Limiting these lines can be useful on systems with huge number CPUs, +disks or interfaces in order to be able to run +.I atop +on a screen/window with e.g. only 24 lines. +.br +For all mentioned resources the maximum number of lines can be specified +interactively. When using the flag +.B -l +the maximum number of per-cpu lines is set to 0, +the maximum number of disk lines to 5 and +the maximum number of interface lines to 3. +These values can be modified again in interactive mode. +.PP +.TP 5 +.B k +Send a signal to an active process (a.k.a. kill a process). +.PP +.TP 5 +.B q +Quit the program. +.PP +.TP 5 +.B PgDn +Show the next page of the process/thread list. +.br +With the arrow-down key the list can be scrolled downwards with single lines. +.PP +.TP 5 +.B ^F +Show the next page of the process/thread list (forward). +.br +With the arrow-down key the list can be scrolled downwards with single lines. +.PP +.TP 5 +.B PgUp +Show the previous page of the process/thread list. +.br +With the arrow-up key the list can be scrolled upwards with single lines. +.PP +.TP 5 +.B ^B +Show the previous page of the process/thread list (backward). +.br +With the arrow-up key the list can be scrolled upwards with single lines. +.PP +.TP 5 +.B ^L +Redraw the screen. +.SH RAW DATA STORAGE +In order to store system and process level statistics for long-term +analysis (e.g. to check the system load and the active processes running +yesterday between 3:00 and 4:00 PM), +.I atop +can store the system and process level statistics in +compressed binary format in a raw file with the flag +.B -w +followed by the filename. +If this file already exists and is recognized as a raw data file, +.I atop +will append new samples to the file (starting with a sample which reflects +the activity since boot); if the file does not exist, it will be created. +.br +All processes/threads are stored in the raw file. +.br +The interval (default: 10 seconds) and number of samples (default: infinite) +can be passed as last arguments. Instead of the number of samples, the flag +.B -S +can be used to indicate that +.I atop +should finish anyhow before midnight. +.PP +A raw file can be read and visualized again with the flag +.B -r +followed by the filename. If no filename is specified, the file +.BI /var/log/atop/atop_ YYYYMMDD +is opened for input (where +.I YYYYMMDD +are digits representing the current date). +If a filename is specified in the format YYYYMMDD (representing any valid +date), the file +.BI /var/log/atop/atop_ YYYYMMDD +is opened. +If a filename with the symbolic name +.BI y +is specified, yesterday's daily logfile is opened +(this can be repeated so 'yyyy' indicates the logfile of four days ago). +.br +The samples from the file can be viewed interactively by using the key 't' +to show the next sample, the key 'T' to show the previous sample, the +key 'b' to branch to a particular time or the key 'r' to rewind to +the begin of the file. +.br +When output is redirected to a file or pipe, +.B atop +prints all samples in plain ASCII. The default line length is 80 characters +in that case; with the flag +.B -L +followed by an alternate line length, more (or less) columns will be shown. +.br +With the flag +.B -b +(begin time) and/or +.B -e +(end time) followed by a time argument of the form HH:MM, +a certain time period within the raw file can be selected. +.PP +When +.B atop +is installed, the script +.B atop.daily +is stored in the +.I /usr/share/atop +directory. +This scripts takes care that +.B atop +is activated every day at midnight to write compressed binary data to the file +.BI /var/log/atop/atop_ YYYYMMDD +with an interval of 10 minutes. +.br +Furthermore the script removes all raw files which are older than four weeks. +.br +The script is activated via the +.B cron +daemon using the file +.I /etc/cron.d/atop +with the contents +.br +.B \ \ \ \ \ \ \ \ 0 0 * * * root /usr/share/atop/atop.daily +.PP +When the package +.B psacct +is installed, the process accounting is automatically restarted via the +.B logrotate +mechanism. The file +.B /etc/logrotate.d/psaccs_atop +takes care that +.B atop +is finished just before the rotation of the process accounting file +and the file +.B /etc/logrotate.d/psaccu_atop +takes care that +.B atop +is restarted again after the rotation. +When the package +.B psacct +is not installed, these logrotate-files have no effect. +.SH OUTPUT DESCRIPTION +The first sample shows the system level activity since boot +(the elapsed time in the header shows the time since boot). +Note that particular counters could have reached their maximum +value (several times) and started by zero again, +so do not rely on these figures. +.PP +For every sample +.I atop +first shows the lines related to system level activity. If a particular +system resource has not been used during the interval, the entire line +related to this resource is suppressed. So the number of system level lines +may vary for each sample. +.br +After that a list is shown of processes which have been active during the last +interval. This list is by default sorted on cpu consumption, but this order +can be changed by the keys which are previously described. +.PP +If values have to be shown by +.I atop +which do not fit in the column width, +another format is used. If e.g. a cpu-consumption of 233216 milliseconds +should be shown in a column width of 4 positions, it is shown as `233s' +(in seconds). +For large memory figures, another unit is chosen if the value does not fit +(Mb instead of Kb, Gb instead of Mb, Tb instead of Gb, ...). +For other values, a kind of exponent notation is used (value 123456789 +shown in a column of 5 positions gives 123e6). +.SH OUTPUT DESCRIPTION - SYSTEM LEVEL +The system level information consists of the following output lines: +.PP +.TP 5 +.B PRC +Process and thread level totals. +.br +This line contains the total cpu time consumed +in system mode (`sys') and in user mode (`user'), +the total number of processes present at this moment (`#proc'), +the total number of threads present at this moment in state `running' (`#trun'), +`sleeping interruptible' (`#tslpi') and `sleeping uninterruptible' (`#tslpu'), +the number of zombie processes (`#zombie'), +the number of clone system calls (`clones'), and +the number of processes that ended during the interval +(`#exit') when process accounting is used. Instead of `#exit` the last +column may indicate that process accounting could not be activated +(`no procacct`). +.br +If the screen-width does not allow all of these counters, +only a relevant subset is shown. +.PP +.TP 5 +.B CPU +CPU utilization. +.br +At least one line is shown for the total occupation of all CPUs together. +.br +In case of a multi-processor system, an additional line is shown +for every individual processor (with `cpu' in lower case), +sorted on activity. Inactive CPUs will not be shown by default. +The lines showing the per-cpu occupation contain the cpu number in +the last field. + +Every line contains the percentage of cpu time spent in +kernel mode by all active processes (`sys'), +the percentage of cpu time consumed in user mode (`user') for all +active processes (including processes running with a nice value larger than +zero), the percentage of cpu time spent for interrupt handling (`irq') +including softirq, the percentage of unused cpu time while no processes +were waiting for disk-I/O (`idle'), and +the percentage of unused cpu time while at least one process was waiting +for disk-I/O (`wait'). +.br +In case of per-cpu occupation, the last column shows the cpu number and +the wait percentage (`w') for that cpu. +The number of lines showing the per-cpu occupation can be limited. + +For virtual machines the steal-percentage is shown (`steal'), reflecting +the percentage of cpu time stolen by other virtual machines +running on the same hardware. +.br +For physical machines hosting one or more virtual machines, +the guest-percentage is shown (`guest'), reflecting +the percentage of cpu time used by the virtual machines. Notice that +this percentage overlaps the user-percentage. + +In case of frequency-scaling, all previously mentioned CPU-percentages +are relative to the used scaling of the CPU during the interval. +If a CPU has been active for e.g. 50% in user mode during the interval +while the frequency-scaling of that CPU was 40%, only 20% of the full +capacity of the CPU has been used in user mode. +.br +In case that the kernel module `cpufreq_stats' is active +(after issueing `modprobe cpufreq_stats'), the +.I average +frequency (`avgf') and the +.I average +scaling percentage (`avgscal') is shown. Otherwise the +.I current +frequency (`curf') and the +.I current +scaling percentage (`curscal') is shown at the moment that the sample +is taken. + +If the screen-width does not allow all of these counters, +only a relevant subset is shown. +.PP +.TP 5 +.B CPL +CPU load information. +.br +This line contains the load average figures reflecting the number +of threads that are available to run on a CPU (i.e. part of the runqueue) +or that are waiting for disk I/O. These figures are averaged over +1 (`avg1'), 5 (`avg5') and 15 (`avg15') minutes. +.br +Furthermore the number of context switches (`csw'), the number +of serviced interrupts (`intr') and the number of available CPUs are shown. + +If the screen-width does not allow all of these counters, +only a relevant subset is shown. +.PP +.TP 5 +.B MEM +Memory occupation. +.br +This line contains the total amount of physical memory +(`tot'), the amount of memory which is currently free (`free'), +the amount of memory in use as page cache including +the total resident shared memory (`cache'), the amount of memory within the +page cache that has to be flushed to disk (`dirty'), the amount +of memory used for filesystem meta data (`buff'), the amount of +memory being used for kernel mallocs (`slab'), the amount of +slab memory that is reclaimable (`slrec'), the resident size of shared +memory including tmpfs (`shmem`), the resident size of shared memory (`shrss`) +the amount of shared memory that is currently swapped (`shswp`), +the amount of memory that is currently claimed by vmware's +balloon driver (`vmbal`), +the amount of memory that is claimed for huge pages (`hptot`), +and the amount of huge page memory that is really in use (`hpuse`). + +If the screen-width does not allow all of these counters, +only a relevant subset is shown. +.PP +.TP 5 +.B SWP +Swap occupation and overcommit info. +.br +This line contains the total amount of swap space on disk (`tot') and +the amount of free swap space (`free'). +.br +Furthermore the committed virtual memory space (`vmcom') and the maximum +limit of the committed space (`vmlim', which is by default swap size +plus 50% of memory size) is shown. +The committed space is the reserved virtual space for all allocations of +private memory space for processes. The kernel only verifies whether the +committed space exceeds the limit if strict overcommit handling is +configured (vm.overcommit_memory is 2). +.PP +.TP 5 +.B PAG +Paging frequency. +.br +This line contains the number of scanned pages (`scan') due to the fact +that free memory drops below a particular threshold and the number +times that the kernel tries to reclaim pages due to an urgent need (`stall'). +.br +Also the number of memory pages the system read from swap space (`swin') +and the number of memory pages the system wrote to swap space (`swout') +are shown. +.PP +.TP 5 +.B LVM/MDD/DSK +Logical volume/multiple device/disk utilization. +.br +Per active unit one line is produced, sorted on unit activity. +Such line shows the name (e.g. VolGroup00-lvtmp for a logical volume or +sda for a hard disk), the busy percentage i.e. the portion of time that the +unit was busy handling requests (`busy'), the number of read requests issued +(`read'), the number of write requests issued (`write'), +the number of KiBytes per read (`KiB/r'), +the number of KiBytes per write (`KiB/w'), +the number of MiBytes per second throughput for reads (`MBr/s'), +the number of MiBytes per second throughput for writes (`MBw/s'), +the average queue depth (`avq') +and the average number of milliseconds needed by a request (`avio') +for seek, latency and data transfer. +.br +If the screen-width does not allow all of these counters, +only a relevant subset is shown. + +The number of lines showing the units can be limited per class (LVM, MDD or +DSK) with the 'l' key or statically (see separate man-page of atoprc). +By specifying the value 0 for a particular class, no lines will be +shown any more for that class. +.PP +.TP 5 +.B NFM +Network Filesystem (NFS) mount at the client side. +.br +For each NFS-mounted filesystem, a line is shown that contains +the mounted server directory, the name of the server (`srv'), +the total number of bytes physically read from the server (`read') and +the total number of bytes physically written to the server (`write'). +Data transfer is subdivided in +the number of bytes read via normal read() system calls (`nread'), +the number of bytes written via normal read() system calls (`nwrit'), +the number of bytes read via direct I/O (`dread'), +the number of bytes written via direct I/O (`dwrit'), +the number of bytes read via memory mapped I/O pages (`mread'), and +the number of bytes written via memory mapped I/O pages (`mwrit'). +.PP +.TP 5 +.B NFC +Network Filesystem (NFS) client side counters. +.br +This line contains the number of RPC calls issues by local processes +(`rpc'), the number of read RPC calls (`read`) and +write RPC calls (`rpwrite') issued to the NFS server, +the number of RPC calls being retransmitted (`retxmit') +and the number of authorization refreshes (`autref'). +.PP +.TP 5 +.B NFS +Network Filesystem (NFS) server side counters. +.br +This line contains the number of RPC calls received from +NFS clients (`rpc'), +the number of read RPC calls received (`cread`), +the number of write RPC calls received (`cwrit'), +the number of network requests handled via TCP (`nettcp'), +the number of network requests handled via UDP (`netudp'), +the number of Megabytes/second returned to read requests by clients (`MBcr/s`), +the number of Megabytes/second passed in write requests by clients (`MBcw/s`), +the number of reply cache hits (`rchits'), +the number of reply cache misses (`rcmiss') and +the number of uncached requests (`rcnoca'). +Furthermore some error counters indicating the number of requests +with a bad format (`badfmt') or a bad authorization (`badaut'), and a +counter indicating the number of bad clients (`badcln'). +and the number of authorization refreshes (`autref'). +.PP +.TP 5 +.B NET +Network utilization (TCP/IP). +.br +One line is shown for activity of the transport layer (TCP and UDP), one line +for the IP layer and one line per active interface. +.br +For the transport layer, +counters are shown concerning the number of received TCP segments +including those received in error (`tcpi'), +the number of transmitted TCP segments excluding +those containing only retransmitted octets (`tcpo'), +the number of UDP datagrams received (`udpi'), +the number of UDP datagrams transmitted (`udpo'), +the number of active TCP opens (`tcpao'), +the number of passive TCP opens (`tcppo'), +the number of TCP output retransmissions (`tcprs'), +the number of TCP input errors (`tcpie'), +the number of TCP output resets (`tcpor'), +the number of UDP no ports (`udpnp'), and +the number of UDP input errors (`udpie'). +.br +If the screen-width does not allow all of these counters, +only a relevant subset is shown. +.br +These counters are related to IPv4 and IPv6 combined. + +For the IP layer, counters are shown concerning the number of IP datagrams +received from interfaces, including those received in error (`ipi'), +the number of IP datagrams that local higher-layer protocols offered for +transmission (`ipo'), the number of received IP datagrams which were +forwarded to other interfaces (`ipfrw'), the number of IP datagrams which +were delivered to local higher-layer protocols (`deliv'), +the number of received ICMP datagrams (`icmpi'), and +the number of transmitted ICMP datagrams (`icmpo'). +.br +If the screen-width does not allow all of these counters, +only a relevant subset is shown. +.br +These counters are related to IPv4 and IPv6 combined. + +For every active network interface one line is shown, +sorted on the interface activity. +Such line shows the name of the interface and its busy percentage +in the first column. +The busy percentage for half duplex is determined by comparing the +interface speed with the number of bits transmitted and received +per second; for full duplex the interface speed is compared with the +highest of either the transmitted or the received bits. +When the interface speed can not be determined (e.g. for the loopback +interface), `---' is shown instead of the percentage. +.br +Furthermore the number of received packets (`pcki'), +the number of transmitted packets (`pcko'), +the line speed of the interface (`sp'), +the effective amount of bits received per second (`si'), +the effective amount of bits transmitted per second (`so'), +the number of collisions (`coll'), +the number of received multicast packets (`mlti'), +the number of errors while receiving a packet (`erri'), +the number of errors while transmitting a packet (`erro'), +the number of received packets dropped (`drpi'), and +the number of transmitted packets dropped (`drpo'). +.br +If the screen-width does not allow all of these counters, +only a relevant subset is shown. +.br +The number of lines showing the network interfaces can be limited. +.SH OUTPUT DESCRIPTION - PROCESS LEVEL +Following the system level information, the processes are shown from which the +resource utilization has changed during the last interval. These processes +might have used cpu time or issued disk or network requests. However a process +is also shown if part of it has been paged out due to lack of memory (while +the process itself was in sleep state). +.PP +Per process the following fields may be shown (in alphabetical order), +depending on the current output mode as described in the section +INTERACTIVE COMMANDS and depending on the current width of your window: +.PP +.TP 9 +.B AVGRSZ +The average size of one read-action on disk. +.PP +.TP 9 +.B AVGWSZ +The average size of one write-action on disk. +.PP +.TP 9 +.B BANDWI +Total bandwidth for received TCP and UDP packets consumed by this process +(bits-per-second). +This value can be compared with the value `si' +on interface level (used bandwidth per interface). +.br +This information will only be shown when the kernel module `netatop' +is loaded. +.PP +.TP 9 +.B BANDWO +Total bandwidth for sent TCP and UDP packets consumed by this process +(bits-per-second). +This value can be compared with the value `so' +on interface level (used bandwidth per interface). +.br +This information will only be shown when the kernel module `netatop' +is loaded. +.PP +.TP 9 +.B CID +Container ID (Docker) of 12 hexadecimal digits, referring to the container +in which the process/thread is running. +If a process has been started and finished during the last +interval, a `?' is shown because the container ID is not part of +the standard process accounting record. +.PP +.TP 9 +.B CMD +The name of the process. +This name can be surrounded by "less/greater than" +signs (`') which means that the process has finished during the last +interval. +.br +Behind the abbreviation `CMD' in the header line, the current page number and +the total number of pages of the process/thread list are shown. +.PP +.TP 9 +.B COMMAND-LINE +The full command line of the process (including arguments). If the length of +the command line exceeds the length of the screen line, the arrow +keys -> and <- can be used for horizontal scroll. +.br +Behind the verb `COMMAND-LINE' in the header line, the current page number +and the total number of pages of the process/thread list are shown. +.PP +.TP 9 +.B CPU +The occupation percentage of this process related to the available capacity +for this resource on system level. +.PP +.TP 9 +.B CPUNR +The identification of the CPU the (main) thread is running on +or has recently been running on. +.PP +.TP 9 +.B CTID +Container ID (OpenVZ). +If a process has been started and finished during the last +interval, a `?' is shown because the container ID is not part of +the standard process accounting record. +.PP +.TP 9 +.B DSK +The occupation percentage of this process related to the total load that +is produced by all processes (i.e. total disk accesses +by all processes during the last interval). +.br +This information is shown when per process "storage accounting" is active +in the kernel. +.PP +.TP 9 +.B EGID +Effective group-id under which this process executes. +.PP +.TP 9 +.B ENDATE +Date that the process has been finished. If the process is still running, +this field shows `active'. +.PP +.TP 9 +.B ENTIME +Time that the process has been finished. If the process is still running, +this field shows `active'. +.PP +.TP 9 +.B ENVID +Virtual environment identified (OpenVZ only). +.PP +.TP 9 +.B EUID +Effective user-id under which this process executes. +.PP +.TP 9 +.B EXC +The exit code of a terminated process (second position of column `ST' is E) +or the fatal signal number (second position of column `ST' is S or C). +.PP +.TP 9 +.B FSGID +Filesystem group-id under which this process executes. +.PP +.TP 9 +.B FSUID +Filesystem user-id under which this process executes. +.PP +.TP 9 +.B MAJFLT +The number of page faults issued by this process that have been solved +by creating/loading the requested memory page. +.PP +.TP 9 +.B MEM +The occupation percentage of this process related to the available capacity +for this resource on system level. +.PP +.TP 9 +.B MINFLT +The number of page faults issued by this process that have been solved +by reclaiming the requested memory page from the free list of pages. +.PP +.TP 9 +.B NET +The occupation percentage of this process related to the total load that +is produced by all processes (i.e. consumed network bandwidth +of all processes during the last interval). +.br +This information will only be shown when kernel module `netatop' is loaded. +.PP +.TP 9 +.B NICE +The more or less static priority that can be given to a process on a +scale from -20 (high priority) to +19 (low priority). +.PP +.TP 9 +.B NPROCS +The number of active and terminated processes accumulated for this user +or program. +.PP +.TP 9 +.B PID +Process-id. +If a process has been started and finished during the last +interval, a `?' is shown because the process-id is not part of +the standard process accounting record. +.PP +.TP 9 +.B POLI +The policies 'norm' (normal, which is SCHED_OTHER), 'btch' (batch) +and 'idle' refer to timesharing processes. +The policies 'fifo' (SCHED_FIFO) and 'rr' (round robin, which is SCHED_RR) +refer to realtime processes. +.PP +.TP 9 +.B PPID +Parent process-id. +If a process has been started and finished during the last +interval, value 0 is shown because the parent process-id is not part of +the standard process accounting record. +.PP +.TP 9 +.B PRI +The process' priority ranges from 0 (highest priority) to 139 (lowest +priority). Priority 0 to 99 are used for realtime processes (fixed +priority independent of their behavior) and priority 100 to 139 for +timesharing processes (variable priority depending on their recent +CPU consumption and the nice value). +.PP +.TP 9 +.B PSIZE +The proportional memory size of this process (or user). +.br +Every process shares resident memory with other processes. E.g. when a +particular program is started several times, the code pages (text) are +only loaded once in memory and shared by all incarnations. Also the code +of shared libraries is shared by all processes using that shared library, +as well as shared memory and memory-mapped files. +For the PSIZE calculation of a process, the resident memory of a process +that is shared with other processes is divided by the number of sharers. +This means, that every process is accounted for a proportional part of +that memory. Accumulating the PSIZE values of all processes in the +system gives a reliable impression of the total resident memory consumed +by all processes. +.br +Since gathering of all values that are needed to calculate the PSIZE is a +relatively time-consuming task, the 'R' key (or '-R' flag) should +be active. Gathering these values also requires superuser privileges +(otherwise '?K' is shown in the output). +.br +If a process has finished during the last interval, no value is shown +since the proportional memory size is not part of the standard +process accounting record. +.PP +.TP 9 +.B RDDSK +When the kernel maintains standard io statistics (>= 2.6.20): +.br +The read data transfer issued physically on disk (so reading from the +disk cache is not accounted for). +.br +Unfortunately, the kernel aggregates the +data tranfer of a process to the data transfer of its parent process when +terminating, so you might see transfers for (parent) processes like +cron, bash or init, that are not really issued by them. +.PP +.TP 9 +.B RGID +The real group-id under which the process executes. +.PP +.TP 9 +.B RGROW +The amount of resident memory that the process has grown during the last +interval. A resident growth can be caused by touching memory pages which +were not physically created/loaded before (load-on-demand). +Note that a resident growth can also be negative e.g. when part of the process +is paged out due to lack of memory or when the process frees dynamically +allocated memory. +For a process which started during the last interval, the resident growth +reflects the total resident size of the process at that moment. +.br +If a process has finished during the last interval, no value is shown +since resident memory occupation is not part of the standard +process accounting record. +.PP +.TP 9 +.B RNET +The number of TCP- and UDP packets received by this process. +This information will only be shown when kernel module `netatop' is installed. +.br +If a process has finished during the last interval, no value is shown +since network counters are not part of the standard process accounting record. +.PP +.TP 9 +.B RSIZE +The total resident memory usage consumed by this process (or user). +Notice that the RSIZE of a process includes all resident memory used +by that process, even if certain memory parts are shared with other processes +(see also the explanation of PSIZE). +.br +If a process has finished during the last interval, no value is shown +since resident memory occupation is not part of the standard +process accounting record. +.PP +.TP 9 +.B RTPR +Realtime priority according the POSIX standard. +Value can be 0 for a timesharing process (policy 'norm', 'btch' or 'idle') +or ranges from 1 (lowest) till 99 (highest) for a realtime process +(policy 'rr' or 'fifo'). +.PP +.TP 9 +.B RUID +The real user-id under which the process executes. +.PP +.TP 9 +.B S +The current state of the (main) thread: `R' for running +(currently processing or in the runqueue), `S' for sleeping interruptible +(wait for an event to occur), +`D' for sleeping non-interruptible, `Z' for zombie (waiting to be synchronized +with its parent process), `T' for stopped (suspended or traced), `W' for +swapping, and `E' (exit) for processes which have finished during the last +interval. +.PP +.TP 9 +.B SGID +The saved group-id of the process. +.PP +.TP 9 +.B SNET +The number of TCP and UDP packets transmitted by this process. +This information will only be shown when the kernel module `netatop' +is loaded. +.PP +.TP 9 +.B ST +The status of a process. +.br +The first position indicates if the process has been +started during the last interval (the value +.I N +means 'new process'). + +The second position indicates if the process has been +finished during the last interval. +.br +The value +.I E +means 'exit' on the process' own initiative; the exit code is displayed +in the column `EXC'. +.br +The value +.I S +means that the process has been terminated unvoluntarily +by a signal; the signal number is displayed in the in the column `EXC'. +.br +The value +.I C +means that the process has been terminated unvoluntarily +by a signal, producing a core dump in its current directory; +the signal number is displayed in the column `EXC'. +.PP +.TP 9 +.B STDATE +The start date of the process. +.PP +.TP 9 +.B STTIME +The start time of the process. +.PP +.TP 9 +.B SUID +The saved user-id of the process. +.PP +.TP 9 +.B SWAPSZ +The swap space consumed by this process (or user). +.PP +.TP 9 +.B SYSCPU +CPU time consumption of this process in system mode (kernel mode), usually +due to system call handling. +.PP +.TP 9 +.B TCPRASZ +The average size of a received TCP buffer in bytes. +This information will only be shown when the kernel module `netatop' +is loaded. +.PP +.TP 9 +.B TCPRCV +The number of TCP packets received for this process. +This information will only be shown when the kernel module `netatop' +is loaded. +.PP +.TP 9 +.B TCPSASZ +The average size of a transmitted TCP buffer in bytes. +This information will only be shown when the kernel module `netatop' +is loaded. +.PP +.TP 9 +.B TCPSND +The number of TCP packets transmitted for this process. +This information will only be shown when the kernel module `netatop' +is loaded. +.PP +.TP 9 +.B THR +Total number of threads within this process. +All related threads are contained in a thread group, represented by +.I atop +as one line or as a separate line when the 'y' key (or -y flag) is active. + +On Linux 2.4 systems it is hardly possible to determine +which threads (i.e. processes) are related to the same thread group. +Every thread is represented by +.I atop +as a separate line. +.PP +.TP 9 +.B TID +Thread-id. +All threads within a process run with the same PID but with a +different TID. This value is shown for individual threads in +multi-threaded processes (when using the key 'y'). +.PP +.TP 9 +.B TRUN +Number of threads within this process that are in the state 'running' (R). +.PP +.TP 9 +.B TSLPI +Number of threads within this process that are in the +state 'interruptible sleeping' (S). +.PP +.TP 9 +.B TSLPU +Number of threads within this process that are in the +state 'uninterruptible sleeping' (D). +.PP +.TP 9 +.B UDPRASZ +The average size of a received UDP packet in bytes. +This information will only be shown when the kernel module `netatop' +is loaded. +.PP +.TP 9 +.B UDPRCV +The number of UDP packets received by this process. +This information will only be shown when the kernel module `netatop' +is loaded. +.PP +.TP 9 +.B UDPSASZ +The average size of a transmitted UDP packets in bytes. +This information will only be shown when the kernel module `netatop' +is loaded. +.PP +.TP 9 +.B UDPSND +The number of UDP packets transmitted by this process. +This information will only be shown when the kernel module `netatop' +is loaded. +.PP +.TP 9 +.B USRCPU +CPU time consumption of this process in user mode, due to processing the +own program text. +.PP +.TP 9 +.B VDATA +The virtual memory size of the private data used by this process +(including heap and shared library data). +.PP +.TP 9 +.B VGROW +The amount of virtual memory that the process has grown during the last +interval. A virtual growth can be caused by e.g. issueing a malloc() +or attaching a shared memory segment. Note that a virtual growth can also +be negative by e.g. issueing a free() or detaching a shared memory segment. +For a process which started during the last interval, the virtual growth +reflects the total virtual size of the process at that moment. +.br +If a process has finished during the last interval, no value is shown +since virtual memory occupation is not part of the standard +process accounting record. +.PP +.TP 9 +.B VPID +Virtual process-id (within an OpenVZ container). +If a process has been started and finished during the last +interval, a `?' is shown because the virtual process-id is not part of +the standard process accounting record. +.PP +.TP 9 +.B VSIZE +The total virtual memory usage consumed by this process (or user). +.br +If a process has finished during the last interval, no value is shown +since virtual memory occupation is not part of the standard +process accounting record. +.PP +.TP 9 +.B VSLIBS +The virtual memory size of the (shared) text of all shared libraries used +by this process. +.PP +.TP 9 +.B VSTACK +The virtual memory size of the (private) stack used by this process +.PP +.TP 9 +.B VSTEXT +The virtual memory size of the (shared) text of the executable program. +.PP +.TP 9 +.B WRDSK +When the kernel maintains standard io statistics (>= 2.6.20): +.br +The write data transfer issued physically on disk (so writing to the +disk cache is not accounted for). +This counter is maintained for the application process that writes its +data to the cache (assuming that this data is physically transferred +to disk later on). Notice that disk I/O needed for swapping is +not taken into account. +.br +Unfortunately, the kernel aggregates the +data tranfer of a process to the data transfer of its parent process when +terminating, so you might see transfers for (parent) processes like +cron, bash or init, that are not really issued by them. +.PP +.TP 9 +.B WCANCL +When the kernel maintains standard io statistics (>= 2.6.20): +.br +The write data transfer previously accounted for this process +or another process that has been cancelled. +Suppose that a process writes new data to a file and that data is removed +again before the cache buffers have been flushed to disk. +Then the original process shows the written data as WRDSK, while +the process that removes/truncates the file shows +the unflushed removed data as WCANCL. +.SH PARSEABLE OUTPUT +With the flag +.B -P +followed by a list of one or more labels (comma-separated), +parseable output is produced for each sample. +The labels that can be specified for system-level statistics +correspond to the labels (first verb of each line) +that can be found in the interactive output: +"CPU", "cpu" "CPL" "MEM", "SWP", "PAG", "LVM", "MDD", "DSK", "NFM", +"NFC", "NFS" and "NET". +.br +For process-level statistics special labels are introduced: +"PRG" (general), "PRC" (cpu), "PRM" (memory), "PRD" (disk, only if +"storage accounting" is active) and "PRN" (network, only if +the kernel module 'netatop' has been installed). +.br +With the label "ALL", all system and process level statistics are shown. +.PP +For every interval all requested lines are shown whereafter +.B atop +shows a line just containing the label "SEP" as a separator before the +lines for the next sample are generated. +.br +When a sample contains the values since boot, +.B atop +shows a line just containing the label "RESET" before the +lines for this sample are generated. +.PP +The first part of each output-line consists of the following six fields: +.B label +(the name of the label), +.B host +(the name of this machine), +.B epoch +(the time of this interval as number of seconds since 1-1-1970), +.B date +(date of this interval in format YYYY/MM/DD), +.B time +(time of this interval in format HH:MM:SS), and +.B interval +(number of seconds elapsed for this interval). +.PP +The subsequent fields of each output-line depend on the label: +.PP +.TP 9 +.B CPU +Subsequent fields: +total number of clock-ticks per second for this machine, +number of processors, +consumption for all CPUs in system mode (clock-ticks), +consumption for all CPUs in user mode (clock-ticks), +consumption for all CPUs in user mode for niced processes (clock-ticks), +consumption for all CPUs in idle mode (clock-ticks), +consumption for all CPUs in wait mode (clock-ticks), +consumption for all CPUs in irq mode (clock-ticks), +consumption for all CPUs in softirq mode (clock-ticks), +consumption for all CPUs in steal mode (clock-ticks), +consumption for all CPUs in guest mode (clock-ticks) overlapping user mode, +frequency of all CPUs and frequency percentage of all CPUs. +.TP 9 +.B cpu +Subsequent fields: +total number of clock-ticks per second for this machine, +processor-number, +consumption for this CPU in system mode (clock-ticks), +consumption for this CPU in user mode (clock-ticks), +consumption for this CPU in user mode for niced processes (clock-ticks), +consumption for this CPU in idle mode (clock-ticks), +consumption for this CPU in wait mode (clock-ticks), +consumption for this CPU in irq mode (clock-ticks), +consumption for this CPU in softirq mode (clock-ticks), +consumption for this CPU in steal mode (clock-ticks), +consumption for this CPU in guest mode (clock-ticks) overlapping user mode, +frequency of this CPU and frequency percentage of this CPU. +.TP 9 +.B CPL +Subsequent fields: +number of processors, +load average for last minute, +load average for last five minutes, +load average for last fifteen minutes, +number of context-switches, and +number of device interrupts. +.TP 9 +.B MEM +Subsequent fields: +page size for this machine (in bytes), +size of physical memory (pages), +size of free memory (pages), +size of page cache (pages), +size of buffer cache (pages), +size of slab (pages), +dirty pages in cache (pages), +reclaimable part of slab (pages), +total size of vmware's balloon pages (pages), +total size of shared memory (pages), +size of resident shared memory (pages), +size of swapped shared memory (pages), +huge page size (in bytes), +total size of huge pages (huge pages), and +size of free huge pages (huge pages). +.TP 9 +.B SWP +Subsequent fields: +page size for this machine (in bytes), +size of swap (pages), +size of free swap (pages), +0 (future use), +size of committed space (pages), and +limit for committed space (pages). +.TP 9 +.B PAG +Subsequent fields: +page size for this machine (in bytes), +number of page scans, +number of allocstalls, +0 (future use), +number of swapins, and +number of swapouts. +.TP 9 +.B LVM/MDD/DSK +For every logical volume/multiple device/hard disk one line is shown. +.br +Subsequent fields: +name, +number of milliseconds spent for I/O, +number of reads issued, +number of sectors transferred for reads, +number of writes issued, +and number of sectors transferred for write. +.TP 9 +.B NFM +Subsequent fields: +mounted NFS filesystem, +total number of bytes read, +total number of bytes written, +number of bytes read by normal system calls, +number of bytes written by normal system calls, +number of bytes read by direct I/O, +number of bytes written by direct I/O, +number of pages read by memory-mapped I/O, and +number of pages written by memory-mapped I/O. +.TP 9 +.B NFC +Subsequent fields: +number of transmitted RPCs, +number of transmitted read RPCs, +number of transmitted write RPCs, +number of RPC retransmissions, and +number of authorization refreshes. +.TP 9 +.B NFS +Subsequent fields: +number of handled RPCs, +number of received read RPCs, +number of received write RPCs, +number of bytes read by clients, +number of bytes written by clients, +number of RPCs with bad format, +number of RPCs with bad authorization, +number of RPCs from bad client, +total number of handled network requests, +number of handled network requests via TCP, +number of handled network requests via UDP, +number of handled TCP connections, +number of hits on reply cache, +number of misses on reply cache, and +number of uncached requests. +.TP 9 +.B NET +First one line is produced for the upper layers of the TCP/IP stack. +.br +Subsequent fields: +the verb "upper", +number of packets received by TCP, +number of packets transmitted by TCP, +number of packets received by UDP, +number of packets transmitted by UDP, +number of packets received by IP, +number of packets transmitted by IP, +number of packets delivered to higher layers by IP, and +number of packets forwarded by IP. + +Next one line is shown for every interface. +.br +Subsequent fields: +name of the interface, +number of packets received by the interface, +number of bytes received by the interface, +number of packets transmitted by the interface, +number of bytes transmitted by the interface, +interface speed, and +duplex mode (0=half, 1=full). +.TP 9 +.B PRG +For every process one line is shown. +.br +Subsequent fields: +PID (unique ID of task), name (between brackets), state, +real uid, real gid, TGID (group number of related tasks/threads), +total number of threads, +exit code, start time (epoch), +full command line (between brackets), PPID, +number of threads in state 'running' (R), +number of threads in state 'interruptible sleeping' (S), +number of threads in state 'uninterruptible sleeping' (D), +effective uid, effective gid, +saved uid, saved gid, +filesystem uid, filesystem gid, elapsed time (hertz), +is_process (y/n), OpenVZ virtual pid (VPID), OpenVZ container id (CTID) +and Docker container id (CID). +.TP 9 +.B PRC +For every process one line is shown. +.br +Subsequent fields: +PID, name (between brackets), state, +total number of clock-ticks per second for this machine, +CPU-consumption in user mode (clockticks), +CPU-consumption in system mode (clockticks), +nice value, priority, realtime priority, +scheduling policy, current CPU, sleep average, +TGID (group number of related tasks/threads) and is_process (y/n). +.TP 9 +.B PRM +For every process one line is shown. +.br +Subsequent fields: +PID, name (between brackets), state, +page size for this machine (in bytes), +virtual memory size (Kbytes), +resident memory size (Kbytes), +shared text memory size (Kbytes), +virtual memory growth (Kbytes), +resident memory growth (Kbytes), +number of minor page faults, +number of major page faults, +virtual library exec size (Kbytes), +virtual data size (Kbytes), +virtual stack size (Kbytes), +swap space used (Kbytes), +TGID (group number of related tasks/threads), is_process (y/n) and +proportional set size (Kbytes) if in 'R' option is specified. +.TP 9 +.B PRD +For every process one line is shown. +.br +Subsequent fields: +PID, name (between brackets), state, +obsoleted kernel patch installed ('n'), +standard io statistics used ('y' or 'n'), +number of reads on disk, +cumulative number of sectors read, +number of writes on disk, +cumulative number of sectors written, +cancelled number of written sectors, +TGID (group number of related tasks/threads) and is_process (y/n). +.br +If the standard I/O statistics (>= 2.6.20) are not used, +the disk I/O counters per process are not relevant. +The counters 'number of reads on disk' and 'number of writes on disk' are +obsoleted anyhow. +.TP 9 +.B PRN +For every process one line is shown. +.br +Subsequent fields: +PID, name (between brackets), state, +kernel module 'netatop' loaded ('y' or 'n'), +number of TCP-packets transmitted, +cumulative size of TCP-packets transmitted, +number of TCP-packets received, +cumulative size of TCP-packets received, +number of UDP-packets transmitted, +cumulative size of UDP-packets transmitted, +number of UDP-packets received, +cumulative size of UDP-packets transmitted, +number of raw packets transmitted (obsolete, always 0), +number of raw packets received (obsolete, always 0), +TGID (group number of related tasks/threads) and is_process (y/n). +.br +If the kernel module is not active, the network I/O counters +per process are not relevant. +.SH EXAMPLES +To monitor the current system load interactively with an interval of 5 seconds: +.PP +.TP 12 +.B \ atop 5 +.PP +To monitor the system load and write it to a file (in plain ASCII) +with an interval of one minute during half an hour with active +processes sorted on memory consumption: +.PP +.TP 12 +.B \ atop -M 60 30 > /log/atop.mem +.PP +Store information about the system and process activity in binary compressed +form to a file with an interval of ten minutes during an hour: +.PP +.TP 12 +.B \ atop -w /tmp/atop.raw 600 6 +.PP +View the contents of this file interactively: +.PP +.B \ atop -r /tmp/atop.raw +.PP +View the processor and disk utilization of this file in parseable format: +.PP +.B \ atop -PCPU,DSK -r /tmp/atop.raw +.PP +View the contents of today's standard logfile interactively: +.PP +.B \ atop -r +.PP +View the contents of the standard logfile of the day before yesterday +interactively: +.PP +.B \ atop -r yy +.PP +View the contents of the standard logfile of 2014, June 7 from +02:00 PM onwards interactively: +.PP +.B \ atop -r 20140607 -b 14:00 +.PP +.SH FILES +.PP +.TP 5 +.B /var/run/pacct_shadow.d/ +Directory containing the process accounting shadow files that are +used by +.I atop +when the +.I atopacctd +daemon is active. +.PP +.TP 5 +.B /var/cache/atop.d/atop.acct +File in which the kernel writes the accounting records when +.I atop +itself has activated the process accounting mechanism. +.PP +.TP 5 +.B /etc/atoprc +Configuration file containing system-wide default values. +See related man-page. +.PP +.TP 5 +.B ~/.atoprc +Configuration file containing personal default values. +See related man-page. +.PP +.TP 5 +.BI /var/log/atop/atop_ YYYYMMDD +Raw file, where +.I YYYYMMDD +are digits representing the current date. +This name is used by the script +.B atop.daily +as default name for the output file, and by +.B atop +as default name for the input file when using the +.B -r +flag. +.br +All binary system and process level data in this file has been stored +in compressed format. +.PP +.TP 5 +.BI /var/run/netatop.log +File that contains the netpertask structs containing the network +counters of exited processes. These structs are written by the +.I netatopd +daemon and read by +.I atop +after reading the standard process accounting records. +.SH SEE ALSO +.B atopsar(1), +.B atoprc(5), +.B atopacctd(8), +.B netatop(4), +.B netatopd(8), +.B logrotate(8) +.br +.B http://www.atoptool.nl +.SH AUTHOR +Gerlof Langeveld (gerlof.langeveld@atoptool.nl) +.br +JC van Winkel diff --git a/sources/man/atopacctd.8 b/sources/man/atopacctd.8 new file mode 100644 index 0000000..d6a8f69 --- /dev/null +++ b/sources/man/atopacctd.8 @@ -0,0 +1,147 @@ +.TH ATOPACCTD 8 "March 2017" "Linux" +.SH NAME +.B atopacctd +- process accounting daemon +.SH SYNOPSIS +.P +.B atopacctd +[-v | topdirectory] +.P +.SH DESCRIPTION +The +.I atopacctd +daemon switches on the process accounting feature in the kernel +and let the process accounting records be written to a file, +called the source file from now. +After process accounting is activated, the +.I atopacctd +daemon transfers every process accounting record that is available +in the source file to a shadow file. +Client processes (like +.I atop +processes) will read the shadow files instead of the +process accounting source file. +.br +In this way, the +.I atopacctd +daemon operates as a 'layer' between the process accounting file +that is written by the kernel and the shadow accounting files that are read by +.I atop +processes. +.PP +This approach has the following advantages: +.PP +.TP 3 +.B o +The +.I atopacctd +daemon takes care that the source file is kept to a limited size. +As soon as its maximum size is reached, it is truncated to a size +of zero again (this is not noticed by the +.I atop +processes). +.PP +.TP 3 +.B o +The +.I atopacct +daemon takes care that a shadow file is kept to a limited size. +As soon as the current shadow file reaches this maximum size, the +.I atopacctd +daemon creates a new (subsequent) shadow file. +While client processes still have the possibility to read the previous +shadow file(s), the +.I atopacctd +daemon continues writing accounting records to the newest (current) +shadow file. +For this reason, the name of a shadow file consists of a 10-digit +sequence number followed by the extension '.paf' (process acounting file). +Old shadow files that are not used by client processes any more, are +automatically removed by the garbage collector in the +.I atopacctd +daemon. +.PP +.TP 3 +.B o +When no client processes are active (any more), all shadow files +will be deleted and no records will be transferred to a shadow file +any more. As soon as at least one client is activate again, the +.I atopacctd +daemon continues writing shadow files. +.PP +The directory +.B /var/run +is used as the default topdirectory. +Below this top-directory, the source file +.B pacct_source +is created to which the kernel writes the process accounting records. +.br +Furthermore, the subdirectory +.B pacct_shadow.d +is created as a 'container' for the shadow files. +Apart from the shadow files, also the file +.B current +is maintained in this subdirectory, containing the sequence number of the +current (newest) shadow file and the maximum number of records that will be +written in each shadow file. +.PP +An alternative topdirectory can be specified as command line argument. +When an alternative topdirectory is defined, also modify the +configuration file +.B /etc/atoprc +to inform +.I atop +clients about this alternative location (see the +.B atoprc +man page). +Such alternative topdirectory should be owned by root and may not be +writable for the group or others (security reasons). +.PP +Notice that the kernel suspends writing process accounting records +when the free space of the filesystem on which the process accounting file +resides drops below 2%. Writing is resumed when the free space is 4% or more. +These lowwater and highwater percentages can be configured via the +.B /proc/sys/kernel/acct +pseudo-file. +.br +The +.I atopacctd +daemon suspends transferring process accounting records to shadow files +when the free space of the filesystem on which the process accounting file +resides drops below 5%. Transfer is resumed when the free space is 5% or more. +Log messages are generated via syslog when writing to the current shadow +file is suspended or resumed. +.PP +The +.B -v +flag can be used to verify the version of the +.I atopacctd +daemon. +.PP +.SH FILES +.PP +.TP 5 +.B /var/run/pacct_source +Regular file to which the kernel writes the process accounting records. +This file will be regularly truncated. +.PP +.TP 5 +.B /var/run/pacct_shadow.d/current +Regular file containing the sequence number of the current shadow file +and the maximum number of records per shadow file. +.PP +.TP 5 +.B /var/run/pacct_shadow.d/N.paf +Regular files containing the process accounting records that have +been copied transparently from the source file (N represents a 10-digit +sequence number). +.SH SEE ALSO +.B atop(1), +.B atopsar(1), +.B atoprc(5), +.B netatop(4), +.B netatopd(8) +.br +.B http://www.atoptool.nl +.SH AUTHOR +Gerlof Langeveld (gerlof.langeveld@atoptool.nl) diff --git a/sources/man/atoprc.5 b/sources/man/atoprc.5 new file mode 100644 index 0000000..6efcde3 --- /dev/null +++ b/sources/man/atoprc.5 @@ -0,0 +1,403 @@ +.TH ATOPRC 5 "March 2017" "Linux" +.SH NAME +.B atoprc +- atop/atopsar related rcfile +.SH DESCRIPTION +This manual page documents the rcfile of the +.I atop +and +.I atopsar +commands. +These commands can be used to monitor the system and process load on a +Linux system. +.PP +The atoprc file contains the default settings. These settings are read +during startup, first from the system-wide rcfile +.I /etc/atoprc +and after that from the user-specific rcfile +.I ~/.atoprc +(so system-wide settings can be overruled by an individual user). +The options in both rcfiles are identical. +.PP +.SH OPTIONS +.PP +The rcfile contains keyword-value pairs, one on every line (blank lines +and lines starting with a #-sign are ignored). +.br +The following keywords can be specified: +.PP +.TP 4 +.B flags +A list of default flags for +.B atop +can be defined here. The flags which are allowed +are 'g', 'm', 'd', 'n', 'u', 'p', 's', 'c', 'v', 'C', 'M', 'D', 'N', 'A', +\&'a', 'y', 'f', 'F', 'G', 'R', '1' and 'x'. +.PP +.TP 4 +.B interval +The default interval value in seconds. +.PP +.TP 4 +.B linelen +The length of a screen line when sending output to a file or pipe (default 80). +.PP +.TP 4 +.B username +The default regular expression for the users for which active +processes will be shown. +.PP +.TP 4 +.B procname +The default regular expression for the process names to be shown. +.PP +.TP 4 +.B maxlinecpu +The maximum number of active CPU's that will be shown. +.PP +.TP 4 +.B maxlinelvm +The maximum number of active logical volumes that will be shown. +.PP +.TP 4 +.B maxlinemdd +The maximum number of active multiple devices that will be shown. +.PP +.TP 4 +.B maxlinedisk +The maximum number of active disks that will be shown. +.PP +.TP 4 +.B maxlinenfsm +The maximum number of NFS mounts that will be shown on an NFS client. +.PP +.TP 4 +.B maxlineintf +The maximum number of active network interfaces that will be shown. +.PP +.TP 4 +.B maxlinecont +The maximum number of active containers that will be shown. +.PP +.TP 4 +.B cpucritperc +The busy percentage considered critical for a processor +(see section COLORS in the man-page of the +.I atop +command). +This percentage is used to determine +a weighted percentage for line coloring and sorting of active processes. +When this value is zero, no line coloring or automatic sorting is performed +for this resource. +.PP +.TP 4 +.B dskcritperc +The busy percentage considered critical for a disk +(see section COLORS in the man-page of the +.I atop +command). +This percentage is used to determine +a weighted percentage for line coloring and sorting of active processes. +When this value is zero, no line coloring or automatic sorting is performed +for this resource. +.PP +.TP 4 +.B netcritperc +The busy percentage considered critical for a network interface +(see section COLORS in the man-page of the +.I atop +command). +This percentage is used to determine +a weighted percentage for line coloring and sorting of active processes. +When this value is zero, no line coloring or automatic sorting is performed +for this resource. +.PP +.TP 4 +.B memcritperc +The percentage considered critical for memory utilization +(see section COLORS in the man-page of the +.I atop +command). +This percentage is used to determine +a weighted percentage for line coloring and sorting of active processes. +When this value is zero, no line coloring or automatic sorting is performed +for this resource. +.PP +.TP 4 +.B swpcritperc +The occupation percentage considered critical for swap space +(see section COLORS in the man-page of the +.I atop +command). +This percentage is used to determine +a weighted percentage for line coloring and sorting of active processes. +When this value is zero, no line coloring or automatic sorting is performed +for this resource. +.PP +.TP 4 +.B swoutcritsec +The number of pages swapped out per second considered critical for +for memory utilization +(see section COLORS in the man-page of the +.I atop +command). +This threshold is used in combination with 'memcritperc' to determine a +weighted percentage for line coloring and sorting of active processes. +When this value is zero, no line coloring or automatic sorting is performed +for this resource. +.PP +.TP 4 +.B almostcrit +A percentage of the critical percentage to determine if the resource +is almost critical +(see section COLORS in the man-page of the +.I atop +command). +When this value is zero, no line coloring for `almost critical' is +performed. +.PP +.TP 4 +.B colorinfo +Definition of color name for information messages (default: green). +.br +Allowed colors are: red green yellow blue magenta cyan black white. +.PP +.TP 4 +.B colorthread +Definition of color name for thread-specific lines when using +the 'y' option (default: yellow). +.br +Allowed colors are: red green yellow blue magenta cyan black white. +.PP +.TP 4 +.B coloralmost +Definition of color name for almost critical resources (default: cyan). +.br +Allowed colors are: red green yellow blue magenta cyan black white. +.PP +.TP 4 +.B colorcritical +Definition of color name for critical resources (default: red). +.br +Allowed colors are: red green yellow blue magenta cyan black white. +.PP +.TP 4 +.B atopsarflags +A list of default flags for +.B atopsar +can be defined here. The flags that are allowed +are 'S', 'x', 'C', 'M', 'H', 'a', 'A' and the flags to select +one or more specific reports. +.PP +.TP 4 +.B pacctdir +The name of the topdirectory used by the +.B atopacctd +daemon. In this directory, the daemon creates a subdirectory +.B pacct_shadow.d +in which files will be written containing the process accounting records. +The default topdirectory is +.B /var/run +and this option only has to be specified when the +.B atopacctd +daemon is started with an alternative topdirectory as command line argument. +.br +This option can only be specified in the +.B /etc/atoprc +file (on system level)! +.PP +An example of the +.B /etc/atoprc +or +.B ~/.atoprc +file: +.TP 8 +\ +.br +flags\ \ \ \ \ \ \ \ \ Aaf +.br +interval\ \ \ \ \ \ 5 +.br +username +.br +procname +.br +maxlinecpu\ \ \ \ 4 +.br +maxlinedisk\ \ \ 10 +.br +maxlineintf\ \ \ 5 +.br +cpucritperc\ \ \ 80 +.br +almostcrit\ \ \ \ 90 +.br +atopsarflags\ \ CMH +.br +ownprocline\ \ \ PID:50 VGROW:40 RGROW:45 COMMAND-LINE:50 +.br +ownpagline\ \ \ \ PAGSCAN:3 BLANKBOX:0 PAGSWIN:3 PAGSWOUT:7 +.PP +The keywords 'ownprocline' and 'ownpagline' are explained in the +subsequent section. +.SH OWN DEFINITION OF OUTPUT LINE +Via the rcfile it is possible to define the layout of the output lines +yourself, i.e. you can define the layout of one line with process information +with the keyword 'ownprocline' (to be selected with the key 'o' +or the flag \-o) and you can redefine all lines with system information. +.PP +The layout of an output-line can be defined as follows +(notice that this should be specified as one line in the rcfile): +.PP +\ \ \ keyword\ \ \ : [: ...] +.PP +The +.B columnid +is the symbolic name of a column that should shown at this position +in the output line. +.br +The +.B prio +is a positive integer value that determines which columns have precedence +whenever not all specified columns fit into the current screen-width. +The higher value, the higher priority. +.br +The column-specifications should be separated by a space. The order +in which columns have been specified is the order in which they will be +shown, with respect to their priority (columns that do not fit, will be +dropped dynamically). +.PP +A special columnid for system lines is 'BLANKBOX'. This indicates +that an empty column is required at this position. Also this +special columnid is followed by a priority (usually low). +.PP +The following definition can be specified for process information: +.PP +.TP 4 +.B ownprocline +The columnid's are the names of the columns that are shown in the +normal output of the process-related lines that are shown by +.I atop +such as 'PID', 'CMD', 'S', .... +The only exception is the special columnid 'SORTITEM' that is used to +show one of the columns CPU%/DSK%/MEM%/NET%, depending on the chosen +sort-criterium. +.br +An example of a user-defined process line: +.PP +.TP 8 +\ +ownprocline\ \ \ PID:20 PPID:10 SYSCPU:15 USRCPU:15 +VGROW:14 VSIZE:12 RGROW:14 RSIZE:12 ST:8 EXC:7 S:11 SORTITEM:18 CMD:20 +.PP +The following definitions are used internally by +.I atop +as the default system lines (you can redefine each of them in the +rcfile as one line): +.PP +.TP 4 +.B ownsysprcline +Redefinition of line labeled with 'PRC': +.PP +.TP 8 +\ +ownsysprcline\ \ \ PRCSYS:8 PRCUSER:8 BLANKBOX:0 PRCNPROC:7 PRCNZOMBIE:5 PRCCLONES:4 BLANKBOX:0 PRCNNEXIT:6 +.PP +.TP 4 +.B ownallcpuline +Redefinition of line labeled with 'CPU' for total CPU-utilization: +.PP +.TP 8 +\ +ownallcpuline\ \ \ CPUSYS:8 CPUUSER:7 CPUIRQ:4 BLANKBOX:0 CPUIDLE:5 CPUWAIT:6 BLANKBOX:0 CPUSTEAL:1 CPUGUEST:3 +.PP +.TP 4 +.B ownonecpuline +Redefinition of line labeled with 'CPU' for utilization of one CPU: +.PP +.TP 8 +\ +ownonecpuline\ \ \ CPUISYS:8 CPUIUSER:7 CPUIIRQ:4 BLANKBOX:0 CPUIIDLE:5 CPUIWAIT:6 BLANKBOX:0 CPUISTEAL:1 CPUIGUEST:3 +.PP +.TP 4 +.B owncplline +Redefinition of line labeled with 'CPL': +.PP +.TP 8 +\ +owncplline\ \ \ CPLAVG1:4 CPLAVG5:3 CPLAVG15:2 BLANKBOX:0 CPLCSW:6 CPLINTR:5 BLANKBOX:0 CPLNUMCPU:1 +.PP +.TP 4 +.B ownmemline +Redefinition of line labeled with 'MEM': +.PP +.TP 8 +\ +ownmemline\ \ \ MEMTOT:2 MEMFREE:5 MEMCACHE:3 MEMDIRTY:1 MEMBUFFER:3 MEMSLAB:3 BLANKBOX:0 BLANKBOX:0 BLANKBOX:0 BLANKBOX:0 +.PP +.TP 4 +.B ownswpline +Redefinition of line labeled with 'SWP': +.PP +.TP 8 +\ +ownswpline\ \ \ SWPTOT:3 SWPFREE:4 BLANKBOX:0 BLANKBOX:0 BLANKBOX:0 BLANKBOX:0 BLANKBOX:0 BLANKBOX:0 SWPCOMMITTED:5 SWPCOMMITLIM:6 +.PP +.TP 4 +.B ownpagline +Redefinition of line labeled with 'PAG': +.PP +.TP 8 +\ +ownpagline\ \ \ PAGSCAN:3 PAGSTALL:1 BLANKBOX:0 PAGSWIN:4 PAGSWOUT:3 +.PP +.TP 4 +.B owndskline +Redefinition of lines labeled with 'LVM', 'MDD' and 'DSK': +.PP +.TP 8 +\ +owndskline\ \ \ DSKNAME:8 DSKBUSY:7 DSKNREAD:6 DSKNWRITE:6 DSKKBPERRD:4 DSKKBPERWR:4 DSKMBPERSECRD:5 DSKMBPERSECWR:5 DSKAVQUEUE:1 DSKAVIO:5 +.PP +.TP 4 +.B ownnettrline +Redefinition of line labeled with 'NET' for transport: +.PP +.TP 8 +\ +ownnettrline\ \ \ NETTRANSPORT:9 NETTCPI:8 NETTCPO:8 NETUDPI:8 NETUDPO:8 NETTCPACTOPEN:6 NETTCPPASVOPEN:5 NETTCPRETRANS:4 NETTCPINERR:3 NETTCPORESET:20 NETUDPNOPORT:1 NETUDPINERR:3 +.PP +.TP 4 +.B ownnetnetline +Redefinition of line labeled with 'NET' for network: +.PP +.TP 8 +\ +ownnetnetline\ \ \ NETNETWORK:5 NETIPI:4 NETIPO:4 NETIPFRW:4 NETIPDELIV:4 BLANKBOX:0 BLANKBOX:0 BLANKBOX:0 NETICMPIN:1 NETICMPOUT:1 +.PP +.TP 4 +.B ownnetifline +Redefinition of line labeled with 'NET' for interfaces: +.PP +.TP 8 +\ +ownnetifline\ \ \ NETNAME:8 NETPCKI:7 NETPCKO:7 NETSPEEDIN:6 NETSPEEDOUT:6 NETCOLLIS:3 NETMULTICASTIN:2 NETRCVERR:5 NETSNDERR:5 NETRCVDROP:4 NETSNDDROP:4 +.PP +The lines above are shown in the order as shown by +.I atop +in combination with the +.B -f +flag (in a very wide window you should be able to see all of the columns). +.SH SEE ALSO +.B atop(1), +.B atopsar(1), +.B atopacctd(8), +.B netatop(4), +.B netatopd(8), +.B logrotate(8) +.br +.B http://www.atoptool.nl +.SH AUTHOR +Gerlof Langeveld (gerlof.langeveld@atoptool.nl) +.br +JC van Winkel diff --git a/sources/man/atopsar.1 b/sources/man/atopsar.1 new file mode 100644 index 0000000..4f4e1e0 --- /dev/null +++ b/sources/man/atopsar.1 @@ -0,0 +1,1151 @@ +.TH ATOPSAR 1 "March 2017" "Linux" +.SH NAME +.B atopsar +- Advanced System Activity Report (atop related) +.SH SYNOPSIS +.P +.B atopsar +[\-flags...] +[\-r +.I file|date +] [\-R +.I cnt +] [\-b +.I hh:mm +] [\-e +.I hh:mm +] +.br +.B atopsar +[\-flags...] +.I interval +[ +.I samples +] +.P +.SH DESCRIPTION +The program +.I atopsar +can be used to report statistics on system level. +.PP +In the first synopsis line (no sampling interval specified), +.I atopsar +extracts data from a raw logfile that has been recorded previously by +the program +.I atop +(option +.B -w +of the +.I atop +program). +.br +You can specify the name of the logfile with the +.B -r +option of the +.I atopsar +program. +When a daily logfile of +.I atop +is used, named +.B /var/log/atop/atop_YYYYMMDD +(where YYYYMMDD reflects the date), +the required date of the form YYYYMMDD can be specified with the +.B -r +option instead of the filename, or +the symbolic name 'y' can be used for yesterday's daily logfile +(this can be repeated so 'yyyy' indicates the logfile of four days ago). +If the +.B -r +option is not specified at all, today's daily logfile is used by default. +.br +The starting and ending times of the report can be defined using the +options +.B -b +and +.B -e +followed by a time argument of the form hh:mm. +.PP +In the second synopsis line, +.B atopsar +reads actual activity counters from the kernel with the specified +.I interval +(in seconds) and the specified number of +.I samples +(optionally). +When +.B atopsar +is activated in this way it immediately sends the output for every requested +report to standard output. +If only one type of report is requested, the header is printed +once and after every +.I interval +seconds the statistical counters are shown for that period. +If several reports are requested, a header is printed per sample +followed by the statistical counters for that period. +.PP +Some generic flags can be specified to influence the behaviour of the +.B atopsar +program: +.PP +.TP 5 +.B -S +By default the timestamp at the beginning of a line is suppressed if more +lines are shown for one interval. With this flag a timestamp is +given for every output-line (easier for post-processing). +.PP +.TP 5 +.B -a +By default certain resources as disks and network interfaces are only +shown when they were active during the interval. +With this flag all resources of a given type are shown, even if +they were inactive during the interval. +.PP +.TP 5 +.B -x +By default +.B atopsar +only uses colors if output is directed to a terminal (window). +These colors might indicate that a critical occupation percentage has +been reached (red) or has been almost reached (cyan) for a particular +resource. +See the man-page of +.B atop +for a detailed description of this feature (section COLORS). +.br +With the flag +.B -x +the use of colors is suppressed unconditionally. +.PP +.TP 5 +.B -C +By default +.B atopsar +only uses colors if output is directed to a terminal (window). +These colors might indicate that a critical occupation percentage has +been reached (red) or has been almost reached (cyan) for a particular +resource. +See the man-page of +.B atop +for a detailed description of this feature (section COLORS). +.br +With the flag +.B -C +colors will always be used, even if output is not directed to a terminal. +.PP +.TP 5 +.B -M +Use markers at the end of a line to indicate that a critical occupation +percentage has been reached ('*') or has been almost reached ('+') +for particular resources. The marker '*' is similar to the color red +and the marker '+' to the color cyan. See the man-page of +.B atop +for a detailed description of these colors (section COLORS). +.PP +.TP 5 +.B -H +Repeat the header line within a report for every +.I N +detail lines. The value of +.I N +is determined dynamically in case of output to a tty/window (depending +on the number of lines); for output to a file or pipe this value is 23. +.PP +.TP 5 +.B -R +Summarize +.I cnt +samples into one sample. When the logfile contains e.g. samples of 10 minutes, +the use of the flag '\-R 6' shows a report with one sample for every hour. +.PP +Other flags are used to define which reports are required: +.PP +.TP 5 +.B -A +Show all possible reports. +.PP +.TP 5 +.B -c +Report about CPU utilization (in total and per cpu). +.PP +.TP 5 +.B -p +Report about processor-related matters, like load-averages and +hardware interrupts. +.PP +.TP 5 +.B -P +Report about processes. +.PP +.TP 5 +.B -m +Current memory- and swap-occupation. +.PP +.TP 5 +.B -s +Report about paging- and swapping-activity, and overcommitment. +.PP +.TP 5 +.B -l +Report about utilization of logical volumes. +.PP +.TP 5 +.B -f +Report about utilization of multiple devices. +.PP +.TP 5 +.B -d +Report about utilization of disks. +.PP +.TP 5 +.B -n +Report about NFS mounted filesystems on NFS client. +.PP +.TP 5 +.B -j +Report about NFS client activity. +.PP +.TP 5 +.B -J +Report about NFS server activity. +.PP +.TP 5 +.B -i +Report about the network interfaces. +.PP +.TP 5 +.B -I +Report about errors for network-interfaces. +.PP +.TP 5 +.B -w +Report about IP version 4 network traffic. +.PP +.TP 5 +.B -W +Report about errors for IP version 4 traffic. +.PP +.TP 5 +.B -y +General report about ICMP version 4 layer activity. +.PP +.TP 5 +.B -Y +Per-type report about ICMP version 4 layer activity. +.PP +.TP 5 +.B -u +Report about UDP version 4 network traffic. +.PP +.TP 5 +.B -z +Report about IP version 6 network traffic. +.PP +.TP 5 +.B -Z +Report about errors for IP version 6 traffic. +.PP +.TP 5 +.B -k +General report about ICMP version 6 layer activity. +.PP +.TP 5 +.B -K +Per-type report about ICMP version 6 layer activity. +.PP +.TP 5 +.B -U +Report about UDP version 6 network traffic. +.PP +.TP 5 +.B -t +Report about TCP network traffic. +.PP +.TP 5 +.B -T +Report about errors for TCP-traffic. +.PP +.TP 5 +.B -O +Report about top-3 processes consuming most processor capacity. +This report is only available when using a log file (not when specifying +an interval). +.PP +.TP 5 +.B -G +Report about top-3 processes consuming most resident memory. +This report is only available when using a log file (not when specifying +an interval). +.PP +.TP 5 +.B -D +Report about top-3 processes issueing most disk transfers. +This report is only available when using a log file (not when specifying +an interval). +.PP +.TP 5 +.B -N +Report about top-3 processes issueing most IPv4/IPv6 socket transfers. +This report is only available when using a log file (not when specifying +an interval). +.SH OUTPUT DESCRIPTION +Depending on the requested report, a number of columns with +output values are produced. +The values are mostly presented as a number of events per second. +.PP +The output for the flag +.B -c +contains the following columns per cpu: +.TP 12 +.B usr% +Percentage of cpu-time consumed in user mode (program text) for all +active processes running with a nice value of zero (default) or a +negative nice value (which means a higher priority than usual). +The cpu consumption in user mode of processes with a nice value larger +than zero (lower priority) is indicated in the nice%-column. +.TP 12 +.B nice% +Percentage of cpu time consumed in user mode (i.e. program text) for all +processes running witn a nice value larger than zero (which means with a +lower priority than average). +.TP 12 +.B sys% +Percentage of cpu time consumed in system mode (kernel text) for all +active processes. A high percentage usually indicates a lot of system calls +being issued. +.TP 12 +.B irq% +Percentage of cpu time consumed for handling of device interrupts. +.TP 12 +.B softirq% +Percentage of cpu time consumed for soft interrupt handling. +.TP 12 +.B steal% +Percentage of cpu time stolen by other virtual machines +running on the same hardware. +.TP 12 +.B guest% +Percentage of cpu time used by other virtual machines +running on the same hardware (overlaps with usr%/nice%). +.TP 12 +.B wait% +Percentage of unused cpu time while +at least one of the processes in wait-state awaits completion of disk I/O. +.TP 12 +.B idle% +Percentage of unused cpu time because all processes are in a wait-state +but not waiting for disk-I/O. +.PP +The output for the flag +.B -p +contains the following values: +.TP 12 +.B pswch/s +Number of process switches (also called context switches) per second on this +cpu. A process switch occurs at the moment that an active thread (i.e. +the thread using a cpu) enters a wait state or has used its time slice +completely; another thread will then be chosen to use the cpu. +.TP 12 +.B devintr/s +Number of hardware interrupts handled per second on this cpu. +.TP 12 +.B clones/s +The number of new threads started per second. +.TP 12 +.B loadavg1 +Load average reflecting the average number of threads in the runqueue +or in non-interruptible wait state (usually waiting for disk or tape I/O) +during the last minute. +.TP 12 +.B loadavg5 +Load average reflecting the average number of threads in the runqueue +or in non-interruptible wait state (usually waiting for disk or tape I/O) +during the last 5 minutes. +.TP 12 +.B loadavg15 +Load average reflecting the average number of threads in the runqueue +or in non-interruptible wait state (usually waiting for disk or tape I/O) +during the last 15 minutes. +.PP +The output for the flag +.B -P +contains information about the processes and threads: +.TP 12 +.B clones/s +The number of new threads started per second. +.TP 12 +.B pexit/s +.TP 12 +.B curproc +Total number of processes present in the system. +.TP 12 +.B curzomb +Number of zombie processes present in the system. +.TP 12 +.B thrrun +Total number of threads present in the system in state 'running'. +.TP 12 +.B thrslpi +Total number of threads present in the system in +state 'interruptible sleeping'. +.TP 12 +.B thrslpu +Total number of threads present in the system in +state 'uninterruptible sleeping'. +.PP +The output for the flag +.B -m +contains information about the memory- and swap-utilization: +.TP 12 +.B memtotal +Total usable main memory size. +.TP 12 +.B memfree +Available main memory size at this moment (snapshot). +.TP 12 +.B buffers +Main memory used at this moment to cache metadata-blocks (snapshot). +.TP 12 +.B cached +Main memory used at this moment to cache data-blocks (snapshot). +.TP 12 +.B dirty +Amount of memory in the page cache that still has to be flushed to disk +at this moment (snapshot). +.TP 12 +.B slabmem +Main memory used at this moment for dynamically allocated memory +by the kernel (snapshot). +.TP 12 +.B swptotal +Total swap space size at this moment (snapshot). +.TP 12 +.B swpfree +Available swap space at this moment (snapshot). +.PP +The output for the flag +.B -s +contains information about the frequency of swapping: +.TP 12 +.B pagescan/s +Number of scanned pages per second due to the fact +that free memory drops below a particular threshold. +.TP 12 +.B swapin/s +The number of memory-pages the system read from the swap-device per second. +.TP 12 +.B swapout/s +The number of memory-pages the system wrote to the swap-device per second. +.TP 12 +.B commitspc +The committed virtual memory space i.e. +the reserved virtual space for all allocations of +private memory space for processes. +.TP 12 +.B commitlim +The maximum limit for the committed space, which is by default swap size +plus 50% of memory size. +The kernel only verifies whether the committed space exceeds the limit +if strict overcommit handling is configured (vm.overcommit_memory is 2). +.PP +The output for the flags +.B -l +(LVM), +.B -f +(MD), and +.B -d +(hard disk) contains the following columns per active unit: +.TP 12 +.B disk +Name. +.TP 12 +.B busy +Busy-percentage of the unit (i.e. the portion of time that the +device was busy handling requests). +.TP 12 +.B read/s +Number of read-requests issued per second on this unit. +.TP 12 +.B KB/read +Average number of Kbytes transferred per read-request for this unit. +.TP 12 +.B writ/s +Number of write-requests issued per second on this unit. +.TP 12 +.B KB/writ +Average number of Kbytes transferred per write-request for this unit. +.TP 12 +.B avque +Average number of requests outstanding in the queue during the time +that the unit is busy. +.TP 12 +.B avserv +Average number of milliseconds needed by a request on this unit +(seek, latency and data-transfer). +.PP +The output for the flag +.B -n +contains information about activity on NFS mounted filesystems (client): +.TP 12 +.B mounted_device +Mounted device containing server name and server directory being mounted. +.TP 12 +.B physread/s +Kilobytes data physically read from the NFS server by processes running +on the NFS client. +.TP 12 +.B KBwrite/s +Kilobytes data physically written to the NFS server by processes running +on the NFS client. +.br +When the NFS filesystem was mounted during the interval, the state 'M' is +shown. +.PP +The output for the flag +.B -j +contains information about NFS client activity: +.TP 12 +.B rpc/s +Number of RPC calls per second issued to NFS server(s). +.TP 12 +.B rpcread/s +Number of read RPC calls per second issued to NFS server(s). +.TP 12 +.B rpcwrite/s +Number of write RPC calls per second issued to NFS server(s). +.TP 12 +.B retrans/s +Number of retransmitted RPC calls per second. +.TP 12 +.B autrefresh/s +Number of authorization refreshes per second. +.PP +The output for the flag +.B -J +contains information about NFS server activity: +.TP 12 +.B rpc/s +Number of RPC calls per second received from NFS client(s). +.TP 12 +.B rpcread/s +Number of read RPC calls per second received from NFS client(s). +.TP 12 +.B rpcwrite/s +Number of write RPC calls per second received from NFS client(s). +.TP 12 +.B MBcr/s +Number of Megabytes per second returned to read requests by clients. +.TP 12 +.B MBcw/s +Number of Megabytes per second passed in write requests by clients. +.TP 12 +.B nettcp/s +Number of requests per second handled via TCP. +.TP 12 +.B netudp/s +Number of requests per second handled via UDP. +.PP +The output for the flag +.B -i +provides information about utilization of network interfaces: +.TP 12 +.B interf +Name of interface. +.TP 12 +.B busy +Busy percentage for this interface. +If the linespeed of this interface could not be determined +(for virtual interfaces or in case that +.B atop +or +.B atopsar +had no root-privileges), a question mark is shown. +.TP 12 +.B ipack/s +Number of packets received from this interface per second. +.TP 12 +.B opack/s +Number of packets transmitted to this interface per second. +.TP 12 +.B iKbyte/s +Number of Kbytes received from this interface per second. +.TP 12 +.B oKbyte/s +Number of Kbytes transmitted via this interface per second. +.TP 12 +.B imbps/s +Effective number of megabits received per second. +.TP 12 +.B ombps/s +Effective number of megabits transmitted per second. +.TP 12 +.B maxmbps/s +Linespeed as number of megabits per second. +If the linespeed could not be determined (for virtual interfaces +or in case that +.B atop +or +.B atopsar +had no root-privileges), value 0 is shown. +.br +The linespeed is followed by the indication 'f' (full duplex) +or 'h' (half duplex). +.PP +The output for the flag +.B -I +provides information about the failures that were detected for +network interfaces: +.TP 12 +.B interf +Name of interface. +.TP 12 +.B ierr/s +Number of bad packets received from this interface per second. +.TP 12 +.B oerr/s +Number of times that packet transmission to this interface failed per second. +.TP 12 +.B coll/s +Number of collisions encountered per second while transmitting packets. +.TP 12 +.B idrop/s +Number of received packets dropped per second due to lack of buffer-space +in the local system. +.TP 12 +.B odrop/s +Number of transmitted packets dropped per second due to lack of buffer-space +in the local system. +.TP 12 +.B iframe/s +Number of frame alignment-errors encountered per second on received packets. +.TP 12 +.B ocarrier/s +Number of carrier-errors encountered per second on transmitted packets. +.PP +The output for the flag +.B -w +provides information about the utilization of the IPv4-layer +(formal SNMP-names between brackets): +.TP 12 +.B inrecv/s +Number of IP datagrams received from interfaces per second, including +those received in error (ipInReceives). +.TP 12 +.B outreq/s +Number of IP datagrams that local higher-layer protocols +supplied to IP in requests for transmission per second (ipOutRequests). +.TP 12 +.B indeliver/s +Number of received IP datagrams that have been successfully delivered to +higher protocol-layers per second (ipInDelivers). +.TP 12 +.B forward/s +Number of received IP datagrams per second for which this entity was not +their final IP destination, as a result of which an attempt was made to +forward (ipForwDatagrams). +.TP 12 +.B reasmok/s +Number of IP datagrams successfully reassembled per second (ipReasmOKs). +.TP 12 +.B fragcreat/s +Number of IP datagram fragments generated per second at this entity +(ipFragCreates). +.PP +The output for the flag +.B -W +provides information about the failures that were detected in +the IPv4-layer (formal SNMP-names between brackets): +.TP 12 +.B in: dsc/s +Number of input IP datagrams per second for which no problems were encountered +to prevent their continued processing but that were discarded, e.g. for lack +of buffer space (ipInDiscards). +.TP 12 +.B in: hder/s +Number of input IP datagrams per second discarded due to errors +in the IP header (ipInHdrErrors). +.TP 12 +.B in: ader/s +Number of input IP datagrams per second discarded because the IP address +in the destination field was not valid to be received by this entity +(ipInAddrErrors). +.TP 12 +.B in: unkp/s +Number of inbound packets per second that were discarded because of an +unknown or unsupported protocol (ipInUnknownProtos). +.TP 12 +.B in: ratim/s +Number of timeout-situations per second while other fragments were +expected for successful reassembly (ipReasmTimeout). +.TP 12 +.B in: rfail/s +Number of failures detected per second by the IP reassembly algorithm +(ipReasmFails). +.TP 12 +.B out: dsc/s +Number of output IP datagrams per second for which no problems were +encountered to prevent their continued processing but that were +discarded, e.g. for lack of buffer space (ipOutDiscards). +.TP 12 +.B out: nrt/s +Number of IP datagrams per second discarded because no route could be found +(ipOutNoRoutes). +.PP +The output for the flag +.B -y +provides information about the general utilization of the ICMPv4-layer and +some information per type of ICMP-message +(formal SNMP-names between brackets): +.TP 12 +.B intot/s +Number of ICMP messages (any type) received per second at this entity +(icmpInMsgs). +.TP 12 +.B outtot/s +Number of ICMP messages (any type) transmitted per second from this entity +(icmpOutMsgs). +.TP 12 +.B inecho/s +Number of ICMP Echo (request) messages received per second +(icmpInEchos). +.TP 12 +.B inerep/s +Number of ICMP Echo-Reply messages received per second +(icmpInEchoReps). +.TP 12 +.B otecho/s +Number of ICMP Echo (request) messages transmitted per second +(icmpOutEchos). +.TP 12 +.B oterep/s +Number of ICMP Echo-Reply messages transmitted per second +(icmpOutEchoReps). +.PP +The output for the flag +.B -Y +provides information about other types of ICMPv4-messages +(formal SNMP-names between brackets): +.TP 12 +.B ierr/s +Number of ICMP messages received per second but determined to have +ICMP-specific errors (icmpInErrors). +.TP 12 +.B isq/s +Number of ICMP Source Quench messages received per second +(icmpInSrcQuenchs). +.TP 12 +.B ird/s +Number of ICMP Redirect messages received per second +(icmpInRedirects). +.TP 12 +.B idu/s +Number of ICMP Destination Unreachable messages received per second +(icmpInDestUnreachs). +.TP 12 +.B ite/s +Number of ICMP Time Exceeded messages received per second +(icmpOutTimeExcds). +.TP 12 +.B oerr/s +Number of ICMP messages transmitted per second but determined to have +ICMP-specific errors (icmpOutErrors). +.TP 12 +.B osq/s +Number of ICMP Source Quench messages transmitted per second +(icmpOutSrcQuenchs). +.TP 12 +.B ord/s +Number of ICMP Redirect messages transmitted per second +(icmpOutRedirects). +.TP 12 +.B odu/s +Number of ICMP Destination Unreachable messages transmitted per second +(icmpOutDestUnreachs). +.TP 12 +.B ote/s +Number of ICMP Time Exceeded messages transmitted per second +(icmpOutTimeExcds). +.PP +The output for the flag +.B -u +provides information about the utilization of the UDPv4-layer +(formal SNMP-names between brackets): +.TP 12 +.B indgram/s +Number of UDP datagrams per second delivered to UDP users (udpInDatagrams). +.TP 12 +.B outdgram/s +Number of UDP datagrams transmitted per second from this entity +(udpOutDatagrams). +.TP 12 +.B inerr/s +Number of received UDP datagrams per second that could not be delivered +for reasons other than the lack of an application at the destination port +(udpInErrors). +.TP 12 +.B noport/s +Number of received UDP datagrams per second for which there was +no application at the destination port (udpNoPorts). +.PP +The output for the flag +.B -z +provides information about the utilization of the IPv6-layer +(formal SNMP-names between brackets): +.TP 12 +.B inrecv/s +Number of input IPv6-datagrams received from interfaces per second, including +those received in error (ipv6IfStatsInReceives). +.TP 12 +.B outreq/s +Number of IPv6-datagrams per second that local higher-layer protocols +supplied to IP in requests for transmission (ipv6IfStatsOutRequests). +This counter does not include any forwarded datagrams. +.TP 12 +.B inmc/s +Number of multicast packets per second that have been received by the +interface (ipv6IfStatsInMcastPkts). +.TP 12 +.B outmc/s +Number of multicast packets per second that have been transmitted to the +interface (ipv6IfStatsOutMcastPkts). +.TP 12 +.B indeliv/s +Number of IP datagrams successfully delivered per second to +IPv6 user-protocols, including ICMP (ipv6IfStatsInDelivers). +.TP 12 +.B reasmok/s +Number of IPv6 datagrams successfully reassembled per second +(ipv6IfStatsReasmOKs). +.TP 12 +.B fragcre/s +Number of IPv6 datagram fragments generated per second at this entity +(ipv6IfStatsOutFragCreates). +.PP +The output for the flag +.B -Z +provides information about the failures that were detected in the IPv6-layer +(formal SNMP-names between brackets): +.TP 12 +.B in: dsc/s +Number of input IPv6 datagrams per second for which no problems +were encountered to prevent their continued processing but that +were discarded, e.g. for lack of buffer space (ipv6IfStatsInDiscards). +.TP 12 +.B in: hder/s +Number of input datagrams per second discarded due to errors in the +IPv6 header (ipv6IfStatsInHdrErrors). +.TP 12 +.B in: ader/s +Number of input datagrams per second discarded because the IPv6 address +in the destination field was not valid to be received by this entity +(ipv6IfStatsInAddrErrors). +.TP 12 +.B in: unkp/s +Number of locally-addressed datagrams per second that were discarded because +of an unknown or unsupported protocol (ipv6IfStatsInUnknownProtos). +.TP 12 +.B in: ratim/s +Number of timeout-situations per second while other IPv6 fragments were +expected for successful reassembly (ipv6ReasmTimeout). +.TP 12 +.B in: rfail/s +Number of failures detected per second by the IPv6 reassembly-algorithm +(ipv6IfStatsReasmFails). +.TP 12 +.B out: dsc/s +Number of output IPv6 datagrams per second for which no problems +were encountered to prevent their continued processing but that +were discarded, e.g. for lack of buffer space (ipv6IfStatsOutDiscards). +.TP 12 +.B out: nrt/s +Number of IPv6 datagrams per second discarded because no route could be found +(ipv6IfStatsInNoRoutes). +.PP +The output for the flag +.B -k +provides information about the general utilization of the ICMPv6-layer and +some information per type of ICMP-message +(formal SNMP-names between brackets): +.TP 12 +.B intot/s +Number of ICMPv6 messages (any type) received per second at the interface +(ipv6IfIcmpInMsgs). +.TP 12 +.B outtot/s +Number of ICMPv6 messages (any type) transmitted per second from this entity +(ipv6IfIcmpOutMsgs). +.TP 12 +.B inerr/s +Number of ICMPv6 messages received per second that had ICMP-specific +errors, such as bad ICMP checksums, bad length, etc (ipv6IfIcmpInErrors). +.TP 12 +.B innsol/s +Number of ICMP Neighbor Solicit messages received per second +(ipv6IfIcmpInNeighborSolicits). +.TP 12 +.B innadv/s +Number of ICMP Neighbor Advertisement messages received per second +(ipv6IfIcmpInNeighborAdvertisements). +.TP 12 +.B otnsol/s +Number of ICMP Neighbor Solicit messages transmitted per second +(ipv6IfIcmpOutNeighborSolicits). +.TP 12 +.B otnadv/s +Number of ICMP Neighbor Advertisement messages transmitted per second +(ipv6IfIcmpOutNeighborAdvertisements). +.PP +The output for the flag +.B -K +provides information about other types of ICMPv6-messages +(formal SNMP-names between brackets): +.TP 12 +.B iecho/s +Number of ICMP Echo (request) messages received per second +(ipv6IfIcmpInEchos). +.TP 12 +.B ierep/s +Number of ICMP Echo-Reply messages received per second +(ipv6IfIcmpInEchoReplies). +.TP 12 +.B oerep/s +Number of ICMP Echo-Reply messages transmitted per second +(ipv6IfIcmpOutEchoReplies). +.TP 12 +.B idu/s +Number of ICMP Destination Unreachable messages received per second +(ipv6IfIcmpInDestUnreachs). +.TP 12 +.B odu/s +Number of ICMP Destination Unreachable messages transmitted per second +(ipv6IfIcmpOutDestUnreachs). +.TP 12 +.B ird/s +Number of ICMP Redirect messages received per second +(ipv6IfIcmpInRedirects). +.TP 12 +.B ord/s +Number of ICMP Redirect messages transmitted per second +(ipv6IfIcmpOutRedirect). +.TP 12 +.B ite/s +Number of ICMP Time Exceeded messages received per second +(ipv6IfIcmpInTimeExcds). +.TP 12 +.B ote/s +Number of ICMP Time Exceeded messages transmitted per second +(ipv6IfIcmpOutTimeExcds). +.PP +The output for the flag +.B -U +provides information about the utilization of the UDPv6-layer +(formal SNMP-names between brackets): +.TP 12 +.B indgram/s +Number of UDPv6 datagrams per second delivered to UDP users (udpInDatagrams), +.TP 12 +.B outdgram/s +Number of UDPv6 datagrams transmitted per second from this entity +(udpOutDatagrams), +.TP 12 +.B inerr/s +Number of received UDPv6 datagrams per second that could not be delivered +for reasons other than the lack of an application at the destination port +(udpInErrors). +.TP 12 +.B noport/s +Number of received UDPv6 datagrams per second for which there was +no application at the destination port (udpNoPorts). +.PP +The output for the flag +.B -t +provides information about the utilization of the TCP-layer +(formal SNMP-names between brackets): +.TP 12 +.B insegs/s +Number of received segments per second, including those received in error +(tcpInSegs). +.TP 12 +.B outsegs/s +Number of transmitted segments per second, excluding those containing only +retransmitted octets (tcpOutSegs). +.TP 12 +.B actopen/s +Number of active opens per second that have been supported by this entity +(tcpActiveOpens). +.TP 12 +.B pasopen/s +Number of passive opens per second that have been supported by this entity +(tcpPassiveOpens). +.TP 12 +.B nowopen +Number of connections currently open (snapshot), for which the state +is either ESTABLISHED or CLOSE-WAIT (tcpCurrEstab). +.PP +The output for the flag +.B -T +provides information about the failures that were detected in the TCP-layer +(formal SNMP-names between brackets): +.TP 12 +.B inerr/s +Number of received segments per second received in error (tcpInErrs). +.TP 12 +.B retrans/s +Number of retransmitted segments per second (tcpRetransSegs). +.TP 12 +.B attfail/s +Number of failed connection attempts per second that have occurred at this +entity (tcpAttemptFails). +.TP 12 +.B estabreset/s +Number of resets per second that have occurred at this entity +(tcpEstabResets). +.TP 12 +.B outreset/s +Number of transmitted segments per second containing the RST flag +(tcpOutRsts). +.PP +The output for the flag +.B -O +provides information about the top-3 of processes with the highest +processor consumption: +.TP 12 +.B pid +Process-id (if zero, the process has exited while the +pid could not be determined). +.TP 12 +.B command +The name of the process. +.TP 12 +.B cpu% +The percentage of cpu-capacity being consumed. +This value can exceed 100% for a multithreaded process running on +a multiprocessor machine. +.PP +The output for the flag +.B -G +provides information about the top-3 of processes with the highest +memory consumption: +.TP 12 +.B pid +Process-id (if zero, the process has exited while the +pid could not be determined). +.TP 12 +.B command +The name of the process. +.TP 12 +.B mem% +The percentage of resident memory-utilization by this process. +.PP +The output for the flag +.B -D +provides information about the top-3 of processes that issue +the most read and write accesses to disk: +.TP 12 +.B pid +Process-id (if zero, the process has exited while the +pid could not be determined). +.TP 12 +.B command +The name of the process. +.TP 12 +.B dsk% +The percentage of read and write accesses related to the total +number of read and write accesses issued on disk by all processes, +so a high percentage does not imply a high disk load on system level. +.PP +The output for the flag +.B -N +provides information about the top-3 of processes that issue +the most socket transfers for IPv4/IPv6: +.TP 12 +.B pid +Process-id (if zero, the process has exited while the +pid could not be determined). +.TP 12 +.B command +The name of the process. +.TP 12 +.B net% +The percentage of socket transfers related to the total +number of transfers issued by all processes, +so a high percentage does not imply a high network load on system level. +.SH EXAMPLES +To see today's cpu-activity so far +(supposed that +.B atop +is logging in the background): +.PP +.TP 12 +.B \ atopsar +.PP +To see the memory occupation for June 5, 2012 between 10:00 and 12:30 +(supposed that +.B atop +has been logging daily in the background): +.PP +.TP 12 +.B \ atopsar -m -r /var/log/atop_20120605 -b 10:00 -e 12:30 +.br +\ +.br + or +.TP 12 +.B \ atopsar -m -r 20120605 -b 10:00 -e 12:30 +.br +\ +.br + or, suppose it is June 8, 2012 at this moment +.TP 12 +.B \ atopsar -m -r yyy -b 10:00 -e 12:30 +.PP +Write a logfile with +.B atop +to record the system behaviour for 30 minutes +(30 samples of one minute) and produce all available reports +afterwards: +.PP +.TP 12 +.B \ atop -w /tmp/atoplog 60 30 +.TP 12 +.B \ atopsar -A -r /tmp/atoplog +.PP +To watch TCP activity evolve for ten minutes (10 samples with sixty seconds +interval): +.PP +.TP 12 +.B \ atopsar -t 60 10 +.PP +To watch the header-lines ('_' as last character) of all reports with only +the detail-lines showing critical resource consumption (marker '*' or '+' +as last character): +.PP +.TP 12 +.B \ atopsar -AM | grep '[_*+]$' +.PP +.SH FILES +.PP +.TP 5 +.B /etc/atoprc +Configuration file containing system-wide default values (mainly flags). +See related man-page. +.PP +.TP 5 +.B ~/.atoprc +Configuration file containing personal default values (mainly flags). +See related man-page. +.PP +.TP 5 +.BI /var/log/atop/atop_ YYYYMMDD +Daily data file, where +.I YYYYMMDD +are digits representing the date. +.SH SEE ALSO +.B atop(1), +.B atoprc(5), +.B atopacctd(8), +.B netatop(4), +.B netatopd(8) +.br +.B http://www.atoptool.nl +.SH AUTHOR +Gerlof Langeveld (gerlof.langeveld@atoptool.nl) diff --git a/sources/netatop.h b/sources/netatop.h new file mode 100644 index 0000000..a14e6fa --- /dev/null +++ b/sources/netatop.h @@ -0,0 +1,47 @@ +#define COMLEN 16 + +struct taskcount { + unsigned long long tcpsndpacks; + unsigned long long tcpsndbytes; + unsigned long long tcprcvpacks; + unsigned long long tcprcvbytes; + + unsigned long long udpsndpacks; + unsigned long long udpsndbytes; + unsigned long long udprcvpacks; + unsigned long long udprcvbytes; + + /* space for future extensions */ +}; + +struct netpertask { + pid_t id; // tgid or tid (depending on command) + unsigned long btime; + char command[COMLEN]; + + struct taskcount tc; +}; + + +/* +** getsocktop commands +*/ +#define NETATOP_BASE_CTL 15661 + +// just probe if the netatop module is active +#define NETATOP_PROBE (NETATOP_BASE_CTL) + +// force garbage collection to make finished processes available +#define NETATOP_FORCE_GC (NETATOP_BASE_CTL+1) + +// wait until all finished processes are read (blocks until done) +#define NETATOP_EMPTY_EXIT (NETATOP_BASE_CTL+2) + +// get info for finished process (blocks until available) +#define NETATOP_GETCNT_EXIT (NETATOP_BASE_CTL+3) + +// get counters for thread group (i.e. process): input is 'id' (pid) +#define NETATOP_GETCNT_TGID (NETATOP_BASE_CTL+4) + +// get counters for thread: input is 'id' (tid) +#define NETATOP_GETCNT_PID (NETATOP_BASE_CTL+5) diff --git a/sources/netatopd.h b/sources/netatopd.h new file mode 100644 index 0000000..c1ba8e0 --- /dev/null +++ b/sources/netatopd.h @@ -0,0 +1,12 @@ +#define SEMAKEY 1541961 + +#define NETEXITFILE "/var/run/netatop.log" +#define MYMAGIC (unsigned int) 0xfeedb0b0 + +struct naheader { + u_int32_t magic; // magic number MYMAGIC + u_int32_t curseq; // sequence number of last netpertask + u_int16_t hdrlen; // length of this header + u_int16_t ntplen; // length of netpertask structure + pid_t mypid; // PID of netatopd itself +}; diff --git a/sources/netatopif.c b/sources/netatopif.c new file mode 100644 index 0000000..30c3cb3 --- /dev/null +++ b/sources/netatopif.c @@ -0,0 +1,531 @@ +/* +** ATOP - System & Process Monitor +** +** The program 'atop' offers the possibility to view the activity of +** the system on system-level as well as process-level. +** +** This source-file contains functions to interface with the netatop +** module in the kernel. That module keeps track of network activity +** per process and thread. +** ================================================================ +** Author: Gerlof Langeveld +** E-mail: gerlof.langeveld@atoptool.nl +** Date: August/September 2012 +** +** This program is free software; you can redistribute it and/or modify it +** under the terms of the GNU General Public License as published by the +** Free Software Foundation; either version 2, or (at your option) any +** later version. +** +** This program is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +** See the GNU General Public License for more details. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "atop.h" +#include "photoproc.h" + +#include "netatop.h" +#include "netatopd.h" + +static int netsock = -1; +static int netexitfd = -1; +static struct naheader *nahp; +static int semid = -1; +static unsigned long lastseq; + +/* +** storage of last exited tasks read from exitfile +** every exitstore struct is registered in hash buckets, +** by its pid or by its begintime +*/ +struct exitstore { + struct exitstore *next; + unsigned char isused; + struct netpertask npt; +}; + +#define NHASH 1024 // must be power of two! +#define HASHCALC(x) ((x)&(NHASH-1)) + +static struct exitstore *esbucket[NHASH]; + +static struct exitstore *exitall; +static int exitnum; +static char exithash; + +static void fill_networkcnt(struct tstat *, struct tstat *, + struct exitstore *); + +/* +** open a raw socket to the IP layer (root privs required) +*/ +void +netatop_ipopen(void) +{ + netsock = socket(PF_INET, SOCK_RAW, IPPROTO_RAW); +} + +/* +** check if at this moment the netatop kernel module is loaded and +** the netatopd daemon is active +*/ +void +netatop_probe(void) +{ + struct sembuf semdecr = {1, -1, SEM_UNDO}; + socklen_t sl = 0; + struct stat exstat; + + /* + ** check if IP socket is open + */ + if (netsock == -1) + return; + + /* + ** probe if the netatop module is active + */ + if ( getsockopt(netsock, SOL_IP, NETATOP_PROBE, NULL, &sl) != 0) + { + supportflags &= ~NETATOP; + supportflags &= ~NETATOPD; + return; + } + + // set appropriate support flag + supportflags |= NETATOP; + + /* + ** check if the netatopd daemon is active to register exited tasks + ** and decrement semaphore to indicate that we want to subscribe + */ + if (semid == -1) + { + if ( (semid = semget(SEMAKEY, 0, 0)) == -1 || + semop(semid, &semdecr, 1) == -1 ) + { + supportflags &= ~NETATOPD; + return; + } + } + + if (semctl(semid, 0, GETVAL, 0) != 1) + { + supportflags &= ~NETATOPD; + return; + } + + /* + ** check if exitfile still open and not removed by netatopd + */ + if (netexitfd != -1) + { + if ( fstat(netexitfd, &exstat) == 0 && + exstat.st_nlink > 0 ) // not removed + { + supportflags |= NETATOPD; + return; + } + else + { + (void) close(netexitfd); + + if (nahp) + munmap(nahp, sizeof *nahp); + + netexitfd = -1; + nahp = NULL; + } + } + + /* + ** open file with compressed stats of exited tasks + ** and (re)mmap the start record, mainly to obtain current sequence + */ + if (netexitfd == -1) + { + if ( (netexitfd = open(NETEXITFILE, O_RDONLY, 0)) == -1) + { + supportflags &= ~NETATOPD; + return; + } + } + + if ( (nahp = mmap((void *)0, sizeof *nahp, PROT_READ, MAP_SHARED, + netexitfd, 0)) == (void *) -1) + { + (void) close(netexitfd); + netexitfd = -1; + nahp = NULL; + supportflags &= ~NETATOPD; + return; + } + + /* + ** if this is a new incarnation of the netatopd daemon, + ** position seek pointer on first task that is relevant to us + ** and remember last sequence number to know where to start + */ + (void) lseek(netexitfd, 0, SEEK_END); + + lastseq = nahp->curseq; + + // set appropriate support flag + supportflags |= NETATOPD; +} + +void +netatop_signoff(void) +{ + struct sembuf semincr = {1, +1, SEM_UNDO}; + + if (netsock == -1 || nahp == NULL) + return; + + if (supportflags & NETATOPD) + { + regainrootprivs(); + + (void) semop(semid, &semincr, 1); + + kill(nahp->mypid, SIGHUP); + + if (! droprootprivs()) + cleanstop(42); + + (void) munmap(nahp, sizeof *nahp); + (void) close(netexitfd); + } +} + +/* +** read network counters for one existing task +** (type 'g' for thread group or type 't' for thread) +*/ +void +netatop_gettask(pid_t id, char type, struct tstat *tp) +{ + struct netpertask npt; + socklen_t socklen = sizeof npt; + int cmd = (type == 'g' ? + NETATOP_GETCNT_TGID : NETATOP_GETCNT_PID); + + /* + ** if kernel module netatop not active on this system, skip call + */ + if (!(supportflags & NETATOP) ) { + memset(&tp->net, 0, sizeof tp->net); + return; + } + + /* + ** get statistics of this process/thread + */ + npt.id = id; + + regainrootprivs(); + + if (getsockopt(netsock, SOL_IP, cmd, &npt, &socklen) != 0) { + memset(&tp->net, 0, sizeof tp->net); + + if (! droprootprivs()) + cleanstop(42); + + if (errno == ENOPROTOOPT || errno == EPERM) + { + supportflags &= ~NETATOP; + supportflags &= ~NETATOPD; + close(netsock); + netsock = -1; + } + + return; + } + + if (! droprootprivs()) + cleanstop(42); + + /* + ** statistics available: fill counters + */ + tp->net.tcpsnd = npt.tc.tcpsndpacks; + tp->net.tcprcv = npt.tc.tcprcvpacks; + tp->net.tcpssz = npt.tc.tcpsndbytes; + tp->net.tcprsz = npt.tc.tcprcvbytes; + + tp->net.udpsnd = npt.tc.udpsndpacks; + tp->net.udprcv = npt.tc.udprcvpacks; + tp->net.udpssz = npt.tc.udpsndbytes; + tp->net.udprsz = npt.tc.udprcvbytes; +} + +/* +** read all exited processes that have been added to the exitfile +** and store them into memory +*/ +unsigned int +netatop_exitstore(void) +{ + socklen_t socklen = 0, nexitnet, sz, nr=0; + unsigned long uncomplen; + unsigned char nextsize; + unsigned char readbuf[nahp->ntplen+100]; + unsigned char databuf[nahp->ntplen]; + struct netpertask *tmp = (struct netpertask *)databuf; + struct exitstore *esp; + + regainrootprivs(); + + /* + ** force garbage collection: + ** netatop module builds new list of exited processes that + ** can be read by netatopd and written to exitfile + */ + if (getsockopt(netsock, SOL_IP, NETATOP_FORCE_GC, NULL, &socklen)!=0) { + if (! droprootprivs()) + cleanstop(42); + + if (errno == ENOPROTOOPT || errno == EPERM) + { + supportflags &= ~NETATOP; + supportflags &= ~NETATOPD; + close(netsock); + netsock = -1; + } + + return 0; + } + + /* + ** wait until list of exited processes is read by netatopd + ** and available to be read by atop + */ + if (getsockopt(netsock, SOL_IP, NETATOP_EMPTY_EXIT, 0, &socklen) !=0) { + if (! droprootprivs()) + cleanstop(42); + + if (errno == ENOPROTOOPT || errno == EPERM) + { + supportflags &= ~NETATOP; + supportflags &= ~NETATOPD; + close(netsock); + netsock = -1; + } + + return 0; + } + + if (! droprootprivs()) + cleanstop(42); + + /* + ** verify how many exited processes are available to be read + ** from the exitfile + */ + nexitnet = nahp->curseq - lastseq; + lastseq = nahp->curseq; + + /* + ** allocate storage for all exited processes + */ + exitall = malloc(nexitnet * sizeof(struct exitstore)); + + ptrverify(exitall, "Malloc failed for %d exited netprocs\n", nexitnet); + + memset(exitall, 0, nexitnet * sizeof(struct exitstore)); + + esp = exitall; + + /* + ** read next byte from exitfile that specifies the length + ** of the next record + */ + if ( read(netexitfd, &nextsize, 1) != 1) + return 0; + + /* + ** read the next record and (if possible) the byte specifying + ** the size of the next record + */ + while ( (sz = read(netexitfd, readbuf, nextsize+1)) >= nextsize) + { + /* + ** decompress record and store it + */ + uncomplen = nahp->ntplen; + + if (nahp->ntplen <= sizeof(struct netpertask)) + { + (void) uncompress((Byte *)&(esp->npt), &uncomplen, + readbuf, nextsize); + } + else + { + (void) uncompress((Byte *)databuf, &uncomplen, + readbuf, nextsize); + esp->npt = *tmp; + } + + esp++; + nr++; + + /* + ** check if we have read all records + */ + if (nr == nexitnet) + { + /* + ** if we have read one byte too many: + ** reposition seek pointer + */ + if (sz > nextsize) + (void) lseek(netexitfd, -1, SEEK_CUR); + + break; + } + + /* + ** prepare reading next record + */ + if (sz > nextsize) + nextsize = readbuf[nextsize]; + else + break; // unexpected: more requested than available + } + + exitnum = nr; + + return nr; +} + +/* +** remove all stored exited processes from the hash bucket list +*/ +void +netatop_exiterase(void) +{ + free(exitall); + memset(esbucket, 0, sizeof esbucket); + exitnum = 0; +} + +/* +** add all stored tasks to a hash bucket, either +** by pid (argument 'p') or by begintime (argument 'b') +*/ +void +netatop_exithash(char hashtype) +{ + int i, h; + struct exitstore *esp; + + for (i=0, esp=exitall; i < exitnum; i++, esp++) + { + if (hashtype == 'p') + h = HASHCALC(esp->npt.id); + else + h = HASHCALC(esp->npt.btime); + + esp->next = esbucket[h]; + esbucket[h] = esp; + } + + exithash = hashtype; +} + +/* +** search for relevant exited network task and +** update counters in tstat struct +*/ +void +netatop_exitfind(unsigned long key, struct tstat *dev, struct tstat *pre) +{ + int h = HASHCALC(key); + struct exitstore *esp; + + /* + ** if bucket empty, forget about it + */ + if ( (esp = esbucket[h]) == NULL) + return; + + /* + ** search thru hash bucket list + */ + for (; esp; esp=esp->next) + { + switch (exithash) + { + case 'p': // search by PID + if (key != esp->npt.id) + continue; + + /* + ** correct PID found + */ + fill_networkcnt(dev, pre, esp); + break; + + case 'b': // search by begintime + if (esp->isused) + continue; + + if (key != esp->npt.btime) + continue; + + /* + ** btime is okay; additional checks required + */ + if ( strcmp(esp->npt.command, pre->gen.name) != 0) + continue; + + if (esp->npt.tc.tcpsndpacks < pre->net.tcpsnd || + esp->npt.tc.tcpsndbytes < pre->net.tcpssz || + esp->npt.tc.tcprcvpacks < pre->net.tcprcv || + esp->npt.tc.tcprcvbytes < pre->net.tcprsz || + esp->npt.tc.udpsndpacks < pre->net.udpsnd || + esp->npt.tc.udpsndbytes < pre->net.udpssz || + esp->npt.tc.udprcvpacks < pre->net.udprcv || + esp->npt.tc.udprcvbytes < pre->net.udprsz ) + continue; + + esp->isused = 1; + + fill_networkcnt(dev, pre, esp); + break; + } + } +} + +static void +fill_networkcnt(struct tstat *dev, struct tstat *pre, struct exitstore *esp) +{ + dev->net.tcpsnd = esp->npt.tc.tcpsndpacks - pre->net.tcpsnd; + dev->net.tcpssz = esp->npt.tc.tcpsndbytes - pre->net.tcpssz; + dev->net.tcprcv = esp->npt.tc.tcprcvpacks - pre->net.tcprcv; + dev->net.tcprsz = esp->npt.tc.tcprcvbytes - pre->net.tcprsz; + + dev->net.udpsnd = esp->npt.tc.udpsndpacks - pre->net.udpsnd; + dev->net.udpssz = esp->npt.tc.udpsndbytes - pre->net.udpssz; + dev->net.udprcv = esp->npt.tc.udprcvpacks - pre->net.udprcv; + dev->net.udprsz = esp->npt.tc.udprcvbytes - pre->net.udprsz; +} diff --git a/sources/netlink.c b/sources/netlink.c new file mode 100644 index 0000000..6f84978 --- /dev/null +++ b/sources/netlink.c @@ -0,0 +1,253 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* +** generic macro's +*/ +#define GENLMSG_DATA(glh) ((void *)(NLMSG_DATA(glh) + GENL_HDRLEN)) +#define GENLMSG_PAYLOAD(glh) (NLMSG_PAYLOAD(glh, 0) - GENL_HDRLEN) +#define NLA_DATA(na) ((void *)((char*)(na) + NLA_HDRLEN)) +#define NLA_PAYLOAD(len) (len - NLA_HDRLEN) + +/* +** function prototypes +*/ +static int nlsock_open(void); +static int nlsock_sendcmd(int, __u16, __u32, __u8, __u16, void *, int); +static int nlsock_getfam(int); +static int getnumcpu(void); + +/* +** message format to communicate with NETLINK +*/ +struct msgtemplate { + struct nlmsghdr n; + struct genlmsghdr g; + char buf[2048]; +}; + +int +netlink_open(void) +{ + int nlsock, famid; + char cpudef[64]; + + /* + ** open the netlink socket + */ + nlsock = nlsock_open(); + + /* + ** get the family id for the TASKSTATS family + */ + famid = nlsock_getfam(nlsock); + + /* + ** determine maximum number of CPU's for this system + ** and specify mask to register all cpu's + */ + sprintf(cpudef, "0-%d", getnumcpu() -1); + + /* + ** indicate to listen for processes from all CPU's + */ + if (nlsock_sendcmd(nlsock, famid, getpid(), TASKSTATS_CMD_GET, + TASKSTATS_CMD_ATTR_REGISTER_CPUMASK, + &cpudef, strlen(cpudef)+1) == -1) + { + fprintf(stderr, "register cpumask failed\n"); + return -1; + } + + return nlsock; +} + + +int +netlink_recv(int nlsock, int flags) +{ + int len; + struct msgtemplate msg; + + if ( (len = recv(nlsock, &msg, sizeof msg, flags)) == -1) + return -errno; // negative: errno + + if (msg.n.nlmsg_type == NLMSG_ERROR || !NLMSG_OK(&msg.n, len)) + { + struct nlmsgerr *err = NLMSG_DATA(&msg); + + return err->error; // negative: errno + } + + return len; // 0 or positive value +} + +static int +nlsock_getfam(int nlsock) +{ + int len; + struct nlattr *nlattr; + struct msgtemplate msg; + + nlsock_sendcmd(nlsock, GENL_ID_CTRL, getpid(), + CTRL_CMD_GETFAMILY, + CTRL_ATTR_FAMILY_NAME, + TASKSTATS_GENL_NAME, sizeof TASKSTATS_GENL_NAME); + + if ( (len = recv(nlsock, &msg, sizeof msg, 0)) == -1) + { + perror("receive NETLINK family"); + exit(1); + } + + if (msg.n.nlmsg_type == NLMSG_ERROR || !NLMSG_OK(&msg.n, len)) + { + struct nlmsgerr *err = NLMSG_DATA(&msg); + + fprintf(stderr, "receive NETLINK family, errno %d\n", + err->error); + + exit(1); + } + + nlattr = (struct nlattr *) GENLMSG_DATA(&msg); + nlattr = (struct nlattr *) ((char *) nlattr + + NLA_ALIGN(nlattr->nla_len)); + + if (nlattr->nla_type != CTRL_ATTR_FAMILY_ID) + { + fprintf(stderr, "unexpected family id\n"); + exit(1); + } + + return *(__u16 *) NLA_DATA(nlattr); +} + + +static int +nlsock_open(void) +{ + int nlsock, rcvsz = 256*1024; + struct sockaddr_nl nlsockaddr; + + if ( (nlsock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC) ) == -1) + { + perror("open NETLINK socket"); + exit(1); + } + + if (setsockopt(nlsock, SOL_SOCKET, SO_RCVBUF, &rcvsz, sizeof rcvsz) + == -1) + { + perror("set length receive buffer"); + exit(1); + } + + memset(&nlsockaddr, 0, sizeof nlsockaddr); + nlsockaddr.nl_family = AF_NETLINK; + + if (bind(nlsock, (struct sockaddr *) &nlsockaddr, sizeof nlsockaddr) + == -1) + { + perror("bind NETLINK socket"); + close(nlsock); + exit(1); + } + + return nlsock; +} + +static int +nlsock_sendcmd(int nlsock, __u16 nlmsg_type, + __u32 nlmsg_pid, __u8 genl_cmd, + __u16 nla_type, void *nla_data, int nla_len) +{ + struct nlattr *na; + struct sockaddr_nl nlsockaddr; + int rv, buflen; + char *buf; + struct msgtemplate msg; + + msg.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); + msg.n.nlmsg_type = nlmsg_type; + msg.n.nlmsg_flags = NLM_F_REQUEST; + msg.n.nlmsg_seq = 0; + msg.n.nlmsg_pid = nlmsg_pid; + msg.g.cmd = genl_cmd; + msg.g.version = 0x1; + na = (struct nlattr *) GENLMSG_DATA(&msg); + na->nla_type = nla_type; + na->nla_len = nla_len + 1 + NLA_HDRLEN; + + memcpy(NLA_DATA(na), nla_data, nla_len); + msg.n.nlmsg_len += NLMSG_ALIGN(na->nla_len); + + buf = (char *) &msg; + buflen = msg.n.nlmsg_len ; + + memset(&nlsockaddr, 0, sizeof(nlsockaddr)); + nlsockaddr.nl_family = AF_NETLINK; + + while ((rv = sendto(nlsock, buf, buflen, 0, + (struct sockaddr *) &nlsockaddr, sizeof(nlsockaddr))) < buflen) + { + if (rv == -1) + { + if (errno != EAGAIN) + { + perror("sendto NETLINK"); + return -1; + } + } + else + { + buf += rv; + buflen -= rv; + } + } + + return 0; +} + +static int +getnumcpu(void) +{ + FILE *fp; + char linebuf[4096], label[256]; + int cpunum, maxcpu = 0; + + if ( (fp = fopen("/proc/stat", "r")) != NULL) + { + while ( fgets(linebuf, sizeof(linebuf), fp) != NULL) + { + sscanf(linebuf, "%255s", label); + + if ( strncmp("cpu", label, 3) == 0 && strlen(label) >3) + { + cpunum = atoi(&label[3]); + + if (maxcpu < cpunum) + maxcpu = cpunum; + } + + if ( strncmp("int", label, 3) == 0) + break; + } + + fclose(fp); + } + + return maxcpu+1; +} diff --git a/sources/netstats.h b/sources/netstats.h new file mode 100644 index 0000000..5f5676f --- /dev/null +++ b/sources/netstats.h @@ -0,0 +1,140 @@ +/* +** structures defined from the output of /proc/net/snmp and /proc/net/snmp6 +*/ +struct ipv4_stats { + count_t Forwarding; + count_t DefaultTTL; + count_t InReceives; + count_t InHdrErrors; + count_t InAddrErrors; + count_t ForwDatagrams; + count_t InUnknownProtos; + count_t InDiscards; + count_t InDelivers; + count_t OutRequests; + count_t OutDiscards; + count_t OutNoRoutes; + count_t ReasmTimeout; + count_t ReasmReqds; + count_t ReasmOKs; + count_t ReasmFails; + count_t FragOKs; + count_t FragFails; + count_t FragCreates; +}; + +struct icmpv4_stats { + count_t InMsgs; + count_t InErrors; + count_t InDestUnreachs; + count_t InTimeExcds; + count_t InParmProbs; + count_t InSrcQuenchs; + count_t InRedirects; + count_t InEchos; + count_t InEchoReps; + count_t InTimestamps; + count_t InTimestampReps; + count_t InAddrMasks; + count_t InAddrMaskReps; + count_t OutMsgs; + count_t OutErrors; + count_t OutDestUnreachs; + count_t OutTimeExcds; + count_t OutParmProbs; + count_t OutSrcQuenchs; + count_t OutRedirects; + count_t OutEchos; + count_t OutEchoReps; + count_t OutTimestamps; + count_t OutTimestampReps; + count_t OutAddrMasks; + count_t OutAddrMaskReps; +}; + +struct udpv4_stats { + count_t InDatagrams; + count_t NoPorts; + count_t InErrors; + count_t OutDatagrams; +}; + +struct tcp_stats { + count_t RtoAlgorithm; + count_t RtoMin; + count_t RtoMax; + count_t MaxConn; + count_t ActiveOpens; + count_t PassiveOpens; + count_t AttemptFails; + count_t EstabResets; + count_t CurrEstab; + count_t InSegs; + count_t OutSegs; + count_t RetransSegs; + count_t InErrs; + count_t OutRsts; +}; + +struct ipv6_stats { + count_t Ip6InReceives; + count_t Ip6InHdrErrors; + count_t Ip6InTooBigErrors; + count_t Ip6InNoRoutes; + count_t Ip6InAddrErrors; + count_t Ip6InUnknownProtos; + count_t Ip6InTruncatedPkts; + count_t Ip6InDiscards; + count_t Ip6InDelivers; + count_t Ip6OutForwDatagrams; + count_t Ip6OutRequests; + count_t Ip6OutDiscards; + count_t Ip6OutNoRoutes; + count_t Ip6ReasmTimeout; + count_t Ip6ReasmReqds; + count_t Ip6ReasmOKs; + count_t Ip6ReasmFails; + count_t Ip6FragOKs; + count_t Ip6FragFails; + count_t Ip6FragCreates; + count_t Ip6InMcastPkts; + count_t Ip6OutMcastPkts; +}; + +struct icmpv6_stats { + count_t Icmp6InMsgs; + count_t Icmp6InErrors; + count_t Icmp6InDestUnreachs; + count_t Icmp6InPktTooBigs; + count_t Icmp6InTimeExcds; + count_t Icmp6InParmProblems; + count_t Icmp6InEchos; + count_t Icmp6InEchoReplies; + count_t Icmp6InGroupMembQueries; + count_t Icmp6InGroupMembResponses; + count_t Icmp6InGroupMembReductions; + count_t Icmp6InRouterSolicits; + count_t Icmp6InRouterAdvertisements; + count_t Icmp6InNeighborSolicits; + count_t Icmp6InNeighborAdvertisements; + count_t Icmp6InRedirects; + count_t Icmp6OutMsgs; + count_t Icmp6OutDestUnreachs; + count_t Icmp6OutPktTooBigs; + count_t Icmp6OutTimeExcds; + count_t Icmp6OutParmProblems; + count_t Icmp6OutEchoReplies; + count_t Icmp6OutRouterSolicits; + count_t Icmp6OutNeighborSolicits; + count_t Icmp6OutNeighborAdvertisements; + count_t Icmp6OutRedirects; + count_t Icmp6OutGroupMembResponses; + count_t Icmp6OutGroupMembReductions; +}; + +struct udpv6_stats { + count_t Udp6InDatagrams; + count_t Udp6NoPorts; + count_t Udp6InErrors; + count_t Udp6OutDatagrams; +}; diff --git a/sources/parseable.c b/sources/parseable.c new file mode 100644 index 0000000..6ba1e95 --- /dev/null +++ b/sources/parseable.c @@ -0,0 +1,716 @@ +/* +** ATOP - System & Process Monitor +** +** The program 'atop' offers the possibility to view the activity of +** the system on system-level as well as process-level. +** +** ========================================================================== +** Author: Gerlof Langeveld +** E-mail: gerlof.langeveld@atoptool.nl +** Date: February 2007 +** -------------------------------------------------------------------------- +** Copyright (C) 2007-2010 Gerlof Langeveld +** +** This program is free software; you can redistribute it and/or modify it +** under the terms of the GNU General Public License as published by the +** Free Software Foundation; either version 2, or (at your option) any +** later version. +** +** This program is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +** See the GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +** -------------------------------------------------------------------------- +** +** $Id: parseable.c,v 1.13 2010/10/23 14:02:19 gerlof Exp $ +** $Log: parseable.c,v $ +** Revision 1.13 2010/10/23 14:02:19 gerlof +** Show counters for total number of running and sleep (S and D) threads. +** +** Revision 1.12 2010/05/18 19:20:55 gerlof +** Introduce CPU frequency and scaling (JC van Winkel). +** +** Revision 1.11 2010/04/23 12:19:35 gerlof +** Modified mail-address in header. +** +** Revision 1.10 2010/03/04 10:52:21 gerlof +** Support I/O-statistics on logical volumes and MD devices. +** +** Revision 1.9 2010/01/08 14:46:42 gerlof +** Added label RESET in case of a sample with values since boot. +** +** Revision 1.8 2009/12/19 22:32:14 gerlof +** Add new counters to parseable output. +** +** Revision 1.7 2008/03/06 09:08:29 gerlof +** Bug-solution regarding parseable output of PPID. +** +** Revision 1.6 2008/03/06 08:38:03 gerlof +** Register/show ppid of a process. +** +** Revision 1.5 2008/01/18 08:03:40 gerlof +** Show information about the number of threads in state 'running', +** 'interruptible sleeping' and 'non-interruptible sleeping'. +** +** Revision 1.4 2007/12/11 13:33:12 gerlof +** Cosmetic change. +** +** Revision 1.3 2007/08/16 12:00:11 gerlof +** Add support for atopsar reporting. +** Concerns networking-counters that have been changed. +** +** Revision 1.2 2007/03/20 13:01:12 gerlof +** Introduction of variable supportflags. +** +** Revision 1.1 2007/02/19 11:55:43 gerlof +** Initial revision +** +*/ +#include +#include +#include +#include +#include +#include + +#include "atop.h" +#include "photosyst.h" +#include "photoproc.h" +#include "parseable.h" + +void print_CPU(); +void print_cpu(); +void print_CPL(); +void print_MEM(); +void print_SWP(); +void print_PAG(); +void print_LVM(); +void print_MDD(); +void print_DSK(); +void print_NFM(); +void print_NFC(); +void print_NFS(); +void print_NET(); + +void print_PRG(); +void print_PRC(); +void print_PRM(); +void print_PRD(); +void print_PRN(); + +/* +** table with possible labels and the corresponding +** print-function for parseable output. +*/ +struct labeldef { + char *label; + int valid; + void (*prifunc)(char *, struct sstat *, struct tstat *, int); +}; + +static struct labeldef labeldef[] = { + { "CPU", 0, print_CPU }, + { "cpu", 0, print_cpu }, + { "CPL", 0, print_CPL }, + { "MEM", 0, print_MEM }, + { "SWP", 0, print_SWP }, + { "PAG", 0, print_PAG }, + { "LVM", 0, print_LVM }, + { "MDD", 0, print_MDD }, + { "DSK", 0, print_DSK }, + { "NFM", 0, print_NFM }, + { "NFC", 0, print_NFC }, + { "NFS", 0, print_NFS }, + { "NET", 0, print_NET }, + + { "PRG", 0, print_PRG }, + { "PRC", 0, print_PRC }, + { "PRM", 0, print_PRM }, + { "PRD", 0, print_PRD }, + { "PRN", 0, print_PRN }, +}; + +static int numlabels = sizeof labeldef/sizeof(struct labeldef); + +/* +** analyse the parse-definition string that has been +** passed as argument with the flag -P +*/ +int +parsedef(char *pd) +{ + register int i; + char *p, *ep = pd + strlen(pd); + + /* + ** check if string passed bahind -P is not another flag + */ + if (*pd == '-') + { + fprintf(stderr, "flag -P should be followed by label list\n"); + return 0; + } + + /* + ** check list of comma-separated labels + */ + while (pd < ep) + { + /* + ** exchange comma by null-byte + */ + if ( (p = strchr(pd, ',')) ) + *p = 0; + else + p = ep-1; + + /* + ** check if the next label exists + */ + for (i=0; i < numlabels; i++) + { + if ( strcmp(labeldef[i].label, pd) == 0) + { + labeldef[i].valid = 1; + break; + } + } + + /* + ** non-existing label has been used + */ + if (i == numlabels) + { + /* + ** check if special label 'ALL' has been used + */ + if ( strcmp("ALL", pd) == 0) + { + for (i=0; i < numlabels; i++) + labeldef[i].valid = 1; + break; + } + else + { + fprintf(stderr, "label %s not found\n", pd); + return 0; + } + } + + pd = p+1; + } + + setbuf(stdout, (char *)0); + + return 1; +} + +/* +** produce parseable output for an interval +*/ +char +parseout(time_t curtime, int numsecs, + struct devtstat *devtstat, struct sstat *sstat, + int nexit, unsigned int noverflow, char flag) +{ + register int i; + char datestr[32], timestr[32], header[256]; + + /* + ** print reset-label for sample-values since boot + */ + if (flag&RRBOOT) + printf("RESET\n"); + + /* + ** search all labels which are selected before + */ + for (i=0; i < numlabels; i++) + { + if (labeldef[i].valid) + { + /* + ** prepare generic columns + */ + convdate(curtime, datestr); + convtime(curtime, timestr); + + snprintf(header, sizeof header, "%s %s %ld %s %s %d", + labeldef[i].label, + utsname.nodename, + curtime, + datestr, timestr, numsecs); + + /* + ** call a selected print-function + */ + (labeldef[i].prifunc)(header, sstat, + devtstat->taskall, devtstat->ntaskall); + } + } + + /* + ** print separator + */ + printf("SEP\n"); + + return '\0'; +} + +/* +** print functions for system-level statistics +*/ +void +calc_freqscale(count_t maxfreq, count_t cnt, count_t ticks, + count_t *freq, int *freqperc) +{ + // if ticks != 0, do full calcs + if (maxfreq && ticks) + { + *freq=cnt/ticks; + *freqperc=100* *freq / maxfreq; + } + else if (maxfreq) // max frequency is known so % can be calculated + { + *freq=cnt; + *freqperc=100*cnt/maxfreq; + } + else if (cnt) // no max known, set % to 100 + { + *freq=cnt; + *freqperc=100; + } + else // nothing is known: set freq to 0, % to 100 + { + *freq=0; + *freqperc=100; + } +} + + +void +print_CPU(char *hp, struct sstat *ss, struct tstat *ps, int nact) +{ + count_t maxfreq=0; + count_t cnt=0; + count_t ticks=0; + count_t freq; + int freqperc; + int i; + + // calculate average clock frequency + for (i=0; i < ss->cpu.nrcpu; i++) + { + cnt += ss->cpu.cpu[i].freqcnt.cnt; + ticks += ss->cpu.cpu[i].freqcnt.ticks; + } + maxfreq = ss->cpu.cpu[0].freqcnt.maxfreq; + calc_freqscale(maxfreq, cnt, ticks, &freq, &freqperc); + + printf("%s %u %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %d\n", + hp, + hertz, + ss->cpu.nrcpu, + ss->cpu.all.stime, + ss->cpu.all.utime, + ss->cpu.all.ntime, + ss->cpu.all.itime, + ss->cpu.all.wtime, + ss->cpu.all.Itime, + ss->cpu.all.Stime, + ss->cpu.all.steal, + ss->cpu.all.guest, + freq, + freqperc + ); +} + +void +print_cpu(char *hp, struct sstat *ss, struct tstat *ps, int nact) +{ + register int i; + count_t maxfreq=0; + count_t cnt=0; + count_t ticks=0; + count_t freq; + int freqperc; + + for (i=0; i < ss->cpu.nrcpu; i++) + { + cnt = ss->cpu.cpu[i].freqcnt.cnt; + ticks = ss->cpu.cpu[i].freqcnt.ticks; + maxfreq= ss->cpu.cpu[0].freqcnt.maxfreq; + + calc_freqscale(maxfreq, cnt, ticks, &freq, &freqperc); + + printf("%s %u %d %lld %lld %lld " + "%lld %lld %lld %lld %lld %lld %lld %d\n", + hp, hertz, i, + ss->cpu.cpu[i].stime, + ss->cpu.cpu[i].utime, + ss->cpu.cpu[i].ntime, + ss->cpu.cpu[i].itime, + ss->cpu.cpu[i].wtime, + ss->cpu.cpu[i].Itime, + ss->cpu.cpu[i].Stime, + ss->cpu.cpu[i].steal, + ss->cpu.cpu[i].guest, + freq, + freqperc); + } +} + +void +print_CPL(char *hp, struct sstat *ss, struct tstat *ps, int nact) +{ + printf("%s %lld %.2f %.2f %.2f %lld %lld\n", + hp, + ss->cpu.nrcpu, + ss->cpu.lavg1, + ss->cpu.lavg5, + ss->cpu.lavg15, + ss->cpu.csw, + ss->cpu.devint); +} + +void +print_MEM(char *hp, struct sstat *ss, struct tstat *ps, int nact) +{ + printf( "%s %u %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld\n", + hp, + pagesize, + ss->mem.physmem, + ss->mem.freemem, + ss->mem.cachemem, + ss->mem.buffermem, + ss->mem.slabmem, + ss->mem.cachedrt, + ss->mem.slabreclaim, + ss->mem.vmwballoon, + ss->mem.shmem, + ss->mem.shmrss, + ss->mem.shmswp, + ss->mem.hugepagesz, + ss->mem.tothugepage, + ss->mem.freehugepage); +} + +void +print_SWP(char *hp, struct sstat *ss, struct tstat *ps, int nact) +{ + printf( "%s %u %lld %lld %lld %lld %lld\n", + hp, + pagesize, + ss->mem.totswap, + ss->mem.freeswap, + (long long)0, + ss->mem.committed, + ss->mem.commitlim); +} + +void +print_PAG(char *hp, struct sstat *ss, struct tstat *ps, int nact) +{ + printf( "%s %u %lld %lld %lld %lld %lld\n", + hp, + pagesize, + ss->mem.pgscans, + ss->mem.allocstall, + (long long)0, + ss->mem.swins, + ss->mem.swouts); +} + +void +print_LVM(char *hp, struct sstat *ss, struct tstat *ps, int nact) +{ + register int i; + + for (i=0; ss->dsk.lvm[i].name[0]; i++) + { + printf( "%s %s %lld %lld %lld %lld %lld\n", + hp, + ss->dsk.lvm[i].name, + ss->dsk.lvm[i].io_ms, + ss->dsk.lvm[i].nread, + ss->dsk.lvm[i].nrsect, + ss->dsk.lvm[i].nwrite, + ss->dsk.lvm[i].nwsect); + } +} + +void +print_MDD(char *hp, struct sstat *ss, struct tstat *ps, int nact) +{ + register int i; + + for (i=0; ss->dsk.mdd[i].name[0]; i++) + { + printf( "%s %s %lld %lld %lld %lld %lld\n", + hp, + ss->dsk.mdd[i].name, + ss->dsk.mdd[i].io_ms, + ss->dsk.mdd[i].nread, + ss->dsk.mdd[i].nrsect, + ss->dsk.mdd[i].nwrite, + ss->dsk.mdd[i].nwsect); + } +} + +void +print_DSK(char *hp, struct sstat *ss, struct tstat *ps, int nact) +{ + register int i; + + for (i=0; ss->dsk.dsk[i].name[0]; i++) + { + printf( "%s %s %lld %lld %lld %lld %lld\n", + hp, + ss->dsk.dsk[i].name, + ss->dsk.dsk[i].io_ms, + ss->dsk.dsk[i].nread, + ss->dsk.dsk[i].nrsect, + ss->dsk.dsk[i].nwrite, + ss->dsk.dsk[i].nwsect); + } +} + +void +print_NFM(char *hp, struct sstat *ss, struct tstat *ps, int nact) +{ + register int i; + + for (i=0; i < ss->nfs.nfsmounts.nrmounts; i++) + { + printf("%s %s %lld %lld %lld %lld %lld %lld %lld %lld\n", + hp, + ss->nfs.nfsmounts.nfsmnt[i].mountdev, + ss->nfs.nfsmounts.nfsmnt[i].bytestotread, + ss->nfs.nfsmounts.nfsmnt[i].bytestotread, + ss->nfs.nfsmounts.nfsmnt[i].bytesread, + ss->nfs.nfsmounts.nfsmnt[i].byteswrite, + ss->nfs.nfsmounts.nfsmnt[i].bytesdread, + ss->nfs.nfsmounts.nfsmnt[i].bytesdwrite, + ss->nfs.nfsmounts.nfsmnt[i].pagesmread, + ss->nfs.nfsmounts.nfsmnt[i].pagesmwrite); + } +} + +void +print_NFC(char *hp, struct sstat *ss, struct tstat *ps, int nact) +{ + printf( "%s %lld %lld %lld %lld %lld\n", + hp, + ss->nfs.client.rpccnt, + ss->nfs.client.rpcread, + ss->nfs.client.rpcwrite, + ss->nfs.client.rpcretrans, + ss->nfs.client.rpcautrefresh); +} + +void +print_NFS(char *hp, struct sstat *ss, struct tstat *ps, int nact) +{ + printf( "%s %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld " + "%lld %lld %lld %lld %lld\n", + hp, + ss->nfs.server.rpccnt, + ss->nfs.client.rpcread, + ss->nfs.client.rpcwrite, + ss->nfs.server.nrbytes, + ss->nfs.server.nwbytes, + ss->nfs.server.rpcbadfmt, + ss->nfs.server.rpcbadaut, + ss->nfs.server.rpcbadcln, + ss->nfs.server.netcnt, + ss->nfs.server.nettcpcnt, + ss->nfs.server.netudpcnt, + ss->nfs.server.nettcpcon, + ss->nfs.server.rchits, + ss->nfs.server.rcmiss, + ss->nfs.server.rcnoca); +} + +void +print_NET(char *hp, struct sstat *ss, struct tstat *ps, int nact) +{ + register int i; + + printf( "%s %s %lld %lld %lld %lld %lld %lld %lld %lld\n", + hp, + "upper", + ss->net.tcp.InSegs, + ss->net.tcp.OutSegs, + ss->net.udpv4.InDatagrams + + ss->net.udpv6.Udp6InDatagrams, + ss->net.udpv4.OutDatagrams + + ss->net.udpv6.Udp6OutDatagrams, + ss->net.ipv4.InReceives + + ss->net.ipv6.Ip6InReceives, + ss->net.ipv4.OutRequests + + ss->net.ipv6.Ip6OutRequests, + ss->net.ipv4.InDelivers + + ss->net.ipv6.Ip6InDelivers, + ss->net.ipv4.ForwDatagrams + + ss->net.ipv6.Ip6OutForwDatagrams); + + for (i=0; ss->intf.intf[i].name[0]; i++) + { + printf( "%s %s %lld %lld %lld %lld %ld %d\n", + hp, + ss->intf.intf[i].name, + ss->intf.intf[i].rpack, + ss->intf.intf[i].rbyte, + ss->intf.intf[i].spack, + ss->intf.intf[i].sbyte, + ss->intf.intf[i].speed, + ss->intf.intf[i].duplex); + } +} + +/* +** print functions for process-level statistics +*/ +void +print_PRG(char *hp, struct sstat *ss, struct tstat *ps, int nact) +{ + register int i; + + for (i=0; i < nact; i++, ps++) + { + printf("%s %d (%s) %c %d %d %d %d %d %ld (%s) %d %d %d %d " + "%d %d %d %d %d %d %ld %c %d %d %s\n", + hp, + ps->gen.pid, + ps->gen.name, + ps->gen.state, + ps->gen.ruid, + ps->gen.rgid, + ps->gen.tgid, + ps->gen.nthr, + ps->gen.excode, + ps->gen.btime, + ps->gen.cmdline, + ps->gen.ppid, + ps->gen.nthrrun, + ps->gen.nthrslpi, + ps->gen.nthrslpu, + ps->gen.euid, + ps->gen.egid, + ps->gen.suid, + ps->gen.sgid, + ps->gen.fsuid, + ps->gen.fsgid, + ps->gen.elaps, + ps->gen.isproc ? 'y':'n', + ps->gen.vpid, + ps->gen.ctid, + ps->gen.container[0] ? ps->gen.container:"-"); + } +} + +void +print_PRC(char *hp, struct sstat *ss, struct tstat *ps, int nact) +{ + register int i; + + for (i=0; i < nact; i++, ps++) + { + printf("%s %d (%s) %c %u %lld %lld %d %d %d %d %d %d %d %c\n", + hp, + ps->gen.pid, + ps->gen.name, + ps->gen.state, + hertz, + ps->cpu.utime, + ps->cpu.stime, + ps->cpu.nice, + ps->cpu.prio, + ps->cpu.rtprio, + ps->cpu.policy, + ps->cpu.curcpu, + ps->cpu.sleepavg, + ps->gen.tgid, + ps->gen.isproc ? 'y':'n'); + } +} + +void +print_PRM(char *hp, struct sstat *ss, struct tstat *ps, int nact) +{ + register int i; + + for (i=0; i < nact; i++, ps++) + { + printf("%s %d (%s) %c %u %lld %lld %lld %lld %lld %lld " + "%lld %lld %lld %lld %lld %d %c %lld\n", + hp, + ps->gen.pid, + ps->gen.name, + ps->gen.state, + pagesize, + ps->mem.vmem, + ps->mem.rmem, + ps->mem.vexec, + ps->mem.vgrow, + ps->mem.rgrow, + ps->mem.minflt, + ps->mem.majflt, + ps->mem.vlibs, + ps->mem.vdata, + ps->mem.vstack, + ps->mem.vswap, + ps->gen.tgid, + ps->gen.isproc ? 'y':'n', + ps->mem.pmem == (unsigned long long)-1LL ? + 0:ps->mem.pmem); + } +} + +void +print_PRD(char *hp, struct sstat *ss, struct tstat *ps, int nact) +{ + register int i; + + for (i=0; i < nact; i++, ps++) + { + printf("%s %d (%s) %c %c %c %lld %lld %lld %lld %lld %d n %c\n", + hp, + ps->gen.pid, + ps->gen.name, + ps->gen.state, + 'n', + supportflags & IOSTAT ? 'y' : 'n', + ps->dsk.rio, ps->dsk.rsz, + ps->dsk.wio, ps->dsk.wsz, ps->dsk.cwsz, + ps->gen.tgid, + ps->gen.isproc ? 'y':'n'); + } +} + +void +print_PRN(char *hp, struct sstat *ss, struct tstat *ps, int nact) +{ + register int i; + + for (i=0; i < nact; i++, ps++) + { + printf("%s %d (%s) %c %c %lld %lld %lld %lld %lld %lld " + "%lld %lld %d %d %d %c\n", + hp, + ps->gen.pid, + ps->gen.name, + ps->gen.state, + supportflags & NETATOP ? 'y' : 'n', + ps->net.tcpsnd, ps->net.tcpssz, + ps->net.tcprcv, ps->net.tcprsz, + ps->net.udpsnd, ps->net.udpssz, + ps->net.udprcv, ps->net.udprsz, + 0, 0, + ps->gen.tgid, ps->gen.isproc ? 'y':'n'); + } +} diff --git a/sources/parseable.h b/sources/parseable.h new file mode 100644 index 0000000..4d592cd --- /dev/null +++ b/sources/parseable.h @@ -0,0 +1,4 @@ +int parsedef(char *); +char parseout(time_t, int, + struct devtstat *, struct sstat *, + int, unsigned int, char); diff --git a/sources/photoproc.c b/sources/photoproc.c new file mode 100644 index 0000000..dc9a0c9 --- /dev/null +++ b/sources/photoproc.c @@ -0,0 +1,815 @@ +/* +** ATOP - System & Process Monitor +** +** The program 'atop' offers the possibility to view the activity of +** the system on system-level as well as process-/thread-level. +** +** This source-file contains functions to read the process-administration +** of every running process from kernel-space and extract the required +** activity-counters. +** ========================================================================== +** Author: Gerlof Langeveld +** E-mail: gerlof.langeveld@atoptool.nl +** Date: November 1996 +** LINUX-port: June 2000 +** -------------------------------------------------------------------------- +** Copyright (C) 2000-2010 Gerlof Langeveld +** +** This program is free software; you can redistribute it and/or modify it +** under the terms of the GNU General Public License as published by the +** Free Software Foundation; either version 2, or (at your option) any +** later version. +** +** This program is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +** See the GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +** -------------------------------------------------------------------------- +** +** $Log: photoproc.c,v $ +** Revision 1.33 2010/04/23 12:19:35 gerlof +** Modified mail-address in header. +** +** Revision 1.32 2009/11/27 13:44:00 gerlof +** euid, suid, fsuid, egid, sgid and fsgid also registered. +** +** Revision 1.31 2008/03/06 08:38:14 gerlof +** Register/show ppid of a process. +** +** Revision 1.30 2008/01/18 07:36:29 gerlof +** Gather information about the state of the individual threads. +** +** Revision 1.29 2007/11/05 12:26:10 gerlof +** Detect disappearing /proc/stat file when process exits +** (credits: Rene Rebe). +** +** Revision 1.28 2007/03/27 10:53:59 gerlof +** Bug-solution: only allow IOSTAT when patches are not installed. +** +** Revision 1.27 2007/03/21 14:21:37 gerlof +** Handle io counters maintained from 2.6.20 +** +** Revision 1.26 2007/02/13 10:32:34 gerlof +** Removal of external declarations. +** +** Revision 1.25 2007/01/15 09:00:14 gerlof +** Add new function to count actual number of processes. +** +** Revision 1.24 2006/02/07 06:47:35 gerlof +** Removed swap-counter. +** +** Revision 1.23 2005/10/21 09:49:57 gerlof +** Per-user accumulation of resource consumption. +** +** Revision 1.22 2004/12/14 15:05:58 gerlof +** Implementation of patch-recognition for disk and network-statistics. +** +** Revision 1.21 2004/09/23 09:07:49 gerlof +** Solved segmentation fault by checking tval. +** +** Revision 1.20 2004/09/08 06:01:01 gerlof +** Correct the priority of a process by adding 100 (the kernel +** subtracts 100 when showing the value via /proc). +** +** Revision 1.19 2004/09/02 10:49:45 gerlof +** Added sleep-average to process-info. +** +** Revision 1.18 2004/08/31 09:51:36 gerlof +** Gather information about underlying threads. +** +** Revision 1.17 2003/07/07 09:26:59 gerlof +** Cleanup code (-Wall proof). +** +** Revision 1.16 2003/06/30 11:30:43 gerlof +** Enlarge counters to 'long long'. +** +** Revision 1.15 2003/02/06 12:09:23 gerlof +** Exchange tab-character in command-line by space. +** +** Revision 1.14 2003/01/24 14:19:39 gerlof +** Exchange newline byte in command-line by space. +** +** Revision 1.13 2003/01/17 14:21:41 root +** Change-directory to /proc to optimize opening /proc-files +** via relative path-names i.s.o. absolute path-names. +** +** Revision 1.12 2003/01/17 07:31:29 gerlof +** Store the full command-line for every process. +** +** Revision 1.11 2003/01/06 13:03:09 gerlof +** Improved command-name parsing (command-names containing a close-bracket +** were not parsed correctly). +** +** Revision 1.10 2002/10/03 11:12:39 gerlof +** Modify (effective) uid/gid to real uid/gid. +** +** Revision 1.9 2002/07/24 11:13:31 gerlof +** Changed to ease porting to other UNIX-platforms. +** +** Revision 1.8 2002/07/08 09:27:45 gerlof +** Avoid buffer overflow during sprintf by using snprintf. +** +** Revision 1.7 2002/01/22 13:39:53 gerlof +** Support for number of cpu's. +** +** Revision 1.6 2001/11/22 08:33:43 gerlof +** Add priority per process. +** +** Revision 1.5 2001/11/13 08:26:15 gerlof +** Small bug-fixes. +** +** Revision 1.4 2001/11/07 09:18:43 gerlof +** Use /proc instead of /dev/kmem for process-level statistics. +** +** Revision 1.3 2001/10/04 13:57:34 gerlof +** Explicit include of sched.h (i.s.o. linux/sched.h via linux/mm.h). +** +** Revision 1.2 2001/10/04 08:47:26 gerlof +** Improved verification of kernel-symbol addresses +** +** Revision 1.1 2001/10/02 10:43:29 gerlof +** Initial revision +** +*/ + +static const char rcsid[] = "$Id: photoproc.c,v 1.33 2010/04/23 12:19:35 gerlof Exp $"; + +#include +#include +#include +#include +#include +#include +#include +#include + +#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)) && tvald_name[0] == '.' || + chdir(tent->d_name) != 0 ) + continue; + + if ( !procstat(curthr, bootepoch, 0)) + { + if ( chdir("..") == -1); + continue; + } + + if ( !procstatus(curthr) ) + { + if ( chdir("..") == -1); + continue; + } + + if ( !procio(curthr) ) + { + if ( chdir("..") == -1); + continue; + } + + strcpy(curthr->gen.container, + curtask->gen.container); + + switch (curthr->gen.state) + { + case 'R': + curtask->gen.nthrrun += 1; + break; + case 'S': + curtask->gen.nthrslpi += 1; + break; + case 'D': + curtask->gen.nthrslpu += 1; + break; + } + + curthr->gen.nthr = 1; + + // read network stats from netatop + netatop_gettask(curthr->gen.pid, 't', + curthr); + + // all stats read now + tval++; /* increment thread-level */ + if ( chdir("..") == -1); /* thread */ + } + + closedir(dirtask); + if ( chdir("..") == -1); /* leave task */ + } + } + + if ( chdir("..") == -1); /* leave process-level directry */ + } + + closedir(dirp); + + if ( chdir(origdir) == -1) + cleanstop(53); + + if (dockstat) + supportflags |= DOCKSTAT; + else + supportflags &= ~DOCKSTAT; + + return tval; +} + +/* +** count number of processes currently running +*/ +unsigned int +countprocs(void) +{ + unsigned int nr=0; + DIR *dirp; + struct dirent *entp; + char origdir[1024]; + + if ( getcwd(origdir, sizeof origdir) == NULL) + cleanstop(53); + + if ( chdir("/proc") == -1) + cleanstop(53); + + dirp = opendir("."); + + while ( (entp = readdir(dirp)) ) + { + /* + ** count subdirectory-names starting with a digit + */ + if (isdigit(entp->d_name[0])) + nr++; + } + + closedir(dirp); + + if ( chdir(origdir) == -1) + cleanstop(53); + + return nr; +} + +/* +** open file "stat" and obtain required info +*/ +static int +procstat(struct tstat *curtask, unsigned long long bootepoch, char isproc) +{ + FILE *fp; + int nr; + char line[4096], *p, *cmdhead, *cmdtail; + + if ( (fp = fopen("stat", "r")) == NULL) + return 0; + + if ( (nr = fread(line, 1, sizeof line-1, fp)) == 0) + { + fclose(fp); + return 0; + } + + line[nr] = '\0'; // terminate string + + /* + ** fetch command name + */ + cmdhead = strchr (line, '('); + cmdtail = strrchr(line, ')'); + + if (!cmdhead || !cmdtail || cmdtail < cmdhead) // parsing failed? + { + fclose(fp); + return 0; + } + + if ( (nr = cmdtail-cmdhead-1) > PNAMLEN) + nr = PNAMLEN; + + p = curtask->gen.name; + + memcpy(p, cmdhead+1, nr); + *(p+nr) = 0; + + while ( (p = strchr(p, '\n')) != NULL) + { + *p = '?'; + p++; + } + + /* + ** fetch other values + */ + curtask->gen.isproc = isproc; + curtask->cpu.rtprio = 0; + curtask->cpu.policy = 0; + curtask->gen.excode = 0; + + sscanf(line, "%d", &(curtask->gen.pid)); /* fetch pid */ + + nr = sscanf(cmdtail+2, SCANSTAT, + &(curtask->gen.state), &(curtask->gen.ppid), + &(curtask->mem.minflt), &(curtask->mem.majflt), + &(curtask->cpu.utime), &(curtask->cpu.stime), + &(curtask->cpu.prio), &(curtask->cpu.nice), + &(curtask->gen.btime), + &(curtask->mem.vmem), &(curtask->mem.rmem), + &(curtask->cpu.curcpu), &(curtask->cpu.rtprio), + &(curtask->cpu.policy)); + + if (nr < 12) /* parsing failed? */ + { + fclose(fp); + return 0; + } + + /* + ** normalization + */ + curtask->gen.btime = (curtask->gen.btime+bootepoch)/hertz; + curtask->cpu.prio += 100; /* was subtracted by kernel */ + curtask->mem.vmem /= 1024; + curtask->mem.rmem *= pagesize/1024; + + fclose(fp); + + switch (curtask->gen.state) + { + case 'R': + curtask->gen.nthrrun = 1; + break; + case 'S': + curtask->gen.nthrslpi = 1; + break; + case 'D': + curtask->gen.nthrslpu = 1; + break; + } + + return 1; +} + +/* +** open file "status" and obtain required info +*/ +static int +procstatus(struct tstat *curtask) +{ + FILE *fp; + char line[4096]; + + if ( (fp = fopen("status", "r")) == NULL) + return 0; + + curtask->gen.nthr = 1; /* for compat with 2.4 */ + curtask->cpu.sleepavg = 0; /* for compat with 2.4 */ + curtask->mem.vgrow = 0; /* calculated later */ + curtask->mem.rgrow = 0; /* calculated later */ + + while (fgets(line, sizeof line, fp)) + { + if (memcmp(line, "Tgid:", 5) ==0) + { + sscanf(line, "Tgid: %d", &(curtask->gen.tgid)); + continue; + } + + if (memcmp(line, "Pid:", 4) ==0) + { + sscanf(line, "Pid: %d", &(curtask->gen.pid)); + continue; + } + + if (memcmp(line, "SleepAVG:", 9)==0) + { + sscanf(line, "SleepAVG: %d%%", + &(curtask->cpu.sleepavg)); + continue; + } + + if (memcmp(line, "Uid:", 4)==0) + { + sscanf(line, "Uid: %d %d %d %d", + &(curtask->gen.ruid), &(curtask->gen.euid), + &(curtask->gen.suid), &(curtask->gen.fsuid)); + continue; + } + + if (memcmp(line, "Gid:", 4)==0) + { + sscanf(line, "Gid: %d %d %d %d", + &(curtask->gen.rgid), &(curtask->gen.egid), + &(curtask->gen.sgid), &(curtask->gen.fsgid)); + continue; + } + + if (memcmp(line, "envID:", 6) ==0) + { + sscanf(line, "envID: %d", &(curtask->gen.ctid)); + continue; + } + + if (memcmp(line, "VPid:", 5) ==0) + { + sscanf(line, "VPid: %d", &(curtask->gen.vpid)); + continue; + } + + if (memcmp(line, "Threads:", 8)==0) + { + sscanf(line, "Threads: %d", &(curtask->gen.nthr)); + continue; + } + + if (memcmp(line, "VmData:", 7)==0) + { + sscanf(line, "VmData: %lld", &(curtask->mem.vdata)); + continue; + } + + if (memcmp(line, "VmStk:", 6)==0) + { + sscanf(line, "VmStk: %lld", &(curtask->mem.vstack)); + continue; + } + + if (memcmp(line, "VmExe:", 6)==0) + { + sscanf(line, "VmExe: %lld", &(curtask->mem.vexec)); + continue; + } + + if (memcmp(line, "VmLib:", 6)==0) + { + sscanf(line, "VmLib: %lld", &(curtask->mem.vlibs)); + continue; + } + + if (memcmp(line, "VmSwap:", 7)==0) + { + sscanf(line, "VmSwap: %lld", &(curtask->mem.vswap)); + continue; + } + + if (memcmp(line, "SigQ:", 5)==0) + break; + } + + fclose(fp); + return 1; +} + +/* +** open file "io" (>= 2.6.20) and obtain required info +*/ +#define IO_READ "read_bytes:" +#define IO_WRITE "write_bytes:" +#define IO_CWRITE "cancelled_write_bytes:" +static int +procio(struct tstat *curtask) +{ + FILE *fp; + char line[4096]; + count_t dskrsz=0, dskwsz=0, dskcwsz=0; + + if (supportflags & IOSTAT) + { + regainrootprivs(); + + if ( (fp = fopen("io", "r")) ) + { + while (fgets(line, sizeof line, fp)) + { + if (memcmp(line, IO_READ, + sizeof IO_READ -1) == 0) + { + sscanf(line, "%*s %llu", &dskrsz); + dskrsz /= 512; // in sectors + continue; + } + + if (memcmp(line, IO_WRITE, + sizeof IO_WRITE -1) == 0) + { + sscanf(line, "%*s %llu", &dskwsz); + dskwsz /= 512; // in sectors + continue; + } + + if (memcmp(line, IO_CWRITE, + sizeof IO_CWRITE -1) == 0) + { + sscanf(line, "%*s %llu", &dskcwsz); + dskcwsz /= 512; // in sectors + continue; + } + } + + fclose(fp); + + curtask->dsk.rsz = dskrsz; + curtask->dsk.rio = dskrsz; // to enable sort + curtask->dsk.wsz = dskwsz; + curtask->dsk.wio = dskwsz; // to enable sort + curtask->dsk.cwsz = dskcwsz; + } + + if (! droprootprivs()) + cleanstop(42); + } + + return 1; +} + +/* +** store the full command line; the command-line may contain: +** - null-bytes as a separator between the arguments +** - newlines (e.g. arguments for awk or sed) +** - tabs (e.g. arguments for awk or sed) +** these special bytes will be converted to spaces +*/ +static void +proccmd(struct tstat *curtask) +{ + FILE *fp; + register int i, nr; + + memset(curtask->gen.cmdline, 0, CMDLEN+1); + + if ( (fp = fopen("cmdline", "r")) != NULL) + { + register char *p = curtask->gen.cmdline; + + nr = fread(p, 1, CMDLEN, fp); + fclose(fp); + + if (nr >= 0) /* anything read ? */ + { + for (i=0; i < nr-1; i++, p++) + { + switch (*p) + { + case '\0': + case '\n': + case '\t': + *p = ' '; + } + } + } + } +} + + +/* +** store the Docker container ID, retrieved from the cpuset +** that could look like this: +** /system.slice/docker-af78216c2a230f1aa5dce56cbf[SNAP].scope (e.g. CentOS) +** /docker/af78216c2a230f1aa5dce56cbf[SNAP] (e.g. openSUSE and Ubuntu)) +*/ +#define CIDPREFIX "docker" +#define CIDSIZE 12 + +static int +proccont(struct tstat *curtask) +{ + FILE *fp; + char line[80]; + + if ( (fp = fopen("cpuset", "r")) != NULL) + { + register char *p; + + if ( fgets(line, sizeof line, fp) ) + { + // default string (so if not used) is "/" + if (line[1] && (p = strstr(line, CIDPREFIX)) ) + { + memcpy(curtask->gen.container, + p + sizeof CIDPREFIX, CIDSIZE); + + fclose(fp); + return 1; + } + } + + fclose(fp); + } + + return 0; +} + + +/* +** open file "smaps" and obtain required info +*/ +static void +procsmaps(struct tstat *curtask) +{ + FILE *fp; + char line[4096]; + count_t pssval; + + + /* + ** open the file (always succeeds, even if no root privs) + */ + regainrootprivs(); + + if ( (fp = fopen("smaps", "r")) ) + { + curtask->mem.pmem = 0; + + while (fgets(line, sizeof line, fp)) + { + if (memcmp(line, "Pss:", 4) != 0) + continue; + + // PSS line found to be accumulated + sscanf(line, "Pss: %llu", &pssval); + curtask->mem.pmem += pssval; + } + + /* + ** verify if fgets returned NULL due to error i.s.o. EOF + */ + if (ferror(fp)) + curtask->mem.pmem = (unsigned long long)-1LL; + + fclose(fp); + } + else + { + curtask->mem.pmem = (unsigned long long)-1LL; + } + + if (! droprootprivs()) + cleanstop(42); +} diff --git a/sources/photoproc.h b/sources/photoproc.h new file mode 100644 index 0000000..2351934 --- /dev/null +++ b/sources/photoproc.h @@ -0,0 +1,169 @@ +/* +** ATOP - System & Process Monitor +** +** The program 'atop' offers the possibility to view the activity of +** the system on system-level as well as process-level. +** +** Include-file describing process-level counters maintained and functions +** to access the process-database. +** ================================================================ +** Author: Gerlof Langeveld +** E-mail: gerlof.langeveld@atoptool.nl +** Date: November 1996 +** LINUX-port: June 2000 +** +** This program is free software; you can redistribute it and/or modify it +** under the terms of the GNU General Public License as published by the +** Free Software Foundation; either version 2, or (at your option) any +** later version. +** +** This program is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +** See the GNU General Public License for more details. +*/ + +#define PNAMLEN 15 +#define CMDLEN 255 + +/* +** structure containing only relevant process-info extracted +** from kernel's process-administration +*/ +struct tstat { + /* GENERAL TASK INFO */ + struct gen { + int tgid; /* threadgroup identification */ + int pid; /* process identification */ + int ppid; /* parent process identification*/ + int ruid; /* real user identification */ + int euid; /* eff. user identification */ + int suid; /* saved user identification */ + int fsuid; /* fs user identification */ + int rgid; /* real group identification */ + int egid; /* eff. group identification */ + int sgid; /* saved group identification */ + int fsgid; /* fs group identification */ + int nthr; /* number of threads in tgroup */ + char name[PNAMLEN+1];/* process name string */ + char isproc; /* boolean: process level? */ + char state; /* process state ('E' = exited) */ + int excode; /* process exit status */ + time_t btime; /* process start time (epoch) */ + time_t elaps; /* process elaps time (hertz) */ + char cmdline[CMDLEN+1];/* command-line string */ + int nthrslpi; /* # threads in state 'S' */ + int nthrslpu; /* # threads in state 'D' */ + int nthrrun; /* # threads in state 'R' */ + + int ctid; /* OpenVZ container ID */ + int vpid; /* OpenVZ virtual PID */ + + int wasinactive; /* boolean: task inactive */ + + char container[16]; /* Docker container id (12 pos) */ + } gen; + + /* CPU STATISTICS */ + struct cpu { + count_t utime; /* time user text (ticks) */ + count_t stime; /* time system text (ticks) */ + int nice; /* nice value */ + int prio; /* priority */ + int rtprio; /* realtime priority */ + int policy; /* scheduling policy */ + int curcpu; /* current processor */ + int sleepavg; /* sleep average percentage */ + int ifuture[4]; /* reserved for future use */ + count_t cfuture[4]; /* reserved for future use */ + } cpu; + + /* DISK STATISTICS */ + struct dsk { + count_t rio; /* number of read requests */ + count_t rsz; /* cumulative # sectors read */ + count_t wio; /* number of write requests */ + count_t wsz; /* cumulative # sectors written */ + count_t cwsz; /* cumulative # written sectors */ + /* being cancelled */ + count_t cfuture[4]; /* reserved for future use */ + } dsk; + + /* MEMORY STATISTICS */ + struct mem { + count_t minflt; /* number of page-reclaims */ + count_t majflt; /* number of page-faults */ + count_t vexec; /* virtmem execfile (Kb) */ + count_t vmem; /* virtual memory (Kb) */ + count_t rmem; /* resident memory (Kb) */ + count_t pmem; /* resident memory (Kb) */ + count_t vgrow; /* virtual growth (Kb) */ + count_t rgrow; /* resident growth (Kb) */ + count_t vdata; /* virtmem data (Kb) */ + count_t vstack; /* virtmem stack (Kb) */ + count_t vlibs; /* virtmem libexec (Kb) */ + count_t vswap; /* swap space used (Kb) */ + count_t cfuture[4]; /* reserved for future use */ + } mem; + + /* NETWORK STATISTICS */ + struct net { + count_t tcpsnd; /* number of TCP-packets sent */ + count_t tcpssz; /* cumulative size packets sent */ + count_t tcprcv; /* number of TCP-packets recved */ + count_t tcprsz; /* cumulative size packets rcvd */ + count_t udpsnd; /* number of UDP-packets sent */ + count_t udpssz; /* cumulative size packets sent */ + count_t udprcv; /* number of UDP-packets recved */ + count_t udprsz; /* cumulative size packets sent */ + count_t avail1; /* */ + count_t avail2; /* */ + count_t cfuture[4]; /* reserved for future use */ + } net; +}; + +struct pinfo { + struct pinfo *phnext; /* next process in hash chain */ + struct pinfo *prnext; /* next process in residue chain */ + struct pinfo *prprev; /* prev process in residue chain */ + + struct tstat tstat; /* per-process statistics */ +}; + +/* +** structure to maintains all deviation info related to one sample +*/ +struct devtstat { + struct tstat *taskall; + struct tstat **procall; + struct tstat **procactive; + + unsigned long ntaskall; + unsigned long ntaskactive; + unsigned long nprocall; + unsigned long nprocactive; + + unsigned long totrun, totslpi, totslpu, totzombie; +}; + +/* +** prototypes of process-database functions +*/ +int pdb_gettask(int, char, time_t, struct pinfo **); +void pdb_addtask(int, struct pinfo *); +int pdb_deltask(int, char); +int pdb_makeresidue(void); +int pdb_cleanresidue(void); +int pdb_srchresidue(struct tstat *, struct pinfo **); + +/* +** prototypes for raw process-statistics functions +*/ +struct netpertask; + +void deviattask(struct tstat *, int, + struct tstat *, int, + struct devtstat *, struct sstat *); + +int photoproc(struct tstat *, int); +unsigned int countprocs(void); diff --git a/sources/photosyst.c b/sources/photosyst.c new file mode 100644 index 0000000..38828ec --- /dev/null +++ b/sources/photosyst.c @@ -0,0 +1,1754 @@ +/* +** ATOP - System & Process Monitor +** +** The program 'atop' offers the possibility to view the activity of +** the system on system-level as well as process-level. +** +** This source-file contains functions to read all relevant system-level +** figures. +** ========================================================================== +** Author: Gerlof Langeveld +** E-mail: gerlof.langeveld@atoptool.nl +** Date: November 1996 +** LINUX-port: June 2000 +** -------------------------------------------------------------------------- +** Copyright (C) 2000-2012 Gerlof Langeveld +** +** This program is free software; you can redistribute it and/or modify it +** under the terms of the GNU General Public License as published by the +** Free Software Foundation; either version 2, or (at your option) any +** later version. +** +** This program is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +** See the GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +** -------------------------------------------------------------------------- +** +** $Log: photosyst.c,v $ +** Revision 1.38 2010/11/19 07:40:40 gerlof +** Support of virtual disk vd... (kvm). +** +** Revision 1.37 2010/11/14 06:42:18 gerlof +** After opening /proc/cpuinfo, the file descriptor was not closed any more. +** +** Revision 1.36 2010/10/23 14:09:50 gerlof +** Add support for mmcblk disks (MMC/SD cardreaders) +** Credits: Anssi Hannula +** +** Revision 1.35 2010/05/18 19:20:30 gerlof +** Introduce CPU frequency and scaling (JC van Winkel). +** +** Revision 1.34 2010/04/23 12:19:35 gerlof +** Modified mail-address in header. +** +** Revision 1.33 2010/03/04 10:58:05 gerlof +** Added recognition of device-type /dev/fio... +** +** Revision 1.32 2010/03/04 10:52:47 gerlof +** Support I/O-statistics on logical volumes and MD devices. +** +** Revision 1.31 2009/12/17 11:59:16 gerlof +** Gather and display new counters: dirty cache and guest cpu usage. +** +** Revision 1.30 2008/02/25 13:47:00 gerlof +** Experimental code for HTTP statistics. +** +** Revision 1.29 2007/08/17 09:45:44 gerlof +** Experimental: gather info about HTTP statistics. +** +** Revision 1.28 2007/08/16 12:00:49 gerlof +** Add support for atopsar reporting. +** Gather more counters, mainly related to networking. +** +** Revision 1.27 2007/07/03 09:01:56 gerlof +** Support Apache-statistics. +** +** Revision 1.26 2007/02/13 10:32:28 gerlof +** Removal of external declarations. +** +** Revision 1.25 2007/02/13 09:29:57 gerlof +** Removal of external declarations. +** +** Revision 1.24 2007/01/22 14:57:12 gerlof +** Support of special disks used by virtual machines. +** +** Revision 1.23 2007/01/22 08:28:50 gerlof +** Support steal-time from /proc/stat. +** +** Revision 1.22 2006/11/13 13:48:20 gerlof +** Implement load-average counters, context-switches and interrupts. +** +** Revision 1.21 2006/01/30 09:14:16 gerlof +** Extend memory counters (a.o. page scans). +** +** Revision 1.20 2005/10/21 09:50:08 gerlof +** Per-user accumulation of resource consumption. +** +** Revision 1.19 2004/10/28 08:31:23 gerlof +** New counter: vm committed space +** +** Revision 1.18 2004/09/24 10:02:35 gerlof +** Wrong cpu-numbers for system level statistics. +** +** Revision 1.17 2004/05/07 05:27:37 gerlof +** Recognize new disk-names and support of diskname-modification. +** +** Revision 1.16 2004/05/06 09:53:31 gerlof +** Skip statistics of ram-disks. +** +** Revision 1.15 2004/05/06 09:46:14 gerlof +** Ported to kernel-version 2.6. +** +** Revision 1.14 2003/07/08 13:53:21 gerlof +** Cleanup code. +** +** Revision 1.13 2003/07/07 09:27:06 gerlof +** Cleanup code (-Wall proof). +** +** Revision 1.12 2003/06/30 11:30:37 gerlof +** Enlarge counters to 'long long'. +** +** Revision 1.11 2003/06/24 06:21:40 gerlof +** Limit number of system resource lines. +** +** Revision 1.10 2003/01/17 14:23:05 root +** Change-directory to /proc to optimize opening /proc-files +** via relative path-names i.s.o. absolute path-names. +** +** Revision 1.9 2003/01/14 07:50:26 gerlof +** Consider IPv6 counters on IP and UDP level (add them to the IPv4 counters). +** +** Revision 1.8 2002/07/24 11:13:38 gerlof +** Changed to ease porting to other UNIX-platforms. +** +** Revision 1.7 2002/07/11 09:12:41 root +** Parsing of /proc/meminfo made 2.5 proof. +** +** Revision 1.6 2002/07/10 05:00:21 root +** Counters pin/pout renamed to swin/swout (Linux conventions). +** +** Revision 1.5 2002/07/08 09:31:11 gerlof +** *** empty log message *** +** +** Revision 1.4 2002/07/02 07:36:45 gerlof +** *** empty log message *** +** +** Revision 1.3 2002/07/02 07:08:36 gerlof +** Recognize more disk driver types via regular expressions +** +** Revision 1.2 2002/01/22 13:40:11 gerlof +** Support for number of cpu's. +** +** Revision 1.1 2001/10/02 10:43:31 gerlof +** Initial revision +** +*/ + +static const char rcsid[] = "$Id: photosyst.c,v 1.38 2010/11/19 07:40:40 gerlof Exp $"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +// #define _GNU_SOURCE +#include +#include + +#include "atop.h" +#include "photosyst.h" + +#define MAXCNT 64 + +/* return value of isdisk() */ +#define NONTYPE 0 +#define DSKTYPE 1 +#define MDDTYPE 2 +#define LVMTYPE 3 + +static int isdisk(unsigned int, unsigned int, + char *, struct perdsk *, int); + +static struct ipv6_stats ipv6_tmp; +static struct icmpv6_stats icmpv6_tmp; +static struct udpv6_stats udpv6_tmp; + +struct v6tab { + char *nam; + count_t *val; +}; + +static struct v6tab v6tab[] = { + {"Ip6InReceives", &ipv6_tmp.Ip6InReceives, }, + {"Ip6InHdrErrors", &ipv6_tmp.Ip6InHdrErrors, }, + {"Ip6InTooBigErrors", &ipv6_tmp.Ip6InTooBigErrors, }, + {"Ip6InNoRoutes", &ipv6_tmp.Ip6InNoRoutes, }, + {"Ip6InAddrErrors", &ipv6_tmp.Ip6InAddrErrors, }, + {"Ip6InUnknownProtos", &ipv6_tmp.Ip6InUnknownProtos, }, + {"Ip6InTruncatedPkts", &ipv6_tmp.Ip6InTruncatedPkts, }, + {"Ip6InDiscards", &ipv6_tmp.Ip6InDiscards, }, + {"Ip6InDelivers", &ipv6_tmp.Ip6InDelivers, }, + {"Ip6OutForwDatagrams", &ipv6_tmp.Ip6OutForwDatagrams, }, + {"Ip6OutRequests", &ipv6_tmp.Ip6OutRequests, }, + {"Ip6OutDiscards", &ipv6_tmp.Ip6OutDiscards, }, + {"Ip6OutNoRoutes", &ipv6_tmp.Ip6OutNoRoutes, }, + {"Ip6ReasmTimeout", &ipv6_tmp.Ip6ReasmTimeout, }, + {"Ip6ReasmReqds", &ipv6_tmp.Ip6ReasmReqds, }, + {"Ip6ReasmOKs", &ipv6_tmp.Ip6ReasmOKs, }, + {"Ip6ReasmFails", &ipv6_tmp.Ip6ReasmFails, }, + {"Ip6FragOKs", &ipv6_tmp.Ip6FragOKs, }, + {"Ip6FragFails", &ipv6_tmp.Ip6FragFails, }, + {"Ip6FragCreates", &ipv6_tmp.Ip6FragCreates, }, + {"Ip6InMcastPkts", &ipv6_tmp.Ip6InMcastPkts, }, + {"Ip6OutMcastPkts", &ipv6_tmp.Ip6OutMcastPkts, }, + + {"Icmp6InMsgs", &icmpv6_tmp.Icmp6InMsgs, }, + {"Icmp6InErrors", &icmpv6_tmp.Icmp6InErrors, }, + {"Icmp6InDestUnreachs", &icmpv6_tmp.Icmp6InDestUnreachs, }, + {"Icmp6InPktTooBigs", &icmpv6_tmp.Icmp6InPktTooBigs, }, + {"Icmp6InTimeExcds", &icmpv6_tmp.Icmp6InTimeExcds, }, + {"Icmp6InParmProblems", &icmpv6_tmp.Icmp6InParmProblems, }, + {"Icmp6InEchos", &icmpv6_tmp.Icmp6InEchos, }, + {"Icmp6InEchoReplies", &icmpv6_tmp.Icmp6InEchoReplies, }, + {"Icmp6InGroupMembQueries", &icmpv6_tmp.Icmp6InGroupMembQueries, }, + {"Icmp6InGroupMembResponses", + &icmpv6_tmp.Icmp6InGroupMembResponses, }, + {"Icmp6InGroupMembReductions", + &icmpv6_tmp.Icmp6InGroupMembReductions, }, + {"Icmp6InRouterSolicits", &icmpv6_tmp.Icmp6InRouterSolicits, }, + {"Icmp6InRouterAdvertisements", + &icmpv6_tmp.Icmp6InRouterAdvertisements, }, + {"Icmp6InNeighborSolicits", &icmpv6_tmp.Icmp6InNeighborSolicits, }, + {"Icmp6InNeighborAdvertisements", + &icmpv6_tmp.Icmp6InNeighborAdvertisements, }, + {"Icmp6InRedirects", &icmpv6_tmp.Icmp6InRedirects, }, + {"Icmp6OutMsgs", &icmpv6_tmp.Icmp6OutMsgs, }, + {"Icmp6OutDestUnreachs", &icmpv6_tmp.Icmp6OutDestUnreachs, }, + {"Icmp6OutPktTooBigs", &icmpv6_tmp.Icmp6OutPktTooBigs, }, + {"Icmp6OutTimeExcds", &icmpv6_tmp.Icmp6OutTimeExcds, }, + {"Icmp6OutParmProblems", &icmpv6_tmp.Icmp6OutParmProblems, }, + {"Icmp6OutEchoReplies", &icmpv6_tmp.Icmp6OutEchoReplies, }, + {"Icmp6OutRouterSolicits", &icmpv6_tmp.Icmp6OutRouterSolicits, }, + {"Icmp6OutNeighborSolicits",&icmpv6_tmp.Icmp6OutNeighborSolicits, }, + {"Icmp6OutNeighborAdvertisements", + &icmpv6_tmp.Icmp6OutNeighborAdvertisements, }, + {"Icmp6OutRedirects", &icmpv6_tmp.Icmp6OutRedirects, }, + {"Icmp6OutGroupMembResponses", + &icmpv6_tmp.Icmp6OutGroupMembResponses, }, + {"Icmp6OutGroupMembReductions", + &icmpv6_tmp.Icmp6OutGroupMembReductions, }, + + {"Udp6InDatagrams", &udpv6_tmp.Udp6InDatagrams, }, + {"Udp6NoPorts", &udpv6_tmp.Udp6NoPorts, }, + {"Udp6InErrors", &udpv6_tmp.Udp6InErrors, }, + {"Udp6OutDatagrams", &udpv6_tmp.Udp6OutDatagrams, }, +}; + +static int v6tab_entries = sizeof(v6tab)/sizeof(struct v6tab); + +void +photosyst(struct sstat *si) +{ + register int i, nr; + count_t cnts[MAXCNT]; + float lavg1, lavg5, lavg15; + FILE *fp; + char linebuf[1024], nam[64], origdir[1024]; + static char part_stats = 1; /* per-partition statistics ? */ + unsigned int major, minor; + struct shm_info shminfo; +#if HTTPSTATS + static int wwwvalid = 1; +#endif + + memset(si, 0, sizeof(struct sstat)); + + if ( getcwd(origdir, sizeof origdir) == NULL) + cleanstop(53); + + if ( chdir("/proc") == -1) + cleanstop(53); + + /* + ** gather various general statistics from the file /proc/stat and + ** store them in binary form + */ + if ( (fp = fopen("stat", "r")) != NULL) + { + while ( fgets(linebuf, sizeof(linebuf), fp) != NULL) + { + nr = sscanf(linebuf, + "%s %lld %lld %lld %lld %lld %lld %lld " + "%lld %lld %lld %lld %lld %lld %lld %lld ", + nam, + &cnts[0], &cnts[1], &cnts[2], &cnts[3], + &cnts[4], &cnts[5], &cnts[6], &cnts[7], + &cnts[8], &cnts[9], &cnts[10], &cnts[11], + &cnts[12], &cnts[13], &cnts[14]); + + if (nr < 2) /* headerline ? --> skip */ + continue; + + if ( strcmp("cpu", nam) == EQ) + { + si->cpu.all.utime = cnts[0]; + si->cpu.all.ntime = cnts[1]; + si->cpu.all.stime = cnts[2]; + si->cpu.all.itime = cnts[3]; + + if (nr > 5) /* 2.6 kernel? */ + { + si->cpu.all.wtime = cnts[4]; + si->cpu.all.Itime = cnts[5]; + si->cpu.all.Stime = cnts[6]; + + if (nr > 8) /* steal support */ + si->cpu.all.steal = cnts[7]; + + if (nr > 9) /* guest support */ + si->cpu.all.guest = cnts[8]; + } + continue; + } + + if ( strncmp("cpu", nam, 3) == EQ) + { + i = atoi(&nam[3]); + + if (i >= MAXCPU) + { + fprintf(stderr, + "cpu %s exceeds maximum of %d\n", + nam, MAXCPU); + continue; + } + + si->cpu.cpu[i].cpunr = i; + si->cpu.cpu[i].utime = cnts[0]; + si->cpu.cpu[i].ntime = cnts[1]; + si->cpu.cpu[i].stime = cnts[2]; + si->cpu.cpu[i].itime = cnts[3]; + + if (nr > 5) /* 2.6 kernel? */ + { + si->cpu.cpu[i].wtime = cnts[4]; + si->cpu.cpu[i].Itime = cnts[5]; + si->cpu.cpu[i].Stime = cnts[6]; + + if (nr > 8) /* steal support */ + si->cpu.cpu[i].steal = cnts[7]; + + if (nr > 9) /* guest support */ + si->cpu.cpu[i].guest = cnts[8]; + } + + si->cpu.nrcpu++; + continue; + } + + if ( strcmp("ctxt", nam) == EQ) + { + si->cpu.csw = cnts[0]; + continue; + } + + if ( strcmp("intr", nam) == EQ) + { + si->cpu.devint = cnts[0]; + continue; + } + + if ( strcmp("processes", nam) == EQ) + { + si->cpu.nprocs = cnts[0]; + continue; + } + + if ( strcmp("swap", nam) == EQ) /* < 2.6 */ + { + si->mem.swins = cnts[0]; + si->mem.swouts = cnts[1]; + continue; + } + } + + fclose(fp); + + if (si->cpu.nrcpu == 0) + si->cpu.nrcpu = 1; + } + + /* + ** gather loadaverage values from the file /proc/loadavg and + ** store them in binary form + */ + if ( (fp = fopen("loadavg", "r")) != NULL) + { + if ( fgets(linebuf, sizeof(linebuf), fp) != NULL) + { + if ( sscanf(linebuf, "%f %f %f", + &lavg1, &lavg5, &lavg15) == 3) + { + si->cpu.lavg1 = lavg1; + si->cpu.lavg5 = lavg5; + si->cpu.lavg15 = lavg15; + } + } + + fclose(fp); + } + + /* + ** gather frequency scaling info. + ** sources (in order of preference): + ** /sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state + ** or + ** /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq + ** /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq + ** + ** store them in binary form + */ + static char fn[80]; + int didone=0; + + for (i = 0; i < si->cpu.nrcpu; ++i) + { + long long f=0; + + sprintf(fn, + "/sys/devices/system/cpu/cpu%d/cpufreq/stats/time_in_state", + i); + + if ((fp=fopen(fn, "r")) != 0) + { + long long hits=0; + long long maxfreq=0; + long long cnt=0; + long long sum=0; + + while (fscanf(fp, "%lld %lld", &f, &cnt) == 2) + { + f /= 1000; + sum += (f*cnt); + hits += cnt; + + if (f > maxfreq) + maxfreq=f; + didone=1; + } + + si->cpu.cpu[i].freqcnt.maxfreq = maxfreq; + si->cpu.cpu[i].freqcnt.cnt = sum; + si->cpu.cpu[i].freqcnt.ticks = hits; + + fclose(fp); + } + else + { // governor statistics not available + sprintf(fn, + "/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq", + i); + + if ((fp=fopen(fn, "r")) != 0) + { + if (fscanf(fp, "%lld", &f) == 1) + { + // convert KHz to MHz + si->cpu.cpu[i].freqcnt.maxfreq =f/1000; + } + + fclose(fp); + } + else + { + si->cpu.cpu[i].freqcnt.maxfreq=0; + } + + sprintf(fn, + "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq", + i); + + if ((fp=fopen(fn, "r")) != 0) + { + if (fscanf(fp, "%lld", &f) == 1) + { + // convert KHz to MHz + si->cpu.cpu[i].freqcnt.cnt = f/1000; + si->cpu.cpu[i].freqcnt.ticks = 0; + didone=1; + } + + fclose(fp); + } + else + { + si->cpu.cpu[i].freqcnt.cnt = 0; + si->cpu.cpu[i].freqcnt.ticks = 0; + } + } + } // for all CPUs + + if (!didone) // did not get processor freq statistics. + // use /proc/cpuinfo + { + if ( (fp = fopen("cpuinfo", "r")) != NULL) + { + // get information from the lines + // processor\t: 0 + // cpu MHz\t\t: 800.000 + + int cpuno=-1; + + while ( fgets(linebuf, sizeof(linebuf), fp) != NULL) + { + if (memcmp(linebuf, "processor", 9)== EQ) + sscanf(linebuf, "%*s %*s %d", &cpuno); + + if (memcmp(linebuf, "cpu MHz", 7) == EQ) + { + if (cpuno >= 0 && cpuno < si->cpu.nrcpu) + { + sscanf(linebuf, + "%*s %*s %*s %lld", + &(si->cpu.cpu[cpuno].freqcnt.cnt)); + } + } + } + + fclose(fp); + } + + } + + /* + ** gather virtual memory statistics from the file /proc/vmstat and + ** store them in binary form (>= kernel 2.6) + */ + if ( (fp = fopen("vmstat", "r")) != NULL) + { + while ( fgets(linebuf, sizeof(linebuf), fp) != NULL) + { + nr = sscanf(linebuf, "%s %lld", nam, &cnts[0]); + + if (nr < 2) /* headerline ? --> skip */ + continue; + + if ( strcmp("pswpin", nam) == EQ) + { + si->mem.swins = cnts[0]; + continue; + } + + if ( strcmp("pswpout", nam) == EQ) + { + si->mem.swouts = cnts[0]; + continue; + } + + if ( strncmp("pgscan_", nam, 7) == EQ) + { + si->mem.pgscans += cnts[0]; + continue; + } + + if ( strncmp("pgsteal_", nam, 8) == EQ) + { + si->mem.pgsteal += cnts[0]; + continue; + } + + if ( strcmp("allocstall", nam) == EQ) + { + si->mem.allocstall = cnts[0]; + continue; + } + } + + fclose(fp); + } + + /* + ** gather memory-related statistics from the file /proc/meminfo and + ** store them in binary form + ** + ** in the file /proc/meminfo a 2.4 kernel starts with two lines + ** headed by the strings "Mem:" and "Swap:" containing all required + ** fields, except proper value for page cache + ** if these lines are present we try to skip parsing the rest + ** of the lines; if these lines are not present we should get the + ** required field from other lines + */ + si->mem.physmem = (count_t)-1; + si->mem.freemem = (count_t)-1; + si->mem.buffermem = (count_t)-1; + si->mem.cachemem = (count_t)-1; + si->mem.slabmem = (count_t) 0; + si->mem.slabreclaim = (count_t) 0; + si->mem.shmem = (count_t) 0; + si->mem.totswap = (count_t)-1; + si->mem.freeswap = (count_t)-1; + si->mem.committed = (count_t) 0; + + if ( (fp = fopen("meminfo", "r")) != NULL) + { + while ( fgets(linebuf, sizeof(linebuf), fp) != NULL) + { + nr = sscanf(linebuf, + "%s %lld %lld %lld %lld %lld %lld %lld " + "%lld %lld %lld\n", + nam, + &cnts[0], &cnts[1], &cnts[2], &cnts[3], + &cnts[4], &cnts[5], &cnts[6], &cnts[7], + &cnts[8], &cnts[9]); + + if (nr < 2) /* headerline ? --> skip */ + continue; + + if ( strcmp("Mem:", nam) == EQ) + { + si->mem.physmem = cnts[0] / pagesize; + si->mem.freemem = cnts[2] / pagesize; + si->mem.buffermem = cnts[4] / pagesize; + } + else if ( strcmp("Swap:", nam) == EQ) + { + si->mem.totswap = cnts[0] / pagesize; + si->mem.freeswap = cnts[2] / pagesize; + } + else if (strcmp("Cached:", nam) == EQ) + { + if (si->mem.cachemem == (count_t)-1) + { + si->mem.cachemem = + cnts[0]*1024/pagesize; + } + } + else if (strcmp("Dirty:", nam) == EQ) + { + si->mem.cachedrt = + cnts[0]*1024/pagesize; + } + else if (strcmp("MemTotal:", nam) == EQ) + { + if (si->mem.physmem == (count_t)-1) + { + si->mem.physmem = + cnts[0]*1024/pagesize; + } + } + else if (strcmp("MemFree:", nam) == EQ) + { + if (si->mem.freemem == (count_t)-1) + { + si->mem.freemem = + cnts[0]*1024/pagesize; + } + } + else if (strcmp("Buffers:", nam) == EQ) + { + if (si->mem.buffermem == (count_t)-1) + { + si->mem.buffermem = + cnts[0]*1024/pagesize; + } + } + else if (strcmp("Shmem:", nam) == EQ) + { + si->mem.shmem = cnts[0]*1024/pagesize; + } + else if (strcmp("SwapTotal:", nam) == EQ) + { + if (si->mem.totswap == (count_t)-1) + { + si->mem.totswap = + cnts[0]*1024/pagesize; + } + } + else if (strcmp("SwapFree:", nam) == EQ) + { + if (si->mem.freeswap == (count_t)-1) + { + si->mem.freeswap = + cnts[0]*1024/pagesize; + } + } + else if (strcmp("Slab:", nam) == EQ) + { + si->mem.slabmem = cnts[0]*1024/pagesize; + } + else if (strcmp("SReclaimable:", nam) == EQ) + { + si->mem.slabreclaim = cnts[0]*1024/ + pagesize; + } + else if (strcmp("Committed_AS:", nam) == EQ) + { + si->mem.committed = cnts[0]*1024/ + pagesize; + } + else if (strcmp("CommitLimit:", nam) == EQ) + { + si->mem.commitlim = cnts[0]*1024/ + pagesize; + } + else if (strcmp("HugePages_Total:", nam) == EQ) + { + si->mem.tothugepage = cnts[0]; + } + else if (strcmp("HugePages_Free:", nam) == EQ) + { + si->mem.freehugepage = cnts[0]; + } + else if (strcmp("Hugepagesize:", nam) == EQ) + { + si->mem.hugepagesz = cnts[0]*1024; + } + } + + fclose(fp); + } + + /* + ** gather vmware-related statistics from /proc/vmmemctl + ** (only present if balloon driver enabled) + */ + si->mem.vmwballoon = (count_t) 0; + + if ( (fp = fopen("vmmemctl", "r")) != NULL) + { + while ( fgets(linebuf, sizeof(linebuf), fp) != NULL) + { + nr = sscanf(linebuf, "%s %lld ", nam, &cnts[0]); + + if ( strcmp("current:", nam) == EQ) + { + si->mem.vmwballoon = cnts[0]; + break; + } + } + + fclose(fp); + } + + /* + ** gather network-related statistics + ** - interface stats from the file /proc/net/dev + ** - IPv4 stats from the file /proc/net/snmp + ** - IPv6 stats from the file /proc/net/snmp6 + */ + + /* + ** interface statistics + */ + if ( (fp = fopen("net/dev", "r")) != NULL) + { + char *cp; + + i = 0; + + while ( fgets(linebuf, sizeof(linebuf), fp) != NULL) + { + if ( (cp = strchr(linebuf, ':')) != NULL) + *cp = ' '; /* substitute ':' by space */ + + nr = sscanf(linebuf, + "%15s %lld %lld %lld %lld %lld %lld %lld " + "%lld %lld %lld %lld %lld %lld %lld %lld " + "%lld\n", + si->intf.intf[i].name, + &(si->intf.intf[i].rbyte), + &(si->intf.intf[i].rpack), + &(si->intf.intf[i].rerrs), + &(si->intf.intf[i].rdrop), + &(si->intf.intf[i].rfifo), + &(si->intf.intf[i].rframe), + &(si->intf.intf[i].rcompr), + &(si->intf.intf[i].rmultic), + &(si->intf.intf[i].sbyte), + &(si->intf.intf[i].spack), + &(si->intf.intf[i].serrs), + &(si->intf.intf[i].sdrop), + &(si->intf.intf[i].sfifo), + &(si->intf.intf[i].scollis), + &(si->intf.intf[i].scarrier), + &(si->intf.intf[i].scompr)); + + if (nr == 17) /* skip header & lines without stats */ + { + if (++i >= MAXINTF-1) + break; + } + } + + si->intf.intf[i].name[0] = '\0'; /* set terminator for table */ + si->intf.nrintf = i; + + fclose(fp); + } + + /* + ** IP version 4 statistics + */ + if ( (fp = fopen("net/snmp", "r")) != NULL) + { + while ( fgets(linebuf, sizeof(linebuf), fp) != NULL) + { + nr = sscanf(linebuf, + "%s %lld %lld %lld %lld %lld %lld %lld %lld %lld " + "%lld %lld %lld %lld %lld %lld %lld %lld %lld %lld " + "%lld %lld %lld %lld %lld %lld %lld %lld %lld %lld " + "%lld %lld %lld %lld %lld %lld %lld %lld %lld %lld " + "%lld\n", + nam, + &cnts[0], &cnts[1], &cnts[2], &cnts[3], + &cnts[4], &cnts[5], &cnts[6], &cnts[7], + &cnts[8], &cnts[9], &cnts[10], &cnts[11], + &cnts[12], &cnts[13], &cnts[14], &cnts[15], + &cnts[16], &cnts[17], &cnts[18], &cnts[19], + &cnts[20], &cnts[21], &cnts[22], &cnts[23], + &cnts[24], &cnts[25], &cnts[26], &cnts[27], + &cnts[28], &cnts[29], &cnts[30], &cnts[31], + &cnts[32], &cnts[33], &cnts[34], &cnts[35], + &cnts[36], &cnts[37], &cnts[38], &cnts[39]); + + if (nr < 2) /* headerline ? --> skip */ + continue; + + if ( strcmp("Ip:", nam) == 0) + { + memcpy(&si->net.ipv4, cnts, + sizeof si->net.ipv4); + continue; + } + + if ( strcmp("Icmp:", nam) == 0) + { + memcpy(&si->net.icmpv4, cnts, + sizeof si->net.icmpv4); + continue; + } + + if ( strcmp("Tcp:", nam) == 0) + { + memcpy(&si->net.tcp, cnts, + sizeof si->net.tcp); + continue; + } + + if ( strcmp("Udp:", nam) == 0) + { + memcpy(&si->net.udpv4, cnts, + sizeof si->net.udpv4); + continue; + } + } + + fclose(fp); + } + + /* + ** IP version 6 statistics + */ + memset(&ipv6_tmp, 0, sizeof ipv6_tmp); + memset(&icmpv6_tmp, 0, sizeof icmpv6_tmp); + memset(&udpv6_tmp, 0, sizeof udpv6_tmp); + + if ( (fp = fopen("net/snmp6", "r")) != NULL) + { + count_t countval; + int cur = 0; + + /* + ** one name-value pair per line + */ + while ( fgets(linebuf, sizeof(linebuf), fp) != NULL) + { + nr = sscanf(linebuf, "%s %lld", nam, &countval); + + if (nr < 2) /* unexpected line ? --> skip */ + continue; + + if (strcmp(v6tab[cur].nam, nam) == 0) + { + *(v6tab[cur].val) = countval; + } + else + { + for (cur=0; cur < v6tab_entries; cur++) + if (strcmp(v6tab[cur].nam, nam) == 0) + break; + + if (cur < v6tab_entries) /* found ? */ + *(v6tab[cur].val) = countval; + } + + if (++cur >= v6tab_entries) + cur = 0; + } + + memcpy(&si->net.ipv6, &ipv6_tmp, sizeof ipv6_tmp); + memcpy(&si->net.icmpv6, &icmpv6_tmp, sizeof icmpv6_tmp); + memcpy(&si->net.udpv6, &udpv6_tmp, sizeof udpv6_tmp); + + fclose(fp); + } + + /* + ** check if extended partition-statistics are provided < kernel 2.6 + */ + if ( part_stats && (fp = fopen("partitions", "r")) != NULL) + { + char diskname[256]; + + i = 0; + + while ( fgets(linebuf, sizeof(linebuf), fp) ) + { + nr = sscanf(linebuf, + "%*d %*d %*d %255s %lld %*d %lld %*d " + "%lld %*d %lld %*d %*d %lld %lld", + diskname, + &(si->dsk.dsk[i].nread), + &(si->dsk.dsk[i].nrsect), + &(si->dsk.dsk[i].nwrite), + &(si->dsk.dsk[i].nwsect), + &(si->dsk.dsk[i].io_ms), + &(si->dsk.dsk[i].avque) ); + + /* + ** check if this line concerns the entire disk + ** or just one of the partitions of a disk (to be + ** skipped) + */ + if (nr == 7) /* full stats-line ? */ + { + if ( isdisk(0, 0, diskname, + &(si->dsk.dsk[i]), + MAXDKNAM) != DSKTYPE) + continue; + + if (++i >= MAXDSK-1) + break; + } + } + + si->dsk.dsk[i].name[0] = '\0'; /* set terminator for table */ + si->dsk.ndsk = i; + + fclose(fp); + + if (i == 0) + part_stats = 0; /* do not try again for next cycles */ + } + + + /* + ** check if disk-statistics are provided (kernel 2.6 onwards) + */ + if ( (fp = fopen("diskstats", "r")) != NULL) + { + char diskname[256]; + struct perdsk tmpdsk; + + si->dsk.ndsk = 0; + si->dsk.nmdd = 0; + si->dsk.nlvm = 0; + + while ( fgets(linebuf, sizeof(linebuf), fp) ) + { + nr = sscanf(linebuf, + "%d %d %255s %lld %*d %lld %*d " + "%lld %*d %lld %*d %*d %lld %lld", + &major, &minor, diskname, + &tmpdsk.nread, &tmpdsk.nrsect, + &tmpdsk.nwrite, &tmpdsk.nwsect, + &tmpdsk.io_ms, &tmpdsk.avque ); + + /* + ** check if this line concerns the entire disk + ** or just one of the partitions of a disk (to be + ** skipped) + */ + if (nr == 9) /* full stats-line ? */ + { + switch ( isdisk(major, minor, diskname, + &tmpdsk, MAXDKNAM) ) + { + case NONTYPE: + continue; + + case DSKTYPE: + if (si->dsk.ndsk < MAXDSK-1) + si->dsk.dsk[si->dsk.ndsk++] = tmpdsk; + break; + + case MDDTYPE: + if (si->dsk.nmdd < MAXMDD-1) + si->dsk.mdd[si->dsk.nmdd++] = tmpdsk; + break; + + case LVMTYPE: + if (si->dsk.nlvm < MAXLVM-1) + si->dsk.lvm[si->dsk.nlvm++] = tmpdsk; + break; + } + } + } + + /* + ** set terminator for table + */ + si->dsk.dsk[si->dsk.ndsk].name[0] = '\0'; + si->dsk.mdd[si->dsk.nmdd].name[0] = '\0'; + si->dsk.lvm[si->dsk.nlvm].name[0] = '\0'; + + fclose(fp); + } + + /* + ** get information about the shared memory statistics + */ + if ( shmctl(0, SHM_INFO, (struct shmid_ds *)&shminfo) != -1) + { + si->mem.shmrss = shminfo.shm_rss; + si->mem.shmswp = shminfo.shm_swp; + } + + /* + ** NFS server statistics + */ + if ( (fp = fopen("net/rpc/nfsd", "r")) != NULL) + { + char label[32]; + count_t cnt[40]; + + /* + ** every line starts with a small label, + ** followed by upto 60 counters + */ + while ( fgets(linebuf, sizeof(linebuf), fp) != NULL) + { + memset(cnt, 0, sizeof cnt); + + nr = sscanf(linebuf, "%31s %lld %lld %lld %lld %lld" + "%lld %lld %lld %lld %lld" + "%lld %lld %lld %lld %lld" + "%lld %lld %lld %lld %lld" + "%lld %lld %lld %lld %lld" + "%lld %lld %lld %lld %lld" + "%lld %lld %lld %lld %lld" + "%lld %lld %lld %lld %lld", + label, + &cnt[0], &cnt[1], &cnt[2], &cnt[3], + &cnt[4], &cnt[5], &cnt[6], &cnt[7], + &cnt[8], &cnt[9], &cnt[10], &cnt[11], + &cnt[12], &cnt[13], &cnt[14], &cnt[15], + &cnt[16], &cnt[17], &cnt[18], &cnt[19], + &cnt[20], &cnt[21], &cnt[22], &cnt[23], + &cnt[24], &cnt[25], &cnt[26], &cnt[27], + &cnt[28], &cnt[29], &cnt[30], &cnt[31], + &cnt[32], &cnt[33], &cnt[34], &cnt[35], + &cnt[36], &cnt[37], &cnt[38], &cnt[39]); + + if (nr < 2) // unexpected empty line ? + continue; + + if (strcmp(label, "rc") == 0) + { + si->nfs.server.rchits = cnt[0]; + si->nfs.server.rcmiss = cnt[1]; + si->nfs.server.rcnoca = cnt[2]; + + continue; + } + + if (strcmp(label, "io") == 0) + { + si->nfs.server.nrbytes = cnt[0]; + si->nfs.server.nwbytes = cnt[1]; + + continue; + } + + if (strcmp(label, "net") == 0) + { + si->nfs.server.netcnt = cnt[0]; + si->nfs.server.netudpcnt = cnt[1]; + si->nfs.server.nettcpcnt = cnt[2]; + si->nfs.server.nettcpcon = cnt[3]; + + continue; + } + + if (strcmp(label, "rpc") == 0) + { + si->nfs.server.rpccnt = cnt[0]; + si->nfs.server.rpcbadfmt = cnt[1]; + si->nfs.server.rpcbadaut = cnt[2]; + si->nfs.server.rpcbadcln = cnt[3]; + + continue; + } + // + // first counter behind 'proc..' is number of + // counters that follow + if (strcmp(label, "proc2") == 0) + { + si->nfs.server.rpcread += cnt[7]; // offset+1 + si->nfs.server.rpcwrite += cnt[9]; // offset+1 + continue; + } + if (strcmp(label, "proc3") == 0) + { + si->nfs.server.rpcread += cnt[7]; // offset+1 + si->nfs.server.rpcwrite += cnt[8]; // offset+1 + continue; + } + if (strcmp(label, "proc4ops") == 0) + { + si->nfs.server.rpcread += cnt[26]; // offset+1 + si->nfs.server.rpcwrite += cnt[39]; // offset+1 + continue; + } + } + + fclose(fp); + } + + /* + ** NFS client statistics + */ + if ( (fp = fopen("net/rpc/nfs", "r")) != NULL) + { + char label[32]; + count_t cnt[10]; + + /* + ** every line starts with a small label, + ** followed by counters + */ + while ( fgets(linebuf, sizeof(linebuf), fp) != NULL) + { + memset(cnt, 0, sizeof cnt); + + nr = sscanf(linebuf, "%31s %lld %lld %lld %lld %lld" + "%lld %lld %lld %lld %lld", + label, + &cnt[0], &cnt[1], &cnt[2], &cnt[3], + &cnt[4], &cnt[5], &cnt[6], &cnt[7], + &cnt[8], &cnt[9]); + + if (nr < 2) // unexpected empty line ? + continue; + + if (strcmp(label, "rpc") == 0) + { + si->nfs.client.rpccnt = cnt[0]; + si->nfs.client.rpcretrans = cnt[1]; + si->nfs.client.rpcautrefresh = cnt[2]; + continue; + } + + // first counter behind 'proc..' is number of + // counters that follow + if (strcmp(label, "proc2") == 0) + { + si->nfs.client.rpcread += cnt[7]; // offset+1 + si->nfs.client.rpcwrite += cnt[9]; // offset+1 + continue; + } + if (strcmp(label, "proc3") == 0) + { + si->nfs.client.rpcread += cnt[7]; // offset+1 + si->nfs.client.rpcwrite += cnt[8]; // offset+1 + continue; + } + if (strcmp(label, "proc4") == 0) + { + si->nfs.client.rpcread += cnt[2]; // offset+1 + si->nfs.client.rpcwrite += cnt[3]; // offset+1 + continue; + } + } + + fclose(fp); + } + + /* + ** NFS client: per-mount statistics + */ + regainrootprivs(); + + if ( (fp = fopen("self/mountstats", "r")) != NULL) + { + char mountdev[128], fstype[32], label[32]; + count_t cnt[8]; + + i = 0; + + while ( fgets(linebuf, sizeof(linebuf), fp) != NULL) + { + // if 'device' line, just remember the mounted device + if (sscanf(linebuf, + "device %127s mounted on %*s with fstype %31s", + mountdev, fstype) == 2) + { + continue; + } + + if (memcmp(fstype, "nfs", 3) != 0) + continue; + + // this is line with NFS client stats + nr = sscanf(linebuf, + "%31s %lld %lld %lld %lld %lld %lld %lld %lld", + label, &cnt[0], &cnt[1], &cnt[2], &cnt[3], + &cnt[4], &cnt[5], &cnt[6], &cnt[7]); + + if (nr >= 2 ) + { + if (strcmp(label, "age:") == 0) + { + strcpy(si->nfs.nfsmounts.nfsmnt[i].mountdev, + mountdev); + + si->nfs.nfsmounts.nfsmnt[i].age = cnt[0]; + } + + if (strcmp(label, "bytes:") == 0) + { + si->nfs.nfsmounts.nfsmnt[i].bytesread = + cnt[0]; + si->nfs.nfsmounts.nfsmnt[i].byteswrite = + cnt[1]; + si->nfs.nfsmounts.nfsmnt[i].bytesdread = + cnt[2]; + si->nfs.nfsmounts.nfsmnt[i].bytesdwrite = + cnt[3]; + si->nfs.nfsmounts.nfsmnt[i].bytestotread = + cnt[4]; + si->nfs.nfsmounts.nfsmnt[i].bytestotwrite = + cnt[5]; + si->nfs.nfsmounts.nfsmnt[i].pagesmread = + cnt[6]; + si->nfs.nfsmounts.nfsmnt[i].pagesmwrite = + cnt[7]; + + if (++i >= MAXNFSMOUNT-1) + break; + } + } + } + + si->nfs.nfsmounts.nrmounts = i; + + fclose(fp); + } + + if (! droprootprivs()) + cleanstop(42); + + /* + ** Container statistics (if any) + */ + if ( (fp = fopen("user_beancounters", "r")) != NULL) + { + unsigned long ctid; + char label[32]; + count_t cnt; + + i = -1; + + /* + ** lines introducing a new container have an extra + ** field with the container id at the beginning. + */ + while ( fgets(linebuf, sizeof(linebuf), fp) != NULL) + { + nr = sscanf(linebuf, "%lu: %31s %lld", + &ctid, label, &cnt); + + if (nr == 3) // new container ? + { + if (++i >= MAXCONTAINER) + break; + + si->cfs.cont[i].ctid = ctid; + } + else + { + nr = sscanf(linebuf, "%31s %lld", label, &cnt); + + if (nr != 2) + continue; + } + + if (i == -1) // no container defined yet + continue; + + if (strcmp(label, "numproc") == 0) + { + si->cfs.cont[i].numproc = cnt; + continue; + } + + if (strcmp(label, "physpages") == 0) + { + si->cfs.cont[i].physpages = cnt; + continue; + } + } + + fclose(fp); + + si->cfs.nrcontainer = i+1; + + if ( (fp = fopen("vz/vestat", "r")) != NULL) + { + unsigned long ctid; + count_t cnt[8]; + + /* + ** relevant lines start with container id + */ + while ( fgets(linebuf, sizeof(linebuf), fp) != NULL) + { + nr = sscanf(linebuf, "%lu %lld %lld %lld %lld" + "%lld %lld %lld %lld %lld", + &ctid, + &cnt[0], &cnt[1], &cnt[2], &cnt[3], + &cnt[4], &cnt[5], &cnt[6], &cnt[7], + &cnt[8]); + + if (nr < 9) // irrelevant contents + continue; + + // relevant stats: search for containerid + for (i=0; i < si->cfs.nrcontainer; i++) + { + if (si->cfs.cont[i].ctid == ctid) + break; + } + + if (i >= si->cfs.nrcontainer) + continue; // container not found + + si->cfs.cont[i].user = cnt[0]; + si->cfs.cont[i].nice = cnt[1]; + si->cfs.cont[i].system = cnt[2]; + si->cfs.cont[i].uptime = cnt[3]; + } + + fclose(fp); + } + } + + /* + ** return to original directory + */ + if ( chdir(origdir) == -1) + cleanstop(53); + + /* + ** fetch application-specific counters + */ +#if HTTPSTATS + if ( wwwvalid) + wwwvalid = getwwwstat(80, &(si->www)); +#endif +} + +/* +** set of subroutines to determine which disks should be monitored +** and to translate name strings into (shorter) name strings +*/ +static void +nullmodname(unsigned int major, unsigned int minor, + char *curname, struct perdsk *px, int maxlen) +{ + strncpy(px->name, curname, maxlen-1); + *(px->name+maxlen-1) = 0; +} + +static void +abbrevname1(unsigned int major, unsigned int minor, + char *curname, struct perdsk *px, int maxlen) +{ + char cutype[128]; + int hostnum, busnum, targetnum, lunnum; + + sscanf(curname, "%[^/]/host%d/bus%d/target%d/lun%d", + cutype, &hostnum, &busnum, &targetnum, &lunnum); + + snprintf(px->name, maxlen, "%c-h%db%dt%d", + cutype[0], hostnum, busnum, targetnum); +} + +/* +** recognize LVM logical volumes +*/ +#define NUMDMHASH 64 +#define DMHASH(x,y) (((x)+(y))%NUMDMHASH) +#define MAPDIR "/dev/mapper" + +struct devmap { + unsigned int major; + unsigned int minor; + char name[MAXDKNAM]; + struct devmap *next; +}; + +static void +lvmmapname(unsigned int major, unsigned int minor, + char *curname, struct perdsk *px, int maxlen) +{ + static int firstcall = 1; + static struct devmap *devmaps[NUMDMHASH], *dmp; + int hashix; + + /* + ** setup a list of major-minor numbers of dm-devices with their + ** corresponding name + */ + if (firstcall) + { + DIR *dirp; + struct dirent *dentry; + struct stat statbuf; + char path[64]; + + if ( (dirp = opendir(MAPDIR)) ) + { + /* + ** read every directory-entry and search for + ** block devices + */ + while ( (dentry = readdir(dirp)) ) + { + snprintf(path, sizeof path, "%s/%s", + MAPDIR, dentry->d_name); + + if ( stat(path, &statbuf) == -1 ) + continue; + + if ( ! S_ISBLK(statbuf.st_mode) ) + continue; + /* + ** allocate struct to store name + */ + if ( !(dmp = malloc(sizeof (struct devmap)))) + continue; + + /* + ** store info in hash list + */ + strncpy(dmp->name, dentry->d_name, MAXDKNAM); + dmp->name[MAXDKNAM-1] = 0; + dmp->major = major(statbuf.st_rdev); + dmp->minor = minor(statbuf.st_rdev); + + hashix = DMHASH(dmp->major, dmp->minor); + + dmp->next = devmaps[hashix]; + + devmaps[hashix] = dmp; + } + + closedir(dirp); + } + + firstcall = 0; + } + + /* + ** find info in hash list + */ + hashix = DMHASH(major, minor); + dmp = devmaps[hashix]; + + while (dmp) + { + if (dmp->major == major && dmp->minor == minor) + { + /* + ** info found in hash list; fill proper name + */ + strncpy(px->name, dmp->name, maxlen-1); + *(px->name+maxlen-1) = 0; + return; + } + + dmp = dmp->next; + } + + /* + ** info not found in hash list; fill original name + */ + strncpy(px->name, curname, maxlen-1); + *(px->name+maxlen-1) = 0; +} + +/* +** this table is used in the function isdisk() +** +** table contains the names (in regexp format) of disks +** to be recognized, together with a function to modify +** the name-strings (i.e. to abbreviate long strings); +** some frequently found names (like 'loop' and 'ram') +** are also recognized to skip them as fast as possible +*/ +static struct { + char *regexp; + regex_t compreg; + void (*modname)(unsigned int, unsigned int, + char *, struct perdsk *, int); + int retval; +} validdisk[] = { + { "^ram[0-9][0-9]*$", {0}, (void *)0, NONTYPE, }, + { "^loop[0-9][0-9]*$", {0}, (void *)0, NONTYPE, }, + { "^sd[a-z][a-z]*$", {0}, nullmodname, DSKTYPE, }, + { "^dm-[0-9][0-9]*$", {0}, lvmmapname, LVMTYPE, }, + { "^md[0-9][0-9]*$", {0}, nullmodname, MDDTYPE, }, + { "^vd[a-z][a-z]*$", {0}, nullmodname, DSKTYPE, }, + { "^hd[a-z]$", {0}, nullmodname, DSKTYPE, }, + { "^rd/c[0-9][0-9]*d[0-9][0-9]*$", {0}, nullmodname, DSKTYPE, }, + { "^cciss/c[0-9][0-9]*d[0-9][0-9]*$", {0}, nullmodname, DSKTYPE, }, + { "^fio[a-z][a-z]*$", {0}, nullmodname, DSKTYPE, }, + { "/host.*/bus.*/target.*/lun.*/disc", {0}, abbrevname1, DSKTYPE, }, + { "^xvd[a-z][a-z]*[0-9]*$", {0}, nullmodname, DSKTYPE, }, + { "^dasd[a-z][a-z]*$", {0}, nullmodname, DSKTYPE, }, + { "^mmcblk[0-9][0-9]*$", {0}, nullmodname, DSKTYPE, }, + { "^emcpower[a-z][a-z]*$", {0}, nullmodname, DSKTYPE, }, +}; + +static int +isdisk(unsigned int major, unsigned int minor, + char *curname, struct perdsk *px, int maxlen) +{ + static int firstcall = 1; + register int i; + + if (firstcall) /* compile the regular expressions */ + { + for (i=0; i < sizeof validdisk/sizeof validdisk[0]; i++) + regcomp(&validdisk[i].compreg, validdisk[i].regexp, + REG_NOSUB); + firstcall = 0; + } + + /* + ** try to recognize one of the compiled regular expressions + */ + for (i=0; i < sizeof validdisk/sizeof validdisk[0]; i++) + { + if (regexec(&validdisk[i].compreg, curname, 0, NULL, 0) == 0) + { + /* + ** name-string recognized; modify name-string + */ + if (validdisk[i].retval != NONTYPE) + (*validdisk[i].modname)(major, minor, + curname, px, maxlen); + + return validdisk[i].retval; + } + } + + return NONTYPE; +} + +/* +** LINUX SPECIFIC: +** Determine boot-time of this system (as number of jiffies since 1-1-1970). +*/ +unsigned long long +getbootlinux(long hertz) +{ + int cpid; + char tmpbuf[1280]; + FILE *fp; + unsigned long startticks; + unsigned long long bootjiffies = 0; + struct timespec ts; + + /* + ** dirty hack to get the boottime, since the + ** Linux 2.6 kernel (2.6.5) does not return a proper + ** boottime-value with the times() system call :-( + */ + if ( (cpid = fork()) == 0 ) + { + /* + ** child just waiting to be killed by parent + */ + pause(); + } + else + { + /* + ** parent determines start-time (in jiffies since boot) + ** of the child and calculates the boottime in jiffies + ** since 1-1-1970 + */ + (void) clock_gettime(CLOCK_REALTIME, &ts); // get current + bootjiffies = 1LL * ts.tv_sec * hertz + + 1LL * ts.tv_nsec * hertz / 1000000000LL; + + snprintf(tmpbuf, sizeof tmpbuf, "/proc/%d/stat", cpid); + + if ( (fp = fopen(tmpbuf, "r")) != NULL) + { + if ( fscanf(fp, "%*d (%*[^)]) %*c %*d %*d %*d %*d " + "%*d %*d %*d %*d %*d %*d %*d %*d " + "%*d %*d %*d %*d %*d %*d %lu", + &startticks) == 1) + { + bootjiffies -= startticks; + } + + fclose(fp); + } + + /* + ** kill the child and get rid of the zombie + */ + kill(cpid, SIGKILL); + (void) wait((int *)0); + } + + return bootjiffies; +} + +#if HTTPSTATS +/* +** retrieve statistics from local HTTP daemons +** via http://localhost/server-status?auto +*/ +int +getwwwstat(unsigned short port, struct wwwstat *wp) +{ + int sockfd, tobefound; + FILE *sockfp; + struct sockaddr_in sockname; + char linebuf[4096]; + char label[512]; + long long value; + + memset(wp, 0, sizeof *wp); + + /* + ** allocate a socket and connect to the local HTTP daemon + */ + if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) + return 0; + + sockname.sin_family = AF_INET; + sockname.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + sockname.sin_port = htons(port); + + if ( connect(sockfd, (struct sockaddr *) &sockname, + sizeof sockname) == -1) + { + close(sockfd); + return 0; + } + + /* + ** write a GET-request for /server-status + */ + if ( write(sockfd, HTTPREQ, sizeof HTTPREQ) < sizeof HTTPREQ) + { + close(sockfd); + return 0; + } + + /* + ** remap socket descriptor to a stream to allow stdio calls + */ + sockfp = fdopen(sockfd, "r+"); + + /* + ** read response line by line + */ + tobefound = 5; /* number of values to be searched */ + + while ( fgets(linebuf, sizeof linebuf, sockfp) && tobefound) + { + /* + ** handle line containing status code + */ + if ( strncmp(linebuf, "HTTP/", 5) == 0) + { + sscanf(linebuf, "%511s %lld %*s\n", label, &value); + + if (value != 200) /* HTTP-request okay? */ + { + fclose(sockfp); + close(sockfd); + return 0; + } + + continue; + } + + /* + ** decode line and search for the required counters + */ + if (sscanf(linebuf, "%511[^:]: %lld\n", label, &value) == 2) + { + if ( strcmp(label, "Total Accesses") == 0) + { + wp->accesses = value; + tobefound--; + } + + if ( strcmp(label, "Total kBytes") == 0) + { + wp->totkbytes = value; + tobefound--; + } + + if ( strcmp(label, "Uptime") == 0) + { + wp->uptime = value; + tobefound--; + } + + if ( strcmp(label, "BusyWorkers") == 0) + { + wp->bworkers = value; + tobefound--; + } + + if ( strcmp(label, "IdleWorkers") == 0) + { + wp->iworkers = value; + tobefound--; + } + } + } + + fclose(sockfp); + close(sockfd); + + return 1; +} +#endif diff --git a/sources/photosyst.h b/sources/photosyst.h new file mode 100644 index 0000000..a7c69b4 --- /dev/null +++ b/sources/photosyst.h @@ -0,0 +1,299 @@ +/* +** ATOP - System & Process Monitor +** +** The program 'atop' offers the possibility to view the activity of +** the system on system-level as well as process-level. +** +** Include-file describing system-level counters maintained. +** ================================================================ +** Author: Gerlof Langeveld +** E-mail: gerlof.langeveld@atoptool.nl +** Date: November 1996 +** LINUX-port: June 2000 +** +** This program is free software; you can redistribute it and/or modify it +** under the terms of the GNU General Public License as published by the +** Free Software Foundation; either version 2, or (at your option) any +** later version. +** +** This program is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +** See the GNU General Public License for more details. +*/ +#include "netstats.h" + +#define MAXCPU 2048 +#define MAXDSK 1024 +#define MAXLVM 2048 +#define MAXMDD 256 +#define MAXINTF 128 +#define MAXCONTAINER 128 +#define MAXNFSMOUNT 64 + +#define MAXDKNAM 32 + +/************************************************************************/ + +struct memstat { + count_t physmem; // number of physical pages + count_t freemem; // number of free pages + count_t buffermem; // number of buffer pages + count_t slabmem; // number of slab pages + count_t cachemem; // number of cache pages + count_t cachedrt; // number of cache pages (dirty) + + count_t totswap; // number of pages in swap + count_t freeswap; // number of free swap pages + + count_t pgscans; // number of page scans + count_t pgsteal; // number of page steals + count_t allocstall; // try to free pages forced + count_t swouts; // number of pages swapped out + count_t swins; // number of pages swapped in + + count_t commitlim; // commit limit in pages + count_t committed; // number of reserved pages + + count_t shmem; // tot shmem incl. tmpfs (pages) + count_t shmrss; // resident shared memory (pages) + count_t shmswp; // swapped shared memory (pages) + + count_t slabreclaim; // reclaimable slab (pages) + + count_t tothugepage; // total huge pages (huge pages) + count_t freehugepage; // free huge pages (huge pages) + count_t hugepagesz; // huge page size (bytes) + + count_t vmwballoon; // vmware claimed balloon pages + + count_t cfuture[8]; // reserved for future use +}; + +/************************************************************************/ + +struct netstat { + struct ipv4_stats ipv4; + struct icmpv4_stats icmpv4; + struct udpv4_stats udpv4; + + struct ipv6_stats ipv6; + struct icmpv6_stats icmpv6; + struct udpv6_stats udpv6; + + struct tcp_stats tcp; +}; + +/************************************************************************/ + +struct freqcnt { + count_t maxfreq;/* frequency in MHz */ + count_t cnt; /* number of clock ticks times state */ + count_t ticks; /* number of total clock ticks */ + /* if zero, cnt is actul freq */ +}; + +struct percpu { + int cpunr; + count_t stime; /* system time in clock ticks */ + count_t utime; /* user time in clock ticks */ + count_t ntime; /* nice time in clock ticks */ + count_t itime; /* idle time in clock ticks */ + count_t wtime; /* iowait time in clock ticks */ + count_t Itime; /* irq time in clock ticks */ + count_t Stime; /* softirq time in clock ticks */ + count_t steal; /* steal time in clock ticks */ + count_t guest; /* guest time in clock ticks */ + struct freqcnt freqcnt;/* frequency scaling info */ + count_t cfuture[4]; /* reserved for future use */ +}; + +struct cpustat { + count_t nrcpu; /* number of cpu's */ + count_t devint; /* number of device interrupts */ + count_t csw; /* number of context switches */ + count_t nprocs; /* number of processes started */ + float lavg1; /* load average last minute */ + float lavg5; /* load average last 5 minutes */ + float lavg15; /* load average last 15 minutes */ + count_t cfuture[4]; /* reserved for future use */ + + struct percpu all; + struct percpu cpu[MAXCPU]; +}; + +/************************************************************************/ + +struct perdsk { + char name[MAXDKNAM]; /* empty string for last */ + count_t nread; /* number of read transfers */ + count_t nrsect; /* number of sectors read */ + count_t nwrite; /* number of write transfers */ + count_t nwsect; /* number of sectors written */ + count_t io_ms; /* number of millisecs spent for I/O */ + count_t avque; /* average queue length */ + count_t cfuture[4]; /* reserved for future use */ +}; + +struct dskstat { + int ndsk; /* number of physical disks */ + int nmdd; /* number of md volumes */ + int nlvm; /* number of logical volumes */ + struct perdsk dsk[MAXDSK]; + struct perdsk mdd[MAXMDD]; + struct perdsk lvm[MAXLVM]; +}; + +/************************************************************************/ + +struct perintf { + char name[16]; /* empty string for last */ + + count_t rbyte; /* number of read bytes */ + count_t rpack; /* number of read packets */ + count_t rerrs; /* receive errors */ + count_t rdrop; /* receive drops */ + count_t rfifo; /* receive fifo */ + count_t rframe; /* receive framing errors */ + count_t rcompr; /* receive compressed */ + count_t rmultic;/* receive multicast */ + count_t rfuture[4]; /* reserved for future use */ + + count_t sbyte; /* number of written bytes */ + count_t spack; /* number of written packets */ + count_t serrs; /* transmit errors */ + count_t sdrop; /* transmit drops */ + count_t sfifo; /* transmit fifo */ + count_t scollis;/* collisions */ + count_t scarrier;/* transmit carrier */ + count_t scompr; /* transmit compressed */ + count_t sfuture[4]; /* reserved for future use */ + + char type; /* interface type ('e'/'w'/'?') */ + long speed; /* interface speed in megabits/second */ + long speedp; /* previous interface speed */ + char duplex; /* full duplex (boolean) */ + count_t cfuture[4]; /* reserved for future use */ +}; + +struct intfstat { + int nrintf; + struct perintf intf[MAXINTF]; +}; + +/************************************************************************/ + +struct pernfsmount { + char mountdev[128]; /* mountdevice */ + count_t age; /* number of seconds mounted */ + + count_t bytesread; /* via normal reads */ + count_t byteswrite; /* via normal writes */ + count_t bytesdread; /* via direct reads */ + count_t bytesdwrite; /* via direct writes */ + count_t bytestotread; /* via reads */ + count_t bytestotwrite; /* via writes */ + count_t pagesmread; /* via mmap reads */ + count_t pagesmwrite; /* via mmap writes */ + + count_t future[8]; +}; + +struct nfsstat { + struct { + count_t netcnt; + count_t netudpcnt; + count_t nettcpcnt; + count_t nettcpcon; + + count_t rpccnt; + count_t rpcbadfmt; + count_t rpcbadaut; + count_t rpcbadcln; + + count_t rpcread; + count_t rpcwrite; + + count_t rchits; /* repcache hits */ + count_t rcmiss; /* repcache misses */ + count_t rcnoca; /* uncached requests */ + + count_t nrbytes; /* read bytes */ + count_t nwbytes; /* written bytes */ + + count_t future[8]; + } server; + + struct { + count_t rpccnt; + count_t rpcretrans; + count_t rpcautrefresh; + + count_t rpcread; + count_t rpcwrite; + + count_t future[8]; + } client; + + struct { + int nrmounts; + struct pernfsmount nfsmnt[MAXNFSMOUNT]; + } nfsmounts; +}; + +/************************************************************************/ + +struct percontainer { + unsigned long ctid; /* container id */ + unsigned long numproc; /* number of processes */ + + count_t system; /* */ + count_t user; /* */ + count_t nice; /* */ + count_t uptime; /* */ + + count_t physpages; /* */ +}; + +struct contstat { + int nrcontainer; + struct percontainer cont[MAXCONTAINER]; +}; + +/************************************************************************/ +/* +** experimental stuff for access to local HTTP daemons +*/ +#define HTTPREQ "GET /server-status?auto HTTP/1.1\nHost: localhost\n\n" + +struct wwwstat { + count_t accesses; /* total number of HTTP-requests */ + count_t totkbytes; /* total kbytes transfer for HTTP-req */ + count_t uptime; /* number of seconds since startup */ + int bworkers; /* number of busy httpd-daemons */ + int iworkers; /* number of idle httpd-daemons */ +}; + +#if HTTPSTATS +int getwwwstat(unsigned short, struct wwwstat *); +#endif +/************************************************************************/ + +struct sstat { + struct cpustat cpu; + struct memstat mem; + struct netstat net; + struct intfstat intf; + struct dskstat dsk; + struct nfsstat nfs; + struct contstat cfs; + + struct wwwstat www; +}; + +/* +** prototypes +*/ +void photosyst (struct sstat *); +void deviatsyst(struct sstat *, struct sstat *, struct sstat *, long); +void totalsyst (char, struct sstat *, struct sstat *); diff --git a/sources/procdbase.c b/sources/procdbase.c new file mode 100644 index 0000000..9cab347 --- /dev/null +++ b/sources/procdbase.c @@ -0,0 +1,364 @@ +/* +** ATOP - System & Process Monitor +** +** The program 'atop' offers the possibility to view the activity of +** the system on system-level as well as process-level. +** +** This source-file contains all functions required to manipulate the +** process-database. This database is implemented as a linked list of +** all running processes, needed to remember the process-counters from +** the previous sample. +** ========================================================================== +** Author: Gerlof Langeveld +** E-mail: gerlof.langeveld@atoptool.nl +** Date: November 1996 +** LINUX-port: June 2000 +** -------------------------------------------------------------------------- +** Copyright (C) 2000-2012 Gerlof Langeveld +** +** This program is free software; you can redistribute it and/or modify it +** under the terms of the GNU General Public License as published by the +** Free Software Foundation; either version 2, or (at your option) any +** later version. +** +** This program is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +** See the GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +** -------------------------------------------------------------------------- +** +** $Log: procdbase.c,v $ +** Revision 1.8 2010/04/23 12:19:35 gerlof +** Modified mail-address in header. +** +** Revision 1.7 2007/11/05 12:12:31 gerlof +** Match processes not only on pid, but also on start time. +** +** Revision 1.6 2005/10/21 09:50:19 gerlof +** Per-user accumulation of resource consumption. +** +** Revision 1.5 2003/07/07 09:26:40 gerlof +** Cleanup code (-Wall proof). +** +** Revision 1.4 2002/10/03 11:19:58 gerlof +** Modify (effective) uid/gid to real uid/gid. +** +** Revision 1.3 2002/07/24 11:13:50 gerlof +** Changed to ease porting to other UNIX-platforms. +** +** Revision 1.2 2002/07/08 09:29:07 root +** Call to calloc i.s.o. malloc + memset. +** +** Revision 1.1 2001/10/02 10:43:33 gerlof +** Initial revision +** +*/ + +static const char rcsid[] = "$Id: procdbase.c,v 1.8 2010/04/23 12:19:35 gerlof Exp $"; + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "atop.h" +#include "photoproc.h" + +/*****************************************************************************/ +#define NPHASH 256 /* number of hash queues for process dbase */ + /* MUST be a power of 2 !!! */ + + /* hash buckets for getting process-info */ + /* for a given PID */ +static struct pinfo *phash[NPHASH]; + + /* cyclic list of all processes, to detect */ + /* which processes were not referred */ +static struct pinfo presidue; +/*****************************************************************************/ + + +/* +** search process database for the given PID +*/ +int +pdb_gettask(int pid, char isproc, time_t btime, struct pinfo **pinfopp) +{ + register struct pinfo *pp; + + pp = phash[pid&(NPHASH-1)]; /* get proper hash bucket */ + + /* + ** scan all entries in hash Q + */ + while (pp) + { + /* + ** if this is required PID, unchain it from the RESIDUE-list + ** and return info + */ + if (pp->tstat.gen.pid == pid && + pp->tstat.gen.isproc == isproc ) + { + int diff = pp->tstat.gen.btime - btime; + + /* + ** with longer intervals, the same PID might be + ** found more than once, so also check the start + ** time of the task + */ + if (diff > 1 || diff < -1) + { + pp = pp->phnext; + continue; + } + + if (pp->prnext) /* if part of RESIDUE-list */ + { + (pp->prnext)->prprev = pp->prprev; /* unchain */ + (pp->prprev)->prnext = pp->prnext; + } + + pp->prnext = NULL; + pp->prprev = NULL; + + *pinfopp = pp; + + return 1; + } + + pp = pp->phnext; + } + + /* + ** end of list; PID not found + */ + return 0; +} + +/* +** add new process-info structure to the process database +*/ +void +pdb_addtask(int pid, struct pinfo *pinfop) +{ + register int i = pid&(NPHASH-1); + + pinfop->phnext = phash[i]; + phash[i] = pinfop; +} + +/* +** delete a process from the process database +*/ +int +pdb_deltask(int pid, char isproc) +{ + register struct pinfo *pp, *ppp; + + pp = phash[pid&(NPHASH-1)]; /* get proper hash bucket */ + + /* + ** check first entry in hash Q + */ + if (pp->tstat.gen.pid == pid && pp->tstat.gen.isproc == isproc) + { + phash[pid&(NPHASH-1)] = pp->phnext; + + if ( pp->prnext ) /* still part of RESIDUE-list ? */ + { + (pp->prprev)->prnext = pp->prnext; + (pp->prnext)->prprev = pp->prprev; /* unchain */ + } + + /* + ** remove process-info from process-database + */ + free(pp); + + return 1; + } + + /* + ** scan other entries of hash-list + */ + ppp = pp; + pp = pp->phnext; + + while (pp) + { + /* + ** if this is wanted PID, unchain it from the RESIDUE-list + ** and return info + */ + if (pp->tstat.gen.pid == pid && pp->tstat.gen.isproc == isproc) + { + ppp->phnext = pp->phnext; + + if ( pp->prnext ) /* part of RESIDUE-list ? */ + { + (pp->prnext)->prprev = pp->prprev; + (pp->prprev)->prnext = pp->prnext; + } + + /* + ** remove process-info from process-database + */ + free(pp); + + return 1; + } + + ppp = pp; + pp = pp->phnext; + } + + return 0; /* PID not found */ +} + +/* +** Chain all process-info structures into the RESIDUE-list; +** every process-info struct which is referenced later on by pdb_gettask(), +** will be removed from this list again. After that, the remaining +** (unreferred) process-info structs can be easily discovered and +** eventually removed. +*/ +int +pdb_makeresidue(void) +{ + register struct pinfo *pp, *pr; + register int i; + + /* + ** prepare RESIDUE-list anchor + */ + pr = &presidue; + + pr->prnext = pr; + pr->prprev = pr; + + /* + ** check all entries in hash list + */ + for (i=0; i < NPHASH; i++) + { + if (!phash[i]) + continue; /* empty hash bucket */ + + pp = phash[i]; /* get start of list */ + + while (pp) /* all entries in hash list */ + { + pp->prnext = pr->prnext; + pr->prnext = pp; + + pp->prprev = (pp->prnext)->prprev; + (pp->prnext)->prprev = pp; + + pp = pp->phnext; /* get next of hash list */ + } + } + + /* + ** all entries chained in doubly-linked RESIDUE-list + */ + return 1; +} + +/* +** remove all remaining entries in RESIDUE-list +*/ +int +pdb_cleanresidue(void) +{ + register struct pinfo *pr; + register int pid; + char isproc; + + /* + ** start at RESIDUE-list anchor and delete all entries + */ + pr = presidue.prnext; + + while (pr != &presidue) + { + pid = pr->tstat.gen.pid; + isproc = pr->tstat.gen.isproc; + + pr = pr->prnext; /* MUST be done before deletion */ + + pdb_deltask(pid, isproc); + } + + return 1; +} + +/* +** search in the RESIDUE-list for process-info which may fit to the +** given process-info, for which the PID is not known +*/ +int +pdb_srchresidue(struct tstat *tstatp, struct pinfo **pinfopp) +{ + register struct pinfo *pr, *prmin=NULL; + register long btimediff; + + /* + ** start at RESIDUE-list anchor and search through + ** all remaining entries + */ + pr = presidue.prnext; + + while (pr != &presidue) /* still entries left ? */ + { + /* + ** check if this entry matches searched info + */ + if ( pr->tstat.gen.ruid == tstatp->gen.ruid && + pr->tstat.gen.rgid == tstatp->gen.rgid && + strcmp(pr->tstat.gen.name, tstatp->gen.name) == EQ ) + { + /* + ** check if the start-time of the process is exactly + ** the same ----> then we have a match; + ** however sometimes the start-time may deviate a + ** second although it IS the process we are looking + ** for (depending on the rounding of the boot-time), + ** so if we don't find the exact match, we will check + ** later on if we found an almost-exact match + */ + btimediff = pr->tstat.gen.btime - tstatp->gen.btime; + + if (btimediff == 0) /* gotcha !! */ + { + *pinfopp = pr; + return 1; + } + + if ((btimediff== -1 || btimediff== 1) && prmin== NULL) + prmin = pr; /* remember this process */ + } + + pr = pr->prnext; + } + + /* + ** nothing found that matched exactly; + ** do we remember a process that matched almost exactly? + */ + if (prmin) + { + *pinfopp = prmin; + return 1; + } + + return 0; /* even not almost */ +} diff --git a/sources/psaccs_atop b/sources/psaccs_atop new file mode 100644 index 0000000..56a9f3a --- /dev/null +++ b/sources/psaccs_atop @@ -0,0 +1,40 @@ +# Logrotate file to take action before psacct is rotated +/var/log/atop/dummy_before { + missingok + daily + rotate 0 + ifempty + create 0600 root root + postrotate + # check if process accounting is installed + # + if [ -e /etc/logrotate.d/psacct ] + then + # check if process accounting is actually in use + # + ACCTFILE=`awk '$2 == "{" {print $1}' /etc/logrotate.d/psacct` + + if [ -f "$ACCTFILE" ] + then + ACCTSIZE1=`ls -l "$ACCTFILE" | awk '{print $5}'` + ACCTSIZE2=`ls -l "$ACCTFILE" | awk '{print $5}'` + + if [ $ACCTSIZE1 -lt $ACCTSIZE2 ] + then + # stop atop daemon before accounting file + # is rotated + # + PIDFILE=/var/run/atop.pid + + if [ -e $PIDFILE ] && \ + ps -p `cat $PIDFILE` | grep 'atop$' > /dev/null + then + kill -USR2 `cat $PIDFILE` # take final sample + rm $PIDFILE + sleep 1 + fi + fi + fi + fi + endscript +} diff --git a/sources/psaccu_atop b/sources/psaccu_atop new file mode 100644 index 0000000..768c970 --- /dev/null +++ b/sources/psaccu_atop @@ -0,0 +1,24 @@ +# Logrotate file to take action after psacct is rotated +/var/log/atop/dummy_after { + missingok + daily + rotate 0 + ifempty + create 0600 root root + postrotate + if [ -e /etc/logrotate.d/psacct ] + then + # if the atop daemon does not run, restart it after + # accounting file is rotated + PIDFILE=/var/run/atop.pid + + if [ -e $PIDFILE ] && \ + ps -p `cat $PIDFILE` | grep 'atop$' > /dev/null + then + : + else + /usr/share/atop/atop.daily& + fi + fi + endscript +} diff --git a/sources/rawlog.c b/sources/rawlog.c new file mode 100644 index 0000000..0f6a47e --- /dev/null +++ b/sources/rawlog.c @@ -0,0 +1,970 @@ +/* +** ATOP - System & Process Monitor +** +** The program 'atop' offers the possibility to view the activity of +** the system on system-level as well as process-level. +** ========================================================================== +** Author: Gerlof Langeveld +** E-mail: gerlof.langeveld@atoptool.nl +** Date: September 2002 +** -------------------------------------------------------------------------- +** Copyright (C) 2000-2010 Gerlof Langeveld +** +** This program is free software; you can redistribute it and/or modify it +** under the terms of the GNU General Public License as published by the +** Free Software Foundation; either version 2, or (at your option) any +** later version. +** +** This program is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +** See the GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +** -------------------------------------------------------------------------- +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "atop.h" +#include "showgeneric.h" +#include "photoproc.h" +#include "photosyst.h" + +#define BASEPATH "/var/log/atop" + +/* +** structure which describes the raw file contents +** +** layout raw file: rawheader +** +** rawrecord \ +** compressed system-level statistics | sample 1 +** compressed process-level statistics / +** +** rawrecord \ +** compressed system-level statistics | sample 2 +** compressed process-level statistics / +** +** etcetera ..... +*/ +#define MYMAGIC (unsigned int) 0xfeedbeef + +struct rawheader { + unsigned int magic; + + unsigned short aversion; /* creator atop version with MSB */ + unsigned short future1; /* can be reused */ + unsigned short future2; /* can be reused */ + unsigned short rawheadlen; /* length of struct rawheader */ + unsigned short rawreclen; /* length of struct rawrecord */ + unsigned short hertz; /* clock interrupts per second */ + unsigned short sfuture[6]; /* future use */ + unsigned int sstatlen; /* length of struct sstat */ + unsigned int tstatlen; /* length of struct tstat */ + struct utsname utsname; /* info about this system */ + char cfuture[8]; /* future use */ + + unsigned int pagesize; /* size of memory page (bytes) */ + int supportflags; /* used features */ + int osrel; /* OS release number */ + int osvers; /* OS version number */ + int ossub; /* OS version subnumber */ + int ifuture[6]; /* future use */ +}; + +struct rawrecord { + time_t curtime; /* current time (epoch) */ + + unsigned short flags; /* various flags */ + unsigned short sfuture[3]; /* future use */ + + unsigned int scomplen; /* length of compressed sstat */ + unsigned int pcomplen; /* length of compressed tstat's */ + unsigned int interval; /* interval (number of seconds) */ + unsigned int ndeviat; /* number of tasks in list */ + unsigned int nactproc; /* number of processes in list */ + unsigned int ntask; /* total number of tasks */ + unsigned int totproc; /* total number of processes */ + unsigned int totrun; /* number of running threads */ + unsigned int totslpi; /* number of sleeping threads(S)*/ + unsigned int totslpu; /* number of sleeping threads(D)*/ + unsigned int totzomb; /* number of zombie processes */ + unsigned int nexit; /* number of exited processes */ + unsigned int noverflow; /* number of overflow processes */ + unsigned int ifuture[6]; /* future use */ +}; + +static int getrawrec (int, struct rawrecord *, int); +static int getrawsstat(int, struct sstat *, int); +static int getrawtstat(int, struct tstat *, int, int); +static int rawwopen(void); +static int lookslikedatetome(char *); +static void testcompval(int, char *); +static void try_other_version(int, int); + +/* +** write a raw record to file +** (file is opened/created during the first call) +*/ +char +rawwrite(time_t curtime, int numsecs, + struct devtstat *devtstat, struct sstat *sstat, + int nexit, unsigned int noverflow, char flag) +{ + static int rawfd = -1; + struct rawrecord rr; + int rv; + struct stat filestat; + + Byte scompbuf[sizeof(struct sstat)], *pcompbuf; + unsigned long scomplen = sizeof scompbuf; + unsigned long pcomplen = sizeof(struct tstat) * + devtstat->ntaskall; + + /* + ** first call: + ** take care that the log file is opened + */ + if (rawfd == -1) + rawfd = rawwopen(); + + /* + ** register current size of file in order to "roll back" + ** writes that have been done while not *all* writes could + ** succeed, e.g. when file system full + */ + (void) fstat(rawfd, &filestat); + + /* + ** compress system- and process-level statistics + */ + rv = compress(scompbuf, &scomplen, + (Byte *)sstat, (unsigned long)sizeof *sstat); + + testcompval(rv, "compress"); + + pcompbuf = malloc(pcomplen); + + ptrverify(pcompbuf, "Malloc failed for compression buffer\n"); + + rv = compress(pcompbuf, &pcomplen, (Byte *)devtstat->taskall, + (unsigned long)pcomplen); + + testcompval(rv, "compress"); + + /* + ** fill record header and write to file + */ + memset(&rr, 0, sizeof rr); + + rr.curtime = curtime; + rr.interval = numsecs; + rr.flags = 0; + rr.ndeviat = devtstat->ntaskall; + rr.nactproc = devtstat->nprocactive; + rr.ntask = devtstat->ntaskall; + rr.nexit = nexit; + rr.noverflow = noverflow; + rr.totproc = devtstat->nprocall; + rr.totrun = devtstat->totrun; + rr.totslpi = devtstat->totslpi; + rr.totslpu = devtstat->totslpu; + rr.totzomb = devtstat->totzombie; + rr.scomplen = scomplen; + rr.pcomplen = pcomplen; + + if (flag&RRBOOT) + rr.flags |= RRBOOT; + + if (supportflags & ACCTACTIVE) + rr.flags |= RRACCTACTIVE; + + if (supportflags & IOSTAT) + rr.flags |= RRIOSTAT; + + if (supportflags & NETATOP) + rr.flags |= RRNETATOP; + + if (supportflags & NETATOPD) + rr.flags |= RRNETATOPD; + + if (supportflags & DOCKSTAT) + rr.flags |= RRDOCKSTAT; + + if ( write(rawfd, &rr, sizeof rr) == -1) + { + fprintf(stderr, "%s - ", rawname); + perror("write raw record"); + if ( ftruncate(rawfd, filestat.st_size) == -1) + cleanstop(8); + cleanstop(7); + } + + /* + ** write compressed system status structure to file + */ + if ( write(rawfd, scompbuf, scomplen) == -1) + { + fprintf(stderr, "%s - ", rawname); + perror("write raw status record"); + if ( ftruncate(rawfd, filestat.st_size) == -1) + cleanstop(8); + cleanstop(7); + } + + /* + ** write compressed list of process status structures to file + */ + if ( write(rawfd, pcompbuf, pcomplen) == -1) + { + fprintf(stderr, "%s - ", rawname); + perror("write raw process record"); + if ( ftruncate(rawfd, filestat.st_size) == -1) + cleanstop(8); + cleanstop(7); + } + + free(pcompbuf); + + return '\0'; +} + + +/* +** open a raw file for writing +** +** if the raw file exists already: +** - read and validate the header record (be sure it is an atop-file) +** - seek to the end of the file +** +** if the raw file does not yet exist: +** - create the raw file +** - write a header record +** +** return the filedescriptor of the raw file +*/ +static int +rawwopen() +{ + struct rawheader rh; + int fd; + + /* + ** check if the file exists already + */ + if ( (fd = open(rawname, O_RDWR)) >= 0) + { + /* + ** read and verify header record + */ + if ( read(fd, &rh, sizeof rh) < sizeof rh) + { + fprintf(stderr, "%s - cannot read header\n", rawname); + cleanstop(7); + } + + if (rh.magic != MYMAGIC) + { + fprintf(stderr, + "file %s exists but does not contain raw " + "atop output (wrong magic number)\n", rawname); + + cleanstop(7); + } + + if ( rh.sstatlen != sizeof(struct sstat) || + rh.tstatlen != sizeof(struct tstat) || + rh.rawheadlen != sizeof(struct rawheader) || + rh.rawreclen != sizeof(struct rawrecord) ) + { + fprintf(stderr, + "existing file %s has incompatible header\n", + rawname); + + if (rh.aversion & 0x8000 && + (rh.aversion & 0x7fff) != getnumvers()) + { + fprintf(stderr, + "(created by version %d.%d - " + "current version %d.%d)\n", + (rh.aversion >> 8) & 0x7f, + rh.aversion & 0xff, + getnumvers() >> 8, + getnumvers() & 0x7f); + } + + cleanstop(7); + } + + (void) lseek(fd, (off_t) 0, SEEK_END); + + return fd; + } + + /* + ** file does not exist (or can not be opened) + */ + if ( (fd = creat(rawname, 0666)) == -1) + { + fprintf(stderr, "%s - ", rawname); + perror("create raw file"); + cleanstop(7); + } + + memset(&rh, 0, sizeof rh); + + rh.magic = MYMAGIC; + rh.aversion = getnumvers() | 0x8000; + rh.sstatlen = sizeof(struct sstat); + rh.tstatlen = sizeof(struct tstat); + rh.rawheadlen = sizeof(struct rawheader); + rh.rawreclen = sizeof(struct rawrecord); + rh.supportflags = supportflags | RAWLOGNG; + rh.osrel = osrel; + rh.osvers = osvers; + rh.ossub = ossub; + rh.hertz = hertz; + rh.pagesize = pagesize; + + memcpy(&rh.utsname, &utsname, sizeof rh.utsname); + + if ( write(fd, &rh, sizeof rh) == -1) + { + fprintf(stderr, "%s - ", rawname); + perror("write raw header"); + cleanstop(7); + } + + return fd; +} + +/* +** read the contents of a raw file +*/ +#define OFFCHUNK 256 + +void +rawread(void) +{ + int i, j, rawfd, len; + char *py; + struct rawheader rh; + struct rawrecord rr; + struct sstat sstat; + static struct devtstat devtstat; + + struct stat filestat; + + /* + ** variables to maintain the offsets of the raw records + ** to be able to see previous samples again + */ + off_t *offlist; + unsigned int offsize = 0; + unsigned int offcur = 0; + char lastcmd = 'X', flags; + + time_t timenow; + struct tm *tp; + + switch ( len = strlen(rawname) ) + { + /* + ** if no filename is specified, assemble the name of the raw file + */ + case 0: + timenow = time(0); + tp = localtime(&timenow); + + snprintf(rawname, RAWNAMESZ, "%s/atop_%04d%02d%02d", + BASEPATH, + tp->tm_year+1900, + tp->tm_mon+1, + tp->tm_mday); + + break; + + /* + ** if date specified as filename in format YYYYMMDD, assemble + ** the full pathname of the raw file + */ + case 8: + if ( access(rawname, F_OK) == 0) + break; /* existing file */ + + if (lookslikedatetome(rawname)) + { + char savedname[RAWNAMESZ]; + + strncpy(savedname, rawname, RAWNAMESZ-1); + + snprintf(rawname, RAWNAMESZ, "%s/atop_%s", + BASEPATH, + savedname); + break; + } + + /* + ** if one or more 'y' (yesterday) characters are used and that + ** string is not known as an existing file, the standard logfile + ** is shown from N days ago (N is determined by the number + ** of y's). + */ + default: + if ( access(rawname, F_OK) == 0) + break; /* existing file */ + + /* + ** make a string existing of y's to compare with + */ + py = malloc(len+1); + + ptrverify(py, "Malloc failed for 'yes' sequence\n"); + + memset(py, 'y', len); + *(py+len) = '\0'; + + if ( strcmp(rawname, py) == 0 ) + { + timenow = time(0); + timenow -= len*3600*24; + tp = localtime(&timenow); + + snprintf(rawname, RAWNAMESZ, "%s/atop_%04d%02d%02d", + BASEPATH, + tp->tm_year+1900, + tp->tm_mon+1, + tp->tm_mday); + } + + free(py); + } + + /* + ** open raw file + */ + if ( (rawfd = open(rawname, O_RDONLY)) == -1) + { + char command[512], tmpname1[RAWNAMESZ], tmpname2[RAWNAMESZ]; + + /* + ** check if a compressed raw file is present + */ + snprintf(tmpname1, sizeof tmpname1, "%s.gz", rawname); + + if ( access(tmpname1, F_OK|R_OK) == -1) + { + fprintf(stderr, "%s - ", rawname); + perror("open raw file"); + cleanstop(7); + } + + /* + ** compressed raw file to be decompressed via gunzip + */ + fprintf(stderr, "Decompressing logfile ....\n"); + snprintf(tmpname2, sizeof tmpname2, "/tmp/atopwrkXXXXXX"); + rawfd = mkstemp(tmpname2); + if (rawfd == -1) + { + fprintf(stderr, "%s - ", rawname); + perror("creating decompression temp file"); + cleanstop(7); + } + + snprintf(command, sizeof command, "gunzip -c %s > %s", + tmpname1, tmpname2); + const int system_res = system (command); + unlink(tmpname2); + + if (system_res) + { + fprintf(stderr, "%s - gunzip failed", rawname); + cleanstop(7); + } + } + + /* + ** read the raw header and verify the magic + */ + if ( read(rawfd, &rh, sizeof rh) < sizeof rh) + { + fprintf(stderr, "can not read raw file header\n"); + cleanstop(7); + } + + if (rh.magic != MYMAGIC) + { + fprintf(stderr, "file %s does not contain raw atop/atopsar " + "output (wrong magic number)\n", rawname); + cleanstop(7); + } + + /* + ** magic okay, but file-layout might have been modified + */ + if (rh.sstatlen != sizeof(struct sstat) || + rh.tstatlen != sizeof(struct tstat) || + rh.rawheadlen != sizeof(struct rawheader) || + rh.rawreclen != sizeof(struct rawrecord) ) + { + fprintf(stderr, + "raw file %s has incompatible format\n", rawname); + + if (rh.aversion & 0x8000 && + (rh.aversion & 0x7fff) != getnumvers()) + { + fprintf(stderr, + "(created by version %d.%d - " + "current version %d.%d)\n", + (rh.aversion >> 8) & 0x7f, + rh.aversion & 0xff, + getnumvers() >> 8, + getnumvers() & 0x7f); + } + else + { + fprintf(stderr, + "(files from other system architectures might" + " be binary incompatible)\n"); + } + + close(rawfd); + + if (((rh.aversion >> 8) & 0x7f) != (getnumvers() >> 8) || + (rh.aversion & 0xff) != (getnumvers() & 0x7f) ) + { + try_other_version((rh.aversion >> 8) & 0x7f, + rh.aversion & 0xff); + } + + cleanstop(7); + } + + memcpy(&utsname, &rh.utsname, sizeof utsname); + utsnodenamelen = strlen(utsname.nodename); + + supportflags = rh.supportflags; + osrel = rh.osrel; + osvers = rh.osvers; + ossub = rh.ossub; + interval = 0; + + if (rh.hertz) + hertz = rh.hertz; + + if (rh.pagesize) + pagesize = rh.pagesize; + + /* + ** allocate a list for backtracking of rawrecord-offsets + */ + offlist = malloc(sizeof(off_t) * OFFCHUNK); + + ptrverify(offlist, "Malloc failed for backtrack list\n"); + + offsize = OFFCHUNK; + + *offlist = lseek(rawfd, 0, SEEK_CUR); + offcur = 1; + + /* + ** read a raw record header until end-of-file + */ + sampcnt = 0; + + while (lastcmd && lastcmd != 'q') + { + while ( getrawrec(rawfd, &rr, rh.rawreclen) == rh.rawreclen) + { + unsigned int secsinday = daysecs(rr.curtime); + unsigned int k, l; + + /* + ** store the offset of the raw record in the offset list + ** in case of offset list overflow, extend the list + */ + *(offlist+offcur) = lseek(rawfd, 0, SEEK_CUR) - + rh.rawreclen; + + if ( ++offcur >= offsize ) + { + offlist = realloc(offlist, + (offsize+OFFCHUNK)*sizeof(off_t)); + + ptrverify(offlist, + "Realloc failed for backtrack list\n"); + + offsize+= OFFCHUNK; + } + + /* + ** check if this sample is within the time-range + ** specified with the -b and -e flags (if any) + */ + if ( (begintime && begintime > secsinday) ) + { + lastcmd = 1; + lseek(rawfd, rr.scomplen+rr.pcomplen, SEEK_CUR); + continue; + } + + begintime = 0; // allow earlier times from now on + + if ( (endtime && endtime < secsinday) ) + { + free(offlist); + close(rawfd); + return; + } + + /* + ** allocate space, read compressed system-level + ** statistics and decompress + */ + if ( !getrawsstat(rawfd, &sstat, rr.scomplen) ) + cleanstop(7); + + /* + ** allocate space, read compressed process-level + ** statistics and decompress + */ + devtstat.taskall = malloc(sizeof(struct tstat ) + * rr.ndeviat); + + if (rr.totproc < rr.nactproc) // compat old raw files + devtstat.procall = malloc(sizeof(struct tstat *) + * rr.nactproc); + else + devtstat.procall = malloc(sizeof(struct tstat *) + * rr.totproc); + + devtstat.procactive = malloc(sizeof(struct tstat *) * rr.nactproc); + + ptrverify(devtstat.taskall, + "Malloc failed for %d stored tasks\n", + rr.ndeviat); + + ptrverify(devtstat.procall, + "Malloc failed for total %d processes\n", + rr.totproc); + + ptrverify(devtstat.procactive, + "Malloc failed for %d active processes\n", + rr.nactproc); + + if ( !getrawtstat(rawfd, devtstat.taskall, + rr.pcomplen, rr.ndeviat) ) + cleanstop(7); + + + for (i=j=k=l=0; i < rr.ndeviat; i++) + { + if ( (devtstat.taskall+i)->gen.isproc) + { + devtstat.procall[j++] = devtstat.taskall+i; + + if (! (devtstat.taskall+i)->gen.wasinactive) + devtstat.procactive[k++] = devtstat.taskall+i; + } + + if (! (devtstat.taskall+i)->gen.wasinactive) + l++; + } + + devtstat.ntaskall = i; + devtstat.nprocall = j; + devtstat.nprocactive = k; + devtstat.ntaskactive = l; + + devtstat.totrun = rr.totrun; + devtstat.totslpi = rr.totslpi; + devtstat.totslpu = rr.totslpu; + devtstat.totzombie = rr.totzomb; + + /* + ** activate the installed print-function to visualize + ** the system- and process-level statistics + */ + sampcnt++; + + if ( (rh.supportflags & RAWLOGNG) == RAWLOGNG) + { + if (rr.flags & RRACCTACTIVE) + supportflags |= ACCTACTIVE; + else + supportflags &= ~ACCTACTIVE; + + if (rr.flags & RRIOSTAT) + supportflags |= IOSTAT; + else + supportflags &= ~IOSTAT; + } + + if (rr.flags & RRNETATOP) + supportflags |= NETATOP; + else + supportflags &= ~NETATOP; + + if (rr.flags & RRNETATOPD) + supportflags |= NETATOPD; + else + supportflags &= ~NETATOPD; + + if (rr.flags & RRDOCKSTAT) + supportflags |= DOCKSTAT; + else + supportflags &= ~DOCKSTAT; + + flags = rr.flags & RRBOOT; + + (void) fstat(rawfd, &filestat); + + if ( filestat.st_size - + lseek(rawfd, (off_t)0, SEEK_CUR) <= rh.rawreclen) + flags |= RRLAST; + + lastcmd = (vis.show_samp)(rr.curtime, rr.interval, + &devtstat, &sstat, + rr.nexit, rr.noverflow, flags); + + free(devtstat.taskall); + free(devtstat.procall); + free(devtstat.procactive); + + switch (lastcmd) + { + case MSAMPPREV: + if (offcur >= 2) + offcur-= 2; + else + offcur = 0; + + lseek(rawfd, *(offlist+offcur), SEEK_SET); + break; + + case MRESET: + lseek(rawfd, *offlist, SEEK_SET); + offcur = 1; + break; + + case MSAMPBRANCH: + if (begintime && begintime < secsinday) + { + lseek(rawfd, *offlist, SEEK_SET); + offcur = 1; + } + } + } + + begintime = 0; // allow earlier times from now on + + if (offcur >= 1) + offcur--; + + lseek(rawfd, *(offlist+offcur), SEEK_SET); + } + + free(offlist); + + close(rawfd); +} + +/* +** read the next raw record from the raw logfile +*/ +static int +getrawrec(int rawfd, struct rawrecord *prr, int rrlen) +{ + return read(rawfd, prr, rrlen); +} + +/* +** read the system-level statistics from the current offset +*/ +static int +getrawsstat(int rawfd, struct sstat *sp, int complen) +{ + Byte *compbuf; + unsigned long uncomplen = sizeof(struct sstat); + int rv; + + if ( (compbuf = malloc(complen)) == NULL) + + ptrverify(compbuf, "Malloc failed for reading compressed sysstats\n"); + + if ( read(rawfd, compbuf, complen) < complen) + { + free(compbuf); + return 0; + } + + rv = uncompress((Byte *)sp, &uncomplen, compbuf, complen); + + testcompval(rv, "uncompress"); + + free(compbuf); + + return 1; +} + +/* +** read the process-level statistics from the current offset +*/ +static int +getrawtstat(int rawfd, struct tstat *pp, int complen, int ndeviat) +{ + Byte *compbuf; + unsigned long uncomplen = sizeof(struct tstat) * ndeviat; + int rv; + + compbuf = malloc(complen); + + ptrverify(compbuf, "Malloc failed for reading compressed procstats\n"); + + if ( read(rawfd, compbuf, complen) < complen) + { + free(compbuf); + return 0; + } + + rv = uncompress((Byte *)pp, &uncomplen, compbuf, complen); + + testcompval(rv, "uncompress"); + + free(compbuf); + + return 1; +} + +/* +** verify if a particular ascii-string is in the format yyyymmdd +*/ +static int +lookslikedatetome(char *p) +{ + register int i; + + for (i=0; i < 8; i++) + if ( !isdigit(*(p+i)) ) + return 0; + + if (*p != '2') + return 0; /* adapt this in the year 3000 */ + + if ( *(p+4) > '1') + return 0; + + if ( *(p+6) > '3') + return 0; + + return 1; /* yes, looks like a date to me */ +} + +static void +testcompval(int rv, char *func) +{ + switch (rv) + { + case Z_OK: + case Z_STREAM_END: + case Z_NEED_DICT: + break; + + case Z_MEM_ERROR: + fprintf(stderr, "atop/atopsar - " + "%s: failed due to lack of memory\n", func); + cleanstop(7); + + case Z_BUF_ERROR: + fprintf(stderr, "atop/atopsar - " + "%s: failed due to lack of room in buffer\n", func); + cleanstop(7); + + case Z_DATA_ERROR: + fprintf(stderr, "atop/atopsar - " + "%s: failed due to corrupted/incomplete data\n", func); + cleanstop(7); + + default: + fprintf(stderr, "atop/atopsar - " + "%s: unexpected error %d\n", func, rv); + cleanstop(7); + } +} + +/* +** try to activate another atop- or atopsar-version +** to read this logfile +*/ +static void +try_other_version(int majorversion, int minorversion) +{ + char tmpbuf[1024], *p; + extern char **argvp; + int fds; + struct rlimit rlimit; + int setresuid(uid_t, uid_t, uid_t); + + /* + ** prepare name of executable file + ** the current pathname (if any) is stripped off + */ + snprintf(tmpbuf, sizeof tmpbuf, "%s-%d.%d", + (p = strrchr(*argvp, '/')) ? p+1 : *argvp, + majorversion, minorversion); + + fprintf(stderr, "trying to activate %s....\n", tmpbuf); + + /* + ** be sure no open file descriptors are passed + ** except stdin, stdout en stderr + */ + (void) getrlimit(RLIMIT_NOFILE, &rlimit); + + for (fds=3; fds < rlimit.rlim_cur; fds++) + close(fds); + + /* + ** be absolutely sure not to pass setuid-root privileges + ** to the loaded program; errno EAGAIN and ENOMEM are not + ** acceptable! + */ + if ( setresuid(getuid(), getuid(), getuid()) == -1 && errno != EPERM) + { + fprintf(stderr, "not possible to drop root-privileges!\n"); + exit(1); + } + + /* + ** load alternative executable image + ** at this moment the saved-uid might still be set + ** to 'root' but this is reset at the moment of exec + */ + (void) execvp(tmpbuf, argvp); + + /* + ** point of no return, except when exec failed + */ + fprintf(stderr, "activation of %s failed!\n", tmpbuf); +} diff --git a/sources/showgeneric.c b/sources/showgeneric.c new file mode 100644 index 0000000..0668b03 --- /dev/null +++ b/sources/showgeneric.c @@ -0,0 +1,3287 @@ +/* +** ATOP - System & Process Monitor +** +** The program 'atop' offers the possibility to view the activity of +** the system on system-level as well as process-level. +** +** This source-file contains the print-functions to visualize the calculated +** figures. +** ========================================================================== +** Author: Gerlof Langeveld +** E-mail: gerlof.langeveld@atoptool.nl +** Date: November 1996 +** LINUX-port: June 2000 +** -------------------------------------------------------------------------- +** Copyright (C) 2000-2010 Gerlof Langeveld +** +** This program is free software; you can redistribute it and/or modify it +** under the terms of the GNU General Public License as published by the +** Free Software Foundation; either version 2, or (at your option) any +** later version. +** +** This program is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +** See the GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +** -------------------------------------------------------------------------- +** +** $Log: showgeneric.c,v $ +** Revision 1.71 2010/10/25 19:08:32 gerlof +** When the number of lines is too small for the system-level +** lines, limit the number of variable resources automatically +** to a minimum. +** +** Revision 1.70 2010/10/23 14:04:05 gerlof +** Counters for total number of running and sleep threads (JC van Winkel). +** +** Revision 1.69 2010/04/23 12:19:35 gerlof +** Modified mail-address in header. +** +** Revision 1.68 2010/04/23 09:58:11 gerlof +** Version (flag -V) handled earlier after startup. +** +** Revision 1.67 2010/04/23 07:57:32 gerlof +** Proper sorting of processes when switching from single process view +** to cumulative view (key 'u' or 'p') and vice versa. +** +** Revision 1.66 2010/04/17 17:20:26 gerlof +** Allow modifying the layout of the columns in the system lines. +** +** Revision 1.65 2010/03/16 21:13:38 gerlof +** Program and user selection can be combined with program and user +** accumulation. +** +** Revision 1.64 2010/03/16 20:18:46 gerlof +** Show in header-line if user selections and program selection are active. +** +** Revision 1.63 2010/03/16 20:08:51 gerlof +** Performance improvement: only sort system-resources once per interval. +** +** Revision 1.62 2010/03/04 10:53:01 gerlof +** Support I/O-statistics on logical volumes and MD devices. +** +** Revision 1.61 2009/12/17 10:55:07 gerlof +** *** empty log message *** +** +** Revision 1.60 2009/12/17 10:50:30 gerlof +** Allow own defined process line with key 'o' and a definition +** in the atoprc file. +** +** Revision 1.59 2009/12/17 09:03:26 gerlof +** Center message "....since boot" in status line on first screen. +** +** Revision 1.58 2009/12/17 08:55:15 gerlof +** Show messages on status line in color to draw attention. +** +** Revision 1.57 2009/12/17 08:16:14 gerlof +** Introduce branch-key to go to specific time in raw file. +** +** Revision 1.56 2009/12/12 09:06:39 gerlof +** Corrected cumulated disk I/O per user/program (JC van Winkel). +** +** Revision 1.55 2009/12/10 13:34:44 gerlof +** Show which toggle-keys are active in the header line. +** +** Revision 1.54 2009/12/10 11:55:03 gerlof +** Introduce system-wide /etc/atoprc +** +** Revision 1.53 2009/12/10 09:53:08 gerlof +** Improved display of header-line (JC van Winkel). +** +** Revision 1.52 2008/03/06 10:14:01 gerlof +** Modified help-messages. +** +** Revision 1.51 2008/02/25 13:47:21 gerlof +** Bug-solution: segmentation-fault in case of invalid regular expression. +** +** Revision 1.50 2008/01/07 11:33:58 gerlof +** Cosmetic changes. +** +** Revision 1.49 2008/01/07 10:18:24 gerlof +** Implement possibility to make summaries with atopsar. +** +** Revision 1.48 2007/03/22 10:12:17 gerlof +** Support for io counters (>= kernel 2.6.20). +** +** Revision 1.47 2007/03/21 14:22:34 gerlof +** Handle io counters maintained from 2.6.20 +** +** Revision 1.46 2007/03/20 11:13:15 gerlof +** Cosmetic changes. +** +** Revision 1.45 2007/03/09 12:39:59 gerlof +** Do not allow 'N' and 'D' when kernel-patch is not installed. +** +** Revision 1.44 2007/02/13 10:31:53 gerlof +** Removal of external declarations. +** +** Revision 1.43 2007/01/18 10:41:45 gerlof +** Add support for colors. +** Add support for automatic determination of most critical resource. +** +** Revision 1.42 2006/11/13 13:48:36 gerlof +** Implement load-average counters, context-switches and interrupts. +** +** Revision 1.41 2006/04/03 05:42:35 gerlof +** *** empty log message *** +** +** Revision 1.40 2006/02/07 08:28:26 gerlof +** Improve screen-handling (less flashing) by exchanging clear() +** by werase() (contribution Folkert van Heusden). +** +** Revision 1.39 2005/11/04 14:16:16 gerlof +** Minor bug-solutions. +** +** Revision 1.38 2005/10/28 09:52:03 gerlof +** All flags/subcommands are defined as macro's. +** Subcommand 'p' has been changed to 'z' (pause). +** +** Revision 1.37 2005/10/24 06:12:17 gerlof +** Flag -L modified into -l. +** +** Revision 1.36 2005/10/21 09:50:46 gerlof +** Per-user accumulation of resource consumption. +** Possibility to send signal to process. +** +** Revision 1.35 2004/12/14 15:06:41 gerlof +** Implementation of patch-recognition for disk and network-statistics. +** +** Revision 1.34 2004/09/27 11:01:13 gerlof +** Corrected usage-info as suggested by Edelhard Becker. +** +** Revision 1.33 2004/09/13 09:19:14 gerlof +** Modify subcommands (former 's' -> 'v', 'v' -> 'V', new 's'). +** +** Revision 1.32 2004/08/31 09:52:47 root +** information about underlying threads. +** +** Revision 1.31 2004/06/01 11:57:58 gerlof +** Regular expressions for selections on process-name and user-name. +** +** Revision 1.30 2003/07/07 09:27:24 gerlof +** Cleanup code (-Wall proof). +** +** Revision 1.29 2003/07/03 11:16:42 gerlof +** Implemented subcommand `r' (reset). +** +** Revision 1.28 2003/06/30 11:29:49 gerlof +** Handle configuration file ~/.atoprc +** +** Revision 1.27 2003/06/24 06:21:57 gerlof +** Limit number of system resource lines. +** +** Revision 1.26 2003/02/07 10:19:18 gerlof +** Possibility to show the version number and date. +** +** Revision 1.25 2003/01/17 07:32:16 gerlof +** Show the full command-line per process (option 'c'). +** +** Revision 1.24 2002/10/30 13:47:20 gerlof +** Generate notification for statistics since boot. +** +** Revision 1.23 2002/10/08 12:00:30 gerlof +** *** empty log message *** +** +** Revision 1.22 2002/09/26 14:17:39 gerlof +** No beep when resizing the window. +** +** Revision 1.21 2002/09/26 13:52:26 gerlof +** Limit header lines by not showing disks. +** +** Revision 1.20 2002/09/18 07:15:59 gerlof +** Modified viewflag to rawreadflag. +** +** Revision 1.19 2002/09/17 13:17:39 gerlof +** Allow key 'T' to be pressed to view previous sample in raw file. +** +** Revision 1.18 2002/08/30 07:11:50 gerlof +** Minor changes to support viewing of raw atop data. +** +** Revision 1.17 2002/08/27 12:10:12 gerlof +** Allow raw data file to be written and to be read (with compression). +** +** Revision 1.16 2002/07/24 11:12:46 gerlof +** Changed to ease porting to other UNIX-platforms. +** +** Revision 1.15 2002/07/11 09:12:05 root +** Some minor updates. +** +** Revision 1.14 2002/07/10 05:00:37 root +** Counters pin/pout renamed to swin/swout (Linux conventions). +** +** Revision 1.13 2002/07/08 09:29:49 root +** Limitation for username and groupname (8 characters truncate). +** +** Revision 1.12 2002/07/02 07:14:02 gerlof +** More positions for the name of the disk-unit in the DSK-line. +** +** Revision 1.11 2002/01/22 13:40:42 gerlof +** Support for number of cpu's. +** Check if the window is large enough for the system-statistics. +** +** Revision 1.10 2001/11/30 09:09:36 gerlof +** Cosmetic chnage. +** +** Revision 1.9 2001/11/29 10:41:44 gerlof +** *** empty log message *** +** +** Revision 1.8 2001/11/29 10:38:16 gerlof +** Exit-code correctly printed. +** +** Revision 1.7 2001/11/26 11:18:45 gerlof +** Modified generic output in case that the kernel-patch is not installed. +** +** Revision 1.6 2001/11/13 08:24:50 gerlof +** Show blank columns for sockets and disk I/O when no kernel-patch installed. +** +** Revision 1.5 2001/11/07 09:19:28 gerlof +** Use /proc instead of /dev/kmem for process-level statistics. +** +** Revision 1.4 2001/10/05 13:46:32 gerlof +** Implemented paging through the process-list +** +** Revision 1.3 2001/10/04 08:47:27 gerlof +** Improved handling of error-messages +** +** Revision 1.2 2001/10/03 08:57:53 gerlof +** Improved help-screen shown in scrollable window +** +** Revision 1.1 2001/10/02 10:43:34 gerlof +** Initial revision +** +*/ + +static const char rcsid[] = "$Id: showgeneric.c,v 1.71 2010/10/25 19:08:32 gerlof Exp $"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "atop.h" +#include "photoproc.h" +#include "photosyst.h" +#include "showgeneric.h" +#include "showlinux.h" + +static struct pselection procsel = {"", {USERSTUB, }, {0,}, + "", 0, { 0, }, "", 0, { 0, } }; +static struct sselection syssel; + +static void showhelp(int); +static int paused; /* boolean: currently in pause-mode */ +static int fixedhead; /* boolean: fixate header-lines */ +static int sysnosort; /* boolean: suppress sort of resources */ +static int avgval; /* boolean: average values i.s.o. total */ +static int suppressexit; /* boolean: suppress exited processes */ + +static char showtype = MPROCGEN; +static char showorder = MSORTCPU; + +static int maxcpulines = 999; /* maximum cpu lines */ +static int maxdsklines = 999; /* maximum disk lines */ +static int maxmddlines = 999; /* maximum MDD lines */ +static int maxlvmlines = 999; /* maximum LVM lines */ +static int maxintlines = 999; /* maximum interface lines */ +static int maxnfslines = 999; /* maximum nfs mount lines */ +static int maxcontlines = 999; /* maximum container lines */ + +static short colorinfo = COLOR_GREEN; +static short coloralmost = COLOR_CYAN; +static short colorcrit = COLOR_RED; +static short colorthread = COLOR_YELLOW; + +static int cumusers(struct tstat **, struct tstat *, int); +static int cumprogs(struct tstat **, struct tstat *, int); +static int cumconts(struct tstat **, struct tstat *, int); +static void accumulate(struct tstat *, struct tstat *); + +static int procsuppress(struct tstat *, struct pselection *); +static void limitedlines(void); +static long getnumval(char *, long, int); +static void generic_init(void); + + +static int (*procsort[])(const void *, const void *) = { + [MSORTCPU&0x1f]=compcpu, + [MSORTMEM&0x1f]=compmem, + [MSORTDSK&0x1f]=compdsk, + [MSORTNET&0x1f]=compnet, +}; + +extern proc_printpair ownprocs[]; + +/* +** global: incremented by -> key and decremented by <- key +*/ +int startoffset; + +/* +** print the deviation-counters on process- and system-level +*/ +char +generic_samp(time_t curtime, int nsecs, + struct devtstat *devtstat, struct sstat *sstat, + int nexit, unsigned int noverflow, char flag) +{ + static int callnr = 0; + char *p; + + + register int i, curline, statline, nproc; + int firstproc = 0, plistsz, alistsz, killpid, killsig; + int lastchar; + char format1[16], format2[16], hhmm[16]; + char *statmsg = NULL, statbuf[80], genline[80]; + char *lastsortp, curorder, autoorder; + char buf[33]; + struct passwd *pwd; + struct syscap syscap; + + /* + ** curlist points to the active list of tstat-pointers that + ** should be displayed; ncurlist indicates the number of entries in + ** this list + */ + struct tstat **curlist; + int ncurlist; + + /* + ** tXcumlist is a list of tstat-structs holding one entry + ** per accumulated (per user or per program) group of processes + ** + ** Xcumlist contains the pointers to all structs in tXcumlist + ** + ** these lists will only be allocated 'lazy' + ** only when accumulation is requested + */ + struct tstat *tpcumlist = 0; // per program accumulation + struct tstat **pcumlist = 0; + int npcum = 0; + char plastorder = 0; + + struct tstat *tucumlist = 0; // per user accumulation + struct tstat **ucumlist = 0; + int nucum = 0; + char ulastorder = 0; + + struct tstat *tccumlist = 0; // per container accumulation + struct tstat **ccumlist = 0; + int nccum = 0; + char clastorder = 0; + + /* + ** tsklist contains the pointers to all structs in tstat + ** sorted on process with the related threads immediately + ** following the process + ** + ** this list will be allocated 'lazy' + */ + struct tstat **tsklist = 0; + int ntsk = 0; + char tlastorder = 0; + char zipagain = 0; + char tdeviate = 0; + + /* + ** sellist contains the pointers to the structs in tstat + ** that are currently selected on basis of a particular + ** username (regexp), program name (regexp), container name + ** or suppressed exited procs + ** + ** this list will be allocated 'lazy' + */ + struct tstat **sellist = 0; + int nsel = 0; + char slastorder = 0; + + char threadallowed = 0; + + + if (callnr == 0) /* first call? */ + generic_init(); + + callnr++; + + startoffset = 0; + + /* + ** compute the total capacity of this system for the + ** four main resources + */ + totalcap(&syscap, sstat, devtstat->procactive, devtstat->nprocactive); + + /* + ** sort per-cpu statistics on busy percentage + ** sort per-logical-volume statistics on busy percentage + ** sort per-multiple-device statistics on busy percentage + ** sort per-disk statistics on busy percentage + ** sort per-interface statistics on busy percentage (if known) + */ + if (!sysnosort) + { + if (sstat->cpu.nrcpu > 1 && maxcpulines > 0) + qsort(sstat->cpu.cpu, sstat->cpu.nrcpu, + sizeof sstat->cpu.cpu[0], cpucompar); + + if (sstat->dsk.nlvm > 1 && maxlvmlines > 0) + qsort(sstat->dsk.lvm, sstat->dsk.nlvm, + sizeof sstat->dsk.lvm[0], diskcompar); + + if (sstat->dsk.nmdd > 1 && maxmddlines > 0) + qsort(sstat->dsk.mdd, sstat->dsk.nmdd, + sizeof sstat->dsk.mdd[0], diskcompar); + + if (sstat->dsk.ndsk > 1 && maxdsklines > 0) + qsort(sstat->dsk.dsk, sstat->dsk.ndsk, + sizeof sstat->dsk.dsk[0], diskcompar); + + if (sstat->intf.nrintf > 1 && maxintlines > 0) + qsort(sstat->intf.intf, sstat->intf.nrintf, + sizeof sstat->intf.intf[0], intfcompar); + + if (sstat->nfs.nfsmounts.nrmounts > 1 && maxnfslines > 0) + qsort(sstat->nfs.nfsmounts.nfsmnt, + sstat->nfs.nfsmounts.nrmounts, + sizeof sstat->nfs.nfsmounts.nfsmnt[0], + nfsmcompar); + + if (sstat->cfs.nrcontainer > 1 && maxcontlines > 0) + qsort(sstat->cfs.cont, sstat->cfs.nrcontainer, + sizeof sstat->cfs.cont[0], contcompar); + } + + /* + ** loop in which the system resources and the list of active + ** processes are shown; the loop will be preempted by receiving + ** a timer-signal or when the trigger-button is pressed. + */ + while (1) + { + curline = 1; + + /* + ** prepare screen or file output for new sample + */ + if (screen) + werase(stdscr); + else + printf("\n\n"); + + /* + ** print general headerlines + */ + convdate(curtime, format1); /* date to ascii string */ + convtime(curtime, format2); /* time to ascii string */ + + if (screen) + attron(A_REVERSE); + + int seclen = val2elapstr(nsecs, buf); + int lenavail = (screen ? COLS : linelen) - + 49 - seclen - utsnodenamelen; + int len1 = lenavail / 3; + int len2 = lenavail - len1 - len1; + + printg("ATOP - %s%*s%s %s%*s%c%c%c%c%c%c%c%c%c%c%c%c%c%c%*s%s" + " elapsed", + utsname.nodename, len1, "", + format1, format2, len1, "", + threadview ? MTHREAD : '-', + fixedhead ? MSYSFIXED : '-', + sysnosort ? MSYSNOSORT : '-', + deviatonly ? '-' : MALLPROC, + usecolors ? '-' : MCOLORS, + avgval ? MAVGVAL : '-', + calcpss ? MCALCPSS : '-', + suppressexit ? MSUPEXITS : '-', + procsel.userid[0] != USERSTUB ? MSELUSER : '-', + procsel.prognamesz ? MSELPROC : '-', + procsel.container[0] ? MSELCONT : '-', + procsel.pid[0] != 0 ? MSELPID : '-', + procsel.argnamesz ? MSELARG : '-', + syssel.lvmnamesz + + syssel.dsknamesz + + syssel.itfnamesz ? MSELSYS : '-', + len2, "", buf); + + if (screen) + attroff(A_REVERSE); + else + printg("\n"); + + /* + ** print cumulative system- and user-time for all processes + */ + pricumproc(sstat, devtstat, nexit, noverflow, avgval, nsecs); + + if (noverflow) + { + snprintf(statbuf, sizeof statbuf, + "Only %d exited processes handled " + "-- %u skipped!", nexit, noverflow); + + statmsg = statbuf; + } + + curline=2; + + /* + ** print other lines of system-wide statistics + */ + if (showorder == MSORTAUTO) + autoorder = MSORTCPU; + else + autoorder = showorder; + + curline = prisyst(sstat, curline, nsecs, avgval, + fixedhead, &syssel, &autoorder, + maxcpulines, maxdsklines, maxmddlines, + maxlvmlines, maxintlines, maxnfslines, + maxcontlines); + + /* + ** if system-wide statistics do not fit, + ** limit the number of variable resource lines + ** and try again + */ + if (screen && curline+2 > LINES) + { + curline = 2; + + move(curline, 0); + clrtobot(); + move(curline, 0); + + limitedlines(); + + curline = prisyst(sstat, curline, nsecs, avgval, + fixedhead, &syssel, &autoorder, + maxcpulines, maxdsklines, maxmddlines, + maxlvmlines, maxintlines, maxnfslines, + maxcontlines); + + /* + ** if system-wide statistics still do not fit, + ** the window is really to small + */ + if (curline+2 > LINES) + { + endwin(); // finish curses interface + + fprintf(stderr, + "Not enough screen-lines available " + "(need at least %d lines)\n", curline+2); + fprintf(stderr, "Please resize window....\n"); + + cleanstop(1); + } + else + { + statmsg = "Number of variable resources" + " limited to fit in this window"; + } + } + + statline = curline; + + if (screen) + move(curline, 0); + + if (statmsg) + { + if (screen) + { + clrtoeol(); + if (usecolors) + attron(COLOR_PAIR(COLORINFO)); + } + + printg(statmsg); + + if (screen) + { + if (usecolors) + attroff(COLOR_PAIR(COLORINFO)); + } + + statmsg = NULL; + } + else + { + if (flag&RRBOOT) + { + if (screen) + { + if (usecolors) + attron(COLOR_PAIR(COLORINFO)); + + attron(A_BLINK); + + printg("%*s", (COLS-45)/2, " "); + } + else + { + printg(" "); + } + + printg("*** system and process activity " + "since boot ***"); + + if (screen) + { + if (usecolors) + attroff(COLOR_PAIR(COLORINFO)); + attroff(A_BLINK); + } + } + } + + /* + ** select the required list with tasks to be shown + ** + ** if cumulative figures required, accumulate resource + ** consumption of all processes in the current list + */ + switch (showtype) + { + case MCUMUSER: + threadallowed = 0; + + if (ucumlist) /* previous list still available? */ + { + free(ucumlist); + free(tucumlist); + ulastorder = 0; + } + + if (deviatonly) + nproc = devtstat->nprocactive; + else + nproc = devtstat->nprocall; + + /* + ** allocate space for new (temporary) list with + ** one entry per user (list has worst-case size) + */ + tucumlist = calloc(sizeof(struct tstat), nproc); + ucumlist = malloc(sizeof(struct tstat *) * nproc); + + ptrverify(tucumlist, + "Malloc failed for %d ucum procs\n", nproc); + ptrverify(ucumlist, + "Malloc failed for %d ucum ptrs\n", nproc); + + for (i=0; i < nproc; i++) + { + /* fill pointers */ + ucumlist[i] = tucumlist+i; + } + + nucum = cumusers(deviatonly ? + devtstat->procactive : + devtstat->procall, + tucumlist, nproc); + + curlist = ucumlist; + ncurlist = nucum; + lastsortp = &ulastorder; + break; + + case MCUMPROC: + threadallowed = 0; + + if (pcumlist) /* previous list still available? */ + { + free(pcumlist); + free(tpcumlist); + plastorder = 0; + } + + if (deviatonly) + nproc = devtstat->nprocactive; + else + nproc = devtstat->nprocall; + + /* + ** allocate space for new (temporary) list with + ** one entry per program (list has worst-case size) + */ + tpcumlist = calloc(sizeof(struct tstat), nproc); + pcumlist = malloc(sizeof(struct tstat *) * nproc); + + ptrverify(tpcumlist, + "Malloc failed for %d pcum procs\n", nproc); + ptrverify(pcumlist, + "Malloc failed for %d pcum ptrs\n", nproc); + + for (i=0; i < nproc; i++) + { + /* fill pointers */ + pcumlist[i] = tpcumlist+i; + } + + npcum = cumprogs(deviatonly ? + devtstat->procactive : + devtstat->procall, + tpcumlist, nproc); + + curlist = pcumlist; + ncurlist = npcum; + lastsortp = &plastorder; + break; + + case MCUMCONT: + threadallowed = 0; + + if (ccumlist) /* previous list still available? */ + { + free(ccumlist); + free(tccumlist); + clastorder = 0; + } + + if (deviatonly) + nproc = devtstat->nprocactive; + else + nproc = devtstat->nprocall; + + /* + ** allocate space for new (temporary) list with + ** one entry per user (list has worst-case size) + */ + tccumlist = calloc(sizeof(struct tstat), nproc); + ccumlist = malloc(sizeof(struct tstat *) * nproc); + + ptrverify(tccumlist, + "Malloc failed for %d ccum procs\n", nproc); + ptrverify(ccumlist, + "Malloc failed for %d ccum ptrs\n", nproc); + + for (i=0; i < nproc; i++) + { + /* fill pointers */ + ccumlist[i] = tccumlist+i; + } + + nccum = cumconts(deviatonly ? + devtstat->procactive : + devtstat->procall, + tccumlist, nproc); + + curlist = ccumlist; + ncurlist = nccum; + lastsortp = &clastorder; + break; + + default: + threadallowed = 1; + + if (deviatonly && showtype != MPROCMEM && + showorder != MSORTMEM ) + { + curlist = devtstat->procactive; + ncurlist = devtstat->nprocactive; + } + else + { + curlist = devtstat->procall; + ncurlist = devtstat->nprocall; + } + + lastsortp = &tlastorder; + + if ( procsel.userid[0] == USERSTUB && + !procsel.prognamesz && + !procsel.container[0] && + !procsel.argnamesz && + !procsel.pid[0] && + !suppressexit ) + /* no selection wanted */ + break; + + /* + ** selection specified for tasks: + ** create new (worst case) pointer list if needed + */ + if (sellist) // remove previous list if needed + free(sellist); + + sellist = malloc(sizeof(struct tstat *) * ncurlist); + + ptrverify(sellist, + "Malloc failed for %d select ptrs\n", ncurlist); + + for (i=nsel=0; i < ncurlist; i++) + { + if (procsuppress(*(curlist+i), &procsel)) + continue; + + if (curlist[i]->gen.state == 'E' && + suppressexit ) + continue; + + sellist[nsel++] = curlist[i]; + } + + curlist = sellist; + ncurlist = nsel; + tlastorder = 0; /* new sort and zip normal view */ + slastorder = 0; /* new sort and zip now */ + lastsortp = &slastorder; + } + + /* + ** sort the list in required order + ** (default CPU-consumption) and print the list + */ + if (showorder == MSORTAUTO) + curorder = autoorder; + else + curorder = showorder; + + /* + ** determine size of list to be displayed + */ + if (screen) + plistsz = LINES-curline-2; + else + if (threadview && threadallowed) + plistsz = devtstat->ntaskactive; + else + plistsz = ncurlist; + + + if (ncurlist > 0 && plistsz > 0) + { + /* + ** if sorting order is changed, sort again + */ + if (*lastsortp != curorder) + { + qsort(curlist, ncurlist, + sizeof(struct tstat *), + procsort[(int)curorder&0x1f]); + + *lastsortp = curorder; + + zipagain = 1; + } + + if (threadview && threadallowed) + { + int ntotal, j, t; + + if (deviatonly && showtype != MPROCMEM && + showorder != MSORTMEM ) + ntotal = devtstat->ntaskactive; + else + ntotal = devtstat->ntaskall; + + /* + ** check if existing pointer list still usable + ** if not, allocate new pointer list to be able + ** to zip process list with references to threads + */ + if (!tsklist || ntsk != ntotal || + tdeviate != deviatonly) + { + if (tsklist) + free(tsklist); // remove current + + tsklist = malloc(sizeof(struct tstat *) + * ntotal); + + ptrverify(tsklist, + "Malloc failed for %d taskptrs\n", + ntotal); + + ntsk = ntotal; + tdeviate = deviatonly; + + zipagain = 1; + } + else + j = ntotal; + + if (zipagain) + { + struct tstat *tall = devtstat->taskall; + struct tstat *pcur; + + for (i=j=0; i < ncurlist; i++) + { + pcur = curlist[i]; + + tsklist[j++] = pcur; + + for (t = pcur - tall + 1; + t < devtstat->ntaskall && + pcur->gen.tgid && + pcur->gen.tgid == + (tall+t)->gen.tgid; + t++) + { + if (deviatonly && + showtype != MPROCMEM && + showorder != MSORTMEM ) + { + if (!(tall+t)->gen.wasinactive) + { + tsklist[j++] = tall+t; + } + } + else + tsklist[j++] = tall+t; + } + } + + zipagain = 0; + } + + curlist = tsklist; + ncurlist = j; + } + + /* + ** print the header + ** first determine the column-header for the current + ** sorting order of processes + */ + if (screen) + { + attron(A_REVERSE); + move(curline+1, 0); + } + + priphead(firstproc/plistsz+1, (ncurlist-1)/plistsz+1, + &showtype, &curorder, + showorder == MSORTAUTO ? 1 : 0); + + if (screen) + { + attroff(A_REVERSE); + clrtobot(); + } + + /* + ** print the list + */ + priproc(curlist, firstproc, ncurlist, curline+2, + firstproc/plistsz+1, (ncurlist-1)/plistsz+1, + showtype, curorder, &syscap, nsecs, avgval); + } + + alistsz = ncurlist; /* preserve size of active list */ + + /* + ** in case of writing to a terminal, the user can also enter + ** a character to switch options, etc + */ + if (screen) + { + /* + ** show blinking pause-indication if necessary + */ + if (paused) + { + move(statline, COLS-6); + attron(A_BLINK); + attron(A_REVERSE); + printw("PAUSED"); + attroff(A_REVERSE); + attroff(A_BLINK); + } + + /* + ** await input-character or interval-timer expiration + */ + switch ( (lastchar = mvgetch(statline, 0)) ) + { + /* + ** timer expired + */ + case ERR: + case 0: + timeout(0); + (void) getch(); + timeout(-1); + if (tpcumlist) free(tpcumlist); + if (pcumlist) free(pcumlist); + if (tucumlist) free(tucumlist); + if (ucumlist) free(ucumlist); + if (tccumlist) free(tccumlist); + if (ccumlist) free(ccumlist); + if (tsklist) free(tsklist); + if (sellist) free(sellist); + + return lastchar; + + /* + ** stop it + */ + case MQUIT: + move(LINES-1, 0); + clrtoeol(); + refresh(); + cleanstop(0); + + /* + ** manual trigger for next sample + */ + case MSAMPNEXT: + if (paused) + break; + + getalarm(0); + + if (tpcumlist) free(tpcumlist); + if (pcumlist) free(pcumlist); + if (tucumlist) free(tucumlist); + if (ucumlist) free(ucumlist); + if (tccumlist) free(tccumlist); + if (ccumlist) free(ccumlist); + if (tsklist) free(tsklist); + if (sellist) free(sellist); + + return lastchar; + + /* + ** manual trigger for previous sample + */ + case MSAMPPREV: + if (!rawreadflag) + { + statmsg = "Only allowed when viewing " + "raw file!"; + beep(); + break; + } + + if (paused) + break; + + if (tpcumlist) free(tpcumlist); + if (pcumlist) free(pcumlist); + if (tucumlist) free(tucumlist); + if (ucumlist) free(ucumlist); + if (tccumlist) free(tccumlist); + if (ccumlist) free(ccumlist); + if (tsklist) free(tsklist); + if (sellist) free(sellist); + + return lastchar; + + /* + ** branch to certain time stamp + */ + case MSAMPBRANCH: + if (!rawreadflag) + { + statmsg = "Only allowed when viewing " + "raw file!"; + beep(); + break; + } + + if (paused) + break; + + echo(); + move(statline, 0); + clrtoeol(); + printw("Enter new time (format hh:mm): "); + + hhmm[0] = '\0'; + scanw("%15s\n", hhmm); + noecho(); + + if ( !hhmm2secs(hhmm, &begintime) ) + { + move(statline, 0); + clrtoeol(); + statmsg = "Wrong time format!"; + beep(); + begintime = 0; + break; + } + + if (tpcumlist) free(tpcumlist); + if (pcumlist) free(pcumlist); + if (tucumlist) free(tucumlist); + if (ucumlist) free(ucumlist); + if (tccumlist) free(tccumlist); + if (ccumlist) free(ccumlist); + if (tsklist) free(tsklist); + if (sellist) free(sellist); + + return lastchar; + + /* + ** sort order automatically depending on + ** most busy resource + */ + case MSORTAUTO: + showorder = MSORTAUTO; + firstproc = 0; + break; + + /* + ** sort in cpu-activity order + */ + case MSORTCPU: + showorder = MSORTCPU; + firstproc = 0; + break; + + /* + ** sort in memory-consumption order + */ + case MSORTMEM: + showorder = MSORTMEM; + firstproc = 0; + break; + + /* + ** sort in disk-activity order + */ + case MSORTDSK: + if ( !(supportflags & IOSTAT) ) + { + statmsg = "No disk-activity figures " + "available; request ignored!"; + break; + } + showorder = MSORTDSK; + firstproc = 0; + break; + + /* + ** sort in network-activity order + */ + case MSORTNET: + if ( !(supportflags & NETATOP) ) + { + statmsg = "Kernel module 'netatop' not " + "active or no root privs; " + "request ignored!"; + break; + } + showorder = MSORTNET; + firstproc = 0; + break; + + /* + ** general figures per process + */ + case MPROCGEN: + showtype = MPROCGEN; + + if (showorder != MSORTAUTO) + showorder = MSORTCPU; + + firstproc = 0; + break; + + /* + ** memory-specific figures per process + */ + case MPROCMEM: + showtype = MPROCMEM; + + if (showorder != MSORTAUTO) + showorder = MSORTMEM; + + firstproc = 0; + break; + + /* + ** disk-specific figures per process + */ + case MPROCDSK: + if ( !(supportflags & IOSTAT) ) + { + statmsg = "No disk-activity figures " + "available; request ignored!"; + break; + } + + showtype = MPROCDSK; + + if (showorder != MSORTAUTO) + showorder = MSORTDSK; + + firstproc = 0; + break; + + /* + ** network-specific figures per process + */ + case MPROCNET: + if ( !(supportflags & NETATOP) ) + { + statmsg = "Kernel module 'netatop' not " + "active or no root privs; " + "request ignored!"; + break; + } + + showtype = MPROCNET; + + if (showorder != MSORTAUTO) + showorder = MSORTNET; + + firstproc = 0; + break; + + /* + ** various info per process + */ + case MPROCVAR: + showtype = MPROCVAR; + firstproc = 0; + break; + + /* + ** command line per process + */ + case MPROCARG: + showtype = MPROCARG; + firstproc = 0; + break; + + /* + ** own defined output per process + */ + case MPROCOWN: + if (! ownprocs[0].f) + { + statmsg = "Own process line is not " + "configured in rc-file; " + "request ignored"; + break; + } + + showtype = MPROCOWN; + firstproc = 0; + break; + + /* + ** scheduling-values per process + */ + case MPROCSCH: + showtype = MPROCSCH; + + if (showorder != MSORTAUTO) + showorder = MSORTCPU; + + firstproc = 0; + break; + + /* + ** accumulated resource consumption per user + */ + case MCUMUSER: + statmsg = "Consumption per user; use 'a' to " + "toggle between all/active processes"; + + showtype = MCUMUSER; + firstproc = 0; + break; + + /* + ** accumulated resource consumption per program + */ + case MCUMPROC: + statmsg = "Consumption per program; use 'a' to " + "toggle between all/active processes"; + + showtype = MCUMPROC; + firstproc = 0; + break; + + /* + ** accumulated resource consumption per container + */ + case MCUMCONT: + statmsg = "Consumption per container; use 'a' to " + "toggle between all/active processes"; + + showtype = MCUMCONT; + firstproc = 0; + break; + + /* + ** help wanted? + */ + case MHELP1: + case MHELP2: + alarm(0); /* stop the clock */ + + move(1, 0); + clrtobot(); /* blank the screen */ + refresh(); + + showhelp(2); + + move(statline, 0); + + if (interval && !paused && !rawreadflag) + alarm(3); /* force new sample */ + + firstproc = 0; + break; + + /* + ** send signal to process + */ + case MKILLPROC: + if (rawreadflag) + { + statmsg = "Not possible when viewing " + "raw file!"; + beep(); + break; + } + + alarm(0); /* stop the clock */ + + killpid = getnumval("Pid of process: ", + 0, statline); + + switch (killpid) + { + case 0: + case -1: + break; + + case 1: + statmsg = "Sending signal to pid 1 not " + "allowed!"; + beep(); + break; + + default: + clrtoeol(); + killsig = getnumval("Signal [%d]: ", + 15, statline); + + if ( kill(killpid, killsig) == -1) + { + statmsg = "Not possible to " + "send signal to this pid!"; + beep(); + } + } + + if (!paused) + alarm(3); /* set short timer */ + + firstproc = 0; + break; + + /* + ** change interval timeout + */ + case MINTERVAL: + if (rawreadflag) + { + statmsg = "Not possible when viewing " + "raw file!"; + beep(); + break; + } + + alarm(0); /* stop the clock */ + + interval = getnumval("New interval in seconds " + "(now %d): ", + interval, statline); + + if (interval) + { + if (!paused) + alarm(3); /* set short timer */ + } + else + { + statmsg = "No timer set; waiting for " + "manual trigger ('t')....."; + } + + firstproc = 0; + break; + + /* + ** focus on specific user + */ + case MSELUSER: + alarm(0); /* stop the clock */ + echo(); + + move(statline, 0); + clrtoeol(); + printw("Username as regular expression " + "(enter=all users): "); + + procsel.username[0] = '\0'; + scanw("%255s\n", procsel.username); + + noecho(); + + if (procsel.username[0]) /* data entered ? */ + { + regex_t userregex; + int u = 0; + + if ( regcomp(&userregex, + procsel.username, REG_NOSUB)) + { + statmsg = "Invalid regular " + "expression!"; + beep(); + + procsel.username[0] = '\0'; + } + else + { + while ( (pwd = getpwent())) + { + if (regexec(&userregex, + pwd->pw_name, 0, + NULL, 0)) + continue; + + if (u < MAXUSERSEL-1) + { + procsel.userid[u] = + pwd->pw_uid; + u++; + } + } + endpwent(); + + procsel.userid[u] = USERSTUB; + + if (u == 0) + { + /* + ** possibly a numerical + ** value specified? + */ + if (numeric( + procsel.username)) + { + procsel.userid[0] = + atoi(procsel.username); + procsel.userid[1] = + USERSTUB; + } + else + { + statmsg = + "No user-names " + "match this " + "pattern!"; + beep(); + } + } + } + } + else + { + procsel.userid[0] = USERSTUB; + } + + if (interval && !paused && !rawreadflag) + alarm(3); /* set short timer */ + + firstproc = 0; + break; + + /* + ** focus on specific process-name + */ + case MSELPROC: + alarm(0); /* stop the clock */ + echo(); + + move(statline, 0); + clrtoeol(); + printw("Process-name as regular " + "expression (enter=no regex): "); + + procsel.prognamesz = 0; + procsel.progname[0] = '\0'; + + scanw("%63s\n", procsel.progname); + procsel.prognamesz = strlen(procsel.progname); + + if (procsel.prognamesz) + { + if (regcomp(&procsel.progregex, + procsel.progname, REG_NOSUB)) + { + statmsg = "Invalid regular " + "expression!"; + beep(); + + procsel.prognamesz = 0; + procsel.progname[0] = '\0'; + } + } + + noecho(); + + move(statline, 0); + + if (interval && !paused && !rawreadflag) + alarm(3); /* set short timer */ + + firstproc = 0; + break; + + /* + ** focus on specific container id + */ + case MSELCONT: + alarm(0); /* stop the clock */ + echo(); + + move(statline, 0); + clrtoeol(); + printw("Containerid 12 postitions " + "(enter=all, " + "'host'=host processes): "); + + procsel.container[0] = '\0'; + scanw("%15s", procsel.container); + procsel.container[12] = '\0'; + + switch (strlen(procsel.container)) + { + case 0: + break; // enter key pressed + + case 4: // host? + if (strcmp(procsel.container, "host")) + { + statmsg="Invalid containerid!"; + beep(); + procsel.container[0] = '\0'; + } + else + { + procsel.container[0] = 'H'; + procsel.container[1] = '\0'; + } + break; + + case 12: // container id + (void)strtol(procsel.container, &p, 16); + + if (*p) + { + statmsg ="Containerid not hex!"; + beep(); + procsel.container[0] = '\0'; + } + break; + + default: + statmsg = "Invalid containerid!"; + beep(); + + procsel.container[0] = '\0'; + } + + noecho(); + + move(statline, 0); + + if (interval && !paused && !rawreadflag) + alarm(3); /* set short timer */ + + firstproc = 0; + break; + + /* + ** focus on specific PIDs + */ + case MSELPID: + alarm(0); /* stop the clock */ + echo(); + + move(statline, 0); + clrtoeol(); + printw("Comma-separated PIDs of processes " + "(enter=no selection): "); + + scanw("%79s\n", genline); + + int id = 0; + + char *pidp = strtok(genline, ","); + + while (pidp) + { + char *ep; + + if (id >= MAXPID-1) + { + procsel.pid[id] = 0; // stub + + statmsg = "Maximum number of" + "PIDs reached!"; + beep(); + break; + } + + procsel.pid[id] = strtol(pidp, &ep, 10); + + if (*ep) + { + statmsg = "Non-numerical PID!"; + beep(); + procsel.pid[0] = 0; // stub + break; + } + + id++; + pidp = strtok(NULL, ","); + } + + procsel.pid[id] = 0; // stub + + noecho(); + + move(statline, 0); + + if (interval && !paused && !rawreadflag) + alarm(3); /* set short timer */ + + firstproc = 0; + break; + + + /* + ** focus on specific command line arguments + */ + case MSELARG: + alarm(0); /* stop the clock */ + echo(); + + move(statline, 0); + clrtoeol(); + printw("Command line string as regular " + "expression (enter=no regex): "); + + procsel.argnamesz = 0; + procsel.argname[0] = '\0'; + + scanw("%63s\n", procsel.argname); + procsel.argnamesz = strlen(procsel.argname); + + if (procsel.argnamesz) + { + if (regcomp(&procsel.argregex, + procsel.argname, REG_NOSUB)) + { + statmsg = "Invalid regular " + "expression!"; + beep(); + + procsel.argnamesz = 0; + procsel.argname[0] = '\0'; + } + } + + noecho(); + + move(statline, 0); + + if (interval && !paused && !rawreadflag) + alarm(3); /* set short timer */ + + firstproc = 0; + break; + + /* + ** focus on specific system resource + */ + case MSELSYS: + alarm(0); /* stop the clock */ + echo(); + + move(statline, 0); + clrtoeol(); + printw("Logical volume name as regular " + "expression (enter=no specific name): "); + + syssel.lvmnamesz = 0; + syssel.lvmname[0] = '\0'; + + scanw("%63s\n", syssel.lvmname); + syssel.lvmnamesz = strlen(syssel.lvmname); + + if (syssel.lvmnamesz) + { + if (regcomp(&syssel.lvmregex, + syssel.lvmname, REG_NOSUB)) + { + statmsg = "Invalid regular " + "expression!"; + beep(); + + syssel.lvmnamesz = 0; + syssel.lvmname[0] = '\0'; + } + } + + move(statline, 0); + clrtoeol(); + printw("Disk name as regular " + "expression (enter=no specific name): "); + + syssel.dsknamesz = 0; + syssel.dskname[0] = '\0'; + + scanw("%63s\n", syssel.dskname); + syssel.dsknamesz = strlen(syssel.dskname); + + if (syssel.dsknamesz) + { + if (regcomp(&syssel.dskregex, + syssel.dskname, REG_NOSUB)) + { + statmsg = "Invalid regular " + "expression!"; + beep(); + + syssel.dsknamesz = 0; + syssel.dskname[0] = '\0'; + } + } + + move(statline, 0); + clrtoeol(); + printw("Interface name as regular " + "expression (enter=no specific name): "); + + syssel.itfnamesz = 0; + syssel.itfname[0] = '\0'; + + scanw("%63s\n", syssel.itfname); + syssel.itfnamesz = strlen(syssel.itfname); + + if (syssel.itfnamesz) + { + if (regcomp(&syssel.itfregex, + syssel.itfname, REG_NOSUB)) + { + statmsg = "Invalid regular " + "expression!"; + beep(); + + syssel.itfnamesz = 0; + syssel.itfname[0] = '\0'; + } + } + + noecho(); + + move(statline, 0); + + if (interval && !paused && !rawreadflag) + alarm(3); /* set short timer */ + + firstproc = 0; + break; + + /* + ** toggle pause-state + */ + case MPAUSE: + if (paused) + { + paused=0; + clrtoeol(); + refresh(); + + if (!rawreadflag) + alarm(1); + } + else + { + paused=1; + clrtoeol(); + refresh(); + alarm(0); /* stop the clock */ + } + break; + + /* + ** toggle between modified processes and + ** all processes + */ + case MALLPROC: + if (deviatonly) + { + deviatonly=0; + statmsg = "All processes/threads will be " + "shown/accumulated..."; + } + else + { + deviatonly=1; + statmsg = "Only active processes/threads " + "will be shown/accumulated..."; + } + + tlastorder = 0; + firstproc = 0; + break; + + /* + ** toggle average or total values + */ + case MAVGVAL: + if (avgval) + avgval=0; + else + avgval=1; + break; + + /* + ** system-statistics lines: + ** toggle fixed or variable + */ + case MSYSFIXED: + if (fixedhead) + { + fixedhead=0; + statmsg = "Only active system-resources" + " will be shown ......"; + } + else + { + fixedhead=1; + statmsg = "Also inactive " + "system-resources will be shown....."; + } + + firstproc = 0; + break; + + /* + ** system-statistics lines: + ** toggle fixed or variable + */ + case MSYSNOSORT: + if (sysnosort) + { + sysnosort=0; + statmsg = "System resources will be " + "sorted on utilization..."; + } + else + { + sysnosort=1; + statmsg = "System resources will not " + "be sorted on utilization..."; + } + + firstproc = 0; + break; + + /* + ** per-thread view wanted with sorting on + ** process level or thread level + */ + case MTHREAD: + if (threadview) + { + threadview = 0; + statmsg = "Thread view disabled"; + firstproc = 0; + } + else + { + threadview = 1; + statmsg = "Thread view enabled"; + firstproc = 0; + } + break; + + /* + ** per-process PSS calculation wanted + */ + case MCALCPSS: + if (calcpss) + { + calcpss = 0; + statmsg = "PSIZE gathering disabled"; + } + else + { + calcpss = 1; + statmsg = "PSIZE gathering enabled"; + } + break; + + /* + ** suppression of exited processes in output + */ + case MSUPEXITS: + if (suppressexit) + { + suppressexit = 0; + statmsg = "Exited processes will " + "be shown/accumulated"; + firstproc = 0; + } + else + { + suppressexit = 1; + statmsg = "Exited processes will " + "not be shown/accumulated"; + firstproc = 0; + } + break; + + /* + ** screen lines: + ** toggle for colors + */ + case MCOLORS: + if (usecolors) + { + usecolors=0; + statmsg = "No colors will be used..."; + } + else + { + if (screen && has_colors()) + { + usecolors=1; + statmsg = + "Colors will be used..."; + } + else + { + statmsg="No colors supported!"; + } + } + + firstproc = 0; + break; + + /* + ** system-statistics lines: + ** toggle no or all active disk + */ + case MSYSLIMIT: + alarm(0); /* stop the clock */ + + maxcpulines = + getnumval("Maximum lines for per-cpu " + "statistics (now %d): ", + maxcpulines, statline); + + if (sstat->dsk.nlvm > 0) + { + maxlvmlines = + getnumval("Maximum lines for LVM " + "statistics (now %d): ", + maxlvmlines, statline); + } + + if (sstat->dsk.nmdd > 0) + { + maxmddlines = + getnumval("Maximum lines for MD " + "device statistics (now %d): ", + maxmddlines, statline); + } + + maxdsklines = + getnumval("Maximum lines for disk " + "statistics (now %d): ", + maxdsklines, statline); + + maxintlines = + getnumval("Maximum lines for interface " + "statistics (now %d): ", + maxintlines, statline); + + maxnfslines = + getnumval("Maximum lines for NFS mount " + "statistics (now %d): ", + maxnfslines, statline); + + maxcontlines = + getnumval("Maximum lines for container " + "statistics (now %d): ", + maxcontlines, statline); + + if (interval && !paused && !rawreadflag) + alarm(3); /* set short timer */ + + firstproc = 0; + break; + + /* + ** reset statistics + */ + case MRESET: + getalarm(0); /* restart the clock */ + paused = 0; + + if (tpcumlist) free(tpcumlist); + if (pcumlist) free(pcumlist); + if (tucumlist) free(tucumlist); + if (ucumlist) free(ucumlist); + if (tccumlist) free(tccumlist); + if (ccumlist) free(ccumlist); + if (tsklist) free(tsklist); + if (sellist) free(sellist); + + return lastchar; + + /* + ** show version info + */ + case MVERSION: + statmsg = getstrvers(); + break; + + /* + ** handle redraw request + */ + case MREDRAW: + wclear(stdscr); + break; + + /* + ** handle arrow right for command line + */ + case KEY_RIGHT: + startoffset++; + break; + + /* + ** handle arrow left for command line + */ + case KEY_LEFT: + if (startoffset > 0) + startoffset--; + break; + + /* + ** handle arrow down to go one line down + */ + case KEY_DOWN: + if (firstproc < alistsz-1) + firstproc += 1; + break; + + /* + ** handle arrow up to go one line up + */ + case KEY_UP: + if (firstproc > 0) + firstproc -= 1; + break; + + /* + ** handle forward + */ + case KEY_NPAGE: + case MLISTFW: + if (alistsz-firstproc > plistsz) + firstproc += plistsz; + break; + + /* + ** handle backward + */ + case KEY_PPAGE: + case MLISTBW: + if (firstproc >= plistsz) + firstproc -= plistsz; + else + firstproc = 0; + break; + + /* + ** handle screen resize + */ + case KEY_RESIZE: + snprintf(statbuf, sizeof statbuf, + "Window resized to %dx%d...", + COLS, LINES); + statmsg = statbuf; + + timeout(0); + (void) getch(); + timeout(-1); + break; + + /* + ** unknown key-stroke + */ + default: + beep(); + } + } + else /* no screen */ + { + if (tpcumlist) free(tpcumlist); + if (pcumlist) free(pcumlist); + if (tucumlist) free(tucumlist); + if (ucumlist) free(ucumlist); + if (tccumlist) free(tccumlist); + if (ccumlist) free(ccumlist); + if (tsklist) free(tsklist); + if (sellist) free(sellist); + + return '\0'; + } + } +} + +/* +** accumulate all processes per user in new list +*/ +static int +cumusers(struct tstat **curprocs, struct tstat *curusers, int numprocs) +{ + register int i, numusers; + + /* + ** sort list of active processes in order of uid (increasing) + */ + qsort(curprocs, numprocs, sizeof(struct tstat *), compusr); + + /* + ** accumulate all processes per user in the new list + */ + for (numusers=i=0; i < numprocs; i++, curprocs++) + { + if (procsuppress(*curprocs, &procsel)) + continue; + + if ((*curprocs)->gen.state == 'E' && suppressexit) + continue; + + if ( curusers->gen.ruid != (*curprocs)->gen.ruid ) + { + if (curusers->gen.pid) + { + numusers++; + curusers++; + } + curusers->gen.ruid = (*curprocs)->gen.ruid; + } + + accumulate(*curprocs, curusers); + } + + if (curusers->gen.pid) + numusers++; + + return numusers; +} + + +/* +** accumulate all processes with the same name (i.e. same program) +** into a new list +*/ +static int +cumprogs(struct tstat **curprocs, struct tstat *curprogs, int numprocs) +{ + register int i, numprogs; + + /* + ** sort list of active processes in order of process-name + */ + qsort(curprocs, numprocs, sizeof(struct tstat *), compnam); + + /* + ** accumulate all processes with same name in the new list + */ + for (numprogs=i=0; i < numprocs; i++, curprocs++) + { + if (procsuppress(*curprocs, &procsel)) + continue; + + if ((*curprocs)->gen.state == 'E' && suppressexit) + continue; + + if ( strcmp(curprogs->gen.name, (*curprocs)->gen.name) != 0) + { + if (curprogs->gen.pid) + { + numprogs++; + curprogs++; + } + strcpy(curprogs->gen.name, (*curprocs)->gen.name); + } + + accumulate(*curprocs, curprogs); + } + + if (curprogs->gen.pid) + numprogs++; + + return numprogs; +} + +/* +** accumulate all processes per container in new list +*/ +static int +cumconts(struct tstat **curprocs, struct tstat *curconts, int numprocs) +{ + register int i, numconts; + + /* + ** sort list of active processes in order of container (increasing) + */ + qsort(curprocs, numprocs, sizeof(struct tstat *), compcon); + + /* + ** accumulate all processes per container in the new list + */ + for (numconts=i=0; i < numprocs; i++, curprocs++) + { + if (procsuppress(*curprocs, &procsel)) + continue; + + if ((*curprocs)->gen.state == 'E' && suppressexit) + continue; + + if ( strcmp(curconts->gen.container, + (*curprocs)->gen.container) != 0) + { + if (curconts->gen.pid) + { + numconts++; + curconts++; + } + strcpy(curconts->gen.container, + (*curprocs)->gen.container); + } + + accumulate(*curprocs, curconts); + } + + if (curconts->gen.pid) + numconts++; + + return numconts; +} + + +/* +** accumulate relevant counters from individual task to +** combined task +*/ +static void +accumulate(struct tstat *curproc, struct tstat *curstat) +{ + count_t nett_wsz; + + curstat->gen.pid++; /* misuse as counter */ + + curstat->gen.isproc = 1; + curstat->gen.nthr += curproc->gen.nthr; + curstat->cpu.utime += curproc->cpu.utime; + curstat->cpu.stime += curproc->cpu.stime; + + if (curproc->dsk.wsz > curproc->dsk.cwsz) + nett_wsz = curproc->dsk.wsz -curproc->dsk.cwsz; + else + nett_wsz = 0; + + curstat->dsk.rio += curproc->dsk.rsz; + curstat->dsk.wio += nett_wsz; + + curstat->dsk.rsz = curstat->dsk.rio; + curstat->dsk.wsz = curstat->dsk.wio; + + curstat->net.tcpsnd += curproc->net.tcpsnd; + curstat->net.tcprcv += curproc->net.tcprcv; + curstat->net.udpsnd += curproc->net.udpsnd; + curstat->net.udprcv += curproc->net.udprcv; + + curstat->net.tcpssz += curproc->net.tcpssz; + curstat->net.tcprsz += curproc->net.tcprsz; + curstat->net.udpssz += curproc->net.udpssz; + curstat->net.udprsz += curproc->net.udprsz; + + if (curproc->gen.state != 'E') + { + if (curstat->mem.pmem != -1) + { + if (curproc->mem.pmem != -1) // no errors? + curstat->mem.pmem += curproc->mem.pmem; + else + curstat->mem.pmem = -1; + } + + curstat->mem.vmem += curproc->mem.vmem; + curstat->mem.rmem += curproc->mem.rmem; + curstat->mem.vlibs += curproc->mem.vlibs; + curstat->mem.vdata += curproc->mem.vdata; + curstat->mem.vstack += curproc->mem.vstack; + curstat->mem.vswap += curproc->mem.vswap; + curstat->mem.rgrow += curproc->mem.rgrow; + curstat->mem.vgrow += curproc->mem.vgrow; + } +} + + +/* +** function that checks if the current process is selected or suppressed; +** returns 1 (suppress) or 0 (do not suppress) +*/ +static int +procsuppress(struct tstat *curstat, struct pselection *sel) +{ + /* + ** check if only processes of a particular user + ** should be shown + */ + if (sel->userid[0] != USERSTUB) + { + int u = 0; + + while (sel->userid[u] != USERSTUB) + { + if (sel->userid[u] == curstat->gen.ruid) + break; + u++; + } + + if (sel->userid[u] != curstat->gen.ruid) + return 1; + } + + /* + ** check if only processes with particular PIDs + ** should be shown + */ + if (sel->pid[0]) + { + int i = 0; + + while (sel->pid[i]) + { + if (sel->pid[i] == curstat->gen.pid) + break; + i++; + } + + if (sel->pid[i] != curstat->gen.pid) + return 1; + } + + /* + ** check if only processes with a particular name + ** should be shown + */ + if (sel->prognamesz && + regexec(&(sel->progregex), curstat->gen.name, 0, NULL, 0)) + return 1; + + /* + ** check if only processes with a particular command line string + ** should be shown + */ + if (sel->argnamesz) + { + if (curstat->gen.cmdline[0]) + { + if (regexec(&(sel->argregex), curstat->gen.cmdline, + 0, NULL, 0)) + return 1; + } + else + { + if (regexec(&(sel->argregex), curstat->gen.name, + 0, NULL, 0)) + return 1; + } + } + + /* + ** check if only processes related to a particular container + ** should be shown (container 'H' stands for native host processes) + */ + if (sel->container[0]) + { + if (sel->container[0] == 'H') // only host processes + { + if (curstat->gen.container[0]) + return 1; + } + else + { + if (memcmp(sel->container, curstat->gen.container, 12)) + return 1; + } + } + + return 0; +} + + +static void +limitedlines(void) +{ + maxcpulines = 0; + maxdsklines = 3; + maxmddlines = 3; + maxlvmlines = 4; + maxintlines = 2; + maxnfslines = 2; + maxcontlines = 0; +} + +/* +** get a numerical value from the user and verify +*/ +static long +getnumval(char *ask, long valuenow, int statline) +{ + char numval[16]; + long retval; + + echo(); + move(statline, 0); + clrtoeol(); + printw(ask, valuenow); + + numval[0] = 0; + scanw("%15s", numval); + + move(statline, 0); + noecho(); + + if (numval[0]) /* data entered ? */ + { + if ( numeric(numval) ) + { + retval = atol(numval); + } + else + { + beep(); + clrtoeol(); + printw("Value not numeric (current value kept)!"); + refresh(); + sleep(2); + retval = valuenow; + } + } + else + { + retval = valuenow; + } + + return retval; +} + +/* +** generic print-function which checks if printf should be used +** (to file or pipe) or curses (to screen) +*/ +void +printg(const char *format, ...) +{ + va_list args; + + va_start(args, format); + + if (screen) + vwprintw(stdscr, (char *) format, args); + else + vprintf(format, args); + + va_end (args); +} + +/* +** initialize generic sample output functions +*/ +static void +generic_init(void) +{ + int i; + + /* + ** check if default sort order and/or showtype are overruled + ** by command-line flags + */ + for (i=0; flaglist[i]; i++) + { + switch (flaglist[i]) + { + case MSORTAUTO: + showorder = MSORTAUTO; + break; + + case MSORTCPU: + showorder = MSORTCPU; + break; + + case MSORTMEM: + showorder = MSORTMEM; + break; + + case MSORTDSK: + showorder = MSORTDSK; + break; + + case MSORTNET: + showorder = MSORTNET; + break; + + case MPROCGEN: + showtype = MPROCGEN; + showorder = MSORTCPU; + break; + + case MPROCMEM: + showtype = MPROCMEM; + showorder = MSORTMEM; + break; + + case MPROCSCH: + showtype = MPROCSCH; + showorder = MSORTCPU; + break; + + case MPROCDSK: + if ( !(supportflags & IOSTAT) ) + { + fprintf(stderr, + "No disk-activity figures " + "available; request ignored\n"); + sleep(3); + break; + } + + showtype = MPROCDSK; + showorder = MSORTDSK; + break; + + case MPROCNET: + if ( !(supportflags & NETATOP) ) + { + fprintf(stderr, "Kernel module 'netatop' not " + "active; request ignored!"); + sleep(3); + break; + } + + showtype = MPROCNET; + showorder = MSORTNET; + break; + + case MPROCVAR: + showtype = MPROCVAR; + break; + + case MPROCARG: + showtype = MPROCARG; + break; + + case MPROCOWN: + showtype = MPROCOWN; + break; + + case MAVGVAL: + if (avgval) + avgval=0; + else + avgval=1; + break; + + case MCUMUSER: + showtype = MCUMUSER; + break; + + case MCUMPROC: + showtype = MCUMPROC; + break; + + case MCUMCONT: + showtype = MCUMCONT; + break; + + case MSYSFIXED: + if (fixedhead) + fixedhead=0; + else + fixedhead=1; + break; + + case MSYSNOSORT: + if (sysnosort) + sysnosort=0; + else + sysnosort=1; + break; + + case MTHREAD: + if (threadview) + threadview = 0; + else + threadview = 1; + break; + + case MCALCPSS: + if (calcpss) + calcpss = 0; + else + calcpss = 1; + break; + + case MSUPEXITS: + if (suppressexit) + suppressexit = 0; + else + suppressexit = 1; + break; + + case MCOLORS: + if (usecolors) + usecolors=0; + else + usecolors=1; + break; + + case MSYSLIMIT: + limitedlines(); + break; + + default: + prusage("atop"); + } + } + + /* + ** set stdout output on line-basis + */ + setvbuf(stdout, (char *)0, _IOLBF, BUFSIZ); + + /* + ** check if STDOUT is related to a tty or + ** something else (file, pipe) + */ + if ( isatty(1) ) + screen = 1; + else + screen = 0; + + /* + ** install catch-routine to finish in a controlled way + ** and activate cbreak-mode + */ + if (screen) + { + /* + ** initialize screen-handling via curses + */ + initscr(); + cbreak(); + noecho(); + keypad(stdscr, TRUE); + + if (COLS < 30) + { + endwin(); // finish curses interface + + fprintf(stderr, "Not enough columns available\n" + "(need at least %d columns)\n", 30); + fprintf(stderr, "Please resize window....\n"); + + cleanstop(1); + } + + if (has_colors()) + { + use_default_colors(); + start_color(); + + init_pair(COLORINFO, colorinfo, -1); + init_pair(COLORALMOST, coloralmost, -1); + init_pair(COLORCRIT, colorcrit, -1); + init_pair(COLORTHR, colorthread, -1); + } + else + { + usecolors = 0; + } + } + + signal(SIGINT, cleanstop); + signal(SIGTERM, cleanstop); +} + +/* +** show help information in interactive mode +*/ +static struct helptext { + char *helpline; + char helparg; +} helptext[] = { + {"Figures shown for active processes:\n", ' '}, + {"\t'%c' - generic info (default)\n", MPROCGEN}, + {"\t'%c' - memory details\n", MPROCMEM}, + {"\t'%c' - disk details\n", MPROCDSK}, + {"\t'%c' - network details\n", MPROCNET}, + {"\t'%c' - scheduling and thread-group info\n", MPROCSCH}, + {"\t'%c' - various info (ppid, user/group, date/time, status, " + "exitcode)\n", MPROCVAR}, + {"\t'%c' - full command line per process\n", MPROCARG}, + {"\t'%c' - use own output line definition\n", MPROCOWN}, + {"\n", ' '}, + {"Sort list of processes in order of:\n", ' '}, + {"\t'%c' - cpu activity\n", MSORTCPU}, + {"\t'%c' - memory consumption\n", MSORTMEM}, + {"\t'%c' - disk activity\n", MSORTDSK}, + {"\t'%c' - network activity\n", MSORTNET}, + {"\t'%c' - most active system resource (auto mode)\n", MSORTAUTO}, + {"\n", ' '}, + {"Accumulated figures:\n", ' '}, + {"\t'%c' - total resource consumption per user\n", MCUMUSER}, + {"\t'%c' - total resource consumption per program (i.e. same " + "process name)\n", MCUMPROC}, + {"\t'%c' - total resource consumption per container\n",MCUMCONT}, + {"\n", ' '}, + {"Process selections (keys shown in header line):\n", ' '}, + {"\t'%c' - focus on specific user name " + "(regular expression)\n", MSELUSER}, + {"\t'%c' - focus on specific program name " + "(regular expression)\n", MSELPROC}, + {"\t'%c' - focus on specific contained id (CID)\n", MSELCONT}, + {"\t'%c' - focus on specific command line string " + "(regular expression)\n", MSELARG}, + {"\t'%c' - focus on specific process id (PID)\n", MSELPID}, + {"\n", ' '}, + {"System resource selections (keys shown in header line):\n",' '}, + {"\t'%c' - focus on specific system resources " + "(regular expression)\n", MSELSYS}, + {"\n", ' '}, + {"Screen-handling:\n", ' '}, + {"\t^L - redraw the screen \n", ' '}, + {"\tPgDn - show next page in the process list (or ^F)\n", ' '}, + {"\tArDn - arrow-down for next line in process list\n", ' '}, + {"\tPgUp - show previous page in the process list (or ^B)\n", ' '}, + {"\tArUp arrow-up for previous line in process list\n", ' '}, + {"\n", ' '}, + {"\tArRt - arrow-right for next character in full command line\n", ' '}, + {"\tArLt - arrow-left for previous character in full command line\n", + ' '}, + {"\n", ' '}, + {"Presentation (keys shown in header line):\n", ' '}, + {"\t'%c' - show individual threads (toggle)\n", + MTHREAD}, + {"\t'%c' - show all processes (default: active processes) (toggle)\n", + MALLPROC}, + {"\t'%c' - show fixed number of header lines (toggle)\n", + MSYSFIXED}, + {"\t'%c' - suppress sorting system resources (toggle)\n", + MSYSNOSORT}, + {"\t'%c' - suppress exited processes in output (toggle)\n", + MSUPEXITS}, + {"\t'%c' - no colors to indicate high occupation (toggle)\n", + MCOLORS}, + {"\t'%c' - show average-per-second i.s.o. total values (toggle)\n", + MAVGVAL}, + {"\t'%c' - calculate proportional set size (PSIZE) (toggle)\n", + MCALCPSS}, + {"\n", ' '}, + {"Raw file viewing:\n", ' '}, + {"\t'%c' - show next sample in raw file\n", MSAMPNEXT}, + {"\t'%c' - show previous sample in raw file\n", MSAMPPREV}, + {"\t'%c' - branch to certain time in raw file\n", MSAMPBRANCH}, + {"\t'%c' - rewind to begin of raw file\n", MRESET}, + {"\n", ' '}, + {"Miscellaneous commands:\n", ' '}, + {"\t'%c' - change interval timer (0 = only manual trigger)\n", + MINTERVAL}, + {"\t'%c' - manual trigger to force next sample\n", MSAMPNEXT}, + {"\t'%c' - reset counters to boot time values\n", MRESET}, + {"\t'%c' - pause button to freeze current sample (toggle)\n", + MPAUSE}, + {"\n", ' '}, + {"\t'%c' - limited lines for per-cpu, disk and interface resources\n", + MSYSLIMIT}, + {"\t'%c' - kill a process (i.e. send a signal)\n", MKILLPROC}, + {"\n", ' '}, + {"\t'%c' - version information\n", MVERSION}, + {"\t'%c' - help information\n", MHELP1}, + {"\t'%c' - help information\n", MHELP2}, + {"\t'%c' - quit this program\n", MQUIT}, +}; + +static int helplines = sizeof(helptext)/sizeof(struct helptext); + +static void +showhelp(int helpline) +{ + int winlines = LINES-helpline, shown, tobeshown=1, i; + WINDOW *helpwin; + + /* + ** create a new window for the help-info in which scrolling is + ** allowed + */ + helpwin = newwin(winlines, COLS, helpline, 0); + scrollok(helpwin, 1); + + /* + ** show help-lines + */ + for (i=0, shown=0; i < helplines; i++, shown++) + { + wprintw(helpwin, helptext[i].helpline, helptext[i].helparg); + + /* + ** when the window is full, start paging interactively + */ + if (i >= winlines-2 && shown >= tobeshown) + { + wmove (helpwin, winlines-1, 0); + wclrtoeol(helpwin); + wprintw (helpwin, "Press 'q' to leave help, " + "space for next page or " + "other key for next line... "); + + switch (wgetch(helpwin)) + { + case 'q': + delwin(helpwin); + return; + case ' ': + shown = 0; + tobeshown = winlines-1; + break; + default: + shown = 0; + tobeshown = 1; + } + + wmove (helpwin, winlines-1, 0); + } + } + + wmove (helpwin, winlines-1, 0); + wclrtoeol(helpwin); + wprintw (helpwin, "End of help - press 'q' to leave help... "); + while ( wgetch(helpwin) != 'q' ); + delwin (helpwin); +} + +/* +** function to be called to print error-messages +*/ +void +generic_error(const char *format, ...) +{ + va_list args; + + va_start(args, format); + vfprintf(stderr, format, args); + va_end (args); +} + +/* +** function to be called when the program stops +*/ +void +generic_end(void) +{ + endwin(); +} + +/* +** function to be called when usage-info is required +*/ +void +generic_usage(void) +{ + printf("\t -%c show fixed number of lines with system statistics\n", + MSYSFIXED); + printf("\t -%c suppress sorting of system resources\n", + MSYSNOSORT); + printf("\t -%c suppress exited processes in output\n", + MSUPEXITS); + printf("\t -%c show limited number of lines for certain resources\n", + MSYSLIMIT); + printf("\t -%c show individual threads\n", MTHREAD); + printf("\t -%c show average-per-second i.s.o. total values\n\n", + MAVGVAL); + printf("\t -%c no colors in case of high occupation\n", + MCOLORS); + printf("\t -%c show general process-info (default)\n", + MPROCGEN); + printf("\t -%c show memory-related process-info\n", + MPROCMEM); + printf("\t -%c show disk-related process-info\n", + MPROCDSK); + printf("\t -%c show network-related process-info\n", + MPROCNET); + printf("\t -%c show scheduling-related process-info\n", + MPROCSCH); + printf("\t -%c show various process-info (ppid, user/group, " + "date/time)\n", MPROCVAR); + printf("\t -%c show command line per process\n", + MPROCARG); + printf("\t -%c show own defined process-info\n", + MPROCOWN); + printf("\t -%c show cumulated process-info per user\n", + MCUMUSER); + printf("\t -%c show cumulated process-info per program " + "(i.e. same name)\n", + MCUMPROC); + printf("\t -%c show cumulated process-info per container\n\n", + MCUMCONT); + printf("\t -%c sort processes in order of cpu-consumption " + "(default)\n", + MSORTCPU); + printf("\t -%c sort processes in order of memory-consumption\n", + MSORTMEM); + printf("\t -%c sort processes in order of disk-activity\n", + MSORTDSK); + printf("\t -%c sort processes in order of network-activity\n", + MSORTNET); + printf("\t -%c sort processes in order of most active resource " + "(auto mode)\n", + MSORTAUTO); +} + +/* +** functions to handle a particular tag in the /etc/atoprc and .atoprc file +*/ +void +do_username(char *name, char *val) +{ + struct passwd *pwd; + + strncpy(procsel.username, val, sizeof procsel.username -1); + procsel.username[sizeof procsel.username -1] = 0; + + if (procsel.username[0]) + { + regex_t userregex; + int u = 0; + + if (regcomp(&userregex, procsel.username, REG_NOSUB)) + { + fprintf(stderr, + "atoprc - %s: invalid regular expression %s\n", + name, val); + exit(1); + } + + while ( (pwd = getpwent())) + { + if (regexec(&userregex, pwd->pw_name, 0, NULL, 0)) + continue; + + if (u < MAXUSERSEL-1) + { + procsel.userid[u] = pwd->pw_uid; + u++; + } + } + endpwent(); + + procsel.userid[u] = USERSTUB; + + if (u == 0) + { + /* + ** possibly a numerical value has been specified + */ + if (numeric(procsel.username)) + { + procsel.userid[0] = atoi(procsel.username); + procsel.userid[1] = USERSTUB; + } + else + { + fprintf(stderr, + "atoprc - %s: user-names matching %s " + "do not exist\n", name, val); + exit(1); + } + } + } + else + { + procsel.userid[0] = USERSTUB; + } +} + +void +do_procname(char *name, char *val) +{ + strncpy(procsel.progname, val, sizeof procsel.progname -1); + procsel.prognamesz = strlen(procsel.progname); + + if (procsel.prognamesz) + { + if (regcomp(&procsel.progregex, procsel.progname, REG_NOSUB)) + { + fprintf(stderr, + "atoprc - %s: invalid regular expression %s\n", + name, val); + exit(1); + } + } +} + +extern int get_posval(char *name, char *val); + + +void +do_maxcpu(char *name, char *val) +{ + maxcpulines = get_posval(name, val); +} + +void +do_maxdisk(char *name, char *val) +{ + maxdsklines = get_posval(name, val); +} + +void +do_maxmdd(char *name, char *val) +{ + maxmddlines = get_posval(name, val); +} + +void +do_maxlvm(char *name, char *val) +{ + maxlvmlines = get_posval(name, val); +} + +void +do_maxintf(char *name, char *val) +{ + maxintlines = get_posval(name, val); +} + +void +do_maxnfsm(char *name, char *val) +{ + maxnfslines = get_posval(name, val); +} + +void +do_maxcont(char *name, char *val) +{ + maxcontlines = get_posval(name, val); +} + + +struct colmap { + char *colname; + short colval; +} colormap[] = { + { "red", COLOR_RED, }, + { "green", COLOR_GREEN, }, + { "yellow", COLOR_YELLOW, }, + { "blue", COLOR_BLUE, }, + { "magenta", COLOR_MAGENTA, }, + { "cyan", COLOR_CYAN, }, + { "black", COLOR_BLACK, }, + { "white", COLOR_WHITE, }, +}; +static short +modify_color(char *colorname) +{ + int i; + + for (i=0; i < sizeof colormap/sizeof colormap[0]; i++) + { + if ( strcmp(colorname, colormap[i].colname) == 0) + return colormap[i].colval; + } + + // required color not found + fprintf(stderr, "atoprc - invalid color used: %s\n", colorname); + fprintf(stderr, "supported colors:"); + for (i=0; i < sizeof colormap/sizeof colormap[0]; i++) + fprintf(stderr, " %s", colormap[i].colname); + fprintf(stderr, "\n"); + + exit(1); +} + + +void +do_colinfo(char *name, char *val) +{ + colorinfo = modify_color(val); +} + +void +do_colalmost(char *name, char *val) +{ + coloralmost = modify_color(val); +} + +void +do_colcrit(char *name, char *val) +{ + colorcrit = modify_color(val); +} + +void +do_colthread(char *name, char *val) +{ + colorthread = modify_color(val); +} + +void +do_flags(char *name, char *val) +{ + int i; + + for (i=0; val[i]; i++) + { + switch (val[i]) + { + case '-': + break; + + case MSORTCPU: + showorder = MSORTCPU; + break; + + case MSORTMEM: + showorder = MSORTMEM; + break; + + case MSORTDSK: + showorder = MSORTDSK; + break; + + case MSORTNET: + showorder = MSORTNET; + break; + + case MSORTAUTO: + showorder = MSORTAUTO; + break; + + case MPROCGEN: + showtype = MPROCGEN; + showorder = MSORTCPU; + break; + + case MPROCMEM: + showtype = MPROCMEM; + showorder = MSORTMEM; + break; + + case MPROCDSK: + showtype = MPROCDSK; + showorder = MSORTDSK; + break; + + case MPROCNET: + showtype = MPROCNET; + showorder = MSORTNET; + break; + + case MPROCVAR: + showtype = MPROCVAR; + break; + + case MPROCSCH: + showtype = MPROCSCH; + showorder = MSORTCPU; + break; + + case MPROCARG: + showtype = MPROCARG; + break; + + case MPROCOWN: + showtype = MPROCOWN; + break; + + case MCUMUSER: + showtype = MCUMUSER; + break; + + case MCUMPROC: + showtype = MCUMPROC; + break; + + case MCUMCONT: + showtype = MCUMCONT; + break; + + case MALLPROC: + deviatonly = 0; + break; + + case MAVGVAL: + avgval=1; + break; + + case MSYSFIXED: + fixedhead = 1; + break; + + case MSYSNOSORT: + sysnosort = 1; + break; + + case MTHREAD: + threadview = 1; + break; + + case MCOLORS: + usecolors = 0; + break; + + case MCALCPSS: + calcpss = 1; + break; + + case MSUPEXITS: + suppressexit = 1; + break; + } + } +} diff --git a/sources/showgeneric.h b/sources/showgeneric.h new file mode 100644 index 0000000..77141a8 --- /dev/null +++ b/sources/showgeneric.h @@ -0,0 +1,150 @@ +/* +** ATOP - System & Process Monitor +** +** The program 'atop' offers the possibility to view the activity of +** the system on system-level as well as process-level. +** +** Include-file describing prototypes and structures for visualization +** of counters. +** ================================================================ +** Author: Gerlof Langeveld +** E-mail: gerlof.langeveld@atoptool.nl +** Date: July 2002 +** +** This program is free software; you can redistribute it and/or modify it +** under the terms of the GNU General Public License as published by the +** Free Software Foundation; either version 2, or (at your option) any +** later version. +** +** This program is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +** See the GNU General Public License for more details. +*/ +#define USERSTUB 9999999 +#define MAXUSERSEL 64 +#define MAXPID 32 + +struct syscap { + int nrcpu; + count_t availcpu; + count_t availmem; + count_t availdsk; + count_t availnet; +}; + +struct pselection { + char username[256]; + uid_t userid[MAXUSERSEL]; + + pid_t pid[MAXPID]; + + char progname[64]; + int prognamesz; + regex_t progregex; + + char argname[64]; + int argnamesz; + regex_t argregex; + + char container[16]; +}; + +struct sselection { + char lvmname[64]; // logical volume selection + int lvmnamesz; + regex_t lvmregex; + + char dskname[64]; // disk selection + int dsknamesz; + regex_t dskregex; + + char itfname[64]; // network interface selection + int itfnamesz; + regex_t itfregex; +}; + +/* +** color names +*/ +#define COLORINFO 2 +#define COLORALMOST 3 +#define COLORCRIT 4 +#define COLORTHR 5 + +/* +** list with keystrokes/flags +*/ +#define MPROCGEN 'g' +#define MPROCMEM 'm' +#define MPROCDSK 'd' +#define MPROCNET 'n' +#define MPROCSCH 's' +#define MPROCVAR 'v' +#define MPROCARG 'c' +#define MPROCOWN 'o' + +#define MCUMUSER 'u' +#define MCUMPROC 'p' +#define MCUMCONT 'j' + +#define MSORTCPU 'C' +#define MSORTDSK 'D' +#define MSORTMEM 'M' +#define MSORTNET 'N' +#define MSORTAUTO 'A' + +#define MTHREAD 'y' +#define MCALCPSS 'R' +#define MSUPEXITS 'G' +#define MCOLORS 'x' +#define MSYSFIXED 'f' +#define MSYSNOSORT 'F' +#define MSYSLIMIT 'l' + +#define MSELUSER 'U' +#define MSELPROC 'P' +#define MSELCONT 'J' +#define MSELPID 'I' +#define MSELARG '/' +#define MSELSYS 'S' + +#define MALLPROC 'a' +#define MKILLPROC 'k' +#define MLISTFW 0x06 +#define MLISTBW 0x02 +#define MREDRAW 0x0c +#define MINTERVAL 'i' +#define MPAUSE 'z' +#define MQUIT 'q' +#define MRESET 'r' +#define MSAMPNEXT 't' +#define MSAMPPREV 'T' +#define MSAMPBRANCH 'b' +#define MVERSION 'V' +#define MAVGVAL '1' +#define MHELP1 '?' +#define MHELP2 'h' + +/* +** general function prototypes +*/ +void totalcap (struct syscap *, struct sstat *, struct tstat **, int); +void pricumproc (struct sstat *, struct devtstat *, + int, unsigned int, int, int); + +void showgenproc(struct tstat *, double, int, int); +void showmemproc(struct tstat *, double, int, int); +void showdskproc(struct tstat *, double, int, int); +void shownetproc(struct tstat *, double, int, int); +void showvarproc(struct tstat *, double, int, int); +void showschproc(struct tstat *, double, int, int); +void showtotproc(struct tstat *, double, int, int); +void showcmdproc(struct tstat *, double, int, int); + +void printg (const char *, ...); +int prisyst(struct sstat *, int, int, int, int, struct sselection *, + char *, int, int, int, int, int, int, int); +int priproc(struct tstat **, int, int, int, int, int, char, char, + struct syscap *, int, int); +void priphead(int, int, char *, char *, char); diff --git a/sources/showlinux.c b/sources/showlinux.c new file mode 100644 index 0000000..9fe4ea3 --- /dev/null +++ b/sources/showlinux.c @@ -0,0 +1,2371 @@ +/* +** ATOP - System & Process Monitor +** +** The program 'atop' offers the possibility to view the activity of +** the system on system-level as well as process-level. +** +** This source-file contains the Linux-specific functions to calculate +** figures to be visualized. +** ========================================================================== +** Author: Gerlof Langeveld +** Original version. +** E-mail: gerlof.langeveld@atoptool.nl +** Date: July 2002 +** +** Author: JC van Winkel - AT Computing, Nijmegen, Holland +** Complete redesign. +** E-mail: jc@ATComputing.nl +** Date: November 2009 +** -------------------------------------------------------------------------- +** Copyright (C) 2009-2010 JC van Winkel +** +** This program is free software; you can redistribute it and/or modify it +** under the terms of the GNU General Public License as published by the +** Free Software Foundation; either version 2, or (at your option) any +** later version. +** +** This program is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +** See the GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +** -------------------------------------------------------------------------- +** +** $Log: showlinux.c,v $ +** Revision 1.70 2010/10/23 14:04:12 gerlof +** Counters for total number of running and sleep threads (JC van Winkel). +** +** Revision 1.69 2010/05/18 19:20:08 gerlof +** Introduce CPU frequency and scaling (JC van Winkel). +** +** Revision 1.68 2010/04/23 12:19:35 gerlof +** Modified mail-address in header. +** +** Revision 1.67 2010/04/17 17:20:33 gerlof +** Allow modifying the layout of the columns in the system lines. +** +** Revision 1.66 2010/03/16 21:14:46 gerlof +** Program and user selection can be combined with program and user +** accumulation. +** +** Revision 1.65 2010/03/04 10:53:26 gerlof +** Support I/O-statistics on logical volumes and MD devices. +** +** Revision 1.64 2010/01/18 18:06:28 gerlof +** Modified priorities for system-level columns. +** +** Revision 1.63 2010/01/16 12:54:33 gerlof +** Corrected order of columns. +** +** Revision 1.62 2010/01/16 11:38:02 gerlof +** Corrected counters for patched kernels (JC van Winkel). +** +** Revision 1.61 2010/01/08 11:25:56 gerlof +** Corrected column-width and priorities of network-stats. +** +** Revision 1.60 2010/01/03 18:27:19 gerlof +** *** empty log message *** +** +** Revision 1.59 2009/12/19 21:01:28 gerlof +** Improved syntax checking for ownprocline keyword (JC van Winkel). +** +** Revision 1.58 2009/12/17 11:59:28 gerlof +** Gather and display new counters: dirty cache and guest cpu usage. +** +** Revision 1.57 2009/12/17 10:51:19 gerlof +** Allow own defined process line with key 'o' and a definition +** in the atoprc file. +** +** Revision 1.56 2009/12/17 09:13:19 gerlof +** Reformatted some fields for better grouping of info. +** +** Revision 1.55 2009/12/12 10:11:18 gerlof +** Register and display end date and end time for process. +** +** Revision 1.54 2009/12/12 09:06:48 gerlof +** \Corrected cumulated disk I/O per user/program (JC van Winkel). +** +** Revision 1.53 2009/12/10 14:02:39 gerlof +** Add EUID, SUID and FSUID (and similar for GID's). +** +** Revision 1.52 2009/12/10 11:56:34 gerlof +** Various bug-solutions. +** +** Revision 1.51 2009/12/10 10:08:01 gerlof +** Major redesign for improved user interface (variable number of columns). +** Made by JC van Winkel. +** +** Revision 1.49 2008/03/06 08:38:28 gerlof +** Register/show ppid of a process. +** +** Revision 1.48 2008/01/18 07:37:05 gerlof +** Show information about the state of the individual threads +** in the scheduling report shown with keystroke 's'. +** +** Revision 1.47 2008/01/07 11:34:18 gerlof +** Correct the sort-order of network-interfaces (on busy-percentage). +** +** Revision 1.46 2007/11/07 09:23:29 gerlof +** Modified format for avg1, avg5 and avg15 (CPL) when counters too large. +** +** Revision 1.45 2007/11/05 11:43:25 gerlof +** Bug-solution for new-process indicator on 64-bits machines. +** +** Revision 1.44 2007/11/05 10:57:56 gerlof +** Bug-solution for huge exit code on 64-bits machines. +** +** Revision 1.43 2007/08/17 09:45:57 gerlof +** Experimental: gather info about HTTP statistics. +** +** Revision 1.42 2007/08/16 12:02:04 gerlof +** Add support for atopsar reporting. +** Concerns modification of networking-counters. +** +** Revision 1.41 2007/07/04 10:18:16 gerlof +** Bug-solution for division by zero. +** +** Revision 1.40 2007/07/03 09:02:29 gerlof +** Support Apache-statistics. +** +** Revision 1.39 2007/03/22 10:12:54 gerlof +** Support for io counters (>= kernel 2.6.20). +** +** Revision 1.38 2007/03/21 14:22:24 gerlof +** Handle io counters maintained from 2.6.20 +** +** Revision 1.37 2007/02/13 10:36:09 gerlof +** Removal of external declarations. +** Use of hertz variable instead of HZ. +** +** Revision 1.36 2007/01/26 12:11:07 gerlof +** Add configuration-value 'swoutcritsec'. +** +** Revision 1.35 2007/01/26 10:25:42 gerlof +** Introduce steal percentage for virtual machines. +** Correct bug: when one interface is colored all subsequent interfaces +** are colored. +** +** Revision 1.34 2007/01/18 10:58:45 gerlof +** Only check for committed limit if it is not zero. +** +** Revision 1.33 2007/01/18 10:37:09 gerlof +** Add support for colors. +** Add support for automatic determination of most critical resource. +** Add support for parsing of new arguments in ~/.atoprc +** +** Revision 1.32 2006/11/13 13:48:46 gerlof +** Implement load-average counters, context-switches and interrupts. +** +** Revision 1.31 2006/02/07 08:38:49 gerlof +** Swapped the zombie counter and exit counter in the PRC-line. +** +** Revision 1.30 2006/02/07 08:30:07 gerlof +** Add possibility to show counters per second. +** Ease parsing of output-lines by fixed number of columns per line. +** +** Revision 1.29 2006/01/30 09:24:12 gerlof +** PRC-line: 'exits' modified to 'exit' to save space. +** +** Revision 1.28 2006/01/30 09:14:26 gerlof +** Extend memory counters (a.o. page scans). +** +** Revision 1.27 2005/11/04 14:16:45 gerlof +** Minor bug-solutions. +** +** Revision 1.26 2005/10/28 09:51:29 gerlof +** All flags/subcommands are defined as macro's. +** Subcommand 'p' has been changed to 'z' (pause). +** +** Revision 1.25 2005/10/21 09:51:11 gerlof +** Per-user accumulation of resource consumption. +** +** Revision 1.24 2004/12/14 15:06:48 gerlof +** Implementation of patch-recognition for disk and network-statistics. +** +** Revision 1.23 2004/10/28 08:31:41 gerlof +** New counter: vm committed space +** +** Revision 1.22 2004/09/24 10:02:46 gerlof +** Wrong cpu-numbers for system level statistics. +** +** Revision 1.21 2004/09/23 08:21:10 gerlof +** Added wait-percentage per cpu. +** +** Revision 1.20 2004/09/23 07:37:34 gerlof +** Consistent handling of CPU percentages on system-level and process-level. +** +** Revision 1.19 2004/09/13 09:20:21 gerlof +** Modify subcommands (former 's' -> 'v', 'v' -> 'V', new 's'). +** +** Revision 1.18 2004/09/02 10:55:21 root +** Added sleep-average to process-info. +** +** Revision 1.17 2004/08/31 09:53:31 gerlof +** Show information about underlying threads. +** +** Revision 1.16 2004/06/01 11:58:34 gerlof +** Regular expressions for selections on process-name and user-name. +** +** Revision 1.15 2004/05/06 09:47:59 gerlof +** Ported to kernel-version 2.6. +** +** Revision 1.14 2003/07/07 09:27:34 gerlof +** Cleanup code (-Wall proof). +** +** Revision 1.13 2003/07/03 12:04:25 gerlof +** Minor bug fixes. +** +** Revision 1.12 2003/06/30 11:29:57 gerlof +** Enlarge counters to 'long long'. +** +** Revision 1.11 2003/06/24 06:22:10 gerlof +** Limit number of system resource lines. +** +** Revision 1.10 2003/02/07 10:43:22 gerlof +** Solved a division-by-zero bug for process-percentage. +** +** Revision 1.9 2003/01/24 14:20:57 gerlof +** If possible, also show commandline when process has exited. +** +** Revision 1.8 2003/01/17 07:32:49 gerlof +** Show the full command-line per process (option 'c'). +** +** Revision 1.7 2002/10/04 10:05:54 gerlof +** Bug-solution: New process indicator in static output set when needed. +** +** Revision 1.6 2002/10/03 11:14:42 gerlof +** Modify (effective) uid/gid to real uid/gid. +** +** Revision 1.5 2002/09/26 13:52:51 gerlof +** Limit header lines by not showing disks. +** Limit header lines by not showing disks. +** +** Revision 1.4 2002/09/16 08:59:13 gerlof +** Change field EXCODE to STATUS for support of indicator of newly created +** processes. +** +** Revision 1.3 2002/09/02 08:42:44 gerlof +** Bug-solution: blank line after header when more than 999 screens of +** process-list information. +** +** Revision 1.2 2002/08/30 07:11:20 gerlof +** Minor changes in the header-line of the process list. +** +** Revision 1.1 2002/07/24 11:14:16 gerlof +** Initial revision +** +** +** Initial +** +*/ + +static const char rcsid[] = "$Id: showlinux.c,v 1.70 2010/10/23 14:04:12 gerlof Exp $"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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= 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 && iconfigname, 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 && iconfigname, name)==0) + { + ar[i].f=allprocpdefs[j]; + ar[i].prio=items[i].prio; + break; + } + } + if (allprocpdefs[j]==0) + { + fprintf(stderr, + "atoprc - ownprocline: item %s invalid!\n", + name); + cleanstop(1); + } + } + ar[i].f=0; + ar[i].prio=0; +} + +/* +** calculate the total consumption on system-level for the +** four main resources +*/ +void +totalcap(struct syscap *psc, struct sstat *sstat, + struct tstat **proclist, int nactproc) +{ + register int i; + + psc->nrcpu = sstat->cpu.nrcpu; + psc->availcpu = sstat->cpu.all.stime + + sstat->cpu.all.utime + + sstat->cpu.all.ntime + + sstat->cpu.all.itime + + sstat->cpu.all.wtime + + sstat->cpu.all.Itime + + sstat->cpu.all.Stime + + sstat->cpu.all.steal; + + psc->availmem = sstat->mem.physmem * pagesize/1024; + + /* + ** calculate total transfer issued by the active processes + ** for disk and for network + */ + for (psc->availnet=psc->availdsk=0, i=0; i < nactproc; i++) + { + struct tstat *curstat = *(proclist+i); + count_t nett_wsz; + + psc->availnet += curstat->net.tcpssz; + psc->availnet += curstat->net.tcprsz; + psc->availnet += curstat->net.udpssz; + psc->availnet += curstat->net.udprsz; + + if (curstat->dsk.wsz > curstat->dsk.cwsz) + nett_wsz = curstat->dsk.wsz - + curstat->dsk.cwsz; + else + nett_wsz = 0; + + psc->availdsk += curstat->dsk.rsz; + psc->availdsk += nett_wsz; + } +} + +/* +** calculate cumulative system- and user-time for all active processes +*/ +void +pricumproc(struct sstat *sstat, struct devtstat *devtstat, + int nexit, unsigned int noverflow, int avgval, int nsecs) +{ + + static int firsttime=1; + + if (firsttime) + { + firsttime=0; + + if (sysprcline[0].f == 0) + { + make_sys_prints(sysprcline, MAXITEMS, + "PRCSYS:8 " + "PRCUSER:8 " + "BLANKBOX:0 " + "PRCNPROC:7 " + "PRCNRUNNING:5 " + "PRCNSLEEPING:5 " + "PRCNDSLEEPING:5 " + "PRCNZOMBIE:5 " + "PRCCLONES:4 " + "BLANKBOX:0 " + "PRCNNEXIT:6", prcsyspdefs, "builtin sysprcline"); + } + if (allcpuline[0].f == 0) + { + make_sys_prints(allcpuline, MAXITEMS, + "CPUSYS:9 " + "CPUUSER:8 " + "CPUIRQ:5 " + "BLANKBOX:0 " + "CPUIDLE:6 " + "CPUWAIT:6 " + "BLANKBOX:0 " + "CPUSTEAL:2 " + "CPUGUEST:3 " + "CPUFREQ:4 " + "CPUSCALE:4 ", cpusyspdefs, "builtin allcpuline"); + } + + if (indivcpuline[0].f == 0) + { + make_sys_prints(indivcpuline, MAXITEMS, + "CPUISYS:9 " + "CPUIUSER:8 " + "CPUIIRQ:5 " + "BLANKBOX:0 " + "CPUIIDLE:6 " + "CPUIWAIT:6 " + "BLANKBOX:0 " + "CPUISTEAL:2 " + "CPUIGUEST:3 " + "CPUIFREQ:4 " + "CPUISCALE:4 ", cpisyspdefs, "builtin indivcpuline"); + } + + if (cplline[0].f == 0) + { + make_sys_prints(cplline, MAXITEMS, + "CPLAVG1:4 " + "CPLAVG5:3 " + "CPLAVG15:2 " + "BLANKBOX:0 " + "CPLCSW:6 " + "CPLINTR:5 " + "BLANKBOX:0 " + "CPLNUMCPU:1", cplsyspdefs, "builtin cplline"); + } + + if (memline[0].f == 0) + { + make_sys_prints(memline, MAXITEMS, + "MEMTOT:6 " + "MEMFREE:7 " + "MEMCACHE:5 " + "MEMDIRTY:3 " + "MEMBUFFER:5 " + "MEMSLAB:5 " + "RECSLAB:2 " + "BLANKBOX:0 " + "SHMEM:4 " + "SHMRSS:3 " + "SHMSWP:1 " + "BLANKBOX:0 " + "VMWBAL:4 " + "BLANKBOX:0 " + "HUPTOT:4 " + "HUPUSE:3 ", memsyspdefs, "builtin memline"); + } + if (swpline[0].f == 0) + { + make_sys_prints(swpline, MAXITEMS, + "SWPTOT:3 " + "SWPFREE:4 " + "BLANKBOX:0 " + "BLANKBOX:0 " + "BLANKBOX:0 " + "BLANKBOX:0 " + "BLANKBOX:0 " + "BLANKBOX:0 " + "SWPCOMMITTED:5 " + "SWPCOMMITLIM:6", swpsyspdefs, "builtin swpline"); + } + if (pagline[0].f == 0) + { + make_sys_prints(pagline, MAXITEMS, + "PAGSCAN:3 " + "PAGSTEAL:3 " + "PAGSTALL:1 " + "BLANKBOX:0 " + "BLANKBOX:0 " + "BLANKBOX:0 " + "BLANKBOX:0 " + "BLANKBOX:0 " + "PAGSWIN:3 " + "PAGSWOUT:4", pagsyspdefs, "builtin pagline"); + } + if (contline[0].f == 0) + { + make_sys_prints(contline, MAXITEMS, + "CONTNAME:8 " + "CONTNPROC:7 " + "CONTCPU:6 " + "CONTMEM:6 " + "BLANKBOX:0 " + "BLANKBOX:0 ", contsyspdefs, "builtin contline"); + } + if (dskline[0].f == 0) + { + make_sys_prints(dskline, MAXITEMS, + "DSKNAME:8 " + "DSKBUSY:7 " + "DSKNREAD:6 " + "DSKNWRITE:6 " + "DSKKBPERRD:4 " + "DSKKBPERWR:4 " + "DSKMBPERSECRD:5 " + "DSKMBPERSECWR:5 " + "DSKAVQUEUE:1 " + "DSKAVIO:5", dsksyspdefs, "builtin dskline"); + } + if (nfsmountline[0].f == 0) + { + make_sys_prints(nfsmountline, MAXITEMS, + "NFMPATH:8 " + "NFMSERVER:8 " + "NFMTOTREAD:8 " + "NFMTOTWRITE:8 " + "BLANKBOX:0 " + "NFMNREAD:7 " + "NFMNWRITE:6 " + "BLANKBOX:0 " + "NFMDREAD:5 " + "NFMDWRITE:4 " + "BLANKBOX:0 " + "NFMMREAD:3 " + "NFMMWRITE:2 " + "BLANKBOX:0 " + "BLANKBOX:0", nfsmntsyspdefs, "builtin nfsmountline"); + } + if (nfcline[0].f == 0) + { + make_sys_prints(nfcline, MAXITEMS, + "NFCRPCCNT:8 " + "NFCRPCREAD:7 " + "NFCRPCWRITE:7 " + "NFCRPCRET:5 " + "NFCRPCARF:5 " + "BLANKBOX:0 " + "BLANKBOX:0 " + "BLANKBOX:0 " + "BLANKBOX:0 " + "BLANKBOX:0 ", nfcsyspdefs, "builtin nfcline"); + } + if (nfsline[0].f == 0) + { + make_sys_prints(nfsline, MAXITEMS, + "NFSRPCCNT:8 " + "NFSRPCREAD:6 " + "NFSRPCWRITE:6 " + "BLANKBOX:0 " + "NFSNRBYTES:7 " + "NFSNWBYTES:7 " + "BLANKBOX:0 " + "NFSNETTCP:5 " + "NFSNETUDP:5 " + "BLANKBOX:0 " + "NFSRCHITS:3 " + "NFSRCMISS:2 " + "NFSRCNOCA:1 " + "BLANKBOX:0 " + "NFSBADFMT:4 " + "NFSBADAUT:4 " + "NFSBADCLN:4 ", nfssyspdefs, "builtin nfsline"); + } + if (nettransportline[0].f == 0) + { + make_sys_prints(nettransportline, MAXITEMS, + "NETTRANSPORT:9 " + "NETTCPI:8 " + "NETTCPO:8 " + "NETUDPI:8 " + "NETUDPO:8 " + "NETTCPACTOPEN:6 " + "NETTCPPASVOPEN:5 " + "NETTCPRETRANS:4 " + "NETTCPINERR:3 " + "NETTCPORESET:2 " + "NETUDPNOPORT:1 " + "NETUDPINERR:3", nettranssyspdefs, "builtin nettransportline"); + } + if (netnetline[0].f == 0) + { + make_sys_prints(netnetline, MAXITEMS, + "NETNETWORK:5 " + "NETIPI:4 " + "NETIPO:4 " + "NETIPFRW:4 " + "NETIPDELIV:4 " + "BLANKBOX:0 " + "BLANKBOX:0 " + "BLANKBOX:0 " + "NETICMPIN:1 " + "NETICMPOUT:1 ", netnetsyspdefs, "builtin netnetline"); + } + if (netinterfaceline[0].f == 0) + { + make_sys_prints(netinterfaceline, MAXITEMS, + "NETNAME:8 " + "BLANKBOX:0 " + "NETPCKI:7 " + "NETPCKO:7 " + "BLANKBOX:0 " + "NETSPEEDMAX:5 " + "NETSPEEDIN:6 " + "NETSPEEDOUT:6 " + "BLANKBOX:0 " + "NETCOLLIS:2 " + "NETMULTICASTIN:2 " + "NETRCVERR:4 " + "NETSNDERR:4 " + "NETRCVDROP:3 " + "NETSNDDROP:3", netintfsyspdefs, "builtin netinterfaceline"); + } + } // firsttime + + + int i; + extraparam extra; + + + for (i=0, extra.totut=extra.totst=0; i < devtstat->nprocactive; i++) + { + struct tstat *curstat = *(devtstat->procactive+i); + + extra.totut += curstat->cpu.utime; + extra.totst += curstat->cpu.stime; + } + + extra.nproc = devtstat->nprocall; + extra.ntrun = devtstat->totrun; + extra.ntslpi = devtstat->totslpi; + extra.ntslpu = devtstat->totslpu; + extra.nzomb = devtstat->totzombie; + extra.nexit = nexit; + extra.noverflow = noverflow; + extra.avgval = avgval; + extra.nsecs = nsecs; + + move(1, 0); + showsysline(sysprcline, sstat, &extra, "PRC", 0); +} + +/* +** print the header for the process list +*/ +void +priphead(int curlist, int totlist, char *showtype, char *showorder, + char autosort) +{ + static int firsttime=1; + static int prev_supportflags = -1, prev_threadview = -1; + + /* + ** determine once the layout of all per-process reports + ** except for the generic report (might change dynamically) + */ + if (firsttime) + { + init_proc_prints(); + + make_proc_prints(memprocs, MAXITEMS, + "PID:10 TID:3 MINFLT:2 MAJFLT:2 VSTEXT:4 VSLIBS:4 " + "VDATA:4 VSTACK:4 VSIZE:6 RSIZE:7 PSIZE:5 " + "VGROW:7 RGROW:8 SWAPSZ:5 RUID:1 EUID:0 " + "SORTITEM:9 CMD:10", + "built-in memprocs"); + + make_proc_prints(schedprocs, MAXITEMS, + "PID:10 TID:6 CID:5 VPID:4 CTID:4 TRUN:7 TSLPI:7 " + "TSLPU:7 POLI:8 NICE:9 PRI:9 RTPR:9 CPUNR:8 ST:8 " + "EXC:8 S:8 SORTITEM:10 CMD:10", + "built-in schedprocs"); + + make_proc_prints(dskprocs, MAXITEMS, + "PID:10 TID:4 RDDSK:9 " + "WRDSK:9 WCANCL:8 " + "SORTITEM:10 CMD:10", + "built-in dskprocs"); + + make_proc_prints(netprocs, MAXITEMS, + "PID:10 TID:6 " + "TCPRCV:9 TCPRASZ:4 TCPSND:9 TCPSASZ:4 " + "UDPRCV:8 UDPRASZ:3 UDPSND:8 UDPSASZ:3 " + "BANDWI:10 BANDWO:10 " + "SORTITEM:10 CMD:10", + "built-in netprocs"); + + make_proc_prints(varprocs, MAXITEMS, + "PID:10 TID:4 PPID:9 CID:2 VPID:1 CTID:1 " + "RUID:8 RGID:8 EUID:5 EGID:4 " + "SUID:3 SGID:2 FSUID:3 FSGID:2 " + "STDATE:7 STTIME:7 ENDATE:5 ENTIME:5 " + "ST:6 EXC:6 S:6 SORTITEM:10 CMD:10", + "built-in varprocs"); + + make_proc_prints(cmdprocs, MAXITEMS, + "PID:10 TID:4 S:8 SORTITEM:10 COMMAND-LINE:10", + "built-in cmdprocs"); + + make_proc_prints(totusers, MAXITEMS, + "NPROCS:10 SYSCPU:9 USRCPU:9 VSIZE:6 " + "RSIZE:8 PSIZE:8 SWAPSZ:5 RDDSK:7 CWRDSK:7 " + "RNET:6 SNET:6 SORTITEM:10 RUID:10", + "built-in totusers"); + + make_proc_prints(totprocs, MAXITEMS, + "NPROCS:10 SYSCPU:9 USRCPU:9 VSIZE:6 " + "RSIZE:8 PSIZE:8 SWAPSZ:5 RDDSK:7 CWRDSK:7 " + "RNET:6 SNET:6 SORTITEM:10 CMD:10", + "built-in totprocs"); + + make_proc_prints(totconts, MAXITEMS, + "NPROCS:10 SYSCPU:9 USRCPU:9 VSIZE:6 " + "RSIZE:8 PSIZE:8 SWAPSZ:5 RDDSK:7 CWRDSK:7 " + "RNET:6 SNET:6 SORTITEM:10 CID:10", + "built-in totconts"); + } + + /* + ** update the generic report if needed + */ + if (prev_supportflags != supportflags || prev_threadview != threadview) + { + make_proc_dynamicgen(); + + prev_supportflags = supportflags; + prev_threadview = threadview; + + if (*showtype == MPROCNET && !(supportflags&NETATOP) ) + { + *showtype = MPROCGEN; + *showorder = MSORTCPU; + } + } + + /* + ** print the header line + */ + switch (*showtype) + { + case MPROCGEN: + showhdrline(genprocs, curlist, totlist, *showorder, autosort); + break; + + case MPROCMEM: + showhdrline(memprocs, curlist, totlist, *showorder, autosort); + break; + + case MPROCDSK: + showhdrline(dskprocs, curlist, totlist, *showorder, autosort); + break; + + case MPROCNET: + showhdrline(netprocs, curlist, totlist, *showorder, autosort); + break; + + case MPROCVAR: + showhdrline(varprocs, curlist, totlist, *showorder, autosort); + break; + + case MPROCARG: + showhdrline(cmdprocs, curlist, totlist, *showorder, autosort); + break; + + case MPROCOWN: + showhdrline(ownprocs, curlist, totlist, *showorder, autosort); + break; + + case MPROCSCH: + showhdrline(schedprocs, curlist, totlist, *showorder, autosort); + break; + + case MCUMUSER: + showhdrline(totusers, curlist, totlist, *showorder, autosort); + break; + + case MCUMPROC: + showhdrline(totprocs, curlist, totlist, *showorder, autosort); + break; + + case MCUMCONT: + showhdrline(totconts, curlist, totlist, *showorder, autosort); + break; + } +} + +/* +** assemble the layout of the generic line, +** depending on the supported features (like +** I/O stats, network stats) and current view +*/ +#define FORMPID "PID:10 " +#define FORMTID "TID:6 " +#define FORMCID "CID:5 " +#define FORMCPU "SYSCPU:9 USRCPU:9 " +#define FORMMEM "VGROW:8 RGROW:8 " +#define FORMDSK "RDDSK:7 CWRDSK:7 " +#define FORMNET "RNET:6 SNET:6 " +#define FORMMSC "RUID:3 EUID:2 ST:4 EXC:4 THR:4 S:4 CPUNR:4 " +#define FORMEND "SORTITEM:10 CMD:10" + +static void +make_proc_dynamicgen() +{ + char format[300], *p = format; + + memcpy(p, FORMPID, sizeof FORMPID -1); + p += sizeof FORMPID -1; + + if (threadview) + { + memcpy(p, FORMTID, sizeof FORMTID -1); + p += sizeof FORMTID -1; + } + + if (supportflags & DOCKSTAT) + { + memcpy(p, FORMCID, sizeof FORMCID -1); + p += sizeof FORMCID -1; + } + + memcpy(p, FORMCPU, sizeof FORMCPU -1); + p += sizeof FORMCPU -1; + + memcpy(p, FORMMEM, sizeof FORMMEM -1); + p += sizeof FORMMEM -1; + + if (supportflags & IOSTAT) + { + memcpy(p, FORMDSK, sizeof FORMDSK -1); + p += sizeof FORMDSK -1; + } + + if (supportflags & NETATOP) + { + memcpy(p, FORMNET, sizeof FORMNET -1); + p += sizeof FORMNET -1; + } + + memcpy(p, FORMMSC, sizeof FORMMSC -1); + p += sizeof FORMMSC -1; + + memcpy(p, FORMEND, sizeof FORMEND); + p += sizeof FORMEND; + + make_proc_prints(genprocs, MAXITEMS, format, "built-in genprocs"); +} + +/* +** print the list of processes from the deviation-list +*/ +int +priproc(struct tstat **proclist, int firstproc, int lastproc, int curline, + int curlist, int totlist, char showtype, char showorder, + struct syscap *sb, int nsecs, int avgval) +{ + register int i; + register struct tstat *curstat; + double perc; + + /* + ** print info per actual process and maintain totals + */ + for (i=firstproc; i < lastproc; i++) + { + curstat = *(proclist+i); + + if (screen && curline >= LINES) /* screen filled entirely ? */ + break; + + /* + ** calculate occupation-percentage of this process + ** depending on selected resource + */ + switch (showorder) + { + case MSORTCPU: + perc = 0.0; + + if (sb->availcpu) + { + perc = (double)(curstat->cpu.stime + + curstat->cpu.utime ) * 100 / + (sb->availcpu / sb->nrcpu); + + if (perc > 100.0 * sb->nrcpu) + perc = 100.0 * sb->nrcpu; + + if (perc > 100.0 * curstat->gen.nthr) + perc = 100.0 * curstat->gen.nthr; + } + break; + + case MSORTMEM: + perc = 0.0; + + if (sb->availmem) + { + perc = (double)curstat->mem.rmem * + 100.0 / sb->availmem; + + if (perc > 100.0) + perc = 100.0; + } + break; + + case MSORTDSK: + perc = 0.0; + + if (sb->availdsk) + { + count_t nett_wsz; + + + if (curstat->dsk.wsz > curstat->dsk.cwsz) + nett_wsz = curstat->dsk.wsz - + curstat->dsk.cwsz; + else + nett_wsz = 0; + + perc = (double)(curstat->dsk.rsz + nett_wsz) * + 100.0 / sb->availdsk; + + if (perc > 100.0) + perc = 100.0; + } + break; + + case MSORTNET: + perc = 0.0; + + if (sb->availnet) + { + perc = (double)(curstat->net.tcpssz + + curstat->net.tcprsz + + curstat->net.udpssz + + curstat->net.udprsz ) * + 100.0 / sb->availnet; + + if (perc > 100.0) + perc = 100.0; + } + break; + + default: + perc = 0.0; + } + + /* + ** now do the formatting of output + */ + if (screen) { + move(curline,0); + } + + switch (showtype) + { + case MPROCGEN: + showprocline(genprocs, curstat, perc, nsecs, avgval); + break; + + case MPROCMEM: + showprocline(memprocs, curstat, perc, nsecs, avgval); + break; + + case MPROCDSK: + showprocline(dskprocs, curstat, perc, nsecs, avgval); + break; + + case MPROCNET: + showprocline(netprocs, curstat, perc, nsecs, avgval); + break; + + case MPROCVAR: + showprocline(varprocs, curstat, perc, nsecs, avgval); + break; + + case MPROCARG: + showprocline(cmdprocs, curstat, perc, nsecs, avgval); + break; + + case MPROCOWN: + showprocline(ownprocs, curstat, perc, nsecs, avgval); + break; + + case MPROCSCH: + showprocline(schedprocs, curstat, perc, nsecs, avgval); + break; + + case MCUMUSER: + showprocline(totusers, curstat, perc, nsecs, avgval); + break; + + case MCUMPROC: + showprocline(totprocs, curstat, perc, nsecs, avgval); + break; + + case MCUMCONT: + showprocline(totconts, curstat, perc, nsecs, avgval); + break; + } + + curline++; + } + + return curline; +} + + +/* +** print the system-wide statistics +*/ +static void pridisklike(extraparam *, struct perdsk *, char *, + char *, int, unsigned int *, int *, int, regex_t *); + +int +prisyst(struct sstat *sstat, int curline, int nsecs, int avgval, + int fixedhead, struct sselection *selp, char *highorderp, + int maxcpulines, int maxdsklines, int maxmddlines, + int maxlvmlines, int maxintlines, int maxnfslines, int maxcontlines) +{ + extraparam extra; + int lin; + count_t busy; + unsigned int badness, highbadness=0; + + extra.nsecs = nsecs; + extra.avgval = avgval; + + /* + ** CPU statistics + */ + extra.cputot = sstat->cpu.all.stime + sstat->cpu.all.utime + + sstat->cpu.all.ntime + sstat->cpu.all.itime + + sstat->cpu.all.wtime + sstat->cpu.all.Itime + + sstat->cpu.all.Stime + sstat->cpu.all.steal; + + busy = (extra.cputot - sstat->cpu.all.itime - sstat->cpu.all.wtime) + * 100.0 / extra.cputot; + + if (cpubadness) + badness = busy * 100 / cpubadness; + else + badness = 0; + + if (highbadness < badness) + { + highbadness = badness; + *highorderp = MSORTCPU; + } + + if (extra.cputot == 0) + extra.cputot = 1; /* avoid divide-by-zero */ + + extra.percputot = extra.cputot / sstat->cpu.nrcpu; + + if (extra.percputot == 0) + extra.percputot = 1; /* avoid divide-by-zero */ + + if (screen) + move(curline, 0); + + showsysline(allcpuline, sstat, &extra, "CPU", badness); + curline++; + + if (sstat->cpu.nrcpu > 1) + { + for (extra.index=lin=0; + extra.index < sstat->cpu.nrcpu && lin < maxcpulines; + extra.index++) + { + extra.percputot = sstat->cpu.cpu[extra.index].stime + + sstat->cpu.cpu[extra.index].utime + + sstat->cpu.cpu[extra.index].ntime + + sstat->cpu.cpu[extra.index].itime + + sstat->cpu.cpu[extra.index].wtime + + sstat->cpu.cpu[extra.index].Itime + + sstat->cpu.cpu[extra.index].Stime + + sstat->cpu.cpu[extra.index].steal; + + if (extra.percputot == + (sstat->cpu.cpu[extra.index].itime + + sstat->cpu.cpu[extra.index].wtime ) && + !fixedhead ) + continue; /* inactive cpu */ + + busy = (extra.percputot - + sstat->cpu.cpu[extra.index].itime - + sstat->cpu.cpu[extra.index].wtime) + * 100.0 / extra.percputot; + + if (cpubadness) + badness = busy * 100 / cpubadness; + else + badness = 0; + + if (highbadness < badness) + { + highbadness = badness; + *highorderp = MSORTCPU; + } + + if (extra.percputot == 0) + extra.percputot = 1; /* avoid divide-by-zero */ + + + move(curline, 0); + showsysline(indivcpuline, sstat, &extra, "cpu", + badness); + curline++; + lin++; + } + } + + /* + ** other CPU-related statistics + */ + if (screen) + move(curline, 0); + + showsysline(cplline, sstat, &extra, "CPL", 0); + curline++; + + /* + ** MEMORY statistics + */ + busy = (sstat->mem.physmem - sstat->mem.freemem + - sstat->mem.cachemem + - sstat->mem.buffermem + - sstat->mem.slabreclaim + + sstat->mem.shmem) + * 100.0 / sstat->mem.physmem; + + if (membadness) + badness = busy * 100 / membadness; + else + badness = 0; + + if (highbadness < badness) + { + highbadness = badness; + *highorderp = MSORTMEM; + } + + if (screen) + move(curline, 0); + + showsysline(memline, sstat, &extra, "MEM", badness); + curline++; + + /* + ** SWAP statistics + */ + busy = (sstat->mem.totswap - sstat->mem.freeswap) + * 100.0 / sstat->mem.totswap; + + if (swpbadness) + { + badness = busy * 100 / swpbadness; + } + else + { + badness = 0; + } + + if (highbadness < badness) + { + highbadness = badness; + *highorderp = MSORTMEM; + } + + if (screen) + move(curline, 0); + + showsysline(swpline, sstat, &extra, "SWP", badness); + curline++; + + /* + ** PAGING statistics + */ + if (fixedhead || + sstat->mem.pgscans || + sstat->mem.pgsteal || + sstat->mem.allocstall || + sstat->mem.swins || + sstat->mem.swouts ) + { + busy = sstat->mem.swouts / nsecs * pagbadness; + + if (busy > 100) + busy = 100; + + if (membadness) + badness = busy * 100 / membadness; + else + badness = 0; + + if (highbadness < badness) + { + highbadness = badness; + *highorderp = MSORTMEM; + } + + /* + ** take care that this line is anyhow colored for + ** 'almost critical' in case of swapouts > 1 per second + */ + if (sstat->mem.swouts / nsecs > 0 && + pagbadness && almostcrit && badness < almostcrit) + badness = almostcrit; + + if (screen) + move(curline, 0); + + showsysline(pagline, sstat, &extra,"PAG", badness); + curline++; + } + + /* + ** Container statistics (if any) + */ + for (extra.index=0, lin=0; + extra.index < sstat->cfs.nrcontainer && lin < maxcontlines; + extra.index++) + { + if (fixedhead || + sstat->cfs.cont[extra.index].system || + sstat->cfs.cont[extra.index].user || + sstat->cfs.cont[extra.index].nice ) + { + if (screen) + move(curline, 0); + + showsysline(contline, sstat, &extra, "CON", 0); + curline++; + lin++; + } + } + + /* + ** DISK statistics + */ + extra.mstot = extra.cputot * 1000 / hertz / sstat->cpu.nrcpu; + + pridisklike(&extra, sstat->dsk.lvm, "LVM", highorderp, maxlvmlines, + &highbadness, &curline, fixedhead, + selp->lvmnamesz ? &(selp->lvmregex) : (void *) 0); + + pridisklike(&extra, sstat->dsk.mdd, "MDD", highorderp, maxmddlines, + &highbadness, &curline, fixedhead, (void *) 0); + + pridisklike(&extra, sstat->dsk.dsk, "DSK", highorderp, maxdsklines, + &highbadness, &curline, fixedhead, + selp->dsknamesz ? &(selp->dskregex) : (void *) 0); + + /* + ** NFS server and client statistics + */ + for (extra.index=0, lin=0; + extra.index < sstat->nfs.nfsmounts.nrmounts && lin < maxnfslines; + extra.index++) + { + int i = extra.index; + + if ( (sstat->nfs.nfsmounts.nfsmnt[i].bytesread + + sstat->nfs.nfsmounts.nfsmnt[i].byteswrite + + sstat->nfs.nfsmounts.nfsmnt[i].bytesdread + + sstat->nfs.nfsmounts.nfsmnt[i].bytesdwrite + + sstat->nfs.nfsmounts.nfsmnt[i].bytestotread + + sstat->nfs.nfsmounts.nfsmnt[i].bytestotwrite + + sstat->nfs.nfsmounts.nfsmnt[i].pagesmread + + sstat->nfs.nfsmounts.nfsmnt[i].pagesmwrite ) || + sstat->nfs.nfsmounts.nfsmnt[i].age < nsecs || + fixedhead ) + { + if (screen) + move(curline, 0); + + showsysline(nfsmountline, sstat, &extra, "NFM", 0); + curline++; + lin++; + } + } + + if (sstat->nfs.client.rpccnt || fixedhead ) + { + if (screen) + move(curline, 0); + + showsysline(nfcline, sstat, &extra, "NFC", 0); + curline++; + } + + if (sstat->nfs.server.rpccnt || fixedhead ) + { + if (screen) + move(curline, 0); + + showsysline(nfsline, sstat, &extra, "NFS", 0); + curline++; + } + + /* + ** NET statistics + */ + if (sstat->net.tcp.InSegs || + sstat->net.tcp.OutSegs || + sstat->net.udpv4.InDatagrams || + sstat->net.udpv6.Udp6InDatagrams || + sstat->net.udpv4.OutDatagrams || + sstat->net.udpv6.Udp6OutDatagrams || + fixedhead ) + { + if (screen) + move(curline, 0); + + showsysline(nettransportline, sstat, &extra, "NET", 0); + curline++; + } + + if (sstat->net.ipv4.InReceives || + sstat->net.ipv6.Ip6InReceives || + sstat->net.ipv4.OutRequests || + sstat->net.ipv6.Ip6OutRequests || + fixedhead ) + { + if (screen) + move(curline, 0); + + showsysline(netnetline, sstat, &extra, "NET", 0); + curline++; + } + + for (extra.index=0, lin=0; + sstat->intf.intf[extra.index].name[0] && lin < maxintlines; + extra.index++) + { + if (sstat->intf.intf[extra.index].rpack || + sstat->intf.intf[extra.index].spack || fixedhead) + { + if (selp->itfnamesz && regexec(&(selp->itfregex), + sstat->intf.intf[extra.index].name, 0, NULL, 0)) + continue; // suppress (not selected) + + /* + ** calculate busy-percentage for interface + */ + + count_t ival, oval; + + /* + ** convert byte-transfers to bit-transfers (* 8) + ** convert bit-transfers to kilobit-transfers (/ 1000) + ** per second + */ + ival = sstat->intf.intf[extra.index].rbyte/125/nsecs; + oval = sstat->intf.intf[extra.index].sbyte/125/nsecs; + + /* speed known? */ + if (sstat->intf.intf[extra.index].speed) + { + if (sstat->intf.intf[extra.index].duplex) + busy = (ival > oval ? ival : oval) / + (sstat->intf.intf[extra.index].speed + *10); + else + busy = (ival + oval) / + (sstat->intf.intf[extra.index].speed + *10); + + } + else + { + busy = 0; + } + + if (netbadness) + badness = busy * 100 / netbadness; + else + badness = 0; + + if (highbadness < badness && (supportflags & NETATOP) ) + { + highbadness = badness; + *highorderp = MSORTNET; + } + + if (screen) + move(curline, 0); + + showsysline(netinterfaceline, sstat, &extra, + "NET", badness); + curline++; + lin++; + } + } + + /* + ** application statistics + ** + ** WWW: notice that we cause one access ourselves by fetching + ** the statistical counters + */ +#if HTTPSTATS + if (sstat->www.accesses > 1 || fixedhead ) + { + char format1[8], format2[8], format3[8], format4[8], format5[8]; + + if (screen) + move(curline, 0); + + printg("WWW | reqs %s | totKB %s | byt/rq %s | iwork %s |" + " bwork %s |", + val2valstr(sstat->www.accesses, format1, 6, + avgval, nsecs), + val2valstr(sstat->www.totkbytes, format2, 6, + avgval, nsecs), + val2valstr(sstat->www.accesses ? + sstat->www.totkbytes*1024/sstat->www.accesses : 0, + format3, 5, 0, 0), + val2valstr(sstat->www.iworkers, format4, 6, 0, 0), + val2valstr(sstat->www.bworkers, format5, 6, 0, 0) ); + if (!screen) + { + printg("\n"); + } + curline++; + } +#endif + + /* + ** if the system is hardly loaded, still CPU-ordering of + ** processes is most interesting (instead of memory) + */ + if (highbadness < 70 && *highorderp == MSORTMEM) + *highorderp = MSORTCPU; + + return curline; +} + +/* +** handle all instances of a specific disk-like device +*/ +static void +pridisklike(extraparam *ep, struct perdsk *dp, char *lp, char *highorderp, + int maxlines, unsigned int *highbadp, int *curlinp, int fixedhead, + regex_t *rep) +{ + int lin; + count_t busy; + unsigned int badness; + + for (ep->perdsk = dp, ep->index=0, lin=0; + ep->perdsk[ep->index].name[0] && lin < maxlines; ep->index++) + { + if (rep && regexec(rep, ep->perdsk[ep->index].name, 0, NULL, 0)) + continue; // suppress (not selected) + + ep->iotot = ep->perdsk[ep->index].nread + + ep->perdsk[ep->index].nwrite; + + busy = (double)(ep->perdsk[ep->index].io_ms * + 100.0 / ep->mstot); + + if (dskbadness) + badness = busy * 100 / dskbadness; + else + badness = 0; + + if (*highbadp < badness && (supportflags & IOSTAT) ) + { + *highbadp = badness; + *highorderp = MSORTDSK; + } + + if (ep->iotot || fixedhead) + { + move(*curlinp, 0); + showsysline(dskline, 0, ep, lp, badness); + (*curlinp)++; + lin++; + } + } +} + + +/* +** sort-functions +*/ +int +compcpu(const void *a, const void *b) +{ + register count_t acpu = (*(struct tstat **)a)->cpu.stime + + (*(struct tstat **)a)->cpu.utime; + register count_t bcpu = (*(struct tstat **)b)->cpu.stime + + (*(struct tstat **)b)->cpu.utime; + + if (acpu < bcpu) return 1; + if (acpu > bcpu) return -1; + return compmem(a, b); +} + +int +compdsk(const void *a, const void *b) +{ + struct tstat *ta = *(struct tstat **)a; + struct tstat *tb = *(struct tstat **)b; + + count_t adsk; + count_t bdsk; + + if (ta->dsk.wsz > ta->dsk.cwsz) + adsk = ta->dsk.rio + ta->dsk.wsz - ta->dsk.cwsz; + else + adsk = ta->dsk.rio; + + if (tb->dsk.wsz > tb->dsk.cwsz) + bdsk = tb->dsk.rio + tb->dsk.wsz - tb->dsk.cwsz; + else + bdsk = tb->dsk.rio; + + if (adsk < bdsk) return 1; + if (adsk > bdsk) return -1; + return compcpu(a, b); +} + +int +compmem(const void *a, const void *b) +{ + register count_t amem = (*(struct tstat **)a)->mem.rmem; + register count_t bmem = (*(struct tstat **)b)->mem.rmem; + + if (amem < bmem) return 1; + if (amem > bmem) return -1; + return 0; +} + +int +compnet(const void *a, const void *b) +{ + register count_t anet = (*(struct tstat **)a)->net.tcpssz + + (*(struct tstat **)a)->net.tcprsz + + (*(struct tstat **)a)->net.udpssz + + (*(struct tstat **)a)->net.udprsz ; + register count_t bnet = (*(struct tstat **)b)->net.tcpssz + + (*(struct tstat **)b)->net.tcprsz + + (*(struct tstat **)b)->net.udpssz + + (*(struct tstat **)b)->net.udprsz ; + + if (anet < bnet) return 1; + if (anet > bnet) return -1; + return compcpu(a, b); +} + +int +compusr(const void *a, const void *b) +{ + register int uida = (*(struct tstat **)a)->gen.ruid; + register int uidb = (*(struct tstat **)b)->gen.ruid; + + if (uida > uidb) return 1; + if (uida < uidb) return -1; + return 0; +} + +int +compnam(const void *a, const void *b) +{ + register char *nama = (*(struct tstat **)a)->gen.name; + register char *namb = (*(struct tstat **)b)->gen.name; + + return strcmp(nama, namb); +} + +int +compcon(const void *a, const void *b) +{ + register char *containera = (*(struct tstat **)a)->gen.container; + register char *containerb = (*(struct tstat **)b)->gen.container; + + return strcmp(containera, containerb); +} + +int +cpucompar(const void *a, const void *b) +{ + register count_t aidle = ((struct percpu *)a)->itime + + ((struct percpu *)a)->wtime; + register count_t bidle = ((struct percpu *)b)->itime + + ((struct percpu *)b)->wtime; + + if (aidle < bidle) return -1; + if (aidle > bidle) return 1; + return 0; +} + +int +diskcompar(const void *a, const void *b) +{ + register count_t amsio = ((struct perdsk *)a)->io_ms; + register count_t bmsio = ((struct perdsk *)b)->io_ms; + + if (amsio < bmsio) return 1; + if (amsio > bmsio) return -1; + return 0; +} + +int +intfcompar(const void *a, const void *b) +{ + register count_t afactor=0, bfactor=0; + + count_t aspeed = ((struct perintf *)a)->speed; + count_t bspeed = ((struct perintf *)b)->speed; + char aduplex = ((struct perintf *)a)->duplex; + char bduplex = ((struct perintf *)b)->duplex; + count_t arbyte = ((struct perintf *)a)->rbyte; + count_t brbyte = ((struct perintf *)b)->rbyte; + count_t asbyte = ((struct perintf *)a)->sbyte; + count_t bsbyte = ((struct perintf *)b)->sbyte; + + + /* + ** if speed of first interface known, calculate busy factor + */ + if (aspeed) + { + if (aduplex) + afactor = (arbyte > asbyte ? arbyte : asbyte) + * 10 / aspeed; + else + afactor = (arbyte + asbyte) * 10 / aspeed; + } + + /* + ** if speed of second interface known, calculate busy factor + */ + if (bspeed) + { + if (bduplex) + bfactor = (brbyte > bsbyte ? brbyte : bsbyte) + * 10 / bspeed; + else + bfactor = (brbyte + bsbyte) * 10 / bspeed; + } + + /* + ** compare interfaces + */ + if (aspeed && bspeed) + { + if (afactor < bfactor) return 1; + if (afactor > bfactor) return -1; + return 0; + } + + if (!aspeed && !bspeed) + { + if ((arbyte + asbyte) < (brbyte + bsbyte)) return 1; + if ((arbyte + asbyte) > (brbyte + bsbyte)) return -1; + return 0; + } + + if (aspeed) + return -1; + else + return 1; +} + +int +nfsmcompar(const void *a, const void *b) +{ + const struct pernfsmount *na = a; + const struct pernfsmount *nb = b; + + register count_t aused = na->bytesread + na->byteswrite + + na->bytesdread + na->bytesdwrite + + na->bytestotread + na->bytestotwrite + + na->pagesmread + na->pagesmwrite; + register count_t bused = nb->bytesread + nb->byteswrite + + nb->bytesdread + nb->bytesdwrite + + nb->bytestotread + nb->bytestotwrite + + nb->pagesmread + nb->pagesmwrite; + + if (aused < bused) return 1; + if (aused > bused) return -1; + return 0; +} + +int +contcompar(const void *a, const void *b) +{ + const struct percontainer *ca = a; + const struct percontainer *cb = b; + + register count_t aused = ca->system + ca->user + ca->nice; + register count_t bused = cb->system + cb->user + cb->nice; + + if (aused < bused) return 1; + if (aused > bused) return -1; + return 0; +} + +/* +** handle modifications from the /etc/atoprc and ~/.atoprc file +*/ +int +get_posval(char *name, char *val) +{ + int value = atoi(val); + + if ( !numeric(val)) + { + fprintf(stderr, "atoprc: %s value %s not a (positive) numeric\n", + name, val); + exit(1); + } + + if (value < 0) + { + fprintf(stderr, + "atoprc: %s value %d not positive\n", + name, value); + exit(1); + } + + return value; +} + +int +get_perc(char *name, char *val) +{ + int value = get_posval(name, val); + + if (value < 0 || value > 100) + { + fprintf(stderr, "atoprc: %s value %d not in range 0-100\n", + name, value); + exit(1); + } + + return value; +} + +void +do_cpucritperc(char *name, char *val) +{ + cpubadness = get_perc(name, val); +} + +void +do_memcritperc(char *name, char *val) +{ + membadness = get_perc(name, val); +} + +void +do_swpcritperc(char *name, char *val) +{ + swpbadness = get_perc(name, val); +} + +void +do_dskcritperc(char *name, char *val) +{ + dskbadness = get_perc(name, val); +} + +void +do_netcritperc(char *name, char *val) +{ + netbadness = get_perc(name, val); +} + +void +do_swoutcritsec(char *name, char *val) +{ + pagbadness = get_posval(name, val); +} + +void +do_almostcrit(char *name, char *val) +{ + almostcrit = get_perc(name, val); +} + +void +do_ownsysprcline(char *name, char *val) +{ + make_sys_prints(sysprcline, MAXITEMS, val, prcsyspdefs, name); +} + +void +do_ownallcpuline(char *name, char *val) +{ + make_sys_prints(allcpuline, MAXITEMS, val, cpusyspdefs, name); +} + +void +do_ownindivcpuline(char *name, char *val) +{ + make_sys_prints(indivcpuline, MAXITEMS, val, cpisyspdefs, name); +} + +void +do_owncplline(char *name, char *val) +{ + make_sys_prints(cplline, MAXITEMS, val, cplsyspdefs, name); +} + +void +do_ownmemline(char *name, char *val) +{ + make_sys_prints(memline, MAXITEMS, val, memsyspdefs, name); +} + +void +do_ownswpline(char *name, char *val) +{ + make_sys_prints(swpline, MAXITEMS, val, swpsyspdefs, name); +} + +void +do_ownpagline(char *name, char *val) +{ + make_sys_prints(pagline, MAXITEMS, val, pagsyspdefs, name); +} + +void +do_owndskline(char *name, char *val) +{ + make_sys_prints(dskline, MAXITEMS, val, dsksyspdefs, name); +} + +void +do_ownnettransportline(char *name, char *val) +{ + make_sys_prints(nettransportline, MAXITEMS, val, nettranssyspdefs, name); +} + +void +do_ownnetnetline(char *name, char *val) +{ + make_sys_prints(netnetline, MAXITEMS, val, netnetsyspdefs, name); +} + +void +do_ownnetinterfaceline(char *name, char *val) +{ + make_sys_prints(netinterfaceline, MAXITEMS, val, netintfsyspdefs, name); +} + +void +do_ownprocline(char *name, char *val) +{ + make_proc_prints(ownprocs, MAXITEMS, val, name); +} diff --git a/sources/showlinux.h b/sources/showlinux.h new file mode 100644 index 0000000..dae7e37 --- /dev/null +++ b/sources/showlinux.h @@ -0,0 +1,365 @@ +/* +** ATOP - System & Process Monitor +** +** The program 'atop' offers the possibility to view the activity of +** the system on system-level as well as process-level. +** +** This source-file contains the Linux-specific functions to calculate +** figures to be visualized. +** ========================================================================== +** Author: JC van Winkel - AT Computing, Nijmegen, Holland +** E-mail: jc@ATComputing.nl +** Date: November 2009 +** -------------------------------------------------------------------------- +** Copyright (C) 2009 JC van Winkel +** +** This program is free software; you can redistribute it and/or modify it +** under the terms of the GNU General Public License as published by the +** Free Software Foundation; either version 2, or (at your option) any +** later version. +** +** This program is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +** See the GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +** -------------------------------------------------------------------------- +** +** $Log: showlinux.h,v $ +** Initial revision +** +** Initial +** +*/ +#define MAXITEMS 80 /* The maximum number of items per line */ + +/* + * structure for extra parameters for system related data +*/ +typedef struct { + count_t totut; + count_t totst; + int nact; + int nproc; + int ntrun; + int ntslpi; + int ntslpu; + int nzomb; + int nexit; + int noverflow; + int avgval; + int nsecs; + count_t mstot; + count_t iotot; + struct perdsk *perdsk; + int index; + count_t cputot; + count_t percputot; +} extraparam; + +/***************************************************************/ +/* + * structure for system print-list +*/ +typedef struct { + char *configname; // name as used to + // config print line + char* (*doconvert)(void *, void *, int, int *); // ptr to convert func +} sys_printdef; + + +/* + * structure for system print-list with priority + * in case of leck of screen space, lowest priority items will be + * removed first +*/ +typedef struct +{ + sys_printdef *f; + int prio; +} sys_printpair; + + + +/* +** structure for process print-list +*/ +typedef struct +{ + char *head; // column header + char *configname; // name as used to config print line + char *(*doactiveconvert)(struct tstat *,int,int); + // pointer to conv function + // for active process + char *(*doexitconvert) (struct tstat *,int,int); + // pointer to conv function + // for exited process + int width; // required width + int varwidth; // width may grow (eg cmd params) +} proc_printdef; + + +typedef struct +{ + proc_printdef *f; + int prio; +} proc_printpair; + +void showsysline(sys_printpair* elemptr, + struct sstat* sstat, extraparam *extra, + char *labeltext, unsigned int badness); + + +void showhdrline(proc_printpair* elemptr, int curlist, int totlist, + char showorder, char autosort); +void showprocline(proc_printpair* elemptr, struct tstat *curstat, + double perc, int nsecs, int avgval); + +extern sys_printdef *prcsyspdefs[]; +extern sys_printdef *cpusyspdefs[]; +extern sys_printdef *cpisyspdefs[]; +extern sys_printdef *cplsyspdefs[]; +extern sys_printdef *memsyspdefs[]; +extern sys_printdef *swpsyspdefs[]; +extern sys_printdef *pagsyspdefs[]; +extern sys_printdef *dsksyspdefs[]; +extern sys_printdef *nettranssyspdefs[]; +extern sys_printdef *netnetsyspdefs[]; +extern sys_printdef *netintfsyspdefs[]; + +extern sys_printdef syspdef_PRCSYS; +extern sys_printdef syspdef_PRCUSER; +extern sys_printdef syspdef_PRCNPROC; +extern sys_printdef syspdef_PRCNRUNNING; +extern sys_printdef syspdef_PRCNSLEEPING; +extern sys_printdef syspdef_PRCNDSLEEPING; +extern sys_printdef syspdef_PRCNZOMBIE; +extern sys_printdef syspdef_PRCCLONES; +extern sys_printdef syspdef_PRCNNEXIT; +extern sys_printdef syspdef_CPUSYS; +extern sys_printdef syspdef_CPUUSER; +extern sys_printdef syspdef_CPUIRQ; +extern sys_printdef syspdef_CPUIDLE; +extern sys_printdef syspdef_CPUWAIT; +extern sys_printdef syspdef_CPUISYS; +extern sys_printdef syspdef_CPUIUSER; +extern sys_printdef syspdef_CPUIIRQ; +extern sys_printdef syspdef_CPUIIDLE; +extern sys_printdef syspdef_CPUIWAIT; +extern sys_printdef syspdef_CPUISTEAL; +extern sys_printdef syspdef_CPUIFREQ; +extern sys_printdef syspdef_CPUFREQ; +extern sys_printdef syspdef_CPUSCALE; +extern sys_printdef syspdef_CPUISCALE; +extern sys_printdef syspdef_CPUSTEAL; +extern sys_printdef syspdef_CPUISTEAL; +extern sys_printdef syspdef_CPUGUEST; +extern sys_printdef syspdef_CPUIGUEST; +extern sys_printdef syspdef_CPLAVG1; +extern sys_printdef syspdef_CPLAVG5; +extern sys_printdef syspdef_CPLAVG15; +extern sys_printdef syspdef_CPLCSW; +extern sys_printdef syspdef_CPLNUMCPU; +extern sys_printdef syspdef_CPLINTR; +extern sys_printdef syspdef_MEMTOT; +extern sys_printdef syspdef_MEMFREE; +extern sys_printdef syspdef_MEMCACHE; +extern sys_printdef syspdef_MEMDIRTY; +extern sys_printdef syspdef_MEMBUFFER; +extern sys_printdef syspdef_MEMSLAB; +extern sys_printdef syspdef_RECSLAB; +extern sys_printdef syspdef_SHMEM; +extern sys_printdef syspdef_SHMRSS; +extern sys_printdef syspdef_SHMSWP; +extern sys_printdef syspdef_VMWBAL; +extern sys_printdef syspdef_HUPTOT; +extern sys_printdef syspdef_HUPUSE; +extern sys_printdef syspdef_SWPTOT; +extern sys_printdef syspdef_SWPFREE; +extern sys_printdef syspdef_SWPCOMMITTED; +extern sys_printdef syspdef_SWPCOMMITLIM; +extern sys_printdef syspdef_PAGSCAN; +extern sys_printdef syspdef_PAGSTEAL; +extern sys_printdef syspdef_PAGSTALL; +extern sys_printdef syspdef_PAGSWIN; +extern sys_printdef syspdef_PAGSWOUT; +extern sys_printdef syspdef_CONTNAME; +extern sys_printdef syspdef_CONTNPROC; +extern sys_printdef syspdef_CONTCPU; +extern sys_printdef syspdef_CONTMEM; +extern sys_printdef syspdef_DSKNAME; +extern sys_printdef syspdef_DSKBUSY; +extern sys_printdef syspdef_DSKNREAD; +extern sys_printdef syspdef_DSKNWRITE; +extern sys_printdef syspdef_DSKMBPERSECWR; +extern sys_printdef syspdef_DSKMBPERSECRD; +extern sys_printdef syspdef_DSKKBPERWR; +extern sys_printdef syspdef_DSKKBPERRD; +extern sys_printdef syspdef_DSKAVQUEUE; +extern sys_printdef syspdef_DSKAVIO; +extern sys_printdef syspdef_NETTRANSPORT; +extern sys_printdef syspdef_NETTCPI; +extern sys_printdef syspdef_NETTCPO; +extern sys_printdef syspdef_NETTCPACTOPEN; +extern sys_printdef syspdef_NETTCPPASVOPEN; +extern sys_printdef syspdef_NETTCPRETRANS; +extern sys_printdef syspdef_NETTCPINERR; +extern sys_printdef syspdef_NETTCPORESET; +extern sys_printdef syspdef_NETUDPNOPORT; +extern sys_printdef syspdef_NETUDPINERR; +extern sys_printdef syspdef_NETUDPI; +extern sys_printdef syspdef_NETUDPO; +extern sys_printdef syspdef_NETNETWORK; +extern sys_printdef syspdef_NETIPI; +extern sys_printdef syspdef_NETIPO; +extern sys_printdef syspdef_NETIPFRW; +extern sys_printdef syspdef_NETIPDELIV; +extern sys_printdef syspdef_NETICMPIN; +extern sys_printdef syspdef_NETICMPOUT; +extern sys_printdef syspdef_NETNAME; +extern sys_printdef syspdef_NETPCKI; +extern sys_printdef syspdef_NETPCKO; +extern sys_printdef syspdef_NETSPEEDMAX; +extern sys_printdef syspdef_NETSPEEDIN; +extern sys_printdef syspdef_NETSPEEDOUT; +extern sys_printdef syspdef_NETCOLLIS; +extern sys_printdef syspdef_NETMULTICASTIN; +extern sys_printdef syspdef_NETRCVERR; +extern sys_printdef syspdef_NETSNDERR; +extern sys_printdef syspdef_NETRCVDROP; +extern sys_printdef syspdef_NETSNDDROP; +extern sys_printdef syspdef_NFMSERVER; +extern sys_printdef syspdef_NFMPATH; +extern sys_printdef syspdef_NFMTOTREAD; +extern sys_printdef syspdef_NFMTOTWRITE; +extern sys_printdef syspdef_NFMNREAD; +extern sys_printdef syspdef_NFMNWRITE; +extern sys_printdef syspdef_NFMDREAD; +extern sys_printdef syspdef_NFMDWRITE; +extern sys_printdef syspdef_NFMMREAD; +extern sys_printdef syspdef_NFMMWRITE; +extern sys_printdef syspdef_NFCRPCCNT; +extern sys_printdef syspdef_NFCRPCREAD; +extern sys_printdef syspdef_NFCRPCWRITE; +extern sys_printdef syspdef_NFCRPCRET; +extern sys_printdef syspdef_NFCRPCARF; +extern sys_printdef syspdef_NFSRPCCNT; +extern sys_printdef syspdef_NFSRPCREAD; +extern sys_printdef syspdef_NFSRPCWRITE; +extern sys_printdef syspdef_NFSBADFMT; +extern sys_printdef syspdef_NFSBADAUT; +extern sys_printdef syspdef_NFSBADCLN; +extern sys_printdef syspdef_NFSNETTCP; +extern sys_printdef syspdef_NFSNETUDP; +extern sys_printdef syspdef_NFSNRBYTES; +extern sys_printdef syspdef_NFSNWBYTES; +extern sys_printdef syspdef_NFSRCHITS; +extern sys_printdef syspdef_NFSRCMISS; +extern sys_printdef syspdef_NFSRCNOCA; +extern sys_printdef syspdef_BLANKBOX; + + +/* +** functions that print ???? for unavailable data +*/ +char *procprt_NOTAVAIL_4(struct tstat *curstat, int avgval, int nsecs); +char *procprt_NOTAVAIL_5(struct tstat *curstat, int avgval, int nsecs); +char *procprt_NOTAVAIL_6(struct tstat *curstat, int avgval, int nsecs); +char *procprt_NOTAVAIL_7(struct tstat *curstat, int avgval, int nsecs); + +extern proc_printdef *allprocpdefs[]; +extern proc_printdef procprt_PID; +extern proc_printdef procprt_TID; +extern proc_printdef procprt_PPID; +extern proc_printdef procprt_SYSCPU; +extern proc_printdef procprt_USRCPU; +extern proc_printdef procprt_VGROW; +extern proc_printdef procprt_RGROW; +extern proc_printdef procprt_MINFLT; +extern proc_printdef procprt_MAJFLT; +extern proc_printdef procprt_VSTEXT; +extern proc_printdef procprt_VSIZE; +extern proc_printdef procprt_RSIZE; +extern proc_printdef procprt_PSIZE; +extern proc_printdef procprt_VSLIBS; +extern proc_printdef procprt_VDATA; +extern proc_printdef procprt_VSTACK; +extern proc_printdef procprt_SWAPSZ; +extern proc_printdef procprt_CMD; +extern proc_printdef procprt_RUID; +extern proc_printdef procprt_EUID; +extern proc_printdef procprt_SUID; +extern proc_printdef procprt_FSUID; +extern proc_printdef procprt_RGID; +extern proc_printdef procprt_EGID; +extern proc_printdef procprt_SGID; +extern proc_printdef procprt_FSGID; +extern proc_printdef procprt_CTID; +extern proc_printdef procprt_VPID; +extern proc_printdef procprt_CID; +extern proc_printdef procprt_STDATE; +extern proc_printdef procprt_STTIME; +extern proc_printdef procprt_ENDATE; +extern proc_printdef procprt_ENTIME; +extern proc_printdef procprt_THR; +extern proc_printdef procprt_TRUN; +extern proc_printdef procprt_TSLPI; +extern proc_printdef procprt_TSLPU; +extern proc_printdef procprt_POLI; +extern proc_printdef procprt_NICE; +extern proc_printdef procprt_PRI; +extern proc_printdef procprt_RTPR; +extern proc_printdef procprt_CURCPU; +extern proc_printdef procprt_ST; +extern proc_printdef procprt_EXC; +extern proc_printdef procprt_S; +extern proc_printdef procprt_COMMAND_LINE; +extern proc_printdef procprt_NPROCS; +extern proc_printdef procprt_RDDSK; +extern proc_printdef procprt_WRDSK; +extern proc_printdef procprt_CWRDSK; +extern proc_printdef procprt_WCANCEL; +extern proc_printdef procprt_TCPRCV; +extern proc_printdef procprt_TCPRASZ; +extern proc_printdef procprt_TCPSND; +extern proc_printdef procprt_TCPSASZ; +extern proc_printdef procprt_UDPRCV; +extern proc_printdef procprt_UDPRASZ; +extern proc_printdef procprt_UDPSND; +extern proc_printdef procprt_UDPSASZ; +extern proc_printdef procprt_RNET; +extern proc_printdef procprt_SNET; +extern proc_printdef procprt_BANDWI; +extern proc_printdef procprt_BANDWO; +extern proc_printdef procprt_SORTITEM; + + + +//extern char *procprt_NRDDSK_ae(struct tstat *, int, int); +//extern char *procprt_NWRDSK_a(struct tstat *, int, int); +//extern char *procprt_NRDDSK_e(struct tstat *, int, int); +//extern char *procprt_NWRDSK_e(struct tstat *, int, int); + +extern char *procprt_SNET_a(struct tstat *, int, int); +extern char *procprt_SNET_e(struct tstat *, int, int); +extern char *procprt_RNET_a(struct tstat *, int, int); +extern char *procprt_RNET_e(struct tstat *, int, int); +extern char *procprt_TCPSND_a(struct tstat *, int, int); +extern char *procprt_TCPRCV_a(struct tstat *, int, int); +extern char *procprt_UDPSND_a(struct tstat *, int, int); +extern char *procprt_UDPRCV_a(struct tstat *, int, int); +extern char *procprt_TCPSASZ_a(struct tstat *, int, int); +extern char *procprt_TCPRASZ_a(struct tstat *, int, int); +extern char *procprt_UDPSASZ_a(struct tstat *, int, int); +extern char *procprt_UDPRASZ_a(struct tstat *, int, int); +extern char *procprt_TCPSND_e(struct tstat *, int, int); +extern char *procprt_TCPRCV_e(struct tstat *, int, int); +extern char *procprt_UDPSND_e(struct tstat *, int, int); +extern char *procprt_UDPRCV_e(struct tstat *, int, int); +extern char *procprt_TCPSASZ_e(struct tstat *, int, int); +extern char *procprt_TCPRASZ_e(struct tstat *, int, int); +extern char *procprt_UDPSASZ_e(struct tstat *, int, int); +extern char *procprt_UDPRASZ_e(struct tstat *, int, int); diff --git a/sources/showprocs.c b/sources/showprocs.c new file mode 100644 index 0000000..79841de --- /dev/null +++ b/sources/showprocs.c @@ -0,0 +1,1915 @@ +/* +** ATOP - System & Process Monitor +** +** The program 'atop' offers the possibility to view the activity of +** the system on system-level as well as process-level. +** +** This source-file contains the Linux-specific functions to calculate +** figures to be visualized. +** ========================================================================== +** Author: JC van Winkel - AT Computing, Nijmegen, Holland +** E-mail: jc@ATComputing.nl +** Date: November 2009 +** -------------------------------------------------------------------------- +** Copyright (C) 2009 JC van Winkel +** +** This program is free software; you can redistribute it and/or modify it +** under the terms of the GNU General Public License as published by the +** Free Software Foundation; either version 2, or (at your option) any +** later version. +** +** This program is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +** See the GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +** -------------------------------------------------------------------------- +** +** $Log: showprocs.c,v $ +** Revision 1.15 2011/09/05 11:44:16 gerlof +** *** empty log message *** +** +** Revision 1.14 2010/12/01 09:05:38 gerlof +** Added a dash in the column PPID for exited processes. +** +** Revision 1.13 2010/11/12 06:11:58 gerlof +** Sometimes segmentation-fault on particular CPU-types +** due to memcpy i.s.o. memmove when moving memory in overlap. +** +** Revision 1.12 2010/04/23 14:06:42 gerlof +** Added special routined for uid/gid not available for exited processes. +** +** Revision 1.11 2010/01/16 12:54:08 gerlof +** Minor change for CPUNR. +** +** Revision 1.10 2010/01/16 11:37:25 gerlof +** Corrected counters for patched kernels (JC van Winkel). +** +** Revision 1.9 2010/01/08 13:44:47 gerlof +** Added policies batch, iso and idle for scheduling class. +** +** Revision 1.8 2010/01/08 11:25:13 gerlof +** Corrected column-width and priorities of network-stats. +** +** Revision 1.7 2010/01/03 18:26:53 gerlof +** Consistent naming of columns for process-related info. +** +** Revision 1.6 2009/12/19 21:03:21 gerlof +** Alignment of CMD column (JC van Winkel). +** +** Revision 1.5 2009/12/12 10:11:49 gerlof +** Register and display end date and end time for process. +** +** Revision 1.4 2009/12/12 09:05:56 gerlof +** Corrected cumulated disk I/O per user/program (JC van Winkel). +** +** Revision 1.3 2009/12/10 14:01:52 gerlof +** Add EUID, SUID and FSUID (and similar for GID's). +** +** Revision 1.2 2009/12/10 11:56:22 gerlof +** Various bug-solutions. +** +** Revision 1.1 2009/12/10 09:31:23 gerlof +** Initial revision +** +** Initial revision +** +** +** Initial +** +*/ + +static const char rcsid[] = "$Id: showprocs.c,v 1.15 2011/09/05 11:44:16 gerlof Exp $"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; iwidth; + 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; jvarwidth) + { + 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; jhead==0) // empty header==special: SORTITEM + { + chead=columnhead[order]; + autoindic= autosort ? "A" : " "; + } + else + { + chead=curelem.f->head; + autoindic=""; + } + + if (screen) + { + col += sprintf(buf+col, "%s%s%*s", autoindic, chead, + colspacings[n], ""); + } + else + { + col += sprintf(buf+col, "%s%s ", autoindic, chead); + } + + elemptr++; + n++; + } + + if (screen) // add page number, eat from last header if needed... + { + pagindiclen=sprintf(pagindic,"%d/%d", curlist, totlist); + allign=COLS-col-pagindiclen; // extra spaces needed + + if (allign >= 0) // allign by adding spaces + { + sprintf(buf+col, "%*s", allign+pagindiclen, pagindic); + } + else + { // allign by removing from the right + sprintf(buf+col+allign, "%s", pagindic); + } + } + + printg("%s", buf); + + if (!screen) + { + printg("\n"); + } +} + + + +/***************************************************************/ +/* + * showprocline: show line for processes. + * if in interactive mode, columns are aligned to fill out rows + * params: + * elemptr: pointer to array of print definition structs ptrs + * curstat: the process to print + * perc: the sort order used + * nsecs: number of seconds elapsed between previous and this sample + * avgval: is averaging out per second needed? + */ +void +showprocline(proc_printpair* elemptr, struct tstat *curstat, + double perc, int nsecs, int avgval) +{ + proc_printpair curelem; + + elemptr=newelems; // point to static array + int n=0; + + if (screen && threadview) + { + if (usecolors && !curstat->gen.isproc) + { + attron(COLOR_PAIR(COLORTHR)); + } + else + { + if (!usecolors && curstat->gen.isproc) + attron(A_BOLD); + } + } + + while ((curelem=*elemptr).f!=0) + { + // what to print? SORTITEM, or active process or + // exited process? + + if (curelem.f->head==0) // empty string=sortitem + { + printg("%3.0lf%%", perc); // cannot pass perc + } + else if (curstat->gen.state != 'E') // active process + { + printg("%s", curelem.f->doactiveconvert(curstat, + avgval, nsecs)); + } + else // exited process + { + printg("%s", curelem.f->doexitconvert(curstat, + avgval, nsecs)); + } + + if (screen) + { + printg("%*s",colspacings[n], ""); + } + else + { + printg(" "); + } + + elemptr++; + n++; + } + + if (screen && threadview) + { + if (usecolors && !curstat->gen.isproc) + { + attroff(COLOR_PAIR(COLORTHR)); + } + else + { + if (!usecolors && curstat->gen.isproc) + attroff(A_BOLD); + } + } + + if (!screen) + { + printg("\n"); + } +} + + +/*******************************************************************/ +/* PROCESS PRINT FUNCTIONS */ +/***************************************************************/ +char * +procprt_NOTAVAIL_4(struct tstat *curstat, int avgval, int nsecs) +{ + return " ?"; +} + +char * +procprt_NOTAVAIL_5(struct tstat *curstat, int avgval, int nsecs) +{ + return " ?"; +} + +char * +procprt_NOTAVAIL_6(struct tstat *curstat, int avgval, int nsecs) +{ + return " ?"; +} + +char * +procprt_NOTAVAIL_7(struct tstat *curstat, int avgval, int nsecs) +{ + return " ?"; +} +/***************************************************************/ +char * +procprt_TID_ae(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[64]; + + if (curstat->gen.isproc) + sprintf(buf, "%*s", procprt_TID.width, "-"); + else + sprintf(buf, "%*d", procprt_TID.width, curstat->gen.pid); + return buf; +} + +proc_printdef procprt_TID = + { "TID", "TID", procprt_TID_ae, procprt_TID_ae, 5}; //DYNAMIC WIDTH! +/***************************************************************/ +char * +procprt_PID_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[64]; + + sprintf(buf, "%*d", procprt_PID.width, curstat->gen.tgid); + return buf; +} + +char * +procprt_PID_e(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[64]; + + if (curstat->gen.pid == 0) + sprintf(buf, "%*s", procprt_PID.width, "?"); + else + sprintf(buf, "%*d", procprt_PID.width, curstat->gen.tgid); + return buf; +} + +proc_printdef procprt_PID = + { "PID", "PID", procprt_PID_a, procprt_PID_e, 5}; //DYNAMIC WIDTH! +/***************************************************************/ +char * +procprt_PPID_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[64]; + + sprintf(buf, "%*d", procprt_PPID.width, curstat->gen.ppid); + return buf; +} + +char * +procprt_PPID_e(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[64]; + + sprintf(buf, "%*s", procprt_PPID.width, "-"); + return buf; +} + +proc_printdef procprt_PPID = + { "PPID", "PPID", procprt_PPID_a, procprt_PPID_e, 5 }; //DYNAMIC WIDTH! +/***************************************************************/ +char * +procprt_VPID_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[64]; + + sprintf(buf, "%*d", procprt_VPID.width, curstat->gen.vpid); + return buf; +} + +char * +procprt_VPID_e(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[64]; + + sprintf(buf, "%*s", procprt_VPID.width, "-"); + return buf; +} + +proc_printdef procprt_VPID = + { "VPID", "VPID", procprt_VPID_a, procprt_VPID_e, 5 }; //DYNAMIC WIDTH! +/***************************************************************/ +char * +procprt_CTID_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[32]; + + sprintf(buf, "%5d", curstat->gen.ctid); + return buf; +} + +char * +procprt_CTID_e(struct tstat *curstat, int avgval, int nsecs) +{ + return " -"; +} + +proc_printdef procprt_CTID = + { " CTID", "CTID", procprt_CTID_a, procprt_CTID_e, 5 }; +/***************************************************************/ +char * +procprt_CID_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[64]; + + if (curstat->gen.container[0]) + sprintf(buf, "%-12s", curstat->gen.container); + else + sprintf(buf, "%-12s", "host--------"); + + return buf; +} + +char * +procprt_CID_e(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[64]; + + if (curstat->gen.container[0]) + sprintf(buf, "%-12s", curstat->gen.container); + else + sprintf(buf, "%-12s", "?"); + + return buf; +} + +proc_printdef procprt_CID = + { "CID ", "CID", procprt_CID_a, procprt_CID_e, 12}; +/***************************************************************/ +char * +procprt_SYSCPU_ae(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[10]; + + val2cpustr(curstat->cpu.stime*1000/hertz, buf); + return buf; +} + +proc_printdef procprt_SYSCPU = + { "SYSCPU", "SYSCPU", procprt_SYSCPU_ae, procprt_SYSCPU_ae, 6 }; +/***************************************************************/ +char * +procprt_USRCPU_ae(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[10]; + + val2cpustr(curstat->cpu.utime*1000/hertz, buf); + return buf; +} + +proc_printdef procprt_USRCPU = + { "USRCPU", "USRCPU", procprt_USRCPU_ae, procprt_USRCPU_ae, 6 }; +/***************************************************************/ +char * +procprt_VGROW_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[10]; + + val2memstr(curstat->mem.vgrow*1024, buf, KBFORMAT, 0, 0); + return buf; +} + +char * +procprt_VGROW_e(struct tstat *curstat, int avgval, int nsecs) +{ + return " 0K"; +} + +proc_printdef procprt_VGROW = + { " VGROW", "VGROW", procprt_VGROW_a, procprt_VGROW_e, 6 }; +/***************************************************************/ +char * +procprt_RGROW_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[10]; + + val2memstr(curstat->mem.rgrow*1024, buf, KBFORMAT, 0, 0); + return buf; +} + +char * +procprt_RGROW_e(struct tstat *curstat, int avgval, int nsecs) +{ + return " 0K"; +} + +proc_printdef procprt_RGROW = + { " RGROW", "RGROW", procprt_RGROW_a, procprt_RGROW_e, 6 }; +/***************************************************************/ +char * +procprt_MINFLT_ae(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[10]; + + val2valstr(curstat->mem.minflt, buf, 6, avgval, nsecs); + return buf; +} + +proc_printdef procprt_MINFLT = + { "MINFLT", "MINFLT", procprt_MINFLT_ae, procprt_MINFLT_ae, 6 }; +/***************************************************************/ +char * +procprt_MAJFLT_ae(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[10]; + + val2valstr(curstat->mem.majflt, buf, 6, avgval, nsecs); + return buf; +} + +proc_printdef procprt_MAJFLT = + { "MAJFLT", "MAJFLT", procprt_MAJFLT_ae, procprt_MAJFLT_ae, 6 }; +/***************************************************************/ +char * +procprt_VSTEXT_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[10]; + + val2memstr(curstat->mem.vexec*1024, buf, KBFORMAT, 0, 0); + return buf; +} + +char * +procprt_VSTEXT_e(struct tstat *curstat, int avgval, int nsecs) +{ + return " 0K"; +} + +proc_printdef procprt_VSTEXT = + { "VSTEXT", "VSTEXT", procprt_VSTEXT_a, procprt_VSTEXT_e, 6 }; +/***************************************************************/ +char * +procprt_VSIZE_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[10]; + + val2memstr(curstat->mem.vmem*1024, buf, KBFORMAT, 0, 0); + return buf; +} + +char * +procprt_VSIZE_e(struct tstat *curstat, int avgval, int nsecs) +{ + return " 0K"; +} + +proc_printdef procprt_VSIZE = + { " VSIZE", "VSIZE", procprt_VSIZE_a, procprt_VSIZE_e, 6 }; +/***************************************************************/ +char * +procprt_RSIZE_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[10]; + + val2memstr(curstat->mem.rmem*1024, buf, KBFORMAT, 0, 0); + return buf; +} + +char * +procprt_RSIZE_e(struct tstat *curstat, int avgval, int nsecs) +{ + return " 0K"; +} + +proc_printdef procprt_RSIZE = + { " RSIZE", "RSIZE", procprt_RSIZE_a, procprt_RSIZE_e, 6 }; +/***************************************************************/ +char * +procprt_PSIZE_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[10]; + + if (curstat->mem.pmem == (unsigned long long)-1LL) + return " ?K"; + + val2memstr(curstat->mem.pmem*1024, buf, KBFORMAT, 0, 0); + return buf; +} + +char * +procprt_PSIZE_e(struct tstat *curstat, int avgval, int nsecs) +{ + return " 0K"; +} + +proc_printdef procprt_PSIZE = + { " PSIZE", "PSIZE", procprt_PSIZE_a, procprt_PSIZE_e, 6 }; +/***************************************************************/ +char * +procprt_VSLIBS_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[10]; + + val2memstr(curstat->mem.vlibs*1024, buf, KBFORMAT, 0, 0); + return buf; +} + +char * +procprt_VSLIBS_e(struct tstat *curstat, int avgval, int nsecs) +{ + return " 0K"; +} + +proc_printdef procprt_VSLIBS = + { "VSLIBS", "VSLIBS", procprt_VSLIBS_a, procprt_VSLIBS_e, 6 }; +/***************************************************************/ +char * +procprt_VDATA_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[10]; + + val2memstr(curstat->mem.vdata*1024, buf, KBFORMAT, 0, 0); + return buf; +} + +char * +procprt_VDATA_e(struct tstat *curstat, int avgval, int nsecs) +{ + return " 0K"; +} + +proc_printdef procprt_VDATA = + { " VDATA", "VDATA", procprt_VDATA_a, procprt_VDATA_e, 6 }; +/***************************************************************/ +char * +procprt_VSTACK_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[10]; + + val2memstr(curstat->mem.vstack*1024, buf, KBFORMAT, 0, 0); + return buf; +} + +char * +procprt_VSTACK_e(struct tstat *curstat, int avgval, int nsecs) +{ + return " 0K"; +} + +proc_printdef procprt_VSTACK = + { "VSTACK", "VSTACK", procprt_VSTACK_a, procprt_VSTACK_e, 6 }; +/***************************************************************/ +char * +procprt_SWAPSZ_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[10]; + + val2memstr(curstat->mem.vswap*1024, buf, KBFORMAT, 0, 0); + return buf; +} + +char * +procprt_SWAPSZ_e(struct tstat *curstat, int avgval, int nsecs) +{ + return " 0K"; +} + +proc_printdef procprt_SWAPSZ = + { "SWAPSZ", "SWAPSZ", procprt_SWAPSZ_a, procprt_SWAPSZ_e, 6 }; +/***************************************************************/ +char * +procprt_CMD_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[15]; + + sprintf(buf, "%-14.14s", curstat->gen.name); + return buf; +} + +char * +procprt_CMD_e(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[15]="<"; + char helpbuf[15]; + + sprintf(helpbuf, "<%.12s>", curstat->gen.name); + sprintf(buf, "%-14.14s", helpbuf); + return buf; +} + +proc_printdef procprt_CMD = + { "CMD ", "CMD", procprt_CMD_a, procprt_CMD_e, 14 }; +/***************************************************************/ +char * +procprt_RUID_ae(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[9]; + struct passwd *pwd; + + if ( (pwd = getpwuid(curstat->gen.ruid)) ) + { + sprintf(buf, "%-8.8s", pwd->pw_name); + } + else + { + snprintf(buf, sizeof buf, "%-8d", curstat->gen.ruid); + } + return buf; +} + +proc_printdef procprt_RUID = + { "RUID ", "RUID", procprt_RUID_ae, procprt_RUID_ae, 8 }; +/***************************************************************/ +char * +procprt_EUID_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[9]; + struct passwd *pwd; + + if ( (pwd = getpwuid(curstat->gen.euid)) ) + { + sprintf(buf, "%-8.8s", pwd->pw_name); + } + else + { + snprintf(buf, sizeof buf, "%-8d", curstat->gen.euid); + } + return buf; +} + +char * +procprt_EUID_e(struct tstat *curstat, int avgval, int nsecs) +{ + return "- "; +} + +proc_printdef procprt_EUID = + { "EUID ", "EUID", procprt_EUID_a, procprt_EUID_e, 8 }; +/***************************************************************/ +char * +procprt_SUID_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[9]; + struct passwd *pwd; + + if ( (pwd = getpwuid(curstat->gen.suid)) ) + { + sprintf(buf, "%-8.8s", pwd->pw_name); + } + else + { + snprintf(buf, sizeof buf, "%-8d", curstat->gen.suid); + } + return buf; +} + +char * +procprt_SUID_e(struct tstat *curstat, int avgval, int nsecs) +{ + return "- "; +} + +proc_printdef procprt_SUID = + { "SUID ", "SUID", procprt_SUID_a, procprt_SUID_e, 8 }; +/***************************************************************/ +char * +procprt_FSUID_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[9]; + struct passwd *pwd; + + if ( (pwd = getpwuid(curstat->gen.fsuid)) ) + { + sprintf(buf, "%-8.8s", pwd->pw_name); + } + else + { + snprintf(buf, sizeof buf, "%-8d", curstat->gen.fsuid); + } + return buf; +} + +char * +procprt_FSUID_e(struct tstat *curstat, int avgval, int nsecs) +{ + return "- "; +} + +proc_printdef procprt_FSUID = + { "FSUID ", "FSUID", procprt_FSUID_a, procprt_FSUID_e, 8 }; +/***************************************************************/ +char * +procprt_RGID_ae(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[10]; + struct group *grp; + char *groupname; + char grname[16]; + + if ( (grp = getgrgid(curstat->gen.rgid)) ) + { + groupname = grp->gr_name; + } + else + { + snprintf(grname, sizeof grname, "%d",curstat->gen.rgid); + groupname = grname; + } + + sprintf(buf, "%-8.8s", groupname); + return buf; +} + +proc_printdef procprt_RGID = + { "RGID ", "RGID", procprt_RGID_ae, procprt_RGID_ae, 8 }; +/***************************************************************/ +char * +procprt_EGID_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[10]; + struct group *grp; + char *groupname; + char grname[16]; + + if ( (grp = getgrgid(curstat->gen.egid)) ) + { + groupname = grp->gr_name; + } + else + { + snprintf(grname, sizeof grname, "%d",curstat->gen.egid); + groupname = grname; + } + + sprintf(buf, "%-8.8s", groupname); + return buf; +} + +char * +procprt_EGID_e(struct tstat *curstat, int avgval, int nsecs) +{ + return "- "; +} + +proc_printdef procprt_EGID = + { "EGID ", "EGID", procprt_EGID_a, procprt_EGID_e, 8 }; +/***************************************************************/ +char * +procprt_SGID_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[10]; + struct group *grp; + char *groupname; + char grname[16]; + + if ( (grp = getgrgid(curstat->gen.sgid)) ) + { + groupname = grp->gr_name; + } + else + { + snprintf(grname, sizeof grname, "%d",curstat->gen.sgid); + groupname = grname; + } + + sprintf(buf, "%-8.8s", groupname); + return buf; +} + +char * +procprt_SGID_e(struct tstat *curstat, int avgval, int nsecs) +{ + return "- "; +} + +proc_printdef procprt_SGID = + { "SGID ", "SGID", procprt_SGID_a, procprt_SGID_e, 8 }; +/***************************************************************/ +char * +procprt_FSGID_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[10]; + struct group *grp; + char *groupname; + char grname[16]; + + if ( (grp = getgrgid(curstat->gen.fsgid)) ) + { + groupname = grp->gr_name; + } + else + { + snprintf(grname, sizeof grname,"%d",curstat->gen.fsgid); + groupname = grname; + } + + sprintf(buf, "%-8.8s", groupname); + return buf; +} + +char * +procprt_FSGID_e(struct tstat *curstat, int avgval, int nsecs) +{ + return "- "; +} + +proc_printdef procprt_FSGID = + { "FSGID ", "FSGID", procprt_FSGID_a, procprt_FSGID_e, 8 }; +/***************************************************************/ +char * +procprt_STDATE_ae(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[11]; + + convdate(curstat->gen.btime, buf); + return buf; +} + +proc_printdef procprt_STDATE = + { " STDATE ", "STDATE", procprt_STDATE_ae, procprt_STDATE_ae, 10 }; +/***************************************************************/ +char * +procprt_STTIME_ae(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[9]; + + convtime(curstat->gen.btime, buf); + return buf; +} + +proc_printdef procprt_STTIME = + { " STTIME ", "STTIME", procprt_STTIME_ae, procprt_STTIME_ae, 8 }; +/***************************************************************/ +char * +procprt_ENDATE_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[11]; + + strcpy(buf, " active "); + + return buf; +} + +char * +procprt_ENDATE_e(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[11]; + + convdate(curstat->gen.btime + curstat->gen.elaps/hertz, buf); + + return buf; +} + +proc_printdef procprt_ENDATE = + { " ENDATE ", "ENDATE", procprt_ENDATE_a, procprt_ENDATE_e, 10 }; +/***************************************************************/ +char * +procprt_ENTIME_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[9]; + + strcpy(buf, " active "); + + return buf; +} + +char * +procprt_ENTIME_e(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[9]; + + convtime(curstat->gen.btime + curstat->gen.elaps/hertz, buf); + + return buf; +} + +proc_printdef procprt_ENTIME = + { " ENTIME ", "ENTIME", procprt_ENTIME_a, procprt_ENTIME_e, 8 }; +/***************************************************************/ +char * +procprt_THR_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[15]; + + sprintf(buf, "%4d", curstat->gen.nthr); + return buf; +} + +char * +procprt_THR_e(struct tstat *curstat, int avgval, int nsecs) +{ + return " 0"; +} + +proc_printdef procprt_THR = + { " THR", "THR", procprt_THR_a, procprt_THR_e, 4 }; +/***************************************************************/ +char * +procprt_TRUN_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[15]; + + sprintf(buf, "%4d", curstat->gen.nthrrun); + return buf; +} + +char * +procprt_TRUN_e(struct tstat *curstat, int avgval, int nsecs) +{ + return " 0"; +} + +proc_printdef procprt_TRUN = + { "TRUN", "TRUN", procprt_TRUN_a, procprt_TRUN_e, 4 }; +/***************************************************************/ +char * +procprt_TSLPI_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[15]; + + sprintf(buf, "%5d", curstat->gen.nthrslpi); + return buf; +} + +char * +procprt_TSLPI_e(struct tstat *curstat, int avgval, int nsecs) +{ + return " 0"; +} + +proc_printdef procprt_TSLPI = + { "TSLPI", "TSLPI", procprt_TSLPI_a, procprt_TSLPI_e, 5 }; +/***************************************************************/ +char * +procprt_TSLPU_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[15]; + + sprintf(buf, "%5d", curstat->gen.nthrslpu); + return buf; +} + +char * +procprt_TSLPU_e(struct tstat *curstat, int avgval, int nsecs) +{ + return " 0"; +} + +proc_printdef procprt_TSLPU = + { "TSLPU", "TSLPU", procprt_TSLPU_a, procprt_TSLPU_e, 5 }; +/***************************************************************/ +#define SCHED_NORMAL 0 +#define SCHED_FIFO 1 +#define SCHED_RR 2 +#define SCHED_BATCH 3 +#define SCHED_ISO 4 +#define SCHED_IDLE 5 + +char * +procprt_POLI_a(struct tstat *curstat, int avgval, int nsecs) +{ + switch (curstat->cpu.policy) + { + case SCHED_NORMAL: + return "norm"; + break; + case SCHED_FIFO: + return "fifo"; + break; + case SCHED_RR: + return "rr "; + break; + case SCHED_BATCH: + return "btch"; + break; + case SCHED_ISO: + return "iso "; + break; + case SCHED_IDLE: + return "idle"; + break; + } + return "? "; +} + +char * +procprt_POLI_e(struct tstat *curstat, int avgval, int nsecs) +{ + return "- "; +} + +proc_printdef procprt_POLI = + { "POLI", "POLI", procprt_POLI_a, procprt_POLI_e, 4 }; +/***************************************************************/ +char * +procprt_NICE_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[15]; + + sprintf(buf, "%4d", curstat->cpu.nice); + return buf; +} + +char * +procprt_NICE_e(struct tstat *curstat, int avgval, int nsecs) +{ + return " -"; +} + +proc_printdef procprt_NICE = + { "NICE", "NICE", procprt_NICE_a, procprt_NICE_e, 4 }; +/***************************************************************/ +char * +procprt_PRI_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[15]; + + sprintf(buf, "%3d", curstat->cpu.prio); + return buf; +} + +char * +procprt_PRI_e(struct tstat *curstat, int avgval, int nsecs) +{ + return " -"; +} + +proc_printdef procprt_PRI = + { "PRI", "PRI", procprt_PRI_a, procprt_PRI_e, 3 }; +/***************************************************************/ +char * +procprt_RTPR_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[15]; + + sprintf(buf, "%4d", curstat->cpu.rtprio); + return buf; +} + +char * +procprt_RTPR_e(struct tstat *curstat, int avgval, int nsecs) +{ + return " -"; +} + +proc_printdef procprt_RTPR = + { "RTPR", "RTPR", procprt_RTPR_a, procprt_RTPR_e, 4 }; +/***************************************************************/ +char * +procprt_CURCPU_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[15]; + + sprintf(buf, "%5d", curstat->cpu.curcpu); + return buf; +} + +char * +procprt_CURCPU_e(struct tstat *curstat, int avgval, int nsecs) +{ + return " -"; +} + +proc_printdef procprt_CURCPU = + { "CPUNR", "CPUNR", procprt_CURCPU_a, procprt_CURCPU_e, 5 }; +/***************************************************************/ +char * +procprt_ST_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[3]="--"; + if (curstat->gen.excode & ~(INT_MAX)) + { + buf[0]='N'; + } + else + { + buf[0]='-'; + } + return buf; +} + +char * +procprt_ST_e(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[3]; + if (curstat->gen.excode & ~(INT_MAX)) + { + buf[0]='N'; + } + else + { + buf[0]='-'; + } + if (curstat->gen.excode & 0xff) + { + if (curstat->gen.excode & 0x80) + buf[1] = 'C'; + else + buf[1] = 'S'; + } + else + { + buf[1] = 'E'; + } + return buf; +} + +proc_printdef procprt_ST = + { "ST", "ST", procprt_ST_a, procprt_ST_e, 2 }; +/***************************************************************/ +char * +procprt_EXC_a(struct tstat *curstat, int avgval, int nsecs) +{ + return " -"; +} + +char * +procprt_EXC_e(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[4]; + + + sprintf(buf, "%3d", + curstat->gen.excode & 0xff ? + curstat->gen.excode & 0x7f : + (curstat->gen.excode>>8) & 0xff); + return buf; +} + + +proc_printdef procprt_EXC = + { "EXC", "EXC", procprt_EXC_a, procprt_EXC_e, 3 }; +/***************************************************************/ +char * +procprt_S_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[2]="E"; + + buf[0]=curstat->gen.state; + return buf; +} + +char * +procprt_S_e(struct tstat *curstat, int avgval, int nsecs) +{ + return "E"; + +} + +proc_printdef procprt_S = + { "S", "S", procprt_S_a, procprt_S_e, 1 }; + +/***************************************************************/ +char * +procprt_COMMAND_LINE_ae(struct tstat *curstat, int avgval, int nsecs) +{ + extern proc_printdef procprt_COMMAND_LINE; + extern int startoffset; // influenced by -> and <- keys + + static char buf[CMDLEN+1]; + + char *pline = curstat->gen.cmdline[0] ? + curstat->gen.cmdline : curstat->gen.name; + + int curwidth = procprt_COMMAND_LINE.width <= CMDLEN ? + procprt_COMMAND_LINE.width : CMDLEN; + + int cmdlen = strlen(pline); + int curoffset = startoffset <= cmdlen ? startoffset : cmdlen; + + if (screen) + sprintf(buf, "%-*.*s", curwidth, curwidth, pline+curoffset); + else + sprintf(buf, "%.*s", CMDLEN, pline+curoffset); + + return buf; +} + +proc_printdef procprt_COMMAND_LINE = + { "COMMAND-LINE (horizontal scroll with <- and -> keys)", + "COMMAND-LINE", + procprt_COMMAND_LINE_ae, procprt_COMMAND_LINE_ae, 0, 1 }; +/***************************************************************/ +char * +procprt_NPROCS_ae(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[10]; + + val2valstr(curstat->gen.pid, buf, 6, 0, 0); // pid abused as proc counter + return buf; +} + +proc_printdef procprt_NPROCS = + { "NPROCS", "NPROCS", procprt_NPROCS_ae, procprt_NPROCS_ae, 6 }; +/***************************************************************/ +char * +procprt_RDDSK_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[10]; + val2memstr(curstat->dsk.rsz*512, buf, KBFORMAT, avgval, nsecs); + + return buf; +} + +char * +procprt_RDDSK_e(struct tstat *curstat, int avgval, int nsecs) +{ + return " -"; +} + +proc_printdef procprt_RDDSK = + { " RDDSK", "RDDSK", procprt_RDDSK_a, procprt_RDDSK_e, 6 }; +/***************************************************************/ +char * +procprt_WRDSK_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[10]; + + val2memstr(curstat->dsk.wsz*512, buf, KBFORMAT, avgval, nsecs); + + return buf; +} + +char * +procprt_WRDSK_e(struct tstat *curstat, int avgval, int nsecs) +{ + return " -"; +} + +proc_printdef procprt_WRDSK = + { " WRDSK", "WRDSK", procprt_WRDSK_a, procprt_WRDSK_e, 6 }; +/***************************************************************/ +char * +procprt_CWRDSK_a(struct tstat *curstat, int avgval, int nsecs) +{ + count_t nett_wsz; + static char buf[10]; + + if (curstat->dsk.wsz > curstat->dsk.cwsz) + nett_wsz = curstat->dsk.wsz - curstat->dsk.cwsz; + else + nett_wsz = 0; + + val2memstr(nett_wsz*512, buf, KBFORMAT, avgval, nsecs); + + return buf; +} + +proc_printdef procprt_CWRDSK = + {" WRDSK", "CWRDSK", procprt_CWRDSK_a, procprt_WRDSK_e, 6 }; +/***************************************************************/ +char * +procprt_WCANCEL_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[10]; + val2memstr(curstat->dsk.cwsz*512, buf, KBFORMAT, avgval, nsecs); + + return buf; +} + +char * +procprt_WCANCEL_e(struct tstat *curstat, int avgval, int nsecs) +{ + return " -"; +} + +proc_printdef procprt_WCANCEL = + {"WCANCL", "WCANCL", procprt_WCANCEL_a, procprt_WCANCEL_e, 6}; +/***************************************************************/ +char * +procprt_TCPRCV_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[10]; + + val2valstr(curstat->net.tcprcv, buf, 6, avgval, nsecs); + + return buf; +} + +char * +procprt_TCPRCV_e(struct tstat *curstat, int avgval, int nsecs) +{ + if (supportflags & NETATOPD) + { + static char buf[10]; + + val2valstr(curstat->net.tcprcv, buf, 6, avgval, nsecs); + + return buf; + } + else + return " -"; +} + + +proc_printdef procprt_TCPRCV = + { "TCPRCV", "TCPRCV", procprt_TCPRCV_a, procprt_TCPRCV_e, 6 }; +/***************************************************************/ +char * +procprt_TCPRASZ_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[10]; + + int avgtcpr = curstat->net.tcprcv ? + curstat->net.tcprsz / curstat->net.tcprcv : 0; + + val2valstr(avgtcpr, buf, 7, 0, 0); + return buf; +} + +char * +procprt_TCPRASZ_e(struct tstat *curstat, int avgval, int nsecs) +{ + if (supportflags & NETATOPD) + { + static char buf[10]; + + int avgtcpr = curstat->net.tcprcv ? + curstat->net.tcprsz / curstat->net.tcprcv : 0; + + val2valstr(avgtcpr, buf, 7, 0, 0); + return buf; + } + else + return " -"; +} + +proc_printdef procprt_TCPRASZ = + { "TCPRASZ", "TCPRASZ", procprt_TCPRASZ_a, procprt_TCPRASZ_e, 7 }; +/***************************************************************/ +char * +procprt_TCPSND_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[10]; + + val2valstr(curstat->net.tcpsnd, buf, 6, avgval, nsecs); + + return buf; +} + +char * +procprt_TCPSND_e(struct tstat *curstat, int avgval, int nsecs) +{ + if (supportflags & NETATOPD) + { + static char buf[10]; + + val2valstr(curstat->net.tcpsnd, buf, 6, avgval, nsecs); + + return buf; + } + else + return " -"; +} + +proc_printdef procprt_TCPSND = + { "TCPSND", "TCPSND", procprt_TCPSND_a, procprt_TCPSND_e, 6 }; +/***************************************************************/ +char * +procprt_TCPSASZ_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[10]; + + int avgtcps = curstat->net.tcpsnd ? + curstat->net.tcpssz / curstat->net.tcpsnd : 0; + + val2valstr(avgtcps, buf, 7, 0, 0); + return buf; +} + +char * +procprt_TCPSASZ_e(struct tstat *curstat, int avgval, int nsecs) +{ + if (supportflags & NETATOPD) + { + static char buf[10]; + + int avgtcps = curstat->net.tcpsnd ? + curstat->net.tcpssz / curstat->net.tcpsnd : 0; + + val2valstr(avgtcps, buf, 7, 0, 0); + return buf; + } + else + return " -"; +} + + +proc_printdef procprt_TCPSASZ = + { "TCPSASZ", "TCPSASZ", procprt_TCPSASZ_a, procprt_TCPSASZ_e, 7 }; +/***************************************************************/ +char * +procprt_UDPRCV_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[10]; + + val2valstr(curstat->net.udprcv, buf, 6, avgval, nsecs); + + return buf; +} + +char * +procprt_UDPRCV_e(struct tstat *curstat, int avgval, int nsecs) +{ + if (supportflags & NETATOPD) + { + static char buf[10]; + + val2valstr(curstat->net.udprcv, buf, 6, avgval, nsecs); + + return buf; + } + else + return " -"; +} + + +proc_printdef procprt_UDPRCV = + { "UDPRCV", "UDPRCV", procprt_UDPRCV_a, procprt_UDPRCV_e, 6 }; +/***************************************************************/ +char * +procprt_UDPRASZ_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[10]; + + int avgudpr = curstat->net.udprcv ? + curstat->net.udprsz / curstat->net.udprcv : 0; + + val2valstr(avgudpr, buf, 7, 0, 0); + return buf; +} + +char * +procprt_UDPRASZ_e(struct tstat *curstat, int avgval, int nsecs) +{ + if (supportflags & NETATOPD) + { + static char buf[10]; + + int avgudpr = curstat->net.udprcv ? + curstat->net.udprsz / curstat->net.udprcv : 0; + + val2valstr(avgudpr, buf, 7, 0, 0); + return buf; + } + else + return " -"; +} + + +proc_printdef procprt_UDPRASZ = + { "UDPRASZ", "UDPRASZ", procprt_UDPRASZ_a, procprt_UDPRASZ_e, 7 }; +/***************************************************************/ +char * +procprt_UDPSND_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[10]; + + val2valstr(curstat->net.udpsnd, buf, 6, avgval, nsecs); + + return buf; +} + +char * +procprt_UDPSND_e(struct tstat *curstat, int avgval, int nsecs) +{ + if (supportflags & NETATOPD) + { + static char buf[10]; + + val2valstr(curstat->net.udpsnd, buf, 6, avgval, nsecs); + + return buf; + } + else + return " -"; +} + +proc_printdef procprt_UDPSND = + { "UDPSND", "UDPSND", procprt_UDPSND_a, procprt_UDPSND_e, 6 }; +/***************************************************************/ +char * +procprt_UDPSASZ_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[10]; + + int avgudps = curstat->net.udpsnd ? + curstat->net.udpssz / curstat->net.udpsnd : 0; + + val2valstr(avgudps, buf, 7, 0, 0); + return buf; +} + +char * +procprt_UDPSASZ_e(struct tstat *curstat, int avgval, int nsecs) +{ + if (supportflags & NETATOPD) + { + static char buf[10]; + + int avgudps = curstat->net.udpsnd ? + curstat->net.udpssz / curstat->net.udpsnd : 0; + + val2valstr(avgudps, buf, 7, 0, 0); + return buf; + } + else + return " -"; +} + + +proc_printdef procprt_UDPSASZ = + { "UDPSASZ", "UDPSASZ", procprt_UDPSASZ_a, procprt_UDPSASZ_e, 7 }; +/***************************************************************/ +char * +procprt_RNET_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[10]; + + val2valstr(curstat->net.tcprcv + curstat->net.udprcv , + buf, 5, avgval, nsecs); + + return buf; +} + +char * +procprt_RNET_e(struct tstat *curstat, int avgval, int nsecs) +{ + if (supportflags & NETATOPD) + { + static char buf[10]; + + val2valstr(curstat->net.tcprcv + curstat->net.udprcv , + buf, 5, avgval, nsecs); + + return buf; + } + else + return " -"; +} + +proc_printdef procprt_RNET = + { " RNET", "RNET", procprt_RNET_a, procprt_RNET_e, 5 }; +/***************************************************************/ +char * +procprt_SNET_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[10]; + + val2valstr(curstat->net.tcpsnd + curstat->net.udpsnd, + buf, 5, avgval, nsecs); + return buf; +} + +char * +procprt_SNET_e(struct tstat *curstat, int avgval, int nsecs) +{ + if (supportflags & NETATOPD) + { + static char buf[10]; + + val2valstr(curstat->net.tcpsnd + curstat->net.udpsnd, + buf, 5, avgval, nsecs); + return buf; + } + else + return " -"; +} + +proc_printdef procprt_SNET = + { " SNET", "SNET", procprt_SNET_a, procprt_SNET_e, 5 }; +/***************************************************************/ +char * +procprt_BANDWI_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[16]; + count_t rkbps = (curstat->net.tcprsz+curstat->net.udprsz)/125/nsecs; + + format_bandw(buf, rkbps); + return buf; +} + +char * +procprt_BANDWI_e(struct tstat *curstat, int avgval, int nsecs) +{ + if (supportflags & NETATOPD) + { + static char buf[16]; + count_t rkbps = (curstat->net.tcprsz + curstat->net.udprsz) + /125/nsecs; + + format_bandw(buf, rkbps); + return buf; + } + else + return " -"; +} + +proc_printdef procprt_BANDWI = + { " BANDWI", "BANDWI", procprt_BANDWI_a, procprt_BANDWI_e, 9}; +/***************************************************************/ +char * +procprt_BANDWO_a(struct tstat *curstat, int avgval, int nsecs) +{ + static char buf[16]; + count_t skbps = (curstat->net.tcpssz+curstat->net.udpssz)/125/nsecs; + + format_bandw(buf, skbps); + return buf; +} + +char * +procprt_BANDWO_e(struct tstat *curstat, int avgval, int nsecs) +{ + if (supportflags & NETATOPD) + { + static char buf[16]; + count_t skbps = (curstat->net.tcpssz + curstat->net.udpssz) + /125/nsecs; + + format_bandw(buf, skbps); + return buf; + } + else + return " -"; +} + +proc_printdef procprt_BANDWO = + { " BANDWO", "BANDWO", procprt_BANDWO_a, procprt_BANDWO_e, 9}; +/***************************************************************/ +static void +format_bandw(char *buf, count_t kbps) +{ + char c; + + if (kbps < 10000) + { + c='K'; + } + else if (kbps < (count_t)10000 * 1000) + { + kbps/=1000; + c = 'M'; + } + else if (kbps < (count_t)10000 * 1000 * 1000) + { + kbps/=1000 * 1000; + c = 'G'; + } + else + { + kbps = kbps / 1000 / 1000 / 1000; + c = 'T'; + } + + sprintf(buf, "%4lld %cbps", kbps, c); +} +/***************************************************************/ +char * +procprt_SORTITEM_ae(struct tstat *curstat, int avgval, int nsecs) +{ + return ""; // dummy function +} + +proc_printdef procprt_SORTITEM = + { 0, "SORTITEM", procprt_SORTITEM_ae, procprt_SORTITEM_ae, 4 }; diff --git a/sources/showsys.c b/sources/showsys.c new file mode 100644 index 0000000..db51154 --- /dev/null +++ b/sources/showsys.c @@ -0,0 +1,2374 @@ +/* +** ATOP - System & Process Monitor +** +** The program 'atop' offers the possibility to view the activity of +** the system on system-level as well as process-level. +** +** This source-file contains the Linux-specific functions to calculate +** figures to be visualized. +** ========================================================================== +** Author: JC van Winkel - AT Computing, Nijmegen, Holland +** E-mail: jc@ATComputing.nl +** Date: November 2009 +** -------------------------------------------------------------------------- +** Copyright (C) 2009 JC van Winkel +** +** This program is free software; you can redistribute it and/or modify it +** under the terms of the GNU General Public License as published by the +** Free Software Foundation; either version 2, or (at your option) any +** later version. +** +** This program is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +** See the GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +** -------------------------------------------------------------------------- +** +** $Log: showsys.c,v $ +** Revision 1.10 2010/11/12 06:06:43 gerlof +** Sometimes segmentation-fault on particular CPU-types +** due to memcpy i.s.o. memmove when moving memory in overlap. +** +** Revision 1.9 2010/10/23 14:04:20 gerlof +** Counters for total number of running and sleep threads (JC van Winkel). +** +** Revision 1.8 2010/05/18 19:20:02 gerlof +** Introduce CPU frequency and scaling (JC van Winkel). +** +** Revision 1.7 2010/04/23 08:16:58 gerlof +** Field 'avque' modified to 'avq' to be able to show higher values +** (especially on LVM-level). +** +** Revision 1.6 2010/03/04 10:53:37 gerlof +** Support I/O-statistics on logical volumes and MD devices. +** +** Revision 1.5 2009/12/17 11:59:36 gerlof +** Gather and display new counters: dirty cache and guest cpu usage. +** +** Revision 1.4 2009/12/17 08:53:03 gerlof +** If no coclors wanted, use bold display for critical resources. +** +** Revision 1.3 2009/12/17 07:33:05 gerlof +** Scale system-statistics properly when modifying window size (JC van Winkel). +** +** Revision 1.2 2009/12/10 11:56:08 gerlof +** Various bug-solutions. +** +** Revision 1.1 2009/12/10 09:46:16 gerlof +** Initial revision +** +** Initial revision +** +** +** Initial +** +*/ + +static const char rcsid[] = "XXXXXX"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; i1) + { + slackitemsover=(double)(avail-nitems)/(nitems); + } + else + { + slackitemsover=(avail-nitems)/2; + } + + + // charslack: the slack in characters after using as many + // items as possible + double charslackover = screen ? ((COLS - 5) % 15) : ((linelen - 5) %15); + + // two places per items where blanks can be added + charslackover /= (avail * 2); + + double charslackused=0.0; + double itemslackused=0.0; + elemptr=newelems; + + while ((curelem=elemptr->f)!=0) + { + char *itemp; + int color; + + printg(" | "); + addblanks(&charslackused, &charslackover); + + /* + ** by default no color is shown for this field (color = 0) + ** + ** the convert-function can set a color-number (color > 0) + ** when a specific color is wanted or the convert-function + ** can leave the decision to display with a color to the piece + ** of code below (color == -1) + */ + color = 0; + itemp = curelem->doconvert(sstat, extra, badness, &color); + + if (screen) + { + if (color == -1) // default color wanted + { + color = 0; + + if (badness >= 100) + color = COLORCRIT; + else if (almostcrit && badness >= almostcrit) + color = COLORALMOST; + } + + if (color) // after all: has a color been set? + { + if (usecolors) + attron(COLOR_PAIR(color)); + else + attron(A_BOLD); + } + } + + printg("%s", itemp); + + if (color && screen) // color set for this value? + { + if (usecolors) + attroff(COLOR_PAIR(color)); + else + attroff(A_BOLD); + } + + itemslackused+=slackitemsover; + while (itemslackused>0.5) + { + addblanks(&charslackused, &charslackover); + printg(" | "); + printg("%s", sysprt_BLANKBOX(0, 0, 0, 0)); + addblanks(&charslackused, &charslackover); + itemslackused-=1; + } + + elemptr++; + + addblanks(&charslackused, &charslackover); + } + + printg(" |"); + + if (!screen) + { + printg("\n"); + } +} + +/*******************************************************************/ +/* SYSTEM PRINT FUNCTIONS */ +/*******************************************************************/ +char * +sysprt_PRCSYS(void *notused, void *q, int badness, int *color) +{ + extraparam *as=q; + static char buf[15]="sys "; + val2cpustr(as->totst * 1000/hertz, buf+6); + return buf; +} + +sys_printdef syspdef_PRCSYS = {"PRCSYS", sysprt_PRCSYS}; +/*******************************************************************/ +char * +sysprt_PRCUSER(void *notused, void *q, int badness, int *color) +{ + extraparam *as=q; + static char buf[15]="user "; + val2cpustr(as->totut * 1000/hertz, buf+6); + return buf; +} + +sys_printdef syspdef_PRCUSER = {"PRCUSER", sysprt_PRCUSER}; +/*******************************************************************/ +char * +sysprt_PRCNPROC(void *notused, void *q, int badness, int *color) +{ + extraparam *as=q; + static char buf[15]="#proc "; + val2valstr(as->nproc, buf+6, 6, 0, 0); + return buf; +} + +sys_printdef syspdef_PRCNPROC = {"PRCNPROC", sysprt_PRCNPROC}; +/*******************************************************************/ +char * +sysprt_PRCNRUNNING(void *notused, void *q, int badness, int *color) +{ + extraparam *as=q; + static char buf[15]="#trun "; + val2valstr(as->ntrun, buf+6, 6, 0, 0); + return buf; +} + +sys_printdef syspdef_PRCNRUNNING = {"PRCNRUNNING", sysprt_PRCNRUNNING}; +/*******************************************************************/ +char * +sysprt_PRCNSLEEPING(void *notused, void *q, int badness, int *color) +{ + extraparam *as=q; + static char buf[15]="#tslpi "; + val2valstr(as->ntslpi, buf+8, 4, 0, 0); + return buf; +} + +sys_printdef syspdef_PRCNSLEEPING = {"PRCNSLEEPING", sysprt_PRCNSLEEPING}; +/*******************************************************************/ +char * +sysprt_PRCNDSLEEPING(void *notused, void *q, int badness, int *color) +{ + extraparam *as=q; + static char buf[15]="#tslpu "; + val2valstr(as->ntslpu, buf+8, 4, 0, 0); + return buf; +} + +sys_printdef syspdef_PRCNDSLEEPING = {"PRCNDSLEEPING", sysprt_PRCNDSLEEPING}; +/*******************************************************************/ +char * +sysprt_PRCNZOMBIE(void *notused, void *q, int badness, int *color) +{ + extraparam *as=q; + static char buf[15]="#zombie "; + + if (as->nzomb > 30) + *color = COLORALMOST; + + if (as->nzomb > 50) + *color = COLORCRIT; + + val2valstr(as->nzomb, buf+8, 4, 0, 0); + return buf; +} + +sys_printdef syspdef_PRCNZOMBIE = {"PRCNZOMBIE", sysprt_PRCNZOMBIE}; +/*******************************************************************/ +char * +sysprt_PRCNNEXIT(void *notused, void *q, int badness, int *color) +{ + static char firstcall = 1; + + extraparam *as=q; + static char buf[15]="#exit "; + + if (supportflags & ACCTACTIVE) + { + if (as->noverflow) + { + *color = COLORCRIT; + buf[6] = '>'; + val2valstr(as->nexit, buf+7, 5, as->avgval, as->nsecs); + } + else + { + val2valstr(as->nexit, buf+6, 6, as->avgval, as->nsecs); + } + + return buf; + } + else + { + if (firstcall) + { + *color = COLORCRIT; + firstcall = 0; + } + else + { + *color = COLORINFO; + } + + switch (acctreason) + { + case 1: + return "no procacct"; // "no acctread"; + case 2: + return "no procacct"; // "no acctwant"; + case 3: + return "no procacct"; // "no acctsema"; + case 4: + return "no procacct"; // "no acctmkdir"; + case 5: + return "no procacct"; // "no rootprivs"; + default: + return "no procacct"; + } + } +} + +sys_printdef syspdef_PRCNNEXIT = {"PRCNNEXIT", sysprt_PRCNNEXIT}; +/*******************************************************************/ +char * +sysprt_CPUSYS(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[15]; + float perc = (sstat->cpu.all.stime * 100.0) / as->percputot; + + if (perc > 1.0) + *color = -1; + + sprintf(buf, "sys %6.0f%%", perc); + return buf; +} + +sys_printdef syspdef_CPUSYS = {"CPUSYS", sysprt_CPUSYS}; +/*******************************************************************/ +char * +sysprt_CPUUSER(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[15]; + float perc = (sstat->cpu.all.utime + sstat->cpu.all.ntime) + * 100.0 / as->percputot; + + if (perc > 1.0) + *color = -1; + + sprintf(buf, "user %6.0f%%", perc); + return buf; +} + +sys_printdef syspdef_CPUUSER = {"CPUUSER", sysprt_CPUUSER}; +/*******************************************************************/ +char * +sysprt_CPUIRQ(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[15]; + float perc = (sstat->cpu.all.Itime + sstat->cpu.all.Stime) + * 100.0 / as->percputot; + + if (perc > 1.0) + *color = -1; + + sprintf(buf, "irq %6.0f%%", perc); + return buf; +} + +sys_printdef syspdef_CPUIRQ = {"CPUIRQ", sysprt_CPUIRQ}; +/*******************************************************************/ +char * +sysprt_CPUIDLE(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[15]; + sprintf(buf, "idle %6.0f%%", + (sstat->cpu.all.itime * 100.0) / as->percputot); + return buf; +} + +sys_printdef syspdef_CPUIDLE = {"CPUIDLE", sysprt_CPUIDLE}; +/*******************************************************************/ +char * +sysprt_CPUWAIT(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[15]; + sprintf(buf, "wait %6.0f%%", + (sstat->cpu.all.wtime * 100.0) / as->percputot); + return buf; +} + +sys_printdef syspdef_CPUWAIT = {"CPUWAIT", sysprt_CPUWAIT}; +/*******************************************************************/ +char * +sysprt_CPUISYS(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[15]; + float perc = sstat->cpu.cpu[as->index].stime * 100.0 + / as->percputot; + + if (perc > 1.0) + *color = -1; + + sprintf(buf, "sys %6.0f%%", perc); + return buf; +} + +sys_printdef syspdef_CPUISYS = {"CPUISYS", sysprt_CPUISYS}; +/*******************************************************************/ +char * +sysprt_CPUIUSER(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[15]; + float perc = (sstat->cpu.cpu[as->index].utime + + sstat->cpu.cpu[as->index].ntime) + * 100.0 / as->percputot; + + if (perc > 1.0) + *color = -1; + + sprintf(buf, "user %6.0f%%", perc); + return buf; +} + +sys_printdef syspdef_CPUIUSER = {"CPUIUSER", sysprt_CPUIUSER}; +/*******************************************************************/ +char * +sysprt_CPUIIRQ(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[15]; + float perc = (sstat->cpu.cpu[as->index].Itime + + sstat->cpu.cpu[as->index].Stime) + * 100.0 / as->percputot; + + if (perc > 1.0) + *color = -1; + + sprintf(buf, "irq %6.0f%%", perc); + return buf; +} + +sys_printdef syspdef_CPUIIRQ = {"CPUIIRQ", sysprt_CPUIIRQ}; +/*******************************************************************/ +char * +sysprt_CPUIIDLE(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[15]; + sprintf(buf, "idle %6.0f%%", + (sstat->cpu.cpu[as->index].itime * 100.0) / as->percputot); + return buf; +} + +sys_printdef syspdef_CPUIIDLE = {"CPUIIDLE", sysprt_CPUIIDLE}; +/*******************************************************************/ +char * +sysprt_CPUIWAIT(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[15]; + sprintf(buf, "cpu%03d w%3.0f%%", + sstat->cpu.cpu[as->index].cpunr, + (sstat->cpu.cpu[as->index].wtime * 100.0) / as->percputot); + return buf; +} + +sys_printdef syspdef_CPUIWAIT = {"CPUIWAIT", sysprt_CPUIWAIT}; +/*******************************************************************/ +void dofmt_cpufreq(char *buf, count_t maxfreq, count_t cnt, count_t ticks) +{ + // if ticks != 0, do full output + if (ticks) + { + count_t curfreq = cnt/ticks; + strcpy(buf, "avgf "); + val2Hzstr(curfreq, buf+5); + } + else if (cnt) // no max, no %. if freq is known: print it + { + strcpy(buf, "curf "); + val2Hzstr(cnt, buf+5); + } + else // nothing is known: print ????? + { + strcpy(buf, "curf ?MHz"); + } +} + + +/* + * sumscaling: sum scaling info for all processors + * + */ +void sumscaling(struct sstat *sstat, count_t *maxfreq, + count_t *cnt, count_t *ticks) +{ + count_t mymaxfreq = 0; + count_t mycnt = 0; + count_t myticks = 0; + + int n=sstat->cpu.nrcpu; + int i; + + for (i=0; i < n; ++i) + { + mymaxfreq+= sstat->cpu.cpu[i].freqcnt.maxfreq; + mycnt += sstat->cpu.cpu[i].freqcnt.cnt; + myticks += sstat->cpu.cpu[i].freqcnt.ticks; + } + *maxfreq= mymaxfreq; + *cnt = mycnt; + *ticks = myticks; +} + + +void dofmt_cpuscale(char *buf, count_t maxfreq, count_t cnt, count_t ticks) +{ + if (ticks) + { + count_t curfreq = cnt/ticks; + int perc = maxfreq ? 100 * curfreq / maxfreq : 0; + + strcpy(buf, "avgscal "); + sprintf(buf+7, "%4d%%", perc); + } + else if (maxfreq) // max frequency is known so % can be calculated + { + strcpy(buf, "curscal "); + sprintf(buf+7, "%4lld%%", 100 * cnt / maxfreq); + } + else // nothing is known: print ????? + { + strcpy(buf, "curscal ?%"); + } +} + +/*******************************************************************/ +char * +sysprt_CPUIFREQ(void *p, void *q, int badness, int *color) +{ + + struct sstat *sstat=p; + extraparam *as=q; + static char buf[15]; + + count_t maxfreq = sstat->cpu.cpu[as->index].freqcnt.maxfreq; + count_t cnt = sstat->cpu.cpu[as->index].freqcnt.cnt; + count_t ticks = sstat->cpu.cpu[as->index].freqcnt.ticks; + + dofmt_cpufreq(buf, maxfreq, cnt, ticks); + return buf; +} + +sys_printdef syspdef_CPUIFREQ = {"CPUIFREQ", sysprt_CPUIFREQ}; +/*******************************************************************/ +char * +sysprt_CPUFREQ(void *p, void *q, int badness, int *color) +{ + + struct sstat *sstat=p; + static char buf[15]; + + count_t maxfreq; + count_t cnt; + count_t ticks; + int n = sstat->cpu.nrcpu; + + sumscaling(sstat, &maxfreq, &cnt, &ticks); + dofmt_cpufreq(buf, maxfreq/n, cnt/n, ticks/n); + return buf; +} + +sys_printdef syspdef_CPUFREQ = {"CPUFREQ", sysprt_CPUFREQ}; +/*******************************************************************/ +char * +sysprt_CPUISCALE(void *p, void *q, int badness, int *color) +{ + + struct sstat *sstat=p; + extraparam *as=q; + static char buf[15]; + + count_t maxfreq = sstat->cpu.cpu[as->index].freqcnt.maxfreq; + count_t cnt = sstat->cpu.cpu[as->index].freqcnt.cnt; + count_t ticks = sstat->cpu.cpu[as->index].freqcnt.ticks; + + dofmt_cpuscale(buf, maxfreq, cnt, ticks); + return buf; +} + +sys_printdef syspdef_CPUISCALE = {"CPUISCALE", sysprt_CPUISCALE}; +/*******************************************************************/ +char * +sysprt_CPUSCALE(void *p, void *q, int badness, int *color) +{ + + struct sstat *sstat=p; + static char buf[15]; + + count_t maxfreq; + count_t cnt; + count_t ticks; + int n = sstat->cpu.nrcpu; + + sumscaling(sstat, &maxfreq, &cnt, &ticks); + dofmt_cpuscale(buf, maxfreq/n, cnt/n, ticks/n); + return buf; +} + +sys_printdef syspdef_CPUSCALE = {"CPUSCALE", sysprt_CPUSCALE}; +/*******************************************************************/ +char * +sysprt_CPUSTEAL(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[15]; + float perc = sstat->cpu.all.steal * 100.0 / as->percputot; + + if (perc > 1.0) + *color = -1; + + sprintf(buf, "steal %5.0f%%", perc); + return buf; +} + +sys_printdef syspdef_CPUSTEAL = {"CPUSTEAL", sysprt_CPUSTEAL}; +/*******************************************************************/ +char * +sysprt_CPUISTEAL(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[15]; + float perc = sstat->cpu.cpu[as->index].steal * 100.0 + / as->percputot; + + if (perc > 1.0) + *color = -1; + + sprintf(buf, "steal %5.0f%%", perc); + return buf; +} + +sys_printdef syspdef_CPUISTEAL = {"CPUISTEAL", sysprt_CPUISTEAL}; +/*******************************************************************/ +char * +sysprt_CPUGUEST(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[15]; + float perc = sstat->cpu.all.guest * 100.0 / as->percputot; + + if (perc > 1.0) + *color = -1; + + sprintf(buf, "guest %5.0f%%", perc); + return buf; +} + +sys_printdef syspdef_CPUGUEST = {"CPUGUEST", sysprt_CPUGUEST}; +/*******************************************************************/ +char * +sysprt_CPUIGUEST(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[15]; + float perc = sstat->cpu.cpu[as->index].guest * 100.0 / as->percputot; + + if (perc > 1.0) + *color = -1; + + sprintf(buf, "guest %5.0f%%", perc); + return buf; +} + +sys_printdef syspdef_CPUIGUEST = {"CPUIGUEST", sysprt_CPUIGUEST}; +/*******************************************************************/ +char * +sysprt_CPLAVG1(void *p, void *notused, int badness, int *color) +{ + struct sstat *sstat=p; + static char buf[15]="avg1 "; + + if (sstat->cpu.lavg1 > 999.0) + { + sprintf(buf+5, "%7.0f", sstat->cpu.lavg1); + } + else + { + sprintf(buf+5, "%7.2f", sstat->cpu.lavg1); + } + return buf; +} + +sys_printdef syspdef_CPLAVG1 = {"CPLAVG1", sysprt_CPLAVG1}; +/*******************************************************************/ +char * +sysprt_CPLAVG5(void *p, void *notused, int badness, int *color) +{ + struct sstat *sstat=p; + static char buf[15]="avg5 "; + + if (sstat->cpu.lavg5 > 999.0) + { + sprintf(buf+5, "%7.0f", sstat->cpu.lavg5); + } + else + { + sprintf(buf+5, "%7.2f", sstat->cpu.lavg5); + } + return buf; +} + +sys_printdef syspdef_CPLAVG5 = {"CPLAVG5", sysprt_CPLAVG5}; +/*******************************************************************/ +char * +sysprt_CPLAVG15(void *p, void *notused, int badness, int *color) +{ + struct sstat *sstat=p; + static char buf[15]="avg15 "; + + if (sstat->cpu.lavg15 > (2 * sstat->cpu.nrcpu) ) + *color = COLORALMOST; + + if (sstat->cpu.lavg15 > 999.0) + { + sprintf(buf+6, "%6.0f", sstat->cpu.lavg15); + } + else + { + sprintf(buf+6, "%6.2f", sstat->cpu.lavg15); + } + return buf; +} + +sys_printdef syspdef_CPLAVG15 = {"CPLAVG15", sysprt_CPLAVG15}; +/*******************************************************************/ +char * +sysprt_CPLCSW(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="csw "; + + val2valstr(sstat->cpu.csw, buf+4 , 8,as->avgval,as->nsecs); + return buf; +} + +sys_printdef syspdef_CPLCSW = {"CPLCSW", sysprt_CPLCSW}; +/*******************************************************************/ +char * +sysprt_PRCCLONES(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="clones "; + + val2valstr(sstat->cpu.nprocs, buf+7 , 5,as->avgval,as->nsecs); + return buf; +} + +sys_printdef syspdef_PRCCLONES = {"PRCCLONES", sysprt_PRCCLONES}; +/*******************************************************************/ +char * +sysprt_CPLNUMCPU(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="numcpu "; + + val2valstr(sstat->cpu.nrcpu, buf+7 , 5,0,as->nsecs); + return buf; +} + +sys_printdef syspdef_CPLNUMCPU = {"CPLNUMCPU", sysprt_CPLNUMCPU}; +/*******************************************************************/ +char * +sysprt_CPLINTR(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="intr "; + + val2valstr(sstat->cpu.devint, buf+5 , 7,as->avgval,as->nsecs); + return buf; +} + +sys_printdef syspdef_CPLINTR = {"CPLINTR", sysprt_CPLINTR}; +/*******************************************************************/ +char * +sysprt_MEMTOT(void *p, void *notused, int badness, int *color) +{ + struct sstat *sstat=p; + static char buf[16]="tot "; + *color = -1; + val2memstr(sstat->mem.physmem * pagesize, buf+6, MBFORMAT, 0, 0); + return buf; +} + +sys_printdef syspdef_MEMTOT = {"MEMTOT", sysprt_MEMTOT}; +/*******************************************************************/ +char * +sysprt_MEMFREE(void *p, void *notused, int badness, int *color) +{ + struct sstat *sstat=p; + static char buf[16]="free "; + *color = -1; + val2memstr(sstat->mem.freemem * pagesize, buf+6, MBFORMAT, 0, 0); + return buf; +} + +sys_printdef syspdef_MEMFREE = {"MEMFREE", sysprt_MEMFREE}; +/*******************************************************************/ +char * +sysprt_MEMCACHE(void *p, void *notused, int badness, int *color) +{ + struct sstat *sstat=p; + static char buf[16]="cache "; + *color = -1; + val2memstr(sstat->mem.cachemem * pagesize, buf+6, MBFORMAT, 0, 0); + return buf; +} + +sys_printdef syspdef_MEMCACHE = {"MEMCACHE", sysprt_MEMCACHE}; +/*******************************************************************/ +char * +sysprt_MEMDIRTY(void *p, void *notused, int badness, int *color) +{ + struct sstat *sstat=p; + static char buf[16] = "dirty "; + val2memstr(sstat->mem.cachedrt * pagesize, buf+6, MBFORMAT, 0, 0); + + return buf; +} + +sys_printdef syspdef_MEMDIRTY = {"MEMDIRTY", sysprt_MEMDIRTY}; +/*******************************************************************/ +char * +sysprt_MEMBUFFER(void *p, void *notused, int badness, int *color) +{ + struct sstat *sstat=p; + static char buf[16]="buff "; + *color = -1; + val2memstr(sstat->mem.buffermem * pagesize, buf+6, MBFORMAT, 0, 0); + return buf; +} + +sys_printdef syspdef_MEMBUFFER = {"MEMBUFFER", sysprt_MEMBUFFER}; +/*******************************************************************/ +char * +sysprt_MEMSLAB(void *p, void *notused, int badness, int *color) +{ + struct sstat *sstat=p; + static char buf[16]="slab "; + *color = -1; + val2memstr(sstat->mem.slabmem * pagesize, buf+6, MBFORMAT, 0, 0); + return buf; +} + +sys_printdef syspdef_MEMSLAB = {"MEMSLAB", sysprt_MEMSLAB}; +/*******************************************************************/ +char * +sysprt_RECSLAB(void *p, void *notused, int badness, int *color) +{ + struct sstat *sstat=p; + static char buf[16]="slrec "; + *color = -1; + val2memstr(sstat->mem.slabreclaim * pagesize, buf+6, MBFORMAT, 0, 0); + return buf; +} + +sys_printdef syspdef_RECSLAB = {"RECSLAB", sysprt_RECSLAB}; +/*******************************************************************/ +char * +sysprt_SHMEM(void *p, void *notused, int badness, int *color) +{ + struct sstat *sstat=p; + static char buf[16]="shmem "; + *color = -1; + val2memstr(sstat->mem.shmem * pagesize, buf+6, MBFORMAT, 0, 0); + return buf; +} + +sys_printdef syspdef_SHMEM = {"SHMEM", sysprt_SHMEM}; +/*******************************************************************/ +char * +sysprt_SHMRSS(void *p, void *notused, int badness, int *color) +{ + struct sstat *sstat=p; + static char buf[16]="shrss "; + *color = -1; + val2memstr(sstat->mem.shmrss * pagesize, buf+6, MBFORMAT, 0, 0); + return buf; +} + +sys_printdef syspdef_SHMRSS = {"SHMRSS", sysprt_SHMRSS}; +/*******************************************************************/ +char * +sysprt_SHMSWP(void *p, void *notused, int badness, int *color) +{ + struct sstat *sstat=p; + static char buf[16]="shswp "; + *color = -1; + val2memstr(sstat->mem.shmswp * pagesize, buf+6, MBFORMAT, 0, 0); + return buf; +} + +sys_printdef syspdef_SHMSWP = {"SHMSWP", sysprt_SHMSWP}; +/*******************************************************************/ +char * +sysprt_HUPTOT(void *p, void *notused, int badness, int *color) +{ + struct sstat *sstat=p; + static char buf[16]="hptot "; + *color = -1; + val2memstr(sstat->mem.tothugepage * sstat->mem.hugepagesz, + buf+6, MBFORMAT, 0, 0); + return buf; +} + +sys_printdef syspdef_HUPTOT = {"HUPTOT", sysprt_HUPTOT}; +/*******************************************************************/ +char * +sysprt_HUPUSE(void *p, void *notused, int badness, int *color) +{ + struct sstat *sstat=p; + static char buf[16]="hpuse "; + *color = -1; + val2memstr( (sstat->mem.tothugepage - sstat->mem.freehugepage) * + sstat->mem.hugepagesz, buf+6, MBFORMAT, 0, 0); + return buf; +} + +sys_printdef syspdef_HUPUSE = {"HUPUSE", sysprt_HUPUSE}; +/*******************************************************************/ +char * +sysprt_VMWBAL(void *p, void *notused, int badness, int *color) +{ + struct sstat *sstat=p; + static char buf[16]="vmbal "; + *color = -1; + val2memstr(sstat->mem.vmwballoon * pagesize, buf+6, MBFORMAT, 0, 0); + return buf; +} + +sys_printdef syspdef_VMWBAL = {"VMWBAL", sysprt_VMWBAL}; +/*******************************************************************/ +char * +sysprt_SWPTOT(void *p, void *notused, int badness, int *color) +{ + struct sstat *sstat=p; + static char buf[16]="tot "; + *color = -1; + val2memstr(sstat->mem.totswap * pagesize, buf+6, MBFORMAT, 0, 0); + return buf; +} + +sys_printdef syspdef_SWPTOT = {"SWPTOT", sysprt_SWPTOT}; +/*******************************************************************/ +char * +sysprt_SWPFREE(void *p, void *notused, int badness, int *color) +{ + struct sstat *sstat=p; + static char buf[16]="free "; + *color = -1; + val2memstr(sstat->mem.freeswap * pagesize, buf+6, MBFORMAT, 0, 0); + return buf; +} + +sys_printdef syspdef_SWPFREE = {"SWPFREE", sysprt_SWPFREE}; +/*******************************************************************/ +char * +sysprt_SWPCOMMITTED(void *p, void *notused, int badness, int *color) +{ + struct sstat *sstat=p; + static char buf[16]="vmcom "; + val2memstr(sstat->mem.committed * pagesize, buf+6, MBFORMAT, 0, 0); + + if (sstat->mem.commitlim && sstat->mem.committed > sstat->mem.commitlim) + *color = COLORALMOST; + + return buf; +} + +sys_printdef syspdef_SWPCOMMITTED = {"SWPCOMMITTED", sysprt_SWPCOMMITTED}; +/*******************************************************************/ +char * +sysprt_SWPCOMMITLIM(void *p, void *notused, int badness, int *color) +{ + struct sstat *sstat=p; + static char buf[16]="vmlim "; + val2memstr(sstat->mem.commitlim * pagesize, buf+6, MBFORMAT, 0, 0); + + if (sstat->mem.commitlim && sstat->mem.committed > sstat->mem.commitlim) + *color = COLORINFO; + + return buf; +} + +sys_printdef syspdef_SWPCOMMITLIM = {"SWPCOMMITLIM", sysprt_SWPCOMMITLIM}; +/*******************************************************************/ +char * +sysprt_PAGSCAN(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="scan "; + val2valstr(sstat->mem.pgscans, buf+5, 7, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_PAGSCAN = {"PAGSCAN", sysprt_PAGSCAN}; +/*******************************************************************/ +char * +sysprt_PAGSTEAL(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="steal "; + val2valstr(sstat->mem.pgsteal, buf+ 6, 6, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_PAGSTEAL = {"PAGSTEAL", sysprt_PAGSTEAL}; +/*******************************************************************/ +char * +sysprt_PAGSTALL(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="stall "; + val2valstr(sstat->mem.allocstall, buf+6, 6, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_PAGSTALL = {"PAGSTALL", sysprt_PAGSTALL}; +/*******************************************************************/ +char * +sysprt_PAGSWIN(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="swin "; + val2valstr(sstat->mem.swins, buf+5, 7, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_PAGSWIN = {"PAGSWIN", sysprt_PAGSWIN}; +/*******************************************************************/ +char * +sysprt_PAGSWOUT(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="swout "; + *color = -1; + val2valstr(sstat->mem.swouts, buf+6, 6, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_PAGSWOUT = {"PAGSWOUT", sysprt_PAGSWOUT}; +/*******************************************************************/ +char * +sysprt_CONTNAME(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[32] = "ctid "; + + *color = -1; + + sprintf(buf+5, "%7lu", sstat->cfs.cont[as->index].ctid); + return buf; +} + +sys_printdef syspdef_CONTNAME = {"CONTNAME", sysprt_CONTNAME}; +/*******************************************************************/ +char * +sysprt_CONTNPROC(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="nproc "; + + *color = -1; + + val2valstr(sstat->cfs.cont[as->index].numproc, + buf+6, 6, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_CONTNPROC = {"CONTNPROC", sysprt_CONTNPROC}; +/*******************************************************************/ +char * +sysprt_CONTCPU(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]; + float perc; + + count_t used = sstat->cfs.cont[as->index].system + + sstat->cfs.cont[as->index].user + + sstat->cfs.cont[as->index].nice; + + *color = -1; + + if (sstat->cfs.cont[as->index].uptime) + { + perc = used * 100.0 / sstat->cfs.cont[as->index].uptime; + sprintf(buf, "cpubusy %3.0f%%", perc); + } + else + sprintf(buf, "cpubusy ?%%"); + + return buf; +} + +sys_printdef syspdef_CONTCPU = {"CONTCPU", sysprt_CONTCPU}; +/*******************************************************************/ +char * +sysprt_CONTMEM(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="mem "; + + *color = -1; + + val2memstr(sstat->cfs.cont[as->index].physpages * pagesize, + buf+6, MBFORMAT, 0, 0); + return buf; +} + +sys_printdef syspdef_CONTMEM = {"CONTMEM", sysprt_CONTMEM}; +/*******************************************************************/ +char * +sysprt_DSKNAME(void *p, void *q, int badness, int *color) +{ + extraparam *as=q; + static char buf[16]; + char *pn; + int len; + + *color = -1; + + if ( (len = strlen(as->perdsk[as->index].name)) > 12) + pn = as->perdsk[as->index].name + len - 12; + else + pn = as->perdsk[as->index].name; + + sprintf(buf, "%12.12s", pn); + return buf; +} + +sys_printdef syspdef_DSKNAME = {"DSKNAME", sysprt_DSKNAME}; +/*******************************************************************/ +char * +sysprt_DSKBUSY(void *p, void *q, int badness, int *color) +{ + extraparam *as=q; + double perc; + static char buf[16]="busy "; + + *color = -1; + + perc = as->perdsk[as->index].io_ms * 100.0 / as->mstot; + + if (perc >= 0.0 && perc < 1000000.0) + sprintf(buf+5, "%6.0lf%%", perc); + else + sprintf(buf+5, "%6.0lf%%", 999999.0); + + return buf; +} + +sys_printdef syspdef_DSKBUSY = {"DSKBUSY", sysprt_DSKBUSY}; +/*******************************************************************/ +char * +sysprt_DSKNREAD(void *p, void *q, int badness, int *color) +{ + extraparam *as=q; + static char buf[16]="read "; + + *color = -1; + + val2valstr(as->perdsk[as->index].nread >= 0 ? + as->perdsk[as->index].nread : 0, + buf+5, 7, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_DSKNREAD = {"DSKNREAD", sysprt_DSKNREAD}; +/*******************************************************************/ +char * +sysprt_DSKNWRITE(void *p, void *q, int badness, int *color) +{ + extraparam *as=q; + static char buf[16]="write "; + + *color = -1; + + val2valstr(as->perdsk[as->index].nwrite >= 0 ? + as->perdsk[as->index].nwrite : 0, + buf+6, 6, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_DSKNWRITE = {"DSKNWRITE", sysprt_DSKNWRITE}; +/*******************************************************************/ +char * +sysprt_DSKKBPERWR(void *p, void *q, int badness, int *color) +{ + extraparam *as=q; + static char buf[16]="KiB/w "; + struct perdsk *dp = &(as->perdsk[as->index]); + + val2valstr(dp->nwrite > 0 ? dp->nwsect / dp->nwrite / 2 : 0, + buf+6, 6, 0, as->nsecs); + return buf; +} + +sys_printdef syspdef_DSKKBPERWR = {"DSKKBPERWR", sysprt_DSKKBPERWR}; +/*******************************************************************/ +char * +sysprt_DSKKBPERRD(void *p, void *q, int badness, int *color) +{ + extraparam *as=q; + static char buf[16]="KiB/r "; + struct perdsk *dp = &(as->perdsk[as->index]); + + val2valstr(dp->nread > 0 ? dp->nrsect / dp->nread / 2 : 0, + buf+6, 6, 0, as->nsecs); + return buf; +} + +sys_printdef syspdef_DSKKBPERRD = {"DSKKBPERRD", sysprt_DSKKBPERRD}; +/*******************************************************************/ +char * +sysprt_DSKMBPERSECWR(void *p, void *q, int badness, int *color) +{ + extraparam *as=q; + static char buf[16]="MBw/s "; + struct perdsk *dp = &(as->perdsk[as->index]); + + snprintf(buf+6, sizeof buf-6, "%6.1lf", + dp->nwsect / 2.0 / 1024 / as->nsecs); + + return buf; +} + +sys_printdef syspdef_DSKMBPERSECWR = {"DSKMBPERSECWR", sysprt_DSKMBPERSECWR}; +/*******************************************************************/ +char * +sysprt_DSKMBPERSECRD(void *p, void *q, int badness, int *color) +{ + extraparam *as=q; + static char buf[16]="MBr/s "; + struct perdsk *dp = &(as->perdsk[as->index]); + + snprintf(buf+6, sizeof buf-6, "%6.1lf", + dp->nrsect / 2.0 / 1024 / as->nsecs); + return buf; +} + +sys_printdef syspdef_DSKMBPERSECRD = {"DSKMBPERSECRD", sysprt_DSKMBPERSECRD}; +/*******************************************************************/ +char * +sysprt_DSKAVQUEUE(void *p, void *q, int badness, int *color) +{ + extraparam *as=q; + static char buf[16]="avq "; + struct perdsk *dp = &(as->perdsk[as->index]); + + sprintf(buf+4, "%8.2f", dp->io_ms > 0 ? + (double)dp->avque / dp->io_ms : 0.0); + return buf; +} + +sys_printdef syspdef_DSKAVQUEUE = {"DSKAVQUEUE", sysprt_DSKAVQUEUE}; +/*******************************************************************/ +char * +sysprt_DSKAVIO(void *p, void *q, int badness, int *color) +{ + extraparam *as=q; + static char buf[16]="avio "; + double tim = as->iotot > 0 ? + (double)(as->perdsk[as->index].io_ms)/as->iotot:0.0; + + *color = -1; + + if (tim > 100.0) + { + sprintf(buf+5, "%4.0lf ms", tim); + } + else if (tim > 10.0) + { + sprintf(buf+5, "%4.1lf ms", tim); + } + else + { + sprintf(buf+5, "%4.2lf ms", tim); + } + + return buf; +} + +sys_printdef syspdef_DSKAVIO = {"DSKAVIO", sysprt_DSKAVIO}; +/*******************************************************************/ +char * +sysprt_NETTRANSPORT(void *p, void *notused, int badness, int *color) +{ + return "transport "; +} + +sys_printdef syspdef_NETTRANSPORT = {"NETTRANSPORT", sysprt_NETTRANSPORT}; +/*******************************************************************/ +char * +sysprt_NETTCPI(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="tcpi "; + val2valstr(sstat->net.tcp.InSegs, buf+5, 7, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NETTCPI = {"NETTCPI", sysprt_NETTCPI}; +/*******************************************************************/ +char * +sysprt_NETTCPO(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="tcpo "; + val2valstr(sstat->net.tcp.OutSegs, buf+5, 7, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NETTCPO = {"NETTCPO", sysprt_NETTCPO}; +/*******************************************************************/ +char * +sysprt_NETTCPACTOPEN(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="tcpao "; + val2valstr(sstat->net.tcp.ActiveOpens, buf+6, 6, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NETTCPACTOPEN = {"NETTCPACTOPEN", sysprt_NETTCPACTOPEN}; +/*******************************************************************/ +char * +sysprt_NETTCPPASVOPEN(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="tcppo "; + val2valstr(sstat->net.tcp.PassiveOpens, buf+6, 6, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NETTCPPASVOPEN = {"NETTCPPASVOPEN", sysprt_NETTCPPASVOPEN}; +/*******************************************************************/ +char * +sysprt_NETTCPRETRANS(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="tcprs "; + val2valstr(sstat->net.tcp.RetransSegs, buf+6, 6, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NETTCPRETRANS = {"NETTCPRETRANS", sysprt_NETTCPRETRANS}; +/*******************************************************************/ +char * +sysprt_NETTCPINERR(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="tcpie "; + val2valstr(sstat->net.tcp.InErrs, buf+6, 6, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NETTCPINERR = {"NETTCPINERR", sysprt_NETTCPINERR}; +/*******************************************************************/ +char * +sysprt_NETTCPORESET(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="tcpor "; + val2valstr(sstat->net.tcp.OutRsts, buf+6, 6, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NETTCPORESET = {"NETTCPORESET", sysprt_NETTCPORESET}; +/*******************************************************************/ +char * +sysprt_NETUDPNOPORT(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="udpnp "; + val2valstr(sstat->net.udpv4.NoPorts, buf+6, 6, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NETUDPNOPORT = {"NETUDPNOPORT", sysprt_NETUDPNOPORT}; +/*******************************************************************/ +char * +sysprt_NETUDPINERR(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="udpie "; + val2valstr(sstat->net.udpv4.InErrors, buf+6, 6, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NETUDPINERR = {"NETUDPINERR", sysprt_NETUDPINERR}; +/*******************************************************************/ +char * +sysprt_NETUDPI(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="udpi "; + count_t udpin = sstat->net.udpv4.InDatagrams + + sstat->net.udpv6.Udp6InDatagrams; + val2valstr(udpin, buf+5, 7, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NETUDPI = {"NETUDPI", sysprt_NETUDPI}; +/*******************************************************************/ +char * +sysprt_NETUDPO(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="udpo "; + count_t udpout = sstat->net.udpv4.OutDatagrams + + sstat->net.udpv6.Udp6OutDatagrams; + val2valstr(udpout, buf+5, 7, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NETUDPO = {"NETUDPO", sysprt_NETUDPO}; +/*******************************************************************/ +char * +sysprt_NETNETWORK(void *p, void *notused, int badness, int *color) +{ + return "network "; +} + +sys_printdef syspdef_NETNETWORK = {"NETNETWORK", sysprt_NETNETWORK}; +/*******************************************************************/ +char * +sysprt_NETIPI(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="ipi "; + count_t ipin = sstat->net.ipv4.InReceives + + sstat->net.ipv6.Ip6InReceives; + val2valstr(ipin, buf+4, 8, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NETIPI = {"NETIPI", sysprt_NETIPI}; +/*******************************************************************/ +char * +sysprt_NETIPO(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="ipo "; + count_t ipout = sstat->net.ipv4.OutRequests + + sstat->net.ipv6.Ip6OutRequests; + val2valstr(ipout, buf+4, 8, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NETIPO = {"NETIPO", sysprt_NETIPO}; +/*******************************************************************/ +char * +sysprt_NETIPFRW(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="ipfrw "; + count_t ipfrw = sstat->net.ipv4.ForwDatagrams + + sstat->net.ipv6.Ip6OutForwDatagrams; + val2valstr(ipfrw, buf+6, 6, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NETIPFRW = {"NETIPFRW", sysprt_NETIPFRW}; +/*******************************************************************/ +char * +sysprt_NETIPDELIV(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="deliv "; + count_t ipindel = sstat->net.ipv4.InDelivers + + sstat->net.ipv6.Ip6InDelivers; + val2valstr(ipindel, buf+6, 6, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NETIPDELIV = {"NETIPDELIV", sysprt_NETIPDELIV}; +/*******************************************************************/ +char * +sysprt_NETICMPIN(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="icmpi "; + count_t icmpin = sstat->net.icmpv4.InMsgs+ + sstat->net.icmpv6.Icmp6InMsgs; + val2valstr(icmpin , buf+6, 6, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NETICMPIN = {"NETICMPIN", sysprt_NETICMPIN}; +/*******************************************************************/ +char * +sysprt_NETICMPOUT(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="icmpo "; + count_t icmpin = sstat->net.icmpv4.OutMsgs+ + sstat->net.icmpv6.Icmp6OutMsgs; + val2valstr(icmpin , buf+6, 6, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NETICMPOUT = {"NETICMPOUT", sysprt_NETICMPOUT}; +/*******************************************************************/ +char * +sysprt_NETNAME(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + count_t busy; + count_t ival = sstat->intf.intf[as->index].rbyte/125/as->nsecs; + count_t oval = sstat->intf.intf[as->index].sbyte/125/as->nsecs; + + static char buf[16] = "ethxxxx ----"; + // 012345678901 + + *color = -1; + + if (sstat->intf.intf[as->index].speed) /* speed known? */ + { + if (sstat->intf.intf[as->index].duplex) + busy = (ival > oval ? ival : oval) / + (sstat->intf.intf[as->index].speed *10); + else + busy = (ival + oval) / + (sstat->intf.intf[as->index].speed *10); + + // especially with wireless, the speed might have dropped + // temporarily to a very low value (snapshot) + // then it might be better to take the speed of the previous + // sample + if (busy > 100 && sstat->intf.intf[as->index].speed < + sstat->intf.intf[as->index].speedp) + { + sstat->intf.intf[as->index].speed = + sstat->intf.intf[as->index].speedp; + + if (sstat->intf.intf[as->index].duplex) + busy = (ival > oval ? ival : oval) / + (sstat->intf.intf[as->index].speed *10); + else + busy = (ival + oval) / + (sstat->intf.intf[as->index].speed *10); + } + + snprintf(buf, sizeof(buf)-1, "%-7.7s %3lld%%", + sstat->intf.intf[as->index].name, busy); + + } + else + { + snprintf(buf, sizeof(buf)-1, "%-7.7s ----", + sstat->intf.intf[as->index].name); + strcpy(buf+8, "----"); + } + return buf; +} + +sys_printdef syspdef_NETNAME = {"NETNAME", sysprt_NETNAME}; +/*******************************************************************/ +char * +sysprt_NETPCKI(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="pcki "; + + *color = -1; + + val2valstr(sstat->intf.intf[as->index].rpack, + buf+5, 7, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NETPCKI = {"NETPCKI", sysprt_NETPCKI}; +/*******************************************************************/ +char * +sysprt_NETPCKO(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="pcko "; + + *color = -1; + + val2valstr(sstat->intf.intf[as->index].spack, + buf+5, 7, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NETPCKO = {"NETPCKO", sysprt_NETPCKO}; +/*******************************************************************/ +/* +** convert byte-transfers to bit-transfers (* 8) +** convert bit-transfers to kilobit-transfers (/ 1000) +** per second +*/ +char *makenetspeed(count_t val, int nsecs) +{ + char c; + static char buf[16]="si ?bps"; + // 012345678901 + + val=val/125/nsecs; // convert to Kbps + + if (val < 10000) + { + c='K'; + } + else if (val < (count_t)10000 * 1000) + { + val/=1000; + c = 'M'; + } + else if (val < (count_t)10000 * 1000 * 1000) + { + val/=1000 * 1000; + c = 'G'; + } + else + { + val = val / 1000 / 1000 / 1000; + c = 'T'; + } + + sprintf(buf+3, "%4lld %cbps", val, c); + + return buf; +} +/*******************************************************************/ +char * +sysprt_NETSPEEDMAX(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat = p; + extraparam *as = q; + static char buf[16]; + count_t speed = sstat->intf.intf[as->index].speed; + + *color = -1; + + if (speed < 10000) + { + snprintf(buf, sizeof buf, "sp %4lld Mbps", speed); + } + else + { + speed /= 1000; + snprintf(buf, sizeof buf, "sp %4lld Gbps", speed); + } + + return buf; +} + +sys_printdef syspdef_NETSPEEDMAX = {"NETSPEEDMAX", sysprt_NETSPEEDMAX}; +/*******************************************************************/ +char * +sysprt_NETSPEEDIN(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + + *color = -1; + + char *ps=makenetspeed(sstat->intf.intf[as->index].rbyte,as->nsecs); + ps[0]='s'; + ps[1]='i'; + return ps; +} + +sys_printdef syspdef_NETSPEEDIN = {"NETSPEEDIN", sysprt_NETSPEEDIN}; +/*******************************************************************/ +char * +sysprt_NETSPEEDOUT(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + + *color = -1; + + char *ps=makenetspeed(sstat->intf.intf[as->index].sbyte,as->nsecs); + ps[0]='s'; + ps[1]='o'; + return ps; +} + +sys_printdef syspdef_NETSPEEDOUT = {"NETSPEEDOUT", sysprt_NETSPEEDOUT}; +/*******************************************************************/ +char * +sysprt_NETCOLLIS(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="coll "; + val2valstr(sstat->intf.intf[as->index].scollis, + buf+5, 7, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NETCOLLIS = {"NETCOLLIS", sysprt_NETCOLLIS}; +/*******************************************************************/ +char * +sysprt_NETMULTICASTIN(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="mlti "; + val2valstr(sstat->intf.intf[as->index].rmultic, + buf+5, 7, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NETMULTICASTIN = {"NETMULTICASTIN", sysprt_NETMULTICASTIN}; +/*******************************************************************/ +char * +sysprt_NETRCVERR(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="erri "; + val2valstr(sstat->intf.intf[as->index].rerrs, + buf+5, 7, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NETRCVERR = {"NETRCVERR", sysprt_NETRCVERR}; +/*******************************************************************/ +char * +sysprt_NETSNDERR(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="erro "; + val2valstr(sstat->intf.intf[as->index].serrs, + buf+5, 7, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NETSNDERR = {"NETSNDERR", sysprt_NETSNDERR}; +/*******************************************************************/ +char * +sysprt_NETRCVDROP(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="drpi "; + val2valstr(sstat->intf.intf[as->index].rdrop, + buf+5, 7, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NETRCVDROP = {"NETRCVDROP", sysprt_NETRCVDROP}; +/*******************************************************************/ +char * +sysprt_NETSNDDROP(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="drpo "; + val2valstr(sstat->intf.intf[as->index].sdrop, + buf+5, 7, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NETSNDDROP = {"NETSNDDROP", sysprt_NETSNDDROP}; +/*******************************************************************/ +char * +sysprt_NFMSERVER(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16] = "srv "; + char mntdev[128], *ps; + + memcpy(mntdev, sstat->nfs.nfsmounts.nfsmnt[as->index].mountdev, + sizeof mntdev); + + if ( (ps = strchr(mntdev, ':')) ) // colon found? + *ps = '\0'; + else + strcpy(mntdev, "?"); + + sprintf(buf+4, "%8.8s", mntdev); + return buf; +} + +sys_printdef syspdef_NFMSERVER = {"NFMSERVER", sysprt_NFMSERVER}; +/*******************************************************************/ +char * +sysprt_NFMPATH(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]; + char mntdev[128], *ps; + int len; + + memcpy(mntdev, sstat->nfs.nfsmounts.nfsmnt[as->index].mountdev, + sizeof mntdev); + + if ( (ps = strchr(mntdev, ':')) ) // colon found? + ps++; + else + ps = mntdev; + + len = strlen(ps); + if (len > 12) + ps = ps + len - 12; + + sprintf(buf, "%12.12s", ps); + return buf; +} + +sys_printdef syspdef_NFMPATH = {"NFMPATH", sysprt_NFMPATH}; +/*******************************************************************/ +char * +sysprt_NFMTOTREAD(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="read "; + + val2memstr(sstat->nfs.nfsmounts.nfsmnt[as->index].bytestotread, + buf+6, KBFORMAT, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NFMTOTREAD = {"NFMTOTREAD", sysprt_NFMTOTREAD}; +/*******************************************************************/ +char * +sysprt_NFMTOTWRITE(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="write "; + + val2memstr(sstat->nfs.nfsmounts.nfsmnt[as->index].bytestotwrite, + buf+6, KBFORMAT, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NFMTOTWRITE = {"NFMTOTWRITE", sysprt_NFMTOTWRITE}; +/*******************************************************************/ +char * +sysprt_NFMNREAD(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="nread "; + + val2memstr(sstat->nfs.nfsmounts.nfsmnt[as->index].bytesread, + buf+6, KBFORMAT, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NFMNREAD = {"NFMNREAD", sysprt_NFMNREAD}; +/*******************************************************************/ +char * +sysprt_NFMNWRITE(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="nwrit "; + + val2memstr(sstat->nfs.nfsmounts.nfsmnt[as->index].byteswrite, + buf+6, KBFORMAT, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NFMNWRITE = {"NFMNWRITE", sysprt_NFMNWRITE}; +/*******************************************************************/ +char * +sysprt_NFMDREAD(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="dread "; + + val2memstr(sstat->nfs.nfsmounts.nfsmnt[as->index].bytesdread, + buf+6, KBFORMAT, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NFMDREAD = {"NFMDREAD", sysprt_NFMDREAD}; +/*******************************************************************/ +char * +sysprt_NFMDWRITE(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="dwrit "; + + val2memstr(sstat->nfs.nfsmounts.nfsmnt[as->index].bytesdwrite, + buf+6, KBFORMAT, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NFMDWRITE = {"NFMDWRITE", sysprt_NFMDWRITE}; +/*******************************************************************/ +char * +sysprt_NFMMREAD(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="mread "; + + val2memstr(sstat->nfs.nfsmounts.nfsmnt[as->index].pagesmread *pagesize, + buf+6, KBFORMAT, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NFMMREAD = {"NFMMREAD", sysprt_NFMMREAD}; +/*******************************************************************/ +char * +sysprt_NFMMWRITE(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="mwrit "; + + val2memstr(sstat->nfs.nfsmounts.nfsmnt[as->index].pagesmwrite *pagesize, + buf+6, KBFORMAT, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NFMMWRITE = {"NFMMWRITE", sysprt_NFMMWRITE}; +/*******************************************************************/ +char * +sysprt_NFCRPCCNT(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="rpc "; + val2valstr(sstat->nfs.client.rpccnt, + buf+4, 8, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NFCRPCCNT = {"NFCRPCCNT", sysprt_NFCRPCCNT}; +/*******************************************************************/ +char * +sysprt_NFCRPCREAD(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="read "; + val2valstr(sstat->nfs.client.rpcread, + buf+5, 7, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NFCRPCREAD = {"NFCRPCREAD", sysprt_NFCRPCREAD}; +/*******************************************************************/ +char * +sysprt_NFCRPCWRITE(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="write "; + val2valstr(sstat->nfs.client.rpcwrite, + buf+6, 6, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NFCRPCWRITE = {"NFCRPCWRITE", sysprt_NFCRPCWRITE}; +/*******************************************************************/ +char * +sysprt_NFCRPCRET(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="retxmit "; + val2valstr(sstat->nfs.client.rpcretrans, + buf+8, 4, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NFCRPCRET = {"NFCRPCRET", sysprt_NFCRPCRET}; +/*******************************************************************/ +char * +sysprt_NFCRPCARF(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="autref "; + val2valstr(sstat->nfs.client.rpcautrefresh, + buf+7, 5, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NFCRPCARF = {"NFCRPCARF", sysprt_NFCRPCARF}; +/*******************************************************************/ +char * +sysprt_NFSRPCCNT(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="rpc "; + val2valstr(sstat->nfs.server.rpccnt, + buf+4, 8, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NFSRPCCNT = {"NFSRPCCNT", sysprt_NFSRPCCNT}; +/*******************************************************************/ +char * +sysprt_NFSRPCREAD(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="cread "; + val2valstr(sstat->nfs.server.rpcread, + buf+6, 6, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NFSRPCREAD = {"NFSRPCREAD", sysprt_NFSRPCREAD}; +/*******************************************************************/ +char * +sysprt_NFSRPCWRITE(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="cwrit "; + val2valstr(sstat->nfs.server.rpcwrite, + buf+6, 6, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NFSRPCWRITE = {"NFSRPCWRITE", sysprt_NFSRPCWRITE}; +/*******************************************************************/ +char * +sysprt_NFSBADFMT(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="badfmt "; + val2valstr(sstat->nfs.server.rpcbadfmt, + buf+7, 5, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NFSBADFMT = {"NFSBADFMT", sysprt_NFSBADFMT}; +/*******************************************************************/ +char * +sysprt_NFSBADAUT(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="badaut "; + val2valstr(sstat->nfs.server.rpcbadaut, + buf+7, 5, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NFSBADAUT = {"NFSBADAUT", sysprt_NFSBADAUT}; +/*******************************************************************/ +char * +sysprt_NFSBADCLN(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="badcln "; + val2valstr(sstat->nfs.server.rpcbadcln, + buf+7, 5, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NFSBADCLN = {"NFSBADCLN", sysprt_NFSBADCLN}; +/*******************************************************************/ +char * +sysprt_NFSNETTCP(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="nettcp "; + val2valstr(sstat->nfs.server.nettcpcnt, + buf+7, 5, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NFSNETTCP = {"NFSNETTCP", sysprt_NFSNETTCP}; +/*******************************************************************/ +char * +sysprt_NFSNETUDP(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="netudp "; + val2valstr(sstat->nfs.server.netudpcnt, + buf+7, 5, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NFSNETUDP = {"NFSNETUDP", sysprt_NFSNETUDP}; +/*******************************************************************/ +char * +sysprt_NFSNRBYTES(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[32]="MBcr/s "; + + sprintf(buf+7, "%5.1lf", + sstat->nfs.server.nrbytes / 1024.0 / 1024.0 / as->nsecs); + + return buf; +} + +sys_printdef syspdef_NFSNRBYTES = {"NFSNRBYTES", sysprt_NFSNRBYTES}; +/*******************************************************************/ +char * +sysprt_NFSNWBYTES(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[32]="MBcw/s "; + + sprintf(buf+7, "%5.1lf", + sstat->nfs.server.nwbytes / 1024.0 / 1024.0 / as->nsecs); + + return buf; +} + +sys_printdef syspdef_NFSNWBYTES = {"NFSNWBYTES", sysprt_NFSNWBYTES}; +/*******************************************************************/ +char * +sysprt_NFSRCHITS(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="rchits "; + val2valstr(sstat->nfs.server.rchits, + buf+8, 4, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NFSRCHITS = {"NFSRCHITS", sysprt_NFSRCHITS}; +/*******************************************************************/ +char * +sysprt_NFSRCMISS(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="rcmiss "; + val2valstr(sstat->nfs.server.rcmiss, + buf+8, 4, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NFSRCMISS = {"NFSRCMISS", sysprt_NFSRCMISS}; +/*******************************************************************/ +char * +sysprt_NFSRCNOCA(void *p, void *q, int badness, int *color) +{ + struct sstat *sstat=p; + extraparam *as=q; + static char buf[16]="rcnoca "; + val2valstr(sstat->nfs.server.rcnoca, + buf+8, 4, as->avgval, as->nsecs); + return buf; +} + +sys_printdef syspdef_NFSRCNOCA = {"NFSRCNOCA", sysprt_NFSRCNOCA}; +/*******************************************************************/ +char * +sysprt_BLANKBOX(void *p, void *notused, int badness, int *color) +{ + return " "; +} + +sys_printdef syspdef_BLANKBOX = {"BLANKBOX", sysprt_BLANKBOX}; diff --git a/sources/various.c b/sources/various.c new file mode 100644 index 0000000..7ab64a8 --- /dev/null +++ b/sources/various.c @@ -0,0 +1,605 @@ +/* +** ATOP - System & Process Monitor +** +** The program 'atop' offers the possibility to view the activity of +** the system on system-level as well as process-level. +** +** This source-file contains various functions to a.o. format the +** time-of-day, the cpu-time consumption and the memory-occupation. +** ========================================================================== +** Author: Gerlof Langeveld +** E-mail: gerlof.langeveld@atoptool.nl +** Date: November 1996 +** LINUX-port: June 2000 +** -------------------------------------------------------------------------- +** Copyright (C) 2000-2010 Gerlof Langeveld +** +** This program is free software; you can redistribute it and/or modify it +** under the terms of the GNU General Public License as published by the +** Free Software Foundation; either version 2, or (at your option) any +** later version. +** +** This program is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +** See the GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +** -------------------------------------------------------------------------- +** +** $Log: various.c,v $ +** Revision 1.21 2010/11/12 06:16:16 gerlof +** Show all parts of timestamp in header line, even when zero. +** +** Revision 1.20 2010/05/18 19:21:08 gerlof +** Introduce CPU frequency and scaling (JC van Winkel). +** +** Revision 1.19 2010/04/28 18:21:11 gerlof +** Cast value larger than 4GB to long long. +** +** Revision 1.18 2010/04/23 12:19:35 gerlof +** Modified mail-address in header. +** +** Revision 1.17 2010/03/26 11:52:45 gerlof +** Introduced unit of Tbytes for memory-usage. +** +** Revision 1.16 2009/12/17 08:28:38 gerlof +** Express CPU-time usage in days and hours for large values. +** +** Revision 1.15 2009/12/10 08:50:39 gerlof +** Introduction of a new function to convert number of seconds +** to a string indicating days, hours, minutes and seconds. +** +** Revision 1.14 2007/02/13 10:32:47 gerlof +** Removal of external declarations. +** Removal of function getpagesz(). +** +** Revision 1.13 2006/02/07 08:27:21 gerlof +** Add possibility to show counters per second. +** Modify presentation of CPU-values. +** +** Revision 1.12 2005/10/31 12:26:09 gerlof +** Modified date-format to yyyy/mm/dd. +** +** Revision 1.11 2005/10/21 09:51:29 gerlof +** Per-user accumulation of resource consumption. +** +** Revision 1.10 2004/05/06 09:46:24 gerlof +** Ported to kernel-version 2.6. +** +** Revision 1.9 2003/07/07 09:27:46 gerlof +** Cleanup code (-Wall proof). +** +** Revision 1.8 2003/07/03 11:16:59 gerlof +** Minor bug solutions. +** +** Revision 1.7 2003/06/30 11:31:17 gerlof +** Enlarge counters to 'long long'. +** +** Revision 1.6 2003/06/24 06:22:24 gerlof +** Limit number of system resource lines. +** +** Revision 1.5 2002/08/30 07:49:09 gerlof +** Convert a hh:mm string into a number of seconds since 00:00. +** +** Revision 1.4 2002/08/27 12:08:37 gerlof +** Modified date format (from yyyy/mm/dd to mm/dd/yyyy). +** +** Revision 1.3 2002/07/24 11:14:05 gerlof +** Changed to ease porting to other UNIX-platforms. +** +** Revision 1.2 2002/07/11 09:43:36 root +** Modified HZ into sysconf(_SC_CLK_TCK). +** +** Revision 1.1 2001/10/02 10:43:36 gerlof +** Initial revision +** +*/ + +static const char rcsid[] = "$Id: various.c,v 1.21 2010/11/12 06:16:16 gerlof Exp $"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "atop.h" +#include "acctproc.h" + +/* +** Function convtime() converts a value (number of seconds since +** 1-1-1970) to an ascii-string in the format hh:mm:ss, stored in +** chartim (9 bytes long). +*/ +char * +convtime(time_t utime, char *chartim) +{ + struct tm *tt; + + tt = localtime(&utime); + + sprintf(chartim, "%02d:%02d:%02d", tt->tm_hour, tt->tm_min, tt->tm_sec); + + return chartim; +} + +/* +** Function convdate() converts a value (number of seconds since +** 1-1-1970) to an ascii-string in the format yyyy/mm/dd, stored in +** chardat (11 bytes long). +*/ +char * +convdate(time_t utime, char *chardat) +{ + struct tm *tt; + + tt = localtime(&utime); + + sprintf(chardat, "%04d/%02d/%02d", + tt->tm_year+1900, tt->tm_mon+1, tt->tm_mday); + + return chardat; +} + + +/* +** Convert a hh:mm string into a number of seconds since 00:00 +** +** Return-value: 0 - Wrong input-format +** 1 - Success +*/ +int +hhmm2secs(char *itim, unsigned int *otim) +{ + register int i; + int hours, minutes; + + /* + ** check string syntax + */ + for (i=0; *(itim+i); i++) + if ( !isdigit(*(itim+i)) && *(itim+i) != ':' ) + return(0); + + sscanf(itim, "%d:%d", &hours, &minutes); + + if ( hours < 0 || hours > 23 || minutes < 0 || minutes > 59 ) + return(0); + + *otim = (hours * 3600) + (minutes * 60); + + if (*otim >= SECSDAY) + *otim = SECSDAY-1; + + return(1); +} + + +/* +** Return number of seconds since midnight according local clock time +** +** Return-value: Number of seconds +*/ +int +daysecs(time_t itime) +{ + struct tm *tt; + + tt = localtime(&itime); + + return( (tt->tm_hour*3600) + (tt->tm_min*60) ); +} + + +/* +** Function val2valstr() converts a positive value to an ascii-string of a +** fixed number of positions; if the value does not fit, it will be formatted +** to exponent-notation (as short as possible, so not via the standard printf- +** formatters %f or %e). The offered string should have a length of width+1. +** The value might even be printed as an average for the interval-time. +*/ +char * +val2valstr(count_t value, char *strvalue, int width, int avg, int nsecs) +{ + count_t maxval, remain = 0; + int exp = 0; + char *suffix = ""; + + if (avg && nsecs) + { + value = (value + (nsecs/2)) / nsecs; /* rounded value */ + width = width - 2; /* subtract two positions for '/s' */ + suffix = "/s"; + } + + if (value < 0) // no negative value expected + { + sprintf(strvalue, "%*s%s", width, "?", suffix); + return strvalue; + } + + maxval = pow(10.0, width) - 1; + + if (value < maxval) + { + sprintf(strvalue, "%*lld%s", width, value, suffix); + } + else + { + if (width < 3) + { + /* + ** cannot avoid ignoring width + */ + sprintf(strvalue, "%lld%s", value, suffix); + } + else + { + /* + ** calculate new maximum value for the string, + ** calculating space for 'e' (exponent) + one digit + */ + width -= 2; + maxval = pow(10.0, width) - 1; + + while (value > maxval) + { + exp++; + remain = value % 10; + value /= 10; + } + + if (remain >= 5) + value++; + + sprintf(strvalue, "%*llde%d%s", + width, value, exp, suffix); + } + } + + return strvalue; +} + +#define DAYSECS (24*60*60) +#define HOURSECS (60*60) +#define MINSECS (60) + +/* +** Function val2elapstr() converts a value (number of seconds) +** to an ascii-string of up to max 13 positions in NNNdNNhNNmNNs +** stored in strvalue (at least 14 positions). +** returnvalue: number of bytes stored +*/ +int +val2elapstr(int value, char *strvalue) +{ + char *p=strvalue; + + if (value >= DAYSECS) + { + p+=sprintf(p, "%dd", value/DAYSECS); + } + + if (value >= HOURSECS) + { + p+=sprintf(p, "%dh", (value%DAYSECS)/HOURSECS); + } + + if (value >= MINSECS) + { + p+=sprintf(p, "%dm", (value%HOURSECS)/MINSECS); + } + + p+=sprintf(p, "%ds", (value%MINSECS)); + + return p-strvalue; +} + + +/* +** Function val2cpustr() converts a value (number of milliseconds) +** to an ascii-string of 6 positions in milliseconds or minute-seconds or +** hours-minutes, stored in strvalue (at least 7 positions). +*/ +#define MAXMSEC (count_t)100000 +#define MAXSEC (count_t)6000 +#define MAXMIN (count_t)6000 + +char * +val2cpustr(count_t value, char *strvalue) +{ + if (value < MAXMSEC) + { + sprintf(strvalue, "%2lld.%02llds", value/1000, value%1000/10); + } + else + { + /* + ** millisecs irrelevant; round to seconds + */ + value = (value + 500) / 1000; + + if (value < MAXSEC) + { + sprintf(strvalue, "%2lldm%02llds", value/60, value%60); + } + else + { + /* + ** seconds irrelevant; round to minutes + */ + value = (value + 30) / 60; + + if (value < MAXMIN) + { + sprintf(strvalue, "%2lldh%02lldm", + value/60, value%60); + } + else + { + /* + ** minutes irrelevant; round to hours + */ + value = (value + 30) / 60; + + sprintf(strvalue, "%2lldd%02lldh", + value/24, value%24); + } + } + } + + return strvalue; +} + +/* +** Function val2Hzstr() converts a value (in MHz) +** to an ascii-string. +** The result-string is placed in the area pointed to strvalue, +** which should be able to contain at least 8 positions. +*/ +char * +val2Hzstr(count_t value, char *strvalue) +{ + if (value < 1000) + { + sprintf(strvalue, "%4lldMHz", value); + } + else + { + double fval=value/1000.0; // fval is double in GHz + char prefix='G'; + + if (fval >= 1000.0) // prepare for the future + { + prefix='T'; + fval /= 1000.0; + } + sprintf(strvalue, "%4.2f%cHz", fval, prefix); + } + return strvalue; +} + + +/* +** Function val2memstr() converts a value (number of bytes) +** to an ascii-string in a specific format (indicated by pformat). +** The result-string is placed in the area pointed to strvalue, +** which should be able to contain at least 7 positions. +*/ +#define ONEKBYTE 1024 +#define ONEMBYTE 1048576 +#define ONEGBYTE 1073741824L +#define ONETBYTE 1099511627776LL +#define ONEPBYTE 1125899906842624LL + +#define MAXBYTE 1024 +#define MAXKBYTE ONEKBYTE*99999L +#define MAXMBYTE ONEMBYTE*999L +#define MAXGBYTE ONEGBYTE*999LL +#define MAXTBYTE ONETBYTE*999LL + +char * +val2memstr(count_t value, char *strvalue, int pformat, int avgval, int nsecs) +{ + char aformat; /* advised format */ + count_t verifyval; + char *suffix = ""; + int basewidth = 6; + + + /* + ** notice that the value can be negative, in which case the + ** modulo-value should be evaluated and an extra position should + ** be reserved for the sign + */ + if (value < 0) + verifyval = -value * 10; + else + verifyval = value; + + /* + ** verify if printed value is required per second (average) or total + */ + if (avgval && nsecs) + { + value /= nsecs; + verifyval = verifyval * 100 /nsecs; + basewidth -= 2; + suffix = "/s"; + } + + /* + ** determine which format will be used on bases of the value itself + */ + if (verifyval <= MAXBYTE) /* bytes ? */ + aformat = ANYFORMAT; + else + if (verifyval <= MAXKBYTE) /* kbytes ? */ + aformat = KBFORMAT; + else + if (verifyval <= MAXMBYTE) /* mbytes ? */ + aformat = MBFORMAT; + else + if (verifyval <= MAXGBYTE) /* gbytes ? */ + aformat = GBFORMAT; + else + if (verifyval <= MAXTBYTE)/* tbytes? */ + aformat = TBFORMAT;/* tbytes! */ + else + aformat = PBFORMAT;/* pbytes! */ + + /* + ** check if this is also the preferred format + */ + if (aformat <= pformat) + aformat = pformat; + + switch (aformat) + { + case ANYFORMAT: + sprintf(strvalue, "%*lld%s", + basewidth, value, suffix); + break; + + case KBFORMAT: + sprintf(strvalue, "%*lldK%s", + basewidth-1, value/ONEKBYTE, suffix); + break; + + case MBFORMAT: + sprintf(strvalue, "%*.1lfM%s", + basewidth-1, (double)((double)value/ONEMBYTE), suffix); + break; + + case GBFORMAT: + sprintf(strvalue, "%*.1lfG%s", + basewidth-1, (double)((double)value/ONEGBYTE), suffix); + break; + + case TBFORMAT: + sprintf(strvalue, "%*.1lfT%s", + basewidth-1, (double)((double)value/ONETBYTE), suffix); + break; + + case PBFORMAT: + sprintf(strvalue, "%*.1lfP%s", + basewidth-1, (double)((double)value/ONEPBYTE), suffix); + break; + + default: + sprintf(strvalue, "!TILT!"); + } + + return strvalue; +} + + +/* +** Function numeric() checks if the ascii-string contains +** a numeric (positive) value. +** Returns 1 (true) if so, or 0 (false). +*/ +int +numeric(char *ns) +{ + register char *s = ns; + + while (*s) + if (*s < '0' || *s > '9') + return(0); /* false */ + else + s++; + return(1); /* true */ +} + + +/* +** Function getboot() returns the boot-time of this system +** as number of jiffies since 1-1-1970. +*/ +unsigned long long +getboot(void) +{ + static unsigned long long boottime; + unsigned long long getbootlinux(long); + + if (!boottime) /* do this only once */ + boottime = getbootlinux(hertz); + + return boottime; +} + +/* +** generic pointer verification after malloc +*/ +void +ptrverify(const void *ptr, const char *errormsg, ...) +{ + va_list args; + + va_start(args, errormsg); + + if (!ptr) + { + acctswoff(); + netatop_signoff(); + + if (vis.show_end) + (vis.show_end)(); + + va_list args; + fprintf(stderr, errormsg, args); + va_end (args); + + exit(13); + } +} + +/* +** signal catcher for cleanup before exit +*/ +void +cleanstop(int exitcode) +{ + acctswoff(); + netatop_signoff(); + (vis.show_end)(); + exit(exitcode); +} + +/* +** drop the root privileges that might be obtained via setuid-bit +** +** this action may only fail with errno EPERM (normal situation when +** atop has not been started with setuid-root privs); when this +** action fails with EAGAIN or ENOMEM, atop should not continue +** without root privs being dropped... +*/ +int +droprootprivs(void) +{ + if (seteuid( getuid() ) == -1 && errno != EPERM) + return 0; /* false */ + else + return 1; /* true */ +} + +/* +** regain the root privileges that might be dropped earlier +*/ +void +regainrootprivs(void) +{ + seteuid(0); +} diff --git a/sources/version.c b/sources/version.c new file mode 100644 index 0000000..eea7113 --- /dev/null +++ b/sources/version.c @@ -0,0 +1,29 @@ +/* No manual changes in this file */ +#include +#include +#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 ", + atopversion, atopdate); + + return vers; +} + +unsigned short +getnumvers(void) +{ + int vers1, vers2; + + sscanf(atopversion, "%u.%u", &vers1, &vers2); + + return (unsigned short) ((vers1 << 8) + vers2); +} diff --git a/sources/version.h b/sources/version.h new file mode 100644 index 0000000..f298461 --- /dev/null +++ b/sources/version.h @@ -0,0 +1,2 @@ +#define ATOPVERS "2.3.0" +#define ATOPDATE "2017/03/25 09:59:59" -- 2.45.2