<!-- LPRng-HOWTO SGML format -->
<!doctype linuxdoc system>
<article>
<!-- must be matched by / article at end -->
<title> LPRng-HOWTO
<author> Patrick Powell <tt><htmlurl url="mailto:papowell@astart.com" name="papowell@astart.com"></tt>
<date> 1 Sept 1998 (For LPRng-3.5.3)
<abstract>
The LPRng software is an enhanced, extended, and portable implementation
of the Berkeley LPR print spooler functionality. While providing the same
interface and meeting RFC1179 requirements, the implementation is completely
new and provides support for the following features: lightweight (no databases
needed) lpr, lpc, and lprm programs; dynamic redirection of print queues;
automatic job holding; highly verbose diagnostics; multiple printers serving
a single queue; client programs do not need to run SUID root; greatly enhanced
security checks; and a greatly improved permission and authorization mechanism.

The source software compiles and runs on a wide variety of UNIX systems,
and is compatible with other print spoolers and network printers that use
the LPR interface and meet RFC1179 requirements. 

The package comes with filters for PostScript and HP printers, as well
as the usual 'dumb' printers. Note that the PostScript and HP filters do
page counting and produce accounting information accounting. In addition,
there are a wide variety of other filters that can do page formatting,
and produce banner pages. 

For users that require compatibility with the SVR4 lp and lpstat interface,
lpr and lpq will simulate this interface, eliminating the need for another
print spooler package. In addition, a publically available PCNFSD server
is distributed with LPRng, and interfaces with the PC/DOS/Windows based
NFS style print spoolers. 

For users that require secure and/or authenticated printing support,
LPRng supports Kerberos V and/or PGP authentication methods. Additional
authentication support is extremely simple to add. 
</abstract>
<toc>
<sect>Introduction
<p>
This document is an attempt to provide some simple guidelines to set
up LPRng. It is aimed at system managers who want to replace their
vendor's printer daemon and its clients by LPRng.
<p>
LPRng can be configured in many ways, making it suitable for a broad
range of applications. It has a large amount of options which are
explained in the extensive documentation.
<p>
Despite (or due to) the extent of the documenting files, many new
users experience problems in setting up the package. One of the
reasons is that most of the LPRng documentation
assumed that you already have a
working knowledge of a BSD-style printer spooler.
<p>
In this document,
I will first try to give some simple guidelines that
should enable you to put together a simple working configuration
without being overwhelmed by options.
Next, there are some detailed descriptions of the way that LPRng
does things.
Finally,
there are detailed discussions about various LPRng facilities that
try to encapsulate common configurations and operations
that administrators and users encounter.
<label id="secfeatures">
<sect1>What is LPRng?
<p>
Gordon Haverland (<tt>haverlan@agric.gov.ab.ca</tt>) put it this way:
<p>
<quote>
LPRng is a print spooling system. It was designed to mimic the
Berkeley (University of California - Berkeley) Line Printers (LPR)
package, first found on Berkeley derivatives of the Unix operating
system. The LPRng package supports users being able to print a
document with little or no knowledge of the content or special
processing required to print the document; on a stand-alone machine,
on a LAN, or remotely. New (as compared to Berkeley LPR) features
include: lightweight <tt>lpr</tt>, <tt>lpc</tt> and <tt>lprm</tt>
programs; dynamic redirection of print queues; automatic job holding;
highly verbose diagnostics; multiple printers per queue; and enhanced
security (SUID not required in most environments),
improved file permissions, authorization checks, ...),
etc.
</quote>
<p>
Patrick Powell
(<tt>&lt;papowell@astart.com&gt</tt>), the author of
LPRng, has the following comments:
<quote>
<p>
LPRng started life in 1986 as PLP (Public Line Printer),
a replacement for the original BSD LPD code.
This was a one-shot effort to develop code that could be freely redistributed
without the restrictions of the BSD/AT&amp;T license,
and would allow non-license sites to fix and patch problems that they were
having with print spooling.
(This was before the Free Software Foundation.)
From 1988 to 1992, various groups added features, hacked, and modified the
PLP code, coordinated largely by Justin Mason
(<tt>&lt;jmason@iona.ie&gt</tt>),
who also started and sponsors the LPRng mailing list (see below).
<p>
In 1992,  I redesigned and reimplemented the PLP code
and named the result LPRng.
The goals of the LPRng project were to build a server system that was as
close to user abuse proof as possible,
that would provide services limited only by the inherent capacities of the
support system,
RFC1179 compliant,
and with extensive debugging capabilities to allow
quick and easy diagnostics of problems.
Over the period from 1994-1997,
LPRng users have suggested extensions,
provided patches,
and
added facilities.
I have tried to incorporate these in a controlled and cautious manner.
<p>
As a side effect of this work,
many security problems that could develop were identified and steps
taken to ensure that they were not present in LPRng.
For example,
if you want, you can run LPRng clients
such as lpr, lprm, lpc, and lpq as a non-privileged user;
this reduces the chances of users exploiting faults in the code and
gaining root privileges.
Bounds checking is performed on all input and formatting (for example,
snprintf() rather
than sprintf() is used),
as well as other preventive measures where appropriate.
</quote>
<p>
In most cases, LPRng is <bf>not</bf>
a drop-in replacement for BSD LPR. For most installations, you will
need adaptations.
However,
most of the changes are minor,
and many of the LPRng facilities are backwards compatible with
existing BSD print spooler facilities.
<p>
<label id="maillist">
<sect1>More Information
<p>
Much of the material in the LPRng documentation has been
included in this document.
For example,
previous releases of LPRng had a large selection of README files;
most of these are now incorporated into the HOWTO document.
<p>
Current information
about LPRng and the latest release
can be found on the LPRng web page:
<p>
<htmlurl url="http://www.astart.com/LPRng.html" name="http://www.astart.com/LPRng.html">
<p>
There is also a mailing list at <tt>lprng@iona.com</tt>. To subscribe,
send an email to <tt>lprng-request@iona.com</tt>. The body should contain
only the word `subscribe'. To get off the list later on, repeat the
same procedure, but use the word `unsubscribe'.
<p>
The LPRng distribution has a DOC directory that contains
several documents that are very helpful.
<bf>DOC/Intro.ps</bf> (Intro.txt for a slightly less readable text version) gives
an overview of the BSD/LPRng printing model,
and describes how LPRng carries out the printing process.
<bf>DOC/Install.ps</bf> (or Install.txt)
gives an overly detailed description of the LPRng installation and porting
procedures;  the README.installation file in the distribution is much shorter
and contains all of the information needed for the average installer.
The
<bf>DOC/Tutorial.ps</bf> (or Tutorial.txt) is a very short tutorial that
uses the non-privileged mode of LPRng which was designed for testing and
tutorial purposes.
<p>
Several presentations of LPRng and print spooling software have been made
at the Large Scale Installation Administrator (LISA) conferences and
are in the LPRng distribution and available on web sites.
<htmlurl url="ftp://ftp.astart.com/pub/LPRng/LPRngLISA95.ps" name="LPRng - An Enhanced Printer Spooler System">
was presented at the LISA95 conference,
and is in the LPRng distribution as DOC/LPRng-LISA95.ps.
On a more general topic,
the slides for the LISA97 tutorial on
<htmlurl url="ftp://ftp.astart.com/pub/LPRng/LISA97.tgz" name="Printers and Network Print Spooling">
are also in the LPRng distribution in the DOC/LISA97 directory.
<p>
The LPRng distribution also has a set of man pages (in the man/ directory)
that are the reference for the LPRng operation.
When in doubt, please consult them.
<sect1>Copyright
<p>
Material included in this document from the LPRng distribution
Copyright Patrick Powell 1988-1997, where applicable.
<p>
The rights to distribute this document complete or in part are hereby
granted for non-commercial purposes. Partial reproductions must
acknowledge the source.
<p>
Permission to distribute this file together with LPRng and `derived
works' (as defined in the LPRng license) is explicitly granted. This
is allowed independent of the license under which the software is
distributed.
<p>
Citing the document is allowed as long as the source is acknowledged.
<sect1>Disclaimer
<p>
<bf>THE MATERIAL IN THIS HOWTO IS PROVIDED WITHOUT FEE AND AS-IS WITH NO
WARRANTY REGARDING FITNESS OF USE FOR ANY PURPOSE. THE AUTHOR AND ALL
CONTRIBUTORS ARE NOT LIABLE FOR ANY DAMAGES, DIRECT OR INDIRECT,
RESULTING FROM THE USE OF INFORMATION PROVIDED IN THIS DOCUMENT.</bf>
<sect1>Commercial Support
<p>
<htmlurl url="http://www.astart.com" name="AStArt Technologies">
provides commercial support and enhancements for
the LPRng and other network software.
AStArt provides network and system consulting services for UNIX and NT
systems, as well as real time and network software.
<sect1>Web Site
<p>
Web Page:
<p>
<htmlurl url="http://www.astart.com/lprng.html" name="http://www.astart.com/lprng.html">
<label id="secftp">
<sect1>FTP Sites
<p>
The software may be obtained from <newline>
<htmlurl url="ftp://ftp.astart.com/pub/LPRng" name="ftp://ftp.astart.com/pub/LPRng">(Main site)
<newline>
<p>
Mirrors:<newline>
<htmlurl url="ftp://ftp.sage-au.org.au/pub/printing/spooler/lprng" name="ftp://ftp.sage-au.org.au/pub/printing/spooler/lprng"> (AU)<newline>
<htmlurl url="ftp://ftp.zod.wau.nl/pub/mirror/plp/LPRng" name="ftp://ftp.zod.wau.nl/pub/mirror/plp/LPRng"> (AU/NZ)<newline>
<htmlurl url="ftp://gwynne.cs.ualberta.ca/pub/LPRng" name="ftp://gwynne.cs.ualberta.ca/pub/LPRng"> (CA)<newline>
<htmlurl url="ftp://ftp.informatik.uni-hamburg.de/pub/os/unix/utils/LPRng" name="ftp://ftp.informatik.uni-hamburg.de/pub/os/unix/utils/LPRng"> (DE)<newline>
<htmlurl url="ftp://ftp.uni-paderborn.de/pub/unix/printer/plp/LPRng" name="ftp://ftp.uni-paderborn.de/pub/unix/printer/plp/LPRng"> (DE)<newline>
<htmlurl url="ftp://ftp.iona.ie/pub/plp/LPRng" name="ftp://ftp.iona.ie/pub/plp/LPRng"> (IE)<newline>
<htmlurl url="ftp://ftp.chembio.ntnu.no/pub/mirrors/LPRng" name="ftp://ftp.chembio.ntnu.no/pub/mirrors/LPRng"> (NO)<newline>
<htmlurl url="ftp://ftp.mono.org/pub/LPRng" name="ftp://ftp.mono.org/pub/LPRng"> (UK)<newline>
<htmlurl url="ftp://ftp.cs.columbia.edu/pub/archives/pkg/LPRng" name="ftp://ftp.cs.columbia.edu/pub/archives/pkg/LPRng"> (US)<newline>
<htmlurl url="ftp://ftp.cs.umn.edu/pub/LPRng" name="ftp://ftp.cs.umn.edu/pub/LPRng"> (US)<newline>
<htmlurl url="ftp://ftp.iona.com/pub/plp/LPRng" name="ftp://ftp.iona.com/pub/plp/LPRng"> (US)<newline>
<htmlurl url="ftp://uiarchive.uiuc.edu/pub/packages/LPRng" name="ftp://uiarchive.uiuc.edu/pub/packages/LPRng"> (US)<newline>
<sect1>Mailing List
<p>
To join the LPRng mailing list, please send mail to
<htmlurl url="mailto: lprng-request@iona.ie" name="lprng-request@iona.ie"> with the word 'subscribe' in the BODY
<label id="faqref">
<sect1>PGP Public Key
<p>
The LPRng distributions have an MD5 checksum calculated,
which is then signed with a PGP public key.
Here is the key for validating the checksums:
<verb>
Type Bits/KeyID    Date       User ID
pub  1024/00D95C9D 1997/01/31 Patrick A. Powell <papowell@astart.com>
							  Patrick A. Powell <papowell@sdsu.edu>

-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: 2.6.3i

mQCNAzLygTQAAAEEANBW5fPYjN3wSAnP9xWOUc3CvsMUxjip0cN2sY5qrdoJyIhn
qbAspBopR+tGQfyp5T7C21yfWRRnfXmoJ3FVtgToAsJUYmzoSFY08eDx+rmSqCLe
rdJjX8aG8jVXpGipEo9U4QsUK+OKzx3/y/OaK4cizoWqKvy1l4lEzDsA2VydAAUT
tCdQYXRyaWNrIEEuIFBvd2VsbCA8cGFwb3dlbGxAYXN0YXJ0LmNvbT6JAJUDBRA0
XonoiUTMOwDZXJ0BAQ2cBAC7zU9Fn3sC3x0USJ+3vjhg/qA+Gjb5Fi1dJd4solc4
vJvtf0UL/1/rGipbR+A0XHpHzJUMP9ZfJzKZjaK/d0ZBNlS3i+JnypypeQiAqo9t
FV0OyUCwDfWybgAORuAa2V6UJnAhvj/7TpxMmCApolaIb4yFyKunHa8aBxN+17Ro
rrQlUGF0cmljayBBLiBQb3dlbGwgPHBhcG93ZWxsQHNkc3UuZWR1PokAlQMFEDLy
gTSJRMw7ANlcnQEBYBYD/0zTeoiDNnI+NjaIei6+6z6oakqO70qFVx0FG3aP3kRH
WlDhdtFaAuaMRh+RItHfFfcHhw5K7jiJdgKiTgGfj5Vt3OdHYkeeh/sddqgf9YnS
tpj0u5NfrotPTUw39n6YTgS5/aW0PQfO9dx7jVUcGeod1TGXTe9mIhDMwDJI4J14
=3Zbp
-----END PGP PUBLIC KEY BLOCK-----
</verb>
<sect>The Most Frequently Asked Questions
<p>
In this section, the Most Frequently Asked Questions
have been placed, together with their answers.
You may notice that some questions have the same answer,
but the symptoms appear differently.
<p>
Some of these answers will reference other material in this FAQ,
or the LPRng man pages.
<sect1>Why do I get malformed from address errors?
<p>
This is the number one question asked by most LPRng users
who try to use LPRng with network printers or other systems
supporting
<ref id="rfc1179" name="RFC1179"> printing.
For details about LPRng and RFC1179, see
<ref id="rfc1179ref" name="RFC1179 and LPRng">.
<p>
The
<tt> malformed from address </tt> error is usually reported when
trying to send a print job from LPRng to other BSD LPR or RFC1179
LPR implementations, or with network connected printers
that have a built in LPR server.
This is due to the following RFC1179 rule:
<quote>
Servers originate a connection from ports in the range 721-731.
</quote>
<p>
WHY?  These are a subset of the 'reserved' ports in UNIX, and normal users
cannot open connections from them.  This provides a small amount
of security from UNIX users on the host 'spoofing' a server.
<p>
IMPLICATION:  in order to do use a reserved port,  the program
must have root privileges.  This means the LPR, LPD, LPQ, etc.,
programs must be installed SUID root.  This can open up a can
of worms with regard to security,  but LPRng has been designed to
take as much paranoid care as possible to avoid problems.
<p>
WHAT TO DO:
<newline>
When installing LPRng,  you will need to install the executables
SUID root.
In the <tt>src/Makefile</tt>,  you can remove the comment from the line
<verb>
PERMS=SUID_ROOT_PERMS
</verb>
and then do <tt> make install</tt>.
This will install the executables
SUID, and owned by root.
<sect1>It was working normally, then I get connection refused errors
<p>
This message usually appears when you have been sending a large number
of jobs to a network printer or a remote system.
The reason for this is a combination the above port 721-731 restriction
and the TCP/IP timeouts.
For details, see
<ref id="rfc1179ref" name="RFC1179 and LPRng">,
but here is a quick explanation.
<p>
A TCP/IP connection is usually specified as between
<tt>srchost:srcport, desthost:destport</tt>,
although in practice the order of source (src) and destination
(dest) is not important.
<p>
When a connection is established,  each end of the connection
exchanges the necessary flow control and error control information.
When a connection is terminated,
each end of the connection will not accept another connection from
the same <tt>host:port</tt> that was previously active
for a specified timeout period,
usually 10 minutes.
<p>
Some TCP/IP implementations go further:  they will not allow
<bf>ANY</bf> connection to be <bf>originated</bf>
(via the <tt>bind()</tt> system call or API)
from a port that was active,
or accepted on a port that was active for this timeout period.
<p>
Now let us see what happens when we have a client program,
which must originate a connection on port 721-731, connect
to the server, which waits for a connection on port 515.
We first try to make a connection from host:port
<tt>1.1.1.1:721</tt> to
<tt>1.1.1.2:515</tt>.
The first time that we make the connection (or the first connection)
we succeed.
We can transfer a file, etc., and then close the connection.
When we try to reconnect from
<tt>1.1.1.1:721</tt> to
<tt>1.1.1.2:515</tt>
we get an error such as
"address already in use"
or "connection refused".
<p>
Luckily,  we can use port 722 to originate a connection,
and we can connect from
<tt>1.1.1.1:722</tt> to
<tt>1.1.1.2:515</tt>.
We continue on, until we come to port 731,
and then we need to wait for our timeouts.
<p>
SOLUTION:
<p>
It appears that most RFC1179 implementations do not check for the exact
port
range 721-731,  but only that the connection originates from a
reserved port,
i.e. - in the range 1-1023.
You can extend the range of ports used by LPRng by changing the
<verb>
originate_port=721 731
</verb>
value in the defaults (<tt>LPRng/src/common/defaults.c</tt>) file or in the <tt>lpd.conf</tt>
file.  I recommend the following:
<verb>
originate_port=512 1022
</verb>
This is, in fact, now the default in LPRng software.
If you get the infamous
<tt>malformed from address</tt>
error message from your spooler, then
you will have to set originate_port=721 731,  and live with
a delayed throughput.
<sect1>Job is not in print queue, but it gets printed!
<p>
In the original BSD LPD implementation,
the LPR program copied users files to a special spool queue directory,
and then caused the LPD server to peek in the directory and print
the files.
<p>
This type of operation required spool directory space,
special SETUID programs,
and a slew of headaches in system security and management.
<p>
The LPR, LPQ, and other user programs in the LPRng suite use TCP/IP
connections and transfer jobs directly to a LPD server running on
a remote host,
or even the local host if appropriate.
Note that this type of operation does not require a LPD server to run
on each local machine.
In fact,  you can have a single host system performing all of your
printing.
This type of operation is very similar to a central mail server versus
individual systems, each having their own mail server and queues.
<p>
However,
some users require or want their jobs to be spooled on the local host system,
and then transferred to the remote printer.
This is usually the case when some type of processing (filtering)
is needed in order to print the job correctly.
There are several methods that can be used to force this.
<p>
Method 1: Explicit Printer Address
<p>
You can force a job to be sent directly to the <tt> pr </tt>
serviced by the LPD server on
<tt>host</tt>
by using the form:
<verb>
lpr -Ppr@host file
</verb>
<p>
You can also set the <tt>PRINTER</tt> environment variable to
a similar form, and get the same effect:
<verb>
PRINTER=pr@host; export PRINTER;
lpr file
</verb>
<p>
Method 2: User and Server Printcap Entries
<p>
If you want to have the benefits of a printcap file,
i.e. - you can use aliases or abbreviations for the names of printers,
then here is a couple of hints.
First,
the LPRng software scans the <tt>/etc/printcap</tt> file for printcap
entries, combining information for the same printer into a single entry.
Information found later in the printcap file will override earlier
information.
In addition,
you can tag entries as either being used for all utilities or just
for the LPD server.
Here are a couple of examples:
<verb>
# for all utilities
pr:lp=pr@host
# just for LPD
pr:server
  :lp=/dev/lp
# more information
pr:check_for_nonprintable@
# --- final result for LPR
pr:lp=pr@host:check_for_nonprintable@
# --- final result for LPD
pr:lp=/dev/lp:check_for_nonprintable@
</verb>
<p>
As you can see,
the <tt>server</tt>
keyword indicates that the printcap entry is only for the printer.
The LPR utility will send the job to the host, while the LPD server
will print it on <tt>/dev/lp</tt>.
<p>
Note that the <tt>lp=...</tt> information overrides the
<tt>:rp:</tt> (remote printer)
and
<tt>:rm:</tt> (remote machine) fields if they are present.
<p>
Method 3: Force sending to server on <tt>localhost</tt>
<p>
The
<tt>force_localhost</tt>
printcap or configuration flag forces non-LPD applications to send all
requests and print jobs to the server running on the local host.
<p>
This method is similar to the previous one,
but has the benefit that it can be configured as a global (i.e. -
applies to all printers) rather than printer specific.
You can put this in the <tt>/etc/lpd.conf</tt> file for general
application,  or have a printcap entry of the following form:
<verb>
# for all utilities
pr:lp=pr@host:force_localhost
</verb>
<p>
The LPD server will ignore the
<tt>force_localhost</tt> flag,
and send jobs to the <tt>pr</tt> queue on the <tt>host</tt>
machine.
However, the LPR, LPQ, etc., utilities will send their requests to the
server running on the local host.
<sect1>Job disappears and is never printed, but lpr works
<p>
This is a rather disconcerting problem,
and usually occurs when sending jobs to either a network printer or
a nonconforming
<ref id="rfc1179" name="RFC1179">
print spooler.
For details about LPRng and RFC1179, see
<ref id="rfc1179ref" name="RFC1179 and LPRng">,
but here is a quick explanation.
<p>
An LPD job consists of a control file,  which contains information
about the job,  and one or more data files.  RFC1179 is silent on the
order that jobs are sent;  however some implementations REQUIRE that
the data files be sent first,  followed by the control file.
<p>
SOLUTION:
<p>
Set the <tt>send_data_first</tt> flag in the printcap for the particular
printer,  or in the <tt>lpd.conf</tt> configuration file.  This is:
<verb>
:send_data_first:  (printcap)
send_data_first    (lpd.conf)
</verb>
<p>
Note that some printers/servers INSIST on the control file first;
You can clear the flag using <tt>send_job_first@</tt> if you need to.
<sect1>I get messages about bad control file format
<p>
RFC1179 describes a set of fields that MAY appear in the control file.
It is silent if other ones can appear as well.
Unfortunately,  some implementations will reject jobs unless they contain
ONLY fields from a very small set.  In addition,  RFC1179 is silent
about the ORDER the fields can appear.
<p>
LPRng quite happily will accept jobs from poor or nonconforming RFC1179
spooler programs,
but may chatter about bad formats.
You can ignore these errors,  or even turn them off by modifying the
appropriate lines of code and recompiling.
<p>
If you are sending jobs to one of these spoolers,
you can force LPRng to send jobs with only the fields described
in RFC1179 and in the BSD LPD implementation by setting the
the <tt> :bk: </tt> (BacKwards compatible) flag in the
printcap for your printer.
<label id="rfc1179">
<sect1>What is RFC 1179, the Line Printer Daemon Protocol?
<p>
RFC1179 defines a standard method by which print jobs can be transferred
using the TCP/IP protocol between hosts.
The standard was developed by simply detailing the way that
a version of the BSD LPD software did its job.
<p>
From the RFC Introduction:
<quote>
RFC 1179 describes a print server protocol widely used on
the Internet for communicating between line printer daemons (both
clients and servers).  RFC1179 is for informational purposes only,
and does not specify an Internet standard.
</quote>
<p>
Having said this,
the RFC then goes on to describe the protocol used
by a particular implementation of LPD.
The problem was that the RFC did not provide
any way to put extensions to the operations into the system,
and failed to specify such interesting details as the order in which
print jobs and their components could be transferred.
<p>
Comment by Patrick Powell <tt> &lt;papowell@astart.com> </tt>:
<quote>
<p>
Since 1988,
there have been a large number of print spooling systems developed which
claim RFC1179 conformance,
but which are mutually incompatible.
<p>
Rather than live with the limited capabilities of the RFC1179 standard,
LPRng has extended them by adding capabilities to perform remote control
of print spoolers,
encrypted and authenticated data transfers,
and other operations missing from the RFC1179 specification.
However,
great effort was made to be backwards compatible with older and other LPD
based systems.
<p>
LPRng was developed in order to be able to both accept and provide
interactions with these systems.  It does so by allowing various options
to be used to <em>tune</em> how print jobs would be exchanged.
Currently,
LPRng can be configured to send and receive print jobs between a vast number
of the existing spooling systems.
It is flexible enough to act as a gateway between non-compatible systems,
and has provisions to transform jobs from one format to another in a dynamic
manner.
</quote>
<p>
For a detailed explanation
about LPRng and RFC1179, see
<ref id="rfc1179ref" name="RFC1179 and LPRng">.
<sect1>I want to replace lp, lpstat, etc, but my programs need them
<p>
LPRng was designed as a replacement the BSD printing system. As such,
it inherited its command names and options from the latter. As you
might know, System&nbsp;V uses a totally different set of commands,
incompatible with the BSD ones.
<p>
The good news is that the LPRng binaries include an emulation for the
System&nbsp;V commands.
(See
<ref id="lpsimulation" name="lp Simulation">
for details.
Briefly, you create links to the appropriate programs,
and invoke them by the link names.
<em>Actually, these links are installed by default in recent versions.</em>
<p>
<verb>
ln -s lpr lp
ln -s lpq lpstat
ln -s lprm cancel
</verb>
<p>
If you make these links, calling <tt>lp</tt>, <tt>lpstat</tt> and
<tt>cancel</tt> will give you a (partial) SVR4 emulation. They have
their own man pages, which you should read if you need the emulation.
<p>
Since it is a <bf>partial</bf> emulation, you shouldn't expect everything
to work. In particular, I would guess that any script which relies on
the output format of one of your system binaries will break.
Again, see
<ref id="lpsimulation" name="lp Simulation">
for more details or additional suggestions.
<sect1>What are the drawbacks to LPRng?
<p>
There are many reasons to run LPRng, and most of them are related to
the extra features it has, compared to vanilla BSD LPR. A short list
is given in section
<ref id="secfeatures" name="What is LPRng?">. (A
more elaborate description can be found in the LPRng package itself.)
<p>
On the other hand, there are also reasons <bf>not</bf> to switch to LPRng:
<itemize>
<item>It is a complex system, and you'll probably need a lot of time
to get it working.</item>
<item>Switching from a System&nbsp;V system will require even more work.
On the other hand,
getting System&nbsp;V printing to work correctly for you may be even more work.
</item>
<item>You don't need any of the enhanced features,
and are not worried about security issues.</item>
<item>While there are many resources and books devoted to the old BSD
printer daemon, documentation for LPRng is rather limited: apart from
the
Introduction documents in the distribution package,
there's only this HOWTO
(at least, to the best of my knowledge).</item>
</itemize>
<p>
You should take these elements into account while deciding whether to
stick with your old software or not. If you do not decide in favor of
LPRng, you can stop reading here.
<label id="installref">
<sect>Installing your printer
<p>
In the following text, I will assume that your printer itself is
working properly. I.e., you are able to send data to the printer in
such a way that it responds by - well, printing the text :)
<p>
How to ensure this is highly system-dependent and falls outside the
scope of this document.
However,  here are some hints.
<sect1>Parallel Printers
<p>
Gordon Haverland
<tt>&lt;haverlan@agric.gov.ab.ca</tt>&gt; supplied this little script,
that will put you on the right track:
<verb>
#!/bin/sh
#set -v -x              # uncomment for debugging
PATH=/bin:/usr/bin
printer=
for $i in $*;
do
    case $i in
        /dev/*) printer=$i ;;
        *) ;;
    esac
done

if test -z &quot;$printer&quot;;
then
    echo USAGE: $0 device_name ;
    exit 1;
fi;

echo PRINTER TEST to $printer 1&gt;&2
for i in 1 2 3 4 5 6 7 8 9;
do
    echo $i &gt; $printer;
done
echo -e \\r\\f &gt; $printer
exit 0;
</verb>
<p>
If your printer is connected to the device name you provided,
then you should get a page of something out.  If the output
suffers from the ``staircase'' effect, you will see the numbers
``marching'' across the page, otherwise the numbers will all be in
a single column.
<label id="secserial">
<sect1>Serial Printers
<p>
If your printer is attached by a serial line,
then you may need to set the serial line characteristictics before sending
the job to the printer.
Here are a set of guidelines to following when attaching a serial port printer
to a serial line.
<p>
1. Check to make sure that the line is not enabled for login.
Logins are usually managed by the
<tt>getty</tt> (BSD)
or
<tt>ttymon</tt> (Solaris, SystemV).
Check your system documentation and make sure that these daemons are not
managing the serial line.
<p>
2. Check the permissions and ownership of the serial line.
For the most easy testing,
set the permissions to 0666 (everybody can open for reading and writing).
After you have made sure that you can send jobs to the printer,
you might want to change the ownership of the serial line to the LPD server
and change the permissions to 0600.
<p>
3. Make sure that you can print a test file on the printer via the
serial port.
This may require setting the line characteristictics and then sending
a file to the printer.
You should try to use 8 bit, no parity, with hardware flow control
and no special character interpretation,
and definitely no LF to CR/LF translation.
The problem is that different versions of UNIX systems have different
sets of stty(1) commands to do this.
The following simple test script can help in this.
<verb>
#!/bin/sh
# 9600, no echo, no CR
FLAGS= 9600 -raw -parenb cs8 crtscts
DEV= /dev/tty01
(stty $FLAGS; stty 1>&2; cat $1 ) <$DEV >$DEV
</verb>
<p>
This shows using stty to set the flags,
then to print the current settings, and then using
cat a file to the output.
If you attach a dumb terminal to the serial port,
you can even use this script to ensure that input from the device
is echoed to the output with the correct speed, parity,
etc.
<label id="fc">
<label id="fs">
<label id="xc">
<label id="xs">
<label id="secconvert">
<sect2>Converting BSD <tt>fc,fs,xc,xs</tt> To LPRng <tt>sy</tt>
<p>
Justin Mason
<tt>&lt;jmason@iona.ie></tt>
and Updated by Patrick Powell
<tt>&lt;papowell@sdsu.edu></tt>:
<p>
One of the worst things about old LPDs is that they expected you
to give it line characteristictics as octal numbers, using the
fc,fs,xc,xs printcap parameters. There are a number of disadvantages
to this, compared to using the <tt>ty</tt> parameter.
<p>
The sy format is portable, as it is supported on all UNIXes, rather
than simply BSD-derived ones; it's also <bf>much</bf> easier to understand
than the <tt>fc,fs,xc,xs</tt> format -- compare the two:
<verb>
# new format
    :sy=pass8 -parity tabs litout nl:
# old format
    :fc#0000374:fs#0000003:xc#0:xs#0040040:
</verb>
<p>
Note, also, that <tt>sy</tt> uses the same symbolic names as the <tt>stty</tt>
command, which is (relatively) well documented.
<p>
Anyway, assuming you have a set of printcaps which use the <tt>fc</tt>,<tt>fs</tt>,<tt>xc</tt>,<tt>xs</tt>
method, you can convert them to <tt>sy</tt> strings using the following
table.
<p>
Each parameter is an octal number, composed by logical-or'ing the
bits for the desired parameters together.  The bit patterns below
have been worked out from SunOS 4.1.2 /usr/include/sys/ttold.h.
For example, the following fc,fs,xc,xs set:
<p>
<verb>
:fc#0000374:fs#0000003:xc#0:xs#0040040:
</verb>
converts to the following sy string:
<verb>
:sy=-parity -echo -crmod -raw -lcase tandem cbreak litout decctq:
</verb>
<p>
Quite often, the resulting strings seem a little complicated, but
usually they can be broken down using combination modes. See the
<tt>printcap(5)</tt> manual page for details of which combination modes
LPRng supports.
<p>
In order to help you,  the 'xlate' program can be used to translate from the old
to the new form.
For example:
<verb>
#> xlate ":fc#0000374:fs#0000003:xc#0:xs#0040040:"
fc = 374
  clear LCASE (simulate lower case) try '-lcase'
  clear ECHO (echo input) try '-echo'
  clear CRMOD (map \r to \r\n on output) try '-crmod'
  clear RAW (no i/o processing) try '-raw'
  clear ODDP (get/send odd parity) try '-oddp'
  clear EVENP (get/send even parity) try '-evenp'
  clear ANYP (get any parity/send none) try '-parity? anyp? pass8? (caution)'
fs = 3
  set TANDEM (send stopc on out q full) try 'tandem'
  set CBREAK (half-cooked mode) try 'cbreak'
xc = 0
xs = 40040
  set LITOUT (literal output) try 'litout'
  set DECCTQ (only ^Q starts after ^S) try 'decctlq'
</verb>
<p>
Note that when you clear odd and even parity,  then you get
no parity or any parity.  You should use -parity to be compatible.
You might also want to add 'pass8' as well.
Thus, using the xlate output, you would try:
<verb>
   :sy=-lcase -echo -crmod -raw -parity pass8 tandem cbreak litout decctlq:
</verb>
<p>
Bits used in the fc, fs parameters:
<verb>
TANDEM    00000001    /* send stopc on out q full */
CBREAK    00000002    /* half-cooked mode */
LCASE     00000004    /* simulate lower case */
ECHO      00000010    /* echo input */
CRMOD     00000020    /* map \r to \r\n on output */
RAW       00000040    /* no i/o processing */
ODDP      00000100    /* get/send odd parity */
EVENP     00000200    /* get/send even parity */
ANYP      00000300    /* get any parity/send none */
NLDELAY   00001400    /* \n delay */
NL0       00000000
NL1       00000400    /* tty 37 */
NL2       00001000    /* vt05 */
NL3       00001400
TBDELAY   00006000    /* horizontal tab delay */
  TAB0    00000000
  TAB1    00002000    /* tty 37 */
  TAB2    00004000
XTABS     00006000    /* expand tabs on output */
CRDELAY   00030000    /* \r delay */
    CR0   00000000
    CR1   00010000    /* tn 300 */
    CR2   00020000    /* tty 37 */
    CR3   00030000    /* concept 100 */
VTDELAY   00040000    /* vertical tab delay */
    FF0   00000000
    FF1   00040000    /* tty 37 */
BSDELAY   00100000    /* \b delay */
    BS0   00000000
    BS1   00100000
ALLDELAY  00177400
</verb>
<p>
Bits used in the xc, xs parameters:
<verb>
CRTBS     00000001    /* do backspacing for crt */
PRTERA    00000002    /* \ ... / erase */
CRTERA    00000004    /* " \b " to wipe out char */
TILDE     00000010    /* hazeltine tilde kludge */
MDMBUF    00000020    /* start/stop output on carrier intr */
LITOUT    00000040    /* literal output */
TOSTOP    00000100    /* SIGSTOP on background output */
FLUSHO    00000200    /* flush output to terminal */
NOHANG    00000400    /* no SIGHUP on carrier drop */
CRTKIL    00002000    /* kill line with " \b " */
PASS8     00004000    /* pass 8 bits */
CTLECH    00010000    /* echo control chars as ^X */
PENDIN    00020000    /* tp->t_rawq needs reread */
DECCTQ    00040000    /* only ^Q starts after ^S */
NOFLSH    00100000    /* no output flush on signal */
</verb>
<p>
The following program translates the various bits to <tt>stty</tt>
flags.
<verb>
#include &lt;stdio.h>
#include &lt;string.h>
struct bits{
  char *name;
  int bitfields;
  char *comment;
  char *try;
  int mask;
};
/* f flags - used with the TIOCGET and the struct sgttyb.sg_flags field */
struct bits tiocget[] = {
{ "TANDEM",00000001, "send stopc on out q full", "tandem" },
{ "CBREAK",00000002, "half-cooked mode", "cbreak" },
{ "LCASE",00000004, "simulate lower case", "lcase" },
{ "ECHO",00000010, "echo input", "echo" },
{ "CRMOD",00000020, "map \\r to \\r\\n on output", "crmod" },
{ "RAW",00000040, "no i/o processing", "raw" },
{ "ODDP",00000100, "get/send odd parity", "oddp" },
{ "EVENP",00000200, "get/send even parity", "evenp" },
{ "ANYP",00000300, "get any parity/send none", "parity? anyp? pass8? (caution)" },
{ "NL0",0000000, "new line delay", "nl??",00001400 },
{ "NL1",00000400, "new line delay tty 37", "nl??",00001400  },
{ "NL2",00001000, "new line delay vt05", "nl??",00001400  },
{ "NL3",00001400, "new line delay", "nl??",00001400  },
{ "TAB0",00000000, "tab expansion delay", "tab??",00006000 },
{ "TAB1",00002000, "tab expansion delay tty 37", "tab??",00006000  },
{ "TAB2",00004000, "tab expansion delay", "tab??",00006000  },
{ "XTABS",00006000, "expand tabs on output", "tabs" },
{ "CR0",00000000, "cr??", "",00030000 },
{ "CR1",00010000, "tn 300", "cr??",00030000 },
{ "CR2",00020000, "tty 37", "cr??",00030000 },
{ "CR3",00030000, "concept 100", "cr??",00030000},
{ "FF1",00040000, "form feed delay tty 37", "ff??" },
{ "BS1",0010000, "backspace timing", "bs??" },
{ 0 } };
/* x flags - used with the TIOCLGET and the struct sgttyb.sg_flags field */
struct bits tiolget[] = {
{ "CRTBS",00000001, "do backspacing for crt", "crterase" },
{ "PRTERA",00000002, "\\ ... / erase", "prterase" },
{ "CRTERA",00000004, "\"\\b\" to wipe out char", "crterase" },
{ "TILDE",00000010, "hazeltine tilde kludge", "don't even think about this" },
{ "MDMBUF",00000020, "start/stop output on carrier intr", "crtscts" },
{ "LITOUT",00000040, "literal output", "litout" },
{ "TOSTOP",00000100, "SIGSTOP on background output", "tostop" },
{ "FLUSHO",00000200, "flush output to terminal", "noflsh?? (caution)" },
{ "NOHANG",00000400, "no SIGHUP on carrier drop", "nohand" },
{ "CRTKIL",00002000, "kill line with \"\\b\"", "crtkill" },
{ "PASS8",00004000, "pass 8 bits", "pass8" },
{ "CTLECH",00010000, "echo control chars as ^X", "echok" },
{ "PENDIN",00020000, "tp->t_rawq needs reread", "don't even think about this" },
{ "DECCTQ",00040000, "only ^Q starts after ^S", "decctlq? -ixany? (caution)" },
{ "NOFLSH",00100000, "no output flush on signal", "noflsh" },
{ 0 } };
char *msg[] = {
 "xlate optionstrings",
 "  Example",
 "  xlate \":fc#0000374:fs#0000003:xc#0:xs#0040040:\"",
    0
};
usage()
{
    char **m;
    for( m = msg; *m; ++m ){ fprintf( stderr, "%s\n", *m ); }
    exit( 1 );
}
main( argc, argv )
    int argc;
    char *argv[];
{
    char *s, *end, *value;
    int c, v, set;
    struct bits *table;
    if( argc != 2 ) usage();
    for( s = argv[1]; s && *s; s = end ){
        end = strchr( s, ':' );
        if( end ){
            *end++ = 0;
        }
        while( (c = *s) && isspace( c ) ) ++s;
        if( c == 0 ) continue;
        /* now translate option */
        value = strchr( s, '#' );
        if( value == 0 ) usage();
        *value++ = 0;
        v = strtol( value, (char **)0, 0 );
        printf( "%s = %o\n", s, v);
        switch( s[0] ){
            case 'f': table = tiocget; break;
            case 'x': table = tiolget; break;
            default: usage();
        }
        switch( s[1] ){
            case 's': set = 1; break;
            case 'c': set = 0; break;
            default: usage();
        }
        /* now we scan down the values */
        for(; table->name; ++table ){
            if( (table->bitfields & v)
                && ( ((table->bitfields & v) ^ table->bitfields)
            & (table->mask?table->mask:~0) ) == 0 ){
                printf( "  %s %s (%s) try '%s%s'\n",
                    set?"set":"clear",
                    table->name, table->comment,
                    set? "":"-", table->try );
            }
        }
    }
}
</verb>
<sect1>Network Printers
<p>
If you have a network printer,
then you may have several options.
First,
your printer may have built-in LPD server support.
However,
this may not function as expected.
It has been observed that most of these builtin servers do not correctly handle
large jobs or jobs with complex PJL, PCL, or PostScript with binary
information.
<p>
On the other hand,
your printer may have a TCP/IP port that connects directly to the
print engine.
For example,
on the HP JetDirect cards, sending a print file to port 9100  will cause
the print engine to process and print it.
In addition,
this channel may provide status and other information during the printing
process.
You can use the
<htmlurl url="http://www.l0pht.com/~weld/netcat/" name="netcat">
utility by Hobbit <tt>&lt;Hobbit@avian.org&gt;</tt>
to send files directly to the printer.
The simplest and easiest way to print a file to a network printer appears
to be: <tt> nc printer.ip.addr 9100 &lt file </tt>.
<label id="installingprograms">
<sect>Installing the programs
<p>
The basic components of the LPRng system are the executables
and the database files.
This section deals with generating and installing the executable
files.
<sect1>Files and Setup
<p>
The LPRng system can run in several different manners.
However,
for most users it will require the executables
for the server <tt>lpd</tt>, and the client applications for
job submission - <tt>lpr</tt>,
job status - <tt>lpq</tt>,
job removal - <tt>lprm</tt>,
and server management - <tt>lprc</tt>,
and the <tt>/etc/lpd.conf</tt>
and <tt>/etc/printcap</tt> files.
<p>
By default,
all the LPRng executables are installed in
<tt>/usr/local/bin</tt>,
which differs from other UNIX lpr systems,
which scatter them in various hidden and arcane locations.  Note that the
original printing system executables will need to be renamed or removed after
installing LPRng.
<p>
The
<tt><ref id="lpdconf" name="/etc/lpd.conf"></tt>
file contains the configuration
information for the server and application programs.
The LPRng system has a compiled in set of defaults that should be
suitable for most user applications.
In fact,  the default
<tt>/etc/lpd.conf</tt> does not override any of the precompiled values.
<p>
The
<tt><ref id="printcapref" name="/etc/printcap"></tt>
file contains the printer database information.
This information can override the defaults in
<tt>/etc/lpd.conf</tt>
<sect1>Source Code and Installation
<p>
If you have a binary distribution, you can skip this section. However,
since LPRng is a rapidly evolving package, I would advise you to check
whether there is a newer stable version available on one of the
<ref id="secftp" name="FTP sites.">
There should be a link to this stable version called
<tt>LPRng-stable.tar.gz</tt>.
<p>
You should also look at the
<ref id="sysdep" name="System specific notes">
to see if there are any special things that you need to do for your system.
<sect2>Building the software
<p>
Before you start to build the software,
you should read the
<tt>README.1st</tt> and <tt>README.installation</tt>
files in the distribution.
If you have GNU Make, do:
<verb>
./configure;
  #if you want internationalization,
  # ./configure --enable-nls
make clean all;
su   # you must do the following commands as root
make install
# if  you have not installed LPRng before,
# install default lpd.perms and lpd.conf file in /etc
if [ ! -f /etc/lpd.perms ]; then
    make default;
fi;
# update permissions,  create files needed for LPRng, check
# /etc/printcap file for problems.  Do as root:
./src/checkpc -f
</verb>
<p>
If you have BSD make do:
<verb>
./configure;
  #if you want internationalization,
  # ./configure --enable-nls
make -f Makefile.bsd clean all;
su   # you must do the following commands as root
make -f Makefile.bsd install
# if  you have not installed LPRng before,
# install default lpd.perms and lpd.conf file in /etc
if [ ! -f /etc/lpd.perms ]; then
    make -f Makefile.bsd default;
fi;
# update permissions,  create files needed for LPRng, check
# /etc/printcap file for problems.  Do as root:
./src/checkpc -f
</verb>
<p>
Use the configure
<tt>--bindir</tt> option to specify
the location of the binaries,
or edit <tt>src/Makefile</tt> or
<tt>src/Makefile.bsd</tt>
after running configure.
The lines you have to change are:
<verb>
INSTALL_BIN =     ${exec_prefix}/bin
# where daemons are installed: lpd
#INSTALL_LIB =     ${prefix}/lib
INSTALL_LIB =     ${prefix}/sbin
# where maintenance commands are installed: checkpc, setstatus
INSTALL_MAINT =   ${exec_prefix}/sbin
</verb>
<p>
By default, all LPRng executables are placed in
<tt>/usr/local/bin.</tt>
<sect2>Problems with compilation
<p>
If you have problems compiling the package, you can try these things:
<enum>
<item>Try <tt>gcc</tt> instead of your vendor's C compiler.
This is the standard compiler used for LPRng.
Almost without exception,
if you have a ANSI C compatible compiler and libraries
a POSIX compatible standard set of system support routines,
LPRng will compile and run out of the box.
The main problems are with missing or modified system support routines,
but configure will usually detect this and set flags to use
suitable alternatives.
</item>
<item>The <tt>configure</tt> and the <tt>make</tt> should be run on
the target host,
especially if the target host has a different version of the operating system.
This is extremely important for SunOS or Solaris,
where
there tend to be changes in the system's include files between
versions
as well as support libraries.
</item>
<item>
<tt>configure</tt>
and the LPRng software tends to make the assumption that newer versions will not
have the same problems that older versions have had.
</item>
</enum>
<p>
If you are not familiar with GNU configure, read the file
<tt>INSTALL</tt> for instructions.
<p>
Also read the notes for your OS in section
<ref id="sysdep" name="System-dependent notes">
for specific installation help (if any).
<label id="installation">
<sect1>Preparing to run the daemon
<p>
Don't be too impatient. Take your time to completely read through this
HOWTO. Things will be a lot easier when you first set up the
configuration files, and then start the new lpd.
<p>
During the course of these steps, you will have to change some files.
Be sure to keep a copy of the original file(s)!
<sect2>Removing Existing Facilities
<p>
Here is a summary and some scripts
to help you prepare your site for
running the server.
<enum>
<item>
Kill off the old server.
<verb>
BSD or Linux:
  ps -aux |grep lpd
  kill (pid of lpd server)
System V:
  ps -ea |grep lpsched
  kill (pid of lpsched server)
</verb>
</item>
<item>
You should remove or rename the existing print system executables.
Use the following script and
examine the <tt>/tmp/candidates</tt> file for possible programs.
Remove or rename the non-LPRng versions of the programs.
<verb>
# you might want to track down the old lpr, lpq, lprm binaries
find /usr -type file -name lp\* \; -print >/tmp/candidates
find /sbin -type file -name lp\* \; -print >>/tmp/candidates

# example rename
mv /usr/libexec/lpd /usr/libexec/lpd.orig
# example link
ln -s /usr/local/bin/lpd /usr/libexec/lpd
</verb>
</item>
<item>
Try starting and running <tt>lpd</tt> before permanently installing it.
You should do the next steps as <bf>root</bf>.
<verb>
/usr/local/bin/lpd;                # start up LPD
lpq;                               # test it with LPQ
</verb>
</enum>
<label id="startup">
<sect2>Startup Scripts
<p>
Don't forget to modify your system print startup files,
i.e. - the <tt>/etc/rc</tt> file in most BSD based UNIX systems,
or those in <tt>/etc/init.d</tt> or <tt>/sbin/init.d</tt> on System V and Linux.
You will need to have them reference the LPRng <tt>lpd</tt>
and not the original system executable.  For systems that use the System V
run-level scripts, you will also likely need to install a symbolic link in the
default system run-level directory (perhaps <tt>/etc/rc3.d</tt> or
<tt>/sbin/rc3.d</tt>) pointing to the master copy of the startup script in the
<tt>init.d</tt> directory.
<p>
Here is the core of a typical UNIX SystemV or LINUX startup file
that can be used to start up and shut down the server.
Note that you will most likely need to modify the
<tt>echo</tt> lines.
<verb>
#!/bin/sh
case "$1" in
  start) # Start daemons.
    echo "Starting lpd: \c"; /usr/local/bin/lpd; bin/echo;
    ;;
  stop) # Stop daemons.
    echo "Shutting down lpd: \c"
    server=`/usr/local/bin/lpc -Pany@localhost lpd \
    | awk '{for(i=1;i<=NF;++i){v=$i+0;if(v>1){ print v;exit;}}}'`
    if [ -n "$server" ]; then
      echo " server pid $server";
      kill $server;
    else
       echo " no server active";
    fi;
    ;;
    *)
       echo "Usage: lpd {start|stop}"
       exit 1
     ;;
esac
</verb>
<label id="lpsimulation">
<sect1>Replacing UNIX SystemV lp, lpstat Printing Services
<p>
Many UNIX utilities in the Solaris and HP UNIX environment use the
UNIX System V <tt>lp</tt> and <tt>lpstat</tt>
programs.
It is almost impossible to modify their operation to use the
LPRng <tt>lpr</tt> or <tt>lpq</tt> programs,
as they depend on various return codes and information.
<p>
Here are Patrick Powell's comments on this:
<quote>
After fighting with LP simulation,  I finally decided to make the
interface part of the LPR/LPQ package.  If LPR is invoked as LP,
then it will 'act' like a 'semi-compatible' LP; similarly for LPQ and LPSTAT,
and LPRM and CANCEL.
</quote>
<p>
To get this functionality, you need to either make a symbolic link
or a hard link to the appropriate executable.
<verb>
cd /usr/local/bin
ln -s lpr lp
ln -s lpq lpstat
ln -s lprm cancel
cd /usr/sbin
ln -s /usr/local/bin/lpr lp
ln -s /usr/local/bin/lpq lpstat
ln -s /usr/local/bin/lprm cancel
</verb>
<p>
See the man pages for lp, lpstat, and cancel in the LPRng/man directory.
Note that not all the functions of the original
lp programs are supported.
These man pages should be installed to replace the normal
lp, etc, man pages.
<p>
For some purposes,  the rather rugged <tt>lpstat</tt>
simulation provided by this method does not work.
<htmlurl url="mailto:garrett@qualcomm.com" name="Garrett D'Amore &lt;garrett@qualcomm.com&gt">
has written some much improved versions;
take a look at
<htmlurl url="http://people.qualcomm.com/garrett/" name="http://people.qualcomm.com/garrett/">
for details.
<label id="user">
<label id="group">
<sect1>Setuid ROOT and Security Issues
<p>
By default,
LPRng executables are installed setuid ROOT.
When running,
they normally perform all operations with the user's effective UID,
and perform all other operations set to the user and group specified by the
<tt>user=daemon</tt>
and
<tt>group=daemon</tt>
printcap entries,
except for a very few places where they take extreme precautions against
actions that could cause security breaches,
change the EUID to ROOT,
and then immediately change back to the normal operation.
<p>
As a scan of the various CERT Security Advisories will indicate,
many programs that run SUID root can be serious security loopholes.
While LPRng has been designed and implemented with security and
paranoia in mind,
there is still the possibility that user level or LPD processes
can have an exposed security loophole.
<p>
To reduce the risk,
the user level utilities such as lpr, lprm, lpq, and lpc can be installed
non-setuid.
This effectively closes several possible security loopholes.
To install the executables as non-setuid,
the distribution <tt>src/Makefile</tt>
must have the following lines commented out,
and then LPRng must be reinstalled:
<verb>
edit src/Makefile
   # comment out the next line to have LPRng installed non-setuid
   PERMS=$(SUID_ROOT_PERMS)
make install
</verb>
<label id="sysdep">
<sect1>System specific notes
<p>
The following are a set of suggestions and recommendations for
specific systems.
<label id="solarisinstall">
<sect1>Solaris 2.4, 2.5, 2.6
<p>
The Sun Solaris operating system is derived from the System V UNIX
baseline.
Use the following installation procedure.
<enum>
<item>
First,
install the LPRng software
and then rename all of the existing Solaris print facilities.
See the
<ref id="installation" name="Installation"> section for details.
You should especially look out for
<tt>lp</tt>,
<tt>lpstat</tt>,
<tt>lpsched</tt>,
<tt>lpadmin</tt>,
and other executables used by Solaris for print support.
</item>
<item>
Next,
make sure you update the <tt>/etc/rc</tt> startup files.
During the startup or initialization,
Solaris will invoke a set of individual startup files.
You will find that the startupfile files are usually links to a
common one in the <tt>/etc/init.d</tt> directory.
<verb>
># grep -l lpsched /etc/rc* /etc/rc*/* init.d/* init.d/*/*
/etc/rc0.d/K20lp
/etc/rc2.d/K20lp
/etc/rc2.d/S80lp
/etc/init.d/lp
># ls -l /etc/rc0.d/K20lp
lrwxrwxr-x  1 root  bin  1 Dec 29 23:39 /etc/rc0.d/K20lp -> ../../init.d/lp
</verb>
</item>
<item>
Replace the existing lp startup file with one similar to the
<ref id="startup" name="startup script"> in the previous section.
</item>
<item>
Check the <tt>/etc/inetd.conf</tt> file for a line like:
<verb>
printer stream tcp nowait root /usr/lib/print/in.lpd in.lpd
</verb>
<p>
and remove it if it is present.
</item>
<item>
<em>Reboot</em>.
Don't try to be fancy and kill off processes,
use the <em>nlsadmin</em> command,
or other insanity.
This is brutal,  but appears to be necessary in order to ensure
that the networking support is set up correctly.
</item>
<item>
When the system restarts, try using <tt>lpq</tt>
to check to see if the <tt>lpd</tt> server is active.
</item>
</enum>
<p>
James P. Dugal <tt>&lt;jpd@usl.ed&gt;</tt> has also makde the following
suggestions as well.
<verb>
From: "Dugal James P." <jpd@usl.edu>
To: lprng@iona.com
Subject: Re: [LPRng] start up trouble

Here are some more tips for Solaris:

1. If /var/spool/cron/crontabs/lp exists, remove it.

In fact, we actually test if /etc/init.d/lp exists on any newly-installed
system, and if so, we issue these commands:
        /etc/init.d/lp stop
        /usr/sbin/pkgrm -n SUNWpsu
        /usr/sbin/pkgrm -n SUNWscplp
        /usr/sbin/pkgrm -n SUNWpcu
        /usr/sbin/pkgrm -n SUNWpsr
        /usr/sbin/pkgrm -n SUNWpcr
        /bin/rm -f /var/spool/cron/crontabs/lp

Regards,
-- James Dugal, N5KNX           Internet: jpd@usl.edu
Associate Director              Ham packet: n5knx@k5arh.#lft.la.usa.noam
Computing Support Services      US Mail: PO Box 42770  Lafayette, LA  70504
University of Southwestern LA.  Tel. 318-482-6417       U.S.A.
</verb>

<sect1>Solaris, Newsprint and FrameMaker
<p>
The following is a guide to using LPRng and
Sun Microsystems Newsprint by
Christopher Hylands, Ptolemy Project Manager
of the University of California.
<p>
The Sun Newsprint printer is actually
an OEM version of the  Textronix PhaserII;
Sun Microsystems appears to have dropped support for Newsprint,
and the recommended migration path is to buy a PostScript printer.
If you want more information on using the Newsprint system,
notes are available via
<tt>http://ptolemy.eecs.berkeley.edu/~cxh/lprng.html</tt>.
<p>
Looking through the mailing list logs, it looks like everyone was
having a hard time getting lprng to work with Sun's braindead
newsprinters.  I tried using ghostscript, but the fonts were, IMHO,
ugly, so I spent a little time getting the newsprint fonts to work.
<p>
The key thing was to grab the file
<tt>/usr/newsprint/lpd/if</tt>
from a SunOS4.1.3 newsprint installation.
If you cannot get this code,
then the installation will be extremely difficult.
<p>
To install lprng on a Solaris2.x machine,
you need to first stop the existing print services and install the
startup scripts for LPRng.
Note that if there is a local printer, you may have
to also fix the permissions of the device. Typical commands are:
<verb>
chown daemon /devices/sbus@1,f8000000/SUNW,lpvi@1,300000:lpvi0
</verb>
<p>
We use the following simple <tt>if</tt> script.
<verb>
#/bin/sh
# extremely simple filter script
/bin/cat
</verb>
<p>
The Sparcprinters use licensed fonts from NeWSprint. To use the
licensed fonts, you must have the lprng spool directory for the
sparcprinter in the same location as spool directory of the brain
dead Solaris lp system.  If your printer is named xsp524, then this
directory would be <tt> /etc/lp/printers/xsp524</tt>.
<p>
The printcap entry looks like:
<verb>
sp524|524:
    :mx#0:sf:sh:sb:
    :lp=:rm=doppler:rp=xsp524:mx#0:
    :sd=/var/spool/lpd/sp524d:
    :lf=/var/spool/lpd/sp524d/log:
xsp524|Sun SPARCprinter NeWSprint printer:
    :mx#0:sf:sb:sh:rs:
    :lp=/dev/lpvi0:
    :sd=/etc/lp/printers/xsp524:
    :lf=/etc/lp/printers/xsp524/log:
    :af=/var/spool/lpd/xsp524/acct:
    :if=/usr/local/lib/newsprint/if:
</verb>
<p>
The
<tt>/usr/local/lib/newsprint/if</tt>
was copied from
<tt>/usr/newsprint/lpd/if</tt>
in a SunOS4.x installation of the newsprint
software.
Unfortunately, the newsprint engine is so brain dead that it
needs many environment variables set, so it is fairly difficult to
come up with a clean script to start the engine. I made the following
changes to the file.
<enum>
<item>
First, set the path in the script.
You may also need to change defaults to suit your preferences:
<verb>
PATH=/usr/ucb:/usr/bin:/etc:/usr/etc:/opt/NeWSprint/bin:/opt/NeWSprint/np/bin:
PATH=$PATH:$NPHOME/pl.$ARCH/bin:$NPHOME/np/bin; export PATH
</verb>
</item>
<item>
You will also need a
<tt>/etc/lp/printers/printername/.params</tt>
file. If you
are using the same spooler directory as the directory that the Solaris
lp system uses, then the .param file should appear there. If you are
using a different spooler directory, then you will need to copy
the .param file from elsewhere and edit it accordingly.
</item>
<item>
If you are going to move a license to a new printer, you should
probably save the .param file in the old printer spooler directory.
Run /opt/NeWSprint/bin/fp_install and remove the license from the
old printer and assign it to the new printer.
You could run /opt/NeWSprint/bin/rm_np_printer and remove the printer,
but that will get rid of the .param file
</item>
<item>
FrameMaker under Solaris2.x uses the lp command. The fix is to edit
$FMHOME/fminit/FMlpr and comment out the lp line and add an lpr line
<verb>
sunxm.s5.sparc)
    lpr -P"$PRINTER" "$FILE"
    #lp -c -d"$PRINTER" "$FILE"
</verb>
</item>
</enum>
<verb>
Christopher Hylands, Ptolemy Project Manager  University of California
cxh@eecs.berkeley.edu                 US Mail: 558 Cory Hall #1770
ph: (510)643-9841 fax:(510)642-2739       Berkeley, CA 94720-1770
home: (510)526-4010 (if busy -4068)       (Office: 493 Cory)
</verb>
<sect1>Linux
<p>
At the time of this writing (Jan 1998),
the three major Linux
distributions (Slackware, Red Hat and Debian) carry an older version
of LPRng. Users of those systems should download the
latest stable release, and install that instead of the distributed
binaries.
<p>
This is mostly important for Slackware 3.2 users, as this version
installs LPRng by default.
Patrick Volkerding changed the default back to
BSD LPR in Slackware 3.3,
as many users had experienced problems
because they didn't realize they weren't using the BSD software.
<p>
Debian's <tt>dselect</tt> utility lets you choose between all
packages. Amongst these are LPRng, as well as the traditional LPR
software.
<p>
You have to make sure your kernel is configured correctly. The
documentation for the kernel sources in
<tt>/usr/src/linux/Documentation/</tt> and the <tt>Kernel-HOWTO</tt>
will help you to generate a new kernel if needed. You will need to set
the following options:
<itemize>
<item>Networking support (<tt>CONFIG_NET</tt>)</item>
<item>TCP/IP support (<tt>CONFIG_INET</tt>)</item>
<item>If your printer is connected to a parallel port, you will also
need the `Parallel Printer Support' (<tt>CONFIG_PRINTER</tt>).
You can use this as a module if you want.</item>
<item>For a serial printer, answer `Yes' when asked if you want
`Support for serial devices' (<tt>CONFIG_SERIAL</tt>). This is
also available as a module.</item>
</itemize>
<p>
Once you have done this,
the current releases of LPRng will install and run without
problems.
See the
<ref id="installingprograms" name="Installing the programs">
section for details on how to install LPRng and
deactivate the existing print support.
<p>
You may need to update the printcap file and filters.
See <ref id="printcapref" name="/etc/printcap Print Spool Database File">
for details.
<sect1>AIX
<p>
This information was supplied by
<htmlurl url="mailto:nitschke@math.unihamburg.de" name="Dirk Nitschke">,
as of August 1997,
and describes how to install the LPRng package on a workstation
running AIX 4.1.x and possibly 3.x.x as well.
Dirk would be interested in any comments or corrections.
<p>
Printing on AIX systems is different. AIX provides a general
queueing facility and printing is only one way to use it. You submit a
print job to a print queue using one of the commands
<tt>qprt</tt>, <tt>enq</tt>. You can use the BSD or
System V printing commands <tt>lpr</tt> or <tt>lp</tt>, too. The
qdaemon watches all (general) queues and knows how to handle your
job. A (general) queue is defined in the file
<tt>/etc/qconfig</tt>. The format of this file is different from
the <tt>printcap</tt> format.
<p>
OK, how to replace the AIX printing system? There is no group
<tt>daemon</tt> on AIX. Therefore you have to change the default
group for file ownership and process permissions. We decided to use
the <tt>printq</tt> group. The user <tt>daemon</tt> exists on
AIX but we have chosen <tt>lpd</tt> as the user who runs
<tt>lpd</tt> and all filters and owns the spooling directories.
You can change the values for <tt>group</tt>,
<tt>server_user</tt> and <tt>user</tt> in your
<tt>lpd.conf</tt> file or in the sources
<tt>src/common/default.c</tt>. This is an example for
<tt>lpd.conf</tt>:
<verb>
# lpd.conf for AIX (change group, server_user and user)
group=printq
server_user=lpd
user=lpd
</verb>
Compile and install the LPRng package. Create your
<tt>printcap</tt>, spooling directories, accounting and logfiles
and so on.
Don't forget to use
<ref id="checkpc" name="checkpc"> to make sure that all the
permissions are set correctly and the necessary files
are created.
<p>
Then stop all print queues defined on your workstation. Use
<verb>
# chque -q queuename -a "up = FALSE"
</verb>
for this (yes, blanks around <tt>=</tt> are needed).
<p>
If you have local printers attached to your system you will have an
<tt>lpd</tt> running. Stop this daemon using SMIT (Print Spooling,
Manage Print Server, Stop the Print Server Subsystem). Choosing
<tt>both</tt> also removes <tt>lpd</tt> from
<tt>/etc/inittab</tt>. Maybe it's faster to do this by hand:
<verb>
# stopsrc -p'pid of /usr/sbin/lpd'
# rmitab "lpd"
</verb>
<p>
Now delete all print queues (managed by qdaemon) defined on your
system. You can use SMIT for this or the commands
<tt>{mk,ch,rm}que</tt>, <tt>{mk,ch,rm}quedev</tt>,
<tt>{mk,ch,rm}virprt</tt>. The SMIT fast path is <tt>smit
rmpq</tt>.
<p>
To start the new <tt>lpd</tt> at system startup you have to add
an entry to <tt>/etc/inittab</tt>:
<verb>
# mkitab "lpd:2:once:/full/path/lpd"
</verb>
<p>
Some work has to be done if have have a local printer attached to
your workstation. You have to create a device file like
<tt>/dev/lp0</tt>. The SMIT fast path for this is <tt>smit
mkdev</tt>. Choose <tt>Printer/Plotter</tt> and then
<tt>Printer/Plotter Devices</tt>. Now <tt>Add a
Printer/Plotter</tt>. To create a parallel
printer device select the following:
<verb>
Plotter type:              opp Other parallel printer
Printer/Plotter Interface: parallel
Parent Adapter:            ppa0 Available
</verb>
Now define the characteristictics of the device:
<verb>
Port Number: p
</verb>
(<tt>p</tt> is for parallel).
Go to the field
<verb>
Send all characters to printer UNMODIFIED   no
</verb>
and select <tt>yes</tt>! We have had a lot of trouble with
<tt>no</tt>.  This is very important! Expect erroneous output if
you choose <tt>no</tt>. If you have already created a device
file, change the characteristictics! SMIT's fast path is <tt>smit
chdev</tt>.
<p>
Finally remove all AIX printing commands like <tt>qprt</tt>,
<tt>lp</tt>, <tt>cancel</tt>, <tt>lpr</tt>,
<tt>lprm</tt>. You will find a lot of them in
<tt>/usr/bin</tt>. Do not remove <tt>enq</tt> and friends if
you want to use the general queueing facility.
<p>
Now you can start your new <tt>lpd</tt>.
<sect1>Appletalk Support
<p>
Netatalk is used to communicate from TCP/IP to
Appletalk printers and vice versa.
The netalk distribution FAQ is at:
<p>
<htmlurl url="http://www.umich.edu/~rsug/netatalk" name="http://www.umich.edu/~rsug/netatalk">
<p>
There are two issues with using netatalk.  The first issue
has to do with printing to LPRng-served printers from Macs.
The second issue has to do with printing from LPRng to
network printers that speak AppleTalk.
<p>
<sect1>Apple to LPRng Printing
<p>
After you have installed and gotten netatalk working,
you can use the following configuration file to print
from a Macintosh to an LPRng printer.
<verb>
From edan@mtu.edu Mon Sep 29 21:31:25 1997
Date: Tue, 30 Sep 1997 00:04:58 -0400 (EDT)
From: Edan Idzerda <edan@mtu.edu>
To: lprng@iona.com
Subject: Re: [LPRng] Netatalk configuration file
> Somebody posted a very nice Netatalk papd.conf file
> that showed the setup for LPRng.  If anybody has this
> handy could you post it?
Well, *I* use:
Your 32 Character Printer Name:\
        :pr=|/your/path/to/lpr -Pprintername
        :ppd=/your/path/to/ppd/files/yourprinter.ppd
--
Edan Idzerda    <edan@mtu.edu>
System Administrator -- Michigan Technological University, Houghton MI USA
</verb>
<sect1>LPRng to Appletalk Printing
<p>
The netatalk package comes with a PostScript filter called <tt>psf</tt>.  After
compilation, it is in (e.g.) <tt>netatalk-1.4b2/etc/psf</tt> and will be installed
in (e.g.) <tt>/usr/local/atalk/etc/</tt>.  After installation, there will also
be a directory <tt>/usr/local/atalk/etc/filters</tt> that contains
<tt>ifpap</tt>, <tt>ofpap</tt>,
et al.  These are just symlinks to <tt>psf</tt>, and <tt>psf</tt> will do the appropriate
thing based on how it was invoked.  If there's a 'pap' in the name,
<tt>psf</tt> uses AppleTalk to talk to the printer.  See psf(8) for more information.
<p>
A printcap entry for a network printer looks like the following:
<verb>
dave|Dave's 32 Character Printer Name:\
    :sd=/var/spool/dave:\
    :lf=/var/adm/lpd-errs:\
    :lo=lock:\
    :if=/usr/local/atalk/etc/filters/ifpap:\
    :of=/usr/local/atalk/etc/filters/ofpap:\
    :lp=/var/spool/dave/null
</verb>
<p>
There are three caveats to using netatalk this way.
<enum>
<item>
The first is that
<tt>psf</tt> (i.e., the filters) needs to run as root.  You can accomplish this
in one of two ways.  The first is to uncomment the following line in
src/Makefile and recompile:
<verb>
# ROOT_CFLAG=-DROOT_PERMS_TO_FILTER_SECURITY_LOOPHOLE
</verb>
<p>
The filter lines then need to look like the following:
<verb>
    :if=ROOT /usr/local/atalk/etc/filters/ifpap:\
    :of=ROOT /usr/local/atalk/etc/filters/ofpap:\
</verb>
<p>
The alternative is to make <tt>psf</tt> setuid root.  To minimize the risk,
you may want to make <tt>psf</tt> executable only by group <tt>daemon</tt>.
(I haven't tested the first option.  I'm currently using the second option.)
<p>
<item>
The second caveat is that each network printer needs a <tt>.paprc</tt> in its
spool directory.  For instance, <tt>/var/spool/dave/.paprc</tt> looks like this:
<verb>
Dave's 32 Character Printer Name:LaserWriter@Your AppleTalk Zone
</verb>
<p>
See psf(8) and pap(1) for more information.
<p>
<item>
The third caveat concerns the lp line in the printcap entry.  For a
single printer, this can be <tt>/dev/null</tt>.  If the host spools to more
than one AppleTalk printer, you shouldn't use <tt>/dev/null</tt> for lp.  You
should use <tt>mknod</tt> to create a null device for each printer.  See psf(8).
</enum>
<verb>
Chad Mynhier <mynhier@cs.utk.edu>
Lab Engineer, CS Department        
University of Tennessee, Knoxville                   
</verb>
<sect1>SAMBA Support
<p>
From the
<htmlurl url="http://samba.canberra.edu.au/pub/samba/" name="http://samba.canberra.edu.au/pub/samba/">
Web Site.
<p>
<sect2>What is SMB
<p>
This is a big question.
<p>
The very short answer is that it is the protocol by which a lot of
PC-related machines share files and printers and other information
such as lists of available files and printers. Operating systems that
support this natively include Windows NT, OS/2, and Linux and add on
packages that achieve the same thing are available for DOS, Windows,
VMS, Unix of all kinds, MVS, and more. There is no reason why Apple
Macs and indeed any Web browser should not be able to speak this
protocol, and current development (in which the Samba team is heavily
involved) is aimed at exactly that. Alternatives to SMB include
Netware, NFS, Appletalk, Banyan Vines, Decnet etc; many of these have
advantages but none are both public specifications and widely
implemented in desktop machines by default.
<p>
The Common Internet Filesystem is what the new SMB initiative is
called. For details watch
<htmlurl url="http://samba.anu.edu.au/cifs" name="http://samba.anu.edu.au/cifs">.
<p>
<bf>WHAT CAN SAMBA DO?</bf>
<p>
Here is a very short list of what samba includes, and what it does.
<itemize>
<item> a SMB server, to provide Windows NT and LAN Manager-style file and print
services to SMB clients such as Windows 95, Warp Server, smbfs and others.
<item> a Netbios (rfc1001/1002) nameserver, which among other things gives
browsing support. Samba can be the master browser on your LAN if you wish.
<item> a ftp-like SMB client so you can access PC resources (disks and
printers) from unix, Netware and other operating systems
<item> a tar extension to the client for backing up PCs
</itemize>
<p>
For a much better overview have a look at the web site at
<htmlurl url="http://samba.canberra.edu.au/pub/samba" name="http://samba.canberra.edu.au/pub/samba">
and browse the user survey.
<p>
Related packages include:
<itemize>
<item> smbfs, a linux-only filesystem allowing you to mount remote SMB
filesystems from PCs on your linux box. This is included as standard with
Linux 2.0 and later.
<item> tcpdump-smb, a extension to tcpdump to allow you to investigate SMB
networking problems over netbeui and tcp/ip.
<item> smblib, a library of smb functions which are designed to make it
easy to smb-ise any particular application. See
ftp://samba.anu.edu.au/pub/samba/smblib.
</itemize>
<p>
<bf>FTP SITE</bf>
<p>
The main anonymous ftp distribution site for this software is
samba.anu.edu.au in the directory pub/samba/.
<bf>WEB SITE</bf>
<p>
A Samba WWW site has been setup with lots of useful info. Connect to:
<p>
<htmlurl url="http://samba.canberra.edu.au/pub/samba/" name="http://samba.canberra.edu.au/pub/samba/">
<p>
As well as general information and documentation, this also has searchable
archives of the mailing list and a user survey that shows who else is using
this package. Have you registered with the survey yet? :-)
<p>
It is maintained by Paul Blackman (thanks Paul!). You can contact him
at ictinus@lake.canberra.edu.au.
<sect2>Samba and LPRng
<p>
The SAMBA code is very easy to configure.  See the SAMBA
documentation for details,  but you only need to modify
the samba.conf file and put in the pathnames of the LPRng
facilities.  The following is a sample.
<p>
<verb>
From: Sascha Ottolski <alzhimer@cs.tu-berlin.de>
To: lprng@iona.com
Subject: Re: [LPRng] lprng-3.2.6 and smb on Linux
webnut@conc.tds.net said:

 I have samba sending print from Win95 machines to LPRng.  The key to
 making it work is in the samba.conf file in the [global] section:

[global]
    printing = lprng
    print command = /usr/local/bin/lpr  -P%p %s -r
    lpq command   = /usr/local/bin/lpq  -P%p
    lprm command  = /usr/local/bin/lprm -P%p %j
    printcap name = /etc/printcap
    load printers = no 
[printers]
   comment = All Printers
   path = /tmp
   browseable = no
   printable = yes
   guest ok = no
   writable = no
   create mode = 0700

Note: the path= value specifies the spool directory where
the print files are temporarily stored.  This should NOT
be the LPRng spool directory,  but some other directory that
Samba has write permissions for.
</verb>
<verb>
Reply-To: "Pascal A. Dupuis" <dupuis@lei.ucl.ac.be>
To: papowell@astart2.astart.com
Subject: Re: LPRng-3.2.10
</verb>
<p>
I include the smbprint script used to send stdin to a NetBEUI printer.
<verb>
#!/bin/sh -x
# This script is an input filter for printcap printing on a unix machine. It
# uses the smbclient program to print the file to the specified smb-based 
# server and service.
# For example you could have a printcap entry like this
#
# smb:lp=/dev/null:sd=/usr/spool/smb:sh:if=/usr/local/samba/smbprint
#
# which would create a unix printer called "smb" that will print via this 
# script. You will need to create the spool directory /usr/spool/smb with
# appropriate permissions and ownerships for your system.
#
# The /usr/spool/smb/.config file should contain:
#   server=PC_SERVER
#   service=PR_SHARENAME
#   password="password"
#
# Set these to the server and service you wish to print to 
# In this example I have a WfWg PC called "lapland" that has a printer 
# exported called "printer" with no password.
#
# E.g.
#   server=PAULS_PC
#   service=CJET_371
#   password=""
# Should read the following variables set in the config file:
#   server, service, password
config_file=.config
eval `cat $config_file`
# echo "server $server, service $service" 2>&1
(
# NOTE You may wish to add the line `echo translate' if you want automatic
# CR/LF translation when printing.
#       echo translate
    echo "print -"
    cat
) | /usr/local/bin/smbclient "\\\\$server\\$service" \
   $password -U $server -N -P
# comment preceeding line and uncomment following 
# to just test for correct filter working
#) | cat > /dev/null
</verb>
<label id="printspec">
<sect1>Printer Specific notes
<p>
This is a small collection of miscellaneous notes about printers
and applications.
<sect1>HP Deskjet
<p>
<verb>
From: jarausch@igpm.rwth-aachen.de (Helmut Jarausch)
Subject: Re: Using gs (ghostscript) as a filter? 
To: lprng@iona.com
Cc: Rick Gaine <rgaine@nbcs.rutgers.edu>
Sender: majordomo-owner@iona.com
Reply-To: lprng@iona.com
>> 
>> Hello All:
>> 
>> I would like to use LPRng 3.1.4 with an HP LaserJet 4P.  I'd like to be
>> able to use gs to convert PostScript files so that I can print them on my
>> HP 4P.  Can I do this with LPRng?  If so, could someone semd me a printcap
>> entry?  I'd appreciate it.  I am not sure how I will be cconnecting the
>> printer yet, but I am thinking either serial or network.  Probably serial
>> though.  Thanks for any help.
</verb>
<p>
This printcap works for my Deskjet:
<verb>
djps
	:cm=Local Deskjet(GhostScript)
	:sd=/var/spool/djps:sf:sh:mx#0
	:lp=/dev/plp
	:if=/usr/LOCAL/bin/LPRng/ps_to_deskjet:
</verb>
<p>
and this is the script /usr/LOCAL/bin/LPRng/ps_to_deskjet
<verb>
#!/bin/sh
nice -19 /usr/LOCAL/bin/gs -sDEVICE=cdj550 -sPAPERSIZE=a4 -sOutputFile=- -q -r300 - 
</verb>
<verb>
Helmut Jarausch
Lehrstuhl f. Numerische Mathematik
Institute of Technology
RWTH Aachen
D 52056 Aachen, Germany
</verb>
<sect1>HP LaserJet IIISiMX
<p>
<verb>
> From majordomo-owner@iona.com Mon Aug 31 11:17:26 1998
> To: lprng@iona.ie
> Subject: [LPRng] problems printing PS-level2 jobs on LJIIIsi's...
> Date: Mon, 31 Aug 1998 15:06:22 -0400
> From: "John Saroglou" <johny@yorku.ca>
>
> Greetings...
>
> I'm wondering if someone got around the problem of printing
> Postscript(R) Level 2  jobs on Laser Jet IIIsi printers.
> Our printers are direct network printers talking to a
> print server running solaris 2.6 and lprng-3.5.1.
>
> Is there a fix (possible drivers?) for such problem?
>
> Thanks in advance.
</verb>
<p>
The LaserJet IIISi does not support PostScript level 2, only level 1
(really, it is called 3SiMX).
The Windows (you are under Windows, right?) HP driver for 3Si/3Si MX
PostScript should produce only PS level 1.
<p>
Beware: latest version of Adobe Windows PS driver produces *ONLY* PS
level 2.
<p>
So, if you have (or receive) level 2 files, read them using ghostscript
and print a screen dump :-) or as a bitmap. 
<p>
You can too convert them into PDF (using either Adobe distiller or
ghostscript ps2pdf) then use acroread to print the result. acroread can
produce either level 1 or level 2 PostScript. THE definite solution !
<p>
This trouble has nothing to do with the way they are connected or
driven, it is only a driver problem.
<p>
I believe that HP had once a PS level2 update, but the price was so high
that bying a new printer was a better solution!
<verb>
Bertrand
-- 
| Bertrand DECOUTY              | mailto:Bertrand.Decouty@irisa.fr   |
| IRISA - INRIA (Atelier)       | PHONE : 0299847346 / 0299847100    |
| Campus de Beaulieu            | FAX   : +33 (0) 299842534          |
| F-35042 Rennes Cedex - FRANCE | http://www.irisa.fr/               |
</verb>
<sect1>HP Deskjet 1600CM
<p>
The following printcap entry was posted by
Olaf Lotzkat (Sysadm), Faculty of Computer Science, TU Dresden, Germany
<tt>&gt;Olaf_Lotzkat@inf.tu-dresden.de&lt;</tt>.
<verb>
# HP DeskJet 1600CM
tinte|:
    :lp=dj1600cm%9100:
    :mx#0:rw:sf:
    :ps=status:af=acct:lf=log:sd=/lpspool/tinte:fx=flpv:
    :if=/usr/local/lib/filters/ifhp \
     -Tpagecount=off,forcepagecount=on,infostatus=off,sync=off:
    :of=/usr/local/lib/filters/ofhp \
     -Tpagecount=off,forcepagecount=on,infostatus=off,sync=off,banner=off:
    :vf=/usr/local/lib/filters/ifhp -c:
    :bp=/usr/local/lib/filters/psbanner:
</verb>
<sect1> HP JetDirect Interface
<p>
The  HPJetDirect  card  can  be configured through the front
panel  or through a set of network files.  Here is a summary
of  the  methods  used  from  UNIX  systems, or when you are
desperate, to configure the printer.
<sect2> Setting Up IP Networking and Address
<p>
You can set the network address from the front panel.
Reset  the printer,
put it in offline mode.
and then use the MENU, +-, SELECT keys as follows:
<verb>
 MENU  -> MIO MENU (use MENU to display MIO MENU)
 ITEM  -> CFG NETWORK=NO*
 +     -> CFG NETWORK=YES
 ENTER -> CFG NETWORK=YES*
 ITEM  -> TCP/IP=OFF* (use ITEM to display TCP/IP)
 +     -> TCP/IP=ON
 ENTER -> TCP/IP=ON*
 ITEM  -> CFG TCP/IP=NO* (use ITEM to display TCP/IP)
 +     -> CFG TCP/IP=YES
 ENTER -> CFG TCP/IP=YES*
 ITEM  -> BOOTP=NO*
	 (Enable BOOTP if you want to - see below)
 ITEM  -> IP BYTE 1=0*
	 This is IP address MSB byte.
	 Use +- keys to change value, and then ENTER to change
	 Use ITEM keys to get IP BYTE=2,3,4
 ITEM  -> SM BYTE 1=255*
	  This is the subnet mask value
	 Use +- keys to change value, and then ENTER to change
	 Use ITEM keys to get IP BYTE=2,3,4
 ITEM  -> LG BYTE 1=255*
	 This is the Syslog server (LoGger) IP address
	 Use +- keys to change value, and then ENTER to change
	 Use ITEM keys to get IP BYTE=2,3,4
 ITEM  -> GW BYTE 1=255*
	 This is the subnet gateway (router) IP address
	 Use +- keys to change value, and then ENTER to change
	 Use ITEM keys to get IP BYTE=2,3,4
 ITEM  -> TIMEOUT=90
	  This is the connection timeout value.  It puts a limit
	 on time between connections.  A value of 10 is reasonable.
</verb>

<sect2> BOOTP Information

<p>
If  you have a bootp server, you can put this information
in  the  bootptab  file.   To  use this, you must enable the
bootp  option  on  the printer.  The T144 option specifies a
file to be read from the bootp server.  This file is read by
using  the  TFTP  protocol, and you must have a TFTPD server
enabled.  Here is a sample bootptab entry.
<verb>
# Example /etc/bootptab: database for bootp server (/etc/bootpd).
# Blank lines and lines beginning with '#' are ignored.
#
# Legend:
#
#       first field -- hostname
#                       (may be full domain name)
#
#       hd -- home directory
#       bf -- bootfile
#       cs -- cookie servers
#       ds -- domain name servers
#       gw -- gateways
#       ha -- hardware address
#       ht -- hardware type
#       im -- impress servers
#       ip -- host IP address
#       lg -- log servers
#       lp -- LPR servers
#       ns -- IEN-116 name servers
#       rl -- resource location protocol servers
#       sm -- subnet mask
#       tc -- template host (points to similar host entry)
#       to -- time offset (seconds)
#       ts -- time servers
#
# Be careful about including backslashes where they're needed.  Weird (bad)
# things can happen when a backslash is omitted where one is intended.
#
peripheral1:
:hn:ht=ether:vm=rfc1048:
:ha=08000903212F:
:ip=190.40.101.22:
:sm=255.255.255.0:
:gw=190.40.101.1:
:lg=190.40.101.3:
:T144="hpnp/peripheral1.cfg":
</verb>
<p>
If  you  are  using the T144 option, you will need to create
the  configuration file.  The sample configuration file from
the HP Direct distribution is included below.
<verb>
#
# Example HP Network Peripheral Interface configuration file
#
# Comments begin with '#' and end at the end of the line.
# Blank lines are ignored.  Entries cannot span lines.

# Name is the peripheral (or node) name.  It is displayed on the peripheral's
# self-test page or configuration plot, and when sysName is obtained through
# SNMP.  This name can be provided in the BOOTP response or can be specified
# in the NPI configuration file to prevent the BOOTP response from overflowing
# the packet.  The domain portion of the name is not necessary because the
# peripheral does not perform Domain Name System (DNS) searches.  Name is
# limited to 64 characters.

name: picasso

# Location describes the physical location of the peripheral.  This is the
# value used by the interface for the MIB-II sysLocation object.  The default
# location is undefined.  Only printable ASCII characters are allowed.
# Maximum length is 64 characters.

location: 1st floor, south wall

# Contact is the name of the person who administers or services the peripheral
# and may include how to contact this person.  It is limited to 64 characters.
# This is the value used by the interface for the MIB-II sysContact object.
# The default contact is undefined.  Only printable ASCII characters are
# allowed.  Maximum length is 64 characters.

contact: Phil, ext 1234

# The host access list contains the list of hosts or networks of hosts
# that are allowed to connect to the peripheral.  The format is
# "allow: netnum [mask]", where netnum is a network number or a host IP
# address.  Mask is an address mask of bits to apply to the network number
# and connecting host's IP address to verify access to the peripheral.
# The mask usually matches the network or subnet mask, but this is not
# required.  If netnum is a host IP address, the mask 255.255.255.255 can
# be omitted.  Up to ten access list entries are permitted.

# to allow all of network 10 to access the peripheral:
allow: 10.0.0.0  255.0.0.0

# to allow a single host without specifying the mask:
allow: 15.1.2.3

# Idle timeout is the time (in seconds) after which an idle
# print data connection is closed.  A value of zero disables
# the timeout mechanism.  The default timeout is 90 seconds.

idle-timeout: 120

# A community name is a password that allows SNMP access to MIB values on
# the network peripheral.  Community names are not highly secure; they are
# not encrypted across the network.  The get community name determines which
# SNMP GetRequests are responded to.  By default, the network peripheral
# responds to all GetRequests.  The get community name is limited to 32
# characters.
#
# For hpnpstat and hpnpadmin, the community name can be stored in
# /usr/lib/hpnp/hpnpsnmp.

get-community-name: blue

# The set community name is similar to the get community name.  The set
# community name determines which SNMP SetRequests are responded to.  In
# addition, SetRequests are only honored if the sending host is on the
# host access list.  By default, the network peripheral does not respond
# to any SetRequests.  The set community name is limited to 32 characters.
#
# The set community name can come from /usr/lib/hpnp/hpnpsnmp
# if it is the same as the get community name.  We recommend that the
# set community name be different from the get community name though.

set-community-name: yellow

# SNMP traps are asynchronous notifications of some event that has occurred.
# SNMP traps are useful only with network management software.  Traps are
# sent to specific hosts and include a trap community name.  Up to four
# hosts can be sent SNMP traps.   The trap community name is limited to
# 32 characters.  The default name is public.

trap-community-name: red

# The SNMP trap destination list specifies systems to which SNMP
# traps are sent.  Up to four IP addresses are allowed.  If no
# trap destinations are listed, traps are not sent.

trap-dest: 15.1.2.3
trap-dest: 15.2.3.4

# The SNMP authentication trap parameter enables or disables the sending
# of SNMP authentication traps.  Authentication traps indicate that an SNMP
# request was received and the community name check failed.  By default,
# the parameter is off.

authentication-trap: on

# The syslog-facility parameter sets the source facility identifier that the
# card uses when issuing syslog messages.  Other facilities, for example,
# include the kernel (LOG_KERN), the mail system (LOG_MAIL), and the spooling
# system (LOG_LPR).  The card only allows its syslog facility to be configured
# to one of the local user values (LOG_LOCAL0 through LOG_LOCAL7).  The
# selectable option strings, local0 through local7 (configured to LOG_LOCAL0
# through LOG_LOCAL7, respectively) are case insensitive.  The default
# syslog-facility for the card is LOG_LPR.

syslog-facility: local2

# This parameter allows the card to treat hosts on other subnets as if the
# hosts were on the card's subnet.  This parameter determines the TCP
# Maximum Segment Size (MSS) advertised by the card to hosts on other subnets
# and affects the card's initial receive-window size.  The card will use a
# TCP MSS of 1460 bytes for local hosts, and 536 bytes for a non-local host.
# The default is off, that is, the card will use the maximum packet sizes
# only on the card's configured subnet.
#
# The configuration utility does not allow access to this parameter.  If you
# want to configure it, you must manually edit the NPI configuration file
# and add it to the bottom of the entry for the network peripheral.

subnets-local: on

# This parameter affects how the card handles TCP connection requests from
# the host.  By default, the JetDirect MPS card will accept a TCP connection
# even if the peripheral is off-line.  If this parameter is set to "on", then
# the card will only accept a TCP connection when the peripheral is on-line.

old-idle-mode: off
</verb>

<sect2> Paper Tray Selection
<p>
Be careful with your paper tray selection.  You should configure the
printer,  using the front panel switches,  to select the FIRST paper
tray.  See your printer documentation on this.  Unfortunately,  different
models of HP printers have different methods of handling paper trays.
<sect1>Lexmark Printers
<p>
Some Lexmark printers do not send
<em>end of job</em> status back unless configured
to do so.
Here is what is needed to force this.
<verb>
Date: Wed, 21 Jan 1998 18:25:50 -0600 (CST)
From: Matt White <whitem@bofh.usask.ca>
To: lprng@iona.com
Subject: Re: [LPRng] CTI-ifhp with Lexmark Optra N printer

On Wed, 21 Jan 1998, Simon Greaves wrote:

> Apologies in advance if this is way off mark, but we've been evaluating a
> commercial print charging package (Geomica) which works by talking to the
> printer in what I think is a similar way to the ifhp filters. Lexmarks are
> currently a big headache because they seem to fail to return the message
> that they have finished printing which screws things up somewhat. In our
> case, it is believed to be a problem with the Lexmark firmware which they
> are looking into. 

There is a fix for that...it is originally from the Lexmark 4039 series,
but it still works on the Optra S 1650 machines that we have (and should
work on the rest of the optra line).  Just send this little chunk of
postscript to the printer once:

-----------snip----------
%! Postscript utility file to set the 4039 printer into synchronous mode
serverdict begin 0 exitserver
statusdict begin true setenginesync end
-----------snip----------

Basically, it causes the printer to wait until it is finished printing
before actually reporting that it is done.  I've got 3 Optra S printers
running with ifhp right now with no extra options (just defaults).
 
---------------------------------------------------------------------
- Matt White                         whitem@arts.usask.ca           -
- Network Technical Support          http://arts.usask.ca/~whitem   -
- College of Arts & Science          University of Saskatchewan     -
---------------------------------------------------------------------
</verb>

<sect1>Tektronix P450 and Family
<p>
The Tektronix P450 has a very odd network interface.
You can open a TCP (stream) connection to port 9100 and send a file to be
printed on the connection.
<p>
When a UDP datagram is sent to UDP Port 9101,
the printer resonds with status information.
This apparently is the poor man's SNMP,
but I digress.
Here is a clever implementation of a filter that handles this printer.
<verb>
From: Russ Thacher &lt;thacher@brl.uiuc.edu>
To: lprng@iona.com
Subject: Re: [LPRng] Tektronix P450 & psfilter

Having only limited success with the psfilter UDP status port option, and
not satisfied with the overall slowness of sending print jobs out via
AppleShare with CAP, I (we) decided to roll our own filter for the Phaser
450 that speaks AppSocket (sending on TCP 9100, monitoring UDP 9101),
grabs reliable page counts and can tell the Phaser to switch to
transparency mode based upon LPRng queue alias ('qq' printcap option).

Here's out printcap entry for the Phaser 450, using our filter:

# Tektronix Phaser 450-2
phaser450-2|phaser450-2t|phaser440|phaser440t
   :lp=/dev/null:qq
   :af=acct:lf=log:fx=flpv:sh:mx#0:ps=status
   :if=/usr/local/lib/filters/phaserif
   :sd=/var/spool/lpd/phaser450-2

Attached is the Perl filter I wrote that has been very slightly modified
since its inception by Al Marquardt.  It's written with Solaris in mind
and is perhaps a little crude, but it works quite well for us.  Feel free
to modify/use it in any way you like- direct any and all comments to Al
Marquardt (almar@uiuc.edu).

--
Russ Thacher
Systems Administrator, UIUC Bioacoustics Research Lab

-------------- Filter -----------------
#!/usr/local/bin/perl5

use Getopt::Std;
use Socket;
use Sys::Hostname;

pop @ARGV;

# Get all the filter options LPRng knows

getopts('a:b:cd:e:f:h:i:j:k:l:m:n:p:r:s:t:w:x:y:F:P:S:C:H:A:J:L:Q:');

# set default exit status (JFAIL)

$! = 1;

# Set default error messages

$udpsockerr = "ERROR: Cannot establish UDP socket: $!\n";
$udpbinderr = "ERROR: Cannot bind to UDP socket: $!\n";
$udpsenderr = "ERROR: Cannot send UDP status request: !$\n";
$udprecverr = "ERROR: Cannot receive UDP status report: !$\n";
$tcpsockerr = "ERROR: Cannot establish TCP socket: $!\n";
$tcpconnecterr = "ERROR: Cannot connect to TCP socket: $!\n";
$tcpcloseerr = "ERROR: Cannot close TCP socket: $!\n";

# Get current time/date

@MONTHS = ( "Jan", "Feb", "Mar", "Apr", "May", "Jun",
           "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" );
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
$month = $MONTHS[$mon];
if (length($sec)  == 1)  { $sec = "0$sec" }
if (length($min)  == 1)  { $min = "0$min" }
if (length($hour) == 1)  { $hour= "0$hour" }
if (length($mday) == 1)  { $mday= " $mday" }

$datestamp = "$month $mday $hour:$min:$sec";

# Write a 'job begin' line to printer log file

print STDERR "START: job number $opt_j (dfile: $opt_e) for $opt_n\@$opt_h on $opt_P at $datestamp\n";

# Setup network info for printers

$phaser4501 = 'phaser450-1';
$phaser4502 = 'phaser440';

$udpport = '9101';
$tcpport = '9100';

# Setting up UDP socket info so we can get status reports from phasers...

$myip = gethostbyname(hostname());

$udpproto = getprotobyname('udp');
$myudppaddr = sockaddr_in(0, $myip);

if ($opt_P eq 'phaser450-1') {
  $printip = inet_aton($phaser4501);
}
elsif ($opt_P eq 'phaser450-2') {
  $printip = inet_aton($phaser4502);
}

$printudppaddr = sockaddr_in($udpport, $printip);     

socket(UDPSOCK, PF_INET, SOCK_DGRAM, $udpproto) or die $udpsockerr;
bind(UDPSOCK, $myudppaddr) or die $udpbinderr;

# Setting up TCP socket info so we can send jobs to the printer
# and read pagecounts

$tcpproto = getprotobyname('tcp');
$mytcppaddr = sockaddr_in(0, $myip);
$printtcppaddr = sockaddr_in($tcpport, $printip);
socket(TCPSOCK, PF_INET, SOCK_STREAM, $tcpproto) or die $tcpsockerr;
setsockopt(TCPSOCK, SOL_SOCKET, SO_KEEPALIVE, 0);
setsockopt(TCPSOCK, SOL_SOCKET, SO_LINGER, 0);     

# Before any printing check to be sure printer is idle
# If it's not, check every 5 seconds until it is 

defined(send(UDPSOCK, "\r\n", 0, $printudppaddr)) or die $udpsenderr;
$udpout = "";
($printudppaddr = recv(UDPSOCK, $udpout, 100, 0)) or die $udprecverr;
$realudpout = unpack("a*", $udpout);

while ($realudpout ne 'status: idle') {
  print STDERR "$opt_P not at idle status.  Cannot start print job.\n";
  defined(send(UDPSOCK, "\r\n", 0, $printudppaddr)) or die $udpsenderr;
  $udpout = "";
  ($printudppaddr = recv(UDPSOCK, $udpout, 100, 0)) or die $udprecverr;
  $realudpout = unpack("a*", $udpout);
  sleep 5;
}

# Get the initial page count

connect(TCPSOCK, $printtcppaddr) or die $tcpconnecterr;
select TCPSOCK;
$| = 1;
select STDOUT;
print TCPSOCK "%!\n";
print TCPSOCK "(%%\[ pagecount: )print statusdict /pagecount get exec ";
print TCPSOCK "(                )cvs print ";
print TCPSOCK "( \]%%) = flush\n";
$tcpout = &lt;TCPSOCK>;
if ($tcpout =~ /%%\[ pagecount:.*/) {
  @tcparray = split /\s/, $tcpout;
  $pagecount1 = $tcparray[2];
}

# Get current time/date (again, this time for the accounting file)

($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
$month = $MONTHS[$mon];
if (length($sec)  == 1)  { $sec = "0$sec" }
if (length($min)  == 1)  { $min = "0$min" }
if (length($hour) == 1)  { $hour= "0$hour" }
if (length($mday) == 1)  { $mday= " $mday" }

$datestamp = "$month $mday $hour:$min:$sec";

# open LPRng accounting file

if( defined( $opt_a ) && $opt_a && open ACCT, ">>$opt_a" ){
print ACCT "DEBUG: printer return string= $tcpout";
print ACCT "start  -p'$pagecount1' -q'$opt_j' -J'$opt_J' -k'$opt_k' -n'$opt_n' -h'$opt_h' -P'$opt_P' -F'$opt_F' -t'$datestamp'\n";  
close ACCT;
}

# Start shoving data out to printer

# Set print/transparency by queue name

if ($opt_Q =~ /.*t/) {
  print TCPSOCK "%!\n";
  print TCPSOCK "mark\n";
  print TCPSOCK "{\n";
  print TCPSOCK " 3 dict begin\n";
  print TCPSOCK " /MediaType null def\n";
  print TCPSOCK " /MediaColor (Transparent) def\n";
  print TCPSOCK " currentdict end setpagedevice\n";
  print TCPSOCK "} stopped cleartomark\n";
} 

# Shove the rest of the data file out to the printer

while ($line = &lt;STDIN>) {
  print TCPSOCK $line;
}

close TCPSOCK or die $tcpcloseerr;

# Listen for 'status:idle' from printer- this signifies that job is done and
# we can ask printer for page count
# we wait until 'status:idle' is received- retrying every 3 seconds

$realudpout = "";

while ($realudpout ne 'status: idle') {
  defined(send(UDPSOCK, "\r\n", 0, $printudppaddr)) or die $udpsenderr;
  $udpout = "";
  ($printudppaddr = recv(UDPSOCK, $udpout, 100, 0)) or die $udprecverr;
  $realudpout = unpack("a*", $udpout);
  sleep 2;
}

# Now we're ready to grab the final page count from the printer

socket(TCPSOCK, PF_INET, SOCK_STREAM, $tcpproto) or die $tcpsockerr;
connect(TCPSOCK, $printtcppaddr) or die $tcpconnecterr;

print TCPSOCK "%!\n";
print TCPSOCK "(%%\[ pagecount: )print statusdict /pagecount get exec ";
print TCPSOCK "(                )cvs print ";
print TCPSOCK "( \]%%) = flush\n";
$tcpout = &lt;TCPSOCK>;
if ($tcpout =~ /%%\[ pagecount:.*/) {
  @tcparray = split /\s/, $tcpout;
  $pagecount2 = $tcparray[2];
}

close TCPSOCK or die $tcpcloseerr;

# Get date/time again

($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
$month = $MONTHS[$mon];
if (length($sec)  == 1)  { $sec = "0$sec" }
if (length($min)  == 1)  { $min = "0$min" }
if (length($hour) == 1)  { $hour= "0$hour" }
if (length($mday) == 1)  { $mday= " $mday" }

$datestamp = "$month $mday $hour:$min:$sec";

# Update accounting file upon close

$pages = $pagecount2 - $pagecount1; 

if( defined( $opt_a ) && $opt_a && open ACCT, ">>$opt_a" ){
print ACCT "DEBUG: printer return string= $tcpout";
print ACCT "end  -b'$pages' -p'$pagecount2' -q'$opt_j' -J'$opt_J' -k'$opt_k' -n'$opt_n' -h'$opt_h' -P'$opt_P' -F'o' -t'$datestamp'\n";
close ACCT;
}

# Write a 'Job End' line to the printer log

print STDERR "END: job number $opt_j (dfile: $opt_e) for $opt_n\@$opt_h on $opt_P at $datestamp\n\n";
</verb>

<sect1>Duplex Printing
<p>
Duplex printing is when you print on both sides of a page.
Some printers which do duplex printing require that you
send them special commands to force this mode.  This is
usually done by the FILTERS.  The CTI-ifhp (for HP PJL printers)
and psfilter (for PostScript Printers) make a stab at sending
the PJL or PostScript commands to the printer.  Many people have
reported problems doing duplex printing,  so  here is a check
list.
<enum>
<item>
Make sure you have enough memory for the worst case
print job.  Usually the printer has to rasterize both
pages before it can produce an impression.  It may require
much more memory than you expect.
<item>
Check your printer manual to discover the EXACT form of the
<tt>enter duplex mode</tt> command and make sure that either the command
is part of the job (PJL language at the start of the job,
postscript header, etc), or that the filter generates the
correct form.
<p>
Note there is a PostScript Printer Description file (PPD) for
most printers that support PostScript,  and they even have the
PJL and PostScript code for this in the PPD file.
<item>
It has been observed that even with what would apparently be
sufficient memory,  that many duplex jobs print 'oddly',
that they are not aligned on the same side in the same way,
etc etc.  This may not be the fault of the software,  but of the
support for duplex operation.
<item>
Get the source code for psfilter or CTI-ifhp filter,  and modify the
appropriate lines to send the appropriate "turn on duplex" strings
to the printer.
</enum>
<p>
I know this is painful,  but until there is a uniform way to get the
correct commands extracted from either PPD or some other database then
this appears to be the only way to do it.
<quote>
<em>
Patrick Powell
</em>
</quote>
<sect1>TESTVERSION  and Portability Testing
<p>
The LPRng code has the ability to run as non-setuid software,
and to use the non-default TCP/IP ports for communication.
This facility allows a <em>TESTVERSION</em> to be run in parallel with the
normal LPRng software.
<p>
To simplify testing and portability issues,
a simple test version of the spool queues and jobs has been supplied with the
LPRng distribution.
These queues can be placed in a suitable location
(<tt>/tmp</tt> is common) and the LPRng software tested.
<p>
The test version of the software will use the <tt>LPD_CONF</tt>
environment variable to specify the location of the configuration file.
It will read this configuration file on startup and use the values
to override the normal defaults.
Since a user could maliciously set up their own configuration files
with values that could compromise system security,
it is strongly recommended that the test version is not made SETUID root.
In fact,
the LPRng code will chatter messages when the LPD_CONF ability is enabled
and it is run as root.
<sect2>Compiling the TESTVERSION
<p>
The TESTVERSION of the software can be compiled in two ways:
<enum>
<item>Remove the <tt>vars.o</tt> file,
and then explicitly request the generation of the test version:
<verb>
cd src
rm ./vars.o 
make TESTVERSION=yes
</verb>
<item>Edit <tt>src/Makefile</tt>, and uncomment the indicated line.
Then run <tt>make</tt> to regenerate the distribution.
This is the preferred method of generating the software.
<verb>
#### ****** TESTING AND SECURITY LOOPHOLE ******************************
# Define TESTVERSION and GETENV to allow the LPD_CONFIG environment
#  variable to be used as the name of a configuration file.  In non-testing
#  systems,  this is a security loophole.
# TESTVERSION=yes
</verb>
</enum>
<sect2>Setting up the TESTVERSION Spool Queues
<p>
The LPRng <tt>TESTSUPPORT</tt> directory contains a set of shell scripts
and files that need to be installed in the appropriate directory.
The following steps are used.
<enum>
<item>
First,
you need to set up your <tt>HOST</tt> environment variable to the fully
qualified domain name of your host
and your <tt>USER</tt> environment variable to your user name.
This is done in order to get values to put into the TESTVERSION configuration files.
<item>
In the <tt>TESTSUPPORT</tt> directory,
edit the <tt>Makefile</tt>,
and specify the location of the <tt>TESTVERSION</tt> spool queues.
The default location is <tt>/tmp</tt>;
since on most systems these files are deleted or are available to everybody,
a more secure location should most likely be used.
<bf>DO NOT USE THE RAW TESTFILE DIRECTORY</bf>.
These files need to be copied and placed in another directory.
<item>
The <tt>LPD_CONF</tt> environment variable should be set to the
location of the installed lpd.conf file.
<item>
In the <tt>TESTSUPPORT</tt> directory,
run <tt>make</tt>.
This will copy and install the necessary files.
</enum>
<p>
Example:
<verb>
  CSH:
    setenv HOST {fully qualified domain name};
    setenv USER `whoami`
    setenv LPD_CONF /tmp/LPD/lpd.conf
    set path=( /tmp/LPD $path )
    unsetenv PRINTER
   Example:
      setenv HOST astart1.astart.com
      setenv USER papowell
      setenv LPD_CONF /tmp/LPD/lpd.conf
      set path=( /tmp/LPD $path )
      unsetenv PRINTER
  Bourne Shell:
    HOST={fully qualified domain name}; export HOST;
    USER='whoami'; export USER
    LPD_CONF=/tmp/LPD/lpd.conf.$HOST; export LPD_CONF
    PATH=/tmp/LPD:$PATH; export PATH
    PRINTER=; export PRINTER
   Example: 
      HOST=astart1.astart.com; export HOST
      USER=papowell; export USER
      LPD_CONF=/tmp/LPD/lpd.conf.$HOST; export LPD_CONF
      PATH=/tmp/LPD:$PATH; export PATH
      PRINTER=; export PRINTER
  cd TESTSUPPORT
  make
</verb>
<sect2>Portability Testing
<p>
You should ignore the information in this section UNLESS you are
trying to do a port to a new or whacko version of a UNIX system.
LPRng has been tested on just about every version of UNIX that supports
POSIX capabilities,
and if it is having problems then most likely it is due to non-portability
issues.
However,
if you feel that your system is not POSIX compatible,
or you are having serious problems due to LPRng's use of the system facilities,
feel free to try the following tests.
<p>
Needless to say,
if you identify problems,  please inform the developers and they will
most likely assist you in resolving them.
<p>
Set your current directory to the location of the compiled <tt>TESTVERSION</tt>
executables.
Execute the various executables using <tt>./cmd</tt>,
or set <tt>.</tt> <bf> as the first entry in the PATH </bf>.
If it is not the first entry,
then the standard system executables will be used.
<enum>
<item> Run <tt>./checkpc -T /tmp/a</tt>.
This will perform a limited set of tests of the LPRng functionality.
Note that some of them will fail as checkpc is not running SUID ROOT
and the <tt>/tmp/a</tt> is not a serial device.
ALL of the non-SUID related messages should indicate success.
<item> Set <tt>checkpc</tt> to setuid ROOT, and then rerun the tests.
<verb>
chown root checkpc
chmod u+s checkpc
./checkpc -T /tmp/a
</verb>
<p>
The SETUID tests should now succeed.
If they do not,
then you have a VERY odd UNIX system,
and you are on your own on this one.
See the comments in <tt>src/common/setuid.c</tt> for help.
<item>
Now run tests for serial line control and locking:
<verb>
./checkpc -T /dev/ttya   # or an appropriate UNUSED tty device
</verb>
<p>
You most likely will have to attach a terminal or modem to the serial device
in order to cause the <tt>open()</tt> to succeed,
as most serial device drivers block when <tt>DSR</tt> is not enabled.
Check the messages concerning the <tt>stty</tt> actions.
Make sure that the appropriate changes have taken place.
<p>
You may get errors about <em>device lock</em> failing.
This is due to whacko differences in the ways that different UNIX systems
(or versions of the same UNIX system) implement serial device locking.
My advice is to ignore this problem unless you INSIST on having multiple users
of the same serial printing port,
in which case you are asking for serious trouble,
and you are on your own.
I am not interested in patches or queries on problems on serial device locking
problems.
In fact,
this facility is only used if the <tt>lk</tt> printcap flag is TRUE
(default FALSE).
</enum>
<sect2>Running the TESTVERSION Software
<p>
Set your current directory to the location of the compiled <tt>TESTVERSION</tt>
executables.
Execute the various executables using <tt>./cmd</tt>,
or set <tt>.</tt> <bf> as the first entry in the PATH </bf>.
If it is not the first entry,
then the standard system executables will be used.
<enum>
<item> Run <tt>./checkpc</tt>.
this will print out the various values for the spool queues in the <tt>TESTVERSION</tt>
setup.
If the <tt>t1</tt>, <tt>t2</tt>,... spool queues are not displayed,
make sure that the LPD_CONF environment variable is set correctly and that you
are using the <tt>TESTVERSION</tt> executable.
<item>Run <tt>./checkpc -f</tt>.
This will fix up the (deliberately introduced) problems in the spool queues.
<item>
Next,  run <tt>./lpd -F</tt> in one window,
and then run <tt>./lpq -a </tt> in another window.
This will check that the server is working.
<item>
You can now amuse yourself by sending jobs,
setting up permissions checking,
and other chores.
<item>
When everything appears to be working correctly,
you can then remove the <tt>TESTVERSION</tt> flag from the
<tt>src/Makefile</tt>, recompile,
and install the LPRng software.
</enum>
<label id="printcapref">
<sect>/etc/printcap Print Spool Database File
<p>
The heart of the LPRng system is the file <tt>/etc/printcap</tt>. 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.
<p>
For details about individual printcap
items, see the <tt>printcap(5)</tt>
man page from the LPRng distribution,
or entries in the
<ref id="index" name="index"> section.
<label id="index">
<label id="options">
<sect1>Index To All The Options
<p>
<table>
<tabular ca="|l|l|">
Option
|
Meaning
@
<tt><ref id="ab" name="ab"></tt>
|
always print banner, ignore lpr -h option
@
<tt><ref id="achk" name="achk"></tt>
|
query accounting server when connected
@
<tt><ref id="ae" name="ae"></tt>
|
accounting at end (see also af, la, ar, as)
@
<tt><ref id="af" name="af"></tt>
|
name of accounting file (see also la, ar)
@
<tt><ref id="ah" name="ah"></tt>
|
automatically hold all jobs
@
<tt><ref id="allowduplicateargs" name="allow&lowbar;duplicate&lowbar;args"></tt>
|
Allow duplicate command line arguments (legacy requirement)
@
<tt><ref id="allowgetenv" name="allow&lowbar;getenv"></tt>
|
Allow use of LPD&lowbar;CONF
@
<tt><ref id="allowuserlogging" name="allow&lowbar;user&lowbar;logging"></tt>
|
allow users to request logging info using lpr -mhost%port
@
<tt><ref id="ar" name="ar"></tt>
|
enable remote transfer accounting (if af is set)
@
<tt><ref id="as" name="as"></tt>
|
accounting at start (see also af, la, ar)
@
<tt><ref id="be" name="be"></tt>
|
Banner at End Generation Program
@
<tt><ref id="bk" name="bk"></tt>
|
Berkeley LPD job file format
@
<tt><ref id="bkfilteroptions" name="bk&lowbar;filter&lowbar;options"></tt>
|
Berkeley LPD filter options
@
<tt><ref id="bkoffilteroptions" name="bk&lowbar;of&lowbar;filter&lowbar;options"></tt>
|
Berkeley LPD OF filter options
@
<tt><ref id="bkf" name="bkf"></tt>
|
backwards-compatible filters: use simple paramters
@
<tt><ref id="bl" name="bl"></tt>
|
short banner line sent to banner printer
@
<tt><ref id="bp" name="bp"></tt>
|
Banner Generation Program (see bs, be)
@
<tt><ref id="bq" name="bq"></tt>
|
Use filters on bounce queue jobs
@
<tt><ref id="br" name="br"></tt>
|
Serial port bit rate (see ty)
@
<tt><ref id="breakclassnameprioritylink" name="break&lowbar;classname&lowbar;priority&lowbar;link"></tt>
|
Don't default priority from classname (legacy requirement)
@
<tt><ref id="bs" name="bs"></tt>
|
Banner at Start Generation Program
@
<tt><ref id="cd" name="cd"></tt>
|
control directory
@
<tt><ref id="checkfornonprintable" name="check&lowbar;for&lowbar;nonprintable"></tt>
|
LPR checks for nonprintable file
@
<tt><ref id="checkidle" name="check&lowbar;idle"></tt>
|
program used to check for idle printer
@
<tt><ref id="classinstatus" name="class&lowbar;in&lowbar;status"></tt>
|
Show job class name in <tt>lpq</tt> status information
@
<tt><ref id="classnamelength" name="classname&lowbar;length"></tt>
|
Maximum length of classname argument (legacy requirement)
@
<tt><ref id="cm" name="cm"></tt>
|
comment identifying printer (LPQ)
@
<tt><ref id="co" name="co"></tt>
|
cost in dollars per thousand pages
@
<tt><ref id="configfile" name="config&lowbar;file"></tt>
|
configuration file
@
<tt><ref id="connectgrace" name="connect&lowbar;grace"></tt>
|
connection control for remote printers
@
<tt><ref id="connectinterval" name="connect&lowbar;interval"></tt>
|
connection control for remote printers
@
<tt><ref id="connecttimeout" name="connect&lowbar;timeout"></tt>
|
connection control for remote printers
@
<tt><ref id="connecttry" name="connect&lowbar;try"></tt>
|
connection control for remote printers
@
<tt><ref id="controlfilterbk" name="control&lowbar;filter"></tt>
|
control file filter
@
<tt><ref id="db" name="db"></tt>
|
debug options for queue
@
<tt><ref id="defaultauth" name="default&lowbar;auth"></tt>
|
default authentication
@
<tt><ref id="defaultformat" name="default&lowbar;format"></tt>
|
default job format
@
<tt><ref id="defaultloggerport" name="default&lowbar;logger&lowbar;port"></tt>
|
default port for logging info
@
<tt><ref id="defaultloggerprotocol" name="default&lowbar;logger&lowbar;protocol"></tt>
|
default protocol for logging info
@
<tt><ref id="defaultpermission" name="default&lowbar;permission"></tt>
|
default permission for files
@
<tt><ref id="defaultprinter" name="default&lowbar;printer"></tt>
|
default printer
@
<tt><ref id="defaultpriority" name="default&lowbar;priority"></tt>
|
default job priority
@
<tt><ref id="defaultremotehost" name="default&lowbar;remote&lowbar;host"></tt>
|
default remote host
@
<tt><ref id="defaulttmpdir" name="default&lowbar;tmp&lowbar;dir"></tt>
|
default directory for temp files
@
<tt><ref id="destinations" name="destinations"></tt>
|
printers that a route filter may return and we should query
@
<tt><ref id="directread" name="direct&lowbar;read"></tt>
|
filter input is direct from data file
@
<tt><ref id="fc" name="fc"></tt>
|
OBSOLETE, see sy
@
<tt><ref id="fd" name="fd"></tt>
|
forwarded jobs not accepted
@
<tt><ref id="ff" name="ff"></tt>
|
string to send for a form feed
@
<tt><ref id="filterldpath" name="filter&lowbar;ld&lowbar;path"></tt>
|
filter LD&lowbar;LIBRARY&lowbar;PATH value
@
<tt><ref id="filteroptions" name="filter&lowbar;options"></tt>
|
filter options
@
<tt><ref id="filterpath" name="filter&lowbar;path"></tt>
|
filter PATH environment variable
@
<tt><ref id="fixbadjob" name="fix&lowbar;bad&lowbar;job"></tt>
|
fix bad job files
@
<tt><ref id="fo" name="fo"></tt>
|
send form feed when device is opened
@
<tt><ref id="forcefqdnhostname" name="force&lowbar;fqdn&lowbar;hostname"></tt>
|
force FQDN HOST value in control file
@
<tt><ref id="forcelocalhost" name="force&lowbar;localhost"></tt>
|
force clients to send all requests to localhost
@
<tt><ref id="forcequeuename" name="force&lowbar;queuename"></tt>
|
force use of this queuename if none provided
@
<tt><ref id="forwardauth" name="forward&lowbar;auth"></tt>
|
do server to server authentication if authenticated by user
@
<tt><ref id="fq" name="fq"></tt>
|
send form feed when device is closed
@
<tt><ref id="fs" name="fs"></tt>
|
OBSOLETE (see sy)
@
<tt><ref id="fulltime" name="full&lowbar;time"></tt>
|
use extended time format
@
<tt><ref id="fx" name="fx"></tt>
|
valid output filter formats
@
<tt><ref id="generatebanner" name="generate&lowbar;banner"></tt>
|
force a banner to be generated
@
<tt><ref id="group" name="group"></tt>
|
Effective Group ID (EGID) for SUID ROOT programs
@
<tt><ref id="hl" name="hl"></tt>
|
Header (banner) last, at end of job
@
|
Ignore control file and data file name format errors
@
<tt><ref id="ignorerequesteduserpriority" name="ignore&lowbar;requested&lowbar;user&lowbar;priority"></tt>
|
Ignore requested user priority
@
<tt><ref id="if" name="if"></tt>
|
default (f, l) filter program
@
<tt><ref id="ipv6" name="ipv6"></tt>
|
using IPV6 conventions
@
<tt><ref id="kerberoskeytab" name="kerberos&lowbar;keytab"></tt>
|
kerberos keytab file location
@
<tt><ref id="kerberoslife" name="kerberos&lowbar;life"></tt>
|
kerberos key lifetime
@
<tt><ref id="kerberosrenew" name="kerberos&lowbar;renew"></tt>
|
kerberos key renewal time
@
<tt><ref id="kerberosserverprinciple" name="kerberos&lowbar;server&lowbar;principle"></tt>
|
kerberos remote server principle name
@
<tt><ref id="kerberosservice" name="kerberos&lowbar;service"></tt>
|
kerberos default service
@
<tt><ref id="la" name="la"></tt>
|
enable local printer accounting (if af is set)
@
<tt><ref id="ld" name="ld"></tt>
|
leader string sent on printer open
@
<tt><ref id="lf" name="lf"></tt>
|
error log file for spool queue
@
<tt><ref id="lk" name="lk"></tt>
|
lock the IO device
@
<tt><ref id="localhost" name="localhost"></tt>
|
localhost name for DNS queries
@
<tt><ref id="lockfile" name="lockfile"></tt>
|
lpd lock file
@
<tt><ref id="logfile" name="logfile"></tt>
|
lpd log file
@
<tt><ref id="loggerdestination" name="logger&lowbar;destination"></tt>
|
additional destination for logging information
@
<tt><ref id="longnumber" name="longnumber"></tt>
|
use long job number when a job is submitted
@
<tt><ref id="lp" name="lp"></tt>
|
printer device name or specification
@
<tt><ref id="lpdforcepoll" name="lpd&lowbar;force&lowbar;poll"></tt>
|
force lpd to poll idle printers
@
<tt><ref id="lpdpolltime" name="lpd&lowbar;poll&lowbar;time"></tt>
|
interval between lpd printer polls
@
<tt><ref id="lpdport" name="lpd&lowbar;port"></tt>
|
lpd listening port
@
<tt><ref id="lpdprintcappath" name="lpd&lowbar;printcap&lowbar;path"></tt>
|
lpd printcap path
@
<tt><ref id="lprbounce" name="lpr&lowbar;bounce"></tt>
|
lpr does filtering as in bounce queue
@
</tabular>
</table>
<table>
<tabular ca="|l|l|">
<tt><ref id="mailfrom" name="mail&lowbar;from"></tt>
|
mail user from user name
@
<tt><ref id="mailoperatoronerror" name="mail&lowbar;operator&lowbar;on&lowbar;error"></tt>
|
mail to this operator on error
@
<tt><ref id="maxconnectinterval" name="max&lowbar;connect&lowbar;interval"></tt>
|
maximum time between connection attempts
@
<tt><ref id="maxlogfilesize" name="max&lowbar;log&lowbar;file&lowbar;size"></tt>
|
maximum size (in K) of spool queue log file
@
<tt><ref id="maxserversactive" name="max&lowbar;servers&lowbar;active"></tt>
|
maximum number of lpd queue servers that can be active
@
<tt><ref id="maxstatusline" name="max&lowbar;status&lowbar;line"></tt>
|
maximum length of status line
@
<tt><ref id="maxstatussize" name="max&lowbar;status&lowbar;size"></tt>
|
maximum size (in K) of status file
@
<tt><ref id="mc" name="mc"></tt>
|
maximum copies allowed
@
<tt><ref id="mi" name="mi"></tt>
|
minimum space (Kb) to be left in spool filesystem
@
<tt><ref id="minlogfilesize" name="min&lowbar;log&lowbar;file&lowbar;size"></tt>
|
minimum size (in K) of spool queue log file
@
<tt><ref id="minstatussize" name="min&lowbar;status&lowbar;size"></tt>
|
minimum size to reduce status file to
@
<tt><ref id="minfree" name="minfree"></tt>
|
minimum amount of free space needed
@
<tt><ref id="ml" name="ml"></tt>
|
minimum number of printable characters for printable check
@
<tt><ref id="ms" name="ms"></tt>
|
stty options for serial lines (ms is an alias for sy)
@
<tt><ref id="mstimeresolution" name="ms&lowbar;time&lowbar;resolution"></tt>
|
millisecond time resolution
@
<tt><ref id="mx" name="mx"></tt>
|
maximum job size (1Kb blocks, 0 = unlimited)
@
<tt><ref id="nb" name="nb"></tt>
|
use nonblocking device open
@
<tt><ref id="networkconnectgrace" name="network&lowbar;connect&lowbar;grace"></tt>
|
pause between transferring jobs to remote printer
@
<tt><ref id="nw" name="nw"></tt>
|
spool dir is on an NFS file system
@
<tt><ref id="of" name="of"></tt>
|
banner output filter
@
<tt><ref id="offilteroptions" name="of&lowbar;filter&lowbar;options"></tt>
|
OF filter options
@
<tt><ref id="originateport" name="originate&lowbar;port"></tt>
|
originate connections from these ports
@
<tt><ref id="passenv" name="pass&lowbar;env"></tt>
|
clients pass these environment variables to filters
@
<tt><ref id="permspath" name="perms&lowbar;path"></tt>
|
lpd.perms files
@
<tt><ref id="pl" name="pl"></tt>
|
page length (in lines)
@
<tt><ref id="pr" name="pr"></tt>
|
pr program for p format
@
<tt><ref id="printcappath" name="printcap&lowbar;path"></tt>
|
/etc/printcap files
@
<tt><ref id="ps" name="ps"></tt>
|
printer status file name
@
<tt><ref id="pw" name="pw"></tt>
|
page width (in characters)
@
<tt><ref id="px" name="px"></tt>
|
page width in pixels (horizontal)
@
<tt><ref id="py" name="py"></tt>
|
page length in pixels (vertical)
@
<tt><ref id="qq" name="qq"></tt>
|
put queue name in control file
@
<tt><ref id="remotesupport" name="remote&lowbar;support"></tt>
|
operations allowed to remote host
@
<tt><ref id="remoteuser" name="remote&lowbar;user"></tt>
|
remote-queue machine (hostname) (with rm)
@
<tt><ref id="reportserveras" name="report&lowbar;server&lowbar;as"></tt>
|
server name for status reports
@
<tt><ref id="retryeconnrefused" name="retry&lowbar;econnrefused"></tt>
|
Retry on connect ECONNREFUSED errors
@
<tt><ref id="retrynolink" name="retry&lowbar;nolink"></tt>
|
Retry device open or connect failures
@
<tt><ref id="returnshortstatus" name="return&lowbar;short&lowbar;status"></tt>
|
return short lpq status when request arrives from specified host
@
<tt><ref id="reuseaddr" name="reuse&lowbar;addr"></tt>
|
set SO&lowbar;REUSEADDR on outgoing ports
@
<tt><ref id="reverselpqformat" name="reverse&lowbar;lpq&lowbar;format"></tt>
|
reverse lpq format when request arrives from specified host
@
<tt><ref id="rg" name="rg"></tt>
|
clients allow only users in this group access to printer
@
<tt><ref id="rm" name="rm"></tt>
|
remote machine (hostname) (with rp)
@
<tt><ref id="router" name="router"></tt>
|
routing filter, returns destinations
@
<tt><ref id="rp" name="rp"></tt>
|
remote printer name (with rm)
@
<tt><ref id="rt" name="rt"></tt>
|
number of times to (re)try printing or transfer (0=infinite)
@
<tt><ref id="rw" name="rw"></tt>
|
open printer for reading and writing
@
<tt><ref id="safechars" name="safe&lowbar;chars"></tt>
|
additional safe characters in control file lines
@
<tt><ref id="saveonerror" name="save&lowbar;on&lowbar;error"></tt>
|
save job when an error
@
<tt><ref id="savewhendone" name="save&lowbar;when&lowbar;done"></tt>
|
save job when done
@
<tt><ref id="sb" name="sb"></tt>
|
short banner (one line only)
@
<tt><ref id="sc" name="sc"></tt>
|
suppress multiple copies
@
<tt><ref id="sd" name="sd"></tt>
|
spool directory pathname
@
<tt><ref id="sendblockformat" name="send&lowbar;block&lowbar;format"></tt>
|
send block of data, rather than individual files
@
<tt><ref id="senddatafirst" name="send&lowbar;data&lowbar;first"></tt>
|
send data files first in job transfer
@
<tt><ref id="sendfailureaction" name="send&lowbar;failure&lowbar;action"></tt>
|
failure action to take after send&lowbar;try attempts failed
@
<tt><ref id="sendjobrwtimeout" name="send&lowbar;job&lowbar;rw&lowbar;timeout"></tt>
|
print job read/write timeout
@
<tt><ref id="sendqueryrwtimeout" name="send&lowbar;query&lowbar;rw&lowbar;timeout"></tt>
|
status query operation read/write timeout
@
<tt><ref id="sendtry" name="send&lowbar;try"></tt>
|
(alias for rt)
@
<tt><ref id="sendmail" name="sendmail"></tt>
|
sendmail program
@
<tt><ref id="serverauthcommand" name="server&lowbar;auth&lowbar;command"></tt>
|
server  authenticate transfer program
@
<tt><ref id="servertmpdir" name="server&lowbar;tmp&lowbar;dir"></tt>
|
server temporary file directory
@
<tt><ref id="serveruser" name="server&lowbar;user"></tt>
|
server user name for authentication
@
<tt><ref id="sf" name="sf"></tt>
|
suppress form feeds separating data files in job
@
<tt><ref id="sh" name="sh"></tt>
|
suppress header (banner) pages
@
<tt><ref id="shortstatuslength" name="short&lowbar;status&lowbar;length"></tt>
|
short lpq status length in lines
@
<tt><ref id="socketlinger" name="socket&lowbar;linger"></tt>
|
set the SO&lowbar;LINGER socket option
@
<tt><ref id="spooldirperms" name="spool&lowbar;dir&lowbar;perms"></tt>
|
spool directory permissions
@
<tt><ref id="spoolfileperms" name="spool&lowbar;file&lowbar;perms"></tt>
|
spool file permissions
@
<tt><ref id="spreadjobs" name="spread&lowbar;jobs"></tt>
|
amount to spread jobs to avoid collisions
@
<tt><ref id="ss" name="ss"></tt>
|
name of queue that server serves (with sv)
@
<tt><ref id="stalledtime" name="stalled&lowbar;time"></tt>
|
time after which to report active job stalled
@
<tt><ref id="stoponabort" name="stop&lowbar;on&lowbar;abort"></tt>
|
stop processing queue on filter abort
@
<tt><ref id="sv" name="sv"></tt>
|
names of servers for queue (with ss)
@
<tt><ref id="sy" name="sy"></tt>
|
stty commands to set output line characteristictics
@
<tt><ref id="syslogdevice" name="syslog&lowbar;device"></tt>
|
name of syslog device
@
<tt><ref id="tr" name="tr"></tt>
|
trailer string to send before closing printer
@
<tt><ref id="translateformat" name="translate&lowbar;format"></tt>
|
translate data format in control file
@
<tt><ref id="ty" name="ty"></tt>
|
stty commands to set serial line characteristictics
@
<tt><ref id="useauth" name="use&lowbar;auth"></tt>
|
authentication type to use
@
<tt><ref id="usedate" name="use&lowbar;date"></tt>
|
force date in control file
@
<tt><ref id="useidentifier" name="use&lowbar;identifier"></tt>
|
force identifier in control file
@
<tt><ref id="useinfocache" name="use&lowbar;info&lowbar;cache"></tt>
|
read and cache information
@
<tt><ref id="usequeuename" name="use&lowbar;queuename"></tt>
|
put queue name in control file (alias for qq)
@
<tt><ref id="useshorthost" name="use&lowbar;shorthost"></tt>
|
Use short hostname for lpr control and data file names
@
<tt><ref id="user" name="user"></tt>
|
Effective User ID (EUID) for SUID ROOT programs
@
<tt><ref id="userauthcommand" name="user&lowbar;auth&lowbar;command"></tt>
|
client side authentication program
@
<tt><ref id="userlpc" name="user&lowbar;lpc"></tt>
|
allow selected lpc commands on user files
@
<tt><ref id="xc" name="xc"></tt>
|
OBSOLETE (see sy)
@
<tt><ref id="xs" name="xs"></tt>
|
OBSOLETE (see ty)
@
<tt><ref id="xt" name="xt"></tt>
|
formats supported on printer
@
</tabular>
</table>
<label id="cm">
<label id="mx">
<label id="secprintcap">
<sect1>A very simple example
<p>
Options used:
<itemize>
<item> <tt>cm=</tt><em>comment for status</em>
<item> <tt>if=</tt><em>default job file filter </em>
<item> <tt>lf=</tt><em>log file </em>
<item> <tt>mx#</tt><em>maximum job size</em>
<item> <tt>lp=</tt><em>output device</em>
<item> <tt>sd=</tt><em>spool directory file </em>
<item> <tt>sh</tt><em>&nbsp;&nbsp;suppress headers (banners) </em>
<item> <tt>sf</tt><em>&nbsp;&nbsp;suppress form feeds between files</em>
</itemize>
<p>
The easiest configuration is this: a local printer directly connected
to a printer port.
<p>
<verb>
# 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
</verb>
<p>
I'll use this simple example to explain the basics of the LPRng
printcap format.
<p>
Lines starting with a <tt>#</tt> sign are comments, and all
leading and trailing <em>whitespace</em>,
i.e. - spaces, tabs, etc, are ignored.
Empty lines are ignored as well.
<p>
Note that you are not required to use continuation backslashes
(<tt>\</tt>) 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.
<p>
A printer (printcap entry) definition starts with the printer
<em>name</em>,
followed by one or more <em>aliases</em>,
followed by a list of options.
The name for a printer will be used be in LPRng
status output to identify the printer.
<p>
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
<tt>\:</tt> to escape the value.
Options take the form of a
keyword/value pair.
Options and values are case sensitive;
for example
<tt>Ts</tt> and
<tt>ts</tt> refer to different options.
Numerical values are indicated by a <tt>#</tt>
separator, while a <tt>=</tt> indicates a string value. Boolean switches
or flags are
set TRUE if no value follows the keyword and FALSE by
appending a <tt>@</tt>.
For example
<tt>sh</tt>
will set the
<tt>sh</tt> flag to TRUE and
<tt>sh@</tt> to FALSE.
<p>
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 &lt;<tt>jhy@gsu.edu</tt>&gt;:
<p>
<quote>
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.
</quote>
<p>
Let's go over the options used in this example:
<p>
<enum>
<item> First of all, there's the <tt>lp</tt> entry. It specifies the
file (or location) to which data is sent.
Here, it is the device <tt>/dev/lp1</tt>.
(This is system-dependent---check your vendor's manual for
information on devices.) The absolute
pathname indicates we have a local printer or file.
</item>
<item>The <tt>cm</tt> field can be used to supply a comment to
be used as a header in <tt>lpq</tt> output.
</item>
<item><tt>sd</tt> specifies the spool directory.
This is where files
are stored until they are printed.
</item>
<item> The <tt>lf</tt> and <tt>af</tt> 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 <tt>touch</tt>)
or by using the
<ref id="checkpc" name="
<tt>checkpc</tt>">
program.
LPRng is very fussy about permissions - you should run
<bf>checkpc</bf> to make sure that the log and status files have the
correct ownership and permissions.
</item>
<item>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
<ref id="secfilter" name="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.
</item>
<item><tt>mx</tt> indicates the maximum file size for a print job.
Supplying 0 here means that there is no limit.
</item>
<item>Finally, I have given the <tt>sh</tt> (suppress headers) flag. This will
inhibit banner pages.</item>
</enum>
<label id="br">
<label id="sy">
<label id="ms">
<label id="ty">
<sect1>A serial printer queue
<p>
Options used:
<itemize>
<item> <tt>br#</tt><em>serial port bit rate</em>
<item> <tt>ms=</tt><em>(alias for sy)</em>
<item> <tt>sy=</tt><em>stty options for serial port configuration</em>
<item> <tt>ty=</tt><em>(alias for sy)</em>
</itemize>
<p>
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 <tt>sy</tt> printcap entry specifies a set of <tt>stty(1)</tt>
flags and line speed that will be used to set up the serial line.
(See
<ref id="secserial" name="Serial Printers">
for details)
The <tt>br</tt> (bit rate) option
can be used to specify the line speed as well.
The following is a typical printcap for a serial printer.
<verb>
# 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
</verb>
<sect1>A remote printer queue
<p>
Options used:
<itemize>
<item> <tt>lp=</tt><em>destination</em>
<item> <tt>rm=</tt><em>remote host (machine)</em>
<item> <tt>rp=</tt><em>remote printer (machine)</em>
</itemize>
<p>
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
<bf> local </bf> or <bf> client </bf> host.
You run the <tt>lpr</tt> program on the client host
to send print jobs to
the
<tt>lpd</tt>
daemon or server process running on
the remote <bf>server</bf> host.
The
<tt>lpd</tt> daemon
accepts jobs for printing,
puts them in its print queue,
and processes them for printing on a printer.
<p>
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
<tt>lpd</tt>
daemon process is the server,
and the <tt>lpr</tt>,
<tt>lpq</tt>,
<tt>lprm</tt>,
and
<tt>lpc</tt> are client programs.
The clients send either jobs or
service requests to the server using the
<ref id="rfc1179" name="RFC1179">
protocol.
<p>
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
<tt>lpd</tt>
server
running on the client machine would then take these files
and transfer them to the remote
<tt>lpd</tt>
server.
This meant that every host
was required to run an
<tt>lpd</tt> daemon process and to have spool queues for every printer
that the user could possibly use.
<p>
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
<tt>lpd</tt> daemon or printer over a TCP/IP network connection.
This eliminates both the need for a
<tt>lpd</tt> daemon as well as the print spool directories
on the local host.
<p>
To send a job to a printer or remote
`<tt>lpd</tt>' daemon
all you need to do is specify the printer name and server hostname or
IP address.
This can be done using:
<verb>
lpr -Ppr@server printfile
</verb>
<p>
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 <tt> PRINTER </tt> environment variable,
then you can simply do the following:
<verb>
PRINTER=pr@server; export PRINTER;
lpr printfile
</verb>
<sect1>Network Printing With /etc/printcap
<p>
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
<em> aliases </em> 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.
<p>
This type of operation requires setting up an <tt>/etc/printcap</tt> file
and using the printcap configuration information.
Both the client programs (<tt>lpr</tt>, <tt>lpc</tt>, <tt>lprm</tt>,
<tt>lpq</tt>)
and the <tt>lpd</tt> daemon use the information in the <tt>/etc/printcap</tt> 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.
<p>
There are several possibilities for specifying network printers.
<em>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.
</em>
<p>
<verb>
# 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
</verb>
<p>
All the above printcap entries will make the lpr print client
connect to the remote host
<tt>server</tt>
and send the file to the <tt>raw</tt> printer.
You can specify the remote printer and remote host using the
<tt>:rp</tt>
and
<tt>:rm</tt>
entries, or the LPRng extension <tt>lp=rp@rm</tt>.
<p>
Note that we don't need a spool directory for this kind of printer,
nor do we need to run <tt>lpd</tt> on the client host machine,
as the job will be transferred directly to the remote host.
<p>
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.
<label id="forcelocalhost">
<label id="localhost">
<sect1>Spooling To Local Server
<p>
Options used:
<itemize>
<item> <tt>force_localhost</tt><em>&nbsp;&nbsp;force clients to send requests to localhost </em>
<item> <tt>localhost=</tt><em>localhost name</em>
</itemize>
<p>
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
<ref id="secfilter" name="filters">
to process the print job.
In this case,  they want the print job sent to a
<tt>lpd</tt> 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.
<verb>
# 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
</verb>
<p>
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 <tt>/usr/spool/lp</tt> and then forward the job to the
lp printer on the remote host.
The
<tt>server</tt> tag indicates that a printcap entry is to be used only
by the
<tt>lpd</tt> daemon (server) process, and the information is ignored by
the client.
The <tt>server</tt> tag is discussed in detail in the
<ref id="secserver" name="Shared printcap files"> section.
<p>
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
<tt>localhost</tt> configuration variable.
<p>
The second method is slightly different.
The <tt>force_localhost</tt> 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.
<p>
The third method is useful when you want the print job to be modified
<em>en passant</em> as it passes through the spool queue.
See the
<ref id="secfilter" name="Filters">
discussion for details.
<label id="secserver">
<sect1>Shared printcap files
<p>
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:
<verb>
#/etc/printcap file
pr:lp=pr@host
pr:lp=/dev/lp:sh
#resulting printcap information:
pr:lp=/dev/lp:sh
</verb>
<p>
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.
<p>
We can use the <tt>server</tt> tag to specify that specific
printcap information is only for use by the lpd daemon.
For example:
<verb>
#/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
</verb>
<p>
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:
<verb>
#/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
</verb>
<p>
This printcap entry also shows the use of the
<tt>tc</tt> option,
which corresponds to the
C Compiler Preprocessor (cpp)
<tt>#include</tt> directive.
Printcap entries starting with periods, underscores (<tt>_</tt>), or
<tt>@</tt> signs are treated as dummy printcap information and can only be
referenced by the <tt>tc</tt>.
<p>
When the printcap information is read,
the LPRng code will substitute the cannonical printer name for any
<tt>%P</tt> tokens that it finds in the printcap.
After processing the information in the printcap entry, the clients and
<tt>lpd</tt> daemon will see the following:
<verb>
# 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
</verb>
<label id="secoh">
<sect1>Master Printcap Files
<p>
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.
<p>
By using the
<tt>server</tt>
tag,
information for the lpd daemons can be separated out from the information
needed by the lpr print client.
The
<tt>oh=pattern</tt>
specifies that this information is only to be used by a specified host.
For example:
<verb>
#/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
</verb>
<p>
The above example is a total abuse of the use of
<tt>oh</tt>
tag,
but has some interesting effects.
The <tt>pattern</tt> is used as a <em>glob</em> pattern
and is applied to the fully qualified domain name (FQDN) of the
host reading the printcap file.
For example,
<tt>*.eng.site.com</tt> would match host
<tt>h1.eng.site.com</tt>
but would not match
<tt>h1.admin.site.com</tt>.
Thus, the effects of the first couple of entries would be to
specify that the
<tt>pr1</tt> and <tt>pr2</tt> printers on the
<tt>eng</tt> hosts would be <tt>pr1@serverhost1</tt>,
and on the
<tt>admin</tt> hosts would be <tt>pr2@serverhost2</tt>,
<p>
Also,
the lpd daemons on
<tt>serverhost1</tt>
and
<tt>serverhost2</tt>
would only extract the additional
information for
<tt>pr1</tt>
and
<tt>pr2</tt> respectively.
<p>
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.
<sect1>Printcap options for lpr, lpq, lprm, and lpc
<p>
These programs are used by users to connect to the <tt>lpd</tt>
server and send print jobs or a request.
For details about the way that this is done,
see
<ref id="rfc1179ref" name="LPRng and RFC1179"> for details.
<p>
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.
<label id="lp">
<label id="rm">
<label id="rp">
<label id="PRINTER">
<label id="defaulthost">
<label id="defaultprinter">
<sect2>lp, rm and rp
<p>
The <tt>rm</tt> (remote machine or host) and <tt>rp</tt>
or <tt>lp</tt> 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.
<enum>
<item>
If the user program is invoked with <tt>-Pxxx</tt> argument,
then the <tt>lp</tt> option is assigned the value <tt>xxx</tt>.
<item>
If no explicit value is specified and the PRINTER environment variable has
value <tt>xxx</tt>,
then the <tt>lp</tt> option is assigned value <tt>xxx</tt>.
<item>
If <tt>lp</tt> has a value of the form
<tt>rp@rm</tt>
or
<tt>rp@rm%port</tt>,
then the
<tt>rp</tt>,
<tt>rm</tt>,
and
<tt>lpd_port</tt> options are assigned the indicated values.
<item>
If <tt>rm</tt> or <tt>rp</tt> does not have a value,
then they are assigned the <tt>default_host</tt> (usually localhost)
and <tt>default_printer</tt> (usually lp) option values.
<item>
A connection is made to <tt>lpd_port</tt> on host <tt>rm</tt>
and the file transfer or command is sent as specified in
<ref id="rfc1179ref" name="RFC1179">.
</enum>
<p>
See the
<ref id="lpdev" name="Opening Output Device">
section for additional details.
<label id="bq">
<label id="generatebanner">
<label id="translateformat">
<label id="bouncequeues">
<sect1>Bounce queues
<p>
Options used:
<itemize>
<item> <tt>bq=</tt><em>destination for processed jobs</em>
<item> <tt>generate_banner</tt><em>&nbsp;&nbsp;do banner generation</em>
<item> <tt>translate_format</tt><em>&nbsp;&nbsp;do banner generation</em>
</itemize>
<p>
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.
<p>
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.
<p>
LPRng supports this capability by indicating that a printer
is actually a
<em>bounce queue</em>.
The term comes from the notion that jobs will <em>bounce</em> 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.
<p>
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.
<p>
<verb>
# 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
</verb>
<p>
Some comments:
<p>
<enum>
<item>The <tt>lp=bounce@host</tt> 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.</item>
<item>After the lpd daemon on the bouncehost does the processing,
the output is then transmitted to the queue and hostname
specified by the
<tt>bq</tt>
option.</item>
<item>We need a spool directory (<tt>sd</tt>) on the lpd server host
to hold the files and temporary output while they are processed.</item>
<item>Next, we need to indicate the filters to be used for different
printer formats.
<em>Oops, I still didn't tell you anything about
filters. Just a moment, it's on its way. See section
<ref id="secfilter" name="Filters">.
</em>
<item>The
<tt>translate_format</tt>
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,
<tt>translate_format=vl</tt>
specifies that after filtering,
format
<tt>v</tt>
files will be renamed to format
<tt>l</tt> (literal or binary).
You do not need to have a filter for a format to rename it.
For example,
<tt>translate_format=vlxf</tt>
will rename format
<tt>x</tt>
files to
<tt>f</tt>
and there is no filter for format
<tt>x</tt>.
See
<ref id="translateformat" name="translate_format">
for more details.
<item>The <tt>generate_banner</tt> flag will force a banner to be
added to the job.
The banner generation is done as discussed in
<ref id="bannerprinting" name="Banner Printing">.
</item>
</enum>
<p>
In this example, anything sent to the printer called <tt>bounce</tt> (on
this host) will be filtered on the client host. After that, it will be
transmitted to the queue <tt>lp</tt> on the server called
<bf>remote</bf>.
<p>
<label id="lprbounce">
<sect1>LPR Filtering
<p>
Options used:
<itemize>
<item> <tt>lpr_bounce</tt><em>&nbsp;&nbsp;lpr does filtering</em>
</itemize>
<p>
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 <tt>lpr</tt> 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.
<verb>
# Simple example of an lpr_bounce entry
bounce
  :lpr_bounce
  :lp=lp@remote
  :if=/usr/local/bin/lpf
</verb>
<p>
The
<tt>lpr_bounce</tt>
flag, if present in the printcap entry,
will force <tt> lpr </tt> to process the job using the
specified filters and send the outputs of the filters to the remote printer
for further processing.
<p>
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 <tt>/tmp</tt> directory and the programs are run as user.
Since no spool directory is used, the <tt>sd</tt> information is not
needed.
<label id="destinations">
<label id="router">
<label id="routing">
<sect1>Dynamic Routing
<p>
Options used:
<itemize>
<item> <tt>destinations=</tt><em>destinations for jobs</em>
<item> <tt>router=</tt><em>router program</em>
</itemize>
<p>
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 <tt>router</tt> filter return a set of destinations.  Here is a sample printcap
to accomplish this:
<verb>
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
</verb>
<p>
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.
<p>
The routing filter exit status is used as follows:
<itemize>
<item>
0  (JSUCC) - normal processing
<item>
37 (JHOLD) - job is held
<item>
any other value - job is deleted from queue
</itemize>
<p>
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.
<verb>
dest (destination queue)
copies (number of copies to be made)
priority (priority letter)
X(controlfile modifications)
end
</verb>
<p>
Example of router output:
<verb>
dest t1@localhost
copies 2
CA
priority B
end
dest t2@localhost
CZ
priority Z
end
</verb>
<p>
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.
<p>
LPQ will display job information in a slightly different format
for multiple destination jobs.  For example:
<verb>
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
</verb>
<p>
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.
<p>
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.
<p>
The optional
<tt>destinations</tt> 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
<tt>destinations</tt>,
and loop over all the names in the list looking for the "job" that you are interested in.
If there is no
<tt>destinations</tt>
information, the
<tt>bq</tt> information will be usued.
<p>
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:
<verb>
 # you need to get PERL to do a 'dup' call on FD 0
 open(CF, '+&lt;0');  
 # read the control file
 @cf_lines = &lt;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;
 } 
</verb>
<p>
This will read the control file,
truncate it,
and then write it out again.
<label id="secnetwork">
<sect1>Support for Network Print Servers
<p>
This section was supplied by Horst Fickenscher
&lt;<tt>horst.fickenscher@it.erlm.siemens.de</tt>&gt;
and updated by Patrick Powell
&lt;<tt>papowell@astart.com</tt>&gt;.
<p>
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.
<p>
The print server may support multiple printing protocols,
such as
<ref id="rfc1179" name="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.
<p>
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.
<p>
Although it's possible to connect to network printers as if they were
remote printers, Patrick Powell advises differently:
<p>
<quote>
<p>
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.
<p>
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&nbsp;9100.
<p>
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.
</quote>
<p>
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:
<verb>
     # 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
</verb>
<p>
The <tt>lp=pr4%9100</tt> means that LPRng is to make a TCP/IP
connection to host <tt>pr4</tt> on its port&nbsp;9100.
The CTI-Print filters
referenced in <tt>if</tt>, <tt>of</tt>, and <tt>vf</tt>,
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.
<p>
Filters are discussed in section
<ref id="secfilter" name="Filters">.
<p>
According to Richard S.&nbsp;Shuford &lt;<tt>s4r@ornl.gov</tt>&gt;, some DEC
printers (e.g., the DEClaser&nbsp;3500) use TCP port&nbsp;10001.
<sect1>Printer load balancing
<p>
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.
<p>
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.
<p>
I'll give two examples for this situation.
<label id="ss">
<label id="sv">
<sect2>Multi-server print queue
<p>
Options used:
<itemize>
<item> <tt>ss=</tt><em>queue served by printer </em>
<item> <tt>sv=</tt><em>printers where jobs are sent (servers)</em>
</itemize>
<p>
A multi-server print queue is one that feeds jobs to other queues.
The main queue
<tt>sv=q1,q2,...</tt> printcap entry specifies the names of the printers
that will be sent jobs.
These printers must have their spool queues on this LPD server.
<p>
Servers that are fed jobs have a
<tt>ss=</tt><em>mainqueue</em>
printcap entry.
This informs the <tt>lpd</tt> server that the queue operates under the
control of the <em>mainqueue</em> print queue,
and is fed jobs from it.
<p>
During normal operation,
when the <tt>lpd</tt> server has a job to print in the <em>mainqueue</em>,
it will check to see if there is an idle <em>service</em>  queue.
If there is,
it will transfer the job to the service queue spooling directory
and start the service queue printing activities.
<p>
Users can send jobs directly to the individual printers serving a queue.
<p>
The next example (and the comments underneath) was supplied by John Perkins
&lt;<tt>john@cs.wisc.edu</tt>&gt; (slightly edited).
<p>
Here's how I've set up a bounce queue that feeds 6 LaserWriters:
<p>
<verb>
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
</verb>
<p>
and so on for the other 4 <tt>laser</tt><em>N</em> queues.
<p>
This will forward a job from <tt>laser</tt> to <tt>laser</tt><em>N</em>, once
one of those queues is available. It will hold jobs in the
``<tt>laser</tt>'' queue until one of the other queues is empty.
<p>
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
<em>hide</em> the load sharing printers,
i.e. - not allow direct spooling to them,
then you would simply remove the non-server entries from the printcap.
<label id="checkidle">
<sect2>Checking Busy Status of Server Queues
<p>
Options used:
<itemize>
<item> <tt>check_idle=</tt><em>check for idle printer program </em>
</itemize>
<p>
The previous section outlined how LPRng uses the <tt>sv</tt> and <tt>ss</tt>
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.
<p>
The <tt>check_idle</tt> option specifies a program that is invoked by the
<tt>lpd</tt>
server to determine if the spool queue device is available.
<p>
The program is invoked with the standard filter options,
STDIN and STDOUT connected to <tt>/dev/null</tt>,
and STDERR to the error log.
<p>
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.
<verb>
Key      Value   Meaning
JSUCC    0       Successful - printer is idle
JABORT   non-zero Printer is not accepting jobs
</verb>
<p>
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. 
<p>
The following is a sample printcap entry, showing how the <tt>check_idle</tt>
facility can be used.
<verb>
pr:
  :lp=laserjet%9100
  :check_idle=/usr/local/filters/remote_check lp@laserjet
  :if=/usr/local/filters/ifhp
</verb>
<p>
The following perl program shows how to generate a query to the
remote printer by simulating an <tt>lpq</tt> query and checking for
returned status.
<verb>
#!/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;
}
</verb>
<p>
The example of the previous section can be modified now
so that it uses the <tt>check_idle</tt> facility.
The master queue will send jobs only to the server queue queues
which report idle status.
<verb>
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
</verb>
<sect2>Using a router filter
<p>
A router filter allows you to re-route jobs in a dynamic way. For
details, see
<tt><ref id="routing" name="routing">.</tt>
<p>
Lars Anderson &lt;<tt>lsa@business.auc.dk</tt>&gt; supplied this example
(slightly edited):
<p>
This script will attempt to distribute print jobs evenly on
2 printers hpl5a and hpl5b when sending to hpl5bounce.
<p>
<verb>
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
</verb>
<p>
The perl script <tt>bouncer.pl</tt> looks like this:
<p>
<verb>
#!/usr/bin/perl
#
# Script for printjob loadsharing
#
#                                 29/5-97 Lars Anderson
#
# Printqueues to check
$printer1=&quot;hpl5a\@localhost&quot;;
$printer2=&quot;hpl5b\@localhost&quot;;
# 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 &quot;dest $printer1\nCA\nend\n&quot;;
    exit;
}
if ($numjobs1 &gt; $numjobs2) {
    print &quot;dest $printer2\nCA\nend\n&quot;;
    exit;
}
print &quot;dest $printer1\nCA\nend\n&quot;; 
</verb>
<label id="lpdprintcappath">
<label id="printcappath">
<label id="nw">
<label id="details">
<sect1>The Missing Details
<p>
Options used:
<itemize>
<item> <tt>printcap_path=</tt><em>printcap file locations</em>
<item> <tt>lpd_printcap_path=</tt><em>additional server printcap file locations</em>
<item> <tt>nw</tt><em>&nbsp;&nbsp;NFS spool directory</em>
</itemize>
<p>
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).
<p>
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.
<p>
If a printcap database is desired, then it is obtained as follows. First,
the
<tt>printcap_path </tt>
and
<tt> lpd_printcap_path </tt>
configuration information (see
<ref id="lpdconf" name="lpd.conf(5)">) specifies where client and server programs find printcap
information.
The client programs use <tt>printcap_path</tt>; <tt>lpd</tt> uses both
<tt>printcap_path</tt> and <tt>lpd_printcap_path</tt>. All files are read and
the printcap entries are
extracted in order from the files. Later printcap information overrides
previous information in the files.
<p>
The common defaults for the printcap locations are:
<verb>
printcap_path          /etc/printcap:/usr/etc/printcap:\
                            /var/spool/lpd/printcap.HOSTNAME
lpd_printcap_path      /etc/lpd_printcap:/usr/etc/lpd_printcap
</verb>
<p>
When the <tt>lpd</tt> server gets a printcap entry,
if the printcap entry obtained has a spool directory and the
spool directory has a <tt>printcap</tt> 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 <tt>nw</tt> (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.
<p>
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.
<verb>
    ----- /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
</verb>
<p>
A careful study of the above example will discover the following features
of the LPRng printcap structure.
<enum>
<item>
Lines ending with a \ indicate continuation to the next line.
In practice,  the \ is replaced with space(s) when line joining is done.
<item>
Blank lines and lines whose first nonwhitespace character
is a <tt>#</tt> are ignored, except if it follows a continuation line.
(Which makes sense.)
<item>
All leading and trailing whitespace on a line are removed.
<item>
A printcap entry consists of a name, 0 or more aliases, and data entries.
<newline>
- name starts with an alphabetic character; dummy entries can start <tt>_</tt>,
<tt>@</tt> or <tt>.</tt>
<newline>
- alias starts with a <tt>|</tt> followed by the alias
<newline>
- fields or data entry starts with <tt>:</tt> followed by the entry
<item>Field or data entry
<newline><em>name</em> - main or cannonical name for printcap entry
<newline><tt>|</tt><em>name</em> - alias <em>name</em> for printcap entry
<newline><tt>:</tt><em>key</em>   - set the <em>key</em> to ON  (1)
<newline><tt>:</tt><em>key</em><tt>@</tt>  - set the <em>key</em> to OFF (0)
<newline><tt>:</tt><em>key</em><tt>#</tt><em>nnn</em> - set the <em>key</em>
to <em>nnn</em>, where <em>nnn</em> follows C language conventions
<newline><tt>:</tt><em>key</em><tt>=</tt><em>string</em> - set the <em>key</em>
to the string value to end of line
<item>
Printcap entries whose cannonical name starts with <tt>_</tt>, <tt>@</tt>,
or <tt>.</tt> (period)
(eg.- <tt>@</tt><em>name</em>)
are treated like dummy
entries.  They can be referenced with <tt>:tc=</tt><em>entry</em><tt>:</tt>,
but will be ignored otherwise.
<item>
The <tt>tc=f1:tc=f2:...</tt> 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 <tt>:tc:</tt> entries.
<item><em>key</em><tt>=</tt><em>value.</em><tt>%</tt><em>X</em>
<newline>a selected set of
<tt>%</tt><em>X</em> values are expanded when the printcap entry is used by
the client or server program.
The following values are expanded:
<newline>
P - printcap cannonical or main name
<newline>
H - Fully Qualified Domain Name for host
<newline>
h - short name for host
<newline>
R - Fully Qualified Domain Name for remote host host
<newline>
r - short name for remote host host
</item>
<item>
The
<tt>oh</tt> entry specifies that a particular printcap entry
can only be used by a host with a matching host name or IP address.
See
<ref id="secoh" name="Master Printcap Files"> for details.
</item>
<item>
The
<tt>server</tt> entry specifies that a particular printcap entry
can only be used by the lpd server,
and is ignored by other programs.
See
<ref id="secserver" name="Shared Printcap Files"> for details.
</enum>
<p>
Printcap information is extracted in order from the printcap files,
and later information for printcap entries overrides earlier ones.
<p>
The
<tt>%</tt><em>X</em> 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
<tt>tc</tt> operator.
As shown in the example,
by making the spool directory name depend on the cannonical printcap name,
it simplifies management of the printer.
<sect2>Spool (Control) Directory Printcap File
<p>
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.
<p>
The
<tt>server</tt>
tag and
<tt>oh</tt>
options have rendered this facility obsolete,
and it may be removed in later releases.
<sect2>Separate Printcap Files for LPD
<p>
Since only the LPD server uses the
<tt>/etc/lpd_printcap</tt>
or
<tt>/usr/etc/lpd_printcap</tt>
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
<tt>lpd_printcap_path</tt>
entry to specify the desired file.
<p>
The
<tt>server</tt>
and
<tt>oh</tt> options have rendered this
facility obsolete and it may be removed
in future releases of LPRng.
<sect2> Printcap Entry all
<p>
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:
<verb>
all:all=pr1,pr2,...
</verb>
<p>
The LPRng software will use the individual entries of
the printer list and request additional printcap information if necessary.
<sect2> More Example Printcap Entries
<p>
The following printcap entries show the formats,  and have
some additional comments about fields in the printcap file.
<verb>
#
#  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        #&lt;- 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
</verb>
<sect2>PC-NFS Print Spooler
<p>
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 <tt>:cd:</tt> (control directory) entry is used
to specify a directory to hold sensitive control information. For example
<verb>
#/etc/lpd_printcap
# PCNFS Spooler
#
pr7
    :lp=pr7@printserver
    :bq=pr1@printserver
    :sd=/usr/spool/pcnfs/pr7
    :cd=/usr/spool/lpd/pr7
</verb>
<p>
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.
<sect1>Management Strategy for Large Sites
<p>
One very effective way to organize print spooling is to have a small
number of print servers running a
<tt>lpd</tt> 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.
<p>
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
<tt>/etc/printcap</tt>
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
<ref id="secnis" name="Using Programs To Get Printcap Information">
for details.
<label id="secnis">
<sect1>Using Programs To Get Printcap Information
<p>
In the
<tt>lpd.conf</tt>
file you can specify:
<verb>
printcap_path=|program
</verb>
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
<tt>STDIN</tt>.
The filter
should supply the printcap information on
<tt>stdout</tt>
and exit with a 0
(success) error code.  By convention,  the printcap name 'all'
requests a printcap entry that lists all printers.
<p>
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
<tt>nismatch</tt> or <tt>ypmatch</tt> programs,
the complexity of incorporating vendor specific code is avoided.
<sect2>How to use NIS and LPRng
<p>
This note is based on material sent to the
<tt>lprng@iona.ie</tt>
mailing list by
Paul Haldane
<tt>&lt;paul@ucs.ed.ac.uk></tt>.
<p>
<verb>
 # 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
 # 
</verb>
<p>
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.
<p>
The input to NIS is a normal printcap file:
<verb>
# 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:
</verb>
<p>
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).
<verb>
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
</verb>
<p>
To specify that you want YP database rather than file access,
use the following entry in your <tt>/etc/lpd.conf</tt> file:
<verb>
printcap_path |/usr/local/lib/pcfilter
</verb>
<p>
Put the following shell script in /usr/local/lib/pcfilter
<verb>
#!/bin/sh
#/usr/local/lib/pcfilter
read key
ypmatch "$key" printcap.byname
</verb>
<p>
<sect2>How to use NIS and LPRng - Sven Rudolph
<p>
<verb>
 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
</verb>
<p>
When I use a cluster-wide printcap,
two entries for each printer will appear, e. g.:
<verb>
---------- 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
</verb>
<p>
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.
<enum>
<item>
Assumptions
<p>
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.
<p>
In the examples the printer is called lp1 .
</item>
<item>
Add the following to your YP Makefile (/var/yp/Makefile) on the YP
server :
<verb>
---------- 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
</verb>
<p>
(These lines are for Debian GNU/Linux, other systems might require
other modifications)
<item>
Install the programs <tt>match_printcap</tt>
and <tt>normalize_printcap</tt> to <tt>/usr/lib/yp</tt>.
normalize_printcap
is only required on the YP server.
The <tt>normalize_printcap</tt> processes only the LPRng printcap format.
<verb>
---------- 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
</verb>
<p>
The result of processing the sample printcap file is:
<verb>
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
</verb>
<p>
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 <tt>ypmatch</tt>:
<verb>
---------- 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
</verb>
<item>
Now test the YP arrangement:
<verb>
$ 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
</verb>
<item>
Add the printcap_path entry to <tt>/etc/lpd.conf</tt>:
<verb>
printcap_path=|/usr/lib/yp/match_printcap
</verb>
<item>
Test the use of the printcap path entry:
<verb>
$ lpq -Plp1 # shows the status of lp1
</verb>
<item>
Restart the lpd server and check to see that it accesses
the right printcap information.
Use the same <tt>lpq</tt> command,
and then try <tt>lpc printcap lp1</tt>.
</enum>
<label id="qq">
<label id="usequeuename">
<label id="forcequeuename">
<sect1>The Record Queue Name qq and force_queuename flags
<p>
Options used:
<itemize>
<item> <tt>qq</tt><em>&nbsp;&nbsp;Insert queue name into control file</em>
<item> <tt>use_queuename</tt><em>&nbsp;&nbsp;(alias for qq)</em>
<item> <tt>force_queuename=</tt><em>&nbsp;Queuename to be used</em>
</itemize>
<p>
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.
<p>
The
<tt>qq</tt>
use queuename option or its
alias
<tt>use_queuename</tt>
tells
LPRng to record the queue name that a job was queued to,
and make it available to other software for processing.
The
<tt>force_queuename=...</tt>
entry forces this name to be used.
This capability has some interesting possibilities,
as shown below.
<verb>
pr1_landscape|pr1_portrait|pr_raw:lp=pr@host:qq
</verb>
<p>
If a job is printed using
<tt>lpr -Ppr1_landscape</tt>,
then
<tt>pr1_landscape</tt>  will be recorded as the spool queue name
by the LPRng software.
<p>
Later,
when the job is processed by a filter,
the filter will be invoked with a
<tt>-Qpr1_landscape</tt>
command line option.
The filter can use the name of the queue to enable say,
landscape,
portrait,
or raw
orientations.
<verb>
john|tom|frank:lp=pr@host:force_queuename=office
</verb>
<p>
This printcap entry forces the queuename to be <tt>office</tt>;
this information could be used by a central routing facility to
process the information is a suitable manner.
<label id="checkfornonprintable">
<label id="ml">
<label id="xt">
<sect1>Using the check_for_nonprintable Flag
<p>
Options used:
<itemize>
<item> <tt>check_for_nonprintable</tt><em>&nbsp;&nbsp;LPR checks for non-printable file </em>
<item> <tt>ml#</tt><em>&nbsp;&nbsp;minimum number of printable characters </em>
<item> <tt>xt</tt><em>&nbsp;&nbsp;(alias for check_for_nonprintable)</em>
</itemize>
<p>
Normally, <tt>lpr</tt>
checks an <tt>f</tt> 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.
<p>
Disabling can be done on a single printcap basis,
or you can do this on a global basis by modifying the
configuration information (see
<ref id="lpdconf" name="lpd.conf">).
<p>
The <tt>ml</tt> value specifies the number of characters that
are to be checked.
Clearly,  if it is 0, none will be checked.
<label id="fd">
<sect1>The fd Forwarding Off Option
<p>
Options used:
<itemize>
<item> <tt>fd</tt><em>&nbsp;&nbsp;Forwarding off</em>
</itemize>
<p>
When the <tt>fd</tt> option is on (default is OFF),
the <tt>lpd</tt> 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.
<p>
This was a wimpy attempt to prevent job spoofing.
<label id="rg">
<sect1>The rg Restrict Use to Group Members Option
<p>
Options used:
<itemize>
<item> <tt>rg=</tt><em>Restricted group list</em>
</itemize>
<p>
The <tt>rg</tt> 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.
<p>
This was a wimpy attempt to do restrictions on print facilities.
The -Ppr@host option overrides this check,
unless the <tt>rg</tt> value is put in the LPRng defaults.
<p>
However,
it does provide a simple tool to have clients do some form of permissions
checking that only the <tt>lpd</tt> server could normally do.
<label id="fx">
<sect1>The fx Allowed Formats Option
<p>
Options used:
<itemize>
<item> <tt>fx=</tt><em>supported formats for printing</em>
</itemize>
<p>
The <tt>fx</tt> option specifies which formats are supported by
a spool queue.
The <tt>lpr</tt> program uses these to check if a requested format
is supported.
The default formats are
<tt>fx=flp</tt>,  i.e. - the default (normal), binary,
and <tt>pr</tt> formats.
<p>
If the <tt>lpr -Fx</tt> option is used,
the allowed formats are not checked.
<sect1>Fixing Bad Control Files and Metacharacters
<p>
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.
<p>
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
<ref id="reverselpqformat" name="reverse_lpq_format">
for an example.
<label id="safechars">
<label id="fixbadjob">
<sect2>Defective RFC1179 Implementations
<p>
Options used:
<itemize>
<item> <tt>fix_bad_job</tt><em>&nbsp;&nbsp;fix bad job</em>
<item> <tt>safe_chars=</tt><em>additional safe characters for control file </em>
</itemize>
<p>
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 <bf>until they need to print a banner
or provide status information</bf>.
<p>
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.
<p>
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:
<verb>
unexpected end of input
</verb>
<p>
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.
<sect2>OS/2
<p>
For various reasons,  some versions of the OS/2 <tt>lpd</tt>
print spooler have decided to make the control file and data file names
have different formats.
This can cause <tt>LPRng</tt> to suspect that somebody is trying to
clobber other users jobs,
and it will normally reject such jobs.
<p>
In addition,
the OS/2 spooler does not follow RFC1179 correctly,  and truncates
the data and job file protocol exchange.
<sect2>Serious Security Loophole
<p>
Finally,
there is the subtle and nasty problem with some print filters
that are not
<em>meta-char-escape</em> proof.
For example,
suppose that a user decided to spool a job as follows:
<verb>
lpr '-J; rm -rf /*;' /tmp/a
</verb>
<p>
This would create a job file with the line:
<verb>
J `rm /etc/passwd; echo Job;`
</verb>
<p>
The job would get printed on a printer with the following printcap:
<verb>
pr:sd=/...
  :if=/usr/local/hack
</verb>
<p>
And of course we have <tt>/usr/local/hack</tt> (yes,
this is a BAD example, so we won't start pointing out all the things):
<verb>
#!/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
</verb>
<p>
The observant reader will notice that the above line gets expanded to:
<verb>
eval /usr/local/realfilter -M`rm /etc/passwd; echo Job;`
</verb>
<p>
The interesting thing to observe is that the <tt>realfilter</tt>
will probably execute correctly,
while the password file will magically vanish.
<sect2>Using the fix_bad_job Option
<p>
Several microprocessor operating systems have decided to use
<em>extended fonts</em> 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.
<p>
In order to prevent such problems,
LPRng ruthlessly purges all characters but
upper and lower case letters,
spaces, tabs, and <tt>-_.@/:()=,+-%</tt> from the control file,
replacing suspicious characters with '_'.
<p>
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
<tt>fix_bad_job</tt> 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.
<p>
For some installations,
the default set of safe characters may be overly restrictive.
For example,
<em>vintage</em> software may generate files with <tt>#</tt> characters
in the <tt>J</tt> line of the control file.
The replacement of this character may cause other things to stop
working.
<p>
The <tt>safe_chars</tt> option allows the user to specify an additional
set of safe characters in the <tt>lpd.conf</tt> configuration file(s).
For example, <tt>safe_chars=#"</tt> would allow the
<tt>#</tt>
and
<tt>"</tt>
characters to appear in the control file.
<label id="bk">
<label id="controlfilterbk">
<sect2>Using the bk Option and Control File Filters
<p>
Options:
<itemize>
<item> <tt>bk</tt><em>&nbsp;&nbsp;Berkeley compatible control file</em>
<item> <tt>control_filter=</tt><em>Control file filter</em>
</itemize>
<p>
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.
<p>
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.
<p>
Some spoolers require that the contents of the control file
be in <bf>exactly</bf> 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.
<p>
The <tt>bk</tt> (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.
<p>
However,
there are some very odd commercial implementations that require
<em>more</em> information than is present.
To assist with this,
the <tt>control_filter</tt> option can be used.
This specifies a program that will process the control file
before it is sent to a remote destination.
See
<ref id="secfilter" name="Filters"> for details on filter
operation,
and
<ref id="controlfilter" name="Control Filters"> for more information.
<p>
The <tt>control_filter</tt> 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.
<p>
The
<tt>control_filter</tt>
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:
<verb>
# you need to get PERL to do a 'dup' call on FD 0
$status = 0;
@cf_lines = &lt;STDIN>;
# mess about with the control file
foreach $line (@cf_lines) {
   # or whatever you want
   print STDOUT $line;
} 
exit $status;
</verb>
<p>
The exit code of the
<tt>control_filter</tt>
is used to determine whether to proceed in processing.
See
<ref id="errorcodes" name="Errorcodes"> for details.
<p>
Also, see
<ref id="controlfilter" name="Control Filters"> for more information.
<label id="mc">
<label id="sc">
<sect1>Maximum Copies
<p>
Options used:
<itemize>
<item> <tt>mc#</tt><em>maximum copies </em>
<item> <tt>sc</tt><em> suppress multiple copies </em>
</itemize>
<p>
The <tt> mc</tt> value specifies the maximum number of
copies of a job that can be printed on a printer using the
<tt>lpr -Knn </tt> or
<tt>lpr -#nn </tt> option.
<p>
The <tt>sc</tt> boolean cause LPR to reject requests to print multiple
copies of a file.
<label id="mi">
<label id="minfree">
<sect1>The mi Minimum Spool Queue Space Option
<p>
Options used:
<itemize>
<item> <tt>mi#</tt><em>minimum spool queue space (Kbytes) </em>
<item> <tt>minfree#</tt><em>alias for mi</em>
</itemize>
<p>
If this value is non-zero,
then the <tt>lpd</tt> server checks to see that there is the
specified number of bytes of file space available before transferring a job.
<label id="db">
<label id="fulltime">
<label id="mstimeresolution">
<label id="syslogdevice">
<label id="usedate">
<label id="useinfocache">
<sect1>Debugging
<p>
Options used:
<itemize>
<item> <tt>debugging=</tt><em>debugging options</em>
<item> <tt>full_time</tt><em>&nbsp;&nbsp;full or extended time format</em>
<item> <tt>ms_time_resolution</tt><em>&nbsp;&nbsp;millisecond time resolution</em>
<item> <tt>syslog_device=</tt><em>syslog alternative device</em>
<item> <tt>use_date</tt><em>&nbsp;&nbsp;put date information in control file</em>
<item> <tt>use_info_cache</tt><em>&nbsp;&nbsp;cache printcap and other information</em>
</itemize>
<p>
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.
<p>
First,
serious errors or other information are logged using the
<em>syslog()</em> facilities.
If these are not present on a system,
then the messages are logged to the device specified by
<tt>syslog_device</tt>.
<p>
For client programs, the debugging options are specified on the command
line and output is directed to STDERR.
For the <tt>lpd</tt> server,
debugging commands can be specified on the command line OR as the
<tt>db=options</tt> printcap value.
Output is directed to the log file (<tt>lf</tt> option value, default log).
<p>
A typical debug entry has the format <tt>2,network+1,database</tt>.
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.
<itemize>
<item>nnn - general purpose debugging level
<item>network - network debugging
<item>database - database debugging
<item>receive - job or command reception debugging
<item>print - detailed job printing debugging
</itemize>
<p>
The <tt>full_time</tt> flag forces the logging and other information
which has timestamps to have a full (year, month, day, etc.) timestamp.
The
<tt>ms_time_resolution</tt> flag forces millisecond time resolution
in the time stamp.
The
<tt>use_date</tt> flag forces a date value to be placed in a control file
if there is none.
<p>
The <tt>use_info_cache</tt> (default ON) causes <tt>lpd</tt>
to cache printcap and configuration information.
This is desirable except when trying to change values in printcap files and
test the results.
By using <tt>use_info_cache@</tt> in the configuration information,
you can get immediate responses.
Also, see
<ref id="lpcreread" name="lpc reread">
for another method.
<label id="ipv6">
<label id="lockfile">
<label id="logfile">
<label id="spooldirperms">
<label id="spoolfileperms">
<label id="spreadjobs">
<label id="reportserveras">
<sect1>LPD Specific
<p>
Options used:
<itemize>
<item> <tt>ipv6</tt><em>&nbsp;&nbsp;use IPV6 Network facilities </em>
<item> <tt>lockfile=</tt><em>lpd server lock file</em>
<item> <tt>logfile=</tt><em>lpd server log file</em>
<item> <tt>report_server_as=</tt><em>server name for status reports</em>
<item> <tt>spool_dir_perms#</tt><em>spool directory permissions </em>
<item> <tt>spool_file_perms#</tt><em>spool file permissions </em>
<item> <tt>spread_jobs#</tt><em>job number spread</em>
</itemize>
<p>
These options are usually LPD specific.
For example,
the <tt>ipv6</tt> specifies that the IPV6 protocol,
rather than IPV4 will be used.
In future versions,  this may not be necessary.
<p>
The <tt>lockfile</tt> and <tt>logfile</tt> specify the location of the
lock file and the log file used by the <tt>lpd</tt> server.
<p>
The <tt>spool_dir_perms</tt> and <tt>spool_file_perms</tt>
(default 0700 and 0600 respectively)
values are the (numeric) permissions for the spool directory and
spool files.
<p>
The <tt>spread_jobs</tt> option is obsolete.
The <tt> spread_jobs</tt> 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.
<p>
The <tt>report_server_as</tt> 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.
<sect1>Legacy Compatibility
<p>
The following arguments have been provided for compatibility with
legacy systems.
<label id="allowduplicateargs">
<sect2>The allow_duplicate_args Option
<p>
Options used:
<itemize>
<item> <tt>allow_duplicate_args</tt><em>&nbsp;&nbsp;allow lpr to have duplicate arguments </em>
</itemize>
<p>
Some users would like duplicate LPR and LPRM command line
arguments to override earlier ones,
i.e. -
<tt>lpr -a x -a y </tt> should be equivalent to
<tt>lpr -a y</tt>
<p>
The <tt>allow_duplicate_args</tt> option
allows the various client programs to have duplicate arguments.
The last specified argument on the command line will override
previous values.
<label id="classinstatus">
<label id="classnamelength">
<label id="breakclassnameprioritylink">
<sect2>The break_classname_priority_link Option
<p>
Options used:
<itemize>
<item> <tt>break_classname_priority_link</tt><em>&nbsp;&nbsp;classname and priority can differ</em>
<item> <tt>class_in_status</tt><em>&nbsp;&nbsp;show class name in status</em>
<item> <tt>classname_length#</tt><em>&nbsp;&nbsp;maximum length of classname in control file </em>
</itemize>
<p>
By default the class name and the job priority are identical.  The
<tt> break_classname_priority_link </tt>
flag breaks this link, and the class can be
specified separately from the priority.
<p>
Also, the maximum classname
size specified by RFC1179 is 32 characters;  the
<tt>classname_length#nnn</tt>
(default 31) allows a longer classname (up to 127 characters) to be used.
<p>
Setting the <tt>class_in_status</tt> option causes the class name rather
than priority to be displayed in the status information.
<label id="reverselpqformat">
<sect2>The reverse_lpq_format Option
<p>
Options used:
<itemize>
<item> <tt>reverse_lpq_format=</tt><em>&nbsp;reverse LPQ status format for specified remote systems</em>
</itemize>
<p>
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.
<p>
The <tt>reverse_lpq_format=</tt> specifies a list of printers or IP addresses
for which the <tt>lpd</tt> server will return LONG status when SHORT is
requested,
and vice versa.
For example:
<verb>
reverse_lpq_format=*.eng.com,130.192.0.0/16
</verb>
<p>
will cause hosts whose Fully Qualified Domain Name (FQDN) ends in
<tt>eng.com</tt> or from subnet <tt>130.192.0.0</tt> to have reversed
status returned.
<label id="shortstatuslength">
<label id="returnshortstatus">
<sect2>The return_short_status and short_status_length Options
<p>
Options used:
<itemize>
<item> <tt>return_short_status=</tt><em>&nbsp;return short LPQ status for specified remote systems</em>
<item> <tt>short_status_length#</tt><em>&nbsp;short LPQ status length in lines</em>
</itemize>
<p>
In order to be compatible with non-LPRng printers,
some administrators would like <tt>lpd</tt> to return a short or brief
status to normal status queries.
<p>
The <tt>return_short_status=</tt> specifies a list of printers or IP addresses
for which the <tt>lpd</tt> server will return an abbreviated
status when LONG status is requested.
For example:
<verb>
return_short_status=*.eng.com,130.192.0.0/16
short_status_length#3
</verb>
<p>
will cause hosts whose Fully Qualified Domain Name (FQDN) ends in
<tt>eng.com</tt> or from subnet <tt>130.192.0.0</tt> to get only
3 lines of detailed status returned.
<label id="ignorerequesteduserpriority">
<label id="forcefqdnhostname">
<sect2>The ignore_requested_user_priority and force_fqdn_host Options
<p>
Options used:
<itemize>
<item><tt>ignore_requested_user_priority</tt><em>prevent users from queue jumping</em>
<item><tt>force_fqdn_hostname</tt><em>force FQDN hostname in control file</em>
</item>
</itemize>
<p>
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 <tt>lpd</tt> server to ignore the
requested user priority.
However, the <tt>topq</tt> operation will still be effective.
<p>
Similarly,  some print spoolers do not put a FQDN host name in their control file.
The <tt>force_fqdn_hostname</tt> flag will cause <tt>lpd</tt> to put a FQDN
host name in.
<p>
<label id="co">
<sect1>Unused Legacy Printcap Options
<p>
Options used:
<itemize>
<item><tt>co</tt>  <em> Cost - passed to filters.</em>
</item>
</itemize>
<p>
The above option is retained only for
compatibility with legacy filter operation.
See
<ref id="filtercmd" name="Filter Command Line Flags">
for details.
<sect1>Compatibility with BSD printcap
<p>
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.
<p>
For example,
the <tt>fc/fs/xc/xs</tt> 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 <tt> stty </tt> commmand compatible
<tt>ty</tt> or <tt>sy</tt> entry.
Both options
are synonyms, and the value is a set of <bf>stty(1)</bf> options.
See
<ref id="secconvert" name="Converting BSD fc,fs,xc,xs To LPRng sy">
for details.
<p>
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.
<label id="secfilter">
<sect>Filters
<p>
This section gives an overview of how LPRng uses filter programs,
and gives a detailed discussion of how the printcap options and
filters interact.
<sect1>What are filters?
<p>
Print filters are one of the most powerful tools in BSD-style printer
systems.
<p>
In general UNIX terms, a <em>filter</em> is a program that takes its input
file(s), does something with it, and sends the result to its standard
output. Most UNIX utilities are designed as filters.
(But since you are a system manager, you should already know that :))
<p>
In the context of a BSD-style print spooler (and also LPRng), the term
<em>filter</em> refers to a program that does processing on a file that is
submitted to the printer. As such, it is a specific example of the
general class of programs called `filters'.
<p>
Usually the
filter is executed with STDIN reading from the file to be
printed
or program generating the output and STDOUT to the printer device.
STDERR (file handle 2) is redirected to a log file,
and file handle&nbsp;3 to an accounting file.
<p>
A filter can be as simple as a <tt>LF</tt> to <tt>CR/LF</tt>
translator (the example used before),
or it can incorporate a complete
accounting system, automatic file type translations,
or even redirect the job to another printing system.
<p>
As part of the LPRng project,
the following filters are supported.
See the associated documentation for details.
<itemize>
<item> Distributed in the LPRng source distribution
<newline>
<ref id="lpf" name="lpf">
a very simple CR/LF or passthrough filter.
<item> Distributed in the FILTERS_LPRng distribution:
<newline>
<ref id="ifhp" name="CTI-ifhp">
HP PCL and PJL printer filters <item>
<ref id="psfilter" name="psfilter">
PostScript printer filter
</itemize>
The supported filters and other facilities are available from
<htmlurl url="ftp://ftp.astart.com/pub/LPRng" name="ftp://ftp.astart.com/pub/LPRng">
<label id="if">
<sect1>What are print formats?
<p>
Options used:
<itemize>
<item>
<tt>if</tt>,
<tt>cf</tt>,
<tt>df</tt>,
<tt>gf</tt>,
<tt>nf</tt>,
<tt>of</tt>,
<tt>rf</tt>,
<tt>tf</tt>,
<tt>vf</tt>,
<em>X</em><tt>f</tt>,
<em>&nbsp;&nbsp;Filter programs </em>
</itemize>
<p>
LPRng has inherited a set of so-called `<bf>print formats</bf>' from its
BSD ancestor.
The format was originally used
to specify the type of file that was being printed.  The
<tt>lpd</tt> daemon
would use the print format to select the required
filter for processing the file.
<bf>The default format is <tt>f</tt></bf>.
<p>
The user can specify the format (i.e., the file type) by giving
the appropriate option to <tt>lpr</tt>:
<p>
<itemize>
<item>
<tt>-b</tt> or <tt>-l</tt>: Binary (literal) file. No processing should
be done.
The
<tt>l</tt> format is recorded as the file format.
</item>
<item><tt>-c</tt>: cifplot(1) output.</item>
<item><tt>-d</tt>: TeX DVI file.</item>
<item><tt>-g</tt>: Output from the plot(3X) routines.</item>
<item><tt>-n</tt> or <tt>-t</tt>: (di)troff output.</item>
<item><tt>-p</tt>: Text file that should be pre-processed by the <tt>pr</tt>
command, and then by the standard text filter.</item>
<item><tt>-r</tt>: Text with FORTRAN carriage control characters in the
first column. (Used to be the <tt>-f</tt> option.)</item>
<item><tt>-v</tt>: Benson Varian raster image.</item>
</itemize>
<p>
Alternatively, one can also use
<tt>-Fx</tt>, where <tt>x</tt> is the format specifier.
(E.g., <tt>-Fc</tt>
instead of <tt>-c</tt>.)
This last form also allows you to use other
(non-standard) format specifiers.
<p>
The filter for format
<tt>X</tt>
is the value for the
<tt>Xf</tt> printcap
option,
with some minor exceptions.
The following
<tt>Xf</tt>
options have a pre-defined meaning.
<p>
<itemize>
<item>
<tt>if</tt>
The <tt>f</tt>
format filter,
i.e. - for the default
<tt>f</tt>
format.
All print jobs are passed
through this one, unless another format is selected.</item>
<item>
<label id="cf">
<tt>cf</tt> Cifplot data filter (for <tt>-c</tt> format).</item>
<item>
<label id="df">
<tt>df</tt> Filter for DVI files (<tt>-d</tt>).</item>
<item>
<label id="gf">
<tt>gf</tt> Graph data filter (<tt>-g</tt>).</item>
<item>
<label id="nf">
<tt>nf</tt> Ditroff data filter (<tt>-n</tt>).</item>
<item><tt>of</tt> This filter is used for processing the (optional)
banner at the start and/or end of the print job,
and also for the interjob separators.
See
<ref id="ofdetails" name="of"> filter for details.
<item>
<label id="rf">
<tt>rf</tt> Filter for Fortran style files (<tt>-r</tt>).</item>
<item>
<label id="tf">
<tt>tf</tt> Troff filter (<tt>-t</tt>).</item>
<item>
<label id="vf">
<tt>vf</tt> (Versatek) raster image filter (<tt>-v</tt>).</item>
</itemize>
<label id="ofdetails">
<sect1>OF Filter
<p>
The
<tt>of</tt>
filter is used to process banners and job separators.
The
<tt>of</tt> filter is responsible for performing appropriate
processing of this information and sending to the printer
for action.
<p>
While the various file filters are invoked on a once per print file basis,
the
<tt>of</tt>
filter is invoked on a once per print job basis.
<p>
This filter is the first one to be started,
and should perform whatever specialized device initialization
is needed.
It should also do whatever accounting procedure is desired
for start of job accounting.
<p>
The
<tt>of</tt>
filter will be given any banner printing or job separation
information for a job.
As part of its operation,
it can detect a specific string,
corresponding to a banner print request,
and generate a banner.
(See the
<ref id="jobsteps" name="Job Processing Steps and Printcap Options">
for details.)
<p>
During operation,
the
<tt>lpd</tt> server will send the special
<bf>stop</bf> sequence of <tt>\031\001</tt> to the
<tt>of</tt> filter.
The filter must then suspend itself using a
<tt>kill -STOP</tt> operation.
The <tt>lpd</tt> server will detect that the
<tt>of</tt> filter has suspended itself and then
will perform other printing operations.
<p>
After the other printing operations have been completed,
the <tt>of</tt> will then be sent a
<tt>kill -CONT</tt> signal.
<p>
This sequence will continue until all information has been printed,
and then the <tt>of</tt> filter's STDIN will be closed.
The filter will then perform whatever cleanup operations are needed,
update accounting or other information,
and exit.
<label id="pr">
<sect1>The lpr -p  format and pr option
<p>
Options used:
<itemize>
<item><tt>pr=</tt><em>pr program for p format</em>
</itemize>
<p>
The <tt>-p</tt> format is implemented by sending the file through
the program
specified by the <tt>pr</tt>
printcap utility (default is <tt>/bin/pr</tt>),
and passing the result to the normal
<tt>:if</tt> filter.
<sect1>The lpr -l  format and binary format
<p>
The binary (or literal) format
is indicated by format type
<tt>-l</tt>.
The <tt>if</tt> filter
is used to process the file,
and is invoked with the
<tt>-c</tt>
(<tt>c</tt>ancel processing?) flag.
<label id="jobsteps">
<sect1>Job Processing and Printcap Options
<p>
Much of the flexibility of the LPRng software is obtained
from the ability to control the details of each step of job processing.
The following section details each step in the processing of a job,
and explains the printcap options used to control each operation.
<p>
Assume the <tt>pr</tt>
printcap entry has the form:
<verb>
pr
    :lp=/dev/lp  OR  :lp=rp@rm
    :sd=/var/spool/lpd/pr
    :lf=log
    :of=/usr/local/bin/lpf
    :if=/usr/local/bin/lpf
</verb>
<p>
Assume that we have used the following command to print
a set of files.
<verb>
lpr -Ppr file1 file2
</verb>
<p>
This will create a control file
in the
<tt>/var/spool/lpd/pr</tt>
directory with the following contents (this is an example -
in practice there may be minor differences between the example
and an actual control file):
<verb>
Hastart4.astart.com
J/tmp/file1 /tmp/file2
CA
Lpapowell
Ppapowell
fdfA002230astart4.astart.com
N/tmp/file1
UdfA002230astart4.astart.com
fdfB002230astart4.astart.com
N/tmp/file2
UdfB002230astart4.astart.com
</verb>
<p>
<label id="achk">
<label id="af">
<label id="as">
<label id="ff">
<label id="fo">
<label id="lk">
<label id="lpdev">
<label id="la">
<label id="ar">
<label id="ld">
<label id="rw">
<label id="connectgrace">
<label id="networkconnectgrace">
<label id="connectinterval">
<label id="connecttimeout">
<label id="connecttry">
<label id="controlfilter">
<label id="nb">
<label id="servertmpdir">
<sect2>Opening the Output Device
<p>
Options used:
<itemize>
<item> <tt>achk</tt><em>&nbsp;&nbsp;Accounting check at start</em>
<item> <tt>af=</tt><em>Accounting File</em>
<item> <tt>ar</tt><em>&nbsp;&nbsp;Remote printer accounting enabled</em>
<item> <tt>as=</tt><em>Accounting at start</em>
<item> <tt>connect_grace#</tt><em>&nbsp;&nbsp;Time between jobs</em>
<item> <tt>connect_interval#</tt><em>&nbsp;&nbsp;Connection interval</em>
<item> <tt>connect_timeout#</tt><em>&nbsp;&nbsp;Connection timeout</em>
<item> <tt>control_filter=</tt><em>Control file filter</em>
<item> <tt>ff</tt><em>&nbsp;&nbsp;form feed</em>
<item> <tt>fo</tt><em>&nbsp;&nbsp;form feed on open</em>
<item> <tt>la</tt><em>&nbsp;&nbsp;Local printer accounting enabled</em>
<item> <tt>ld=</tt><em>leader on open (initialization string)</em>
<item> <tt>lk</tt><em>&nbsp;&nbsp;Lock IO device</em>
<item> <tt>lp=</tt><em>IO device pathname</em>
<item> <tt>nb</tt><em>&nbsp;&nbsp;Nonblocking device open</em>
<item> <tt>network_connect_grace#</tt><em>&nbsp;&nbsp;Time between jobs</em>
<item> <tt>of=</tt><em>of filter</em>
<item> <tt>retry_econnrefused#</tt><em>&nbsp;&nbsp;Retry if open failed</em>
<item> <tt>retry_nolink#</tt><em>&nbsp;&nbsp;Retry if open failed</em>
<item> <tt>rm</tt><em>&nbsp;&nbsp;the remote machine to send the job to</em>
<item> <tt>rp</tt><em>&nbsp;&nbsp;the remote print queue to send the job to</em>
<item> <tt>rw</tt><em>&nbsp;&nbsp;device opened RW flag</em>
<item> <tt>server_tmp_dir=</tt><em>temporary directory</em>
</itemize>
Sequence of Operations:
<enum>
<item>
During the server operations,
it will try to create temporary files in the print queue spool directory.
If this is not desirable,
it will create them in the <tt>server_tmp_dir</tt> directory.
<item>
If the accounting file specified by
<tt>af</tt>
exists,
it is opened (af_fd) and the af_fd is passed as file descriptor
3 to all filters.
If the <tt>af</tt> value has the form <tt>af=|/program</tt>
then the program is started and the program STDIN is used as af_fd.
If the <tt>af</tt> value has the form <tt>af=host%port</tt>,
then a TCP/IP connection to the corresponding port on the remote host
is made and the port used as af_fd.
In the latter two cases,  the filter STDIN (file descriptor 0)
is actualy opened read/write, and is used when information is needed
from the accounting filter or remote server.
See
<ref id="accountingserver" name="Accounting Printcap Options">
for more information on the LPRng accounting support.
<item>
If the
<tt>connect_grace</tt>
value is non-zero and the server is opening a device or
<tt>network_connect_grace</tt> is non-zero and a network connection
is being made,
the server will pause the specified time.
This is to accommodate devices which need a recovery time between jobs.
<item>
The <tt>lp</tt> option is checked to determine the type of IO device.
<table>
<tabular ca="|l|l|">
Format
|
Meaning
@
<tt>/pathname</tt>
|
Absolute pathname of IO device
@
<tt>pr@host</tt>
|
transfer to <tt>pr</tt> on remote <tt>host</tt>
@
<tt>host%port</tt>
|
open a TCP/IP connection to port on host.  host can be name or IP address
@
<tt>|filter</tt>
|
run the filter program; it STDIN will be used as device
@
</tabular>
</table>
<item>
The IO device specified by
<tt>lp</tt> is opened write-only or read-write if the
<tt>rw</tt>
flag is true, and the resulting file descriptor is io_fd.
If the <tt>nb</tt> flag is set,
a non-blocking open will be done as well.
If the <tt>lk</tt> (lock device) flag is true,
the device will be locked against use by other LPD servers.
<item>
If a <tt>host%port</tt> combination,
a TCP/IP connection will be opened to the remote port and the connection will
be used as io_fd.
<item>
If a filter program is specified,
the filter program will be run and the STDIN of the filter will be
used as the device file descriptor.
<item>
If a <tt>rp@rm</tt> combination,
or none of the above combinations are true and the
<tt>rm</tt> and <tt>rp</tt> values are non-zero,
then the job will be transferred to a remote printer.
The type of operation will be a job transfer,
rather than printing operation.
<item>
If the <tt>connect_timeout</tt> value is non-zero,
a timeout is setup for the device or socket open.
If the device or connection open does not succeed within the timeout,
then the open operation fails.
<item>
If a connection is to a network address
(i.e. - <tt>connect()</tt> system call)
and the connection attempt fails with an <tt>ECONNREFUSED</tt>
error,
if the <tt>retry_econnrefused</tt>
flag is set then the connection attempt is retried,
but this time using an alternative port number.
See
<ref id="rfc1179ref" name="RFC1179"> for details.
This is repeated until all of the possible originating port numbers
are exhausted.
<item>
If the open or connect operation fails,
and the <tt>retry_nolink</tt> flag is set,
then the server will pause for a minimum of
<tt>connect_grace</tt> plus a multiple of
<tt>connect_interval</tt> seconds
based on the number of attempts
before retrying the open operation.
Note that the interval may increase as the number of attempts
increases.
<item>
If printing a job and the
<tt>of</tt> filter is specified,
it is created with its STDOUT (fd 1) attached to the io_fd.
Its stdin (of_fd) will be used in the steps listed below.
If there is no
<tt>of</tt> filter,
then the of_fd value will be the io_fd descriptor.
<item>
If transferring a job and the <tt>control_filter</tt> option is specified,
then the program specified by the <tt>control_filter</tt>
value will be run. It will have its STDIN set to the control file,
and its STDOUT output will be used as the new value of the control file
to transfer to the remote host.
See
<ref id="filtercmd" name="Filter Command Line Flags">
for details of options passed to the control filter,
and
<ref id="errorcodes" name="errorcodes"> for the exit codes of the filter.
<item>
<label id="accountstart">
If <tt>la</tt> (local accounting) is true and we are printing a job
or <tt>ar</tt> (remote accounting) is true and we are transferring a job,
the <tt>as</tt> value is examined.
If it is a filter (program) specification,
then the program is started with its STDIN attached to
<tt>/dev/null</tt> and STDOUT to the io_fd,
STDERR to the error file,
and file descriptor 3 to the accounting file descriptor af_fd.
The lpd program will wait until it terminates,
and examine the error code for action, as for the filters
(see
<ref id="errorcodes" name="errorcodes"> below).
If it is a string,
then it is interpreted, the escape sequences replaced with the appropriate
information,  and written to the accounting file.
<item>
If the <tt>achk</tt> (accounting check) flag is set, 
a line is read from the accounting filter af_fd file descriptor.
This line should be <tt>accept</tt>,
otherwise the job processing terminates with a JFAIL indication.
<item>
If the operation is a job transfer, the operation proceeds as outlined in
<ref id="rfc1179ref" name="RFC1179">,
and then the
<ref id="normalterm" name="Normal Termination"> operations are
carried out.
<item>
If the operation is a print operation
and the
<tt>ld</tt> (leader on open) value is provided,
the string
is translated (escapes removed)
and written to the of_fd file descriptor.
<item>
If the
<tt>fo</tt> (form feed on open) flag is true, then the
<tt>ff</tt> (form feed) string
is translated (escapes removed)
and written to the of_fd file descriptor.
</enum>
<label id="ab">
<label id="hl">
<label id="be">
<label id="bl">
<label id="bp">
<label id="bs">
<label id="sb">
<label id="sh">
<label id="of">
<label id="bannerprinting">
<sect2>Printing Banner At Beginning
<p>
Options used:
<itemize>
<item> <tt>ab</tt><em>&nbsp;&nbsp;Always print banner (default FALSE)</em>
<item> <tt>be=</tt><em>End banner generator program</em>
<item> <tt>bl=</tt><em>Short banner line format</em>
<item> <tt>bp=</tt><em>Banner generator program</em>
<item> <tt>bs=</tt><em>Start banner generator</em>
<item> <tt>hl</tt><em>&nbsp;&nbsp;Banner (header) Last</em>
<item> <tt>of=</tt><em>Banner and File Separator Filter</em>
<item> <tt>sb</tt><em>&nbsp;&nbsp;Short banner (default FALSE)</em>
<item> <tt>sh</tt><em>&nbsp;&nbsp;Suppress header (banners) (default FALSE)</em>
</itemize>
Sequence of Operations:
<enum>
<item>
If the
<tt>sh</tt> (suppress header) flag is true, no banner is
printed,
and the actions in this section are skipped.
<item>
If the <tt>hl</tt> flag is true, the banner is printed at the end
of the job,
and the actions in this section are skipped.
<item>
If the user does not supply a banner name,
(the <tt>L</tt> line in the control file)
and
<tt>ab</tt> (always print a banner) is false
(the default),
then no banner is printed.
If no name is supplied and
<tt>ab</tt> is true, then ANONYMOUS is used.
<item>
There are two types of banners - short and long.
If the
<tt>sb</tt> flag is set, then we send the
<tt>bl</tt>
(banner line) contents directly to the of_fd;
By default the
<tt>bl</tt> value is:
<tt>bl=$-'C:$-'n Job: $-'J Date: $-'t</tt>
(See
<ref id="filtercmd" name="Filter Command Line Flags">
for details.)
This will get translated to:
<newline>
<tt>papowell:A Job: file1 file2 Date: Thu Nov 27 23:02:04 PST 1997</tt>
<item>
If the
<tt>sb</tt> flag is clear,
we will generate a long banner using a program instead.
If
<tt>bs</tt> (start banner) program is specified, then it is used
to generate a banner,
otherwise if the
<tt>bp</tt> (banner) program is specified, then it is used
to generate a banner.
If no program is available, we skip the banner generation.
The banner generator program is started with the normal command line
flags
(see
<ref id="filtercmd" name="Filter Command Line Flags">),
with its STDOUT attached to the of_fd descriptor.
The short banner string described in the previous step is written
to the STDIN.
The banner printer is responsible for generating a banner
appropriate to the printing device.
<item>
The
<tt>ff</tt> (form feed) string
will be interpreted and sent to the of_fd.
</enum>
<label id="directread">
<label id="sendjobrwtimeout">
<label id="sendqueryrwtimeout">
<label id="sf">
<label id="format">
<sect2>Printing Job Files
<p>
Options used:
<itemize>
<item> <tt>Xf=</tt><em>Format Filter</em>
<item> <tt>direct_read</tt><em>&nbsp;&nbsp;Direct connection to file</em>
<item> <tt>if=</tt><em>Default F Format Filter</em>
<item> <tt>pr=</tt><em>pr formatting program</em>
<item> <tt>send_job_rw_timeout=</tt><em> print job read/write timeout </em>
<item> <tt>send_query_rw_timeout=</tt><em> status query operation read/write timeout </em>
<item> <tt>sf</tt><em>&nbsp;&nbsp;Suppress FF Print File Separators</em>
</itemize>
<p>
Sequence of Operations:
for each job in listed in the control file,
the following operations are done in turn.
<enum>
<item>
If there is an <tt>of</tt> filter present,
the suspend string <tt>\031\001</tt> is written to of_fd
and the no further action is taken until the of filter is suspended.
<item>
The control file line for the job is examined,
and the first letter of the data file specification is used as the format.
<item>
If the format is
<tt>p</tt>,
the job is first processed by the program specified by the
<tt>pr</tt>
program,
and the program output used as the print file.
<item>
If the format is
<tt>f</tt>,
<tt>l</tt>,
or
<tt>p</tt>
then the <tt>if</tt> filter is used,
otherwise the keyword
<tt>Xf</tt> is used.
Note that certain formats such as
<tt>p, a, l</tt>, may not be used as formats.
<item>
The
<tt>direct_read</tt>
flag determines how the print file is provided
to the filter.
Normally, the <tt>lpd</tt> writes the file to the filter process,
and can monitor the printing activity in this way.
However,
some filters require a direct connection in order to
do <tt>lseek</tt> or other operations on the file.
If the
<tt>direct_read</tt>
flag is true,
then the print file is opened and passed directly to the filter
process,
otherwise the <tt>lpd</tt> program will read the file
and write its contents to the filter.
<item>
The filter program is started with an appropriate set of command line options
(see
<ref id="filtercmd" name="Filter Command Line Flags">),
and with its STDOUT attached to the printing device (io_fd),
STDERR to the log file (<tt>lf</tt>),
and file descriptor 3 to the accounting fd af_fd.
If <tt>direct_read</tt> is false,
the file is then written to the STDIN of the filter.
This allows the server to monitor job progress.
<item>
When doing a read/write operation to a device or remote system,
a timeout can be specified.
When doing a print or job transfer operation,
the <tt>send_job_rw_timeout</tt> value is used.
When doing a status or query operation,
the <tt>send_query_rw_timeout</tt> value is used.
If a write or write operation does not complete within
the specified timeout seconds, then we have an error
condition and job processing or the query operation
is terminated with JFAIL status.
If the timeout value is 0, then no timeout is done.
<item>
<label id="errorcodes">
<tt>lpd</tt> will then wait for the filter to exit.
The exit status can be as follows:
<verb>
Key      Value   Meaning
JSUCC    0       Successful
JFAIL    1, 32   Failed - retry later
JABORT   2, 33   Abort - terminate queue processing
JREMOVE  3, 34   Failed - remove job
JHOLD    6, 37   Failed - hold this job
Other            Abort - terminate queue processing
</verb>
<item>
If the filter exit status was JSUCC (0), or no error indicated,
then processing will continue otherwise the job termination takes
(see
<ref id="termination" name="Abnormal Termination">).
<item>
If the <tt>of</tt> filter is present,
then it is reactivated with a <tt>kill -CONT</tt> signal.
<item>
If the <tt>sf</tt> (suppress FF print file separators ) is false,
then the
<tt>ff</tt> (form feed) string
will be interpreted and sent to the of_fd.
</enum>
<sect2>Printing Banner At End
<p>
Options used:
<itemize>
<item> <tt>hl</tt><em>&nbsp;&nbsp;Header (Banner) Last</em>
</itemize>
<p>
The actions taken in this step are identical to those for the
<ref id="bp" name="Printing Banner At Beginning">,
with the exception that the
<tt>be</tt> (end banner program) is used in the procedure
rather than the
<tt>bs</tt> (start banner program).
<label id="ae">
<label id="fq">
<label id="savewhendone">
<label id="tr">
<label id="normalterm">
<sect2>Normal Termination
<p>
Options used:
<itemize>
<item> <tt>fq</tt><em>&nbsp;&nbsp;Form Feed on Close</em>
<item> <tt>la</tt><em>&nbsp;&nbsp;Local Printer Accounting</em>
<item> <tt>tr=</tt><em>Trailer on Close</em>
<item> <tt>ae=</tt><em>Accounting at end</em>
<item> <tt>save_when_done</tt><em>&nbsp;&nbsp;Save when done</em>
</itemize>
<p>
Sequence of Operations:
<enum>
<item>
If we are printing and the <tt>fq</tt> flag is set and the
<tt>sf</tt> (suppress interfile FF) flag is set,
then the
<tt>ff</tt> (form feed) string
will be interpreted and sent to the of_fd.
<item>
If we are printing, the <tt>tr</tt> (trailer) string
will be interpreted and sent to the of_fd.
<item>
If printing and the <tt>la</tt> (local printer accounting) flag is set
or transferring a job and the <tt>ar</tt> (remote accounting) flag is set,
the
<tt>ae</tt> is examined and accounting is done as described
for the
<tt><ref id="accountstart" name="as"> field.</tt>
<item>
If the <tt>of</tt> filter is present,
its STDIN is closed,
and the <tt>lpd</tt> server waits for it to exit.
The exit status is used as described above.
<item>
The device (io_fd) is closed.
<item>
The job is marked as completed in the spool queue.
<item>
If the <tt>save_when_done</tt> flag is not specified,
the job is removed.
</enum>
<label id="rt">
<label id="saveonerror">
<label id="sendtry">
<label id="sendfailureaction">
<label id="mailfrom">
<label id="mailoperatoronerror">
<label id="sendmail">
<label id="stoponabort">
<label id="maxconnectinterval">
<label id="termination">
<sect2>Abnormal Termination
<p>
Options used:
<itemize>
<item> <tt>mail_from=</tt><em>Mail from user name</em>
<item> <tt>mail_operator_on_error=</tt><em>Mail to operator on error</em>
<item> <tt>rt#</tt><em>&nbsp;&nbsp;Maximum Print or Transfer Attempts</em>
<item> <tt>send_try#</tt><em>&nbsp;&nbsp;(alias for <tt>rt</tt></em>
<item> <tt>save_on_error</tt><em>&nbsp;&nbsp;Do not delete on error</em>
<item> <tt>send_failure_action=</tt><em>Action on Failure</em>
<item> <tt>sendmail=</tt><em>sendmail path name and options</em>
<item> <tt>stop_on_abort</tt><em>&nbsp;&nbsp;Stop processing queue on filter abort</em>
</itemize>
<p>
If the job processing terminates abnormally,
the following sequence of events occurs:
<enum>
<item>
The job is marked as having an error during processing.
<item>
The LPD server will attempt to kill all filters and other associated process
by using a sequence of
<tt>kill -INT</tt>,
<tt>kill -QUIT</tt>,
and finally
<tt>kill -KILL</tt> operations.
<item>
If there is a <tt>mail_operator_on_error</tt> value,
the specified operator will be mailed an error indication.
The <tt>sendmail</tt> option specifies the pathname of the
<em>sendmail</em> program and the options needed to have it read
mail addresses from its standard input.
For example, <tt>sendmail=/usr/sbin/sendmail -oi -t</tt>
is a commonly used set of options.
<item>
The <tt>mail_from</tt> value specifies the user name used for
mail origination.  If not specified, the default is to use the print spool
queue or printer name.
<item>
If there is a <tt>send_failure_action</tt> specified,
then it is decoded and the corresponding action taken.
If the value is
<tt>remove</tt>,
<tt>hold</tt>,
<tt>abort</tt>,
or
<tt>retry</tt>,
then the job is removed, held, aborted, or retried.
If the value is <tt>|/program</tt>,
the program is executed and
the number of attempts are written to the filter STDIN.
The exit status of the filter will be used to determine the consequent actions.
That is, JSUCC (0) will be success, and the standard success action will
be taken;
JFAIL will cause retry,
JREMOVE will cause the job to be removed,
JHOLD will cause the job to be held,
JABORT or other status will abort processing.
<item>
If the status is ABORT and the
<tt>stop_on_abort</tt>
flag is set,
then further processing of jobs is terminated.
The job is not removed from the queue.
<item>
If the error status indicates removal,
and the <tt>save_on_error</tt> flag is clear
then the job is removed from the spool queue.
<item>
If the error status indicates that no further operations should
be performed on the queue,
then the <tt>lpd</tt> server will stop processing jobs.
<item>
If the error code indicated that the job should be retried,
and the
<tt>rt</tt> value is 0 or the number of attempts is less than
the <tt>rt</tt> value,
then the job is retried.
Between each attempt to transfer a job to a remote site.
This pause will double after each attempt,
reaching a maximum of <tt>max_connect_interval</tt> seconds.
If <tt>max_connect_interval</tt> is 0, there is no limit on the interval value.
</enum>
<label id="lpdforcepoll">
<label id="lpdpolltime">
<label id="maxserversactive">
<sect2>LPD Spool Queue Processing
<p>
Options used:
<itemize>
<item> <tt>lpd_force_poll=</tt><em>Force LPD to periodically poll print queues </em>
<item> <tt>lpd_poll_time#</tt><em>Time between polls</em>
<item> <tt>max_servers_active#</tt><em>Maximum number of active servers</em>
</itemize>
<p>
When the <tt>lpd</tt> server starts,
it will fork a set of subserver processes,
each which will handle an individual queue.
<p>
If a system has a large number of queues,
then this forking operation may result in the <tt>lpd</tt> server
exhausting the process resources.
To control this,  the
<tt>max_servers_active</tt> value restricts the number of active
children to the specified value.
If this value is 0,
then 50% of the maximum system processes value will be used.
<p>
Due to the limits on the number of processes,
there may be times when a job is placed in a queue,
but the <tt>lpd</tt> server is unable to start handling the job.
When all of the children of the main <tt>lpd</tt> server have
exited,
the server starts a timer.
After <tt>lpd_poll_time</tt> seconds,  it will scan the queues,
looking for jobs to process,
and starts a process to service them.
If it does not find any jobs it remains idle.
<p>
The <tt>lpd_force_poll</tt> flag causes the server to periodically
poll the queues.
This is useful when there is a high possibility that jobs could fail to be
printed due to high loads on the server.
<label id="bkfilteroptions">
<label id="bkoffilteroptions">
<label id="bkf">
<label id="filteroptions">
<label id="offilteroptions">
<label id="filterldpath">
<label id="filterpath">
<label id="passenv">
<label id="pl">
<label id="pw">
<label id="px">
<label id="py">
<label id="filtercmd">
<sect1>Filter Command Line Flags
<p>
Options used:
<itemize>
<item> <tt>bk_filter_options=</tt><em>Backwards Compatible Filter options</em>
<item> <tt>bk_of_filter_options=</tt><em>Backwards Compatible OF Filter options</em>
<item> <tt>bkf</tt><em>&nbsp;&nbsp;Backwards Compatible Filters</em>
<item> <tt>filter_ld_path=</tt><em>Filter LD_LIBRARY_PATH environment</em>
<item> <tt>filter_options=</tt><em>Filter options</em>
<item> <tt>filter_path=</tt><em>Filter PATH environment</em>
<item> <tt>of_filter_options=</tt><em>OF Filter options</em>
<item> <tt>pass_env=</tt><em>Environment variables to copy to Filter environment</em>
<item> <tt>pl#</tt><em>line count for page</em>
<item> <tt>pw#</tt><em>column count for page</em>
<item> <tt>px#</tt><em>pixel width for page</em>
<item> <tt>py#</tt><em>pixel length for page</em>
</itemize>
<p>
A filter (or program) specification in the LPRng printcap database
usually has the form:
<verb>
:option=| [flags] /path [arguments]
:option=[flags] /path [arguments]
</verb>
<p>
The first case is used where the option value can be a string or filter,
and the second where a program is always expected.
The following procedure is used to run a filter program.
<p>
The sequence of operations to run a filter is as follows:
<enum>
<item>
The program must be specified with an absolute path name.
<item>
By default, the program is run as the user if invoked from a client
program such as <tt>lpr</tt>, <tt>lpc</tt>, etc.
If invoked from <tt>lpd</tt>,  it is run as the
<tt>server_user</tt>
user
(default <tt>daemon</tt>) configuration entry.
<item>
The
<em>flags</em> control how the program is to be run.
The following flags are supported:
<itemize>
<item><bf>ROOT</bf>
This opens a horrible security loophole,
as it will run the program as ROOT.
To enable this option,
you must set various compilation flags,
and perform other arcane operations.
This is deliberately done to make administrators
read the warnings and admonitions.
<p>
The alternative to ROOT is to have a setuid ROOT executable.
Under NO circumstances should you run a shell script setuid ROOT,
with general execute permissions on it.
<item><bf>-$</bf>
This very odd looking flag is used to suppress the addition of
additional command line arguments
specified by the value of <tt>filter_options</tt>
to the program command line.
</itemize>
<item>If the <bf>-$</bf> flag is not specified,
the arguments determined by the value of the <tt>bkf</tt>
(Berkeley LPD filter compatible flag) flag are added to the
filter command line.
If <tt>bkf</tt> is false the
<tt>filter_options</tt> are added for OF filters and
<tt>of_filter_options</tt>
are added for non-OF filters;
if it is true, then the
<tt>bk_filter_options</tt> and <tt>bk_of_filter_options</tt> are added for
OF and non-OF filters respectively.
<p>
<table>
<tabular ca="|l|l|">
Option
|
DefaultValue
@
<tt>filter&lowbar;options</tt>
|
$C $F $H $J $L $P $Q $R $Z $a $c $d $e $f $h $i $j $k $l $n $p
$r $s $w $x $y $-a
@
<tt>of&lowbar;filter&lowbar;options</tt>
|
(same as <tt>filter&lowbar;options</tt>)
@
<tt>bk&lowbar;filter&lowbar;options</tt>
|
$P $w $l $x $y $F $c $L $i $J $C $0n $0h $-a
@
<tt>bk&lowbar;of&lowbar;filter&lowbar;options</tt>
|
$w $l $x $y
@
</tabular>
</table>
<p>
<item>
By default,
for programs that are not being invoked as print job file filters,
the
<tt>filter_options</tt>
arguments are added.
For print job filters, if the <tt>bkf</tt> flag is set,
then the
<tt>bk_filter_options</tt>
and
<tt>bk_of_filter_options</tt>
entries are used.
The default <tt>bk</tt> filter options are the same as originally used
with the BSD LPR filters.
For the <tt>of</tt> filter,
either the <tt>of_filter_options</tt>
or <tt>bk_of_filter_options</tt> arguments will be added.
<item>
The program arguments will then be scanned and interpreted.
Arguments of the form <tt>$</tt><em>letter</em> will be
translated into values from the
print job control file and/or printcap entry.
The letters have the following meaning:
<table>
<tabular ca="|l|l|">
Letter
|
TranslatedValue
@
<tt>a </tt>
|
printcap <tt>af</tt> (accounting file name)
@
<tt>b </tt>
|
job size (in K bytes)
@
<tt>c </tt>
|
binary file (<tt>l</tt> format for print file)
@
<tt>d </tt>
|
printcap <tt>cd</tt> or <tt>sd</tt> entry
@
<tt>e </tt>
|
print job data file name (currently being processed)
@
<tt>f </tt>
|
print job original name when spooled for printing (N info from control file)
@
<tt>h </tt>
|
print job originating host (H info from control file)
@
<tt>i </tt>
|
indent request (I info from control file)
@
<tt>j </tt>
|
job number in spool queue
@
<tt>k </tt>
|
print job control file name
@
<tt>l </tt>
|
printcap <tt>pl</tt> (page length)
@
<tt>m </tt>
|
printcap <tt>co</tt>
@
<tt>n </tt>
|
user name (L info from control file)
@
<tt>p </tt>
|
remote printer (when processing for bounce queue)
@
<tt>r </tt>
|
remote host (when processing for bounce queue)
@
<tt>s </tt>
|
printcap <tt>sf</tt> (status file)
@
<tt>t </tt>
|
time in common UNIX format
@
<tt>w </tt>
|
printcap <tt>pw</tt> (page width)
@
<tt>x </tt>
|
printcap <tt>px</tt> (page x dimension)
@
<tt>y </tt>
|
printcap <tt>py</tt> (page y dimension)
@
<tt>F </tt>
|
print file format
@
<tt>P </tt>
|
printer name
@
<tt>S </tt>
|
printcap <tt>cm</tt> (comment field)
@
Capital letter
|
Corresponding line from control file
@
{key}
|
printcap value for <tt>key</tt>
@
</tabular>
</table>
<item>
If there is no value for the specified argument,
then the argument is removed from the list.
If there is a value, the actual form of the substitution is
controlled by additional flags as follows.
<table>
<tabular ca="|l|l|">
Form
|
TranslatedValue
@
<tt> $x </tt>
|
<tt>'-x<em>value</em>' </tt>
@
<tt> $-x </tt>
|
<tt> '<em>value</em>' </tt>
@
<tt> $0x </tt>
|
<tt> -x '<em>value</em>' </tt>
@
<tt> $'x </tt>
|
<tt> -x <em>value</em> </tt>
@
</tabular>
</table>
<p>
Each entry in quotes is treated as a single value,
as in /bin/sh.
The <tt>$'x</tt> does not quote the value.
Combinations of the various flags are allowed.  For example,
<tt>$-x</tt> would simply substitute the value for <tt>x</tt>,
and then pass the whitespace separated components as individual arguments.
This last form is useful for adding in additional flags on the command line.
<item>
The command line is parsed,
metacharacters are ruthlessly stripped from all arguments and pathnames
and replaced by <tt>_</tt> (underscores),
and an argument list suitable for the <tt>execve</tt> system call
is formed.
<item>
A sanitized environment is set up for the program execution,
with the following environment variables.
<p>
<table>
<tabular ca="|l|l|">
<tt> USER </tt>
|
User name (client only)
@
<tt> LOGNAME </tt>
|
L control file info
@
<tt> HOME </tt>
|
Home directory (client only)
@
<tt> LOGDIR </tt>
|
Home directory (client only)
@
<tt> PATH </tt>
|
<tt>filter&lowbar;path</tt> configuration information
@
<tt> LD&lowbar;LIBRARY&lowbar;PATH </tt>
|
<tt> filter&lowbar;ld&lowbar;path </tt> configuration information
@
<tt> SHELL </tt>
|
<tt>/bin/sh</tt>
@
<tt> IFS </tt>
|
<tt>" \t"</tt>
@
<tt> TZ </tt>
|
Time zone
@
<tt> SPOOL&lowbar;DIR </tt>
|
<tt>sd</tt> printcap info
@
<tt> CONTROL&lowbar;DIR </tt>
|
<tt>cd</tt> printcap info
@
<tt> PRINTCAP&lowbar;ENTRY </tt>
|
printcap info
@
<tt> CONTROL </tt>
|
control file
@
</tabular>
</table>
<p>
<item>
If the filter is to be run by a client program such as <tt>lpr</tt>,
then the environment variables specified by the
<tt>pass_env</tt> configuration or printcap option will be
extracted from the environment,
have any metacharacters removed,
and then placed in the environment variable list.
Commonly, the
<tt>PGPPASS</tt> and <tt>PGPPATH</tt> are specified.
<item>
The program is started,
with STDIN, STDOUT, and STDERR attached to the appropriate files or
file descriptors.
If none is specified, then they are attached to
<tt>/dev/null</tt>.
</enum>
<sect1>Bounce queues and filters: caveats
<p>
There are a few situations in which a filter of a bounce queue will
behave differently from an ordinary queue.
<sect1>The lpr -p  format and :pr filter
<p>
The <tt>-p</tt> format doesn't behave as expected. Instead of running
<tt>pr | if</tt>, the daemon will try to call the <tt>:pf</tt> filter.
<p>
After filtering, the file might be of a different type than before.
Since the result is transfered to another print service (which might
do its own filtering), it is important that the right file type (by
means of the print format) is passed on to the second queue.
<p>
Use the
<tt>
<ref id="translateformat" name="translate_format">
=oNoN...
</tt>
printcap option.
Its value takes the form of old/new pairs of formats. For example:
<verb>
translate_format=pf
</verb>
<p>
The <tt>-p</tt> format file will now be renamed with the
<tt>f</tt> format.
<sect1>LPRng Supported Filters
<p>
There already exists a large library of ready-to-use filters. Some of
them have LPRng-specific versions, which can be found at the
<ref id="secftp" name="LPRng ftp mirror sites">.
<sect2>Filter Distribution Conventions
<p>
By convention,
most filters are either totally standalone (very rare),
or require a set of support files.
There are two types of support files: per print queue configuration information
and global support information.
<p>
Since a print filter will execute with the current directory set to the
spool queue directory,
most filters expect that per print queue configuration information
should be kept in the spool directory.
Most <em>vintage</em> filters insist on having these files <em>hidden</em>
with names such as <bf><tt>.setup</tt></bf>.
This can make it difficult for administrators to determine where the
configuration files are.
<p>
It is strongly recommended that filters and information
be placed in commonly accessible directories such as
<tt><bf>/usr/local/lib/filters</bf></tt>,
and the executables in subdirectories.
This allows the LPRng administrator to set the privileges on these
directories such that only the <tt>lpd</tt> process can
access them.
<p>
Most of the LPRng supported filters can either be used as a
<tt>if</tt> or <tt>of</tt> filter.
The filter will examine the format type passed by the <tt>-F<em>X</em></tt>
command line argument,
and if it is <tt>o</tt> it will perform as an <tt>of</tt> filter.
<p>
Alternatively,
the filter will check the filename in the pathname by which is was invoked.
If the name has the substring <tt>of</tt> in the filename,
then it assumes it is to act as an <tt>of</tt> filter.
This allows symbolic links to be made to a common filter executable,
each of which corresponds to the filter name by which it is to be invoked.
<p>
When a filter is invoked,
it is passed a large number of options,
many of which are totally ignored in filter operation.
However,
for many purposes it is necessary to provide options to the
filters to tailor their operation to the particular spool queue needs.
<p>
By convention,
all LPRng supported filters use the
<verb>
-Tkey=value[,key=value]
</verb>
<p>
convention for specifying filter configuration option values.
<label id="lpf">
<sect2>lpf
<p>
Source code:
<ref id="secftp" name="LPRng Distribution">
<p>
This filter is distributed as part of the LPRng source code,
and has a very limited functionality.
By default,
it only translates <tt>\n</tt> to <tt>\r\n</tt>
sequences,
and detects the OF Filter Stop sequence when invoked as an OF filter.
<itemize>
<item>Options:
<newline><tt>-Tcrlf</tt> - suppress <tt>\n</tt> to <tt>\r\n</tt> translation
</itemize>
<label id="ifhp">
<sect1>CTI-ifhp ifhp Filters
<p>
Source code:
<ref id="secftp" name="LPRng Distribution, CTI-ifhp-<em>version</em>.tgz">
<p>
This filter supports a wide variety of Hewlett-Packard printers,
or to be more specific,
printers which support the Hewlett-Packard PCL and/or PJL languages.
In addition,
they try to detect PostScript jobs and send the correct commands to the
printers to enable PostScript rather than PJL operation.
<p>
This filter was originally developed by
Panos Dimakopoulos, Systems Programmer,
of the CTI-Print project
at the Division of Computing Facilities of the Computer
Technology Institute (CTI), Patras, Greece.
The code has been heavily modified by Patrick Powell
<tt>&lt;papowell@astart.com></tt>
to support newer versions of HP Printers.
It is intended to replace the HPJetDirect drivers supplied by Hewlett-Packard.
<sect2>Printer Capabilities
<p>
As explained in
<ref id="installref" name="Setting Up Your Printer">,
you can have a parallel (unidirectional),
serial (bidirectional),
or network (bidirectional) connection.
When using a bidirectional connection,
you can sometime obtain or gratuitously receive error and/or status
information from the printer.
<p>
Some printers will spontaneously generate error messages when printing
a job on a bidirectional interface.
Usually, though,
it it necessary to force the printer to provide status in a reasonable format.
<p>
Some printers have the capability of printing either PCL or PostScript;
some require special setup commands and some will <em>autosense</em> which
type of job is being printed.
<p>
If you are printing text,
and not using a Page Description Language like PostScript or PCL,
then you may want to download a font to the printer.
This is especially the case when you are trying to print text files
in a non-English font.
<p>
Some printers will provide a <em>hardware</em> page counter value when requested;
however,
the means of requesting differ from model to model.
<p>
Sometimes you want to generate a special banner for a particular printer,
and need to put in some dynamic information.
While this can be done by the <tt>lpd</tt> server using the
<tt>bp</tt> program specification,
it turns out that non-LPRng systems which want to use the <tt>ifhp</tt>
want to have the same facilities.
Thus,  you need to have some way to get the same effect as the <tt>bp</tt>
option,  but at the filter level.
<p>
Having done <tt>lpd</tt> banner generation and printing,
why not have the filter run an accounting script as well?
<p>
At this point,  I suspect that the reader is beginning to suspect that
making a general purpose filter to support all of these possibilities is
difficult.
That is incorrect.  It is <bf>extremely</bf> difficult.
<sect2>hpif Options
<p>
These options are specified by the <tt>-Tkey=value [key=value]*</tt>
on the command line.
<p>
<table>
<tabular ca="|l|l|">
Option
|
Purpose
@
<tt>accounting=accounting&lowbar;script&lowbar;pathname</tt>
|
Invoke  the  accounting script with a subset of the
options passed to the  filter.   In  addition,  the
-bpagecount  option  indicates  the number of pages
printed for the job.
@
<tt>autodetect=[on|off*]</tt>
|
The printer has or does not have job  type  autode-
tect  capability.   Do not download fonts or try to
determine job type if autodetect is on.
@
<tt>banner=[on|off*]</tt>
|
If banner is on, then the ifhp filter will  attempt
to  print  a banner using information passed on the
command line or on the standard input.   The  title
option  can  be  used  to  specify additional title
information on the banner.  See BANNERS  below  for
details.
@
<tt>cartridge=[*on|off]</tt>
|
(Alias for postscript)
If cartridge is on, the printer has PostScript sup-
port.  The filter will try to determine if a job is
postscript  and  send Printer Job Language commands
to put the printer in PostScript mode.
@
<tt>debug=debuglevel</tt>
|
Sets the debugging  level;  2  is  the  default;  a
larger number causes more verbose error messages.
@
<tt>defaultfont=fontname</tt>
|
Sets  the default font to be downloaded; default is
NONE.
@
<tt>dev=/device or dev=host%port</tt>
|
Open the specified device or connection  to  remote
host; by default ifhp filter uses file descriptor 1
(stdout).  If the optional orig&lowbar;port is  specified,
connections  will  be  originated  from  this port.
Some printers require  that  connections  originate
from a port in the range 1-1024.
@
<tt>infostatus=[*on|off]</tt>
|
The PJL INFOSTATUS request is not supported on some
HP printers.  Use this to turn the  status  request
off.  Note that you cannot get real time reports of
the printer status if you do this.  This will  also
suppress  getting  pagecount  information using the
PJL facilities.
@
<tt>forcepagecount=[on|off*]</tt>
|
If you have a  printer  that  has  PostScript  page
count  information  support, you can set infostatus
to OFF and forcepagecount to ON.  This  will  cause
the  PostScript  facility  to  be used.  If you set
cartridge or postscript to OFF then this  will  not
be done.
@
<tt>logall</tt>
|
Save all of the error and information messages from
the printer in the log file.  This is  useful  when
you  wish  to  examine  returned  status  from  the
printer.
@
<tt>model=(C5M|III|IIID|IIISi|IV*)</tt>
|
The model of HP printer.  C5M is Color 5M,  III  is
HP  LaserJet 3, IIID is HP LaserJet 3D, etc.  Addi-
tional printers may be added or defined at  various
times  -  please  consult  the  source for details.
This selects various timing and format characteristic-
tics.   This  is  a desperation parameter for users
with antique or non-conforming PJL based equipment;
read  the source code for details on the particular
peculiarities.
@
<tt>pagecount=[on*|off]</tt>
|
Get the hardware pagecounter value for  accounting.
Some printers such as the HP LJ4s do not have hard-
ware support for  pagecounters,  and  return  bogus
numbers.   Use  this  to suppress attempting to get
valid information.  If your  printer  does  support
PostScript,  then  you can get the page count value
using PostScript by setting forcepagecount to ON.
@
<tt>plp=[on|off*]</tt>
|
Return PLP status values on exit; by default  LPRng
status values are returned.
@
<tt>postscript=[on*|off]</tt>
|
The printer has postscript support.
@
<tt>quiet=[on|off*]</tt>
|
If set, do not report common status messages.
@
<tt>retries=count</tt>
|
The  number  of  times  to  retry connecting to the
printer.
@
<tt>sleep=time</tt>
|
The number of seconds to wait before trying to con-
nect to the printer.
@
<tt>status=[*on|off]</tt>
|
When  on,  the  printer  is treated as a write only
device and is not queried for pagecount and  status
information.  Set status=OFF for parallel printers.
If status is OFF, then the ifhp filter simply  adds
job  control  language headers, fonts, and trailers
to the jobs.
@
<tt>stty=stty flags</tt>
|
if the output device is a serial line, set the line
characters  according  to  the  stty  flags.  These
flags are (most likely) identical to  those  avail-
able with the stty(1) command on the host system.
@
<tt>summary=[filename|host%port]</tt>
|
This option specifies that summary or informational
messages should be placed in the specified file  or
sent, using the UDP protocol, to the indicated host
and port address.  This allows remote monitoring of
the  printing and error activity.  The undocumented
program included with the filter distribution is  a
simple program that can be used to perform the mon-
itoring.
@
<tt>sync=[*on|off]</tt>
|
Try to  synchronize  communications  with  printer.
This  will  ensure that the printer has been reset,
and no problems involving  the  previous  job  will
result.
@
<tt>tbcp=[on|*off]</tt>
|
When  invoked  as  an  IF filter and transferring a
PostScript job,  the  filter  will  use  the  Adobe
Tagged Binary Communications protocol.  This allows
binary data to be transferred and  not  interpreted
as control information.
@
<tt>title=line[/line]*</tt>
|
The  title  information  is  printed  on the banner
page; it consists of a list of  /  separated  lines
which are added to the banner information.
@
<tt>wrap=[on|off*]</tt>
|
enables  or  disables  line  wrapping in PCL
mode.
@
</tabular>
</table>
<sect2>Parallel Port Printer
<p>
On a parallel port printer, you cannot get status, or do much besides
set up the printer to either handle PostScript or do autosensee.
The following is a typical printcap entry:
<verb>
pr:.... options
  :of=/usr/local/lib/filters/ifhp -Tstatus=off
  :if=/usr/local/lib/filters/ifhp -Tstatus=off
</verb>
<p>
You might want to also look at the <tt>autodetect</tt>
or <tt>postscript</tt> options.
<label id="ifhpbanner">
<sect2>Printing Banners
<p>
By default, the <tt>ifhp</tt> filter when used as an OF filter will
interpret the first line to it as a
<em>short banner</em> line,
and use the information on this line to produce a PCL based banner.
The short banner line should have the format:
<p>
<em>class</em>:<em>username</em> <tt>Job:</tt> <em>jobinfo</em> <tt>Date:</tt> <em>dateformat</em>
<p>
<verb>
Example:
  A:papowell Job: (stdin) Date: Sun Dec 14 07:13:34 PST 1997
</verb>
<p>
This is produced by the default
short banner line option value:
<verb>
bl=$-'C:$-'n Job: $-'J Date: $-'t
</verb>
<p>
If you want to suppress banner printing,
then you need to suppress generation of this short banner line.
If you want to have the <tt>lpd</tt> program to generate the
default <em>long</em> special banner,
then you need to suppress <tt>ifhp</tt> from interpreting the
information sent to is as banner information.
Finally, you may want to have <tt>lpd</tt> invoke the <tt>bp</tt>
(banner program) and have its output used as the banner.
Here are the various possible ways:
<p>
<verb>
# no banner at all, use :sh: - suppress headers
lp:....
  :sh
  :of=/usr/local/lib/filters/ifhp
# have ifhp generate banner from short banner input
lp:....
  :sb
  :of=/usr/local/lib/filters/ifhp
# have LPD generate long banner, have of filter pass it
lp:...
  :sb@
  :of=/usr/local/lib/filters/ifhp -Tbanner=off
# have LPD invoke bp banner generation program, have of filter pass it
# bp programs require short banner on STDIN to work, so we need to
# generate short banner
lp:...
  :sb
  :bp=/usr/local/lib/filters/banner_program
  :of=/usr/local/lib/filters/ifhp -Tbanner=off
</verb>
<p>
The <tt>ifhp</tt> banner is generated in PCL,
and uses the minimum PCL facilities.
Since when you send a banner to an autosensing printer you cause it to enter
the requested mode, the <tt>if</tt> filter (<tt>ifhp</tt>) will need to reset
the printer to autosense mode.  The <tt>ifhp</tt> filter automatically does this.
<p>
If you want very fancy banners,
the
<tt>banner.sh</tt> (PCL) and
<tt>psbanner.sh</tt> (PostScript) banner generating programs
in the CTI-ifhp distribution make a good starting point.
<label id="ifhperror">
<sect2>Error Logging
<p>
Error logging and reporting is done by the <tt>ifhp</tt>
filter as follows.
<enum>
<item>
Messages are produced by the actions of the <tt>ifhp</tt>
software.
This are logged to the STDERR output of the filter.
<item>
Messages are produced by status returned from the printer,
when the <tt>-Tstatus=on</tt> (default) option is enabled.
These are classified according to the Hewlett-Packard Printer Job Language
error status definitions,
and logged to the STDERR output of the filter.
<item>
In addition to error messages,
ongoing status messages are also produced.
If the printcap entry has a <tt>ps=</tt><em>statusfile</em> entry and the
<em>statusfile</em> exists and is writeable,
then the error and status messages will be written to the log file.
<item>
If the message concerns a serious matter or has been returned from the
printer as an 'ALERT' in it,
then the message can also be sent to a 'summaryfile'.
This file can be either a file
OR a UDP socket on a host. This is specified with the
<tt>-Tsummary=</tt><em>summaryfile</em> option. For example,
<verb>
	ifhp -Tsummary=taco%3000
</verb> would send messages to UDP port 3000
on host taco.
<item>
If you do not want the filter to report status on its STDERR output,
use the <tt>-Tquiet</tt> option to suppress this, or
compile it with the -DQUIET option.
</enum>
<label id="ifhpaccounting">
<sect2>Accounting Information
<p>
Doing printer accounting  is not simple.
Read
<ref id="accountingref" name="LPRng Accounting">
for more information.
<p>
In order to help aid in accounting,
by default the <tt>ifhp</tt> filter will query the printer
to get the current value of the <bf>hardware</bf> page counter value,
if there is such a thing on the printer.
Unfortunately,
due to different types of printers and errors in their PJL, PCL,
and PostScript implementations,
several different methods need to be used.
<enum>
<item>Only a printer with a bidirectional port will return status,
so you need to have a bidirectional connection.
<item>
If the printer is still printing a job,
then getting the value of the hardware page counter will be useless;
you need to wait until the printer is idle,
i.e. - synchronize your operations with the printer.
<p>
Unfortunately,
some printers return an <tt>idle</tt> indication even when
they are printing pages of the previous job.
This means that the printer has to be polled,
and only when it is idle <bf>and</bf> the pagecounter value has been
stable for a reasonable time (5 seconds?)
can you trust the page counter value.
This slows down job printing very seriously.
<p>
Some of the newer PJL printers have a
<tt>PJL TEOJ</tt>,
or return end of job indication when the last page of a job
has been printed.
If you have this capability,
you can speed up printing.
<item>
If your printer supports Hewlett-Packard Printer Job Language
<tt>PJL INFO PAGECOUNT</tt>
facility,
then it will first be tried to get the page count.
<item>
If your printer does not return pagecount information
using the PJL facility and it has PostScript support (default),
then a small PostScript job will be sent to the printer
requesting the <em>systemdict pagecounter</em> value.
Unfortunately,
different implementations and versions of PostScript will
need different programs.
The PostScript Printer Definition file for the printer will
have the correct script that is needed.
The default script that is used is:
<verb>
 /ps { print flush } def
 (\tPAGECOUNT ) ps
 statusdict begin pagecount end == flush
</verb>
<item>
To confuse matters totally,
some printers
which can do PostScript
interpretation do not support PJL <em>PAGECOUNT</em> reporting.
You can use the PostScript method to get the pagecount information,
but you cannot get status.
<item>
The pagecounter information is obtained at the start and end of processing
a job,
and is printed in the accounting file and also on File Descriptor 3
(if it is open).
This information has the format:
<p>
<verb>
  start -ppagecounter -Ff -kjob -uuser -hhost -R...
  end  -ppages -qpagecounter -Ff -kjob -uuser -hhost -R...
</verb>
<p>
When we use the OF filter and/or banners,  we will see the
individual jobs bracketed by the OF filter records:
<p>
<verb>
    start -p100 -Fo -kcfA100taco -uuser -hhost -R...
    start -p101 -Ff -kcfA100taco -uuser -hhost -R...
    end  -p1 -q102 -Ff -kcfA100taco -uuser -hhost -R...
    start -p102 -Ff -kcfA100taco -uuser -hhost -R...
</verb>
<p>
We can use the various job numbers and other information
to track page usage.
</enum>
<p>
The following are a selected set of printcap entries that can
be used to get page counting information:
<verb>
# use defaults, try to get pagecount using all methods, wait for stable
# value of pagecount before proceeding
pr:...
  :of=/usr/local/lib/filter/ifhp
# printer support PJL True End of Job and PAGECOUNT
pr:...
  :of=/usr/local/lib/filter/ifhp -Ttrue_eoj=on
# no PJL INFO status available, but you can get page count using postscript
pr:...
  :of=/usr/local/lib/filter/ifhp -Tinfostatus=off,forcepagecount
</verb>
<p>
You should try connecting to your printer directly
and testing the accounting facilities.
You can do this by using the <tt>ifhp -Tdev=...</tt>
facility.
For example:
<verb>
ifhp '-Tdev=/dev/ttyb,stty=38400 -echo -crmod -raw -oddp \
-evenp ixon pass8 -ixany cbreak' -Tdebug=5 &lt;ellipse.ps
ifhp -Tdev=astart14%9100 -Tdebug=5 -Ttrue_eoj &lt;ellipse.ps
</verb>
<label id="psfilter">
<sect1>psfilter PostScript Printer Filter
<p>
Source code:
<ref id="secftp" name="LPRng Distribution, psfilter-&lt;version>.tgz">
<p>
The
<tt>psfilter</tt> is similar to the CTI-ifhp
filter in its operation and functionality.
Its general operation and procedures are similar,
but it has the following additions:
<enum>
<item> It will translate text jobs to PostScript and print them
using PostScript unless the jobs are <em>literal</em> or binary formats.
<item> It will use the PostScript serial line status query support
to obtain status and other error information.
<item> When you are sending a PostScript file with <em>binary</em> information,
you need to either suppress status gathering,
or you need to use the
<em>Transparent Binary Communications Protocol</em>.
Note that older PostScript printers do not support this.
</enum>
<label id="psfilterprintcap">
<sect2>IF and OF Filter Support
<p>
During the installation process,
<tt> psif </tt> and
<tt> psof </tt> symbolic links are made to the
<tt> psfilter</tt> executable.
Thus,
the following printcap entries can be used:
<verb>
# common use:
lp:...
  :of=/usr/local/lib/filters/psof
  :if=/usr/local/lib/filters/psif
# same effect:
lp:...
  :of=/usr/local/lib/filters/psfilter
  :if=/usr/local/lib/filters/psfilter
</verb>
<label id="psfilteroptions">
<sect2>Options
<p>
The <tt>-Tkey=value[,key=value]</tt> convention is used to
pass options to the psfilter.
The following options are supported.
Many of these are identical in intent to CTI-ifhp options.
<table>
<tabular ca="|l|l|">
Option
|
Purpose
@
<tt>accounting=/pathname&lowbar;to&lowbar;executable</tt>
|
When  the  -Taccounting is specified, the specified
program is executed with the same  options  as  the
filter.
@
<tt>debug=level</tt>
|
set  debugging  level.   The  default  level  is 2;
higher values produce more verbose output.
@
<tt>dev=/device</tt>
|
dev=host%port
Open a connection to either the device or  to  the
port  on  the  host  and  send  output  to  it.  By
default, output is send to stdout.
@
<tt>endpause=delay</tt>
|
When used as the OF filter, at the start and end of
the  job  psfilter  will  query the printer for the
value of the hardware pagecounter.  The  difference
between the start and end values is reported as the
number of pages used for the  job.   Unfortunately,
some  printers  such  as  the  HP LaserJet 4 and HP
LaserJet 5 report the current  value  of  the  page
counter at the time of request, and it is difficult
to determine if the last page has  been  completely
printed  by  examining  returned PostScript status.
Before querying the printer at the end  of  a  job,
the  psfilter  software  will wait endpause seconds
(default is 5).  On a 12 page per  minute  printer,
this  appears  to  be  sufficient to allow the last
page to be completely  printed.   However,  if  the
printer jams on the last page, it will still not be
reported and the page count will be off by one.
@
<tt>forcepagecount=[off*|on]</tt>
|
To find the pagecount value, a  PostScript  program
is  sent  to the printer.  This overrides any other
flags and forces the program to be  sent.   Usually
used  in  combination  with -Tstatus=off -Tforceps,
and -Tnosync flags.  See  the  comments  below  for
printer specific problems.
@
<tt>forceps=[off*|on]</tt>
|
Forces  a  dummy  PostScript  job to be sent at the
start of operations.  This usually tricks autosense-
ing  printers  into  going into PostScript mode and
responding to the PostScript conventions.  See  the
comments below for printer specific problems.
@
<tt>maxresponse=seconds(default30seconds)</tt>
|
Specifies  how  long  to wait for a status response
before giving an error indication.
@
<tt>nosync=[off*|on]</tt>
|
Suppresses trying to get status from the PostScript
printer, as would normally be returned by sending a
^T.  See the comments below  for  printer  specific
problems.
@
<tt>pagecount=[off|on*]</tt>
|
Get  the pagecounter value from the printer and use
for accounting  information.   Note  that  some  HP
printers (LJ4s) do not appear to have pagecounters.
(Default is on).
@
<tt>reverse=[off*|on]</tt>
|
Reverse page order on output.
@
<tt>status=[off|on*]</tt>
|
Query the print device for status  and  page  count
information.   If status is off, then psfilter sim-
ply formats input into postscript and does  not  do
accounting.
@
<tt>stty=stty&lowbar;options</tt>
|
If the output device is a serial port, set the con-
figuration according to the  stty  options.   These
options  should  be  identical  to  those  used  by
stty(1).
@
<tt>summary=destination</tt>
|
Write a one line status summary to the destination.
The  destination can be a file (i.e. - /tmp/status)
or a UDP port on a host (host%port). The host  name
can   be  an  hostname  or  an  IP  address  (i.e.-
info.sdsu.edu%2000 or 130.191.163.56%1000).
@
<tt>tbcp=[off*|on]</tt>
|
Use the Adobe Tagged Binary Communications protocol
to  send the document when invoked as an IF filter.
The OF filter cannot handle binary PostScript docu-
ments  as it will remove various control sequences.
The -Ztbcp LPRng option will also enable TBCP oper-
ations.
@
<tt>udp&lowbar;status&lowbar;port=host%port</tt>
|
The  HP5m and possibly other printers use port 9101
(UDP) to request and send status.  When sent a  UDP
packet on this port, the printer responds by sending
status back to the sender.  This option forces  the
use of this port for sending status.  However, page
counts and other information still use the standard
connection to the printer.
@
</tabular>
</table>
<label id="psfilteraccounting">
<sect2>Accounting
<p>
The <tt>psfilter</tt> uses the same methods for doing accounting as the
<tt>CTI-ifhp</tt> filter.
See
<ref id="ifhpaccounting" name="IFHP Accounting"> for details.
<p>
Consult your printer's PostScript Printer Description file
to determine the PostScript script needed to do accounting.
You may need to modify the default one supplied in the <tt>psfilter</tt>
code.
<p>
Always test that the printer returns the right accounting information using
a test similar to the following:
<verb>
psfilter -Tdebug=8 '-Tdev=/dev/ttyb,stty=38400 -echo -crmod -raw -oddp \
-evenp ixon pass8 -ixany cbreak' &lt;ellipse.ps
psfilter -Tdebug=8 -Tdev=astart14%9100 -Ttrue_eoj &lt;ellipse.ps
</verb>
<label id="lppipe">
<sect1>lp_pipe Filters
<p>
Source code:
<ref id="secftp" name="LPRng Distribution, part of FILTERS_LPRng-&lt;version>.tgz">
<p>
The <tt>lp_pipe</tt> family of filters was developed to act
as a <em>network pipe</em> to network devices.
They are largely replaced by the <tt>lp=host%port</tt>, facility.
<itemize>
<item><tt>tcp-pipe</tt>: uses a tcp socket (<bf>OBSOLETED</bf> by
<tt>lp=host%port</tt>), but good starting point if you have special
device requirements;</item>
<item><tt>annex-pipe</tt>: supports annex terminal server.</item>
</itemize>
<label id="apsfilter">
<sect1>apsfilter Filter
<p>
Source code:
<ref id="secftp" name="LPRng Distribution, apsfilter-&lt;version>.tgz">
<p>
The <tt>apsfilter</tt>
is basically a simple front end to the <tt>a2ps</tt> program
(See:
<htmlurl url="http://wwwinf.enst.fr/~demaille/a2ps/" name="http://www-inf.enst.fr/~demaille/a2ps/"> for details),
and is an example of a <em>MagicFilter</em> that has powerful
processing capability.
The <tt>apsfilter</tt> program sets up options for the
<tt>a2ps</tt> program and then invokes it.
<p>
The <tt>a2ps</tt> program can convert just about any type of file
into PostScript,
and then by using the GhostScript facility can convert this to
the output compatible with a particular printer.
<p>
Combined with the LPRng <tt>qq</tt> and <tt>force_queuename</tt>
options, we can set up virtual queues that do various types of reformatting.
Here is a sample set of printcap entries:
<verb>
# seen by users - note that the queue name is put into control file,
#  and we then send it to the frontend@host queue for processing
raw:qq:lp=frontend@host
twoup:qq:lp=frontend@host
landscape:qq:lp=frontend@host
frontend:lp=frontend@host:force_queuename=raw
# frontend does the job conversions and accounting
frontend:server
  :lp=/dev/lp:force_queuename=raw
  :if=/usr/local/lib/filter/apsfilter
</verb>
<sect1>Using your own filters
<p>
If you already have a working setup, with its own specific filter
programs, you might want to keep them. Or, you might want to write a
set of your own.
<p>
See the source code in the
<ref id="secftp" name="LPRng Distribution, FILTERS_LPRng-&lt;version>.tgz"> files for examples.
<label id="sd">
<label id="cd">
<sect>Spool Queues and Files
<p>
When files are accepted by the <tt>lpd</tt> server for printing,
they are stored in a spool queue directory,
together with other files controlling the print operation.
This section describes these files and how the LPRng software uses them.
<p>
For descriptive purposes,
we will use the following printcap entry as a guide:
<p>
<verb>
pr|alias
  :sd=/var/lpd/pr_public
  :cd=/var/lpd/pr
</verb>
<sect1>Spool Queue and Control Queue
<p>
<itemize>
<item> <tt>sd=</tt><em>Spool queue directory name</em>
<item> <tt>cd=</tt><em>Control queue directory name</em>
</itemize>
<p>
The
<tt>sd</tt>
option in the printcap entry specifies the spool queue
directory.
If there is no
<tt>sd</tt>
entry or value,
then the printer can only be used by the clients such as <tt>lpr</tt>
to locate the destination for a print job.
<p>
Normally,
all information,
files,
etc.,
for a print queue is stored in the spool directory.
However,
some software packages such as the PC-NFS spooling package from
Sun Microsystems originally required read/write access to the directory
in order to create print jobs on behalf of the user.
To prevent unauthorized or accidental tampering with LPRng operations,
the <tt>cd</tt> (control directory) entry specifies that only the
print job files should be placed in the spool queue,
and that all the control and informational files should be in the
control directory.
<p>
By default,
the <tt>cd</tt> value will be the same as the <tt>sd</tt>
value unless explicitly overridden in the printcap entry.
<sect1>Job State
<p>
<label id="ah">
<p>
Options used:
<itemize>
<item> <tt>ah</tt><em>&nbsp;&nbsp;Automatically hold jobs</em>
</itemize>
<p>
A job can be in the following state:
<enum>
<item>Initial.
This is the state during job submission.
Jobs in the initial state do not have any status displayed for them.
<item>Held.
Once a job is submitted,
it can either be printed or <em>held</em>.
The <tt>ah</tt> printcap option specifies that all jobs are
automatically held on submission.
The
<tt>lpc release</tt>
and
<tt>lpc redo</tt>
command will cause these jobs to be printed and
the <tt>lprm</tt> command can remove these jobs.
<item>
Active.
The job is being processed for printing or transfer to another queue.
<item>
Pending.
Jobs which can be printed but are not active.
<item>
Error.
Jobs which have encountered an error during printing.
The
<tt>lpc release</tt>
and
<tt>lpc redo</tt>
command will cause these jobs to be printed and
the <tt>lprm</tt> command can remove these jobs.
<item>
Done.
Jobs which have completed printing,
but which are not yet removed from the print queue.
See the
<tt><ref id="savewhendone" name="save_when_done"></tt>
flag for more information.
The <tt>lprm</tt> command can remove these jobs.
</enum>
<p>
Normally the job sequences is initial, pending, active, and done.
However, a job may be put in the error state by problems processing the job
or by actions of the <tt>lpc</tt> command.
<sect1>Printer Lock File
<p>
When the <tt>lpd</tt> server starts printing,
it will fork individual worker processes to service each queue.
To prevent multiple processes from working on the same queue,
a printer lock file with the cannonical spool queue name is used.
In our example,
the lock file would be:
<tt>/var/lpd/pr/pr</tt>.
<p>
The process ID of the currently active printer is stored in the lock file.
By reading the lock file and testing to see if the process is still active,
programs such as <tt>lpq</tt> can determine queue activity.
<p>
Similarly,
the worker process may need to create other processes to assist it.
These in turn will create lock or temporary files in the spool directory
as well.
<sect1>Spool Control File
<p>
The spool control file is used to control the operations of the
spooler,
and is in the spool or control directory.
The file name has the form <tt>control.<em>printer</em></tt>;
in our example,
the control file would be:
<tt>/var/lpd/pr/control.pr</tt>.
<p>
The <tt>lpc</tt> program sends spool control requests to the
<tt>lpd</tt> daemon,
which updates the control file and then signals the appropriate
spool server processes that an update has been performed.
The control file contents have the form:
<verb>
key value
</verb>
<p>
The following keys and their values are currently supported.
<p>
<table>
<tabular ca="|l|l|l|">
Key
|
Values
|
Purpose
@
printing&lowbar;disabled
|
0 or 1
|
disable printing of jobs in queue
@
spooling&lowbar;disabled
|
0 or 1
|
disable placing jobs in queue
@
holdall
|
0 or 1
|
hold jobs until released
@
redirect
|
printer
|
transfer jobs to indicated printer
@
class
|
glob expression
|
print only jobs whose class matches glob expression
@
server&lowbar;order
|
printer name list
|
preferred order of printer use
@
debug
|
debugging options
|
debugging and tracing
@
</tabular>
</table>
<p>
The <tt>printing_disabled</tt> and <tt>spooling_disabled</tt>
are managed using the <tt>lpc start</tt>, <tt>lpc stop</tt>,
<tt>lpc enable</tt> and <tt>lpc disable</tt>
commands.
Similary,
<tt>holdall</tt> is enabled and disabled by <tt>holdall</tt> and
<tt>noholdall</tt>
commands respectively.
When holdall is enabled,
jobs placed in the print queue will be held until they are explicitly
released for printing by an <tt>lpc release</tt> command.
<p>
The <tt>redirect</tt> entry is used to redirect or transfer jobs
which are spooled to this queue to another queue,
and is managed by the redirect command.
The <tt>lpc redirect off</tt> removes the redirect entry from the
control file.
<p>
The <tt>class</tt> entry is similar in operation to the
<tt>holdall</tt>,
but allows jobs whose class identification matches the glob
expression to be printed.
This can be useful when you have special forms or paper required for a
print job,
and want to run only these jobs when the paper is in the printer.
<p>
The <tt>server_order</tt>
entry is created and updated for a multiple printer queue.
It records the order in which printers should next be used
for normal print operations.
This allows <em>round robin</em> use of printers,
rather than having all jobs printed to the first printer in the
list of printers.
<p>
The <tt>debug</tt>
entry is set by the <tt>lpc debug</tt> command,
and is used to enable or disable debugging and tracing information
for a spool queue.
This facility is for diagnostic purposes only.
<label id="lf">
<label id="ps">
<label id="maxstatusline">
<label id="maxstatussize">
<label id="minstatussize">
<label id="maxlogfilesize">
<label id="minlogfilesize">
<sect1>Log and Status Files
<p>
<itemize>
<item> <tt>lf=</tt><em>log file name (default: log)</em>
<item> <tt>max_log_file_size#</tt><em>&nbsp;&nbsp;maximum log file size (Kbytes)</em>
<item> <tt>min_log_file_size#</tt><em>&nbsp;&nbsp;minimum log file size (Kbytes)</em>
<item> <tt>max_status_line#</tt><em>&nbsp;&nbsp;maximum status line length (characters) </em>
<item> <tt>max_status_size#</tt><em>&nbsp;&nbsp;maximum status file size (Kbytes)</em>
<item> <tt>min_status_size#</tt><em>&nbsp;&nbsp;minimum status file size (Kbytes)</em>
<item> <tt>ps=</tt><em>filter status file name (default: status)</em>
</itemize>
<p>
During operation,
the <tt>lpd</tt> server records the current printing operations
in the <tt>status.<em>printer</em></tt> file.
For our example, this would be
<tt>/var/lpd/pr/status.pr</tt>.
In order to prevent this file from growing too large,
the server will periodically truncate the file.
The
<tt>max_status_size</tt> configuration or printcap option
sets the maximum size (in Kbytes) of the status file;
if the file exceeds this,  only the last
<tt>min_status_size</tt> bytes
or 25% of the maximum size (default if not specified)
will be preserved.
<p>
Similarly,
the server logs its operations in the log file specified by the
<tt>lf</tt> (log file) option (default is <tt>lf=log</tt>).
The <tt>max_log_file_size</tt>
value
(default 0)
specifies the maximum length
of the log file in Kbytes.
If this value is non-zero,
then the log file is truncated to
<tt>min_log_file_size</tt> bytes
or 25% of the maximum file size.
Again, the last portion of the log file is preserved.
If the <tt>max_log_file_size</tt> value is 0,
then the log file grows without limit.
<p>
Some filters require an additional filter status file
that they use for recording additional filter status or
other operational information.
The
<tt>ps</tt> names this file,
and it is passed to a print filter using the <tt>$s</tt>
option
(see
<ref id="filtercmd" name="Filter Command Line Flags">).
<p>
The log file (<tt>lf</tt>) is opened and used as the
STDERR output for filters and debugging information from
the <tt>lpd</tt> server.
<p>
When reporting status information,
the length of line returned can be a problem.
The <tt>max_status_line#79</tt> option restricts the status line
to a maximum of 79 characters.
<sect1>Job Control File
<p>
<itemize>
<item> <tt>longnumber</tt><em>&nbsp;&nbsp; long job number</em>
<item> <tt>default_priority=</tt><em>default job priority</em>
</itemize>
<p>
A print job consists of a control file and one or more data files.
<ref id="rfc1179" name="RFC1179">
specifies the general format of these files and how they are to be
transfered between servers.
LPRng has extended the contents of the control files and the transfer protocol
to provide a more powerful set of features,
but has extensive provisions for backwards compatibility with
non-LPRng software.
A sample control file is shown below:
<verb>
Hastart4.astart.com
J/tmp/file1 /tmp/file2
CA
Lpapowell
Ppapowell
fdfA002230astart4.astart.com
N/tmp/file1
UdfA002230astart4.astart.com
fdfB002230astart4.astart.com
N/tmp/file2
UdfB002230astart4.astart.com
</verb>
<p>
The first part of the control file contains general information generated
by the <tt>lpr</tt> or other spooling program.
The information lines start with an uppercase letter or digit.
Some other spooling systems also start information lines with
various punctuation marks such as underscores (_) or periods (.).
<p>
Following this are a set of entries about each of the various files to
be printed.
These lines start with a lower case letter,
followed by the print file name.
The lower case letter is the
<em>format</em> to be used to process the file.
See
<ref id="format" name="print file formats">
for more information about its use.
<p>
<table>
<tabular ca="|l|l|l|">
Key
|
Meaning
|
Generated By
@
A
|
identifier *
|
LPRng internal
@
C
|
class
|
lpr -C class
@
D
|
date
|
lpr
@
H
|
originating host
|
lpr
@
I
|
indent
|
lpr -i indent
@
J
|
jobname
|
lpr -J jobname (default: list of files)
@
L
|
bnrname
|
lpr -U username
@
N
|
filename
|
(see text)
@
M
|
mailname
|
lpr -m mailname
@
P
|
logname
|
lpr
@
Q
|
queuename
|
lpr -Q
@
R
|
accntname
|
lpr -R accntname
@
S
|
slinkdata *
|
lpr
@
T
|
prtitle
|
lpr -T prtitle
@
U
|
unlnkfile
|
(see text)
@
W
|
width
|
lpr -w width
@
Z
|
zopts *
|
lpr -Z zopts
@
1
|
font1
|
lpr -1 font1
@
2
|
font2
|
lpr -2 font2
@
3
|
font3
|
lpr -3 font3
@
4
|
font4
|
lpr -4 font4
@
</tabular>
</table>
<p>
The entries marked with * are used only by LPRng.
<tt>N</tt> and <tt>U</tt> lines
are associated with a print file.
The <tt>N</tt> line is the original name of the print
file.
The <tt>U</tt> line originally was used to indicate that the
named file was to be unlinked after printing.
This information is now ignored by LPRng.
These lines are always grouped with a print file entry.
<p>
The names of control and data files follow a very strict pattern.
Control files have the format <tt>cfX<em>number</em><bf>host</bf></tt>,
where X is an upper case letter,
<em>number</em> is (usually) a 3 digit number,
and <bf>host</bf> is the host name.
<ref id="rfc1179" name="RFC1179">
restricted the total length of the control file name to 32 characters;
LPRng has a much looser limit.
<p>
Data file names must follow the same pattern as the control file name,
and have the format
<tt>dfX<em>number</em><bf>host</bf></tt>.
The X can be in the range A-Za-z,
allowing at most 52 data files for a job.
The <em>number</em> and <bf>host</bf> must be identical to the corresponding
control file.
<p>
By convention,
LPRng uses the X of the control file name to set a priority for the
job.
A job with control file name
<tt>cfA...</tt>
will have <em>lower</em> format
than a job with format
<tt>cfB...</tt>,
and so forth.
The <tt>lpr</tt> program uses the first letter of the class name
or an explicit priority indication to set the letter value.
If none of these are specified, then the
<tt>default_priority</tt> value from the configuration or printcap
entry is used.
<p>
The job number is usually a 3 digit value.
However,
in systems where a large number of jobs are spooled and need to be
kept for printing at scheduled times,
this can lead to problems.
The
<tt>longnumber</tt>
option will use 6 digit job numbers.
This must be used with care when operating with non-LPRng software.
<sect1>Job Hold File
<p>
Associated with each control file is a
hold file that has additional information controlling the printing operations.
The entries in this file have the form:
<verb>
key [value]
</verb>
<p>
The following is an example of a hold file:
<verb>
server 0
subserver 0
attempt 3
error cannot open printer
hold 0
priority 0
remove 0
routed 0
</verb>
<p>
The
<tt>server</tt>
and
<tt>subserver</tt>
entry records the process ID of the server process
and the subserver process that is printing the job.
The
<tt>attempt</tt>
field records the total number of attempts to print the job.
The
<tt>error</tt>
field records any error that would prevent the job from being printed.
This information is reported by the <tt>lpq</tt> program.
<p>
The
<tt>hold</tt>
field is non-zero when the
<tt>lpc hold</tt>
command is used to
explicitly prevent the job from being printed;
<tt>lpc release</tt>
will clear the field and allow the job to be printed.
<p>
The
<tt>priority</tt>
field is modified by the
<tt>lpc topq</tt>
command and is used to provide an overriding priority to printing the file.
<p>
The
<tt>remove</tt>
field is non-zero when the file has been printed and should be removed.
<p>
The
<tt>routed</tt>
field is used to indicate that there is routing information present in
the hold file,
and that special handling is needed.
The routing information is provided by a
<ref id="routing" name="routing filter">.
The information is recorded by information in the hold file.
The following is an example of routing information:
<p>
<verb>
active 0
attempt 0
done 0
hold 0
priority 0
remove 0
routed 880892602
route dest t1
route ident papowell@astart4+705.1
route error 
route copies 1
route copy_done 0
route status 0
route active 0
route attempt 0
route done 0
route hold 0
route sequence 0
route priority B
route CB
route end 
route dest t1
route ident papowell@astart4+705.2
route error 
route copies 0
route copy_done 0
route status 0
route active 0
route attempt 0
route done 0
route hold 0
route sequence 1
route end 
</verb>
<p>
Routing information lines start with
<tt>route</tt> followed by individual routing entry information.
The <tt>route</tt> <tt>dest</tt>,
<tt>copies</tt>,
<tt>priority</tt>,
and
<tt>Xnnnn</tt>
entries are
derived from the output of the router program;
other fields are used during the printing process.
The <tt>copy_done</tt> records the numbers of copies done,
while the <tt>done</tt> records that the entry has been completed.
The <tt>status</tt> is the process ID of the server process
doing the printing.
<p>
The output from  route filter  that generated the above file was:
<verb>
dest t1
copies 1
priority B
CB
end
dest t1
end
</verb>
<label id="useidentifier">
<sect1>Job Identifier
<p>
Options:
<itemize>
<item> <tt>use_identifier</tt><em>&nbsp;&nbsp;put job identifier in control file</em>
</itemize>
<p>
For each job in a spool queue,
the LPRng software creates a unique identifier.
This identifier is recorded in the control file <tt>A</tt> line.
It can be used by the various client programs for identifying jobs,
and is displayed by the <tt>lpq</tt> program as status information.
<p>
<label id="configfile">
<label id="lpdconf">
<sect>/etc/lpd.conf Configuration File
<p>
The values in the LPRng configuration file
(default: <tt>/etc/lpd.conf</tt>)
specify values for global options
and default values for printcap options.
See the man pages for
<tt>lpd.conf(5)</tt> and
<tt>printcap(5)</tt>
for a complete list of configuration variables and their effects.
<sect1>Configuration File Format
<p>
The LPRng distribution contains a template <tt>lpd.conf</tt> file
which can be installed as
<tt>/etc/lpd.conf</tt>.
The configuration file has the following format:
<verb>
# Default version of the lpd.conf file
# ae=jobend $H $n $P $k $b $t
# allow_getenv
# ar
# architecture
# as=jobstart $H $n $P $k $b $t
# bk_filter_options=$P $w $l $x $y $F $c $L $i $J $C $0n $0h $-a
# bk_of_filter_options=$w $l $x $y
# bl=$-'C:$-'n Job: $-'J Date: $-'t
# check_for_nonprintable
  check_for_nonprintable@
# client_auth_command
...
</verb>
<p>
The file uses the same notation for
<ref id="printcapref" name="printcap">
entries,
but does not use the : (colon) separator.
A line starting with <tt>#</tt> is a comment.
<p>
To change the default value of an option,
remove the comment character and edit the entry.
In the above example,
the default value (1 or TRUE) for
<tt>check_for_nonprintable</tt>
has been changed to 0 or off.
<p>
To force the <tt>lpd</tt> server to use the new options,
use the <tt>lpc reread</tt> command.
<p>
As for printcap entries,
the <tt>%X</tt> combinations are interpreted when a
configuration entry is used for configuration or defaults,
and can be used to do site and host dependent customization.
This interpolation is done when the default or configuration value
is used in a printcap context.
See
<ref id="details" name="Missing Details"> for more
information.
<p>
<label id="allowgetenv">
<sect1>Obtaining Configuration Information
<p>
The location of the configuration file is compiled into the
LPRng software.
The <tt>config_file</tt> entry in the compilation defaults
is normally set to search for configuration information in the following
files:
<verb>
config_file=/etc/lpd.conf:/usr/etc/lpd.conf
</verb>
<p>
To change any of the <tt>config_file</tt> specifications,
the the <tt>LPRng/src/common/vars.c</tt>
file will need to be modified and the LPRng software recompiled.
<p>
In addition,
LPRng has a special <em>debug</em> mode.
When compiled with the <tt>-DGET_ENV</tt> option enabled,
this sets the value of the <tt>allow_getenv</tt> option to 1.
LPRng can then use the value of the
<tt>LPD_CONF</tt> environment variable instead of the compiled in
<tt>config_file</tt> value.
<bf> This is a possible security loophole,
and should not be used when running SETUID ROOT or as ROOT.</bf>
To enable this option,
see the <tt>TESTVERSION</tt> comments in the <tt> LPRng/src/Makefile</tt>.
<sect1>Useful Configuration Options
<p>
The following variables are used to set default behavior
for the LPRng software,
or are commonly used for configuration of LPRng operations.
<label id="defaultformat">
<sect2>default_format
<p>
Default format for printing.
Usually,
<tt>default_format=f</tt>,
but setting it to
<tt>default_format=l</tt> will cause all files spooled by lpr
to be spooled as binary files.
<label id="defaultpermission">
<sect2>default_permission=ACCEPT
<p>
The default permissions to use when checking for printing or other permissions.
<label id="defaultpriority">
<sect2>default_priority=A
<p>
The default priority for a print job.
<label id="defaultremotehost">
<sect2>default_remote_host=localhost
<p>
The default <tt>lpd</tt> server host.
<label id="defaulttmpdir">
<sect2>default_tmp_dir=/tmp
<p>
The default directory for temporary files.
<p>
This option can be used to specify a default
printer. If no value is given (default), the first printer in the
printcap file will be used when no printer is specified.
<sect2>domain_name=domain.name
<p>
You will only need to set this if LPRng can't determine your
hosts domain name itself.
This is usually a desperation option when DNS or some other
database system is not available.
<label id="lpdpermsref">
<sect>The /etc/lpd.perms Permissions File
<p>
This file is used to specify the restrictions on the use
of the LPRng software,
printers,
and other facilities.
The following is an example of a <tt>lpd.perms</tt> file.
<p>
<verb>
# allow root on server to control jobs
ACCEPT SERVICE=C SERVER REMOTEUSER=root
REJECT SERVICE=C
#
# allow same user on originating host to remove a job
ACCEPT SERVICE=M SAMEHOST SAMEUSER
# allow root on server to remove a job
ACCEPT SERVICE=M SERVER REMOTEUSER=root
REJECT SERVICE=M
# all other operations allowed
DEFAULT ACCEPT
</verb>
<p>
The structure of the lpd.perms file was inspired by network
packet filter configuration files.
When the LPD server gets a request from a remote client program,
it performs the checks specified by the rules in the
<tt>lpd.perms</tt>
to decide whether to accept or reject the request.
<p>
A rule will ACCEPT or REJECT a request
if all of the patterns specified in the rule match.
If there is a match failure,
the next rule in sequence will be applied.
If all of the rules are exhausted,
then the last specified default authorization will be used.
<p>
The sense of a pattern match can be inverted using the NOT keyword.
For example,
the rules with
<tt>ACCEPT NOT USER=john,bill</tt>
succeeds only if USER is defined and the USER value
is not <tt>john</tt> or <tt>bill</tt>.
<p>
The following patterns and matching are applied.
<p>
<table>
<tabular ca="|l|l|">
Keyword
|
Match
@
<tt>DEFAULT</tt>
|
default result
@
<tt>SERVICE</tt>
|
lpC Status and User, lpR, lprM, lpQ request
@
<tt>USER</tt>
|
user name in print job
@
<tt>REMOTEUSER</tt>
|
user making request
@
<tt>HOST</tt>
|
host name in print job
@
<tt>REMOTEHOST</tt>
|
host making request
@
<tt>IP</tt>
|
IP address and mask of host in print job
@
<tt>REMOTEIP</tt>
|
IP address and mask of host making request
@
<tt>PORT</tt>
|
TCP/IP port of host making request
@
<tt>SAMEUSER</tt>
|
USER and REMOTEUSER same
@
<tt>SAMEHOST</tt>
|
HOST and REMOTEHOST same
@
<tt>SERVER</tt>
|
request originates on lpd server
@
<tt>FORWARD</tt>
|
destination of job is not host
@
<tt>GROUP</tt>
|
USER is in the particular group
@
<tt>REMOTEGROUP</tt>
|
REMOTEUSER is in the particular group
@
<tt>CONTROLLINE</tt>
|
match a line in control file
@
<tt>AUTH</tt>
|
authentication type
@
<tt>AUTHUSER</tt>
|
authenticated user
@
<tt>FWDUSER</tt>
|
authenticated forwarder
@
</tabular>
</table>
<p>
Most of the patterns can be lists of alternative values to match,
and can even contain wild cards.
The full details of the rules and keywords are detailed in
the <tt>lpd.conf(5)</tt> man page.
<sect1>Information for matching
<p>
In order to do matching,
the <tt>lpd</tt> server obtains and sets up the following information:
<enum>
<item>
If the request is coming over a network connection,
then the IP address (REMOTEIP)
port (PORT) of the source of the connection and
FQDN of the remote host (REMOTEHOST) are obtained
and the indicated values are set.
To be specific,
the IP address of the remote host is obtained using
<tt>getpeername()</tt>.
The <tt>gethostbyaddr()</tt> is used to look up the
host's fully qualified domain name,
which is then assigned to the REMOTEHOST value.
The REMOTEIP value is the
<em>set</em> or <em>list</em> of IP addresses that could be
used by this host.
This is possible in the IPV6 environment.
<item>
If the request contains the name of the user,
then REMOTEUSER is assigned the name.
<item>
If the request contains the name of the printer,
then PRINTER is assigned the name.
<item>
If a print job is being printed,
then the USER, HOST, IP, and PRINTER are set to the
user name, host, and printer information in the control file for the
print job.
To be specific,
the HOST entry in the control file is used by
<tt>gethostbyname()</tt> to get the fully qualified domain name of the host.
The IP value is assigned a 
<em>set</em> or <em>list</em> of IP addresses that could be
used by this host.
<item>
If one of the optional authentication methods is being used,
(see
<ref id="authref" name="Authentication and Encryption">),
then AUTH is set to NONE,
USER,
or FWD,
depending on the type of operation and authentication present.
AUTHUSER to the authenticated originating user of the request
and FWDUSER is set to the forwarding servers authentication information
(if any).
</enum>
<sect1>Permission Checks
<p>
When a connection is made to the <tt>lpd</tt> server,
the originating site's IP address and hostname are determined,
and a check with <tt>SERVICE=X</tt> is made.
The REMOTEHOST,
REMOTEIP,
and PORT will be defined for the purposes of this check.
<p>
If the result is to accept the connection,
then the request is then read from the connection,
and the
SERVICE,
REMOTEUSER and PRINTER
will be defined.
A further check is performed to determine if the service request would
be accepted.
<p>
When performing a service activity and a particular job is to
be acted on,
the
USER, HOST, and other control file information will be available,
and a further check can be performed.
<p>
If a rule is specified and the particular value is not defined,
then a rule will fail to match.
<sect1>Match Procedure
<p>
<verb>
key=pattern                         substring match
key=pattern1,pattern2,pattern3,...  glob and exact
key=IP1/mask1,IP2/mask2,...         IP address
</verb>
<p>
Each of the indicated values is matched against a list of patterns.
The following types of matches are used:
<enum>
<item>
substring match.
The indicated entry is present as a substring in the pattern.
<item>
GLOB matches.
The pattern is interpreted as a GLOB style pattern,
where * matches 0 or more characters,
and ? matches a single character.
<item>Exact string match.
There is no glob matching performed and the strings must match
exactly.
<item>IP address match.  The address must be specified in the
standard <tt>nn.nn.nn.nn</tt> format.
The mask must be either an integer number
corresponding to the number of significant bits,
or in the standard <tt>nn.nn.nn.nn</tt> format.
Addresses are compared by doing
<verb>
( IPaddr XOR IP ) AND mask
</verb>
<p>
If the result is 0, then a match results.
Note that there may be one or more addresses being checked for;
this can occur when a host may have multiple IP addresses assigned to it.
<item>integer range match.
The pattern has the form <tt>low-high</tt>,
where low and high are integer numbers.
The match succeeds if the value is in the specified range.
<item>Same IP Address Match.
This compares two lists of IP addresses;
a match is found when there is one or more common addresses.
</enum>
<sect2>DEFAULT
<p>
<verb>
DEFAULT ACCEPT
DEFAULT REJECT
</verb>
<p>
The DEFAULT rule specifies the default if no rule matches.
Normally,
there is one DEFAULT entry in a permissions file.
<label id="userlpc">
<sect2>SERVICE
<p>
Options used:
<itemize>
<item> <tt>user_lpc=</tt><em>allow set of lpc actions on user files</em>
</itemize>
<p>
Match type: substring
<p>
The SERVICE key is based on the type of request.
<table>
<tabular ca="|l|l|">
Key
|
Request
@
<tt>C</tt>
|
LPC Control Request
@
<tt>M</tt>
|
LPRM Removal Request
@
<tt>P</tt>
|
Job Printing
@
<tt>Q</tt>
|
LPQ Status Request
@
<tt>R</tt>
|
LPR Job Transfer
@
<tt>S</tt>
|
LPC Status Request
@
<tt>U</tt>
|
LPC User File Request
@
<tt>X</tt>
|
Connection Request
@
</tabular>
</table>
<p>
Each of the above codes corresponds either directly to the user command,
or a set of subcommands.
<p>
All the LPC subcommands are SERVICE=C;
status commands such as <tt>lpc status, printcap, active, </tt>
or <tt>lpd</tt> are SERVICE=S commands as well.
<p>
The <tt>user_lpc</tt> configuration option can be used to specify a
set of control commands that can be applied to individual user files
in a spool queue.
These commands include <tt>user_lpc=</tt>
<tt>hold</tt>,
<tt>release</tt>,
<tt>move</tt>,
<tt>topq</tt>,
<tt>kill</tt>,
and
<tt>abort</tt>.
When the <tt>user_lpc</tt> specifies one of these commands,
the <tt>lpd</tt> program will delay rejecting a request until
the individual files in a queue are checked for permission.
This allows the following permissions line to be used:
<verb>
#allow root on server all permissions
ACCEPT SERVICE=C SERVER REMOTEUSER=root
#allow all status commands
ACCEPT SERVICE=S
#allow same user on same host to use user_lpc commands
ACCEPT SERVICE=U SAMEUSER SAMEHOST
#note that this has effect for U only when file checks are performed
REJECT SERVICE=UCS
</verb>
<p>
The above permissions entry will allow users to perform the actions
specified by <tt>user_lpc</tt> on their files.
<sect2>USER
<p>
Match type: GLOB
<p>
The USER information is taken from the <tt>P</tt> (person or logname)
information in the print job control file.
<sect2>REMOTEUSER
<p>
Match type: GLOB
<p>
The REMOTEUSER information is taken from the user information sent
with a service request.
<p>
Note that one of the flaws of
<ref id="rfc1179" name="RFC1179">
is that an LPQ (print status)
request does not provide a REMOTEUSER name.
<sect2>HOST
<p>
Match type: GLOB
<p>
The HOST information is taken from the <tt>H</tt> (host)
information in the print job control file.
<sect2>REMOTEHOST
<p>
Match type: GLOB
<p>
The REMOTEHOST information is obtained by doing a reverse IP name lookup
on the remote host address.
If there is no FQDN available,
then the IP address in text form will be used.
<sect2>PORT
<p>
Match type: integer range
<p>
The PORT value is obtained from the originating port of the TCP/IP
connection.
The match succeeds if it is in the specified range.
<sect2>IP
<p>
Match type: IPaddr
<p>
The IP information is obtained by doing a DNS lookup on the
H (host) information in the control file.
If there is no host information, the IP address is undefined.
Note that for a single host name there may be multiple IP addresses;
address matches are performed against the list of addresses and succeeds
if there is one or more individual address matches.
<sect2>REMOTEIP
<p>
Match type: IPaddr
<p>
The REMOTEIP information is the IP address of the host making the
service request.
Note that the REMOTEIP value is obtained by using the
<tt>gethostbyaddr</tt> lookup to obtain the DNS information for the
remote host.  This information may include multiple IP addresses;
address matches are performed against the list of addresses and succeeds
if there is one or more individual address matches.
<sect2>SAMEUSER
<p>
Match type: exact string match
<p>
Both the REMOTEUSER and USER information must be present and identical.
<sect2>SAMEHOST
<p>
Match type: Same IP Address
<p>
The REMOTEHOST and HOST address lists are checked;
if there is a common value the match succeeds.
<sect2>SERVER
<p>
Match type: Same IP Address
<p>
One of the REMOTEHOST addresses 
must be the same as one of the addresses of the <tt>lpd</tt> server host,
or must be one of the addresses found by looking up the <tt>localhost</tt>
name using <tt>gethostbyname()</tt>.
<sect2>FORWARD
<p>
Match type: Address Match
<p>
The list of REMOTEHOST and HOST addresses must not have a common entry.
This is identical to NOT SAMEHOST.
This is usually the case when a remote <tt>lpd</tt> server is forwarding
jobs to the <tt>lpd</tt> server.
<sect2>GROUP
<p>
Match type: modified GLOB
<p>
If the pattern does not start with a <tt>@</tt> character,
then the USER information must be present
and the USER must be present in
one of the groups in <tt>/etc/group</tt> or whatever permissions mechanism is used
to determine group ownership
which matches the GLOB pattern.
<p>
If the pattern starts with a <tt>@</tt> character,
then the USER information must be present
and the user must be in the specified <tt>netgroup</tt>.
This match will be performed only if the <tt>netgroup</tt>
mechanism is supported on the system and the specified netgroup
exists.
No wildcard match will be done for netgroups.
<sect2>REMOTEGROUP
<p>
The same rules as for GROUP,
but using the REMOTEUSER value.
<sect2>CONTROLLINE
<p>
Match type: GLOB
<p>
A <tt>CONTROLLINE</tt> pattern has the form
<verb>
X=pattern1,pattern2,...
</verb>
<p>
X is a single upper case letter.
The corresponding line must be present in a control file,
and the pattern is applied to the line contents.
<p>
This pattern can be used to select only files with specific control
file information for printing.
<sect2>AUTH
<p>
Match type: GLOB
<p>
The AUTH value can be NONE,
indicating that no authentication was done.
If authentication was done,
then AUTH=USER checks to see if there was user information,
and AUTH=FWD checks to see if there was forwarding system identification.
<sect2>AUTHUSER
<p>
Match type: GLOB
<p>
If AUTH=USER check succeeds,
the AUTHUSER rule will check to see if the user identification
matches the pattern.
<sect2>FWDUSER
<p>
Match type: GLOB
<p>
If AUTH=FWD check succeeds,
the FWDUSER rule will check to see if the forwarding system identification
matches the pattern.
<sect2>IFIP
<p>
Match type: IPmatch, but for IPV6 as well as IPV4
<p>
There is a subtle problem with names and IP addresses which are
obtained for 'multi-homed hosts', i.e. - those with multiple
ethernet interfaces,  and for IPV6 (IP Version 6),  in which a host
can have multiple addresses,  and for the normal host which can have
both a short name and a fully qualified domain name.
<p>
The IFIP (interface IP) field can be used to check the IP address
of the origination of the request,  as reported by the information
returned by the accept() system call.  Note that this information may
be IPV4 or IPV6 information,  depending on the origination of the
system.  This information is used by gethostbyaddr() to obtain the
originating host fully qualified domain name (FQDN) and set of IP addresses.
Note that this FQDN will be for the originating interface,  and may
not be the cannonical host name.  Some systems which use the Domain Name Server
(DNS) system may add the cannonical system name as an alias.
<label id="permspath">
<sect1>Permission File Location
<p>
Options used:
<itemize>
<item> <tt>perms_path=</tt><em> directory path list</em>
</itemize>
<p>
The <tt>perms_path=</tt> configuration variable specifies the
location of the default permissions file.
The default value is:
<verb>
perms_path=/etc/lpd.perms:/usr/etc/lpd.perms
</verb>
<p>
The <tt>lpd.perms</tt> file can be obtained by running a program,
in a similar manner to the <tt>/etc/printcap</tt> file.
See
<ref id="secfilter" name="Filters"> for details on how
the program would be invoked.
For example, assume the configuration information specified:
<verb>
perms_path=|/usr/local/libexec/get_perms
</verb>
<p>
Then the <tt>get_perms</tt> program would be invoked with STDIN attached
to <tt>/dev/null</tt> and the complete set of permission information
would be read from its STDOUT.
<sect1>Example Permission File
<p>
<verb>
# allow root on server to control jobs
ACCEPT SERVICE=C SERVER REMOTEUSER=root
REJECT SERVICE=C
#
# allow same user on originating host to remove a job
ACCEPT SERVICE=M SAMEHOST SAMEUSER
# allow root on server to remove a job
ACCEPT SERVICE=M SERVER REMOTEUSER=root
REJECT SERVICE=M
# all other operations allowed
DEFAULT ACCEPT
</verb>
<p>
In the above sample, we first specify that
lp<tt>C</tt>
commands from user <tt>root</tt> on the lpd server will be accepted.
This is traditionally the way that most lpc commands operate.
<p>
Next,  we reject any other lpc requests.
<p>
We accept
lpr<tt>M</tt>
requests from the host and user that submitted the job,
as well as from root on the server,
and reject any others.
<p>
Finally,
all other types of commands (lpq, lpr) are allowed by default.
<sect1>Complex Permission Checking
<p>
One of the more useful types of permission checking is to
restrict access to your printers from users outside your
networks.
The IP pattern can specify a list of IP addresses and netmasks
to apply to them.
<p>
For example
<tt>IP=10.3.4.0/24</tt> would match all hosts with the IP
addresses
<tt>IP=10.3.4.0</tt> to
<tt>IP=10.3.4.255</tt>.
<p>
Similarly, the HOST pattern can specify a set of hostnames
or patterns to match against based on the GLOB notation.
<p>
For example
<tt>REMOTEHOST=*.astart.com</tt>
would match all hosts with a DNS entry which ended with
<tt>astart.com</tt>.
<p>
The NOT keyword reverse the match sense.  For example
<tt>REJECT NOT REMOTEHOST=*.astart.com,*.murpy.com</tt>
would reject all requests from hosts which did not have a DNS entry
ending in
<tt>astart.com</tt>
or
<tt>murphy.com</tt>
as one of the host name components.
<sect1>More Examples
<p>
The following is a more complex lpd.perms file.
<verb>
# All operations allowed except those specifically forbidden
DEFAULT ACCEPT
#Reject connections which do not originate from hosts with an
# address on 130.191.0.0 or from localhost,
# or name is not assigned to Engineering pc's
  REJECT SERVICE=X NOT IFIP=130.191.0.0/16,127.0.0.1/32
  REJECT SERVICE=X NOT REMOTEHOST=engpc*
#Do not allow anybody but root or papowell on
#astart1.astart.com or the server to use control
#facilities.
  ACCEPT SERVICE=C SERVER REMOTEUSER=root
  ACCEPT SERVICE=C REMOTEHOST=astart1.astart.com REMOTEUSER=papowell
#Allow root on talker.astart.com to control printer hpjet
  ACCEPT SERVICE=C HOST=talker.astart.com PRINTER=hpjet REMOTEUSER=root
#Reject all others
  REJECT SERVICE=C
#Do not allow forwarded jobs or requests
  REJECT SERVICE=R,C,M FORWARD
# allow same user on originating host to remove a job
ACCEPT SERVICE=M SAMEHOST SAMEUSER
# allow root on server to remove a job
ACCEPT SERVICE=M SERVER REMOTEUSER=root
</verb>
<sect>Running the software
<p>
<label id="checkpc">
<sect1>LPRng's little helper: checkpc
<p>
The program <tt>checkpc</tt> (check printcap file) is one of the most
useful utilities in the LPRng package.
<p>
It will read all the configuration files, printcap files and tests
whether devices are set up correctly. Optionally, it will also set the
permissions for spool directories and device files. Additionally, it
will truncate the accounting and log files to a maximum size. Another
use for <tt>checkpc</tt> is to remove old entries from queue
directories.
<p>
For a new installation, you will want to run
<verb>
checkpc -f
</verb>
to set the permissions right. The <tt>-f</tt> flag instructs the
program to correct file permissions. If you don't run this as
<tt>root</tt>, you'll receive a warning about that fact and any
<bf>chown(2)</bf> calls will (most likely) fail.
<p>
The program reports everything it changes. Since it isn't too clever
about some things (visit the man page), you should keep an eye on the
output, and run it again if needed. If it keeps failing, change the
permissions yourself.
<p>
These are the permissions of my spool directory:
<verb>
drwx--S---   2 lp       lp           1024 Jul 22 21:15 ./
drwxr-xr-x  16 root     root         1024 May 29 21:55 ../
-rw-------   1 lp       lp          10222 Jul 23 05:32 acct
-rw-------   1 lp       lp              0 Feb 14 21:14 control.lp1
-rw-------   1 lp       lp          10229 Jul 23 05:32 log
-rw-------   1 lp       lp              5 Jul 22 21:13 lp1
-rw-------   1 lp       lp           9064 Jul 22 21:15 status.lp1
-rw-------   1 lp       lp              5 Jul 22 21:13 unspooler.lp1
</verb>
<p>
And this is lpd's master directory:
<verb>
drwx--S---   2 lp       lp           1024 May 11 18:44 ./
drwxr-xr-x  16 root     root         1024 May 29 21:55 ../
-rw-------   1 lp       lp              0 Feb 18 07:05 lpd.lock.duff
-rw-------   1 lp       lp              3 Jul 13 22:42 lpd.lock.duff.printer
-rw-------   1 lp       lp              0 Apr  1 22:40 lpd.log.duff
</verb>
<p>
Later, you will want to use <tt>checkpc</tt> for the daily
maintenance of your system. I have this line in user <tt>lp</tt>'s
crontab:
<verb>
32 5 * * * checkpc -t 10K -A3 -r &gt;/dev/null 2&gt;&1
</verb>
This job will:
<enum>
<item>truncate all log and accounting files to 10KB (<tt>-t 10K</tt>).
Actually, it will keep the last 10K from the file, starting on a
complete line.</item>
<item>remove all stale files older than three days (<tt>-A3 -r</tt>).</item>
</enum>
I'm redirecting output to <tt>/dev/null</tt>, because <tt>checkpc</tt>
is a little noisy to my taste. (But too noisy is better than too
silent :)
<sect1>Starting the daemon
<p>
Now comes the moment of truth: will it work? (I hope so, otherwise it
means there are errors here.)
Where should I run the daemon?
<p>
In order to work, vanilla LPR needs to be run on all computers on the
network. This is because a job is first transmitted to the local
<tt>lpd</tt>, and then (if needed) to the remote host.
<p>
LPRng eliminates the local <tt>lpd</tt> from this chain, and connects
directly to the remote daemon (except in the case of a bounce queue).
Therefore, you won't need to start a daemon on all machines.
<p>
In short: where do you need the daemon? Only on those machines where you have
spool directories.
Almost there...
<p>
These are the last steps in the installation:
<itemize>
<item>Kill your old lpd/lpsched.</item>
<item>Start the newly installed lpd program.</item>
<item>Print a sample file using the new lpr:
<verb>
lpr /etc/printcap
</verb>
</item>
</itemize>
<p>
If it works, you can remove your old printing software, and change
your system startup files to run the new daemon automatically.
<p>
Then, read the rest of the documentation to build whatever complex
configuration you need.
<label id="accountingref">
<sect>Accounting
<p>
The LPRng method for doing accounting is based on experiences in a
Academic environment,  where avoiding printing accounting procedures
has long been practiced.  While the LPRng procedures are not bombproof,
they do provide a wide range of facilities,  with various degrees
of trust built into them.
<sect1>Printer Accounting Reality Check
<p>
The following was written
by Patrick Powell
<tt>&lt;papowell@astart.com></tt>
in response to the expressions of frustration
that are periodically vented in the
<tt>
<ref id="maillist" name="lprng@iona.ie">
</tt>
mailing list.
While this addresses the use of a particular set of printer filters,
i.e. - the
<ref id="ifhp" name="CTI-ifhp">
set,
the comments are appropriate to other issues.
<p>
In Academic institutions, avoiding printing accounting has been
regarded as a challenge,  an ongoing game of fat cat and poor starving
mouse, between the Administration and the downtrodden, poor, overcharged
student.  The following is a lighthearted ramble down the dark lane of
printing accounting.
<p>
We will disregard the fact that if most students put as much effort
into their studies as in finding ways to avoid accounting procedures
then they would be Rhodes Scholar material,  but I digress...
<p>
The accounting procedures put into the LPRng and the hpif filters may
appear to be extraordinarily complex,  but believe me, they are not.
Firstly, we make the assumption that the printer has some sort of
non-volatile page counter mechanism that is reliable and impervious to
power on/off cycles.  Without this mechanism the enterprising student
ummm... user will simply turn off the printer.  Software that prescans
jobs for line counts and pages is notoriously unreliable,  given even
the most modest efforts of users to hide these procedures.   The cost
of running a PostScript simulator simply to do accounting has its
flaws; without ensuring that the simulator has all of the interesting
security loopholes closed, such as opening files, etc.,  it can become
a trap door to hell for the system administrator.
<p>
Secondly,  we must make the assumption that the student... uhhh...
user will not be able to tinker with the page counter mechanism, i.e.-
they will not be able to roll back the odometer on the printer, FOR THE
DURATION OF A SINGLE JOB.  I will digress and point out that a student
actualy did this for a challenge;  it only took him a couple of weeks
of study and a fully equipped micrcontroller lab, and two (2) laser
printers which he ruined in the experiment.  HP was not amused when we
sent them back under warranty,  claiming that this our 'normal lab usage.'
<p>
Lastly,  you should not mind a small amount of pilferage, or a few
pages here and there being charged to the wrong account.
<p>
<bf>How Does It Work?</bf>
<p>
The basic mechanism the CTI/LPRng filter uses is to record the page
counter value at the start and end of each part of a print job. Each
record has the form:
<verb>
start -ppagecounter -Ff -kjob -uuser -hhost -R... 
end  -ppages -qpagecounter -Ff -kjob -uuser -hhost -R...
</verb>
<p>
When we use the OF filter and/or banners,  we will see the
individual jobs bracketed by the OF filter records:
<verb>
start -p100 -Fo -kcfA100taco -uuser -hhost -R...  
start -p101 -Ff -kcfA100taco -uuser -hhost -R...  
end  -p1 -q102 -Ff -kcfA100taco -uuser -hhost -R...  
start -p102 -Ff -kcfA100taco -uuser -hhost -R...
end  -p3 -q105 -Ff -kcfA100taco -uuser -hhost -R...  
end  -p5 -q105 -Fo -kcfA100taco -uuser -hhost -R...
</verb>
<p>
It should be clear from the above that all we need to do is to add up
the values for the -Fo (OF) filter lines and we are done.
<p>
Unfortunately,  this is too simplistic.  If for some reason the job is
killed or terminates due to error conditions,  the OF filter may not
get to finish its work.  Thus,  we may see the following:
<verb>
start -p100 -Fo -kcfA100taco -uuser -hhost -R...  
start -p101 -Ff -kcfA100taco -uuser -hhost -R...  
start -p110 -Fo -kcfA101taco -uuser -hhost -R...
</verb>
<p>
This is a clear indication that the user's job has been terminated.  In
this case we need to use the differences between pagecounters of the start
records to do accounting.
<p>
There is a caveat to all of this;  that is the problem of the last dead
job in the list.  If the last line in the accounting file is:
<verb>
start -p110 -Fo -kcfA101taco -uuser -hhost -R...
</verb>
is the last job finished or did it abort?
<p>
<bf>Who Used Up 2000 Pages of Paper Today?</bf>
<p>
Now we move on to the problem of real time accounting.  Due to limited
budgets, etc., many institutions would like to strictly enforce limits
on paper use by students. As jobs are printed their accounts should be
docked for the amount of paper use.  One way to do this is to have an
external accounting procedure update a shared database.  The CTI filter
has provision for a shell script to be called at the end of print job;
this is done by both the OF and IF filter.  Thus, we can blithely
assume that there is a central database carefully getting updates
from the LPRng software, probably from dozens of different printers,
and updating the accounting information.
<p>
The first question to be asked is simple:  is this worth it?
Perhaps doing accounting as a batch job once an hour/four times
a day/once a day is cheaper than building an running such a database.
If it costs $5K/year for the database software, you might just consider
ignoring the 10,000 pages that get lost in the shuffle and use
a simple set of awk/sed/perl scripts to update a database once
an hour.
<p>
<bf>BAD JOBS - Who Do We Bill?</bf>
<p>
We inevitably run into an interesting question:
what happens if a job does not complete correctly?
<p>
If you use the completion of the OF filter as a success status, I have
to point out that many students... ummm... users soon find ways to send
jobs to the printer that will cause it to lock up after their output
has been printed. These jobs require power cycling of the printer and
restarting the filter; a bit extreme, perhaps, but it has happened.
<p>
I suggest that you simply adopt a 'bill to last user of record'
attitude,  using the pagecount information as follows:
<verb>
start OF -- starting point for THIS job
start IF --  nice information, but not useful
start IF --
end   OF -- ending point for this job - can record infomation
start OF --
if no end OF for previous job,  then treat as end OF and
      update accounting.
</verb>
<p>
Now somebody is sure to complain that they got charged for a bunch of
pages that they did not use.  This is inevitable;  always carry a
can of oil for the squeaky wheels.  I might make the observation that
once is accident, twice is coincidence, but three times is malice;
be wary of the constant complainer and check out not only him or her but
also their co-workers.
<p>
<bf>How Do We Update the Database?</bf>
<p>
I suggest that database update be done as follows:
<p>
You maintain a 'last page reported' counter for each printer in the
database.  When a successful job reports in,  check to see that
<newline>
pagecount + joblength ==  newpagecount;
<p>
If this is not the case,  then you have had a some unsuccessful jobs.
In this case I strongly recommend that you have a means to request the
accounting reporting program to go back through the accounting file and
find the last report for the page counter value and try to backtrack
through the accounting files.  The accounting file is one of the first
things to be attacked by students... Ummm...  users.  It should NOT be
kept on an NFS exported or mounted file system.  It should be
carefully pruned and copied, perhaps on an hourly basis.
<p>
Now some administrators have fallen in love with network based printers;
do not believe ANYTHING that comes over a network connection without
some form of authentication;  PGP has some very nice Public Key
mechansims for handling this.  This is a major weakness in using a
database for keeping track of accounting - a weak authentication
mechanism may lead to denial of service attacks by students flooding
the database with bogus print usage reports;  suddenly NOBODY can print
and the administrator is driven to turning off accounting.
<p>
Good luck.  I am never surprised when I encounter yet another wrinkle in
this area.
<p>
Patrick ("You call me a Bean Counter?  Guido,  break this kid's fingers
<newline>
&nbsp; &nbsp; &nbsp; &nbsp; with an adding machine!") Powell
<sect1>How HP Printers Implement Page Counters
<p>
The following is from
<htmlurl
url="http://www.hp.com/cposupport/printers/support_doc/bpl02119.html"
name="http://www.hp.com/cposupport/printers/support_doc/bpl02119.html" >
<p>
<bf>HP LaserJet Printer Family - Page Count</bf>
<p>
Description Of The Page Count Feature On HP LaserJet 4 Family
Printers
<p>
All HP LaserJet 4/5/6 family printers have a page count feature
built into the firmware. However, this feature works differently
depending on which HP LaserJet printer is being used. The following
is a description of how the page count feature works for each
printer within the HP LaserJet 4/5/6 printer families.
<verb>
HP LaserJet 4/4M printers
HP LaserJet 4 Plus/4M Plus printers
HP LaserJet 4P/4MP printers
HP LaserJet 4Si/4Si MX printers
HP LaserJet 4ML printers
HP LaserJet 5P/5MP printers
HP LaserJet 6P/6MP printers
</verb>
<p>
All of the above printers use the same method for keeping track of
the number of copies. There are really two different page count
values: Primary and Secondary values. Every time a page is printed,
whether it is an internal job (such as a self-test) or a standard
print job, the Secondary page count increases by one. This value
is stored in standard RAM. Once the Secondary page count value
reaches 10, the Primary page count will increase by 10. The Primary
page count value is stored in a type of memory called NVRAM
(Non-Volatile RAM). This is important, since NVRAM is not cleared
when the printer is powered off. Standard RAM, on the other hand,
is cleared when the printer is turned off or reset. Thus, the
Primary page count only increases in increments of 10.
<p>
Example
<p>
You have a brand new HP LaserJet 6P printer and you print a self-test
page. When you look on the test page for the Page Count value, you
will see that it says 1. Next, you decide to print a two page letter
and, after that, another self-test. The page count value now says
4. Internally, the printers Secondary page count (stored in RAM)
has the value of 4 while the Primary page count (stored in NVRAM)
still has the value of 0. Now, you turn the printer off, then back
on, and print another self-test. The page count value again says
1 since the previous value of 4, stored in RAM, was cleared when
the printer was powered off. Finally, print a ten page document
and then turn the printer off. Upon turning the printer back on
and printing out another self test, you see that the page count
value is 11.  Internally, the Secondary page count value is back
at 1 while the Primary page count value (stored in NVRAM) is 10.
Added together, you end up with the resulting value seen on the
self-test page.
<p>
HP LaserJet 4L/5L/6L Printers
<p>
The reason that the page count method for the HP LaserJet 4L/5L/6L
printers differ from that of the other printers is that the HP
LaserJet 4L/5L/6L printers do not have any NVRAM available. Thus,
no way exists for the printer to retain a page count value once
the printer is powered off. The HP LaserJet 4L/5L/6L printers have
only a single page count value that increases in increments of one
until the printer is powered off. At that point, the page count
value is reset and begins from 0 once again.
<p>
<label id="accountingserver">
<sect1>Accounting Printcap Options
<p>
The accounting facilities are controlled and enabled by the following
entries in the printcap file.  The default value is indicated.
<p>
<table>
<tabular ca="|l|l|l|">
Tag
|
Default Value
|
Purpose
@
af
|
NULL
|
accounting file name
@
as
|
"jobstart $H $n $P $k $b $t"
|
accounting info for job start
@
ae
|
"jobend $H $n $P $k $b $t"
|
accounting info for job end
@
accounting&lowbar;server
|
NULL
@
achk
|
FALSE
@
la
|
TRUE
|
do accounting for 'local' printer
@
ar
|
FALSE
|
do accounting for 'remote' transfers
@
</tabular>
</table>
<p>
<sect1>Accounting File
<p>
The most common method of accounting is to record the start and end
times of a job and its size to the accounting file. A typical entry
for the printcap defaults are shown below.
<verb>
jobstart -H'taco.astart.com' -n'root' -P'ps' -k'cfA938taco.astart.com' \
    -b'1093' -t'Nov  5 19:39:59'
start -p'12942' -k'cfA938taco.astart.com' -n'root' -h'taco.astart.com' -P'ps' \
    -c'0' -F'o' -t'Sun Nov  5 19:39:25 1995'
start -p'12944' -k'cfA938taco.astart.com' -n'root' -h'taco.astart.com' -P'ps' \
    -c'0' -F'f' -t'Sun Nov  5 19:39:27 1995'
end -p'12944' -k'cfA938taco.astart.com' -n'root' -h'taco.astart.com' -P'ps' \
    -b'3' -c'0' -F'f' -t'Sun Nov  5 19:39:58 1995'
end -p'12942' -k'cfA938taco.astart.com' -n'root' -h'taco.astart.com' -P'ps' \
    -b'2' -c'0' -F'o' -t'Sun Nov  5 19:39:59 1995'
jobend -H'taco.astart.com' -n'root' -P'ps' -k'cfA938taco.astart.com' \
    -b'1093' -t'Nov  5 19:39:59'
</verb>
<p>
The <tt>jobstart</tt> and <tt>jobend</tt> lines are added by the LPD server,  as
specified by the <tt>as</tt> and <tt>ae</tt> printcap options;
the -b (byte count) indicates the numbers of bytes in the job.
<p>
The <tt>start</tt> and <tt>end</tt>
lines are produced by the filters;
the of filter has an -Fo, and the if filter a -Ff entry.
The filters in the LPRng distribution produce the
indicated output format by default.
The -p value is the current value of a page counter device (if any),
and the -b value indicates the total number of pages used.
<p>
It should be clear that a simple AWK or Perl script will be able to
process an accounting file and update accounting information for
accounting purposes;  the usual problems with truncation, time stamps,
etc., are left as an exercise for the system administrator.
<p>
Note that the accounting file must exist; LPRng will not create it
(and also will not create the log file).  This prevents accidentally
growing log and accounting files.
<sect1>Remote Server Accounting
<p>
To accommodate even more aggressive and centralized accounting,
a method to make a connection to a print server and send information
to the server has been provided as well.
If <tt>achk</tt> option is set,
it is assumed that the <tt>af</tt> entry specifies a connection to
server on a remote host.
The <tt>lpd</tt> server will send the <tt>as</tt> string to the server,
and then wait for a single line of text from the remote server.
If the first word on the return line is
<tt>accept</tt> or <tt>hold</tt>,
the job will be either accepted for printing or held.
Any other value will cause the job to be deleted.
<p>
At the end of the job the <tt>ae</tt> string will be sent to the server.
No response is expected.  Example:
<p>
<verb>
:af=accounting.site.com%2300,tcp
:achk
:as=starting
:ae=ending
</verb>
<p>
The port that the connection originates from will be in the range
set by the configuration or printcap
<tt>
<ref id="originateport" name="originate_port">
</tt>
option.
<sect1>Using Filters For Accounting
<p>
Some sites have expressed interest in using a central accounting
mechanism to check that users have permissions.  This can be done by
using the an alternative form of the as (accounting start) and ae
(accounting end) printcap tags.  If the as and ae are filter
specifications,  then a filter is invoked.  If the as (accounting
start) filter returns a non-zero exit status,  then its value
is used to handle the job as indicated by
the
<ref id="termination" name="Abnormal Termination">
codes for filters.
At the end of the job the :ae: filter will be invoked in a similar manner,
but its exit status is ignored.
<p>
When using an accounting filter,
the STDIN  is attached (read/write) to the accounting file or remote host
specified by the <tt>af</tt> printcap option,
STDOUT to the output device,
and STDERR to the log file.
The filter program would be invoked with the default filter options.
<p>
For example, here is a sample entry to check and update accounting
<verb>
printer
   :as=|/usr/local/lib/filters/accounting.pl start
   :ae=|/usr/local/lib/filters/accounting.pl end
</verb>
<sect1>Accounting Utility accounting.pl
<p>
In order to provide a framework for doing using the outlined accounting
methods,  the LPRng distribution
<tt>UTILS</tt> directory has a <tt>accounting.pl</tt> script.
This script does the following.
<enum>
<item>
It is assumed that the accounting filter is invoked with the
following printcap entry.
The start and end is used by the filter to determine at which point in
the accounting process it is invoked.
<verb>
printer
   :as=|/usr/local/lib/filters/accounting.pl start
   :ae=|/usr/local/lib/filters/accounting.pl end
</verb>
</item>
<item>
It maintains the accounting file as a set of entries in the following format:
<verb>
START [job identification]
start -pnn ...
...
end -pnn+pagecount ...
END -ppagecount [job identification]
</verb>
</item>
<item>
Each time the filter is invoked with the <tt>start</tt>
tags,
it will add a <tt>START</tt> record to the end of the accounting file.
</item>
<item>
When it is invoked with the <tt>end</tt> option,
it will update the accounting file and add an <tt>END</tt> entry.
</item>
<item>
It will handle aborted jobs by looking for jobs with have a <tt>START</tt>
entry and a following <tt>start</tt> line and assuming that they
progressed to the point of starting print operations,
i.e. - the printer page counter was accessed and reported.
It will then look for the next <tt>START</tt> entry with a
following <tt>start</tt> line,
and assume that the pages between the two points were used by the
aborted job.
</item>
</enum>
<p>
Administrators can use this script as a starting point for more advanced
accounting.
For example,
rather than just recording the information,
at the job start the script can query either a local database
or a remote server to see if the user has permissions to access the printer.
At the end of the job or when an <tt>END</tt> line is written to the
accounting file,
the local database or remote accounting server can be updated.
<label id="authref">
<sect>Authentication and Encryption
<p>
One of the major problems in a print spooler system is providing
privacy and authentication services for users.  One method is to
construct a specific set of protocols which will be used for
providing the privacy or authentication;  another is to provide a
simple interface to a set of tools that will do the authentication
and/or encryption.  The LPRng system adopts this latter approach.
<sect1>Authentication
<p>
A careful study of the authentication problem shows that it should be done
during reception of commands and/or jobs from a remote user and/or
spooler.  At this time the following must be done:
<enum>
<item>
The received command must be checked for consistency,  and the
remote user and host must be determined.
<item>
The remote user and host must be authenticated.
<item>
The command and/or spooling operation must be carried out.
<item>
The results must be returned to the remote system.
</enum>
<p>
<sect1>Identifiers
<p>
When a user logs into a system,  they are assigned a user name
and a corresponding UserID.  This user name is used by the LPRng
software when transferring jobs to identify the user.
<p>
When we look into the problem of authentication,  we will possibly
have a more global user identification to deal with, the
authentication identifier (AuthID).  One way to deal with this problem is to
give LPRng intimate knowledge of the UserID and AuthID relationship.
While this is possible,  it may be difficult to deal with in a
simple and extensible manner.  An alternate solution is to provide
a mapping service,  where the authentication procedure provides
a map between the UserID and AuthID.
<p>
<sect1>RFC1179 Protocol Extensions
<p>
The RFC1179 protocol specifies that a LPD server command sent on
a connection has the form:
<verb>
\nnn[additional fields]\n
</verb>
<p>
<tt>\nnn</tt> is a one octet (byte) value with the following meaning:
<p>
<verb>
REQ_START   1    start printer
REQ_RECV    2    transfer a printer job
REQ_DSHORT  3    print short form of queue status
REQ_DLONG   4    print long form of queue status
REQ_REMOVE  5    remove jobs
</verb>
<p>
The LPRng system extends the protocol with the following additional
types:
<verb>
REQ_CONTROL 6    do control operation
REQ_BLOCK   7    transfer a block format print job
REQ_SECURE  8    do operation with authentication
</verb>
<p>
The REQ_CONTROL allows a remote user to send LPC commands to the
server.  The REQ_BLOCK provides an alternate method to transfer a
job.  Rather than transferring the control and data files individually,
this format transfers one file.  The REQ_AUTH provides a mechanism
for providing an authentication mechanism and is described in this
document.
<p>
<label id="useauth">
<label id="defaultauth">
<label id="remoteuser">
<label id="serveruser">
<label id="userauthcommand">
<sect1>Client Operations for Client To lpd Server Authentication
<p>
Options used:
<itemize>
<item> <tt>use_auth=</tt><em>Authentication type</em>
<item> <tt>default_auth=</tt><em>Default authentication type</em>
<item> <tt>remote_user=</tt><em>Remote server identification </em>
<item> <tt>server_user=</tt><em>Server identification</em>
<item> <tt>user_auth_command=</tt><em>User to server authentication program</em>
</itemize>
<enum>
<item>
First, the <tt> use_auth </tt> option is checked to see if it specifies
an authentication type.
If it does, then the <tt>use_auth</tt> value is used to identify the
type of authentication to be done.
<item>
If,  by using a command line flag to the <tt>lpr</tt>, <tt>lpq</tt>,
etc program,
the user specifies that an authenticated transfer is to be done
and the <tt>use_auth</tt> value is not set,
then <tt>use_auth</tt> is set to the <tt>default_auth</tt> value.
<item>
If after the above actions <tt>use_auth</tt> has a value,
then an authenticated transfer will be done.
<item>
Some sort of identifier specific  to the remote system must be
provided to the authentication facility.
For client to server operations,
the remote system is identified by the
<tt>remote_user</tt>
and
<tt>server_user</tt>
printcap option values.
If the <tt>remote_user</tt> value is not specified, then
it will be set to <tt>server_user</tt>
(default <tt>daemon</tt>).
As discussed below,
the <tt>server_user</tt> is usually used to identify the
<tt>lpd</tt> server on an originating host,
but a general form of identification can be used.
<item>
The <tt>userid</tt> is the value obtained by using the current UID
of the program as the search value for <tt>getpwuid()</tt>.
<item>
The client will open a connection to the server
and sends a command with the following format:
<verb>
\008printer C userid use_auth\n          - for commands
\008printer C userid use_auth controlfilename\n - for print jobs
</verb>
<p>
Note that \008 is a one byte code indicating an authenticated
transfer.  Printer is the spool queue name, C in the character 'C'
indicating a client request, userid is the login id of the user,
use_auth is a keyword identifying the authentication type,
and controlfile name is the name of a controlfile to be transferred.
<item>
On reception of this command,  the server will send a one byte
success code.
If an error is indicated by a non-zero response,
additional error status may follow
the non-zero success code byte.  At the end of this information
the connection will be terminated.
The values used by LPRng are:
<verb>
   ACK_SUCCESS 0   success
   ACK_STOP_Q  1   failed; no spooling to the remote queue
   ACK_RETRY   2   failed; retry later
   ACK_FAIL    3   failed; job rejected, no retry
</verb>
<p>
If the success code is zero, the sender or client will start the
client side authentication program specified by the
<tt>user_auth_command</tt> option.
This program will be passed the same options as normal filter
program.
See
<ref id="secfilter" name="Filters"> for details.
The authentication program will have the following IO assignments:
<verb>
FD  Options Purpose
0  R/W     socket connection to remote host (R/W)
1  W       pipe or file descriptor,  for responses to client
2  W       error log
</verb>
<p>
Command line arguments:
<verb>
program -C -Pprinter -nuser -asend_auth -Rremote_user -Ttempfile
</verb>
<p>
The authenticator program can create additional temporary or working
files with the pathnames <tt>tempfile.ext</tt>;  these will be deleted
after the authentication process has been completed.
<p>
The command line arguments will undergo replacement of %H and %R
values.  This means that when authenticating to host <em>nowhere.com</em>,
a <tt>remote_user=lpr@%R</tt> will be expanded to
<tt>remote_user=lpr@</tt><em>nowhere.com</em>.
<item>
If the authentication type is for a built-in type,
then the built-in facilities will be used.
Commonly built-in facilities are <tt>kerberos</tt> authentication.
The same actions are carried out.
<item>
The <tt> tempfile </tt> will contain either a command line as would be
transferred using the standard RFC1179 protocol,
or a print job in block format.
See
<ref id="rfc1179ref" name="RFC1179 Protocol"> for details.
The client authenticator program will open and transfer
the contents of <tt>tempfile</tt> to the server authenticator,
using FD 0
and a format compatible with the underlying authentication mechanism.
<p>
If the transfer fails the client
authenticator will log error information on FD 2 and then exit
with error code JFAIL.
<item>
The server will send the client authentication program
any error or logging information over the FD 0 connection,
in a form appropriate to the authentication operation.
The client authenticator will write this information to FD 1.
If data transfer or authentication fails,  the authenticator
will write an error message to FD 2 and exit with error code
JFAIL
<item>
If no error has occured the client authenticator will
then exit with error code JSUCC.
</enum>
<label id="serverauthcommand">
<sect1>Server Operations for Client To lpd Server Authentication
<p>
Options used:
<itemize>
<item> <tt>server_auth_command=</tt><em>Server (lpd) authentication program</em>
</itemize>
<enum>
<item>
When an authentication command arrives at the server,
it has the following form:
<verb>
\008printer C userid use_auth\n          - for commands
\008printer C userid use_auth controlfilename\n - for print jobs
</verb>
<p>
The server will decode the various fields,  and set the following permission keys:
<newline>PRINTER=<tt>printer</tt>
<newline>USER=<tt>user</tt>
<newline>AUTHTYPE=<tt>use_auth</tt>
<newline>AUTH=<tt>USER</tt>
<item>
The server will attempt to find the printcap for the specified printer.
For some operations this printer will be a dummy entry;
this will simply cause the following operations to use the default
information in the <tt>lpd</tt> configuration.
<item>
If a print job is being performed and the spool queue does not exist,
then the job will be rejected.
A non-zero error code will be written to the connection and the operation
will terminate.
<item>
If the AUTHTYPE is not built in,
and the <tt>server_auth_command</tt> value is not specified,
then authentication will fail.
<p>
An error message will be logged to the server log file,
and a non-zero error code and message will be written to the connection
to the remote client program.
<item>If the authentication program exists,
a 0 success code is written to the connection
and authentication proceeds.
(Note that the client side of the authentication will use this
as an indication to start the client side authentication program).
<item>
Many authentication programs require that the
users provide some form of key or identification.
The <tt>server_user</tt> option is used for this purpose.
If there is no value specified,
<tt>server_user</tt> is set to the <tt>user</tt>
(default <tt>daemon</tt>) option value.
<item>
The server will start the server authenticator program and provide the
following open file descriptors for it.  The program
will run as the same UID as the <tt>lpd</tt> server.
If this is a print job transfer,
the current directory will be the spool directory of the print queue.
<verb>
FD  Options Purpose
0  R/W     socket connection to remote host (R/W)
1  W       pipe or file descriptor,  for information for server
2  W       error log
3  R       pipe or file descriptor,  for responses to client
</verb>
<p>
Command line arguments:
<verb>
program -S -PPRINTER -nUSER -aAUTHTYPE -Rserver_user -Ttempfile
</verb>
<p>
The authenticator can create additional temporary or working
files with the pathnames tempfile.ext;  these will be deleted
after the authentication process has been completed.
<item>
After performing the necessary authentication operations,
the client side authenticator will transfer a file containing either
commands or a print job to the server side authenticator.
The server authenticator will put this file in the specified
<tt>tempfile</tt>.
<p>
In order for the LPRng software to perform permissions checking,
it needs an authenticated identifier for the user.
The server authenticator will write such an identifier to FD 1.
This should be a single line, of a maximum of 127 characters
(LPRng LINEBUFFER maximum size), i.e.:
<verb>
authentication_info\n
</verb>
<p>
If the transfer step or authentication fails,  then the server
authenticator
will write an error message to FD 2 and exit with error code
JFAIL.
<item>
The <tt>lpd</tt> server will record the
authentication information returned by the server in the
AUTHUSER permissions key.
<p>
If a print job is being transferred, it will also be stored in the control
file.
This allows job forwarding as is discussed below.
<item>
The <tt>lpd</tt> server will perform the usual permissions checks,
with the addition of the indicated permission keys and associated values.
During this process,
any error messages or logging information normally returned to client
programs will be written to the authentication program FD 3.
<item>
The <tt>lpd</tt> server will carry out either the commands or
print job specified in the temporary file.
During this process,
any error messages or logging information normally returned to client
programs will be written to the authentication program FD 3.
<item>
At the end of the remote servers operations,
the FD 3 file descriptor will be closed and the <tt>lpd</tt> server
will wait for the authentication process to exit.
<item> The server authentication process will read
input from FD 3 until the end of input,
and then transfer the received information to the client
side authenticator.
It may use the tempfile to hold the information during the
reading and transfer process.
<p>
If the transfer of the logging information fails,
then the authenticator process will exit with
error code JFAIL, otherwise it will exit with error code JSUCC.
</enum>
<label id="forwardauth">
<sect1>lpd Server to Server Authentication
<p>
Options used:
<itemize>
<item> <tt>forward_auth=</tt><em>Server forwarding authentication type</em>
</itemize>
<p>
The Server to Server authentication procedure is used by one server
to forward jobs or commands to another server.  It should be noted
that this forwarding operation puts an implicit trust in the security
of the client to server to server chain.
<enum>
<item>
The <tt>lpd</tt> server will perform an authenticated transfer to
another server when it either needs to transfer a job to a remote printer
or when it needs to propagate a
<tt>lpq</tt>,
<tt>lprm</tt>,
or
<tt>lprc</tt> operation.
The
<ref id="remotesupport" name="
<tt>remote_support</tt>">
command can be used to control this forwarding
operation.
<item>
When propagating a command,
the server uses the authentication information provided for the remote
user by the client to server authentication program.
When propagating or forwarding a job,
the server will use the authentication information stored in the job
control or hold file.
This information will be represented as AUTHUSER in the following discussion.
<item>
The identification for the sending server and receiving server for the
authentication programs is generated.
For the sending server,
the <tt>server_user</tt> value is used.
If it is not specified, the <tt>server_user</tt> value is set to the
<tt>user</tt> (default <tt>daemon</tt>) value.
For the remote server,
the <tt>remote_user</tt> value is used.
If it is not specified, the <tt>remote_user</tt> value is set to the
<tt>user</tt> (default <tt>daemon</tt>) value.
<item>
If there is no user authentication information,
then a normal, non-authenticated transfer will be done.
<item>
The <tt>server_auth_command</tt> will be used for the forwarding
operation.
If it does not have a value,
then a normal, non-authenticated transfer will be done.
<item>
The sending server takes the part of the client, and will
transfer a job acting similar to a client.  The initial information
transfer from the sending server will have the format:
<verb>
\008printer F server_user authtype \n          - for commands
\008printer F server_user authtype controlfilename\n - for print jobs
</verb>
<item>
The sending server will invoke its authenticator with the arguments:
<verb>
server_auth_command -F -Pprinter -nserver_user -aauthtype \
    -Rremote_user -Ttempfile
</verb>
<item>
The tempfile containing the job or command information to be sent
will have the form:
<verb>
user_authentication\n
<normal file contents>
</verb>
That is,
the user authentication information is place in the tempfile.
<item>
The tempfile will be transferred to the remote server in the same fashion
as for a user job.
Any error or logging information returned will either be written to the
<tt>lpd</tt> log file or to the previous <tt>lpd</tt> process in
the transfer chain.
<item>
On the destination server the same operations
for receiving an authentication request from a client
is performed.
The USER and AUTHTYPE values will be the derived from the
authentication request <tt>server_user</tt> and <tt>authtype</tt>
fields in the command sent to the server.
The <tt>server_user</tt> value is obtained as for client to server
transfers.
The server side authenticator is invoked with the arguments:
<verb>
server_auth_command -S -Pprinter -nUSER -aAUTHTYPE \
   -Rremote_user -Ttempfile
</verb>
<item>
As for client to server operations,
the authenticator will write the authenticated user name on FD 1,
and the FWDUSER is set to this value.
<item>
The <tt>lpd</tt> server will remove the first line of the transferred file,
which contains the user authentication information,
and set
AUTHUSER to this value.
<item>
Authentication is performed using the indicated values.
<item>
If authentication succeeds,
then the command line or print job control file is processed
in the normal manner.
This might now add more permissions values to tags,
but the authentication information will not be changed.
</enum>
<sect1>Permission Checking
<p>
The following patterns and values can be used
to check that a particular type of authentication
has been used,
and what the authenticated user information is.
<itemize>
<item>
AUTH=&lsqb;NONE,USER,FWD&rsqb;  - authentication
<newline>
Values can be:
<verb>
AUTH=NONE     - no authentication
AUTH=USER     - authentication from a client
AUTH=FWD      - forwarded authentication from a lpd server
</verb>
<item>
AUTHUSER=globmatch
<newline>
<p>
For a command received from a client, the value is auth_user information
returned by the authenticator
<p>
For a command received from a server (i.e.- forwarded by a server),
the value is the original auth_user_id determined by the remote server.
<item>
FWDUSER=globmatch
<newline>
<p>
For a command received from a server (i.e.- forwarded by a server),
the value is the the forwarding servers authentication information.
</itemize>
<p>
For example,  to reject non-authenticated operations, the following
line could be put in the permissions file.
<verb>
REJECT AUTH=NONE
</verb>
<p>
To reject server forwarded authentication as well, we use:
<verb>
REJECT AUTH=NONE,FWD
</verb>
<p>
If a remote server has id information FFEDBEEFDEAF,  then the
following will accept only forwarded jobs from this server.  Note
that AUTHUSER will only match on authenticated transfers;
FWDUSER will only match on forwarded transfers.
<verb>
ACCEPT FWDUSER=FFEDBEEFDEAF
REJECT AUTH=FWD
</verb>
<sect1>Using PGP for Authentication
<p>
PGP is a well known encryption and authentication program.
For more details see the web site
<htmlurl url="http://www.pgp.net" name="http://www.pgp.net">
or the ftp site
<htmlurl url="ftp://ftp.pgp.net" name="ftp://ftp.pgp.net">.
<sect2>authenticate_pgp
<p>
The
<tt>LPRng/src/AUTHENTICATOR/authenticate_pgp.sh</tt>
program uses the standard <tt>pgp</tt> program and
facilities to do authentication.
It uses the following <tt>pgp</tt> key organization.
<enum>
<item>
Each user should have a private keyring in their home directory,
in the ~/.pgp directory, i.e. - the default used by PGP.
The users public key identification should have the format:
<verb>
User Name <userid@host> <userid1@host> ...
</verb>
<p>
<tt>userid</tt> must be the userid used for submitting jobs.
Note that that different userids can be put
into the key string as long as there is one entry for
each userid that the user has.
<p>
For example,  suppose that
user John Smith has several accounts on different hosts:
john, jsmith, jsmith1. Then his key should have in it:
<verb>
   John Smith <john@whereever> <jsmith@nowhere> <jsmith1@whatever> ...
</verb>
<p>
If the user is daring,  then the pass phrase can be put in the file:
<tt>  ~/.pgp/clientkey </tt>.  This file will be read by the
<tt>authenticate_pgp</tt> script and the contents passed to
PGP as the key.
<p>
This file MUST have 0400 permissions (read only by user)
and MUST owned by the user.  This is very dangerous;
but so is setting the PGPPASS environment variable.
<p>
<item>
Each server must have a private keyring for user daemon,
or whatever you run the LPD server as.  This must be in the
in the <tt>~daemon/.pgp</tt> directory, i.e. - the default used by PGP.
The daemon public key identification should have the format:
<verb>
daemon_hostname daemon <daemon_hostname@hostname>
</verb>
<p>
In addition,  the pass phrase for the daemon user must be
put in the file
<tt> ~daemon/.pgp/serverkey</tt>.
<p>
Note: you may have one daemon user with the same key for ALL
servers, or you may have different ones for different servers.
<item>
Each user who wants to send jobs to the server must have the
daemons public key on their public keyring.  The server must
have their public key on its public keyring.
<item>
When a job is submitted,  the server will search for the users
public key using the username submitted by the LPRng client.
This will be the same as their login name.
<item>
The LPRng software searches the printcap database for the tag
to be used for the server key lookup.  The :server_user:
value is checked for first;  if it is not found the configuration
user value (default 'daemon') is used.
</enum>
<sect2>Installing authenticate_pgp
<p>
By default, the
<tt>authenticate_pgp</tt> script and several helper
programs,  <tt>readfilecount</tt> and <tt>removeoneline</tt>,
will be installed in the standard executable locations.
<sect2>Printcap/Configuration Modifications
<p>
You must set the following printcap and/or configuration
variable to on.  Note that the printcap keys override the
configuration keys.
<verb>
user_auth_command=/usr/local/.../authenticate_pgp
use_auth=pgp
server_auth=pgp
server_user=daemon_id_for_server
pass_env=PGPPASS,PGPPATH
</verb>
<p>
Example printcap entry:
<verb>
pr: 
    :lp=pr@wayoff
    :use_auth=pgp
    :server_auth=pgp
    :server_user=daemon_wayoff
</verb>
<p>
<sect2>Permissions Modifications:
<p>
If you wish to enforce the use of authentication,  then you
should modify the lpd.perms file.  Here are some examples.
<verb>
   # force authentication
   REJECT AUTH=NONE
   # in addition to above,
   # do not accept forwarded authentication
   REJECT AUTH=FWD
   # if the above is too strong,  you can
   # reject forwarded authentication unless from specified server
   # note: U1, U2, etc. are the userids of the remote server
   REJECT AUTH=FWD NOT FWDUSER=U1@*,U2@*,U3@*
   # you can be paranoid and also check to see that that host
   # agrees with the userid reported.
   REJECT AUTH=FWD FWDUSER=U1@host1
</verb>
<sect2>Client/User Configuration
<p>
The user should either have the PGPPASS environment variable defined,
or have in his home directory the file ~/.pgp/clientkey.  This file will
be read by the script and used for the value of the PGPPASS
environment variable.  If neither of these is available,  then the
pgp program will interactively request this information.
<sect2>Checking authenticate_pgp Operation
<p>
<enum>
<item>In order to use the script in debug mode, you must first create
the user 'daemon' private keys on your private keyring for
testing purposes.
<verb>
pgp -kg
    User ID: daemon_test <daemon_test@host>
    Pass phrase: daemon_test
pgp -kg
    User ID: user_test <user_test@host>
    Pass phrase: user_test
</verb>
<item> Put the pass phrases for daemon_test in ~/.pgp/serverkey
and the pass phrases for user_test in ~/.pgp/clientkey
<verb>
echo user_test >~/.pgp/serverkey
echo user_test >~/.pgp/clientkey
</verb>
<item>  The first test will ensure that you have the pgp software
installed in an accessible manner.  Execute the script as a user:
<verb>
sh authenticate_pgp -D -nuser_test -Rdaemon_test
</verb>
<p>
The output should resemble:
<verb>
server PGPPASS daemon_test
ORIGKEY PRaTACFJGcQV92TE6bX72W2JHNNGPRIR7
SERVER AUTH TRANSFERFILE temp.str
398
-----BEGIN PGP MESSAGE-----
Version: 2.6.3i
...
-----END PGP MESSAGE-----
client PGPPASS user_test
</verb>
<p>
If there is an error, an error message will be printed.
</enum>
<sect2>Setting up and Testing Daemon Keys
<p>
The next test will make sure that the daemon user can access its
public and secret keyring when running as user daemon.
<enum>
<item>As a user, extract your user public key.  As the user do:
<verb>
pgp -kxa userid userid_public_key
</verb>
<item> You should have a user <tt>deamon </tt> with a home directory
that is owned and readable only by
<tt>daemon</tt>.
You do not need a password (logon) capabilities for
<tt>daemon</tt>.
<p>
Create the ~daemon/.pgp directory,  and make sure that
it has 0700 permissions.
<item> Use the SU command to run as <tt>daemon</tt>.
The SU command does not update the $HOME environment variable.
Do the following to set $HOME:
<verb>
export HOME=`eval echo ~daemon`
</verb>
<item> Create a pgp key for the daemon.  As user daemon do:
the following to create the permanent daemon PGP key
(<tt>host</tt> should be the host name for this server).
Set the pass phrase to a suitable value.
Make a note of these values - you will need them later.
<verb>
pgp -kg
   User ID: daemon_host <daemon_host@host>
   Pass phrase: ....
</verb>
<item> Extract the public key for daemon_host into
a file. As user daemon do:
<verb>
pgp -kxa daemon_host daemon_public_key
</verb>
<item> Add the user's public key to the user daemon public key ring.
The <tt>userid_public_key</tt> was created previously.
As daemon do:
<verb>
pgp -ka userid_public_key
</verb>
<item> Add the daemon's public key to the user public key ring.
As user do:
<verb>
pgp -ka daemon_public_key  (from step 4.5 above).
</verb>
<item> As user,
encrypt a message check to see if it can be decoded by daemon.  As user:
<verb>
echo hi >/tmp/msg
pgp -seat msg daemon_host -u userid -o /tmp/msg.pgp
chmod 777 /tmp/msg.pgp
</verb>
<p>
As daemon, check to see if you can decode this message.
<verb>
pgp /tmp/msg.pgp -o /tmp/msg.dec
diff /tmp/msg /tmp/msg.dec
</verb>
<item> As user daemon, encode a message for user and see if it can be
decoded by user.  As daemon:
<verb>
pgp -seat /tmp/msg.dec userid -o /tmp/msg.pgp -u daemon_host
chmod 777 /tmp/msg.pgp
</verb>
<p>
As user, decode the message for user.
<verb>
pgp /tmp/msg.pgp -o /tmp/msg.dec2
diff /tmp/msg.dec2 /tmp/msg
</verb>
</enum>
<sect2>Testing authenticate_pgp
<p>
This test will check the actual authentication process carried
out by
<tt>authenticate_pgp</tt>.
<enum>
<item>  Compile the
<tt>setupauth</tt>
program
and install it in a temporary location.
Note that
<tt>setupauth</tt>
is not
an installed part of the LPRng distribution, but is a
accessory.
<verb>
cd LPRng/src
make setupauth
mv setupauth /tmp/setupauth
</verb>
<item>
<tt>setupauth</tt> has the command line:
<verb>
setupauth clientid 'client command' serverid 'servercommand'
</verb>
<p>
For example:
<verb>
/tmp/setupauth \
   papowell "/bin/ksh -c 'echo CLIENT; printenv ;'" \
   papowell "/bin/ksh -c 'echo SERVER; printenv ;'"
</verb>
<p>
This would display the environment variables set up by
the setupauth program.  This are similar to those used
by LPRng.
<item>
The LPRng/src/AUTHENTICATE/testauth script has some simple
tests you might want to run.  Uncomment out the
appropriate ones and substitute the appropriate user
name for papowell and daemon.
<verb>
#!/bin/sh
user=${USER:-`whoami`}
# show environment variables
#/tmp/setupauth \
#   "${user}" "/bin/ksh -c 'echo CLIENT; printenv ;'" \
#   "${user}" "/bin/ksh -c 'echo SERVER; printenv ;'"
#
#exit 0;
#
# check bidirectionality of data transfer
#  /tmp/setupauth \
#   "${user}" "/bin/ksh -c 'echo CLIENT STARTING SENDING 1>&2; echo hi 1>&0;'" \
#   "${user}" "/bin/ksh -c 'echo SERVER READING; cat ;'" \
# sleep 3;
#  /tmp/setupauth \
#   "${user}" "/bin/ksh -c 'echo CLIENT STARTING READING; cat '" \
#   "${user}" "/bin/ksh -c 'echo SERVER SENDING 1>&2; echo hi 1>&0'"
# 
# check the authenticate pgp operation
#  Note: you can also use
#  'sh -x ./authenticate_pcp ...' to see the detailed actions.
#  Note: you must be root to run the next test.
#
rm /tmp/tempc /tmp/temps
echo Hi $$ >/tmp/tempc
chmod 777 /tmp/tempc
/tmp/setupauth \
    "${user}" './authenticate_pgp -C -n"${user}" -R"${user}" -T/tmp/tempc' \
    "${user}" './authenticate_pgp -S -n"${user}" -R"${user}" -T/tmp/temps'
rm /tmp/tempc /tmp/temps
echo Hi $$ >/tmp/tempc
chmod 777 /tmp/tempc
/tmp/setupauth \
    "${user}" './authenticate_pgp -C -n"${user}" -Rdaemon -T/tmp/tempc' \
    daemon './authenticate_pgp -S -n"${user}" -Rdaemon -T/tmp/temps'
</verb>
<item>
You must now SU to ROOT,
as <tt>testauth</tt> needs to run as root to change userids.
As <tt>root</tt>,
Set the USER environment variable to the user you desire to simulate and
execute the testauth script.
<verb>
USER=myself
/tmp/testauth
</verb>
<p>
</enum>
<sect2>Testing authentication
<p>
<enum>
<item>
Add the following lines to one of the printcap entries:
<verb>
:user_auth=pgp
:user_authentication_command=/usr/local/lib/authenticate_pgp
:server_auth=pgp
:server_authentication_command=/usr/local/lib/authenticate_pgp
</verb>
<item>restart the server, using  lpc lpd, to force it to read the
new printcap information.
<item> test the authentication facilities by using lpr to send a job
to the appropriate spool queue.
</enum>
<sect2>Key distribution
<p>
You can distribute the daemon PGP public key fairly easily -
see the PGP documentation for key server information.
<p>
Each daemon must have the user's PGP key installed,  or must
in some way get the key from a trusted server.  This is
very site dependent and needs to be done by each site administrator.
<sect1>Using Kerberos for Authentication
<p>
The LPRng
use of Kerberos authentication was
based on the
Kerberos5-1.0 release as of December 20, 1996.  This was obtained
from MIT:
<enum>
<item> ftp to ATHENA-DIST.MIT.EDU (18.159.0.42), login anonymous, password
your_email_address
<item> Change into the directory '/pub/kerberos/
<item> Get the README files and look at the details of using FTP to get
the distribution.  Note that there are also patches available
which you might want to use.
</enum>
<p>
Note that the distribution has only the most superficial documentation.
There are no man pages for any of the support libraries, etc. etc.
<label id="kerberoskeytab">
<label id="kerberoslife">
<label id="kerberosrenew">
<label id="kerberosservice">
<label id="kerberosserverprinciple">
<sect2>Kerberos Installation Procedure
<p>
<enum>
<item> Get the Kerberos 5 distribution.
<item> Compile and install the distribution.
<item> Create the /etc/krb5.conf, /usr/local/var/krb5kdc/kdc.conf
files using templates from the src/conf-files subdirectory.
See the Installation notes and the System Administrators Guide.
<item> Don't forget to create the /usr/local/var/krb5kdc/kdc.acl file;
I did and it took me HOURS to figure out what was wrong...
<item> Start up the KDC and KADMIN servers - you might want to put
the following in your rc.local or equivalent file:
<verb>
if [ -f /etc/krb5.conf -a -f /usr/local/var/krb5kdc/kdc.conf  ]; then
    echo -n ' krb5kdc ';    /usr/local/sbin/krb5kdc;
    echo -n ' kadmind ';    /usr/local/sbin/kadmind;
fi
</verb>
<item> use kadmin (or kadmin.local) to create principals for your users.
<item> Now you need to create principals for the lprng servers.  I have been
using:  lpr/hostname.REALM  as a template-
i.e. lpr/astart1.astart.com@astart.com for an example
<p>
Do this for all the servers.  You should use fully qualified domain names
for the principals.
<item> Now you need to extract the keytab for each of the servers:
<verb>
kadmin ...
ktadd -k file_for_host  lpr/hostname.REALM
</verb>
<p>
The 'file_for_host' contains the keytab information, which is the
equivalent information for the server.
<item> Copy the 'file_for_host' to the server (you might want to encrypt
or use a secure transfer for this).  You need to put this in
<tt>/etc/lpd.keytab</tt>.
Make sure that this file is readable only by user <tt>daemon</tt>,
as it will try to read the file to get its server key.
<verb>
#> ls -l /etc/lpd.keytab
-rw-------  1 daemon  wheel  128 Jan 16 11:06 /etc/lpd.keytab
</verb>
<item> Add the following entries to lpd.conf:
<verb>
use_auth=kerberos
default_auth=kerberos
kerberos_keytab=/etc/lpd.keytab
kerberos_service=lpr
kerberos_life=
kerberos_renew=
</verb>
<p>
The kerberos_keytab entry is the location of the keytab file;
kerberos_service is the service that will be used to generate
a server principal name.  This is the "lpr" that
appears in the above operations.
<p>
kerberos_life and kerberos_renew determine the lifetime and renewability
of Kerberos tickets.  The lifetime defaults to 10
hours,  and the ticket will be refreshed when it expires
if necessary.
<item>  You might like to check out the authentication using the sclient and
sserver test programs.  These link in the kerberos authentication and
allow you to test it without all of LPD being involved.
<verb>
cd LPRng/src; make sserver sclient
usage: sserver [-D] [-p port] [-s service] [-S keytab] file
   -D turns debugging on
   1. opens TCP   port 'port' (default 1234)
   2. waits for a connection
   3. when a connection comes in,  uses 'service' to get the principal
       name of the server,  and looks up the key in keytab file.
   4. Goes through the kerberos authentication.
   5. Copies the input from remote server to 'file'
   6. exits.
 usage: sclient [-D] [-p port] [-s service] host file
   -D turns debugging on
   1. opens a connection to port on host (i.e. - host%port)
   2. does the authentication.  You must have done kinit to get
       for your ticket to be valid.
   3. sends the file to remote host.
</verb>
<p>
To test this, start up sserver on one host/window, then run sclient.
The error messages are pretty straight forward,
and when in doubt,
look at the source code which has more than sufficient information.
</enum>
<sect2>Testing Transfers
<p>
Restart the server,
and then try getting information using LPQ.
<p>
You can turn on tracing at LPQ to see if authentication is being used
and is working:
<verb>
lpq -Dnetwork,database 
</verb>
<p>
If the lpq works,
then try send a job and see if the transfer is successful.
<sect2>Explicit Server Principal Name
<p>
If you are using printers in different domains,  then you can
put the explicit principal name of the server in the printcap file,
using the server_principal entry.  For example:
<verb>
lp_offsite
    :lp=printer@erehwon.org
    :use_auth=kerberos
    :kerberos_server_principal=lpr/erehwon.org@BLUESKY.ORG
</verb>
<label id="statusref">
<label id="stalledtime">
<sect>Status Monitoring and Logging
<p>
Options used:
<itemize>
<item><tt>stalled_time#</tt><em>time after which to report a stalled active job
</em></itemize>
<p>
The most commonly used tool for LPRng status is LPQ.
However,
the LPC command can be used,
and you can also get real time logging of status to a remote host.
<p>
<sect1>LPQ status reporting
<p>
The LPQ status display produced by LPRng has three formats.
<sect1>LPQ Short Format (lpq -s)
<p>
This is one line per spool queue:
<verb>
% lpq -sa
t1@astart110  (printing disabled) 1 job
t2@astart110  (routed/bounce to t1@astart110.astart.com) 0 jobs
t3@astart110  (forwarding to t3a@astart110.astart.com)
t3a@astart110  (forwarding to t2@astart110.astart.com)
t4@astart110  (subservers t5, t6)  0 jobs
t5@astart110  (serving t4) 0 jobs
t6@astart110  (serving t4) 0 jobs
</verb>
<p>
Note that the name of the printer/host is first,
followed by optional status information, followed by
the number of jobs.  Only printcap entries with
spool queues have a jobs word in the last position.
The
<tt>-a</tt> option forces status for all queues or the
queues in the <tt>all</tt> printcap entry to be returned.
<p>
The <tt>stalled_time</tt> (default 120 seconds) printcap option can be used to set a
time after which active jobs will be reported as stalled.
<sect1>LPQ Long Format (lpq default)
<p>
This is the default status display.
It is a nicely formatted, extremely verbose format
that is suitable for humble human interpretation. For example:
<verb>
% lpq -a
Printer: t1@astart110  'Test Printer 1' (printing disabled)
 Queue: 1 printable job
 Server: no server active
 Status: finished operations at 09:44:00
 Rank   Owner/ID                   Class Job  Files               Size Time    
1       papowell@astart110+202228663    A 10663 /tmp/hi               3 20:22:29
Printer: t2@astart110  'Test Printer 2' (routed/bounce to t1@astart110.astart.com)
 Queue: no printable jobs in queue
 Status: finished operations at 16:30:08
Printer: t3@astart110  (forwarding to t3a@astart110.astart.com)
Printer: t3a@astart110  (forwarding to t2@astart110.astart.com)
Printer: t4@astart110  (subservers t5, t6) 
 Queue: no printable jobs in queue
 Status: finished operations at 09:44:06
Server Printer: t5@astart110  (serving t4)
 Queue: no printable jobs in queue
 Status: finished operations at 09:44:06
Server Printer: t6@astart110  (serving t4)
 Queue: no printable jobs in queue
 Status: finished operations at 09:10:00
</verb>
<sect1>LPQ Verbose Format (lpq -v)
<p>
This uses an extension to the RFC1179 protocol,
and is supported only by LPRng.
The amount of information displayed is the brutal,
and in effect does a total database dump
of the LPD.
This has been developed in order to provide diagnostic
and status information for databases that need to keep track of
job progress through a spool queue.
<verb>
% lpq -v
Printer: t1@astart110
 Comment: Test Printer 1
 Printing: no
 Spooling: yes
 Queue: 1 printable job
 Server: no server active
 Status: accounting at end 'papowell@astart110+094352860' at 09:44:00
 Status: printing 'papowell@astart110+094352860', closing device at 09:44:00
 Status: printing 'papowell@astart110+094352860', finished  at 09:44:00
 Status: subserver status 'JSUCC' for 'papowell@astart110+094352860' \
            on attempt 1 at 09:44:00
 Status: finished operations at 09:44:00
 Job: papowell@astart110+202228663 status= 1
 Job: papowell@astart110+202228663 CONTROL=
 - Hastart110.astart.com
 - Ppapowell
 - J/tmp/hi
 - CA
 - Lpapowell
 - Apapowell@astart110+202228663
 - Qt1
 - fdfA010663astart110.astart.com
 - N/tmp/hi
 - UdfA010663astart110.astart.com
 Job: papowell@astart110+202228663 HOLDFILE=
 - active 0
 - done 0
 - hold 0
 - move 0
....
</verb>
<sect1>lpc status
<p>
The LPC status command is used to show the status of the queues
currently being managed by the LPRng server.
Note that this form of the command is supported only by LPRng,
and is not backwards compatible with BSD LPR implementations.
<verb>
%lpc status all
 Printer           Printing Spooling Jobs  Server   Slave Redirect Status/Debug
lw4@astart4         enabled  enabled    0    none    none
lw5@astart4         enabled  enabled    0    none    none
</verb>
<p>
The status display has a heading line and summary of the server status.
<sect1>Remote Logger Operation
<p>
Several sites have wanted a way to provide central logging of job
status and/or information.  In order to do this,  the following functionality
is provided with LPRng.
<label id="loggerdestination">
<label id="defaultloggerport">
<label id="defaultloggerprotocol">
<sect2>Logger Network Communication
<p>
Options used:
<itemize>
<item><tt>default_logger_port=</tt><em>default destination port for logging information</em>
<item><tt>default_logger_protocol=</tt><em>default protocol for logging information</em>
<item><tt>logger_destination=</tt><em>logger information destination</em>
</itemize>
<p>
The printcap/configuration variable <tt>logger_destination</tt> specifies
a destination in the following format:
<verb>
host[%port][/(TCP|UPD)]
</verb>
<p>
Host is the destination host, and can be a name or IP address.
Port is the port on the destination host;
if not specified the <tt>default_logger_port</tt> (default 2001) is  used.
TCP or UDP is the protocol to be used;
if not specified the <tt>default_logger_protocol</tt> (default TCP) is  used.
<sect2>Logger Message Format
<p>
Messages are sent to the logger with the following format.
They are written as a single TCP write or UDP message.
<verb>
IDENTIFIER id AT timestamp
STATUS | TRACE | FILTER_STATUS PID nnnn
  ... data
.
</verb>
<p>
Examples:
<verb>
IDENTIFIER papowell@astart1+560 at Sat Aug 31 08:47:19 1996
STATUS
printing 'cfA560astart1.astart.com', sending trailer
.
IDENTIFIER papowell@astart1+560 at Sat Aug 31 08:47:19 1996
STATUS
accounting at end 'cfA560astart1.astart.com'
.
IDENTIFIER papowell@astart1+560 at Sat Aug 31 08:47:19 1996
STATUS
job 'cfA560astart1.astart.com', closing device
.
IDENTIFIER papowell@astart1+560 at Sat Aug 31 08:47:19 1996
FILTER_STATUS PID 2564
FINISHED
.
IDENTIFIER papowell@astart1+560 at Sat Aug 31 08:47:19 1996
STATUS
job 'cfA560astart1.astart.com' finished
.
IDENTIFIER papowell@astart1+560 at Sat Aug 31 08:47:19 1996
STATUS
subserver status 'SUCCESS' for 'papowell@astart1+560' on attempt 1
.
IDENTIFIER papowell@astart1+560 at Sat Aug 31 08:47:19 1996
TRACE
t1@astart1.astart.com: job printed
.
</verb>
<p>
The IDENTIFIER line reports the job identifier and timestamp
for the following message.  The STATUS, TRACE, and FILTER_STATUS
messages identify the information that follows.
<label id="mail">
<sect2>LPR -mhost%port and user logging support
<p>
The <tt>lpr -m</tt> option is used to request that <tt>lpd</tt>
send mail to the user when a job has completed.
LPRng extends this to allow mail addresses of the form
<tt> host[%port][/(TCP|UPD)]</tt> to request that logging information
be sent to the user as well.
<p>
The administrator should be aware that this is a possible security loophole,
and that the
<label id="allowuserlogging">
<tt>allow_user_logging</tt>
flag must be enabled to
allow this operation.
<label id="rfc1179ref">
<sect>RFC 1179 - Line Printer Daemon Protocol
<p>
RFC1179 can be obtained from the LPRng distribution, in the DOC/rfc1179 directory,
or from one of many sites which mirror the RFCs.
<p>
This RFC is an <em>informational</em> RFC,
which means that the information in it is meant as a guide to users,
and not as a fixed standard.
In addition,
the RFC tried to document the behavior of the BSD LPD print server,
and left out many details dealing with error recover,
error messages,
extensions to the protocol,
etc.
<p>
In this section,
I will try to explain what RFC1179 specifies as a protocol,
and many of the problems encountered in trying to use it.
<label id="lpdport">
<label id="originateport">
<label id="reuseaddr">
<label id="retryeconnrefused">
<label id="retrynolink">
<label id="socketlinger">
<sect1>Ports and Connections
<p>
Options used:
<itemize>
<item> <tt>lpd_port=</tt><em>Port for LPD to accept connection</em>
<item> <tt>originate_port=</tt><em>Ports to originate connections on</em>
<item> <tt>reuse_addr</tt><em>&nbsp;&nbsp;Set SO_REUSEADDR flag on connection</em>
<item> <tt>retry_econnrefused</tt><em>&nbsp;&nbsp;Retry on connect ECONNREFUSED error</em>
<item> <tt>retry_nolink</tt><em>&nbsp;&nbsp;Retry on device open or connection ffailure</em>
<item> <tt>socket_linger#</tt><em>&nbsp;&nbsp;Linger time for sockets</em>
</itemize>
<p>
RFC1179 requires that the <tt>lpd</tt> server listen for TCP/IP connections
on port 515.
This port is registered with the Internet Naming Authority,
and the <tt>/etc/services</tt> file or TCP/IP services database usually has an entry:
<verb>
printer     515/tcp     spooler     # line printer spooler
</verb>
<p>
RFC1179 explicitly states that all connections to port 515 must originate from
ports 721-731.
The reason for this restriction is due to the UNIX concept of <em>reserved</em>
and <em>privileged</em> ports.
By convention,
ports in the range 1-1023 can only <bf>bound</bf> by processes whose Effective User ID (EUID)
is 0 (root).
This,
ordinary users could not originate a connection from the reserved or privileged port range.
<p>
In a UNIX environment,  this means that the user programs
<tt>lpr</tt>,
<tt>lprm</tt>,
<tt>lpq</tt>,
and
<tt>lpc</tt>
would have to be SETUID root.
<p>
As experience has shown, for security purposes,
the fewer programs that need to have privileged status,
the better.
LPRng uses the
<tt>lpd_port=printer</tt> configuration option to set the actual port to be use.
By default, this is port 515, but can be set to other values.
<p>
The restriction of originating ports to 721-731 causes another set of problems.
Part of the TCP/IP protocol is concerned with avoiding communications problems
resulting from the arrival of old or <em>stale</em> packets.
When a connection between
<tt>sourcehost, sourceport</tt> and <tt>desthost, destport</tt>
is made,
a set of sequence numbers is established and used for sending and acknowledgement of data.
When the connection terminates,
the TCP/IP protocol restricts the establishment of a new connection between
<tt>sourcehost, sourceport</tt> and <tt>desthost, destport</tt> for a period long
enough for all <em>stale</em> packets to be removed from the system.
This is approximately 10 minutes long.
<p>
In order to simplify assignments of ports,
timing out connections, and other matters,
many TCP/IP packages do keep track of explicit connections
<em>originating</em> from a port,
but simply prevent the port from being reused for either origination
or reception of a connection.
They do,
however,
keep track of the active connections <bf>to</bf> a port,
and perform timeouts on these.
This is usually much simpler to implement,
as it can be done with a list attached to the port.
<p>
This implementation method creates some problems when
a large number of connections must be originated from a
relatively small number of port numbers.
Observe what happens when host 1 tries to send a large number of jobs to a server 2.
The following connections are established and terminated:
<newline><tt>host 1, port 721</tt> and <tt>host 2, port 515</tt>
<newline><tt>host 1, port 722</tt> and <tt>host 2, port 515</tt>
<newline><tt>host 1, port 723</tt> and <tt>host 2, port 515</tt>
<newline><tt>host 1, port 724</tt> and <tt>host 2, port 515</tt>
<newline><tt>host 1, port 725</tt> and <tt>host 2, port 515</tt>
<newline><tt>host 1, port 726</tt> and <tt>host 2, port 515</tt>
<newline><tt>host 1, port 727</tt> and <tt>host 2, port 515</tt>
<newline><tt>host 1, port 728</tt> and <tt>host 2, port 515</tt>
<newline><tt>host 1, port 729</tt> and <tt>host 2, port 515</tt>
<newline><tt>host 1, port 730</tt> and <tt>host 2, port 515</tt>
<newline><tt>host 1, port 731</tt> and <tt>host 2, port 515</tt>
<p>
Now according to the RFC1179 rules and the TCP/IP protocol,
we will have to wait until one of these connections terminates before we
can make another.
On the originating system,
if the TCP/IP implementation does timeouts on the originating port,
we will have to wait for the timeout to elapse before we can make a new
connection.
Unfortunately,  there is no way to find out what the status of the port
is,  so we will have to try them each in turn until we get
a successful connection.
<p>
The LPRng code has tried to provide several methods to deal with
these problems.
Firstly,
the
<tt>originate_port=512 1023</tt>
option specifies the range
of ports used to originate connections
when the software is running either as ROOT or SETUID root.
By strict RFC1179 rules,
this should be
<tt>originate_port=721 731</tt>,
but it turns out that most BSD LPD based implementations only
check for a <em>reserved</em> originating port.
By using 512 ports we get a greatly reduced rate of errors due
to lack of ports due to pending timeouts.
<p>
However,
on some systems which are acting as servers for a large number of
printers even increasing this port range is insufficient,
and steps need to be taken use the originating port numbers
more efficiently.
The Berkeley TCP/IP implementation
<tt>getsockopt()</tt>
and
<tt>setsockopt()</tt>
allows the user to manipulate some of the underlying timeouts and options
of the TCP/IP network.
When a TCP/IP connection is established,
the
<tt>setsockopt()</tt>
facility can be used to set the
<tt>SO_REUSEADDR</tt>
flag on the connection.
This flag effectively sets the timeout value on the ports
and connections to 0,
allowing immediate reuse of the ports.
When done on an originating end of a connection,
this will allow the originating port number to be reused immediately.
<p>
It would appear that by setting
<tt>SO_REUSEADDR</tt>
on the originating end that we have solved our problems.
However,
unless the destination end of the connection sets its
<tt>SO_REUSEADDR</tt>
flag on the connection,
it will still do a timeout.
Thus when we try to make a connection from a port
that was active within a short period of time to the
same host,
then it will reject the connection until the
timeout is over.
<p>
The
<tt>reuse_addr</tt>
flag (default off) forces
the LPRng software to set the
<tt>SO_REUSEADDR</tt>
flag on originating connections.
As indicated,
this will allow ports to be reused immediately for outgoing connections,
rather than waiting for a timeout.
<p>
While the
<tt>reuse_addr</tt>
flag usually allows us to reuse ports,
there is still the problem of dealing with connections failing due to the
remote site rejecting the connection due to a pending timeout
from a previous connection.
A careful study of the original BSD TCP/IP network code and of some
others indicates that when a connection fails due to a pending timeout,
an ECONNREFUSED error code is returned to a
<tt>connect()</tt> system call.
If this happens and we suspect that the remote site is rejecting
the connection due to a timeout problem,
then we should retry making the connection but from a new port,
and continue retrying until all possible ports are used.
<p>
The <tt>retry_econnrefused</tt> (default on) flag is used to
specify that we retry connections in this manner.
When this is set,
a <tt>connection refused</tt>
error causes the connection to be retried using a new port.
This will be repeated until all available ports have been tried.
<p>
When 
printing a job and the <tt>lpd</tt> server connection to a remote
site or device open fails,
the <tt>retry_nolink</tt> (default on)
will cause the attempt to be retried indefinately.
The combination of <tt>retry_econnrefused</tt> and <tt>retry_nolink</tt>
will provide robust connection attempts to remote systems.
<p>
While the above problems cause difficulties when making connections,
there are also problems when terminating connections.
After closing a socket,
the TCP/IP software will try to flush any pending data to the destination.
Unfortunately,
on some systems it will only do this while the process is active.
This has caused problems on systems which terminate
a process it has received an abnormal (signal caused) termination.
<p>
The <tt>setsockopt()</tt> SO_LINGER option allows the user to specify
that when a socket is closed normally,
that the process should block until pending data is flushed or
for the <tt>socket_linger</tt> period.
If <tt>socket_linger</tt> is 0,
then no SO_LINGER operation is done.
<p>
In summary, if you experience problems with connection failures due
to port exhaustion,
first try setting the
<tt>reuse_port</tt> flag,
and you should see a reduction.
Check to ensure that the <tt>retry_econnrefused</tt>
and <tt>retry_nolink</tt> flags are set,
and the error code in the log and status files.
If the failures continue,  then the problem is caused by the
remote end having timeout limitations and there is little you
can do except to set a very long <tt>connect_retry</tt>
interval, say <tt>connect_retry=120</tt> (2 minutes).
<label id="remotesupport">
<sect1>Protocol Requests and Replies
<p>
Options used:
<itemize>
<item> <tt>remote_support=</tt><em>Remote operations supported</em>
</itemize>
<p>
After a connection has been established,
a request can be sent to the <tt>lpd</tt>
server.
The request consists of a single octet indicating the request type,
followed by the printer (or print queue) name, followed by
a set of options for the request,
followed by a LF (line feed) character.
<verb>
\NNNprinter[ options]\n
  NNN    Operation
</verb>
<p>
<table>
<tabular ca="|l|l|l|l|">
NNN
|
RFC1179
|
Operation
|
program
@
1
|
yes
|
start print
|
<tt>lpc</tt>
@
2
|
yes
|
transfer a printer job
|
<tt>lpr</tt>
@
3
|
yes
|
print short form of queue status
|
<tt>lpq</tt>
@
4
|
yes
|
print long form of queue status
|
<tt>lpq</tt>
@
5
|
yes
|
remove jobs
|
<tt>lprm</tt>
@
6
|
LPRng
|
do control operation
|
<tt>lpc</tt>
@
7
|
LPRng
|
transfer a block format print job
|
<tt>lpr</tt>
@
8
|
LPRng
|
secure command transfer
|
<tt>lpc</tt>
@
9
|
LPRng
|
verbose status information
|
<tt>lpq</tt>
@
</tabular>
</table>
<p>
After the request has been sent,
then a reply will be returned.
In general the reply has the following form:
<verb>
\000\n    Success
\NNN\n    Failure (NNN is error code)
text\n    Text or status information
</verb>
<p>
As can be seen,
this protocol is extremely simple,
but there are a set of problems due to the loosely written language of RFC1179.
<enum>
<item>
Firstly,
while RFC1179 sets limits on the lengths of commands,
it does not strictly set limits on the characters set used in the commands.
This can result in problems when trying to print status information,
headers on banners,
and other details.
<item>
The original RFC1179 protocol did not provide any way to do remote control
of queues or LPD servers.
This has been added to the protocol.
As a side effect,
if you try to use
<tt>lpc</tt> to control a non-LPRng printer,
it will not work.
<item>
You can specify that a network printer is non-LPRng by using the
<tt>remote_support=RQVMC</tt> option.
The letters R, Q, V, M, and C stand for
<tt>lpr</tt>,
<tt>lpq</tt>,
<tt>lpq -v</tt> (verbose),
verbose <tt>lpq</tt>,
<tt>lprm</tt>,
and <tt>lpc</tt> operations respectively.
If <tt>remote_support</tt> does not allow a particular operation,
then the LPRng software will not send a corresponding request to the printer.
</enum>
<label id="senddatafirst">
<label id="longnumber">
<label id="useshorthost">
<sect1>Job Transfer
<p>
Options used:
<itemize>
<item> <tt>longnumber</tt><em>&nbsp;&nbsp;Long job number (6 digits)</em>
<item> <tt>send_data_first</tt><em>&nbsp;&nbsp;Send data files first</em>
<item> <tt>use_shorthost</tt><em>&nbsp;&nbsp;Use short hostname</em>
</itemize>
<p>
A job transfer operation starts with a job transfer request,
followed by several file transfer operations.
At the end of the file transfers,
the connection should be closed.
<p>
A file transfer request has the form:
<table>
<tabular ca="|l|l|l|l|">
Command
|
Purpose
@
\001\n
|
abort
@
\002nnnn cfname
|
control file transfer
@
\003nnnn dfname
|
data file transfer
@
</tabular>
</table>
<p>
The abort operation is used to terminate job transfer and indicate that
the job should not be processed for printing.
The connection will be closed and the partly transferred job
will be discarded.
<p>
The control file and data file transfer commands have a length (in bytes)
of the file and the name of the file to be transferred.
When the command is received,
the server will reply with a status line:
<table>
<tabular ca="|l|l|l|l|">
Status
|
Purpose
@
\000
|
Accepted, proceed
@
\nnn
|
Rejected with error code
@
</tabular>
</table>
<p>
The reply is only a single octet.
Some defective implementations of RFC1179 send a LF after the octet,
which makes life very difficult.
LPRng makes an effort to detect these non-conforming RFC1179 systems
and will accept jobs from them.
However,  it will not send jobs to them.
<p>
If LPRng sends a reject code, as an extension to RFC1179 it also
sends an error message.   Note that the values for error codes
are not defined,
nor are their causes.
LPRng uses the following values for error codes,
which appear to be compatible with many,
but not all, of the BSD LPD based systems:
<table>
<tabular ca="|l|l|l|l|">
Code
|
Error
@
\000
|
Accepted, proceed
@
\001
|
Queue not accepting jobs
@
\002
|
Queue temporarily full, retry later
@
\003
|
Bad job format, do not retry
@
</tabular>
</table>
<p>
When the sender gets the reply indicating success,
it sends the <tt>nnnn</tt> bytes of the control or data file,
followed by a <tt>\000</tt> octet.
The receiver will then reply as above;
a single <tt>\000</tt> octet indicating success.
<p>
The above procedure is carried out until all data files and the control
file of a job are transferred.
<p>
RFC1179 is silent on the following issues:
<enum>
<item>
When sending a job,
do you send the control file first, followed by the data file(s),
or the data files first?
<item>
When sending multiple jobs,
can you send them on a single connection,
or do you have to establish a new connection for each job?
</enum>
<p>
LPRng will <em>accept</em> jobs whether they are sent control or data files
first.
By default,
it sends the control file first,
followed by the data file.
If the destination system requires that the data files
be sent first,
the <tt>send_data_first</tt> printcap option can be used to force
data files to be sent first.
<p>
RFC1179 states that:
<quote>
The name of the control file ... should start with ASCII "cfA", followed by a three
digit job number, followed by the host name which has constructed the
control file.
</quote>
<p>
The <em>should</em> in this wording indicates that this is simply a guideline,
and that other formats are possible.
Some of the major problems with this format are as follows:
<enum>
<item> The restriction to 3 digits means that at most 1000 jobs
can be in a queue.
Strangely,  some systems generate far more than 1000 jobs a day,
and need to archive them on a regular basis.
The <tt>longnumber</tt> option will allow LPRng to use a 6 digit
job number for files in the print queue.
<item>The host name format is not specified.
Some implementations consider that this is the short host name,
while others think it is the fully qualified domain name (FQDN).
LPRng,
by default,
will use the FQDN host name.
However,  the <tt>use_shorthost</tt> option will force it to
use short host names in control and data files.
<item>
The <tt>cfA</tt> control file name was modified to allow the
job priority to be used as the A letter of the control file.
By default,
this is A (lowest, i.e. <tt>cfA</tt>) and
but can range to Z (highest, i.e. <tt>cfZ</tt>).
All known spoolers except LPRng seem to ignore the actual value of
the letter.
</enum>
<sect1>Data File Transfer
<p>
As discussed,
a data file is transferred using the command below.
<table>
<tabular ca="|l|l|l|l|">
Command
|
Purpose
@
\003nnnn dfname
|
data file transfer
@
</tabular>
</table>
<p>
From RFC1179:
<quote>
The data file may contain any 8 bit values at all.  The total number
of bytes in the stream may be sent as the first operand, otherwise
the field should be cleared to 0.  The name of the data file should
start with ASCII "dfA".  This should be followed by a three digit job
number.  The job number should be followed by the host name which has
constructed the data file.  Interpretation of the contents of the
data file is determined by the contents of the corresponding control
file.
</quote>
<p>
There are several surprises in RFC1179.
<enum>
<item>
Apparently a job should only consist of a single data file.
This is a severe limitation,  and in fact the BSD LPR and other
print spoolers process jobs with multiple data files.
By convention, these data files have names of the form
<tt>dfA</tt>,
<tt>dfB</tt>,
...
<tt>dfZ</tt>,
<tt>dfa</tt>,
<tt>dfz</tt>.
<item>
The RFC does not specify that the control file and data file job numbers
must be identical.
Most implementations follow this convention, which simplifies life
tremendously.
<item>
The RFC does not specify that the control file and data file job host names
must be identical.
Most implementations follow this convention, which simplifies life
tremendously.
<item>
A zero length data file does not cause a data transfer to take place.
LPRng modifies this action to be slightly different.
When a zero length data file transfer is indicated,
all of the input until the connection is closed is used as the
contents of the data file.
<p>
When 'piping' into the <tt>lpr</tt> program,
this can be very useful as it eliminates the need to create temporary
files on the senders host.
The <tt>lpr -k</tt> option for details.
Note that some print spoolers do not use this interpretation,
and this option should be used carefully.
</enum>
<sect1>Control File Contents
<p>
The control file consists of a set of lines which either provide
printing information or specify data files to be printed.
The information lines start with upper case letters or digits,
while the data files lines start with lower case letters.
Here is a sample control file:
<verb>
Hastart4.astart.com
J(stdin)
CA
Lpapowell
Apapowell@astart4+955
Ppapowell
fdfA955astart4.astart.com
N(stdin)
UdfA955astart4.astart.com
</verb>
<p>
The following are the letters and their meanings in the control file.
<table>
<tabular ca="|l|l|l|l|">
X
|
RFC1179
|
Meaning
@
A
|
LPRng
|
Identifier for job
@
C
|
RFC1179
|
Class for banner page
@
H
|
RFC1179
|
Host name
@
I
|
RFC1179
|
Indent Printing
@
J
|
RFC1179
|
Job name for banner page
@
L
|
RFC1179
|
Print banner page
@
M
|
RFC1179
|
Mail When Printed
@
N
|
RFC1179
|
Name of source file
@
P
|
RFC1179
|
User identification
@
Q
|
LPRng
|
Queue name
@
R
|
LPRng
|
Accounting info
@
S
|
RFC1179
|
Symbolic link data
@
T
|
RFC1179
|
Title for pr
@
U
|
RFC1179
|
Unlink data file
@
W
|
RFC1179
|
Width of output
@
Z
|
LPRng
|
Filter options
@
1
|
RFC1179
|
troff R font
@
2
|
RFC1179
|
troff I font
@
3
|
RFC1179
|
troff B font
@
4
|
RFC1179
|
troff S font
@
c
|
RFC1179
|
Plot CIF file
@
d
|
RFC1179
|
Print DVI file
@
f
|
RFC1179
|
Print formatted file
@
g
|
RFC1179
|
Plot file
@
k
|
RFC1179
|
Reserved for use by Kerberized LPR clients and servers.
@
l
|
RFC1179
|
Print file leaving control characters
@
n
|
RFC1179
|
Print ditroff output file
@
o
|
RFC1179
|
Print Postscript output file
@
p
|
RFC1179
|
Print file with 'pr' format
@
r
|
RFC1179
|
File to print with FORTRAN carriage control
@
t
|
RFC1179
|
Print troff output file
@
v
|
RFC1179
|
Print raster file
@
z
|
RFC1179
|
Reserved for future use with the Palladium print system.
@
</tabular>
</table>
<p>
The
<tt>A</tt> (Identifier)
line was introduced to record a unique
system wide job identifier for LPRng submitted jobs.
This is basically formed from the user name,
job number, and host at the time of submission.
For example: <tt>papowell@astart4+955</tt>
is job number 995 submitted by papowell from host astart4.
<p>
The
<tt>C</tt> (Class)
line is set by the <tt>lpr -C class</tt> option,
and the value can be used to control printing.
For example,
the <tt>lpc class zone </tt> command would restrict job printing to
only jobs with class <tt>zone</tt>.
<p>
The
<tt>H</tt> (hostname),
<tt>P</tt> (username),
and
<tt>J</tt> (jobname)
fields are used to identify the host and user which sent the job,
and to provide information to be displayed by <tt>lpq</tt>
when reporting job status.
<p>
The
<tt>L</tt> (print banner page) field is one that has caused many
problems for users.
RFC1179 indicates that its presence causes the banner page to be printed,
and its absense suppresses banner pages.
The <tt>lpr -h</tt> option suppresses putting this line into the
control file.
Usually the <tt>L</tt> field is a duplicate of the <tt>P</tt>
field.
<p>
The <tt>M</tt> (mail information)
field supplies a mail address for LPRng to send mail to when
a job is completed.
See
<ref id="mail" name="LPR -m and user logging">
for more details.
<p>
The <tt>N</tt> (file name) field is usually provided to identify
the file name corresponding to the data file.
This can be used to print names on page separators, etc.
LPRng largely ignores this line.
<p>
The
<tt>I</tt> (indent)
and
<tt>W</tt> (width)
fields are supposed to specify a page indent and width for printing.
These fields are passed to filters if they are present.
<p>
The <tt>Q</tt> (queue name)
field is an LPRng extension,
and contains the name of the print queue the job was originally sent to.
See
<ref id="qq" name="qq printcap option"> for details.
<p>
The <tt>R</tt> (accounting info) field was added by LPRng to allow
a specified account to be billed for job printing.
The <tt>lpr -Rname</tt> option can be used to specify the accounting name.
<p>
The
<tt>S</tt> (symbolic link)
and
<tt>U</tt> (unlink after printing)
lines were used by the original BSD LPD print system to control
how it passed files to the print server.
LPRng ignores these lines.
In fact, it will remove <tt>S</tt> lines and force the <tt>U</tt>
lines to refer only to job data files.
This closes a nasty security loophole on non-LPRng print spoolers.
<p>
The <tt>T</tt> (pr job title) is used with the <tt>lpr -p</tt>
operation to supply a banner to the <tt>pr</tt> program.
<p>
The <tt>Z</tt> (filter options) value is specified with
<tt>lpr -Zoption</tt> and is passed to the data file filters
during the printing operation.
See
<ref id="secfilter" name="Filters"> for details on how the
this is used during the printing process.
<p>
All of the lower case letters are reserved for format specifications for
data files.
In the control file, these are followed by the name of the data file
to which they correspond.
While in principle different data files in the control file can have
different formats,
this has not been implemented in any known spooling system.
See
<ref id="secfilter" name="Filters"> for details on how the
data file formats are used during the printing process.
<sect1>LPQ Requests
<p>
The RFC1179 protocol specifies that <tt>lpq</tt> print status
requests can be sent to the <tt>lpd</tt> server.
The lpq requests have the format:
<verb>
\003printer [id]* \n    short
\004printer [id]* \n    long
\009printer [id]* \n    LPRng extension- verbose
</verb>
<p>
The <tt>lpd</tt> print server will then return queue status
and close the data connection.
<p>
RFC1179 does not state in any manner what the format of the queue status
should be.
Thus, implementors have been free to augment or change the status as
they like.
Even the BSD LPR status format has been changed from different versions.
<p>
See
<ref id="statusref" name="Status Monitoring and Logging">
for information on the formats returned.
<p>
The <tt>id</tt> values are used to select the jobs to be displayed.
LPRng displays any job whose ID, hostname, or user name information
from the control file
<tt>A</tt>,
<tt>H</tt>,
or
<tt>P</tt> fields match any of the id values.
<p>
Note that since there is no identification of the information requestor,
then restriction of information is almost impossible.
<p>
<sect1>LPRM Requests
<p>
The RFC1179 protocol specifies that <tt>lprm</tt> job removal
requests can be sent to the <tt>lpd</tt> server.
The lpq requests have the format:
<verb>
\003printer user [id]* \n
</verb>
<p>
The <tt>lpd</tt> print server will search the specified print queue
and remove any job whose ID, hostname, or user name information
from the control file
<tt>A</tt>,
<tt>H</tt>,
or
<tt>P</tt> fields match any of the id values
and for which the user has permission to perform a removal operation.
See the
<ref id="lpdpermsref" name="/etc/lpd.perms"> file for details on
permissions.
<p>
Most RFC1179 compatible spoolers use the user information in the
request as the name of the user which spooled the job.
However,
in a network environment this is extremely easy to fabricate,
and is at best a weak type of authentication.
<label id="lpcreread">
<sect1>LPC Requests
<p>
LPRng has extended the RFC1179 protocol to allow queue and printer control
commands to be sent to the LPD server.
The format of these commands are:
<p>
<verb>
\006printer user key [options]
</verb>
<p>
The following commands are supported.
<table>
<tabular ca="|l|l|l|l|">
Command
|
Operation
@
<tt> active [printer[@host]]</tt>
|
check to see if server accepting connections
@
<tt> abort   (printer[@host] | all)  </tt>
|
terminate server process printing job
@
<tt> disable (printer[@host] | all)  </tt>
|
disable queueing
@
<tt> debug   (printer[@host] | all) debugparms </tt>
|
set debug level for printer
@
<tt> enable  (printer[@host] | all)  </tt>
|
enable  queueing
@
<tt> hold    (printer[@host] | all) (name[@host] | job | all)* </tt>
|
hold job
@
<tt> holdall (printer[@host] | all)  </tt>
|
hold all jobs on
@
<tt> kill    (printer[@host] | all)  </tt>
|
stop and restart server
@
<tt> lpd [printer[@host]]  </tt>
|
get LPD PID for server
@
<tt> lpq (printer[@host] | all) (name[@host] | job | all)*     </tt>
|
invoke LPQ
@
<tt> lprm (printer[@host] | all) (name[@host]|host|job| all)*  </tt>
|
invoke LPRM
@
<tt> move printer (user|jobid)* target </tt>
|
move jobs to new queue
@
<tt> noholdall (printer[@host] | all)  </tt>
|
hold all jobs off
@
<tt> printcap (printer[@host] | all) </tt>
|
report printcap values
@
<tt> quit                            </tt>
|
exit LPC
@
<tt> redirect (printer[@host] | all) (printer@host | off )*    </tt>
|
redirect jobs
@
<tt> release  (printer[@host] | all) (name[@host] | job | all)* </tt>
|
release job
@
<tt> reread [printer[@host]]</tt>
|
LPD reread database information
@
<tt> start   (printer[@host] | all)  </tt>
|
start printing
@
<tt> status  (printer[@host] | all)  </tt>
|
status of printers
@
<tt> stop    (printer[@host] | all)  </tt>
|
stop  printing
@
<tt> topq    (printer[@host] | all) (name[@host] | job | all)* </tt>
|
reorder job
@
<tt> defaultq                         </tt>
|
default queue for LPD server
@
</tabular>
</table>
<p>
Many of these commands support extremely specialized operations for
print queue management,
However, the following are the most commonly used and are supported by
the BSD LPD print spooling system as well:
<itemize>
<item>
<tt> start, stop, enable, disable </tt>
<newline> Start and stop will start and stop printing for a specified queue.
Enable and disable enable and disable sending and/or accepting jobs
for the queue.
<item>
<tt> abort, kill </tt>
<newline>
Abort will cause the process doing the actual job printing to be terminated.
Kill does an abort, and then restarts the printing process.
These commands are used to restart a queue printing after some disaster.
<item>
<tt> topq </tt>
Places selected jobs at the top of the print queue.
<item>
<tt> status </tt>
<newline>
Shows a status display of the print spools on the server.
</itemize>
<p>
The following commands are extensions to the basic set provided by the
BSD LPD system.
<itemize>
<item>
<tt> lpq, lprm </tt>
<newline>
Invokes the lpq or lprm program from lpc.
Useful when in the interactive mode.
<item>
<tt> hold, holdall, release </tt>
<newline>
The hold command will cause the selected jobs to be held until
released.
The holdall jobs sets all jobs submitted to the queue to be held until
released.
The release command releases jobs for printing.
If a job has had an error and is in the error state,
the release command will cause it to be reprinted.
<item>
<tt> move, redirect </tt>
<newline>
The move command will move selected jobs to the specified spool queue.
The redirect command sends all jobs submitted to the queue to be
sent to the specified queue.
<item>
<tt> active, lpd, reread </tt>
<newline>
The active command will connect to the server for the printer.
This is used to check to see if non-LPRng print servers are active.
The lpd command will connect to the server and
get the process id (PID) of the <tt>lpd</tt> server.
The reread command causes a SIGHUP signal to be sent to the lpd process,
causing it to reread the
<tt>/etc/lpd.conf</tt>,
<tt>/etc/printcap</tt>,
or
<tt>/etc/lpd.perms</tt> files.
This is usually done when some important configuration information has
been modified and the administrator wants to have the server use the
new information.
<item>
<tt> debug </tt>
<newline>
This is a desperation facility for developers that allows dynamic enabling
of debug information generation.
Not normally used in general operation.
</itemize>
<label id="sendblockformat">
<label id="blocktransfer">
<sect1>Block Job Transfer
<p>
Options used:
<itemize>
<item> <tt>send_block_format</tt><em>&nbsp;&nbsp;Transfer job as a block</em>
</itemize>
<p>
In normal job transfer operations,
the sender and receiver have a handshake interaction in order to transfer
a print job.
Each file is sent individually.
The <tt>send_block_format</tt> option forces
a Block Job Transfer operation.
This causes the sender to transfer a single file containing all the
job printing information,
including control file and data files.
<p>
The transfer command line has the form:
<verb>
\006printer user@host size\n
</verb>
<p>
The receiver will return any acknowledgement of a single 0 octet,
and then the size bytes of the job will be transferred by the sender.
At the end of the transfer a single 0 octet is added,
and the receiver will indicate success by returning a single 0 octet.
Any other value returned by the receiver indicates an error condition.
<p>
The file transferred by the sender is simply the command lines that it
would have normally sent for job transfer,
followed by the control or data file values.
<sect1>Authenticated Transfer
<p>
RFC1179 does not provide any authentication or encryption mechanism
for the transfer of jobs or commands to the <tt>lpd</tt>
print server.
The Authenticated Transfer operation was added to allow an encrypted
or authenticated transfer of print jobs or commands.
<p>
Since there are various restrictions on the incorporation of authentication
facilities into programs,
LPRng supports authentication by providing a simple interface to
encryption programs.
<p>
The idea is that when authentication is required when sending a job,
LPRng will generate a block transfer job as described for the
<ref id="blocktransfer" name="Block Transfer operation,">
and then invoke a set of programs to encryt and transfer the file,
and encrypt and transfer the returned status.
<p>
Similarly,
when sending a command,
the command information will be placed in a file
and the encrypted file will be transferred.
<p>
This technique means that the programs and support to do encryption
are external to LPRng,
and can use any type of method that they choose to implement the
secure and/or authenticated transfer.
<p>
See
<ref id="authref" name="Authentication and Encryption">
for details on the authentication interface.
<sect>Acknowledgements
I'd like to thank the proof-readers from the LPRng mailing list,
in particular: Lars Anderson, Bertrand Decouty, Horst Fickenscher,
Philip Griffith, Gordon Haverland, John Perkins, Richard S. Shuford,
James H. Young and the ones I forgot.
<p>
Finally,  Patrick would like to thank all of the LPRng users
who so relentlessly tried the incredible number of permutations and
combinations of printers and software,
and made requests for <em>just one more feature</em>.
<!-- LPRng-HOWTO SGML format -->
</article>
