Pracc for CUPS

Pracc for CUPS is a backend for CUPS, the new Unix printing system,
is part of the pracc system, and has these features:

Contents:
Usage, Configuration, Accounting Logic, Account Types, Miscellaneous, Author.

Usage

CUPS uses "backends" to communicate with printers. Backends are ordinary applications but must adhere to the interface described in the backend(7) manual page that comes with CUPS. Depending on how a CUPS backend is invoked, it must operate in device discovery mode or in normal mode.

Device Discovery

If no command line arguments are specified, CUPS expects backends to list supported devices to standard output. This is called device discovery. The pracc backends writes one line of this form:

network pracc "Unknown" "AppSocket/JetDirect w/Acct"

This informs CUPS about the supported device class (network) and the URI scheme (which is the backend's file name, "pracc" by default).

Note: The filename of a backend must correspond with the URI scheme used in device discovery, otherwise CUPS will not use the backend. The pracc for CUPS backend uses its own file name when advertising its services and can therefore be named arbitrarily. It is recommended to name it pracc, yielding device URIs of the form pracc://...

Normal Operation

For normal printing, the backend is invoked with 6 or 7 command line arguments:

printer-uri0 job-id1 user2 title3 copies4 options5 [file6]

The printer-uri is used for connecting to the printer. The user is taken as the name of the account to charge, unless the job-billing attribute is set to a valid account name (see Account Types below). The title is copied to the accounting record to help the user identify jobs.

Note that printer-uri is the zeroth argument, that is, the program name. With current CUPS versions, the printer-uri can also be communicated to the backend using the DEVICE_URI environment variable. The pracc backend uses the environment variable, if defined, otherwise its zeroth argument.

The pracc backend performs these steps in order:

  1. If accounting: check if access to the claimed account can be granted; if there is no account, use the default account.
  2. If accounting: check if the account has enough credit and quit if there are insufficient funds.
  3. Connect to the printer device.
  4. Send the print job, either from standard input (if there are 6 arguments) or from the named file (if there are 7 arguments). If accounting is enabled, the print job will be preceded and succeeded by special codes to elicit the printer's page count. While and after sending the job, read and parse potential answers from the printer until there is reason to assume the job has finished printing.
  5. Close connection to printer device.
  6. If accounting: append a record to the selected account.
  7. Optionally, append a record to the pagecount log.

The pracc backend appends a debit record to the account if the number of pages printed could be determined, otherwise, an error record is appended.

Job Scanning (aka pre-counting)

Optionally, the pracc backend can send the printjob through a program that tries to figure out how many pages the printjob would produce when printed. A typical program for that job would be Pykota's pkpgcounter. The job scanning program must read a print job from its standard input and write the number of pages to its standard output. Error messages may be sent to standard error, which is inherited from the backend, so they should end up in the CUPS error log. The job scanning program must return a status of 0 if (and only if) it was successful.

Queue Stopping

Backends can stop the queue by using a special exit value. The pracc backend does this in the following cases to alert the administrator of a serious problem:

In all other error situations, the pracc backend returns the value CUPS_BACKEND_FAILED, which causes the CUPS scheduler to react according to the printer's error policy.

Pagecount Logging

If the file PRACCPCLOG (as defined in pracc.h at compile-time) exists, then a pagecount record will be appended after printing the job. The pagecount record is a line of the form

@timestamp pagecount printername

where timestamp is in TAI64 format and pagecount is the value of the printer's pagecount register or -1 if unknown. The information accumulated in the pagecount logfile is useful for statistical purposes.

Debug Logging

It seems the CUPS scheduler does not route all messages from backends to its error_log file; in particular, INFO lines are dropped. More recent versions of CUPS may behave differently.

The pracc backend writes to its own private log file, /var/log/cups/pracc, provided that it exists and is writable to the pracc backend. At present, the path of the debugging log file is hard-coded. The file is silently truncated to zero bytes if its size exceeds 1000000 bytes.

Permissions

In order to get access to the pracc files, the backend must be run as root or with an appropriate group. According to the backend(7) man page, the CUPS scheduler executes backends as root if they are not world-writable, and as an unprivileged user (typically lp) otherwise.

Tests showed that the non-world-executable-means-root thing did not work. A look at the source of CUPS 1.2.9 showed that group and other must no permissions at all on the backend file for CUPS to execute it as root.

By default, the pracc for CUPS backend is installed as root:PRACCGROUP with mode 700, where PRACCGROUP is a compile-time configuration setting.

Configuration

The pracc backend is configured through parameters in the device URI. By default, that is, without any parameters, the pracc backend behaves like the socket backend that comes with CUPS.

ParameterTypeDefaultDescription
acct off | PS | PJL | job off Accounting method
wait0 positive integer 20 (PS) or 300 (PJL) Initial timeout (see text)
wait1 positive integer 10 (PS) or 120 (PJL) Subsequent timeout (see text)
jobscan program none Precounting program
pagecost integer 0 Cost per page, 0 = free printing

The acct parameter specifies the method to be used for counting the number of pages effectively printed: off means no counting (the default), PS uses PostScript commands, PJL uses PJL commands, SNMP uses SNMP datagrams to get the pagecount, and job just uses the page number from the job scanner. Note: SNMP is not yet implemented.

The parameters wait0 and wait1 specify timeouts and/or delays in seconds, depending on the accounting method. With acct=PS, wait0 is the time to pause after sending the job but before reading the pagecount for the first time, and wait1 is the time to pause between subsequent pagecount readings. With acct=PJL, wait0 is used as the first select(2) timeout and wait1 is used as the timeout for subsequent select(2) calls (the select calls wait till the printer sends some information back but no longer than the specified timeout). Depending on the printer and the job, some tuning may be necessary.

The jobscan parameter names a program that counts pages in the print job. The result of this job scanning will be used to determine if the user has enough printing credits for printing the job. If the printer does not return any information on the number of pages printed, then this is also so only information for accounting. Note that some people use the term precounting for this job scanning.

The number of pages printed is multiplied with the value of the pagecost parameter and the resulting figure is used for accounting. The default value of 0 implies free printing.

Disclaimer: page counting is a delicate matter! Be sure to read counting.html, which contains background information on page counting, including a description of the PS and PJL methods.

Accounting Logic

These variables are involved in accounting:

VarSourceDescriptionDefault
c device URI cost per page 0 = free printing
m jobscan #pages in print job -1 = unknown
n printer #pages actually printed -1 = unknown
b account account's balance always known
l account account's limit (keep b>l) always known

The accounting procedure is as follows:

Init: Read the account file and set d and l accordingly.

Check: if (m>0) subtract m×c from b.
If (b<l) report "insufficient funds" and quit.

Send: If the jobscan parameter specifies a job scanning program, feed the print job through this program and read the value m. Send the print job to the printer.

Sync: Wait for the "true-end-of-job" and try determining n.

Accounting: Set d=c×f(m,n), the amount to debit;
set type=(d≥0) ? debit : error, the record type;
append an accounting record with type=type and amount=d.

Both m and n are determined by heuristics and may be wrong or unknown. Pracc estimates the pages actually printed based on m and n:

if (m < 0 && n < 0) return -1; // unknown
if (m < 0) return n;
if (n < 0) return m;
if (m < n) return n;
return (m+n)/2;

Account Types

Pracc supports personal accounts and group accounts.

Personal accounts are named after Unix user names.
They are accessible only to the user whose name equals the account's name.

Group accounts are named after Unix groups.
They are accessible only to users in the named Unix group.

If the job-billing attribute is set and names a valid group account, the named group accont will be billed. Otherwise, the user's personal account will be billed.

Personal and group accounts share the same account name space! It is the administrator's responsibility to avoid name clashes between personal and group accounts! Failure to do so makes this scenario possible: If there are users x and y, and also a group x of which y is a member, then if user y asks to charge a print job to group account x, it will be charged to the personal account of x.

The default account is used whenever a user tries to bill an account that does not exist; in particular, it is used for all users that do not have an account.

Miscellaneous

Why doesn't the backend create missing pracc files?

Because the backend does not have easy access to the required information, namely, the initial balance and the initial limit of the account to be created.

What is the default account useful for?

Use it to determine if printing shall be allowed or denied by default.

What are the rules to access pracc files?

Tools that run with real or effective uid PRACCOWNER or real or effective gid PRACCGROUP must enforce these rules:

  1. each user can access only his/her own personal account
  2. each user can access group account x only if he/she belongs to group x
  3. users must not be able to tamper with their pracc files

The pracc backend implements these rules in the checkaccess() function that invokes the praccGrant() function in the pracc API. It relies on the CUPS scheduler to properly authenticate the user parameter that is passed to backends as argv[2].

What are the backend's exit codes?

The CUPS scheduler interprets backend exit codes as follows:

0CUPS_BACKEND_OKprint job successfully transmitted
1CUPS_BACKEND_FAILEDscheduler reacts according to error-policy
2CUPS_BACKEND_AUTH_REQUIREDno or invalid authentication
3CUPS_BACKEND_HOLDscheduler will hold the job
4CUPS_BACKEND_STOPscheduler will stop the queue
5CUPS_BACKENd_CANCELscheduler will cancel the job

The pracc backend makes use of most of these codes.

Author

Pracc was written by Urs-Jakob Rüetschi while working at PZM and PHZ Luzern.

Pracc is free software under the terms of the GNU General Public License.