Home Wiki > SDB:Using Your Own Backends to Print with CUPS
Sign up | Login

SDB:Using Your Own Backends to Print with CUPS

tagline: From openSUSE


Tested on openSUSE Recommended articles Related articles
Icon-checked.png

Icon-manual.png Icon-help.png

Situation

You want to use a custom backend to print with CUPS.

Requirements:

You should have a basic knowledge of the printing system and be well acquainted with CUPS (see SDB:CUPS in a Nutshell), bash scripts, and common command-line tools.

CUPS Backends Background Information

Usually, the backend gets printer-specific data as input and sends it to the printer device. See the section "The Backends" in SDB:CUPS in a Nutshell.

The backend gets its input data either via stdin or it must read its input data from a file when the backend was called with a filename argument as argv[6].

The backend must not send its output data via stdout. Instead the backend must implement whatever is needed to send it directly to the printer device or to whatever other recipient up to any external destination (compare the section about "Allow printer admin tasks for a normal user" in SDB:CUPS in a Nutshell).

When the backend is called without an argument, it must output on stdout "device discovery" information. For example when a backend /usr/lib/cups/backend/mybackend is called without arguments it would have to output at least:

direct mybackend "Unknown" "no device info"

Usually the second field (here only "mybackend") is a specific device URI, see "The Backends" at SDB:CUPS in a Nutshell.

When the backend is called as usual by the cupsd, the normal stderr output of the backend is forwarded to /var/log/cups/error_log if there is "LogLevel debug" set in /etc/cups/cupsd.conf - see the section how to "Get CUPS debug messages if it does not work" in SDB:CUPS in a Nutshell. The stderr output of the backend can be prefixed e.g. with "INFO:" or "ERROR:" to distinguish how it is logged, see "man 7 backend" for details.

See the man pages "man 7 backend" for what is specific for CUPS backends and "man 7 filter" for general information regarding CUPS filters (backends are a special type of filters for CUPS).

Custom Backend Examples

The CUPS default backends are usually the best choice to send printing data to its recipient.

In particular when the data is sent via network the CUPS backends provide some possibilities for tuning how the data is sent to meet usual requirements. You may have a look at "Network Protocols Supported by CUPS" on http://www.cups.org/documentation.php/doc-1.4/network.html

However, custom backends might be needed in some exceptional cases.

The following examples implement the same basic functionality as the CUPS backends "usb" and "socket" to show the very basic ideas how to implement a backend for CUPS.

A careless backend for a single USB printer

The following bash script sends data to /dev/usb/lp0 which is the device file for the first USB printer regardless which USB printer model might be actually connected there.

Such a careless backend could be useful when only one USB printer will be used and a print queue should be set up but the printer is not connected when the print queue is set up. For example an admin may have to set up a print queue on a workstation for a printer which is not available at that time. In this case the CUPS backend "usb" cannot detect the printer so that it cannot report the right device URI as "device discovery" information (see above). But without the right device URI it is not possible to set up a queue with the CUPS backend "usb". In this case a careless backend could help because its device URI is always the same "usblp0:/dev/usb/lp0" regardless which USB printer might become connected later. The drawback is that the careless backend sends any data to any connected USB printer regardless if the data matches the printer model which may cause that tons of sheets get printed with nonsense.

See SDB:Installing a Printer how you can test if the first USB printer is accessible via /dev/usb/lp0.

 #! /bin/bash
 # Have debug info in /var/log/cups/error_log:
 set -x
 # Output "device discovery" information on stdout:
 if test "$#" = "0"
 then echo 'direct usblp0:/dev/usb/lp0 "Unknown" "any USB printer"'
      exit 0
 fi
 # Have the input at fd0 (stdin) in any case:
 if test -n "$6"
 then exec <"$6"
 fi
 # Infinite retries to access the device file:
 until cat /dev/null >/dev/usb/lp0
 do echo 'INFO: cannot access /dev/usb/lp0 - retry in 30 seconds' 1>&2
    sleep 30
 done
 echo 'INFO: sending data to /dev/usb/lp0' 1>&2
 # Forward the data from stdin to the device file:
 if cat - >/dev/usb/lp0
 then echo 'INFO:' 1>&2
      exit 0
 else echo 'ERROR: failed to send data to /dev/usb/lp0' 1>&2
      exit 1
 fi
 

Install this script as /usr/lib/cups/backend/usblp0 with the same owner, group and permissions as the other backends (usually "rwxr-xr-x root root").

