#!/usr/bin/perl -w # ### # Project: pflogstats # Program: rsyncstats.pl # Description: Main program for extract accounting information from rsync log file # # Copyright (C) 2003-2003 by Dr. Peter Bieringer # ftp://ftp.aerasec.de/pub/linux/postfix/pflogsumm/ # # License: GNU GPL v2 # CVS: $Id: rsyncstats.pl,v 1.1 2003/10/20 13:14:37 rootadm Exp $ # # See also following files: LICENSE, ChangeLog ### ### # ChangeLog # 0.01 # - copy from apachelogiostats.pl and adapt it ### # ToDo # - timerange (set) # - implement "format" ### use strict; use Getopt::Long; ## Name and version use vars qw{$release $progName}; $release = "0.01"; $progName = "rsyncstats.pl"; ## Define global variables # option handling use vars qw{%options %opts}; $options{'help|h|?'} = \$opts{'help'}; $options{"version"} = \$opts{'version'}; # module hooks use vars qw{%hooks}; # Number formats use vars qw{%numberformat}; ## Module loader # 1st: look into current directory push @INC, "."; # 2nd: look into /usr/local/lib/pflogstats push @INC, "/usr/local/lib/pflogstats"; # 3rd: look into /usr/lib/pflogstats push @INC, "/usr/lib/pflogstats"; # General require "pflogstats-common-support.pm"; require "pflogstats-extensions-networking.pm"; ## Print options (debug) #for my $key (keys %options) { # print $key . "\n"; #}; #exit 0; # Local variables my %accounting; $accounting{'sent'} += 0; $accounting{'rcvd'} += 0; my @mainhelptext; my $p_hook; # Time range of logdata use vars qw{$timemin $timemax}; my ($time); # Local functions prototyping sub print_rsync_stats(); ## Help function sub print_help() { print "$progName $release\n\n"; print STDERR " Options from included modules:\n\n"; ## Hook 'help' for my $p_hook (sort keys %{$hooks{'help'}}) { my $helpstring = &{$hooks{'help'}->{$p_hook}}; print STDERR " Options from module '" . $p_hook . "':"; print STDERR $helpstring . "\n"; }; }; ## Hook 'early_begin' for my $p_hook (keys %{$hooks{'early_begin'}}) { &{$hooks{'early_begin'}->{$p_hook}}; }; ## Get options my $ret = GetOptions(%options); if (! $ret ) { print_help(); exit 1; }; # Print help or version if(defined($opts{'help'})) { print_help(); exit 0; }; if(defined($opts{'version'})) { print "$progName $release\n"; exit 0; }; ## Hook 'checkoptions' for $p_hook (keys %{$hooks{'checkoptions'}}) { &{$hooks{'checkoptions'}->{$p_hook}}; }; ## Hook 'beforemainloopstarts' for $p_hook (keys %{$hooks{'beforemainloopstarts'}}) { &{$hooks{'beforemainloopstarts'}->{$p_hook}}; }; print "DEBUG: start parsing logfile\n" if ($opts{'debug'}); ## Start parsing logfile ################################################# my $skip; my ($pid, $ip, $rcvd, $sent, $date, $direction); my %tracker; while(<>) { chomp; $_ =~ s/^M$//g; # Remove trailing CR $~ =~ s/^[[:space:][:cntrl:]]+$//g; # Remove spaces and ctrl chars only next if (length($_) == 0); # skip empty lines # Parsing rsync log # Todo: Datematching!!!! undef $pid; undef $ip; undef $rcvd; undef $sent; undef $date; undef $direction; # Loglines: # PID tracking for network: # 2003/10/01 05:00:01 [7831] rsync on server-rsync from client.domain.example (1.2.3.4) # Accounting line: # 2003/10/01 05:00:02 [7831] wrote 3914 bytes read 121 bytes total size 669824 printf STDERR "DEBUG/rsync: line='" . $_ . "'\n" if ( $opts{'debug'} & 0x0040 ); if ( /rsync: name lookup failed for /i ) { next; } elsif ( /rsync: forward name lookup for /i ) { next; } elsif ( /rsync: read error: /i ) { next; } elsif ( /rsync error: error in rsync protocol data stream /i ) { next; } elsif ( /max connections/i ) { next; } elsif ( ($date, $pid, $direction, $ip) = /^(\S+ \S+)\s+\[(\d+)\] rsync (on|to) .* from .*\s+\((\S+)\)$/ ) { # Get PID tracking line printf STDERR "DEBUG/rsync: ip='" . $ip . "' pid='" . $pid . "' date='" . $date . "'\n" if ( $opts{'debug'} & 0x0020 ); if (defined $tracker{$pid}) { die "ERROR/rsync: pid duplication: " . $_; }; # Fill information into tracker $tracker{$pid}->{'ip'} = $ip; $tracker{$pid}->{'date'} = $date; #$tracker{$pid}->{'direction'} = $direction; } elsif ( ($date, $pid, $sent, $rcvd) = /^(\S+ \S+)\s+\[(\d+)\] wrote (\d+) bytes\s+read (\d+) bytes.*$/ ) { # Get Accouting line $skip = 0; # Look for tracker information given if (defined $tracker{$pid}) { $ip = $tracker{$pid}->{'ip'}; $date = $tracker{$pid}->{'date'}; # Remove tracker entry undef $tracker{$pid}; # Hook "testipaddress" for my $p_hook (keys %{$main::hooks{'testipaddress'}}) { if ( &{$main::hooks{'testipaddress'}->{$p_hook}} ($ip) != 0 ) { # excluded printf STDERR "DEBUG/rsync: excluded from accounting\n" if ($opts{'debug'} & 0x0010 ) ; $skip = 1; last; }; }; } else { # No tracker information, account always # Happen on direction: to }; # Calculate Unixtime if (! ($date =~ /^(\d+)\/(\d+)\/(\d+) (\d+):(\d+):(\d+)$/)) { print STDERR "ERROR/rsync: line contains no valid date: $date\n"; }; $time = timelocal( $6, $5, $4, $3, $2, $1); # Catch min/max times for late timerange display if (! defined $timemin || ! defined $timemax ) { # initial values if (! defined $timemin) { $timemin = $time }; if (! defined $timemax) { $timemax = $time }; } else { # get min/max if ($time < $timemin) { $timemin = $time; } elsif ($time > $timemax) { $timemax = $time; }; }; if ($skip == 0) { $accounting{'sent'} += $sent; $accounting{'rcvd'} += $rcvd; }; } else { die "WARN/rsync: unrecognized log line: " . $_; }; }; print "DEBUG/rsync: end parsing logfile\n" if ($opts{'debug'}); &print_rsync_stats(); exit 0; #### END # statistics sub print_rsync_stats() { print_headline("rsync logio accounting statistics", "default"); print '='x75 . "\n"; printf "%-50s: %6s\n", "", "BytesTraffic"; print_timerange_normal(); print '-'x75 . "\n"; printf "%-50s: %9d %9s\n", "received (requests)", $accounting{'rcvd'}, $numberformat{$opts{'numberformat'}}->format_bytes($accounting{'rcvd'}); printf "%-50s: %9d %9s\n", "sent (data)", $accounting{'sent'}, $numberformat{$opts{'numberformat'}}->format_bytes($accounting{'sent'}); print '-'x75 . "\n"; printf "%-50s: %9d %9s\n", "Total", $accounting{'rcvd'} + $accounting{'sent'}, $numberformat{$opts{'numberformat'}}->format_bytes($accounting{'rcvd'} + $accounting{'sent'}); print '='x75 . "\n"; print "\n"; return 0; };