The FreeBSD Corporate Networker's Guide
Prev | Chapter 8 Printserving | Next

# 8.7 Setting up LPR/LPD on FreeBSD

When a FreeBSD system is booted, it starts the LPD spooler control daemon program if the `/etc/rc.conf` file has `lpd_enable="YES"` set. If this is not set, attempts to print through and from the FreeBSD system will fail with an “`lpr: connect: No such file or directory`” error message.

The LPD program manages all incoming print jobs, whether they come in from the network, or from local users on the UNIX system. It transfers print jobs to all locally attached parallel or serial printers, as well as defined remote printers. Several programs also are used to manipulate jobs in the print spools that LPD manages, as well as the user programs to submit them from the UNIX command prompt. All of these programs use the `/etc/printcap` file, which is the master control file for the printing system.

Back when printing was mostly text, it was common to place printers on a serial connection that stretched for long distances. Often, 9600bps was used because it could work reliably up to a block away, which allowed printers to be located almost anywhere on an office high-rise floor. Modern office print jobs, on the other hand, are generally graphics-laden and tend to be rather large. These jobs would take hours to transfer over a slower 9600bps serial printer connection. Today, most printers that are not connected to a remote hardware print server box are directly connected to the server using parallel cables. All of the examples shown here are direct connections that are parallel connections.

