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:
#

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>.