chiark / gitweb /
use libinn logging where applicable - debugged
[inn-innduct.git] / scripts / innreport_inn.pm
1 ##########################################################
2 # INN module for innreport (3.*).
3 #
4 # Sample file tested with INN 2.4, 2.3, 2.2, 1.7.2 and 1.5.1
5 #
6 # (c) 1997-1999 by Fabien Tassin <fta@sofaraway.org>
7 # version 3.0.2
8 ##########################################################
9
10 # TODO: add the map file.
11
12 package innreport_inn;
13
14 my $MIN = 1E10;
15 my $MAX = -1;
16
17 my %ctlinnd = ('a', 'addhist',     'D', 'allow',
18                'b', 'begin',       'c', 'cancel',
19                'u', 'changegroup', 'd', 'checkfile',
20                'e', 'drop',        'f', 'flush',
21                'g', 'flushlogs',   'h', 'go',
22                'i', 'hangup',      's', 'mode',
23                'j', 'name',        'k', 'newgroup',
24                'l', 'param',       'm', 'pause',
25                'v', 'readers',     't', 'refile',
26                'C', 'reject',      'o', 'reload',
27                'n', 'renumber',    'z', 'reserve',
28                'p', 'rmgroup',     'A', 'send',
29                'q', 'shutdown',    'B', 'signal',
30                'r', 'throttle',    'w', 'trace',
31                'x', 'xabort',      'y', 'xexec',
32                'E', 'logmode',     'F', 'feedinfo',
33                'T', 'filter',      'P', 'perl',);
34
35 my %timer_names = (idle     => 'idle',
36                    hishave  => 'history lookup',
37                    hisgrep  => 'history grep',
38                    hiswrite => 'history write',
39                    hissync  => 'history sync',
40                    nntpread => 'nntp read',
41                    artparse => 'article parse',
42                    artclean => 'article cleanup',
43                    artwrite => 'article write',
44                    artcncl  => 'article cancel',
45                    artlog   => 'article logging',
46                    sitesend => 'site send',
47                    overv    => 'overview write',
48                    perl     => 'perl filter',
49                    python   => 'python filter',
50                    datamove => 'data move'
51 );
52
53 my %innfeed_timer_names = (
54                    'idle'    => 'idle',
55                    'blstats' => 'backlog stats',
56                    'stsfile' => 'status file',
57                    'newart'  => 'article new',
58                    'prepart' => 'article prepare',
59                    'readart' => 'article read',
60                    'read'    => 'data read',
61                    'write'   => 'data write',
62                    'cb'      => 'callbacks',
63 );
64
65 my %nnrpd_timer_names = (
66                    'idle'    => 'idle',
67                    'newnews' => 'newnews',
68 );
69
70 # init innd timer
71 foreach (values %timer_names) {
72   $innd_time_min{$_} = $MIN;
73   $innd_time_max{$_} = $MAX;
74   $innd_time_time{$_} = 0;   # to avoid a warning... Perl < 5.004
75   $innd_time_num{$_} = 0;    # ...
76 }
77 $innd_time_times = 0;        # ...
78
79 # init innfeed timer
80 foreach (values %innfeed_timer_names) {
81   $innfeed_time_min{$_} = $MIN;
82   $innfeed_time_max{$_} = $MAX;
83   $innfeed_time_time{$_} = 0;   # to avoid a warning... Perl < 5.004
84   $innfeed_time_num{$_} = 0;    # ...
85 }
86 $innfeed_time_times = 0;        # ...
87
88 # init nnrpd timer
89 foreach (values %nnrpd_timer_names) {
90   $nnrpd_time_min{$_} = $MIN;
91   $nnrpd_time_max{$_} = $MAX;
92   $nnrpd_time_time{$_} = 0;   # to avoid a warning... Perl < 5.004
93   $nnrpd_time_num{$_} = 0;    # ...
94 }
95 $nnrpd_time_times = 0;        # ...
96
97 # collect: Used to collect the data.
98 sub collect {
99   my ($day, $hour, $prog, $res, $left, $CASE_SENSITIVE) = @_;
100
101   return 1 if $left =~ /Reading config from (\S+)$/o;
102
103   ########
104   ## inn (from the "news" log file - not from "news.notice")
105   ##
106   if ($prog eq "inn") {
107     # accepted article
108     if ($res =~ m/[\+j]/o) {
109       $hour =~ s/:.*$//o;
110       $inn_flow{"$day $hour"}++;
111       $inn_flow_total++;
112
113       # Memorize the size. This can only be done with INN >= 1.5xx and
114       # DO_LOG_SIZE = DO.
115
116       # server <msg-id> size [feeds]
117       # or
118       # server <msg-id> (filename) size [feeds]
119
120       my ($s) = $left =~ /^\S+ \S+ (?:\(\S+\) )?(\d+)(?: |$)/o;
121       if ($s) {
122         $inn_flow_size{"$day $hour"} += $s;
123         $inn_flow_size_total += $s;
124       }
125       return 1;
126     }
127
128     # 437 Duplicate article
129     if ($left =~ /(\S+) <[^>]+> 437 Duplicate(?: article)?$/o) {
130       my $server = $1;
131       $server = lc $server unless $CASE_SENSITIVE;
132       $inn_badart{$server}++;
133       $inn_duplicate{$server}++;
134       return 1;
135     }
136     # 437 Unapproved for
137     if ($left =~ /(\S+) <[^>]+> 437 Unapproved for \"([^\"]+)\"$/o) {
138       my ($server, $group) = ($1, $2);
139       $server = lc $server unless $CASE_SENSITIVE;
140       $inn_badart{$server}++;
141       $inn_unapproved{$server}++;
142       $inn_unapproved_g{$group}++;
143       return 1;
144     }
145     # 437 Too old -- ...
146     if ($left =~ /(\S+) <[^>]+> 437 Too old -- /o) {
147       my $server = $1;
148       $server = lc $server unless $CASE_SENSITIVE;
149       $inn_badart{$server}++;
150       $inn_tooold{$server}++;
151       return 1;
152     }
153     # 437 Unwanted site ... in path
154     if ($left =~ /(\S+) <[^>]+> 437 Unwanted site (\S+) in path$/o) {
155       my ($server, $site) = ($1, $2);
156       $server = lc $server unless $CASE_SENSITIVE;
157       $inn_badart{$server}++;
158       $inn_uw_site{$server}++;
159       $inn_site_path{$site}++;
160       return 1;
161     }
162     # 437 Unwanted newsgroup "..."
163     if ($left =~ /(\S+) <[^>]+> 437 Unwanted newsgroup \"(\S+)\"$/o) {
164       my ($server, $group) = ($1, $2);
165       ($group) = split(/,/, $group);
166       $server = lc $server unless $CASE_SENSITIVE;
167       $inn_badart{$server}++;
168       $inn_uw_ng_s{$server}++;
169       $inn_uw_ng{$group}++;
170       return 1;
171     }
172     # 437 Unwanted distribution "..."
173     if ($left =~ /(\S+) <[^>]+> 437 Unwanted distribution \"(\S+)\"$/o) {
174       my ($server, $dist) = ($1, $2);
175       $server = lc $server unless $CASE_SENSITIVE;
176       $inn_badart{$server}++;
177       $inn_uw_dist_s{$server}++;
178       $inn_uw_dist{$dist}++;
179       return 1;
180     }
181     # 437 Linecount x != y +- z
182     if ($left =~ /(\S+) <[^>]+> 437 Linecount/o) {
183       my $server = $1;
184       $server = lc $server unless $CASE_SENSITIVE;
185       $inn_badart{$server}++;
186       $inn_linecount{$server}++;
187       return 1;
188     }
189     # 437 No colon-space in "xxxx" header
190     if ($left =~ /(\S+) <[^>]+> 437 No colon-space in \"[^\"]+\" header/o) {
191       my $server = $1;
192       $server = lc $server unless $CASE_SENSITIVE;
193       $inn_badart{$server}++;
194       $innd_others{$server}++;
195       $innd_no_colon_space{$server}++;
196       return 1;
197     }
198     # 437 Article posted in the future -- "xxxxx"
199     if ($left =~ /(\S+) <[^>]+> 437 Article posted in the future -- \"[^\"]+\"/o) {
200       my $server = $1;
201       $server = lc $server unless $CASE_SENSITIVE;
202       $innd_posted_future{$server}++;
203       $innd_others{$server}++;
204       $inn_badart{$server}++;
205       return 1;
206     }
207     # 437 article includes "....."
208     if ($left =~ /(\S+) <[^>]+> 437 article includes/o) {
209       my $server = $1;
210       $server = lc $server unless $CASE_SENSITIVE;
211       $innd_strange_strings{$server}++;
212       $innd_others{$server}++;
213       $inn_badart{$server}++;
214       return 1;
215     }
216     # Cancelling <...>
217     if ($left =~ /(\S+) <[^>]+> Cancelling/o) {
218       return 1;
219     }
220     # all others are just counted as "Other"
221     if ($left =~ /(\S+) /o) {
222       my $server = $1;
223       $server = lc $server unless $CASE_SENSITIVE;
224       $inn_badart{$server}++;
225       $innd_others{$server}++;
226       return 1;
227     }
228   }
229
230   ########
231   ## innd
232   if ($prog eq "innd") {
233     ## Note for innd logs:
234     ## there's a lot of entries detected but still not used
235     ## (because of a lack of interest).
236
237     # think it's a dotquad
238     return 1 if $left =~ /^think it\'s a dotquad$/o;
239     if ($left =~ /^SERVER /o) {
240       # SERVER perl filtering enabled
241       return 1 if $left =~ /^SERVER perl filtering enabled$/o;
242       # SERVER perl filtering disabled
243       return 1 if $left =~ /^SERVER perl filtering disabled$/o;
244       # SERVER Python filtering enabled
245       return 1 if $left =~ /^SERVER Python filtering enabled$/o;
246       # SERVER Python filtering disabled
247       return 1 if $left =~ /^SERVER Python filtering disabled$/o;
248       # SERVER cancelled +id
249       return 1 if $left =~ /^SERVER cancelled /o;
250     }
251     # Python filter
252     return 1 if $left =~ /^defined python methods$/o;
253     return 1 if $left =~ /^reloading pyfilter$/o;
254     return 1 if $left =~ /^reloaded pyfilter OK$/o;
255     return 1 if $left =~ /^python interpreter initialized OK$/o;
256     return 1 if $left =~ /^python method \w+ not found$/o; 
257     return 1 if $left =~ /^python: First load, so I can do initialization stuff\.$/o;
258     return 1 if $left =~ /^python: filter_before_reload executing\.\.\.$/o;
259     return 1 if $left =~ /^python: I\'m just reloading, so skip the formalities\.$/o;
260     return 1 if $left =~ /^python: spamfilter successfully hooked into INN$/o;
261     return 1 if $left =~ /^python: state change from \w+ to \w+ - /o;
262     return 1 if $left =~ /^python: filter_close running, bye!$/o;
263     # rejecting[perl]
264     if ($left =~ /^rejecting\[perl\] <[^>]+> \d+ (.*)/o) {
265       $innd_filter_perl{$1}++;
266       return 1;
267     }
268     # rejecting[python]
269     if ($left =~ /^rejecting\[python\] <[^>]+> \d+ (.*)/o) {
270       $innd_filter_python{$1}++;
271       return 1;
272     }
273     # closed lost
274     return 1 if $left =~ /^\S+ closed lost \d+/o;
275     # new control command
276     if ($left =~ /^ctlinnd command (\w)(:.*)?/o) {
277       my $command = $1;
278       my $cmd = $ctlinnd{$command};
279       $cmd = $command unless $cmd;
280       return 1 if $cmd eq 'flush'; # to avoid a double count
281       $innd_control{"$cmd"}++;
282       return 1;
283     }
284     # old control command (by letter)
285     if ($left =~ /^(\w)$/o) {
286       my $command = $1;
287       my $cmd = $ctlinnd{$command};
288       $cmd = $command unless $cmd;
289       return 1 if $cmd eq 'flush'; # to avoid a double count
290       $innd_control{"$cmd"}++;
291       return 1;
292     }
293     # old control command (letter + reason)
294     if ($left =~ /^(\w):.*$/o) {
295       my $command = $1;
296       my $cmd = $ctlinnd{$command};
297       $cmd = $command unless $cmd;
298       return 1 if $cmd eq 'flush'; # to avoid a double count
299       $innd_control{"$cmd"}++;
300       return 1;
301     }
302     # opened
303     return 1 if $left =~ /\S+ opened \S+:\d+:file$/o;
304     # buffered
305     return 1 if $left =~ /\S+ buffered$/o;
306     # spawned
307     return 1 if $left =~ /\S+ spawned \S+:\d+:proc:\d+$/o;
308     return 1 if $left =~ /\S+ spawned \S+:\d+:file$/o;
309     # running
310     return 1 if $left =~ /\S+ running$/o;
311     # sleeping
312     if ($left =~ /(\S+):\d+:proc:\d+ sleeping$/o) {
313       my $server = $1;
314       $server = lc $server unless $CASE_SENSITIVE;
315       $innd_blocked{$server}++;
316       return 1;
317     }
318     # blocked sleeping
319     if ($left =~ /(\S+):\d+:proc:\d+ blocked sleeping/o) {
320       my $server = $1;
321       $server = lc $server unless $CASE_SENSITIVE;
322       $innd_blocked{$server}++;
323       return 1;
324     }
325     if ($left =~ /(\S+):\d+ blocked sleeping/o) {
326       my $server = $1;
327       $server = lc $server unless $CASE_SENSITIVE;
328       $innd_blocked{$server}++;
329       return 1;
330     }
331     # restarted
332     return 1 if $left =~ m/^\S+ restarted$/o;
333     # starting
334     return 1 if $left =~ m/^\S+ starting$/o;
335     # readclose
336     return 1 if $left =~ m/^\S+:\d+ readclose+$/o;
337     # rejected 502
338     if ($left =~ m/^(\S+) rejected 502$/) {
339       my $server = $1;
340       $server = lc $server unless $CASE_SENSITIVE;
341       $innd_no_permission{$server}++;
342       return 1;
343     }
344     # rejected 505
345     if ($left =~ m/^(\S+) rejected 505$/) {
346       my $server = $1;
347       $server = lc $server unless $CASE_SENSITIVE;
348       $innd_too_many_connects_per_minute{$server}++;
349       return 1;
350     }
351     # connected
352     if ($left =~ /^(\S+) connected \d+/o) {
353       my $server = $1;
354       $server = lc $server unless $CASE_SENSITIVE;
355       $innd_connect{$server}++;
356       return 1;
357     }
358     # closed (with times)
359     if ($left =~ /(\S+):\d+ closed seconds (\d+) accepted (\d+) refused (\d+) rejected (\d+) duplicate (\d+) accepted size (\d+) duplicate size (\d+)(?: rejected size (\d+))?$/o) {
360       my ($server, $seconds, $accepted, $refused, $rejected, $duplicate, $accptsize, $dupsize) =
361         ($1, $2, $3, $4, $5, $6, $7, $8);
362       $server = lc $server unless $CASE_SENSITIVE;
363       $innd_seconds{$server} += $seconds;
364       $innd_accepted{$server} += $accepted;
365       $innd_refused{$server} += $refused;
366       $innd_rejected{$server} += $rejected;
367       $innd_stored_size{$server} += $accptsize;
368       $innd_duplicated_size{$server} += $dupsize;
369       return 1;
370     } elsif ($left =~ /(\S+):\d+ closed seconds (\d+) accepted (\d+) refused (\d+) rejected (\d+)$/o) {
371       # closed (with times)
372       my ($server, $seconds, $accepted, $refused, $rejected) =
373         ($1, $2, $3, $4, $5);
374       $server = lc $server unless $CASE_SENSITIVE;
375       $innd_seconds{$server} += $seconds;
376       $innd_accepted{$server} += $accepted;
377       $innd_refused{$server} += $refused;
378       $innd_rejected{$server} += $rejected;
379       return 1;
380     }
381     # closed (without times (?))
382     return 1 if $left =~ m/\S+ closed$/o;
383     # closed (for a cancel feed - MODE CANCEL)
384     return 1 if $left =~ m/localhost:\d+ closed seconds \d+ cancels \d+$/o;
385     # checkpoint
386     return 1 if $left =~ m/^\S+:\d+ checkpoint /o;
387     # if ($left =~ /(\S+):\d+ checkpoint seconds (\d+) accepted (\d+)
388     #     refused (\d+) rejected (\d+)$/) {
389     #   # Skipped...
390     #   my ($server, $seconds, $accepted, $refused, $rejected) =
391     #      ($1, $2, $3, $4, $5);
392     #   $innd_seconds{$server} += $seconds;
393     #   $innd_accepted{$server} += $accepted;
394     #   $innd_refused{$server} += $refused;
395     #   $innd_rejected{$server} += $rejected;
396     #   return 1;
397     # }
398
399     # flush
400     if ($left =~ /(\S+) flush$/o) {
401       $innd_control{"flush"}++;
402       return 1;
403     }
404     # flush-file
405     if ($left =~ /flush_file/) {
406        $innd_control{"flush_file"}++;
407        return 1;
408      }
409     # too many connections from site
410     if ($left =~ /too many connections from (\S+)/o) {
411       $innd_max_conn{$1}++;
412       return 1;
413     }
414     # overview exit 0 elapsed 23 pid 28461
415     return 1 if $left =~ m/\S+ exit \d+ .*$/o;
416     # internal rejecting huge article
417     if ($left =~ /(\S+) internal rejecting huge article/o) {
418       my $server = $1;
419       $server =~ s/:\d+$//o;
420       $server = lc $server unless $CASE_SENSITIVE;
421       $innd_huge{$server}++;
422       return 1;
423     }
424     # internal closing free channel
425     if ($left =~ /(\S+) internal closing free channel/o) {
426       $innd_misc{"Free channel"}++;
427       return 1;
428     }
429     # internal (other)
430     return 1 if $left =~ /\S+ internal/o;
431     # wakeup
432     return 1 if $left =~ /\S+ wakeup$/o;
433     # throttle
434     if ($left =~ /(\S+) throttled? /) {
435       $innd_control{"throttle"}++;
436       return 1;
437     }
438     # profile timer
439     # ME time X nnnn X(X) [...]
440     # The exact timers change from various versions of INN, so try to deal
441     # with this in a general fashion.
442     if ($left =~ m/^\S+\s+                         # ME
443                    time\s(\d+)\s+                  # time
444                    ((?:\S+\s\d+\(\d+\)\s*)+)       # timer values
445                    $/ox) {
446       $innd_time_times += $1;
447       my $timers = $2;
448
449       while ($timers =~ /(\S+) (\d+)\((\d+)\)\s*/g) {
450         my $name = $timer_names{$1} || $1;
451         my $average = $2 / ($3 || 1);
452         $innd_time_time{$name} += $2;
453         $innd_time_num{$name} += $3;
454         $innd_time_min{$name} = $average
455           if ($3 && $innd_time_min{$name} > $average);
456         $innd_time_max{$name} = $average
457           if ($3 && $innd_time_max{$name} < $average);
458       }
459       return 1;
460     }
461     # ME time xx idle xx(xx)     [ bug ? a part of timer ?]
462     return 1 if $left =~ m/^ME time \d+ idle \d+\(\d+\)\s*$/o;
463     # ME HISstats x hitpos x hitneg x missed x dne
464     #
465     # from innd/his.c:
466     # HIShitpos: the entry existed in the cache and in history.
467     # HIShitneg: the entry existed in the cache but not in history.
468     # HISmisses: the entry was not in the cache, but was in the history file.
469     # HISdne:    the entry was not in cache or history.
470     if ($left =~ m/^ME\ HISstats                  # ME HISstats
471                    \ (\d+)\s+hitpos               # hitpos
472                    \ (\d+)\s+hitneg               # hitneg
473                    \ (\d+)\s+missed               # missed
474                    \ (\d+)\s+dne                  # dne
475                    $/ox) {
476       $innd_his{'Positive hits'} += $1;
477       $innd_his{'Negative hits'} += $2;
478       $innd_his{'Cache misses'}  += $3;
479       $innd_his{'Do not exist'}  += $4;
480       return 1;
481     }
482     # SERVER history cache final: 388656 lookups, 1360 hits
483     if ($left =~ m/^SERVER history cache final: (\d+) lookups, (\d+) hits$/) {
484       $innd_cache{'Lookups'} += $1;
485       $innd_cache{'Hits'}    += $2;
486       return 1;
487     }
488     # bad_hosts (appears after a "cant gesthostbyname" from a feed)
489     return 1 if $left =~ m/\S+ bad_hosts /o;
490     # cant read
491     return 1 if $left =~ m/\S+ cant read/o;
492     # cant write
493     return 1 if $left =~ m/\S+ cant write/o;
494     # cant flush
495     return 1 if $left =~ m/\S+ cant flush/o;
496     # spoolwake
497     return 1 if $left =~ m/\S+ spoolwake$/o;
498     # spooling
499     return 1 if $left =~ m/\S+ spooling/o;
500     # DEBUG
501     return 1 if $left =~ m/^DEBUG /o;
502     # NCmode
503     return 1 if $left =~ m/\S+ NCmode /o;
504     # outgoing
505     return 1 if $left =~ m/\S+ outgoing/o;
506     # inactive
507     return 1 if $left =~ m/\S+ inactive/o;
508     # timeout
509     return 1 if $left =~ m/\S+ timeout/o;
510     # lcsetup
511     return 1 if $left =~ m/\S+ lcsetup/o;
512     # rcsetup
513     return 1 if $left =~ m/\S+ rcsetup/o;
514     # flush_all
515     return 1 if $left =~ m/\S+ flush_all/o;
516     # buffered
517     return 1 if $left =~ m/\S+ buffered$/o;
518     # descriptors
519     return 1 if $left =~ m/\S+ descriptors/o;
520     # ccsetup
521     return 1 if $left =~ m/\S+ ccsetup/o;
522     # renumbering
523     return 1 if $left =~ m/\S+ renumbering/o;
524     # renumber
525     return 1 if $left =~ m/\S+ renumber /o;
526     # ihave from me
527     if ($left =~ m/\S+ ihave_from_me /o) {
528       $controlchan_ihave_site{'ME'}++;
529       return 1;
530     }
531     # sendme from me
532     if ($left =~ m/\S+ sendme_from_me /o) {
533       $controlchan_sendme_site{'ME'}++;
534       return 1;
535     }
536     # newgroup
537     if ($left =~ m/\S+ newgroup (\S+) as (\S)/o) {
538       $innd_newgroup{$1} = $2;
539       return 1;
540     }
541     # rmgroup
542     if ($left =~ m/\S+ rmgroup (\S+)$/o) {
543       $innd_rmgroup{$1}++;
544       return 1;
545     }
546     # changegroup
547     if ($left =~ m/\S+ change_group (\S+) to (\S)/o) {
548       $innd_changegroup{$1} = $2;
549       return 1;
550     }
551     # paused
552     if ($left =~ m/(\S+) paused /o) {
553       $innd_control{"paused"}++;
554       return 1;
555     }
556     # throttled
557     return 1 if $left =~ m/\S+ throttled/o;
558     # reload
559     if ($left =~ m/(\S+) reload/o) {
560       $innd_control{"reload"}++;
561       return 1;
562     }
563     # shutdown
564     if ($left =~ m/(\S+) shutdown/o) {
565       $innd_control{"shutdown"}++;
566       return 1;
567     }
568     # SERVER servermode paused
569     return 1 if ($left =~ /(\S+) servermode paused$/o);
570     # SERVER servermode running
571     return 1 if ($left =~ /(\S+) servermode running$/o);
572     # SERVER flushlogs paused
573     if ($left =~ /(\S+) flushlogs /) {
574       $innd_control{"flushlogs"}++;
575       return 1;
576     }
577     # think it's a dotquad
578     return 1 if $left =~ /think it\'s a dotquad: /o;
579     # bad_ihave
580     if ($left =~ /(\S+) bad_ihave /) {
581       my $server = $1;
582       $server =~ s/:\d+$//o;
583       $server = lc $server unless $CASE_SENSITIVE;
584       $innd_bad_ihave{$server}++;
585       return 1;
586     }
587     # bad_messageid
588     if ($left =~ /(\S+) bad_messageid/o) {
589       my $server = $1;
590       $server =~ s/:\d+$//o;
591       $server = lc $server unless $CASE_SENSITIVE;
592       $innd_bad_msgid{$server}++;
593       return 1;
594     }
595     # bad_sendme
596     if ($left =~ /(\S+) bad_sendme /o) {
597       my $server = $1;
598       $server =~ s/:\d+$//o;
599       $server = lc $server unless $CASE_SENSITIVE;
600       $innd_bad_sendme{$server}++;
601       return 1;
602     }
603     # bad_command
604     if ($left =~ /(\S+) bad_command /o) {
605       my $server = $1;
606       $server =~ s/:\d+$//o;
607       $server = lc $server unless $CASE_SENSITIVE;
608       $innd_bad_command{$server}++;
609       return 1;
610     }
611     # bad_newsgroup
612     if ($left =~ /(\S+) bad_newsgroup /o) {
613       my $server = $1;
614       $server =~ s/:\d+$//o;
615       $innd_bad_newsgroup{$server}++;
616       $server = lc $server unless $CASE_SENSITIVE;
617       return 1;
618     }
619     if ($left =~ m/ cant /o) {
620       # cant select Bad file number
621       if ($left =~ / cant select Bad file number/o) {
622         $innd_misc{"Bad file number"}++;
623         return 1;
624       }
625       # cant gethostbyname
626       if ($left =~ / cant gethostbyname/o) {
627         $innd_misc{"gethostbyname error"}++;
628         return 1;
629       }
630       # cant accept RCreader
631       if ($left =~ / cant accept RCreader /o) {
632         $innd_misc{"RCreader"}++;
633         return 1;
634       }
635       # cant sendto CCreader
636       if ($left =~ / cant sendto CCreader /o) {
637         $innd_misc{"CCreader"}++;
638         return 1;
639       }
640       # cant (other) skipped - not particularly interesting
641       return 1;
642     }
643     # bad_newsfeeds no feeding sites
644     return 1 if $left =~ /\S+ bad_newsfeeds no feeding sites/o;
645     # CNFS: cycbuff rollover - possibly interesting
646     return 1 if $left =~ /CNFS(?:-sm)?: cycbuff \S+ rollover to cycle/o;
647     # CNFS: CNFSflushallheads: flushing - possibly interesting
648     return 1 if $left =~ /CNFS(?:-sm)?: CNFSflushallheads: flushing /o;
649     # CNFS: metacycbuff rollover with SEQUENTIAL
650     return 1 if $left =~ /CNFS(?:-sm)?: metacycbuff \S+ cycbuff is moved to /o;
651     # Cleanfeed status reports
652     return 1 if $left =~ /^filter: status/o;
653     return 1 if $left =~ /^filter: Reloading bad files/o;
654   }
655   ########
656   ## innfeed
657   if ($prog eq "innfeed") {
658     # connected
659     if ($left =~ /(\S+):\d+ connected$/) {
660       my $server = $1;
661       $server = lc $server unless $CASE_SENSITIVE;
662       $innfeed_connect{$server}++;
663       return 1;
664     }
665     # closed periodic
666     return 1 if $left =~ m/\S+:\d+ closed periodic$/o;
667     # periodic close
668     return 1 if $left =~ m/\S+:\d+ periodic close$/o;
669     # final (child)
670     return 1 if $left =~ m/\S+:\d+ final seconds \d+ offered \d+ accepted \d+ refused \d+ rejected \d+/o;
671     # global (real)
672     return 1 if $left =~ m/\S+ global seconds \d+ offered \d+ accepted \d+ refused \d+ rejected \d+ missing \d+/o;
673     # final (real) (new format)
674     if ($left =~ /(\S+) final seconds (\d+) offered (\d+) accepted (\d+) refused (\d+) rejected (\d+) missing (\d+) accsize (\d+) rejsize (\d+) spooled (\d+)/o) {
675       my ($server, $seconds, $offered, $accepted, $refused, $rejected,
676           $missing, $accepted_size, $rejected_size, $spooled) = ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10);
677       $server = lc $server unless $CASE_SENSITIVE;
678       $innfeed_seconds{$server} += $seconds;
679       $innfeed_offered{$server} += $offered;
680       $innfeed_accepted{$server} += $accepted;
681       $innfeed_refused{$server} += $refused;
682       $innfeed_rejected{$server} += $rejected;
683       $innfeed_missing{$server} += $missing;
684       $innfeed_spooled{$server} += $spooled;
685       $innfeed_accepted_size{$server} += $accepted_size;
686       $innfeed_rejected_size{$server} += $rejected_size;
687       return 1;
688     } elsif ($left =~ /(\S+) final seconds (\d+) offered (\d+) accepted (\d+) refused (\d+) rejected (\d+) missing (\d+) spooled (\d+)/o) {
689       my ($server, $seconds, $offered, $accepted, $refused, $rejected,
690           $missing, $spooled) = ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10);
691       $server = lc $server unless $CASE_SENSITIVE;
692       $innfeed_seconds{$server} += $seconds;
693       $innfeed_offered{$server} += $offered;
694       $innfeed_accepted{$server} += $accepted;
695       $innfeed_refused{$server} += $refused;
696       $innfeed_rejected{$server} += $rejected;
697       $innfeed_missing{$server} += $missing;
698       $innfeed_spooled{$server} += $spooled;
699       return 1;
700     }
701     # final (only seconds & spooled)
702     if ($left =~ /(\S+) final seconds (\d+) spooled (\d+)/o) {
703       my ($server, $seconds, $spooled) = ($1, $2, $3);
704       $server = lc $server unless $CASE_SENSITIVE;
705       $innfeed_seconds{$server} += $seconds;
706       $innfeed_spooled{$server} += $spooled;
707       return 1;
708     }
709     # checkpoint
710     return 1 if $left =~ m/\S+ checkpoint seconds/o;
711     # ME file xxxx shrunk from yyyy to zzz
712     if ($left =~ /^ME file (.*)\.output shrunk from (\d+) to (\d+)$/) {
713       my ($file, $s1, $s2) = ($1, $2, $3);
714       $file =~ s|^.*/([^/]+)$|$1|; # keep only the server name
715       $innfeed_shrunk{$file} += $s1 - $s2;
716       return 1;
717     }
718     # profile timer
719     # ME time X nnnn X(X) [...]
720     return 1 if $left =~ m/backlogstats/;
721     if ($left =~ m/^\S+\s+                         # ME
722                    time\s(\d+)\s+                  # time
723                    ((?:\S+\s\d+\(\d+\)\s*)+)       # timer values
724                    $/ox) {
725       $innfeed_time_times += $1;
726       my $timers = $2;
727
728       while ($timers =~ /(\S+) (\d+)\((\d+)\)\s*/g) {
729         my $name = $innfeed_timer_names{$1} || $1;
730         my $average = $2 / ($3 || 1);
731         $innfeed_time_time{$name} += $2;
732         $innfeed_time_num{$name} += $3;
733         $innfeed_time_min{$name} = $average
734           if ($3 && $innfeed_time_min{$name} > $average);
735         $innfeed_time_max{$name} = $average
736           if ($3 && $innfeed_time_max{$name} < $average);
737       }
738       return 1;
739     }
740     # xxx grabbing external tape file
741     return 1 if $left =~ m/ grabbing external tape file/o;
742     # hostChkCxns - maxConnections was
743     return 1 if $left =~ m/hostChkCxns - maxConnections was /o;
744     # cxnsleep
745     return 1 if $left =~ m/\S+ cxnsleep .*$/o;
746     # idle
747     return 1 if $left =~ m/\S+ idle tearing down connection$/o;
748     # remote
749     return 1 if $left =~ m/\S+ remote .*$/o;
750     # spooling
751     return 1 if $left =~ m/\S+ spooling no active connections$/o;
752     # ME articles total
753     return 1 if $left =~ m/(?:SERVER|ME) articles total \d+ bytes \d+/o;
754     # ME articles active
755     return 1 if $left =~ m/(?:SERVER|ME) articles active \d+ bytes \d+/o;
756     # connect : Connection refused
757     return 1 if $left =~ m/connect : Connection refused/o;
758     # connect : Network is unreachable
759     return 1 if $left =~ m/connect : Network is unreachable/o;
760     # connect : Address family not supported by protocol
761     return 1 if $left =~ m/connect : Address family not supported by protocol/o;
762     # connect : No route to host
763     return 1 if $left =~ m/connect : No route to host/o;
764     # connection vanishing
765     return 1 if $left =~ m/connection vanishing/o;
766     # can't resolve hostname
767     return 1 if $left =~ m/can\'t resolve hostname/o;
768     # new hand-prepared backlog file
769     return 1 if $left =~ m/new hand-prepared backlog file/o;
770     # flush re-connect failed
771     return 1 if $left =~ m/flush re-connect failed/o;
772     # internal QUIT while write pending
773     return 1 if $left =~ m/internal QUIT while write pending/o;
774     # ME source lost . Exiting
775     return 1 if $left =~ m/(?:SERVER|ME) source lost . Exiting/o;
776     # ME starting innfeed (+version & date)
777     return 1 if $left =~ m/(?:SERVER|ME) starting (?:innfeed|at)/o;
778     # ME finishing at (date)
779     return 1 if $left =~ m/(?:SERVER|ME) finishing at /o;
780     # mode no-CHECK entered
781     return 1 if $left =~ m/mode no-CHECK entered/o;
782     # mode no-CHECK exited
783     return 1 if $left =~ m/mode no-CHECK exited/o;
784     # closed
785     return 1 if $left =~ m/^(\S+) closed$/o;
786     # global (+ seconds offered accepted refused rejected missing)
787     return 1 if $left =~ m/^(\S+) global/o;
788     # idle connection still has articles
789     return 1 if $left =~ m/^(\S+) idle connection still has articles$/o;
790     # missing article for IHAVE-body
791     return 1 if $left =~ m/^(\S+) missing article for IHAVE-body$/o;
792     # cannot continue
793     return 1 if $left =~ m/^cannot continue/o;
794     if ($left =~ /^(?:SERVER|ME)/o) {
795       # ME dropping articles into ...
796       return 1 if $left =~ / dropping articles into /o;
797       # ME dropped ...
798       return 1 if $left =~ / dropped /o;
799       # ME internal bad data in checkpoint file
800       return 1 if $left =~ m/ internal bad data in checkpoint/o;
801       # ME two filenames for same article
802       return 1 if $left =~ m/ two filenames for same article/o;
803       # ME unconfigured peer
804       return 1 if $left =~ m/ unconfigured peer/o;
805       # exceeding maximum article size
806       return 1 if $left =~ m/ exceeding maximum article byte/o;
807       # no space left on device errors
808       return 1 if $left =~ m/ ioerr fclose/o;
809       return 1 if $left =~ m/ lock failed for host/o;
810       return 1 if $left =~ m/ lock file pid-write/o;
811       return 1 if $left =~ m/ locked cannot setup peer/o;
812       return 1 if $left =~ m/ received shutdown signal/o;
813       # unconfigured peer
814       return 1 if $left =~ m/ unconfigured peer/o;
815       # ME lock
816       return 1 if $left =~ m/ lock/o;
817       # ME exception: getsockopt (0): Socket operation on non-socket
818       return 1 if $left =~ m/ exception: getsockopt /o;
819       # ME config aborting fopen (...) Permission denied
820       return 1 if $left =~ m/ config aborting fopen /o;
821       # ME cant chmod innfeed.pid....
822       return 1 if $left =~ m/ cant chmod \S+\/innfeed.pid/o;
823       return 1 if $left =~ m/ tape open failed /o;
824       return 1 if $left =~ m/ oserr open checkpoint file:/o;
825       # ME finishing (quickly)
826       return 1 if $left =~ m/\(quickly\) /o;
827       # ME config: value of streaming is not a boolean
828       return 1 if $left =~ m/config: value of \S+ is not/o;
829     }
830     # hostChkCxn - now: x.xx, prev: x.xx, abs: xx, curr: x
831     return 1 if $left =~ m/ hostChkCxn - now/o;
832     # loading path_to_config_file/innfeed.conf
833     return 1 if $left =~ m/loading /o;
834     # Finnaly, to avoid problems with strange error lines, ignore them.
835     #return 1 if ($left =~ /ME /);
836   }
837   ########
838   ## innxmit
839   if ($prog eq "innxmit") {
840     # 437 Duplicate article
841     if ($left =~ /(\S+) rejected [^\s]+ \(.*?\) 437 Duplicate article$/o) {
842       my $server = $1;
843       $server = lc $server unless $CASE_SENSITIVE;
844       $innxmit_badart{$server}++;
845       $innxmit_duplicate{$server}++;
846       return 1;
847     }
848     # 437 Unapproved for
849     if ($left =~ /(\S+) rejected [^\s]+ \(.*\) 437 Unapproved for \"(.*?)\"$/o) {
850       my ($server, $group) = ($1, $2);
851       $server = lc $server unless $CASE_SENSITIVE;
852       $innxmit_badart{$server}++;
853       $innxmit_unapproved{$server}++;
854       $innxmit_unapproved_g{$group}++;
855       return 1;
856     }
857     # 437 Too old -- ...
858     if ($left =~ /(\S+) rejected [^\s]+ \(.*\) 437 Too old -- \".*?\"$/o) {
859       my $server = $1;
860       $server = lc $server unless $CASE_SENSITIVE;
861       $innxmit_badart{$server}++;
862       $innxmit_tooold{$server}++;
863       return 1;
864     }
865     # 437 Unwanted site ... in path
866     if ($left =~
867       /(\S+) rejected [^\s]+ \(.*?\) 437 Unwanted site (\S+) in path$/o) {
868       my ($server, $site) = ($1, $2);
869       $server = lc $server unless $CASE_SENSITIVE;
870       $innxmit_badart{$server}++;
871       $innxmit_uw_site{$server}++;
872       # $innxmit_site_path{$site}++;
873       return 1;
874     }
875     # 437 Unwanted newsgroup "..."
876     if ($left =~
877       /(\S+) rejected [^\s]+ \(.*?\) 437 Unwanted newsgroup \"(\S+)\"$/o) {
878       my ($server, $group) = ($1, $2);
879       $server = lc $server unless $CASE_SENSITIVE;
880       $innxmit_badart{$server}++;
881       $innxmit_uw_ng_s{$server}++;
882       $innxmit_uw_ng{$group}++;
883       return 1;
884     }
885     # 437 Unwanted distribution "..."
886     if ($left =~
887       /(\S+) rejected [^\s]+ \(.*?\) 437 Unwanted distribution \"(\S+)\"$/o) {
888       my ($server, $dist) = ($1, $2);
889       $server = lc $server unless $CASE_SENSITIVE;
890       $innxmit_badart{$server}++;
891       $innxmit_uw_dist_s{$server}++;
892       $innxmit_uw_dist{$dist}++;
893       return 1;
894     }
895     # xx rejected foo.bar/12345 (foo/bar/12345) 437 Unwanted distribution "..."
896     if ($left =~ /^(\S+) rejected .* 437 Unwanted distribution \"(\S+)\"$/o) {
897       my ($server, $dist) = ($1, $2);
898       $server = lc $server unless $CASE_SENSITIVE;
899       $innxmit_badart{$server}++;
900       $innxmit_uw_dist_s{$server}++;
901       $innxmit_uw_dist{$dist}++;
902       return 1;
903     }
904     # 437 Linecount x != y +- z
905     if ($left =~ /(\S+) rejected [^\s]+ \(.*?\) 437 Linecount/o) {
906       my $server = $1;
907       $server = lc $server unless $CASE_SENSITIVE;
908       $innxmit_badart{$server}++;
909       $innxmit_linecount{$server}++;
910       return 1;
911     }
912     # 437 Newsgroup name illegal -- "xxx"
913     if ($left =~ /(\S+) rejected .* 437 Newsgroup name illegal -- "[^\"]*"$/) {
914       my $server = $1;
915       $server = lc $server unless $CASE_SENSITIVE;
916       $innxmit_others{$server}++;
917       $innxmit_badart{$server}++;
918       return 1;
919     }
920     # Streaming retries
921     return 1 if ($left =~ /\d+ Streaming retries$/o);
922     # ihave failed
923     if ($left =~ /(\S+) ihave failed/o) {
924       my $server = $1;
925       $server = lc $server unless $CASE_SENSITIVE;
926       $innxmit_ihfail{$server} = 1;
927       if ($left = /436 \S+ NNTP \S+ out of space/o) {
928         $innxmit_nospace{$server}++;
929         return 1;
930       }
931       if ($left = /400 \S+ space/o) {
932         $innxmit_nospace{$server}++;
933         return 1;
934       }
935       if ($left = /400 Bad file/o) {
936         $innxmit_crefused{$server}++;
937         return 1;
938       }
939       if ($left = /480 Transfer permission denied/o) {
940         $innxmit_crefused{$server}++;
941         return 1;
942       }
943     }
944     # stats (new format)
945     if ($left =~
946       /(\S+) stats offered (\d+) accepted (\d+) refused (\d+) rejected (\d+) missing (\d+) accsize (\d+) rejsize (\d+)$/o) {
947       my ($server, $offered, $accepted, $refused, $rejected, $missing, $accbytes, $rejbytes) =
948         ($1, $2, $3, $4, $5, $6, $7, $8);
949       $server = lc $server unless $CASE_SENSITIVE;
950       $innxmit_offered{$server} += $offered;
951       $innxmit_offered{$server} -= $innxmit_ihfail{$server}
952         if ($innxmit_ihfail{$server});
953       $innxmit_accepted{$server} += $accepted;
954       $innxmit_refused{$server} += $refused;
955       $innxmit_rejected{$server} += $rejected;
956       $innxmit_missing{$server} += $missing;
957       $innxmit_accepted_size{$server} += $accbytes;
958       $innxmit_rejected_size{$server} += $rejbytes;
959       $innxmit_site{$server}++;
960       $innxmit_ihfail{$server} = 0;
961       return 1;
962     }
963     # stats
964     if ($left =~
965       /(\S+) stats offered (\d+) accepted (\d+) refused (\d+) rejected (\d+)$/o) {
966       my ($server, $offered, $accepted, $refused, $rejected) =
967         ($1, $2, $3, $4, $5);
968       $server = lc $server unless $CASE_SENSITIVE;
969       $innxmit_offered{$server} += $offered;
970       $innxmit_offered{$server} -= $innxmit_ihfail{$server}
971         if ($innxmit_ihfail{$server});
972       $innxmit_accepted{$server} += $accepted;
973       $innxmit_refused{$server} += $refused;
974       $innxmit_rejected{$server} += $rejected;
975       $innxmit_site{$server}++;
976       $innxmit_ihfail{$server} = 0;
977       return 1;
978     }
979     # times
980     if ($left =~ /(\S+) times user (\S+) system (\S+) elapsed (\S+)$/o) {
981       my ($server, $user, $system, $elapsed) = ($1, $2, $3, $4);
982       $server = lc $server unless $CASE_SENSITIVE;
983       $innxmit_times{$server} += $elapsed;
984       return 1;
985     }
986     # connect & no space
987     if ($left =~ /(\S+) connect \S+ 400 No space/o) {
988       my $server = $1;
989       $server = lc $server unless $CASE_SENSITIVE;
990       $innxmit_nospace{$server}++;
991       $innxmit_site{$server}++;
992       return 1;
993     }
994     # connect & NNTP no space
995     if ($left =~ /(\S+) connect \S+ 400 \S+ out of space/o) {
996       my $server = $1;
997       $server = lc $server unless $CASE_SENSITIVE;
998       $innxmit_nospace{$server}++;
999       $innxmit_site{$server}++;
1000       return 1;
1001     }
1002     # connect & loadav
1003     if ($left =~ /(\S+) connect \S+ 400 loadav/o) {
1004       my $server = $1;
1005       if ($left =~ /expir/i) {
1006         $server = lc $server unless $CASE_SENSITIVE;
1007         $innxmit_expire{$server}++;
1008         $innxmit_site{$server}++;
1009         return 1;
1010       }
1011     }
1012     # connect 400 (other)
1013     if ($left =~ /(\S+) connect \S+ 400/o) {
1014       my $server = $1;
1015       $server = lc $server unless $CASE_SENSITIVE;
1016       $innxmit_crefused{$server}++;
1017       $innxmit_site{$server}++;
1018       return 1;
1019     }
1020     # connect failed
1021     if ($left =~ /(\S+) connect failed/o) {
1022       my $server = $1;
1023       $server = lc $server unless $CASE_SENSITIVE;
1024       $innxmit_cfail_host{$server}++;
1025       $innxmit_site{$server}++;
1026       return 1;
1027     }
1028     # authenticate failed
1029     if ($left =~ /(\S+) authenticate failed/o) {
1030       my $server = $1;
1031       $server = lc $server unless $CASE_SENSITIVE;
1032       $innxmit_afail_host{$server}++;
1033       $innxmit_site{$server}++;
1034       return 1;
1035     }
1036     # xxx ihave failed 400 loadav [innwatch:hiload] yyy gt zzz
1037     if ($left =~ /^(\S+) ihave failed 400 loadav/o) {
1038       my $server = $1;
1039       $server = lc $server unless $CASE_SENSITIVE;
1040       $innxmit_hiload{$server}++;
1041       return 1;
1042     }
1043     # ihave failed
1044     return 1 if ($left =~ /\S+ ihave failed/o);
1045     # requeued (....) 436 No space
1046     return 1 if ($left =~ /\S+ requeued \S+ 436 No space/o);
1047     # requeued (....) 400 No space
1048     return 1 if ($left =~ /\S+ requeued \S+ 400 No space/o);
1049     # requeued (....) 436 Can't write history
1050     return 1 if ($left =~ /\S+ requeued \S+ 436 Can\'t write history/o);
1051     # unexpected response code
1052     return 1 if ($left =~ /unexpected response code /o);
1053   }
1054
1055   ########
1056   ## nntplink
1057   if ($prog eq "nntplink") {
1058     $left =~ s/^(\S+):/$1/;
1059     # EOF
1060     if ($left =~ /(\S+) EOF /o) {
1061       my $server = $1;
1062       $server = lc $server unless $CASE_SENSITIVE;
1063       $nntplink_site{$server}++;
1064       $nntplink_eof{$server}++;
1065       return 1;
1066     }
1067     # Broken pipe
1068     if ($left =~ /(\S+) Broken pipe$/o) {
1069       my $server = $1;
1070       $server = lc $server unless $CASE_SENSITIVE;
1071       $nntplink_site{$server}++;
1072       $nntplink_bpipe{$server}++;
1073       return 1;
1074     }
1075     # already running - won't die
1076     return 1 if $left =~ /\S+ nntplink.* already running /o;
1077     # connection timed out
1078     if ($left =~ /(\S+) connection timed out/o) {
1079       my $server = $1;
1080       $server = lc $server unless $CASE_SENSITIVE;
1081       $nntplink_site{$server}++;
1082       $nntplink_bpipe{$server}++;
1083       return 1;
1084     }
1085     # greeted us with 400 No space
1086     if ($left =~ /(\S+) greeted us with 400 No space/o) {
1087       my $server = $1;
1088       $server = lc $server unless $CASE_SENSITIVE;
1089       $nntplink_site{$server}++;
1090       $nntplink_nospace{$server}++;
1091       return 1;
1092     }
1093     # greeted us with 400 loadav
1094     if ($left =~ /(\S+) greeted us with 400 loadav/o) {
1095       my $server = $1;
1096       $server = lc $server unless $CASE_SENSITIVE;
1097       $nntplink_site{$server}++;
1098       $nntplink_hiload{$server}++;
1099       return 1;
1100     }
1101     # greeted us with 400 (other)
1102     if ($left =~ /(\S+) greeted us with 400/o) {
1103       my $server = $1;
1104       $server = lc $server unless $CASE_SENSITIVE;
1105       $nntplink_site{$server}++;
1106       if ($left =~ /expir/i) {
1107         $nntplink_expire{$server}++;
1108       } else {
1109         $nntplink_fail{$server}++;
1110       }
1111       return 1;
1112     }
1113     # greeted us with 502
1114     if ($left =~ /(\S+) greeted us with 502/o) {
1115       my $server = $1;
1116       $server = lc $server unless $CASE_SENSITIVE;
1117       $nntplink_site{$server}++;
1118       $nntplink_auth{$server}++;
1119       return 1;
1120     }
1121     # sent authinfo
1122     if ($left =~ /(\S+) sent authinfo/o) {
1123       my $server = $1;
1124       $server = lc $server unless $CASE_SENSITIVE;
1125       $nntplink_site{$server}++;
1126       $nntplink_auth{$server}++;
1127       return 1;
1128     }
1129     # socket()
1130     if ($left =~ /(\S+) socket\(\): /o) {
1131       my $server = $1;
1132       $server = lc $server unless $CASE_SENSITIVE;
1133       $nntplink_site{$server}++;
1134       $nntplink_sockerr{$server}++;
1135       return 1;
1136     }
1137     # select()
1138     if ($left =~ /(\S+) select\(\) /o) {
1139       my $server = $1;
1140       $server = lc $server unless $CASE_SENSITIVE;
1141       $nntplink_site{$server}++;
1142       $nntplink_selecterr{$server}++;
1143       return 1;
1144     }
1145     # sent IHAVE
1146     if ($left =~ /(\S+) sent IHAVE/o) {
1147       my $server = $1;
1148       $server = lc $server unless $CASE_SENSITIVE;
1149       $nntplink_ihfail{$server}++;
1150       if (($left =~ / 436 /) && ($left =~ / out of space /)) {
1151         $nntplink_fake_connects{$server}++;
1152         $nntplink_nospace{$server}++;
1153       }
1154       return 1;
1155     }
1156     # article .... failed(saved): 436 No space
1157     if ($left =~ /(\S+) .* failed\(saved\): 436 No space$/o) {
1158       my $server = $1;
1159       $server = lc $server unless $CASE_SENSITIVE;
1160       $nntplink_nospace{$server}++;
1161       return 1;
1162     }
1163     # article .. 400 No space left on device writing article file -- throttling
1164     if ($left =~ /(\S+) .* 400 No space left on device writing article file -- throttling$/o) {
1165       my $server = $1;
1166       $server = lc $server unless $CASE_SENSITIVE;
1167       $nntplink_nospace{$server}++;
1168       return 1;
1169     }
1170     # stats
1171     if ($left =~ /(\S+) stats (\d+) offered (\d+) accepted (\d+) rejected (\d+) failed (\d+) connects$/o) {
1172       my ($server, $offered, $accepted, $rejected, $failed, $connects) =
1173         ($1, $2, $3, $4, $5, $6);
1174       $server = lc $server unless $CASE_SENSITIVE;
1175       $nntplink_offered{$server} += $offered - $nntplink_ihfail{$server}++;
1176       $nntplink_accepted{$server} += $accepted;
1177       $nntplink_rejected{$server} += $rejected;
1178       $nntplink_failed{$server} += $failed;
1179       $nntplink_connects{$server} += $connects;
1180       $nntplink_ihfail{$server} = 0;
1181       if ($nntplink_fake_connects{$server}) {
1182         $nntplink_site{$server} += $nntplink_fake_connects{$server};
1183         $nntplink_fake_connects{$server} = 0;
1184       } else {
1185         $nntplink_site{$server}++;
1186       }
1187       return 1;
1188     }
1189     # xmit
1190     if ($left =~ /(\S+) xmit user (\S+) system (\S+) elapsed (\S+)$/o) {
1191       my ($server, $user, $system, $elapsed) = ($1, $2, $3, $4);
1192       $server = lc $server unless $CASE_SENSITIVE;
1193       $nntplink_times{$server} += $elapsed;
1194       return 1;
1195     }
1196     # xfer
1197     return 1 if $left =~ /\S+ xfer/o;
1198     # Links down .. x hours
1199     if ($left =~ /(\S+) Links* down \S+ \d+/o) {
1200       # Collected but not used
1201       # my $server = $1;
1202       # $server = lc $server unless $CASE_SENSITIVE;
1203       # $nntplink_down{$server} += $hours;
1204       return 1;
1205     }
1206     # 503 Timeout
1207     if ($left =~ /^(\S+) \S+ \S+ \S+ 503 Timeout/o) {
1208       # Collected but not used
1209       # my $server = $1;
1210       # $server = lc $server unless $CASE_SENSITIVE;
1211       # $nntplink_timeout{$server}++;
1212       return 1;
1213     }
1214     # read() error while reading reply
1215     if ($left =~ /^(\S+): read\(\) error while reading reply/o) {
1216       my $server = $1;
1217       $server = lc $server unless $CASE_SENSITIVE;
1218       $nntplink_failed{$server}++;
1219       return 1;
1220     }
1221     # Password file xxxx not found
1222     return 1 if $left =~ /^\S+ Password file \S+ not found/;
1223     # No such
1224     return 1 if $left =~ /^\S+ \S+ \S+ No such/;
1225     # already running
1226     return 1 if $left =~ /^\S+ \S+ already running/;
1227     # error reading version from datafile
1228     return 1 if $left =~ /error reading version from datafile/;
1229   }
1230   ########
1231   ## nnrpd
1232   if ($prog =~ /^nnrpd(?:-ssl)?$/)
1233   {
1234     # Fix a small bug of nnrpd (inn 1.4*)
1235     $left =~ s/^ /\? /o;
1236     # Another bug (in INN 1.5b1)
1237     return 1 if $left =~ /^\020\002m$/o; # ^P^Bm
1238     # bad_history at num for <ref>
1239     return 1 if $left =~ /bad_history at \d+ for /o;
1240     # timeout short
1241     return 1 if $left =~ /\S+ timeout short$/o;
1242     # < or > + (blablabla)
1243     return 1 if $left =~ /^\S+ [\<\>] /o;
1244     # cant opendir ... I/O error
1245     return 1 if $left =~ /\S+ cant opendir \S+ I\/O error$/o;
1246     # perl filtering enabled
1247     return 1 if $left =~ /perl filtering enabled$/o;
1248     # Python filtering enabled
1249     return 1 if $left =~ /Python filtering enabled$/o;
1250     return 1 if $left =~ /^python interpreter initialized OK$/o;
1251     return 1 if $left =~ /^python method \S+ not found$/o;
1252     return 1 if $left =~ /^python authenticate method succeeded, return code \d+, error string /o;
1253     return 1 if $left =~ /^python access method succeeded$/o;
1254     return 1 if $left =~ /^python dynamic method \(\w+ access\) succeeded, refusion string: /o;
1255     return 1 if $left =~ /^python: .+ module successfully hooked into nnrpd$/o;
1256     return 1 if $left =~ /^python: nnrpd .+ class instance created$/o;
1257     return 1 if $left =~ /^python: n_a authenticate\(\) invoked: hostname \S+, ipaddress \S+, interface \S+, user /o;
1258     return 1 if $left =~ /^python: n_a access\(\) invoked: hostname \S+, ipaddress \S+, interface \S+, user /o;
1259     return 1 if $left =~ /^python: n_a dynamic\(\) invoked against type \S+, hostname \S+, ipaddress \S+, interface \S+, user /o;
1260     return 1 if $left =~ /^python: authentication by username succeeded$/o;
1261     return 1 if $left =~ /^python: authentication by username failed$/o;
1262     return 1 if $left =~ /^python: authentication access by IP address succeeded$/o;
1263     return 1 if $left =~ /^python: authentication access by IP address failed$/o;
1264     return 1 if $left =~ /^python: dynamic access module successfully hooked into nnrpd$/o;
1265     return 1 if $left =~ /^python: dynamic authorization access for read access granted$/o;
1266     return 1 if $left =~ /^python: dynamic authorization access type is not known: /o;
1267     # connect
1268     if ($left =~ /(\S+) (\([0-9a-fA-F:.]*\) )?connect$/o) {
1269       my $cust = $1;
1270       $cust = lc $cust unless $CASE_SENSITIVE;
1271       my $dom = &host2dom($cust);
1272       $nnrpd_dom_connect{$dom}++;
1273       $nnrpd_connect{$cust}++;
1274       return 1;
1275     }
1276     # group
1277     if ($left =~ /(\S+) group (\S+) (\d+)$/o) {
1278       my ($cust, $group, $num) = ($1, $2, $3);
1279       if ($num) {
1280         $nnrpd_group{$group} += $num;
1281         my ($hierarchy) = $group =~ /^([^\.]+).*$/o;
1282         $nnrpd_hierarchy{$hierarchy} += $num;
1283       }
1284       return 1;
1285     }
1286     # post failed
1287     if ($left =~ /(\S+) post failed (.*)$/o) {
1288       my ($cust, $error) = ($1, $2);
1289       $nnrpd_post_error{$error}++;
1290       return 1;
1291     }
1292     # post ok
1293     return 1 if $left =~ /\S+ post ok/o;
1294     # posts
1295     if ($left =~ /(\S+) posts received (\d+) rejected (\d+)$/o) {
1296       my ($cust, $received, $rejected) = ($1, $2, $3);
1297       $cust = lc $cust unless $CASE_SENSITIVE;
1298       my $dom = &host2dom($cust);
1299       $nnrpd_dom_post_ok{$dom} += $received;
1300       $nnrpd_dom_post_rej{$dom} += $rejected;
1301       $nnrpd_post_ok{$cust} += $received;
1302       $nnrpd_post_rej{$cust} += $rejected;
1303       return 1;
1304     }
1305     # noperm post without permission
1306     if ($left =~ /(\S+) noperm post without permission/o) {
1307       my $cust = $1;
1308       $cust = lc $cust unless $CASE_SENSITIVE;
1309       my $dom = &host2dom($cust);
1310       $nnrpd_dom_post_rej{$dom} ++;
1311       $nnrpd_post_rej{$cust} ++;
1312       return 1;
1313     }
1314     # no_permission
1315     if ($left =~ /(\S+) no_(permission|access)$/o) {
1316       my $cust = $1;
1317       $cust = lc $cust unless $CASE_SENSITIVE;
1318       my $dom = &host2dom($cust);
1319       $nnrpd_no_permission{$cust}++;
1320       $nnrpd_dom_no_permission{$dom}++;
1321       return 1;
1322     }
1323     # bad_auth
1324     if ($left =~ /(\S+) bad_auth$/o) {
1325       my $cust = $1;
1326       $cust = lc $cust unless $CASE_SENSITIVE;
1327       my $dom = &host2dom($cust);
1328       $nnrpd_dom_no_permission{$dom}++;
1329       $nnrpd_no_permission{$cust}++;
1330       return 1;
1331     }
1332     # Authentication failure
1333     # User not known to the underlying authentication module
1334     return 1 if $left =~ / ckpasswd: pam_authenticate failed: /o;
1335     return 1 if $left =~ / ckpasswd: user .+ unknown$/o;
1336     # authinfo
1337     if ($left =~ /\S+ user (\S+)$/o) {
1338       my $user = $1;
1339       $nnrpd_auth{$user}++;
1340       return 1;
1341     }
1342     # unrecognized + command
1343     if ($left =~ /(\S+) unrecognized (.*)$/o) {
1344       my ($cust, $error) = ($1, $2);
1345       $cust = lc $cust unless $CASE_SENSITIVE;
1346       my $dom = &host2dom($cust);
1347       $error = "_null command_" if ($error !~ /\S/);
1348       $error =~ s/^(xmotd) .*$/$1/i if ($error =~ /^xmotd .*$/i);
1349       $nnrpd_dom_unrecognized{$dom}++;
1350       $nnrpd_unrecognized{$cust}++;
1351       $nnrpd_unrecogn_cmd{$error}++;
1352       return 1;
1353     }
1354     # exit
1355     if ($left =~ /(\S+) exit articles (\d+) groups (\d+)$/o) {
1356       my ($cust, $articles, $groups) = ($1, $2, $3);
1357       $cust = lc $cust unless $CASE_SENSITIVE;
1358       my $dom = &host2dom($cust) || '?';
1359       $nnrpd_connect{$cust}++, $nnrpd_dom_connect{$dom}++ if $cust eq '?';
1360       $nnrpd_groups{$cust} += $groups;
1361       $nnrpd_dom_groups{$dom} += $groups;
1362       $nnrpd_articles{$cust} += $articles;
1363       $nnrpd_dom_articles{$dom} += $articles;
1364       return 1;
1365     }
1366     # times
1367     if ($left =~ /(\S+) times user (\S+) system (\S+) idle (\S+) elapsed (\S+)$/o) {
1368       my ($cust, $user, $system, $idle, $elapsed) = ($1, $2, $3, $4, $5);
1369       $cust = lc $cust unless $CASE_SENSITIVE;
1370       my $dom = &host2dom($cust);
1371       $nnrpd_times{$cust} += $elapsed;
1372       $nnrpd_resource_user{$cust} += $user;
1373       $nnrpd_resource_system{$cust} += $system;
1374       $nnrpd_resource_idle{$cust} += $idle;
1375       $nnrpd_resource_elapsed{$cust} += $elapsed;
1376       $nnrpd_dom_times{$dom} += $elapsed;
1377       return 1;
1378     }
1379     # artstats
1380     if ($left =~ /(\S+) artstats get (\d+) time (\d+) size (\d+)$/o) {
1381       my ($cust, $articles, $time, $bytes) = ($1, $2, $3, $4);
1382       $cust = lc $cust unless $CASE_SENSITIVE;
1383       my $dom = &host2dom($cust);
1384       $nnrpd_bytes{$cust} += $bytes;
1385       $nnrpd_dom_bytes{$dom} += $bytes;
1386       return 1;
1387     }
1388     # timeout
1389     if ($left =~ /(\S+) timeout$/o) {
1390       my $cust = $1;
1391       $cust = lc $cust unless $CASE_SENSITIVE;
1392       my $dom = &host2dom($cust);
1393       $nnrpd_dom_timeout{$dom}++;
1394       $nnrpd_timeout{$cust}++;
1395       return 1;
1396     }
1397     # timeout in post
1398     if ($left =~ /(\S+) timeout in post$/o) {
1399       my $cust = $1;
1400       $cust = lc $cust unless $CASE_SENSITIVE;
1401       my $dom = &host2dom($cust);
1402       $nnrpd_dom_timeout{$dom}++;
1403       $nnrpd_timeout{$cust}++;
1404       return 1;
1405     }
1406     # can't read: Connection timed out
1407     if ($left =~ /(\S+) can\'t read: Connection timed out$/o) {
1408       my $cust = $1;
1409       $cust = lc $cust unless $CASE_SENSITIVE;
1410       my $dom = &host2dom($cust);
1411       $nnrpd_dom_timeout{$dom}++;
1412       $nnrpd_timeout{$cust}++;
1413       return 1;
1414     }
1415     # can't read: Operation timed out
1416     if ($left =~ /(\S+) can\'t read: Operation timed out$/o) {
1417       my $cust = $1;
1418       $cust = lc $cust unless $CASE_SENSITIVE;
1419       my $dom = &host2dom($cust);
1420       $nnrpd_dom_timeout{$dom}++;
1421       $nnrpd_timeout{$cust}++;
1422       return 1;
1423     }
1424     # can't read: Connection reset by peer
1425     if ($left =~ /(\S+) can\'t read: Connection reset by peer$/o) {
1426       my $cust = $1;
1427       $cust = lc $cust unless $CASE_SENSITIVE;
1428       my $dom = &host2dom($cust);
1429       $nnrpd_dom_reset_peer{$dom}++;
1430       $nnrpd_reset_peer{$cust}++;
1431       return 1;
1432     }
1433     # can't read: Network is unreachable
1434     return 1 if $left =~ /(\S+) can\'t read: Network is unreachable$/o;
1435     # gethostbyaddr: xxx.yyy.zzz != a.b.c.d
1436     if ($left =~ /^gethostbyaddr: (.*)$/o) {
1437       my $msg = $1;
1438       $nnrpd_gethostbyaddr{$msg}++;
1439       return 1;
1440     }
1441     # cant gethostbyaddr
1442     if ($left =~ /\? cant gethostbyaddr (\S+) .*$/o) {
1443       my $ip = $1;
1444       $nnrpd_gethostbyaddr{$ip}++;
1445       return 1;
1446     }
1447     # cant getpeername
1448     if ($left =~ /\? cant getpeername/o) {
1449       # $nnrpd_getpeername++;
1450       $nnrpd_gethostbyaddr{"? (can't getpeername)"}++;
1451       return 1;
1452     }
1453     # can't getsockname
1454     return 1 if $left =~ /^\S+ can\'t getsockname$/o;
1455     # reverse lookup failed
1456     return 1 if $left =~ /^\? reverse lookup for \S+ failed: .* -- using IP address for access$/o;
1457     # profile timer
1458     # ME time X nnnn X(X) [...]
1459     # The exact timers change from various versions of INN, so try to deal
1460     # with this in a general fashion.
1461     if ($left =~ m/^\S+\s+                         # ME
1462                    time\s(\d+)\s+                  # time
1463                    ((?:\S+\s\d+\(\d+\)\s*)+)       # timer values
1464                    $/ox) {
1465       $nnrpd_time_times += $1;
1466       my $timers = $2;
1467
1468       while ($timers =~ /(\S+) (\d+)\((\d+)\)\s*/g) {
1469         my $name = $nnrpd_timer_names{$1} || $1;
1470         my $average = $2 / ($3 || 1);
1471         $nnrpd_time_time{$name} += $2;
1472         $nnrpd_time_num{$name} += $3;
1473         if ($3) {
1474           my $min = $nnrpd_time_min{$name};
1475           $nnrpd_time_min{$name} = $average
1476             if (defined($min) && $min > $average);
1477           my $max = $nnrpd_time_max{$name};
1478           $nnrpd_time_max{$name} = $average
1479             if (defined($max) && $max < $average);
1480         }
1481       }
1482       return 1;
1483     }
1484     # ME dropping articles into ...
1485     return 1 if $left =~ /ME dropping articles into /o;
1486     # newnews (interesting but ignored till now)
1487     return 1 if $left =~ /^\S+ newnews /o;
1488     # cant fopen (ignored too)
1489     return 1 if $left =~ /^\S+ cant fopen /o;
1490     # can't read: No route to host
1491     return 1 if $left =~ /can\'t read: No route to host/o;
1492     # can't read: Broken pipe
1493     return 1 if $left =~ /can\'t read: Broken pipe/o;
1494     # eof in post
1495     return 1 if $left =~ /^\S+ eof in post$/o;
1496     # ioctl: ...
1497     return 1 if $left =~ /^ioctl: /o;
1498     # other stats
1499     return 1 if $left =~ /^\S+ overstats count \d+ hit \d+ miss \d+ time \d+ size \d+ dbz \d+ seek \d+ get \d+ artcheck \d+$/o;
1500     # starttls
1501     return 1 if $left =~ /^starttls: \S+ with cipher \S+ \(\d+\/\d+ bits\) no authentication$/o;
1502   }
1503   ########
1504   ## inndstart
1505   if ($prog eq "inndstart") {
1506     # cant bind Address already in use
1507     # cant bind Permission denied
1508     return 1 if $left =~ /cant bind /o;
1509     # cant setgroups Operation not permitted
1510     return 1 if $left =~ /cant setgroups /o;
1511   }
1512   ########
1513   ## overchan
1514   if ($prog eq "overchan") {
1515     # times
1516     if ($left =~ /timings (\d+) arts (\d+) of (\d+) ms$/o) {
1517       my ($articles, $work_time, $run_time) = ($1, $2, $3);
1518       # ??? What to do with numbers
1519       return 1;
1520     }
1521   }
1522   ########
1523   ## batcher
1524   if ($prog eq "batcher") {
1525     # times
1526     if ($left =~ /(\S+) times user (\S+) system (\S+) elapsed (\S+)$/o) {
1527       my ($server, $user, $system, $elapsed) = ($1, $2, $3, $4);
1528       $server = lc $server unless $CASE_SENSITIVE;
1529       # $batcher_user{$server} += $user;
1530       # $batcher_system{$server} += $system;
1531       $batcher_elapsed{$server} += $elapsed;
1532       return 1;
1533     }
1534     # stats
1535     if ($left =~ /(\S+) stats batches (\d+) articles (\d+) bytes (\d+)$/o) {
1536       my ($server, $batches, $articles, $bytes) = ($1, $2, $3, $4);
1537       $server = lc $server unless $CASE_SENSITIVE;
1538       $batcher_offered{$server} += $batches;
1539       $batcher_articles{$server} += $articles;
1540       $batcher_bytes{$server} += $bytes;
1541       return 1;
1542     }
1543   }
1544   ########
1545   ## rnews
1546   if ($prog eq "rnews") {
1547     # rejected connection
1548     if ($left =~ /rejected connection (.*)$/o) {
1549       $rnews_rejected{$1}++;
1550       return 1;
1551     }
1552     # cant open_remote
1553     if ($left =~ /(cant open_remote .*)$/o) {
1554       $rnews_rejected{$1}++;
1555       return 1;
1556     }
1557     # rejected 437 Unwanted newsgroup
1558     if ($left =~ /rejected 437 Unwanted newsgroup \"(.*)\"$/o) {
1559       $rnews_bogus_ng{$1}++;
1560       return 1;
1561     }
1562     # rejected 437 Unapproved for "xx"
1563     if ($left =~ /rejected 437 Unapproved for \"(.*)\"$/o) {
1564       $rnews_unapproved{$1}++;
1565       return 1;
1566     }
1567     # rejected 437 Unwanted distribution
1568     if ($left =~ /rejected 437 Unwanted distribution (.*)$/o) {
1569       $rnews_bogus_dist{$1}++;
1570       return 1;
1571     }
1572     # rejected 437 Bad "Date"
1573     if ($left =~ /rejected 437 Bad \"Date\" (.*)$/o) {
1574       $rnews_bogus_date{$1}++;
1575       return 1;
1576     }
1577     # rejected 437 Article posted in the future
1578     if ($left =~ /rejected 437 Article posted in the future -- \"(.*)\"$/o) {
1579       $rnews_bogus_date{"(future) $1"}++;
1580       return 1;
1581     }
1582     # rejected 437 Too old -- "..."
1583     if ($left =~ /rejected 437 Too old -- (.*)$/o) {
1584       $rnews_too_old++;
1585       return 1;
1586     }
1587     # rejected 437 Linecount...
1588     if ($left =~ /rejected 437 (Linecount) \d+ \!= \d+/o) {
1589       $rnews_linecount++;
1590       return 1;
1591     }
1592     # rejected 437 Duplicate
1593     if ($left =~ /rejected 437 Duplicate$/o) {
1594       $rnews_duplicate++;
1595       return 1;
1596     }
1597     # rejected 437 Duplicate article
1598     if ($left =~ /rejected 437 (Duplicate article)/o) {
1599       $rnews_duplicate++;
1600       return 1;
1601     }
1602     # rejected 437 No colon-space ...
1603     if ($left =~ /rejected 437 No colon-space in \"(.*)\" header$/o) {
1604       $rnews_no_colon_space++;
1605       return 1;
1606     }
1607     # duplicate <msg-id> path..
1608     if ($left =~ /^duplicate /o) {
1609       $rnews_duplicate++;
1610       return 1;
1611     }
1612     # offered <msg-id> feed
1613     if ($left =~ /^offered \S+ (\S+)/o) {
1614       my $host = $1;
1615       $host = lc $host unless $CASE_SENSITIVE;
1616       # Small hack used to join article spooled when innd is throttle.
1617       # In this situation, the hostname is a 8 hex digits string
1618       # To avoid confusions with real feeds, the first character is forced
1619       # to be a '3' or a '4' (will work between 9/7/1995 and 13/7/2012).
1620       $host = "Local postings" if $host =~ /^[34][0-9a-f]{7}$/;
1621       $rnews_host{$host}++;
1622       return 1;
1623     }
1624     # rejected 437 ECP rejected
1625     return 1 if $left =~ m/rejected 437 ECP rejected/o;
1626     # rejected 437 "Subject" header too long
1627     return 1 if $left =~ m/header too long/o;
1628     # rejected 437 Too long line in header 1163 bytes
1629     return 1 if $left =~ m/rejected 437 Too long line in header/o;
1630     # rejected 437 Too many newsgroups (meow)
1631     return 1 if $left =~ m/rejected 437 Too many newsgroups/o;
1632     # rejected 437 Space before colon in "<a" header
1633     return 1 if $left =~ m/rejected 437 Space before colon in/o;
1634     # rejected 437 EMP (phl)
1635     return 1 if $left =~ m/rejected 437 EMP/o;
1636     # rejected 437 Scoring filter (8)
1637     return 1 if $left =~ m/rejected 437 Scoring filter/o;
1638     # bad_article missing Message-ID
1639     return 1 if $left =~ m/bad_article missing Message-ID/o;
1640     # cant unspool saving to xxx
1641     return 1 if $left =~ m/cant unspool saving to/o;
1642   }
1643
1644   ###########
1645   ## ncmspool
1646   if ($prog eq "ncmspool") {
1647     # <article> good signature from foo@bar.com
1648     if ($left =~ /good signature from (.*)/o) {
1649       $nocem_goodsigs{$1}++;
1650       $nocem_totalgood++;
1651       $nocem_lastid = $1;
1652       return 1;
1653     }
1654     # <article> bad signature from foo@bar.com
1655     if ($left =~ /bad signature from (.*)/o) {
1656       $nocem_badsigs{$1}++;
1657       $nocem_goodsigs{$1} = 0 unless ($nocem_goodsigs{$1});
1658       $nocem_totalbad++;
1659       $nocem_lastid = $1;
1660       return 1;
1661     }
1662     # <article> contained 123 new 456 total ids
1663     if ($left =~ /contained (\d+) new (\d+) total ids/o) {
1664       $nocem_newids += $1;
1665       $nocem_newids{$nocem_lastid} += $1;
1666       $nocem_totalids += $2;
1667       $nocem_totalids{$nocem_lastid} += $2;
1668       return 1;
1669     }
1670     return 1;
1671   }
1672
1673   ########
1674   ## nocem
1675   if ($prog eq "nocem") {
1676     if ($left =~ /processed notice .* by (.*) \((\d+) ids,/o) {
1677       $nocem_goodsigs{$1}++;
1678       $nocem_totalgood++;
1679       $nocem_lastid = $1;
1680       $nocem_newids += $2;
1681       $nocem_newids{$nocem_lastid} += $2;
1682       $nocem_totalids += $2;
1683       $nocem_totalids{$nocem_lastid} += $2;
1684       return 1;
1685     }
1686     if ($left =~ /Article <[^>]*>: (.*) \(ID [[:xdigit:]]*\) not in keyring/o) {
1687        $nocem_badsigs{$1}++;
1688        $nocem_goodsigs{$1} = 0 unless ($nocem_goodsigs{$1});
1689        $nocem_totalbad++;
1690        $nocem_lastid = $1;
1691        return 1;
1692      }
1693     if ($left =~ /Article <[^>]*>: bad signature from (.*)/o) {
1694       $nocem_badsigs{$1}++;
1695       $nocem_goodsigs{$1} = 0 unless ($nocem_goodsigs{$1});
1696       $nocem_totalbad++;
1697       $nocem_lastid = $1;
1698       return 1;
1699     }
1700     if ($left =~ /Article <[^>]*>: malformed signature/o) {
1701       $nocem_badsigs{'N/A'}++;
1702       $nocem_goodsigs{'N/A'} = 0 unless ($nocem_goodsigs{'N/A'});
1703       $nocem_totalbad++;
1704       $nocem_lastid = 'N/A';
1705       return 1;
1706     }
1707
1708     return 1;
1709   }
1710
1711   ###########
1712   ## controlchan
1713   if ($prog eq "controlchan") {
1714     # loaded /x/y/z/foo.pl
1715     return 1 if $left =~ m/^loaded /;
1716     # starting
1717     return 1 if $left =~ m/^starting/;
1718     # skipping rmgroup x@y (pgpverify failed) in <foo@bar>
1719     if ($left =~ m/^skipping \S+ (\S+) \(pgpverify failed\) in /) {
1720       $controlchan_skippgp{$1}++;
1721       $controlchan_who{$1}++;
1722       return 1;
1723     }
1724     if ($left =~ m/^control_(sendme|ihave), [^,]+, (\S+), doit,/o) {
1725       if ($1 eq "sendme") {
1726         $controlchan_sendme_site{$2}++;
1727       } else {
1728         $controlchan_ihave_site{$2}++;
1729       }
1730       return 1;
1731     }
1732     # control_XXgroup, foo.bar [moderated] who who /x/y/12, peer, action, 1
1733     #
1734     # Various other random junk can end up in the moderated field, like y,
1735     # unmoderated, m, etc. depending on what the control message says.  It
1736     # can even have multiple words, which we still don't handle.
1737     if ($left =~ m/^control_(\S+),    # type of msg
1738                   \s(?:\S+)?          # newsgroup name
1739                   (\s\S+)?            # optional
1740                   \s(\S+)             # e-mail
1741                   \s\S+               # e-mail
1742                   \s\S+,              # filename
1743                   \s\S+,              # server
1744                   \s([^=,]+(?:=\S+)?),            # action
1745                   \s*(.*)             # code
1746                   /x) {
1747       if ($1 eq 'newgroup') {
1748         $controlchan_new{$3}++;
1749       } elsif ($1 eq 'rmgroup') {
1750         $controlchan_rm{$3}++;
1751       } else {
1752         $controlchan_other{$3}++;
1753       }
1754       $controlchan_who{$3}++;
1755       $controlchan_ok{$3} += $5;
1756       my $action = $4;
1757       my $email = $3;
1758       $action =~ s/=.*//;
1759       $controlchan_doit{$email}++ if $action eq 'doit';
1760       return 1;
1761     }
1762     # checkgroups processed (no change or not)
1763     return 1 if $left =~ /^checkgroups by \S+ processed/o;
1764   }
1765
1766   ###########
1767   ## crosspost
1768   if ($prog eq "crosspost") {
1769     # seconds 1001 links 3182 0 symlinks 0 0 mkdirs 0 0
1770     # missing 13 toolong 0 other 0
1771     if ($left =~ /^seconds\ (\d+)
1772                    \ links\ (\d+)\ (\d+)
1773                    \ symlinks\ (\d+)\ (\d+)
1774                    \ mkdirs\ (\d+)\ (\d+)
1775                    \ missing\ (\d+)
1776                    \ toolong\ (\d+)
1777                    \ other\ (\d+)
1778                  $/ox) {
1779       $crosspost_time += $1;
1780       $crosspost{'Links made'} += $2;
1781       $crosspost{'Links failed'} += $3;
1782       $crosspost{'Symlinks made'} += $4;
1783       $crosspost{'Symlinks failed'} += $5;
1784       $crosspost{'Mkdirs made'} += $6;
1785       $crosspost{'Mkdirs failed'} += $7;
1786       $crosspost{'Files missing'} += $8;
1787       $crosspost{'Paths too long'} += $9;
1788       $crosspost{'Others'} += $10;
1789       return 1;
1790     }
1791   }
1792
1793   ###########
1794   ## cnfsstat
1795   if ($prog eq "cnfsstat") {
1796     # Class ALT for groups matching "alt.*" article size min/max: 0/1048576
1797     # Buffer T3, len: 1953  Mbytes, used: 483.75 Mbytes (24.8%)   0 cycles
1798     if ($left =~ m|^Class\ (\S+)\ for\ groups\ matching\ \S+
1799                     (\ article\ size\ min/max:\ \d+/\d+)?
1800                     \ Buffer\ (\S+),
1801                     \ len:\ ([\d.]+)\s+Mbytes,
1802                     \ used:\ ([\d.]+)\ Mbytes\ \(\s*[\d.]+%\)
1803                     \s+(\d+)\ cycles\s*
1804                  $|ox) {
1805       my ($class, $buffer, $size, $used, $cycles) = ($1, $3, $4, $5, $6);
1806       my ($h, $m, $s) = $hour =~ m/^(\d+):(\d+):(\d+)$/;
1807       my $time = $h * 3600 + $m * 60 + $s;
1808       $size *= 1024 * 1024;
1809       $used *= 1024 * 1024;
1810       $cnfsstat{$buffer} = $class;
1811
1812       # If the size changed, invalidate all of our running fill rate stats.
1813       if (!exists($cnfsstat_size{$buffer}) ||  $size != $cnfsstat_size{$buffer}) {
1814         delete $cnfsstat_rate{$buffer};
1815         delete $cnfsstat_samples{$buffer};
1816         delete $cnfsstat_time{$buffer};
1817         $cnfsstat_size{$buffer} = $size;
1818       }
1819       elsif ($cnfsstat_time{$buffer}) {
1820         # We want to gather the rate at which cycbuffs fill.  Store a
1821         # running total of bytes/second and a total number of samples.
1822         # Ideally we'd want a weighted average of those samples by the
1823         # length of the sample period, but we'll ignore that and assume
1824         # cnfsstat runs at a roughly consistent interval.
1825         my ($period, $added);
1826         $period = $time - $cnfsstat_time{$buffer};
1827         $period = 86400 - $cnfsstat_time{$buffer} + $time if $period <= 0;
1828         $added = $used - $cnfsstat_used{$buffer};
1829         if ($cycles > $cnfsstat_cycles{$buffer}) {
1830           $added += $size * ($cycles - $cnfsstat_cycles{$buffer});
1831         }
1832         if ($added > 0) {
1833           $cnfsstat_rate{$buffer} += $added / $period;
1834           $cnfsstat_samples{$buffer}++;
1835         }
1836       }
1837       $cnfsstat_used{$buffer} = $used;
1838       $cnfsstat_cycles{$buffer} = $cycles;
1839       $cnfsstat_time{$buffer} = $time;
1840       return 1;
1841     }
1842   }
1843
1844   # Ignore following programs :
1845   return 1 if ($prog eq "uxfxn");
1846   return 1 if ($prog eq "beverage");
1847   return 1 if ($prog eq "newsx");
1848   return 1 if ($prog eq "demmf");
1849   return 1 if ($prog eq "nnnn");
1850   return 1 if ($prog eq "slurp");
1851   return 0;
1852 }
1853
1854 #################################
1855 # Adjust some values..
1856
1857 sub adjust {
1858   my ($first_date, $last_date) = @_;
1859
1860   my $nnrpd_doit = 0;
1861   my $curious;
1862
1863   {
1864     my $serv;
1865     if (%nnrpd_connect) {
1866       my $c = keys (%nnrpd_connect);
1867       foreach $serv (keys (%nnrpd_connect)) {
1868         my $dom = &host2dom($serv);
1869         if ($nnrpd_no_permission{$serv}) {
1870           $nnrpd_dom_connect{$dom} -= $nnrpd_connect{$serv}
1871             if defined $nnrpd_dom_connect{$dom} && defined $nnrpd_connect{$serv};
1872           $nnrpd_dom_groups{$dom}  -= $nnrpd_groups{$serv}
1873             if defined $nnrpd_dom_groups{$dom} && defined $nnrpd_groups{$serv};
1874           $nnrpd_dom_times{$dom}   -= $nnrpd_times{$serv}
1875             if defined $nnrpd_dom_times{$dom};
1876           $nnrpd_connect{$serv} -= $nnrpd_no_permission{$serv};
1877           $nnrpd_groups{$serv} -= $nnrpd_no_permission{$serv}
1878             if defined $nnrpd_groups{$serv};
1879           delete $nnrpd_connect{$serv} unless $nnrpd_connect{$serv};
1880           delete $nnrpd_groups{$serv}  unless $nnrpd_groups{$serv};
1881           delete $nnrpd_times{$serv}   unless $nnrpd_times{$serv};
1882           delete $nnrpd_usr_times{$serv}   unless $nnrpd_usr_times{$serv};
1883           delete $nnrpd_sys_times{$serv}   unless $nnrpd_sys_times{$serv};
1884           delete $nnrpd_dom_connect{$dom} unless $nnrpd_dom_connect{$dom};
1885           delete $nnrpd_dom_groups{$dom}  unless $nnrpd_dom_groups{$dom};
1886           delete $nnrpd_dom_times{$dom}   unless $nnrpd_dom_times{$dom};
1887           $c--;
1888         }
1889         $nnrpd_doit++
1890           if $nnrpd_groups{$serv} || $nnrpd_post_ok{$serv};
1891       }
1892       undef %nnrpd_connect unless $c;
1893     }
1894     foreach $serv (keys (%nnrpd_groups)) {
1895       $curious = "ok" unless $nnrpd_groups{$serv} || $nnrpd_post_ok{$serv} ||
1896         $nnrpd_articles{$serv};
1897     }
1898   }
1899
1900   # Fill some hashes
1901   {
1902     my $key;
1903     foreach $key (keys (%innd_connect)) {
1904       $innd_offered{$key} = ($innd_accepted{$key} || 0)
1905         + ($innd_refused{$key} || 0)
1906         + ($innd_rejected{$key} || 0);
1907       $innd_offered_size{$key} = ($innd_stored_size{$key} || 0)
1908         + ($innd_duplicated_size{$key} || 0);
1909     }
1910
1911
1912     # adjust min/max of innd timer stats.
1913     if (%innd_time_min) {
1914       foreach $key (keys (%innd_time_min)) {
1915         $innd_time_min{$key} = 0 if ($innd_time_min{$key} == $MIN);
1916         $innd_time_max{$key} = 0 if ($innd_time_max{$key} == $MAX);
1917
1918         #$innd_time_min{$key} /= 1000;
1919         #$innd_time_max{$key} /= 1000;
1920       }
1921     }
1922     if (%innfeed_time_min) {
1923       foreach $key (keys (%innfeed_time_min)) {
1924         $innfeed_time_min{$key} = 0 if ($innfeed_time_min{$key} == $MIN);
1925         $innfeed_time_max{$key} = 0 if ($innfeed_time_max{$key} == $MAX);
1926       }
1927     }
1928     if (%nnrpd_time_min) {
1929       foreach $key (keys (%nnrpd_time_min)) {
1930         $nnrpd_time_min{$key} = 0 if ($nnrpd_time_min{$key} == $MIN);
1931         $nnrpd_time_max{$key} = 0 if ($nnrpd_time_max{$key} == $MAX);
1932       }
1933     }
1934     # remove the innd timer stats if not used.
1935     unless ($innd_time_times) {
1936       undef %innd_time_min;
1937       undef %innd_time_max;
1938       undef %innd_time_num;
1939       undef %innd_time_time;
1940     }
1941     # same thing for innfeed timer
1942     unless ($innfeed_time_times) {
1943       undef %innfeed_time_min;
1944       undef %innfeed_time_max;
1945       undef %innfeed_time_num;
1946       undef %innfeed_time_time;
1947     }
1948     # same thing for nnrpd timer
1949     unless ($nnrpd_time_times) {
1950       undef %nnrpd_time_min;
1951       undef %nnrpd_time_max;
1952       undef %nnrpd_time_num;
1953       undef %nnrpd_time_time;
1954     }
1955
1956     # adjust the crosspost stats.
1957     if (%crosspost) {
1958       foreach $key (keys (%crosspost)) {
1959         $crosspost_times{$key} = $crosspost_time ?
1960           sprintf "%.2f", $crosspost{$key} / $crosspost_time * 60 : "?";
1961       }
1962     }
1963   }
1964
1965   if (%inn_flow) {
1966     my ($prev_dd, $prev_d, $prev_h) = ("", -1, -1);
1967     my $day;
1968     foreach $day (sort datecmp keys (%inn_flow)) {
1969       my ($r, $h) = $day =~ /^(.*) (\d+)$/;
1970       my $d = index ("JanFebMarAprMayJunJulAugSepOctNovDec",
1971                      substr ($r,0,3)) / 3 * 31 + substr ($r, 4, 2);
1972       $prev_h = $h if ($prev_h == -1);
1973       if ($prev_d == -1) {
1974         $prev_d = $d;
1975         $prev_dd = $r;
1976       }
1977       if ($r eq $prev_dd) { # Same day and same month ?
1978         if ($h != $prev_h) {
1979           if ($h == $prev_h + 1) {
1980             $prev_h++;
1981           }
1982           else {
1983             my $j;
1984             for ($j = $prev_h + 1; $j < $h; $j++) {
1985               my $t = sprintf "%02d", $j;
1986               $inn_flow{"$r $t"} = 0;
1987             }
1988             $prev_h = $h;
1989           }
1990         }
1991       }
1992       else {
1993         my $j;
1994         # then end of the first day...
1995         for ($j = ($prev_h == 23) ? 24 : $prev_h + 1; $j < 24; $j++) {
1996           my $t = sprintf "%02d", $j;
1997           $inn_flow{"$prev_dd $t"} = 0;
1998         }
1999
2000         # all the days between (if any)
2001         # well, we can forget them as it is supposed to be a tool
2002         # launched daily.
2003
2004         # the beginning of the last day..
2005         for ($j = 0; $j < $h; $j++) {
2006           my $t = sprintf "%02d", $j;
2007           $inn_flow{"$r $t"} = 0;
2008         }
2009         $prev_dd = $r;
2010         $prev_d = $d;
2011         $prev_h = $h;
2012       }
2013     }
2014     my $first = 1;
2015     my (%hash, %hash_time, %hash_size, $date, $delay);
2016     foreach $day (sort datecmp keys (%inn_flow)) {
2017       my ($r, $h) = $day =~ /^(.*) (\d+)$/o;
2018       if ($first) {
2019         $first = 0;
2020         my ($t) = $first_date =~ m/:(\d\d:\d\d)$/o;
2021         $date = "$day:$t - $h:59:59";
2022         $t =~ m/(\d\d):(\d\d)/o;
2023         $delay = 3600 - $1 * 60 - $2;
2024       }
2025       else {
2026         $date = "$day:00:00 - $h:59:59";
2027         $delay = 3600;
2028       }
2029       $hash{$date} = $inn_flow{$day};
2030       $hash_size{$date} = $inn_flow_size{$day};
2031       $inn_flow_labels{$date} = $h;
2032       $hash_time{$date} = $delay;
2033     }
2034     my ($h, $t) = $last_date =~ m/ (\d+):(\d\d:\d\d)$/o;
2035     my ($h2) = $date =~ m/ (\d+):\d\d:\d\d /o;
2036     my $date2 = $date;
2037     $date2 =~ s/$h2:59:59$/$h:$t/;
2038     $hash{$date2} = $hash{$date};
2039     delete $hash{"$date"};
2040     $hash_size{$date2} = $hash_size{$date};
2041     delete $hash_size{"$date"};
2042     $t =~ m/(\d\d):(\d\d)/o;
2043     $hash_time{$date2} = $hash_time{$date} - ($h2 == $h) * 3600 + $1 * 60 + $2;
2044     delete $hash_time{"$date"};
2045     $inn_flow_labels{$date2} = $h;
2046     %inn_flow = %hash;
2047     %inn_flow_time = %hash_time;
2048     %inn_flow_size = %hash_size;
2049   }
2050
2051   if (%innd_bad_ihave) {
2052     my $key;
2053     my $msg = 'Bad ihave control messages received';
2054     foreach $key (keys %innd_bad_ihave) {
2055       $innd_misc_stat{$msg}{$key} = $innd_bad_ihave{$key};
2056     }
2057   }
2058   if (%innd_bad_msgid) {
2059     my $key;
2060     my $msg = 'Bad Message-ID\'s offered';
2061     foreach $key (keys %innd_bad_msgid) {
2062       $innd_misc_stat{$msg}{$key} = $innd_bad_msgid{$key};
2063     }
2064   }
2065   if (%innd_bad_sendme) {
2066     my $key;
2067     my $msg = 'Ignored sendme control messages received';
2068     foreach $key (keys %innd_bad_sendme) {
2069       $innd_misc_stat{$msg}{$key} = $innd_bad_sendme{$key};
2070     }
2071   }
2072   if (%innd_bad_command) {
2073     my $key;
2074     my $msg = 'Bad command received';
2075     foreach $key (keys %innd_bad_command) {
2076       $innd_misc_stat{$msg}{$key} = $innd_bad_command{$key};
2077     }
2078   }
2079   if (%innd_bad_newsgroup) {
2080     my $key;
2081     my $msg = 'Bad newsgroups received';
2082     foreach $key (keys %innd_bad_newsgroup) {
2083       $innd_misc_stat{$msg}{$key} = $innd_bad_newsgroup{$key};
2084     }
2085   }
2086   if (%innd_posted_future) {
2087     my $key;
2088     my $msg = 'Article posted in the future';
2089     foreach $key (keys %innd_posted_future) {
2090       $innd_misc_stat{$msg}{$key} = $innd_posted_future{$key};
2091     }
2092   }
2093   if (%innd_no_colon_space) {
2094     my $key;
2095     my $msg = 'No colon-space in header';
2096     foreach $key (keys %innd_no_colon_space) {
2097       $innd_misc_stat{$msg}{$key} = $innd_no_colon_space{$key};
2098     }
2099   }
2100   if (%innd_huge) {
2101     my $key;
2102     my $msg = 'Huge articles';
2103     foreach $key (keys %innd_huge) {
2104       $innd_misc_stat{$msg}{$key} = $innd_huge{$key};
2105     }
2106   }
2107   if (%innd_blocked) {
2108     my $key;
2109     my $msg = 'Blocked server feeds';
2110     foreach $key (keys %innd_blocked) {
2111       $innd_misc_stat{$msg}{$key} = $innd_blocked{$key};
2112     }
2113   }
2114   if (%innd_strange_strings) {
2115     my $key;
2116     my $msg = 'Including strange strings';
2117     foreach $key (keys %innd_strange_strings) {
2118       $innd_misc_stat{$msg}{$key} = $innd_strange_strings{$key};
2119     }
2120   }
2121   if (%rnews_bogus_ng) {
2122     my $key;
2123     my $msg = 'Unwanted newsgroups';
2124     foreach $key (keys %rnews_bogus_ng) {
2125       $rnews_misc{$msg}{$key} = $rnews_bogus_ng{$key};
2126     }
2127   }
2128   if (%rnews_bogus_dist) {
2129     my $key;
2130     my $msg = 'Unwanted distributions';
2131     foreach $key (keys %rnews_bogus_dist) {
2132       $rnews_misc{$msg}{$key} = $rnews_bogus_dist{$key};
2133     }
2134   }
2135   if (%rnews_unapproved) {
2136     my $key;
2137     my $msg = 'Articles unapproved';
2138     foreach $key (keys %rnews_unapproved) {
2139       $rnews_misc{$msg}{$key} = $rnews_unapproved{$key};
2140     }
2141   }
2142   if (%rnews_bogus_date) {
2143     my $key;
2144     my $msg = 'Bad Date';
2145     foreach $key (keys %rnews_bogus_date) {
2146       $rnews_misc{$msg}{$key} = $rnews_bogus_date{$key};
2147     }
2148   }
2149
2150   $rnews_misc{'Too old'}{'--'} = $rnews_too_old if $rnews_too_old;
2151   $rnews_misc{'Bad linecount'}{'--'} = $rnews_linecount if $rnews_linecount;
2152   $rnews_misc{'Duplicate articles'}{'--'} = $rnews_duplicate
2153     if $rnews_duplicate;
2154   $rnews_misc{'No colon-space'}{'--'} = $rnews_no_colon_space
2155     if $rnews_no_colon_space;
2156
2157   if (%nnrpd_groups) {
2158     my $key;
2159     foreach $key (keys (%nnrpd_connect)) {
2160       unless ($nnrpd_groups{"$key"} || $nnrpd_post_ok{"$key"} ||
2161               $nnrpd_articles{"$key"}) {
2162         $nnrpd_curious{$key} = $nnrpd_connect{$key};
2163         undef $nnrpd_connect{$key};
2164       }
2165     }
2166   }
2167 }
2168
2169 sub report_unwanted_ng {
2170   my $file = shift;
2171   open (FILE, "$file") && do {
2172     while (<FILE>) {
2173       my ($c, $n) = $_ =~ m/^\s*(\d+)\s+(.*)$/;
2174       next unless defined $n;
2175       $n =~ s/^newsgroup //o; # for pre 1.8 logs
2176       $inn_uw_ng{$n} += $c;
2177     }
2178     close (FILE);
2179   };
2180
2181   unlink ("${file}.old");
2182   rename ($file, "${file}.old");
2183
2184   open (FILE, "> $file") && do {
2185     my $g;
2186     foreach $g (sort {$inn_uw_ng{$b} <=> $inn_uw_ng{$a}} (keys (%inn_uw_ng))) {
2187       printf FILE "%d %s\n", $inn_uw_ng{$g}, $g;
2188     }
2189     close (FILE);
2190     chmod(0660, "$file");
2191   };
2192   unlink ("${file}.old");
2193 }
2194
2195 ###########################################################################
2196
2197 # Compare 2 dates (+hour)
2198 sub datecmp {
2199   # ex: "May 12 06"   for May 12, 6:00am
2200   local($[) = 0;
2201   # The 2 dates are near. The range is less than a few days that's why we
2202   # can cheat to determine the order. It is only important if one date
2203   # is in January and the other in December.
2204
2205   my($date1) = substr($a, 4, 2) * 24;
2206   my($date2) = substr($b, 4, 2) * 24;
2207   $date1 += index("JanFebMarAprMayJunJulAugSepOctNovDec",substr($a,0,3)) * 288;
2208   $date2 += index("JanFebMarAprMayJunJulAugSepOctNovDec",substr($b,0,3)) * 288;
2209   if ($date1 - $date2 > 300 * 24) {
2210     $date2 += 288 * 3 * 12;
2211   }
2212   elsif ($date2 - $date1 > 300 * 24) {
2213     $date1 += 288 * 3 * 12;
2214   }
2215   $date1 += substr($a, 7, 2);
2216   $date2 += substr($b, 7, 2);
2217   $date1 - $date2;
2218 }
2219
2220 sub host2dom {
2221   my $host = shift;
2222
2223   $host =~ m/^[^\.]+(.*)/;
2224   $host =~ m/^[\d\.]+$/ ? "unresolved" : $1 ? "*$1" : "?";
2225 }
2226
2227 1;