Mini Shell
#!/usr/bin/perl
# encoding: utf-8
#
# author: Kyle Yetter
#
package IMH::ShowConns::Formatters;
our $VERSION = "1.1";
use strict;
use warnings;
use IMH::Terminal;
require Exporter;
our @ISA = qw(Exporter);
our %EXPORT_TAGS = ( 'all' => [ qw(
tabular_report flat_report
) ] );
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
our @EXPORT = qw( tabular_report flat_report );
sub hjoin;
sub vjoin;
sub flow;
sub build_table_cell;
sub build_group;
our %PORT_NAMES = (
80 => 'HTTP',
443 => 'HTTPS',
2286 => 'WHM',
2287 => 'WHM(S)',
2082 => 'cPanel',
2083 => 'cPanel(S)',
2095 => 'Webmail',
2096 => 'Webmail(S)',
20 => 'FTP (Data)',
21 => 'FTP',
783 => 'spamd',
143 => 'IMAP',
993 => 'IMAPS',
110 => 'POP3',
995 => 'POP3S',
25 => 'SMTP',
587 => 'SMTP (Alt)',
465 => 'SMTPS',
22 => 'SSH',
3306 => "MySQL"
);
our %PORT_GROUPS = (
"HTTP" => [ 80, 443 ],
"cPanel" => [ 2082, 2083 ],
"WHM" => [ 2086, 2087 ],
"Webmail" => [ 2095, 2096 ],
"FTP" => [ 20, 21 ],
"IMAP" => [ 143, 993 ],
"POP3" => [ 110, 995 ],
"SMTP" => [ 25, 587, 465 ],
"Misc" => [ 22, 783, 3306 ],
);
our @GROUP_NAMES = qw( HTTP cPanel WHM Webmail IMAP POP3 SMTP FTP Misc );
our %SECTIONS = (
"WWW" => [ qw( HTTP cPanel WHM Webmail ) ],
"Email" => [ qw( IMAP POP3 SMTP ) ],
"Other" => [ qw( FTP Misc ) ]
);
our @SECTION_NAMES = qw( WWW Email Other );
our $screen_width = screen_width - 10;
sub flat_report {
my ( $data, %options ) = @_;
my $display_count = $options{ display_count } || 0;
for my $port ( sort { 0 + $a <=> 0 + $b } keys %$data ) {
my $port_data = $data->{ $port };
my $total = $port_data->{total};
my $ip_stats = $port_data->{ips};
#
# now, for this port's stat table, extract a list all IP addresses recorded and sort it
# in ascending order by the number of connections each IP has
#
my @ips = sort { $ip_stats->{ $a } <=> $ip_stats->{ $b } } keys %$ip_stats;
if ( $display_count ) {
#
# Select the last 10 entries of the last, which corresponds to the top 10 IPs by number
# of connections on this port
#
my $start = $#ips - $display_count + 1;
if ( $start > 0 ) {
@ips = @ips[ $start ... $#ips ];
}
}
for my $ip ( @ips ) {
printf "%i\t%s\t%i\n", $port, $ip, $ip_stats->{ $ip };
}
}
}
sub tabular_report {
my ( $data, %options ) = @_;
my $display_count = $options{ display_count } || 10;
my %tables;
for my $port ( sort { 0 + $a <=> 0 + $b } keys %$data ) {
my $port_data = $data->{ $port };
my $total = $port_data->{total};
my $ip_stats = $port_data->{ips};
#
# now, for this port's stat table, extract a list all IP addresses recorded and sort it
# in ascending order by the number of connections each IP has
#
my @ips = sort { $ip_stats->{ $a } <=> $ip_stats->{ $b } } keys %$ip_stats;
#
# Select the last 10 entries of the last, which corresponds to the top 10 IPs by number
# of connections on this port
#
my $start = $#ips - $display_count + 1;
if ( $start > 0 ) {
@ips = @ips[ $start ... $#ips ];
}
my $title = "$port";
$title .= " - $PORT_NAMES{$port}" if $PORT_NAMES{$port};
$title .= " ($total)";
my $table = build_table_cell( $title, map { [ $_, $ip_stats->{ $_ } ] } @ips );
$table->{total} = $total;
$tables{ $port } = $table;
}
my %groups;
for my $name ( @GROUP_NAMES ) {
my @ports = grep { $tables{ $_ } } @{$PORT_GROUPS{ $name }};
if ( @ports ) {
my @group_tables = @tables{ @ports };
delete @tables{ @ports };
$groups{$name} = build_group( $name, @group_tables );
}
}
if ( keys %tables ) {
my @other_ports = sort keys %tables;
$groups{ "Other" } = build_group( "Other", @tables{@other_ports} );
push @{ $SECTIONS{ "Other" } }, "Other";
#push @SECTION_NAMES, "Other";
}
for my $name ( @SECTION_NAMES ) {
my @group_names = grep { $groups{$_} } @{ $SECTIONS{ $name } };
my @groups = @groups{ @group_names };
local $\ = "\n";
my @section;
if ( @groups ) {
while ( @groups ) {
my $first_group = shift @groups;
my @row = ( $first_group );
my $slack = $screen_width - $first_group->{width};
while ( $groups[ 0 ] and $groups[ 0 ]->{width} < $slack ) {
my $group = shift @groups;
$slack -= $group->{width} + 3;
push @row, $group;
}
my $r = hjoin( ' ', @row );
push @section, $r;
}
my $section_block = vjoin( "\n \n", @section );
my @section_label_lines = split( //, " $name" );
my $section_label = {
lines => \@section_label_lines,
height => scalar( @section_label_lines ),
width => 1
};
my $out = hjoin( ' ', $section_label, $section_block );
for ( @{ $out->{lines} } ) {
s<^(\S)> <c( $1, "red" )>e;
s/[ \t]+$//;
print;
}
print "\n";
}
}
}
sub hjoin {
my ( $joint, @blocks ) = @_;
my $width = clen( $joint ) * @blocks;
my $height = 0;
my @joined_lines;
for my $block ( @blocks ) {
my $h = $block->{height};
$width += $block->{width};
$height = $h if $h > $height;
}
for my $block ( @blocks ) {
my $lines = $block->{lines};
if ( $height > @$lines ) {
my $pad = ' ' x $block->{width};
push( @$lines, $pad ) for ( 1 ... $height - @$lines );
}
}
for my $i ( 0 ... $height - 1 ) {
$joined_lines[ $i ] = join( $joint, map { $_->{lines}->[ $i ] } @blocks );
}
return(
{
lines => \@joined_lines,
width => $width,
height => $height
}
);
}
sub vjoin {
my ( $spacer, @blocks ) = @_;
my @spacer_lines = split( "\n", $spacer );
my @joined_lines;
my $width = 0;
for my $block ( @blocks ) {
my $w = $block->{width};
if ( $w > $width ) { $width = $w; }
}
for my $i ( 0 ... $#blocks ) {
my $block = $blocks[ $i ];
my $lines = $block->{lines};
if ( $i > 0 ) {
push @joined_lines, @spacer_lines;
}
push( @joined_lines, map { ljust( $_, $width ) } @$lines );
}
return(
{
lines => \@joined_lines,
width => $width,
height => scalar( @joined_lines )
}
);
}
sub flow {
my ( $hspacer, $vspacer, @blocks ) = @_;
my $hspacer_width = clen( $hspacer );
my @rows;
while ( @blocks ) {
my $first_block = shift @blocks;
my @row = ( $first_block );
my $slack = $screen_width - $first_block->{width};
while ( $blocks[ 0 ] and $blocks[ 0 ]->{width} < $slack ) {
my $block = shift @blocks;
$slack -= $block->{width} + $hspacer_width;
push @row, $block;
}
push @rows, hjoin( $hspacer, @row );
}
return vjoin( $vspacer, @rows );
}
sub build_table_cell {
local $\ = "\n";
my $string = '';
my @rows = @_;
my @arrays;
my @titles;
my @header_row;
my @lines;
my $title = undef;
for my $item ( @rows ) {
if ( ref( $item ) eq 'ARRAY' ) {
push( @arrays, $item );
} elsif ( ref( \$item ) eq "SCALAR" ) {
push( @titles, $item );
}
}
unless ( @arrays ) {
$@ = "build_table_cell: no rows provided";
return;
}
my $ncols = scalar( @{$arrays[ 0 ]} );
my @widths;
for my $c ( 0 .. ( $ncols - 1 ) ) {
my $w = 0;
for my $row ( @arrays ) {
my $col = $row->[$c];
my $col_width = clen( $col );
if ( $w < $col_width ) { $w = $col_width; }
}
$widths[ $c ] = $w;
}
my $inner_width = ( $ncols - 1 ) * 3;
$inner_width += $_ for @widths;
if ( @titles ) {
my $title_width = 0;
for my $t ( @titles ) {
my $l = clen( $t );
$title_width = $l if $l > $title_width;
}
if ( $title_width > $inner_width ) {
$widths[-1] += $title_width - $inner_width;
$inner_width = $title_width;
}
}
if ( ref( \$rows[0] ) eq "SCALAR" ) {
$title = shift( @rows );
push @lines, '+-' . ( '-' x $inner_width ) . '-+';
push @lines, '| ' . center( $title, $inner_width ) . ' |';
}
my $mask = '| ' . join( " | ", map { "%-${_}s" } @widths ) . ' |';
my $border = '+-' . join( "-+-", map { '-' x $_ } @widths ) . '-+';
my $outline = '+-' . ( '-' x $inner_width ) . '-+';
my $after_title = 1;
#my @title_row = @{ shift( @rows ) };
#push @lines, $border;
#push @lines, '| ' . join( ' | ', map { center( $title_row[ $_ ], $widths[ $_ ] ) } ( 0 ... $#title_row ) ) . ' |';
#push @lines, $border;
#$after_title = 0;
for my $row ( @rows ) {
if ( ref( $row ) eq 'ARRAY' ) {
if ( $after_title ) {
push @lines, $border;
}
push @lines, '| ' . join( ' | ', map { ljust( $row->[ $_ ], $widths[ $_ ] ); } (0 ... $#widths) ) . ' |';
#print( sprintf( $mask, @{ $row } ) );
$after_title = 0;
} elsif ( ref( \$row ) eq 'SCALAR' ) {
if ( $row eq '-' ) {
push @lines, $after_title ? $outline : $border;
$after_title = 0;
} else {
push @lines, $after_title ? $outline : $border;
push @lines, '| ' . center( $row, $inner_width ) . ' |';
$after_title = 1;
}
}
}
push @lines, $after_title ? $outline : $border;
return(
{
lines => \@lines,
width => $inner_width + 4,
height => scalar( @lines )
}
);
}
sub build_group {
my ( $title, @tables ) = @_;
my @rows;
my $group_total = 0;
while ( @tables ) {
my $first_cell = shift @tables;
my @row = ( $first_cell );
my $slack = $screen_width - $first_cell->{width};
$group_total += $first_cell->{total};
while ( $tables[ 0 ] and $tables[ 0 ]->{width} < $slack ) {
my $cell = shift @tables;
$group_total += $cell->{total};
$slack -= $cell->{width} + 2;
push @row, $cell;
}
push @rows, hjoin( ' ', @row );
}
my $group_block = vjoin( "\n \n", @rows );
$title = center( "\e[4m$title ($group_total)\e[0m", $group_block->{width} );
unshift @{$group_block->{lines}}, $title;
$group_block->{height}++;
return $group_block;
}
1;
__END__
Zerion Mini Shell 1.0