]>
Commit | Line | Data |
---|---|---|
ef416fc2 | 1 | <!-- |
bc44d920 | 2 | "$Id: api-filter.shtml 6649 2007-07-11 21:46:42Z mike $" |
ef416fc2 | 3 | |
5a738aea MS |
4 | Filter and backend programming introduction for the Common UNIX Printing |
5 | System (CUPS). | |
ef416fc2 | 6 | |
5a738aea | 7 | Copyright 2007-2008 by Apple Inc. |
bc44d920 | 8 | Copyright 1997-2006 by Easy Software Products, all rights reserved. |
ef416fc2 | 9 | |
10 | These coded instructions, statements, and computer programs are the | |
bc44d920 | 11 | property of Apple Inc. and are protected by Federal copyright |
12 | law. Distribution and use rights are outlined in the file "LICENSE.txt" | |
13 | which should have been included with this file. If this file is | |
14 | file is missing or damaged, see the license at "http://www.cups.org/". | |
ef416fc2 | 15 | --> |
16 | ||
5a738aea | 17 | <h2 class='title'><a name="OVERVIEW">Overview</a></h2> |
ef416fc2 | 18 | |
5a738aea MS |
19 | <p>Filters, printer drivers, port monitors, and backends use a common interface |
20 | for processing print jobs and communicating status information to the scheduler. | |
21 | Each filter is run with a standard set of command-line arguments:<p> | |
ef416fc2 | 22 | |
5a738aea | 23 | <dl class="code"> |
ef416fc2 | 24 | |
5a738aea MS |
25 | <dt>argv[1]</dt> |
26 | <dd>The job ID</dd> | |
ef416fc2 | 27 | |
5a738aea MS |
28 | <dt>argv[2]</dt> |
29 | <dd>The user printing the job</dd> | |
f7deaa1a | 30 | |
5a738aea MS |
31 | <dt>argv[3]</dt> |
32 | <dd>The job name/title</dd> | |
ef416fc2 | 33 | |
5a738aea MS |
34 | <dt>argv[4]</dt> |
35 | <dd>The number of copies to print</dd> | |
ef416fc2 | 36 | |
5a738aea MS |
37 | <dt>argv[5]</dt> |
38 | <dd>The options that were provided when the job was submitted</dd> | |
f7deaa1a | 39 | |
5a738aea MS |
40 | <dt>argv[6]</dt> |
41 | <dd>The file to print (first filter only)</dd> | |
42 | </dl> | |
ef416fc2 | 43 | |
5a738aea MS |
44 | <p>The scheduler runs one or more of these programs to print any given job. The |
45 | first filter reads from the print file and writes to the standard output, while | |
46 | the remaining filters read from the standard input and write to the standard | |
47 | output. The backend is the last filter in the chain and writes to the | |
48 | device.</p> | |
f7deaa1a | 49 | |
ac884b6a MS |
50 | <h3><a name="SECURITY">Security Considerations</a></h3> |
51 | ||
52 | <p>It is always important to use security programming practices. Filters and | |
53 | most backends are run as a non-priviledged user, so the major security | |
54 | consideration is resource utilization - filters should not depend on unlimited | |
55 | amounts of CPU, memory, or disk space, and should protect against conditions | |
56 | that could lead to excess usage of any resource like infinite loops and | |
57 | unbounded recursion. In addition, filters must <em>never</em> allow the user to | |
58 | specify an arbitrary file path to a separator page, template, or other file | |
59 | used by the filter since that can lead to an unauthorized disclosure of | |
60 | information. <em>Always</em> treat input as suspect and validate it!</p> | |
61 | ||
62 | <p>If you are developing a backend that runs as root, make sure to check for | |
63 | potential buffer overflows, integer under/overflow conditions, and file | |
64 | accesses since these can lead to privilege escalations. When writing files, | |
65 | always validate the file path and <em>never</em> allow a user to determine | |
66 | where to store a file.</p> | |
67 | ||
68 | <blockquote><b>Note:</b> | |
69 | ||
70 | <p><em>Never</em> write files to a user's home directory. Aside from the | |
71 | security implications, CUPS is a network print service and as such the network | |
72 | user may not be the same as the local user and/or there may not be a local home | |
73 | directory to write to.</p> | |
74 | ||
75 | <p>In addition, some operating systems provide additional security mechanisms | |
76 | that further limit file system access, even for backends running as root. On | |
77 | Mac OS X, for example, no backend may write to a user's home directory.</p> | |
78 | </blockquote> | |
79 | ||
80 | <h3><a name="TEMPFILES">Temporary Files</a></h3> | |
81 | ||
82 | <p>Temporary files should be created in the directory specified by the | |
83 | "TMPDIR" environment variable. The | |
84 | <a href="#cupsTempFile2"><code>cupsTempFile2</code></a> function can be | |
85 | used to safely create temporary files in this directory.</p> | |
86 | ||
87 | <h3><a name="COPIES">Copy Generation</a></h3> | |
88 | ||
89 | <p>The <code>argv[4]</code> argument specifies the number of copies to produce | |
90 | of the input file. In general, you should only generate copies if the | |
91 | <em>filename</em> argument is supplied. The only exception to this are | |
92 | filters that produce device-independent PostScript output, since the PostScript | |
93 | filter <var>pstops</var> is responsible for generating copies of PostScript | |
94 | files.</p> | |
95 | ||
5a738aea | 96 | <h3><a name="EXITCODES">Exit Codes</a></h3> |
f7deaa1a | 97 | |
5a738aea MS |
98 | <p>Filters must exit with status 0 when they successfully generate print data |
99 | or 1 when they encounter an error. Backends can return any of the | |
100 | <a href="#cups_backend_t"><code>cups_backend_t</code></a> constants.</p> | |
f7deaa1a | 101 | |
5a738aea | 102 | <h3><a name="ENVIRONMENT">Environment Variables</a></h3> |
f7deaa1a | 103 | |
5a738aea | 104 | <p>The following environment variables are defined by the printing system:</p> |
f7deaa1a | 105 | |
5a738aea | 106 | <dl class="code"> |
f7deaa1a | 107 | |
5a738aea MS |
108 | <dt>APPLE_LANGUAGES</dt> |
109 | <dd>The Apple language identifier associated with the job | |
110 | (Mac OS X only).</dd> | |
f7deaa1a | 111 | |
5a738aea MS |
112 | <dt>CHARSET</dt> |
113 | <dd>The job character set, typically "utf-8".</dd> | |
f7deaa1a | 114 | |
5a738aea MS |
115 | <dt>CLASS</dt> |
116 | <dd>When a job is submitted to a printer class, contains the name of | |
117 | the destination printer class. Otherwise this environment | |
118 | variable will not be set.</dd> | |
f7deaa1a | 119 | |
5a738aea MS |
120 | <dt>CONTENT_TYPE</dt> |
121 | <dd>The MIME type associated with the file (e.g. | |
122 | application/postscript).</dd> | |
f7deaa1a | 123 | |
5a738aea MS |
124 | <dt>CUPS_CACHEDIR</dt> |
125 | <dd>The directory where cache files can be stored.</dd> | |
f7deaa1a | 126 | |
5a738aea MS |
127 | <dt>CUPS_DATADIR</dt> |
128 | <dd>The directory where data files can be found.</dd> | |
f7deaa1a | 129 | |
5a738aea MS |
130 | <dt>CUPS_SERVERROOT</dt> |
131 | <dd>The root directory of the server.</dd> | |
f7deaa1a | 132 | |
5a738aea MS |
133 | <dt>DEVICE_URI</dt> |
134 | <dd>The device-uri associated with the printer.</dd> | |
f7deaa1a | 135 | |
5a738aea MS |
136 | <dt>FINAL_CONTENT_TYPE</dt> |
137 | <dd>The MIME type associated with the printer (e.g. | |
138 | application/vnd.cups-postscript).</dd> | |
f7deaa1a | 139 | |
5a738aea MS |
140 | <dt>LANG</dt> |
141 | <dd>The language locale associated with the job.</dd> | |
f7deaa1a | 142 | |
5a738aea MS |
143 | <dt>PPD</dt> |
144 | <dd>The full pathname of the PostScript Printer Description (PPD) | |
145 | file for this printer.</dd> | |
f7deaa1a | 146 | |
5a738aea MS |
147 | <dt>PRINTER</dt> |
148 | <dd>The name of the printer.</dd> | |
f7deaa1a | 149 | |
5a738aea MS |
150 | <dt>RIP_CACHE</dt> |
151 | <dd>The recommended amount of memory to use for Raster Image | |
152 | Processors (RIPs).</dd> | |
f7deaa1a | 153 | |
5a738aea | 154 | </dl> |
f7deaa1a | 155 | |
5a738aea | 156 | <h3><a name="MESSAGES">Communicating with the Scheduler</a></h3> |
f7deaa1a | 157 | |
5a738aea MS |
158 | <p>Filters and backends communicate wih the scheduler by writing messages |
159 | to the standard error file. For example, the following code sets the current | |
160 | printer state message to "Printing page 5":</p> | |
f7deaa1a | 161 | |
5a738aea MS |
162 | <pre class="example"> |
163 | int page = 5; | |
f7deaa1a | 164 | |
5a738aea MS |
165 | fprintf(stderr, "INFO: Printing page %d\n", page); |
166 | </pre> | |
f7deaa1a | 167 | |
5a738aea MS |
168 | <p>Each message is a single line of text starting with one of the following |
169 | prefix strings:</p> | |
170 | ||
171 | <dl class="code"> | |
172 | ||
173 | <dt>ALERT: message</dt> | |
174 | <dd>Sets the printer-state-message attribute and adds the specified | |
175 | message to the current error log file using the "alert" log level.</dd> | |
176 | ||
177 | <dt>ATTR: attribute=value [attribute=value]</dt> | |
178 | <dd>Sets the named printer or job attribute(s). Typically this is used | |
179 | to set the <code>marker-colors</code>, <code>marker-levels</code>, | |
180 | <code>marker-names</code>, <code>marker-types</code>, | |
181 | <code>printer-alert</code>, and <code>printer-alert-description</code> | |
182 | printer attributes.</dd> | |
183 | ||
184 | <dt>CRIT: message</dt> | |
185 | <dd>Sets the printer-state-message attribute and adds the specified | |
186 | message to the current error log file using the "critical" log | |
187 | level.</dd> | |
188 | ||
189 | <dt>DEBUG: message</dt> | |
190 | <dd>Sets the printer-state-message attribute and adds the specified | |
191 | message to the current error log file using the "debug" log level.</dd> | |
192 | ||
193 | <dt>DEBUG2: message</dt> | |
194 | <dd>Sets the printer-state-message attribute and adds the specified | |
195 | message to the current error log file using the "debug2" log level.</dd> | |
196 | ||
197 | <dt>EMERG: message</dt> | |
198 | <dd>Sets the printer-state-message attribute and adds the specified | |
199 | message to the current error log file using the "emergency" log | |
200 | level.</dd> | |
201 | ||
202 | <dt>ERROR: message</dt> | |
203 | <dd>Sets the printer-state-message attribute and adds the specified | |
204 | message to the current error log file using the "error" log level.</dd> | |
205 | ||
206 | <dt>INFO: message</dt> | |
207 | <dd>Sets the printer-state-message attribute. If the current log level | |
208 | is set to "debug2", also adds the specified message to the current error | |
209 | log file using the "info" log level.</dd> | |
210 | ||
211 | <dt>NOTICE: message</dt> | |
212 | <dd>Sets the printer-state-message attribute and adds the specified | |
213 | message to the current error log file using the "notice" log level.</dd> | |
214 | ||
215 | <dt>PAGE: page-number #-copies</dt> | |
216 | <dt>PAGE: total #-pages</dt> | |
217 | <dd>Adds an entry to the current page log file. The first form adds | |
218 | #-copies to the job-media-sheets-completed attribute. The second | |
219 | form sets the job-media-sheets-completed attribute to #-pages.</dd> | |
220 | ||
221 | <dt>STATE: printer-state-reason [printer-state-reason ...]</dt> | |
222 | <dt>STATE: + printer-state-reason [printer-state-reason ...]</dt> | |
223 | <dt>STATE: - printer-state-reason [printer-state-reason ...]</dt> | |
224 | <dd>Sets, adds, or removes printer-state-reason keywords to the | |
225 | current queue. Typically this is used to indicate media, ink, and | |
226 | toner conditions on a printer.</dd> | |
227 | ||
228 | <dt>WARNING: message</dt> | |
229 | <dd>Sets the printer-state-message attribute and adds the specified | |
230 | message to the current error log file using the "warning" log | |
231 | level.</dd> | |
232 | ||
233 | </dl> | |
234 | ||
235 | <p>Messages without one of these prefixes are treated as if they began with | |
236 | the "DEBUG:" prefix string.</p> | |
237 | ||
238 | <h3><a name="COMMUNICATING">Communicating with the Backend</a></h3> | |
239 | ||
240 | <p>Filters can communicate with the backend via the | |
241 | <a href="#cupsBackChannelRead"><code>cupsBackChannelRead</code></a> and | |
242 | <a href="#cupsSideChannelDoRequest"><code>cupsSideChannelDoRequest</code></a> | |
243 | functions. The | |
244 | <a href="#cupsBackChannelRead"><code>cupsBackChannelRead</code></a> function | |
245 | reads data that has been sent back from the device and is typically used to | |
246 | obtain status and configuration information. For example, the following code | |
247 | polls the backend for back-channel data:</p> | |
248 | ||
249 | <pre class="example"> | |
250 | #include <cups/cups.h> | |
f7deaa1a | 251 | |
5a738aea MS |
252 | char buffer[8192]; |
253 | ssize_t bytes; | |
f7deaa1a | 254 | |
5a738aea MS |
255 | /* Use a timeout of 0.0 seconds to poll for back-channel data */ |
256 | bytes = cupsBackChannelRead(buffer, sizeof(buffer), 0.0); | |
f7deaa1a | 257 | </pre> |
258 | ||
5a738aea MS |
259 | The |
260 | <a href="#cupsSideChannelDoRequest"><code>cupsSideChannelDoRequest</code></a> | |
261 | function allows you to get out-of-band status information and do synchronization | |
262 | with the device. For example, the following code gets the current IEEE-1284 | |
263 | device ID string from the backend:</p> | |
f7deaa1a | 264 | |
5a738aea | 265 | <pre class="example"> |
f7deaa1a | 266 | #include <cups/sidechannel.h> |
267 | ||
268 | char data[2049]; | |
269 | int datalen; | |
5a738aea | 270 | <a href="#cups_sc_status_t">cups_sc_status_t</a> status; |
f7deaa1a | 271 | |
272 | /* Tell cupsSideChannelDoRequest() how big our buffer is, less 1 byte for nul-termination... */ | |
273 | datalen = sizeof(data) - 1; | |
274 | ||
275 | /* Get the IEEE-1284 device ID, waiting for up to 1 second */ | |
5a738aea | 276 | status = <a href="#cupsSideChannelDoRequest">cupsSideChannelDoRequest</a>(CUPS_SC_CMD_GET_DEVICE_ID, data, &datalen, 1.0); |
f7deaa1a | 277 | |
278 | /* Use the returned value if OK was returned and the length is non-zero */ | |
279 | if (status == CUPS_SC_STATUS_OK && datalen > 0) | |
280 | data[datalen] = '\0'; | |
281 | else | |
282 | data[0] = '\0'; | |
283 | </pre> | |
284 | ||
5a738aea MS |
285 | <p>Backends communicate with filters using the reciprocal functions |
286 | <a href="#cupsBackChannelWrite"><code>cupsBackChannelWrite</code></a>, | |
287 | <a href="#cupsSideChannelRead"><code>cupsSideChannelRead</code></a>, and | |
288 | <a href="#cupsSideChannelWrite"><code>cupsSideChannelWrite</code></a>. We | |
289 | recommend writing back-channel data using a timeout of 1.0 seconds:</p> | |
f7deaa1a | 290 | |
5a738aea MS |
291 | <pre class="example"> |
292 | #include <cups/cups.h> | |
f7deaa1a | 293 | |
5a738aea MS |
294 | char buffer[8192]; |
295 | ssize_t bytes; | |
f7deaa1a | 296 | |
5a738aea MS |
297 | /* Use a timeout of 1.0 seconds to give filters a chance to read */ |
298 | cupsBackChannelWrite(buffer, bytes, 1.0); | |
f7deaa1a | 299 | </pre> |
300 | ||
5a738aea MS |
301 | <p>The <a href="#cupsSideChannelRead"><code>cupsSideChannelRead</code></a> |
302 | function reads a side-channel command from a filter, driver, or port monitor. | |
303 | Backends can either poll for commands using a <code>timeout</code> of 0.0, wait | |
304 | indefinitely for commands using a <code>timeout</code> of -1.0 (probably in a | |
305 | separate thread for that purpose), or use <code>select</code> or | |
306 | <code>poll</code> on the <code>CUPS_SC_FD</code> file descriptor (4) to handle | |
307 | input and output on several file descriptors at the same time. Backends can pass | |
308 | <code>NULL</code> for the <code>data</code> and <code>datalen</code> parameters | |
309 | since none of the commands sent by upstream filters contain any data at this | |
310 | time.</p> | |
311 | ||
312 | <p>Once a command is processed, the backend uses the | |
313 | <a href="#cupsSideChannelWrite"><code>cupsSideChannelWrite</code></a> function | |
314 | to send its response. For example, the following code shows how to poll for a | |
315 | side-channel command and respond to it:</p> | |
316 | ||
317 | <pre class="example"> | |
f7deaa1a | 318 | #include <cups/sidechannel.h> |
319 | ||
5a738aea MS |
320 | <a href="#cups_sc_command_t">cups_sc_command_t</a> command; |
321 | <a href="#cups_sc_status_t">cups_sc_status_t</a> status; | |
f7deaa1a | 322 | |
323 | /* Poll for a command... */ | |
5a738aea | 324 | if (!<a href="#cupsSideChannelRead">cupsSideChannelRead</a>(&command, &status, NULL, NULL, 0.0)) |
f7deaa1a | 325 | { |
326 | char data[2048]; | |
327 | int datalen; | |
328 | ||
329 | switch (command) | |
330 | { | |
5a738aea | 331 | /* handle supported commands, file data/datalen/status with values as needed */ |
f7deaa1a | 332 | |
333 | default : | |
334 | status = CUPS_SC_STATUS_NOT_IMPLEMENTED; | |
335 | datalen = 0; | |
336 | break; | |
337 | } | |
338 | ||
339 | /* Send a response... */ | |
5a738aea | 340 | <a href="#cupsSideChannelWrite">cupsSideChannelWrite</a>(command, status, data, datalen, 1.0); |
f7deaa1a | 341 | } |
342 | </pre> | |
ac884b6a MS |
343 | |
344 | <h3><a name="SNMP">Doing SNMP Queries with Network Printers</a></h3> | |
345 | ||
346 | <p>The Simple Network Management Protocol (SNMP) allows you to get the current | |
347 | status, page counter, and supply levels from most network printers. Every | |
348 | piece of information is associated with an Object Identifier (OID), and | |
349 | every printer has a <em>community</em> name associated with it. OIDs can be | |
350 | queried directly or by "walking" over a range of OIDs with a common prefix.</p> | |
351 | ||
352 | <p>The CUPS SNMP functions provide a simple API for querying network printers. | |
353 | Queries are made using a datagram socket that is created using | |
354 | <a href="#cupsSNMPOpen"><code>cupsSNMPOpen</code></a> and destroyed using | |
355 | <a href="#cupsSNMPClose"><code>cupsSNMPClose</code></a>:</p> | |
356 | ||
357 | <pre class="example"> | |
358 | #include <cups/snmp.h> | |
359 | ||
360 | int snmp = <a href="#cupsSNMPOpen">cupsSNMPOpen</a>(AF_INET); | |
361 | ||
362 | /* do some queries */ | |
363 | ||
364 | <a href="#cupsSNMPClose">cupsSNMPClose</a>(snmp); | |
365 | </pre> | |
366 | ||
367 | <p>OIDs are simple C arrays of integers, terminated by a value of -1. For | |
368 | example, the page counter OID .1.3.6.1.2.1.43.10.2.1.4.1.1 would be:</p> | |
369 | ||
370 | <pre class="example"> | |
371 | int page_counter_oid[] = { 1, 3, 6, 1, 2, 1, 43, 10, 2, 1, 4, 1, 1, -1 }; | |
372 | </pre> | |
373 | ||
374 | <p>You send a query using | |
375 | <a href="#cupsSNMPWrite"><code>cupsSNMPWrite</code></a> and read the value back | |
376 | using <a href="#cupsSNMPRead"><code>cupsSNMPRead</code></a>. The value is read | |
377 | into a structure called <a href="#cups_snmp_t"><code>cups_snmp_t</code></a>:</p> | |
378 | ||
379 | <pre class="example"> | |
380 | #include <cups/snmp.h> | |
381 | ||
382 | int page_counter_oid[] = { 1, 3, 6, 1, 2, 1, 43, 10, 2, 1, 4, 1, 1, -1 }; | |
383 | http_addrlist_t *host = httpAddrGetList("myprinter", AF_UNSPEC, "161"); | |
384 | int snmp = <a href="#cupsSNMPOpen">cupsSNMPOpen</a>(host->addr.addr.sa_family); | |
385 | <a href="#cups_snmp_t">cups_snmp_t</a> packet; | |
386 | ||
387 | <a href="#cupsSNMPWrite">cupsSNMPWrite</a>(snmp, &(host->addr), CUPS_SNMP_VERSION_1, | |
388 | <a href="#cupsSNMPDefaultCommunity">cupsSNMPDefaultCommunity</a>(), CUPS_ASN1_GET_REQUEST, 1, | |
389 | page_counter_oid); | |
390 | if (<a href="#cupsSNMPRead">cupsSNMPRead</a>(snmp, &packet, 5000)) | |
391 | { | |
392 | /* Do something with the value */ | |
393 | printf("Page counter is: %d\n", packet.object_value.integer); | |
394 | } | |
395 | </pre> | |
396 | ||
397 | <p>The <a href="#cupsSNMPWalk"><code>cupsSNMPWalk</code></a> function allows you | |
398 | to query a whole group of OIDs, calling a function of your choice for each OID | |
399 | that is found:</p> | |
400 | ||
401 | <pre class="example"> | |
402 | #include <cups/snmp.h> | |
403 | ||
404 | void | |
405 | my_callback(<a href="#cups_snmp_t">cups_snmp_t</a> *packet, void *data) | |
406 | { | |
407 | /* Do something with the value */ | |
408 | } | |
409 | ||
410 | int printer_mib_oid[] = { 1, 3, 6, 1, 2, 1, 43, -1 }; | |
411 | http_addrlist_t *host = httpAddrGetList("myprinter", AF_UNSPEC, "161"); | |
412 | int snmp = <a href="#cupsSNMPOpen">cupsSNMPOpen</a>(host->addr.addr.sa_family); | |
413 | void *my_data; | |
414 | ||
415 | <a href="#cupsSNMPWalk">cupsSNMPWalk</a>(snmp, &(host->addr), CUPS_SNMP_VERSION_1, | |
416 | <a href="#cupsSNMPDefaultCommunity">cupsSNMPDefaultCommunity</a>(), printer_mib_oid, my_callback, my_data); | |
417 | </pre> |