The `printcap` configuration file, like most UNIX configuration files, indicates comment lines starting with a hash character. Lines without a hash character are meant to be part of a printer queue description line. Each printer queue description line starts with a symbolic name, and ends with a newline. Since the description lines are often quite long, they are often written to span multiple lines by escaping intermediate newlines with the backslash (`\`) character. The `/etc/printcap` file, as supplied, defines a single printer queue, `lp`. The `lp` queue is the default queue. Most UNIX-supplied printing utilities send print output to this queue if no printer is specified by the user. It should be set to point to the most popular print queue with local UNIX print users, (i.e., users that have shell accounts).

The layout of `/etc/printcap` is covered in the manual page, which is reached by running the `man printcap` command. The stock `/etc/printcap` file at the line defining the spool `lp` shows:

    #
    lp|local line printer:\
        :lp=/dev/lpt0:sd=/var/spool/output/lpd:lf=/var/log/lpd-errs:
    #

In this example the first line defines the names by which the printer is known, and ends with an escaped newline. The next line defines the physical device, the PC parallel port, by `/dev/lpt0`, and the directory in which the spool files are stored at `/var/spool/output/lpd`, and the error log file. Note that this particular error log file will not show all LPD errors, such as bad job submittals, it usually shows only the errors that originate within the printing system itself.

In general, the administrator creates two print queues for every printer that is connected to the FreeBSD machine. The first queue entry contains whatever additional capabilities UNIX shell users on the server require. The second is a raw queue that performs no print processing on the incoming print job. This queue is used by remote clients, such as Windows clients, that format their own jobs.

If the administrator is setting up the printer to allow incoming LPR jobs from network clients, such as other Windows or UNIX systems, those systems must be listed in `/etc/hosts.lpd`.

## 8.7.1 Creating the spools

Building new print spools is merely a matter of making an entry in the `/etc/printcap` file, creating the spool directories, and setting the correct permissions on them. For example, the following additional line defines a PostScript printer named NEC (in addition to the `lp` definition):

    #
    lp|local line printer:\
        :lp=/dev/lpt0:sd=/var/spool/output/lpd:lf=/var/log/lpd-errs:

    NEC|NEC Silentwriter 95 PostScript printer:\
        :lp=/dev/lpt0:sd=/var/spool/output/NEC:lf=/var/log/lpd-errs:
    #

Because UNIX is case sensitive, NEC is different from `nec` in both the name of the printer and the name of the Spool directory. With the print spooler LPD, the Spool directories must be different from each other, or the spooler gets confused and doesen't print.

After the `/etc/printcap` is modified, the root user must create the `/var/spool/output/NEC` directory and assign ownership of it to the `bin` user, assign group ownership to `daemon`, and set permissions with the following commands:

    % su root
    # cd /var/spool/output
    # mkdir NEC
    # chown bin NEC
    # chgrp daemon NEC
    # chmod 755 NEC

## 8.7.2 Additional spool capabilities

Because modern print jobs (especially PostScript) can sometimes reach hundreds of megabytes, the `sd` capability entry in the `/etc/printcap` file should always point to a Spool directory on a filesystem that has enough space. The `/var` directory on a default FreeBSD installation is generally set to a fairly small amount, which can easily overflow the spool. There are four ways to handle this problem:

1. During FreeBSD installation, if the administrator knows a lot of print jobs are going to go through the spooler, `/var` should be set to a large amount of free space.

2. Modify the `sd` capability in the `/etc/printcap` file to point to a spool directory in a different, larger filesystem, such as `/usr/spool`.

3. Use soft links to point the `/var/spool/output` directory to directories on a larger filesystem.

4. Don't define a `/var` directory at all during FreeBSD installation; this would make the installer link `/var` to `/usr/var`.

In addition to spools, the following other capabilities are usually placed in a production `/etc/printcap` file.

The entry `fo` prints a form feed when the printer is opened. It is handy for HPPCL (HP LaserJets) or other non-PostScript printers that are located behind electronic print sharing devices. It can also be used for printers that accept input from multiple connections, such as a parallel port, serial port, and localtalk port. An example is an HP LaserJet with an MIO card in it plugged into both Ethernet and LocalTalk networks. It will clear any garbage out of the printer before the job is processed.

The entry `mx` defines the maximum size of a print job, which is a must for modern print jobs that frequently grow far past the default print size of a megabyte. The original intent of this capability was to prevent errant programs from stuffing the spool with jobs so large that they would use up all paper in a printer. Graphics-heavy print jobs have made it impossible to depend on this kind of space limitation, so `mx` is usually set to zero, which turns it off.

The entry `sh` suppresses printing of banner pages in case the printer cannot handle ASCII and the client mistakenly requests them.

The entry `ct` denotes a TCP Connection timeout. This is useful if the remote print server doesn't close the connection properly.

> Note: FreeBSD 2.2.5 contains a bug in the LPD system - as a workaround the `ct` capability needs to be set very large, such as 3600, or the appropriate patch installed and LPD recompiled. More recent versions of FreeBSD do not have this bug.

## 8.7.3 Printing to hardware print server boxes or remote print servers.

Hardware print server boxes, such as the HP JetDirect internal and external cards, need some additional capabilities defined in the `/etc/printcap` entry; `rp`, for remote print spool, and `rm` for remote machine name.

The `rm` capability is simply the DNS or `/etc/hosts` name of the IP number associated with the remote printserver device. Obviously, print server devices, such as the HP JetDirect, must not use a dynamic TCP/IP network numbering assignment. If they get their numbering via DHCP, the IP number should be assigned from the static pool; it should always be the same IP number.

Determining the name used for `rp`, on the other hand, can be rather difficult. Here are some common names:

Windows NT Server: Printer name of the printer icon created in Print Manager

FreeBSD: Print queue name defined in `/etc/printcap`

HP JetDirect: Either the name `TEXT` or the name `RAW`. `TEXT` automatically converts incoming UNIX newline text to DOS-like CR/LF text that the printer can print. `RAW` should be used for PostScript, and HPPCL printing.

HP JetDirect EX +3: External, 3 port version of the JetDirect. Use `RAW1`, `RAW2`, `RAW3`, `TEXT1`, `TEXT2`, or `TEXT3` depending on the port desired.

Intel NetPort: Either use `TEXT` for UNIX text conversion printing or use `PASSTHRU` for normal printing.

DPI: Use `PORT1` or `PORT2` depending on which port the printer is plugged into.

For other manufacturer's print servers refer to the manuals supplied with those devices.

The following is an example printcap that redefines the default `lp` print queue to send print jobs to the first parallel port on a remote HP LaserJet plugged into a JetDirect EX +3 named `floor2hp4.biggy.com`.

    #
    lp|local line printer:\
        :rm=floor2hp4.biggy.com:rp=RAW1:\
        :sd=/var/spool/output/lpd:\
        :lf=/var/log/lpd-errs:
    #

> Note: The `rp` capability must be defined or the job goes to the default print queue on the remote host. If the remote device does not have a single print queue, such as another UNIX system, this causes problems. For example, if the remote device was a JetDirect EX + 3 and `rp` was omitted, all queues defined would print out of the first parallel port.

## 8.7.4 Filters

The last two important printcap capabilities concern print filters, `if` (input filter) and `of` (output filter). If defined, incoming print jobs are run through the filters that these entries point to for further processing.

Filters are the reason that the UNIX print spooling system is so much more powerful than any other commercial server operating system. Under FreeBSD, incoming print jobs are acted on by any filters specified in the `/etc/printcap` no matter where they originate. Incoming print jobs from remote Windows, Mac, NT, OS/2 or other clients can be intercepted and manipulated by any program specified as a filter. Want a PostScript Printer? There's a filter that adds PostScript capability to a non-PostScript printer. Want to make a cheap Epson MX 80 dot-matrix emulate an expensive Okidata Microline dot-matrix for some archaic mainframe application? Write a filter that will rewrite the print codes to do it. Want custom-built banner pages? Use a filter. Many UNIX `/etc/printcap` filters on many Internet sites can do a variety of interesting and unique things. Someone may have already written a filter that does what you want!

### 8.7.4.1 Types of Filters

Three types of filters can be defined in the `/etc/printcap` file. In this book all filter examples are for Input filters.

#### 8.7.4.1.1 Input Filters

Input filters are specified by the `if` capability. Every job that comes into the spool is acted on by any filter specified in the `if` entry for that spool. Virtually all filters that an administrator would use are specified here. These filters can be either shell scripts, or compiled programs.

#### 8.7.4.1.2 Fixed Filters

Fixed filters are specified by separate capabilities, such as `cf`, `df`, and `gf`. Mostly, these exist for historical reasons. Originally, the idea of LPD was that incoming jobs would be submitted with the type fields set to trigger whatever filter was desired. However, type codes are confusing and annoying to the user, who has to remember which option is needed to trigger which type. It is much easier to set up multiple queues with different names, and this is what most sites do these days. For example, originally a DVI fixed filter might be specified in a spool for `lp`, triggered by the `-d` option passed to `lpr`. Jobs without this option aren't acted on by the DVI filter. However, the same thing can be done by creating a queue named `lp` that doesn't have a DVI filter, and a queue named `lpdvi` which has the DVI filter specified in the `if` capability. Users just need to remember which queue to print to, instead of what option needed for this or that program.

#### 8.7.4.1.3 Output Filters

These are specified by the `of` capability. Output filters are much more complicated than input filters and are hardly ever used in normal circumstances. They also generally require a compiled program somewhere, either directly specified or wrapped in a shell script, since they have to do their own signal-handling.

### 8.7.4.2 Printing Raw UNIX Text with a Filter

One of the first things that a new UNIX user will discover when plugging a standard LaserJet or impact printer into a UNIX system is the stairstep problem. The symptom is that the user dumps text to the printer, either through LPR or redirection (by catting it to the parallel device) and instead of receiving the expected Courier 10-point printout, gets a page with a single line of text, or two lines of text "stairstepped", text and nothing else.

The problem is rooted in how printers and UNIX handle textfiles internally. Printers by and large follow the "MS-DOS Textfile" convention of requiring a carriage return, then a linefeed, at the end of every text line. This is a holdover from the early days when printers were mechanical devices, and the print head needed to return and the platen to advance to start a new line. UNIX uses only the linefeed character to terminate a text line. So, simply dumping raw text out the parallel port works on MS-DOS, but not on UNIX.

If the printer is a PostScript printer, and doesn't support standard ASCII, then dumping UNIX text to it doesn't work. But then, neither would dumping MS-DOS text to it. (Raw text printing on PostScript printers is discussed later in this chapter.) Note also that if the printer is connected over the network to an HP JetDirect hardware print server, internal or external, the TEXT queue on the hardware print automatically adds the extra Carriage Return character to the end of a text line.

If the printer is the garden-variety HP LaserJet, DeskJet, or an impact printer, and under DOS the administrator is used to printing raw text from the command line for directory listings, there are two ways to fix stairstep. The first is to send a command to the printer to make it print in "unix textfile" mode, which makes the printer supply its own carriage return. This solution is ugly in a printer environment with UNIX and Windows machines attempting to share use of the same printer. Switching the printer to work with UNIX disrupts DOS/Windows raw text printouts.

The better solution is to use a simple filter that converts incoming text from UNIX style to DOS style. The following filter posted on questions@FreeBSD.org and the sample `/etc/printcap` entry can be used to do this:

    #!/bin/sh
    # /usr/local/libexec/crlfilter
    #
    # simple parlor trick to add CR to LF for printer
    # Every line of standard input is printed with CRLF
    # attached.
    #

    awk '{printf "%s\r\n", $0}' -

An alternative filter posted using sed could be written as:

    #!/bin/sh
    # /usr/local/libexec/crlfilter
    #
    # Add CR to LF for printer
    # Every line of standard input is printed with CRLF
    # attached.
    #
    # Note, the ^M is a *real* ^M (^V^M if your typing in vi)
    #

    sed 's/$/^M/' -

Here is an example of a filter that triggers the printers automatic LF-to-CR/LF converter (this option is only useful on HP LaserJets that support this command):

    #!/bin/sh
    # Simply copies stdin to stdout.  Ignores all filter
    # arguments.
    # Tells printer to treat LF as CR+LF.  Writes a form feed
    # character after printing job.

    printf "\033&k2G" && cat && printf "\f" && exit 0

    exit 2

The printcap file used to trigger the filter is:

    #/etc/printcap
    # The trailer (tr) is used when the queue empties.  I found that the
    # form feed (\f) was basically required for the HP to print properly.
    # Banners also need to be shut off.
    #

    lp|local line printer:\
        :lp=/dev/lpt0:sd=/var/spool/output/lpd:lf=/var/log/lpd-errs:
        :if=/usr/local/libexec/crlfilter:sh:tr=\f:mx#0:
    #

### 8.7.4.3 The `pr` filter

Although most filters are built by scripts or programs and are added to the UNIX machine by the administrator, there is one filter that is supplied with the FreeBSD operating system is very useful for raw text files: the `pr` filter. It is most commonly used when printing from the UNIX command shell. The `pr` filter paginates and applies headers and footers to ASCII text files. It is automatically invoked with the `-p` option used with the `lpr` program at the UNIX command prompt.

The `pr` filter is special - it runs in addition to any input filters specified for the print queue in `/etc/printcap`, if the user sets the option for a print job. This allows headers and pagination to be applied in addition to any special conversion, such as CR to CR/LF that a specified input filter may apply.

### 8.7.4.4 Printing PostScript Banner Pages with a Filter.

Unfortunately, the canned banner page supplied in the LPD program prints only on a text-compatible printer. If the attached printer understands only PostScript and the administrator wants to print banner pages, it is possible to install a filter into the `/etc/printcap` file to do this.

The following filter is taken from the FreeBSD Handbook. I've slightly changed its invocation for a couple of reasons. First, some PostScript printers have difficulty when two print files are sent within the same print job or they lack the trailing Control-D. Second is that the handbook invocation uses the LPRPS program, which requires a serial connection to the printer.

The following filter shows another trick: calling LPR from within a filter program to spin off another print job. Unfortunately, the problem with using this trick is that the banner page always gets printed after the job. This is because the incoming job spools first, and then FreeBSD runs the filter against it, so the banner page generated by the filter always spools behind the existing job.

There are two scripts, both should be put in the `/usr/local/libexec` directory, and the modes set to executable. The `printcap` also must be modified to create the nonbanner and banner versions of the print queue. Following the scripts is the `/etc/printcap` file showing how they are called. Notice that the `sh` parameter is turned on since the actual printed banner is being generated on the fly by the filter:

    #!/bin/sh
    # Filename /usr/local/libexec/psbanner
    # parameter spacing comes from if= filter call template of:
    # if -c -w -l -i -n login -h host
    # parsing trickiness is to allow for the presence or absence of -c
    # sleep is in there for ickiness of some PostScript printers

    for dummy
    do
      case "$1" in
        -n) alogname="$2" ;;
        -h) ahostname="$2" ;;
      esac
      shift
    done

    /usr/local/libexec/make-ps-header $alogname $ahostname "PostScript" | \
      lpr -P lpnobanner

    sleep 10

    cat && exit 0

Here is the `make-ps-header` listing.

    #!/bin/sh
    # Filename /usr/local/libexec/make-ps-header
    #
    # These are PostScript units (72 to the inch).  Modify for A4 or
    # whatever size paper you are using:
    #

    page_width=612
    page_height=792
    border=72

    #
    # Save these, mostly for readability in the PostScript, below.
    #

    user=$1
    host=$2
    job=$3
    date=`date`

    #
    # Send the PostScript code to stdout.
    #

    exec cat <<EOF
    %!PS
    %
    % Make sure we do not interfere with user's job that will follow
    %

    %
    % Make a thick, unpleasant border around the edge of the paper.
    %

    $border $border moveto
    $page_width $border 2 mul sub 0 rlineto
    0 $page_height $border 2 mul sub rlineto
    currentscreen 3 -1 roll pop 100 3 1 roll setscreen
    $border 2 mul $page_width sub 0 rlineto closepath
    0.8 setgray 10 setlinewidth stroke 0 setgray

    %
    % Display user's login name, nice and large and prominent
    %

    /Helvetica-Bold findfont 64 scalefont setfont
    $page_width ($user) stringwidth pop sub 2 div $page_height 200 sub moveto
    ($user) show

    %
    % Now show the boring particulars
    %

    /Helvetica findfont 14 scalefont setfont
    /y 200 def
    [ (Job:) (Host:) (Date:) ] {
    200 y moveto show /y y 18 sub def
    } forall
    /Helvetica-Bold findfont 14 scalefont setfont
    /y 200 def
    [ ($job) ($host) ($date) ] {
    270 y moveto show /y y 18 sub def
    } forall

    %
    % That is it
    %

    showpage

Here is the `/etc/printcap` file.

    #
    lp|local line printer, PostScript, banner:\
      :lp=/dev/lpt0:sd=/var/spool/output/lpd:lf=/var/log/lpd-errs:\
      :if=/usr/local/libexec/psbanner:sh:mx#0:

    lpnobanner|local line printer, PostScript, no banner:\
      :lp=/dev/lpt0:sd=/var/spool/output/lpd-noban:\
      :lf=/var/log/lpd-errs:sh:mx#0:
    #

Prev | Home | Next
Checking PostScript Printer capabilities | Up | Printer Accounting

This, and other documents, can be downloaded from ftp://ftp.FreeBSD.org/pub/FreeBSD/doc/.

For questions about FreeBSD, read the documentation before contacting <questions@FreeBSD.org>.
For questions about this documentation, e-mail <doc@FreeBSD.org>.