]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - cups/api-filter.shtml
Merge changes from CUPS 1.4svn-r8628.
[thirdparty/cups.git] / cups / api-filter.shtml
index f43a8d140b9c670cdab10c8b32f3c57d1f40a2aa..49bf24be3657c7398f83f095cc2c2b9efa4f9dd1 100644 (file)
 <!--
-  "$Id$"
+  "$Id: api-filter.shtml 7677 2008-06-19 23:22:19Z mike $"
 
-  Filter and backend API introduction for the Common UNIX Printing System (CUPS).
+  Filter and backend programming introduction for the Common UNIX Printing
+  System (CUPS).
 
-  Copyright 1997-2005 by Easy Software Products.
+  Copyright 2007-2009 by Apple Inc.
+  Copyright 1997-2006 by Easy Software Products, all rights reserved.
 
   These coded instructions, statements, and computer programs are the
-  property of Easy Software Products and are protected by Federal
-  copyright law.  Distribution and use rights are outlined in the file
-  "LICENSE.txt" which should have been included with this file.  If this
-  file is missing or damaged please contact Easy Software Products
-  at:
-
-      Attn: CUPS Licensing Information
-      Easy Software Products
-      44141 Airport View Drive, Suite 204
-      Hollywood, Maryland 20636 USA
-
-      Voice: (301) 373-9600
-      EMail: cups-info@cups.org
-       WWW: http://www.cups.org
+  property of Apple Inc. and are protected by Federal copyright
+  law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+  which should have been included with this file.  If this file is
+  file is missing or damaged, see the license at "http://www.cups.org/".
 -->
 
-<h2 class='title'>Introduction</h2>
+<h2 class='title'><a name="OVERVIEW">Overview</a></h2>
 
-<p>The CUPS filter and backend APIs provide...</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>
 
-<h2 class='title'>General Usage</h2>
+<dl class="code">
 
-<p>The <var>&lt;cups/backend.h&gt;</var> and
-<var>&lt;cups/cups.h&gt;</var> header files must be included to
-use the <tt>CUPS_BACKEND_</tt> constants and
-<tt>cupsBackchannel</tt> functions, respectively.</p>
+       <dt>argv[1]</dt>
+       <dd>The job ID</dd>
 
-<p>Programs using these functions must be linked to the CUPS
-library: <var>libcups.a</var>, <var>libcups.so.2</var>,
-<var>libcups.2.dylib</var>, <var>libcups_s.a</var>, or
-<var>libcups2.lib</var> depending on the platform. The following
-command compiles <var>myprogram.c</var> using GCC and the CUPS
-library:</p>
+       <dt>argv[2]</dt>
+       <dd>The user printing the job</dd>
 
