-
Notifications
You must be signed in to change notification settings - Fork 54
/
check_files.pl
executable file
·901 lines (833 loc) · 38.9 KB
/
check_files.pl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
#!/usr/bin/perl
#
# ============================== SUMMARY =====================================
#
# Program : check_files.pl
# Version : 0.421
# Date : Aug 20, 2013
# Author : William Leibzon - william@leibzon.org
# Summary : This is a nagios plugin that counts files in a directory and
# checks their age an size. Various thresholds on this can be set.
# Licence : GPL - summary below, full text at http://www.fsf.org/licenses/gpl.txt
#
# =========================== PROGRAM LICENSE ================================
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GnU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
# ===================== INFORMATION ABOUT THIS PLUGIN ========================
#
# This is a nagios plugin that checks number of files of specified type
# in a directory and can give an error if there are too few or too few.
# It can also check files age, file size and set thresholds based on these
# parameters such as report an error if files are too old.
#
# This program is written and maintained by:
# William Leibzon - william(at)leibzon.org
#
# =============================== SETUP NOTES ================================
#
# List of files to check are specified with -F option. These should be
# specified in a way you'd specify files for ls, so for example to check
# all perl files you use "*.pl" which is specified as:
# $ ./check_files.pl -L Files -F '*.pl' -w 4 -c 7
# (above will give a warning if there are > 4 *.pl files and critical alert if > 7)
#
# You can specify more than one file type to check, for example:
# $ ./check_files.pl -L Files -F '*.pl,*.sh' -w 4,3 -c 7,5
# (above will give a warning if there are more than 4 .pl or more than 3 *.sh files
# and CRITICAL alert if there are more than 7 .pl or more than 5 *.sh files)
#
# And these are examples provided by Patrick Bailat for options he added:
# Compute the sum size of files:
# $ ./check_files.pl -D /opt/oradata -F "t" -f -S -H "10.0.0.1"
# OK - Sum of file sizes is 3897 octet, 2 t files found | 't'=2 size_sum=3897o
# Search largest file:
# $ ./check_files.pl -D /opt/oradata -F "t" -f -s -H "10.0.0.1"
# OK - Largest size file is 3896 octet, 2 t files found | 't'=2 size_largest=3896o size_smallest=1o
#
# About Threhold Format:
#
# Warning and critical levels are specified with '-w' and '-c' and each one
# must have exactly same number of values (separated by ',') as number of
# file type checks specified with '-F'. Any values you dont want
# to compare you specify as ~. There are also number of other one-letter
# modifiers that can be used before actual data value to direct how data is
# to be checked. These are as follows:
# > : issue alert if data is above this value (default)
# < : issue alert if data is below this value
# = : issue alert if data is equal to this value
# ! : issue alert if data is NOT equal to this value
#
# Supported are also two specifications of range formats:
# number1:number2 issue alert if data is OUTSIDE of range [number1..number2]
# i.e. alert if data<$number1 or data>$number2
# @number1:number2 issue alert if data is WITHIN range [number1..number2]
# i.e. alert if data>=$number and $data<=$number2
#
# A special modifier '^' can also be used to disable checking that warn values
# are less than (or greater than) critical values (it is rarely needed).
#
# Additional Options:
#
# You can check file age with '--age' option which allows to set threshold
# if any file (in any of the file specs given with -F) is older than specified
# number of seconds. The option either takes one number separated by ',' for
# WARNING and CRITICAL alerts. If you want only CRITICAL specify WARNING as ~.
# For example -a '~,60' would give CRITICAL alert if any file is older than minute
#
# Just '-a' will show how old found files are which was the behavior prior to 0.36
# version even if -a was not given as an option.
#
# You can also check file size with '--size' option which allows to set threshold
# if any file (in any of the file specs given with -F) is larger than specified
# number of octets (bytes). The option either takes one number separated by ','
# for WARNING and CRITICAL alerts. If you want only CRITICAL specify WARNING as ~.
# For example -s '~,60' would give CRITICAL alert if any file is larger than 60
# octets (bytes)
#
# If you want performance output then use '-f' option. The plugin will then
# output number of files of each type and age of oldest and newest files.
#
# Execution Options:
#
# This plugin checks list of files by executing 'ls' on a local system where
# it is run. It can also execute ls on a remote system or process the output
# from ls command executed through some other plugin. Options -C, -H and -I
# are used to specify how and where to execute 'ls' and process results.
#
# With -I the plugin will expect output from "ls -l" in standard input.
#
# With -C you specify actual shell command and it is executed by the plugin.
#
# With option -H plugin executes ls on a specified host using ssh, it is expected
# that you'd have proper keys for this option to work. This is simple option is
# basically a special case of -C and if you want something more complex use -C
#
# Note: I first wrote -C but found that -I is easier to use and cleaner as
# far as nagios command specification. Patrick Bailat added -H option.
#
# ========================== VERSION CHANGES AND TODO =========================
#
# [0.2] Apr 19, 2012 - First version written based on check_netstat.pl 0.351
# [0.3] Apr 21, 2012 - Added -l -r and -T options and fixed bugs
# [0.32] Apr 21, 2012 - Added -I as an alternative to -C
# [0.33] Apr 27, 2012 - Fixed bug with determining file ages
# [0.34] Jun 22, 2012 - Added better reporting of file age than just seconds
# [0.35] Aug 21, 2012 - Option '-T' was broken. Bug reported by Jeremy Mauro
# [0.36] Sep 20, 2012 - Made reporting of age optional only when -a option is given.
# This is a suggestion by Bernhard Eisenschmid
# [0.37] Jan 21, 2013 - Added -s option for check file size (Patrick Bailat)
# [0.38] Jan 22, 2013 - Added -S option for check sum of file size (Patrick Bailat)
# [0.39] Jan 23, 2013 - Added -H option for execute ls -l by ssh (Patrick Bailat)
# [0.40] Feb 10, 2013 - Documentation cleanup. New release.
# [0.41] Mar 23, 2013 - Fixed bug in parse_threshold function
# [0.416] May 3, 2013 - Fixed bugs reported by Joerg Heinemann that caused
# failures under embedded perl. Bugs were in
# open_shell_stream() and parse_lsline() functions
# [0.417] Jun 17, 2013 - More bug fixes that showed up with embedded perl
# [0.42] July 30, 2013 - Several bugs fixed. Main was git issue #36 by
# bradcavanagh when -l is used and no files in directory.
# [0.421] Aug 20, 2013 - More bug fixes for embedded perl, see issue #32
#
# TODO: This plugin is using early threshold check code that became the base
# of Naglio library and should be updated to use the library later
#
# ========================== LIST OF CONTRIBUTORS =============================
#
# The following individuals have contributed code, patches, bug fixes and ideas to
# this plugin (listed in last-name alphabetical order):
#
# Patrick Bailat
# Vincent Besançon
# Bernhard Eisenschmid
# William Leibzon
# Jeremy Mauro
#
# Open source community is grateful for all your contributions.
#
# ========================== START OF PROGRAM CODE ============================
use strict;
use Getopt::Long;
use Date::Parse;
# Nagios specific
use lib "/usr/lib/nagios/plugins";
our $TIMEOUT;
our %ERRORS;
eval 'use utils qw(%ERRORS $TIMEOUT)';
if ($@) {
$TIMEOUT = 20;
%ERRORS = ('OK'=>0,'WARNING'=>1,'CRITICAL'=>2,'UNKNOWN'=>3,'DEPENDENT'=>4);
}
my $Version='0.421';
my $o_help= undef; # help option
my $o_timeout= 10; # Default 10s Timeout
my $o_verb= undef; # verbose mode
my $o_version= undef; # version info option
my $o_perf= undef; # Performance data option
my $o_files= undef; # What files(s) to check
my @o_filesLv= (); # array for above list
my @o_filesL= (); # array of regex based on above
my $o_warn= undef; # warning level option
my @o_warnLv= (); # array from warn options, before further processing
my @o_warnL= (); # array of warn options, each array element is an array of threshold spec
my $o_crit= undef; # Critical level option
my @o_critLv= (); # array of critical options before processing
my @o_critL= (); # array of critical options, each element is an array of threshold spec
my $o_dir= undef; # directory in which to check files
my $o_age= undef; # option to specify threshold of file age in seconds
my $o_age_warn= undef; # processed warning and critical thresholds for file age
my $o_age_crit= undef; # critical threshold for age
my $o_size= undef; # option to specify threshold of file size in octet
my $o_size_warn= undef; # processed warning and critical thresholds for file size
my $o_size_crit= undef; # critical threshold for size
my $o_sumsize= undef; # option to specify threshold of sum of file size in octet
my $o_sumsize_warn= undef; # processed warning and critical thresholds for sum of file size
my $o_sumsize_crit= undef; # critical threshold for sum of file size
my $o_recurse= undef; # recurse into subdirectories
my $o_filetype= undef; # option to look into only files or only directories
my $o_lsfiles= undef; # will cause ls to actually look for specified files
my $o_label= undef; # optional label
my $o_cmd= undef; # specify shell command here that does equivalent to 'ls -l'
my $o_stdin= undef; # instead of executing 'ls -l', expect this data from std input
my $o_host= undef; # optional hostadress execute ls -l by ssh
my $ls_pid= undef;
# For verbose output
sub verb { my $t=shift; print $t,"\n" if defined($o_verb) ; }
sub print_version { print "$0: $Version\n" };
sub print_usage {
print "Usage: $0 [-v] [-t <timeout>] -D <directory> -F <files to check> -w <warn level(s)> -c <crit level(s)> [-a [<warn age>,<crit age>]] [-s [<warn size>,<crit size>]] [-S [<warn size>,<crit size>]] [-f] [-r] [-l] [-T files|dir] [-L label] [-V] [-H <hostaddress> | -I | -C <cmd that does 'ls -l>']\n";
}
# Return true if arg is a number - in this case negative and real numbers are not allowed
sub isnum {
my $num = shift;
if ( $num =~ /^[+]?(\d*)$/ ) { return 1 ;}
return 0;
}
sub perf_name {
my $iname = shift;
$iname =~ s/'\/\(\)/_/g; #' get rid of special characters in performance description name
return "'".$iname."'";
}
sub todebugstr {
my $in = shift;
return 'undef' if !defined($in);
return $in;
}
# help function used when checking data against critical and warn values
sub check_threshold {
my ($attrib, $data, $th_array) = @_;
my $mod = $th_array->[0];
my $lv1 = $th_array->[1];
my $lv2 = $th_array->[2];
# verb("debug check_threshold: $mod : ".(defined($lv1)?$lv1:'')." : ".(defined($lv2)?$lv2:''));
return "" if !defined($lv1) || ($mod eq '' && $lv1 eq '');
return " " . $attrib . " is " . $data . " (equal to $lv1)" if $mod eq '=' && $data eq $lv1;
return " " . $attrib . " is " . $data . " (not equal to $lv1)" if $mod eq '!' && $data ne $lv1;
return " " . $attrib . " is " . $data . " (more than $lv1)" if $mod eq '>' && $data>$lv1;
return " " . $attrib . " is " . $data . " (more than $lv2)" if $mod eq ':' && $data>$lv2;
return " " . $attrib . " is " . $data . " (more than or equal $lv1)" if $mod eq '>=' && $data>=$lv1;
return " " . $attrib . " is " . $data . " (less than $lv1)" if ($mod eq '<' || $mod eq ':') && $data<$lv1;
return " " . $attrib . " is " . $data . " (less than or equal $lv1)" if $mod eq '<=' && $data<=$lv1;
return " " . $attrib . " is " . $data . " (in range $lv1..$lv2)" if $mod eq '@' && $data>=$lv1 && $data<=$lv2;
return "";
}
# this is a help function called when parsing threshold options data
sub parse_threshold {
my $thin = shift;
# link to an array that holds processed threshold data
# array: 1st is type of check, 2nd is value2, 3rd is value2, 4th is option, 5th is nagios spec string representation for perf out
my $th_array = [ '', undef, undef, '', '' ];
my $th = $thin;
my $at = '';
$at = $1 if $th =~ s/^(\^?[@|>|<|=|!]?~?)//; # check mostly for my own threshold format
$th_array->[3]='^' if $at =~ s/\^//; # deal with ^ option
$at =~ s/~//; # ignore ~ if it was entered
if ($th =~ /^\:([-|+]?\d+\.?\d*)/) { # :number format per nagios spec
$th_array->[1]=$1;
$th_array->[0]=($at !~ /@/)?'>':'<=';
$th_array->[5]=($at !~ /@/)?('~:'.$th_array->[1]):($th_array->[1].':');
}
elsif ($th =~ /([-|+]?\d+\.?\d*)\:$/) { # number: format per nagios spec
$th_array->[1]=$1;
$th_array->[0]=($at !~ /@/)?'<':'>=';
$th_array->[5]=($at !~ /@/)?'':'@';
$th_array->[5].=$th_array->[1].':';
}
elsif ($th =~ /([-|+]?\d+\.?\d*)\:([-|+]?\d+\.?\d*)/) { # nagios range format
$th_array->[1]=$1;
$th_array->[2]=$2;
if ($th_array->[1] > $th_array->[2]) {
print "Incorrect format in '$thin' - in range specification first number must be smaller then 2nd\n";
print_usage();
exit $ERRORS{"UNKNOWN"};
}
$th_array->[0]=($at !~ /@/)?':':'@';
$th_array->[5]=($at !~ /@/)?'':'@';
$th_array->[5].=$th_array->[1].':'.$th_array->[2];
}
if (!defined($th_array->[1])) {
$th_array->[0] = ($at eq '@')?'<=':$at;
$th_array->[1] = $th;
$th_array->[5] = '~:'.$th_array->[1] if ($th_array->[0] eq '>' || $th_array->[0] eq '>=');
$th_array->[5] = $th_array->[1].':' if ($th_array->[0] eq '<' || $th_array->[0] eq '<=');
$th_array->[5] = '@'.$th_array->[1].':'.$th_array->[1] if $th_array->[0] eq '=';
$th_array->[5] = $th_array->[1].':'.$th_array->[1] if $th_array->[0] eq '!';
}
if ($th_array->[0] =~ /[>|<]/ && !isnum($th_array->[1])) {
print "Numeric value required when '>' or '<' are used !\n";
print_usage();
exit $ERRORS{"UNKNOWN"};
}
# verb("debug parse_threshold: $th_array->[0] and $th_array->[1]");
$th_array->[0] = '=' if !$th_array->[0] && !isnum($th_array->[1]) && $th_array->[1] ne '';
if (!$th_array->[0] && isnum($th_array->[1])) { # this is just the number by itself, becomes 0:number check per nagios guidelines
$th_array->[2]=$th_array->[1];
$th_array->[1]=0;
$th_array->[0]=':';
$th_array->[5]=$th_array->[2];
}
return $th_array;
}
# this function checks that for numeric data warn threshold is within range of critical threshold
# where within range depends on actual threshold spec and normally just means less
sub threshold_specok {
my ($warn_thar,$crit_thar) = @_;
return 0 if (defined($warn_thar->[1]) && !isnum($warn_thar->[1])) || (defined($crit_thar->[1]) && !isnum($crit_thar->[1]));
return 1 if defined($warn_thar) && defined($warn_thar->[1]) &&
defined($crit_thar) && defined($crit_thar->[1]) &&
isnum($warn_thar->[1]) && isnum($crit_thar->[1]) &&
$warn_thar->[0] eq $crit_thar->[0] &&
(!defined($warn_thar->[3]) || $warn_thar->[3] !~ /\^/) &&
(!defined($crit_thar->[3]) || $crit_thar->[3] !~ /\^/) &&
(($warn_thar->[1]>$crit_thar->[1] && ($warn_thar->[0] =~ />/ || $warn_thar->[0] eq '@')) ||
($warn_thar->[1]<$crit_thar->[1] && ($warn_thar->[0] =~ /</ || $warn_thar->[0] eq ':')) ||
($warn_thar->[0] eq ':' && $warn_thar->[2]>=$crit_thar->[2]) ||
($warn_thar->[0] eq '@' && $warn_thar->[2]<=$crit_thar->[2]));
return 0; # return with 0 means specs check out and are ok
}
sub help {
print "\nFile(s) Age and Count Monitor Plugin for Nagios version ",$Version,"\n";
print " by William Leibzon - william(at)leibzon.org\n\n";
print_usage();
print <<EOD;
-h, --help
print this help message
-V, --version
prints version number
-t, --timeout=INTEGER
timeout for command to finish (Default : 5)
-L, --label
output label (what prints first on status line)
-v, --verbose
print extra debugging information
File and Directory Selection options:
-F, --files=STR[,STR[,STR[..]]]
Which files to check. What is here is similar to what you use for listing
file with ls i.e. *.temp would look for all temp files. This is converted
to a regex and NOT an actual ls command input, so some errors are possible.
-D, --dir=<STR>
Directory name in which to check files. If this is specifies all file names
given in -F will be relative to this directory.
-T, --filetype='files'|'dir'
Allows to specify if we should count only files or only directories.
Default is to count both and ignore file type.
-r, --recurse
When present ls will do 'ls -r' and recursive check in subdirectories
-l, --lsfiles
When present this adds specified file spec to ls. Now ls will list
only files you specified with -F where as by default 'ls -l' will
list all files in directory and choose some with regex. This option
should be used if there are a lot of files in a directory.
WARNING: using this option will cause -r not to work on most systems
Execution Options:
-C, --cmd=STR
By default the plugin will chdir to specified directory, do 'ls -l'
and parse results. Here you can specify alternative cmd to execute
that provides the data. This is used, for example, when files are
to be checked on a remote system, in which case here you could be
using 'ssh'.
-I, --stdin
Instead of executing "ls -l" directory or command specified with -C
plugin expects to get results in standard input. This is basically an
alternative to -C which may not work in all cases
-H, --hostaddress=<STR>
This option followed by the IP address or the server name execute "ls"
on the remote server with ssh. Beware the script must be run with an account
that has its public key to the remote server.
This option does not work with the -C option
Threshold Checks and Performance Data options:
-f, --perfparse
Give number of files and file oldest file age in perfout
-w, --warn=STR[,STR[,STR[..]]]
Warning level(s) for number of files - must be a number
Warning values can have the following prefix modifiers:
> : warn if data is above this value (default)
< : warn if data is below this value
= : warn if data is equal to this value
! : warn if data is not equal to this value
~ : do not check this data (must be by itself)
^ : this disables checks that warning is less than critical
Threshold values can also be specified as range in two forms:
num1:num2 - warn if data is outside range i.e. if data<num1 or data>num2
\@num1:num2 - warn if data is in range i.e. data>=num1 && data<=num2
-c, --crit=STR[,STR[,STR[..]]]
Critical level(s) (if more than one file spec, must have multiple values)
Critical values can have the same prefix modifiers as warning, except '^'
-a, --age[=WARN[,CRIT]]
Show file age if no WARN/CRIT threshold parameter specified.
Check to make sure files are not older than the specified threshold(s).
This number is in seconds. Also supports same extended spec as -w and -c
-s, --size[=WARN[,CRIT]]
Show file size if no WARN/CRIT threshold parameter specified.
Check to make sure files are not larger than the specified threshold(s).
This number is in octet/byte. Also supports same extended spec as -w and -c
-S, --sumsize[=WARN[,CRIT]]
Show sum of file sizes if no WARN/CRIT threshold parameter specified.
Check to make sure sum of file sizes are not larger than the specified threshold(s).
This number is in octet/byte. Also supports same extended spec as -w and -c
EOD
}
sub check_options {
my $i;
Getopt::Long::Configure ("bundling");
GetOptions(
'v' => \$o_verb, 'verbose' => \$o_verb,
'h' => \$o_help, 'help' => \$o_help,
't:i' => \$o_timeout, 'timeout:i' => \$o_timeout,
'V' => \$o_version, 'version' => \$o_version,
'L:s' => \$o_label, 'label:s' => \$o_label,
'c:s' => \$o_crit, 'crit:s' => \$o_crit,
'w:s' => \$o_warn, 'warn:s' => \$o_warn,
'f' => \$o_perf, 'perfparse' => \$o_perf,
'F:s' => \$o_files, 'files:s' => \$o_files,
'a:s' => \$o_age, 'age:s' => \$o_age,
's:s' => \$o_size, 'size:s' => \$o_size,
'S:s' => \$o_sumsize, 'sumsize:s' => \$o_sumsize,
'D:s' => \$o_dir, 'dir:s' => \$o_dir,
'C:s' => \$o_cmd, 'cmd:s' => \$o_cmd,
'r' => \$o_recurse, 'recurse' => \$o_recurse,
'l' => \$o_lsfiles, 'lsfiles' => \$o_lsfiles,
'T:s' => \$o_filetype, 'filetype:s' => \$o_filetype,
'I' => \$o_stdin, 'stdin' => \$o_stdin,
'H:s' => \$o_host, 'hostaddress:s' => \$o_host,
);
if (defined($o_help) ) { help(); exit $ERRORS{"UNKNOWN"}; }
if (defined($o_version)) { print_version(); exit $ERRORS{"UNKNOWN"}; }
@o_filesLv=split(/,/,$o_files) if defined($o_files);
if (!defined($o_files) || scalar(@o_filesLv)==0) {
print "You must specify files to check on with '-F'\n";
print_usage();
exit $ERRORS{"UNKNOWN"};
}
@o_filesLv=split(/,/, $o_files);
for (my $i=0; $i<scalar(@o_filesLv); $i++) {
$o_filesL[$i] = parse_filespec($o_filesLv[$i]);
verb("Translated filespec '".$o_filesLv[$i]."' to regex '".$o_filesL[$i]."'");
}
if (defined($o_warn) || defined($o_crit)) {
@o_filesLv=split(/,/, $o_files);
@o_warnLv=split(/,/ ,$o_warn) if defined($o_warn);
@o_critLv=split(/,/ ,$o_crit) if defined($o_crit);
if (scalar(@o_warnLv)!=scalar(@o_filesLv) || scalar(@o_critLv)!=scalar(@o_filesLv)) {
if (scalar(@o_warnLv)==0 && scalar(@o_critLv)==scalar(@o_filesLv)) {
verb('Only critical value check is specified - setting warning to ~');
for($i=0;$i<scalar(@o_filesLv);$i++) { $o_warnLv[$i]='~'; }
}
elsif (scalar(@o_critLv)==0 && scalar(@o_warnLv)==scalar(@o_filesLv)) {
verb('Only warning value check is specified - setting critical to ~');
for($i=0;$i<scalar(@o_filesLv);$i++) { $o_critLv[$i]='~'; }
}
else {
printf "Number of specified warning levels (%d) and critical levels (%d) must be equal to the number checks specified at '-F' (%d). If you need not set threshold specify it as '~'\n", scalar(@o_warnLv), scalar(@o_critLv), scalar(@o_filesL);
print_usage();
exit $ERRORS{"UNKNOWN"};
}
}
for (my $i=0; $i<scalar(@o_filesLv); $i++) {
$o_warnL[$i] = parse_threshold($o_warnLv[$i]);
$o_critL[$i] = parse_threshold($o_critLv[$i]);
if (threshold_specok($o_warnL[$i],$o_critL[$i])) {
print "Problem with warn threshold '".$o_warnL[$i][5]."' and/or critical threshold '".$o_critL[$i][5]."'\n";
print "All warning and critical values must be numeric or ~. Warning must be less then critical\n";
print "or greater then when '<' is used or within or outside of range for : and @ specification\n";
print "Note: to override less than check prefix warning value with ^\n";
print_usage();
exit $ERRORS{"UNKNOWN"};
}
}
}
if (defined($o_age)) {
my @agetemp = split(',',$o_age);
$o_age_warn = parse_threshold($agetemp[0]) if defined($agetemp[0]) && $agetemp[0] ne '';
$o_age_crit = parse_threshold($agetemp[1]) if defined($agetemp[1]) && $agetemp[1] ne '';
}
if (defined($o_size)) {
my @sizetemp = split(',',$o_size);
$o_size_warn = parse_threshold($sizetemp[0]) if defined($sizetemp[0]) && $sizetemp[0] ne '';
$o_size_crit = parse_threshold($sizetemp[1]) if defined($sizetemp[1]) && $sizetemp[1] ne '';
}
if (defined($o_sumsize)) {
my @sumsizetemp = split(',',$o_sumsize);
$o_sumsize_warn = parse_threshold($sumsizetemp[0]) if defined($sumsizetemp[0]) && $sumsizetemp[0] ne '';
$o_sumsize_crit = parse_threshold($sumsizetemp[1]) if defined($sumsizetemp[1]) && $sumsizetemp[1] ne '';
}
if (defined($o_filetype)) {
$o_filetype = lc $o_filetype;
$o_filetype = 'file' if $o_filetype eq 'files';
$o_filetype = 'dir' if $o_filetype eq 'dirs';
if ($o_filetype ne 'file' && $o_filetype ne 'dir') {
print "Filetype must be one word - either 'file' or 'dir'\n";
print_usage();
exit $ERRORS{"UNKNOWN"};
}
}
if ((defined($o_stdin) && defined($o_cmd)) ||
(defined($o_stdin) && defined($o_host)) ||
(defined($o_host) && defined($o_cmd))) {
print "Can use only one of -C or -I or -H\n";
print_usage();
exit $ERRORS{"UNKNOWN"};
}
}
sub parse_filespec {
my $spec = shift;
$spec =~ s/\./\\\./g;
$spec =~ s/\?/\.\?/g;
$spec =~ s/\*/\.\*/g;
return $spec;
}
# ls -l line example:
# -rwxr-xr-x 1 WLeibzon users 21747 Apr 20 23:04 check_files.pl
sub parse_lsline {
my $line = shift;
my @parsed = split (/\s+/, $line);
my %ret = ('type' => 'unset');
# parse file mode into std number
if (defined($parsed[0]) && $parsed[0] =~ /([-d])(.{3})(.{3})(.{3})/) {
my ($file_type,$mod_user, $mod_group, $mod_all) = ($1,$2,$3,$4);
if ($file_type eq 'd') {
$ret{'type'}='dir';
}
else {
$ret{'type'}='file';
}
$ret{'mode'} = 0;
$ret{'mode'} += 400 if $mod_user =~ /r/;
$ret{'mode'} += 200 if $mod_user =~ /w/;
$ret{'mode'} += 100 if $mod_user =~ /x/;
$ret{'mode'} += 40 if $mod_group =~ /r/;
$ret{'mode'} += 20 if $mod_group =~ /w/;
$ret{'mode'} += 10 if $mod_group =~ /x/;
$ret{'mode'} += 4 if $mod_all =~ /r/;
$ret{'mode'} += 2 if $mod_all =~ /w/;
$ret{'mode'} += 1 if $mod_all =~ /x/;
$ret{'nfiles'} = $parsed[1] if defined($parsed[1]); # number of files, dir start with 2
$ret{'user'} = $parsed[2] if defined($parsed[2]);
$ret{'group'} = $parsed[3] if defined($parsed[3]);
$ret{'size'} = $parsed[4] if defined($parsed[4]);
$ret{'time_line'} = $parsed[5].' '.$parsed[6].' '.$parsed[7] if defined($parsed[5]) && defined($parsed[6]) && defined($parsed[7]);
$ret{'filename'} = $parsed[8] if defined($parsed[8]);
$ret{'time'} = str2time($ret{'time_line'}) if defined($ret{'time_line'});
}
elsif ($line =~ /No such file or directory/) {
$ret{'nofilesfound'}=1;
}
return \%ret;
}
sub div_mod { return int( $_[0]/$_[1]) , ($_[0] % $_[1]); }
sub readable_time {
my $total_sec = shift;
my ($sec,$mins,$hrs,$days);
($mins,$sec) = div_mod($total_sec,60);
($hrs,$mins) = div_mod($mins,60);
($days,$hrs) = div_mod($hrs,24);
my $txtout="";
$txtout .= "$days days " if $days>0;
$txtout .= "$hrs hours " if $hrs>0;
$txtout .= "$mins minutes " if $mins>0 && ($days==0 || $hrs==0);
$txtout .= "$sec seconds" if ($sec>0 || $mins==0) && ($hrs==0 && $days==0);
return $txtout;
}
sub open_shell_stream {
my $shell_command_ref = shift;
my $cd_dir=undef;
my $shell_command=undef;
if (defined($o_stdin)) {
$shell_command = "<stdin>";
$$shell_command_ref = $shell_command if defined($shell_command_ref);
return \*STDIN;
}
else {
if ($o_dir) {
if (not defined($o_host)) {
if (!chdir($o_dir)) {
print "UNKNOWN ERROR - could not chdir to $o_dir - $!";
exit $ERRORS{'UNKNOWN'};
} else {
verb("Changed to directory '".$o_dir."'");
}
} else {
$cd_dir="\"cd ".$o_dir." && ";
}
}
if (defined($o_cmd)) {
verb("Command Specified: ".$o_cmd);
$shell_command=$o_cmd;
} else {
if(defined($o_host)) {
$shell_command = "ssh -o BatchMode=yes -o ConnectTimeout=30 ".$o_host." ";
}
else {
$shell_command = "";
}
$shell_command .= $cd_dir if defined($cd_dir);
$shell_command .= "LANG=C ls -l";
verb("Command: ".$shell_command);
}
$shell_command .= " -R" if defined($o_recurse);
$shell_command .= " ".join(" ",@o_filesLv) if defined($o_lsfiles);
$shell_command .= "\"" if defined($cd_dir);
# I would have preferred open3 [# if (!open3($cin, $cout, $cerr, $shell_command))]
# but there are problems when using it within nagios embedded perl
verb("Executing ".$shell_command." 2>&1");
$ls_pid=open(SHELL_DATA, "$shell_command 2>&1 |");
if (!$ls_pid) {
print "UNKNOWN ERROR - could not execute $shell_command - $!";
exit $ERRORS{'UNKNOWN'};
}
$$shell_command_ref = $shell_command if defined($shell_command_ref);
return \*SHELL_DATA;
}
return undef; # it should never get here
}
# Get the alarm signal (just in case timeout screws up)
$SIG{'ALRM'} = sub {
print ("ERROR: Alarm signal (Nagios time-out)\n");
kill 9, $ls_pid if defined($ls_pid);
exit $ERRORS{"UNKNOWN"};
};
########## MAIN ##############
check_options();
verb("check_files.pl plugin version ".$Version);
# Check global timeout if something goes wrong
if (defined($TIMEOUT)) {
verb("Alarm at ".($TIMEOUT+10));
alarm($TIMEOUT+10);
} else {
verb("no timeout defined : $o_timeout + 10");
alarm ($o_timeout+10);
}
# next part of the code builds list of attributes to be retrieved
my $statuscode = "OK";
my $statusinfo = "";
my $statusdata = "";
my $perfdata = "";
my $chk = "";
my $i;
my $shell_command = "";
my $nlines=0;
my @ls=();
my $oldest_filetime=undef;
my $oldest_filename=undef;
my $newest_filetime=undef;
my $newest_filename=undef;
my $smallest_filesize=undef;
my $smallest_filesizename=undef;
my $largest_filesize=undef;
my $largest_filesizename=undef;
my @nmatches=();
my $READTHIS=undef;
my $matched=0;
my $temp;
my $sum_filesize=0;
$READTHIS = open_shell_stream(\$shell_command);
# go through each line
while (<$READTHIS>) {
chomp($_);
$ls[$nlines]=parse_lsline($_);
# $ls[$nlines]{'ls_text'}=$_;
verb('got line: '.$_);
$temp=""; # these 3 lines are all for debug output
$temp .= ' '.$_.'='. $ls[$nlines]{$_} foreach (keys %{$ls[$nlines]});
verb (" processed:".$temp);
if (defined($ls[$nlines]{'filename'}) && (!defined($o_filetype) ||
(defined($o_filetype) && $ls[$nlines]{'type'} eq $o_filetype))) {
$matched=0;
for (my $i=0; $i<scalar(@o_filesL); $i++) {
if ($ls[$nlines]{'filename'} =~ /$o_filesL[$i]/) {
$nmatches[$i] = 0 if !defined($nmatches[$i]);
$nmatches[$i]++;
verb(" file matches regex '".$o_filesL[$i]."' for file spec '".$o_filesLv[$i]."'");
$matched=1;
}
}
if ($matched==1 && defined($ls[$nlines]{'time'})) {
if (!defined($newest_filetime) || $ls[$nlines]{'time'}>$newest_filetime) {
$newest_filetime=$ls[$nlines]{'time'};
$newest_filename=$ls[$nlines]{'filename'};
}
if (!defined($oldest_filetime) || $ls[$nlines]{'time'}<$oldest_filetime) {
$oldest_filetime=$ls[$nlines]{'time'};
$oldest_filename=$ls[$nlines]{'filename'};
}
}
if ($matched==1 && defined($ls[$nlines]{'size'})) {
$sum_filesize += $ls[$nlines]{'size'};
if (!defined($largest_filesize) || $ls[$nlines]{'size'}>$largest_filesize) {
$largest_filesize=$ls[$nlines]{'size'};
$largest_filesizename=$ls[$nlines]{'filename'};
}
if (!defined($smallest_filesize) || $ls[$nlines]{'size'}<$smallest_filesize) {
$smallest_filesize=$ls[$nlines]{'size'};
$smallest_filesizename=$ls[$nlines]{'filename'};
}
}
}
$nlines++;
}
if (defined($ls[0]{'nofilesfound'}) && $ls[0]{'nofilesfound'}) {
close($READTHIS) if defined($o_stdin);
$nlines=1;
}
else {
if (!defined($o_stdin) && defined($READTHIS) && !close($READTHIS)) {
print "UNKNOWN ERROR - execution of $shell_command resulted in an error $? - $!";
exit $ERRORS{'UNKNOWN'};
}
if ($nlines eq 0) {
print "UNKNOWN ERROR - did not receive any results";
exit $ERRORS{'UNKNOWN'};
}
}
# Check time
my $tnow = time();
verb("Date ".$tnow." Oldest_filetime: ".todebugstr($oldest_filetime)." Newest_filetime: ".todebugstr($newest_filetime));
my $oldest_secold=$tnow-$oldest_filetime if defined($oldest_filetime);
my $newest_secold=$tnow-$newest_filetime if defined($newest_filetime);
verb("Oldest file has age of ".$oldest_secold." seconds and newest ".$newest_secold." seconds") if defined($oldest_secold) && defined($newest_secold);
if (defined($o_age) && defined($oldest_secold)) {
if (defined($o_age_crit) && ($chk = check_threshold($oldest_filename." ",$oldest_secold,$o_age_crit)) ) {
$statuscode = "CRITICAL";
$statusinfo .= "," if $statusinfo;
$statusinfo .= $chk." seconds old";
}
if (defined($o_age_warn) && ($chk = check_threshold($oldest_filename." ",$oldest_secold,$o_age_warn)) && $statuscode eq 'OK' ) {
$statuscode = "WARNING" if $statuscode eq "OK";
$statusinfo .= "," if $statusinfo;
$statusinfo .= $chk." seconds old";
}
if ($statuscode eq 'OK') {
$statusdata .= "," if ($statusdata);
$statusdata .= " Oldest timestamp is ".readable_time($oldest_secold)." old";
}
}
# Check size
verb("Largest file has size of ".$largest_filesize." octet and smallest ".$smallest_filesize." octet");
if (defined($o_size) && defined($largest_filesize)) {
my $flag_data=0;
if (defined($o_size_crit) && ($chk = check_threshold($largest_filesizename." ",$largest_filesize,$o_size_crit)) ) {
$flag_data=1;
$statuscode = "CRITICAL";
$statusinfo .= "," if $statusinfo;
$statusinfo .= $chk." bytes";
}
if (defined($o_size_warn) && ($chk = check_threshold($largest_filesizename." ",$largest_filesize,$o_size_warn)) && ($statuscode eq 'OK' || $statuscode eq 'WARNING') ) {
$flag_data=1;
$statuscode = "WARNING" if $statuscode eq "OK";
$statusinfo .= "," if $statusinfo;
$statusinfo .= $chk." bytes";
}
if ($flag_data==0) {
$statusdata .= "," if ($statusdata);
$statusdata .= " Largest size file is ".$largest_filesize." bytes";
}
}
# Check sum of file sizes
if (defined($o_sumsize) && $sum_filesize > 0) {
my $flag_data=0;
verb("Sum of file sizes is ".$sum_filesize." octet");
if (defined($o_sumsize_crit) && ($chk = check_threshold("Sum of file sizes ",$sum_filesize,$o_sumsize_crit)) ) {
$flag_data=1;
$statuscode = "CRITICAL";
$statusinfo .= "," if $statusinfo;
$statusinfo .= $chk." octet";
}
if (defined($o_sumsize_warn) && ($chk = check_threshold("Sum of file sizes ",$sum_filesize,$o_sumsize_warn)) && ($statuscode eq 'OK' || $statuscode eq 'WARNING') ) {
$flag_data=1;
$statuscode="WARNING" if $statuscode eq "OK";
$statusinfo .= "," if $statusinfo;
$statusinfo .= $chk." bytes ";
}
if ($flag_data==0) {
$statusdata .= "," if ($statusdata);
$statusdata .= " Sum of file sizes is ".$sum_filesize." bytes";
}
}
# loop to check if warning & critical attributes are ok
for ($i=0;$i<scalar(@o_filesL);$i++) {
$nmatches[$i]=0 if !defined($nmatches[$i]);
if ($chk = check_threshold($o_filesLv[$i],$nmatches[$i],$o_critL[$i])) {
$statuscode = "CRITICAL";
$statusinfo .= "," if $statusinfo;
$statusinfo .= $chk;
} elsif ($chk = check_threshold($o_filesLv[$i],$nmatches[$i],$o_warnL[$i])) {
$statuscode="WARNING" if $statuscode eq "OK";
$statusinfo .= "," if $statusinfo;
$statusinfo .= $chk;
} else {
$statusdata .= "," if ($statusdata);
$statusdata .= " ".$nmatches[$i]." ". $o_filesLv[$i] ." files found";
}
$perfdata .= " ". perf_name($o_filesLv[$i]) ."=". $nmatches[$i] if defined($o_perf);
if (defined($o_perf) && defined($o_warnL[$i][5]) && defined($o_critL[$i][5])) {
$perfdata .= ';' if $o_warnL[$i][5] ne '' || $o_critL[$i][5] ne '';
$perfdata .= $o_warnL[$i][5] if $o_warnL[$i][5] ne '';
$perfdata .= ';'.$o_critL[$i][5] if $o_critL[$i][5] ne '';
}
}
# perffata for age
if (defined($o_perf) && defined($o_age)) {
$oldest_secold=0 if !defined($oldest_secold);
$newest_secold=0 if !defined($newest_secold);
$perfdata .= " age_oldest=".$oldest_secold."s";
$perfdata .= ';' if (defined($o_age_warn) && $$o_age_warn[5] ne '') || (defined($o_age_crit) && $$o_age_crit[5] ne '');
$perfdata .= $$o_age_warn[5] if defined($$o_age_warn[5]) && $$o_age_warn[5] ne '';
$perfdata .= ';'.$$o_age_crit[5] if defined($$o_age_crit[5]) && $$o_age_crit[5] ne '';
$perfdata .= " age_newest=".$newest_secold."s";
}
# perffata for size
if (defined($o_perf) && defined($o_size)) {
$largest_filesize=0 if !defined($largest_filesize);
$smallest_filesize=0 if !defined($smallest_filesize);
$perfdata .= " size_largest=".$largest_filesize."o";
$perfdata .= ';' if (defined($o_size_warn) && $$o_size_warn[5] ne '') || (defined($o_size_crit) && $$o_size_crit[5] ne '');
$perfdata .= $$o_size_warn[5] if defined($$o_size_warn[5]) && $$o_size_warn[5] ne '';
$perfdata .= ';'.$$o_size_crit[5] if defined($$o_size_crit[5]) && $$o_size_crit[5] ne '';
$perfdata .= " size_smallest=".$smallest_filesize."o";
}
# perffata for sum of file sizes
if (defined($o_perf) && defined($o_sumsize)) {
$perfdata .= " size_sum=".$sum_filesize."o";
$perfdata .= ';' if (defined($o_sumsize_warn) && $$o_sumsize_warn[5] ne '') || (defined($o_sumsize_crit) && $$o_sumsize_crit[5] ne '');
$perfdata .= $$o_sumsize_warn[5] if defined($$o_sumsize_warn[5]) && $$o_sumsize_warn[5] ne '';
$perfdata .= ';'.$$o_sumsize_crit[5] if defined($$o_sumsize_crit[5]) && $$o_sumsize_crit[5] ne '';
}
$o_label .= " " if $o_label ne '';
print $o_label . $statuscode;
print " -".$statusinfo if $statusinfo;
print " -".$statusdata if $statusdata;
print " |".$perfdata if $perfdata;
print "\n";
exit $ERRORS{$statuscode};