Accsnmp is a hardware accounting wrapper backend for CUPS that queries the printer for pagecount directly over SNMP.

Download accsnmp




Accsnmp is an accounting backend for CUPS, inserted between the filter layer and the backend layer as a wrapper around other CUPS backends. It attempts to address some of the shortcomings inherent in the CUPS accounting design:

1) It will account correctly for raw queues, for which CUPS cannot natively.

2) It deals directly with the hardware printer device to find out how many pages were printed. This means that any and every printer job language is supported, requiring no parsing of the job itself. Even in postscript, there is a relative lack of standard practice in creating DSC compliant documents. Dealing with the hardware is bound to be more accurate.

This backend is designed to work with any of the other backends that address a printer by IP address. In other words, printer device types that have a URI containing an IP address. Accsnmp works by wrapping that other backend and parsing the device URI to figure out what device to query. It has been tested extensively with the lpd backend, and also the socket and ipp backends.

Accounting is accomplished by storing each user's printing total in a series of flat text files, one per user. Very simple and all done in one self-contained accounting function. Rewrite to taste.

Example URIs: accsnmp:/lpd:// accsnmp:/socket:// accsnmp:/ipp://


This backend requires:

1) Perl 5.8.x

2) The Net::SNMP Perl module

3) A printer that supports the common printer MIB

Virtually every OS comes with Perl, and to install the Net::SNMP module, read up on using CPAN. You can test number three easily enough by using the snmpget command-line tool on a Linux box. At the top of the script you will find a couple of variables named *_oid. Run manual queries against your printers using the values of those variables like so:

PAGECOUNT: $ snmpget -v 1 -c 'public'

PRINTER STATUS $ snmpget -v 1 -c 'public'

If they return something other than a timeout or garbage, you should be set.


If you have gotten this far, the rest is easy. Download the script and copy it into the CUPS backends directory, which is typically /usr/lib/cups/backend. Once there, you have to send CUPS a SIGHUP for it to re-scan all the available backends:

 $ killall -HUP cupsd


 $ /etc/init.d/cups reload

A restart will work just as well, but there is no real need to stop and start the server daemon.

Now check the web interface. When creating or modifying printers, you should see an option for an "Accounted Printer (SNMP)" in the dropdown box of available devices.

Since CUPS 1.2 or 1.3, it seems that specific permissions must be set on the backend. Otherwise it will fail to appear in the dropdown list, or report "/usr/lib/cups/backend/accsnmp failed" in the logs. Matching the lpd backend in recent versions of CUPS:

 $ chown root:root /usr/lib/cups/backend/accsnmp
 $ chmod 700 /usr/lib/cups/backend/accsnmp

This backend will almost certainly not work on your system until you visit the next section about setup and configuration.


This script is designed to be very flexible and assumes a few things about your system which you may need or want to change. This is controlled by a series of variables at the top of the script. While a handful are to be understood as system globals to be set in the script, all variables in the \%conf hash, while possible to set in-script, can be overridden on a per-queue basis in the qconf configuration file.

The below variables are listed with their default setting and description.

System Global Variables

These variables are set in the script, and are therefore global in scope.

backend_dir = /usr/lib/cups/backend

The location of the CUPS backends on your system.

queues_conf = /etc/cups/queues.conf

The location of the accsnmp configuration file in "qconf" format (see below). If this file does not exist, accsnmp will use only the defaults in the script.

acc_printer_list = /etc/cups/accounted-printers.txt

Deprecated configuration file that merely marked certain queues as color printers to adjust page accounting. This file will still be referenced if it exists, but is due to be removed in a future release.

Per-Queue Variables

These variables have vanilla defaults in the script, but can be overridden on a per-queue basis in the qconf file if you so choose.

acc_dir => /var/log/cups/acc

This is the location of the directory containing the text files into which user printing totals are written by the accounting function provided within the script. Make sure this directory exists and is writable by the CUPS user:group (example lp:sys). When set to nothing, user totals are not kept.

acc_factor => 1

Multiply page counts by this number. Can be any whole number or decimal. If decimal, see acc_precision.

acc_log => ''

Normally accsnmp sends page log messages to the CUPS page_log. However, it can log elsewhere if this variable is set. When set to nothing, logs to standard page_log.