-<pre class='command'>
-<kbd>gcc -o myprogram myprogram.c -lcups</kbd>
+       <dt>argv[3]</dt>
+       <dd>The job name/title</dd>
+
+       <dt>argv[4]</dt>
+       <dd>The number of copies to print</dd>
+
+       <dt>argv[5]</dt>
+       <dd>The options that were provided when the job was submitted</dd>
+
+       <dt>argv[6]</dt>
+       <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
+first filter reads from the print file and writes to the standard output, while
+the remaining filters read from the standard input and write to the standard
+output. The backend is the last filter in the chain and writes to the
+device.</p>
+
+<p>Filters are always run as a non-privileged user, typically "lp", with no
+connection to the user's desktop. Backends are run either as a non-privileged
+user or as root if the file permissions do not allow user or group execution.
+The <a href="#PERMISSIONS">file permissions</a> section talks about this in
+more detail.</p>
+
+<h3><a name="SECURITY">Security Considerations</a></h3>
+
+<p>It is always important to use security programming practices. Filters and
+most backends are run as a non-priviledged user, so the major security
+consideration is resource utilization - filters should not depend on unlimited
+amounts of CPU, memory, or disk space, and should protect against conditions
+that could lead to excess usage of any resource like infinite loops and
+unbounded recursion. In addition, filters must <em>never</em> allow the user to
+specify an arbitrary file path to a separator page, template, or other file
+used by the filter since that can lead to an unauthorized disclosure of
+information. <em>Always</em> treat input as suspect and validate it!</p>
+
+<p>If you are developing a backend that runs as root , make sure to check for
+potential buffer overflows, integer under/overflow conditions, and file
+accesses since these can lead to privilege escalations. When writing files,
+always validate the file path and <em>never</em> allow a user to determine
+where to store a file.</p>
+
+<blockquote><b>Note:</b>
+
+<p><em>Never</em> write files to a user's home directory. Aside from the
+security implications, CUPS is a network print service and as such the network
+user may not be the same as the local user and/or there may not be a local home
+directory to write to.</p>
+
+<p>In addition, some operating systems provide additional security mechanisms
+that further limit file system access, even for backends running as root. On
+Mac OS X, for example, no backend may write to a user's home directory.</p>
+</blockquote>
+
+<h3><a name="PERMISSIONS">File Permissions</a></h3>
+
+<p>For security reasons, CUPS will only run filters and backends that are owned
+by root and do not have world write permissions. The recommended permissions for
+filters and backends are 0555 - read and execute but no write. Backends that
+must run as root should use permissions of 0500 - read and execute by root, no
+access for other users. Write permissions can be enabled for the root user
+only.</p>
+
+<p>To avoid a warning message, the directory containing your filter(s) must also
+be owned by root and have world write disabled - permissions of 0755 or 0555 are
+strongly encouraged.</p>
+
+<h3><a name="TEMPFILES">Temporary Files</a></h3>
+
+<p>Temporary files should be created in the directory specified by the
+"TMPDIR" environment variable. The
+<a href="#cupsTempFile2"><code>cupsTempFile2</code></a> function can be
+used to safely create temporary files in this directory.</p>
+
+<h3><a name="COPIES">Copy Generation</a></h3>
+
+<p>The <code>argv[4]</code> argument specifies the number of copies to produce
+of the input file. In general, you should only generate copies if the
+<em>filename</em> argument is supplied. The only exception to this are
+filters that produce device-independent PostScript output, since the PostScript
+filter <var>pstops</var> is responsible for generating copies of PostScript
+files.</p>
+
+<h3><a name="EXITCODES">Exit Codes</a></h3>
+
+<p>Filters must exit with status 0 when they successfully generate print data
+or 1 when they encounter an error. Backends can return any of the
+<a href="#cups_backend_t"><code>cups_backend_t</code></a> constants.</p>
+
+<h3><a name="ENVIRONMENT">Environment Variables</a></h3>
+
+<p>The following environment variables are defined by the printing system
+when running print filters and backends:</p>
+
+<dl class="code">
+
+       <dt>APPLE_LANGUAGES</dt>
+       <dd>The Apple language identifier associated with the job
+       (Mac OS X only).</dd>
+
+       <dt>CHARSET</dt>
+       <dd>The job character set, typically "utf-8".</dd>
+
+       <dt>CLASS</dt>
+       <dd>When a job is submitted to a printer class, contains the name of
+       the destination printer class. Otherwise this environment
+       variable will not be set.</dd>
+
+       <dt>CONTENT_TYPE</dt>
+       <dd>The MIME type associated with the file (e.g.
+       application/postscript).</dd>
+
+       <dt>CUPS_CACHEDIR</dt>
+       <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 (read-only) CUPS data files can be found.</dd>
+
+       <dt>CUPS_FILETYPE</dt>
+       <dd>The type of file being printed: "job-sheet" for a banner page and
+       "document" for a regular print file.</dd>
+
+       <dt>CUPS_SERVERROOT</dt>
+       <dd>The root directory of the server.</dd>
+
+       <dt>DEVICE_URI</dt>
+       <dd>The device-uri associated with the printer.</dd>
+
+       <dt>FINAL_CONTENT_TYPE</dt>
+       <dd>The MIME type associated with the printer (e.g.
+       application/vnd.cups-postscript).</dd>
+
+       <dt>LANG</dt>
+       <dd>The language locale associated with the job.</dd>
+
+       <dt>PPD</dt>
+       <dd>The full pathname of the PostScript Printer Description (PPD)
+       file for this printer.</dd>
+
+       <dt>PRINTER</dt>
+       <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 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;
+
+fprintf(stderr, "INFO: Printing page %d\n", page);
+</pre>
+
+<p>Each message is a single line of text starting with one of the following
+prefix strings:</p>
+
+<dl class="code">
+
+       <dt>ALERT: message</dt>
+       <dd>Sets the printer-state-message attribute and adds the specified
+       message to the current error log file using the "alert" log level.</dd>
+
+       <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-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
+       message to the current error log file using the "critical" log
+       level.</dd>
+
+       <dt>DEBUG: message</dt>
+       <dd>Sets the printer-state-message attribute and adds the specified
+       message to the current error log file using the "debug" log level.</dd>
+
+       <dt>DEBUG2: message</dt>
+       <dd>Sets the printer-state-message attribute and adds the specified
+       message to the current error log file using the "debug2" log level.</dd>
+
+       <dt>EMERG: message</dt>
+       <dd>Sets the printer-state-message attribute and adds the specified
+       message to the current error log file using the "emergency" log
+       level.</dd>
+
+       <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.
+       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
+       is set to "debug2", also adds the specified message to the current error
+       log file using the "info" log level.</dd>
+
+       <dt>NOTICE: message</dt>
+       <dd>Sets the printer-state-message attribute and adds the specified
+       message to the current error log file using the "notice" log level.</dd>
+
+       <dt>PAGE: page-number #-copies</dt>
+       <dt>PAGE: total #-pages</dt>
+       <dd>Adds an entry to the current page log file. The first form adds
+       #-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 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
+       message to the current error log file using the "warning" log
+       level.</dd>
+
+</dl>
+
+<p>Messages without one of these prefixes are treated as if they began with
+the "DEBUG:" prefix string.</p>
+
+
+<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
+<a href="#cupsSideChannelDoRequest"><code>cupsSideChannelDoRequest</code></a>
+functions. The 
+<a href="#cupsBackChannelRead"><code>cupsBackChannelRead</code></a> function
+reads data that has been sent back from the device and is typically used to
+obtain status and configuration information. For example, the following code
+polls the backend for back-channel data:</p>
+
+<pre class="example">
+#include &lt;cups/cups.h&gt;
+
+char buffer[8192];
+ssize_t bytes;
+
+/* Use a timeout of 0.0 seconds to poll for back-channel data */
+bytes = cupsBackChannelRead(buffer, sizeof(buffer), 0.0);
 </pre>
 
