#!/usr/common/bin/perl -w # This script contains subroutines to check printer status ############################################################ # 05/10/2001 rcc2 - Fix printer_idle; Rennovate vendor independence scheme # 06/24/2000 rcc2 - Convert to DBMS # 10/11/99 rcc - Add "printer_idle"; Remove "service request" as a "down" # 09/24/99 rcc - Add "get_pagecount" # 08/01/99 rcc - Convert NPStat.pl to Perl 5 module npstatlib.pm # 04/09/99 rcc - Reduce SNMP retries from 5 to 2 # 10/01/98 rcc ############################################################ package npstatlib; # "use diagnostics" should be commented out for production environment #use diagnostics; use strict; use vars qw{ @ISA @EXPORT }; require Exporter; @ISA = qw{ Exporter }; @EXPORT = qw{ get_status get_snmp_info err2str printer_ok printer_idle get_pagecount snmp_get $PSTAT_SERVICE_REQ $PSTAT_OFFLINE $PSTAT_PAPER_JAM $PSTAT_DOOR_OPEN $PSTAT_TONER_OUT $PSTAT_TONER_LOW $PSTAT_PAPER_OUT $PSTAT_PAPER_LOW $PSTAT_IDLE $PSTAT_PRINTING $PSTAT_WARMING_UP $PSTAT_UNREACHABLE $PSTAT_MAX }; # exported use vars qw{ $PSTAT_SERVICE_REQ $PSTAT_OFFLINE $PSTAT_PAPER_JAM $PSTAT_DOOR_OPEN $PSTAT_TONER_OUT $PSTAT_TONER_LOW $PSTAT_PAPER_OUT $PSTAT_PAPER_LOW $PSTAT_IDLE $PSTAT_PRINTING $PSTAT_WARMING_UP $PSTAT_UNREACHABLE $PSTAT_MAX }; # non-exported but global to the package: use vars qw{ %statmsg %prtstatmsg %encoded_oids $PAGECOUNT_OID $MODEL_OID $DEVICE_STATUS_OID $PRINTER_STATUS_OID $ERROR_STATE_OID $MEMORY_SIZE_OID $LEX_MODEL_OID $LEX_CONSOLE_OID $LEX_OPTRA_SERIAL_NO_OID $LEX_INT_SERIAL_NO_OID $HP_SERIAL_NO_OID $HP_5M_SERIAL_NO_OID }; # Loads modules from source directory if executed in source directory use lib qw(. /usr/local/netprint/lib); use npparams; use SNMP_Session; use BER; #### Global data BEGIN { # OIDs for SNMP variables # Techniques for figuring out OIDs: # To get OID for printer serial number: # snmpwalk uris1 public .iso.3.6.1 | grep 11CKWB3 # To access an OID: # snmpget uris1 public .1.3.6.1.4.1.641.2.1.2.1.6.1 # Helpful URL: www.ibr.cs.tu-bs.de/cgi-bin/sbrowser.cgi # OIDs common to all printers (I hope) $PAGECOUNT_OID = '1.3.6.1.2.1.43.10.2.1.4.1.1'; # prtMarkerLifeCount.1.1 $MODEL_OID = '1.3.6.1.2.1.25.3.2.1.3.1'; # hrDeviceDescr.1 $DEVICE_STATUS_OID = '1.3.6.1.2.1.25.3.2.1.5.1'; # hrDeviceStatus.1 $PRINTER_STATUS_OID= '1.3.6.1.2.1.25.3.5.1.1.1'; # hrPrinterStatus.1 $ERROR_STATE_OID = '1.3.6.1.2.1.25.3.5.1.2.1'; # hrPrinterDetectedErrorState.1 $MEMORY_SIZE_OID = '1.3.6.1.2.1.25.2.2.0'; # hrMemorySize.0 # Manufacturer or model-specific OIDs $LEX_MODEL_OID = '1.3.6.1.4.1.641.2.1.2.1.2.1'; # prtgenPrinterName.1 $LEX_CONSOLE_OID = '1.3.6.1.2.1.43.16.5.1.2.1.1'; # prtConsoleDisplayBufferText.1.1 $LEX_OPTRA_SERIAL_NO_OID = '1.3.6.1.4.1.641.2.1.2.1.6.1'; # Not in published Lexmark MIB $LEX_INT_SERIAL_NO_OID = '1.3.6.1.4.1.641.2.1.2.1.5.1'; # Bogus (not available) $HP_SERIAL_NO_OID = '1.3.6.1.2.1.43.5.1.1.17.1', # Not in published RFC1759 $HP_5M_SERIAL_NO_OID= '1.3.6.1.4.1.11.2.3.9.4.2.1.1.3.3.0', # HP private MIB # Note: The bit definitions for the first 8 of the items below are taken # from the description of the values for hrPrinterDetectedErrorState in # RFC1759. $PSTAT_SERVICE_REQ = 1; $PSTAT_OFFLINE = 2; $PSTAT_PAPER_JAM = 4; $PSTAT_DOOR_OPEN = 8; $PSTAT_TONER_OUT = 16; $PSTAT_TONER_LOW = 32; $PSTAT_PAPER_OUT = 64; $PSTAT_PAPER_LOW = 128; $PSTAT_IDLE = 256; $PSTAT_PRINTING = 512; $PSTAT_WARMING_UP = 1024; $PSTAT_UNREACHABLE = 2048; $PSTAT_MAX = 2048; %statmsg = ( $PSTAT_SERVICE_REQ, 'Service Requested', $PSTAT_OFFLINE, 'Offline', $PSTAT_PAPER_JAM, 'Paper Jam', $PSTAT_DOOR_OPEN, 'Door Open', $PSTAT_TONER_OUT, 'Toner Out', $PSTAT_TONER_LOW, 'Toner Low', $PSTAT_PAPER_OUT, 'Paper Out', $PSTAT_PAPER_LOW, 'Paper Low', $PSTAT_IDLE, 'Idle', $PSTAT_PRINTING, 'Printing', $PSTAT_WARMING_UP, 'Warming Up', $PSTAT_UNREACHABLE, 'DISCONNECTED, OFFLINE, OR NOT RESPONDING', ); %prtstatmsg = ( 3, $PSTAT_IDLE, 4, $PSTAT_PRINTING, 5, $PSTAT_WARMING_UP, ); } ######################################################## # get_status($ip) # # $ip IP address or DNS name of printer # $model Printer model or undef # Returns: # $status Bit encoded printer status # Uses SNMP to find the status and returns an errorcode # which can be deciphered in err2str, which will return an array of # strings specifying the error(s) occuring. sub get_status { my($ip, $model) = @_; # No idea what this is about # if ( $ip eq '' ) { # return( { 'status' => $PSTAT_IDLE } ); # } my($community) = 'public'; # The following line can be commented out for debugging purposes. $SNMP_Session::suppress_warnings = 1; my($session); if ( ! ($session = SNMP_Session->open ($ip, $community, 161)) ) { warn "Couldn't open SNMP session to $ip: $SNMP_Session::errmsg"; return($PSTAT_UNREACHABLE); } $session->set_retries(2); # Get printer model if necessary if ( ! defined($model) ) { $model = snmp_get_model($session); } if ( ! defined($model) ) { return($PSTAT_UNREACHABLE); } my($error, $prtstatus, $console); if ( $model =~ /^Lexmark/ ) { # Lexmark printers require looking at the console status too because # printer_status doesn't show 'printing' until the paper starts moving. ($error, $prtstatus, $console) = snmp_get($session, $ERROR_STATE_OID, $PRINTER_STATUS_OID, $LEX_CONSOLE_OID); $error = unpack("C", $error); if ( ! defined($error) ) { return($PSTAT_UNREACHABLE); } if ( $console =~ /Ready|Power Saver/ ) { if ( defined($prtstatmsg{$prtstatus}) ) { $error |= $prtstatmsg{$prtstatus}; } } else { $error |= $PSTAT_PRINTING; } } elsif ( $model =~ /^HP/ ) { # HP printer ($error, $prtstatus) = snmp_get($session, $ERROR_STATE_OID, $PRINTER_STATUS_OID); if ( ! defined($error) ) { return($PSTAT_UNREACHABLE); } $error = unpack("C", $error); if ( defined($prtstatmsg{$prtstatus}) ) { $error |= $prtstatmsg{$prtstatus}; } } $session->close; return($error); } ######################################################## # get_snmp_info($ip) # # $ip IP address or DNS name of printer # $model Printer model or undef # Returns: # $snmpinfo Hash reference containing keys 'pagecount', 'model', 'status', # 'device_status', 'printer_status', 'error_state', # 'memory_size', 'serial_no', 'status', 'real_model'. sub get_snmp_info { my($ip, $model) = @_; # No idea what this is about # if ( $ip eq '' ) { # return( { 'status' => $PSTAT_IDLE } ); # } my($community) = 'public'; # The following line can be commented out for debugging purposes. $SNMP_Session::suppress_warnings = 1; my($session); if ( ! ($session = SNMP_Session->open ($ip, $community, 161)) ) { warn "Couldn't open SNMP session to $ip: $SNMP_Session::errmsg"; return( { 'status' => $PSTAT_UNREACHABLE } ); } $session->set_retries(2); # Get printer model if necessary if ( ! defined($model) ) { $model = snmp_get_model($session); } if ( ! defined($model) ) { return( { 'status' => $PSTAT_UNREACHABLE } ); } my($count, $devstatus, $error, $prtstatus, $console, $memsize, $serialno, $status, $real_model); if ( $model =~ /^Lexmark/ ) { # Lexmark printers require looking at the console status too because # printer_status doesn't show 'printing' until the paper starts moving. my($serial_no_oid) = ($model =~ /^Lexmark Optra/ ? $LEX_OPTRA_SERIAL_NO_OID : $LEX_INT_SERIAL_NO_OID); ($count, $devstatus, $error, $prtstatus, $console, $memsize, $serialno, $real_model) = snmp_get($session, $PAGECOUNT_OID, $DEVICE_STATUS_OID, $ERROR_STATE_OID, $PRINTER_STATUS_OID, $LEX_CONSOLE_OID, $MEMORY_SIZE_OID, $serial_no_oid, $LEX_MODEL_OID); if ( ! defined($count) ) { return( { 'status' => $PSTAT_UNREACHABLE } ); } $error = unpack("C", $error); $status = $error; if ( $console =~ /Ready|Power Saver/ ) { if ( defined($prtstatmsg{$prtstatus}) ) { $status |= $prtstatmsg{$prtstatus}; } } else { $status |= $PSTAT_PRINTING; } } elsif ( $model =~ /^HP/ ) { # HP printer my($serial_no_oid) = ($model =~ /^HP LaserJet 5M/ ? $HP_5M_SERIAL_NO_OID : $HP_SERIAL_NO_OID); ($count, $devstatus, $error, $prtstatus, $console, $memsize, $serialno) = snmp_get($session, $PAGECOUNT_OID, $DEVICE_STATUS_OID, $ERROR_STATE_OID, $PRINTER_STATUS_OID, $LEX_CONSOLE_OID, $MEMORY_SIZE_OID, $serial_no_oid); if ( ! defined($count) ) { return( { 'status' => $PSTAT_UNREACHABLE } ); } $error = unpack("C", $error); $status = $error; if ( defined($prtstatmsg{$prtstatus}) ) { $status |= $prtstatmsg{$prtstatus}; } if ( $model eq 'HP LaserJet 5M' ) { $serialno = substr($serialno, 2); } $real_model = $model; } $session->close; return( { 'pagecount' => $count, 'model' => $model, 'device_status' => $devstatus, 'printer_status' => $prtstatus, 'error_state' => $error, 'memory_size' => $memsize, 'serial_no' => $serialno, 'status' => $status, 'real_model' => $real_model } ); } ######################################################### # err2str ($code) # # Given an errorcode, this sub will convert the # code to an array of strings, each ending in a newline. sub err2str { my($code) = @_; my(@msgs); my($i); @msgs = (); for ( $i = 1; $i <= $PSTAT_MAX; $i <<= 1 ) { if ( $i & $code ) { push(@msgs, $statmsg{$i}); } } return(@msgs); } ######################################################### # printer_ok($code) # # Given a printer status code, return TRUE for printer OK, and FALSE # for printer not functioning. sub printer_ok { my($code) = @_; return( ! ($code & ($PSTAT_OFFLINE | $PSTAT_PAPER_JAM | $PSTAT_DOOR_OPEN | $PSTAT_TONER_LOW | $PSTAT_TONER_OUT | $PSTAT_PAPER_OUT | $PSTAT_UNREACHABLE)) ); } ######################################################### # printer_idle($code) # # Given a printer status code, return TRUE for printer idle, and FALSE # for any other printer status. sub printer_idle { my($code) = @_; return( (($code & $PSTAT_IDLE) != 0) and printer_ok($code) ); } ######################################################### # Get printer page count # # $ip Printer IP address or DNS name # Returns: # $pagecount Printer page count or -1 if error sub get_pagecount { my($ip) = @_; # The following line can be commented out for debugging purposes. $SNMP_Session::suppress_warnings = 1; my($session); unless ( $session = SNMP_Session->open($ip, 'public', 161) ) { return(-1); } my(@snmpvals) = snmp_get($session, $PAGECOUNT_OID); $session->close(); unless ( $#snmpvals >= 0 ) { return(-1); # Will come here if 'noSuchName' error!! } return($snmpvals[0]); } ######################################################### # Get printer model using SNMP # # $session SNMP session ID # Returns: # $model Printer model or undef if error sub snmp_get_model { my($session) = @_; my($model); if ( ! (($model) = snmp_get($session, $MODEL_OID)) ) { return(undef); } return(undef) if ! defined($model); $model =~ s/\s+$//; return($model); } ######################################################### # Query the SNMP agent # # $session SNMP session ID # @oids Array of OIDs # Returns: # @values Array of values or undef if error # Given a session ID and an array of variable nicknames, query the SNMP # agent and return an array of values. We are assuming that the values # come back in the same order as the OIDs. sub snmp_get { my($session, @oids) = @_; my(@eoids); my($oid); foreach $oid ( @oids ) { # print "--> $oid\n"; my($eoid); if ( ! defined($eoid = $encoded_oids{$oid}) ) { $eoid = encode_oid(split(/\./, $oid)); $encoded_oids{$oid} = $eoid; } push(@eoids, $eoid); } my(@values); if ( $session->get_request_response(@eoids) ) { my($response) = $session->pdu_buffer; my($bindings) = $session->decode_get_response($response); my($binding); while ( $bindings ne '' ) { my($value); ($binding, $bindings) = decode_sequence($bindings); ($oid, $value) = decode_by_template($binding, "%O%@"); push(@values, pretty_print($value)); } } else { return(undef); } return @values; } 1; .