Next Previous Contents

5. /etc/printcap Print Spool Database File

The heart of the LPRng system is the file /etc/printcap. This file contains a list of printer definitions. I'll describe a few simple configurations. I would advise you to read through all of them, as I will introduce several features gradually throughout this section.

For details about individual printcap items, see the printcap(5) man page from the LPRng distribution, or entries in the index section.

5.1 Index To All The Options


Option
Meaning
abalways print banner, ignore lpr -h option
achkquery accounting server when connected
aeaccounting at end (see also af, la, ar, as)
afname of accounting file (see also la, ar)
ahautomatically hold all jobs
allow_duplicate_argsAllow duplicate command line arguments (legacy requirement)
allow_getenvAllow use of LPD_CONF
allow_user_loggingallow users to request logging info using lpr -mhost%port
arenable remote transfer accounting (if af is set)
asaccounting at start (see also af, la, ar)
beBanner at End Generation Program
bkBerkeley LPD job file format
bk_filter_optionsBerkeley LPD filter options
bk_of_filter_optionsBerkeley LPD OF filter options
bkfbackwards-compatible filters: use simple paramters
blshort banner line sent to banner printer
bpBanner Generation Program (see bs, be)
bqUse filters on bounce queue jobs
brSerial port bit rate (see ty)
break_classname_priority_linkDon't default priority from classname (legacy requirement)
bsBanner at Start Generation Program
cdcontrol directory
check_for_nonprintableLPR checks for nonprintable file
check_idleprogram used to check for idle printer
class_in_statusShow job class name in lpq status information
classname_lengthMaximum length of classname argument (legacy requirement)
cmcomment identifying printer (LPQ)
cocost in dollars per thousand pages
config_fileconfiguration file
connect_graceconnection control for remote printers
connect_intervalconnection control for remote printers
connect_timeoutconnection control for remote printers
connect_tryconnection control for remote printers
control_filtercontrol file filter
dbdebug options for queue
default_authdefault authentication
default_formatdefault job format
default_logger_portdefault port for logging info
default_logger_protocoldefault protocol for logging info
default_permissiondefault permission for files
default_printerdefault printer
default_prioritydefault job priority
default_remote_hostdefault remote host
default_tmp_dirdefault directory for temp files
destinationsprinters that a route filter may return and we should query
direct_readfilter input is direct from data file
fcOBSOLETE, see sy
fdforwarded jobs not accepted
ffstring to send for a form feed
filter_ld_pathfilter LD_LIBRARY_PATH value
filter_optionsfilter options
filter_pathfilter PATH environment variable
fix_bad_jobfix bad job files
fosend form feed when device is opened
force_fqdn_hostnameforce FQDN HOST value in control file
force_localhostforce clients to send all requests to localhost
force_queuenameforce use of this queuename if none provided
forward_authdo server to server authentication if authenticated by user
fqsend form feed when device is closed
fsOBSOLETE (see sy)
full_timeuse extended time format
fxvalid output filter formats
generate_bannerforce a banner to be generated
groupEffective Group ID (EGID) for SUID ROOT programs
hlHeader (banner) last, at end of job
Ignore control file and data file name format errors
ignore_requested_user_priorityIgnore requested user priority
ifdefault (f, l) filter program
ipv6using IPV6 conventions
kerberos_keytabkerberos keytab file location
kerberos_lifekerberos key lifetime
kerberos_renewkerberos key renewal time
kerberos_server_principlekerberos remote server principle name
kerberos_servicekerberos default service
laenable local printer accounting (if af is set)
ldleader string sent on printer open
lferror log file for spool queue
lklock the IO device
localhostlocalhost name for DNS queries
lockfilelpd lock file
logfilelpd log file
logger_destinationadditional destination for logging information
longnumberuse long job number when a job is submitted
lpprinter device name or specification
lpd_force_pollforce lpd to poll idle printers
lpd_poll_timeinterval between lpd printer polls
lpd_portlpd listening port
lpd_printcap_pathlpd printcap path
lpr_bouncelpr does filtering as in bounce queue

mail_from
mail user from user name
mail_operator_on_errormail to this operator on error
max_connect_intervalmaximum time between connection attempts
max_log_file_sizemaximum size (in K) of spool queue log file
max_servers_activemaximum number of lpd queue servers that can be active
max_status_linemaximum length of status line
max_status_sizemaximum size (in K) of status file
mcmaximum copies allowed
miminimum space (Kb) to be left in spool filesystem
min_log_file_sizeminimum size (in K) of spool queue log file
min_status_sizeminimum size to reduce status file to
minfreeminimum amount of free space needed
mlminimum number of printable characters for printable check
msstty options for serial lines (ms is an alias for sy)
ms_time_resolutionmillisecond time resolution
mxmaximum job size (1Kb blocks, 0 = unlimited)
nbuse nonblocking device open
network_connect_gracepause between transferring jobs to remote printer
nwspool dir is on an NFS file system
ofbanner output filter
of_filter_optionsOF filter options
originate_portoriginate connections from these ports
pass_envclients pass these environment variables to filters
perms_pathlpd.perms files
plpage length (in lines)
prpr program for p format
printcap_path/etc/printcap files
psprinter status file name
pwpage width (in characters)
pxpage width in pixels (horizontal)
pypage length in pixels (vertical)
qqput queue name in control file
remote_supportoperations allowed to remote host
remote_userremote-queue machine (hostname) (with rm)
report_server_asserver name for status reports
retry_econnrefusedRetry on connect ECONNREFUSED errors
retry_nolinkRetry device open or connect failures
return_short_statusreturn short lpq status when request arrives from specified host
reuse_addrset SO_REUSEADDR on outgoing ports
reverse_lpq_formatreverse lpq format when request arrives from specified host
rgclients allow only users in this group access to printer
rmremote machine (hostname) (with rp)
routerrouting filter, returns destinations
rpremote printer name (with rm)
rtnumber of times to (re)try printing or transfer (0=infinite)
rwopen printer for reading and writing
safe_charsadditional safe characters in control file lines
save_on_errorsave job when an error
save_when_donesave job when done
sbshort banner (one line only)
scsuppress multiple copies
sdspool directory pathname
send_block_formatsend block of data, rather than individual files
send_data_firstsend data files first in job transfer
send_failure_actionfailure action to take after send_try attempts failed
send_job_rw_timeoutprint job read/write timeout
send_query_rw_timeoutstatus query operation read/write timeout
send_try(alias for rt)
sendmailsendmail program
server_auth_commandserver authenticate transfer program
server_tmp_dirserver temporary file directory
server_userserver user name for authentication
sfsuppress form feeds separating data files in job
shsuppress header (banner) pages
short_status_lengthshort lpq status length in lines
socket_lingerset the SO_LINGER socket option
spool_dir_permsspool directory permissions
spool_file_permsspool file permissions
spread_jobsamount to spread jobs to avoid collisions
ssname of queue that server serves (with sv)
stalled_timetime after which to report active job stalled
stop_on_abortstop processing queue on filter abort
svnames of servers for queue (with ss)
systty commands to set output line characteristictics
syslog_devicename of syslog device
trtrailer string to send before closing printer
translate_formattranslate data format in control file
tystty commands to set serial line characteristictics
use_authauthentication type to use
use_dateforce date in control file
use_identifierforce identifier in control file
use_info_cacheread and cache information
use_queuenameput queue name in control file (alias for qq)
use_shorthostUse short hostname for lpr control and data file names
userEffective User ID (EUID) for SUID ROOT programs
user_auth_commandclient side authentication program
user_lpcallow selected lpc commands on user files
xcOBSOLETE (see sy)
xsOBSOLETE (see ty)
xtformats supported on printer

5.2 A very simple example

Options used:

The easiest configuration is this: a local printer directly connected to a printer port.

# Local ASCII printer
lp1|printer
  # alias is printer or lp
  |lp
  :lp=/dev/lp1
  :cm=Dumb printer
  :sd=/var/spool/lpd/lp1
  :lf=log:af=acct
  :if=/usr/local/sbin/lpf
  :mx#0:sh:sf

I'll use this simple example to explain the basics of the LPRng printcap format.

Lines starting with a # sign are comments, and all leading and trailing whitespace, i.e. - spaces, tabs, etc, are ignored. Empty lines are ignored as well.

Note that you are not required to use continuation backslashes (\) as would be needed for an BSD LPR style printcap file in the printcap entries. You are allowed to use them, however: this comes in handy if you happen to have a BSD LPR printcap file. The backslash will cause the next line to be appended to the current line; watch out for comments and ends of printcap entries if you use this.

A printer (printcap entry) definition starts with the printer name, followed by one or more aliases, followed by a list of options. The name for a printer will be used be in LPRng status output to identify the printer.

The colon (:) is used to start the definition of an option or flag values. The definition continues to the end of the line or until the next colon. If an option value contains a colon, then use \: to escape the value. Options take the form of a keyword/value pair. Options and values are case sensitive; for example Ts and ts refer to different options. Numerical values are indicated by a # separator, while a = indicates a string value. Boolean switches or flags are set TRUE if no value follows the keyword and FALSE by appending a @. For example sh will set the sh flag to TRUE and sh@ to FALSE.

There may be several options on the same line, separated by colons. However, this does make the file less readable. The next tip was supplied by James H. Young <jhy@gsu.edu>:

My personal preference for readability is to always put each option on its own line. Putting each option on its own line is worth the trouble even though it detracts from the usability of certain grepping techniques when trying to maintain these types of files.

Let's go over the options used in this example:

  1. First of all, there's the lp entry. It specifies the file (or location) to which data is sent. Here, it is the device /dev/lp1. (This is system-dependent---check your vendor's manual for information on devices.) The absolute pathname indicates we have a local printer or file.
  2. The cm field can be used to supply a comment to be used as a header in lpq output.
  3. sd specifies the spool directory. This is where files are stored until they are printed.
  4. The lf and af options specify the location for the log and accounting files, respectively. They are not strictly needed for the printer to function, but a log file is highly recommended, in case things go wrong. One special point to note: if these files don't exist, they will not be created, and no logging or accounting will be done. You will need to create them manually (e.g., by using touch) or by using the <tt>checkpc</tt> program. LPRng is very fussy about permissions - you should run checkpc to make sure that the log and status files have the correct ownership and permissions.
  5. The if entry specifies a `filter' for the `text' format. The filter will be applied to any incoming job (of a certain format), even the ones coming from another host. Filters and print formats are discussed in section Filters. This particular filter would translate UNIX LF line endings to DOS CR/LF, to prevent the `staircase effect' and expand tabs to spaces. As this problem also occurs with other printing daemons, it is likely that your system has a similar utility.
  6. mx indicates the maximum file size for a print job. Supplying 0 here means that there is no limit.
  7. Finally, I have given the sh (suppress headers) flag. This will inhibit banner pages.

5.3 A serial printer queue

Options used:

When connecting to a serial printer, you need to set up the serial line so that it will pass data without any additional processing. This will allow binary files to be transferred safely. The sy printcap entry specifies a set of stty(1) flags and line speed that will be used to set up the serial line. (See Serial Printers for details) The br (bit rate) option can be used to specify the line speed as well. The following is a typical printcap for a serial printer.

# Local Serial ASCII printer
lp2
  :lp=/dev/ttya
  :cm=Serial printer
  :sd=/var/spool/lpd/lp2
  :sy=9600 -echo -crmod -raw -oddp -evenp pass8 cbreak ixon
  :if=/usr/local/sbin/lpf
  :mx#0:sh

5.4 A remote printer queue

Options used:

LPRng will allow you to print over a TCP/IP network, just like the BSD LPR software. The machine from which you are printing is called the local or client host. You run the lpr program on the client host to send print jobs to the lpd daemon or server process running on the remote server host. The lpd daemon accepts jobs for printing, puts them in its print queue, and processes them for printing on a printer.

This operation of having a user (client) program send requests or jobs to another (server) program which does the actual work is called the client/server model of computing. The lpd daemon process is the server, and the lpr, lpq, lprm, and lpc are client programs. The clients send either jobs or service requests to the server using the RFC1179 protocol.

One of the major ways that LPRng differs from the BSD LPD software is that the client programs are vastly more powerful and flexible. The BSD LPR software client programs transferred print jobs to spool (queue) directories on the client's host machine; the lpd server running on the client machine would then take these files and transfer them to the remote lpd server. This meant that every host was required to run an lpd daemon process and to have spool queues for every printer that the user could possibly use.

Imagine the problems that exist in a facility with literally thousands of printers, many of which are being added or removed from service on a daily basis. This would require the system administrators to create spool directories on each host. The LPRng software eliminates these problems by allowing client programs to directly transfer jobs to a remote lpd daemon or printer over a TCP/IP network connection. This eliminates both the need for a lpd daemon as well as the print spool directories on the local host.

To send a job to a printer or remote `lpd' daemon all you need to do is specify the printer name and server hostname or IP address. This can be done using:

lpr -Ppr@server printfile

This will cause the lpr program to transfer the printfile to the lpd daemon running on the server host, and spool it for the pr printer. If you set the PRINTER environment variable, then you can simply do the following:

PRINTER=pr@server; export PRINTER;
lpr printfile

5.5 Network Printing With /etc/printcap

While the above method is the simplest way to use the LPRng software for network printing, some users discover that they need the full power of a lpd server running on their local host to support printing, or need more than the simple transfer and network capabilities of the client programs. Another problem is that they sometimes need to have aliases for their printer names, or want to be able to specify just the name of the printer and have the remote server for it supplied by the printing software.

This type of operation requires setting up an /etc/printcap file and using the printcap configuration information. Both the client programs (lpr, lpc, lprm, lpq) and the lpd daemon use the information in the /etc/printcap file to specify how they will process print jobs. The same printcap file is used by both client programs and the lpd daemon, but each will use different parts of the information.

There are several possibilities for specifying network printers. I'm showing some different syntaxes for the entries here. This is the information that clients will need to have to send a job to the network printer.

# This is LPRng specific
remote|Remote Printer
   :lp=raw@server
# Convention from `old' BSD spooler
remote:Old Format:\
    :rp=raw:rm=server
# Sometimes you have to connect to a non-standard port
special:lp=lp@server%2000

All the above printcap entries will make the lpr print client connect to the remote host server and send the file to the raw printer. You can specify the remote printer and remote host using the :rp and :rm entries, or the LPRng extension lp=rp@rm.

Note that we don't need a spool directory for this kind of printer, nor do we need to run lpd on the client host machine, as the job will be transferred directly to the remote host.

Many printers now come equipped with a network support card that has a built in LPD print spooler. If this is the case you can print directly to the printer and do not need to spool your print job to a server.

5.6 Spooling To Local Server

Options used:

Some users may not want to send a print job directly to a remote printer. The printer may need the more powerful management functions of a print spooler to handle problems such as paper feed errors, multiple users, etc, or needs to run special filters to process the print job. In this case, they want the print job sent to a lpd server on the local host, which in turn will spool the job and send it to the remote printer. This can be done in several ways.

# Method 1: force client transfer to localhost
#    have server send to real printer
#    both client and server will see the following information
remote|Spool to localhost
   :lp=lp@localhost
# Note that only the server will see the following information
lp:server:lp=lp@remote:sd=/usr/spool/lp
# Method 2: force client transfer to localhost
#    both client and server will see the following information
remote|Spool to localhost
   :force_localhost
   :lp=lp@remote:sd=/usr/spool/lp
# Method 3: force a bounce queue with no effect
remote|Spool to localhost
   :lp=lp@localhost
   :bq=lp@remote:sd=/usr/spool/lp

In the first method, we have the clients send jobs to the lp printer on the localhost; localhost is the 'canonical' network name for the host on which the program is running. The lpd server on the localhost will get the print job, store it in the spool directory /usr/spool/lp and then forward the job to the lp printer on the remote host. The server tag indicates that a printcap entry is to be used only by the lpd daemon (server) process, and the information is ignored by the client. The server tag is discussed in detail in the Shared printcap files section.

One of the problems is finding the name of the local host. On some implementations which are not using a Domain Name Server, it is necessary to specify the local host name by using the localhost configuration variable.

The second method is slightly different. The force_localhost flag has meaning only for the client application programs, and forces them to send the job to the localhost. The server will then send the job to the remote host.

The third method is useful when you want the print job to be modified en passant as it passes through the spool queue. See the Filters discussion for details.

5.7 Shared printcap files

When both the client host and server host are the same machine, then the same /etc/printcap file will be used by both the lpr and lpd daemon programs. When LPRng programs read the /etc/printcap file, they accumulate information in the printcap file on individual printer entries. For example, the following printcap entries would result in the indicated information:

#/etc/printcap file
pr:lp=pr@host
pr:lp=/dev/lp:sh
#resulting printcap information:
pr:lp=/dev/lp:sh

This allows us to split up the printcap information into different blocks, and has been used to manage complex printcap entries on large sites with many printers.

We can use the server tag to specify that specific printcap information is only for use by the lpd daemon. For example:

#/etc/printcap file
pr:lp=pr@host
pr:server:lp=/dev/lp:sh
#resulting printcap information for client
pr:lp=pr@host
#resulting printcap information for lpd daemon
pr:lp=/dev/lp:sh

Many administrators at large sites will split up their printcap files so that the information needed to tell clients where the servers are for a printer is located at the start of the /etc/printcap file, and the actual information needed by the lpd daemon is at the end. Here is a sample:

#/etc/printcap file
pr1:lp=pr1@serverhost
pr2:lp=pr2@serverhost
pr1:server:lp=/dev/lp:tc=.common
pr2:server:lp=/dev/lp:tc=.common
.common:sd=/usr/local/lpd/%P
  :cm=Dumb printer %P
  :lf=log:af=acct
  :if=/usr/local/sbin/lpf
  :mx#0:sh

This printcap entry also shows the use of the tc option, which corresponds to the C Compiler Preprocessor (cpp) #include directive. Printcap entries starting with periods, underscores (_), or @ signs are treated as dummy printcap information and can only be referenced by the tc.

When the printcap information is read, the LPRng code will substitute the cannonical printer name for any %P tokens that it finds in the printcap. After processing the information in the printcap entry, the clients and lpd daemon will see the following:

# clients
pr1:lp=pr1@serverhost
pr2:lp=pr2@serverhost
# server
pr1:lp=/dev/lp
  :sd=/usr/local/lpd/pr1
  :cm=Dumb printer pr1
  :lf=log:af=acct
  :if=/usr/local/sbin/lpf
  :mx#0:sh
