diff options
| author | Paul Buetow <paul@buetow.org> | 2012-04-20 22:23:41 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2012-04-20 22:23:41 +0200 |
| commit | 46b7f1cfa7ab8da5b09ae765638f68bba20b2e02 (patch) | |
| tree | 54e794fd8f41896b46a7cc9dd24df8b121874110 | |
| parent | b1fd6740d411c4960c6cc3b4bcdaa7b29d6a901f (diff) | |
re-add0.5.2
| -rw-r--r-- | Makefile | 21 | ||||
| -rw-r--r-- | debian/README | 7 | ||||
| -rw-r--r-- | debian/changelog | 150 | ||||
| -rw-r--r-- | debian/compat | 1 | ||||
| -rw-r--r-- | debian/control | 15 | ||||
| -rw-r--r-- | debian/copyright | 30 | ||||
| -rw-r--r-- | debian/files | 1 | ||||
| -rw-r--r-- | debian/loadbars.debhelper.log | 45 | ||||
| -rw-r--r-- | debian/loadbars.manpages | 1 | ||||
| -rw-r--r-- | debian/loadbars.substvars | 2 | ||||
| -rwxr-xr-x | debian/rules | 13 | ||||
| -rw-r--r-- | debian/source/format | 1 | ||||
| -rw-r--r-- | docs/bugs | 1 | ||||
| -rw-r--r-- | docs/loadbars.1 | 146 | ||||
| -rw-r--r-- | docs/loadbars.pod | 23 | ||||
| -rw-r--r-- | docs/loadbars.txt | 25 | ||||
| -rw-r--r-- | docs/wishlist | 6 | ||||
| -rw-r--r-- | fonts/font.png | bin | 0 -> 14883 bytes | |||
| -rw-r--r-- | lib/Loadbars/Config.pm | 142 | ||||
| -rw-r--r-- | lib/Loadbars/Constants.pm | 39 | ||||
| -rw-r--r-- | lib/Loadbars/HelpDispatch.pm | 350 | ||||
| -rw-r--r-- | lib/Loadbars/Main.pm | 887 | ||||
| -rw-r--r-- | lib/Loadbars/Shared.pm | 52 | ||||
| -rw-r--r-- | lib/Loadbars/Utils.pm | 41 | ||||
| -rwxr-xr-x | loadbars | 68 |
25 files changed, 2067 insertions, 0 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9f6ae13 --- /dev/null +++ b/Makefile @@ -0,0 +1,21 @@ +NAME=loadbars +all: documentation perltidy +perltidy: + find . -name \*.pm | xargs perltidy -b + perltidy -b $(NAME) + find . -name \*.bak -delete +documentation: + pod2man --release="$(NAME) $$(cut -d' ' -f2 debian/changelog | head -n 1 | sed 's/(//;s/)//')" \ + --center="User Commands" ./docs/$(NAME).pod > ./docs/$(NAME).1 + pod2text ./docs/$(NAME).pod > ./docs/$(NAME).txt +install: + test ! -d $(DESTDIR)/usr/bin && mkdir -p $(DESTDIR)/usr/bin || exit 0 + test ! -d $(DESTDIR)/usr/share/$(NAME) && mkdir -p $(DESTDIR)/usr/share/$(NAME) || exit 0 + cp $(NAME) $(DESTDIR)/usr/bin + cp -r ./lib $(DESTDIR)/usr/share/$(NAME)/lib + cp -r ./fonts $(DESTDIR)/usr/share/$(NAME)/fonts +deinstall: + test ! -z "$(DESTDIR)" && test -f $(DESTDIR)/usr/bin/$(NAME) && rm $(DESTDIR)/usr/bin/$(NAME) || exit 0 + test ! -z "$(DESTDIR)/usr/share/$(NAME)" && -d $(DESTDIR)/usr/share/$(NAME) && rm -r $(DESTDIR)/usr/share/$(NAME) || exit 0 +deb: + dpkg-buildpackage diff --git a/debian/README b/debian/README new file mode 100644 index 0000000..2279588 --- /dev/null +++ b/debian/README @@ -0,0 +1,7 @@ +The Debian Package loadbars +---------------------------- + +This is just a hobby project. Not sure if everything meets the debian +policy though. + + -- Paul Buetow <paul@buetow.org> Sun, 08 Apr 2012 15:23:53 +0200 diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..aaa6c56 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,150 @@ +loadbars (0.5.2) stable; urgency=low + + * Initial .deb + + -- Paul Buetow <paul@buetow.org> Sun, 08 Apr 2012 15:23:53 +0200 + +loadbars (0.0.0) stable; urgency=low + + * ALL CHANGES FOR PREVIOUS VERSIONS (NON DEBIAN PACKAGE) + + Thu Apr 19 21:41:52 CEST 2012 + * Minor change, sleep 0.5s instead of 3s if ssh command fails + + Fri Apr 6 10:17:30 CEST 2012 + * Minor fixes such as redraw background on toggle text display which + should fix some weird display bugs. + + Fri Mar 16 07:20:50 CET 2012 + * Release v0.5.1.1 + * Dropped FreeBSD support / focus is Linux + * On shutdown all sub-processes are gonna be terminated instantly + (was old bug). Needs Proc::ProcessTable module. + * Dont quit loadbars if ~/.loadbarsrc can not be overwritten + + Sat Feb 25 20:09:02 CET 2012 + * Release v0.5.1 + * Add config file support (~/.loadbarsrc) and it's possible to configure + any option you find in --help but without leading '--'. For comments + just use the '#' sign. Sample config: + showcores=1 # Always show cores on startup + showtext=0 # Always don't display text on startup + * Add hotkey 'w' which writes current settings to the configfile + * Remove --title option (no need anyway) + * Some code cleanups + * Some bugfixes + + Sat Feb 4 10:56:27 CET 2012 + * Release v0.5.0 + * Add stats for rudimentary memory and swap usage (--showmem option or m hotkey) + * Remove --width and --inter options + * Add --barwidth option, each bar is barwidth pixels now + * Add --maxwidth option, which represents the max total window width + * Auto disable text display if text does not fit into window (maxwidth) pixels + * Auto re-enable text display if text does fit again into window + * Key right increases window width by 100px and left decreases by 100px + * Key down increases window height by 100px and up decreases by 100px + * Set 'samples' default values from 1000 down to 500. + * Displays a text warning on stdout if computer may be too slow + * No sporadic crashes on shutdown anymore + * Some internal tweaks, no separate event thread needed anymore. This fixes + some sporadic bugs. + + Sun Jan 21 14:16:37 CET 2012 + * Released v0.4.0 + * Also show stats for idle, iowait, irq, softirq, steal and guest cpu time + * Some parameters have been renamed (see --help) + * Introduced extended mode (use --extended 1 at startup or 'e' hotkey) + * Modified the bar colors a little bit (see --help) + * Some Bugfixes + + Tue Dec 27 12:28:40 CET 2011 + * Released v0.3.1 + * --cluster option (which reads the ClusterSSH config file /etc/clusters/) + also supports clusters of clusters. e.g.: + $ cat /etc/clusters + clusterA server01 server02 + clusterB clusterA server03 + So --cluster clusterB will connect to server01 server02 and server03 + * --hosts option supports username to be specified. E.g.: + # ./loadbars --hosts user1@server01,user2@server02 + will connect to server01 using user1 and server02 with user2. + + Mon Dec 26 14:46:25 CET 2011 + * Released v0.3.0 + * Peak CPU load is not displayed by default anymore. User 'p' command or + the --togglepeak 1 startup option. + * Peak CPU load is now also displayd in text format (marked as pk) + * New option --cluster which brings rudimentary ClusterSSH config file + support. E.g. './loadbars --cluster server' reads cluster server from + the /etc/clusters file. + + Sat Nov 19 11:54:51 CET 2011 + * Released v0.2.2 + * Added a 1px horizontal line to each bar which represent the max. peak + of user and system cpu load of the last N samples (max. of the last 15 + samples by default, it can be configured using --average) + * Default value for --average has been decreased from 30 to 15 sample + values + + Fr 12. Aug 21:41:46 CEST 2011 + * Released v0.2.1 + + Di 9. Aug 20:42:43 CEST 2011 + * Released v0.2.0.2 (Bugfixes only; Bar width was wrong by 1px) + + So 7. Aug 15:53:08 CEST 2011 + * Added grey separator lines between each hosts during CPU toggle mode + * More intelligent CPU core numbering during CPU toggle mode + * FreeBSD server support for CPU graphs has been tested and is working using + linprocfs mounted on /compat/linux/proc. + * Changed licence to GPL 2 + * Some more documentation + * Some minor bugfixes + + So 7. Aug 14:06:45 CEST 2011 + * Released v0.2.0.1 (Bugfixes only) + + Sa 6. Aug 22:04:15 CEST 2011 + * Released v0.2.0 (new major version) + * No interactive CLI shell anymore but instead hotkeys for the + SDL interface (press h and see). + * Bugfixes (E.g. Loadbars does not hang anymore after typing commands) + * Major code refactoring + + Fr 5. Aug 23:52:49 CEST 2011 + * Released v0.1.3.1 + * Some more minor bugfixes + + Fr 5. Aug 23:29:19 CEST 2011 + * Released v0.1.3 + * Fixed a segfault bug on SDL::Font using threads + * Added an advanced help option (h vs. H) + * Added new toggle option: Displaying bar number vs. hostname + * Some little code refactoring + + Fr 22. Apr 13:08:08 CEST 2011 + * Released v0.1.2.1 + + Mi 20. Apr 08:37:49 CEST 2011 + * Added ./BUGS which includes a summary of all current known bugs + + Fri Jan 14 23:03:47 CET 2011 + * Released v0.1.2 + * Added 'toggle summary' option + * Removed all old screenshots + * Added a newer one + * Fixed lots of bugs (including segfaults) + * Cosmetic code fixes + + Tue Jan 11 14:01:32 CET 2011 + * Released v0.1.1 + * Extended help text ('h' command) + * Added CHANGELOG and README files + * Fixed a typo + + Tue Jan 11 13:??:?? CET 2011 + * Released v0.1.0 + * With initial font support (text display of the stats) + * Everything else which has been implemented up to 0.1-beta8-pre6 + -- Paul Buetow <paul@buetow.org> Sun, 08 Apr 2012 15:23:53 +0200 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..45a4fb7 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +8 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..84c7583 --- /dev/null +++ b/debian/control @@ -0,0 +1,15 @@ +Source: loadbars +Section: utils +Priority: optional +Maintainer: Paul Buetow <paul@buetow.org> +Build-Depends: perl, perltidy +Standards-Version: 3.9.2 +Homepage: http://loadbars.buetow.org +Vcs-Git: git://git.bueto.org/loadbars +Vcs-Browser: http://web.buetow.org/git/?p=loadbars.git;a=summary + +Package: loadbars +Architecture: all +Depends: ${perl:Depends}, libsdl-perl (>= 2.2.5-1), libproc-processtable-perl (>= 0.45-1) +Description: Real time monitoring tool + Loadbars is a tool to observe loads of several remote servers at once. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..b563119 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,30 @@ +Format: http://dep.debian.net/deps/dep5 +Upstream-Name: loadbars +Source: http://loadbars.buetow.org + +Files: * +Copyright: 2012 Paul Buetow <paul@buetow.org> +License: GPL-3.0+ + +Files: debian/* +Copyright: 2012 Paul Buetow <paul@buetow.org> +License: GPL-3.0+ + +License: GPL-3.0+ + 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 3 of the License, or + (at your option) any later version. + . + This package 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, see <http://www.gnu.org/licenses/>. + . + On Debian systems, the complete text of the GNU General + Public License version 3 can be found in "/usr/share/common-licenses/GPL-3". + + diff --git a/debian/files b/debian/files new file mode 100644 index 0000000..d1d5b07 --- /dev/null +++ b/debian/files @@ -0,0 +1 @@ +loadbars_0.5.2_all.deb utils optional diff --git a/debian/loadbars.debhelper.log b/debian/loadbars.debhelper.log new file mode 100644 index 0000000..2d06fcd --- /dev/null +++ b/debian/loadbars.debhelper.log @@ -0,0 +1,45 @@ +dh_auto_configure +dh_auto_build +dh_auto_test +dh_prep +dh_installdirs +dh_auto_install +dh_install +dh_installdocs +dh_installchangelogs +dh_installexamples +dh_installman +dh_installcatalogs +dh_installcron +dh_installdebconf +dh_installemacsen +dh_installifupdown +dh_installinfo +dh_pysupport +dh_installinit +dh_installmenu +dh_installmime +dh_installmodules +dh_installlogcheck +dh_installlogrotate +dh_installpam +dh_installppp +dh_installudev +dh_installwm +dh_installxfonts +dh_bugfiles +dh_lintian +dh_gconf +dh_icons +dh_perl +dh_usrlocal +dh_link +dh_compress +dh_fixperms +dh_strip +dh_makeshlibs +dh_shlibdeps +dh_installdeb +dh_gencontrol +dh_md5sums +dh_builddeb diff --git a/debian/loadbars.manpages b/debian/loadbars.manpages new file mode 100644 index 0000000..a3155c7 --- /dev/null +++ b/debian/loadbars.manpages @@ -0,0 +1 @@ +docs/loadbars.1 diff --git a/debian/loadbars.substvars b/debian/loadbars.substvars new file mode 100644 index 0000000..bcb0957 --- /dev/null +++ b/debian/loadbars.substvars @@ -0,0 +1,2 @@ +perl:Depends=perl +misc:Depends= diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..b760bee --- /dev/null +++ b/debian/rules @@ -0,0 +1,13 @@ +#!/usr/bin/make -f +# -*- makefile -*- +# Sample debian/rules that uses debhelper. +# This file was originally written by Joey Hess and Craig Small. +# As a special exception, when this file is copied by dh-make into a +# dh-make output file, you may use that output file without restriction. +# This special exception was added by Craig Small in version 0.37 of dh-make. + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +%: + dh $@ diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000..89ae9db --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/docs/bugs b/docs/bugs new file mode 100644 index 0000000..e169314 --- /dev/null +++ b/docs/bugs @@ -0,0 +1 @@ +No known bugs there atm diff --git a/docs/loadbars.1 b/docs/loadbars.1 new file mode 100644 index 0000000..62ed39a --- /dev/null +++ b/docs/loadbars.1 @@ -0,0 +1,146 @@ +.\" Automatically generated by Pod::Man 2.22 (Pod::Simple 3.07) +.\" +.\" Standard preamble: +.\" ======================================================================== +.de Sp \" Vertical space (when we can't use .PP) +.if t .sp .5v +.if n .sp +.. +.de Vb \" Begin verbatim text +.ft CW +.nf +.ne \\$1 +.. +.de Ve \" End verbatim text +.ft R +.fi +.. +.\" Set up some character translations and predefined strings. \*(-- will +.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left +.\" double quote, and \*(R" will give a right double quote. \*(C+ will +.\" give a nicer C++. Capital omega is used to do unbreakable dashes and +.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, +.\" nothing in troff, for use with C<>. +.tr \(*W- +.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' +.ie n \{\ +. ds -- \(*W- +. ds PI pi +. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch +. ds L" "" +. ds R" "" +. ds C` "" +. ds C' "" +'br\} +.el\{\ +. ds -- \|\(em\| +. ds PI \(*p +. ds L" `` +. ds R" '' +'br\} +.\" +.\" Escape single quotes in literal strings from groff's Unicode transform. +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" +.\" If the F register is turned on, we'll generate index entries on stderr for +.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index +.\" entries marked with X<> in POD. Of course, you'll have to process the +.\" output yourself in some meaningful fashion. +.ie \nF \{\ +. de IX +. tm Index:\\$1\t\\n%\t"\\$2" +.. +. nr % 0 +. rr F +.\} +.el \{\ +. de IX +.. +.\} +.\" +.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). +.\" Fear. Run. Save yourself. No user-serviceable parts. +. \" fudge factors for nroff and troff +.if n \{\ +. ds #H 0 +. ds #V .8m +. ds #F .3m +. ds #[ \f1 +. ds #] \fP +.\} +.if t \{\ +. ds #H ((1u-(\\\\n(.fu%2u))*.13m) +. ds #V .6m +. ds #F 0 +. ds #[ \& +. ds #] \& +.\} +. \" simple accents for nroff and troff +.if n \{\ +. ds ' \& +. ds ` \& +. ds ^ \& +. ds , \& +. ds ~ ~ +. ds / +.\} +.if t \{\ +. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" +. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' +. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' +. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' +. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' +. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' +.\} +. \" troff and (daisy-wheel) nroff accents +.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' +.ds 8 \h'\*(#H'\(*b\h'-\*(#H' +.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] +.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' +.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' +.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] +.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] +.ds ae a\h'-(\w'a'u*4/10)'e +.ds Ae A\h'-(\w'A'u*4/10)'E +. \" corrections for vroff +.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' +.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' +. \" for low resolution devices (crt and lpr) +.if \n(.H>23 .if \n(.V>19 \ +\{\ +. ds : e +. ds 8 ss +. ds o a +. ds d- d\h'-1'\(ga +. ds D- D\h'-1'\(hy +. ds th \o'bp' +. ds Th \o'LP' +. ds ae ae +. ds Ae AE +.\} +.rm #[ #] #H #V #F C +.\" ======================================================================== +.\" +.IX Title "LOADBARS 1" +.TH LOADBARS 1 "2012-04-20" "loadbars 0.5.2" "User Commands" +.\" For nroff, turn off justification. Always turn off hyphenation; it makes +.\" way too many mistakes in technical documents. +.if n .ad l +.nh +.SH "NAME" +loadbars \- Small tool to observe server loads +.SH "SYNOPSIS" +.IX Header "SYNOPSIS" +For any program help check out \-\-help on command line or 'h' during program +execution. +.SH "DESCRIPTION" +.IX Header "DESCRIPTION" +Loadbars is a small script that can be used to observe \s-1CPU\s0 loads of several remote servers at once in real time. It connects with \s-1SSH\s0 (using \s-1SSH\s0 public/private key auth) to several servers at once and vizualizes all server CPUs and memory statistics right next each other (either summarized or each core separately). Loadbars is not a tool for collecting \s-1CPU\s0 loads and drawing graphs for later analysis. However, since such tools require a significant amount of time before producing results, Loadbars lets you observe the current state immediately. Loadbars does not remember or record any load information. It just shows the current \s-1CPU\s0 usages like top or vmstat does. +.SH "LICENSE" +.IX Header "LICENSE" +See package description or project website. +.SH "AUTHOR" +.IX Header "AUTHOR" +Paul Buetow \- <http://loadbars.buetow.org> diff --git a/docs/loadbars.pod b/docs/loadbars.pod new file mode 100644 index 0000000..4a029bd --- /dev/null +++ b/docs/loadbars.pod @@ -0,0 +1,23 @@ +=head1 NAME + +loadbars - Small tool to observe server loads + +=head1 SYNOPSIS + +For any program help check out --help on command line or 'h' during program +execution. + +=head1 DESCRIPTION + +Loadbars is a small script that can be used to observe CPU loads of several remote servers at once in real time. It connects with SSH (using SSH public/private key auth) to several servers at once and vizualizes all server CPUs and memory statistics right next each other (either summarized or each core separately). Loadbars is not a tool for collecting CPU loads and drawing graphs for later analysis. However, since such tools require a significant amount of time before producing results, Loadbars lets you observe the current state immediately. Loadbars does not remember or record any load information. It just shows the current CPU usages like top or vmstat does. + + +=head1 LICENSE + +See package description or project website. + +=head1 AUTHOR + +Paul Buetow - <http://loadbars.buetow.org> + +=cut diff --git a/docs/loadbars.txt b/docs/loadbars.txt new file mode 100644 index 0000000..4791cd5 --- /dev/null +++ b/docs/loadbars.txt @@ -0,0 +1,25 @@ +NAME + loadbars - Small tool to observe server loads + +SYNOPSIS + For any program help check out --help on command line or 'h' during + program execution. + +DESCRIPTION + Loadbars is a small script that can be used to observe CPU loads of + several remote servers at once in real time. It connects with SSH (using + SSH public/private key auth) to several servers at once and vizualizes + all server CPUs and memory statistics right next each other (either + summarized or each core separately). Loadbars is not a tool for + collecting CPU loads and drawing graphs for later analysis. However, + since such tools require a significant amount of time before producing + results, Loadbars lets you observe the current state immediately. + Loadbars does not remember or record any load information. It just shows + the current CPU usages like top or vmstat does. + +LICENSE + See package description or project website. + +AUTHOR + Paul Buetow - <http://loadbars.buetow.org> + diff --git a/docs/wishlist b/docs/wishlist new file mode 100644 index 0000000..ea234ff --- /dev/null +++ b/docs/wishlist @@ -0,0 +1,6 @@ +* More stats for memory +* Stats for network +* .deb for Debian and Ubuntu +* Auto detect single core boxes +* Make code modular (script is growing...) +* Optimize code (too much cpu usage if there are too many hosts involved) diff --git a/fonts/font.png b/fonts/font.png Binary files differnew file mode 100644 index 0000000..02e9eb1 --- /dev/null +++ b/fonts/font.png diff --git a/lib/Loadbars/Config.pm b/lib/Loadbars/Config.pm new file mode 100644 index 0000000..57fad2f --- /dev/null +++ b/lib/Loadbars/Config.pm @@ -0,0 +1,142 @@ +package Loadbars::Config; + +use strict; +use warnings; + +use Loadbars::Utils; + +use Exporter; + +use base 'Exporter'; + +our @EXPORT = qw ( %C %I ); + +# Global configuration hash +our %C : shared; + +# Global configuration hash for internal settings (not configurable) +our %I : shared; + +# Setting defaults +%C = ( + average => 15, + barwidth => 35, + extended => 0, + factor => 1, + height => 230, + maxwidth => 1280, + samples => 1000, + showcores => 0, + showmem => 0, + showtext => 1, + showtexthost => 0, + sshopts => '', +); + +%I = ( + cpuregexp => 'cpu', + showtextoff => 0, +); + +sub read () { + return unless -f Loadbars::Constants->CONFFILE; + + display_info( + "Reading configuration from " . Loadbars::Constants->CONFFILE ); + open my $conffile, Loadbars::Constants->CONFFILE + or die "$!: " . Loadbars::Constants->CONFFILE . "\n"; + + while (<$conffile>) { + chomp; + s/[\t\s]*?#.*//; + + next unless length; + + my ( $key, $val ) = split '='; + + unless ( defined $val ) { + display_warn("Could not parse config line: $_"); + next; + } + + trim($key); + trim($val); + + if ( not exists $C{$key} ) { + display_warn("There is no such config key: $key, ignoring"); + + } + else { + display_info( +"Setting $key=$val, it might be overwritten by command line params." + ); + $C{$key} = $val; + } + } + + close $conffile; +} + +sub write () { + display_warn( "Overwriting config file " . Loadbars::Constants->CONFFILE ) + if -f Loadbars::Constants->CONFFILE; + + open my $conffile, '>', Loadbars::Constants->CONFFILE or do { + display_warn( "$!: " . Loadbars::Constants->CONFFILE ); + + return undef; + }; + + for ( keys %C ) { + print $conffile "$_=$C{$_}\n"; + } + + close $conffile; +} + +# Recursuve function +sub get_cluster_hosts ($;$); + +sub get_cluster_hosts ($;$) { + my ( $cluster, $recursion ) = @_; + + unless ( defined $recursion ) { + $recursion = 1; + + } + elsif ( $recursion > Loadbars::Constants->CSSH_MAX_RECURSION ) { + error( "CSSH_MAX_RECURSION reached. Infinite circle loop in " + . Loadbars::Constants->CSSH_CONFFILE + . "?" ); + } + + open my $fh, Loadbars::Constants->CSSH_CONFFILE + or error( "$!: " . Loadbars::Constants->CSSH_CONFFILE ); + my $hosts; + + while (<$fh>) { + if (/^$cluster\s*(.*)/) { + $hosts = $1; + last; + } + } + + close $fh; + + unless ( defined $hosts ) { + error( "No such cluster in " + . Loadbars::Constants->CSSH_CONFFILE + . ": $cluster" ) + unless defined $recursion; + + return ($cluster); + } + + my @hosts; + push @hosts, get_cluster_hosts $_, ( $recursion + 1 ) + for ( split /\s+/, $hosts ); + + return @hosts; +} + +1; diff --git a/lib/Loadbars/Constants.pm b/lib/Loadbars/Constants.pm new file mode 100644 index 0000000..af242bd --- /dev/null +++ b/lib/Loadbars/Constants.pm @@ -0,0 +1,39 @@ +package Loadbars::Constants; + +use strict; +use warnings; + +use SDL::Color; + +use constant { + VERSION => 'loadbars v0.5.2-devel', + COPYRIGHT => '2010-2012 (c) Paul Buetow <loadbars@mx.buetow.org>', + CONFFILE => $ENV{HOME} . '/.loadbarsrc', + CSSH_CONFFILE => '/etc/clusters', + CSSH_MAX_RECURSION => 10, + COLOR_DEPTH => 8, + BLACK => SDL::Color->new( -r => 0x00, -g => 0x00, -b => 0x00 ), + BLUE0 => SDL::Color->new( -r => 0x00, -g => 0x00, -b => 0xff ), + BLUE => SDL::Color->new( -r => 0x00, -g => 0x00, -b => 0x88 ), + GREEN => SDL::Color->new( -r => 0x00, -g => 0x90, -b => 0x00 ), + ORANGE => SDL::Color->new( -r => 0xff, -g => 0x70, -b => 0x00 ), + PURPLE => SDL::Color->new( -r => 0xa0, -g => 0x20, -b => 0xf0 ), + RED => SDL::Color->new( -r => 0xff, -g => 0x00, -b => 0x00 ), + WHITE => SDL::Color->new( -r => 0xff, -g => 0xff, -b => 0xff ), + GREY0 => SDL::Color->new( -r => 0x11, -g => 0x11, -b => 0x11 ), + GREY => SDL::Color->new( -r => 0xaa, -g => 0xaa, -b => 0xaa ), + DARK_GREY => SDL::Color->new( -r => 0x15, -g => 0x15, -b => 0x15 ), + YELLOW0 => SDL::Color->new( -r => 0xff, -g => 0xa0, -b => 0x00 ), + YELLOW => SDL::Color->new( -r => 0xff, -g => 0xc0, -b => 0x00 ), + SYSTEM_BLUE0 => 30, + USER_ORANGE => 70, + USER_YELLOW0 => 50, + INTERVAL => 0.1, + INTERVAL_WARN => 1.0, + SUCCESS => 0, + E_UNKNOWN => 1, + E_NOHOST => 2, +}; + +1; + diff --git a/lib/Loadbars/HelpDispatch.pm b/lib/Loadbars/HelpDispatch.pm new file mode 100644 index 0000000..dfee7f5 --- /dev/null +++ b/lib/Loadbars/HelpDispatch.pm @@ -0,0 +1,350 @@ +package Loadbars::HelpDispatch; + +use strict; +use warnings; + +use Loadbars::Constants; +use Loadbars::Shared; + +sub create () { + my $hosts = ''; + + my $textdesc = <<END; +CPU stuff: + st = Steal in % [see man proc] (extended) + Color: Red + gt = Guest in % [see man proc] (extended) + Color: Red + sr = Soft IRQ usage in % (extended) + Color: White + ir = IRQ usage in % (extended) + Color: White + io = IOwait cpu sage in % + Color: Purple + id = Idle cpu usage in % (extended) + Color: Black + ni = Nice cpu usage in % + Color: Green + us = User cpu usage in % + Color: Yellow, dark yellow if to>50%, orange if to>50% + sy = System cpu sage in % + Blue, lighter blue if >30% + to = Total CPU usage, which is (100% - id) + pk = Max us+sy peak of last avg. samples (extended) + avg = System load average; desc. order: 1, 5 and 15 min. avg. + 1px horizontal line: Maximum sy+us+io of last 'avg' samples (extended) + Extended means: text display only if extended mode is turned on +Memory stuff: + Ram: System ram usage in % + Color: Dark grey + Swp: System swap usage in % + Color: Grey +Config file support: + Loadbars tries to read ~/.loadbarsrc and it's possible to configure any + option you find in --help but without leading '--'. For comments just use + the '#' sign. Sample config: + showcores=1 # Always show cores on startup + showtext=0 # Always don't display text on startup + extended=1 # Always use extended mode on startup + will always show all CPU cores in extended mode but no text display. +Examples: + loadbars --extended 1 --showcores 1 --height 300 --hosts localhost + loadbars --hosts localhost,server1.example.com,server2.example.com + loadbars --cluster foocluster (foocluster is in /etc/clusters [ClusterSSH]) +END + + # mode 1: Option is shown in the online help menu (stdout not sdl) + # mode 2: Option is shown in the 'usage' screen from the command line + # mode 4: Option is used to generate the GetOptions parameters for Getopt::Long + # Combinations: Like chmod(1) + + my %d = ( + average => { + menupos => 3, + help => 'Num of samples for avg. (more fluent animations)', + mode => 6, + type => 'i' + }, + average_hot_up => { + menupos => 4, + cmd => 'a', + help => 'Increases number of samples for calculating avg. by 1', + mode => 1 + }, + average_hot_dn => { + menupos => 5, + cmd => 'y', + help => 'Decreases number of samples for calculating avg. by 1', + mode => 1 + }, + + barwidth => { + menupos => 5, + help => 'Set bar width', + mode => 6, + type => 'i' + }, + windowwidth_hot_up => { + menupos => 90, + help => 'Increase window width by 100px', + cmd => 'right', + mode => 1, + }, + windowwidth_hot_dn => { + menupos => 91, + help => 'Decrease window width by 100px', + cmd => 'left', + mode => 1, + }, + windowheight_hot_up => { + menupos => 92, + help => 'Increase window height by 100px', + cmd => 'down', + mode => 1, + }, + windowheight_hot_dn => { + menupos => 93, + help => 'Decrease window height by 100px', + cmd => 'up', + mode => 1, + }, + + cluster => { + menupos => 6, + help => 'Cluster name from /etc/clusters', + var => \$C{cluster}, + mode => 6, + type => 's' + }, + configuration => { + menupos => 6, + cmd => 'c', + help => 'Show current configuration', + mode => 4 + }, + + extended => { + menupos => 6, + help => 'Toggle extended display (0 or 1)', + mode => 7, + type => 'i' + }, + extended_hot => { + menupos => 23, + cmd => 'e', + help => 'Toggle extended mode', + mode => 1 + }, + + factor => { + menupos => 7, + help => 'Set graph scale factor (1.0 means 100%)', + mode => 6, + type => 's' + }, + factor_hot_up => { + menupos => 8, + cmd => 's', + help => 'Increases graph scale factor by 0.1', + mode => 1 + }, + factor_hot_dn => { + menupos => 9, + cmd => 'x', + help => 'Decreases graph scale factor by 0.1', + mode => 1 + }, + + height => { + menupos => 10, + help => 'Set windows height', + mode => 6, + type => 'i' + }, + + help_hot => { + menupos => 11, + cmd => 'h', + help => 'Prints this help screen', + mode => 1 + }, + + hosts => { + menupos => 12, + help => + 'Comma sep. list of hosts; optional: user@ in front to each host', + var => \$hosts, + mode => 6, + type => 's' + }, + + maxwidth => { + menupos => 16, + help => 'Set max width', + mode => 6, + type => 'i' + }, + + quit_hot => { menupos => 16, cmd => 'q', help => 'Quits', mode => 1 }, + writeconfig_hot => { + menupos => 16, + cmd => 'w', + help => 'Write config to config file', + mode => 1 + }, + + samples => { + menupos => 17, + help => 'Set number of samples until ssh reconnects', + mode => 6, + type => 'i' + }, + + showcores => { + menupos => 17, + help => 'Toggle core display (0 or 1)', + mode => 7, + type => 'i' + }, + showcores_hot => + { menupos => 17, cmd => '1', help => 'Toggle show cores', mode => 1 }, + + showmem => { + menupos => 17, + help => 'Toggle mem display (0 or 1)', + mode => 7, + type => 'i' + }, + showmem_hot => + { menupos => 17, cmd => 'm', help => 'Toggle show mem', mode => 1 }, + + showtexthost => { + menupos => 18, + help => 'Toggle hostname/num text display (0 or 1)', + mode => 7, + type => 'i' + }, + showtexthost_hot => { + menupos => 18, + cmd => 'u', + help => 'Toggle hostname/num text display', + mode => 1 + }, + + showtext => { + menupos => 19, + help => 'Toggle text display (0 or 1)', + mode => 7, + type => 'i' + }, + showtext_hot => { + menupos => 19, + cmd => 't', + help => 'Toggle text display', + mode => 1 + }, + + sshopts => + { menupos => 20, help => 'Set SSH options', mode => 6, type => 's' }, + ); + + my %d_by_short = map { + $d{$_}{cmd} => $d{$_} + + } grep { + exists $d{$_}{cmd} + + } keys %d; + + my $closure = sub ($;$) { + my ( $arg, @rest ) = @_; + + if ( $arg eq 'command' ) { + my ( $cmd, @args ) = @rest; + + my $cb = $d{$cmd}; + $cb = $d_by_short{$cmd} unless defined $cb; + + unless ( defined $cb ) { + system $cmd; + return 0; + } + + if ( length $cmd == 1 ) { + for my $key ( grep { exists $d{$_}{cmd} } keys %d ) { + do { $cmd = $key; last } if $d{$key}{cmd} eq $cmd; + } + } + + } + elsif ( $arg eq 'hotkeys' ) { + $textdesc . "Hotkeys:\n" . ( + join "\n", + map { + "$_\t- $d_by_short{$_}{help}" + + } grep { + $d_by_short{$_}{mode} & 1 and exists $d_by_short{$_}{help}; + + } sort { $d_by_short{$a}{menupos} <=> $d_by_short{$b}{menupos} } + sort keys %d_by_short + ); + + } + elsif ( $arg eq 'usage' ) { + $textdesc . ( + join "\n", + map { + if ( $_ eq 'help' ) + { + "--$_\t\t- $d{$_}{help}"; + } + else { + "--$_ <ARG>\t- $d{$_}{help}"; + } + + } grep { + $d{$_}{mode} & 2 + and exists $d{$_}{help} + + } sort { $d{$a}{menupos} <=> $d{$b}{menupos} } sort keys %d + ); + + } + elsif ( $arg eq 'options' ) { + map { + "$_=" + . $d{$_}{type} => ( + defined $d{$_}{var} + ? $d{$_}{var} + : \$C{$_} + ); + + } grep { + $d{$_}{mode} & 4 and exists $d{$_}{type}; + + } sort keys %d; + } + }; + + $d{configuration}{cb} = sub { + Loadbars::Main::say sort map { + "$_->[0] = $_->[1]" + + } grep { + defined $_->[1] + + } map { + [ + $_ => exists $d{$_}{var} + ? ${ $d{$_}{var} } + : $C{$_} + ] + + } keys %d; + }; + + return ( \$hosts, $closure ); +} + +1; diff --git a/lib/Loadbars/Main.pm b/lib/Loadbars/Main.pm new file mode 100644 index 0000000..5b8f161 --- /dev/null +++ b/lib/Loadbars/Main.pm @@ -0,0 +1,887 @@ + +package Loadbars::Main; + +use strict; +use warnings; + +use SDL; +use SDL::App; +use SDL::Rect; +use SDL::Event; + +use SDL::Surface; +use SDL::Font; + +use Time::HiRes qw(usleep gettimeofday); + +use Proc::ProcessTable; + +use threads; +use threads::shared; + +use Loadbars::Config; +use Loadbars::Constants; +use Loadbars::Shared; +use Loadbars::Utils; + +$| = 1; + +sub set_showcores_regexp () { + $I{cpuregexp} = $C{showcores} ? 'cpu' : 'cpu '; +} + +sub percentage ($$) { + my ( $total, $part ) = @_; + + return int( null($part) / notnull( null($total) / 100 ) ); +} + +sub norm ($) { + my $n = shift; + + return $n if $C{factor} != 1; + return $n > 100 ? 100 : ( $n < 0 ? 0 : $n ); +} + +sub parse_cpu_line ($) { + my $line = shift; + my ( $name, %load ); + + ( $name, @load{qw(user nice system idle iowait irq softirq steal guest)} ) = + split ' ', $line; + + # Not all kernels support this + $load{steal} = 0 unless defined $load{steal}; + $load{guest} = 0 unless defined $load{guest}; + + $load{TOTAL} = + sum( @load{qw(user nice system idle iowait irq softirq steal guest)} ); + + return ( $name, \%load ); +} + +sub terminate_pids (@) { + my @threads = @_; + + display_info 'Terminating sub-processes, hasta la vista!'; + $_->kill('TERM') for @threads; + display_info_no_nl 'Terminating PIDs'; + for my $pid ( keys %PIDS ) { + my $proc_table = Proc::ProcessTable->new(); + for my $proc ( @{ $proc_table->table() } ) { + if ( $proc->ppid == $pid ) { + print $proc->pid . ' '; + kill 'TERM', $proc->pid if $proc->ppid == $pid; + } + } + + print $pid . ' '; + kill 'TERM', $pid; + } + + say ''; + + display_info 'Terminating done. I\'ll be back!'; +} + +sub stats_thread ($;$) { + my ( $host, $user ) = @_; + $user = defined $user ? "-l $user" : ''; + + my ( $sigusr1, $sigterm ) = ( 0, 0 ); + my $loadavgexp = qr/(\d+\.\d{2}) (\d+\.\d{2}) (\d+\.\d{2})/; + my $inter = Loadbars::Constants->INTERVAL; + + until ($sigterm) { + my $bash = <<"BASH"; + loadavg=/proc/loadavg + stat=/proc/stat + meminfo=/proc/meminfo + + for i in \$(seq $C{samples}); do + echo CPUSTATS + cat \$loadavg \$stat + echo MEMSTATS + cat \$meminfo + sleep $inter + done +BASH + + my $cmd = + ( $host eq 'localhost' || $host eq '127.0.0.1' ) + ? $bash + : "ssh $user -o StrictHostKeyChecking=no $C{sshopts} $host '$bash'"; + + my $pid = open my $pipe, "$cmd |" or do { + say "Warning: $!"; + sleep 0.5; + next; + }; + + $PIDS{$pid} = 1; + + # Toggle CPUs + $SIG{USR1} = sub { $sigusr1 = 1 }; + $SIG{TERM} = sub { $sigterm = 1 }; + + my $cpuregexp = qr/$I{cpuregexp}/; + + # 1=cpu, 2=mem, 3=net + my $mode = 0; + + while (<$pipe>) { + chomp; + + if ( $mode == 0 ) { + if ( $_ eq 'MEMSTATS' ) { + $mode = 1; + + } + elsif (/^$loadavgexp/) { + $AVGSTATS{$host} = "$1;$2;$3"; + + } + elsif (/$cpuregexp/) { + my ( $name, $load ) = parse_cpu_line $_; + $CPUSTATS{"$host;$name"} = join ';', + map { $_ . '=' . $load->{$_} } + grep { defined $load->{$_} } keys %$load; + } + } + elsif ( $mode == 1 ) { + if ( $_ eq 'CPUSTATS' ) { + $mode = 0; + + } + else { + for my $meminfo ( + qw(MemTotal MemFree Buffers Cached SwapTotal SwapFree)) + { + + # TODO: Precompile regexp + if (/^$meminfo: *(\d+)/) { + $MEMSTATS_HAS{$host} = 1; + $MEMSTATS{"$host;$meminfo"} = $1; + } + } + } + } + + if ($sigusr1) { + + # TODO: Use index instead of regexp for cpuregexp + $cpuregexp = qr/$I{cpuregexp}/; + $sigusr1 = 0; + + } + elsif ($sigterm) { + close $pipe; + last; + } + } + + delete $PIDS{$pid}; + } + + return undef; +} + +sub get_rect ($$) { + my ( $rects, $name ) = @_; + + return $rects->{$name} if exists $rects->{$name}; + return $rects->{$name} = SDL::Rect->new(); +} + +sub normalize_loads (%) { + my %loads = @_; + + return %loads unless exists $loads{TOTAL}; + + my $total = $loads{TOTAL} == 0 ? 1 : $loads{TOTAL}; + return map { $_ => $loads{$_} / ( $total / 100 ) } keys %loads; +} + +sub get_cpuaverage ($@) { + my ( $factor, @loads ) = @_; + my ( %cpumax, %cpuaverage ); + + for my $l (@loads) { + for ( keys %$l ) { + $cpuaverage{$_} += $l->{$_}; + + $cpumax{$_} = $l->{$_} + if not exists $cpumax{$_} + or $cpumax{$_} < $l->{$_}; + } + } + + my $div = @loads / $factor; + + for ( keys %cpuaverage ) { + $cpuaverage{$_} /= $div; + $cpumax{$_} /= $factor; + } + + return ( \%cpumax, \%cpuaverage ); +} + +sub draw_background ($$) { + my ( $app, $rects ) = @_; + my $rect = get_rect $rects, 'background'; + + $rect->width( $C{width} ); + $rect->height( $C{height} ); + $app->fill( $rect, Loadbars::Constants->BLACK ); + $app->update($rect); + + return undef; +} + +sub create_threads (@) { + return map { $_->detach(); $_ } + map { threads->create( 'stats_thread', split ':' ) } @_; +} + +sub auto_off_text ($) { + my ($barwidth) = @_; + + if ( $barwidth < $C{barwidth} - 1 && $I{showtextoff} == 0 ) { + return unless $C{showtext}; + display_warn +'Disabling text display, text does not fit into window. Use \'t\' to re-enable.'; + $I{showtextoff} = 1; + $C{showtext} = 0; + + } + elsif ( $I{showtextoff} == 1 && $barwidth >= $C{barwidth} - 1 ) { + display_info 'Re-enabling text display, text fits into window now.'; + $C{showtext} = 1; + $I{showtextoff} = 0; + } + + return undef; +} + +sub set_dimensions ($$) { + my ( $width, $height ) = @_; + my $display_info = 0; + + if ( $width < 1 ) { + $C{width} = 1 if $C{width} != 1; + + } + elsif ( $width > $C{maxwidth} ) { + $C{width} = $C{maxwidth} if $C{width} != $C{maxwidth}; + + } + elsif ( $C{width} != $width ) { + $C{width} = $width; + } + + if ( $height < 1 ) { + $C{height} = 1 if $C{height} != 1; + + } + elsif ( $C{height} != $height ) { + $C{height} = $height; + } +} + +sub loop ($@) { + my ( $dispatch, @threads ) = @_; + + my $num_stats = 1; + $C{width} = $C{barwidth}; + + my $app = SDL::App->new( + -title => Loadbars::Constants->VERSION + . ' (press h for help on stdout)', + -icon_title => Loadbars::Constants->VERSION, + -width => $C{width}, + -height => $C{height}, + -depth => Loadbars::Constants->COLOR_DEPTH, + -resizeable => 1, + ); + + my $font = do { + my $fontbase = 'fonts/font.png'; + + if ( -f "./$fontbase" ) { + "./$fontbase"; + } + elsif ( -f "/usr/share/loadbars/$fontbase" ) { + "/usr/share/loadbars/$fontbase"; + } + }; + + SDL::Font->new($font)->use(); + + my $rects = {}; + my %prev_stats; + my %last_loads; + + my $redraw_background = 0; + my $font_height = 14; + + my $infotxt : shared = ''; + my $quit : shared = 0; + my $resize_window : shared = 0; + my %newsize : shared; + my $event = SDL::Event->new(); + + my ( $t1, $t2 ) = ( Time::HiRes::time(), undef ); + + # Closure for event handling + my $event_handler = sub { + + # While there are events to poll, poll them all! + while ( $event->poll() == 1 ) { + next if $event->type() != 2; + my $key_name = $event->key_name(); + + if ( $key_name eq '1' ) { + $C{showcores} = !$C{showcores}; + set_showcores_regexp; + $_->kill('USR1') for @threads; + %AVGSTATS = (); + %CPUSTATS = (); + $redraw_background = 1; + display_info 'Toggled CPUs'; + + } + elsif ( $key_name eq 'e' ) { + $C{extended} = !$C{extended}; + $redraw_background = 1; + display_info 'Toggled extended display'; + + } + elsif ( $key_name eq 'h' ) { + say '=> Hotkeys to use in the SDL interface'; + say $dispatch->('hotkeys'); + display_info 'Hotkeys help printed on terminal stdout'; + + } + elsif ( $key_name eq 'm' ) { + $C{showmem} = !$C{showmem}; + display_info 'Toggled show mem'; + + } + elsif ( $key_name eq 't' ) { + $C{showtext} = !$C{showtext}; + $redraw_background = 1; + display_info 'Toggled text display'; + + } + elsif ( $key_name eq 'u' ) { + $C{showtexthost} = !$C{showtexthost}; + $redraw_background = 1; + display_info 'Toggled number/hostname display'; + + } + elsif ( $key_name eq 'q' ) { + terminate_pids @threads; + $quit = 1; + return; + + } + elsif ( $key_name eq 'w' ) { + Loadbars::Config::write; + + } + elsif ( $key_name eq 'a' ) { + ++$C{average}; + display_info "Set sample average to $C{average}"; + } + elsif ( $key_name eq 'y' or $key_name eq 'z' ) { + my $avg = $C{average}; + --$avg; + $C{average} = $avg > 1 ? $avg : 2; + display_info "Set sample average to $C{average}"; + + } + elsif ( $key_name eq 's' ) { + $C{factor} += 0.1; + display_info "Set scale factor to $C{factor}"; + } + elsif ( $key_name eq 'x' or $key_name eq 'z' ) { + $C{factor} -= 0.1; + display_info "Set scale factor to $C{factor}"; + + } + elsif ( $key_name eq 'left' ) { + $newsize{width} = $C{width} - 100; + $newsize{height} = $C{height}; + $resize_window = 1; + } + elsif ( $key_name eq 'right' ) { + $newsize{width} = $C{width} + 100; + $newsize{height} = $C{height}; + $resize_window = 1; + + } + elsif ( $key_name eq 'up' ) { + $newsize{width} = $C{width}; + $newsize{height} = $C{height} - 100; + $resize_window = 1; + } + elsif ( $key_name eq 'down' ) { + $newsize{width} = $C{width}; + $newsize{height} = $C{height} + 100; + $resize_window = 1; + } + } + }; + + do { + my ( $x, $y ) = ( 0, 0 ); + + # Also substract 1 (each bar is followed by an 1px separator bar) + my $width = $C{width} / notnull($num_stats) - 1; + + my ( $current_barnum, $current_corenum ) = ( -1, -1 ); + + for my $key ( sort keys %CPUSTATS ) { + last if ( ++$current_barnum > $num_stats ); + ++$current_corenum; + my ( $host, $name ) = split ';', $key; + + next unless defined $CPUSTATS{$key}; + + my %stat = map { + my ( $k, $v ) = split '='; + $k => $v + + } split ';', $CPUSTATS{$key}; + + unless ( exists $prev_stats{$key} ) { + $prev_stats{$key} = \%stat; + next; + } + + my $prev_stat = $prev_stats{$key}; + my %loads = + null $stat{TOTAL} == null $prev_stat->{TOTAL} + ? %stat + : map { $_ => $stat{$_} - $prev_stat->{$_} } keys %stat; + + $prev_stats{$key} = \%stat; + + %loads = normalize_loads %loads; + push @{ $last_loads{$key} }, \%loads; + shift @{ $last_loads{$key} } + while @{ $last_loads{$key} } >= $C{average}; + + my ( $cpumax, $cpuaverage ) = get_cpuaverage $C{factor}, + @{ $last_loads{$key} }; + + my %heights = map { + $_ => defined $cpuaverage->{$_} + ? $cpuaverage->{$_} * ( $C{height} / 100 ) + : 1 + } keys %$cpuaverage; + + my $is_host_summary = $name eq 'cpu' ? 1 : 0; + + my $rect_separator = undef; + + my $rect_idle = get_rect $rects, "$key;idle"; + my $rect_steal = get_rect $rects, "$key;steal"; + my $rect_guest = get_rect $rects, "$key;guest"; + my $rect_irq = get_rect $rects, "$key;irq"; + my $rect_softirq = get_rect $rects, "$key;softirq"; + my $rect_nice = get_rect $rects, "$key;nice"; + my $rect_iowait = get_rect $rects, "$key;iowait"; + my $rect_user = get_rect $rects, "$key;user"; + my $rect_system = get_rect $rects, "$key;system"; + + my $rect_peak; + + $y = $C{height} - $heights{system}; + $rect_system->width($width); + $rect_system->height( $heights{system} ); + $rect_system->x($x); + $rect_system->y($y); + + $y -= $heights{user}; + $rect_user->width($width); + $rect_user->height( $heights{user} ); + $rect_user->x($x); + $rect_user->y($y); + + $y -= $heights{nice}; + $rect_nice->width($width); + $rect_nice->height( $heights{nice} ); + $rect_nice->x($x); + $rect_nice->y($y); + + $y -= $heights{idle}; + $rect_idle->width($width); + $rect_idle->height( $heights{idle} ); + $rect_idle->x($x); + $rect_idle->y($y); + + $y -= $heights{iowait}; + $rect_iowait->width($width); + $rect_iowait->height( $heights{iowait} ); + $rect_iowait->x($x); + $rect_iowait->y($y); + + $y -= $heights{irq}; + $rect_irq->width($width); + $rect_irq->height( $heights{irq} ); + $rect_irq->x($x); + $rect_irq->y($y); + + $y -= $heights{softirq}; + $rect_softirq->width($width); + $rect_softirq->height( $heights{softirq} ); + $rect_softirq->x($x); + $rect_softirq->y($y); + + $y -= $heights{guest}; + $rect_guest->width($width); + $rect_guest->height( $heights{guest} ); + $rect_guest->x($x); + $rect_guest->y($y); + + $y -= $heights{steal}; + $rect_steal->width($width); + $rect_steal->height( $heights{steal} ); + $rect_steal->x($x); + $rect_steal->y($y); + + my $all = 100 - $cpuaverage->{idle}; + my $max_all = 0; + + $app->fill( $rect_idle, Loadbars::Constants->BLACK ); + $app->fill( $rect_steal, Loadbars::Constants->RED ); + $app->fill( $rect_guest, Loadbars::Constants->RED ); + $app->fill( $rect_irq, Loadbars::Constants->WHITE ); + $app->fill( $rect_softirq, Loadbars::Constants->WHITE ); + $app->fill( $rect_nice, Loadbars::Constants->GREEN ); + $app->fill( $rect_iowait, Loadbars::Constants->PURPLE ); + + my $add_x = 0; + my $rect_memused = get_rect $rects, "$host;memused"; + my $rect_memfree = get_rect $rects, "$host;memfree"; + my $rect_buffers = get_rect $rects, "$host;buffers"; + my $rect_cached = get_rect $rects, "$host;cached"; + my $rect_swapused = get_rect $rects, "$host;swapused"; + my $rect_swapfree = get_rect $rects, "$host;swapfree"; + + my %meminfo; + if ($is_host_summary) { + if ( $C{showmem} ) { + $add_x = $width + 1; + + my $ram_per = percentage $MEMSTATS{"$host;MemTotal"}, + $MEMSTATS{"$host;MemFree"}; + my $swap_per = percentage $MEMSTATS{"$host;SwapTotal"}, + $MEMSTATS{"$host;SwapFree"}; + + %meminfo = ( + ram_per => $ram_per, + swap_per => $swap_per, + ); + + my %heights = ( + MemFree => $ram_per * ( $C{height} / 100 ), + MemUsed => ( 100 - $ram_per ) * ( $C{height} / 100 ), + SwapFree => $swap_per * ( $C{height} / 100 ), + SwapUsed => ( 100 - $swap_per ) * ( $C{height} / 100 ), + ); + + my $half_width = $width / 2; + $y = $C{height} - $heights{MemUsed}; + $rect_memused->width($half_width); + $rect_memused->height( $heights{MemUsed} ); + $rect_memused->x( $x + $add_x ); + $rect_memused->y($y); + + $y -= $heights{MemFree}; + $rect_memfree->width($half_width); + $rect_memfree->height( $heights{MemFree} ); + $rect_memfree->x( $x + $add_x ); + $rect_memfree->y($y); + + $y = $C{height} - $heights{SwapUsed}; + $rect_swapused->width($half_width); + $rect_swapused->height( $heights{SwapUsed} ); + $rect_swapused->x( $x + $add_x + $half_width ); + $rect_swapused->y($y); + + $y -= $heights{SwapFree}; + $rect_swapfree->width($half_width); + $rect_swapfree->height( $heights{SwapFree} ); + $rect_swapfree->x( $x + $add_x + $half_width ); + $rect_swapfree->y($y); + + $app->fill( $rect_memused, Loadbars::Constants->DARK_GREY ); + $app->fill( $rect_memfree, Loadbars::Constants->BLACK ); + + $app->fill( $rect_swapused, Loadbars::Constants->GREY ); + $app->fill( $rect_swapfree, Loadbars::Constants->BLACK ); + } + + if ( $C{showcores} ) { + $current_corenum = 0; + $rect_separator = get_rect $rects, "$key;separator"; + $rect_separator->width(1); + $rect_separator->height( $C{height} ); + $rect_separator->x( $x - 1 ); + $rect_separator->y(0); + $app->fill( $rect_separator, Loadbars::Constants->GREY ); + } + } + + if ( $C{extended} ) { + my %maxheights = map { + $_ => defined $cpumax->{$_} + ? $cpumax->{$_} * ( $C{height} / 100 ) + : 1 + } keys %$cpumax; + + $rect_peak = get_rect $rects, "$key;max"; + $rect_peak->width($width); + $rect_peak->height(1); + $rect_peak->x($x); + $rect_peak->y( + $C{height} - $maxheights{system} - $maxheights{user} ); + + $max_all = + sum @{$cpumax} + {qw(user system iowait irq softirq steal guest)}; + + $app->fill( + $rect_peak, + $max_all > Loadbars::Constants->USER_ORANGE + ? Loadbars::Constants->ORANGE + : ( + $max_all > Loadbars::Constants->USER_YELLOW0 + ? Loadbars::Constants->YELLOW0 + : ( Loadbars::Constants->YELLOW ) + ) + ); + } + + $app->fill( + $rect_user, + $all > Loadbars::Constants->USER_ORANGE + ? Loadbars::Constants->ORANGE + : ( + $all > Loadbars::Constants->USER_YELLOW0 + ? Loadbars::Constants->YELLOW0 + : ( Loadbars::Constants->YELLOW ) + ) + ); + $app->fill( $rect_system, + $cpuaverage->{system} > Loadbars::Constants->SYSTEM_BLUE0 + ? Loadbars::Constants->BLUE0 + : Loadbars::Constants->BLUE ); + + my ( $y, $space ) = ( 5, $font_height ); + + my @loadavg = split ';', $AVGSTATS{$host}; + + if ( $C{showtext} ) { + if ( $C{showmem} && $is_host_summary ) { + my $y_ = $y; + $app->print( $x + $add_x, $y_, 'Ram:' ); + $app->print( + $x + $add_x, + $y_ += $space, + sprintf '%02d', + ( 100 - $meminfo{ram_per} ) + ); + $app->print( $x + $add_x, $y_ += $space, 'Swp:' ); + $app->print( + $x + $add_x, + $y_ += $space, + sprintf '%02d', + ( 100 - $meminfo{swap_per} ) + ); + } + if ( $C{showtexthost} && $is_host_summary ) { + + # If hostname is printed don't use FQDN + # because of its length. + $host =~ /([^\.]*)/; + $app->print( $x, $y, sprintf '%s:', $1 ); + + } + else { + $app->print( $x, $y, sprintf '%i:', + $C{showcores} + ? $current_corenum + : $current_barnum + 1 ); + } + + if ( $C{extended} ) { + $app->print( + $x, + $y += $space, + sprintf '%02d%s', + norm $cpuaverage->{steal}, 'st' + ); + $app->print( + $x, + $y += $space, + sprintf '%02d%s', + norm $cpuaverage->{guest}, 'gt' + ); + $app->print( + $x, + $y += $space, + sprintf '%02d%s', + norm $cpuaverage->{softirq}, 'sr' + ); + $app->print( + $x, + $y += $space, + sprintf '%02d%s', + norm $cpuaverage->{irq}, 'ir' + ); + } + + $app->print( + $x, + $y += $space, + sprintf '%02d%s', + norm $cpuaverage->{iowait}, 'io' + ); + + $app->print( + $x, + $y += $space, + sprintf '%02d%s', + norm $cpuaverage->{idle}, 'id' + ) if $C{extended}; + + $app->print( + $x, + $y += $space, + sprintf '%02d%s', + norm $cpuaverage->{nice}, 'ni' + ); + $app->print( + $x, + $y += $space, + sprintf '%02d%s', + norm $cpuaverage->{user}, 'us' + ); + $app->print( + $x, + $y += $space, + sprintf '%02d%s', + norm $cpuaverage->{system}, 'sy' + ); + $app->print( + $x, + $y += $space, + sprintf '%02d%s', + norm $all, 'to' + ); + + $app->print( + $x, + $y += $space, + sprintf '%02d%s', + norm $max_all, 'pk' + ) if $C{extended}; + + if ($is_host_summary) { + if ( defined $loadavg[0] ) { + $app->print( $x, $y += $space, 'Avg:' ); + $app->print( + $x, + $y += $space, + sprintf "%.2f", + $loadavg[0] + ); + $app->print( + $x, + $y += $space, + sprintf "%.2f", + $loadavg[1] + ); + $app->print( + $x, + $y += $space, + sprintf "%.2f", + $loadavg[2] + ); + } + } + } + + $app->update( + $rect_idle, $rect_iowait, $rect_irq, + $rect_nice, $rect_softirq, $rect_steal, + $rect_guest, $rect_system, $rect_user, + ); + + $app->update( + $rect_memfree, $rect_memused, + $rect_swapused, $rect_swapfree + ) if $C{showmem}; + $app->update($rect_separator) if defined $rect_separator; + + $x += $width + 1 + $add_x; + + } + + TIMEKEEPER: + $t2 = Time::HiRes::time(); + my $t_diff = $t2 - $t1; + + if ( Loadbars::Constants->INTERVAL > $t_diff ) { + usleep 10000; + + # Goto is OK as long you don't produce spaghetti code + goto TIMEKEEPER; + + } + elsif ( Loadbars::Constants->INTERVAL_WARN < $t_diff ) { + display_warn +"WARN: Loop is behind $t_diff seconds, your computer may be too slow"; + } + + $t1 = $t2; + $event_handler->(); + + my $new_num_stats = keys %CPUSTATS; + $new_num_stats += keys %MEMSTATS_HAS if $C{showmem}; + + if ( $new_num_stats != $num_stats ) { + %prev_stats = (); + %last_loads = (); + + $num_stats = $new_num_stats; + $newsize{width} = $C{barwidth} * $num_stats; + $newsize{height} = $C{height}; + $resize_window = 1; + } + + if ($resize_window) { + set_dimensions $newsize{width}, $newsize{height}; + $app->resize( $C{width}, $C{height} ); + $resize_window = 0; + $redraw_background = 1; + } + + if ($redraw_background) { + draw_background $app, $rects; + $redraw_background = 0; + } + + auto_off_text $width; + + } until $quit; + + say "Good bye"; + + exit Loadbars::Constants->SUCCESS; +} + +1; diff --git a/lib/Loadbars/Shared.pm b/lib/Loadbars/Shared.pm new file mode 100644 index 0000000..0f02b7e --- /dev/null +++ b/lib/Loadbars/Shared.pm @@ -0,0 +1,52 @@ +package Loadbars::Shared; + +use Exporter; + +use base 'Exporter'; + +our @EXPORT = qw( + %PIDS + %AVGSTATS + %CPUSTATS + %MEMSTATS + %MEMSTATS_HAS + %C + %I +); + +our %PIDS : shared; +our %AVGSTATS : shared; +our %CPUSTATS : shared; +our %MEMSTATS : shared; +our %MEMSTATS_HAS : shared; + +#my %NETSTATS : shared; +#my %NETSTATS_HAS : shared; + +# Global configuration hash +our %C : shared; + +# Global configuration hash for internal settings (not configurable) +our %I : shared; + +# Setting defaults +%C = ( + average => 15, + barwidth => 35, + extended => 0, + factor => 1, + height => 230, + maxwidth => 1280, + samples => 1000, + showcores => 0, + showmem => 0, + showtext => 1, + showtexthost => 0, + sshopts => '', +); + +%I = ( + cpuregexp => 'cpu', + showtextoff => 0, +); + diff --git a/lib/Loadbars/Utils.pm b/lib/Loadbars/Utils.pm new file mode 100644 index 0000000..bfb8027 --- /dev/null +++ b/lib/Loadbars/Utils.pm @@ -0,0 +1,41 @@ +package Loadbars::Utils; + +use strict; +use warnings; + +use Exporter; + +use base 'Exporter'; + +our @EXPORT = qw ( + debugsay + display_info + display_info_no_nl + display_warn + newline + notnull + null + say + sum + trim +); + +sub say (@) { print "$_\n" for @_; return undef } +sub newline () { say ''; return undef } +sub debugsay (@) { say "Loadbars::DEBUG: $_" for @_; return undef } +sub sum (@) { my $sum = 0; $sum += $_ for @_; return $sum } +sub null ($) { defined $_[0] ? $_[0] : 0 } +sub notnull ($) { $_[0] != 0 ? $_[0] : 1 } +sub error ($) { die shift, "\n" } + +sub trim (\$) { + my $str = shift; + $$str =~ s/^[\s\t]+//; + $$str =~ s/[\s\t]+$//; + return undef; +} +sub display_info_no_nl ($) { print "==> " . (shift) . ' ' } +sub display_info ($) { say "==> " . shift } +sub display_warn ($) { say "!!! " . shift } + +1; diff --git a/loadbars b/loadbars new file mode 100755 index 0000000..ea1ea06 --- /dev/null +++ b/loadbars @@ -0,0 +1,68 @@ +#!/usr/bin/perl + +# loadbars (c) 2010 - 2012, Dipl.-Inform. (FH) Paul Buetow +# E-Mail: loadbars@mx.buetow.org WWW: http://loadbars.buetow.org +# For legal informations see COPYING and COPYING.FONT + +use strict; +use warnings; + +use Getopt::Long; + +my $lib; + +BEGIN { + if ( -d './lib/Loadbars' ) { + $lib = 'lib'; + + } + else { + $lib = '/usr/share/loadbars/lib'; + } +} + +use lib $lib; + +use Loadbars::Main; +use Loadbars::Constants; +use Loadbars::HelpDispatch; +use Loadbars::Shared; +use Loadbars::Utils; + +my ( $hosts, $dispatch ) = Loadbars::HelpDispatch::create; +my $usage; + +say( Loadbars::Constants->VERSION . ' ' . Loadbars::Constants->COPYRIGHT ); + +Loadbars::Config::read; + +GetOptions( 'help|?' => \$usage, $dispatch->('options') ); + +if ( defined $usage ) { + say $dispatch->('usage'); + exit Loadbars::Constants->SUCCESS; +} + +Loadbars::Main::set_showcores_regexp; + +my @hosts = map { + my ( $a, $b ) = split /\@/, $_; + defined $b ? "$b:$a" : $a; +} split ',', $$hosts; + +if ( @hosts || defined $Loadbars::Main::C{cluster} ) { + push @hosts, Loadbars::Config::get_cluster_hosts $C{cluster} + if defined $C{cluster}; + system 'ssh-add'; + +} +else { + Loadbars::Main::say $dispatch->('usage'); + exit Loadbars::Constants->E_NOHOST; +} + +my @threads = Loadbars::Main::create_threads @hosts; +Loadbars::Main::loop $dispatch, @threads; + +exit Loadbars::Constants->SUCCESS; + |