-<h2 class='title'>Compatibility</h2>
+<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
+device ID string from the backend:</p>
+
+<pre class="example">
+#include &lt;cups/sidechannel.h&gt;
+
+char data[2049];
+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... */
+datalen = sizeof(data) - 1;
+
+/* Get the IEEE-1284 device ID, waiting for up to 1 second */
+status = <a href="#cupsSideChannelDoRequest">cupsSideChannelDoRequest</a>(CUPS_SC_CMD_GET_DEVICE_ID, data, &amp;datalen, 1.0);
+
+/* Use the returned value if OK was returned and the length is non-zero */
+if (status == CUPS_SC_STATUS_OK && datalen > 0)
+  data[datalen] = '\0';
+else
+  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
+<a href="#cupsSideChannelWrite"><code>cupsSideChannelWrite</code></a>. We
+recommend writing back-channel data using a timeout of 1.0 seconds:</p>
+
+<pre class="example">
+#include &lt;cups/cups.h&gt;
 
-<p>All of these functions require CUPS 1.2 or higher.</p>
+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>
+
+<p>The <a href="#cupsSideChannelRead"><code>cupsSideChannelRead</code></a>
+function reads a side-channel command from a filter, driver, or port monitor.
+Backends can either poll for commands using a <code>timeout</code> of 0.0, wait
+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.</p>
+
+<p>Once a command is processed, the backend uses the
+<a href="#cupsSideChannelWrite"><code>cupsSideChannelWrite</code></a> function
+to send its response. For example, the following code shows how to poll for a
+side-channel command and respond to it:</p>
+
+<pre class="example">
+#include &lt;cups/sidechannel.h&gt;
+
+<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>(&amp;command, &amp;status, data, &amp;datalen, 0.0))
+{
+  switch (command)
+  {
+    /* handle supported commands, fill data/datalen/status with values as needed */
+
+    default :
+        status  = CUPS_SC_STATUS_NOT_IMPLEMENTED;
+       datalen = 0;
+       break;
+  }
+
+  /* Send a response... */
+  <a href="#cupsSideChannelWrite">cupsSideChannelWrite</a>(command, status, data, datalen, 1.0);
+}
+</pre>
+
+<h3><a name="SNMP">Doing SNMP Queries with Network Printers</a></h3>
+
+<p>The Simple Network Management Protocol (SNMP) allows you to get the current
+status, page counter, and supply levels from most network printers. Every
+piece of information is associated with an Object Identifier (OID), and
+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 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>
+
+<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 &lt;cups/sidechannel.h&gt;
+
+char data[512];
+int datalen = sizeof(data);
+
+if (<a href="#cupsSideChannelSNMPGet">cupsSideChannelSNMPGet</a>(".1.3.6.1.2.1.43.10.2.1.4.1.1", data, &amp;datalen, 5.0)
+        == CUPS_SC_STATUS_OK)
+{
+  /* Do something with the value */
+  printf("Page counter is: %s\n", data);
+}
+</pre>
+
+<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 &lt;cups/sidechannel.h&gt;
+
+void
+my_callback(const char *oid, const char *data, int datalen, void *context)
+{
+  /* Do something with the value */
+  printf("%s=%s\n", oid, data);
+}
+
+...
+
+void *my_data;
+
+<a href="#cupsSideChannelSNMPWalk">cupsSNMPSideChannelWalk</a>(".1.3.6.1.2.1.43", 5.0, my_callback, my_data);
+</pre>