pr2:server:lp=/dev/lp:
  :sd=/usr/local/lpd/pr2
  :cm=Dumb printer pr2
  :lf=log:af=acct
  :if=/usr/local/sbin/lpf
  :mx#0:sh

5.8 Master Printcap Files

One of the major problems faced by administrators of large sites is how to distribute printcap information. They would like to have a single printcap file either distributed by a file server (NFS) or by some other method such as rdist.

By using the server tag, information for the lpd daemons can be separated out from the information needed by the lpr print client. The oh=pattern specifies that this information is only to be used by a specified host. For example:

#/etc/printcap file
pr1:lp=pr1@serverhost1:oh=*.eng.site.com,130.191.12.0/24
pr2:lp=pr1@serverhost1:oh=*.eng.site.com,130.191.12.0/24
pr1:lp=pr2@serverhost2:oh=*.admin.site.com
pr2:lp=pr2@serverhost2:oh=*.admin.site.com
pr1:server:oh=serverhost1.eng.com:lp=/dev/lp:tc=.common
pr2:server:oh=serverhost2.admin.com:lp=/dev/lp:tc=.common
.common:sd=/usr/local/lpd/%P

The above example is a total abuse of the use of oh tag, but has some interesting effects. The pattern is used as a glob pattern and is applied to the fully qualified domain name (FQDN) of the host reading the printcap file. For example, *.eng.site.com would match host h1.eng.site.com but would not match h1.admin.site.com. Thus, the effects of the first couple of entries would be to specify that the pr1 and pr2 printers on the eng hosts would be pr1@serverhost1, and on the admin hosts would be pr2@serverhost2,

Also, the lpd daemons on serverhost1 and serverhost2 would only extract the additional information for pr1 and pr2 respectively.

You can also specify network addresses and subnet masks as well. In this case, if the host matches the network address then it will use the information.

5.9 Printcap options for lpr, lpq, lprm, and lpc

These programs are used by users to connect to the lpd server and send print jobs or a request. For details about the way that this is done, see LPRng and RFC1179 for details.

The following options and configuration variables are used by the various programs to control how they will generate jobs and send them to the server.

lp, rm and rp

The rm (remote machine or host) and rp or lp printer printcap options are used to specify the remote host and printer to be used. These values can be extracted from a printcap entry, or supplied in the following manner.

  1. If the user program is invoked with -Pxxx argument, then the lp option is assigned the value xxx.
  2. If no explicit value is specified and the PRINTER environment variable has value xxx, then the lp option is assigned value xxx.
  3. If lp has a value of the form rp@rm or rp@rm%port, then the rp, rm, and lpd_port options are assigned the indicated values.
  4. If rm or rp does not have a value, then they are assigned the default_host (usually localhost) and default_printer (usually lp) option values.
  5. A connection is made to lpd_port on host rm and the file transfer or command is sent as specified in RFC1179.

See the Opening Output Device section for additional details.

5.10 Bounce queues

Options used:

Normally, when using a remote queue, the print job is transmitted to the server computer without any modifications. When it gets there, it might be processed by one or more programs before being submitted to the printer.

Sometimes, you will want to make some modifications to a print job on the computer that sends the job, or even a computer that is acting as a relay for job processing. There are several reasons for doing this: the filter programs or some of the fonts it needs might not be available on the server, or you want to minimize the system load.

LPRng supports this capability by indicating that a printer is actually a bounce queue. The term comes from the notion that jobs will bounce through this print queue, getting processed by the various facilities. The output, rather than going to a physical device, will then be forwarded to final destination over the network for actual printing.

Again, you will modify the client printcap entry to declare this. In the next example, `host' is the name of the host which will do the processing, and `remote' is the target host with the printer.

# Simple example of a bounce queue
bounce
    :lp=bounce@bouncehost
    :bq=lp@remote
    :sd=/usr/spool/lpd/bounce
    :if=/usr/local/bin/lpf
    :vf=/usr/local/bin/lpf
    :translate_format=vl
    # uncomment if you want banner
    #:generate_banner

Some comments:

  1. The lp=bounce@host entry specifies the queue and hostname where the filtering will be done. This information is used by the lpr program to determine where to send the print job.
  2. After the lpd daemon on the bouncehost does the processing, the output is then transmitted to the queue and hostname specified by the bq option.
  3. We need a spool directory (sd) on the lpd server host to hold the files and temporary output while they are processed.
  4. Next, we need to indicate the filters to be used for different printer formats. Oops, I still didn't tell you anything about filters. Just a moment, it's on its way. See section Filters.
  5. The translate_format specifies the way the data file formats will be renamed after filtering. It the value consists of pairs of letters, the original and renamed format. For example, translate_format=vl specifies that after filtering, format v files will be renamed to format l (literal or binary). You do not need to have a filter for a format to rename it. For example, translate_format=vlxf will rename format x files to f and there is no filter for format x. See translate_format for more details.
  6. The generate_banner flag will force a banner to be added to the job. The banner generation is done as discussed in Banner Printing.

In this example, anything sent to the printer called bounce (on this host) will be filtered on the client host. After that, it will be transmitted to the queue lp on the server called remote.

5.11 LPR Filtering

Options used:

Some users would like to have all of the advantages of having the filtering and processing capabilities of a lpd daemon without needing to deal with actually running a lpd daemon on their system. By having the lpr program process the job by passing it through the various filters and then send the output of the filters as the print job you can get the desired effect.

# Simple example of an lpr_bounce entry
bounce
  :lpr_bounce
  :lp=lp@remote
  :if=/usr/local/bin/lpf

The lpr_bounce flag, if present in the printcap entry, will force lpr to process the job using the specified filters and send the outputs of the filters to the remote printer for further processing.

In order to do filtering, it may need to create some temporary files and run some programs. By default, the temporary files are created in the /tmp directory and the programs are run as user. Since no spool directory is used, the sd information is not needed.

5.12 Dynamic Routing

Options used:

The LPD bounce queue functionality has been extended to allow a job to not only be passed through filters before sending to a remote destination, but also to reroute the job to one or more destinations in a dynamic manner. This is accomplished by having a router filter return a set of destinations. Here is a sample printcap to accomplish this:

t2|Test Printer 2:sd=/var/spool/LPD/t2
    :lf=log
    :lp=t2@printserver
    :bq=t1@localhost
    :destinations=t1@localhost,t2@localhost
    :router=/usr/local/LPD/router

The lp entry is used to force all jobs to be sent to the bounce queue on host 'printserver'. Once they arrive, the 'router' filter is invoked with the standard filter options which include the user, host, and other information obtained from the control file. STDIN is connected to a temporary copy of the control file, and the CONTROL environment variable is set to the value of the actual control file itself.

The routing filter exit status is used as follows:

The router filter returns one or more routing entries with the following format. Note that entry order is not important, but each entry must end with the 'end' tag.

dest (destination queue)
copies (number of copies to be made)
priority (priority letter)
X(controlfile modifications)
end

Example of router output:

dest t1@localhost
copies 2
CA
priority B
end
dest t2@localhost
CZ
priority Z
end

The above routing information will have copies of the job sent to the t1 and t2 spool queue servers. If no valid routing information is returned by the router filter the job will be sent to the default bounce queue destination.

LPQ will display job information in a slightly different format for multiple destination jobs. For example:

Printer: t2@astart2 'Test Printer 2' (routed/bounce queue to 't1@astart2.astart.com')
  Queue: 1 printable jobs in queue
 Rank  Owner/ID        Class Job Files                           Size Time
active  papowell@astart2+707 A 707  /tmp/hi                         3 10:04:49
 - actv papowell@astart2+707.1 A 707 ->t1@localhost <cpy 1/2>       3 10:04:49
 -      papowell@astart2+707.2 A 707 ->t2@localhost                 3 10:04:49

The routing information is displayed below the main job information. Each destination will have its transfer status displayed as it is transferred. By convention, the job identifier of the routed jobs will have a suffix of the form .N added; copies will have CN added as well. For example, papowell@astart2+707.1C2 will be the job sent to the first destination, copy two.

Routed jobs can be held, removed, etc., just as normal jobs. In addition, the individual destination jobs can be manipulated as well. The LPC functionality has been extended to recognize destination jobids as well as the main job id for control and/or selection operations.

The optional destinations entry specifies the possible set of destinations that the job can be sent to, and is for informational purposes only. In order for LPQ/LPRM to find the job once it has passed through LPD, LPQ/LPRM uses the list of printers in the destinations, and loop over all the names in the list looking for the "job" that you are interested in. If there is no destinations information, the bq information will be usued.

One of the more interesting use of the router filter is to actualy modify the control file before it is put into the spool queue. The routing filter has STDIN attached to the control file READ/WRITE, allowing the following interesting bit of Perl code to be used:

 # you need to get PERL to do a 'dup' call on FD 0
 open(CF, '+<0');  
 # read the control file
 @cf_lines = <CF>;
 # TRUNCATE the control file
 truncate(CF,0);
 # mess about with the control file
 foreach $line (@cf_lines) {
    # or whatever you want
    print CF $line;
 } 

This will read the control file, truncate it, and then write it out again.

5.13 Support for Network Print Servers

This section was supplied by Horst Fickenscher <horst.fickenscher@it.erlm.siemens.de> and updated by Patrick Powell <papowell@astart.com>.

