1#!/usr/bin/perl 2 3# 4# bfha -- binkleyforce history analyzer 5# 6# Copyright (C) 2000 Serge N. Pokhodyaev 7# 8# E-mail: snp@ru.ru 9# Fido: 2:5020/1838 10# 11# 12# This program is free software; you can redistribute it and/or modify 13# it under the terms of the GNU General Public License as published by 14# the Free Software Foundation; either version 2 of the License, or 15# (at your option) any later version. 16# 17# 18# $Id: bfha.pl,v 1.1.1.1 2004/09/09 09:52:36 kstepanenkov Exp $ 19# 20 21# 22# ������� ���������� �� ���������� ���� 23# 24 25# 26# TODO: 27# 28# 1. ������ ������, ����� ��� ��ף���� logrotate'�� 29# 30# Known bugs: 31# 32# 1. H���������� ������� cps (��������) ���� ������ � ��� ������� 33# 34 35use strict; 36use Time::Local; 37use POSIX qw(strftime); 38 39my $PROGNAME = 'bfha'; 40my $VERSION = '$Revision: 1.1.1.1 $ '; 41 42######## Configurable part ################################################### 43 44my $inews = "/usr/bin/inews -h -O -S"; 45 46my $log = "/var/spool/fido/history"; 47 48my $rep_newsgroups = "ftn.1838.stat"; 49my $rep_from = "\"Statistics generator\" <snp\@gloom.intra.eu.org>"; 50my $rep1_subj = "Sessions history"; 51my $rep2_subj = "Sessions history"; 52my $rep3_subj = "Links statistics"; 53 54my $count_failed = 1; 55 56#my %line_names = ( 57# "ttyS1" => "Modem", 58# "tcpip" => "IP" 59#); 60 61############################################################################## 62 63my $devel = 0; 64 65my (%st, %lines, %nodes, @nodes_sorted); 66my @tm; 67my $time; 68 69die "Usage: $PROGNAME [-d]\n" if (!((0 == $#ARGV) && ($ARGV[0] eq '-d')) && !(-1 == $#ARGV)); 70$devel = 1 if ((0 == $#ARGV) && ($ARGV[0] eq '-d')); 71 72$log = "./history" if ($devel); 73 74# Make version string 75# 76$VERSION =~ s/(\$Rev)ision:\s([\d.]+).*/$PROGNAME\/$2/; 77 78# Get time of 0:00:00 of yesterday 79# 80@tm = localtime(time - 86400); 81$tm[0] = 0; # sec 82$tm[1] = 0; # min 83$tm[2] = 0; # hours 84$time = timelocal(@tm); 85 86# At first read logs 87# 88undef %st; 89undef %lines; 90undef %nodes; 91die "Log reading error\n" if (0 != log_read($time)); 92 93@nodes_sorted = sort(node_cmp keys(%nodes)); 94 95# Now generate statistics 96# 97out_close(); 98#rep1(); 99rep2(); 100rep3(); 101 102 103sub log_read 104 { 105 my $i; 106 my $t; 107 my @l; 108 109 die if (0 != $#_); 110 111 $t = $_[0]; 112 113 open(LOG, "< $log") || die("Can't open $log: $!\n"); 114 while (<LOG>) 115 { 116 chomp; 117 @l = split(/,/); 118 return 1 if ($#l != 11); 119 return 1 if (! ($l[4] =~ /[IO]/)); 120 121 # Check date 122 # 123 next if ($l[2] < $t); 124 last if ($l[2] >= ($t + 86400)); 125 126 # Entries without address 127 # 128 if ($l[1] eq '') 129 { 130 next if (0 == $count_failed); 131 $l[1] = 'failed' if (0 != $count_failed); 132 } 133 134 # Remove domain 135 # 136 $l[1] =~ s/@.*$//; 137 138 # Add statistics 139 # 140 $i = $#{$st{beg}} + 1; 141 142 $lines{$l[0]}[$#{$lines{$l[0]}} + 1] = $i; 143 $nodes{$l[1]}[$#{$nodes{$l[1]}} + 1] = $i; 144 145 $st{beg}[$i] = $l[2]; 146 $st{len}[$i] = $l[3]; 147 $st{addr}[$i] = $l[1]; 148 $st{line}[$i] = $l[0]; 149 $st{rc}[$i] = $l[5]; 150 $st{snt_nm}[$i] = $l[6]; 151 $st{snt_am}[$i] = $l[7]; 152 $st{snt_f}[$i] = $l[8]; 153 $st{rcv_nm}[$i] = $l[9]; 154 $st{rcv_am}[$i] = $l[10]; 155 $st{rcv_f}[$i] = $l[11]; 156 $st{islst}[$i] = 0; 157 $st{islst}[$i] = 1 if ($l[4] =~ /L/); 158 $st{isprot}[$i] = 0; 159 $st{isprot}[$i] = 1 if ($l[4] =~ /P/); 160 $st{type}[$i] = 'I' if ($l[4] =~ /I/); 161 $st{type}[$i] = 'O' if ($l[4] =~ /O/); 162 } 163 } 164 165######## rep1 ################################################################ 166 167sub rep1 168 { 169 my ($i, $j); 170 my (%se, %se_t); 171 my $node; 172 173 out_open($rep1_subj); 174 printf "Date: %s\n\n", strftime("%a, %e %b %Y", localtime($time)); 175 176 $~ = "rep1_header"; 177 write; 178 179 $~ = "rep1_body"; 180 $se_t{num_in} = 0; 181 $se_t{num_out} = 0; 182 $se_t{snt} = 0; 183 $se_t{rcv} = 0; 184 $se_t{len} = 0; 185 foreach $node (@nodes_sorted) 186 { 187 $se{num_in} = 0; 188 $se{num_out} = 0; 189 $se{snt} = 0; 190 $se{rcv} = 0; 191 $se{len} = 0; 192 for ($i = 0; $i <= $#{$nodes{$node}}; ++$i) 193 { 194 $j = $nodes{$node}[$i]; 195 ++$se{num_in} if ($st{type}[$j] eq 'I'); 196 ++$se{num_out} if ($st{type}[$j] eq 'O'); 197 $se{snt} += $st{snt_am}[$j] + $st{snt_nm}[$j] + $st{snt_f}[$j]; 198 $se{rcv} += $st{rcv_am}[$j] + $st{rcv_nm}[$j] + $st{rcv_f}[$j]; 199 $se{len} += $st{len}[$j]; 200 } 201 $se{time} = time_int2str($se{len}); 202 # FIXME (cps) 203 $se{cps} = div_int(($se{snt} + $se{rcv}), $se{len}); 204 write; 205 206 $se_t{num_in} += $se{num_in}; 207 $se_t{num_out} += $se{num_out}; 208 $se_t{snt} += $se{snt}; 209 $se_t{rcv} += $se{rcv}; 210 $se_t{len} += $se{len}; 211 } 212 213 $~ = "rep1_footer"; 214 $se_t{time} = time_int2str($se_t{len}); 215 # FIXME (cps) 216 $se_t{cps} = div_int(($se_t{snt} + $se_t{rcv}), $se_t{len}); 217 write; 218 219 print "\n"; 220 out_close(); 221 222 223format rep1_header = 224��������������������������������������������������������������������������� 225� System � Sessions � Sent � Received � Time � CPS � 226� address � in out � bytes � bytes � online � � 227��������������������������������������������������������������������������� 228. 229 230format rep1_body = 231� @<<<<<<<<<<<<<< � @>> @>> � @>>>>>>>> � @>>>>>>>>> � @>>>>>>>> � @>>>> � 232$node, $se{num_in}, $se{num_out}, $se{snt}, $se{rcv}, $se{time}, $se{cps} 233. 234 235format rep1_footer = 236��������������������������������������������������������������������������� 237� TOTAL � @>> @>> � @>>>>>>>> � @>>>>>>>>> � @>>>>>>>> � @>>>> � 238$se_t{num_in}, $se_t{num_out}, $se_t{snt}, $se_t{rcv}, $se_t{time}, $se_t{cps} 239��������������������������������������������������������������������������� 240. 241 } 242 243######## rep2 ################################################################ 244 245sub rep2 246 { 247 my ($i, $j); 248 my @t; 249 my ($t1, $t2, $str, $lv); 250 my $node; 251 my %se; 252 253 out_open($rep2_subj); 254 printf "Date: %s\n\n", strftime("%a, %e %b %Y", localtime($time)); 255 256 $~ = "rep2_header"; 257 write; 258 259 $~ = "rep2_body"; 260 foreach $node (@nodes_sorted) 261 { 262 $se{snt} = 0; 263 $se{rcv} = 0; 264 $se{len} = 0; 265 for ($i = 0; $i <= 95; ++$i) 266 { 267 $t[$i] = 0; 268 } 269 for ($i = 0; $i <= $#{$nodes{$node}}; ++$i) 270 { 271 $j = $nodes{$node}[$i]; 272 273 # Fill array 274 # 275 $t1 = $st{beg}[$j] - $time; 276 $t2 = $t1 + $st{len}[$j]; 277 $t2 = 86399 if ($t2 > 86399); 278 $t1 = div_int($t1, 900); 279 $t2 = div_int($t2, 900); 280 while ($t1 <= $t2) 281 { 282 $t[$t1++] = 1; 283 } 284 285 $se{snt} += $st{snt_am}[$j] + $st{snt_nm}[$j] + $st{snt_f}[$j]; 286 $se{rcv} += $st{rcv_am}[$j] + $st{rcv_nm}[$j] + $st{rcv_f}[$j]; 287 $se{len} += $st{len}[$j]; 288 } 289 $se{time} = time_int2str($se{len}); 290 # FIXME (cps) 291 $se{cps} = div_int(($se{snt} + $se{rcv}), $se{len}); 292 293 # Visualize 294 # 295 $i = 0; 296 $str = ""; 297 if ($t[$i++]) 298 { 299 $str = $str . "�"; 300 } 301 else 302 { 303 $str = $str . "�"; 304 } 305 while ($i < $#t) 306 { 307 $lv = 0; 308 $lv += 1 if ($t[$i++]); 309 $lv += 2 if ($t[$i++]); 310 311 if (0 == $lv) 312 { 313 if (div_rest(($i - 1), 8) == 0) 314 { 315 $str = $str . "�"; 316 } 317 else 318 { 319 $str = $str . " "; 320 } 321 } 322 elsif (1 == $lv) 323 { 324 $str = $str . "�"; 325 } 326 elsif (2 == $lv) 327 { 328 $str = $str . "�"; 329 } 330 elsif (3 == $lv) 331 { 332 $str = $str . "�"; 333 } 334 } 335 if ($t[$i]) 336 { 337 $str = $str . "�"; 338 } 339 else 340 { 341 $str = $str . "�"; 342 } 343 344 write; 345 } 346 347 $~ = "rep2_footer"; 348 write; 349 350 out_close(); 351 352 353format rep2_header = 354 0 2 4 6 8 10 12 14 16 18 20 22 24 355 ������������������������������������������������� 356. 357 358format rep2_body = 359@<<<<<<<<<<<<<<@|||||||||||||||||||||||||||||||||||||||||||||||| 360$node, $str 361. 362 363format rep2_footer = 364 ������������������������������������������������� 365 0 2 4 6 8 10 12 14 16 18 20 22 24 366. 367 } 368 369######## rep3 ################################################################ 370 371sub rep3 372 { 373 my $node; 374 my (%se, %se_t); 375 my ($i, $j); 376 377 out_open($rep3_subj); 378 printf "Date: %s\n\n", strftime("%a, %e %b %Y", localtime($time)); 379 380 $~ = "rep3_header"; 381 write; 382 383 $~ = "rep3_body"; 384 $se_t{num_in} = 0; 385 $se_t{num_out} = 0; 386 $se_t{time_in} = 0; 387 $se_t{time_out} = 0; 388 $se_t{snt} = 0; 389 $se_t{rcv} = 0; 390 $se_t{time} = 0; 391 foreach $node (@nodes_sorted) 392 { 393 $se{num_in} = 0; 394 $se{num_out} = 0; 395 $se{time_in} = 0; 396 $se{time_out} = 0; 397 $se{snt} = 0; 398 $se{rcv} = 0; 399 $se{time} = 0; 400 for ($i = 0; $i <= $#{$nodes{$node}}; ++$i) 401 { 402 $j = $nodes{$node}[$i]; 403 ++$se{num_in} if ($st{type}[$j] eq 'I'); 404 ++$se{num_out} if ($st{type}[$j] eq 'O'); 405 $se{time_in} += $st{len}[$j] if ($st{type}[$j] eq 'I'); 406 $se{time_out} += $st{len}[$j] if ($st{type}[$j] eq 'O'); 407 $se{snt} += $st{snt_am}[$j] + $st{snt_nm}[$j] + $st{snt_f}[$j]; 408 $se{rcv} += $st{rcv_am}[$j] + $st{rcv_nm}[$j] + $st{rcv_f}[$j]; 409 $se{time} += $st{len}[$j]; 410 } 411 412 # Total counters 413 # 414 $se_t{num_in} += $se{num_in}; 415 $se_t{num_out} += $se{num_out}; 416 $se_t{snt} += $se{snt}; 417 $se_t{rcv} += $se{rcv}; 418 $se_t{time} += $se{time}; 419 $se_t{time_out} += $se{time_out}; 420 $se_t{time_in} += $se{time_in}; 421 422 # Output string 423 # 424 # FIXME (cps) 425 $se{cps} = dash_if_zero(div_int(($se{snt} + $se{rcv}), $se{time})); 426 $se{time} = time_int2str($se{time}); 427 $se{time_in} = time_int2str($se{time_in}); 428 $se{time_out} = time_int2str($se{time_out}); 429 $se{num_in} = dash_if_zero($se{num_in}); 430 $se{num_out} = dash_if_zero($se{num_out}); 431 $se{rcv} = shrink_size(dash_if_zero($se{rcv})); 432 $se{snt} = shrink_size(dash_if_zero($se{snt})); 433 write; 434 } 435 436 $~ = "rep3_footer"; 437 # FIXME (cps) 438 $se_t{cps} = dash_if_zero(div_int(($se_t{snt} + $se_t{rcv}), $se_t{time})); 439 $se_t{time} = time_int2str($se_t{time}); 440 $se_t{time_in} = time_int2str($se_t{time_in}); 441 $se_t{time_out} = time_int2str($se_t{time_out}); 442 $se_t{num_in} = dash_if_zero($se_t{num_in}); 443 $se_t{num_out} = dash_if_zero($se_t{num_out}); 444 $se_t{rcv} = shrink_size(dash_if_zero($se_t{rcv})); 445 $se_t{snt} = shrink_size(dash_if_zero($se_t{snt})); 446 $~ = "rep3_footer"; 447 write; 448 449 print "\n"; 450 out_close(); 451 452 453format rep3_header = 454������������������������������������������������������������������������������� 455� � Sessions/Online � Time � Traffic � � 456� Address ����������������������������� online ��������������� CPS � 457� � Incoming � Outgoing � � Rcvd � Sent � � 458������������������������������������������������������������������������������� 459. 460 461format rep3_body = 462� @<<<<<<<<<<<<<<�@>>�@>>>>>>> �@>>�@>>>>>>> �@>>>>>>>> �@>>>>>�@>>>>>�@>>>>> � 463$node, $se{num_in}, $se{time_in}, $se{num_out}, $se{time_out}, $se{time}, $se{rcv}, $se{snt}, $se{cps} 464. 465 466format rep3_footer = 467������������������������������������������������������������������������������� 468� TOTAL �@>>�@>>>>>>> �@>>�@>>>>>>> �@>>>>>>>> �@>>>>>�@>>>>>�@>>>>> � 469$se_t{num_in}, $se_t{time_in}, $se_t{num_out}, $se_t{time_out}, $se_t{time}, $se_t{rcv}, $se_t{snt}, $se_t{cps} 470������������������������������������������������������������������������������� 471. 472 } 473 474############################################################################## 475 476sub shrink_size 477 { 478 die if (0 != $#_); 479 480 return $_[0] if ($_[0] < 1024); 481 return sprintf("%.1fk", $_[0] / 1024) if ($_[0] < 1048576); 482 return sprintf("%.1fM", $_[0] / 1048576); 483 } 484 485sub dash_if_zero 486 { 487 die if (0 != $#_); 488 489 return "-" if (0 == $_[0]); 490 return $_[0]; 491 } 492 493sub time_int2str 494 { 495 my $time; 496 my $h; 497 my $m; 498 my $s; 499 500 die if (0 != $#_); 501 die if (86399 < $time); 502 503 return "-:--:--" if (0 == $_[0]); 504 505 $time = $_[0]; 506 $h = div_int($time, 3600); 507 $time = div_rest($time, 3600); 508 $m = div_int($time, 60); 509 $s = div_rest($time, 60); 510 511 return sprintf("%d:%2.2d:%2.2d", $h, $m, $s); 512 } 513 514# ������������� ������� 515sub div_int 516 { 517 use integer; 518 519 die if (1 != $#_); 520 521 return 0 if ($_[1] == 0); 522 return $_[0] / $_[1]; 523 } 524 525 526# ������� �� �������������� ������� 527sub div_rest 528 { 529 die if (1 != $#_); 530 531 return 0 if ($_[1] == 0); 532 return $_[0] - (div_int($_[0], $_[1]) * $_[1]); 533 } 534 535sub out_open 536 { 537 die if (0 != $#_); 538 539 open(STDOUT, "| $inews") || die("Can't pipe to inews: $!\n") if (! $devel); 540 printf "Newsgroups: %s\n", $rep_newsgroups; 541 printf "From: %s\n", $rep_from; 542 printf "Subject: %s\n", $_[0]; 543 printf "X-FTN-Tearline: %s\n\n", $VERSION; 544 } 545 546sub out_close 547 { 548 close(STDOUT) if (! $devel); 549 } 550 551sub node_cmp 552 { 553 my (@na, @nb); 554 555 @na = split('[:/.]', $::a); 556 @nb = split('[:/.]', $::b); 557 558 # zone 559 return -1 if ($na[0] < $nb[0]); 560 return 1 if ($na[0] > $nb[0]); 561 562 # net 563 return -1 if ($na[1] < $nb[1]); 564 return 1 if ($na[1] > $nb[1]); 565 566 # node 567 return -1 if ($na[2] < $nb[2]); 568 return 1 if ($na[2] > $nb[2]); 569 570 #point 571 return -1 if ($na[3] < $nb[3]); 572 return 1 if ($na[3] > $nb[3]); 573 574 return 0; 575 } 576