From dd173e785241d737e0697b3fc93cc006714e0b8d Mon Sep 17 00:00:00 2001 From: "Paul Buetow (mars)" Date: Fri, 25 Nov 2011 12:27:35 +0100 Subject: Remove a wish --- WISHLIST | 1 - loadbars.pl | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/WISHLIST b/WISHLIST index 2a71f48..aeabd86 100644 --- a/WISHLIST +++ b/WISHLIST @@ -2,6 +2,5 @@ * Dynamic/online resizing of the window * Rudimentary support for /etc/clusters, the Cluster-SSH configuration file * Adding and removing hosts online -* Toggle cpu core vs. hostname vs. nothing option * .deb for Debian and Ubuntu * Allow sshusername@hostname in --hosts diff --git a/loadbars.pl b/loadbars.pl index 9136911..1b556cd 100755 --- a/loadbars.pl +++ b/loadbars.pl @@ -26,7 +26,7 @@ use threads::shared; use constant { DEPTH => 8, - VERSION => 'loadbars v0.2.2', + VERSION => 'loadbars v0.2.2-master', Copyright => '2010-2011 (c) Paul Buetow ', BLACK => SDL::Color->new(-r => 0x00, -g => 0x00, -b => 0x00), BLUE => SDL::Color->new(-r => 0x00, -g => 0x00, -b => 0xff), -- cgit v1.2.3 From 6378d5a4bf8a2e89338d7a055a1b64bced1d8d17 Mon Sep 17 00:00:00 2001 From: "Paul Buetow (mars)" Date: Mon, 28 Nov 2011 19:13:07 +0100 Subject: Renamed loadbars script --- loadbars | 698 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ loadbars.pl | 698 ------------------------------------------------------------ 2 files changed, 698 insertions(+), 698 deletions(-) create mode 100755 loadbars delete mode 100755 loadbars.pl diff --git a/loadbars b/loadbars new file mode 100755 index 0000000..1b556cd --- /dev/null +++ b/loadbars @@ -0,0 +1,698 @@ +#!/usr/bin/perl + +# loadbars (c) 2010 - 2011, Dipl.-Inform. (FH) Paul Buetow +# E-Mail: loadbars@mx.buetow.org WWW: http://loadbars.buetow.org +# For legal informations see COPYING and COPYING.FONT + +package Loadbars; + +use strict; +use warnings; + +use Getopt::Long; + +use SDL::App; +use SDL::Rect; +use SDL::Color; +use SDL::Event; + +use SDL::Surface; +use SDL::Font; + +use Time::HiRes qw(usleep gettimeofday); + +use threads; +use threads::shared; + +use constant { + DEPTH => 8, + VERSION => 'loadbars v0.2.2-master', + Copyright => '2010-2011 (c) Paul Buetow ', + BLACK => SDL::Color->new(-r => 0x00, -g => 0x00, -b => 0x00), + BLUE => SDL::Color->new(-r => 0x00, -g => 0x00, -b => 0xff), + 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), + GREY => SDL::Color->new(-r => 0xaa, -g => 0xaa, -b => 0xaa), + YELLOW0 => SDL::Color->new(-r => 0xff, -g => 0xa0, -b => 0x00), + YELLOW => SDL::Color->new(-r => 0xff, -g => 0xc0, -b => 0x00), + SYSTEM_PURPLE => 30, + USER_WHITE => 99, + USER_RED => 90, + USER_ORANGE => 70, + USER_YELLOW0 => 50, + NULL => 0, + DEBUG => 0, +}; + +$| = 1; + +my %AVGSTATS : shared; +my %CPUSTATS : shared; + +# Global configuration hash +my %C : shared; + +# Setting defaults +%C = ( + title => Loadbars::VERSION . ' (press h for help)', + average => 15, + togglecpu => 1, + cpuregexp => 'cpu', + factor => 1, + displaytxt => 1, + displaytxthost => 0, + inter => 0.1, + samples => 1000, + sshopts => '', + width => 1250, + height => 150, +); + +# Quick n dirty helpers +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 ($) { my $arg = shift; return defined $arg ? $arg : 0 } +sub set_togglecpu_regexp () { $C{cpuregexp} = $C{togglecpu} ? 'cpu ' : 'cpu' } + +sub parse_cpu_line ($) { + my ($name, %load); + + ($name, @load{qw(user nice system iowait irq softirq)}) = split ' ', shift; + $load{TOTAL} = sum @load{qw(user nice system iowait)}; + + return ($name, \%load); +} + +sub thread_get_stats ($) { + my $host = shift; + + my ($sigusr1, $quit) = (0, 0); + my $loadavgexp = qr/(\d+\.\d{2}) (\d+\.\d{2}) (\d+\.\d{2})/; + + for (;;) { + my $bash = <<"BASH"; + if [ -e /proc/stat ]; then + loadavg=/proc/loadavg + stat=/proc/stat + + for i in \$(seq $C{samples}); do + cat \$loadavg \$stat + sleep $C{inter} + done + else + loadavg=/compat/linux/proc/loadavg + stat=/compat/linux/proc/stat + + for i in \$(jot $C{samples}); do + cat \$loadavg \$stat + sleep $C{inter} + done + fi +BASH + my $cmd = $host eq 'localhost' ? $bash + : "ssh -o StrictHostKeyChecking=no $C{sshopts} $host '$bash'"; + + my $pid = open my $pipe, "$cmd |" or do { + say "Warning: $!"; + sleep 3; + next; + }; + + # Toggle CPUs + $SIG{USR1} = sub { $sigusr1 = 1 }; + my $cpuregexp = qr/$C{cpuregexp}/; + + # $SIG{STOP} = sub { debugsay kill 9, $pid; $quit = 1 }; + + while (<$pipe>) { + if (/^$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; + } + + if ($sigusr1) { + $cpuregexp = qr/$C{cpuregexp}/; + $sigusr1 = 0; + } + } + + } + + 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::BLACK); + $app->update($rect); + + return undef; +} + +sub create_threads (@) { + return map { + $_->detach(); + $_; + + } map { + threads->create('thread_get_stats', $_); + + } @_; +} + +sub main_loop ($@) { + my ($dispatch, @threads) = @_; + + # Planned for the future + my $statusbar_height = 0; + + my $app = SDL::App->new( + -title => $C{title}, + -icon_title => $C{title}, + -width => $C{width}, + -height => $C{height}+$statusbar_height, + -depth => Loadbars::DEPTH, + -resizeable => 0, + ); + + SDL::Font->new('font.png')->use(); + + my $num_stats = keys %CPUSTATS; + + my $rects = {}; + my %prev_stats; + my %last_loads; + + my $redraw_background = 0; + my $font_height = 14; + + my $displayinfo_time = 5; + my $displayinfo_start = 0; + my $displayinfo : shared = ''; + my $infotxt : shared = ''; + my $quit : shared = 0; + + my ($t1, $t2) = (Time::HiRes::time(), undef); + my $event = SDL::Event->new(); + + my $event_thread = async { + for (;;) { + $event->pump(); + $event->poll(); + $event->wait(); + + my $type = $event->type(); + my $key_name = $event->key_name(); + + debugsay "Event type=$type key_name=$key_name" if Loadbars::DEBUG; + next if $type != 2; + + if ($key_name eq '1') { + $C{togglecpu} = !$C{togglecpu}; + set_togglecpu_regexp; + $_->kill('USR1') for @threads; + %AVGSTATS = (); + %CPUSTATS = (); + $displayinfo = 'Toggled CPUs'; + + } elsif ($key_name eq 'h') { + say '=> Hotkeys to use in the SDL interface'; + say $dispatch->('hotkeys'); + $displayinfo = 'Hotkeys help printed on terminal stdout'; + + } elsif ($key_name eq 't') { + $C{displaytxt} = !$C{displaytxt}; + $displayinfo = 'Toggled text display'; + + } elsif ($key_name eq 'u') { + $C{displaytxthost} = !$C{displaytxthost}; + $displayinfo = 'Toggled number/hostname display'; + + } elsif ($key_name eq 'q') { + $quit = 1; + last; + + # Increase and decrease pairs + } elsif ($key_name eq 'a') { + ++$C{average}; + $displayinfo = "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; + $displayinfo = "Set sample average to $C{average}"; + + } elsif ($key_name eq 's') { + $C{factor} += 0.1; + $displayinfo = "Set scale factor to $C{factor}"; + } elsif ($key_name eq 'x' or $key_name eq 'z') { + $C{factor} -= 0.1; + $displayinfo = "Set scale factor to $C{factor}"; + + } elsif ($key_name eq 'd') { + $C{inter} += 0.1; + $displayinfo = "Set graph update interval to $C{inter}"; + } elsif ($key_name eq 'c' or $key_name eq 'z') { + my $int = $C{inter}; + $int -= 0.1; + $C{inter} = $int > 0 ? $int : 0.1; + $displayinfo = "Set graph update interval to $C{inter}"; + } + } + }; + + do { + my ($x, $y) = (0, 0); + my %is_host_summary; + + my $new_num_stats = keys %CPUSTATS; + + if ($new_num_stats != $num_stats) { + %prev_stats = (); + %last_loads = (); + + $num_stats = $new_num_stats; + $redraw_background = 1; + } + + # Avoid division by null + # Also substract 1 (each bar is followed by an 1px separator bar) + my $width = $C{width} / ($num_stats ? $num_stats : 1) - 1; + + my ($current_barnum, $current_corenum) = (-1, -1); + + for my $key (sort keys %CPUSTATS) { + ++$current_barnum; + ++$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 %maxheights = map { + $_ => defined $cpumax->{$_} ? $cpumax->{$_} * ($C{height}/100) : 1 + + } keys %$cpumax; + + my $is_host_summary = exists $is_host_summary{$host}; + + my $rect_separator = undef; + my $rect_user = get_rect $rects, "$key;user"; + my $rect_system = get_rect $rects, "$key;system"; + my $rect_iowait = get_rect $rects, "$key;iowait"; + my $rect_nice = get_rect $rects, "$key;nice"; + my $rect_max = get_rect $rects, "$key;max"; + + unless ($is_host_summary || $C{togglecpu}) { + $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::GREY); + } + + $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{iowait}; + $rect_iowait->width($width); + $rect_iowait->height($heights{iowait}); + $rect_iowait->x($x); + $rect_iowait->y($y); + + $rect_max->width($width); + $rect_max->height(1); + $rect_max->x($x); + $rect_max->y($C{height} - $maxheights{system} - $maxheights{user}); + + my $system_n_user = sum @{$cpuaverage}{qw(user system)}; + my $max_system_n_user = sum @{$cpumax}{qw(user system)}; + + $app->fill($rect_iowait, Loadbars::BLACK); + $app->fill($rect_nice, Loadbars::GREEN); + $app->fill($rect_max, $max_system_n_user > Loadbars::USER_WHITE ? Loadbars::WHITE + : ($max_system_n_user > Loadbars::USER_RED ? Loadbars::RED + : ($max_system_n_user > Loadbars::USER_ORANGE ? Loadbars::ORANGE + : ($max_system_n_user > Loadbars::USER_YELLOW0 ? Loadbars::YELLOW0 + : (Loadbars::YELLOW))))); + $app->fill($rect_user, $system_n_user > Loadbars::USER_WHITE ? Loadbars::WHITE + : ($system_n_user > Loadbars::USER_RED ? Loadbars::RED + : ($system_n_user > Loadbars::USER_ORANGE ? Loadbars::ORANGE + : ($system_n_user > Loadbars::USER_YELLOW0 ? Loadbars::YELLOW0 + : (Loadbars::YELLOW))))); + $app->fill($rect_system, $cpuaverage->{system} > Loadbars::SYSTEM_PURPLE + ? Loadbars::PURPLE + : Loadbars::BLUE); + $app->fill($rect_user, $system_n_user > Loadbars::USER_WHITE ? Loadbars::WHITE + : ($system_n_user > Loadbars::USER_RED ? Loadbars::RED + : ($system_n_user > Loadbars::USER_ORANGE ? Loadbars::ORANGE + : ($system_n_user > Loadbars::USER_YELLOW0 ? Loadbars::YELLOW0 + : (Loadbars::YELLOW))))); + + my ($y, $space) = (5, $font_height); + my @loadavg = split ';', $AVGSTATS{$host}; + $is_host_summary{$host} = 1 if defined $loadavg[0]; + + if ($C{displaytxt}) { + if ($C{displaytxthost} && not $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{togglecpu} ? $current_barnum + 1: $current_corenum); + } + + $app->print($x, $y+=$space, sprintf '%d%s', $cpuaverage->{nice}, 'ni'); + $app->print($x, $y+=$space, sprintf '%d%s', $cpuaverage->{user}, 'us'); + $app->print($x, $y+=$space, sprintf '%d%s', $cpuaverage->{system}, 'sy'); + $app->print($x, $y+=$space, sprintf '%d%s', $system_n_user, 'su'); + + unless ($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]); + } + + } + } + + # Display an informational text message if any + $app->print(0, $y+=$space, $displayinfo) if length $displayinfo; + + $app->update($rect_nice, $rect_iowait, $rect_system, $rect_user); + $app->update($rect_separator) if defined $rect_separator; + + $x += $width + 1; + } + +TIMEKEEPER: + $t2 = Time::HiRes::time(); + + if (length $displayinfo) { + if ($displayinfo_start == 0) { + $displayinfo_start = $t2; + + } else { + if ($displayinfo_time < $t2 - $displayinfo_start) { + $displayinfo = ''; + $displayinfo_start = 0; + } + } + } + + if ($C{inter} > $t2 - $t1) { + usleep 10000; + # Goto is OK if you don't produce spaghetti code with it + goto TIMEKEEPER; + } + + $t1 = $t2; + + if ($redraw_background) { + draw_background $app, $rects; + $redraw_background = 0; + } + + } until $quit; + + say "Good bye"; + # $_->kill('STOP') for @threads; + $event_thread->join(); + exit 0; +} + + +sub dispatch_table () { + my $hosts = ''; + + my $textdesc = <30% + Yellow: User cpu usage + Darker yellow: User usage if system & user cpu is >50% + Orange: User usage if system & user cpu is >70% + White: Usage usage if system & user cpu is >99% + Green: Nice cpu usage + 1px horizontal line: Maximum sy+us cpu of last 'avg' samples +Explanation text display: + ni = Nice cpu usage in % + us = User cpu usage in % + sy = System cpu sage in % + su = System & user cpu usage in % + avg = System load average (desc. order: 1, 5 and 15 min. avg.) +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 = ( + togglecpu => { menupos => 1, help => 'Toggle CPUs (0 or 1)', mode => 7, type => 'i' }, + togglecpu_hot => { menupos => 2, cmd => '1', help => 'Toggle CPUs', mode => 1 }, + + 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 }, + + configuration => { menupos => 6, cmd => 'c', help => 'Show current configuration', mode => 4 }, + + 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 separated list of hosts', var => \$hosts, mode => 6, type => 's' }, + + inter => { menupos => 13, help => 'Set update interval in seconds (default 0.1)', mode => 7, type => 's' }, + inter_hot_up => { menupos => 14, cmd => 'd', help => 'Increases update interval in seconds by 0.1', mode => 1 }, + inter_hot_dn => { menupos => 15, cmd => 'c', help => 'Decreases update interval in seconds by 0.1', mode => 1 }, + + quit_hot => { menupos => 16, cmd => 'q', help => 'Quits', mode => 1 }, + + samples => { menupos => 17, help => 'Set number of samples until ssh reconnects', mode => 6, type => 'i' }, + sshopts => { menupos => 18, help => 'Set SSH options', mode => 6, type => 's' }, + title => { menupos => 19, help => 'Set the window title', var => \$C{title}, mode => 6, type => 's' }, + + toggletxthost => { menupos => 20, help => 'Toggle hostname/num text display (0 or 1)', mode => 7, type => 'i' }, + toggletxthost_hot => { menupos => 21, cmd => 'u', help => 'Toggle hostname/num text display', mode => 1 }, + + toggletxt => { menupos => 22, help => 'Toggle text display (0 or 1)', mode => 7, type => 'i' }, + toggletxt_hot => { menupos => 23, cmd => 't', help => 'Toggle text display', mode => 1 }, + + width => { menupos => 24, help => 'Set windows width', mode => 6, type => 'i' }, + ); + + 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 { + "--$_ \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 { + say sort map { + "$_->[0] = $_->[1]" + + } grep { + defined $_->[1] + + } map { + [$_ => exists $d{$_}{var} ? ${$d{$_}{var}} : $C{$_}] + + } keys %d + }; + + return (\$hosts, $closure); +} + +sub main () { + my ($hosts, $dispatch) = dispatch_table; + my $usage; + + GetOptions ('help|?' => \$usage, $dispatch->('options')); + + if (defined $usage) { + say $dispatch->('usage'); + exit 0; + } + + set_togglecpu_regexp; + + my @hosts = split ',', $$hosts; + + if (@hosts) { + system 'ssh-add'; + + } else { + @hosts = 'localhost'; + } + + my @threads = create_threads @hosts; + main_loop $dispatch, @threads; +} + +main; + +1; diff --git a/loadbars.pl b/loadbars.pl deleted file mode 100755 index 1b556cd..0000000 --- a/loadbars.pl +++ /dev/null @@ -1,698 +0,0 @@ -#!/usr/bin/perl - -# loadbars (c) 2010 - 2011, Dipl.-Inform. (FH) Paul Buetow -# E-Mail: loadbars@mx.buetow.org WWW: http://loadbars.buetow.org -# For legal informations see COPYING and COPYING.FONT - -package Loadbars; - -use strict; -use warnings; - -use Getopt::Long; - -use SDL::App; -use SDL::Rect; -use SDL::Color; -use SDL::Event; - -use SDL::Surface; -use SDL::Font; - -use Time::HiRes qw(usleep gettimeofday); - -use threads; -use threads::shared; - -use constant { - DEPTH => 8, - VERSION => 'loadbars v0.2.2-master', - Copyright => '2010-2011 (c) Paul Buetow ', - BLACK => SDL::Color->new(-r => 0x00, -g => 0x00, -b => 0x00), - BLUE => SDL::Color->new(-r => 0x00, -g => 0x00, -b => 0xff), - 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), - GREY => SDL::Color->new(-r => 0xaa, -g => 0xaa, -b => 0xaa), - YELLOW0 => SDL::Color->new(-r => 0xff, -g => 0xa0, -b => 0x00), - YELLOW => SDL::Color->new(-r => 0xff, -g => 0xc0, -b => 0x00), - SYSTEM_PURPLE => 30, - USER_WHITE => 99, - USER_RED => 90, - USER_ORANGE => 70, - USER_YELLOW0 => 50, - NULL => 0, - DEBUG => 0, -}; - -$| = 1; - -my %AVGSTATS : shared; -my %CPUSTATS : shared; - -# Global configuration hash -my %C : shared; - -# Setting defaults -%C = ( - title => Loadbars::VERSION . ' (press h for help)', - average => 15, - togglecpu => 1, - cpuregexp => 'cpu', - factor => 1, - displaytxt => 1, - displaytxthost => 0, - inter => 0.1, - samples => 1000, - sshopts => '', - width => 1250, - height => 150, -); - -# Quick n dirty helpers -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 ($) { my $arg = shift; return defined $arg ? $arg : 0 } -sub set_togglecpu_regexp () { $C{cpuregexp} = $C{togglecpu} ? 'cpu ' : 'cpu' } - -sub parse_cpu_line ($) { - my ($name, %load); - - ($name, @load{qw(user nice system iowait irq softirq)}) = split ' ', shift; - $load{TOTAL} = sum @load{qw(user nice system iowait)}; - - return ($name, \%load); -} - -sub thread_get_stats ($) { - my $host = shift; - - my ($sigusr1, $quit) = (0, 0); - my $loadavgexp = qr/(\d+\.\d{2}) (\d+\.\d{2}) (\d+\.\d{2})/; - - for (;;) { - my $bash = <<"BASH"; - if [ -e /proc/stat ]; then - loadavg=/proc/loadavg - stat=/proc/stat - - for i in \$(seq $C{samples}); do - cat \$loadavg \$stat - sleep $C{inter} - done - else - loadavg=/compat/linux/proc/loadavg - stat=/compat/linux/proc/stat - - for i in \$(jot $C{samples}); do - cat \$loadavg \$stat - sleep $C{inter} - done - fi -BASH - my $cmd = $host eq 'localhost' ? $bash - : "ssh -o StrictHostKeyChecking=no $C{sshopts} $host '$bash'"; - - my $pid = open my $pipe, "$cmd |" or do { - say "Warning: $!"; - sleep 3; - next; - }; - - # Toggle CPUs - $SIG{USR1} = sub { $sigusr1 = 1 }; - my $cpuregexp = qr/$C{cpuregexp}/; - - # $SIG{STOP} = sub { debugsay kill 9, $pid; $quit = 1 }; - - while (<$pipe>) { - if (/^$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; - } - - if ($sigusr1) { - $cpuregexp = qr/$C{cpuregexp}/; - $sigusr1 = 0; - } - } - - } - - 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::BLACK); - $app->update($rect); - - return undef; -} - -sub create_threads (@) { - return map { - $_->detach(); - $_; - - } map { - threads->create('thread_get_stats', $_); - - } @_; -} - -sub main_loop ($@) { - my ($dispatch, @threads) = @_; - - # Planned for the future - my $statusbar_height = 0; - - my $app = SDL::App->new( - -title => $C{title}, - -icon_title => $C{title}, - -width => $C{width}, - -height => $C{height}+$statusbar_height, - -depth => Loadbars::DEPTH, - -resizeable => 0, - ); - - SDL::Font->new('font.png')->use(); - - my $num_stats = keys %CPUSTATS; - - my $rects = {}; - my %prev_stats; - my %last_loads; - - my $redraw_background = 0; - my $font_height = 14; - - my $displayinfo_time = 5; - my $displayinfo_start = 0; - my $displayinfo : shared = ''; - my $infotxt : shared = ''; - my $quit : shared = 0; - - my ($t1, $t2) = (Time::HiRes::time(), undef); - my $event = SDL::Event->new(); - - my $event_thread = async { - for (;;) { - $event->pump(); - $event->poll(); - $event->wait(); - - my $type = $event->type(); - my $key_name = $event->key_name(); - - debugsay "Event type=$type key_name=$key_name" if Loadbars::DEBUG; - next if $type != 2; - - if ($key_name eq '1') { - $C{togglecpu} = !$C{togglecpu}; - set_togglecpu_regexp; - $_->kill('USR1') for @threads; - %AVGSTATS = (); - %CPUSTATS = (); - $displayinfo = 'Toggled CPUs'; - - } elsif ($key_name eq 'h') { - say '=> Hotkeys to use in the SDL interface'; - say $dispatch->('hotkeys'); - $displayinfo = 'Hotkeys help printed on terminal stdout'; - - } elsif ($key_name eq 't') { - $C{displaytxt} = !$C{displaytxt}; - $displayinfo = 'Toggled text display'; - - } elsif ($key_name eq 'u') { - $C{displaytxthost} = !$C{displaytxthost}; - $displayinfo = 'Toggled number/hostname display'; - - } elsif ($key_name eq 'q') { - $quit = 1; - last; - - # Increase and decrease pairs - } elsif ($key_name eq 'a') { - ++$C{average}; - $displayinfo = "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; - $displayinfo = "Set sample average to $C{average}"; - - } elsif ($key_name eq 's') { - $C{factor} += 0.1; - $displayinfo = "Set scale factor to $C{factor}"; - } elsif ($key_name eq 'x' or $key_name eq 'z') { - $C{factor} -= 0.1; - $displayinfo = "Set scale factor to $C{factor}"; - - } elsif ($key_name eq 'd') { - $C{inter} += 0.1; - $displayinfo = "Set graph update interval to $C{inter}"; - } elsif ($key_name eq 'c' or $key_name eq 'z') { - my $int = $C{inter}; - $int -= 0.1; - $C{inter} = $int > 0 ? $int : 0.1; - $displayinfo = "Set graph update interval to $C{inter}"; - } - } - }; - - do { - my ($x, $y) = (0, 0); - my %is_host_summary; - - my $new_num_stats = keys %CPUSTATS; - - if ($new_num_stats != $num_stats) { - %prev_stats = (); - %last_loads = (); - - $num_stats = $new_num_stats; - $redraw_background = 1; - } - - # Avoid division by null - # Also substract 1 (each bar is followed by an 1px separator bar) - my $width = $C{width} / ($num_stats ? $num_stats : 1) - 1; - - my ($current_barnum, $current_corenum) = (-1, -1); - - for my $key (sort keys %CPUSTATS) { - ++$current_barnum; - ++$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 %maxheights = map { - $_ => defined $cpumax->{$_} ? $cpumax->{$_} * ($C{height}/100) : 1 - - } keys %$cpumax; - - my $is_host_summary = exists $is_host_summary{$host}; - - my $rect_separator = undef; - my $rect_user = get_rect $rects, "$key;user"; - my $rect_system = get_rect $rects, "$key;system"; - my $rect_iowait = get_rect $rects, "$key;iowait"; - my $rect_nice = get_rect $rects, "$key;nice"; - my $rect_max = get_rect $rects, "$key;max"; - - unless ($is_host_summary || $C{togglecpu}) { - $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::GREY); - } - - $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{iowait}; - $rect_iowait->width($width); - $rect_iowait->height($heights{iowait}); - $rect_iowait->x($x); - $rect_iowait->y($y); - - $rect_max->width($width); - $rect_max->height(1); - $rect_max->x($x); - $rect_max->y($C{height} - $maxheights{system} - $maxheights{user}); - - my $system_n_user = sum @{$cpuaverage}{qw(user system)}; - my $max_system_n_user = sum @{$cpumax}{qw(user system)}; - - $app->fill($rect_iowait, Loadbars::BLACK); - $app->fill($rect_nice, Loadbars::GREEN); - $app->fill($rect_max, $max_system_n_user > Loadbars::USER_WHITE ? Loadbars::WHITE - : ($max_system_n_user > Loadbars::USER_RED ? Loadbars::RED - : ($max_system_n_user > Loadbars::USER_ORANGE ? Loadbars::ORANGE - : ($max_system_n_user > Loadbars::USER_YELLOW0 ? Loadbars::YELLOW0 - : (Loadbars::YELLOW))))); - $app->fill($rect_user, $system_n_user > Loadbars::USER_WHITE ? Loadbars::WHITE - : ($system_n_user > Loadbars::USER_RED ? Loadbars::RED - : ($system_n_user > Loadbars::USER_ORANGE ? Loadbars::ORANGE - : ($system_n_user > Loadbars::USER_YELLOW0 ? Loadbars::YELLOW0 - : (Loadbars::YELLOW))))); - $app->fill($rect_system, $cpuaverage->{system} > Loadbars::SYSTEM_PURPLE - ? Loadbars::PURPLE - : Loadbars::BLUE); - $app->fill($rect_user, $system_n_user > Loadbars::USER_WHITE ? Loadbars::WHITE - : ($system_n_user > Loadbars::USER_RED ? Loadbars::RED - : ($system_n_user > Loadbars::USER_ORANGE ? Loadbars::ORANGE - : ($system_n_user > Loadbars::USER_YELLOW0 ? Loadbars::YELLOW0 - : (Loadbars::YELLOW))))); - - my ($y, $space) = (5, $font_height); - my @loadavg = split ';', $AVGSTATS{$host}; - $is_host_summary{$host} = 1 if defined $loadavg[0]; - - if ($C{displaytxt}) { - if ($C{displaytxthost} && not $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{togglecpu} ? $current_barnum + 1: $current_corenum); - } - - $app->print($x, $y+=$space, sprintf '%d%s', $cpuaverage->{nice}, 'ni'); - $app->print($x, $y+=$space, sprintf '%d%s', $cpuaverage->{user}, 'us'); - $app->print($x, $y+=$space, sprintf '%d%s', $cpuaverage->{system}, 'sy'); - $app->print($x, $y+=$space, sprintf '%d%s', $system_n_user, 'su'); - - unless ($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]); - } - - } - } - - # Display an informational text message if any - $app->print(0, $y+=$space, $displayinfo) if length $displayinfo; - - $app->update($rect_nice, $rect_iowait, $rect_system, $rect_user); - $app->update($rect_separator) if defined $rect_separator; - - $x += $width + 1; - } - -TIMEKEEPER: - $t2 = Time::HiRes::time(); - - if (length $displayinfo) { - if ($displayinfo_start == 0) { - $displayinfo_start = $t2; - - } else { - if ($displayinfo_time < $t2 - $displayinfo_start) { - $displayinfo = ''; - $displayinfo_start = 0; - } - } - } - - if ($C{inter} > $t2 - $t1) { - usleep 10000; - # Goto is OK if you don't produce spaghetti code with it - goto TIMEKEEPER; - } - - $t1 = $t2; - - if ($redraw_background) { - draw_background $app, $rects; - $redraw_background = 0; - } - - } until $quit; - - say "Good bye"; - # $_->kill('STOP') for @threads; - $event_thread->join(); - exit 0; -} - - -sub dispatch_table () { - my $hosts = ''; - - my $textdesc = <30% - Yellow: User cpu usage - Darker yellow: User usage if system & user cpu is >50% - Orange: User usage if system & user cpu is >70% - White: Usage usage if system & user cpu is >99% - Green: Nice cpu usage - 1px horizontal line: Maximum sy+us cpu of last 'avg' samples -Explanation text display: - ni = Nice cpu usage in % - us = User cpu usage in % - sy = System cpu sage in % - su = System & user cpu usage in % - avg = System load average (desc. order: 1, 5 and 15 min. avg.) -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 = ( - togglecpu => { menupos => 1, help => 'Toggle CPUs (0 or 1)', mode => 7, type => 'i' }, - togglecpu_hot => { menupos => 2, cmd => '1', help => 'Toggle CPUs', mode => 1 }, - - 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 }, - - configuration => { menupos => 6, cmd => 'c', help => 'Show current configuration', mode => 4 }, - - 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 separated list of hosts', var => \$hosts, mode => 6, type => 's' }, - - inter => { menupos => 13, help => 'Set update interval in seconds (default 0.1)', mode => 7, type => 's' }, - inter_hot_up => { menupos => 14, cmd => 'd', help => 'Increases update interval in seconds by 0.1', mode => 1 }, - inter_hot_dn => { menupos => 15, cmd => 'c', help => 'Decreases update interval in seconds by 0.1', mode => 1 }, - - quit_hot => { menupos => 16, cmd => 'q', help => 'Quits', mode => 1 }, - - samples => { menupos => 17, help => 'Set number of samples until ssh reconnects', mode => 6, type => 'i' }, - sshopts => { menupos => 18, help => 'Set SSH options', mode => 6, type => 's' }, - title => { menupos => 19, help => 'Set the window title', var => \$C{title}, mode => 6, type => 's' }, - - toggletxthost => { menupos => 20, help => 'Toggle hostname/num text display (0 or 1)', mode => 7, type => 'i' }, - toggletxthost_hot => { menupos => 21, cmd => 'u', help => 'Toggle hostname/num text display', mode => 1 }, - - toggletxt => { menupos => 22, help => 'Toggle text display (0 or 1)', mode => 7, type => 'i' }, - toggletxt_hot => { menupos => 23, cmd => 't', help => 'Toggle text display', mode => 1 }, - - width => { menupos => 24, help => 'Set windows width', mode => 6, type => 'i' }, - ); - - 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 { - "--$_ \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 { - say sort map { - "$_->[0] = $_->[1]" - - } grep { - defined $_->[1] - - } map { - [$_ => exists $d{$_}{var} ? ${$d{$_}{var}} : $C{$_}] - - } keys %d - }; - - return (\$hosts, $closure); -} - -sub main () { - my ($hosts, $dispatch) = dispatch_table; - my $usage; - - GetOptions ('help|?' => \$usage, $dispatch->('options')); - - if (defined $usage) { - say $dispatch->('usage'); - exit 0; - } - - set_togglecpu_regexp; - - my @hosts = split ',', $$hosts; - - if (@hosts) { - system 'ssh-add'; - - } else { - @hosts = 'localhost'; - } - - my @threads = create_threads @hosts; - main_loop $dispatch, @threads; -} - -main; - -1; -- cgit v1.2.3 From f26e4f64fa7c1dcc2add2dc5f8611f840bbf800e Mon Sep 17 00:00:00 2001 From: "Paul Buetow (mars)" Date: Mon, 26 Dec 2011 14:39:12 +0100 Subject: initial release 0.3 --- loadbars | 92 ++++++++++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 67 insertions(+), 25 deletions(-) diff --git a/loadbars b/loadbars index 1b556cd..b212f5c 100755 --- a/loadbars +++ b/loadbars @@ -26,7 +26,7 @@ use threads::shared; use constant { DEPTH => 8, - VERSION => 'loadbars v0.2.2-master', + VERSION => 'loadbars v0.3.0', Copyright => '2010-2011 (c) Paul Buetow ', BLACK => SDL::Color->new(-r => 0x00, -g => 0x00, -b => 0x00), BLUE => SDL::Color->new(-r => 0x00, -g => 0x00, -b => 0xff), @@ -51,6 +51,7 @@ $| = 1; my %AVGSTATS : shared; my %CPUSTATS : shared; +my %MEMSTATS : shared; # Global configuration hash my %C : shared; @@ -64,11 +65,12 @@ my %C : shared; factor => 1, displaytxt => 1, displaytxthost => 0, + togglepeak => 0, inter => 0.1, samples => 1000, sshopts => '', width => 1250, - height => 150, + height => 200, ); # Quick n dirty helpers @@ -78,6 +80,7 @@ sub debugsay (@) { say "Loadbars::DEBUG: $_" for @_; return undef } sub sum (@) { my $sum = 0; $sum += $_ for @_; return $sum } sub null ($) { my $arg = shift; return defined $arg ? $arg : 0 } sub set_togglecpu_regexp () { $C{cpuregexp} = $C{togglecpu} ? 'cpu ' : 'cpu' } +sub error ($) { die shift, "\n" } sub parse_cpu_line ($) { my ($name, %load); @@ -99,9 +102,10 @@ sub thread_get_stats ($) { if [ -e /proc/stat ]; then loadavg=/proc/loadavg stat=/proc/stat + meminfo=/proc/meminfo for i in \$(seq $C{samples}); do - cat \$loadavg \$stat + cat \$loadavg \$stat \$meminfo sleep $C{inter} done else @@ -127,8 +131,6 @@ BASH $SIG{USR1} = sub { $sigusr1 = 1 }; my $cpuregexp = qr/$C{cpuregexp}/; - # $SIG{STOP} = sub { debugsay kill 9, $pid; $quit = 1 }; - while (<$pipe>) { if (/^$loadavgexp/) { $AVGSTATS{$host} = "$1;$2;$3"; @@ -273,6 +275,10 @@ sub main_loop ($@) { say $dispatch->('hotkeys'); $displayinfo = 'Hotkeys help printed on terminal stdout'; + } elsif ($key_name eq 'p') { + $C{togglepeak} = !$C{togglepeak}; + $displayinfo = 'Toggled peak display'; + } elsif ($key_name eq 't') { $C{displaytxt} = !$C{displaytxt}; $displayinfo = 'Toggled text display'; @@ -367,14 +373,8 @@ sub main_loop ($@) { my %heights = map { $_ => defined $cpuaverage->{$_} ? $cpuaverage->{$_} * ($C{height}/100) : 1 - } keys %$cpuaverage; - my %maxheights = map { - $_ => defined $cpumax->{$_} ? $cpumax->{$_} * ($C{height}/100) : 1 - - } keys %$cpumax; - my $is_host_summary = exists $is_host_summary{$host}; my $rect_separator = undef; @@ -382,8 +382,8 @@ sub main_loop ($@) { my $rect_system = get_rect $rects, "$key;system"; my $rect_iowait = get_rect $rects, "$key;iowait"; my $rect_nice = get_rect $rects, "$key;nice"; - my $rect_max = get_rect $rects, "$key;max"; - + my $rect_peak; + unless ($is_host_summary || $C{togglecpu}) { $current_corenum = 0; $rect_separator = get_rect $rects, "$key;separator"; @@ -417,22 +417,34 @@ sub main_loop ($@) { $rect_iowait->height($heights{iowait}); $rect_iowait->x($x); $rect_iowait->y($y); - - $rect_max->width($width); - $rect_max->height(1); - $rect_max->x($x); - $rect_max->y($C{height} - $maxheights{system} - $maxheights{user}); my $system_n_user = sum @{$cpuaverage}{qw(user system)}; - my $max_system_n_user = sum @{$cpumax}{qw(user system)}; + my $max_system_n_user = 0; $app->fill($rect_iowait, Loadbars::BLACK); $app->fill($rect_nice, Loadbars::GREEN); - $app->fill($rect_max, $max_system_n_user > Loadbars::USER_WHITE ? Loadbars::WHITE - : ($max_system_n_user > Loadbars::USER_RED ? Loadbars::RED - : ($max_system_n_user > Loadbars::USER_ORANGE ? Loadbars::ORANGE - : ($max_system_n_user > Loadbars::USER_YELLOW0 ? Loadbars::YELLOW0 - : (Loadbars::YELLOW))))); + + if ($C{togglepeak}) { + 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_system_n_user = sum @{$cpumax}{qw(user system)}; + + $app->fill($rect_peak, $max_system_n_user > Loadbars::USER_WHITE ? Loadbars::WHITE + : ($max_system_n_user > Loadbars::USER_RED ? Loadbars::RED + : ($max_system_n_user > Loadbars::USER_ORANGE ? Loadbars::ORANGE + : ($max_system_n_user > Loadbars::USER_YELLOW0 ? Loadbars::YELLOW0 + : (Loadbars::YELLOW))))); + } + $app->fill($rect_user, $system_n_user > Loadbars::USER_WHITE ? Loadbars::WHITE : ($system_n_user > Loadbars::USER_RED ? Loadbars::RED : ($system_n_user > Loadbars::USER_ORANGE ? Loadbars::ORANGE @@ -467,6 +479,8 @@ sub main_loop ($@) { $app->print($x, $y+=$space, sprintf '%d%s', $cpuaverage->{user}, 'us'); $app->print($x, $y+=$space, sprintf '%d%s', $cpuaverage->{system}, 'sy'); $app->print($x, $y+=$space, sprintf '%d%s', $system_n_user, 'su'); + $app->print($x, $y+=$space, sprintf '%d%s', $max_system_n_user, 'pk') + if $C{togglepeak}; unless ($is_host_summary) { if (defined $loadavg[0]) { @@ -543,6 +557,7 @@ Explanation text display: us = User cpu usage in % sy = System cpu sage in % su = System & user cpu usage in % + pk = Max System & user cpu usage peak of last avg. samples in % avg = System load average (desc. order: 1, 5 and 15 min. avg.) END @@ -559,6 +574,7 @@ END 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 }, + 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 }, factor => { menupos => 7, help => 'Set graph scale factor (1.0 means 100%)', mode => 6, type => 's' }, @@ -587,6 +603,9 @@ END toggletxt => { menupos => 22, help => 'Toggle text display (0 or 1)', mode => 7, type => 'i' }, toggletxt_hot => { menupos => 23, cmd => 't', help => 'Toggle text display', mode => 1 }, + togglepeak => { menupos => 22, help => 'Toggle peak display (0 or 1)', mode => 7, type => 'i' }, + togglepeak_hot => { menupos => 23, cmd => 'p', help => 'Toggle peak display', mode => 1 }, + width => { menupos => 24, help => 'Set windows width', mode => 6, type => 'i' }, ); @@ -667,6 +686,27 @@ END return (\$hosts, $closure); } +sub get_cluster_hosts ($) { + my $cluster = shift; + my $confile = '/etc/clusters'; + + open my $fh, $confile or error "$!: $confile"; + my $hosts; + + while (<$fh>) { + if (/^$cluster\s*(.*)/) { + $hosts = $1; + last; + } + } + + close $fh; + + error "No such cluster in $confile: $cluster" unless defined $hosts; + + return split /\s+/, $hosts; +} + sub main () { my ($hosts, $dispatch) = dispatch_table; my $usage; @@ -682,13 +722,15 @@ sub main () { my @hosts = split ',', $$hosts; - if (@hosts) { + if (@hosts || defined $C{cluster}) { + push @hosts, get_cluster_hosts $C{cluster} if defined $C{cluster}; system 'ssh-add'; } else { @hosts = 'localhost'; } + my @threads = create_threads @hosts; main_loop $dispatch, @threads; } -- cgit v1.2.3 From 90d2dccee1583fc904de216a3f21d4c37f598d45 Mon Sep 17 00:00:00 2001 From: "Paul Buetow (mars)" Date: Mon, 26 Dec 2011 14:40:14 +0100 Subject: It is now -master --- loadbars | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/loadbars b/loadbars index b212f5c..d014f8b 100755 --- a/loadbars +++ b/loadbars @@ -26,7 +26,7 @@ use threads::shared; use constant { DEPTH => 8, - VERSION => 'loadbars v0.3.0', + VERSION => 'loadbars v0.3.0-master', Copyright => '2010-2011 (c) Paul Buetow ', BLACK => SDL::Color->new(-r => 0x00, -g => 0x00, -b => 0x00), BLUE => SDL::Color->new(-r => 0x00, -g => 0x00, -b => 0xff), -- cgit v1.2.3 From 2d3408b4aa5c420c061f6b847f7b5044db40efc6 Mon Sep 17 00:00:00 2001 From: "Paul Buetow (mars)" Date: Mon, 26 Dec 2011 14:49:48 +0100 Subject: Add changelog --- CHANGELOG | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 5b4e3b0..a57a70f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,12 @@ +Mon Dec 26 14:46:25 CET 2011 +* Releases 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 -- cgit v1.2.3 From e5602aad08db086a7063e6ded6f05dda417c9c47 Mon Sep 17 00:00:00 2001 From: "pbuetow (lxpbuetow)" Date: Tue, 27 Dec 2011 13:05:23 +0100 Subject: Bugfix --- CHANGELOG | 3 +++ loadbars | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index c50f79d..3c436c6 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +Tue Dec 27 13:04:43 CET 2011 +* Bugfix: Max system+user peak is never >100% + Tue Dec 27 12:28:40 CET 2011 * Released v0.3.1 * --cluster option (which reads the ClusterSSH config file /etc/clusters/) diff --git a/loadbars b/loadbars index 1995bb0..ad4f371 100755 --- a/loadbars +++ b/loadbars @@ -26,7 +26,7 @@ use threads::shared; use constant { DEPTH => 8, - VERSION => 'loadbars v0.3.1', + VERSION => 'loadbars v0.3.1-devel', Copyright => '2010-2011 (c) Paul Buetow ', CSSH_CONFFILE => '/etc/clusters', CSSH_MAX_RECURSION => 10, @@ -176,7 +176,7 @@ sub normalize_loads (%) { sub get_cpuaverage ($@) { my ($factor, @loads) = @_; - my (%cpumax, %cpuaverage); + my (%cpumax, %cpuaverage); for my $l (@loads) { for (keys %$l) { @@ -439,6 +439,7 @@ sub main_loop ($@) { $rect_peak->y($C{height} - $maxheights{system} - $maxheights{user}); $max_system_n_user = sum @{$cpumax}{qw(user system)}; + $max_system_n_user = 100 if $max_system_n_user > 100; $app->fill($rect_peak, $max_system_n_user > Loadbars::USER_WHITE ? Loadbars::WHITE : ($max_system_n_user > Loadbars::USER_RED ? Loadbars::RED -- cgit v1.2.3 From 7532f8adc0f3e8fd99cadc23f6ecbfd809178b84 Mon Sep 17 00:00:00 2001 From: "pbuetow (lxpbuetow)" Date: Tue, 27 Dec 2011 13:09:26 +0100 Subject: it is master not devel --- loadbars | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/loadbars b/loadbars index ad4f371..121ebe6 100755 --- a/loadbars +++ b/loadbars @@ -26,7 +26,7 @@ use threads::shared; use constant { DEPTH => 8, - VERSION => 'loadbars v0.3.1-devel', + VERSION => 'loadbars v0.3.1-master', Copyright => '2010-2011 (c) Paul Buetow ', CSSH_CONFFILE => '/etc/clusters', CSSH_MAX_RECURSION => 10, -- cgit v1.2.3 From 0570cf65895b94940dc3550e0cd2eab49f95be91 Mon Sep 17 00:00:00 2001 From: "Paul Buetow (venus)" Date: Sat, 21 Jan 2012 15:54:15 +0100 Subject: Add help file --- CHANGELOG | 3 +++ HELP | 30 ++++++++++++++++++++++++++++++ README | 20 +------------------- 3 files changed, 34 insertions(+), 19 deletions(-) create mode 100644 HELP diff --git a/CHANGELOG b/CHANGELOG index 3c436c6..00f5d60 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +Sat Jan 21 15:54:06 CET 2012 +* Add HELP file + Tue Dec 27 13:04:43 CET 2011 * Bugfix: Max system+user peak is never >100% diff --git a/HELP b/HELP new file mode 100644 index 0000000..ead04a7 --- /dev/null +++ b/HELP @@ -0,0 +1,30 @@ +Explanation colors: + Blue: System cpu usage + Purple: System usage if system cpu is >30% + Yellow: User cpu usage + Darker yellow: User usage if system & user cpu is >50% + Orange: User usage if system & user cpu is >70% + White: Usage usage if system & user cpu is >99% + Green: Nice cpu usage + 1px horizontal line: Maximum sy+us cpu of last 'avg' samples +Explanation text display: + ni = Nice cpu usage in % + us = User cpu usage in % + sy = System cpu sage in % + su = System & user cpu usage in % + pk = Max System & user cpu usage peak of last avg. samples in % + avg = System load average (desc. order: 1, 5 and 15 min. avg.) +--togglecpu - Toggle CPUs (0 or 1) +--average - Num of samples for avg. (more fluent animations) +--cluster - Cluster name from /etc/clusters +--factor - Set graph scale factor (1.0 means 100%) +--height - Set windows height +--hosts - Comma separated list of hosts +--inter - Set update interval in seconds (default 0.1) +--samples - Set number of samples until ssh reconnects +--sshopts - Set SSH options +--title - Set the window title +--toggletxthost - Toggle hostname/num text display (0 or 1) +--togglepeak - Toggle peak display (0 or 1) +--toggletxt - Toggle text display (0 or 1) +--width - Set windows width diff --git a/README b/README index 1553511..6b86bf3 100644 --- a/README +++ b/README @@ -22,22 +22,4 @@ Versioning schema: a.b.c[.d], where It's not a release if there is a -devel suffix. It's a development version (in the trunk) then. -Explanation colors: - Blue: System cpu usage - Purple: System usage if it's >30% - Yellow: User cpu usage - Darker yellow: User usage if system & user cpu is >50% - Orange: User usage if system & user cpu is >70% - White: Usage usage if system & user cpu is >99% - Green: Nice cpu usage - -Explanation text display: - ni = Nice cpu usage in % - us = User cpu usage in % - sy = System cpu sage in % - su = System & user cpu usage in % - avg = System load average (desc. order: 1, 5 and 15 min. avg.) - - - - +For some help/usage please consult the HELP file. -- cgit v1.2.3 From 0902ddf2efa6196bf67f87207d7e6f0f74de51f7 Mon Sep 17 00:00:00 2001 From: "Paul Buetow (venus)" Date: Sat, 21 Jan 2012 18:33:11 +0100 Subject: Release v0.4.0 --- CHANGELOG | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 95fb74c..f035904 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,5 @@ +Sat Jan 21 18:32:45 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) -- cgit v1.2.3 From f95323f9e6b322908d23225ae07bf510b9433ba1 Mon Sep 17 00:00:00 2001 From: "Paul Buetow (venus)" Date: Sat, 21 Jan 2012 18:53:02 +0100 Subject: typos --- CHANGELOG | 3 +++ loadbars | 10 +++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index f035904..029bc15 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +Sat Jan 21 18:52:50 CET 2012 +* Fixed some docu typos + Sat Jan 21 18:32:45 CET 2012 * Released v0.4.0 * Also show stats for idle, iowait, irq, softirq, steal and guest cpu time diff --git a/loadbars b/loadbars index 246a7d4..80663ee 100755 --- a/loadbars +++ b/loadbars @@ -25,8 +25,8 @@ use threads; use threads::shared; use constant { - VERSION => 'loadbars v0.4.0-devel', - Copyright => '2010-2011 (c) Paul Buetow ', + VERSION => 'loadbars v0.4.0-master', + Copyright => '2010 - 2012 (c) Paul Buetow ', CSSH_CONFFILE => '/etc/clusters', CSSH_MAX_RECURSION => 10, COLOR_DEPTH => 8, @@ -623,7 +623,7 @@ Explanations: Color: White ir = IRQ usage in % (extended) Color: White - io = IOwait cpu sage in % + io = IOwait cpu usage in % Color: Purple id = Idle cpu usage in % (extended) Color: Black @@ -631,9 +631,9 @@ Explanations: Color: Green us = User cpu usage in % Color: Yellow, dark yellow if to>50%, orange if to>50% - sy = System cpu sage in % + sy = System cpu usage in % Blue, lighter blue if >30% - to = Total CPU usage, which is (100% - id) + 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) -- cgit v1.2.3 From 10487a8d77e3e461e8564d0c0895333a0608c83c Mon Sep 17 00:00:00 2001 From: "Paul Buetow (mars)" Date: Sun, 29 Jan 2012 12:20:21 +0100 Subject: Convert tabs by whitespaces --- loadbars | 1112 +++++++++++++++++++++++++++++++------------------------------- 1 file changed, 556 insertions(+), 556 deletions(-) mode change 100755 => 100644 loadbars diff --git a/loadbars b/loadbars old mode 100755 new mode 100644 index 80663ee..18f45f5 --- a/loadbars +++ b/loadbars @@ -25,28 +25,28 @@ use threads; use threads::shared; use constant { - VERSION => 'loadbars v0.4.0-master', - Copyright => '2010 - 2012 (c) Paul Buetow ', - CSSH_CONFFILE => '/etc/clusters', + VERSION => 'loadbars v0.4.0-master', + Copyright => '2010 - 2012 (c) Paul Buetow ', + 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), - 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, - NULL => 0, - DEBUG => 0, + 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), + 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, + NULL => 0, + DEBUG => 0, }; $| = 1; @@ -60,19 +60,19 @@ my %C : shared; # Setting defaults %C = ( - title => Loadbars::VERSION . ' (press h for help on stdout)', + title => Loadbars::VERSION . ' (press h for help on stdout)', average => 15, - showcores => 0, - cpuregexp => 'cpu', - factor => 1, - extended => 0, - displaytxt => 1, - displaytxthost => 0, - inter => 0.1, - samples => 1000, - sshopts => '', - width => 1250, - height => 230, + showcores => 0, + cpuregexp => 'cpu', + factor => 1, + extended => 0, + displaytxt => 1, + displaytxthost => 0, + inter => 0.1, + samples => 1000, + sshopts => '', + width => 1250, + height => 230, ); # Quick n dirty helpers @@ -93,392 +93,392 @@ sub norm ($) { sub parse_cpu_line ($) { my $line = shift; - my ($name, %load); + my ($name, %load); - ($name, @load{qw(user nice system idle iowait irq softirq steal guest)}) = split ' ', $line; + ($name, @load{qw(user nice system idle iowait irq softirq steal guest)}) = split ' ', $line; $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)}; + $load{TOTAL} = sum @load{qw(user nice system idle iowait irq softirq steal guest)}; - return ($name, \%load); + return ($name, \%load); } sub thread_get_stats ($;$) { - my ($host, $user) = @_; - $user = defined $user ? "-l $user" : ''; - - my ($sigusr1, $quit) = (0, 0); - my $loadavgexp = qr/(\d+\.\d{2}) (\d+\.\d{2}) (\d+\.\d{2})/; - - for (;;) { - my $bash = <<"BASH"; - if [ -e /proc/stat ]; then - loadavg=/proc/loadavg - stat=/proc/stat - meminfo=/proc/meminfo - - for i in \$(seq $C{samples}); do - cat \$loadavg \$stat \$meminfo - sleep $C{inter} - done - else - loadavg=/compat/linux/proc/loadavg - stat=/compat/linux/proc/stat - - for i in \$(jot $C{samples}); do - cat \$loadavg \$stat - sleep $C{inter} - done - fi + my ($host, $user) = @_; + $user = defined $user ? "-l $user" : ''; + + my ($sigusr1, $quit) = (0, 0); + my $loadavgexp = qr/(\d+\.\d{2}) (\d+\.\d{2}) (\d+\.\d{2})/; + + for (;;) { + my $bash = <<"BASH"; + if [ -e /proc/stat ]; then + loadavg=/proc/loadavg + stat=/proc/stat + meminfo=/proc/meminfo + + for i in \$(seq $C{samples}); do + cat \$loadavg \$stat \$meminfo + sleep $C{inter} + done + else + loadavg=/compat/linux/proc/loadavg + stat=/compat/linux/proc/stat + + for i in \$(jot $C{samples}); do + cat \$loadavg \$stat + sleep $C{inter} + done + fi BASH - my $cmd = $host eq 'localhost' ? $bash - : "ssh $user -o StrictHostKeyChecking=no $C{sshopts} $host '$bash'"; + my $cmd = $host eq 'localhost' ? $bash + : "ssh $user -o StrictHostKeyChecking=no $C{sshopts} $host '$bash'"; - my $pid = open my $pipe, "$cmd |" or do { - say "Warning: $!"; - sleep 3; - next; - }; + my $pid = open my $pipe, "$cmd |" or do { + say "Warning: $!"; + sleep 3; + next; + }; - # Toggle CPUs - $SIG{USR1} = sub { $sigusr1 = 1 }; - my $cpuregexp = qr/$C{cpuregexp}/; + # Toggle CPUs + $SIG{USR1} = sub { $sigusr1 = 1 }; + my $cpuregexp = qr/$C{cpuregexp}/; - while (<$pipe>) { - if (/^$loadavgexp/) { - $AVGSTATS{$host} = "$1;$2;$3"; + while (<$pipe>) { + if (/^$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 (/$cpuregexp/) { + my ($name, $load) = parse_cpu_line $_; + $CPUSTATS{"$host;$name"} = join ';', + map { $_ . '=' . $load->{$_} } + grep { defined $load->{$_} } keys %$load; + } - if ($sigusr1) { - $cpuregexp = qr/$C{cpuregexp}/; - $sigusr1 = 0; - } - } + if ($sigusr1) { + $cpuregexp = qr/$C{cpuregexp}/; + $sigusr1 = 0; + } + } - } + } - return undef; + return undef; } sub get_rect ($$) { - my ($rects, $name) = @_; + my ($rects, $name) = @_; - return $rects->{$name} if exists $rects->{$name}; - return $rects->{$name} = SDL::Rect->new(); + return $rects->{$name} if exists $rects->{$name}; + return $rects->{$name} = SDL::Rect->new(); } sub normalize_loads (%) { - my %loads = @_; + my %loads = @_; - return %loads unless exists $loads{TOTAL}; + return %loads unless exists $loads{TOTAL}; - my $total = $loads{TOTAL} == 0 ? 1 : $loads{TOTAL}; - return map { $_ => $loads{$_} / ($total / 100) } keys %loads; + my $total = $loads{TOTAL} == 0 ? 1 : $loads{TOTAL}; + return map { $_ => $loads{$_} / ($total / 100) } keys %loads; } sub get_cpuaverage ($@) { - my ($factor, @loads) = @_; - my (%cpumax, %cpuaverage); + my ($factor, @loads) = @_; + my (%cpumax, %cpuaverage); - for my $l (@loads) { - for (keys %$l) { - $cpuaverage{$_} += $l->{$_}; + for my $l (@loads) { + for (keys %$l) { + $cpuaverage{$_} += $l->{$_}; $cpumax{$_} = $l->{$_} if not exists $cpumax{$_} or $cpumax{$_} < $l->{$_}; } - } + } - my $div = @loads / $factor; + my $div = @loads / $factor; for (keys %cpuaverage) { - $cpuaverage{$_} /= $div; - $cpumax{$_} /= $factor; + $cpuaverage{$_} /= $div; + $cpumax{$_} /= $factor; } - return (\%cpumax, \%cpuaverage); + return (\%cpumax, \%cpuaverage); } sub draw_background ($$) { - my ($app, $rects) = @_; - my $rect = get_rect $rects, 'background'; + my ($app, $rects) = @_; + my $rect = get_rect $rects, 'background'; - $rect->width($C{width}); - $rect->height($C{height}); - $app->fill($rect, Loadbars::BLACK); - $app->update($rect); + $rect->width($C{width}); + $rect->height($C{height}); + $app->fill($rect, Loadbars::BLACK); + $app->update($rect); - return undef; + return undef; } sub create_threads (@) { - return map { - $_->detach(); - $_; + return map { + $_->detach(); + $_; - } map { - threads->create('thread_get_stats', split /:/); + } map { + threads->create('thread_get_stats', split /:/); - } @_; + } @_; } sub main_loop ($@) { - my ($dispatch, @threads) = @_; - - my $app = SDL::App->new( - -title => $C{title}, - -icon_title => $C{title}, - -width => $C{width}, - -height => $C{height}, - -depth => Loadbars::COLOR_DEPTH, - -resizeable => 0, - ); - - SDL::Font->new('font.png')->use(); - - my $num_stats = keys %CPUSTATS; - - my $rects = {}; - my %prev_stats; - my %last_loads; - - my $redraw_background = 0; - my $font_height = 14; - - my $displayinfo_time = 5; - my $displayinfo_start = 0; - my $displayinfo : shared = ''; - my $infotxt : shared = ''; - my $quit : shared = 0; - - my ($t1, $t2) = (Time::HiRes::time(), undef); - my $event = SDL::Event->new(); - - my $event_thread = async { - for (;;) { - $event->pump(); - $event->poll(); - $event->wait(); - - my $type = $event->type(); - my $key_name = $event->key_name(); - - debugsay "Event type=$type key_name=$key_name" if Loadbars::DEBUG; - next if $type != 2; - - if ($key_name eq '1') { - $C{showcores} = !$C{showcores}; - set_showcores_regexp; - $_->kill('USR1') for @threads; - %AVGSTATS = (); - %CPUSTATS = (); - $displayinfo = 'Toggled CPUs'; - - } elsif ($key_name eq 'e') { - $C{extended} = !$C{extended}; - $displayinfo = 'Toggled extended display'; - - } elsif ($key_name eq 'h') { - say '=> Hotkeys to use in the SDL interface'; - say $dispatch->('hotkeys'); - $displayinfo = 'Hotkeys help printed on terminal stdout'; - - } elsif ($key_name eq 't') { - $C{displaytxt} = !$C{displaytxt}; - $displayinfo = 'Toggled text display'; - - } elsif ($key_name eq 'u') { - $C{displaytxthost} = !$C{displaytxthost}; - $displayinfo = 'Toggled number/hostname display'; - - } elsif ($key_name eq 'q') { - $quit = 1; - last; - - # Increase and decrease pairs - } elsif ($key_name eq 'a') { - ++$C{average}; - $displayinfo = "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; - $displayinfo = "Set sample average to $C{average}"; - - } elsif ($key_name eq 's') { - $C{factor} += 0.1; - $displayinfo = "Set scale factor to $C{factor}"; - } elsif ($key_name eq 'x' or $key_name eq 'z') { - $C{factor} -= 0.1; - $displayinfo = "Set scale factor to $C{factor}"; - - } elsif ($key_name eq 'd') { - $C{inter} += 0.1; - $displayinfo = "Set graph update interval to $C{inter}"; - } elsif ($key_name eq 'c' or $key_name eq 'z') { - my $int = $C{inter}; - $int -= 0.1; - $C{inter} = $int > 0 ? $int : 0.1; - $displayinfo = "Set graph update interval to $C{inter}"; - } - } - }; - - do { - my ($x, $y) = (0, 0); - my %is_host_summary; - - my $new_num_stats = keys %CPUSTATS; - - if ($new_num_stats != $num_stats) { - %prev_stats = (); - %last_loads = (); - - $num_stats = $new_num_stats; - $redraw_background = 1; - } - - # Avoid division by null - # Also substract 1 (each bar is followed by an 1px separator bar) - my $width = $C{width} / ($num_stats ? $num_stats : 1) - 1; - - my ($current_barnum, $current_corenum) = (-1, -1); - - for my $key (sort keys %CPUSTATS) { - ++$current_barnum; - ++$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 = exists $is_host_summary{$host}; - - 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; - - unless ($is_host_summary || !$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::GREY); - } - - $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 ($dispatch, @threads) = @_; + + my $app = SDL::App->new( + -title => $C{title}, + -icon_title => $C{title}, + -width => $C{width}, + -height => $C{height}, + -depth => Loadbars::COLOR_DEPTH, + -resizeable => 0, + ); + + SDL::Font->new('font.png')->use(); + + my $num_stats = keys %CPUSTATS; + + my $rects = {}; + my %prev_stats; + my %last_loads; + + my $redraw_background = 0; + my $font_height = 14; + + my $displayinfo_time = 5; + my $displayinfo_start = 0; + my $displayinfo : shared = ''; + my $infotxt : shared = ''; + my $quit : shared = 0; + + my ($t1, $t2) = (Time::HiRes::time(), undef); + my $event = SDL::Event->new(); + + my $event_thread = async { + for (;;) { + $event->pump(); + $event->poll(); + $event->wait(); + + my $type = $event->type(); + my $key_name = $event->key_name(); + + debugsay "Event type=$type key_name=$key_name" if Loadbars::DEBUG; + next if $type != 2; + + if ($key_name eq '1') { + $C{showcores} = !$C{showcores}; + set_showcores_regexp; + $_->kill('USR1') for @threads; + %AVGSTATS = (); + %CPUSTATS = (); + $displayinfo = 'Toggled CPUs'; + + } elsif ($key_name eq 'e') { + $C{extended} = !$C{extended}; + $displayinfo = 'Toggled extended display'; + + } elsif ($key_name eq 'h') { + say '=> Hotkeys to use in the SDL interface'; + say $dispatch->('hotkeys'); + $displayinfo = 'Hotkeys help printed on terminal stdout'; + + } elsif ($key_name eq 't') { + $C{displaytxt} = !$C{displaytxt}; + $displayinfo = 'Toggled text display'; + + } elsif ($key_name eq 'u') { + $C{displaytxthost} = !$C{displaytxthost}; + $displayinfo = 'Toggled number/hostname display'; + + } elsif ($key_name eq 'q') { + $quit = 1; + last; + + # Increase and decrease pairs + } elsif ($key_name eq 'a') { + ++$C{average}; + $displayinfo = "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; + $displayinfo = "Set sample average to $C{average}"; + + } elsif ($key_name eq 's') { + $C{factor} += 0.1; + $displayinfo = "Set scale factor to $C{factor}"; + } elsif ($key_name eq 'x' or $key_name eq 'z') { + $C{factor} -= 0.1; + $displayinfo = "Set scale factor to $C{factor}"; + + } elsif ($key_name eq 'd') { + $C{inter} += 0.1; + $displayinfo = "Set graph update interval to $C{inter}"; + } elsif ($key_name eq 'c' or $key_name eq 'z') { + my $int = $C{inter}; + $int -= 0.1; + $C{inter} = $int > 0 ? $int : 0.1; + $displayinfo = "Set graph update interval to $C{inter}"; + } + } + }; + + do { + my ($x, $y) = (0, 0); + my %is_host_summary; + + my $new_num_stats = keys %CPUSTATS; + + if ($new_num_stats != $num_stats) { + %prev_stats = (); + %last_loads = (); + + $num_stats = $new_num_stats; + $redraw_background = 1; + } + + # Avoid division by null + # Also substract 1 (each bar is followed by an 1px separator bar) + my $width = $C{width} / ($num_stats ? $num_stats : 1) - 1; + + my ($current_barnum, $current_corenum) = (-1, -1); + + for my $key (sort keys %CPUSTATS) { + ++$current_barnum; + ++$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 = exists $is_host_summary{$host}; + + 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; + + unless ($is_host_summary || !$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::GREY); + } + + $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::BLACK); - $app->fill($rect_steal, Loadbars::RED); - $app->fill($rect_guest, Loadbars::RED); - $app->fill($rect_irq, Loadbars::WHITE); - $app->fill($rect_softirq, Loadbars::WHITE); - $app->fill($rect_nice, Loadbars::GREEN); - $app->fill($rect_iowait, Loadbars::PURPLE); + + $app->fill($rect_idle, Loadbars::BLACK); + $app->fill($rect_steal, Loadbars::RED); + $app->fill($rect_guest, Loadbars::RED); + $app->fill($rect_irq, Loadbars::WHITE); + $app->fill($rect_softirq, Loadbars::WHITE); + $app->fill($rect_nice, Loadbars::GREEN); + $app->fill($rect_iowait, Loadbars::PURPLE); if ($C{extended}) { my %maxheights = map { @@ -494,31 +494,31 @@ sub main_loop ($@) { $max_all = sum @{$cpumax}{qw(user system iowait irq softirq steal guest)}; $app->fill($rect_peak, $max_all > Loadbars::USER_ORANGE ? Loadbars::ORANGE - : ($max_all > Loadbars::USER_YELLOW0 ? Loadbars::YELLOW0 - : (Loadbars::YELLOW))); + : ($max_all > Loadbars::USER_YELLOW0 ? Loadbars::YELLOW0 + : (Loadbars::YELLOW))); } - $app->fill($rect_user, $all > Loadbars::USER_ORANGE ? Loadbars::ORANGE - : ($all > Loadbars::USER_YELLOW0 ? Loadbars::YELLOW0 - : (Loadbars::YELLOW))); - $app->fill($rect_system, $cpuaverage->{system} > Loadbars::SYSTEM_BLUE0 ? Loadbars::BLUE0 - : Loadbars::BLUE); - - my ($y, $space) = (5, $font_height); - my @loadavg = split ';', $AVGSTATS{$host}; - $is_host_summary{$host} = 1 if defined $loadavg[0]; - - if ($C{displaytxt}) { - if ($C{displaytxthost} && not $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); - } + $app->fill($rect_user, $all > Loadbars::USER_ORANGE ? Loadbars::ORANGE + : ($all > Loadbars::USER_YELLOW0 ? Loadbars::YELLOW0 + : (Loadbars::YELLOW))); + $app->fill($rect_system, $cpuaverage->{system} > Loadbars::SYSTEM_BLUE0 ? Loadbars::BLUE0 + : Loadbars::BLUE); + + my ($y, $space) = (5, $font_height); + my @loadavg = split ';', $AVGSTATS{$host}; + $is_host_summary{$host} = 1 if defined $loadavg[0]; + + if ($C{displaytxt}) { + if ($C{displaytxthost} && not $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}) { @@ -528,35 +528,35 @@ sub main_loop ($@) { $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->{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 $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}; unless ($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]); - } + 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]); + } } - } + } - # Display an informational text message if any + # Display an informational text message if any #$app->print(0, $y+=$space, $displayinfo) if length $displayinfo; if (length $displayinfo) { say $displayinfo; $displayinfo = ''; } - - $app->update( + + $app->update( $rect_idle, $rect_iowait, $rect_irq, @@ -568,52 +568,52 @@ sub main_loop ($@) { $rect_user, ); - $app->update($rect_separator) if defined $rect_separator; + $app->update($rect_separator) if defined $rect_separator; - $x += $width + 1; - } + $x += $width + 1; + } TIMEKEEPER: - $t2 = Time::HiRes::time(); - - if (length $displayinfo) { - if ($displayinfo_start == 0) { - $displayinfo_start = $t2; - - } else { - if ($displayinfo_time < $t2 - $displayinfo_start) { - $displayinfo = ''; - $displayinfo_start = 0; - } - } - } - - if ($C{inter} > $t2 - $t1) { - usleep 10000; - # Goto is OK if you don't produce spaghetti code - goto TIMEKEEPER; - } - - $t1 = $t2; - - if ($redraw_background) { - draw_background $app, $rects; - $redraw_background = 0; - } - - } until $quit; - - say "Good bye"; - # $_->kill('STOP') for @threads; - $event_thread->join(); - exit 0; + $t2 = Time::HiRes::time(); + + if (length $displayinfo) { + if ($displayinfo_start == 0) { + $displayinfo_start = $t2; + + } else { + if ($displayinfo_time < $t2 - $displayinfo_start) { + $displayinfo = ''; + $displayinfo_start = 0; + } + } + } + + if ($C{inter} > $t2 - $t1) { + usleep 10000; + # Goto is OK if you don't produce spaghetti code + goto TIMEKEEPER; + } + + $t1 = $t2; + + if ($redraw_background) { + draw_background $app, $rects; + $redraw_background = 0; + } + + } until $quit; + + say "Good bye"; + # $_->kill('STOP') for @threads; + $event_thread->join(); + exit 0; } sub dispatch_table () { - my $hosts = ''; + my $hosts = ''; - my $textdesc = < { 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 }, + 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 }, - 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 }, + 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 peak display', mode => 1 }, + extended => { menupos => 6, help => 'Toggle extended display (0 or 1)', mode => 7, type => 'i' }, + extended_hot => { menupos => 23, cmd => 'e', help => 'Toggle peak display', 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 }, + 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' }, + height => { menupos => 10, help => 'Set windows height', mode => 6, type => 'i' }, - help_hot => { menupos => 11, cmd => 'h', help => 'Prints this help screen', mode => 1 }, + 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' }, + hosts => { menupos => 12, help => 'Comma sep. list of hosts; optional: user@ in front to each host', var => \$hosts, mode => 6, type => 's' }, - inter => { menupos => 13, help => 'Set update interval in seconds (default 0.1)', mode => 7, type => 's' }, - inter_hot_up => { menupos => 14, cmd => 'd', help => 'Increases update interval in seconds by 0.1', mode => 1 }, - inter_hot_dn => { menupos => 15, cmd => 'c', help => 'Decreases update interval in seconds by 0.1', mode => 1 }, + inter => { menupos => 13, help => 'Set update interval in seconds (default 0.1)', mode => 7, type => 's' }, + inter_hot_up => { menupos => 14, cmd => 'd', help => 'Increases update interval in seconds by 0.1', mode => 1 }, + inter_hot_dn => { menupos => 15, cmd => 'c', help => 'Decreases update interval in seconds by 0.1', mode => 1 }, - quit_hot => { menupos => 16, cmd => 'q', help => 'Quits', mode => 1 }, + quit_hot => { menupos => 16, cmd => 'q', help => 'Quits', mode => 1 }, - samples => { menupos => 17, help => 'Set number of samples until ssh reconnects', mode => 6, type => 'i' }, + 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 CPUs', mode => 1 }, + showcores => { menupos => 17, help => 'Toggle core display (0 or 1)', mode => 7, type => 'i' }, + showcores_hot => { menupos => 17, cmd => '1', help => 'Toggle CPUs', 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 }, + 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 }, + 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' }, - title => { menupos => 21, help => 'Set the window title', var => \$C{title}, mode => 6, type => 's' }, + sshopts => { menupos => 20, help => 'Set SSH options', mode => 6, type => 's' }, + title => { menupos => 21, help => 'Set the window title', var => \$C{title}, mode => 6, type => 's' }, - width => { menupos => 24, help => 'Set windows width', mode => 6, type => 'i' }, - ); + width => { menupos => 24, help => 'Set windows width', mode => 6, type => 'i' }, + ); - my %d_by_short = map { - $d{$_}{cmd} => $d{$_} + my %d_by_short = map { + $d{$_}{cmd} => $d{$_} - } grep { - exists $d{$_}{cmd} + } grep { + exists $d{$_}{cmd} - } keys %d; + } keys %d; - my $closure = sub ($;$) { - my ($arg, @rest) = @_; + my $closure = sub ($;$) { + my ($arg, @rest) = @_; - if ($arg eq 'command') { - my ($cmd, @args) = @rest; + if ($arg eq 'command') { + my ($cmd, @args) = @rest; - my $cb = $d{$cmd}; - $cb = $d_by_short{$cmd} unless defined $cb; + my $cb = $d{$cmd}; + $cb = $d_by_short{$cmd} unless defined $cb; - unless (defined $cb) { - system $cmd; - return 0; - } + 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; - } - } + 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}" + } 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}; + } 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); + } 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 { - "--$_ \t- $d{$_}{help}" - } + } elsif ($arg eq 'usage') { + $textdesc . (join "\n", map { + if ($_ eq 'help') { + "--$_\t\t- $d{$_}{help}" + } else { + "--$_ \t- $d{$_}{help}" + } - } grep { - $d{$_}{mode} & 2 and exists $d{$_}{help} + } grep { + $d{$_}{mode} & 2 and exists $d{$_}{help} - } sort { $d{$a}{menupos} <=> $d{$b}{menupos} } sort keys %d); + } sort { $d{$a}{menupos} <=> $d{$b}{menupos} } sort keys %d); - } elsif ($arg eq 'options') { - map { - "$_=".$d{$_}{type} => (defined $d{$_}{var} ? $d{$_}{var} : \$C{$_}); + } elsif ($arg eq 'options') { + map { + "$_=".$d{$_}{type} => (defined $d{$_}{var} ? $d{$_}{var} : \$C{$_}); - } grep { - $d{$_}{mode} & 4 and exists $d{$_}{type}; + } grep { + $d{$_}{mode} & 4 and exists $d{$_}{type}; - } sort keys %d; - } - }; + } sort keys %d; + } + }; - $d{configuration}{cb} = sub { - say sort map { - "$_->[0] = $_->[1]" + $d{configuration}{cb} = sub { + say sort map { + "$_->[0] = $_->[1]" - } grep { - defined $_->[1] + } grep { + defined $_->[1] - } map { - [$_ => exists $d{$_}{var} ? ${$d{$_}{var}} : $C{$_}] + } map { + [$_ => exists $d{$_}{var} ? ${$d{$_}{var}} : $C{$_}] - } keys %d - }; + } keys %d + }; - return (\$hosts, $closure); + return (\$hosts, $closure); } # Recursuve function sub get_cluster_hosts ($;$); sub get_cluster_hosts ($;$) { - my ($cluster, $recursion) = @_; + my ($cluster, $recursion) = @_; unless (defined $recursion) { $recursion = 1; @@ -783,19 +783,19 @@ sub get_cluster_hosts ($;$) { } open my $fh, CSSH_CONFFILE or error "$!: " . CSSH_CONFFILE; - my $hosts; + my $hosts; - while (<$fh>) { - if (/^$cluster\s*(.*)/) { - $hosts = $1; - last; - } - } + while (<$fh>) { + if (/^$cluster\s*(.*)/) { + $hosts = $1; + last; + } + } - close $fh; + close $fh; unless (defined $hosts) { - error "No such cluster in " . CSSH_CONFFILE . ": $cluster" + error "No such cluster in " . CSSH_CONFFILE . ": $cluster" unless defined $recursion; return ($cluster); @@ -807,34 +807,34 @@ sub get_cluster_hosts ($;$) { } sub main () { - my ($hosts, $dispatch) = dispatch_table; - my $usage; + my ($hosts, $dispatch) = dispatch_table; + my $usage; - GetOptions ('help|?' => \$usage, $dispatch->('options')); + GetOptions ('help|?' => \$usage, $dispatch->('options')); - if (defined $usage) { - say $dispatch->('usage'); - exit 1; - } + if (defined $usage) { + say $dispatch->('usage'); + exit 1; + } - set_showcores_regexp; + set_showcores_regexp; - my @hosts = map { - my ($a, $b) = split /\@/, $_; - defined $b ? "$b:$a" : $a; - } split ',', $$hosts; + my @hosts = map { + my ($a, $b) = split /\@/, $_; + defined $b ? "$b:$a" : $a; + } split ',', $$hosts; - if (@hosts || defined $C{cluster}) { - push @hosts, get_cluster_hosts $C{cluster} if defined $C{cluster}; - system 'ssh-add'; + if (@hosts || defined $C{cluster}) { + push @hosts, get_cluster_hosts $C{cluster} if defined $C{cluster}; + system 'ssh-add'; - } else { - say $dispatch->('usage'); + } else { + say $dispatch->('usage'); exit 1; - } + } - my @threads = create_threads @hosts; - main_loop $dispatch, @threads; + my @threads = create_threads @hosts; + main_loop $dispatch, @threads; } main; -- cgit v1.2.3 From 617ec6163f5a3c408f467097a5ac7ff2963ce29b Mon Sep 17 00:00:00 2001 From: "Paul Buetow (mars)" Date: Sun, 29 Jan 2012 12:21:53 +0100 Subject: Prepare for v0.4.0.1 release --- loadbars | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/loadbars b/loadbars index 18f45f5..694bfa9 100644 --- a/loadbars +++ b/loadbars @@ -25,7 +25,7 @@ use threads; use threads::shared; use constant { - VERSION => 'loadbars v0.4.0-master', + VERSION => 'loadbars v0.4.0.1', Copyright => '2010 - 2012 (c) Paul Buetow ', CSSH_CONFFILE => '/etc/clusters', CSSH_MAX_RECURSION => 10, -- cgit v1.2.3 From 47014c815e77ce772d070f06d1fe008e3d17f7a2 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Sun, 29 Jan 2012 12:30:54 +0100 Subject: Changelog --- CHANGELOG | 3 ++- WISHLIST | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 029bc15..83d048f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,6 @@ -Sat Jan 21 18:52:50 CET 2012 +Sun Jan 29 12:30:00 CET 2012 * Fixed some docu typos +* Code cleanup: Replaced tabs by 4 whitespaces Sat Jan 21 18:32:45 CET 2012 * Released v0.4.0 diff --git a/WISHLIST b/WISHLIST index a309f07..1c87363 100644 --- a/WISHLIST +++ b/WISHLIST @@ -2,5 +2,4 @@ * Stats for memory * Stats for network * Dynamic/online resizing of the window -* Adding and removing hosts online * .deb for Debian and Ubuntu -- cgit v1.2.3 From 2ad50b481a05d30744531939ac5a108a06926110 Mon Sep 17 00:00:00 2001 From: "pbuetow (lap824)" Date: Sun, 29 Jan 2012 17:23:59 +0100 Subject: : --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 83d048f..64d004e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,5 @@ Sun Jan 29 12:30:00 CET 2012 +* Releases v0.4.0.1 * Fixed some docu typos * Code cleanup: Replaced tabs by 4 whitespaces -- cgit v1.2.3 From 389f995ab4e1aec24f5e0da292a3e70ba12d4183 Mon Sep 17 00:00:00 2001 From: "Paul Buetow (venus)" Date: Sat, 4 Feb 2012 11:10:20 +0100 Subject: Release of v0.5.0 --- loadbars | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 loadbars diff --git a/loadbars b/loadbars old mode 100644 new mode 100755 -- cgit v1.2.3 From da8b7a939a25b5618a69ecd055c21dc46c6c72c2 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Sat, 25 Feb 2012 20:15:22 +0100 Subject: cleanup in HELP file --- HELP | 5 ----- loadbars | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/HELP b/HELP index e077c51..ebf68f8 100644 --- a/HELP +++ b/HELP @@ -1,8 +1,3 @@ -==> Reading configuration from /home/pb/.loadbarsrc -==> Setting showcores=1, it might be overwritten by command line params. -==> Setting showtext=0, it might be overwritten by command line params. -==> Setting extended=1, it might be overwritten by command line params. -==> Setting showmem=1, it might be overwritten by command line params. CPU stuff: st = Steal in % [see man proc] (extended) Color: Red diff --git a/loadbars b/loadbars index 8b241d1..5f61e59 100755 --- a/loadbars +++ b/loadbars @@ -26,7 +26,7 @@ use threads; use threads::shared; use constant { - VERSION => 'loadbars v0.5.1', + VERSION => 'loadbars v0.5.1-master', COPYRIGHT => '2010-2012 (c) Paul Buetow ', CONFFILE => $ENV{HOME} . '/.loadbarsrc', CSSH_CONFFILE => '/etc/clusters', -- cgit v1.2.3 From 98506745e9ccc7ce1216b1d6e84e2fbcd76c48f6 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 9 Mar 2012 08:00:25 +0100 Subject: new readme stuff --- README | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README b/README index dbfe164..00a6de6 100644 --- a/README +++ b/README @@ -11,7 +11,7 @@ For a changelog see CHANGELOG For a list of all known bugs see BUGS -Supported platforms are: Linux and FreeBSD (the latter with linprocfs mounted). +Supported platforms are: Linux and FreeBSD (the latter with linprocfs mounted, the latter may not have full functionality). For Linux at least a 2.6 Kernel is required. @@ -21,7 +21,10 @@ Versioning schema: a.b.c[.d], where b = Major release (new features or major code refactoring) a = Don't know yet. Maybe will stay forever 0. + It's not a release if there is a -master suffix. It's a stable + version (master branch) then. + It's not a release if there is a -devel suffix. It's a development - version (in the trunk) then. + version (devel branch) then. For some help/usage please consult the HELP file. -- cgit v1.2.3 From 6ac0225ead3454a0aed6768584ad7fe9c87b7772 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 9 Mar 2012 08:09:41 +0100 Subject: add return --- README | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README b/README index 00a6de6..3d8e545 100644 --- a/README +++ b/README @@ -11,7 +11,8 @@ For a changelog see CHANGELOG For a list of all known bugs see BUGS -Supported platforms are: Linux and FreeBSD (the latter with linprocfs mounted, the latter may not have full functionality). +Supported platforms are: Linux and FreeBSD (the latter with linprocfs mounted, +the latter may not have full functionality). For Linux at least a 2.6 Kernel is required. -- cgit v1.2.3 From 5978aee6db88bfcd57abe1ce8fc5e5a2f7dd6e24 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 16 Mar 2012 07:27:10 +0100 Subject: Docu fix --- README | 3 ++- loadbars | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README b/README index 3d8e545..c493564 100644 --- a/README +++ b/README @@ -11,7 +11,8 @@ For a changelog see CHANGELOG For a list of all known bugs see BUGS -Supported platforms are: Linux and FreeBSD (the latter with linprocfs mounted, +Supported platforms are: Linux + the latter may not have full functionality). For Linux at least a 2.6 Kernel is required. diff --git a/loadbars b/loadbars index 9431ae5..ff7e1b7 100755 --- a/loadbars +++ b/loadbars @@ -28,7 +28,7 @@ use threads; use threads::shared; use constant { - VERSION => 'loadbars v0.5.1.1', + VERSION => 'loadbars v0.5.1.1-master', COPYRIGHT => '2010-2012 (c) Paul Buetow ', CONFFILE => $ENV{HOME} . '/.loadbarsrc', CSSH_CONFFILE => '/etc/clusters', -- cgit v1.2.3 From db1cdb1c45698006194c1bf545e1541f74795315 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 16 Mar 2012 22:05:08 +0100 Subject: bugfix only --- CHANGELOG | 4 ++++ loadbars | 10 ++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 2f49a68..49bec47 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,7 @@ +Fri Mar 16 22:03:18 CET 2012 +* Release v0.5.1.2 +* Bugfix only release + Fri Mar 16 07:20:50 CET 2012 * Release v0.5.1.1 * Dropped FreeBSD support / focus is Linux diff --git a/loadbars b/loadbars index ff7e1b7..78cceba 100755 --- a/loadbars +++ b/loadbars @@ -28,7 +28,7 @@ use threads; use threads::shared; use constant { - VERSION => 'loadbars v0.5.1.1-master', + VERSION => 'loadbars v0.5.1.2', COPYRIGHT => '2010-2012 (c) Paul Buetow ', CONFFILE => $ENV{HOME} . '/.loadbarsrc', CSSH_CONFFILE => '/etc/clusters', @@ -59,7 +59,7 @@ use constant { $| = 1; -my @PIDS : shared; +my %PIDS : shared; my %AVGSTATS : shared; my %CPUSTATS : shared; my %MEMSTATS : shared; @@ -200,7 +200,7 @@ sub terminate_pids (@) { display_info 'Terminating sub-processes, hasta la vista!'; $_->kill('TERM') for @threads; display_info_no_nl 'Terminating PIDs'; - for my $pid (@PIDS) { + for my $pid (keys %PIDS) { my $proc_table = Proc::ProcessTable->new(); for my $proc (@{$proc_table->table()}) { if ($proc->ppid == $pid) { @@ -252,7 +252,7 @@ BASH next; }; - push @PIDS, $pid; + $PIDS{$pid} = 1; # Toggle CPUs $SIG{USR1} = sub { $sigusr1 = 1 }; @@ -303,6 +303,8 @@ BASH last; } } + + delete $PIDS{$pid}; } return undef; -- cgit v1.2.3 From 62ef27dff47a5629e2a5b075824297cffae0d8c4 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 16 Mar 2012 22:11:42 +0100 Subject: new README --- README | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README b/README index c493564..2eacc74 100644 --- a/README +++ b/README @@ -19,8 +19,8 @@ For Linux at least a 2.6 Kernel is required. Versioning schema: a.b.c[.d], where d = Optional, bugfixes only release - c = Minor release (new features or minor code refactoring) - b = Major release (new features or major code refactoring) + c = Minor release (new minor features or code refactoring) + b = Major release (new major features) a = Don't know yet. Maybe will stay forever 0. It's not a release if there is a -master suffix. It's a stable -- cgit v1.2.3 From b09e956b543f57b4016de677a0b7880e743cd688 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 16 Mar 2012 22:19:36 +0100 Subject: introduced versioning shema for cosmetic only releases --- README | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README b/README index 2eacc74..d839d8d 100644 --- a/README +++ b/README @@ -17,11 +17,12 @@ the latter may not have full functionality). For Linux at least a 2.6 Kernel is required. -Versioning schema: a.b.c[.d], where - d = Optional, bugfixes only release - c = Minor release (new minor features or code refactoring) - b = Major release (new major features) +Versioning schema: a.b.c[.d][.e], where a = Don't know yet. Maybe will stay forever 0. + b = Major release (new major features) + c = Minor release (new minor features or code refactoring) + d = Optional, bugfixes only release + e = Optional, cosmetic fixes only release It's not a release if there is a -master suffix. It's a stable version (master branch) then. -- cgit v1.2.3 From b314697216acdd0adf61d174125b8951059e10d9 Mon Sep 17 00:00:00 2001 From: pbuetow Date: Sat, 17 Mar 2012 10:55:38 +0100 Subject: Some minor tweak --- loadbars | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/loadbars b/loadbars index 78cceba..d5f4de6 100755 --- a/loadbars +++ b/loadbars @@ -226,6 +226,10 @@ sub stats_thread ($;$) { my $loadavgexp = qr/(\d+\.\d{2}) (\d+\.\d{2}) (\d+\.\d{2})/; my $inter = INTERVAL; + # Toggle CPUs + $SIG{USR1} = sub { $sigusr1 = 1 }; + $SIG{TERM} = sub { $sigterm = 1 }; + until ($sigterm) { my $bash = <<"BASH"; loadavg=/proc/loadavg @@ -254,17 +258,12 @@ BASH $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; -- cgit v1.2.3 From 86ee73997c4cb51ed751efc4e4040696692658c8 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 6 Apr 2012 10:18:23 +0200 Subject: minor fixes --- CHANGELOG | 4 ++++ loadbars | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 49bec47..ce84d5e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,7 @@ +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 22:03:18 CET 2012 * Release v0.5.1.2 * Bugfix only release diff --git a/loadbars b/loadbars index d5f4de6..f1d2b45 100755 --- a/loadbars +++ b/loadbars @@ -28,7 +28,7 @@ use threads; use threads::shared; use constant { - VERSION => 'loadbars v0.5.1.2', + VERSION => 'loadbars v0.5.1.2-master', COPYRIGHT => '2010-2012 (c) Paul Buetow ', CONFFILE => $ENV{HOME} . '/.loadbarsrc', CSSH_CONFFILE => '/etc/clusters', @@ -456,6 +456,7 @@ sub main_loop ($@) { } elsif ( $key_name eq 'e' ) { $C{extended} = !$C{extended}; + $redraw_background = 1; display_info 'Toggled extended display'; } elsif ( $key_name eq 'h' ) { @@ -469,10 +470,12 @@ sub main_loop ($@) { } 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' ) { -- cgit v1.2.3 From 4d85ec71fdf3b787e3cb0ad8a2a716cdb77f51a7 Mon Sep 17 00:00:00 2001 From: pbuetow Date: Thu, 19 Apr 2012 21:40:06 +0200 Subject: minor change --- CHANGELOG | 3 +++ loadbars | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index ce84d5e..d75884e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +Thu Apr 19 21:39:38 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. diff --git a/loadbars b/loadbars index f1d2b45..41eb0d3 100755 --- a/loadbars +++ b/loadbars @@ -252,7 +252,7 @@ BASH my $pid = open my $pipe, "$cmd |" or do { say "Warning: $!"; - sleep 3; + sleep 0.5; next; }; -- cgit v1.2.3 From 42f7ad472267b0b0e943229e150370bccb680087 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Thu, 19 Apr 2012 23:40:41 +0200 Subject: release of v0.5.1.3 --- CHANGELOG | 5 ++--- loadbars | 6 +----- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index d75884e..0395a01 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,7 +1,6 @@ -Thu Apr 19 21:39:38 CEST 2012 +Thu Apr 19 23:40:20 CEST 2012 +* Release v0.5.1.3 * 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. diff --git a/loadbars b/loadbars index 41eb0d3..40dba2e 100755 --- a/loadbars +++ b/loadbars @@ -28,7 +28,7 @@ use threads; use threads::shared; use constant { - VERSION => 'loadbars v0.5.1.2-master', + VERSION => 'loadbars v0.5.1.3', COPYRIGHT => '2010-2012 (c) Paul Buetow ', CONFFILE => $ENV{HOME} . '/.loadbarsrc', CSSH_CONFFILE => '/etc/clusters', @@ -64,8 +64,6 @@ my %AVGSTATS : shared; my %CPUSTATS : shared; my %MEMSTATS : shared; my %MEMSTATS_HAS : shared; -#my %NETSTATS : shared; -#my %NETSTATS_HAS : shared; # Global configuration hash my %C : shared; @@ -1271,5 +1269,3 @@ sub main () { } main; - -1; -- cgit v1.2.3 From b1fd6740d411c4960c6cc3b4bcdaa7b29d6a901f Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 20 Apr 2012 22:23:26 +0200 Subject: remove stuff --- BUGS | 1 - CHANGELOG | 142 ------- COPYING | 280 ------------- COPYING.FONT | 280 ------------- HELP | 55 --- README | 33 -- WISHLIST | 6 - font.png | Bin 14883 -> 0 bytes loadbars | 1271 ---------------------------------------------------------- 9 files changed, 2068 deletions(-) delete mode 100644 BUGS delete mode 100644 CHANGELOG delete mode 100644 COPYING delete mode 100644 COPYING.FONT delete mode 100644 HELP delete mode 100644 README delete mode 100644 WISHLIST delete mode 100644 font.png delete mode 100755 loadbars diff --git a/BUGS b/BUGS deleted file mode 100644 index 40b75cd..0000000 --- a/BUGS +++ /dev/null @@ -1 +0,0 @@ -* No known bugs ATM diff --git a/CHANGELOG b/CHANGELOG deleted file mode 100644 index 0395a01..0000000 --- a/CHANGELOG +++ /dev/null @@ -1,142 +0,0 @@ -Thu Apr 19 23:40:20 CEST 2012 -* Release v0.5.1.3 -* Minor change, sleep 0.5s instead of 3s if ssh command fails -* Minor fixes such as redraw background on toggle text display which - should fix some weird display bugs. - -Fri Mar 16 22:03:18 CET 2012 -* Release v0.5.1.2 -* Bugfix only release - -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 diff --git a/COPYING b/COPYING deleted file mode 100644 index d4b4b89..0000000 --- a/COPYING +++ /dev/null @@ -1,280 +0,0 @@ -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 - diff --git a/COPYING.FONT b/COPYING.FONT deleted file mode 100644 index d4b4b89..0000000 --- a/COPYING.FONT +++ /dev/null @@ -1,280 +0,0 @@ -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 - diff --git a/HELP b/HELP deleted file mode 100644 index ebf68f8..0000000 --- a/HELP +++ /dev/null @@ -1,55 +0,0 @@ -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]) ---average - Num of samples for avg. (more fluent animations) ---barwidth - Set bar width ---cluster - Cluster name from /etc/clusters ---extended - Toggle extended display (0 or 1) ---factor - Set graph scale factor (1.0 means 100%) ---height - Set windows height ---hosts - Comma sep. list of hosts; optional: user@ in front to each host ---maxwidth - Set max width ---samples - Set number of samples until ssh reconnects ---showcores - Toggle core display (0 or 1) ---showmem - Toggle mem display (0 or 1) ---showtexthost - Toggle hostname/num text display (0 or 1) ---showtext - Toggle text display (0 or 1) ---sshopts - Set SSH options diff --git a/README b/README deleted file mode 100644 index d839d8d..0000000 --- a/README +++ /dev/null @@ -1,33 +0,0 @@ -For general program informations see http://loadbars.buetow.org - -For legal informations see COPYING and COPYING.FONT - -For any program help check out --help on command line or 'h' during program -execution. - -For a list of features which might be added in the future see WHISHLIST - -For a changelog see CHANGELOG - -For a list of all known bugs see BUGS - -Supported platforms are: Linux - -the latter may not have full functionality). - -For Linux at least a 2.6 Kernel is required. - -Versioning schema: a.b.c[.d][.e], where - a = Don't know yet. Maybe will stay forever 0. - b = Major release (new major features) - c = Minor release (new minor features or code refactoring) - d = Optional, bugfixes only release - e = Optional, cosmetic fixes only release - - It's not a release if there is a -master suffix. It's a stable - version (master branch) then. - - It's not a release if there is a -devel suffix. It's a development - version (devel branch) then. - -For some help/usage please consult the HELP file. diff --git a/WISHLIST b/WISHLIST deleted file mode 100644 index ea234ff..0000000 --- a/WISHLIST +++ /dev/null @@ -1,6 +0,0 @@ -* 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/font.png b/font.png deleted file mode 100644 index 02e9eb1..0000000 Binary files a/font.png and /dev/null differ diff --git a/loadbars b/loadbars deleted file mode 100755 index 40dba2e..0000000 --- a/loadbars +++ /dev/null @@ -1,1271 +0,0 @@ -#!/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 - -package Loadbars; - -use strict; -use warnings; - -use Getopt::Long; - -use SDL; -use SDL::App; -use SDL::Rect; -use SDL::Color; -use SDL::Event; - -use SDL::Surface; -use SDL::Font; - -use Time::HiRes qw(usleep gettimeofday); - -use Proc::ProcessTable; - -use threads; -use threads::shared; - -use constant { - VERSION => 'loadbars v0.5.1.3', - COPYRIGHT => '2010-2012 (c) Paul Buetow ', - 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; - -my %PIDS : shared; -my %AVGSTATS : shared; -my %CPUSTATS : shared; -my %MEMSTATS : shared; -my %MEMSTATS_HAS : shared; - -# Global configuration hash -my %C : shared; -# Global configuration hash for internal settings (not configurable) -my %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, -); - -# Quick n dirty helpers -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 set_showcores_regexp () { $I{cpuregexp} = $C{showcores} ? 'cpu' : 'cpu ' } -sub error ($) { die shift, "\n" } -sub display_info_no_nl ($) { print "==> " . shift . ' ' } -sub display_info ($) { say "==> " . shift } -sub display_warn ($) { say "!!! " . shift } - -sub trim (\$) { - my $str = shift; - - $$str =~ s/^[\s\t]+//; - $$str =~ s/[\s\t]+$//; - - return undef; -} - -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 read_config () { - return unless -f CONFFILE; - - display_info "Reading configuration from " . CONFFILE; - open my $conffile, CONFFILE or die "$!: " . 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_config () { - display_warn "Overwriting config file " . CONFFILE if -f CONFFILE; - - open my $conffile, '>', CONFFILE or do { - display_warn "$!: " . CONFFILE; - - return undef; - }; - - for (keys %C) { - print $conffile "$_=$C{$_}\n"; - } - - close $conffile; -} - -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 = INTERVAL; - - # Toggle CPUs - $SIG{USR1} = sub { $sigusr1 = 1 }; - $SIG{TERM} = sub { $sigterm = 1 }; - - 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; - - 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::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 main_loop ($@) { - my ( $dispatch, @threads ) = @_; - - my $num_stats = 1; - $C{width} = $C{barwidth}; - - my $app = SDL::App->new( - -title => Loadbars::VERSION . ' (press h for help on stdout)', - -icon_title => Loadbars::VERSION, - -width => $C{width}, - -height => $C{height}, - -depth => Loadbars::COLOR_DEPTH, - -resizeable => 1, - ); - - SDL::Font->new('font.png')->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 = (); - 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' ) { - write_config; - - } 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::BLACK ); - $app->fill( $rect_steal, Loadbars::RED ); - $app->fill( $rect_guest, Loadbars::RED ); - $app->fill( $rect_irq, Loadbars::WHITE ); - $app->fill( $rect_softirq, Loadbars::WHITE ); - $app->fill( $rect_nice, Loadbars::GREEN ); - $app->fill( $rect_iowait, Loadbars::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::DARK_GREY ); - $app->fill( $rect_memfree, Loadbars::BLACK ); - - $app->fill( $rect_swapused, Loadbars::GREY ); - $app->fill( $rect_swapfree, Loadbars::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::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::USER_ORANGE ? Loadbars::ORANGE - : ( $max_all > Loadbars::USER_YELLOW0 ? Loadbars::YELLOW0 : (Loadbars::YELLOW))); - } - - $app->fill( $rect_user, $all > Loadbars::USER_ORANGE ? Loadbars::ORANGE - : ( $all > Loadbars::USER_YELLOW0 ? Loadbars::YELLOW0 : (Loadbars::YELLOW))); - $app->fill( $rect_system, $cpuaverage->{system} > Loadbars::SYSTEM_BLUE0 - ? Loadbars::BLUE0 : Loadbars::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 ( INTERVAL > $t_diff ) { - usleep 10000; - - # Goto is OK as long you don't produce spaghetti code - goto TIMEKEEPER; - - } elsif ( 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 SUCCESS; -} - -sub dispatch_table () { - my $hosts = ''; - - my $textdesc = <50%, orange if to>50% - sy = System cpu usage 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 { - "--$_ \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 { - say sort map { - "$_->[0] = $_->[1]" - - } grep { - defined $_->[1] - - } map { - [ $_ => exists $d{$_}{var} ? ${ $d{$_}{var} } : $C{$_} ] - - } keys %d; - }; - - return ( \$hosts, $closure ); -} - -# Recursuve function -sub get_cluster_hosts ($;$); - -sub get_cluster_hosts ($;$) { - my ( $cluster, $recursion ) = @_; - - unless ( defined $recursion ) { - $recursion = 1; - - } - elsif ( $recursion > CSSH_MAX_RECURSION ) { - error "CSSH_MAX_RECURSION reached. Infinite circle loop in " - . CSSH_CONFFILE . "?"; - } - - open my $fh, CSSH_CONFFILE or error "$!: " . CSSH_CONFFILE; - my $hosts; - - while (<$fh>) { - if (/^$cluster\s*(.*)/) { - $hosts = $1; - last; - } - } - - close $fh; - - unless ( defined $hosts ) { - error "No such cluster in " . CSSH_CONFFILE . ": $cluster" - unless defined $recursion; - - return ($cluster); - } - - my @hosts; - push @hosts, get_cluster_hosts $_, ( $recursion + 1 ) - for ( split /\s+/, $hosts ); - - return @hosts; -} - -sub main () { - my ( $hosts, $dispatch ) = dispatch_table; - my $usage; - - say VERSION . ' ' . COPYRIGHT; - - read_config; - - GetOptions( 'help|?' => \$usage, $dispatch->('options') ); - - if ( defined $usage ) { - say $dispatch->('usage'); - exit SUCCESS; - } - - set_showcores_regexp; - - my @hosts = map { - my ( $a, $b ) = split /\@/, $_; - defined $b ? "$b:$a" : $a; - } split ',', $$hosts; - - if ( @hosts || defined $C{cluster} ) { - push @hosts, get_cluster_hosts $C{cluster} if defined $C{cluster}; - system 'ssh-add'; - - } - else { - say $dispatch->('usage'); - exit E_NOHOST; - } - - my @threads = create_threads @hosts; - main_loop $dispatch, @threads; - - exit SUCCESS; -} - -main; -- cgit v1.2.3 From 46b7f1cfa7ab8da5b09ae765638f68bba20b2e02 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 20 Apr 2012 22:23:41 +0200 Subject: re-add --- Makefile | 21 + debian/README | 7 + debian/changelog | 150 +++++++ debian/compat | 1 + debian/control | 15 + debian/copyright | 30 ++ debian/files | 1 + debian/loadbars.debhelper.log | 45 +++ debian/loadbars.manpages | 1 + debian/loadbars.substvars | 2 + debian/rules | 13 + debian/source/format | 1 + docs/bugs | 1 + docs/loadbars.1 | 146 +++++++ docs/loadbars.pod | 23 ++ docs/loadbars.txt | 25 ++ docs/wishlist | 6 + fonts/font.png | Bin 0 -> 14883 bytes lib/Loadbars/Config.pm | 142 +++++++ lib/Loadbars/Constants.pm | 39 ++ lib/Loadbars/HelpDispatch.pm | 350 +++++++++++++++++ lib/Loadbars/Main.pm | 887 ++++++++++++++++++++++++++++++++++++++++++ lib/Loadbars/Shared.pm | 52 +++ lib/Loadbars/Utils.pm | 41 ++ loadbars | 68 ++++ 25 files changed, 2067 insertions(+) create mode 100644 Makefile create mode 100644 debian/README create mode 100644 debian/changelog create mode 100644 debian/compat create mode 100644 debian/control create mode 100644 debian/copyright create mode 100644 debian/files create mode 100644 debian/loadbars.debhelper.log create mode 100644 debian/loadbars.manpages create mode 100644 debian/loadbars.substvars create mode 100755 debian/rules create mode 100644 debian/source/format create mode 100644 docs/bugs create mode 100644 docs/loadbars.1 create mode 100644 docs/loadbars.pod create mode 100644 docs/loadbars.txt create mode 100644 docs/wishlist create mode 100644 fonts/font.png create mode 100644 lib/Loadbars/Config.pm create mode 100644 lib/Loadbars/Constants.pm create mode 100644 lib/Loadbars/HelpDispatch.pm create mode 100644 lib/Loadbars/Main.pm create mode 100644 lib/Loadbars/Shared.pm create mode 100644 lib/Loadbars/Utils.pm create mode 100755 loadbars 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 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 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 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 +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 +License: GPL-3.0+ + +Files: debian/* +Copyright: 2012 Paul Buetow +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 . + . + 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 \- 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 - + +=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 - + 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 new file mode 100644 index 0000000..02e9eb1 Binary files /dev/null and b/fonts/font.png differ 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 ', + 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 = <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 { + "--$_ \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; + -- cgit v1.2.3 From 7074e7feb78758d2e5b97f7e447b7fa59691fbef Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 20 Apr 2012 23:04:17 +0200 Subject: hotfix release --- debian/changelog | 6 ++++++ debian/files | 2 +- docs/loadbars.1 | 2 +- lib/Loadbars/Constants.pm | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/debian/changelog b/debian/changelog index aaa6c56..76907c6 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +loadbars (0.5.2.1) stable; urgency=low + + * Hotfix release, contains only a corrected version string. + + -- Paul Buetow Sun, 08 Apr 2012 15:23:53 +0200 + loadbars (0.5.2) stable; urgency=low * Initial .deb diff --git a/debian/files b/debian/files index d1d5b07..5e56941 100644 --- a/debian/files +++ b/debian/files @@ -1 +1 @@ -loadbars_0.5.2_all.deb utils optional +loadbars_0.5.2.1_all.deb utils optional diff --git a/docs/loadbars.1 b/docs/loadbars.1 index 62ed39a..1c982ab 100644 --- a/docs/loadbars.1 +++ b/docs/loadbars.1 @@ -124,7 +124,7 @@ .\" ======================================================================== .\" .IX Title "LOADBARS 1" -.TH LOADBARS 1 "2012-04-20" "loadbars 0.5.2" "User Commands" +.TH LOADBARS 1 "2012-04-20" "loadbars 0.5.2.1" "User Commands" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l diff --git a/lib/Loadbars/Constants.pm b/lib/Loadbars/Constants.pm index af242bd..adffc47 100644 --- a/lib/Loadbars/Constants.pm +++ b/lib/Loadbars/Constants.pm @@ -6,7 +6,7 @@ use warnings; use SDL::Color; use constant { - VERSION => 'loadbars v0.5.2-devel', + VERSION => 'loadbars v0.5.2.1', COPYRIGHT => '2010-2012 (c) Paul Buetow ', CONFFILE => $ENV{HOME} . '/.loadbarsrc', CSSH_CONFFILE => '/etc/clusters', -- cgit v1.2.3