A ``network print server'' is usually a box (external model) or card in a printer (internal model) which has a network connection to a TCP network and software to implement a LPD print server. If it is an external model, The parallel or serial port of the printer is connected to the box, and the print server may support multiple printers. If it is an internal model, the server is usually nothing more than a Network Interface Controller and a ROM containing software that the microprocessor in the printer uses.

The print server may support multiple printing protocols, such as RFC1179 (TCP/IP printing using the LPD print protocol), Novell Printer Protocols, SMB print protocols, and Appletalk protocols. One of the observed problems with Network Print servers is that while they can usually support one protocol and one user at a time quite well, when you try to use multiple protocols and/or multiple users try to transfer print jobs to the printer, the printer may behave in a very odd manner. Usually this results in a printer failing to finish a job currently being printed, and unable to accept new jobs.

Several of the newer models of print servers have Simple Network Management Protocol (SNMP) agents built into them, and can provide detailed information about their internal functions. By using a SNMP manager such as SunNetmanage or HP-Openview, you can monitor your network printers activities.

Although it's possible to connect to network printers as if they were remote printers, Patrick Powell advises differently:

I recommend that you use only a single protocol to send jobs to the printer. If you can, I also recommend that you use a print spooler and have only a single host system send a job to the printer.

My best advice on connecting to network printers is not to use the the built-in LPD server, but to use the direct TCP/IP connection to the print engine. Usually this is done to particular TCP/IP port on the printer. For the HP JetDirect and other HP products, this is usually 9100.

Once you have the direct connection, you can now use various filters to preprocess the print job, insert PJL and PCL commands, or convert text to PostScript or PCL for better print quality.

Here is a sample printcap for an HP LaserJet 4 or above, attached via an HP JetDirect print server. It uses the CTI-ifhp filters:

     # printcap file for pr4
     # PostScript via JetDirect card, IP address pr4, port 9100.
     # Note: some PC's LPR packages use the v format for their jobs
     #
     pr4|network
         :rw:sh:lp=pr4%9100:sd=/usr/spool/lpd/pr4
         :af=acct: :lf=log: :ps=status
         # only allow the following formats
         :fx=flpv
         #filters
         :if=/usr/local/lib/CTI-Print/bin/ifhp
         :of=/usr/local/lib/CTI-Print/bin/ofhp
         :vf=/usr/local/lib/CTI-Print/bin/ifhp -c

The lp=pr4%9100 means that LPRng is to make a TCP/IP connection to host pr4 on its port 9100. The CTI-Print filters referenced in if, of, and vf, send PJL (and PCL) commands along with the print files to the printer. The CTI-Print filters are available separately from the LPRng distribution sites.

Filters are discussed in section Filters.

According to Richard S. Shuford <s4r@ornl.gov>, some DEC printers (e.g., the DEClaser 3500) use TCP port 10001.

5.14 Printer load balancing

In a large site, you could have several equivalent printers, which will be used by many people. The reason for this is, of course, to increase the printer output by enabling several jobs to be printed at once.

