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.
-----------------------------------------------------------------------------

Reply via email to