I have finally bitten the bullet and integrated SNMP into ifhp to do status and page counts.
It turned out to be VERY nasty to try to do this in C, so I cheated and used the 'use_snmp_for_status' script posted by John Perkins <john at cs.wisc.edu> and in the ifup/UTILS directory. If you have a desire to try using SNMP to get page counts, etc. for your problem child printers, or you have some other printers that do not send status back over the TCP/IP connection, but have an SNMP agent, then you might want to try this. Since this is REALLY a test version, and I have not added support to configure to check for the Net::SNMP libraries. Get the test version from: http://astart2.astart.com/pub/ifhp-3.5.12<testversion>.tgz I will be updating this as results come in. Patrick Powell Astart Technologies [EMAIL PROTECTED] 6741 Convoy Court Network and System San Diego, CA 92111 Consulting 858-874-6543 FAX 858-751-2435 LPRng - Print Spooler (http://www.lprng.com) Here is the README.SNMP from the ifhp distribution: Using SNMP To Get Printer Status Patrick Powell <papowell at lprng.com> 16 Oct 2003 1. Overview The IFHP filter can use a SNMP helper program 'snmp_printer_status' to query the printer for status information. This program can be modified to suit the particular printer. The original idea for the snmp_printer_status program was by: John Perkins <john at cs.wisc.edu> The code has been modified, changed, etc. etc. I think that there might be a comma or period left from the original, but that is probably all. 2. SNMP Basics The Simple Network Managment Protocol was conceived in the wild and youthful years of the TCP/IP protocol. It has the following properties: a) queries and results are sent using UDP (version 1) b) The 'agent' program runs on the target system and reports the information requested by a query. c) There is a standard set of 'queries' that can be used. d) The Mananagement Information Base (sic) (MIB) describes the things that can be queried. e) There is little to no security in the Version 1 and Version 2 of the protocols. f) almost all printers use Version 1, and implement all or part of the Printer MIB. 3. Status Information The following items are usually queried. 3.1 hrDeviceStatus HOST-RESOURCES-MIB::hrDeviceStatus.1 = INTEGER: unknown(1), running(2), warning(3), testing(4), down(5) OID Text format: .iso.org.dod.internet.mgmt.mib-2.host .hrDevice.hrDeviceTable.hrDeviceEntry.hrDeviceStatus.1 OID Numberical format: .1.3.6.1.2.1.25.3.2.1.5.1 The current operational state of the device described by this row of the table. A value unknown(1) indicates that the current state of the device is unknown. running(2) indicates that the device is up and running and that no unusual error conditions are known. The warning(3) state indicates that agent has been informed of an unusual error condition by the operational software (e.g., a disk device driver) but that the device is still 'operational'. An example would be a high number of soft errors on a disk. A value of testing(4), indicates that the device is not available for use because it is in the testing state. The state of down(5) is used only when the agent has been informed that the device is not available for any use. 3.2 hrPrinterStatus HOST-RESOURCES-MIB::hrPrinterStatus.1 = INTEGER: other(1), unknown(2), idle(3), printing(4), warmup(5) OID Text format: .iso.org.dod.internet.mgmt.mib-2.host .hrDevice.hrPrinterTable.hrPrinterEntry.hrPrinterStatus.1 OID Numberical format: .1.3.6.1.2.1.25.3.5.1.1.1 The current status of this printer device. 3.3 prtMarkerLifeCount Printer-MIB::prtMarkerLifeCount.1.1 = Counter32 .iso.org.dod.internet.mgmt.mib-2.printmib .prtMarker.prtMarkerTable.prtMarkerEntry.prtMarkerLifeCount.1.1 .1.3.6.1.2.1.43.10.2.1.4.1.1 This is the number of items that have been marked. There are other fields that tell what the unit values are, and what this measures. Almost always this is the number of impressions, not pages, that have been made by the printer. This is the value of interest for page counts. 3.4 prtConsoleDisplayBufferText Printer-MIB::prtConsoleDisplayBufferText.1.1 = STRING .iso.org.dod.internet.mgmt.mib-2.printmib. .prtConsoleDisplayBuffer.prtConsoleDisplayBufferTable .prtConsoleDisplayBufferEntry.prtConsoleDisplayBufferText.1.1 .1.3.6.1.2.1.43.16.5.1.2.1.1 = STRING The content of a line in the logical display buffer of the operator's console of the printer. When a write operation occurs, normally a critical message, to one of the LineText strings, the agent should make that line displayable if a physical display is present. Writing a zero length string clears the line. It is an implementation-specific matter as to whether the agent allows a line to be overwritten before it has been cleared. Printer generated strings shall be in the localization specified by prtConsoleLocalization. Management Application generated strings should be localized by the Management Application. Briefly, this is the status information that is usually shown on the front panel. By experiment, in most printers it appears to be the same status that is returned by various PJL error reporting facilities. 3.5 Alerts These monitor conditions in the printer. It appears that some printers do not support these conditions, and only report errors using the prtConsoleDisplayBufferText or some other proprietary OID. 3.5.1 prtAlertSeverityLevel Printer-MIB::prtAlertSeverityLevel.1.1 = INTEGER other(1), critical(3), warning(4), warningBinaryChangeEvent(5) .iso.org.dod.internet.mgmt.mib-2.printmib.prtAlert .prtAlertTable.prtAlertEntry.prtAlertSeverityLevel.1.1 .1.3.6.1.2.1.43.18.1.1.2.1.1 = INTEGER: warning(4) The level of severity of this alert table entry. The printer determines the severity level assigned to each entry in the table. A critical alert is binary by nature and definition. A warning is defined to be a non-critical alert. The original and most common warning is unary. The binary warning was added later and given a more distinguished name. 3.5.2 prtAlertGroup Printer-MIB::prtAlertGroup.1.3 = INTEGER other(1), hostResourcesMIBStorageTable(3), hostResourcesMIBDeviceTable(4), generalPrinter(5), cover(6), localization(7), input(8), output(9), marker(10), markerSupplies(11), markerColorant(12), mediaPath(13), channel(14), interpreter(15), consoleDisplayBuffer(16), consoleLights(17), alert(18), finDevice(30), finSupply(31), finSupplyMediaInput(32), finAttributeTable(33) .iso.org.dod.internet.mgmt.mib-2.printmib.prtAlert .prtAlertTable.prtAlertEntry.prtAlertGroup.1.3 = INTEGER .1.3.6.1.2.1.43.18.1.1.4.1.3 = INTEGER The type of sub-unit within the printer model that this alert is related. Input, output, and markers are examples of printer model groups, i.e., examples of types of sub-units. Wherever possible, these enumerations match the sub-identifier that identifies the relevant table in the printer MIB. NOTE: Alert type codes have been added for the host resources MIB storage table and device table. These additional types are for situations in which the printer's storage and device objects must generate alerts (and possibly traps for critical alerts). 3.5.3 prtAlertDescription Printer-MIB::prtAlertDescription.1.1 = STRING .iso.org.dod.internet.mgmt.mib-2.printmib.prtAlert.prtAlertTable .prtAlertEntry.prtAlertDescription.1.1 = STRING .1.3.6.1.2.1.43.18.1.1.8.1.1 = STRING A description of this alert entry in the localization specified by prtGeneralCurrentLocalization. The description is provided by the printer to further elaborate on the enumerated alert or provide information in the case where the code is classified as 'other' or 'unknown'. The printer is required to return a description string but the string may be a null string. 3.6 prtMarkerPowerOnCount Printer-MIB::prtMarkerPowerOnCount.1.1 = Counter32 .iso.org.dod.internet.mgmt.mib-2.printmib.prtMarker .prtMarkerTable.prtMarkerEntry.prtMarkerPowerOnCount.1.1 .1.3.6.1.2.1.43.10.2.1.5.1.1 = Counter32 The count of the number of units of measure counted since the equipment was most recently powered on using units of measure as specified by prtMarkerCounterUnit. 4. Getting SNMP Status The set of tools that I have been using is the SNMP Toolkit: Net-SNMP: If you are brave, as root you can do: perl -MCPAN -e "install Net::SNMP" You can get the SNMP tools from Sourceforge: http://sourceforge.net/projects/net-snmp/ You will need snmpwalk at the very least. If you are running FreeBSD, you can simply do (as root): cd /usr/ports/net/p5-Net-SNMP make make install You can get a set of printer MIBS from the LPRng Web or FTP Site: ftp://ftp.lprng.com/pub/LPRng/RESOURCES/MIB/mibs.tar,gz Untar these and put them into the default location used by the SNMP software. Candidates include: - /usr/local/share/snmp/mibs - /usr/share/snmp/mibs Now try using the snmpwalk program to get the printer information. Now you can try running the following script: #!/bin/sh V=IP.ADDRESS.OF.PRINTER snmpwalk -Cc -v1 -c public -m ALL $V .iso snmpwalk -Cc -v1 -c public -Of -m ALL $V .iso snmpwalk -Cc -v1 -c public -On -m ALL $V .iso 5. snmp_printer_status The snmp_printer_status command is really just a cut down version of the snmpwalk command. It queries the printer for the OID values shown above and formats the status information. use: snmp_printer_status [-vps][-t interval][-h ipaddr][-m model][-f xml_configuration] -v - verbose mode -p - no pagecount -s - show poweron pagecount -t interval - interval in secs between queries (default 1) -h ipaddr - printer ip address or DNS name -m model - model information -f xml_configuration - xml configuration file The required parameters are: -h ipaddr or DNS name This can have the format: ip%port or hostname%port to be compatible with LPRng Example of use: h110: {785} % snmp_printer_status -h 10.0.0.31 PRINTERSTATUS= idle DEVICESTATUS= unknown STATUS= READY PAGECOUNT= 12532 << NEWLINE entered PRINTERSTATUS= idle DEVICESTATUS= unknown STATUS= READY PAGECOUNT= 12532 The PRINTERSTATUS, DEVICESTATUS, STATUS, and PAGECOUNT are simply the values report by the Printer Agent. The snmp_printer_status periodically queries the printer, checking to see if any have changed. If they have, the new values are printed out. When the program reads a line of input it reads the MIB values and dumps them out again. If the prAlert MIB OID's are supported, you can monitor these as well: h110: {787} % snmp_printer_status -h 10.0.0.28 STATUS= level: warning group: input message: Input Tray Low STATUS= level: warning group: input message: Input Tray Low STATUS= level: warningBinaryChangeEvent group: generalPrinter message: Power Saver PRINTERSTATUS= other DEVICESTATUS= unknown STATUS= Power Save Mode PAGECOUNT= 93162 The snmp_printer_status.xml file has a set of configuration values for the snmp_printer_status program: <config> <snmp_params community="public" timeout="2" retries="2" > </snmp_params> <printerdata name="default" alert_oid =".1.3.6.1.2.1.43.18.1.1" prtAlertSeverityLevel = ".2.1" prtAlertDescription = ".8.1" prtAlertGroup = ".4.1" pagecount_oid=".1.3.6.1.2.1.43.10.2.1.4.1" poweroncount_oid=".1.3.6.1.2.1.43.10.2.1.5.1.1" devicestatus_oid=".1.3.6.1.2.1.25.3.2.1.5.1" printerstatus_oid=".1.3.6.1.2.1.25.3.5.1.1.1" display_oid=".1.3.6.1.2.1.43.16.5.1.2.1.1" > </printerdata> <printerdata name="noalert" alert_oid ="none" > </printerdata> <devicestatus other="1" unknown="2" idle="3" printing="4" warmup="5"></devicestatus> <printerstatus other="1" unknown="2" idle="3" printing="4" warmup="5"></printerstatus> <alertseveritylevel other="1" critical="3" warning="4" warningBinaryChangeEvent="5"></alertseveritylevel> <alertgroup other="1" hostResourcesMIBStorageTable="3" hostResourcesMIBDeviceTable="4" generalPrinter="5" cover="6" localization="7" input="8" output="9" marker="10" markerSupplies="11" markerColorant="12" mediaPath="13" channel="14" interpreter="15" consoleDisplayBuffer="16" consoleLights="17" alert="18" finDevice="30" ></alertgroup> </config> The <snmp_parms> section sets parameter values for the SNMP queries. The <printerdata> section defines the OID values to be used for fetching status. The name parameter set the name of the various entries. The other entries are used to decode the SNMP return values into user strings. 7. IFHP and snmp_printer_status The use of snmp for getting printer status has been merged into the ifhp baseline distribution. Here are the changes to the ifhp.conf file: #--- START SNMP monitoring # if you want to use SNMP monitoring then use the snmp_for_ifhp # program. This program will report the status of a printer # by polling it using SNMP. Status will be read from both the # back channel (i.e. - connection to printer) and from the SNMP # monitoring program. snmp_monitor@ snmp_program=/usr/local/libexec/filters/snmp_printer_status -h "\%s{dev}" -m "\%s{snmp_model}" # the snmp_printer_status.xml file has entries to specify the # SNMP Object Identifiers (OIDs) for variuos printers # The default is to use the standard Printer MIB entries for status. # Again, you should look at the code and modify the entries for your # printer. snmp_model=default # when we get status, we look for the following words in the # status to indicate that the printer is ready and/or idle. # Spaces in the status are replaced by underscores if necessary snmp_sync_status=ready power_save_mode snmp_end_status=ready idle #--- END SNMP monitoring When you use SNMP monitoring you do not need to send SYNC, PAGECOUNT, or WAITEND requests to the printer, as the status will be determined by the snmp_printer_status program. SYNC Condition: STATUS= value in the snmp_sync_status list END Condition: STATUS= value in the snmp_end_status list The PAGECOUNT value is reoprted only when conditions are appropriate to an end of job: No error condition and: DEVICESTATUS != 'down' and DEVICESTATUS != 'testing' and PRINTERSTATUS != 'printing' and PRINTERSTATUS != 'unknown' The ifhp filter will use snmp_printer_status to get the current printer status. The following set of options has been tried and seems to work: 8. Testing ifhp and snmp_printer_status Use the following test script. Edit it and set the appropriate values. #!/bin/sh # test ifhp + snmp # location of ifhp ifhp=/usr/local/libexec/filters/ifhp out=/tmp/out if [ -f "$out" ] ; then cp /dev/null $out ; fi # replace with your printers IP address # and port number out=10.0.0.31%9100 debug=4 # lots of debug=1 # simple debugging debug=0 # no debugging config=/usr/local/etc/ifhp.conf file=/usr/local/libexec/filters/UTILS/one.ps # set the option you want options=snmp_monitor,appsocket,close_connection,pjl_console@ options=snmp_monitor,appsocket,close_connection options=snmp_monitor,appsocket options=snmp_monitor $ifhp "-Tdev=$out,trace,config=$config,debug=$debug,model=$model,$options" <$file 2>&1 | tee /tmp/log Here is a sample of the output to an HP printer using options=snmp_monitor: h110: {47} % sh /tmp/sendhp.sh ifhp 15:42:47.203 [60711] main: using model '' ifhp 15:42:47.211 [60711] Check_pagecount: pagecount using 'snmp' ifhp 15:42:47.216 [60711] Process_job: setting up printer ifhp 15:42:47.226 [60711] Do_sync: getting sync using 'snmp' ifhp 15:42:47.536 [60711] Check_device_status: printerstatus = 'idle' ifhp 15:42:47.569 [60711] Check_device_status: devicestatus = 'unknown' ifhp 15:42:47.604 [60711] Check_device_status: status = 'READY' ifhp 15:42:47.604 [60711] Do_sync: sync done ifhp 15:42:47.639 [60711] Do_pagecount: pagecounter 12533 after 1 attempts ifhp 15:42:47.639 [60711] Do_accounting: pagecounter 12533 ifhp 15:42:47.640 [60711] Process_job: sending job file ifhp 15:42:47.640 [60711] Send_job: starting transfer ifhp 15:42:47.641 [60711] Send_job: initial job type 'POSTSCRIPT' ifhp 15:42:47.641 [60711] Send_job: decoded job type 'POSTSCRIPT' ifhp 15:42:47.641 [60711] Send_job: foomatic enabled and no PPD file specified ifhp 15:42:47.641 [60711] Send_job: job type 'POSTSCRIPT' ifhp 15:42:47.642 [60711] Send_job: transferring 145 bytes ifhp 15:42:47.642 [60711] Send_job: 100 percent done ifhp 15:42:47.642 [60711] Process_job: sent job file ifhp 15:42:47.642 [60711] Do_waitend: getting end using 'snmp' ifhp 15:42:48.331 [60711] Do_waitend: end of job detected ifhp 15:42:49.099 [60711] Check_device_status: id = '"HP LaserJet 2200"' ifhp 15:42:49.886 [60711] Check_device_status: printerstatus = 'printing' ifhp 15:42:49.954 [60711] Check_device_status: status = 'PRINTING DOCUMENT' ifhp 15:43:05.846 [60711] Check_device_status: printerstatus = 'idle' ifhp 15:43:05.948 [60711] Do_pagecount: pagecounter 12534 after 1 attempts ifhp 15:43:05.949 [60711] Do_accounting: pagecounter 12534, pages 1 ifhp 15:43:05.949 [60711] Process_job: done Here is a sample of output to another printer: fhp 17:17:35.690 [61562] main: using model '' ifhp 17:17:36.303 [61562] Check_pagecount: pagecount using 'snmp' ifhp 17:17:36.313 [61562] Process_job: setting up printer ifhp 17:17:36.323 [61562] Do_sync: getting sync using 'snmp' ifhp 17:17:36.571 [61562] Check_device_status: devicedescr = 'Sharp AR-507/S507' ifhp 17:17:36.607 [61562] Check_device_status: printername = 'Sharp' ifhp 17:17:36.981 [61562] Check_device_status: status = 'level: warning group: input message: Input Tray Low' ifhp 17:17:36.981 [61562] Check_device_status: status = 'level: warning group: input message: Input Tray Low' ifhp 17:17:36.982 [61562] Check_device_status: status = 'level: warningBinaryChangeEvent group: generalPrinter message: Power Saver' ifhp 17:17:38.379 [61562] Check_device_status: printerstatus = 'other' ifhp 17:17:38.414 [61562] Check_device_status: devicestatus = 'unknown' ifhp 17:17:38.445 [61562] Check_device_status: status = 'Power Save Mode' ifhp 17:17:38.447 [61562] Do_sync: sync done ifhp 17:17:38.475 [61562] Do_pagecount: pagecounter 93168 after 1 attempts ifhp 17:17:38.475 [61562] Do_accounting: pagecounter 93168 ifhp 17:17:38.476 [61562] Process_job: sending job file ifhp 17:17:38.476 [61562] Send_job: starting transfer ifhp 17:17:38.477 [61562] Send_job: initial job type 'POSTSCRIPT' ifhp 17:17:38.477 [61562] Send_job: decoded job type 'POSTSCRIPT' ifhp 17:17:38.477 [61562] Send_job: foomatic enabled and no PPD file specified ifhp 17:17:38.477 [61562] Send_job: job type 'POSTSCRIPT' ifhp 17:17:38.478 [61562] Send_job: transferring 145 bytes ifhp 17:17:38.478 [61562] Send_job: 100 percent done ifhp 17:17:38.478 [61562] Process_job: sent job file ifhp 17:17:38.479 [61562] Do_waitend: getting end using 'snmp' ifhp 17:17:41.521 [61562] Check_device_status: status = 'level: warningBinaryChangeEvent group: generalPrinter message: Power Saver CLEARED' ifhp 17:17:42.807 [61562] Check_device_status: printerstatus = 'printing' ifhp 17:17:42.863 [61562] Check_device_status: status = 'Printer: Receiving data' ifhp 17:18:35.473 [61562] Check_device_status: status = 'Printer: Printing in progress' 9. Problem Printers The main problem encountered using SNMP for status is that some printers do not seem to either update their SNMP status in sync with the printing activity. The PJL reverse channel may report printing completed while the SNMP agent will still report it as active. In addition, the agent is 'slow' at startup. For example, the job may actually be transferred to the printer before the agent has seen any status change. To deal with this, various experiments have been tried. The most successful seems to be to use 'appsocket' printing, ie, to have the ifhp process open and close the TCP/IP connection. This has the side effect of forcing an end of connection action that seems to make most printer SNMP agents update the status. Also, there is a slight delay in the close operation that may have the same effect. Another issue that has surfaced is that some printers have defective TCP/IP stacks that cannot handle a shutdown() on a TCP/IP connection. These require the use of the (previously undocumented) close_connect flag. Finally, there are printers that do not correctly report 'end of printing' activity using PJL. These may be better handled using the 'appsocket' and SNMP approach. 8. LPRng and IFHP The following printcap options seem to yeild the best results. lp: :lp=10.0.0.14%9100 # You must use socket protocol. :... # this work for many printers that have 'sane' network connections :ifhp=snmp_monitor # For problem printers that report End of Job early, # you may need to do this to force an 'end of job' condition :ifhp=snmp_monitor,appsocket # some printers will require that you close the connection # as their TCP/IP stacks are broken and they cannot handle # a shutdown :ifhp=snmp_monitor,appsocket,close_connection snmp_monitor - use the snmp to get the status appsocket - you need to force the connection open and closed. This appears to solve some issues with jobs not completing and the SNMP status indicates 'printing' close_connection - this may be necessary to force the end of job condition on some printers. Experimental Results: :ifhp=snmp_monitor worked on 3 different HP printers :ifhp=snmp_monitor,appsocket worked on printers that required appsocket. It also seemed to report end of job much better. :ifhp=snmp_monitor,appsocket,close_connection worked on printers that required appsocket AND required the connection to be closed for end of job to be detected. Several MOPIERS fell into this group, as the copier/printer would only change mode from copy to printing once it had the entire job, or sufficient number of pages to cause it to print. ----------------------------------------------------------------------------- YOU MUST BE A LIST MEMBER IN ORDER TO POST TO THE LPRNG MAILING LIST The address you post from MUST be your subscription address If you need help, send email to [EMAIL PROTECTED] (or lprng-requests or lprng-digest-requests) with the word 'help' in the body. For the impatient, to subscribe to a list with name LIST, send mail to [EMAIL PROTECTED] with: | example: subscribe LIST <mailaddr> | subscribe lprng-digest [EMAIL PROTECTED] unsubscribe LIST <mailaddr> | unsubscribe lprng [EMAIL PROTECTED] If you have major problems, send email to [EMAIL PROTECTED] with the word LPRNGLIST in the SUBJECT line. -----------------------------------------------------------------------------