LPRng supplies mechanisms to define a `virtual' printer for such a set of real printers. If properly set up, print jobs will be distributed evenly over all printers.

I'll give two examples for this situation.

Multi-server print queue

Options used:

A multi-server print queue is one that feeds jobs to other queues. The main queue sv=q1,q2,... printcap entry specifies the names of the printers that will be sent jobs. These printers must have their spool queues on this LPD server.

Servers that are fed jobs have a ss=mainqueue printcap entry. This informs the lpd server that the queue operates under the control of the mainqueue print queue, and is fed jobs from it.

During normal operation, when the lpd server has a job to print in the mainqueue, it will check to see if there is an idle service queue. If there is, it will transfer the job to the service queue spooling directory and start the service queue printing activities.

Users can send jobs directly to the individual printers serving a queue.

The next example (and the comments underneath) was supplied by John Perkins <john@cs.wisc.edu> (slightly edited).

Here's how I've set up a bounce queue that feeds 6 LaserWriters:

laser|pi|Room 1359 LaserWriters
    :lp=laser@server.com
laser|pi|Room 1359 LaserWriters
    :server
    :lf=/usr/adm/laser-log
    :sv=laser1,laser2,laser3,laser4,laser5,laser6
    :sd=/usr/spool/laser
@commonlaser
    :sd=/usr/spool/%P
    :rw:mx#0:sh
    :lf=/usr/adm/laser1-log
    :if=/s/lprng/lib/filters/cappsif
    :of=/s/lprng/depend/cap/bin/papof
    :ss=laser
    :fx=fdginpt
laser1|pi1|Room 1359 LaserWriter #1
    :lp=laser1@server.com
laser1|pi1|Room 1359 LaserWriter #1
    :server
    :lp=/dev/laser1
    :tc=@commonlaser
laser2|pi2|Room 1359 LaserWriter #1
    :lp=laser2@server.com
laser2|pi2|Room 1359 LaserWriter #2
    :server
    :lp=/dev/laser2
    :tc=@commonlaser

and so on for the other 4 laserN queues.

This will forward a job from laser to laserN, once one of those queues is available. It will hold jobs in the ``laser'' queue until one of the other queues is empty.

Even though the queues are not meant for direct use, people can print directly to individual queues. This allows a specific load sharing printer to be used. If you wanted to hide the load sharing printers, i.e. - not allow direct spooling to them, then you would simply remove the non-server entries from the printcap.

Checking Busy Status of Server Queues

Options used:

The previous section outlined how LPRng uses the sv and ss flags to indicate that the server spool queue has multiple destination queues. However, there is a problem when the actual printer being served by the destination queue is a remote device, and can be busy or offline.

The check_idle option specifies a program that is invoked by the lpd server to determine if the spool queue device is available.

The program is invoked with the standard filter options, STDIN and STDOUT connected to /dev/null, and STDERR to the error log.

The program should make a connection to the remote device or system and should determine that the remote device is available for use, and then exit with the following status.

Key      Value   Meaning
JSUCC    0       Successful - printer is idle
JABORT   non-zero Printer is not accepting jobs

If the printer is accepting jobs but is temporarily busy, the program should poll the printer until it becomes free, only exiting when it is available for use. If the printer is not accepting jobs, the program should exit with a non-zero exit code.

The following is a sample printcap entry, showing how the check_idle facility can be used.

pr:
  :lp=laserjet%9100
  :check_idle=/usr/local/filters/remote_check lp@laserjet
  :if=/usr/local/filters/ifhp

The following perl program shows how to generate a query to the remote printer by simulating an lpq query and checking for returned status.

#!/usr/local/bin/perl
# Usage:
#  remote_check printer@host[%port] [-options]
#   -Tflag[,flags]*
#  flag
#    debug  - turns debugging on
#    long   - use long status format
#
# query the remote printer whose name is passed on the command line
# 
# Note that -Txxx options are passed AFTER the printer
use English;
use IO::Socket;

my $JSUCC = 0;
my $JABORT = 33;
my $JNOSPOOL = 38;
my $JNOPRINT = 39;

my $debug = 0;
my $optind;

# pull out the options
my($key,$value,$opt,$long,$opt_c);

$printer = $ARGV[0];

for( $i = 1; $i < @ARGV; ++$i ){
    $opt = $ARGV[$i];
    print STDERR "XX opt= $opt\n" if $debug;
    if( $opt eq '-c' ){
        $opt_c = 1;
    } elsif( ($key, $value) = ($opt =~ /^-(.)(.*)/) ){
        if( $value eq "" ){
            $value = $ARGV[++$i];
        }
        ${"opt_$key"} = $value;
        print STDERR "XX opt_$key = " . ${"opt_$key"} . "\n" if $debug;
    } else {
        $optind = $i;
        last;
    }
    print STDERR "XX opt_P = $opt_P\n" if $debug;
}

$long = 0;  # short

if( defined($opt_T) ){
    print STDERR "XX CHECK_REMOTE opt_T=$opt_T\n" if $debug;
    if( $opt_T =~ /debug/ ){
        $debug = 1;
    }
    if( $opt_T =~ /short/ ){
        $long = 1;
    }
    if( $opt_T =~ /long/ ){
        $long = 0;
    }
}

print STDERR "XX CHECK_REMOTE " . join(" ",@ARGV) . "\n" if $debug;

if( !defined($printer) or $printer =~ /^-/ ){
    print STDERR "$0: no printer value\n";
    exit( $JABORT );
}

while( checkstatus( $printer, $long ) ){
    print STDERR "XX CHECK_REMOTE sleeping\n" if $debug;
    sleep(10);
}

exit $JSUCC;

sub checkstatus {
    my ($printer,$long) = @_;
    my ($remote,$port);
    my ($count, $socket, $line);

    if( $long ){
        $long = 4;
    } else {
        $long = 3;
    }
    if( $printer =~ /@/ ){
        ($printer,$remote) = $printer =~ m/(.*)@(.*)/;
    }
    $remote="localhost" unless $remote;

    if( $remote =~ /%/ ){
        ($remote,$port) = $remote =~ m/(.*)%(.*)/;
    }
    $port = 515 unless $port;
    print STDERR "XX CHECK_REMOTE remote='$remote',"
        . " port='$port', pr='$printer', op='$long'\n" if $debug;

    $socket = getconnection( $remote, $port );

    $count = -1;
    # send the command
    printf $socket "%c%s\n", $long, $printer;

    while ( defined( $line = <$socket>) && $count < 0 ){
        chomp $line;
        print STDERR "XX CHECKREMOTE '$line'\n" if $debug;
        if( $line =~ /printing disa/ ){
            print STDERR "XX CHECKREMOTE printing disable\n" if $debug;
            exit $JNOPRINT;
        } elsif( $line =~ /spooling disa/ ){
            print STDERR "XX CHECKREMOTE printing disable\n" if $debug;
            exit $JNOSPOOL;
        } elsif( $line =~ /([0-9]*)\s+job.?$/ ){
            $count = $1;
            print STDERR "XX CHECKREMOTE $count jobs\n" if $debug;
        }
    }
    close $socket;
    if( $count < 0 ){
        print STDERR "CHECKREMOTE cannot decode status\n";
        exit $JABORT;
    }
    return $count;
}

sub getconnection {
    my ($remote,$port) = @_;
    my ($socket);
    print STDERR "XX CHECK_REMOTE remote='$remote', port=$port\n" if $debug;
    $socket = IO::Socket::INET->new(
        Proto => "tcp",
        PeerAddr => $remote,
        PeerPort => $port,
        );
    if( !$socket ){
        print STDERR "CHECK_REMOTE IO::Socket::INET failed - $!\n";
        exit $JABORT;
    }
    $socket->autoflush(1);
    $socket;
}

The example of the previous section can be modified now so that it uses the check_idle facility. The master queue will send jobs only to the server queue queues which report idle status.

laser1|pi1|Room 1359 LaserWriter #1
    :server:check_idle=/usr/local/lib/filters/remote_check pr@laser1
    :lp=laser1%9100
    :tc=@commonlaser
laser2|pi2|Room 1359 LaserWriter #2
    :server:check_idle=/usr/local/lib/filters/remote_check pr@laser1
    :lp=laser2%9100
    :tc=@commonlaser

Using a router filter

A router filter allows you to re-route jobs in a dynamic way. For details, see routing.

Lars Anderson <lsa@business.auc.dk> supplied this example (slightly edited):

This script will attempt to distribute print jobs evenly on 2 printers hpl5a and hpl5b when sending to hpl5bounce.

hpl5bounce|for PLP/LPRng software - network based HP Jetdirect card:
        :lf=log.hpl5
        :lpr_bounce
        :lp=hpl5b@localhost
        :router=/usr/local/admscripts/bouncer.pl
hpl5a|for PLP/LPRng software - network based HP Jetdirect card:
        :af=acc.hpl5a:
        :lp=hpl5a%9100:sd=/var/spool/lpd/hpl5a:
        :lf=log.hpl5a:
        :tc=@hplcommon
hpl5b|for PLP/LPRng software - network based HP Jetdirect card:
        :af=acc.hpl5b:
        :lp=hpl5b%9100:sd=/var/spool/lpd/hpl5b:
        :lf=log.hpl5b:
        :tc=@hplcommon
# Common settings
@hplcommon:
        :rw:sh:ps=status:
        :fx=flp:
        :if=/usr/local/lib/filters/ifhp -Tbanner=on
        :of=/usr/local/lib/filters/ofhp -Tbanner=on

The perl script bouncer.pl looks like this:

#!/usr/bin/perl
#
# Script for printjob loadsharing
#
#                                 29/5-97 Lars Anderson
#
# Printqueues to check
$printer1="hpl5a\@localhost";
$printer2="hpl5b\@localhost";
# obtain number of jobs in each printqueue
$lpq1=`/usr/local/bin/lpq -s -P$printer1`;
$lpq2=`/usr/local/bin/lpq -s -P$printer2`;
$lpq1=~ (/(\d+) jobs?/); $numjobs1=$1;
$lpq2=~ (/(\d+) jobs?/); $numjobs2=$1;
if ($numjobs1 == 0) {
    print "dest $printer1\nCA\nend\n";
    exit;
}
if ($numjobs1 > $numjobs2) {
    print "dest $printer2\nCA\nend\n";
    exit;
}
print "dest $printer1\nCA\nend\n"; 

5.15 The Missing Details

Options used:

The LPRng software uses a greatly simplified set of printcap conventions. This section discusses the details of the printcap database. LPRng can use vintage (i.e.- Berkeley LPR) format printcap files; use the checkpc program to make sure they are totally compatible with LPRng (see checkpc man page, README.install, DOC/Intro).

The client programs (LPR, LPRM, LPQ, LPC) do not need access to a printcap database, but will use it if available. The -Pprinter@host option or PRINTER environment variable specifies the printer and LPD host; the LPD server does all of the various spool queue activities. The client programs send requests and/or jobs to the LDP server which carries out all activity. If no printcap is available and the host is not specified, a default host value is provided.

If a printcap database is desired, then it is obtained as follows. First, the printcap_path and lpd_printcap_path configuration information (see lpd.conf(5)) specifies where client and server programs find printcap information. The client programs use printcap_path; lpd uses both printcap_path and lpd_printcap_path. All files are read and the printcap entries are extracted in order from the files. Later printcap information overrides previous information in the files.

The common defaults for the printcap locations are:

printcap_path          /etc/printcap:/usr/etc/printcap:\
                            /var/spool/lpd/printcap.HOSTNAME
lpd_printcap_path      /etc/lpd_printcap:/usr/etc/lpd_printcap

When the lpd server gets a printcap entry, if the printcap entry obtained has a spool directory and the spool directory has a printcap file in it, then this file is assumed to contain additional printcap information, and the server will read and append this information to the obtained printcap information. The nw (NFS mounted or network file system) flag will suppress this operation, as there is a security loophole when obtaining information from an NFS mounted system.

The most common method of printcap information distribution is to have a master printcap file shared or distributed to all system. This usually has only the printer name and lpd host specified in the printcap entries, as shown below.

    ----- /etc/printcap on clients and server ------- 
    #parallel attached DUMB printer
    pr1|dumb
        :lp=pr1@taco.astart.com
    # server information
    pr2|postscript
        :lp=pr2@taco.astart.com
    pr3|laserjet
        :lp=pr3@taco.astart.com
    @common|common code for printers
        :if=/bin/filter
        :of=/bin/filter
    @morecommon|show the configuration expansion
        :sd=/var/spool/lpd/%P
        
    realpr:tc=@common:tc=@morecommon

A careful study of the above example will discover the following features of the LPRng printcap structure.

  1. Lines ending with a \ indicate continuation to the next line. In practice, the \ is replaced with space(s) when line joining is done.
  2. Blank lines and lines whose first nonwhitespace character is a # are ignored, except if it follows a continuation line. (Which makes sense.)
  3. All leading and trailing whitespace on a line are removed.
  4. A printcap entry consists of a name, 0 or more aliases, and data entries.
    - name starts with an alphabetic character; dummy entries can start _, @ or .
    - alias starts with a | followed by the alias
    - fields or data entry starts with : followed by the entry
  5. Field or data entry
    name - main or cannonical name for printcap entry
    |name - alias name for printcap entry
    :key - set the key to ON (1)
    :key@ - set the key to OFF (0)
    :key#nnn - set the key to nnn, where nnn follows C language conventions
    :key=string - set the key to the string value to end of line
  6. Printcap entries whose cannonical name starts with _, @, or . (period) (eg.- @name) are treated like dummy entries. They can be referenced with :tc=entry:, but will be ignored otherwise.
  7. The tc=f1:tc=f2:... acts similar to a file inclusion operator, but substitutes printcap entries. The specified tc entries are logically append to the end of the current printcap entry, and the appended information will override the previous information. Note that you can have multiple :tc: entries.
  8. key=value.%X
    a selected set of %X values are expanded when the printcap entry is used by the client or server program. The following values are expanded:
    P - printcap cannonical or main name
    H - Fully Qualified Domain Name for host
    h - short name for host
    R - Fully Qualified Domain Name for remote host host
    r - short name for remote host host
  9. The oh entry specifies that a particular printcap entry can only be used by a host with a matching host name or IP address. See Master Printcap Files for details.
  10. The server entry specifies that a particular printcap entry can only be used by the lpd server, and is ignored by other programs. See Shared Printcap Files for details.

Printcap information is extracted in order from the printcap files, and later information for printcap entries overrides earlier ones.

The %X substitution is especially useful when most of the information for a set of printers is common or identical. This can be placed in a printcap entry and referenced with the tc operator. As shown in the example, by making the spool directory name depend on the cannonical printcap name, it simplifies management of the printer.

Spool (Control) Directory Printcap File

You can put a printcap file in a spool queue directory. This file is only consulted by the LPD server when performing operations on a spool queue. It allows you to put information particular to a spool queue in well controlled location.

The server tag and oh options have rendered this facility obsolete, and it may be removed in later releases.

Separate Printcap Files for LPD

Since only the LPD server uses the /etc/lpd_printcap or /usr/etc/lpd_printcap file, you can place server specific information there. This allows you to have a common printcap file for clients and an additional one for the lpd servers. You may have to modify the lpd.conf file lpd_printcap_path entry to specify the desired file.

The server and oh options have rendered this facility obsolete and it may be removed in future releases of LPRng.

Printcap Entry all

The 'all' printcap entry is reserved to provide a list of all printers available for use by the spooling software. This was intended to be used with systems that did not have ways to provide a wildcard search of the printcap database. The 'all' printcap entry has the form:

all:all=pr1,pr2,...

The LPRng software will use the individual entries of the printer list and request additional printcap information if necessary.

More Example Printcap Entries

The following printcap entries show the formats, and have some additional comments about fields in the printcap file.

#
#  NOTE:
#  Use the lpf filter (supplied with LPRng) or the of and if filter.
#  Banners will be printed using the lpbanner
#  program, supplied with LPRng.  You can also create your own banner
#  program and specify it as the banner printer (printcap :bp: entry.)
#  Put -$ at the start of a filter or program specification to suppress
#  additional command line options. (see lpd.conf).
#  Note: some PC's LPR packages use the v format instead of the l or f format
#
# This is the VINTAGE form of printcap,  with trailing \ to extend information
# to next line.  Note the -$ to suppress adding options to command line
# typical dump printer, no banner, parallel port
pr1|dumb- no banner:\
    :sh:lp=/dev/lpr1:sd=/usr/spool/lpd/pr1:\
    :fx=flpv:\
    :af=acct:lf=log:\
    :if=/usr/local/bin/lpf:\
    :vf=-$ /bin/cat
# dumb with banner - note that lprng will use the default banner program
#  /usr/local/bin/lpbanner to generate full banner
# Note: we use the standard LPRng printcap format
pr1b|dumb- banner
    :lp=/dev/lpr1:sd=/usr/spool/lpd/pr1        #<- sh deleted
    :fx=flpv
    :af=acct:lf=log
    :of=/usr/local/bin/lpf
    :if=/usr/local/bin/lpf
    :vf=/usr/local/bin/lpf -c
# common printer information:
# we define a @common entry
@filter|printcap filter information
    :of=/usr/local/bin/lpf
    :if=/usr/local/bin/lpf
    :vf=/usr/local/bin/lpf -c
# dumb with user banner - bp specifies banner printer
#  If we wanted the banner at the END of the job, we would use
#  :hl: (header last) flag.
#  We can also have headers at start and end, using the
#  be={banner printer} and bs={banner printer} overrides
#  Note: -$ suppresses adding command line options
pr1b|dumb- user supplied banner
    :lp=/dev/lpr1:sd=/usr/spool/lpd/pr1
    :fx=flpv
    :af=acct:lf=log
    :bp=/usr/local/lib/my_banner_printer
    :tc=@filter
#serial attached PostScript printer
# Note that fields can have terminating colons (:)
# You can put comments into this printcap with this form
# Note that the of filter does accounting
pr2|postscript - no banner
    :rw:sh:lp=/dev/ttya:sd=/usr/spool/lpd/pr2
    :sy=9600 -raw -parenb cs8 crtscts
    :af=acct:lf=log:ps=status
    # only allow the following formats
    :fx=flpv
    # filters
    :tc=@filter
#serial attached PostScript printer with psof created banner
pr2|postscript - psof will expand short banner
    # Note: sb is short banner format
    # psof filter recognizes this and produces a fancy banner
    # from the input
    :rw:sb:lp=/dev/ttya:sd=/usr/spool/lpd/pr2
    :sy=9600 -echo -crmod -raw -oddp -evenp pass8 cbreak ixon
    :af=acct:lf=log:ps=status
    # only allow the following formats
    :fx=flpv
    # filters
    :tc=@filter
#serial attached PostScript printer with user created banner
pr2|postscript - psof will expand short banner
    # Note: sb is short banner format
    # psof filter recognizes this and produces a fancy banner
    # from the input
    :rw:sb:lp=/dev/ttya:sd=/usr/spool/lpd/pr2
    :sy=9600 -echo -crmod -raw -oddp -evenp pass8 cbreak ixon
    :af=acct:lf=log:ps=status
    # only allow the following formats
    :fx=flpv
    # filters
    :tc=@filter
# parallel attached Laser Jet
# Note that fields do not need terminating colons
#
pr3|laserjet
    :rw:sh:lp=/dev/lp:sd=/usr/spool/lpd/pr3
    :af=acct:lf=log:ps=status
    # only allow the following formats
    :fx=flpv
    #filters
    :if=/usr/local/lib/CTI-Print/bin/ifhp -Tstatus=off
    :of=/usr/local/lib/CTI-Print/bin/ofhp -Tstatus=off
    :vf=/usr/local/lib/CTI-Print/bin/ifhp -c -Tstatus=off
# printcap file for pr4
# PostScript via JetDirect card, IP address pr4, port 9100.
# Note: some PC's LPR packages use the v format for their jobs
#
pr4|network
    :rw:sh:lp=pr3%9100:sd=/usr/spool/lpd/pr4
    :af=acct: :lf=log: :ps=status
    # only allow the following formats
    :fx=flpv
    #filters
    :if=/usr/local/lib/CTI-Print/bin/ifhp
    :of=/usr/local/lib/CTI-Print/bin/ofhp
    :vf=/usr/local/lib/CTI-Print/bin/ifhp -c

PC-NFS Print Spooler

If you are using PC-NFS to do print spooling you have several security loopholes exposed. You must modify the permissions on the spool directory to allow other users to access it and place jobs into the directory. Printcap and other control information by default is placed in the spool directory, and can be easily modified by malicious users. To reduce this risk, the :cd: (control directory) entry is used to specify a directory to hold sensitive control information. For example

#/etc/lpd_printcap
# PCNFS Spooler
#
pr7
    :lp=pr7@printserver
    :bq=pr1@printserver
    :sd=/usr/spool/pcnfs/pr7
    :cd=/usr/spool/lpd/pr7

This printcap entry will implement a simple 'bounce queue', in which jobs are stored temporarily and then transferred to another spool queue, and is the recommended way to support PC-NFS printing.

5.16 Management Strategy for Large Sites

One very effective way to organize print spooling is to have a small number of print servers running a lpd daemon, and to have all the other systems send their jobs directly to them. By using the above methods of specifying the printer and server host you eliminate the need for more complex management strategies.

However, you still need to inform users of the names and existence of these printers, and how to contact them. One method is to use a common /etc/printcap file which is periodically updated and transfered to all sites. Another method is to distribute the information using the NIS or some other database. LPRng has provided a very flexible method of obtaining and distributing database information: see Using Programs To Get Printcap Information for details.

5.17 Using Programs To Get Printcap Information

In the lpd.conf file you can specify:

printcap_path=|program
This will cause the LPRng software to execute the specified program, which should then provide the printcap information. The program is invoked with the standard filter options, and has the name of the printcap entry provided on STDIN. The filter should supply the printcap information on stdout and exit with a 0 (success) error code. By convention, the printcap name 'all' requests a printcap entry that lists all printers.

This technique has been used to interface to the Sun Microsystem NIS and NIS+ databases with great success. By having the invoked program a simple shell script or front end to the nismatch or ypmatch programs, the complexity of incorporating vendor specific code is avoided.

How to use NIS and LPRng

This note is based on material sent to the lprng@iona.ie mailing list by Paul Haldane <paul@ucs.ed.ac.uk>.

 # From: Paul Haldane <paul@ucs.ed.ac.uk>
 # To: lprng@iona.ie
 # Subject: Re: Problem using plp with NIS
 # Sender: majordomo-owner@iona.ie
 # Precedence: bulk
 # Reply-To: lprng@iona.ie
 # Status: RO
 # 

We generally don't use NIS for printcap files (we've moved to hesiod) but I can show you what we've done in the past.

The input to NIS is a normal printcap file:

# Classical printcap entry
lp23a|lp23|lp|main printhost printer - KB, EUCS front Door:\
        :lp=lp23a@printhost:\
        :sd=/usr/spool/lpr/lp23a:
 
#lprng printcap entry
lplabel|lpl|TEST - Labels printer:
        :lp=:rm=printhost:rp=lplabel:
        :sd=/usr/spool/lpr/lplabel:
        :rg=lpadm:mx#1:

To build the NIS printcap.byname map we add the following to the NIS makefile (along the other bits and pieces that the makefile needs to know about a new map).

PRINTCAP=$(DIR)/printcap
#PRINTCAP=/etc/printcap
# warning : [  ] is actualy [<space><tab>] in the script
printcap.time: $(PRINTCAP) Makefile
  if [ -f $(PRINTCAP) ]; then \
    sed < $(PRINTCAP) \
      -e 's/[   ][  ]*$$//' -e '/\\$$/s/\\$$/ /' \
    | awk '$$1 ~ /^#/{next;} $$1 ~ /^[:|]/ {printf "%s", $$0; next;} \
        {printf "\n%s", $$0 }' \
    | sed -e 's/[   ]*:[  ]*:/:/g' -e 's/[  ]*|[  ]*/|/g' \
      -e '/^[   ]*$$/d' > .printcap.$$$$; \
    cat .printcap.$$$$; \
    if [ $$? = 0 -a -s .printcap.$$$$ ]; then \
      awk <.printcap.$$$$ '{ FS=":"; OFS="\t"; } { \
          n = split($$1, names, "|"); \
          for (i=1; i<=n; i++) \
              if (length(names[i]) > 0 \
              && names[i] !~ /[ \t]/) \
                  print names[i], $$0; \
      }' | $(MAKEDBM) - $(YPDBDIR)/$(DOM)/printcap.byname; \
      awk <.printcap.$$$$ '{ FS=":"; OFS="\t"; } { \
          n = split($$1, names, "|"); \
          if (n && length(names[1]) > 0 && names[1] !~ /[ \t]/) \
              print names[1], $$0; \
      }' | $(MAKEDBM) - $(YPDBDIR)/$(DOM)/printcap.bykey; \
      rm -f .printcap.$$$$; \
      touch printcap.time; echo "updated printcap"; \
    fi \
  fi
  @if [ ! $(NOPUSH) -a -f $(PRINTCAP) ]; then \
      $(YPPUSH) printcap.byname; \
      $(YPPUSH) printcap.bykey; \
      touch printcap.time; echo "pushed printcap"; \
  fi

To specify that you want YP database rather than file access, use the following entry in your /etc/lpd.conf file:

printcap_path |/usr/local/lib/pcfilter

Put the following shell script in /usr/local/lib/pcfilter

#!/bin/sh
#/usr/local/lib/pcfilter
read key
ypmatch "$key" printcap.byname

How to use NIS and LPRng - Sven Rudolph

 Date: Wed, 11 Sep 1996 00:11:02 +0200
 From: Sven Rudolph <sr1@os.inf.tu-dresden.de>
 To: lprng@iona.ie
 Subject: Using :oh=server: with NIS

When I use a cluster-wide printcap, two entries for each printer will appear, e. g.:

---------- start of /etc/printcap snippet
lp1
 :lp=lp1@server
lp2
 :lp=lp2@server
lp1
 :server:oh=servername
 :sd=/var/spool/lpd/lp1
 :lp=/dev/lp1
 :sh:mx#0
---------- end of /etc/printcap snippet

When I create a NIS map out of this, the printer name is used as a key and must be unique. So NIS' makedbm decides to drop all but the last entry for each printer. This makes the printer on the clients unavailable. I solved this by a hack where the second entry is called lp1.server and the NIS client script has to request the right entry.

  1. Assumptions

    Perl is available at the YP server in /usr/bin/perl . A Bourne Shell is available at all clients in /bin/sh The printcap that is to be exported is in /etc/printcap . The printcap is written in the new format.

    In the examples the printer is called lp1 .

  2. Add the following to your YP Makefile (/var/yp/Makefile) on the YP server :
    ---------- start of /var/yp/Makefile snippet
    PRINTCAP  = /etc/printcap
    printcap: $(PRINTCAP)
        @echo "Updating $@..."
        $(CAT) $(PRINTCAP) | \
            /usr/lib/yp/normalize_printcap | $(DBLOAD) -i $(PRINTCAP) \
            -o $(YPMAPDIR)/$@ - $@
        @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
        @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
    ---------- end of /var/yp/Makefile snippet
    

    (These lines are for Debian GNU/Linux, other systems might require other modifications)

  3. Install the programs match_printcap and normalize_printcap to /usr/lib/yp. normalize_printcap is only required on the YP server. The normalize_printcap processes only the LPRng printcap format.
    ---------- start of /usr/lib/yp/normalize_printcap
    #! /usr/bin/perl
    $debug = 0;
    $line = "";
    $new = "";
    while (<>) {
        chomp;
        next if ( /^\s*\#.*/ );
        s/^\s*$//;
        next if ( $_ eq '' );
        print "new: " . $_ . "\n" if $debug;;
        if (/^\s/) { # continuation line
            $line = $line.$_;
            print "continued: $line\n" if $debug;
            next;
        } else {
            $line =~ s/\s+\:/:/g;
            $line =~ s/\:\s+/:/g;
            $line =~ s/\:\s*\:/:/g;
            print "line: $line\n" if $debug;
            push(@lines, $line) if $line;
            $line = $_;
        }
    }
    $line =~ s/\s+\:/:/g;
    $line =~ s/\:\s+/:/g;
    $line =~ s/\:\s*\:/:/g;
    push(@lines,$line) if $line;
    @lines = sort(@lines);
    foreach $line (@lines) {
        ($printers) = split(/\:/,$line);
        @printers = split(/\|/,$printers);
        foreach $printer (@printers) {
          $num{$printer}++;
          push(@allprinters,$printer);
          print "allprinters: @allprinters\n" if $debug;
          print $printer."_".$num{$printer}."\t$line\n";
        }
    }
    @pr = keys %num;
    print "printers @pr\n" if $debug;
    if ($#allprinters >=0) {
        print "all_1\tall:all=".join(",",@pr)."\n";
    }
    ---------- end of /usr/lib/yp/normalize_printcap
    

    The result of processing the sample printcap file is:

    lp1_1 lp1:lp=lp1@server
    lp1_2 lp1:server:oh=servername:sd=/var/spool/lpd/lp1:lp=/dev/lp1:sh:mx#0
    lp2_1 lp2:lp=lp2@server
    all_1 all:all=lp1,lp2
    

    Observe that each of the real printer entries has a key consisting of the printer name with a numerical suffix. This leads to the following method of extracting the printcap information using ypmatch:

    ---------- start of /usr/lib/yp/match_printcap
    #!/bin/sh
    read p
    n=1
    while ypmatch "${p}_${n}" printcap 2>/dev/null; do
        n=`expr $n + 1`
    done
    ---------- end of /usr/lib/yp/match_printcap
    
  4. Now test the YP arrangement:
    $ cd /var/yp; make # this should create the printcap map
    $ ypcat printcap # should provide the whole normalized printcap
    $ echo lp1 |/usr/lib/yp/match_printcap # yields lp1 printcap
    
  5. Add the printcap_path entry to /etc/lpd.conf:
    printcap_path=|/usr/lib/yp/match_printcap
    
  6. Test the use of the printcap path entry:
    $ lpq -Plp1 # shows the status of lp1
    
  7. Restart the lpd server and check to see that it accesses the right printcap information. Use the same lpq command, and then try lpc printcap lp1.

5.18 The Record Queue Name qq and force_queuename flags

Options used:

The printcap information consists of the printer name and aliases; when a job is spooled to a printer alias, it is actualy spooled to the main printer entry.

The qq use queuename option or its alias use_queuename tells LPRng to record the queue name that a job was queued to, and make it available to other software for processing. The force_queuename=... entry forces this name to be used. This capability has some interesting possibilities, as shown below.

pr1_landscape|pr1_portrait|pr_raw:lp=pr@host:qq

If a job is printed using lpr -Ppr1_landscape, then pr1_landscape will be recorded as the spool queue name by the LPRng software.

Later, when the job is processed by a filter, the filter will be invoked with a -Qpr1_landscape command line option. The filter can use the name of the queue to enable say, landscape, portrait, or raw orientations.

john|tom|frank:lp=pr@host:force_queuename=office

This printcap entry forces the queuename to be office; this information could be used by a central routing facility to process the information is a suitable manner.

5.19 Using the check_for_nonprintable Flag

Options used:

Normally, lpr checks an f format file for non-printable characters (i.e., escape characters) at the start of the print file. Disabling this check allows you to print executable files, etc., which can cause extreme abuse of your printer.

Disabling can be done on a single printcap basis, or you can do this on a global basis by modifying the configuration information (see lpd.conf).

The ml value specifies the number of characters that are to be checked. Clearly, if it is 0, none will be checked.

5.20 The fd Forwarding Off Option

Options used:

When the fd option is on (default is OFF), the lpd server will not accept jobs whose host name in the control file is not the same as one of the hostnames for the host which originates the connection.

This was a wimpy attempt to prevent job spoofing.

5.21 The rg Restrict Use to Group Members Option

Options used:

The rg value specifies a list of groups. If this value is present use of a printer or operation is restricted to only users in a particular group.

This was a wimpy attempt to do restrictions on print facilities. The -Ppr@host option overrides this check, unless the rg value is put in the LPRng defaults.

However, it does provide a simple tool to have clients do some form of permissions checking that only the lpd server could normally do.

5.22 The fx Allowed Formats Option

Options used:

The fx option specifies which formats are supported by a spool queue. The lpr program uses these to check if a requested format is supported. The default formats are fx=flp, i.e. - the default (normal), binary, and pr formats.

If the lpr -Fx option is used, the allowed formats are not checked.

5.23 Fixing Bad Control Files and Metacharacters

RFC1179 defines a simple protocol and standard for print jobs to be interchanged between print spooling systems. Unfortunately, there were some major mistakes in not specifying the exact form that text would take when placed in the control file.

In addition, there are some simple coding errors that have been made, but due to their wide distribution in major vendors software, need to be accommodated. See reverse_lpq_format for an example.

Defective RFC1179 Implementations

Options used:

Most printer (or print server box) manufacturers totally ignore the details of the RFC1179 protocol and simply accept the data files for printing, disregarding the control file until they need to print a banner or provide status information.

At this point, you suddenly discover that all sorts of little details will cause horrible problems. For example, the use of non-ASCII characters (i.e. - values are 128-255) in the J (job) line of a control file has been known to crash one network interface card in such a manner that a power-up is needed to restart the printer.

Also, as an exercise for the reader, here is another little gem. If you send one particular RFC1179 compatible print spooler a control file with a character whose value is 255 (i.e. 0xFF), the job will never get printed, and there is a mysterious diagnostic on the console:

unexpected end of input

This is due to the fact that the 0xFF eight bit value is getting sign extended to a 16 bit value 0xFFFF, which just turns out to be -1, or the error indication from a read.

OS/2

For various reasons, some versions of the OS/2 lpd print spooler have decided to make the control file and data file names have different formats. This can cause LPRng to suspect that somebody is trying to clobber other users jobs, and it will normally reject such jobs.

In addition, the OS/2 spooler does not follow RFC1179 correctly, and truncates the data and job file protocol exchange.

Serious Security Loophole

Finally, there is the subtle and nasty problem with some print filters that are not meta-char-escape proof. For example, suppose that a user decided to spool a job as follows:

lpr '-J; rm -rf /*;' /tmp/a

This would create a job file with the line:

J `rm /etc/passwd; echo Job;`

The job would get printed on a printer with the following printcap:

pr:sd=/...
  :if=/usr/local/hack

And of course we have /usr/local/hack (yes, this is a BAD example, so we won't start pointing out all the things):

#!/bin/sh
while [ -n "$1" ] ; do
        case "$1" in
        -J  )  shift; args="$args -M$1";;
        esac;
        shift;
done;
# reformat the command line
eval /usr/local/realfilter $args

The observant reader will notice that the above line gets expanded to:

eval /usr/local/realfilter -M`rm /etc/passwd; echo Job;`

The interesting thing to observe is that the realfilter will probably execute correctly, while the password file will magically vanish.

Using the fix_bad_job Option

Several microprocessor operating systems have decided to use extended fonts for information in the control file. While this is obnoxious, it is not serious. It will cause problems when trying to print messages on consoles, etc.

In order to prevent such problems, LPRng ruthlessly purges all characters but upper and lower case letters, spaces, tabs, and -_.@/:()=,+-% from the control file, replacing suspicious characters with '_'.

However desirable it may to detect when such obnoxious behavior is taking place, it is usually more desirable to replace suspicious characters with safe ones and proceed with processing the job. If the fix_bad_job configuration or printcap option is 0 (false) then when a suspicious character is spotted the job processing is aborted. If it is 1 (true), then the characters are silently purged and job processing continues. In addition, if the names used for control and data files are bogus or poorly formed, then they are renamed to something sensible.

For some installations, the default set of safe characters may be overly restrictive. For example, vintage software may generate files with # characters in the J line of the control file. The replacement of this character may cause other things to stop working.

The safe_chars option allows the user to specify an additional set of safe characters in the lpd.conf configuration file(s). For example, safe_chars=#" would allow the # and " characters to appear in the control file.

Using the bk Option and Control File Filters

Options:

One of the more serious problems is when a print spooler (LPR) program does not generate print jobs in a manner compatible with a remote system.

While LPRng performs checks for improper implementations of RFC1179, it will try to accept a job, even under the most severe abuse of the protocol. However, other spoolers are not so forgiving.

Some spoolers require that the contents of the control file be in exactly the order that the original 1988 BSD LPR software generated them. While some entries can be missing, all the entries present in the file must be in an explicit order.

The bk (Berkeley LPD compatible control file) option causes LPR and LPD to reformat the control file, removing objectionable entries. The control file of a job being sent to a remote printer will have its control file entries restricted to letters in (and the same order) as HPJCLIMWT1234.

However, there are some very odd commercial implementations that require more information than is present. To assist with this, the control_filter option can be used. This specifies a program that will process the control file before it is sent to a remote destination. See Filters for details on filter operation, and Control Filters for more information.

The control_filter program is run with the standard set of filter options. STDIN is attached (read/write) to the control file and the filter STDOUT will be used as the control file value sent to the remote host.

The control_filter can rewrite the control file, modify the names and formats of the data files, or perform other changes. Here is a small snip of PERL code that shows how to rewrite the control file:

# you need to get PERL to do a 'dup' call on FD 0
$status = 0;
@cf_lines = <STDIN>;
# mess about with the control file
foreach $line (@cf_lines) {
   # or whatever you want
   print STDOUT $line;
} 
exit $status;

The exit code of the control_filter is used to determine whether to proceed in processing. See Errorcodes for details.

Also, see Control Filters for more information.

5.24 Maximum Copies

Options used:

The mc value specifies the maximum number of copies of a job that can be printed on a printer using the lpr -Knn or lpr -#nn option.

The sc boolean cause LPR to reject requests to print multiple copies of a file.

5.25 The mi Minimum Spool Queue Space Option

Options used:

If this value is non-zero, then the lpd server checks to see that there is the specified number of bytes of file space available before transferring a job.

5.26 Debugging

Options used:

The LPRng software has a very powerful debugging capability. Since most printing problems occur on remote systems where it is impossible to run debuggers, and since most systems do not do core dumps of SETUID ROOT programs, the LPRng software provides a very verbose set of log file trace messages.

First, serious errors or other information are logged using the syslog() facilities. If these are not present on a system, then the messages are logged to the device specified by syslog_device.

For client programs, the debugging options are specified on the command line and output is directed to STDERR. For the lpd server, debugging commands can be specified on the command line OR as the db=options printcap value. Output is directed to the log file (lf option value, default log).

A typical debug entry has the format 2,network+1,database. This sets the general debugging level to 2, network debugging to 1 and the database debugging level to the default. The following debugging options and levels are supported.

The full_time flag forces the logging and other information which has timestamps to have a full (year, month, day, etc.) timestamp. The ms_time_resolution flag forces millisecond time resolution in the time stamp. The use_date flag forces a date value to be placed in a control file if there is none.

The use_info_cache (default ON) causes lpd to cache printcap and configuration information. This is desirable except when trying to change values in printcap files and test the results. By using use_info_cache@ in the configuration information, you can get immediate responses. Also, see lpc reread for another method.

5.27 LPD Specific

Options used:

These options are usually LPD specific. For example, the ipv6 specifies that the IPV6 protocol, rather than IPV4 will be used. In future versions, this may not be necessary.

The lockfile and logfile specify the location of the lock file and the log file used by the lpd server.

The spool_dir_perms and spool_file_perms (default 0700 and 0600 respectively) values are the (numeric) permissions for the spool directory and spool files.

The spread_jobs option is obsolete. The spread_jobs option was a desperation fix to handle difficulties with the arrival of a large number of jobs with the same or close job number. The LPD server would fork children, each of whom tried to lock the job files. The spread value randomly chose a new number in the range about the original job number. However, it is still preserved for legacy systems which still have problems with file locking.

The report_server_as option allows an administrator to masquerade a server with another name. This could be useful if various load sharing activities are being carried out, or if there are problems reconfiguring DNS to cause the correct server name to be reported.

5.28 Legacy Compatibility

The following arguments have been provided for compatibility with legacy systems.

The allow_duplicate_args Option

Options used:

Some users would like duplicate LPR and LPRM command line arguments to override earlier ones, i.e. - lpr -a x -a y should be equivalent to lpr -a y

The allow_duplicate_args option allows the various client programs to have duplicate arguments. The last specified argument on the command line will override previous values.

The break_classname_priority_link Option

Options used:

By default the class name and the job priority are identical. The break_classname_priority_link flag breaks this link, and the class can be specified separately from the priority.

Also, the maximum classname size specified by RFC1179 is 32 characters; the classname_length#nnn (default 31) allows a longer classname (up to 127 characters) to be used.

Setting the class_in_status option causes the class name rather than priority to be displayed in the status information.

The reverse_lpq_format Option

Options used:

Various Solaris and other System V implementations support an RFC1179 interface to remote printers. Unfortunately, there is a problem in that when they send a status request, the status format is reversed. That is, when LONG status format is wanted, they send SHORT, and vice versa.

The reverse_lpq_format= specifies a list of printers or IP addresses for which the lpd server will return LONG status when SHORT is requested, and vice versa. For example:

reverse_lpq_format=*.eng.com,130.192.0.0/16

will cause hosts whose Fully Qualified Domain Name (FQDN) ends in eng.com or from subnet 130.192.0.0 to have reversed status returned.

The return_short_status and short_status_length Options

Options used:

In order to be compatible with non-LPRng printers, some administrators would like lpd to return a short or brief status to normal status queries.

The return_short_status= specifies a list of printers or IP addresses for which the lpd server will return an abbreviated status when LONG status is requested. For example:

return_short_status=*.eng.com,130.192.0.0/16
short_status_length#3

will cause hosts whose Fully Qualified Domain Name (FQDN) ends in eng.com or from subnet 130.192.0.0 to get only 3 lines of detailed status returned.

The ignore_requested_user_priority and force_fqdn_host Options

Options used:

Some students... um... users... will request a high priority for their job in order to jump the queue of waiting jobs. This option will cause the lpd server to ignore the requested user priority. However, the topq operation will still be effective.

Similarly, some print spoolers do not put a FQDN host name in their control file. The force_fqdn_hostname flag will cause lpd to put a FQDN host name in.

5.29 Unused Legacy Printcap Options

Options used:

The above option is retained only for compatibility with legacy filter operation. See Filter Command Line Flags for details.

5.30 Compatibility with BSD printcap

If you previously had a BSD-style printer spooler, you might be lucky: your printcap will be directly usable by LPRng in many cases, i.e. - LPRng is almost totally backwards compatible with the old BSD printcaps. However, a lot of people have found out the hard way that LPRng is not completely compatible with BSD LPR.

For example, the fc/fs/xc/xs flag fields were used to specify serial line options and are no longer supported. The flag fields and their meanings are version and OS dependent and were not portable. We now use an stty commmand compatible ty or sy entry. Both options are synonyms, and the value is a set of stty(1) options. See Converting BSD fc,fs,xc,xs To LPRng sy for details.

There are other items, such as the fact that the keywords used by LPRng can be variable length, not just two letters, and other commenting and formatting conventions which are not supported by the older BSD servers.


Next Previous Contents