1<!-- 2 Filter and backend programming introduction for CUPS. 3 4 Copyright © 2020-2024 by OpenPrinting. 5 Copyright © 2007-2016 by Apple Inc. 6 Copyright © 1997-2006 by Easy Software Products, all rights reserved. 7 8 Licensed under Apache License v2.0. See the file "LICENSE" for more 9 information. 10--> 11 12<h2 class='title'><a name="OVERVIEW">Overview</a></h2> 13 14<p>Filters (which include printer drivers and port monitors) and backends 15are used to convert job files to a printable format and send that data to the 16printer itself. All of these programs use a common interface for processing 17print jobs and communicating status information to the scheduler. Each is run 18with a standard set of command-line arguments:<p> 19 20<dl class="code"> 21 22 <dt>argv[1]</dt> 23 <dd>The job ID</dd> 24 25 <dt>argv[2]</dt> 26 <dd>The user printing the job</dd> 27 28 <dt>argv[3]</dt> 29 <dd>The job name/title</dd> 30 31 <dt>argv[4]</dt> 32 <dd>The number of copies to print</dd> 33 34 <dt>argv[5]</dt> 35 <dd>The options that were provided when the job was submitted</dd> 36 37 <dt>argv[6]</dt> 38 <dd>The file to print (first program only)</dd> 39</dl> 40 41<p>The scheduler runs one or more of these programs to print any given job. The 42first filter reads from the print file and writes to the standard output, while 43the remaining filters read from the standard input and write to the standard 44output. The backend is the last filter in the chain and writes to the 45device.</p> 46 47<p>Filters are always run as a non-privileged user, typically "lp", with no 48connection to the user's desktop. Backends are run either as a non-privileged 49user or as root if the file permissions do not allow user or group execution. 50The <a href="#PERMISSIONS">file permissions</a> section talks about this in 51more detail.</p> 52 53<h3><a name="SECURITY">Security Considerations</a></h3> 54 55<p>It is always important to use security programming practices. Filters and 56most backends are run as a non-privileged user, so the major security 57consideration is resource utilization - filters should not depend on unlimited 58amounts of CPU, memory, or disk space, and should protect against conditions 59that could lead to excess usage of any resource like infinite loops and 60unbounded recursion. In addition, filters must <em>never</em> allow the user to 61specify an arbitrary file path to a separator page, template, or other file 62used by the filter since that can lead to an unauthorized disclosure of 63information. <em>Always</em> treat input as suspect and validate it!</p> 64 65<p>If you are developing a backend that runs as root, make sure to check for 66potential buffer overflows, integer under/overflow conditions, and file 67accesses since these can lead to privilege escalations. When writing files, 68always validate the file path and <em>never</em> allow a user to determine 69where to store a file.</p> 70 71<blockquote><b>Note:</b> 72 73<p><em>Never</em> write files to a user's home directory. Aside from the 74security implications, CUPS is a network print service and as such the network 75user may not be the same as the local user and/or there may not be a local home 76directory to write to.</p> 77 78<p>In addition, some operating systems provide additional security mechanisms 79that further limit file system access, even for backends running as root. On 80macOS, for example, no backend may write to a user's home directory. See the <a href="#SANDBOXING">Sandboxing on macOS</a> section for more information.</p> 81</blockquote> 82 83<h3><a name="SIGNALS">Canceled Jobs and Signal Handling</a></h3> 84 85<p>The scheduler sends <code>SIGTERM</code> when a printing job is canceled or 86held. Filters, backends, and port monitors <em>must</em> catch 87<code>SIGTERM</code> and perform any cleanup necessary to produce a valid output 88file or return the printer to a known good state. The recommended behavior is to 89end the output on the current page, preferably on the current line or object 90being printed.</p> 91 92<p>Filters and backends may also receive <code>SIGPIPE</code> when an upstream or downstream filter/backend exits with a non-zero status. Developers should generally ignore <code>SIGPIPE</code> at the beginning of <code>main()</code> with the following function call:</p> 93 94<pre class="example"> 95#include <signal.h> 96 97... 98 99int 100main(int argc, char *argv[]) 101{ 102 signal(SIGPIPE, SIG_IGN); 103 104 ... 105} 106</pre> 107 108<h3><a name="PERMISSIONS">File Permissions</a></h3> 109 110<p>For security reasons, CUPS will only run filters and backends that are owned 111by root and do not have world or group write permissions. The recommended 112permissions for filters and backends are 0555 - read and execute but no write. 113Backends that must run as root should use permissions of 0500 - read and execute 114by root, no access for other users. Write permissions can be enabled for the 115root user only.</p> 116 117<p>To avoid a warning message, the directory containing your filter(s) must also 118be owned by root and have world and group write disabled - permissions of 0755 119or 0555 are strongly encouraged.</p> 120 121<h3><a name="TEMPFILES">Temporary Files</a></h3> 122 123<p>Temporary files should be created in the directory specified by the 124"TMPDIR" environment variable. The 125<a href="#cupsTempFile2"><code>cupsTempFile2</code></a> function can be 126used to safely create temporary files in this directory.</p> 127 128<h3><a name="COPIES">Copy Generation</a></h3> 129 130<p>The <code>argv[4]</code> argument specifies the number of copies to produce 131of the input file. In general, you should only generate copies if the 132<em>filename</em> argument is supplied. The only exception to this are 133filters that produce device-independent PostScript output, since the PostScript 134filter <var>pstops</var> is responsible for generating copies of PostScript 135files.</p> 136 137<h3><a name="EXITCODES">Exit Codes</a></h3> 138 139<p>Filters must exit with status 0 when they successfully generate print data 140or 1 when they encounter an error. Backends can return any of the 141<a href="#cups_backend_t"><code>cups_backend_t</code></a> constants.</p> 142 143<h3><a name="ENVIRONMENT">Environment Variables</a></h3> 144 145<p>The following environment variables are defined by the printing system 146when running print filters and backends:</p> 147 148<dl class="code"> 149 150 <dt>APPLE_LANGUAGE</dt> 151 <dd>The Apple language identifier associated with the job 152 (macOS only).</dd> 153 154 <dt>CHARSET</dt> 155 <dd>The job character set, typically "utf-8".</dd> 156 157 <dt>CLASS</dt> 158 <dd>When a job is submitted to a printer class, contains the name of 159 the destination printer class. Otherwise this environment 160 variable will not be set.</dd> 161 162 <dt>CONTENT_TYPE</dt> 163 <dd>The MIME type associated with the file (e.g. 164 application/postscript).</dd> 165 166 <dt>CUPS_CACHEDIR</dt> 167 <dd>The directory where cache files can be stored. Cache files can be 168 used to retain information between jobs or files in a job.</dd> 169 170 <dt>CUPS_DATADIR</dt> 171 <dd>The directory where (read-only) CUPS data files can be found.</dd> 172 173 <dt>CUPS_FILETYPE</dt> 174 <dd>The type of file being printed: "job-sheet" for a banner page and 175 "document" for a regular print file.</dd> 176 177 <dt>CUPS_SERVERROOT</dt> 178 <dd>The root directory of the server.</dd> 179 180 <dt>DEVICE_URI</dt> 181 <dd>The device-uri associated with the printer.</dd> 182 183 <dt>FINAL_CONTENT_TYPE</dt> 184 <dd>The MIME type associated with the printer (e.g. 185 application/vnd.cups-postscript).</dd> 186 187 <dt>LANG</dt> 188 <dd>The language locale associated with the job.</dd> 189 190 <dt>PPD</dt> 191 <dd>The full pathname of the PostScript Printer Description (PPD) 192 file for this printer.</dd> 193 194 <dt>PRINTER</dt> 195 <dd>The queue name of the class or printer.</dd> 196 197 <dt>RIP_CACHE</dt> 198 <dd>The recommended amount of memory to use for Raster Image 199 Processors (RIPs).</dd> 200 201 <dt>TMPDIR</dt> 202 <dd>The directory where temporary files should be created.</dd> 203 204</dl> 205 206<h3><a name="MESSAGES">Communicating with the Scheduler</a></h3> 207 208<p>Filters and backends communicate with the scheduler by writing messages 209to the standard error file. The scheduler reads messages from all filters in 210a job and processes the message based on its prefix. For example, the following 211code sets the current printer state message to "Printing page 5":</p> 212 213<pre class="example"> 214int page = 5; 215 216fprintf(stderr, "INFO: Printing page %d\n", page); 217</pre> 218 219<p>Each message is a single line of text starting with one of the following 220prefix strings:</p> 221 222<dl class="code"> 223 224 <dt>ALERT: message</dt> 225 <dd>Sets the printer-state-message attribute and adds the specified 226 message to the current error log file using the "alert" log level.</dd> 227 228 <dt>ATTR: attribute=value [attribute=value]</dt> 229 <dd>Sets the named printer or job attribute(s). Typically this is used 230 to set the <code>marker-colors</code>, <code>marker-high-levels</code>, 231 <code>marker-levels</code>, <code>marker-low-levels</code>, 232 <code>marker-message</code>, <code>marker-names</code>, 233 <code>marker-types</code>, <code>printer-alert</code>, and 234 <code>printer-alert-description</code> printer attributes. Standard 235 <code>marker-types</code> values are listed in <a href='#TABLE1'>Table 236 1</a>. String values need special handling - see <a href="#ATTR_STRINGS">Reporting Attribute String Values</a> below.</dd> 237 238 <dt>CRIT: message</dt> 239 <dd>Sets the printer-state-message attribute and adds the specified 240 message to the current error log file using the "critical" log 241 level.</dd> 242 243 <dt>DEBUG: message</dt> 244 <dd>Sets the printer-state-message attribute and adds the specified 245 message to the current error log file using the "debug" log level.</dd> 246 247 <dt>DEBUG2: message</dt> 248 <dd>Sets the printer-state-message attribute and adds the specified 249 message to the current error log file using the "debug2" log level.</dd> 250 251 <dt>EMERG: message</dt> 252 <dd>Sets the printer-state-message attribute and adds the specified 253 message to the current error log file using the "emergency" log 254 level.</dd> 255 256 <dt>ERROR: message</dt> 257 <dd>Sets the printer-state-message attribute and adds the specified 258 message to the current error log file using the "error" log level. 259 Use "ERROR:" messages for non-persistent processing errors.</dd> 260 261 <dt>INFO: message</dt> 262 <dd>Sets the printer-state-message attribute. If the current log level 263 is set to "debug2", also adds the specified message to the current error 264 log file using the "info" log level.</dd> 265 266 <dt>NOTICE: message</dt> 267 <dd>Sets the printer-state-message attribute and adds the specified 268 message to the current error log file using the "notice" log level.</dd> 269 270 <dt>PAGE: page-number #-copies</dt> 271 <dt>PAGE: total #-pages</dt> 272 <dd>Adds an entry to the current page log file. The first form adds 273 #-copies to the job-media-sheets-completed attribute. The second 274 form sets the job-media-sheets-completed attribute to #-pages.</dd> 275 276 <dt>PPD: keyword=value [keyword=value ...]</dt> 277 <dd>Changes or adds keywords to the printer's PPD file. Typically 278 this is used to update installable options or default media settings 279 based on the printer configuration.</dd> 280 281 <dt>STATE: + printer-state-reason [printer-state-reason ...]</dt> 282 <dt>STATE: - printer-state-reason [printer-state-reason ...]</dt> 283 <dd>Sets or clears printer-state-reason keywords for the current queue. 284 Typically this is used to indicate persistent media, ink, toner, and 285 configuration conditions or errors on a printer. 286 <a href='#TABLE2'>Table 2</a> lists some of the standard "printer-state-reasons" keywords from the <a href="http://www.iana.org/assignments/ipp-registrations/ipp-registrations.xhtml#ipp-registrations-4">IANA IPP Registry</a> - 287 use vendor-prefixed ("com.example.foo") keywords for custom states. See 288 <a href="#MANAGING_STATE">Managing Printer State in a Filter</a> for more 289 information. 290 291 <dt>WARNING: message</dt> 292 <dd>Sets the printer-state-message attribute and adds the specified 293 message to the current error log file using the "warning" log 294 level.</dd> 295 296</dl> 297 298<p>Messages without one of these prefixes are treated as if they began with 299the "DEBUG:" prefix string.</p> 300 301<div class='table'><table width='80%' summary='Table 1: Standard marker-types Values'> 302<caption>Table 1: <a name='TABLE1'>Standard marker-types Values</a></caption> 303<thead> 304<tr> 305 <th>marker-type</th> 306 <th>Description</th> 307</tr> 308</thead> 309<tbody> 310<tr> 311 <td>developer</td> 312 <td>Developer unit</td> 313</tr> 314<tr> 315 <td>fuser</td> 316 <td>Fuser unit</td> 317</tr> 318<tr> 319 <td>fuser-cleaning-pad</td> 320 <td>Fuser cleaning pad</td> 321</tr> 322<tr> 323 <td>fuser-oil</td> 324 <td>Fuser oil</td> 325</tr> 326<tr> 327 <td>ink</td> 328 <td>Ink supply</td> 329</tr> 330<tr> 331 <td>opc</td> 332 <td>Photo conductor</td> 333</tr> 334<tr> 335 <td>solid-wax</td> 336 <td>Wax supply</td> 337</tr> 338<tr> 339 <td>staples</td> 340 <td>Staple supply</td> 341</tr> 342<tr> 343 <td>toner</td> 344 <td>Toner supply</td> 345</tr> 346<tr> 347 <td>transfer-unit</td> 348 <td>Transfer unit</td> 349</tr> 350<tr> 351 <td>waste-ink</td> 352 <td>Waste ink tank</td> 353</tr> 354<tr> 355 <td>waste-toner</td> 356 <td>Waste toner tank</td> 357</tr> 358<tr> 359 <td>waste-wax</td> 360 <td>Waste wax tank</td> 361</tr> 362</tbody> 363</table></div> 364 365<br> 366 367<div class='table'><table width='80%' summary='Table 2: Standard State Keywords'> 368<caption>Table 2: <a name='TABLE2'>Standard State Keywords</a></caption> 369<thead> 370<tr> 371 <th>Keyword</th> 372 <th>Description</th> 373</tr> 374</thead> 375<tbody> 376<tr> 377 <td>connecting-to-device</td> 378 <td>Connecting to printer but not printing yet.</td> 379</tr> 380<tr> 381 <td>cover-open</td> 382 <td>The printer's cover is open.</td> 383</tr> 384<tr> 385 <td>input-tray-missing</td> 386 <td>The paper tray is missing.</td> 387</tr> 388<tr> 389 <td>marker-supply-empty</td> 390 <td>The printer is out of ink.</td> 391</tr> 392<tr> 393 <td>marker-supply-low</td> 394 <td>The printer is almost out of ink.</td> 395</tr> 396<tr> 397 <td>marker-waste-almost-full</td> 398 <td>The printer's waste bin is almost full.</td> 399</tr> 400<tr> 401 <td>marker-waste-full</td> 402 <td>The printer's waste bin is full.</td> 403</tr> 404<tr> 405 <td>media-empty</td> 406 <td>The paper tray (any paper tray) is empty.</td> 407</tr> 408<tr> 409 <td>media-jam</td> 410 <td>There is a paper jam.</td> 411</tr> 412<tr> 413 <td>media-low</td> 414 <td>The paper tray (any paper tray) is almost empty.</td> 415</tr> 416<tr> 417 <td>media-needed</td> 418 <td>The paper tray needs to be filled (for a job that is printing).</td> 419</tr> 420<tr> 421 <td>paused</td> 422 <td>Stop the printer.</td> 423</tr> 424<tr> 425 <td>timed-out</td> 426 <td>Unable to connect to printer.</td> 427</tr> 428<tr> 429 <td>toner-empty</td> 430 <td>The printer is out of toner.</td> 431</tr> 432<tr> 433 <td>toner-low</td> 434 <td>The printer is low on toner.</td> 435</tr> 436</tbody> 437</table></div> 438 439 440<h4><a name="ATTR_STRINGS">Reporting Attribute String Values</a></h4> 441 442<p>When reporting string values using "ATTR:" messages, a filter or backend must take special care to appropriately quote those values. The scheduler uses the CUPS option parsing code for attributes, so the general syntax is:</p> 443 444<pre class="example"> 445name=simple 446name=simple,simple,... 447name='complex value' 448name="complex value" 449name='"complex value"','"complex value"',... 450</pre> 451 452<p>Simple values are strings that do not contain spaces, quotes, backslashes, or the comma and can be placed verbatim in the "ATTR:" message, for example:</p> 453 454<pre class="example"> 455int levels[4] = { 40, 50, 60, 70 }; /* CMYK */ 456 457fputs("ATTR: marker-colors=#00FFFF,#FF00FF,#FFFF00,#000000\n", stderr); 458fputs("ATTR: marker-high-levels=100,100,100,100\n", stderr); 459fprintf(stderr, "ATTR: marker-levels=%d,%d,%d,%d\n", levels[0], levels[1], 460 levels[2], levels[3], levels[4]); 461fputs("ATTR: marker-low-levels=5,5,5,5\n", stderr); 462fputs("ATTR: marker-types=toner,toner,toner,toner\n", stderr); 463</pre> 464 465<p>Complex values that contains spaces, quotes, backslashes, or the comma must be quoted. For a single value a single set of quotes is sufficient:</p> 466 467<pre class="example"> 468fputs("ATTR: marker-message='Levels shown are approximate.'\n", stderr); 469</pre> 470 471<p>When multiple values are reported, each value must be enclosed by a set of single and double quotes:</p> 472 473<pre class="example"> 474fputs("ATTR: marker-names='\"Cyan Toner\"','\"Magenta Toner\"'," 475 "'\"Yellow Toner\"','\"Black Toner\"'\n", stderr); 476</pre> 477 478<p>The IPP backend includes a <var>quote_string</var> function that may be used to properly quote a complex value in an "ATTR:" message:</p> 479 480<pre class="example"> 481static const char * /* O - Quoted string */ 482quote_string(const char *s, /* I - String */ 483 char *q, /* I - Quoted string buffer */ 484 size_t qsize) /* I - Size of quoted string buffer */ 485{ 486 char *qptr, /* Pointer into string buffer */ 487 *qend; /* End of string buffer */ 488 489 490 qptr = q; 491 qend = q + qsize - 5; 492 493 if (qend < q) 494 { 495 *q = '\0'; 496 return (q); 497 } 498 499 *qptr++ = '\''; 500 *qptr++ = '\"'; 501 502 while (*s && qptr < qend) 503 { 504 if (*s == '\\' || *s == '\"' || *s == '\'') 505 { 506 if (qptr < (qend - 4)) 507 { 508 *qptr++ = '\\'; 509 *qptr++ = '\\'; 510 *qptr++ = '\\'; 511 } 512 else 513 break; 514 } 515 516 *qptr++ = *s++; 517 } 518 519 *qptr++ = '\"'; 520 *qptr++ = '\''; 521 *qptr = '\0'; 522 523 return (q); 524} 525</pre> 526 527 528<h4><a name="MANAGING_STATE">Managing Printer State in a Filter</a></h4> 529 530<p>Filters are responsible for managing the state keywords they set using 531"STATE:" messages. Typically you will update <em>all</em> of the keywords that 532are used by the filter at startup, for example:</p> 533 534<pre class="example"> 535if (foo_condition != 0) 536 fputs("STATE: +com.example.foo\n", stderr); 537else 538 fputs("STATE: -com.example.foo\n", stderr); 539 540if (bar_condition != 0) 541 fputs("STATE: +com.example.bar\n", stderr); 542else 543 fputs("STATE: -com.example.bar\n", stderr); 544</pre> 545 546<p>Then as conditions change, your filter sends "STATE: +keyword" or "STATE: 547-keyword" messages as necessary to set or clear the corresponding keyword, 548respectively.</p> 549 550<p>State keywords are often used to notify the user of issues that span across 551jobs, for example "media-empty-warning" that indicates one or more paper trays 552are empty. These keywords should not be cleared unless the corresponding issue 553no longer exists.</p> 554 555<p>Filters should clear job-related keywords on startup and exit so that they 556do not remain set between jobs. For example, "connecting-to-device" is a job 557sub-state and not an issue that applies when a job is not printing.</p> 558 559<blockquote><b>Note:</b> 560 561<p>"STATE:" messages often provide visible alerts to the user. For example, 562on macOS setting a printer-state-reason value with an "-error" or 563"-warning" suffix will cause the printer's dock item to bounce if the 564corresponding reason is localized with a cupsIPPReason keyword in the 565printer's PPD file.</p> 566 567<p>When providing a vendor-prefixed keyword, <em>always</em> provide the 568corresponding standard keyword (if any) to allow clients to respond to the 569condition correctly. For example, if you provide a vendor-prefixed keyword 570for a low cyan ink condition ("com.example.cyan-ink-low") you must also set the 571"marker-supply-low-warning" keyword. In such cases you should also refrain 572from localizing the vendor-prefixed keyword in the PPD file - otherwise both 573the generic and vendor-specific keyword will be shown in the user 574interface.</p> 575 576</blockquote> 577 578<h4><a name="REPORTING_SUPPLIES">Reporting Supply Levels</a></h4> 579 580<p>CUPS tracks several "marker-*" attributes for ink/toner supply level 581reporting. These attributes allow applications to display the current supply 582levels for a printer without printer-specific software. <a href="#TABLE3">Table 3</a> lists the marker attributes and what they represent.</p> 583 584<p>Filters set marker attributes by sending "ATTR:" messages to stderr. For 585example, a filter supporting an inkjet printer with black and tri-color ink 586cartridges would use the following to initialize the supply attributes:</p> 587 588<pre class="example"> 589fputs("ATTR: marker-colors=#000000,#00FFFF#FF00FF#FFFF00\n", stderr); 590fputs("ATTR: marker-low-levels=5,10\n", stderr); 591fputs("ATTR: marker-names=Black,Tri-Color\n", stderr); 592fputs("ATTR: marker-types=ink,ink\n", stderr); 593</pre> 594 595<p>Then periodically the filter queries the printer for its current supply 596levels and updates them with a separate "ATTR:" message:</p> 597 598<pre class="example"> 599int black_level, tri_level; 600... 601fprintf(stderr, "ATTR: marker-levels=%d,%d\n", black_level, tri_level); 602</pre> 603 604<div class='table'><table width='80%' summary='Table 3: Supply Level Attributes'> 605<caption>Table 3: <a name='TABLE3'>Supply Level Attributes</a></caption> 606<thead> 607<tr> 608 <th>Attribute</th> 609 <th>Description</th> 610</tr> 611</thead> 612<tbody> 613<tr> 614 <td>marker-colors</td> 615 <td>A list of comma-separated colors; each color is either "none" or one or 616 more hex-encoded sRGB colors of the form "#RRGGBB".</td> 617</tr> 618<tr> 619 <td>marker-high-levels</td> 620 <td>A list of comma-separated "almost full" level values from 0 to 100; a 621 value of 100 should be used for supplies that are consumed/emptied like ink 622 cartridges.</td> 623</tr> 624<tr> 625 <td>marker-levels</td> 626 <td>A list of comma-separated level values for each supply. A value of -1 627 indicates the level is unavailable, -2 indicates unknown, and -3 indicates 628 the level is unknown but has not yet reached capacity. Values from 0 to 100 629 indicate the corresponding percentage.</td> 630</tr> 631<tr> 632 <td>marker-low-levels</td> 633 <td>A list of comma-separated "almost empty" level values from 0 to 100; a 634 value of 0 should be used for supplies that are filled like waste ink 635 tanks.</td> 636</tr> 637<tr> 638 <td>marker-message</td> 639 <td>A human-readable supply status message for the user like "12 pages of 640 ink remaining."</td> 641</tr> 642<tr> 643 <td>marker-names</td> 644 <td>A list of comma-separated supply names like "Cyan Ink", "Fuser", 645 etc.</td> 646</tr> 647<tr> 648 <td>marker-types</td> 649 <td>A list of comma-separated supply types; the types are listed in 650 <a href="#TABLE1">Table 1</a>.</td> 651</tr> 652</tbody> 653</table></div> 654 655<h3><a name="COMMUNICATING_BACKEND">Communicating with the Backend</a></h3> 656 657<p>Filters can communicate with the backend via the 658<a href="#cupsBackChannelRead"><code>cupsBackChannelRead</code></a> and 659<a href="#cupsSideChannelDoRequest"><code>cupsSideChannelDoRequest</code></a> 660functions. The 661<a href="#cupsBackChannelRead"><code>cupsBackChannelRead</code></a> function 662reads data that has been sent back from the device and is typically used to 663obtain status and configuration information. For example, the following code 664polls the backend for back-channel data:</p> 665 666<pre class="example"> 667#include <cups/cups.h> 668 669char buffer[8192]; 670ssize_t bytes; 671 672/* Use a timeout of 0.0 seconds to poll for back-channel data */ 673bytes = cupsBackChannelRead(buffer, sizeof(buffer), 0.0); 674</pre> 675 676<p>Filters can also use <code>select()</code> or <code>poll()</code> on the 677back-channel file descriptor (3 or <code>CUPS_BC_FD</code>) to read data only 678when it is available.</p> 679 680<p>The 681<a href="#cupsSideChannelDoRequest"><code>cupsSideChannelDoRequest</code></a> 682function allows you to get out-of-band status information and do synchronization 683with the device. For example, the following code gets the current IEEE-1284 684device ID string from the backend:</p> 685 686<pre class="example"> 687#include <cups/sidechannel.h> 688 689char data[2049]; 690int datalen; 691<a href="#cups_sc_status_t">cups_sc_status_t</a> status; 692 693/* Tell cupsSideChannelDoRequest() how big our buffer is, less 1 byte for 694 nul-termination... */ 695datalen = sizeof(data) - 1; 696 697/* Get the IEEE-1284 device ID, waiting for up to 1 second */ 698status = <a href="#cupsSideChannelDoRequest">cupsSideChannelDoRequest</a>(CUPS_SC_CMD_GET_DEVICE_ID, data, &datalen, 1.0); 699 700/* Use the returned value if OK was returned and the length is non-zero */ 701if (status == CUPS_SC_STATUS_OK && datalen > 0) 702 data[datalen] = '\0'; 703else 704 data[0] = '\0'; 705</pre> 706 707<h4><a name="DRAIN_OUTPUT">Forcing All Output to a Printer</a></h4> 708 709<p>The 710<a href="#cupsSideChannelDoRequest"><code>cupsSideChannelDoRequest</code></a> 711function allows you to tell the backend to send all pending data to the printer. 712This is most often needed when sending query commands to the printer. For example:</p> 713 714<pre class="example"> 715#include <cups/cups.h> 716#include <cups/sidechannel.h> 717 718char data[1024]; 719int datalen = sizeof(data); 720<a href="#cups_sc_status_t">cups_sc_status_t</a> status; 721 722/* Flush pending output to stdout */ 723fflush(stdout); 724 725/* Drain output to backend, waiting for up to 30 seconds */ 726status = <a href="#cupsSideChannelDoRequest">cupsSideChannelDoRequest</a>(CUPS_SC_CMD_DRAIN_OUTPUT, data, &datalen, 30.0); 727 728/* Read the response if the output was sent */ 729if (status == CUPS_SC_STATUS_OK) 730{ 731 ssize_t bytes; 732 733 /* Wait up to 10.0 seconds for back-channel data */ 734 bytes = cupsBackChannelRead(data, sizeof(data), 10.0); 735 /* do something with the data from the printer */ 736} 737</pre> 738 739<h3><a name="COMMUNICATING_FILTER">Communicating with Filters</a></h3> 740 741<p>Backends communicate with filters using the reciprocal functions 742<a href="#cupsBackChannelWrite"><code>cupsBackChannelWrite</code></a>, 743<a href="#cupsSideChannelRead"><code>cupsSideChannelRead</code></a>, and 744<a href="#cupsSideChannelWrite"><code>cupsSideChannelWrite</code></a>. We 745recommend writing back-channel data using a timeout of 1.0 seconds:</p> 746 747<pre class="example"> 748#include <cups/cups.h> 749 750char buffer[8192]; 751ssize_t bytes; 752 753/* Obtain data from printer/device */ 754... 755 756/* Use a timeout of 1.0 seconds to give filters a chance to read */ 757cupsBackChannelWrite(buffer, bytes, 1.0); 758</pre> 759 760<p>The <a href="#cupsSideChannelRead"><code>cupsSideChannelRead</code></a> 761function reads a side-channel command from a filter, driver, or port monitor. 762Backends can either poll for commands using a <code>timeout</code> of 0.0, wait 763indefinitely for commands using a <code>timeout</code> of -1.0 (probably in a 764separate thread for that purpose), or use <code>select</code> or 765<code>poll</code> on the <code>CUPS_SC_FD</code> file descriptor (4) to handle 766input and output on several file descriptors at the same time.</p> 767 768<p>Once a command is processed, the backend uses the 769<a href="#cupsSideChannelWrite"><code>cupsSideChannelWrite</code></a> function 770to send its response. For example, the following code shows how to poll for a 771side-channel command and respond to it:</p> 772 773<pre class="example"> 774#include <cups/sidechannel.h> 775 776<a href="#cups_sc_command_t">cups_sc_command_t</a> command; 777<a href="#cups_sc_status_t">cups_sc_status_t</a> status; 778char data[2048]; 779int datalen = sizeof(data); 780 781/* Poll for a command... */ 782if (!<a href="#cupsSideChannelRead">cupsSideChannelRead</a>(&command, &status, data, &datalen, 0.0)) 783{ 784 switch (command) 785 { 786 /* handle supported commands, fill data/datalen/status with values as needed */ 787 788 default : 789 status = CUPS_SC_STATUS_NOT_IMPLEMENTED; 790 datalen = 0; 791 break; 792 } 793 794 /* Send a response... */ 795 <a href="#cupsSideChannelWrite">cupsSideChannelWrite</a>(command, status, data, datalen, 1.0); 796} 797</pre> 798 799<h3><a name="SNMP">Doing SNMP Queries with Network Printers</a></h3> 800 801<p>The Simple Network Management Protocol (SNMP) allows you to get the current 802status, page counter, and supply levels from most network printers. Every 803piece of information is associated with an Object Identifier (OID), and 804every printer has a <em>community</em> name associated with it. OIDs can be 805queried directly or by "walking" over a range of OIDs with a common prefix.</p> 806 807<p>The two CUPS SNMP functions provide a simple API for querying network 808printers through the side-channel interface. Each accepts a string containing 809an OID like ".1.3.6.1.2.1.43.10.2.1.4.1.1" (the standard page counter OID) 810along with a timeout for the query.</p> 811 812<p>The <a href="#cupsSideChannelSNMPGet"><code>cupsSideChannelSNMPGet</code></a> 813function queries a single OID and returns the value as a string in a buffer 814you supply:</p> 815 816<pre class="example"> 817#include <cups/sidechannel.h> 818 819char data[512]; 820int datalen = sizeof(data); 821 822if (<a href="#cupsSideChannelSNMPGet">cupsSideChannelSNMPGet</a>(".1.3.6.1.2.1.43.10.2.1.4.1.1", data, &datalen, 5.0) 823 == CUPS_SC_STATUS_OK) 824{ 825 /* Do something with the value */ 826 printf("Page counter is: %s\n", data); 827} 828</pre> 829 830<p>The 831<a href="#cupsSideChannelSNMPWalk"><code>cupsSideChannelSNMPWalk</code></a> 832function allows you to query a whole group of OIDs, calling a function of your 833choice for each OID that is found:</p> 834 835<pre class="example"> 836#include <cups/sidechannel.h> 837 838void 839my_callback(const char *oid, const char *data, int datalen, void *context) 840{ 841 /* Do something with the value */ 842 printf("%s=%s\n", oid, data); 843} 844 845... 846 847void *my_data; 848 849<a href="#cupsSideChannelSNMPWalk">cupsSNMPSideChannelWalk</a>(".1.3.6.1.2.1.43", 5.0, my_callback, my_data); 850</pre> 851 852<h2><a name="SANDBOXING">Sandboxing on macOS</a></h2> 853 854<p>Starting with macOS 10.6, filters and backends are run inside a security "sandbox" which further limits (beyond the normal UNIX user/group permissions) what a filter or backend can do. This helps to both secure the printing system from malicious software and enforce the functional separation of components in the CUPS filter chain. What follows is a list of actions that are explicitly allowed for all filters and backends:</p> 855 856<ol> 857 858 <li>Reading of files: pursuant to normal UNIX file permissions, filters and backends can read files for the current job from the <var>/private/var/spool/cups</var> directory and other files on mounted filesystems <em>except</em> for user home directories under <var>/Users</var>.</li> 859 860 <li>Writing of files: pursuant to normal UNIX file permissions, filters and backends can read/write files to the cache directory specified by the <code>CUPS_CACHEDIR</code> environment variable, to the state directory specified by the <code>CUPS_STATEDIR</code> environment variable, to the temporary directory specified by the <code>TMPDIR</code> environment variable, and under the <var>/private/var/db</var>, <var>/private/var/folders</var>, <var>/private/var/lib</var>, <var>/private/var/mysql</var>, <var>/private/var/run</var>, <var>/private/var/spool</var> (except <var>/private/var/spool/cups</var>), <var>/Library/Application Support</var>, <var>/Library/Caches</var>, <var>/Library/Logs</var>, <var>/Library/Preferences</var>, <var>/Library/WebServer</var>, and <var>/Users/Shared</var> directories.</li> 861 862 <li>Execution of programs: pursuant to normal UNIX file permissions, filters and backends can execute any program not located under the <var>/Users</var> directory. Child processes inherit the sandbox and are subject to the same restrictions as the parent.</li> 863 864 <li>Bluetooth and USB: backends can access Bluetooth and USB printers through IOKit. <em>Filters cannot access Bluetooth and USB printers directly.</em></li> 865 866 <li>Network: filters and backends can access UNIX domain sockets under the <var>/private/tmp</var>, <var>/private/var/run</var>, and <var>/private/var/tmp</var> directories. Backends can also create IPv4 and IPv6 TCP (outgoing) and UDP (incoming and outgoing) socket, and bind to local source ports. <em>Filters cannot directly create IPv4 and IPv6 TCP or UDP sockets.</em></li> 867 868 <li>Notifications: filters and backends can send notifications via the Darwin <code>notify_post()</code> API.</li> 869 870</ol> 871 872<blockquote><b>Note:</b> 873 874<p>The sandbox profile used in CUPS still allows some actions that are not listed above - these privileges will be removed over time until the profile matches the list above.</p> 875</blockquote> 876