acc_precision => 0

Determines the number of decimal places to maintain when page counts are fractional numbers. Note that decimals may not print correctly in the standard page log.

backend_attempts => 0

Number of times accsnmp will retry the backend it is wrapping before giving up and exiting with error. In CUPS 1.1.x, backends try to print to a printer for several minutes before exiting with error and disabling the queue, while later CUPS behavior is determined by printer-error-policy. If set to 0, accsnmp will try forever.

check_job => 0

If set to 1, all printjob titles are checked against a list of baddies. This is not horribly safe, but can be useful as a quick measure to prevent certain jobs from printing. Salt the check_job function to taste. If set to 0, no checks are done.

check_user => 0

If set to 1, all printjob usernames are checked against a list of baddies. This is not horribly safe, but can be useful as a quick measure to prevent certain accounts from printing. Salt the check_user function to taste. If set to 0, no checks are done.

Note: As there is no authentication at this stage of printing, usernames are only as reliable as any authentication (or complete lack thereof) that is done in the frontend on the queue itself.

debug => 0

If set to 1, print debugging info to /tmp/$q.debug.

lock_alarm => 0

When locking is in use (see lock_file), determines how long in seconds accsnmp will try to establish a lock before exiting with error. If set to 0, accsnmp will try forever. See the section on locking.

lock_file => ''

When set to a writable file path, accsnmp will ensure no other accsnmp processes set to use this same lock file will print until the lock is released. When set to nothing, no locking is done. See the section on locking.

pagecount_oid => ''

The SNMP OID for the printer hardware page counter.

printerstatus_oid => ''

The SNMP OID for the printer status indicator.

save_job => 0

When set to 1, temporary files are saved in the CUPS temp directory for later examination.

snmp_alarm => 0

Determines how long in seconds accsnmp will keep trying snmp gets against the printer. When set to 0, accsnmp will try forever.

snmp_community => 'public'

The public, read-only SNMP community.


Variables above can be optionally overridden on a per-queue basis through an external configuration file.


The qconf file is formatted exactly like the printcap file, except that comments, blank lines, and whitespace between all elements are allowed. All variables above can be overridden on a per-queue basis. Location is determined by the queues_conf variable. If this file does not exist, variable values are simply those set in the script. Example file:

 # TEST1 has debugging, saving jobs, and factor four set
 # TEST2 is identical
 TEST2 | debug : save_jobs : acc_factor=4
 # Some other random configuration
 TEST3 | acc_factor=.49 : acc_precision=6
 TEST4 | acc_dir=/var/cupsacc/counts : acc_log=/var/cupsacc/fakelog


This is a deprecated configuration file that simply lists accounted queues, with a scheme for marking those that are color. Location is determined by the acc_printer_list variable. If this file exists, containing the queue in question, and the queue is marked color, the acc_factor is set to 3. Example file:



Because accsnmp queries the hardware to determine the pagecount for a job, accsnmp takes great pains to ensure that jobs are submitted serially to the printer, returning only when the wrapped backend is finished and any accounting chores are done. No extra locking is necessary when just one accsnmp queue prints to just one printer.

When multiple accsnmp queues print to one printer, external locking must be done to ensure all queues coordinate to submit jobs serially. Otherwise, a previous job may still be in progress, and SNMP queries for count and status could be thrown off on subsequent jobs, practically ensuring accounting unreliability.

With accsnmp-1.03, the option to specify a lock_file exists. As an example, assume two queues TEST1 and TEST2. If we edit the queues_conf file as such:

 TEST1 | lock_file=/etc/cups/locks/TEST
 TEST2 | lock_file=/etc/cups/locks/TEST

Both queues will abide by the locks set on the TEST file at the given path, ensuring that jobs are printed serially, and print totals are accounted correctly. A lock_alarm can also be set, which will cause accsnmp to exit after the given number of seconds if the file cannot be locked.

Note: The chosen file path needs to be writable by the backend, often root, and will be overwritten with each print job, so take care not to point it at anything important.

Note: Lock alarms are not of much use when using the retry-job printer error policy, since the job will remain queued, and CUPS will simply try again.

