Code

Fix for regex input of '|', being output causing problems with Nagios' parsing of
[nagiosplug.git] / contrib / check_snmp_printer.pl
1 #!/usr/local/bin/perl -w
3 # check_snmp_printer - check for printer status via snmp
4 #  Supports both standard PRINT-MIB (RFC-1759) and HP Enterprise print-mib
5 #  that is supported by some of the older JetDirect interfaces
7 # Acknowledgements:
8 # the JetDirect code is taken from check_hpjd.c by Ethan Galstad
9 #   
10 #   The idea for the plugin (as well as some code) were taken from Jim
11 #   Trocki's pinter alert script in his "mon" utility, found at
12 #   http://www.kernel.org/software/mon
13 #
15 # Notes:
16 # 'JetDirect' is copyrighted by Hewlett-Packard
17 #
18 #
19 # License Information:
20 # This program is free software; you can redistribute it and/or modify
21 # it under the terms of the GNU General Public License as published by
22 # the Free Software Foundation; either version 2 of the License, or
23 # (at your option) any later version.
24 #
25 # This program is distributed in the hope that it will be useful,
26 # but WITHOUT ANY WARRANTY; without even the implied warranty of
27 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
28 # GNU General Public License for more details.
29 #
30 # You should have received a copy of the GNU General Public License
31 # along with this program; if not, write to the Free Software
32 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
33 #
34 ############################################################################
35 #
36 # TODO: Query HOST-RESOURCE MIB for a quick status
37 #
38 # hrPrinterStatus = .1.3.6.1.2.1.25.3.5.1;
39 # hrPrinterDetectedErrorState = .1.3.6.1.2.1.25.3.5.1.2
40 #
41 # hrPrinterStatus OBJECT-TYPE
42 #    SYNTAX     INTEGER {
43 #                   other(1),
44 #                   unknown(2),
45 #                   idle(3),
46 #                   printing(4),
47 #                   warmup(5)
48 #               }
49 #
50 # hrPrinterDetectedErrorState OBJECT-TYPE
51 #    SYNTAX     OCTET STRING
52 #    MAX-ACCESS read-only
53 #    STATUS     current
54 #    DESCRIPTION
55 #        "This object represents any error conditions detected
56 #        by the printer.  The error conditions are encoded as
57 #        bits in an octet string, with the following
58 #        definitions:
59 #
60 #             Condition         Bit #
61 #
62 #             lowPaper              0
63 #
64 #             noPaper               1
65 #             lowToner              2
66 #             noToner               3
67 #             doorOpen              4
68 #             jammed                5
69 #             offline               6
70 #             serviceRequested      7
71 #             inputTrayMissing      8
72 #             outputTrayMissing     9
73 #             markerSupplyMissing  10
74 #             outputNearFull       11
75 #             outputFull           12
76 #             inputTrayEmpty       13
77 #             overduePreventMaint  14
78 #
79 #  
80 #
81 use strict;
82 use Getopt::Long;
83 use vars qw($opt_V $opt_h $opt_H $opt_P $opt_t $opt_d $session $error $answer $key
84    $response $PROGNAME $port $hostname );
85 use lib  "utils.pm";
86 use utils qw(%ERRORS &print_revision &support &usage );
87 use Net::SNMP;
89 sub print_help ();
90 sub print_usage ();
92 $ENV{'PATH'}='';
93 $ENV{'BASH_ENV'}=''; 
94 $ENV{'ENV'}='';
96 # defaults 
97 my $ptype = 1;                                                  # to standard RFC printer type
98 my $state = $ERRORS{'UNKNOWN'};
99 my $community = "public";
100 my $snmp_version = 1;
101 my $port = 161;
103 Getopt::Long::Configure('bundling');
104 GetOptions
105         ("d"   => \$opt_d, "debug"                      => \$opt_d,
106          "V"   => \$opt_V, "version"            => \$opt_V,
107          "P=s" => \$opt_P, "Printer=s"      => \$opt_P,    # printer type - HP or RFC
108          "v=i" => \$snmp_version, "snmp_version=i"  => \$snmp_version,
109          "p=i" => \$port, "port=i" => \$port,
110          "C=s" => \$community,"community=s" => \$community,
111          "h"   => \$opt_h, "help"               => \$opt_h,
112          "H=s" => \$opt_H, "hostname=s"         => \$opt_H);
116 $PROGNAME = "check_snmp_printer";
118 if ($opt_V) {
119         print_revision($PROGNAME,'$Revision: 795 $');
120         exit $ERRORS{'OK'};
123 if ($opt_h) {print_help(); exit $ERRORS{'OK'};}
125 unless (defined $opt_H) {
126         print "No target hostname specified\n";
127         exit $ERRORS{"UNKNOWN"};
129 $hostname = $opt_H;
130 if (! utils::is_hostname($hostname)){
131         usage(" $hostname did not match pattern\n");
132         exit $ERRORS{"UNKNOWN"};
135 if (defined $opt_P) {
136         if ($opt_P eq "HP" ) {
137                 $ptype = 2;
138         }elsif ($opt_P eq "RFC" ) {
139                 $ptype = 1;
140         }else{
141                 print "Only \"HP\" and \"RFC\" are supported as printer options at this time.\n";
142                 exit $ERRORS{"UNKNOWN"};
143         }
147 if ( $snmp_version =~ /[12]/ ) {
148                 
149         ($session, $error) = Net::SNMP->session(
150               -hostname  => $hostname,
151               -community => $community,
152               -port      => $port,
153                   -version      => $snmp_version
154                    );
156         if (!defined($session)) {
157               $state='UNKNOWN';
158               $answer=$error;
159               print ("$state: no session - $answer\n");
160               exit $ERRORS{$state};
161         }
163         print "Opened session|" if (defined ($opt_d));
164                 
165 }elsif ( $snmp_version =~ /3/ ) {
166         $state='UNKNOWN';
167         print ("$state: No support for SNMP v3 yet\n");
168         exit $ERRORS{$state};
169 }else{
170         $state='UNKNOWN';
171         print ("$state: No support for SNMP v$snmp_version yet\n");
172         exit $ERRORS{$state};
180 ### main logic
182 if ( $ptype == 1 ) {   # STD MIB
183         print "STD-MIB|" if (defined ($opt_d));
185         my %snmp_response;
186         my $snmp_index;
187         my $col_oid;
188         my %std_mib_inst_count ;
189         my %std_mib_instances;
190         my $display;
191         my $inst;
192         my $group;
193         
195         #### RFC1759 MIB OIDS
197         # sub-unit status - textual convention
198         my $subunit_status;   # integer from 0-126                                              
199                                                 
201         # column oid - not instances
202         my %std_mib = (
203                 std_mib_input_status =>                                 ".1.3.6.1.2.1.43.8.2.1.11",  # 2 element index
204                 std_mib_input_name   =>                                 ".1.3.6.1.2.1.43.8.2.1.13",
205                 std_mib_output_remaining_capacity =>    ".1.3.6.1.2.1.43.9.2.1.5", 
206                 std_mib_output_status =>                                ".1.3.6.1.2.1.43.9.2.1.6",
207                 std_mib_marker_tech =>                                  ".1.3.6.1.2.1.43.10.2.1.2",
208                 std_mib_marker_counter_unit =>  ".1.3.6.1.2.1.43.10.2.1.3",
209                 std_mib_marker_life_count =>            ".1.3.6.1.2.1.43.10.2.1.4",
210                 std_mib_marker_status =>                                ".1.3.6.1.2.1.43.10.2.1.15",
211                 std_mib_supplies_type =>                                ".1.3.6.1.2.1.43.11.1.1.5",
212                 std_mib_supplies_level =>                               ".1.3.6.1.2.1.43.11.1.1.9",
213                 std_mib_media_path_type =>                              ".1.3.6.1.2.1.43.13.4.1.9",
214                 std_mib_media_path_status =>                    ".1.3.6.1.2.1.43.13.4.1.11",
216                 std_mib_status_display =>                               ".1.3.6.1.2.1.43.16.5.1.2",  # 2 element index
218                 std_mib_alert_sev_level =>                              ".1.3.6.1.2.1.43.18.1.1.2",
219                 std_mib_alert_grp =>                                    ".1.3.6.1.2.1.43.18.1.1.4",
220                 std_mib_alert_location =>                               ".1.3.6.1.2.1.43.18.1.1.5",
222         );
224         my %std_mib_marker_tech = (
225                                         1 => "other",
226                                         2 => "unknown",
227                                         3 => "electrophotographicLED",
228                                         4 => "electrophotographicLaser",
229                                         5 => "electrophotographicOther",
230                                         6 => "impactMovingHeadDotMatrix9pin",
231                                         7 => "impactMovingHeadDotMatrix24pin",
232                                         8 => "impactMovingHeadDotMatrixOther",
233                                         9 => "impactMovingHeadFullyFormed",
234                                         10 => "impactBand",
235                                         11 => "impactOther",
236                                         12 => "inkjectAqueous",
237                                         13 => "inkjetSolid",
238                                         14 => "inkjetOther",
239                                         15 => "pen",
240                                         16 => "thermalTransfer",
241                                         17 => "thermalSensitive",
242                                         18 => "thermalDiffusion",
243                                         19 => "thermalOther",
244                                         20 => "electroerosion",
245                                         21 => "electrostatic",
246                                         22 => "photographicMicrofiche",
247                                         23 => "photographicImagesetter",
248                                         24 => "photographicOther",
249                                         25 => "ionDeposition",
250                                         26 => "eBeam",
251                                         27 => "typesetter",
252         );
253         
254         my %std_mib_marker_counter_units = (
255                                         3 => "tenThousandthsOfInches",
256                                         4 => "micrometers",
257                                         5 => "characters",
258                                         6 => "lines",
259                                         7 => "impressions",
260                                         8 => "sheets",
261                                         9 => "dotRow",
262                                         11 => "hours",
263                                         16 => "feet",
264                                         17 => "meters",
265         );
267         my %std_mib_alert_groups = (
268                 1 => "unspecifiedOther",
269                 3 => "printerStorageMemory",  # hostResourcesMIBStorageTable
270                 4 => "internalDevice",                  # hostResourcesMIBDeviceTable
271                 5 => "generalPrinter",
272                 6 => "cover",
273                 7 => "localization",
274                 8 => "input",
275                 9 => "output",
276                 10 => "marker",
277                 11 => "markerSupplies",
278                 12 => "markerColorant",
279                 13 => "mediaPath",
280                 14 => "connectionChannel",
281                 15 => "interpreter",
282                 16 => "consoleDisplayBuffer",
283                 17 => "consoleLights",
284         );
286                 
287         my %std_mib_prt_alert_code = (
288                 1 => "other",                      # ok if on power save
289                 2 => "unknown",
290         # -- codes common to serveral groups
291                 3 => "coverOpen",
292                 4 => "coverClosed",
293                 5 => "interlockOpen",
294                 6 => "interlockClosed",
295                 7 => "configurationChange",
296                 8 => "jam",                        # critical
297                 # -- general Printer group
298                 501 => "doorOpen",
299                 502 => "doorClosed",
300                 503 => "powerUp",
301                 504 => "powerDown",
302                 # -- Input Group
303                 801 => "inputMediaTrayMissing",
304                 802 => "inputMediaSizeChange",
305                 803 => "inputMediaWeightChange",
306                 804 => "inputMediaTypeChange",
307                 805 => "inputMediaColorChange",
308                 806 => "inputMediaFormPartsChange",
309                 807 => "inputMediaSupplyLow",
310                 808 => "inputMediaSupplyEmpty",
311                 # -- Output Group
312                 901 => "outputMediaTrayMissing",
313                 902 => "outputMediaTrayAlmostFull",
314                 903 => "outputMediaTrayFull",
315                 # -- Marker group
316                 1001 => "markerFuserUnderTemperature",
317                 1002 => "markerFuserOverTemperature",
318                 # -- Marker Supplies group
319                 1101 => "markerTonerEmpty",
320                 1102 => "markerInkEmpty",
321                 1103 => "markerPrintRibbonEmpty",
322                 1104 => "markerTonerAlmostEmpty",
323                 1105 => "markerInkAlmostEmpty",
324                 1106 => "markerPrintRibbonAlmostEmpty",
325                 1107 => "markerWasteTonerReceptacleAlmostFull",
326                 1108 => "markerWasteInkReceptacleAlmostFull",
327                 1109 => "markerWasteTonerReceptacleFull",
328                 1110 => "markerWasteInkReceptacleFull",
329                 1111 => "markerOpcLifeAlmostOver",
330                 1112 => "markerOpcLifeOver",
331                 1113 => "markerDeveloperAlmostEmpty",
332                 1114 => "markerDeveloperEmpty",
333                 # -- Media Path Device Group
334                 1301 => "mediaPathMediaTrayMissing",
335                 1302 => "mediaPathMediaTrayAlmostFull",
336                 1303 => "mediaPathMediaTrayFull",
337                 # -- interpreter Group  
338                 1501 => "interpreterMemoryIncrease",
339                 1502 => "interpreterMemoryDecrease",
340                 1503 => "interpreterCartridgeAdded",
341                 1504 => "interpreterCartridgeDeleted",
342                 1505 => "interpreterResourceAdded",
343                 1506 => "interpreterResourceDeleted",
344         );
346         ## Need multiple passes as oids are all part of tables  
347         foreach $col_oid (sort keys %std_mib ){ 
349                 if ( !defined( $response = $session->get_table($std_mib{$col_oid}) ) ) {
350                         print "Error col_oid $col_oid|" if (defined ($opt_d));
352                         if (! ($col_oid =~ m/std_mib_alert/ ) ) {             # alerts don't have to exist all the time!
353                                 $answer=$session->error;
354                                 $session->close;
355                                 $state = 'CRITICAL';
356                                 print ("$state: $answer for $std_mib{$col_oid}\n");
357                                 exit $ERRORS{$state};
358                         }
359                 }
360                 
361                 print "NoError col_oid $col_oid|" if (defined ($opt_d));
362                 
363                 foreach $key (keys %{$response}) {
364                         $key =~ /.*\.(\d+)\.(\d+)$/;     # all oids have a two part index appended
365                         $snmp_index = $1 . "." . $2;   
366                         print "\n$key => $col_oid.$snmp_index  = $response->{$key} \n" if (defined ($opt_d));
367                         $snmp_response{$key} = $response->{$key} ;
368                         
369                         $std_mib_inst_count{$col_oid} += 1 ;   # count how many instances
370                         $std_mib_instances{$col_oid} .= $snmp_index .":" ;
372                 }
373         
374         }
376         #foreach $key ( keys %std_mib_inst_count) {
377         #       print "$key = $std_mib_inst_count{$key} $std_mib_instances{$key}  \n";
378         #}
379         # get (total) "page count" - perfdata
380         #print "\n \n $std_mib_instances{'std_mib_marker_tech'} \n";    
381         # how many marker technologies are in use?
382         my ($pg, $pt, $pfd);
383         my @mark_tech = split(/:/, $std_mib_instances{'std_mib_marker_tech'});
384         foreach $inst (sort @mark_tech){
385                 $pfd = $std_mib_marker_tech{$snmp_response{$std_mib{'std_mib_marker_tech'}."." .$inst}} ;
386                 $pfd .= ",".$snmp_response{$std_mib{'std_mib_marker_life_count'}.".".$inst};
387                 $pfd .= ",".$std_mib_marker_counter_units{$snmp_response{$std_mib{'std_mib_marker_counter_unit'}.".".$inst}};
388                 $pfd .= ";"; #perf data separator for multiple marker tech
391                 print "pfd = $pfd\n" if (defined ($opt_d));
392         };
394         # combine all lines of status display into one line
395         #$std_mib_instances{'std_mib_status_display'} = substr($std_mib_instances{'std_mib_status_display'}, 1);
396         my @display_index = split(/:/,  $std_mib_instances{'std_mib_status_display'} );
397         
398         foreach $inst ( sort @display_index) {
399                 $display .= $snmp_response{$std_mib{'std_mib_status_display'} . "." . $inst} . " ";
400         }
404         # see if there are any alerts
405         if (defined ( $std_mib_inst_count{'std_mib_alert_sev_level'} )  ) {
406                 
407                 if ( ( lc($display) =~ /save/  || lc($display) =~ /warm/ ) &&   $std_mib_inst_count{'std_mib_alert_sev_level'} == 1 ) {
408                         $state='OK';
409                         $answer = "Printer ok - $display";
410                         print $answer . "|$pfd\n";
411                         exit $ERRORS{$state};
412                 }
413                 
414                 # sometime during transitions from power save to warming there are 2 alerts
415                 # if the 2nd alert is for something else it should get caught in the
416                 # next call since warmup typically is much smaller than check time
417                 # interval.
418                 if (  lc($display) =~ /warm/  &&        $std_mib_inst_count{'std_mib_alert_sev_level'} == 2 )   {
419                         $state='OK';
420                         $answer = "$state: Printer - $display";
421                         print $answer . "|$pfd\n";
422                         exit $ERRORS{$state};
423                 }
425                 
426                 # We have alerts and the display does not say power save or warming up
427                 $std_mib_instances{'std_mib_alert_sev_level'} = substr($std_mib_instances{'std_mib_alert_sev_level'}, 1);
428                 @display_index = split(/:/,     $std_mib_instances{'std_mib_alert_sev_level'} );
429                 $answer = "Alert location(s): ";
430                 
431                 for $inst (@display_index) {
432                         $state = 'WARNING';
433                         if ( $snmp_response{$std_mib{'std_mib_alert_location'} . "." . $inst} < 1) {
434                                 $answer .= "unknown location ";
435                         }else{
436                                 $answer .= $std_mib_prt_alert_code{$snmp_response{$std_mib{'std_mib_alert_location'} . "." . $inst} } . " ";
437                         
438                                 #print $std_mib_prt_alert_code{$snmp_response{$std_mib{'std_mib_alert_location'}. "." . $inst}} ;
439                         }
440                 }
442                 print "$state: $answer|$pfd\n";
443                 exit $ERRORS{$state};
444                 
445         }else{
446                 $state='OK';
447                 $answer = "$state: Printer ok - $display ";
448                 print $answer . "|$pfd\n";
449                 exit $ERRORS{$state};
451         }
452         
453         
454         
457 elsif( $ptype == 2 ) {  # HP MIB - JetDirect
458         
459         #### HP MIB OIDS - instance OIDs
460         my $HPJD_LINE_STATUS=                   ".1.3.6.1.4.1.11.2.3.9.1.1.2.1.0";
461         my $HPJD_PAPER_STATUS=                  ".1.3.6.1.4.1.11.2.3.9.1.1.2.2.0";
462         my $HPJD_INTERVENTION_REQUIRED= ".1.3.6.1.4.1.11.2.3.9.1.1.2.3.0";
463         my $HPJD_GD_PERIPHERAL_ERROR=   ".1.3.6.1.4.1.11.2.3.9.1.1.2.6.0";
464         my $HPJD_GD_PAPER_JAM=                  ".1.3.6.1.4.1.11.2.3.9.1.1.2.8.0";
465         my $HPJD_GD_PAPER_OUT=                  ".1.3.6.1.4.1.11.2.3.9.1.1.2.9.0";
466         my $HPJD_GD_TONER_LOW=                  ".1.3.6.1.4.1.11.2.3.9.1.1.2.10.0";
467         my $HPJD_GD_PAGE_PUNT=                  ".1.3.6.1.4.1.11.2.3.9.1.1.2.11.0";
468         my $HPJD_GD_MEMORY_OUT=                 ".1.3.6.1.4.1.11.2.3.9.1.1.2.12.0";
469         my $HPJD_GD_DOOR_OPEN=                  ".1.3.6.1.4.1.11.2.3.9.1.1.2.17.0";
470         my $HPJD_GD_PAPER_OUTPUT=               ".1.3.6.1.4.1.11.2.3.9.1.1.2.19.0";
471         my $HPJD_GD_STATUS_DISPLAY=             ".1.3.6.1.4.1.11.2.3.9.1.1.3.0";
472         #define ONLINE          0
473         #define OFFLINE         1
475         my @hp_oids = ( $HPJD_LINE_STATUS,$HPJD_PAPER_STATUS,$HPJD_INTERVENTION_REQUIRED,$HPJD_GD_PERIPHERAL_ERROR,
476                                 $HPJD_GD_PAPER_JAM,$HPJD_GD_PAPER_OUT,$HPJD_GD_TONER_LOW,$HPJD_GD_PAGE_PUNT,$HPJD_GD_MEMORY_OUT,
477                                 $HPJD_GD_DOOR_OPEN,$HPJD_GD_PAPER_OUTPUT,$HPJD_GD_STATUS_DISPLAY);
481         
482         $state = $ERRORS{'OK'};
484         if (!defined($response = $session->get_request(@hp_oids))) {
485                 $answer=$session->error;
486                 $session->close;
487                 $state = 'CRITICAL';
488                 print ("$state: $answer \n");
489                 exit $ERRORS{$state};
490         }
492         # cycle thru the responses and set the appropriate state
493         
494         if($response->{$HPJD_GD_PAPER_JAM} ) {
495                 $state='WARNING';
496                 $answer = "Paper Jam";
497         }
498         elsif($response->{$HPJD_GD_PAPER_OUT} ) {
499                 $state='WARNING';
500                 $answer = "Out of Paper";
501         }
502         elsif($response->{$HPJD_LINE_STATUS} ) {
503                 if ($response->{$HPJD_LINE_STATUS} ne "POWERSAVE ON" ) {
504                         $state='WARNING';
505                         $answer = "Printer Offline";
506                 }
507         }
508         elsif($response->{$HPJD_GD_PERIPHERAL_ERROR} ) {
509                 $state='WARNING';
510                 $answer = "Peripheral Error";
511         }
512         elsif($response->{$HPJD_INTERVENTION_REQUIRED} ) {
513                 $state='WARNING';
514                 $answer = "Intervention Required";
515         }
516         elsif($response->{$HPJD_GD_TONER_LOW} ) {
517                 $state='WARNING';
518                 $answer = "Toner Low";
519         }
520         elsif($response->{$HPJD_GD_MEMORY_OUT} ) {
521                 $state='WARNING';
522                 $answer = "Insufficient Memory";
523         }
524         elsif($response->{$HPJD_GD_DOOR_OPEN} ) {
525                 $state='WARNING';
526                 $answer = "Insufficient Memory";
527         }
528         elsif($response->{$HPJD_GD_PAPER_OUTPUT} ) {
529                 $state='WARNING';
530                 $answer = "OutPut Tray is Full";
531         }
532         elsif($response->{$HPJD_GD_PAGE_PUNT} ) {
533                 $state='WARNING';
534                 $answer = "Data too slow for Engine";
535         }
536         elsif($response->{$HPJD_PAPER_STATUS} ) {
537                 $state='WARNING';
538                 $answer = "Unknown Paper Error";
539         }
540         else            # add code to parse STATUS DISPLAY here
541         {
542                 $state='OK';
543                 $answer = "Printer ok - $response->{$HPJD_GD_STATUS_DISPLAY} ";
544         }
546         # print and exit
548         print "$state: $answer \n";
549         exit $ERRORS{$state};
553 else{  # 3rd printer type - not yet supported
554         
555         print "Printer type $opt_P has not been implemented\n";
556         $state='UNKNOWN';
557         exit $ERRORS{$state};
563 #### subroutines
564 sub unit_status {
565         my $stat = shift;
566         
570 sub print_usage () {
571         print "Usage: $PROGNAME -H <host> [-C community] [-P HP or RFC] [-p port]  [-v snmp_version] [-h help]  [-V version]\n";
574 sub print_help () {
575         print_revision($PROGNAME,'$Revision: 795 $');
576         print "Copyright (c) 2002 Subhendu Ghosh/Ethan Galstad.
578 This plugin reports the status of an network printer with an SNMP management
579 module.
581 ";
582         print_usage();
583         print "
584 -H, --hostname=HOST
585    Name or IP address of host to check
586 -C --community
587    snmp community string (default: public)
588 -P --Printer
589    supported values are \"HP\" for Jetdirect printers and
590    \"RFC\" for RFC 1759 Print MIB based implementations (default: RFC)
591 -p --port
592    Port where snmp agent is listening (default: 161)
593 -v --snmp_version
594    SNMP version to use (default: version 1)
595 -h --help
596    This screen
597 -V --version
598    Plugin version
600 ";
601         support();