As described in the
Print Spooling Overview,
the heart of the LPRng system is information in the /etc/printcap
file.
The printcap information specifies:
lpc
print server.lpd
server processes jobs in each print queue.In order to explain a complex subject, we will start with a set of simple printer configurations, and explain the purpose and effect of each entry in the printcap.
For details about individual printcap
options, see the printcap(5)
man page from the LPRng distribution,
or use the
Index To All The Configuration and Printcap Options to find a specific
printcap option and its effects.
Options used:
rm=
remote host (machine)rp=
remote printerlp=
destination printer informationclient
client only printcap FLAGI'll use this simple example to explain the basics of the LPRng
printcap format
and introduce some of the LPRng network configuration options.
Here is a simple printcap file used to provide client programs
(lpr, lprm,
etc)
with remote printer and server information.
# printer lp1
lp1|printer1
:rm=localhost
# printer lp2 with continuation
lp2:\
:lp=pr@10.0.0.1:client
# printcap lp3, to printer pr, with overrides
lp3:rp=pr:rm=hpprinter.astart.com
:force_localhost@
# Simplest possible printcap entry - defaults for everything
lp4
#
sign are comments, and all
leading and trailing whitespace,
i.e. - spaces, tabs, etc, are ignored.
Empty lines are ignored as well.lp1
with an alias
printer1
and lp2
, lp3
, and lp4
with no aliases.|
character and options with the :
character;
tabs and spaces before and after the |
or :
characters
and at the start and end of lines are ignored.
You can use backslash (tt/\/) at the end of a line to create a multi-line
value for an option.
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 facility.
As you can see from the example,
there is no Name
printcap entry - this is part of the cm
option on the previous line.:option=value :option#value (legacy, not advised for new systems) :option :option@
Ts
and ts
are the same option name,
ts=Testing
and ts=testing
have their case preserved.
A string or integer value is specified by option=value
or option#value
.option#value
form is NOT recommended as some preprocessors
and database systems will treat # as the start of a comment
and delete the remainder of the line.
This has caused great consternation for sysadmins who wonder why their
NIS distributed printcap entries have been mysteriously truncated.option=
. The option
will set it to 1
.
If an option value contains a colon, then use the C (or Perl or Tck/Tk)
string escape \072
to represent the value.@
.
For example
sh
will set
sh
to TRUE and
sh@
to FALSE.There may be multiple 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.
Now let's examine the first printcap entry in detail. It is reproduced here for convenience:
# printer lp1
lp1|printer1
:rm=localhost
rm
(remote machine or host) option specifies the name or IP address
of the lpd
host running lpd
.
In this example the remote host is localhost
or the machine that the client is running on
and we assume that the lpd
server is running on the localhost.
Thus,
we would communicate with printer
lp1@localhost
.Let's look at the next printcap entry:
# printer lp2 with continuation
lp2:\
:lp=pr@10.0.0.1:client
lp2
printcap entry illustrates the use (and abuse)
of the \
continuation.
If you think about this,
we have really defined a printcap entry of the form:
lp2: :lp=pr@10.0.0.1:client
Luckily, LPRng ignores empty options like : :
.
While it is strongly recommended that \
be avoided it may be necessary for compatiblitye with other system utilities.
lp=pr@10.0.0.1
option is an alternate way to
specify a remote queue and server.
If the force_localhost
default is being used,
then the LPRng clients will ignore the 10.0.0.1
address
and still connect to pr@localhost
.
There is further discussion about this in the next section.client
option explicitly labels client only printcap information.
The lpd
server will ignore any printcap with the client
option.
When constructing complex printcaps,
this option is used to keep ensure that you have consistent printcap information.force_localhost
default,
and force the LPRng clients to connect directly to a remote server:
lp3:rp=pr:rm=hpprinter.astart.com
:force_localhost@
rp=
(remote printer)
remote print queue name to used when sending commands to the lpd
print server.force_localhost@
option is an example of a flag option.
The @
sets the option value to 0 (false).
We set force_localhost
to false,
which now allows the LPRng clients to connect directly to the
specified remote printer.
In this example,
the hpprinter.astart.com
could be a HP LaserJet Printer with a
JetDirect interface,
which supports the RFC1179 protocol.lpr
program will not
terminate or exit until all of the files have been transferred to the printer,
and this may take a long time
as the printer processes the files as they are received.Now let's look at the last printcap entry:
# Simplest possible printcap entry - defaults for everything
lp4
The last example is the simplest possible printcap entry.
This will cause LPRng clients to use the default values for everything.
The printer will be lp4
,
i.e. - the name of the printcap,
and the server will be localhost
if force_localhost
is set,
or the value of the default_remote_host
configuration option
if it is not.
Options used:
cm=
comment for statusif=
default job file filter lf=
log file mx#
maximum job sizelp=
output devicesd=
spool directory file sh
suppress headers (banners) sf
suppress form feeds between filesThe previous section discussed printcap entries for use by the client programs.
Now we will discuss printcap entries for use by the lpd
server.
In simple configurations or when we have the force_localhost
option enabled
we can use the same printcap for both LPRng clients and the lpd
server.
# Local ASCII printer
lp1|printer
:server
:cm=Dumb printer
:lp=/dev/lp1
:sd=/var/spool/lpd/lp1
:lf=log:af=acct
:if=/usr/local/sbin/lpf
:mx#0:sh:sf
lp1
.
This information will be displayed when requesting status information using
the lpq
program.printer
alias.
This allows a single spool queue to have multiple names.server
corresponds to the client
flag
and indicates this printcap entry is for the lpd
server only.cm
field supplies a information field
for lpq
(printer status) output.lp
value
specifies the destination file, device or remote spool queue to which data is sent.
In this example it is the device /dev/lp1
.
By default,
IO devices are opened for write-only operation.sd
specifies the spool directory where print job files
are stored until they are printed.lf
and af
options specify the names
for the log and accounting files, respectively.
These have the default values log
and acct
respectively,
and if not absolute pathnames are relative to the spool queue directory.
A log file is highly recommended.
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
checkpc
program.if
option specifies a filter program to be used
for jobs with the f
or default job format format.
Filters and print formats are discussed in section
Filters.
The lpf
filter translates LF
(line feed)
to CR/LF
(carriage return/line feed) sequences,
eliminating staircase output.mx
indicates the maximum file size for a print job.
Specifying 0 means that there is no limit.sh
suppress headers flag will suppress
printing banner pages.sf
suppress form feeds flag will suppress
form feeds between the files of a multi-file print job.In this section,
we will discuss the remaining tricky parts of the LPRng printcap
database:
combined client and server printcaps,
host specific printcap entries,
and the
tc
include facility.
The following is a complete description of how a printcap file is processed:
#
and blank lines are ignored.\
will have the \
discarded,
and all lines of a printcap entry are joined by removing the line separators (\n
)
and replacing them with a space.:
act as option separators,
and leading and trailing whitespaces are removed.tc=...
option only the last option setting is retained.server
option
and
server programs will discard a printcap entry with a client
options.oh
(on this host) option specifies a list of
IP addresses and mask pairs or glob strings which are used to determine
if this printcap entry is valid for this host
(see discussion below).tc
entries
which are combined.tc
include option
are extracted and combined in order.
(This allows include entries to appear after the referring printcap entry.)
Then printcap options will be combined with the included ones.
This has the effect that the options specified in the printcap entry
will override the ones from the tc
included entries.
%P printcap name
%h short host name (host)
%H fully qualified host name (host.dns.whatever)
%R remote printer (rp value)
%M remote host (rm value)
%D date in YYYY-MM-DD format
tc
resolution and %X expansion is done after all the files
have been processed.The following examples show how to use the above rules to your advantage. You can combine both client and server printcap information in a single file as well as dividing a printcap entry into several parts. Here is an example:
# seen by both client and server
lp1:lp=lp@pr1:mx#100
lp1:sd=/usr/local/spool/lp1:mx#0
# seen only by client
lp2:lp=lp@pr2:client
# seen only by server
lp2:lp=/dev/lp:server
lp1
,
the information is seen by both client and server.
The next printcap entry, with the same name lp1
,
will be combined with the second one.
The order of options is important - the entries are scanned in order
and an option will have the last value set.
Thus,
after having read both the lp1
printcap entries,
both client and server will have:
lp1:lp=lp@pr1
:mx#0
:sd=/usr/local/spool/lp1P
lp2
has a client and server version.
This is recommended when complex printcaps on multiple hosts and servers
are used.
Thus, the LPRng clients will see:
lp1
:lp=lp@pr1
:mx#0
:sd=/usr/local/spool/lp1P
lp2
:client
:lp=lp@pr2
and the server will see:
lp1
:lp=lp@pr1
:mx#0
:sd=/usr/local/spool/lp1P
lp2
:lp=/dev/lp
:server
If you have multiple printers of the same type whose configuration is almost
identical,
then you can define a set of tc only printcap entries containing common
information
and use the tc
include facility.
By convention,
all printer names start with an alphanumeric character and contain only
alphanumeric values, underscore (_
) or hyphen (-
).
A printcap entry starting with punctuation such as
period (.
) or at (@
) is processed by LPRng
but can only be used with the tc
include facility.
For example:
.hp:
:sd=/usr/local/spool/%P
:mx#0:sf:sh
hp1:tc=.hp,.filter
:lp=lp@10.0.0.1
hp2:tc=.hp,@filter
:lp=lp@10.0.0.2
@filter
:if=/usr/local/bin/ifhp
:lpd_bounce
.hp
and @filter
printcap enties will not be used as
real printcaps by LPRng,
but can be referenced by the tc
printcap include facility.
After tc
include processing is completed,
the printcap information would resemble:
hp1
:lp=lp@10.0.0.1
:if=/usr/local/bin/ifhp
:lpd_bounce
:mx#0
:sd=/usr/local/spool/%P
:sf
:sh
hp2
:lp=lp@10.0.0.1
:if=/usr/local/bin/ifhp
:lpd_bounce
:mx#0
:sd=/usr/local/spool/%P
:sf
:sh
%P
with the
printcap name,
so we would have:
hp1
:lp=lp@10.0.0.1
:mx#0
:sd=/usr/local/spool/hp1
:sf
:sh
hp2
:lp=lp@10.0.0.1
:mx#0
:sd=/usr/local/spool/hp1
:sf
:sh
When administring a large number of printers over a large area,
it is sometimes desireable to have a default printer for
each host.
This default printer may be different for each host,
and can be selected by using the oh
entry.
The oh
value is a list of the following entries
IP/n - address + mask length 10.0.0.0/8
IP/IP - address + mask 10.0.0.0/255.0.0.0
vvv - glob for hostname pc*.org.com
The LPRng software will determine the hostnames and IP addresses assigned to the host and then check to see if there is a match in the listed hostnames or IP addresses. If there is a match, the printcap entry will be used. If not, then the entry will be discarded. For example:
lp:oh=*.admin.org.com,10.0.0.5,10.2.0.0/16:lp=pr1@server1
lp:oh=*.eng.org.com:lp=hp@server2
booster.admin.org.com
,
then we would use lp=pr1@server1
,
as the *.admin.org.com
glob pattern would match our host name.booster.dev.org.com
and our IP address is 10.2.0.1,
then we would use lp=pr1@server1
,
as the 10.0.0.0/16
ip address would match.If you are generating complex printcap entries,
you might need to find out exactly what the LPRng servers or clients
will actually see.
The LPRng software has several diagnostic tools to help you.
The most simple to use is the
lpc
program.
The lpc client all
and
lpc server all
commands will display the printcap information that the
LPRng clients and lpc
server would see when executing on the host.
For example:
#> lpc client all
lp1
:lp=lp@pr1
:mx#0
:sd=/usr/local/spool/lp1P
lp2
:client
:lp=/dev/lp
Options used:
lp=
destinationrm=
remote host (machine)rp=
remote printer (machine)You can have the lpd
server forward jobs to another server
or print which supports the RFC1179 procol by using the
following printcap:
# Simplest
remote|Remote Printer
:lp=raw@server
# historical
remote:
rp=raw:rm=server
# Sometimes you have to connect to a non-standard port
special:lp=lp@server%2000
lp
printer entry is present, it will override the
rm
and rp
printer entries.lp=pr@host
format specifies that the output device is actually
a remote spool queue,
and jobs should be transferred using RFC1179 protocol.control file
,
and will not modify any of the job information.If the spool queue destination is a remote printer supporting the Socket API, then you can have LPRng open a connection directly to the printer. These include the older Apple printers with TCP/IP support and the HP JetDirect supported printers.
# Simplest
remote
:lp=10.24.2.3%9100
:sh:sf
lp=server%port
or lp=IPaddr%port
format
specifies that lpd
should open a TCP/IP connection to the remote
host and simply transfer verbatum the files to be printed.sh
and sf
will prevent lpd
from trying to generate
banner pages or put form feeds between jobs.While this is the simplest printcap, it is also the most dangerous as there is nothing to prevent a malformed job from being sent to the printer. The next printcap example is much more robust:
# Simplest
remote
:lp=10.24.2.3%9100
:of=/usr/local/lib/ifhp
:if=/usr/local/lib/ifhp
:sh:sf
ifhp
filter to precondition the printer
and to process jobs.
See
IFHP Filter for details.
The ifhp
filter will perform the appropriate printer resets,
translate job information,
and ensure correct printer operation in the presence of errors.
It will also produce voluminous error messages and logging information.The parallel printer printcap is very simple.
# parallel printer
lp:
:lp=/dev/lpr
:sh:sf
lp=/dev/lpr
specifies that lpd
should open the device for APPEND and simply transfer
job files to it.sh
and sf
will prevent lpd
from trying to generate
banner pages or put form feeds between jobs.If you discover that UNIX print jobs result in a staircase
appearance,
then you need to force your printer to do LF
(linefeed) to CR/LF
(carriage return/line feed) translation,
or do the translation yourself.
# Simple parallel printer
lp:
:lp=/dev/lpr
:if=/usr/local/bin/lpf
:sh:sf
By using the if=...lpf
filter,
the job will be passed through the lpf
filter,
which will do the LF
to CR/LF
translation.
If you have a more complex printer that handles PostScript, PCL, and PJL,
then you will need to use the more powerful
ifhp
filter:
# Simple parallel printer
lp:
:lp=/dev/lpr
:ifhp=model=hp4,status@
:of=/usr/local/bin/ifhp
:if=/usr/local/bin/ifhp
:sh:sf
See IFHP Filter for details. This entry will specify that the printer is an HP4, and that no status information is available. This is usually the case with a parallel port.
Options used:
rw
device opened RW flagbr#
serial port bit ratestty=
stty options for serial port configurationThe following is a typical printcap for a serial printer:
# Local Serial ASCII printer
lp2
:lp=/dev/ttya
:rw
:cm=Serial printer
:sd=/var/spool/lpd/lp2
:stty=9600 -echo -crmod -raw -oddp -evenp pass8 cbreak ixon
:if=/usr/local/sbin/lpf
:mx#0:sh
Let's examine the new options:
rw
flag will cause the printer port to be openned
read-write
,
and the lpd
server will report status information.sy
option specifies the stty(1)
flags and line speed needed to configure the serial line
(See
Serial Printers
for details).br
(bit rate) option
can be used to specify the line speed as well.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
and
oh
tags,
information for the specific sites can be separated out.
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 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 extract the additional
information for
pr1
and
pr2
respectively,
overriding the common lp
entries.
Options used:
lpd_bounce
lpd filters and then forwardsbq=
destination for filtered jobbq_format=
format of filtered jobWhen the destination of a spool queue is another spool queue the job is simply forwarded without any modifications. However, sometimes it is essential that the job be modified before forwarding, as when the remote spool queue is actually a printer, and jobs need to be converted to the format acceptable by the remote printer or banner pages added.
The lpd_bounce
flag marks a spool queue as a bounce queue.
Lpd
will perform all of the usually job processing steps,
such as banner generation,
filtering files,
etc,
but
saves the output to a file.
This file is then sent to the destination print queue for further
processing.
# Simple example of a bounce queue
bounce:lp=bounce@bouncehost
bounce:server
:lp=lp@remote
:lpd_bounce
# LEGACY
#bq=lp@remote
:sd=/usr/spool/lpd/%P
:if=/usr/local/bin/lpf
:vf=/usr/local/bin/lpf
:bq_format=l
# uncomment ab if you want banner
#ab
Some comments:
lpd_bounce
option marks the job as a bounce queue,
and the lpd
server will process the job through the appropriate
filter programs.bq=host
has the same effect as
lpd_bounce
.
This option is retained for compatibility with previous versions of LPRng.
It is recommended that this option not be used.bq_format
specifies the job format for the output file sent to the remote spool queue.
If not specified, it defaults to
l
(literal or binary).ab
(always print a banner) flag will force a banner to be
added to the job.
The banner generation is done as discussed in
Banner Printing.Options used:
translate_format=
format of filtered jobA rarely encountered problem is when a job is printed with
one format but for compatiblity needs to be processed with another.
The simple
translate_format=vlxf
option will rename format
x
files to
f
format.
This can be used to resolve problems with PC based sofware,
which spools jobs using the v
format.
Unfortunately,
many RCF1179 print spoolers do not understand the
v
format and mishandle the job.
A simple forwarding queue with the following entries will rename
v
format to l
(binary) format.
lp
:sd=/usr/spool/lpd/%P
:translate_format=vl
:lp=lp@printerserver
Options used:
lpr_bounce
lpr does filteringSome users would like the advantages of
the filtering and processing capabilities of a lpd daemon
without 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.
Options used:
destinations=
destinations for jobsrouter=
router programLPRng has the ability to route a job to one or
more destinations in a dynamic manner.
This is not the same as
load balancing,
as the destinations are hard coded and not able to be changed.
This is accomplished by having
a router
filter generate a set of destinations.
Here is a sample printcap entry:
t2|Test Printer 2
:sd=/var/spool/LPD/t2
:lf=log
:destinations=t1@server1,t1@server2,t1@localhost
:router=/usr/local/LPD/router
When a job arrives at the lpd
server,
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 writes to STDOUT a file specifying the destinations for the job. The destinations entries in this file file have the following format. Entry order is not important, but each destination 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
In this example, two copies of the job will be sent to the t1 and t2 spool queue servers. The Class (C letter value) and job priority information will be rewritten with the indicated values.
If routing information is specified by the router filter the job will be sent to the default 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.
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:
:lpd_bounce
#default
:rp=hpl5b
:destinations=hp5a,hp5b
:router=/usr/local/admscripts/bouncer.pl
hpl5a|for PLP/LPRng software - network based HP Jetdirect card:
:lp=hpl5a%9100
:tc=@hplcommon
hpl5b|for PLP/LPRng software - network based HP Jetdirect card:
:lp=hpl5b%9100
:tc=@hplcommon
# Common settings
@hplcommon:
:sd=/var/spool/lpd/%P
: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
# This is static, not dynamic balancing
#
# 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";
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.
Options used:
ss=
queue served by printer sv=
printers where jobs are sent (servers)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 laser
N queues.
This will forward a job from laser
to laser
N, 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.
Options used:
check_idle=
check for idle printer program 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
Options used:
printcap_path=
printcap file locationslpd_printcap_path=
additional server printcap file locationsThe
printcap_path
and
lpd_printcap_path
configuration options (see
lpd.conf(5)) specify a set of paths for the
printcap information.
Client programs use only printcap_path
and the lpd
server uses both
printcap_path
and lpd_printcap_path
.
The path names can be separated with whitespace,
commas, semicolons, or colons.
The default values are:
printcap_path /etc/printcap /usr/etc/printcap
lpd_printcap_path /etc/lpd_printcap /usr/etc/lpd_printcap
Since only the LPD server uses the
printcap file specified by the
lpd_printcap_path
,
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.
The all
printcap name and all
option
is reserved to provide a list of printers
available for use by the spooling software.
This is a desperation,
last ditch,
back to the wall option for
administrators
with systems that do not have ways to provide a list of printcap entries.
The 'all' printcap entry has the form:
all:all=pr1,pr2,...
The value of the all
option should be a list of printcap names
whose values will then be extracted.
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.
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.
This note is based on material sent to the
lprng@lprng.org
mailing list by
Paul Haldane
<paul@ucs.ed.ac.uk>
.
# From: Paul Haldane <paul@ucs.ed.ac.uk>
# To: lprng@lprng.org
# Subject: Re: Problem using plp with NIS
#
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 actually [<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
Date: Wed, 11 Sep 1996 00:11:02 +0200
From: Sven Rudolph <sr1@os.inf.tu-dresden.de>
To: lprng@lprng.org
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.
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 .
---------- 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)
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
$ 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
/etc/lpd.conf
:
printcap_path=|/usr/lib/yp/match_printcap
$ lpq -Plp1 # shows the status of lp1
lpq
command,
and then try lpc printcap lp1
.