Run as root "lpinfo -v" and verify that it reports "direct usblp0:/dev/usb/lp0", otherwise the device URI "usblp0:/dev/usb/lp0" of the new backend could not be used to set up a print queue.

See the section "How to set up a print queue in full compliance with CUPS" in SDB:CUPS in a Nutshell.

A hardcoded backend for a network printer

The following bash script sends data to the hardcoded IP address 192.168.1.2 to TCP port 9100 which is the usual port for network printers which support data transfer via a plain TCP socket, see SDB:Printing via TCP/IP network.

 #! /bin/bash
 # Have debug info in /var/log/cups/error_log:
 set -x
 # Output "device discovery" information on stdout:
 if test "$#" = "0"
 then echo 'network mysocket://192.168.1.2:9100 "Unknown" "192.168.1.2:9100"'
      exit 0
 fi
 # Set INPUTFILE to where the input comes from:
 INPUTFILE="-"
 if test -n "$6"
 then INPUTFILE="$6"
 fi
 # 5 retries with 60 seconds delay to access the remote port:
 for I in first second third fourth last
 do if netcat -z 192.168.1.2 9100
    then break
    fi   
    echo "INFO: 192.168.1.2:9100 busy - $I of 5 retries" 1>&2
    sleep 60
 done
 sleep 1
 if netcat -z 192.168.1.2 9100
 then echo 'INFO: sending data to 192.168.1.2:9100' 1>&2
      sleep 1
 else echo 'ERROR: failed to access 192.168.1.2:9100' 1>&2
      exit 1
 fi
 # Send the data to the remote port:
 if cat $INPUTFILE | netcat -w 1 192.168.1.2 9100
 then echo 'INFO:' 1>&2
      exit 0
 else echo 'ERROR: failed to send data to 192.168.1.2:9100' 1>&2
      exit 1
 fi
 

Adapt "192.168.1.2" to the IP address of yout network printer. Additionally you may have to adapt the port "9100" according to what your network printer uses. You may have a look at "Common Network Printer URIs" on http://www.cups.org/documentation.php/doc-1.4/network.html

Install this script as /usr/lib/cups/backend/mysocket with the same owner, group and permissions as the other backends (usually "rwxr-xr-x root root").

Run as root "lpinfo -v" and verify that it reports "network mysocket://192.168.1.2:9100", otherwise the device URI "mysocket://192.168.1.2:9100" of the new backend could not be used to set up a print queue.

See the section "How to set up a print queue in full compliance with CUPS" in SDB:CUPS in a Nutshell.

You may wonder why there are "sleep" delays between consecutive netcat calls. This avoids failures in the backend when the receiver in the printer's network interface is too slow to accept consecutive connections.

You may try to speed it up by using the following optimistic backend for a network printer which does nothing else than to send the data:

 #! /bin/bash
 # Have debug info in /var/log/cups/error_log:
 set -x
 # Output "device discovery" information on stdout:
 if test "$#" = "0"
 then echo 'network mysocket://192.168.1.2:9100 "Unknown" "192.168.1.2:9100"'
      exit 0
 fi
 # Set INPUTFILE to where the input comes from:
 INPUTFILE="-"
 if test -n "$6"
 then INPUTFILE="$6"
 fi
 # Send the data to the remote port:
 echo 'INFO: sending data to 192.168.1.2:9100' 1>&2
 if cat $INPUTFILE | netcat -w 1 192.168.1.2 9100
 then echo 'INFO:' 1>&2
      exit 0
 else echo 'ERROR: failed to send data to 192.168.1.2:9100' 1>&2
      exit 1
 fi
 

Probably under higher load when several print jobs have to be sent continuously to the printer i.e. when speed does actually matter, then such kind of optimistic backend may fail depending on whether or not the printer device accepts consecutive connections. If it fails in this case you need "sleep" delays to get it sufficiently fail-safe so that there is a dilemma between speed and reliability - at least for such simple custom-made backends (cf. "fast good cheap: pick any two").

Backends to send data to other recipients

The openSUSE RPM package cups-backends provides the bash script /usr/lib/cups/backend/pipe which is a wrapper backend for printing to any program. It forwards the print job data like a pipe into another command. You may have a look at its code for an example that CUPS backends are not restricted to send data only to printer devices.

The RPM package cups-pdf which is available via the openSUSE build service project "Printing" provides /usr/lib/cups/backend/cups-pdf which saves the data in a file, see SDB:Printing to PDF.

See also

Portal:Printing