Note: You probably only want to set this in the qconf file rather than the script unless you really do want only one system-wide accsnmp job to print at a time. This is intended for queues pointing to the same printer to avoid accounting overflows.


You may wish to simply setup a printer and try to fire away test pages, looking at the CUPS log to see what problems exist. Set it up initially with a device URI like this:


Check the logs. Try enabling debugging within the script. If it all works, enjoy. Good luck!


1) With a wrapper inserted in this way, stopping and starting the queue while jobs are backed up will requeue the first job and leave an orphan wrapped backend job sitting on the system waiting to print. I know of no way around this and it hasn't been much of an issue in our experience.

2) Currently this backend may not do duplex accounting the way you wish. It accounts each face of a duplex sheet as one page of printing, so a two-sided printout will count for two pages. I am aware of a potential fix courtesy of a gentleman named Werner Maes, but I have not implemented it because we find that the reality of duplex printing is that it costs just as much or more per sheet than one-sided printing given the extra mechanical complexity and wear-and-tear involved. With the new qconf file, this is more feasible.

3) I have run across printers now and then whose status does not change during printing or changes unreliably. This is a pain because the backend will not loop correctly checking the printer status, or will report that something printed when in fact nothing has. On most of the HPs, Canons, and Epsons I have had access to, only a couple printers were a problem. And usually these issues only presented on edge cases (printing to a printer that is jammed or out of paper)


accsnmp 1.04 (20120419)

- Integrated perldoc.

accsnmp 1.03 (20100413)

- Tested heavily against cups-1.2.x, lightly against cups-1.3.x, and particularly against cups-1.4.x.

- All script variables can be overridden on a per-queue basis using new "qconf" configuration file format. Most variables renamed in a more consistent manner to fit with this scheme.

- New config variables acc_factor and acc_precision, to allow for custom per-queue page accounting, and even fractional values.

- New config variables lock_file and lock_alarm, to allow for multiple accsnmp queues to safely share one printer.

- URI syntax check loosened to accept accsnmp:/<original uri> as well as accsnmp://<original uri>, since the latter will no longer work with cups-1.4.x. Reported by Gilles Sadowski.

- Page log output now prints "npages accsnmp" rather than "username npages" since the latter will no longer work with cups-1.4.x, and reprinting the username is redundant anyway.

- Signals warn and die are now trapped and send messages to cups error log.

- More debug output.

- Fixed a logic error when backend attempts = 1 that caused the backend to bail out with an error but still print.

- Fixed comment errors pointed out by Marcus Lauer.

- James Nau pointed out that everywhere I was doing a forceExit, I should have just exited with return code 5, which means cancel the job. RTFM the backend docs.

- Tempfile renamed to indicate it was created by accsnmp.

- The snmp_get function now checks to see if there was actually a non-null value returned. Some devices are crap.

accsnmp 1.02 (20070124)

- The addition of a changelog :)

- The accounting function will now set the username to "NO_USER" if no username is given. Otherwise, accsnmp will print, but not log the job. This is moot if $USER_BLACKLIST is on (in which case printing will be blocked with no username).

- More comment cleanup.

- Main body eval/alarm loop removed and put into snmpGet function to control all snmp gets. Controlled by $SNMP_ALARM variable, where accsnmp will try to reach the printer on each snmp get for $SNMP_ALARM seconds. 0 for infinite.

- Fixed bug where ugly Net::SNMP hash reference was printed instead of the error itself. RTFM.

- The URI is now handled much more simply, and there is now a syntax check of the URI itself as seen in the beh backend.

- Fixed bug reported by Till Kamppeter: copies needs to be set to 1 when a filter/backend does not see a printfile argument. RTFM.

- Now only spools a temp file if job arrives on STDIN, or $SAVE_JOBS = 1. Saves us from pointless spooling as we already have a print file to work with.

- The backend we are wrapping is now also governed by a while loop. Controlled by $BACKEND_ATTEMPTS variable. Setting to 0 means infinite retries.

- The DEVICE_URI environment variable was not reset before handoff to real backend. Affected ipp backend at least. Found by Sarosh Jamal.

accsnmp 1.01 (20050606)

- 1.00 with very minor documentation cleanup.

accsnmp 1.00 (20050224)

- Initial release.