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
8 # the JetDirect code is taken from check_hpjd.c by Ethan Galstad
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
16 # 'JetDirect' is copyrighted by Hewlett-Packard
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.
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.
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.
34 ############################################################################
36 # TODO: Query HOST-RESOURCE MIB for a quick status
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
41 # hrPrinterStatus OBJECT-TYPE
50 # hrPrinterDetectedErrorState OBJECT-TYPE
52 # MAX-ACCESS read-only
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
73 # markerSupplyMissing 10
77 # overduePreventMaint 14
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 );
86 use utils qw(%ERRORS &print_revision &support &usage );
97 my $ptype = 1; # to standard RFC printer type
98 my $state = $ERRORS{'UNKNOWN'};
99 my $community = "public";
100 my $snmp_version = 1;
103 Getopt::Long::Configure('bundling');
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";
119 print_revision($PROGNAME,'$Revision$');
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"};
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" ) {
138 }elsif ($opt_P eq "RFC" ) {
141 print "Only \"HP\" and \"RFC\" are supported as printer options at this time.\n";
142 exit $ERRORS{"UNKNOWN"};
147 if ( $snmp_version =~ /[12]/ ) {
149 ($session, $error) = Net::SNMP->session(
150 -hostname => $hostname,
151 -community => $community,
153 -version => $snmp_version
156 if (!defined($session)) {
159 print ("$state: no session - $answer\n");
160 exit $ERRORS{$state};
163 print "Opened session|" if (defined ($opt_d));
165 }elsif ( $snmp_version =~ /3/ ) {
167 print ("$state: No support for SNMP v3 yet\n");
168 exit $ERRORS{$state};
171 print ("$state: No support for SNMP v$snmp_version yet\n");
172 exit $ERRORS{$state};
182 if ( $ptype == 1 ) { # STD MIB
183 print "STD-MIB|" if (defined ($opt_d));
188 my %std_mib_inst_count ;
189 my %std_mib_instances;
195 #### RFC1759 MIB OIDS
197 # sub-unit status - textual convention
198 my $subunit_status; # integer from 0-126
201 # column oid - not instances
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",
224 my %std_mib_marker_tech = (
227 3 => "electrophotographicLED",
228 4 => "electrophotographicLaser",
229 5 => "electrophotographicOther",
230 6 => "impactMovingHeadDotMatrix9pin",
231 7 => "impactMovingHeadDotMatrix24pin",
232 8 => "impactMovingHeadDotMatrixOther",
233 9 => "impactMovingHeadFullyFormed",
236 12 => "inkjectAqueous",
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",
254 my %std_mib_marker_counter_units = (
255 3 => "tenThousandthsOfInches",
267 my %std_mib_alert_groups = (
268 1 => "unspecifiedOther",
269 3 => "printerStorageMemory", # hostResourcesMIBStorageTable
270 4 => "internalDevice", # hostResourcesMIBDeviceTable
271 5 => "generalPrinter",
277 11 => "markerSupplies",
278 12 => "markerColorant",
280 14 => "connectionChannel",
282 16 => "consoleDisplayBuffer",
283 17 => "consoleLights",
287 my %std_mib_prt_alert_code = (
288 1 => "other", # ok if on power save
290 # -- codes common to serveral groups
293 5 => "interlockOpen",
294 6 => "interlockClosed",
295 7 => "configurationChange",
296 8 => "jam", # critical
297 # -- general Printer 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",
312 901 => "outputMediaTrayMissing",
313 902 => "outputMediaTrayAlmostFull",
314 903 => "outputMediaTrayFull",
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",
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;
356 print ("$state: $answer for $std_mib{$col_oid}\n");
357 exit $ERRORS{$state};
361 print "NoError col_oid $col_oid|" if (defined ($opt_d));
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} ;
369 $std_mib_inst_count{$col_oid} += 1 ; # count how many instances
370 $std_mib_instances{$col_oid} .= $snmp_index .":" ;
376 #foreach $key ( keys %std_mib_inst_count) {
377 # print "$key = $std_mib_inst_count{$key} $std_mib_instances{$key} \n";
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?
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));
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'} );
398 foreach $inst ( sort @display_index) {
399 $display .= $snmp_response{$std_mib{'std_mib_status_display'} . "." . $inst} . " ";
404 # see if there are any alerts
405 if (defined ( $std_mib_inst_count{'std_mib_alert_sev_level'} ) ) {
407 if ( ( lc($display) =~ /save/ || lc($display) =~ /warm/ ) && $std_mib_inst_count{'std_mib_alert_sev_level'} == 1 ) {
409 $answer = "Printer ok - $display";
410 print $answer . "|$pfd\n";
411 exit $ERRORS{$state};
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
418 if ( lc($display) =~ /warm/ && $std_mib_inst_count{'std_mib_alert_sev_level'} == 2 ) {
420 $answer = "$state: Printer - $display";
421 print $answer . "|$pfd\n";
422 exit $ERRORS{$state};
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): ";
431 for $inst (@display_index) {
433 if ( $snmp_response{$std_mib{'std_mib_alert_location'} . "." . $inst} < 1) {
434 $answer .= "unknown location ";
436 $answer .= $std_mib_prt_alert_code{$snmp_response{$std_mib{'std_mib_alert_location'} . "." . $inst} } . " ";
438 #print $std_mib_prt_alert_code{$snmp_response{$std_mib{'std_mib_alert_location'}. "." . $inst}} ;
442 print "$state: $answer|$pfd\n";
443 exit $ERRORS{$state};
447 $answer = "$state: Printer ok - $display ";
448 print $answer . "|$pfd\n";
449 exit $ERRORS{$state};
457 elsif( $ptype == 2 ) { # HP MIB - JetDirect
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";
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);
482 $state = $ERRORS{'OK'};
484 if (!defined($response = $session->get_request(@hp_oids))) {
485 $answer=$session->error;
488 print ("$state: $answer \n");
489 exit $ERRORS{$state};
492 # cycle thru the responses and set the appropriate state
494 if($response->{$HPJD_GD_PAPER_JAM} ) {
496 $answer = "Paper Jam";
498 elsif($response->{$HPJD_GD_PAPER_OUT} ) {
500 $answer = "Out of Paper";
502 elsif($response->{$HPJD_LINE_STATUS} ) {
503 if ($response->{$HPJD_LINE_STATUS} ne "POWERSAVE ON" ) {
505 $answer = "Printer Offline";
508 elsif($response->{$HPJD_GD_PERIPHERAL_ERROR} ) {
510 $answer = "Peripheral Error";
512 elsif($response->{$HPJD_INTERVENTION_REQUIRED} ) {
514 $answer = "Intervention Required";
516 elsif($response->{$HPJD_GD_TONER_LOW} ) {
518 $answer = "Toner Low";
520 elsif($response->{$HPJD_GD_MEMORY_OUT} ) {
522 $answer = "Insufficient Memory";
524 elsif($response->{$HPJD_GD_DOOR_OPEN} ) {
526 $answer = "Insufficient Memory";
528 elsif($response->{$HPJD_GD_PAPER_OUTPUT} ) {
530 $answer = "OutPut Tray is Full";
532 elsif($response->{$HPJD_GD_PAGE_PUNT} ) {
534 $answer = "Data too slow for Engine";
536 elsif($response->{$HPJD_PAPER_STATUS} ) {
538 $answer = "Unknown Paper Error";
540 else # add code to parse STATUS DISPLAY here
543 $answer = "Printer ok - $response->{$HPJD_GD_STATUS_DISPLAY} ";
548 print "$state: $answer \n";
549 exit $ERRORS{$state};
553 else{ # 3rd printer type - not yet supported
555 print "Printer type $opt_P has not been implemented\n";
557 exit $ERRORS{$state};
571 print "Usage: $PROGNAME -H <host> [-C community] [-P HP or RFC] [-p port] [-v snmp_version] [-h help] [-V version]\n";
575 print_revision($PROGNAME,'$Revision$');
576 print "Copyright (c) 2002 Subhendu Ghosh/Ethan Galstad.
578 This plugin reports the status of an network printer with an SNMP management
585 Name or IP address of host to check
587 snmp community string (default: public)
589 supported values are \"HP\" for Jetdirect printers and
590 \"RFC\" for RFC 1759 Print MIB based implementations (default: RFC)
592 Port where snmp agent is listening (default: 161)
594 SNMP version to use (default: version 1)