<!--
- "$Id: api-filter.shtml 6649 2007-07-11 21:46:42Z mike $"
+ "$Id: api-filter.shtml 7677 2008-06-19 23:22:19Z mike $"
Filter and backend programming introduction for the Common UNIX Printing
System (CUPS).
<h2 class='title'><a name="OVERVIEW">Overview</a></h2>
-<p>Filters, printer drivers, port monitors, and backends use a common interface
-for processing print jobs and communicating status information to the scheduler.
-Each filter is run with a standard set of command-line arguments:<p>
+<p>Filters (which include printer drivers and port monitors) and backends
+are used to convert job files to a printable format and send that data to the
+printer itself. All of these programs use a common interface for processing
+print jobs and communicating status information to the scheduler. Each is run
+with a standard set of command-line arguments:<p>
<dl class="code">
<dd>The options that were provided when the job was submitted</dd>
<dt>argv[6]</dt>
- <dd>The file to print (first filter only)</dd>
+ <dd>The file to print (first program only)</dd>
</dl>
<p>The scheduler runs one or more of these programs to print any given job. The
<h3><a name="ENVIRONMENT">Environment Variables</a></h3>
-<p>The following environment variables are defined by the printing system:</p>
+<p>The following environment variables are defined by the printing system
+when running print filters and backends:</p>
<dl class="code">
application/postscript).</dd>
<dt>CUPS_CACHEDIR</dt>
- <dd>The directory where cache files can be stored.</dd>
+ <dd>The directory where cache files can be stored. Cache files can be
+ used to retain information between jobs or files in a job.</dd>
<dt>CUPS_DATADIR</dt>
- <dd>The directory where data files can be found.</dd>
+ <dd>The directory where (read-only) CUPS data files can be found.</dd>
<dt>CUPS_SERVERROOT</dt>
<dd>The root directory of the server.</dd>
file for this printer.</dd>
<dt>PRINTER</dt>
- <dd>The name of the printer.</dd>
+ <dd>The queue name of the class or printer.</dd>
<dt>RIP_CACHE</dt>
<dd>The recommended amount of memory to use for Raster Image
Processors (RIPs).</dd>
+ <dt>TMPDIR</dt>
+ <dd>The directory where temporary files should be created.</dd>
+
</dl>
<h3><a name="MESSAGES">Communicating with the Scheduler</a></h3>
-<p>Filters and backends communicate wih the scheduler by writing messages
-to the standard error file. For example, the following code sets the current
-printer state message to "Printing page 5":</p>
+<p>Filters and backends communicate with the scheduler by writing messages
+to the standard error file. The scheduler reads messages from all filters in
+a job and processes the message based on its prefix. For example, the following
+code sets the current printer state message to "Printing page 5":</p>
<pre class="example">
int page = 5;
<dt>ATTR: attribute=value [attribute=value]</dt>
<dd>Sets the named printer or job attribute(s). Typically this is used
to set the <code>marker-colors</code>, <code>marker-levels</code>,
- <code>marker-names</code>, <code>marker-types</code>,
- <code>printer-alert</code>, and <code>printer-alert-description</code>
- printer attributes.</dd>
+ <code>marker-message</code>, <code>marker-names</code>,
+ <code>marker-types</code>, <code>printer-alert</code>, and
+ <code>printer-alert-description</code> printer attributes. Standard
+ <code>marker-types</code> values are listed in <a href='#TABLE1'>Table
+ 1</a>.</dd>
<dt>CRIT: message</dt>
<dd>Sets the printer-state-message attribute and adds the specified
<dt>ERROR: message</dt>
<dd>Sets the printer-state-message attribute and adds the specified
- message to the current error log file using the "error" log level.</dd>
+ message to the current error log file using the "error" log level.
+ Use "ERROR:" messages for non-persistent processing errors.</dd>
<dt>INFO: message</dt>
<dd>Sets the printer-state-message attribute. If the current log level
#-copies to the job-media-sheets-completed attribute. The second
form sets the job-media-sheets-completed attribute to #-pages.</dd>
+ <dt>PPD: keyword=value [keyword=value ...]</dt>
+ <dd>Changes or adds keywords to the printer's PPD file. Typically
+ this is used to update installable options or default media settings
+ based on the printer configuration.</dd>
+
<dt>STATE: printer-state-reason [printer-state-reason ...]</dt>
<dt>STATE: + printer-state-reason [printer-state-reason ...]</dt>
<dt>STATE: - printer-state-reason [printer-state-reason ...]</dt>
<dd>Sets, adds, or removes printer-state-reason keywords to the
- current queue. Typically this is used to indicate media, ink, and
- toner conditions on a printer.</dd>
+ current queue. Typically this is used to indicate persistent media,
+ ink, toner, and configuration conditions or errors on a printer.
+ <a href='#TABLE2'>Table 2</a> lists the standard state keywords -
+ use vendor-prefixed ("com.acme.foo") keywords for custom states.</dd>
<dt>WARNING: message</dt>
<dd>Sets the printer-state-message attribute and adds the specified
<p>Messages without one of these prefixes are treated as if they began with
the "DEBUG:" prefix string.</p>
-<h3><a name="COMMUNICATING">Communicating with the Backend</a></h3>
+
+<div class='table'><table width='80%' summary='Table 1: Standard marker-types Values'>
+<caption>Table 1: <a name='TABLE1'>Standard marker-types Values</a></caption>
+<thead>
+<tr>
+ <th>marker-type</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>developer</td>
+ <td>Developer unit</td>
+</tr>
+<tr>
+ <td>fuser</td>
+ <td>Fuser unit</td>
+</tr>
+<tr>
+ <td>fuserCleaningPad</td>
+ <td>Fuser cleaning pad</td>
+</tr>
+<tr>
+ <td>fuserOil</td>
+ <td>Fuser oil</td>
+</tr>
+<tr>
+ <td>ink</td>
+ <td>Ink supply</td>
+</tr>
+<tr>
+ <td>opc</td>
+ <td>Photo conductor</td>
+</tr>
+<tr>
+ <td>solidWax</td>
+ <td>Wax supply</td>
+</tr>
+<tr>
+ <td>staples</td>
+ <td>Staple supply</td>
+</tr>
+<tr>
+ <td>toner</td>
+ <td>Toner supply</td>
+</tr>
+<tr>
+ <td>transferUnit</td>
+ <td>Transfer unit</td>
+</tr>
+<tr>
+ <td>wasteInk</td>
+ <td>Waste ink tank</td>
+</tr>
+<tr>
+ <td>wasteToner</td>
+ <td>Waste toner tank</td>
+</tr>
+<tr>
+ <td>wasteWax</td>
+ <td>Waste wax tank</td>
+</tr>
+</tbody>
+</table></div>
+
+<br>
+
+<div class='table'><table width='80%' summary='Table 2: Standard State Keywords'>
+<caption>Table 2: <a name='TABLE2'>Standard State Keywords</a></caption>
+<thead>
+<tr>
+ <th>Keyword</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>connecting-to-device</td>
+ <td>Connecting to printer but not printing yet</td>
+</tr>
+<tr>
+ <td>cover-open</td>
+ <td>A cover is open on the printer</td>
+</tr>
+<tr>
+ <td>input-tray-missing</td>
+ <td>An input tray is missing from the printer</td>
+</tr>
+<tr>
+ <td>marker-supply-empty</td>
+ <td>Out of ink</td>
+</tr>
+<tr>
+ <td>marker-supply-low</td>
+ <td>Low on ink</td>
+</tr>
+<tr>
+ <td>marker-waste-almost-full</td>
+ <td>Waste tank almost full</td>
+</tr>
+<tr>
+ <td>marker-waste-full</td>
+ <td>Waste tank full</td>
+</tr>
+<tr>
+ <td>media-empty</td>
+ <td>Out of media</td>
+</tr>
+<tr>
+ <td>media-jam</td>
+ <td>Media is jammed in the printer</td>
+</tr>
+<tr>
+ <td>media-low</td>
+ <td>Low on media</td>
+</tr>
+<tr>
+ <td>paused</td>
+ <td>Stop the printer</td>
+</tr>
+<tr>
+ <td>timed-out</td>
+ <td>Unable to connect to printer</td>
+</tr>
+<tr>
+ <td>toner-empty</td>
+ <td>Out of toner</td>
+</tr>
+<tr>
+ <td>toner-low</td>
+ <td>Low on toner</td>
+</tr>
+</tbody>
+</table></div>
+
+<h3><a name="COMMUNICATING_BACKEND">Communicating with the Backend</a></h3>
<p>Filters can communicate with the backend via the
<a href="#cupsBackChannelRead"><code>cupsBackChannelRead</code></a> and
bytes = cupsBackChannelRead(buffer, sizeof(buffer), 0.0);
</pre>
-The
+<p>Filters can also use <code>select()</code> or <code>poll()</code> on the
+back-channel file descriptor (3 or <code>CUPS_BC_FD</code>) to read data only
+when it is available.</p>
+
+<p>The
<a href="#cupsSideChannelDoRequest"><code>cupsSideChannelDoRequest</code></a>
function allows you to get out-of-band status information and do synchronization
with the device. For example, the following code gets the current IEEE-1284
int datalen;
<a href="#cups_sc_status_t">cups_sc_status_t</a> status;
-/* Tell cupsSideChannelDoRequest() how big our buffer is, less 1 byte for nul-termination... */
+/* Tell cupsSideChannelDoRequest() how big our buffer is, less 1 byte for
+ nul-termination... */
datalen = sizeof(data) - 1;
/* Get the IEEE-1284 device ID, waiting for up to 1 second */
data[0] = '\0';
</pre>
+<h3><a name="COMMUNICATING_FILTER">Communicating with Filters</a></h3>
+
<p>Backends communicate with filters using the reciprocal functions
<a href="#cupsBackChannelWrite"><code>cupsBackChannelWrite</code></a>,
<a href="#cupsSideChannelRead"><code>cupsSideChannelRead</code></a>, and
char buffer[8192];
ssize_t bytes;
+/* Obtain data from printer/device */
+...
+
/* Use a timeout of 1.0 seconds to give filters a chance to read */
cupsBackChannelWrite(buffer, bytes, 1.0);
</pre>
indefinitely for commands using a <code>timeout</code> of -1.0 (probably in a
separate thread for that purpose), or use <code>select</code> or
<code>poll</code> on the <code>CUPS_SC_FD</code> file descriptor (4) to handle
-input and output on several file descriptors at the same time. Backends can pass
-<code>NULL</code> for the <code>data</code> and <code>datalen</code> parameters
-since none of the commands sent by upstream filters contain any data at this
-time.</p>
+input and output on several file descriptors at the same time.</p>
<p>Once a command is processed, the backend uses the
<a href="#cupsSideChannelWrite"><code>cupsSideChannelWrite</code></a> function
<a href="#cups_sc_command_t">cups_sc_command_t</a> command;
<a href="#cups_sc_status_t">cups_sc_status_t</a> status;
+char data[2048];
+int datalen = sizeof(data);
/* Poll for a command... */
-if (!<a href="#cupsSideChannelRead">cupsSideChannelRead</a>(&command, &status, NULL, NULL, 0.0))
+if (!<a href="#cupsSideChannelRead">cupsSideChannelRead</a>(&command, &status, data, &datalen, 0.0))
{
- char data[2048];
- int datalen;
-
switch (command)
{
- /* handle supported commands, file data/datalen/status with values as needed */
+ /* handle supported commands, fill data/datalen/status with values as needed */
default :
status = CUPS_SC_STATUS_NOT_IMPLEMENTED;
every printer has a <em>community</em> name associated with it. OIDs can be
queried directly or by "walking" over a range of OIDs with a common prefix.</p>
-<p>The CUPS SNMP functions provide a simple API for querying network printers.
-Queries are made using a datagram socket that is created using
-<a href="#cupsSNMPOpen"><code>cupsSNMPOpen</code></a> and destroyed using
-<a href="#cupsSNMPClose"><code>cupsSNMPClose</code></a>:</p>
-
-<pre class="example">
-#include <cups/snmp.h>
-
-int snmp = <a href="#cupsSNMPOpen">cupsSNMPOpen</a>(AF_INET);
+<p>The two CUPS SNMP functions provide a simple API for querying network
+printers through the side-channel interface. Each accepts a string containing
+an OID like ".1.3.6.1.2.1.43.10.2.1.4.1.1" (the standard page counter OID)
+along with a timeout for the query.</p>
-/* do some queries */
-
-<a href="#cupsSNMPClose">cupsSNMPClose</a>(snmp);
-</pre>
-
-<p>OIDs are simple C arrays of integers, terminated by a value of -1. For
-example, the page counter OID .1.3.6.1.2.1.43.10.2.1.4.1.1 would be:</p>
-
-<pre class="example">
-int page_counter_oid[] = { 1, 3, 6, 1, 2, 1, 43, 10, 2, 1, 4, 1, 1, -1 };
-</pre>
-
-<p>You send a query using
-<a href="#cupsSNMPWrite"><code>cupsSNMPWrite</code></a> and read the value back
-using <a href="#cupsSNMPRead"><code>cupsSNMPRead</code></a>. The value is read
-into a structure called <a href="#cups_snmp_t"><code>cups_snmp_t</code></a>:</p>
+<p>The <a href="#cupsSideChannelSNMPGet"><code>cupsSideChannelSNMPGet</code></a>
+function queries a single OID and returns the value as a string in a buffer
+you supply:</p>
<pre class="example">
-#include <cups/snmp.h>
+#include <cups/sidechannel.h>
-int page_counter_oid[] = { 1, 3, 6, 1, 2, 1, 43, 10, 2, 1, 4, 1, 1, -1 };
-http_addrlist_t *host = httpAddrGetList("myprinter", AF_UNSPEC, "161");
-int snmp = <a href="#cupsSNMPOpen">cupsSNMPOpen</a>(host->addr.addr.sa_family);
-<a href="#cups_snmp_t">cups_snmp_t</a> packet;
+char data[512];
+int datalen = sizeof(data);
-<a href="#cupsSNMPWrite">cupsSNMPWrite</a>(snmp, &(host->addr), CUPS_SNMP_VERSION_1,
- <a href="#cupsSNMPDefaultCommunity">cupsSNMPDefaultCommunity</a>(), CUPS_ASN1_GET_REQUEST, 1,
- page_counter_oid);
-if (<a href="#cupsSNMPRead">cupsSNMPRead</a>(snmp, &packet, 5000))
+if (<a href="#cupsSideChannelSNMPGet">cupsSideChannelSNMPGet</a>(".1.3.6.1.2.1.43.10.2.1.4.1.1", data, &datalen, 5.0)
+ == CUPS_SC_STATUS_OK)
{
/* Do something with the value */
- printf("Page counter is: %d\n", packet.object_value.integer);
+ printf("Page counter is: %s\n", data);
}
</pre>
-<p>The <a href="#cupsSNMPWalk"><code>cupsSNMPWalk</code></a> function allows you
-to query a whole group of OIDs, calling a function of your choice for each OID
-that is found:</p>
+<p>The
+<a href="#cupsSideChannelSNMPWalk"><code>cupsSideChannelSNMPWalk</code></a>
+function allows you to query a whole group of OIDs, calling a function of your
+choice for each OID that is found:</p>
<pre class="example">
-#include <cups/snmp.h>
+#include <cups/sidechannel.h>
void
-my_callback(<a href="#cups_snmp_t">cups_snmp_t</a> *packet, void *data)
+my_callback(const char *oid, const char *data, int datalen, void *context)
{
/* Do something with the value */
+ printf("%s=%s\n", oid, data);
}
-int printer_mib_oid[] = { 1, 3, 6, 1, 2, 1, 43, -1 };
-http_addrlist_t *host = httpAddrGetList("myprinter", AF_UNSPEC, "161");
-int snmp = <a href="#cupsSNMPOpen">cupsSNMPOpen</a>(host->addr.addr.sa_family);
+...
+
void *my_data;
-<a href="#cupsSNMPWalk">cupsSNMPWalk</a>(snmp, &(host->addr), CUPS_SNMP_VERSION_1,
- <a href="#cupsSNMPDefaultCommunity">cupsSNMPDefaultCommunity</a>(), printer_mib_oid, my_callback, my_data);
+<a href="#cupsSideChannelSNMPWalk">cupsSNMPSideChannelWalk</a>(".1.3.6.1.2.1.43", 5.0, my_callback, my_data);
</pre>