3 use strict qw(refs vars);
17 open DEBUG, ">/dev/null" or die $!;
19 while (@ARGV && $ARGV[0] =~ m/^\-./) {
23 if (s/^-h//) { $dohosts{$_}=1; last; }
24 elsif (s/^-O//) { $outpfx=$_; last; }
25 elsif (s/^-s//) { $sep=$_; last; }
26 elsif (s/^-D/-/) { $debug++; }
27 else { die "bad option $_"; }
32 my ($sysname, $nodename) = POSIX::uname();
33 die unless defined $nodename;
34 $dohosts{$nodename}= 1;
38 my $totail= pop @ARGV;
40 if ($debug) { open DEBUG, ">& STDERR" or die $!; }
42 our @fields_in= qw(seconds accepted refused rejected duplicate
43 accepted_size duplicate_size);
44 our $tmpl_in= join ':',@fields_in;
48 DstArguments => "7200:0:U",
50 Archives => [ [ 3600*4, 60 ], # 4hr, 1min resolution
51 [ 3600*25, 180 ], # 25h, 3min resolution
52 [ 86400*14*5, 3600 ], # 14wks, 1hr resolution
53 [ 86400*370*2, 3600*24 ] ], # 2yr+, 1day resolution
57 our ($time,$host,$peer,$conn,$stats);
61 return "${outpfx}${host}${sep}${peer}_${inout}.rrd";
64 sub perhaps_create_rrd ($$) {
65 my ($inout, $fields) = @_;
66 my $path= path_rrd($inout);
68 $!==&ENOENT or die "$path $!";
69 my $details= $details{$inout};
71 my @sargs= ($path, '--start','now-1y', '--step',$details->{Step});
73 push @largs, "DS:$_:ABSOLUTE:$details->{DstArguments}" foreach @$fields;
74 foreach (@{ $details->{Archives} }) {
75 my ($whole,$reso) = @$_;
76 my $steps= $reso / $details->{Step};
77 my $rows= $whole / $reso;
78 push @largs, "RRA:AVERAGE:$details->{Xff}:$steps:$rows";
80 print DEBUG join(" \\\n ", "creating @sargs", @largs),"\n";
81 RRDs::create(@sargs,@largs);
83 die "$err [@sargs @largs]" if defined $err;
86 sub update_rrd ($$$) {
87 my ($inout,$tmpl,$vals) = @_;
88 my $path= path_rrd($inout);
89 my @args= ($path, '--template',$tmpl, join(':',$time,@$vals));
90 print DEBUG "update @args\n";
93 die "$err [@args]" if defined $err;
98 sub inbound_connected () {
99 #print "$host $peer $conn START\n";
100 perhaps_create_rrd('in',\@fields_in);
101 $in_conns{$host,$peer,$conn} = [ (0) x @fields_in ];
103 sub inbound_closed () {
104 #print "$host $peer $conn STOP\n";
105 delete $in_conns{$host,$peer,$conn};
107 sub inbound_stats () {
110 s/(?<=[a-z]) (?=[a-z])/_/g;
111 my $hpc= $in_conns{$host,$peer,$conn};
113 perhaps_create_rrd('in',\@fields_in);
114 $in_conns{$host,$peer,$conn}= $hpc= [ (undef) x @fields_in ];
116 while (s/^([a-z_]+) (\d+)\s//) { $s{$1}= $2; }
118 foreach my $f (@fields_in) {
120 if (!defined $this) {
125 my $last= $hpc->[@v];
127 push @v, defined($last) ? $this - $last : 'U';
129 update_rrd('in',$tmpl_in,\@v);
132 sub outbound_stats () {
133 print "$host $peer OUT $stats\n";
138 my $parser= new Parse::Syslog $object, repeat=>0, arrayref=>1;
139 my $host_re= '[-.0-9a-z]+';
140 my $conn_re= '[1-9]\d{0,5}';
141 my ($process,$pid,$msg,$cc,$sl);
142 while ($sl= $parser->next) {
143 ($time,$host,$process,$pid,$msg) = @$sl;
144 next unless exists $dohosts{$host};
145 #print join("|", map { defined($_) ? $_ : "<undef>" } @$sl), "\n";
146 if ($process eq 'innd' && !defined $pid) {
147 if (($peer,$conn) = $msg =~ m/^($host_re) connected ($conn_re)$/) {
149 } elsif (($peer,$conn,$cc,$stats) = $msg =~
150 m/^($host_re):($conn_re) (closed|checkpoint) (seconds .*)$/) {
152 inbound_closed() if $cc eq 'closed';
154 } elsif ($process eq 'innduct') {
155 if (($peer,$stats) = $msg =~
156 m/^($host_re)\| (?:completed|processed) \S+ (read=.*)$/) {
163 #seconds (\d+) accepted (\d+) refused (\d+) rejected (\d+) duplicate (\d+) accepted size (\d+) duplicate size (\d+)
166 foreach my $staticpath (@ARGV) {
167 if ($staticpath =~ m/\.gz$/) {
168 my $fh= new IO::Handle;
169 open $fh, '-|', 'gunzip', '--', $staticpath or die $!;
171 !$fh->error or die "$staticpath $!";
172 $!=0;$?=0; close $fh or die "$staticpath $! $?";
174 my $fh= new IO::File $staticpath, '<' or die $!;
176 !$fh->error or die "$staticpath $!";
177 close $fh or die "$staticpath $!";
181 exit 0 if $totail eq '';
183 my $tailer= new File::Tail name=>$totail,
184 interval=>60, adjustafter=>2, ignore_nonexistant=>1, tail=>-1