<!DOCTYPE book
  SYSTEM "LPRng-Reference.dtd"
 -- PUBLIC "-//FreeBSD//DTD DocBook V3.1-Based Extension//EN" --
 -- PUBLIC "-//OASIS//DTD DocBook V3.1//EN" --
[
  <!ENTITY LPRng "<application/LPRng/">
  <!ENTITY LPRngTool "<application/LPRngTool/">
  <!ENTITY lpd "<application/lpd/">
  <!ENTITY lpr "<application/lpr/">
  <!ENTITY ifhp "<application/ifhp/">
  <!ENTITY license SYSTEM "license.enc">
  <!ENTITY y2k SYSTEM "y2k.txt">
  <!ENTITY genindex.sgml SYSTEM "genindex.sgml">
]>

<book>
<bookinfo><title> LPRng Reference Manual</title>
<subtitle> 6 Sep 2007 (For LPRng-3.8.32)</subtitle>

<author>
<firstname>Patrick</firstname>
<othername role=mi>A</othername>
<surname>Powell</surname>
<affiliation>
<address>
<email>papowell@lprng.com</email>
AStArt Technologies
6741 Convoy Court,
San Diego, CA 92111
Phone <phone>858-874-6543</phone>
Fax <fax>858-751-2435</fax>
</address>
</affiliation>
</author>
<copyright>
<year>1996-2001</year>
<holder role="mailto:papowell@lprng.com">Patrick Powell</holder>
</copyright>

<releaseinfo>$Id: LPRng-Reference.sgml,v 1.4 2005/04/14 20:05:22 papowell Exp $</releaseinfo>

    <legalnotice>
     <important>
    <para>
<!-- <mediaobject> -->
<!-- <imageobject> -->
<!-- <imagedata fileref="somegraphic.eps"> -->
<!-- </imageobject> -->
<!-- <imageobject> -->
<!-- <imagedata fileref="somegraphic.jpg"> -->
<!-- </imageobject> -->
<!-- </mediaobject> -->
        THIS DOCUMENTATION AND THE DESCRIBED SOFTWARE IS PROVIDED BY
      THE AUTHORS "AS IS" AND ANY
      EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
      IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
      PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
      ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
      CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
      SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
      BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
      LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
      NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
      DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
      DAMAGE.</para>
      </important>
    </legalnotice>

<abstract>
<para>The LPRng; Printing Software consists of the &LPRng; print spooler,
the &ifhp; print filter, and
the &LPRngTool; graphical user interface.
</para>
<para>The &LPRng; print spooler is an enhanced, extended, and portable implementation
of the Berkeley <application/lpr/ print spooler functionality. While providing the same
interface and meeting RFC1179 requirements, the implementation is completely
independent and provides support for the following features: lightweight (no databases
needed) lpr, lpc, and lprm programs; dynamic redirection of print queues;
printer pooling and load balancing;
automatic job holding; highly verbose diagnostics;
client programs do not need to run SETUID root; greatly enhanced
security checks; load balancing across multiple printers;
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 <application/lpr/ interface and meet RFC1179 requirements.
Included in the &LPRng; print spooler distribution is
a set of customizable banner page generation programs.
</para>
<para>
The SVR4 lp and lpstat functionality is provided by a set of emulator
programs,
and &LPRng; can be easily integrated with the Samba SMB support package.
For users that require secure and/or authenticated printing support,
&LPRng; supports
SSL (using <application/OpenSSL/),
Kerberos 5,
MIT Kerberos 4 extensions to LPR,
PGP,
and simple MD5 based authentication.
Additional authentication support is extremely simple to add.
</para>
<para>
The &ifhp; print filter converts
print jobs into formats compatible with
PostScript, PCL, text, and other printers and
provides diagnostic and error information as well as
accounting information.
</para>
<para>
The ;LPRngTool& Graphical User Interface provides a simple to use
configuration and monitoring tool.
It allows users to monitor printers and generate printcap entries
in a simple manner,  as well as providing extensive help and diagnostics.
</para>
</abstract>
</bookinfo>

<preface><title>Preface</title>

<sect1><title>Introduction</title>
<para>
The &LPRng; Print Spooler provides the essential printing services
for UNIX and UNIX-like operating systems.
It can be configured to work in small,
large,
or enterprise level environments.
The &ifhp; Print Filter
is used with &LPRng; to convert print jobs into a format compatible
with a particular printer and the
while it may briefly describe the &ifhp; operation,
Finally,
the &LPRngTool; Graphical User Interface  provides an easy to use
configuration and monitoring tool for the &LPRng; print spooler.
</para>
<para>
This document is the basic reference for the &LPRng; print spooler software;
the &ifhp; documentation
and &LPRngTool;
should be consulted for details about their operation.
</para>
</sect1>


<sect1><title>Acknowledgements</title>
<para>I 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 whose requests for <emphasis>just one more feature</emphasis>
led to the development of the software.
</para>
</sect1>

<sect1><title>Shell Prompts</title>

      <para>The following table shows the default system prompt and superuser
    prompt. The examples will use this prompt to indicate which user you
    should be running the example as.</para>

      <informaltable frame="none">
    <tgroup cols="2">
      <thead>
        <row>
          <entry>User</entry>
          <entry>Prompt</entry>
        </row>
      </thead>

      <tbody>
        <row>
          <entry>Normal user</entry>
          <entry><prompt>%</prompt></entry>
        </row>

        <row>
          <entry><literal>root</literal></entry>
          <entry><prompt>#</prompt></entry>
        </row>
      </tbody>
    </tgroup>
      </informaltable>
    </sect1>

<sect1><title>Typographic Conventions</title>

      <para>The following table describes the typographic conventions used in
    this book.</para>

      <informaltable frame="none">
    <tgroup cols="2">
      <thead>
        <row>
          <entry>Meaning</entry>
          <entry>Examples</entry>
        </row>
      </thead>

      <tbody>
        <row>
          <entry>The name of commands, files, and directories. On screen
        computer output.</entry>
          <entry><para>Edit your <filename>.login</filename>
          file.</para><para>Use <command>ls -a</command> to list all
          files.</para><para><screen>You have mail.</screen>
        </para></entry>
        </row>

        <row>
          <entry>What you type, when contrasted with on-screen computer
        output.</entry>

          <entry><screen><prompt>%</prompt> <userinput>su</userinput>
Password:</screen></entry>
        </row>

        <row>
          <entry>Manual page references.</entry>

          <entry>Use <citerefentry>
          <refentrytitle>su</refentrytitle>
          <manvolnum>1</manvolnum>
        </citerefentry> to change user names.</entry>
        </row>

        <row>
          <entry>User and group names</entry>

          <entry>Only <literal>root</literal> can do this.</entry>
        </row>

        <row>
          <entry>Emphasis</entry>

          <entry>You <emphasis>must</emphasis>
do this.</entry>
        </row>

        <row>
          <entry>Command line variables; replace with the real name or
        variable.</entry>

          <entry>To delete a file, type <command>rm <filename><replaceable>filename</replaceable></filename></command></entry>
        </row>

        <row>
          <entry>Environment variables</entry>

          <entry><envar>$HOME</envar> is your home directory.</entry>
        </row>
      </tbody>
    </tgroup>
      </informaltable>
    </sect1>

<sect1><title>Notes, warnings, and examples</title>

      <para>Within the text appear notes, warnings, and examples.</para>

      <note>
    <para>Notes are represented like this, and contain information that
      you should take note of, as it may affect what you do.</para>
      </note>

      <warning>
    <para>Warnings are represented like this, and contain information
      warning you about possible damage if you do not follow the
      instructions. This damage may be physical, to your hardware or to
      you, or it may be non-physical, such as the inadvertent deletion of
      important files.</para>
      </warning>

      <informalexample>
    <para>Examples are represented like this, and typically contain
      examples you should walk through, or show you what the results of a
      particular action should be.</para>
      </informalexample>
    </sect1>

  </preface>


<chapter id=introduction><title>Introduction</title>

<para>Printing is one of the essential services provided by computer systems.
Users want reliable and easy to use methods of printing
that require a minimum amount of effort to used and understand.
On single user systems with a directly attached printer they
perceive that the printing process is simply a matter of
<emphasis>storing</emphasis>
or
<emphasis>spooling</emphasis>
a file,
and then transferring it to the printer in a timely manner.
In the classical
<emphasis>multi-user</emphasis>
systems,
each user expects to share a common printer with one or more users;
the print
<emphasis>spooling</emphasis>
system provides arbitration and sharing of the printer among the various users.
In a <emphasis>network</emphasis>
based multi-user system,
there may be one or more printers shared by multiple users on many different
systems.
The print <emphasis>spoolers</emphasis>
will need to cooperate to provide print services to
the users in a simple an predictable manner.</para>

<sect1 id="secfeatures"><title>What is &LPRng;?</title>
<indexterm/<primary/History//
<para>The &LPRng; print spooler software was developed to be robust,
reliable,
secure,
scalable,
and portable.
It has been used since 1988 in extremely demanding
academic printing environments such as University of Minnesota, MIT,
and Rutgers,
commercial companies such as Dow Jones and Abbot Pharmaceuticals,
as well as being distributed with Linux, FreeBSD, and other systems.
Each of these environments has a unique set of problems,
demanding various configuration and administrative capabilities.
For example,
the simple single user system with a single or limited number of printers
requires easy configuration and simple diagnostic procedures,
while the network based printing system requires highly robust
error logging,
authentication,
and failover support.
&LPRng; provides a highly flexible configuration system that
allows it to perform optimally in all of these environments.
</para>

<para>
The &LPRng; software has three components:
the &lpd; print spooler and the user client applications
<application/lpr/,
<application/lpq/,
<application/lprm/,
etc.;
the IFHP print filter (<application/ifhp/) which is used to convert
jobs into a suitable for a particular printer,
and the
the LPRngTool Graphic User Interface (<application/lprngtool/) which provides
a simple and easy to use configuration and monitoring tool for the &LPRng; print spooler.
</para>

<para>
&LPRng; mimics many of the features of the
<emphasis>vintage</emphasis>
or <emphasis>legacy</emphasis>
Berkeley (University of California - Berkeley) Line Printer (LPR)
package found on Berkeley derivatives of the Unix operating
system.
&LPRng; will print a document with little or no knowledge of the content
or special processing required to print the document on a stand-alone machine
or in a distributed printing environment.
New (as compared to Berkeley LPR) features include: lightweight <application>lpr</application>,
<application>lpc</application> and <application>lprm</application>
programs, dynamic redirection of print queues, automatic job holding,
highly verbose diagnostics, load balancing queues; enhanced
security (SUID not required in most environments),
and easy configuration.</para>

<para>&LPRng; started life at the University of Waterloo in 1986
as PLP (Public Line Printer), a replacement for the original BSD <application/lpd/ code.
This was a one-shot effort by the author,
Patrick Powell,
to develop
freely redistributed
code
without the restrictions of the BSD/AT&amp;T license
and would allow non-licensed sites to fix and patch problems.
From 1988 to 1992
individuals and groups added features, hacked, slashed, and modified the
PLP code, coordinated largely by Justin Mason (<email>jmason@iona.ie</email>)
who started the &LPRng; mailing list.
</para>

<para>In 1992 while at San Diego State University
Prof. Powell 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.
</para>

<para>In 1999
the code base for &LPRng; was again reorganized in order to provide a common
method for running on non-UNIX platforms such as Microsoft Windows NT,
Apple Rhapsody, and embedded systems.</para>

<para>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,
&LPRng; clients such as lpr, lprm, lpc, and lpq can run as
ordinary users programs,
the lpd server can run as a non-root user once a network port has
been opened,
and all text formatting operations done by &LPRng; use a very restricted and
highly secure version of the <application/snprintf/ function.
</para>
</sect1>

<sect1 id="maillist"><title>Additional Resources </title>

<para>The main &LPRng; documentation is the LPRng Reference Manual,
which is available in several formats.
Information
about &LPRng; and the latest release
can be found on the &LPRng; web page
<ulink URL="http://www.lprng.com/LPRng.html">http://www.lprng.com/LPRng.html</ulink></para>
<indexterm/<primary/download//
<indexterm/<primary/FTP site//

<para>The &ifhp; documentation is the IFHP-HOWTO,
which is available in the &ifhp; distribution.
Information
about &ifhp; and the latest release
can be found on the &LPRng; web page
<ulink URL="http://www.lprng.com/LPRng.html">http://www.lprng.com/LPRng.html</ulink></para>
<indexterm/<primary/download//
<indexterm/<primary/FTP site//

<para>There is also a mailing list at
<ulink URL="mailto:lprng-request@lprng.com?subject=subscribe">lprng@lprng.com</ulink>.
To post to the list you must subscribe by sending send an email to
<ulink URL="mailto:lprng-request@lprng.com?subject=subscribe">lprng-request@lprng.com</ulink>,
with the message subject or body containing the word `subscribe' or `help'.</para>
<indexterm/<primary/Mailing List//

<para>Several presentations of &LPRng; and print spooling software have been made
at the Large Installation System Administrator (LISA) conferences.
The presentation at the LISA 98 conference
is in the PowerPoint file <filename>LISA98.ppt</filename>
in the &LPRng; distribution documentation.
<indexterm/<primary/Tutorial notes//
</para>
</sect1>

<sect1><title>Frequently Asked Questions</title>
<para>There are a list of Frequently Asked Questions that appear regularly
on the &LPRng; mailing list.
See
<link linkend="FAQ">The Most Frequently Asked Questions</link>.</para>
<indexterm/<primary/FAQ//
<indexterm/<primary/Frequently Asked Questions//
</sect1>
<sect1><title>License, Copyright, and Disclaimer</title>

<para>The &LPRng; Print Spooler and the ifhp Print Filter software
are distributed under the
<ulink URL="license.txt">GNU Public License (GPL) and the Artistic License</ulink>.
Users can choose to redistribute or use the software under a license
that is appropriate for their purpose.
Other licenses and distribution agreements are available by contacting
<ulink URL="http://www.astart.com">AStArt Technologies</ulink>
for information.</para>

<para>THE MATERIAL IN THESE SOFTWARE PACKAGES AND DOCUMENTS
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 THE SOFTWARE OR ANY
INFORMATION PROVIDED IN THIS DOCUMENT.
</para>
</sect1>

<sect1><title>Commercial Support</title>

<para><ulink URL="http://www.astart.com">AStArt Technologies</ulink>
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.</para>

</sect1>

<sect1><title>Web Site</title>

<para>Web Page:
<ulink URL="http://www.lprng.com">http://www.lprng.com</ulink></para>

</sect1>

<sect1 id="secftp"><title>FTP Sites </title>

<para>Main FTP Site:
<simplelist type=vert columns=1>
<member>
<ulink URL="ftp://ftp.lprng.com/pub/LPRng">ftp://ftp.lprng.com/pub/LPRng</ulink> (US)
</member>
</simplelist>
</para>

<!-- MIRRORSTART -->
<para>Mirrors:<!-- <br> -->
<simplelist type=vert columns=1>
<member><ulink URL="ftp://ftp.u-aizu.ac.jp/pub/net/lpr/LPRng">ftp://ftp.u-aizu.ac.jp/pub/net/lpr/LPRng</ulink> (JA) </member>
<member><ulink URL="ftp://ftp.cs.columbia.edu/pub/archives/pkg/LPRng">ftp://ftp.cs.columbia.edu/pub/archives/pkg/LPRng</ulink> (US) </member>
<member><ulink URL="ftp://ftp.cise.ufl.edu/pub/mirrors/LPRng">ftp://ftp.cise.ufl.edu/pub/mirrors/LPRng</ulink> (US) </member>
<member><ulink URL="ftp://ftp.cs.umn.edu/pub/LPRng">ftp://ftp.cs.umn.edu/pub/LPRng</ulink> (US) </member>
<member><ulink URL="ftp://uiarchive.uiuc.edu/pub/ftp/ftp.lprng.com/pub/LPRng">ftp://uiarchive.uiuc.edu/pub/ftp/ftp.lprng.com/pub/LPRng</ulink> (US) </member>
<member><ulink URL="ftp://ftp.sage-au.org.au/pub/printing/spooler/lprng/">ftp://ftp.sage-au.org.au/pub/printing/spooler/lprng/</ulink> (AU) </member>
<member><ulink URL="ftp://mirror.aarnet.edu.au/pub/LPRng/">ftp://mirror.aarnet.edu.au/pub/LPRng/</ulink> (AU/NZ) </member>
<member><ulink URL="http://mirror.aarnet.edu.au/pub/LPRng/">http://mirror.aarnet.edu.au/pub/LPRng/</ulink> (AU/NZ) </member>
<member><ulink URL="ftp://sunsite.ualberta.ca/pub/Mirror/LPRng">ftp://sunsite.ualberta.ca/pub/Mirror/LPRng</ulink> (CA) </member>
<member><ulink URL="ftp://ftp.informatik.uni-hamburg.de/pub/os/unix/utils/LPRng">ftp://ftp.informatik.uni-hamburg.de/pub/os/unix/utils/LPRng</ulink> (DE) </member>
<member><ulink URL="ftp://ftp.uni-paderborn.de/pub/unix/printer/LPRng">ftp://ftp.uni-paderborn.de/pub/unix/printer/LPRng</ulink> (DE) </member>
<member><ulink URL="ftp://ftp.mono.org/pub/LPRng">ftp://ftp.mono.org/pub/LPRng</ulink> (UK) </member>
<member><ulink URL="ftp://ftp.iona.com/pub/plp/LPRng">ftp://ftp.iona.com/pub/plp/LPRng</ulink> (IE) </member>
<member><ulink URL="ftp://uabgate.uab.ericsson.se/pub/unix/LPRng">ftp://uabgate.uab.ericsson.se/pub/unix/LPRng</ulink> (SE) </member>
</simplelist>
</para>
<!-- MIRROREND -->

</sect1>

<sect1><title>Mailing List</title>

<para>To join the &LPRng; mailing list, please send mail to
<ulink URL="mailto: lprng-request@lprng.com">lprng-request@lprng.com</ulink>
with the word 'subscribe' in the BODY.</para>

<para>The &LPRng; mailing list is archived on<!-- <br> -->
<ulink URL="http://www.findmail.com/list/lprng">http://www.findmail.com/list/lprng</ulink></para>

</sect1>

<sect1 id="faqref"><title>PGP Public Key </title>

<para>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:
<informalexample>
<screen>Type Bits/KeyID    Date       User ID
pub  1024/00D95C9D 1997/01/31 Patrick A. Powell \
   &lt;papowell@lprng.com&gt;

-----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-----</screen>
</informalexample>
</para>

</sect1>

<sect1><title>References and Standards</title>

<para>The following references and standards have been used
in the development of the &LPRng; software.</para>


<sect2><title>RFCs</title>

<para>During the early development of the Internet
developers did not want to go through the laborious process of
developing formal standards and applying to a standards body such as the
EIA, IEEE, or ISO.
Instead,
they called the standards documents they developed
<citation>Requests for Comments</citation>.
These soon became <emphasis>de facto</emphasis>
standards,
and with the formal acceptance of the TCP/IP protocol as a
network standard,
<emphasis>de jure</emphasis>
as well.</para>

<para>You can get copies of the RFCs from literally
hundreds of network sites,
including
<ulink URL="http://www.isi.edu">http://www.isi.edu</ulink>,
<ulink URL="http://www.faqs.org/rfcs">http://www.faqs.org/rfcs</ulink>,
<ulink URL="ftp://NIS.NSF.NET">NIS.NSF.NET</ulink>,
<ulink URL="ftp://RFC.JVNC.NET">RFC.JVNC.NET</ulink>,
or
<ulink URL="ftp://FTP.ISI.EDU">FTP.ISI.EDU</ulink>.</para>

<para>The <citation>RFC1179 - Line Printer Daemon Protocol</citation>
describes the protocol used to transfer jobs from client program to
print server.
See
<link linkend="rfc1179ref">RFC1179</link>
for more a discussion of this protocol and more details about the RFC.
The <ulink URL="rfc1179.txt">rfc1179.txt</ulink> file is included
in the &LPRng; distribution documentation.</para>

</sect2>

<sect2 id="postscript"><title>PostScript </title>

<para>PostScript is one of the <emphasis>de facto</emphasis>
standards for print jobs.
The
Adobe Corporation
(<ulink URL="http://www.adobe.com">http://www.adobe.com</ulink>)
provides an excellent set of references for the PostScript language.
They have made many of these available for downloading from their
web sites or have published them in book form.</para>

<para>The <citation>PostScript Language Reference Manual</citation>
contains a great deal of technical information about the PostScript Language,
and is the language reference manual.</para>

<para>The <citation>PostScript Language Tutorial and Cookbook</citation>
is a very nice and easy to read introduction to PostScript programming,
and has some very useful utilities.
Combined with
<link linkend="ghostscript"> GhostScript </link>
and the
<link linkend="gv"> gv </link>
display program
you can very easily learn to write your own small PostScript programs,
and more importantly,
can learn to understand the contents of PostScript files.</para>

<para> The <citation>PostScript Language Program Design</citation>
is the companion to the
<citation>PostScript Language Tutorial and Cookbook</citation>,
and has more complex examples of PostScript programs.
More importantly,
it also introduces,
although without explanation,
the PostScript Document Structuring Conventions described in Appendix G
of the
The <citation>PostScript Language Reference Manual</citation>.
This alone makes it useful.</para>

</sect2>

<sect2 id="pcl"><title>HP PCL 5 </title>

<para>The Hewlett-Packard (HP) PCL Printer Language is the second de-facto
standard for print jobs.
Currently,
Hewlett-Packard
makes documentation for PCL available through their
<citation>Developer Program</citation>.
You will need to register and then search their site for the
<citation>PCL 5 Printer Language Reference Manual</citation>.</para>

</sect2>

<sect2 id="pjl"><title>HP PJL </title>

<para>The Hewlett-Packard (HP) Printer Job Language is used
to control various features of HP printers.
The <citation>Printer Job Language Reference Manual</citation>
is also available from
Hewlett-Packard
(<ulink URL="http://www.hp.com">http://www.hp.com</ulink>)
through their
<citation>Developer Program</citation>.</para>

</sect2>

<sect2><title>PDF</title>

<para>The Portable Document Format (<literal>pdf</literal>) was developed by Adobe to be
a more useful method of distributing documentation for view
by online systems and software.
The
<citation>Portable Document Format Reference Manual</citation>
is available from
Adobe
(<ulink URL="http://www.adobe.com">http://www.adobe.com</ulink>).
While <literal>pdf</literal> is not used directly as a print job language,
it is one of the more common formats for files that need to be printed.
It can be converted to PostScript by most <literal>pdf</literal> viewers such
as GhostScript and
Adobe Acrobat.</para>

</sect2>
</sect1>
</chapter>

<chapter id=installation><title>Installation
</title>

<para>The basic components of the &LPRng; system are the executables
and the database files.
This section deals with generating and installing the executable
files.</para>


<sect1><title>Getting Source Code and Support Programs</title>

<para>
<orderedlist>

<listitem>
<para>Obtain the latest or stable version of the &LPRng; source code from a

<link linkend="secftp">&LPRng; FTP Site</link>.</para>
</listitem>

<listitem>
<para>Obtain the latest or stable version of the <application>ifhp</application> filter source code from a

<link linkend="secftp">&LPRng; FTP Site</link>.
This filter is used to support
PostScript,
PCL,
and text printers.</para>
</listitem>

<listitem>
<para>Obtain the following GNU programs from one of the many
<ulink URL="http://www.gnu.org">GNU Software Mirror Sites</ulink>
and install them.
See the directions in the GNU Zip distribution for details.

<variablelist>
<varlistentry><term>GNU <application>gzip</application> Compression Utility</term>
<listitem>
<para>Used to generate the compressed &LPRng; distribution.</para>

</listitem>
</varlistentry>

<varlistentry><term>GNU <application>tar</application> Archive Utility</term>
<listitem>
<para>
<ulink URL="http://www.gnu.org">GNU</ulink>
<application>tar</application> supports
<application>gzip</application> compression and decompression
and is used to generate the &LPRng; distribution.</para>
</listitem>
</varlistentry>

<varlistentry><term>GNU <application>make</application></term>
<listitem>
<para>&LPRng; requires
<ulink URL="http://www.gnu.org">GNU</ulink>
<application>make</application>
for configuration and installation.</para>
</listitem>
</varlistentry>

<varlistentry><term>GNU <application>gcc</application> Compiler or ANSI C Compiler</term>
<listitem>
<para>
&LPRng; requires and ANSI C compiler.
If you do not have an ANSI C compiler
then please use the
<ulink URL="http://www.gnu.org">GNU</ulink>
<application>gcc</application> compiler.</para>
</listitem>
</varlistentry>

</variablelist>
</para>
</listitem>


<listitem>
<para>Solaris Sparc and X86 Binaries for GCC and Make  can be obtained from
<ulink URL="http://sunfreeware.com/">http://sunfreeware.com/</ulink>.</para>
</listitem>

<listitem>
<para>While the following are not essential to &LPRng;
they are used by the <application>ifhp</application> filter.
<variablelist>
<varlistentry><term><application>file</application> - File Identification Utility</term>
<listitem>
<anchor id=fileprog>
<para>The Open Source <application/file/ utility by Ian F. Darwin
can be obtained from
<ulink URL="ftp://ftp.astron.com/pub/file/">
ftp://ftp.astron.com/pub/file/
</ulink>.
or
<ulink URL="ftp://ftp.lprng.com/pub/LPRng/UNIXTOOLS/file/">
ftp://ftp.lprng.com/pub/LPRng/UNIXTOOLS/file/
</ulink>.
This is a greatly improved version of the original UNIX file utility and
may be used by the <application>ifhp</application> filter to do file recognition.</para>

</listitem>
</varlistentry>

<varlistentry><term><application>gs</application> - GhostScript</term>
<listitem>
<anchor id="ghostscript">
<para>
GhostScript
can be obtained from
<ulink URL="http://www.cs.wisc.edu/~ghost/">http://www.cs.wisc.edu/~ghost/</ulink>
or
<ulink URL="http://www.ghostscript.com">http://www.ghostscript.com</ulink>.
GhostScript is a PostScript interpreter that allows you to translate
PostScript to various printer compatible formats such as PCL,
as well as displaying the code on a terminal.
You might also want to get the PDF extensions that allows GhostScript to
read and print PDF files.</para>

</listitem>
</varlistentry>

<varlistentry><term><application>gv</application> - GhostView</term>
<listitem>
<anchor id="gv">
<para>
Of course you will want to get the <application>gv</application> program that
uses GhostScript to display PostScript on an X terminal.
It can be obtained from
<ulink URL="http://wwwthep.physik.uni-mainz.de/~plass/gv/">http://wwwthep.physik.uni-mainz.de/~plass/gv/</ulink></para>
</listitem>
</varlistentry>
</variablelist>
</para>
</listitem>

</orderedlist>
</para>

</sect1>

<sect1><title><envar>PATH</envar> Environment Variable and Utilities</title>

<para>
Make sure that directory where you
you have installed
the GNU tools
is one of the first entries in the shell search <envar>PATH</envar>
environment variable.
For example,
if you have installed the utilities in the (default) directory
<filename>/usr/local/bin</filename>,
this should be one of the first entries in the <envar>PATH</envar>.
For example:
<informalexample>
<screen>PATH=/usr/local/bin:/bin:/sbin:/usr/bin
</screen>
</informalexample>
</para>

<para>If you are compiling the distribution on a Solaris system
you will need to use the utilities in the
<filename>/usr/ccs/bin</filename>
directory.
This directory must be in the <envar>PATH</envar>
<emphasis>after</emphasis>
the directory where the GNU utilities have been installed.
For example:
<informalexample>
<screen>PATH=/usr/local/bin:/bin:/sbin:/usr/bin:/usr/ccs/bin
</screen>
</informalexample>
</para>

<para>
Check to see that the GNU <application>make</application> utility
and not the default OS <application>make</application> is being used by default.
Use <command>make -v</command> to determine what version you are using:
<informalexample>
<literallayout><prompt>h4: {1} % </prompt><userinput>make -v</userinput>
GNU Make version 3.78, by Richard Stallman and Roland McGrath.
Copyright (C) 1988-1999</literallayout>
</informalexample>
</para>

</sect1>
<sect1 id="nfsmount"><title>Network Mounted File System and Spool Directories</title>
<warning>
<para>
It is strongly recommended that the print spool directories
and essential printer configuration file files
should <emphasis/NOT/ be in an NFS mounted file system
or on a file system which is NFS exported.
</para>
</warning>
<para>
The
&LPRng;
<application/lpd/
print server makes heavy use of
file locking to coordinate and synchronize
process activities.
Given the historical and ongoing problems of file locking on
and NFS mounted file system,
not to mention the high overhead for doing file locking in a
distributed environment,
it is strongly recommended that spool directories <emphasis/NOT/
be on an NFS mounted partition or on a partition that is
NFS exported.
</para>
<para>
This warning extends to other network file systems as well.
</para>
<para>
In addition,
the configuration file such as <filename>/etc/printcap</filename>
must be available or printing will not function.
Care should be taken to ensure that these files are stable and
available at all times.
</para>

</sect1>
<sect1 id="usergroup"><title>Daemon User and Daemon Group </title>

<para>The <application>lpd</application> server is started at system initialization time and initially runs as ROOT (Effective <acronym>UID</acronym> 0).
It performs all file and other operations with
<emphasis/non-privileged/ user <literal>daemon</literal> Effective <acronym/UID/
and group <literal>daemon</literal> Effective GID,
and does a <function/setuid()/ to these UID and GID values when
running programs.
The client programs such as
<application>lpr</application>
operate with the effective user IDs of the user which started them.
</para>
<para>
Most UNIX systems already have
user <literal>daemon</literal> and group <literal>daemon</literal>,
or a similar ones.
If suitable user and group IDs are not present then
the appropriate system administration tools should be used to create them.
The configuration
<literal>--with-userid=UID</literal>
and
<literal>--with-groupid=GID</literal>
can be used to specify the user and group IDs.
The user ID must <emphasis>not</emphasis>
have login capability.
</para>

</sect1>

<sect1><title>Configuration</title>

<para>The &LPRng; package consists of the following executables and configuration files:
<itemizedlist>
<listitem>
<para><application>lpd</application> - the <application/lpd/ print server program</para>
</listitem>
<listitem>
<para><application>lpr</application>,
<application>lpq</application>,
<application>lprm</application>,
<application>lpc</application>,
and
<application>lpstat</application>
client programs for printing,
status queries, job removal, server configuration,
and System V <application>lpstat</application> emulation respectively.</para>
</listitem>

<listitem>
<para><filename>printcap</filename> print queue database file
which is used by all the server and client programs</para>
</listitem>

<listitem>
<para><filename>lpd.conf</filename> &LPRng; configuration options
which is used by all the server and client programs</para>
</listitem>

<listitem>
<para><filename>lpd.perms</filename> permission information
which is used by the <application>lpd</application> server to control user actions.</para>
</listitem>

</itemizedlist>
</para>

<para>
&LPRng; uses the <application>configure</application>
script generated by the
<ulink URL="http://www.gnu.org">GNU</ulink>
<application/autoconf/ utility
to generate a set of <filename>Makefiles</filename>.
These are used by
<ulink URL="http://www.gnu.org">GNU</ulink>
<application>make</application>
to compile and install the &LPRng; software.
The following <filename>Makefile</filename>
variables and values are set by <application>configure</application>
to specify the location of the &LPRng; software:
</para>
<informaltable>
<tgroup cols=4 align=left>
<thead>
<row><entry>Configure Variable</entry><entry>Default Value</entry>
<entry>Expanded Default Value</entry>
<entry>Override</entry> </row>
</thead>
<tbody>
<row><entry><literal>${prefix}</literal></entry><entry><literal>/usr/local</literal></entry><entry><literal>--prefix=PATH</literal></entry></row>
<row><entry><literal>${exec_prefix}</literal></entry><entry><literal>${prefix}</literal></entry><entry><literal>/usr/local</literal></entry><entry><literal>--execprefix= PATH</literal></entry></row>
<row><entry><literal>${bindir}</literal></entry><entry><literal>${exec_prefix}/bin</literal></entry><entry><literal>/usr/local/bin</literal></entry><entry><literal>--bindir= PATH</literal></entry></row>
<row><entry><literal>${sbindir}</literal></entry><entry><literal>${exec_prefix}/sbin</literal></entry><entry><literal>/usr/local/sbin</literal></entry><entry><literal>--sbindir= PATH</literal></entry></row>
<row><entry><literal>${libexecdir}</literal></entry><entry><literal>${exec_prefix} /libexec</literal></entry><entry><literal>/usr/local/libexec</literal></entry><entry><literal>--libexecdir= PATH</literal></entry></row>
<row><entry><literal>${sysconfdir}</literal></entry><entry><literal>${prefix}/etc</literal></entry><entry><literal>/usr/local/etc</literal></entry><entry><literal>--sysconfdir= PATH</literal></entry></row>
<row><entry><literal>${mandir}</literal></entry><entry><literal>${prefix}/man</literal></entry><entry><literal>/usr/local/man</literal></entry><entry><literal>--mandir= PATH</literal></entry></row>
</tbody>
</tgroup>
</informaltable>

<para>These are used to install the following files:</para>
<informaltable>
<tgroup cols=2 align=left>
<thead>
<row><entry>Configure Variable</entry><entry>Files</entry></row>
</thead>
<tbody>
<row><entry><literal>${bindir}</literal></entry><entry><literal>lpr, lprm, lpq, lpstat</literal></entry></row>
<row><entry><literal>${sbindir}</literal></entry><entry><literal>lpc, checkpc, lpd</literal></entry></row>
<row><entry><literal>${libexecdir}/filters</literal></entry><entry><literal>lpf, pclbanner, psbanner, lpbanner</literal></entry></row>
<row><entry><literal>${sysconfdir}</literal></entry><entry><literal>lpd.conf, lpd.perms, printcap</literal></entry></row>
<row><entry><literal>${mandir}/man[1-9]</literal></entry><entry><literal>man pages</literal></entry></row>
</tbody>
</tgroup>
</informaltable>

<para>You can set explicit values for the
paths by using the override <literal>--name=PATH</literal>.
For example:
<informalexample>
<screen>./configure --prefix=/usr --sysconfdir=/etc \
    --mandir=/usr/share/man</screen>
</informalexample>
</para>
<informaltable>
<tgroup cols=3 align=left>
<thead>
<row><entry>Variable</entry><entry>Value</entry><entry>Files</entry></row>
</thead>
<tbody>
<row><entry>${bindir}</entry><entry>/usr/bin</entry><entry>/usr/bin/{lpr,lprm,lpq,lpstat}</entry></row>
<row><entry>${sbindir}</entry><entry>/usr/sbin</entry><entry>/usr/sbin/{lpc,checkpc,lpd}</entry></row>
<row><entry>${libexecdir}/filters</entry><entry>/usr/libexec/filters</entry><entry>/usr/libexec/filters{lpf, pclbanner, psbanner, lpbanner}</entry></row>
<row><entry>${sysconfdir}</entry><entry>/etc</entry><entry>/etc/{lpd.conf,lpd.perms,printcap}</entry></row>
<row><entry>${mandir}/man[1-9]</entry><entry>/usr/share/man</entry><entry>/usr/share/man/man[1-9]/{man pages}</entry></row>
</tbody>
</tgroup>
</informaltable>

<para>In addition to these standard <application>configure</application>
options the following options provided.
<variablelist>

<varlistentry><term><literal> --disable-setuid </literal></term>

<listitem>
<para>Install the executables without setuid ROOT permissions.
Non-setuid clients and programs are inherently more secure than SETUID programs,
and system administrators would be well advised to install them without
SETUID root permissions.
Please see
<link linkend="setuid">Security Considerations</link>
for more details about this option.</para>
</listitem>
</varlistentry>

<varlistentry><term><literal> --enable-priv_ports  </literal></term>

<listitem>
<para>Require connections to the <application>lpd</application> server
to come from a privileged port (range 1-1023).
By default
&LPRng; will allow connections from any port.
Please see
<link linkend="setuid">Security Considerations</link>

for more details about this option.</para>


</listitem>
</varlistentry>

<varlistentry><term><literal>--disable-force_localhost</literal></term>

<listitem>
<para>The default &LPRng; configuration assumes that all printing will be done
via a <application>lpd</application> print spooler running on the local host system.
However,
many larger sites prefer that all users do their printing via a
few central servers,
and do not run
<application>lpd</application> servers on user systems.
The
<literal>--disable-force_localhost</literal>
configuration sets the default value of the <literal>force_localhost</literal>
value to
<literal remap=tt>false</literal>,
by default allowing the &LPRng; clients to connect directly to <application>lpd</application> servers
on remote hosts.</para>


</listitem>
</varlistentry>

<varlistentry><term><literal>--disable-require_configfiles</literal></term>

<listitem>
<para>By default, the
<application>lpr</application>,
<application>lpq</application>,
<application>lprm</application>,
and
<application>lpc</application>
clients require the <filename>lpd.conf</filename> and <filename>printcap</filename> files
to be present on the localhost.
The <literal>--disable-require_configfiles</literal> literal removes this requirement.</para>

</listitem>
</varlistentry>

<varlistentry><term><literal>--enable-kerberos</literal></term>

<listitem>
<para>Include support for Kerberos 5 authenticated transfers.</para>

</listitem>
</varlistentry>

<varlistentry><term><literal>--enable-mit_kerberos4</literal></term>

<listitem>
<para>Include support for MIT Kerberos 4 authenticated transfers.</para>

</listitem>
</varlistentry>

<varlistentry><term><literal>--disable-kerberos_checks</literal></term>

<listitem>
<para>Disable checks for kerberos support libraries, etc.</para>

</listitem>
</varlistentry>

<varlistentry><term><literal> --with-lpddir=DIR </literal></term>

<listitem>
<para>lpd executable directory (default ${sbindir}).
For historical configuration compatibility.</para>

</listitem>
</varlistentry>

<varlistentry><term><literal> --with-filterdir=DIR </literal></term>

<listitem>
<para>Filter directory (default ${libexecdir}/filters).
For historical configuration compatibility.</para>

</listitem>
</varlistentry>

<varlistentry><term><literal> --with-lpd_conf_path=PATH </literal></term>

<listitem>
<para>Path of <filename>lpd.conf</filename> file.
For historical configuration compatibility.</para>

</listitem>
</varlistentry>

<varlistentry><term><literal> --with-lpd_perms_path=PATH </literal></term>

<listitem>
<para>Path of <filename>lpd.perms</filename> file.
For historical configuration compatibility.</para>

</listitem>
</varlistentry>

<varlistentry><term><literal> --with-printcap_path=PATH </literal></term>

<listitem>
<para>Path of <filename>printcap</filename> file.
For historical configuration compatibility.</para>

</listitem>
</varlistentry>

<varlistentry><term><literal> --with-ld_library_path=PATHLIST </literal></term>

<listitem>
<para>Set the LD_LIBRARY_PATH environment variable of filters to this value.</para>

</listitem>
</varlistentry>

<varlistentry><term><literal> --with-filter_path=PATHLIST </literal></term>

<listitem>
<para>Set the PATH environment variable of filters to this value.</para>

</listitem>
</varlistentry>

<varlistentry><term><literal> --with-userid=NAME</literal></term>

<listitem>
<para>Run &LPRng; as this user, default daemon</para>

</listitem>
</varlistentry>

<varlistentry><term><literal> --with-groupid=NAME</literal></term>

<listitem>
<para>Run &LPRng; as this group, default daemon</para>

</listitem>
</varlistentry>

<varlistentry><term><literal> --with-lockfile=PATH </literal></term>

<listitem>
<para>The lockfile path.  This will be expanded to PATH.server or PATH.port
allowing multiple &LPRng; servers to run on a single host.</para>

</listitem>
</varlistentry>

<varlistentry><term><literal> --with-filterdir=PATH </literal></term>

<listitem>
<para>Location of the filters installed by &LPRng;.</para>


</listitem>
</varlistentry>

<varlistentry><term><literal> --with-done_jobs=N </literal></term>

<listitem>
<para>retain status of last N done jobs.</para>


</listitem>
</varlistentry>

<varlistentry><term><literal> --with-done_jobs_max_age=N </literal></term>

<listitem>
<para>remove status of done jobs older than N seconds.</para>


</listitem>
</varlistentry>

<varlistentry><term><literal>--with-chooser_routine=NAME </literal></term>
<listitem>
<para>name of chooser routine provided by user</para>
</listitem>
</varlistentry>

<varlistentry><term><literal>--with-order_routine=NAME </literal></term>

<listitem>
<para>name of order routine provided by user</para>

</listitem>
</varlistentry>

<varlistentry><term><literal>--with-user_objs=NAME </literal></term>

<listitem>
<para>object file with routines provided by user</para>

</listitem>
</varlistentry>

<varlistentry><term><literal>--with-user_include=NAME </literal></term>

<listitem>
<para>include file with templates for routines provided by user</para>


</listitem>
</varlistentry>

<varlistentry><term><literal> --disable-strip </literal></term>

<listitem>
<para>Do not strip the executables before installing.
For debugging and diagnostic purposes.</para>

</listitem>
</varlistentry>

<varlistentry><term><literal>--with-unixsocketpath=PATHNAME</literal></term>
<listitem>
<para>the pathname of the UNIX socket (off or blank to disable)</para>
</listitem>
</varlistentry>

<varlistentry><term><literal>--disable-ssl</literal></term>
<listitem>
<para>disable ssl support</para>
</listitem>
</varlistentry>

<varlistentry><term><literal>--with-openssl=DIR</literal></term>
<listitem>
<para>root location for OpenSSL</para>
</listitem>
</varlistentry>

<varlistentry><term><literal>--with-openssl-inc</literal></term>
<listitem>
<para>OpenSSL include files</para>
</listitem>
</varlistentry>

<varlistentry><term><literal>--with-openssl-lib</literal></term>
<listitem>
<para>OpenSSL library files</para>
</listitem>
</varlistentry>

<varlistentry><term><literal>--with-ssl_ca_file=FILE</literal></term>
<listitem>
<para>ssl Certificate Authority CERT file (default ${sysconfdir}/lpd/ssl.ca/ca.crt)</para>
</listitem>
</varlistentry>

<varlistentry><term><literal>--with-ssl_ca_key=KEY</literal></term>
<listitem>
<para>ssl Certificate Authority private key file (default ${sysconfdir}/lpd/ssl.ca/ca.key)</para>
</listitem>
</varlistentry>

<varlistentry><term><literal>--with-ssl_certs_dir=DIR</literal></term>
<listitem>
<para>ssl Certificate Authority certs working directory (default ${sysconfdir}/lpd/ssl.certs/)</para>
</listitem>
</varlistentry>

<varlistentry><term><literal>--with-ssl_server_cert=FILE</literal></term>
<listitem>
<para>ssl server certificate file (default ${sysconfdir}/lpd/ssl.server/server.crt)</para>
</listitem>
</varlistentry>

<varlistentry><term><literal>--with-ssl_server_password_file=FILE</literal></term>
<listitem>
<para>ssl server private key in password file (default ${sysconfdir}/lpd/ssl.server/server.pwd)</para>
</listitem>
</varlistentry>


</variablelist>
</para>

<para>It is recommended that you use one of the following
configurations:
<orderedlist>

<listitem>
<para>If you already have a print spooling system installed and
want to install &LPRng; for testing purposes or as an alternative
to the existing system and keep your existing print spooling system, use:
<informalexample>
<screen>./configure

use defaults for file locations and permissions:
  /usr/local/{bin,sbin,libexec/filters,man}
requires lpd to run on the local host
executables installed setuid ROOT</screen>
</informalexample>
</para>
</listitem>

<listitem>
<para>If you have manual pages in <filename>/usr/share/man</filename>,
your existing print spooling system has executables in
<filename>/usr/bin</filename> and <filename>/usr/sbin</filename>,
and you want to replace your existing print spooling system, use:
<informalexample>
<screen>./configure --prefix=/usr --sysconfdir=/etc \
  --mandir=/usr/share/man

executables and files in
   /usr/{bin,sbin,libexec/filters}
   /usr/share/man/man[0-9]
requires lpd to run on the local host
everything installed setuid ROOT</screen>
</informalexample>
</para>
</listitem>

<listitem>
<para>If you have manual pages in <filename>/usr/share/man</filename>
and allow jobs (by default) to be sent directly to the server host
(lightweight operation), use:
<informalexample>
<screen>./configure --prefix=/usr --sysconfdir=/etc \
   --mandir=/usr/share/man --disable-force_localhost

executables and files in
   /usr/{bin,sbin,libexec/filters}
   /usr/share/man/man[0-9]
does not require lpd to run on the local host
everything installed setuid ROOT</screen>
</informalexample>
</para>
</listitem>

</orderedlist>
</para>

</sect1>

<sect1 id="requireconfig"><title>System and User Printcap, lpd.conf, and lpd.perms files </title>

<para>The
system <filename>printcap</filename> file contains the
definitions of the print queues used by &LPRng;,
and is located in the directory specified by the
configuration
<literal>${sysconfdir}</literal>
value.
For a complete description of the <filename>printcap</filename> file see
<link linkend="printcapref">Printcap Database</link>.
If your system does not have an existing <filename>printcap</filename> file
then a <emphasis>dummy</emphasis>
file similar to the following is
installed by default:
<informalexample>
<screen># dummy printcap file
lp:cm=Dummy Printcap Entry:
 :lp=/dev/null
 :sd=/var/spool/lpd/%P</screen>
</informalexample>
</para>

<para>
In addition to the system <filename/printcap/ file,
each user can have a <literal>${HOME}/.printcap</literal>
which contains printcap entries as well.
The the user <filename/printcap/ file is in effect appended to the
system printcap file, and values in the user printcap file override the
system printcap file.
However,
in order to allow users to specify a default printer
after reading the printcap file information the entries are
sorted so that printcap entries defined by users come first.
</para>

<para>The <filename>lpd.conf</filename>
is located in the
<literal>${sysconfdir}</literal>
directory
and provides configuration settings for both the &LPRng; client and server
programs.
For a complete description of the <filename>lpd.conf</filename> file see
<link linkend="configfile">
Configuration File, Defaults and Overrides</link>.
During installation the
<filename>${sysconfdir}/lpd.conf.template</filename>
is created with the default &LPRng; information
and if there is not an existing <filename>${sysconfdir}/lpd.conf</filename> file
is copied to it.
</para>

<para>The <filename>lpd.perms</filename>
is located in the
<literal remap=tt>${sysconfdir}</literal>
directory
and is only by <application>lpd</application> to determine user permissions
for printing activities.
For a complete description of the <filename>lpd.perms</filename> file see
<link linkend="permsref">
Permissions and Authentication
</link> for details.
During installation the
<filename>lpd.perms.template</filename> file
is installed in the
<filename>${sysconfdir}/lpd.perms.template</filename>
and if there is not an existing <filename>lpd.perms</filename> file
is copied to it.
</para>

<para>By default,
the &LPRng; client programs
<application>lpr</application>,
<application>lpq</application>,
<application>lprm</application>,
and
<application>lprc</application>
will require a <filename>lpd.conf</filename> and <filename>printcap</filename>
file to be installed on the local host.
You can relax this requirement by setting
using the
<literal>--disable-require_configfile</literal>
configuration option.
You can also create empty files to satisfy the program requirements.</para>
</sect1>

<sect1><title>Checking System Installation with <application>checkpc</application></title>
<para>The
<application>checkpc</application>
program is used to make sure that
the spool directories and files used by &LPRng; have the correct permissions
and are in place.
By default,
<application>checkpc</application> will check permissions and report if there are any problems.
You should run this as
<literal remap=tt>root</literal>.
For example:
<informalexample>
<screen><prompt>h4: {2} # </prompt><userinput>checkpc</userinput>
Warning - No configuration file '/etc/lpd.conf'
Warning - No lpd only printcap file found in '/etc/lpd_printcap'
Warning -  ** cannot open '/var/run/lpd.printer' - 'Permission denied'
Warning -  bad directory - /var/spool/lpd/lp
Warning -   Printer 'lp' spool dir '/var/spool/lpd/lp' needs fixing</screen>
</informalexample>
</para>

<para>In the above example,
<application>checkpc</application> has discovered that the <filename>lpd.conf</filename>
file is missing.
This is a serious problem and indicates that the
software may be incorrectly installed.
</para>

<para>The <literal/lpd only printcap/
message is usually of concern to administrators who wish to use some of
&LPRng;'s more exotic configuration options.
It is possible to have separate <filename/printcap/
files for client and server programs.
This is useful when <filename/printcap/ files get extremely large and cuts down
substantially on system management problems.</para>

<para>The <literal/permission denied/ message for <filename>/var/run/lpd.printer</filename> is serious,
as the <application/lpd/ server uses this as a lock file.</para>

<para>The <literal/bad directory/
message about the spool directory is usually caused by
wrong permissions or a missing directory.</para>

<para>The <command>checkpc -f</command> (fix) option causes <application>checkpc</application> to take action to rectify errors.
The <command>checkpc -f -V</command> (verbose) option causes the fixup activity to be displayed a well:
<informalexample>
<screen><prompt>h4: {3} # </prompt><userinput>checkpc -f -V</userinput>
Checking for configuration files '/etc/lpd.conf'
  found '/usr/local/etc/lpd.conf', mod 0100644
Checking for printcap files '/etc/printcap'
  found '/usr/local/etc/printcap', mod 0100644
Checking for lpd only printcap files '/etc/lpd_printcap'
 DaemonUID 1, DaemonGID 1
Using Config file '/etc/lpd.conf'
LPD lockfile '/var/run/lpd.printer'
 Checking directory: '/var/run'
   directory '/var'
   directory '/var/run'
  checking '/var/run/lpd.printer' file

Checking printer 'lp'
 Checking directory: '/var/spool/lp'
   directory '/var'
   directory '/var/spool'
   directory '/var/spool/lp'
  file 'control.lp', zero length file unchanged in 1 hours
  file 'status.lp', zero length file unchanged in 1 hours
  file 'status', zero length file unchanged in 1 hours
  file 'log', zero length file unchanged in 1 hours
  file 'acct', zero length file unchanged in 1 hours
  checking 'control.lp' file
  checking 'status.lp' file
  checking 'status' file
  cleaning 'status' file, 0K bytes: no truncation
  checking 'log' file
  cleaning 'log' file, 0K bytes: no truncation
  checking 'acct' file
  cleaning 'acct' file, 0K bytes: no truncation</screen>
</informalexample>
</para>
<para><application>checkpc</application> will create the spool directories
and any missing files, and fix the permissions of existing files.
</para>
</sect1>

<sect1><title>Compilation and Install</title>

<para>Once you have decided on the configuration you want and
understand what files need to be installed,
then you are ready  to do the install.
It is extremely simple to
extract the files,
configure, compile and install the software:
<informalexample>
<screen><prompt>h4: {4} % </prompt><userinput>gunzip -c LPRng-&lt;version&gt;.tgz | tar xvf -</userinput>
<prompt>h4: {5} % </prompt><userinput>cd LPRng-&lt;version&gt;</userinput>
<prompt>h4: {6} % </prompt><userinput>./configure  [ ... configuration options ]</userinput>
<prompt>h4: {7} % </prompt><userinput>make clean all</userinput>
<prompt>h4: {8} % </prompt><userinput>su   # you must do the following commands as root</userinput>
<prompt>h4: {9} # </prompt><userinput>make install</userinput>
<prompt>h4: {10} # </prompt><userinput># if checkpc did not run, do the next command</userinput>
<prompt>h4: {11} # </prompt><userinput># make sure checkpc and lpq are in your paths</userinput>
<prompt>h4: {12} # </prompt><userinput># if using CSH, use 'rehash' as well</userinput>
<prompt>h4: {13} # </prompt><userinput>checkpc -f</userinput>
<prompt>h4: {14} # </prompt><userinput># check to see if the server is running</userinput>
<prompt>h4: {15} # </prompt><userinput>lpq</userinput>
</screen>
</informalexample>
</para>
<para>
During installation you may get an error
message indicating that the <application/lpd/ file could
not be installed or the <application/lpd/ server could not be
started.
If this is the case,
then carry out the appropriate steps in
<link linkend=update>
Updating Print Spooler Software and Startup Scripts
</link>
to remove the existing print spooling software and then
try to reinstall &LPRng;.
You should also to check the
<link linkend=systemspecific>
System Specific Notes
</link>
section for any system specific installation requirements.
</para>
<para>
While the &LPRng; installation scripts try hard to set up the
<application/lpd/ server in place,
you will still need to reboot your host and make sure that the
various printing facilities are working correctly after reboot.
</para>
</sect1>

<sect1><title>Installation Problems</title>
<para>Read the notes for your OS in section
<link linkend="systemspecific">System-dependent notes</link>
for specific installation help (if any).</para>

<para>The following is a list of commonly encountered problems
and their solution.
If these do not solve your problem,
then send mail to the 
<link linkend="maillist">lprng@lprng.com</link>
mailing list.
You will have to subscribe to the list in order to post to the list.
<orderedlist>

<listitem>
<para>
<application>Make</application>
complains about a malformed <filename>make</filename> or
<filename>Makefile</filename> file,
illegal syntax in the file,
or illegal entries in the file.
You are most likely not running GNU Make.
You <emphasis>must</emphasis>
use GNU <application>make</application>
or you should be a Unix Wizard able to master
the mysteries of converting GNU Makefiles to your local system
<application>make</application>.
It is easier to simply install GNU <application>make</application>.</para>
</listitem>

<listitem>
<para>The C Compiler complains about missing files or has a large number of
errors.
Use
<application>gcc</application>
instead of your vendor's C compiler.
<informalexample>
<screen>configure --with-cc=gcc</screen>
</informalexample>
</para>
<para>
If there are messages about missing system files,
then you most likely have an incomplete set of system
<filename>include</filename> files,
or the <filename>include</filename> do not properly reference other
required include files,
or the include files are located in an <emphasis>unusual</emphasis>
location.
If you are using <application>gcc</application>
then make sure that the <application>gcc</application> was carried out
correctly on your system.
The easiest way to assure this is to recompile and reinstall the
<application>gcc</application> compiler.
</para>
</listitem>

<listitem>
<para>If you have checked your compiler installation and are still
missing libraries or files
then the <filename>include</filename> files may be in
<filename>/usr/local/include</filename>
and libraries may be in
<filename>/usr/local/include</filename>
and these directories may not searched or used by the compiler
by default.
This can be fixed by using the
<literal>--with-cppopts=</literal>
and
<literal>--with-ldopts=</literal>
configure options.
<informalexample>
<screen>configure \
  --with-cppopts="-I/usr/local/include -I/usr/include/kerberosIV" \
  --with-ldopts="-L/usr/local/lib -L/usr/lib/kerberosIV"</screen>
</informalexample>
</para>
</listitem>
<listitem>
<para>
The software compiles
but will not run on the system.
Make sure that you have followed your system specific
rules for compiling and installing <literal>setuid ROOT</literal>
programs on your system.  You may need to statically link your
executables.
</para>
</listitem>
<listitem>
<para>
The software was compiled on one system and copied to another
system,
but will not run on the other system.
Try compiling the software on the target system.
If it compiles and runs,
then you most likely have an issue with libraries
or Operating System Versions.
</para>
</listitem>
</orderedlist>
</para>
<para>
After you have installed the
LPRng software
and rebooted your system,
do the following commands:
<informalexample>
<screen><prompt>h4: {16} # </prompt><userinput>lpq</userinput>
Printer: lp@astart
 Queue: no printable jobs in queue</screen>
</informalexample>
</para>
<para>
If you do not get status displayed,
or you get some other error message,
then the following are a series of tests can use to check that
&LPRng; is installed correctly.
</para>
<para>First we will run <application>lpd</application> in the
<emphasis>foreground</emphasis>
and are used to make sure that our system configuration is
correct.
You will need <literal>root</literal> permissions to do
the following steps.
Stop the running currently running <application>lpd</application> process.
Next,
run <application>lpd</application> in foreground mode:
<informalexample>
<screen><prompt>h4: {17} # </prompt><userinput>ps -aux | grep lpd </userinput>
daemon   240  0.0  0.0  1292  0  ??  IWs  -     0:00.00 lpd: lpd Waiting
<prompt>h4: {18} # </prompt><userinput>kill 240</userinput>
<prompt>h4: {19} # </prompt><userinput>checkpc -f</userinput>
<prompt>h4: {20} # </prompt><userinput>/usr/local/bin/lpd -F -D1</userinput>
Fatal error - Another print spooler is using TCP printer port</screen>
</informalexample>
</para>

<para>If you get the above error message,
then you have either not terminated the running
<application>lpd</application> server,
there is another process using TCP/IP port 515,
or you are not starting the <application>lpd</application> server as ROOT.
See the 
<link linkend=systemspecific>
System Specific Notes
</link>
for details on how to resolve these issues.
</para>
<para>
Correct the problem and then restart the server.
You should see the output indicated below:
<informalexample>
<screen><prompt>h4: {21} # </prompt><userinput>/usr/local/bin/lpd -F -D1</userinput>
1999-04-05-14:35:14.023 astart27 [2667] Waiting  lpd: LOOP START
1999-04-05-14:35:14.024 astart27 [2667] Waiting  Get_max_servers: \
   getrlimit returns 256
1999-04-05-14:35:14.024 astart27 [2667] Waiting  Get_max_servers: \
   returning 128
1999-04-05-14:35:14.025 astart27 [2667] Waiting  lpd: \
   max_servers 128, active 0
1999-04-05-14:35:14.025 astart27 [2667] Waiting  lpd: \
   starting select timeout 'yes', 600 sec</screen>
</informalexample>
</para>

<para>Now from another window do the following commands:
<informalexample>
<screen><prompt>h4: {22} # </prompt><userinput>lpq</userinput>
Printer: lp@astart
 Queue: no printable jobs in queue</screen>
</informalexample>
</para>

<para>At this point your &LPRng; software has been installed and tested.
See the
<link linkend=update>
Updating Print Spooler Software and Startup Scripts
</link>
for details on how
to automatically start <application/lpd/ at boot time.
</para>

</sect1>


<sect1 id=update><title>Updating Print Spooler Software and Startup Scripts</title>
<para>If you are replacing your
existing print spooling spooling system,
you must shut down and remove the existing print spooler
software before installing the &LPRng; software.
This process is fairly system dependent,
and requires a small amount of system expertise.
</para>

<para>
To assist in this process the &LPRng; installation
has a set of
<literal>preinstall</literal>,
<literal>postinstall</literal>,
<literal>preremove</literal>,
and
<literal>postremove</literal>
scripts in the distribution that may be suitable for your local
system use.
If these fail to work during the system installation,
you will need to carry out the steps described here by hand.
</para>
<sect2><title>SunOS, Linux, and BSD Derived Systems</title>

<para>The
<literal/SunOS/,
<literal/Linux/,
and
<literal/BSD/ derived systems such as
<literal/BSDi/,
<literal/FreeBSD/,
<literal/OpenBSD/,
and others use a version of the <emphasis/legacy/
or <emphasis/vintage/
<application>lpd</application> print server and the
<application/lpr/,
<application/lprm/,
<application/lpq/,
and
<application/lpc/ client programs.
By convention,
most of the printing related programs are in the
<filename>/usr/bin</filename>,
<filename>/usr/sbin</filename>,
<filename>/usr/libexec</filename>,
and
<filename>/usr/ucb</filename> directories.</para>

<para>The <application/lpd/ print spooler is started by
either the <command>rc</command> startup script
or by a <emphasis>startup script</emphasis>
file in the
<filename>/etc/rc.d/init.d</filename>
or
<filename>/etc/init.d</filename> directory.
You can first locate the startup commands as follows.
<orderedlist>

<listitem>
<para>Use the
<citerefentry/<refentrytitle/find/<manvolnum/1//
utility to search the <filename>/etc</filename>
directory for the file that contains the startup command.
<informalexample>
<screen><prompt>h4: {23} # </prompt><userinput>cd /etc</userinput>
<prompt>h4: {24} # </prompt><userinput>find . -type f -exec grep -l lpd {} \; -print</userinput>
./rc.local</screen>
</informalexample>
</para>
</listitem>

<listitem>
<para>Examine each of the files found find the one
that starts the <application>lpd</application> print spooler.
You can simply comment out the command or change it
to start the &LPRng; <application/lpd/ print server.
<informalexample>
<screen><prompt>h4: {25} # </prompt><userinput>more /etc/rc.local</userinput>
if [ -f /etc/printcap  -a -f /usr/libexec/lpd ] ; then
  /usr/libexec/lpd ;
fi

--- change this to
if [ -f /etc/printcap  -a -f /usr/sbin/lpd ] ; then
  /usr/sbin/lpd ;
fi</screen>
</informalexample>
</para>
</listitem>

<listitem>
<para>If you have an existing <filename>printcap</filename> file,
then you should either copy this to the location used by &LPRng;
or make a symbolic link to it.</para>
</listitem>

</orderedlist>
</para>

<para>Next we kill the currently running <application>lpd</application> process.
<informalexample>
<screen><prompt>h4: {26} # </prompt><userinput>ps -auxw |grep lpd</userinput>
papowell 23932  0.0  0.3  224  184  p3  S+  10:40AM  0:00.01 grep lpd
daemon  17763  0.0  0.2  448  120  ??  IWs  29Mar99  0:01.35 (lpd)
<prompt>h4: {27} % </prompt><userinput>kill 135</userinput>
<prompt>h4: {28} % </prompt><userinput>kill 135</userinput>
135: No such process</screen>
</informalexample>
</para>

<para>Next,
you should remove or rename the existing print system executables.
The following example shows how to use the
<application>find</application> utility to track down candidates.
<informalexample>
<screen><prompt>h4: {29} # </prompt><userinput>find /usr -type f -name lp\*  -print &gt;/tmp/candidates</userinput>
<prompt>h4: {30} # </prompt><userinput>find /sbin -type f -name lp\*  -print &gt;&gt;/tmp/candidates</userinput>
<prompt>h4: {31} # </prompt><userinput>cat /tmp/candidates</userinput>
/usr/bin/lpunlock
/usr/bin/lpqall.faces
/usr/bin/lpq             &lt;---- old
/usr/bin/lpr             &lt;---- old
/usr/bin/lprm            &lt;---- old
/usr/bin/lptest
/usr/doc/samba-1.9.18p10/examples/printer-accounting/lp-acct
/usr/man/man1/lpq.1
/usr/man/man1/lpr.1
/usr/man/man1/lprm.1
/usr/man/man1/lptest.1
/usr/man/man4/lp.4
/usr/man/man8/lpc.8
/usr/man/man8/lpd.8
/usr/sbin/lpc            &lt;--- old
/usr/sbin/lpd            &lt;--- old
/usr/sbin/lpf            &lt;--- old
<prompt>h4: {32} # </prompt><userinput>mv /usr/bin/lpq  /usr/bin/lpq.old</userinput>
<prompt>h4: {33} # </prompt><userinput>mv /usr/bin/lpr  /usr/bin/lpr.old</userinput>
<prompt>h4: {34} # </prompt><userinput>mv /usr/bin/lprm /usr/bin/lprm.old</userinput>
<prompt>h4: {35} # </prompt><userinput>mv /usr/sbin/lpc /usr/sbin/lpc.old</userinput>
<prompt>h4: {36} # </prompt><userinput>mv /usr/sbin/lpd /usr/sbin/lpd.old</userinput>
<prompt>h4: {37} # </prompt><userinput>mv /usr/sbin/lpf /usr/sbin/lpf.old</userinput>
</screen>
</informalexample>
</para>

<para>After all this,
you should now run <command>checkpc -f</command>
(as root) to make sure that the &LPRng; configuration is present and
correctly set up,
and then start <application>lpd</application> by hand.
You should try to use <application>lpq</application> to see if the spool queues are present
and set up correctly and the system is functional.</para>

<para>
<informalexample>
<screen><prompt>h4: {38} #</prompt> <userinput>checkpc -f</userinput>
<prompt>h4: {39} #</prompt> <userinput>lpd</userinput>
<prompt>h4: {40} #</prompt> <userinput>lpq</userinput>
Printer: lw4@h2  'Hp : LaserWriter'
 Queue: no printable jobs in queue
 Status: job 'root@h2+884' removed at 11:27:25.864
 Filter_status: done at 11:27:25.766
<prompt>h4: {41} #</prompt> <userinput>lpr /etc/motd</userinput>
<prompt>h4: {42} #</prompt> <userinput>lpq</userinput>
Printer: lw4@h2  'Hp : LaserWriter'
 Queue: no printable jobs in queue
 Status: job 'root@h2+888' removed at 11:27:25.864
 Filter_status: done at 11:33:17.020</screen>
</informalexample>
</para>

<para>Finally,
you should reboot your machine and make sure that the <application>lpd</application>
print server starts correctly.</para>

</sect2>

<sect2 id="solarisinstall"><title>Solaris, HP-UX, and other SysVR4 Derived Systems </title>

<para>The original SysVR4 (System V, Release 4)
software did not have
any support for RFC1179 network printing (Berkeley <application/lpd/).
Support for this was added in a wide variety of different ways.
There are a wide range of different ways that this was done,
but most are based on the following system or process structure.</para>

<para>The
<literal remap=tt>lpsched</literal>
process (<filename>/usr/lib/lp/lpsched/</filename>)
process performs many of the functions of the &LPRng; and BSD
<application>lpd</application>
server.
This process is responsible for overseeing job printing
and starting processes for handling the print queues on the local host.
This process must be shut down and the running print spooling
servers terminated before &LPRng; can be correctly installed.
While
there is no simple
and reliable method of shutting down a running
<literal remap=tt>lpsched</literal>
process and the associated network services,
it is simple to <emphasis>prevent</emphasis>
the process from being started.</para>

<para>The <filename>preinstall.solaris</filename>
script is a file in the &LPRng; distribution
that contains most of the commands needed to
remove the Solaris System V printing software.
These are explained in detail in the sections below.
The procedures outlined below can be used on other SystemVR4
systems.
<informalexample>
<screen>#!/bin/sh
# This is an effort to automate the setup
#  needed to install the &LPRng; software on the
#  Solaris OS.  This is effectively a one way path.
#  You are warned.
PATH=/etc:/usr/etc:/usr/bin:/bin:/sbin:/usr/sbin:$PATH
# remove the init.d entry and links
for i in /etc/rc*.d/*lp ; do
    b=`basename $i`;
    d=`dirname $i`;
    mv $i $d/UNUSED.$b.UNUSED
done
# rename files
renameit () {
    for i in $* ; do
        if [ -f $i -a '!' -f $i.old ] ; then
            echo "renaming $i $i.old";
            mv $i $i.old
        fi
    done
}
renameit /usr/bin/lp /usr/bin/lpstat /usr/sbin/lpadmin \
  /usr/sbin/lpfilter /usr/sbin/lpforms /usr/sbin/lpmove \
  /usr/sbin/lpshut /usr/sbin/lpsystem /usr/sbin/lpusers \
  /usr/ucb/lpc /usr/ucb/lpq /usr/ucb/lpr /usr/ucb/lprm \
  /usr/ucb/lptest /usr/lib/lp/lpsched /usr/lib/lp/lpNet
# remove the cron entry
if [ -f /var/spool/cron/crontabs/lp ] ; then
    mv /var/spool/cron/crontabs/lp \
       /var/spool/cron/UNUSED.crontabs.lp
fi
# comment out inetd.conf entry
if egrep '^printer' /etc/inetd.conf &gt;/dev/null 2&gt;&amp; ; then
    mv /etc/inetd.conf /etc/inetd.conf.bak
    sed -e 's/^printer/# printer/' &lt;/etc/inetd.conf.bak \
       &gt;/etc/inetd.conf
fi
# remove the nlsadmin entry
nlsadmin -r lpd tcp
nlsadmin -r lp tcp
echo REBOOT SYSTEM and then install LPRng</screen>
</informalexample>
</para>

<para>First,
you will need to remove the <filename>/etc/rc</filename> startup files in the <filename>/etc/rc*.d</filename>
directories that start the
<literal remap=tt>lpsched</literal>
process;
see the
<literal remap=tt>init</literal>
program man page for details.
You can find these files by using:
<informalexample>
<screen><prompt>h4: {43} # </prompt><userinput>cd /</userinput>
<prompt>h4: {44} # </prompt><userinput>find . -type f -exec grep -l lpsched {} \; -print &gt;/tmp/files</userinput>
<prompt>h4: {45} # </prompt><userinput>cat /tmp/files</userinput>
/etc/rc0.d/K20lp
/etc/rc2.d/K20lp
/etc/rc2.d/S80lp
/etc/init.d/lp
<prompt>h4: {46} # </prompt><userinput>ls -l ` cat /tmp/files `</userinput>
lrwxrwxr-x 1 root bin 1 Dec 29 23:39 /etc/rc0.d/K20lp -&gt; ../../init.d/lp
lrwxrwxr-x 1 root bin 1 Dec 29 23:39 /etc/rc2.d/K20lp -&gt; ../../init.d/lp
lrwxrwxr-x 1 root bin 1 Dec 29 23:39 /etc/rc2.d/S80lp -&gt; ../../init.d/lp
-rwxr--r-- 5 root sys 460 Sep 1 1998 /etc/rcS.d/K39lp</screen>
</informalexample>
</para>

<para>You can remove these files,
or simply comment out all of the executable commands in the
<filename>/etc/init.d/lp</filename> file.
Next,
find all of the printing related commands and rename them.
For example:
<informalexample>
<screen><prompt>h4: {47} # </prompt><userinput>find /usr -type f -name lp\* -print &gt;/etc/printingfiles</userinput>
<prompt>h4: {48} # </prompt><userinput>cat /tmp/printingfiles</userinput>
/usr/bin/lp
/usr/bin/lpstat
/usr/lib/lp/bin/lp.cat
/usr/lib/lp/bin/lp.set
/usr/lib/lp/bin/lp.tell
/usr/lib/lp/lpNet
/usr/lib/lp/lpsched
/usr/lib/lp/lpdata
/usr/sbin/lpadmin
/usr/sbin/lpfilter
/usr/sbin/lpforms
/usr/sbin/lpmove
/usr/sbin/lpshut
/usr/sbin/lpsystem
/usr/sbin/lpusers
/usr/ucb/lpc
/usr/ucb/lpq
/usr/ucb/lpr
/usr/ucb/lprm
/usr/ucb/lptest
<prompt>h4: {49} # </prompt><userinput>vi /tmp/printingfiles  # remove ones you want to save</userinput>
<prompt>h4: {50} # </prompt><userinput>for i in ` cat /tmp/printingfiles ` ; do</userinput>
<prompt>&gt;</prompt> <userinput>  if [ -f $i -a '!' -f $i.old ] ; then  mv $i $i.old ; fi;</userinput>
<prompt>&gt;</prompt> <userinput>done</userinput></screen>
</informalexample>
</para>

<para>On some systems there may be a <command>cron</command>
file
<filename>/var/spool/cron/crontabs/lp</filename>
which is used to to periodically update and roll over error logs.
You may want to remove this file or comment out its contents.</para>

<para>Check the <filename>/etc/inetd.conf</filename> file for a line like the one below
and comment it out.
This line is not present on all systems.
<informalexample>
<screen>printer stream tcp nowait root /usr/lib/print/in.lpd in.lpd</screen>
</informalexample>
</para>

<para>Use
<command remap=tt>nlsadmin</command>
to force the
<literal>TCP/IP listener</literal>
to release the port, as illustrated below.
This may not be present on all system.
<informalexample>
<screen><prompt>h4: {51} # </prompt><userinput>nlsadmin -v tcp</userinput>
lpd  \x00020203000000000000000000000000  ENABLED  \
  NORPC  root  NOMODULES  /var/spool/lp/fifos/listenBSD  #
0  \x00020ACE000000000000000000000000  ENABLED    \
  NORPC  root  NOMODULES  /usr/lib/saf/nlps_server  #
lp  NOADDR  ENABLED  NORPC  root  NOMODULES \
  /var/spool/lp/fifos/listenS5  #
<prompt>h4: {52} # </prompt><userinput>nlsadmin -r lpd tcp</userinput>
<prompt>h4: {53} # </prompt><userinput>nlsadmin -r lp tcp</userinput></screen>
</informalexample>
</para>

<para>Run
<command>pmadm -l</command> as shown below.
<informalexample>
<screen><prompt>h2.private: {2} # </prompt><userinput>pmadm -l</userinput>
PMTAG    PMTYPE   SVCTAG   FLGS ID    &lt;PMSPECIFIC&gt;
zsmon    ttymon   ttya     u    root  /dev/term/a I - /usr/bin/login ...
zsmon    ttymon   ttyb     u    root  /dev/term/b I - /usr/bin/login ...</screen>
</informalexample>

If you see
<command remap=tt>zsmon</command>
entries for SystemV
<command remap=tt>lpsched</command>
support,
then use <command>pmadm -r </command>to remove them.
These may not be present on all system.
See the
<command remap=tt>pmadm</command>
man page for details on the <literal>-r</literal> literal.</para>

<para>You must now <command>reboot</command>
the host.
<informalexample>
<screen><prompt>h4: {54} # </prompt><userinput>shutdown -y "Whooga! Whooga! Dive! Dive! System going down."</userinput></screen>
</informalexample>
</para>

<para>When the system reboots,
make sure that there is no process listening on port 515 (printer port)
by using:
<informalexample>
<screen><prompt>h4: {55} # </prompt><userinput>telnet localhost 515</userinput></screen>
</informalexample>
</para>

<para>If you can connect,
then there is a problem beyond the scope of these instructions.</para>

<para>Compile and/or install the &LPRng; software.
Make sure that the &LPRng; startup files have been
installed correctly in <filename>/etc/init.d/lprng</filename>
and that the symbolic links to the file have been made correctly.
The &LPRng; startup file will usually have the following contents and
you should use the same filename formats that the
<application remap=tt>lp</application>
startup files had for the
links to the <filename>/etc/init.d/lprng</filename> startup file:
<informalexample>
<screen>LPD_PATH=/usr/sbin/lpd
SHOWALL=-e
case "$1" in
  start)
        # Start daemons.
        /bin/echo "Starting lpd: \c"
        ${LPD_PATH}
        /bin/echo ""
        ;;
  stop)
        # Stop daemons.
        /bin/echo "Shutting down lpd: \c"
        kill -INT `ps ${SHOWALL} \
           | awk '/lpd/{ print $1;}'` &gt;/dev/null 2&gt;&amp;1
        /bin/echo " server stopped";
        ;;
  *)
        echo "Usage: $0 {start|stop}"
        exit 1
        ;;
esac</screen>
</informalexample>

Start the <application>lpd</application> server and then test it:
<informalexample>
<screen><prompt>h4: {56} # </prompt><userinput>checkpc -f</userinput>
<prompt>h4: {57} # </prompt><userinput>/usr/sbin/lpd (or /usr/local/sbin/lpd)</userinput>
<prompt>h4: {58} # </prompt><userinput>lpq</userinput>
Printer: lp
 Queue: no printable jobs in queue</screen>
</informalexample>
</para>

</sect2>
</sect1>
<sect1 id="lpsimulation"><title>Emulation for UNIX SystemV <application/lp/ and <application/lpstat/ </title>

<para>Many utilities in the UNIX System V environment require the
<application remap=tt>lp</application>,
<application remap=tt>lpstat</application>,
and
<application remap=tt>cancel</application>
programs.
It is almost impossible to modify these utilities,
as many are <emphasis>vintage</emphasis>
software which is unsupported or which would
be too costly to update.
In order to support these applications
&LPRng; emulates the
<application remap=tt>lp</application>,
<application remap=tt>lpstat</application>,
and
<application remap=tt>clean</application>
programs.
See the &LPRng; man pages for
<application/lp/, <application/lpstat/,
and <application/cancel/ in the &LPRng; distribution for details
and compatibility.</para>

<para>The &LPRng;
<application remap=tt>lpstat</application> emulator
accepts the
<application remap=tt>lpstat</application>
command line options returns
status in a format that is close to the one that common
<application/lpstat/
implementations return.
Unfortunately,
due to the wide variety of different modifications
and vendor versions of
<application/lpstat/
there are slight differences between the
this status and the status returned by the original
<application/lpstat/.
If this is the case,
then there is little to do but to modify the source code
for <application/lpstat/ and compile a version that implements
the required format.
</para>
<para>If the <application>lpr</application> program is invoked with the name
<application remap=tt>lp</application>,
it will simulate the
<application remap=tt>lp</application>
options.
This can be done by making a symbolic link to the <application/lpr/
program or by making a copy of the
<application/lpr/ program with the name <application/lp/.
<informalexample>
<screen><prompt>h4: {59} # </prompt><userinput>cd /usr/bin</userinput>
<prompt>h4: {60} # </prompt><userinput>ln -s lpr lp</userinput>
<prompt>h4: {61} # </prompt><userinput>lp /tmp/hi</userinput>
request id is root@h4+489</screen>
</informalexample>
</para>
<para>
Finally, if the <application>lprm</application> program is invoked with the name
<application remap=tt>cancel</application>
it will simulate the
<application remap=tt>cancel</application>
command.
This can be done by making a symbolic link to the <application/lprm/
program or by making a copy of the <application/lprm/ program
with the name <application/cancel/.
<informalexample>
<screen><prompt>h4: {62} # </prompt><userinput>cd /usr/bin</userinput>
<prompt>h4: {63} # </prompt><userinput>ln -s lprm cancel</userinput>
<prompt>h4: {64} # </prompt><userinput>cancel 489</userinput>
cancel 513
Printer lp@h9:
  checking perms 'root@h9+513'
  dequeued 'root@h9+513'</screen>
</informalexample>
</para>

<para>Many
<emphasis>vintage</emphasis>
or
<emphasis>legacy</emphasis>
applications have fully qualified paths to the
<application remap=tt>lp</application>
and
<application remap=tt>lpstat</application>
executables,
and it may be necessary to make additional symbolic links or copies
of the &LPRng; executables to satisfy their pathname requirements.
<informalexample>
<screen><prompt>h4: {65} # </prompt><userinput>ln -s /usr/bin/lpr /usr/ucb/lpr</userinput></screen>
</informalexample>
</para>
</sect1>

<sect1 id="smb"><title>SAMBA and &LPRng;</title>

<para>The SMB network protocol
is used by many Microsoft Operating Systems
to implement file and printer sharing.
SAMBA is a UNIX package that implements the SMB protocol and provides
a simple and easy way to import and export file systems and printer
facilities.
The web site for SAMBA is
<ulink URL="http://www.samba.org">http://www.samba.org</ulink>.
The SAMBA code is extremely easy to install
and the <acronym>SWAT</acronym> (Samba Web Administration Tool)
makes configuration almost trivial.</para>

<para>
See the SAMBA
<filename>doc/text/Printing.txt</filename> and related documentation for details on
printing.
In the
<filename>samba.conf</filename> file
<literal remap=tt>[global]</literal>
section
or in the SWAT page for printing configuration
you need to specify the that you want to have
Samba handle printing,
the
<literal remap=tt>print</literal>, <application>lpq</application>, and <application>lprm</application> commands to be used when a user
prints a job,
asks for status,
or removes a job,
and a temporary directory to hold print jobs when they are submitted.
The following is a simple example of to set up printing for
authenticated users.
<informalexample>
<screen>[printers]
    path = /var/spool/lpd/samba
    #  ---  do not use the Samba default path = /tmp
    print ok = yes
    printing = lprng
    load printers = yes
    guest ok = no
    printcap name = /etc/printcap
    print command =      /usr/bin/lpr  -P%p -r %s
    lpq command   =      /usr/bin/lpq  -P%p
    lprm command  =      /usr/bin/lprm -P%p %j
    lppause command =    /usr/sbin/lpc hold %p %j
    lpresume command =   /usr/sbin/lpc release %p %j
    queuepause command = /usr/sbin/lpc  stop %p
    queueresume command = /usr/sbin/lpc start %p
</screen>
</informalexample>
</para>


<orderedlist>
<listitem>
<para>Samba will make a copy of the files to be printed
in the directory specified by <literal>path</literal>.
If the print operation fails then sometimes the print file
is left in the directory.
</para></listitem>
<listitem><para>
The directory should have
the same ownership and permissions as
<filename>/tmp</filename>,
i.e.- owner and group <literal/root/ and <literal/bin/,
with
<literal/01777/ permissions,
where <literal/01000/ is the sticky bit.
</para><para>
A directory whose `sticky bit' is set becomes an append-only directory,
or, more accurately, a directory in which the deletion of files is re-
stricted.  A file in a sticky directory may only be removed or renamed by
a user if the user has write permission for the directory and the user is
the owner of the file, the owner of the directory, or the super-user.
This feature is usefully applied to directories such as /tmp which must
be publicly writable but should deny users the license to arbitrarily
delete or rename each others' files.
</para></listitem>
<listitem><para>
The directory should be examined periodically and files
older then a day should be removed.  The following command
can be used to do this,  and should be put in a file that
is periodically (one a day) executed by the <application/cron/
facility:
<informalexample>
<screen>find /var/spool/lpd/samba -type f -mtime 2d -exec rm -f {} \;</screen>
</informalexample>
</para>
</listitem>

<listitem>
<para>You must specify the print method as <literal>printing = lprng</literal>.
This will allow Samba to parse the
&LPRng; <application/lpq/ status format correctly.</para>
</listitem>

<listitem>
<para>You must put all of the printers which Samba has access to
in the <filename>printcap</filename> file.
Your Samba server may support reading the printcap file by
using a program.  In this case the printcap file entry can
be one of the following:
<informalexample>
<screen>[printers]
  #
    printcap name = |/usr/local/libexec/filters/getpc
  # or
    printcap name = |/usr/bin/lpc client all 

#!/bin/sh
# getpc program
/usr/bin/lpq -as | /bin/sed -e 's/[@:].*//p'

</screen>
</informalexample>
</para>
<para>
The <literal/lpc client all/ command
will generate the printcap entries for all of the printers.
This was done to support Samba and other printer gateway systems.
You can also use a simple script to modify the output of the
printer status command as shown in the example.
</para>
</listitem>

<listitem>
<para>
Samba can be configured to allow guests or non-authenticated users
to spool print jobs.
Unfortunately,
by default <application/lpr/ will mark the jobs
as submitted by the Samba server, not the remote users. 
To solve this problem,
the <command>lpr -U%U@%M</command> option causes
<application/lpr/ to mark the jobs as submitted by user <literal/%U/ on host <literal/%M/,
instead of the Samba server process.
The use of this option is restricted to root and a set of userids listed
in the
<link linkend="allowusersetting">allow_user_setting</link>
configuration option.
If the userid of the submitter is not in this list,
then the option is quietly ignored.
The <command>-U%U@M</command> can also be used with the other LPRng commands as well.
For example:
<informalexample>
<screen>[printers]
    guest ok = yes
    print command =       /usr/bin/lpr  -U%U@%M -P%p -r %s
    lpq command   =       /usr/bin/lpq  -U%U@%M -P%p
    lprm command  =       /usr/bin/lprm -U%U@%M -P%p %j
    lppause command =     /usr/sbin/lpc -U%U@%M hold %p %j
    lpresume command =    /usr/sbin/lpc -U%U@%M release %p %j
    queuepause command =  /usr/sbin/lpc -U%U@%M stop %p
    queueresume command = /usr/sbin/lpc -U%U@%M start %p
</screen>
</informalexample>
</para>
</listitem>

</orderedlist>

<para>
When Samba gets a request for print queue status,
it runs the <literal/lpq command/ program and then
parses the output of this command.
Unfortunately,
different versions of Samba have different ways of parsing
the output - some are more flexible than others.
<para/>
<para>
One of the problems that might occur is when the
&LPRng; <literal/done_jobs/ feature is enabled.
This causes that status of the last few jobs to be retained
so that users can see what happened to their jobs.
For example:
<informalexample>
<screen>
h110: {588} % lpq
Printer: t1@h110 'Test Printer 1'
 Queue: no printable jobs in queue
 Server: no server active
 Status: job 'papowell@h110+336' saved at 14:42:54.607
 Filter_status: FILTER DONE
 Rank   Owner/ID         Class Job Files    Size Time
done   papowell@h110+336   A   336 /tmp/hi     3 14:42:53
</screen>
</informalexample>
</para>
<para>
In this example, the <literal/done/ job will have its status
displayed by the lpq command.
However, this may confuse Samba, and it may report odd or
unusual status for your jobs. 
If the
<application/lpq/ command reports that your job has completed
but Samba reports that it is printing or is stopped,
then you should disable the <literal/done_jobs/ option
in the printcap entry:
<informalexample>
<screen>
lp:
  :done_jobs=0
  :...
</screen>
</informalexample>
</para>

</sect1>

<sect1 id="setuid"><title>Security Concerns </title>

<para>While the &LPRng; software has been written with security as the primary goal
there is always the problem with undetected errors in the &LPRng;
software that
when exploited
could compromise system security.
The most serious concern is that of gaining ROOT (UID 0) permissions.</para>
<para>
The simplest way to handle this problem is to not install
LPRng with <literal>setuid ROOT</literal> permissions.
Client programs will be able to connect to the <application/lpd/
server.
Since the <application/lpd/ server is started by the system
startup script with effective UID root,
it is the only program in this suite that will have an privileged
user id.
</para>

<para>
A more radical step is to run the <application/lpd/
server as a non-privileged user entirely.
However,
the RFC1179 protocol specifies
that the <application/lpd/ TCP/IP port is 515
and
<application>lpd</application>
requires root permissions to open and bind to port 515.
The <application>lpd</application>
server can use the <function/setuid()/ system call
after binding to this port do drop ROOT capabilities.
However,
in order to fully compatible with RFC1179,
<application>lpd</application> must originate connections from a <emphasis>reserved</emphasis>
port in the range 721-731,
although in practice port 1-1023 seems to be acceptable.</para>

<para>If inter-operability with non-&LPRng; print spoolers is not desired,
then it is <emphasis>trivial</emphasis>
to configure &LPRng; to use a non-privileged
port by using the <filename>lpd.conf</filename> file.
For example,
in the <filename>/etc/lpd.conf</filename> file,
you only need to change the indicated lines:
<informalexample>
<screen># Purpose: lpd port
#   default lpd_port=printer
lpd_port=2000
# or lpd_port=localhost%2000</screen>
</informalexample>
The <literal>lpd_port</literal> specifies the (optional)
IP address and port to which the <application/lpd/ server binds
and to which the clients will connect.
&LPRng; applications will connect to port 2000
to transfer jobs and ask for status.
You can also use this facility to establish a
<emphasis>private</emphasis>
set of print spoolers which can be used for testing
See
<link linkend=testing>Testing and Diagnostic Facilities</link>
for more details.</para>

<para>Some <emphasis>legacy</emphasis> print filters
are not <emphasis>meta-char-escape</emphasis> proof.
For example,
suppose that a user decided to spool a job as follows:
<informalexample>
<screen><prompt>h4: {66} #</prompt> <userinput>lpr "-J`;rm -rf /;`" /tmp/a</userinput></screen>
</informalexample>
This would create a job file with the line:
<informalexample>
<screen>J`rm -rf /;`</screen>
</informalexample>
and gets passed to a print filter as
<informalexample>
<screen>/usr/local/printfilter  -J`rm -rf /;`</screen>
</informalexample>
The observant reader will observe that the above line
may have the most hideous consequences if it is processed
by a shell.
For this reason the &LPRng; software takes extreme precautions
and <emphasis/sanitizes/ control file contents and file names
so that they do not contain any control or metacharacters.
</para>

<para>
Finally,
you can use a Unix socket (i.e. - FIFO) for connections to the server on
the localhost,
and disable the &lpd; listening socket by setting the
<literal/lpd_listen_port/ value to <literal/off/.
</para>
</sect1>
</chapter>

<chapter id=systemspecific><title>System Specific Notes </title>

<para>The following are a set of suggestions and recommendations for
specific systems.</para>


<sect1 id="solaris"><title>Solaris</title>

<para>The Sun Microsystems Solaris printing system is derived from the
System V UNIX system.
Please see the

<link linkend="solarisinstall">Solaris, HP, and SysVR4 Derived Systems</link>

installation information for a detailed description of how
to install &LPRng; and remove the Solaris Print Services.
</para>

<para>
If you want to simply forward jobs from a Solaris system to a
BSD print spooling system you can use the following commands to
create a printer.  Check your specific system references and man
pages for options:
<informalexample>
<screen><prompt>h4: {67} # </prompt><userinput>lpsystem -t bsd servername</userinput>   # add server
<prompt>h4: {68} # </prompt><userinput>lpadmin -p pr -s servername -T unknown -I any</userinput> # set up printer
<prompt>h4: {69} # </prompt><userinput>accept pr</userinput>   # enable queueing
<prompt>h4: {70} # </prompt><userinput>enable pr</userinput>   # enable printing
<prompt>h4: {71} # </prompt><userinput>lpstat -t</userinput>   # show status
scheduler is running
system for pr: servername
lp accepting requests since Mon Aug  6 12:00:00 PST 2000
Printer: pr@servername  'Hp : Laserwriter' (printing disabled)
 Queue: 1 printable job
 Rank  Owner/ID            Class Job Files            Size Time
1      papowell@h4+207       A   207 h4.023205           3 18:24:54</screen>
</informalexample>
</para>
<para>
The above commands will create the necessary directories and files
for the printer.
If you want to use the <literal/lp -o option/ syntax to pass options
to the LPRng print spooler you will have to enable this
by hand.
The <filename>/etc/printers.conf</filename> (this may be in some other
directory besides <filename>/etc</filename>) needs to be modified
so that it allows options to be passed using the Solaris convention,
which is to put them on the <literal/S/ or <literal/O/ line of the 
control file.
For example:
<informalexample>
<screen>
#
# The preferred method of modifying this file is through the use of
# lpset(1M) or fncreate_printer(1M)
#
pr:bsdaddr=servername,pr,Solaris:
_default:use=pr:</screen>
</informalexample>
</para>
<para>
The <literal/bsdaddr=host,printer[,Solaris]/
indicates that the entry is for a remote RFC1179 printer
on server <literal/host/ with name <literal/printer/.
The <literal/Solaris/ option indicates that the Solars extensions
to the RFC1179 protocol are to be used.
</para>
</sect1>

<sect1 id="linux"><title>Linux</title>

<para>
There is no universal way to install &LPRng; cleanly on all of the different
Linux systems.
The major difficulty is the fragmentation in the various libraries,
location of files,
and system dependencies.
If the &LPRng; installation procedure does not install the software
correctly,
please use the following remedial steps.
<orderedlist>
<listitem>
<para>Check the &LPRng; web site
<ulink URL="http://www.lprng.com">http://www.lprng.com</ulink>
and see if there is a Linux Release RPM or other binary distribution
for your version of Linux.
If not, then you will have to do a source install.</para>
</listitem>

<listitem>
<para>Obtain the source distribution and follow
the instructions outlined in other sections to compile and
install the software.
Use the following configuration options
which correspond to Linux:
<informalexample>
<screen><prompt>h4: {72} # </prompt><userinput>./configure --prefix=/usr \
  --sysconfdir=/etc --mandir=/usr/share/man</userinput>
<prompt>h4: {73} # </prompt><userinput>make clean all install</userinput>
<prompt>h4: {74} # </prompt><userinput>checkpc -f</userinput></screen>
</informalexample>
</para>
</listitem>
<listitem>
<para>
You may need to modify your existing printcap file by adding the
<option/:bkf/ flag as shown below,
or replace the entries with other filters.
<informalexample>
<screen>lp:\
  :if=/usr/local/libexec/lpr/hplaserjet3:\
  :bkf:\     # added to the printcap by hand
  :...</screen>
</informalexample>
</para>
</listitem>
<listitem>
<para>
Test the system by printing a file.
If this does not work,
then you will have to determine if the problem is in the print spooling
software or in the filter.
See the section on
<application remap=tt>ifhp</application>
for directions on how to replace the vendor
supplied filters with
<application remap=tt>ifhp</application>.</para>
</listitem>
</orderedlist>
</para>

</sect1>

<sect1 id="aix"><title>AIX</title>

<para>This information was supplied by
<ulink URL="mailto:nitschke@math.unihamburg.de">Dirk Nitschke</ulink>,
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.</para>

<para>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
<command remap=tt>qprt</command> or
<command remap=tt>enq</command>.
You can use the BSD or
System V printing commands <application>lpr</application> or
<application remap=tt>lp</application>, too. The
<command>qdaemon</command>
watches all (general) queues and knows how to handle your
job. A (general) queue is defined in the file
<filename>/etc/qconfig</filename>. The format of this file is different from
the <filename>printcap</filename> format.</para>

<para>OK, how to replace the AIX printing system? There is no group
<literal>daemon</literal> on AIX. Therefore you have to change the default
group for file ownership and process permissions
or create a <literal>daemon</literal> user and group.
We decided to use the
<literal remap=tt>printq</literal>
group;
on reflection it would have been easier
to have created a <literal>daemon</literal> group.
The user <literal>daemon</literal> exists on
AIX but we have chosen <application>lpd</application> as the user who runs
<application>lpd</application> and all filters and owns the spooling directories.
You can change the values for
<literal remap=tt>group</literal>
and
<literal remap=tt>user</literal>
in your
<filename>lpd.conf</filename> file or in the sources
<filename>src/common/vars.c</filename>. This is an example for
<filename>lpd.conf</filename>:
<informalexample>
<screen># Purpose: group to run SUID ROOT programs
#   default group=daemon
group=printq
# Purpose: server user for SUID purposes
#   default user=daemon
user=lpd</screen>
</informalexample>

Compile and install the &LPRng; package. Create your
<filename>printcap</filename>, spooling directories, accounting and logfiles
and so on.
Don't forget to use

<link linkend="checkpc">checkpc</link>
to make sure that all the
permissions are set correctly and the necessary files
are created.</para>

<para>Then stop all print queues defined on your workstation. Use
<informalexample>
<screen># chque -q queuename -a "up = FALSE"</screen>
</informalexample>

for this (yes, blanks around <literal>=</literal> are needed).</para>

<para>If you have local printers attached to your system you will have an
<application>lpd</application> running. Stop this daemon using SMIT (Print Spooling,
Manage Print Server, Stop the Print Server Subsystem). Choosing
<emphasis>both</emphasis>
also removes <application>lpd</application> from
<filename>/etc/inittab</filename>. Maybe it's faster to do this by hand:
<informalexample>
<screen><prompt>h4: {75} # </prompt><userinput>topsrc -p'pid of /usr/sbin/lpd'</userinput>
<prompt>h4: {76} # </prompt><userinput>rmitab "lpd"</userinput></screen>
</informalexample>
</para>

<para>Now delete all print queues managed by <literal>qdaemon</literal> defined on your
system. You can use <command/SMIT/ for this or the commands
<command remap=tt>{mk,ch,rm}que</command>,
<command remap=tt>{mk,ch,rm}quedev</command>,
<command remap=tt>{mk,ch,rm}virprt</command>. The <command/SMIT/ fast path is
<command remap=tt>smit rmpq</command>.</para>

<para>To start the new <application>lpd</application> at system startup you have to add
an entry to <filename>/etc/inittab</filename>:
<informalexample>
<screen><prompt>h4: {77} # </prompt><userinput>mkitab "lpd:2:once:/full/path/lpd"</userinput></screen>
</informalexample>
</para>

<para>Some work has to be done if have have a local printer attached to
your workstation. You have to create a device file like
<filename>/dev/lp0</filename>. The <command/SMIT/ fast path for this is
<command remap=tt>smit mkdev</command>.
Choose <literal>Printer/Plotter</literal>,
then
<literal remap=tt>Printer/Plotter Devices</literal>,
then
<literal remap=tt>Add a Printer/Plotter</literal>.
To create a parallel printer device select the following:
<informalexample>
<screen>Plotter type:              opp Other parallel printer
Printer/Plotter Interface: parallel
Parent Adapter:            ppa0 Available</screen>
</informalexample>
Now define the characteristics of the device:
<informalexample>
<screen>Port Number: p</screen>
</informalexample>
Option <literal remap=tt>p</literal>
is for parallel.
Go to the field:
<informalexample>
<screen>Send all characters to printer UNMODIFIED   no</screen>
</informalexample>
</para>
<para>
and select
<literal remap=tt>yes</literal>! We have had a lot of trouble with
<literal remap=tt>no</literal>.
This is very important! Expect erroneous output if
you choose
<literal remap=tt>no</literal>. If you have already created a device
file, change the characteristics! SMIT's fast path is
<command remap=tt>smit chdev</command>.</para>

<para>Finally remove all AIX printing commands like
<application remap=tt>qprt</application>,
<application remap=tt>lp</application>,
<application remap=tt>cancel</application>, <application>lpq</application>,
and
<application>lprm</application>. You will find a lot of them in
<filename>/usr/bin</filename>. Do not remove
<application remap=tt>enq</application>
and friends if
you want to use the general queueing facility.</para>

<para>Now you can start your new <application>lpd</application>.</para>

</sect1>

<sect1 id="appletalk"><title>AppleTalk Support </title>

<para>Netatalk is used to communicate from TCP/IP to
AppleTalk printers and vice versa.
The Netatalk distribution FAQ is at:
<ulink URL="http://www.umich.edu/~rsug/netatalk">http://www.umich.edu/~rsug/netatalk</ulink>.
Also,
The University of Melbourne web site
<ulink URL="http://www.cs.mu.oz.au/appletalk/">http://www.cs.mu.oz.au/appletalk/</ulink>
has additional tutorial and installation information.
In addition,
Anders Brownworth's Web page
<ulink URL="http://thehamptons.com/anders/netatalk/">http://thehamptons.com/anders/netatalk/</ulink>
has a useful collection of information for Linux users.</para>

<para>
The University of Michigan doesn't seem to be doing anything more
for <application/netatalk/, and Adrian Sun has continued development.  You'll
find his updates at
<ulink URL="ftp://ftp.cobaltnet.com/pub/users/asun/testing/">
ftp://ftp.cobaltnet.com/pub/users/asun/testing/</ulink>
He has improved <application/netatalk/ considerably over the last UMich version.
Documentation on <application/netatalk/ is,
unfortunately,
almost non-existent.
</para>
<para>
There are a couple of very active mail lists for Netatalk:
<ulink URL="mailto:netatalk-admins@umich.edu">netatalk-admins@umich.edu</ulink>
and
<ulink URL="mailto:linux-atalk@netspace.org">linux-atalk@netspace.org</ulink>.
</para>

<para>After you have installed and gotten <application/netatalk/ working
you can use the following AppleTalk configuration file to print
from a Macintosh to an &LPRng; printer.
<informalexample>
<screen>Your 32 Character Printer Name:\
        :pr=|/your/path/to/lpr -Pprintername:\
        :op=username.for.printing:\
        :pd=/your/path/to/ppd/files/yourprinter.ppd:

Examples:

Student Printers:\
        :pr=|/usr/bin/lpr -Pstudent:\
        :op=root:\
        :pd=/var/spool/lpd/student/HP4000.PPD:
HP 2500c:\
        :pr=|/usr/bin/lpr -Php2500c:\
        :op=root:\
        :pd=/var/spool/lpd/hp2500c/HP2500.PPD:</screen>
</informalexample>
</para>

</sect1>
</chapter>

<chapter id=tutorial><title>Print Spooling Tutorial </title>

<para>A print spooler is a program that accepts
<emphasis>print jobs</emphasis>
(which are usually one or more files)
from a program or network interface,
stores them in a
<emphasis>spool queue</emphasis>,
and then sends them to a printer or another
print spooler.
Usually there are facilities to submit jobs,
check on the current job status,
remove jobs from spool queues,
and perform administrative functions such as starting or
stopping printing.</para>

<para>A print spooler is a client/server application.
The client programs are used to submit jobs to the print spooler
program which performs the actual printing operations.
In order to carry out these operations,
the server may need to use other programs to convert print job files
into a format acceptable to a printer,
or perform various accounting or administrative functions.</para>


<sect1 id=overview><title>Overview</title>

<para>
<informalexample>
<screen>
+---------+    +-----+    +-----+     +--------+    +---------+
| program | -&gt; | lpr | -&gt; | lpd |  -&gt; | filter | -&gt; | printer |
+---------+    +-----+  * +-----+     +--------+    +---------+
                  *    *     |
               printcap      V
                          +-----+     +--------+    +---------+
                          | lpd |  -&gt; | filter | -&gt; | printer |
                          +-----+     +--------+    +---------+

                           Figure 1</screen>
</informalexample>
</para>

<para>Figure 1 shows the flow of data between the individual components of the
&LPRng; print spooling system.
A program (or user) will use the <application>lpr</application> program to send a file
to the <application>lpd</application> server over a TCP/IP connection.
The <application>lpd</application> server will store the file temporarily in a
spool queue directory.
The information needed by the <application>lpr</application> and <application>lpd</application> programs to carry
out this activity is stored in the
<filename>printcap</filename>  (usually called the <filename>/etc/printcap</filename>) database file.</para>

<para>The <application>lpd</application> server sorts the queue entries and determines the print order.
It will select a job to be printed,
open a connection to the printer,
and then use a <emphasis>filter</emphasis>
program to convert the file contents into a
format suitable for the printer.
If the file does not need conversion,
then the <application>lpd</application> server will send the file directly to the printer.</para>

<para>The <application>lpd</application> server can also
<emphasis>forward</emphasis>
jobs to another print server
over a network connection,
optionally sending them through a filter as well.
The destination server can in turn forward the job or send it to a printer.</para>

<para>The protocol or commands used to do this job forward and transfer are
specified by

<link linkend="rfc1179ref">RFC1179</link>.
This protocol specifies how the <application>lpr</application> client program sends a job
to the <application>lpd</application> server,
as well as how the <application>lpd</application> server forwards jobs to another server.
In addition to job submission,
RFC1179 specifies commands to obtain queue status,
to remove jobs from the queue,
and to start and stop print queues.</para>

</sect1>

<sect1 id="sampleprintcap"><title>Sample Printcap Entry</title>

<para>As described in the

<link linkend="overview">Print Spooling Overview</link>,
the information in the <filename>printcap</filename> database is used control printing
operations.
While there is no RFC specifying its format or content,
there is a strong <emphasis>de facto</emphasis>
standard for its format.
For a complete description of the <filename>printcap</filename> database see
<link linkend="printcapref">Printcap Database</link>.
For a list of all of the <filename>printcap</filename> and configuration options see
<link linkend="optionindex">Index To All The Configuration and Printcap Options</link>.</para>

<para>Here is a sample printcap suitable for use by the &LPRng; clients:
<informalexample>
<screen>LPRng use :lp for destination (device and spool queue)
   lp:lp=lp@h4.private
   lpreal:lp=/dev/lp0

Vintage BSD uses :rp:rm for spooler queue and :lp for device
  lp:rp=lp:rm=h4.private
  lpdev:lp=/dev/lp0
</screen>
</informalexample>
</para>

<para>The <literal>:lp=lp@h4.private</literal> printcap entry
tells the <emphasis>client</emphasis>
programs that jobs for the
<application remap=tt>lp</application>
printer or print queue are sent to the
<application remap=tt>lp</application>
print queue queue on host <filename>h4.private</filename>.
This can also be specified using the
The <emphasis>legacy</emphasis>
BSD <literal>:rp</literal>
and <literal>:rm</literal>.
The
<literal remap=tt>:rp=</literal><emphasis>queue</emphasis>
option
specifies the <emphasis>print queue</emphasis>
<literal remap=tt>:rm=</literal><emphasis>host</emphasis>
option
specifies the host.
When both
<literal remap=tt>lp</literal>
and
<literal>:rp:rm</literal> are present the
<literal remap=tt>:lp</literal>
option has precedence.</para>

<para>On the printserver
the following is a sample printcap entry
suitable for the <application>lpd</application> server:
<informalexample>
<screen>lp:
  :lp=/dev/lp0
  :sd=/var/spool/lpd/%P
  :filter=/usr/local/libexec/filters/ifhp</screen>
</informalexample>
</para>

<para>The
<literal remap=tt>:sd=</literal><emphasis>directory</emphasis>
option
(spool queue directory) specifies the directory where
print jobs will be placed.
The <literal>%P</literal> will be replaced with the name of the spool queue.
The
<literal remap=tt>:lp</literal> literal
is now the path to the output device
the <application>lpd</application> server will use to print the job.
The <literal>:filter</literal> literal specifies the filter
program to use to process the job before sending to the output device.
</para>
<para> Here is another example of a printcap entry using the
<literal remap=tt>%P</literal>
notation:
<informalexample>
<screen>lp:lp=%P@h4.private</screen>
</informalexample>
This entry will cause all jobs sent to the
<literal>lp</literal>
spool queue to be sent to the
<literal>lp</literal> queue on <literal>server</literal>.
<literal remap=tt>%X</literal>
strings in the printcap entries are expanded as shown:
<informaltable frame=all>
<tgroup cols=2 align=left rowsep=1 colsep=1>
<thead>
<row><entry>Key</entry><entry>Replaced By</entry></row>
</thead>
<tbody>
<row><entry><literal/%P/</entry><entry>printcap entry primary name (printer)</entry></row>
<row><entry><literal/%Q/</entry><entry>queue requested</entry></row>
<row><entry><literal/%h/</entry><entry>short host name  (host)</entry></row>
<row><entry><literal/%H/</entry><entry>fully qualified host name  (host.dns.whatever)</entry></row>
<row><entry><literal/%R/</entry><entry>remote printer (rp value)</entry></row>
<row><entry><literal/%M/</entry><entry>remote host (rm value)</entry></row>
<row><entry><literal/%D/</entry><entry>date in YYYY-MM-DD format</entry></row>
</tbody>
</tgroup>
</informaltable>
</para>

</sect1>

<sect1 id="tutorialconfig"><title>Setting Up the Tutorial Configuration</title>

<para>The previous section has given a very high level view of printing
operations and shown a sample of some printcap files.
In order to do experiment with these LPRng facilities,
we will need to be able to modify the
<filename>printcap</filename>
information and try various system configurations.
</para>

<para>We will use a series of simple printcap entries during this tutorial.
We will assume that the &LPRng; system is using
the <filename>/etc/printcap</filename> file.
If your system is configured to use another one,
then you can make a symbolic link from <filename>/etc/printcap</filename>
or you can simply use your default
<filename>printcap</filename> file.
</para>
<para>
Save the existing <filename>printcap</filename> file and then
create a new printcap file
with the contents as shown below.
You will need to have ROOT (superuser) permissions to change
the file and perform some of the maintenance operations.
<informalexample>
<screen>
<prompt>h4: {78} # </prompt><userinput>cd /etc </userinput>
<prompt>h4: {79} # </prompt><userinput>mv printcap printcap.orig</userinput>
<prompt>h4: {80} # </prompt><userinput>vi printcap</userinput>
<userinput>#  printcap file contents:</userinput>
<userinput>lp:sd=/var/spool/lpd/%P</userinput>
<userinput>  :force_localhost</userinput>
<userinput>  :lp=/tmp/lp</userinput>
<userinput>lp2:sd=/var/spool/lpd/%P</userinput>
<userinput>  :force_localhost</userinput>
<userinput>  :lp=/tmp/lp2</userinput>
<prompt>h4: {81} # </prompt><userinput>#  set permissions so everybody can read file</userinput>
<prompt>h4: {82} # </prompt><userinput>chmod 666 printcap</userinput></screen>
</informalexample>
</para>

<para>
We save the original <filename>printcap</filename> file
and create a new one.
We give the file world writable permissions so that later we can modify this
file without needing to have root permissions.
The <filename>printcap</filename> file has two entries:
<literal remap=tt>lp</literal>
and
<literal remap=tt>lp2</literal>.
Each print queue on the server needs a spool file to hold print jobs,
jobs,
and the
<literal remap=tt>:sd</literal>
value specifies its location.
The
<literal remap=tt>%P</literal>
value is replaced with the name of the printer
when it is used.
In classical BSD operation
each host has an <application>lpd</application> print spooler running on the local host
(we use localhost in this manual for simplicity).
Files were copied to spool directories on the localhost and then then
print spooler would send them to the destination,
which could be another print spooler.
This meant that each localhost machine had to have a print spooler
and spool queue directory structure.
Management of this becomes very difficult in large organizations.
The <literal>force_localhost</literal> forces this mode of
operation
and means that the <application/lpd/ server and clients must run
on the same host.
</para>
<para>
We use files
for the output devices (<literal>:lp=</literal>)
so that we can see easily view the output
(and also to save trees).
We will also need to have some simple test files.
Create the files using the following commands.
<informalexample>
<screen><prompt>h4: {83} # </prompt><userinput>cp /dev/null /tmp/lp</userinput>
<prompt>h4: {84} # </prompt><userinput>cp /dev/null /tmp/lp2</userinput>
<prompt>h4: {85} # </prompt><userinput>chmod 666  /tmp/lp /tmp/lp2</userinput>
<prompt>h4: {86} # </prompt><userinput>echo hi &gt;/tmp/hi</userinput>
<prompt>h4: {87} # </prompt><userinput>echo there &gt;/tmp/there</userinput></screen>
</informalexample>
</para>

<para>We will use
a <emphasis>dummy</emphasis>
<filename>lpd.perms</filename>
file that allows all users to do anything.
This is useful for testing,
but dangerous in a working environment.
<informalexample>
<screen><prompt>h4: {88} # </prompt><userinput>#  we modify the lpd.perms to allow an ordinary user to control</userinput>
<prompt>h4: {89} # </prompt><userinput>mv lpd.perms lpd.perms.orig</userinput>
<prompt>h4: {90} # </prompt><userinput>echo "DEFAULT ACCEPT" &gt;lpd.perms</userinput>
<prompt>h4: {91} # </prompt><userinput>chmod 666 lpd.perms</userinput></screen>
</informalexample>
</para>

<para>Finally we run <command>checkpc</command>
to make sure that our printcap is correct and to create the
necessary spool directories:
<informalexample>
<screen><prompt>h4: {92} # </prompt><userinput>checkpc -f -V</userinput>
Checking printer 'lp'
 Checking directory: '/var/spool/lp'
   directory '/var'
   directory '/var/spool'
   directory '/var/spool/lp'
 Warning -   changing ownership '/var/spool/lp' to 1/1
   checking 'control.lp' file
   checking 'status.lp' file
   checking 'status' file
   cleaning 'status' file, 0K bytes: no truncation
   checking 'log' file
   cleaning 'log' file, 0K bytes: no truncation
   checking 'acct' file
   cleaning 'acct' file, 0K bytes: no truncation
Checking printer 'lp2'
  Checking directory: '/var/spool/lp2'
    directory '/var'
  ....
<prompt>h4: {93} # </prompt><userinput>lpc reread</userinput>
<prompt>h4: {94} # </prompt><userinput>lpd</userinput>
<prompt>h4: {95} # </prompt><userinput>lpq</userinput>
Printer: lp@h4
   Queue: no printable jobs in queue</screen>
</informalexample>
</para>

<para>
<link linkend="checkpc"><application/Checkpc/</link>
performs consistency checks on the printcap file and spool queue entries.
The
<command>checkpc -f</command> (fix) option will change permissions and create
directories and can only be executed by ROOT.
<link linkend="checkpc"><application/Checkpc/</link>
has other functions as well - you can view printcap information,
see default configuration values,
and
remove junk files from spool queues with it.</para>

<para>The
<command remap=tt>lpc reread</command>
command sends a request to the <application/lpd/ server to reread the configuration
and printcap information.
The <application>lpd</application> command is added as insurance in case your <application>lpd</application>
server is not running.
The
<command remap=tt>exit</command>
command restores ordinary user privileges,
and the <application>lpq</application> command is used to check that the server is running.
Finally,
we check to see that the
<command remap=tt>lpc reread</command>
command is accepted
from an ordinary user.</para>

</sect1>

<sect1 id="restoring"><title>Restoring Original Configuration</title>

<para>To restore the original configuration,
you simply need to restore the original <filename>printcap</filename>
and <filename>lpd.perms</filename> file and then restart the <application>lpd</application> server.
These operations require ROOT permissions.
<informalexample>
<screen><prompt>h4: {96} # </prompt><userinput>su</userinput>
<prompt>h4: {97} # </prompt><userinput>cd /etc    OR    cd /usr/local/etc</userinput>
<prompt>h4: {98} # </prompt><userinput>mv printcap.orig printcap</userinput>
<prompt>h4: {99} # </prompt><userinput>mv lpd.perms.orig lpd.perms</userinput>
<prompt>h4: {100} # </prompt><userinput>checkpc -f</userinput>
<prompt>h4: {101} # </prompt><userinput>lpc reread</userinput>
<prompt>h4: {102} # </prompt><userinput>rm -rf /var/spool/lpd/lp /var/spool/lpd/lp2</userinput>
<prompt>h4: {103} # </prompt><userinput>rm -f /tmp/lp /tmp/lp2</userinput></screen>
</informalexample>
</para>

</sect1>

<sect1 id="printingandchecking"><title>Printing a File and Checking Status</title>

<para>Try the following commands.  The commands appear after the prompt,
and sample output that you might see is shown.
<informalexample>
<screen><prompt>h4: {104} % </prompt><userinput>lpr -V /tmp/hi</userinput>
Version LPRng-3.6.14
sending job 'papowell@h4+238' to lp@localhost
connecting to 'localhost', attempt 1
connected to 'localhost'
requesting printer lp@localhost
sending control file 'cfA238h4.private' to lp@localhost
completed sending 'cfA238h4.private' to lp@localhost
sending data file 'dfA238h4.private' to lp@localhost
completed sending 'dfA238h4.private' to lp@localhost
done job 'papowell@h4+238' transfer to lp@localhost</screen>
</informalexample>
</para>

<para>The <command>lpr -V</command> (Verbose) option causes <application/lpr/ to print
status output.
As you can see from the above lines,
it first tries to connect to the <application>lpd</application> server on host
<literal remap=tt>localhost</literal>,
then sends a print request (which is accepted),
then sends a <emphasis>control</emphasis>
file
containing information about the job
and a
<emphasis>data</emphasis>
file or files which are copies of the files to be
printed.</para>

<para>If you check the <filename>/tmp/lp</filename> file
and you will find that a copy of <filename>/tmp/hi</filename>
has been written to it.
By default,
the <application>lpd</application> print spooler acts as a store and forward system,
accepting files to be printed,
holding them in the print queue,
and then forwarding them to the destination system or output device.</para>

<para>You can use the <application>lpq</application> command to view the status of the print job.
<informalexample>
<screen><prompt>h4: {105} % </prompt><userinput>lpq</userinput>
Printer: lp@h4
 Queue: no printable jobs in queue
 Status: job 'papowell@h4+238' removed at 09:39:03.256</screen>
</informalexample>
</para>

<para>If you want to see more status information,
use <command>lpq -l</command>, <command>lpq -ll</command>, or even <command>lpq -L</command>.
The <literal>-L</literal> provides
al<literal remap=tt>L</literal>
the status.
<informalexample>
<screen><prompt>h4: {106} % </prompt><userinput>lpq -l</userinput>
Printer: lp@h4
 Queue: no printable jobs in queue
 Status: lp@h4.private: job 'papowell@h4+238' printed at 09:39:03.112
 Status: job 'papowell@h4+238' removed at 09:39:03.256
<prompt>h4: {107} % </prompt><userinput>lpq -ll</userinput>
Printer: lp@h4
 Queue: no printable jobs in queue
 Status: finished 'papowell@h4+238', status 'JSUCC' at 09:39:03.108
 Status: subserver pid 8240 exit status 'JSUCC' at 09:39:03.110
 Status: lp@h4.private: job 'papowell@h4+238' printed at 09:39:03.112
 Status: job 'papowell@h4+238' removed at 09:39:03.256
<prompt>h4: {108} % </prompt><userinput>lpq -L</userinput>
Printer: lp@h4
 Queue: no printable jobs in queue
 Status: subserver pid 8240 starting at 09:39:03.105
 Status: accounting at start at 09:39:03.105
 Status: opening device '/tmp/lp' at 09:39:03.105
 Status: printing job 'papowell@h4+238' at 09:39:03.106
 Status: no banner at 09:39:03.107
 Status: printing data file 'dfA238h4.private', size 3 at 09:39:03.107
 Status: printing done 'papowell@h4+238' at 09:39:03.107
 Status: accounting at end at 09:39:03.108
 Status: finished 'papowell@h4+238', status 'JSUCC' at 09:39:03.108
 Status: subserver pid 8240 exit status 'JSUCC' at 09:39:03.110
 Status: lp@h4.private: job 'papowell@h4+238' printed at 09:39:03.112
 Status: job 'papowell@h4+238' removed at 09:39:03.256</screen>
</informalexample>
</para>

<para>There are different status formats available as well.
The <command>lpq -s</command> (summary) produces a single line of status per spool queue,
while the <command>lpq -v</command> (verbose)
produces output that is very suitable for processing with
programs such as
<application remap=tt>Perl</application>
or
<application remap=tt>awk</application>:
<informalexample>
<screen><prompt>h4: {109} % </prompt><userinput>lpq -s</userinput>
lp@h4  0 jobs
<prompt>h4: {110} % </prompt><userinput>lpq -v</userinput>
Printer: lp@h4
 Printing: no
 Aborted: no
 Spooling: no
 Queue: no printable jobs in queue
 SPOOLCONTROL=
 Status: subserver pid 8240 starting at 09:39:03.105
 Status: accounting at start at 09:39:03.105
 Status: opening device '/tmp/lp' at 09:39:03.105
 Status: printing job 'papowell@h4+238' at 09:39:03.106
 Status: no banner at 09:39:03.107
 Status: printing data file 'dfA238h4.private', size 3 at 09:39:03.107
 Status: printing done 'papowell@h4+238' at 09:39:03.107
 Status: accounting at end at 09:39:03.108
 Status: finished 'papowell@h4+238', status 'JSUCC' at 09:39:03.108
 Status: subserver pid 8240 exit status 'JSUCC' at 09:39:03.110
 Status: lp@h4.private: job 'papowell@h4+238' printed at 09:39:03.112
 Status: job 'papowell@h4+238' removed at 09:39:03.256</screen>
</informalexample>
</para>

<para>If you check the <filename>/tmp/lp</filename> file
and you will find that a copy of <filename>/tmp/hi</filename>
has been written to it.
By default,
the <application>lpd</application> print spooler acts as a store and forward system,
accepting files to be printed,
holding them in the print queue,
and then forwarding them to the destination system or output device.</para>

</sect1>

<sect1 id="selectingprintqueue"><title>Selecting the Print Queue</title>

<para>In the previous section we used the default print queue.
How does &LPRng; determine what print queue to use?
First,
you can explicitly specify the printer using the  <command>lpq -Pprintqueue</command>
option
and the <command>lpq -a</command> or <command>lpq -Pall</command> to select all print queues:
<informalexample>
<screen><prompt>h4: {111} % </prompt><userinput>lpq -Plp</userinput>
Printer: lp@h4
 Queue: no printable jobs in queue
<prompt>h4: {112} % </prompt><userinput>lpq -Plp2</userinput>
Printer: lp2@h4
 Queue: no printable jobs in queue
<prompt>h4: {113} % </prompt><userinput>lpq -a</userinput>
Printer: lp@h4
 Queue: no printable jobs in queue
Printer: lp2@h4
 Queue: no printable jobs in queue</screen>
</informalexample>
</para>

<para>You can combine the <command>lpq -a</command> with the <command>lpq -s</command> option for a summary listing:
<informalexample>
<screen><prompt>h4: {114} % </prompt><userinput>lpq -a</userinput>
Printer: lp@h4
 Queue: no printable jobs in queue
Printer: lp2@h4
 Queue: no printable jobs in queue
<prompt>h4: {115} % </prompt><userinput>lpq -s -a</userinput>
lp@h4  0 jobs
lp2@h4  0 jobs</screen>
</informalexample>
</para>

<para>There is another way to explicitly specify the printqueues
listed by <command>lpq -a</command>;
see the

<link linkend="allpc"><literal>all</literal> Printcap Entry</link>

for details.</para>

<para>Users can set their default printer by using the
<envar>PRINTER</envar> (highest priority),
<envar>LPDEST</envar> (next),
<envar>NPRINTER</envar> (next),
and
<envar>NGPRINTER</envar> (lowest priority),
environment variables.
For example:
<informalexample>
<screen><prompt>h4: {116} % </prompt><userinput>setenv PRINTER lp2</userinput>
<prompt>h4: {117} % </prompt><userinput>lpq</userinput>
Printer: lp2@h4
 Queue: no printable jobs in queue
<prompt>h4: {118} % </prompt><userinput>unsetenv PRINTER</userinput>
<prompt>h4: {119} % </prompt><userinput>lpq</userinput>
Printer: lp@h4
 Queue: no printable jobs in queue</screen>
</informalexample>
</para>

<para>
If the printer is not specified on the command line or by the environment
variables,
then the first printer in the printcap database will be used
and then the default printer in the configuration database,
and finally the compile time default.

<link linkend="printerinfo">Printer and Server Information</link>

for details.</para>

</sect1>

<sect1 id="controllingprintqueue"><title>Controlling the Print Queue</title>

<para>The <application>lpc</application> command is used to examine and control the
print server operation.
The
<command remap=tt>lpc status</command>
command displays the administrative status of a print queue.
The <application/lpd/ program caches status and job information
in order to improve performance.
The
<command remap=tt>lpc flush</command>
command will flush the cached information
and cause the server to regenerate it.

The
<command remap=tt>lpc enable</command>
and
<command remap=tt>lpc disable</command>
commands
enable or disable spooling to the print queue,
and the

<command remap=tt>lpc stop</command>
and

<command remap=tt>lpc start</command>
commands
stop and start printing (or transfers) of jobs in the print queue.</para>

<para>Let's look at the status displayed when we use these commands:
<informalexample>
<screen><prompt>h4: {120} % </prompt><userinput>lpc status</userinput>
 Printer  Printing Spooling Jobs Server Subserver Redirect Status/(Debug)
lp@h4      enabled  enabled    0   none    none
<prompt>h4: {121} % </prompt><userinput>lpc status all</userinput>
 Printer  Printing Spooling Jobs Server Subserver Redirect Status/(Debug)
lp@h4      enabled  enabled    0    none    none
lp2@h4     enabled  enabled    0    none    none
<prompt>h4: {122} % </prompt><userinput>lpc</userinput>
lpc&gt;status
 Printer  Printing Spooling Jobs Server Subserver Redirect Status/(Debug)
lp@h4      enabled  enabled    0    none    none
lpc&gt;status all
 Printer  Printing Spooling Jobs Server Subserver Redirect Status/(Debug)
lp@h4      enabled  enabled    0    none    none
lp2@h4     enabled  enabled    0    none    none
lpc&gt;quit
</screen>
</informalexample>
</para>

<para>The <application>lpc</application>
command can be used in command line or interactive mode as shown above.
When used with no parameters it will run in interactive mode,
reading one or more commands from its standard input (<acronym/STDIN/).
The
<command remap=tt>lpc status</command>
command shows the administrative status of the select print queue.
The
<literal remap=tt>all</literal>
queue name selects all print queues for display.
As shown in the above example,
both print queues have printing and spooling enabled
and there are no jobs in the print queue.
The
<emphasis>Server</emphasis>
and
<emphasis>Subserver</emphasis>
information shows if there is a process which is printing jobs,
and its helper process that does the actual communication with the printer.</para>

<para>It might be puzzling at first why &LPRng; uses two processes for this operation,
but the reason is very simple.
Many operating system implementations have <emphasis>memory leaks</emphasis>
that cause the actual process size to grow as it runs.
This is especially true if a large number of databases such as the
password,  Domain Name Server, or other system database is consulted
frequently with different queries.
Since this is usually done quite a lot by the process which deals with the
actual printing,
the printing process would soon grow very large and then die when it could
no longer obtain more memory.
The
<emphasis>Server</emphasis>
process will fork or create a child process
<emphasis>Subserver</emphasis>
process that is
responsible for the printing of a single job.
When the job printing has been completed,
the
<emphasis>Subserver</emphasis>
process will exit and the
<emphasis>Server</emphasis>
process will then create another child until there are no
more jobs to be printed.
The
<emphasis>Redirect</emphasis>
and
<emphasis>Debug</emphasis>
fields will be discussed in later sections.</para>

<para>Now let's use the basic spool queue control commands:
<informalexample>
<screen><prompt>h4: {123} % </prompt><userinput>lpc disable</userinput>
Printer: lp@h4
lp@h4.private: disabled
<prompt>h4: {124} % </prompt><userinput>lpq</userinput>
Printer: lp@h4  (spooling disabled)
 Queue: no printable jobs in queue
<prompt>h4: {125} % </prompt><userinput>lpc enable</userinput>
Printer: lp@h4
lp@h4.private: enabled
<prompt>h4: {126} % </prompt><userinput>lpq</userinput>
Printer: lp@h4
 Queue: no printable jobs in queue
<prompt>h4: {127} % </prompt><userinput>lpc stop</userinput>
Printer: lp@h4
lp@h4.private: stopped
<prompt>h4: {128} % </prompt><userinput>lpq</userinput>
Printer: lp@h4  (printing disabled)
 Queue: no printable jobs in queue
<prompt>h4: {129} % </prompt><userinput>lpc start</userinput>
Printer: lp@h4
lp@h4.private: started
<prompt>h4: {130} % </prompt><userinput>lpq</userinput>
Printer: lp@h4
 Queue: no printable jobs in queue</screen>
</informalexample>
</para>

<para>As we can see,
the <application>lpc</application> command also reports on the status of the print queue.
Let's see what happens when we print to a stopped queue:
<informalexample>
<screen><prompt>h4: {131} % </prompt><userinput>lpc stop</userinput>
Printer: lp@h4
lp@h4.private: stopped
<prompt>h4: {132} % </prompt><userinput>lpr /tmp/hi</userinput>
<prompt>h4: {133} % </prompt><userinput>lpr /tmp/hi /tmp/there</userinput>
<prompt>h4: {134} % </prompt><userinput>lpq</userinput>
Printer: lp@h4  (printing disabled)
 Queue: 2 printable jobs
 Server: no server active
 Rank   Owner/ID               Class Job Files            Size Time
1      papowell@h4+17920         A 17920 /tmp/hi             3 18:14:22
2      papowell@h4+17922         A 17922 /tmp/hi,/tmp/there  9 18:14:30
<prompt>h4: {135} % </prompt><userinput>lpc status</userinput>
 Printer  Printing Spooling Jobs  Server Subserver Redirect Status/(Debug)
lp@h4     disabled  enabled    2    none    none</screen>
</informalexample>
</para>

<para>The <application>lpc</application> status shows that we have two jobs spooled.
The
<emphasis>Rank</emphasis>
field shows the order,
the <filename>Owner/ID</filename> shows the unique job ID that is assigned to the
job
and
the
<emphasis>Class</emphasis>
field is the job class (this may be changed with the <command>lpr -C class</command>
option).
The
<emphasis>Job</emphasis>
field shows the <emphasis>job number</emphasis>
assigned to this job in this
particular spool queue.
While the <acronym>ID</acronym> value never changes as a job moves through the &LPRng; system,
the <emphasis>job number</emphasis>
is specific to a particular spool queue and may change
if a job is <emphasis>forwarded</emphasis>
to another spool queue that has a job with the
same job number.
The
<emphasis>Size</emphasis>
field is the total number of printable bytes in the job,
and the
<emphasis>Time</emphasis>
field shows the timestamp associated with the job.</para>

<para>Now let's start the print queue and watch what happens.
<informalexample>
<screen><prompt>h4: {136} % </prompt><userinput>lpc start</userinput>
Printer: lp@h4
lp@h4.private: started
<prompt>h4: {137} % </prompt><userinput>lpq</userinput>
Printer: lp@h4
 Queue: 2 printable jobs
 Server: pid 17928 active
 Unspooler: pid 17929 active
 Status: opening device '/tmp/lp' at 18:14:43.921
 Rank   Owner/ID             Class Job Files            Size Time
active papowell@h4+17920       A 17920 /tmp/hi             3 18:14:22
2      papowell@h4+17922       A 17922 /tmp/hi,/tmp/there  9 18:14:30
<prompt>h4: {138} % </prompt><userinput>lpq -ll</userinput>
Printer: lp@h4
 Queue: 2 printable jobs
 Server: pid 17928 active
 Unspooler: pid 17929 active
 Status: printing job 'papowell@h4+17920' at 18:14:43.921
 Status: no banner at 18:14:43.921
 Status: printing data file 'dfA017920h4.private', size 57 at 18:14:43.922
 Rank   Owner/ID             Class Job Files            Size Time
active papowell@h4+17920       A 17920 /tmp/hi             3 18:14:22
2      papowell@h4+17922       A 17922 /tmp/hi,/tmp/there  9 18:14:30</screen>
</informalexample>
</para>

<para>The
<emphasis>Rank</emphasis>
value of the first job has been changed to
<literal remap=tt>active</literal>
and there is new
<emphasis>Status</emphasis>
information.
If we use <command>lpq -ll</command>  we can see the times that the various print operations
are carried out,
and details of their success or failure.</para>

<para>We can also use the <application>lpc</application> command to see the status of a particular job.
We can select jobs by the user name, the ID, or the job number.
For example:
<informalexample>
<screen><prompt>h4: {139} % </prompt><userinput>lpc stop</userinput>
Printer: lp@h4
lp@h4.private: stopped
<prompt>h4: {140} % </prompt><userinput>echo hi |lpr</userinput>
<prompt>h4: {141} % </prompt><userinput>echo there | lpr</userinput>
<prompt>h4: {142} % </prompt><userinput>echo test |lpr</userinput>
<prompt>h4: {143} % </prompt><userinput>lpq</userinput>
Printer: lp@h4  (printing disabled)
 Queue: 3 printable jobs
 Server: no server active
 Status: job 'papowell@h4+17922' removed at 18:15:13.981
 Rank   Owner/ID            Class Job Files           Size Time
1      papowell@h4+17959      A 17959 (stdin)            3 18:23:24
2      papowell@h4+17962      A 17962 (stdin)            6 18:23:30
3      papowell@h4+17970      A 17970 (stdin)            5 18:23:35
<prompt>h4: {144} % </prompt><userinput>lpq 17970</userinput>
Printer: lp@h4  (printing disabled)
 Queue: 3 printable jobs
 Server: no server active
 Status: job 'papowell@h4+17922' removed at 18:15:13.981
 Rank   Owner/ID            Class Job Files           Size Time
3      papowell@h4+17970      A 17970 (stdin)            5 18:23:35
<prompt>h4: {145} % </prompt><userinput>lpq papowell</userinput>
Printer: lp@h4  (printing disabled)
 Queue: 3 printable jobs
 Server: no server active
 Status: job 'papowell@h4+17922' removed at 18:15:13.981
 Rank   Owner/ID            Class Job Files           Size Time
1      papowell@h4+17959      A 17959 (stdin)            3 18:23:24
2      papowell@h4+17962      A 17962 (stdin)            6 18:23:30
3      papowell@h4+17970      A 17970 (stdin)            5 18:23:35
<prompt>h4: {146} % </prompt><userinput>lpq -s 17970</userinput>
lp@h4  1 jobs
<prompt>h4: {147} % </prompt><userinput>lpq -s papowell</userinput>
lp@h4  3 jobs
<prompt>h4: {148} % </prompt><userinput>lpq -s nobody</userinput>
lp@h4  0 jobs</screen>
</informalexample>
</para>

<para>We use <command>lpq -Pqueuename</command> to select a specific
print queue
and <command>lpq -a</command> or <command>lpq -Pall</command> to select all queues:
<informalexample>
<screen><prompt>h4: {149} % </prompt><userinput>lpc -a stop</userinput>
Printer: lp@h4
lp@h4.private: stopped
Printer: lp2@h4
lp2@h4.private: stopped
<prompt>h4: {150} % </prompt><userinput>lpc -Pall start</userinput>
Printer: lp@h4
lp@h4.private: started
Printer: lp2@h4
lp2@h4.private: started</screen>
</informalexample>
</para>

<para>You can use the <application>lpc</application> command in <emphasis>interactive</emphasis>
mode:
<informalexample>
<screen><prompt>h4: {151} % </prompt><userinput>lpc</userinput>
lpc&gt;status
 Printer  Printing Spooling Jobs  Server Subserver Redirect Status/(Debug)
lp@h4      enabled  enabled    3    17990   17993
lpc&gt;status all
 Printer  Printing Spooling Jobs  Server Subserver Redirect Status/(Debug)
lp@h4      enabled  enabled    3    17990   17993
lp2@h4     enabled  enabled    3    none    none
lpc&gt;stop lp
Printer: lp@h4
lp@h4.private: stopped
lpc&gt;start lp
Printer: lp@h4
lp@h4.private: started
lpc&gt;quit</screen>
</informalexample>
</para>

<para> The

<command remap=tt>lpc topq</command>

command can be used to put a job (or jobs) at the head of the spool queue.
This command is very useful when some job requires priority service.
You can select the job by using the job number or the job ID.
<informalexample>
<screen><prompt>h4: {152} % </prompt><userinput>lpc topq lp 17970</userinput>
Printer: lp@h4
lp: selected 'papowell@h4+17970'
lp@h4.private: started
<prompt>h4: {153} % </prompt><userinput>lpq</userinput>
Printer: lp@h4
 Queue: 3 printable jobs
 Server: pid 17999 active
 Rank   Owner/ID          Class Job Files      Size Time
active papowell@h4+17970    A 17970 (stdin)      5 18:23:35
1      papowell@h4+17959    A 17959 (stdin)      3 18:23:24
2      papowell@h4+17962    A 17962 (stdin)      6 18:23:30</screen>
</informalexample>
</para>

</sect1>

<sect1 id="jobremoval"><title>Job Removal</title>

<para>Occasionally we print a file
and then change our mind and want to cancel the job.
The <application>lprm</application> command allows us to do this.
<informalexample>
<screen><prompt>h4: {154} % </prompt><userinput>lpq</userinput>
Printer: lp@h4  (printing disabled)
 Queue: 3 printable jobs
 Server: no server active
 Status: job 'papowell@h4+17922' removed at 18:15:13.981
 Rank   Owner/ID          Class Job Files      Size Time
1      papowell@h4+17959    A 17959 (stdin)       3 18:23:24
2      papowell@h4+17962    A 17962 (stdin)       6 18:23:30
3      papowell@h4+17970    A 17970 (stdin)       5 18:23:35
<prompt>h4: {155} % </prompt><userinput>lprm</userinput>
Printer lp@h4:
  checking perms 'papowell@h4+17959'
  dequeued 'papowell@h4+17959'
<prompt>h4: {156} % </prompt><userinput>lpq</userinput>
Printer: lp@h4  (printing disabled)
 Queue: 2 printable jobs
 Server: no server active
 Status: job 'papowell@h4+17922' removed at 18:15:13.981
 Rank   Owner/ID          Class Job Files      Size Time
1      papowell@h4+17962    A 17962 (stdin)       6 18:23:30
2      papowell@h4+17970    A 17970 (stdin)       5 18:23:35
<prompt>h4: {157} % </prompt><userinput>lprm 17970</userinput>
Printer lp@h4:
  checking perms 'papowell@h4+17970'
  dequeued 'papowell@h4+17970'
<prompt>h4: {158} % </prompt><userinput>lpq</userinput>
Printer: lp@h4  (printing disabled)
 Queue: 1 printable job
 Server: no server active
 Status: job 'papowell@h4+17922' removed at 18:15:13.981
 Rank   Owner/ID          Class Job Files     Size Time
1      papowell@h4+17962    A 17962 (stdin)      6 18:23:30</screen>
</informalexample>
</para>

<para>By default,
the <application>lprm</application>
command removes the first job in the queue that the user has permission to
remove.
Also,
as shown in the example,
you can remove a job by specifying the job ID or the job number.
If you specify a user name,
you remove <literal remap=bf>all</literal>
of the user's jobs.
This can be dangerous:
<informalexample>
<screen><prompt>h4: {159} % </prompt><userinput>lpq</userinput>
Printer: lp@h4  (printing disabled)
 Queue: 3 printable jobs
 Server: no server active
 Status: job 'papowell@h4+17922' removed at 18:15:13.981
 Rank   Owner/ID          Class Job Files     Size Time
1      papowell@h4+17962    A 17962 (stdin)      6 18:23:30
2      papowell@h4+18499    A 18499 /tmp/hi      3 18:56:00
3      papowell@h4+18501    A 18501 /tmp/there   6 18:56:02
<prompt>h4: {160} % </prompt><userinput>lprm papowell</userinput>
Printer lp@h4:
  checking perms 'papowell@h4+17962'
  dequeued 'papowell@h4+17962'
  checking perms 'papowell@h4+18499'
  dequeued 'papowell@h4+18499'
  checking perms 'papowell@h4+18501'
  dequeued 'papowell@h4+18501'
<prompt>h4: {161} % </prompt><userinput>lpq</userinput>
Printer: lp@h4  (printing disabled)
 Queue: no printable jobs in queue
 Status: job 'papowell@h4+17922' removed at 18:15:13.981</screen>
</informalexample>
</para>

<para>The special user
<literal remap=tt>all</literal>
matches all jobs in a print queue.
Clearly you should be careful not to specify
<command remap=tt>lprm all</command>
by accident.
Even more dangerous is the following command:
<informalexample>
<screen><prompt>h4: {162} % </prompt><userinput>lprm -a all</userinput></screen>
</informalexample>
</para>

<para>As you might surmise,
this removes <literal remap=bf>all</literal>
print jobs in <literal remap=bf>all</literal>
queues,
which is an excellent way to purge print queues of all jobs.</para>

</sect1>

<sect1 id="printjobfilters"><title>Print Job Filters</title>

<para>A printer usually understands one or more
<emphasis>Print Job Languages</emphasis>.
Files sent to this printer must be in one of these languages
and have the appropriate
<emphasis>job format</emphasis>.
The most common Print Job Languages are

<link linkend="postscript">PostScript</link>

and

<link linkend="pcl">PCL</link>.

Text files are PCL with no special PCL control sequences.
</para>

<para>In order for a printer to reliably print a job
it needs to be reset to a known configuration and then
at the end of job having it flush all of the output to the
printing device.
This is done by sending it
<emphasis>start of job</emphasis>
and
<emphasis>end of job</emphasis>
commands.
These commands differ from printer to printer
and depend on the print job language as well.
Some <emphasis>vintage</emphasis>
line printers also have a set of
proprietary <emphasis>escape sequences</emphasis>
that are used to set up margins,
form size,
and other printing characteristics.
Usually a
<emphasis>setup string</emphasis>
with these escape sequences must be sent to the printer before
a file can be printed.</para>

<para>
When sending a job to the printer the print spooler will first
process the job using a <emphasis/print job filter/ or
<emphasis/filter/ program.
This program reads the job file and then produces output
in the format required for the printer.
</para>

<para>
When a print job is created the files in the print job are assigned
a <emphasis/format/.
This format was meant as a guide to the print spooler and was to be
used to select the filter program for the files in the job.
The format was a lower case letter;
the <literal remap=tt>f</literal>
is the de<literal remap=tt>f</literal>ault format
and indicates normal processing
and the
<literal remap=tt>l</literal>
format indicates a
literal or binary file.
Job files that are flagged as having
literal or binary format
are usually passed directly to the printer
or have at the most a minimal amount of processing.
See

<link linkend="printjobformats">Print Job Formats</link>

for more information about formats and their use with filters.</para>

<para>
There are two ways to specify filters:  the default <literal/:filter=.../
option and the more specific <literal/Xf=.../ option.
The <literal/X/ is a lower case letter corresponding to a format.
Here is a sample printcap entry with a filter specification:
<informalexample>
<screen>lp:sd=/var/spool/lpd/%P
  :filter=/usr/local/lib/filters/ifhp
  :rf=/usr/local/lib/filters/rfilter
</screen>
</informalexample>
All jobs with formats other than <literal/r/ will be processed using the
<literal/ifhp/ program
while the jobs with the <literal/r/ format will be processed using the
<literal/rfilter/ program.
</para>

<para>We will set up a very simple filter and use it to demonstrate
how filtering is done by the <application/lpd/ print spooler.
First,
set up the <filename>/tmp/testf</filename> file as shown below.
<informalexample>
<screen>#!/bin/sh
# /tmp/testf - test filter for LPRng
PATH=/bin:/usr/bin; export PATH
echo TESTF $0 "$@" &gt;&amp;2
echo TESTF $0 "$@"
echo ENV
set
echo LEADER
/bin/cat
echo TRAILER
exit 0</screen>
</informalexample>
</para>

<para>Let us carefully examine the script line by line.
The first couple of lines are <emphasis/boilerplate/.
You should
<emphasis/always/
set the <envar/PATH/
value in a filter script or use full pathnames for executable programs.
This is a good practice
as it ensures that only the specified directories will be
searched for commands.</para>

<para>The next lines echo the command line
arguments to file descriptor 2 (<acronym/STDERR/) and to <acronym/STDOUT/.
We will soon see how this information is displayed by the &LPRng; software.
We then use the
<command remap=tt>set</command>
command to list the shell variables to
<acronym/STDOUT/,
print <acronym>LEADER</acronym> to <acronym/STDOUT/,
copy <acronym/STDIN/ to <acronym/STDOUT/,
and print <acronym>TRAILER</acronym> to <acronym/STDOUT/.
We exit with a zero result code.</para>

<para>We can test our script, with the results shown below:
<informalexample>
<screen><prompt>h4: {163} % </prompt><userinput>chmod 755 /tmp/testf</userinput>
<prompt>h4: {164} % </prompt><userinput>echo hi |/tmp/testf -a1</userinput>
TESTF /tmp/testf -a1
TESTF /tmp/testf -a1
ENV
USER=papowell
HOSTNAME=h4
...
PATH=/bin:/usr/bin
LEADER
hi
TRAILER</screen>
</informalexample>
</para>

<para>Let's now use this filter.
Edit the
<literal remap=tt>lp</literal>
printcap entry so it has contents indicated below,
use <command>checkpc -f</command> to check the printcap,
and then use
<command remap=tt>lpc reread</command>
to restart the <application>lpd</application> server.
<informalexample>
<screen>lp:sd=/var/spool/lpd/%P
  :force_localhost
  :lp=/tmp/lp
  :filter=/tmp/testf</screen>
</informalexample>
</para>

<para>Execute the following commands
to print the <filename>/tmp/hi</filename> file
and observe the results:
<informalexample>
<screen><prompt>h4: {165} % </prompt><userinput>cp /dev/null /tmp/lp</userinput>
<prompt>h4: {166} % </prompt><userinput>lpr /tmp/hi</userinput>
<prompt>h4: {167} % </prompt><userinput>lpq -llll</userinput>
Printer: lp@h4
 Queue: no printable jobs in queue
 Status: lp@h4.private: job 'papowell@h4+26593' printed at 21:37:21.312
 Status: job 'papowell@h4+26593' removed at 21:37:21.323
 Status: subserver pid 26683 starting at 21:39:21.908
 Status: accounting at start at 21:39:21.908
 Status: opening device '/tmp/lp' at 21:39:21.909
 Status: printing job 'papowell@h4+26681' at 21:39:21.909
 Status: no banner at 21:39:21.909
 Status: printing data file 'dfA026681h4.private', size 3, \
    IF filter 'testf' at 21:39:21.909
 Status: IF filter msg - 'TESTF /tmp/testf -Apapowell@h4+26681 \
     -CA -D2000-04 -11-21:39:21.877 -Ff -Hh4.private -J/tmp/hi \
      -Lpapowell -Plp -Qlp -aacct -b3 -d/var/tmp/LPD/lp \
      -edfA026681h4.private -f/tmp/hi -hh4.private -j026681 \
      -kcfA026681h4.private -l66 -npapowell -sstatus \
      -t2000-04-11-21:39:21.000 -w80 -x0 -y0 acct' \
       at 21:39:21.914
 Status: IF filter finished at 21:39:22.070
 Status: printing done 'papowell@h4+26681' at 21:39:22.070
 Status: accounting at end at 21:39:22.070
 Status: finished 'papowell@h4+26681', status 'JSUCC' at 21:39:22.070
 Status: subserver pid 26683 exit status 'JSUCC' at 21:39:22.072
 Status: lp@h4.private: job 'papowell@h4+26681' printed at 21:39:22.072
 Status: job 'papowell@h4+26681' removed at 21:39:22.085
<prompt>h4: {168} % </prompt><userinput>more /tmp/lp</userinput>
TESTF /tmp/testf -Apapowell@h4+26681 -CA -D2000-04-11-21:39:21.877 \
 -Ff -Hh4.private -J/tmp/hi -Lpapowell -Plp -Qlp -aacct -b3 \
 -d/var/tmp/LPD/lp -edfA026681h4.private -f/tmp/hi -hh4.private \
 -j026681 -kcfA026681h4.private -l66 -npapowell -sstatus \
 -t2000-04-11-21:39:21.000 -w80 -x0 -y0 acct
ENV
USER=papowell
LD_LIBRARY_PATH=/lib:/usr/lib:/usr/5lib:/usr/ucblib
HOME=/home/papowell
PRINTCAP_ENTRY=lp
 :force_localhost
 :filter=/tmp/testf
 :lp=/var/tmp/lp
 :sd=/var/tmp/LPD/lp

PS1=$
OPTIND=1
PS2=&gt;
SPOOL_DIR=/var/tmp/LPD/lp
LOGNAME=papowell

CONTROL=Hh4.private
 Ppapowell
 J/tmp/hi
 CA
 Lpapowell
 Apapowell@h4+15850
 D2000-04-26-18:13:55.505
 Qlp
 N/tmp/hi
 fdfA015850h4.private
 UdfA015850h4.private

PATH=/bin:/usr/bin
SHELL=/bin/sh
LOGDIR=/home/papowell
IFS=
PRINTER=lp
LEADER
test Test
TRAILER</screen>
</informalexample>
</para>

<para>The
<command remap=tt>cp</command>
command clears out the <filename>/tmp/lp</filename>
file we are using as a dummy output device.
The <command>lpr</command> command prints the <filename>/tmp/hi</filename> file
and the <command>lpq -llll</command> command shows the status information.
The status information now contains
the line that the
<command remap=tt>testf</command>
script wrote to <acronym/STDERR/.
The <application>lpd</application> server captures filter <acronym/STDERR/
messages and puts it them in the spool queue status file.</para>

<para>As we see from the lpq status,
<application>lpd</application>
passes a large number of command line options to our filter.
These options and their meanings are discussed in detail in

<link linkend="filteroptions">Filter Command Line Options and Environment Variables</link>.
We will discuss these in more detail in the next section.</para>

<para>If we look at the <filename>/tmp/lp</filename> file,
we see the command line options and
values of the shell variables.
For a full discussion of the environment variables passed to a
filter see
<link linkend="filteroptions">Filter Command Line Options and Environment Variables</link>.
The more interesting environment variables include
<envar>PRINTCAP_ENTRY</envar> - the printcap entry for this printer,
<envar>CONTROL</envar> - the control file,
and <envar>HF</envar> - the hold file.
</para>

<sect2 id="controlfiles"><title>Control Files and Filter Options</title>

<para>
When you transfer a print job the <application/lpd/ print spooler
will send the job as two or more files: 
<emphasis>control</emphasis>
file that contains information about
the job and <emphasis>data</emphasis>
files that contain the information to be printed.
Here is sample control file:
<informalexample>
<screen>Hh4.private
Ppapowell
J/tmp/hi
CA
Lpapowell
Apapowell@h4+15850
D2000-04-26-18:13:55.505
Qlp
N/tmp/hi
fdfA015850h4.private
UdfA015850h4.private</screen>
</informalexample>
</para>

<para>Lines starting with upper case letters contain
job information such as the user who submitted the job.
Lines starting with lower case letters indicate the data file
to be printed and the corresponding format.
For full details about the exact format of the control file
see
<link linkend="jobfiles">Job Files</link>.
</para>
<para><xref linkend=filteroptionstable> shows the correspondence
between lines in the control file and <application/lpr/ command line options.
The <literal/N/ values are the names of the files that are printed.
The <literal remap=tt>U</literal> indicates a data file
is in a job and is present to meet RCF1179 and
<emphasis>vintage</emphasis>
print spooler requirements.</para>

<table frame=all id=filteroptionstable><title>Filter Options</title>
<tgroup cols=3 colsep=1 rowsep=1 align=left>
<thead>
<row><entry>Control File </entry><entry> Filter Option </entry><entry> Purpose or Value </entry></row>
</thead>
<tbody>
<row><entry></entry><entry> <literal>-P</literal><emphasis>Printer</emphasis>
</entry><entry> Print queue name - printcap information </entry></row>
<row><entry><emphasis>H</emphasis>
</entry><entry> <literal>-H</literal><emphasis>Host</emphasis>
</entry><entry> Host Name </entry></row>
<row><entry><literal remap=tt>P</literal>
</entry><entry> <literal>-n</literal><emphasis>User</emphasis>
</entry><entry> User Login Name of job originator </entry></row>
<row><entry><literal remap=tt>J</literal>
</entry><entry> <literal>-J</literal><emphasis>Job name</emphasis>
</entry><entry> lpr -J option or file name </entry></row>
<row><entry><literal remap=tt>C</literal>
</entry><entry> <literal>-C</literal><emphasis>Class</emphasis>
</entry><entry> Print class (lpr -C option) </entry></row>
<row><entry><literal remap=tt>L</literal>
</entry><entry> <literal>-L</literal><emphasis>Banner</emphasis>
</entry><entry> Banner page request</entry></row>
<row><entry><literal remap=tt>A</literal>
</entry><entry> <literal>-A</literal><emphasis>Jobid</emphasis>
</entry><entry> Job Id </entry></row>
<row><entry><literal remap=tt>D</literal>
</entry><entry> <literal>-D</literal><emphasis>Date</emphasis>
</entry><entry> Date or time information </entry></row>
<row><entry><literal remap=tt>Q</literal>
</entry><entry> <literal>-Q</literal><emphasis>Queue</emphasis>
</entry><entry> Original Print queue job was sent to </entry></row>
<row><entry><literal remap=tt>N</literal>
</entry><entry> <literal>-N</literal><emphasis>Filename</emphasis>
</entry><entry> Filename </entry></row>
<row><entry><literal remap=tt>f,l,p,...</literal>
</entry><entry> <literal>-F</literal><emphasis>f</emphasis>
</entry><entry>Datafile format </entry></row>
<row><entry><literal remap=tt>U</literal>
</entry><entry>
</entry><entry>Datafile (historical)</entry></row>
</tbody>
</tgroup>
</table>

<para>
When a print filter processes these jobs the values in the control
file are passed on the command line as options starting with
upper case letters:
<informalexample>
<screen>/tmp/testf -Apapowell@h4+26681 -CA \
   -D2000-04-11-21:39:21.877 -Ff -Hh4.private ....
</screen>
</informalexample>

Sometimes we want to pass only a small subset of these command line options
to a filter or provide them in a specific order in order to be compatible
with
<emphasis>legacy</emphasis>
print filters.
&LPRng; provides several different ways to do this
and we will explore how to control command line options.</para>

<para>If the filter entry starts with <literal>-$</literal>,
this suppresses the automatic addition of command line options;
we can then add our own options to the command line.
Modify the printcap entry to have the following form:
<informalexample>
<screen>lp:sd=/var/spool/lpd/%P
  :force_localhost
  :lp=/tmp/lp
  :filter= -$ /tmp/testf '$P' $0P -X$-P ${lp} G\072 or \:</screen>
</informalexample>
</para>

<para>Lets print our <filename>/tmp/hi</filename> test file and then look at
the <application>lpq</application> status:
<informalexample>
<screen><prompt>h4: {169} % </prompt><userinput>cp /dev/null /tmp/lp</userinput>
<prompt>h4: {170} % </prompt><userinput>lpr /tmp/hi</userinput>
<prompt>h4: {171} % </prompt><userinput>lpq -llll</userinput>
Printer: lp@h4
 ....
 Status: IF filter msg - 'TESTF /tmp/testf -Plp -P lp -Xlp \
    -Ylp /tmp/lp G: or :' at 01:20:21.560</screen>
</informalexample>
</para>

<para>The <literal>-$</literal> suppresses the adding the default literals to the
filter command line.
You can pass specific options using
<literal remap=tt>$X</literal>;
if the option has a non-null value then it will be expanded in the following
format:
<informalexample>
<screen>Option    Value Expansion
$X        -X&lt;value&gt;
$'X       -X'&lt;value&gt;'
$0X       -X '&lt;value&gt;'
$-X       &lt;value&gt;
$'-X      '&lt;value&gt;'
${X}      control file X option value
$'{X}     control file X option value (in quotes)
${name}   printcap option value if value nonzero length
$'{name}  printcap option value if value nonzero length in quotes
\nnn      single printable character
$*        all options in control file expanded using $X</screen>
</informalexample>
</para>

<para>Command line options can be grouped and passed as a single argument
by enclosing them in single or double quotes.
You should be aware that &LPRng; has an
<emphasis>extremely</emphasis>
primitive
way of handling quotes.
When the
<command remap=tt>/bin/sh -c</command>
parameter is not used,
the the command line is broken on spaces and each unit
is passed as an individual argument.
If the first character after a space is a quote (single or double),
the next quote is found,
and then entire element is then used as a single parameter.
Substitution of
<literal remap=tt>$X</literal>
parameters is then done.
As a special case,
when you have a
<literal remap=tt>$0X</literal>,
this causes a split and all of the string previous and including the <literal>-X</literal>
flag is passed as a single option and all of the option value and following
are passed as another option.
If the result of the expansion is a zero length parameter then it is
removed from the parameter list.
When the
<command remap=tt>/bin/sh -c</command>
is used
the command line is not broken,
and all non-empty option values are enclosed in single quotes.</para>

<para>The
<literal remap=tt>${name}</literal>
option is used to pass a printcap option value.
For example,
you can pass the value of the printcap option
<literal remap=tt>form</literal>
as shown
below.
You can experiment with this by using the <filename>/tmp/testf</filename>
filter and printcap shown below.
<informalexample>
<screen>printcap:
lp:sd=/var/spool/lpd/%P
  :force_localhost
  :lp=/tmp/lp
  :filter=/tmp/testf -F ${form}
  :form=payroll

<prompt>h4: {172} % </prompt><userinput>cp /dev/null /tmp/lp</userinput>
<prompt>h4: {173} % </prompt><userinput>lpr /tmp/hi</userinput>
<prompt>h4: {174} % </prompt><userinput>lpq -llll</userinput>
Printer: lp@h4
 ...
 Status: IF filter msg - 'TESTF /tmp/testf -F payroll' at 09:55:31.276
 ...</screen>
</informalexample>
</para>

<para>If we have a
<emphasis>legacy</emphasis>
print filter that was originally written
for the <acronym>BSD</acronym> print spooler,
then we may find that it requires a small number of command line options in
a very specific order.
We can use the
<literal remap=tt>:bkf</literal>
(BSD Kompatible Filter or BacKwards compatible Filter)
flag to pass suitable options.
Modify the printcap entry to have the following form:
<informalexample>
<screen>lp:sd=/var/spool/lpd/%P
  :force_localhost
  :lp=/tmp/lp
  :filter=/tmp/testf
  :bk</screen>
</informalexample>
</para>

<para>Lets print our <filename>/tmp/hi</filename> test file and then look at
the <application>lpq</application> status:
<informalexample>
<screen><prompt>h4: {175} % </prompt><userinput>cp /dev/null /tmp/lp</userinput>
<prompt>h4: {176} % </prompt><userinput>lpr /tmp/hi</userinput>
<prompt>h4: {177} % </prompt><userinput>lpq -llll</userinput>
Printer: lp@h4
 ....
 Status: IF filter msg - 'TESTF /tmp/testf -Plp -w80 -l66 \
    -x0 -y0 -Ff -Lpapowell -J/tmp/hi -CA -n papowell \
    -h h4.private acct' at 08:07:46.583</screen>
</informalexample>
</para>

<para>Finally,
there are times when we would like the print filter to be
a simple shell command or to chain several programs together
in a simple pipeline.
While this is possible using a print filter,
you can also do this in the filter specification.
If your filter specification starts with a
parenthesis (<literal remap=tt>(</literal>)
or contains the IO redirection for
pipeto (<literal remap=tt>|</literal>),
input redirection (<literal remap=tt>&lt;</literal>),
or
output redirection (<literal>&gt;</literal>)
then the <application>lpd</application> server will use the
<literal remap=tt>:shell</literal>
configuration option value (default <filename>/bin/sh</filename>)
and execute it using:
<informalexample>
<screen>${shell} -c "( ${if} )"</screen>
</informalexample>
</para>
<para>If this is done,
then no command line options are added to the command.
However,
expansion of
<literal remap=tt>$X</literal>
parameters are still done.
Modify the printcap entry to have the following form:
<informalexample>
<screen>lp:sd=/var/spool/lpd/%P
  :force_localhost
  :lp=/tmp/lp
  :filter=(echo "PREAMBLE"; /tmp/testf; echo "APPENDIX")</screen>
</informalexample>
</para>

<para>Lets print our <filename>/tmp/hi</filename> test file and then look at
the <application>lpq</application> status:
<informalexample>
<screen><prompt>h4: {178} % </prompt><userinput>cp /dev/null /tmp/lp</userinput>
<prompt>h4: {179} % </prompt><userinput>lpr /tmp/hi</userinput>
<prompt>h4: {180} % </prompt><userinput>lpq -llll</userinput>
Printer: lp@h4
 ....
 Status: printing data file 'dfA018881h4.private', size 3, \
   IF filter 'echo' at 09:22:11.476
 Status: IF filter msg - 'TESTF /tmp/testf' at 09:22:11.510
 Status: IF filter finished at 09:22:11.514</screen>
</informalexample>
</para>

<para>If we examine the <filename>/tmp/lp</filename> file we find:
<informalexample>
<screen>PREAMBLE
TESTF /tmp/testf
ENV
USER=papowell
LD_LIBRARY_PATH=/lib:/usr/lib:/usr/5lib:/usr/ucblib
...
PRINTER=lp
LEADER
hi
TRAILER
APPENDIX</screen>
</informalexample>
</para>

<para>As we expected, no options were passed on the command line.
If the printcap is modified to have the following contents,
then you will see:
<informalexample>
<screen>lp:sd=/var/spool/lpd/%P
  :force_localhost
  :lp=/tmp/lp
  :filter=(echo "PREAMBLE"; /tmp/testf $*; echo "APPENDIX")

<prompt>h4: {181} % </prompt><userinput>lpr /tmp/hi</userinput>
<prompt>h4: {182} % </prompt><userinput>lpq -llll</userinput>
Printer: lp@h4
 ....
 Status: IF filter msg - 'TESTF /tmp/testf -Apapowell@h4+18941 \
   -CA -D2000-04-29-09:27:30.700 -Ff -Hh4.private -J/tmp/hi \
   -Lpapowell -Plp -Qlp -aacct -b3 -d/var/tmp/LPD/lp \
   -edfA018941h4.private -f/tmp/hi -hh4.private -j018941 \
   -kcfA018941h4.private -l66 -npapowell -sstatus \
   -t2000-04-29-09:27:30.864 -w80 -x0 -y0 acct' at 09:27:30.879</screen>
</informalexample>
</para>

<para>Using the shell invocation is especially useful when you may have a parameter
that has an empty string value,
and need to pass this as a command line parameter.
Modify the <filename>/tmp/testf</filename> filter,
the printcap, and execute the following commands:
<informalexample>
<screen>printcap:
  lp:sd=/var/spool/lpd/%P
    :force_localhost
    :lp=/tmp/lp
    :filter=( /tmp/testf -F '${form}' )
    :form=

#!/bin/sh
# /tmp/testf - test filter for LPRng
PATH=/bin:/usr/bin; export PATH
echo TESTF $0 "$@" &gt;&amp;2
echo TESTF $0 "$@"
while test $# -gt 0 ; do
  echo "PARM '$1'";
  shift;
done
echo LEADER
/bin/cat
echo TRAILER
exit 0

<prompt>h4: {183} % </prompt><userinput>cp /dev/null /tmp/lp</userinput>
<prompt>h4: {184} % </prompt><userinput>lpr /tmp/hi</userinput>
<prompt>h4: {185} % </prompt><userinput>lpq -llll</userinput>
Printer: lp@h4
 ...
 Status: IF filter msg - 'TESTF /tmp/testf -F' at 09:59:27.365

<prompt>h4: {186} % </prompt><userinput>more /tmp/lp</userinput>
TESTF /tmp/testf -F
PARM '-F'
PARM ''
LEADER
hi
TRAILER</screen>
</informalexample>
</para>

<para>As you can see,
there are <emphasis/empty/ parameters passed to the filter.
This is due to the combination of the
<literal remap=tt>$'{form}</literal>
and using the <literal>:filter=(...)</literal>
form.</para>

</sect2>

<sect2 id="filterenvironmentvars"><title>Filter Environment Variables</title>

<para>In this section we will look further at the environment variables
passed to the filter.
We printed the shell variable values for the filter at the start
of the file:
<informalexample>
<screen><prompt>h4: {187} % </prompt><userinput>cat /tmp/lp</userinput>
/tmp/testf -Plp -P lp -Xlp -Ylp /tmp/lp G:' at 01:20:21.560
ENV
CONTROL=Hh4.private
 Ppapowell
 J/tmp/hi
 CA
 Lpapowell
 Apapowell@h4+105
 D2000-04-12-15:27:26.662
 Qlp
 N/tmp/hi
 fdfA105h4.private
 UdfA105h4.private
HF=H=h4.private
 P=papowell
 J=/tmp/hi
 C=A
 L=papowell
 A=papowell@h4+105
 D=2000-04-12-15:27:26.662
 Q=lp
 transfername=cfA105h4.private
 datafiles=N=/tmp/hi,transfername=fdfA105h4.private,
HOME=/home/daemon
LD_LIBRARY_PATH=/lib:/usr/lib:/usr/5lib:/usr/ucblib
LOGDIR=/home/daemon
LOGNAME=daemon
OPTIND=1
PATH=/bin:/usr/bin:/usr/local/bin
PRINTCAP_ENTRY=lp
 :force_localhost
 :filter=/tmp/testf
 :lp=/tmp/lp
 :sd=/tmp/LPD/lp
PRINTER=lp
PS1=$
PS2=&gt;
SHELL=/bin/sh
SPOOL_DIR=/tmp/LPD/lp
USER=daemon

LEADER
hi
TRAILER</screen>
</informalexample>
</para>

<para>The <envar>HOME</envar>,
<envar>USER</envar>,
<envar>SHELL</envar>,
<envar remap=tt>PS1</envar>,
and
<envar remap=tt>PS2</envar>
variables are usually set by the shell,
and are reflect the information for the <acronym>UID</acronym> of the user
running the shell.</para>

<para>The
<envar>PATH</envar>
and
<literal>LP_LIBRARY_PATH</literal>
are set by the <application>lpd</application> server to values specified in the
printcap or configuration information.
It is recommended that users set these to site specific values
if the defaults are not suitable for their sites.</para>

<para>The <application>lpd</application> server sets the
<literal/PRINTER/,
<literal/PRINTCAP_ENTRY/,
<literal/CONTROL/
and
<literal/HF/
environment variables to the printer name,
printcap entry,
control file,
and hold file for the print job.
This information is very useful to filters that must
make decisions based on values passed to the print server
in the control file
and which use parameters in the printcap entry
to control their actions.</para>

</sect2>

<sect2 id="usingcommandlineandprintcap"><title>Using Command Line and Printcap Options In Filters</title>

<para>One of the problems commonly encountered problem
in writing a filter is getting the command line values.
The UNIX POSIX Standard provides a C Language
<function remap=tt>getopt</function>
function that can be used for command line options,
and some, but not all shell implementations have a corresponding
shell
<function remap=tt>getopt</function>
function.
Also,
many times it would be useful to get the values of the printcap
options.
These could be used to specify options or operations that are
not easily done by passing command lines.
<itemizedlist>

<listitem>
<para>Observe that all the command line options are single letters.
If we set the shell variables to the corresponding option value,
then we could access them by using
<literal remap=tt>$x</literal>, where
<literal remap=tt>x</literal>
is the option letter.
There is an exception to this rule,
which is the <literal>-c</literal> command line literal,
which for various historical and compatibility reasons does not take a value.
But if it is present,
we might as well assign it the value
<literal remap=tt>1</literal>.</para>
</listitem>

<listitem>
<para>Observe that by convention all printcap options have
lowercase names of two or more letters,
and that all environment variables have all upper case letters.
If we set shell variables with the corresponding printcap entry values,
then we can access them using
<literal remap=tt>$literal</literal>.
If we need to create a local shell variable for use,
we can use <literal>mIxEd</literal>
case and not have a conflict.</para>
</listitem>

</itemizedlist>
</para>

<para>The <literal>decode_args_with_sh</literal>
script which is in the <acronym>UTILS</acronym> directory of the &LPRng; distribution
follows these conventions and sets the appropriate shell variables.
We have also include a bit of code that will extract the
control file control line values and put them into variables
as well.</para>

<para>Save the current <filename>/tmp/testf</filename> filter file in
<filename>/tmp/testf.old</filename>
and replace it with the following:
<informalexample>
<screen>#!/bin/sh
# this is an example of how to use /bin/sh and LPRng
# to get the command line and printcap option values
# and set shell variables from them
#  Note that we use a couple of variables
#PATH=/bin:/usr/bin
Args=""
vAr=""
vAlue=""
vAls=""
iI=""
Tf=""
Debug=1
if -n $Debug ; then
    set &gt;/tmp/before
fi
Args="$@"
if -n $Debug ; then
    echo "$@" &gt;&gt;/tmp/before
fi
while expr "$1" : '-.*' &gt;/dev/null ; do
  vAr=`expr "$1" : '-\(.\).*'`;
  vAlue=`expr "$1" : '-.\(.*\)`;
  case "$vAr" in
    - ) break;;
    c ) c=1;;
    [a-zA-Z] )
      if test "X$vAlue" = "X" ; then shift; vAlue=$1; fi;
      eval $vAr='$vAlue';
      #setvar $vAr "$vAlue"
      ;;
  esac;
  shift;
done

# set shell variables to the printcap options
#  flag   -&gt;  flag=1
#  flag@  -&gt;  flag=0
#  option=value -&gt;  option='value'
#
setpcvals () {
    while test "$#" -gt 0 ; do
      iI=$1
      if expr "$iI" : " *\:" &gt;/dev/null ; then
        vAr=`expr "$iI" : " *\:\([^=][^=]*\)=.*"`;
        vAlue=`expr "$iI" : " *\:[^=][^=]*=\(.*\)"`;
        if test "X$vAr" = "X" ; then
          vAr=`expr "$iI" : " *:\(.*\)@"`;
          vAlue=0;
        fi
        if test "X$vAr" = "X" ; then
          vAr=`expr "$iI" : " *:\(.*\)"`;
          vAlue=1;
        fi
        if test "X$vAr" != "X" ; then
          eval $vAr='$vAlue';
          #setvar $vAr "$vAlue"
        fi
      else
        vAr=`expr "$iI" : " *\([^|][^|]*\).*"`;
        if test "X$vAr" != "X" ; then
          eval Printer="$vAr"
        fi
      fi;
      shift
    done
}

# set shell variables to the printcap options
#  flag   -&gt;  flag=1
#  flag@  -&gt;  flag=0
#  option=value -&gt;  option='value'
#
setcontrolvals () {
    while test "$#" -gt 0 ; do
      iI=$1
      vAr=`expr "$iI" : " *\([A-Z]\).*"`;
      vAlue=`expr "$iI" : " *[A-Z]\(.*\)"`;
      if test "X$vAr" != "X" ; then
        eval $vAr='$vAlue';
        #setvar $vAr "$vAlue";
      fi;
      shift
    done
}

Tf=$IFS
IFS="
"
setpcvals $PRINTCAP_ENTRY
setcontrolvals $CONTROL
IFS=$Tf

#
# restore argument list
set -- $Args
Args=""
vAr=""
vAlue=""
vAls=""
iI=""
Tf=""

if test -n "$Debug" ; then
    set &gt;/tmp/after
    echo "$@" &gt;&gt;/tmp/after
    diff /tmp/before /tmp/after
fi
/bin/cat
exit 0</screen>
</informalexample>
</para>

<para>Lets print our <filename>/tmp/hi</filename> test file and then look at
the results in <filename>/tmp/lp</filename>:
<informalexample>
<screen><prompt>h4: {188} % </prompt><userinput>cp /dev/null /tmp/lp</userinput>
<prompt>h4: {189} % </prompt><userinput>lpr /tmp/hi</userinput>
<prompt>h4: {190} % </prompt><userinput>more /tmp/lp</userinput>
0a1
&gt; e=dfA021771h4.private
2a4,6
&gt; l=66
&gt; s=status
&gt; L=papowell
10a15,17
&gt; j=021771
&gt; C=A
&gt; J=/tmp/hi
12a20
&gt; a=acct
...
33a58
&gt; Printer=lp
...
hi</screen>
</informalexample>
</para>

<para>As we see from the output,
shell variables have the values of our command line and printcap options.
It is left as an exercise for the reader to add the necessary
<literal remap=tt>export</literal>
statements to cause these values to be exported to
subshells.
It is <emphasis>not</emphasis>
recommended that a wholesale export of the shell variables
be done,
but only selected ones.</para>

<para>The paranoid and security minded reader
will see some possible security problem with this script.
The <literal>eval $vAr='$vAlue'</literal> command sets
the value of the shell variable
<literal remap=tt>$vAr</literal>
to the value
<literal remap=tt>$vAlue</literal>.
The
<literal remap=tt>$vAr</literal>
variable is always taken from either a single letter or is the
name of an option in the printcap file.
Clearly the printcap file must not be modifiable by users,
and should have the same security considerations as any other
system configuration file.
The values of the
<literal remap=tt>$vAlue</literal>
are taken directly from the control file,
whose contents are under the control of the originator of the
print job request.</para>

<para>For this reason &LPRng; takes the rather brutal step of <emphasis>sanitizing</emphasis>
the control file.
Only alphanumerics or a character in the list <literal>@/:()=,+-%_</literal>
are used in the control file; all others replaced by the underscore (<literal/_/)
character.
In addition,
all filters are run as the <application>lpd</application> user specified in the <filename>lpd.conf</filename>
configuration file.</para>

<para>The following is an example of how to extract the same information
in Perl:
<informalexample>
<screen>#!/usr/bin/perl
eval 'exec /usr/bin/perl -S $0 ${1+"$@"}'
    if $running_under_some_shell;
            # this emulates #! processing on NIH machines.
            # (remove #! line above if indigestible)

use Getopt::Std;
my(%args,%options);
# get the arguments
getopt(
    "a:b:cd:e:f:g:h:i:j:l:m:n:o:p:q:r:s:t:u:v:w:x:y:z:" .
    "A:B:C:D:E:F:G:H:I:J:L:M:N:O:P:Q:R:S:T:U:V:W:X:Y:Z:",
    \%args );

# set :key=value  -&gt; $option{$key}=$value
# set :key@       -&gt; $option{$key}="0"
# set :key        -&gt; $option{$key}="1"
 map {
    if( m/^\s*:([^=]+)=(.*)/ ){
     $options{$1}=$2;
    } elsif( m/^\s*:([^=]+)\@$/ ){
     $options{$1}="0";
    } elsif( m/^\s*:([^=]+)/ ){
     $options{$1}="1";
    } elsif( m/^\s*([^|]+)/ ){
     $options{"Printer"}=$1;
    }
 } split( "\n", $ENV{'PRINTCAP_ENTRY'});

# get the control file entries
 map {
    if( m/^\s*([A-Z])(.*)/ ){
     $options{$1}=$2;
    } elsif( m/^\s*([a-z])/ ){
     $options{'Format'}=$1;
    }
 } split( "\n", $ENV{'CONTROL'});</screen>
</informalexample>
</para>

<para>The Perl
<literal remap=tt>Getopt::Std</literal>
routine
parses the command line options and puts their values in the
<literal remap=tt>%args</literal>
hash variable
where they can be accessed using <literal>$args{'x'}</literal>.
Similarly,
the
<literal remap=tt>map</literal>
and
<literal remap=tt>split</literal>
functions process the PRINTCAP_ENTRY
and CONTROL environment variable and set
<literal remap=tt>%options</literal>
with the printcap entry options and the values from the control file.
The
<function remap=tt>map</function>
function could be replaced by a
<function remap=tt>foreach</function>
loop,
but this is Perl: <emphasis>There is more than one way to do it</emphasis>
and no
tutorial would be complete without at least one mind stretching
example that has the reader reaching for the reference manual.</para>

</sect2>

<sect2 id="filterexitcodes"><title>Filter Exit Codes</title>

<para>The <application>lpd</application>
server uses the exit code of the filter to determine if
the filter was successful or unsuccessful.
The

<link linkend="exitcodes">Filter Exit Codes</link>

section discusses these values in detail,
but here are the most important:
<variablelist>
<varlistentry><term> 0 - JSUCC </term>
<listitem>
<para>A JSUCC exit code indicates that the filter was successful
in doing its work.</para>

</listitem>
</varlistentry>

<varlistentry><term> 1 - JFAIL </term>

<listitem>
<para>A JFAIL exit code indicates that the filter was unsuccessful
in doing its work,
possibly due to a transient condition such as out of paper,
printer jam, etc.,
but an additional attempt might be successful.
Usually the <application>lpd</application> server will try at most

<link linkend="abnormalterm">send_try</link>

attempts before giving up.</para>

</listitem>
</varlistentry>

<varlistentry><term> 2 - JABORT </term>

<listitem>
<para>A JABORT exit code indicates that the filter was unsuccessful
in doing its work
and has detected a condition that would make it impossible
to print the job.
In addition,
the printer may require administrative attention,
and the print queue operation may need to be suspended
until the problem is rectified. </para>

</listitem>
</varlistentry>

<varlistentry><term> 3 - JREMOVE </term>

<listitem>
<para>The JREMOVE exit code will cause the job to be removed
from the print queue.</para>

</listitem>
</varlistentry>

<varlistentry><term> 6 - JHOLD </term>

<listitem>
<para>The JHOLD exit code will cause the job to be
temporarily prevented from printing until release by
the
<command remap=tt>lpc release</command>
command.</para>

</listitem>
</varlistentry>

<varlistentry><term> Other Values</term>

<listitem>
<para>Usually any other value,
including exit due to a signal,
is treated as a JABORT exit,
and the same action is taken.</para>

</listitem>
</varlistentry>
</variablelist>
</para>

<para>It should be obvious that the filter exit code
is very important,
and that care needs to be taken to return the
correct value.</para>

</sect2>

<sect2 id="jobformatsandfilterselection"><title>Job Formats and Filter Selection</title>

<para>In the previous sections we discussed how a print filter was
executed and how it could be used.
Now we will look at how the <application>lpd</application> spooler chooses a print filter program.
Let us re-examine our example print job control file:
<informalexample>
<screen>Hh4.private
Ppapowell
J/tmp/hi
CA
Lpapowell
Apapowell@h4+105
D2000-04-12-15:27:26.662
Qlp
N/tmp/hi
fdfA105h4.private
UdfA105h4.private</screen>
</informalexample>
</para>

<para>Each data file for a print job has a name with the format
<literal remap=tt>df</literal><emphasis>X</emphasis>nnn<filename>h4.private</filename>.
The
<literal remap=tt>df</literal>
is used to indicate that the file is a <emphasis>data file</emphasis>,
and the remainder is a unique name for the file in the job.
The
<literal remap=tt>X</literal>
part of the name must be an upper or lower case letter,
setting a limit of 52 different files in a single print job.</para>

<para>The <filename>fdfA105h4.private</filename> line in the control file
specifies that we are to print the job using the filter for the
<literal remap=tt>f</literal>
format;
the file printing information consists of the format assigned to the file
and the name of the file.
In the legacy BSD print spoolers,
this format was used to select the print filter
to be used.
&LPRng; has expanded this by providing a <emphasis/default/ filter specification.
</para>
<table id=jobformat frame=all><title>Job Formats and Filter Selection</title>
<tgroup cols=3 align=left colsep=1 rowsep=1>
<thead>
<row><entry><application/lpr/ command line option </entry><entry> Control File Line </entry><entry> Printcap Option For Filter </entry><entry> Filter Command Line </entry></row>
</thead>
<tbody>
<row><entry>(default) </entry><entry> fdfAnnn </entry><entry> :if=/path </entry><entry> /path -Ff ... </entry></row>
<row><entry>-b or -l </entry><entry> ldfAnnn </entry><entry> :if=/path </entry><entry> /path ... -Ff -c </entry></row>
<row><entry>-p </entry><entry> pdfAnnn </entry><entry> :if=/path </entry><entry> pr | format f filter </entry></row>
<row><entry>-c, -d, -n, -r, -t, -v, -FX </entry><entry> XdfAnnn </entry><entry> :Xf=/path ... </entry><entry> /path -FX </entry></row>
<row><entry>any format</entry><entry> XdfAnnn </entry><entry>:filter=/path ... </entry><entry> /path -FX </entry></row>
</tbody>
</tgroup>
</table>

<para><xref linkend=jobformat> shows the rather baroque relationship between the
format options specified by the <application>lpr</application>
command
and the way that <application>lpd</application>
uses them.
The reason for this complexity lies in the various implementations and
variations that occurred during the development and deployment of the
original BSD print spooling software.
</para>

<para>Here is the complete,
arcane,
and baroque set of rules that are used to select
and print filters.
The de<literal remap=bf>f</literal>ault format used by <application/lpr/
is <literal remap=tt>f</literal>;
unless some other format is specified this is used.
The
<command>lpr -b</command> and <command>lpr -l</command>
(<literal/b/inary and <literal/l/iteral literals)
are a request to <application>lpd</application>
to do as little processing as possible of this file before printing it.
<application/Lpd/ use the <literal/:if/ filter for formats
<literal/f/
and
<literal/l/;
the <literal/l/ literal causes the the <literal>-c</literal>
filter command line flag to be used as well.
The <command/lpr/ <literal/-c/,
<literal/-d/,
<literal/-n/,
<literal/-r/,
<literal/-t/,
and <literal/-v/
options cause the corresponding format to be used,
and for <application/lpd/ to use the filter specified by the printcap option
<literal/:Xf/, where <literal/X/ is the specified format.
</para>
<para>
The
<command>lpr -p</command>
(<literal remap=bf>p</literal>retty-print literal)
selects <literal/p/ format,
and <application>lpd</application> is supposed
to use the program specified by the
<literal remap=tt>:pr</literal> printcap
option to format the file and then process the output of this
program according to format <literal/f/.
Unpredictable results may occur using this facility.
</para>
<para>
the <command>lpr -FX</command> allows you to explicitly specify format
<literal remap=tt>X</literal> where <literal/X/ is a lower case letter,
and <application/lpd/ will use the filter specified by printcap option
<literal/:Xf/, where <literal/X/ is the specified format.
If there is no <literal/:Xf/ printcap literal value then the printcap
<literal/:filter/ literal value will be used as the filter,
and if this is undefined then the file will be passed without processing
through to the printing device.
</para>
<para>
If a filter is not specified for the format
then the default filter specified by
<literal remap=tt>:filter=/path</literal>
filter is used,
and if there is no default,
then the output is sent directly to the output device.
</para>

<para>
If the <literal/:fx=/<emphasis/formats/ is present in a printcap entry,
it specifies the formats that are allowed.
For example, <literal/:fx=lfv/ would allow only formats
<literal/l/,
<literal/f/,
and
<literal/v/ to be used on a particular spool queue.
</para>
<para>
Some
<literal remap=tt>Xf</literal>
options have pre-assigned meanings
and cannot be used for filter selection.
</para>
<informaltable>
<tgroup cols=2>
<thead>
<row><entry>Printcap Option </entry><entry> Purpose </entry></row>
</thead>
<tbody>
<row><entry>Printcap Option </entry><entry> Purpose </entry></row>
<row><entry>:af=/path </entry><entry> Accounting File </entry></row>
<row><entry>:ff=formfeed </entry><entry> Form Feed String </entry></row>
<row><entry>:if=/path </entry><entry> filter (l,b,p,f formats) </entry></row>
<row><entry>:filter=/path </entry><entry> Default filter </entry></row>
<row><entry>:lf=/path </entry><entry> Log file </entry></row>
<row><entry>:of=/path </entry><entry> OF filter </entry></row>
<row><entry>:sf </entry><entry> suppress form feed between job files </entry></row>
</tbody>
</tgroup>
</informaltable>
<para>The
<literal remap=tt>:of</literal>
filter is a special case
and is used for banner printing and accounting purposes.
See <link linkend="ofdetails"> OF Filter </link> for details.
</para>
</sect2>
</sect1>

<sect1 id="jobfileformatconversion"><title>Job File Format Conversion with Filters</title>

<para>One of the major problems that face new users to UNIX printing
is when they have a printer that has a proprietary
print job format such as the HP DeskJet series of printers.
The solution to this problem is quite simple:
generate your output in PostScript,
and then use the
<link linkend="ghostscript"> GhostScript </link>
program to convert the GhostScript output to
a format compatible with your printer.
<informalexample>
<screen>lp:filter=/usr/local/lib/filters/myfilter:...

/tmp/myfilter:

#!/bin/sh
/usr/local/bin/gs -dSAFER -dNOPAUSE -q -sDEVICE=djet500 \
    -sOutputFile=- - &amp;&amp; exit 0
exit 2</screen>
</informalexample>
</para>

<para>This simple
<emphasis>tutorial</emphasis>
example suffers from some serious problems.
If you accidentally send a non-PostScript file to the printer
GhostScript will detect this and exit with an error message
but only after trying to interpret the input file as PostScript.
If the input file was a text file,
this can result in literally thousands of error messages and hundreds
of pages of useless output.</para>

<para>In order to make a more robust filter we need to meet
the following minimum requirements:
<orderedlist>

<listitem>
<para>The file type should be determined,
and only files that are PostScript should be passed to GhostScript.</para>
</listitem>

<listitem>
<para>We may have some conversion routines that can convert files into PostScript
files and then we can send them to GhostScript for raster conversion.</para>
</listitem>

<listitem>
<para>If we cannot convert a file,
then we should simply terminate the printing and cause the spooler
to remove the job.</para>
</listitem>

</orderedlist>
</para>

<para>
The <application/ifhp/ Print Filter program is a companion to the
&LPRng; software and does this type of operation.
If you are using Linux,
then you may find the <emphasis/RedHat Print Filters/
(<ulink URL="http://www.debian.org">http://www.debian.org</ulink>)
installed and
in use on your system.
The <emphasis>magicfilter</emphasis>
developed by  H. Peter Anvin
<ulink URL="http://www.debian.org">http://www.debian.org</ulink>
is distributed with Debian Linux.
The
<application remap=tt>apsfilter</application>
by Andreas Klemm
<ulink URL="http://www.freebsd.org/~andreas/index.html">http://www.freebsd.org/~andreas/index.html</ulink>
is also widely used,
although now most of its functionality is directly available in &LPRng;.
Finally,
the
<application remap=tt>a2ps</application>
(Ascii to PostScript)
converter by
<ulink URL="demaille@inf.enst.fr">Akim Demaille</ulink>
and
<ulink URL="santana@st.com">Miguel Santana</ulink> is available from
<ulink URL="www-inf.enst.fr/~demaille/a2ps">www-inf.enst.fr/~demaille/a2ps</ulink>.
This package provides a very nice set of facilities for
massaging,
mangling,
bending,
twisting,
and being downright nasty with text or other files.
</para>
<sect2><title>Simple Filter with File Format Detection</title>
<para>
Since this is a tutorial,
we will demonstrate a simple way to make your own
<emphasis>multi-format</emphasis>
print filter,
and provide insight into how more complex filters work.</para>

<para>The
<link linkend="fileprog">file</link> utility  developed
by Ian F. Darwin uses a database of file signatures to determine
what the contents of a file are.
For example:
<informalexample>
<screen><prompt>h4: {191} % </prompt><userinput>cd /tmp</userinput>
<prompt>h4: {192} % </prompt><userinput>echo hi &gt;hi</userinput>
<prompt>h4: {193} % </prompt><userinput>gzip -c hi &gt;hi.gz</userinput>
<prompt>h4: {194} % </prompt><userinput>echo "%!PS-Adobe-3.0" &gt;test.ps</userinput>
<prompt>h4: {195} % </prompt><userinput>gzip -c test.ps &gt;test.ps.gz</userinput>
<prompt>h4: {196} % </prompt><userinput>file hi hi.gz test.ps test.ps.gz</userinput>
hi:        ASCII text
hi.gz:     gzip compressed data, deflated
test.ps:   PostScript document text conforming at level 3.0
test.ps.gz: gzip compressed data, deflated
<prompt>h4: {197} % </prompt><userinput>file - &lt;test.ps</userinput>
standard input: PostScript document text conforming at level 3.0</screen>
</informalexample>
</para>

<para>If we are given a file,
we can now use <application/file/ to recognize the file type
and if the file type is suitable for our printer
we can send it to the printer,
otherwise we can reject it.
The following is a simple
yet very powerful shell script that does this.
<informalexample>
<screen>#!/bin/sh
# set up converters
gs="/usr/local/bin/gs -dSAFER -dNOPAUSE -q -sDEVICE=djet500 \
    -sOutputFile=/dev/fd/3 - 3&gt;&amp;1 1&gt;&amp;2"
a2ps="/usr/local/bin/a2ps -q -B -1 -M Letter --borders=no -o-"
decompress=""
# get the file type
type=`file - | tr A-Z a-z | sed -e 's/  */_/g'`;
echo TYPE $type &gt;&amp;2
case "$type" in
  *gzip_compressed* ) decompress="gunzip -c |" compressed="compressed" ;;
esac

# we need to rewind the file
perl -e "seek STDIN, 0, 0;"

if test "X$decompress" != "X" ; then
    type=`$decompress head | file - | tr A-Z a-z | sed -e 's/  */_/g'`;
    echo COMPRESSED TYPE $type &gt;&amp;2
    # we need to rewind the file
    perl -e "seek STDIN, 0, 0;"
fi
case "$type" in
  *postscript* ) process="$gs" ;;
  *text* )       process="$a2ps | $gs" ;;
  * )
    echo "Cannot print type $compressed '$type'" &gt;&amp;2
    # exit with JREMOVE status
    exit 3
    ;;
esac
# in real life, replace 'echo' with 'exec'
echo "$decompress $process"
# exit with JABORT if this fails
exit 2</screen>
</informalexample>
</para>

<para>Copy this to the <filename>/tmp/majik</filename> file,
and give it
<literal remap=tt>0755</literal>
(executable) permissions.
Here is an example of the output of the script:
<informalexample>
<screen><prompt>h4: {198} % </prompt><userinput>/tmp/majik &lt;test.ps.gz</userinput>
TYPE standard_input:_gzip_compressed_data,_deflated...
COMPRESSED TYPE standard_input:_postscript_document_level_3.0
gunzip -c | /usr/local/bin/gs -dSAFER -dNOPAUSE -q -sDEVICE=djet500 \
   -sOutputFile=/dev/fd/3 - 3&gt;&amp;1 1&gt;&amp;2
<prompt>h4: {199} % </prompt><userinput>/tmp/majik &lt;/tmp/hi</userinput>
TYPE standard_input:_ascii_text
 /usr/local/bin/a2ps -q -B -1 -M Letter --borders=no -o- \
  | /usr/local/bin/gs -dSAFER -dNOPAUSE -q -sDEVICE=djet500 \
   -sOutputFile=/dev/fd/3 - 3&gt;&amp;1 1&gt;&amp;2</screen>
</informalexample>
</para>

<para>The first part of the script sets up a standard set of commands that
we will use in the various conversions.
A full blown package for conversion would use a database or setup file
to get these values.
We then use the
<application remap=tt>file</application>
utility to determine the input file type.
The output of the
<application remap=tt>file</application>
utility is translated to lower case
and multiple blanks and tabs are removed.</para>

<para>We use a simple shell
<literal remap=tt>case</literal>
statement to determine if we have a
compressed file and get a decompression program to use.
We reapply the
<application remap=tt>file</application>
utility to the decompressed file (if it was compressed)
and get the file type.</para>

<para>Finally we use another
<literal remap=tt>case</literal>
statement to get the output converter
and then we run the command.
For tutorial purposes,
we use an
<command remap=tt>echo</command>
rather than an
<command remap=tt>exec</command>
so we can see the actual
command, rather than the output.</para>

<para>Just for completeness,
here is
<application remap=tt>majikperl</application>:
<informalexample>
<screen>#!/usr/bin/perl
eval 'exec /usr/bin/perl -S $0 ${1+"$@"}'
    if $running_under_some_shell;
            # this emulates #! processing on NIH machines.
            # (remove #! line above if indigestible)
my($gs) = "/usr/local/bin/gs -dSAFER -dNOPAUSE -q -sDEVICE=djet500 \
    -sOutputFile=/dev/fd/3 - 3&gt;&amp;1 1&gt;&amp;2";
my($a2ps)="/usr/local/bin/a2ps -q -B -1 -M Letter --borders=no -o-";

my($decompress,$compressed,$process,$type);
$decompress=$compressed=$process=$type="";

# get the file type
$type = ` file - `;
$type =~ tr /A-Z/a-z/;
$type =~ s/\s+/_/g;
print STDERR "TYPE $type\n";
($decompress,$compressed) = ("gunzip -c |", "gzipped")
  if( $type =~ /gzip_compressed/ );
print STDERR "decompress $decompress\n";
unless( seek STDIN, 0, 0 ){
    print "seek STDIN failed - $!\n"; exit 2; }
if( $decompress ne "" ){
    $type = ` $decompress file - `;
    $type =~ tr /A-Z/a-z/;
    $type =~ s/\s+/_/g;
    print STDERR "COMPRESSED TYPE $type\n";
    unless( seek STDIN, 0, 0 ){
        print "seek STDIN failed - $!\n"; exit 2; }
}
$_ = $type;
if( /postscript/ ){
    $process="$gs";
} elsif( /_text_/ ){
    $process="$a2ps | $gs" ;;
} else {
    print STDERR "Cannot print $compressed '$type'" &gt;&amp;2;
    # JREMOVE
    exit 3;
}
exec "$decompress $process";
print "exec failed - $!\n";
exit 2;</screen>
</informalexample>
</para>

</sect2>

<sect2><title>The <application/ifhp/ Filter</title>

<para>
The
<link linkend="secftp">ifhp</link>
Print Filter is the companion print filter supplied with &LPRng;
and is normally
installed together with the &LPRng; software.
<application remap=tt>Ifhp</application>
supports a wide range of PostScript,
PCL, text, and raster printers,
and can be configured to support almost any type of printer
with a stream based interface.
It provides diagnostic and error information as well as
accounting information.
It recognizes a wide range of file types by using
the
<application remap=tt>file</application>
utility and the pattern matching technique
demonstrated in the previous section,
and can do selective conversions from one format to others.</para>

<para>The
<link linkend="postscript">PostScript</link>
and
<link linkend="pcl">PCL</link>
printer job languages are supported by most printer manufacturers.
However,
in order to have a job printed correctly the following steps must be taken.
<orderedlist>

<listitem>
<para>The printer must be put into a known <emphasis/initial/
state by sending it the appropriate
reset strings or performing a correct set of IO operations.</para>
</listitem>

<listitem>
<para>If accounting is being done,
then the printer accounting information must be obtained
and recorded.
See
<link linkend="accountingref">Accounting</link>
for more information about &LPRng; support for accounting.</para>
</listitem>

<listitem>
<para>The file to be printed must be checked to see if it is
compatible with the printer,
and if not,
a format conversion program invoked to convert it to
the required format.</para>
</listitem>

<listitem>
<para>If the user selects a set of printer specific options such
as
landscape mode,
duplex printing,
multiple copies,
or
special paper,
the
appropriate commands must be sent to the
printer to select these options.</para>
</listitem>

<listitem>
<para>The file must be transferred to the printer
and the printer is monitored for any error conditions.</para>
</listitem>

<listitem>
<para> Any required end of job commands are sent to the printer,
and the printer monitored for error conditions while the job finishes printing.</para>
</listitem>

<listitem>
<para>If accounting is being done,
the printer accounting information such as page count and time used
must be obtained and recorded.
See
<link linkend="accountingref">Accounting</link>
for more information about &LPRng; support for accounting.</para>
</listitem>

</orderedlist>
</para>

<para>The
<application remap=tt>ifhp</application>
filter uses the <filename>ifhp.conf</filename> configuration file
to determine the actions and commands
appropriate for various models of printers.
See the
<application remap=tt>ifhp</application>
documentation for details about the
format and contents of this file.
This file contains entries for a large number of PostScript,
PJL, and other printers.
The default printer used by <application/ifhp/
is the HP LaserJet 4M Plus which supports PostScript,  PCL, and PJL.
The commands and formats used by this printer
is compatible with a large number of other HP printers.
</para>

<para>We will demonstrate how to add the
<application remap=tt>ifhp</application>
filter to your printcap entry.
Find the path to the
<application remap=tt>ifhp</application>
filter using the
<application>find</application> command as we did in the previous exercise.
Modify the printcap as shown below
and use
<command remap=tt>lpc lpd</command>
to restart <application>lpd</application>.
<informalexample>
<screen>lp:sd=/var/spool/lpd/%P
  :force_localhost
  :lp=/tmp/lp
  :ifhp=model=default
  # modify the path to ifhp appropriately
  :filter=/usr/local/libexec/filters/ifhp</screen>
</informalexample>
</para>

<para>Now print the <filename>/tmp/hi</filename>
and then display <filename>/tmp/lp</filename>
using a text editor such as
<application remap=tt>vi</application>
or
<application remap=tt>emacs</application>
that shows
control characters:
<informalexample>
<screen><prompt>h4: {200} % </prompt><userinput>cp /dev/null /tmp/lp</userinput>
<prompt>h4: {201} % </prompt><userinput>lpr /tmp/hi</userinput>
<prompt>h4: {202} % </prompt><userinput>vi /tmp/lp</userinput>
^[%-12345X@PJL
@PJL JOB NAME = "PID 405" DISPLAY = "papowell"
@PJL RDYMSG DISPLAY = "papowell"
@PJL USTATUSOFF
@PJL USTATUS JOB = ON
@PJL USTATUS DEVICE = ON
@PJL USTATUS PAGE = ON
@PJL USTATUS TIMED = 10
@PJL ENTER LANGUAGE = PCL
^]E^]&amp;^]&amp;k2G^]&amp;s0C^]&amp;l0O^]9^](s0P^](s10.00H^](s4099Thi
^]E^]%-12345X@PJL
@PJL RDYMSG DISPLAY = "papowell"
@PJL EOJ NAME = "PID 405"
@PJL USTATUSOFF
@PJL USTATUS JOB = ON
@PJL USTATUS DEVICE = ON
@PJL USTATUS PAGE = ON
@PJL USTATUS TIMED = 10
@PJL RDYMSG DISPLAY = ""
^[%-12345X</screen>
</informalexample>
</para>

<para>The output now contains all of the control sequences and setup
codes needed to print a text file on the default printer.
The <literal/:ifhp=model=default/ printcap entry is used by <application/ifhp/
to get the information it needs to perform its operation.
The following options are commonly provided in the
<literal/:ifhp=/ option to configure the <application/ifhp/
filter.
</para>
<table id=ifhpopts frame=all><title><application/:ifhp=/ Options</title>
<tgroup cols=2 align=left colsep=1 rowsep=1>
<thead>
<row><entry>
Option</entry><entry>
Purpose</entry></row>
</thead>
<tbody>
<row><entry>
<literal/model=/<emphasis/name/</entry><entry>
Use <emphasis/name/ entry in <literal/ifhp.conf/</entry></row>
<row><entry>
<literal/status/ or <literal/status@/</entry><entry>
Printer does or does not provide status information</entry></row>
<row><entry>
<literal/sync/, <literal/sync@/, <literal/sync=/<emphasis/(ps|pjl)/ </entry><entry>
Printer does or does not indicate ready to operate at start of job,
or use PostScript or PJL code sequence to determine if printer is ready.
</entry></row>
<row><entry>
<literal/pagecount/, <literal/pagecount@/, <literal/pagecount=/<emphasis/(ps|pjl)/ </entry><entry>
Printer does or does not have pagecount support,
or use PostScript or PJL code sequence to determine pagecount.
</entry></row>
<row><entry>
<literal/waitend/, <literal/waitend@/, <literal/waitend=/<emphasis/(ps|pjl)/ </entry><entry>
Wait or do not wait for end of job,  or send PostScript or PJL code sequence
to have printer report end of job.
</entry></row>
</tbody>
</tgroup>
</table>
<para>
The 
<literal/model=/<emphasis/name/
entry is used to specify the configuration entry in the <literal/ifhp.conf/
file to be used by <application/ifhp/.
This entry usually has all of the specific information needed by the
<application/ifhp/ filter.
</para>

<para>
The <literal/status/
option is the most common option usually provided in a printcap entry.
This option is needed when the communication with the printer is
<emphasis/write-only/
and no status information will be returned.
If a printer normally supports returning status information then the
<literal/ifhp.conf/ configuration entry will indicate this
and the <application/ifhp/ filter will try to get status.
When no status is returned it will either terminate operation after
a timeout or sit in an endless loop waiting for status.
By specifying <literal/status@/ you will suppress getting status.
This also has the effect of doing
<literal/sync@/,
<literal/pagecount@/,
and
<literal/waitend@/
</para>
<para>
The <literal/sync/ option is used to cause <application/ifhp/
to wait for an <emphasis/end of job/ indication from the printer
before starting the next job.
This is usually done in order to make sure that all jobs have been
flushed from a printer before starting another job.
If you specify <literal/sync@/ then you may get slightly faster startup
but at the expense of losing the ends of previous print jobs.
</para>
<para>
The <literal/pagecount/ option is used to cause <application/ifhp/
to get the value of a hardware pagecounter from the printer.
If your printer supports such an item then the <literal/ifhp.conf/
configuration option usually indicates this.
However,
it takes a small amount of time to get the pagecounter information from
the printer and you may not need it.
Use <literal/sync@/ if you do not want page counts.
</para>

<para>
Finally,  <literal/waitend/ option is used to cause <application/ifhp/
to wait for an <emphasis/end of job/ indication from the printer before
exiting.
If you specify <literal/waitend@/ then the filter will exit
immediately after sending the job,
but you will possibly lose any error information or status reports from
the printer.
</para>

<para>
For a complete list of all of the <application/ifhp/ options please
see the IFHP documentation.
</para>

</sect2>

<sect2 id="jaggies"><title>The Jaggies - LF to CR-LF Conversion With lpf</title>

<para>When printing to vintage hard copy devices or to
printers that support a <emphasis>text</emphasis>
mode,
many UNIX users discover that their output suffers from
a case of the jaggies.
<informalexample>
<screen>Input file:

  This is
  a nice day

Output:

  This is
         a nice day</screen>
</informalexample>
</para>

<para>UNIX systems terminate lines with a single <acronym>NL</acronym> (new line) character.
This causes the printer to move down one line on the printing page
but does not change its horizontal position
and print the next character at the left margin.
This is done by using the <acronym>CR</acronym> (carriage return) character.
You need to convert the single <acronym>NL</acronym> to a
<literal remap=tt>CR-LF</literal>
combination and the
<application remap=tt>lpf</application>
filter supplied with &LPRng; does this.</para>

<para>First,
locate the
<application remap=tt>lpf</application>
filter.
You can find it by using the command:
<informalexample>
<screen><prompt>h9: {160} % </prompt><userinput>find /usr/ -type f -name lpf -print</userinput>
/usr/libexec/lpr/lpf</screen>
</informalexample>
</para>

<para>We will first see what the output is like without
<application remap=tt>lpf</application>,
and then see what it does.
Modify the
<application remap=tt>lp</application>
printcap entry as shown below
and then use
<command remap=tt>lpc restart</command>
to restart the <application>lpd</application> server.
<informalexample>
<screen>lp:sd=/var/spool/lpd/%P
  :force_localhost
  :lp=/tmp/lp</screen>
</informalexample>
</para>

<para>Print a file and view the output using the following
commands.
If you do not have the
<application remap=tt>od</application>
(octal dump) program,
try using
<application remap=tt>hexdump</application>
or some other appropriate program
that displays the numerical contents of the file.
<informalexample>
<screen><prompt>h4: {203} % </prompt><userinput>cp /dev/null /tmp/lp</userinput>
<prompt>h4: {204} % </prompt><userinput>lpr /tmp/hi</userinput>
<prompt>h4: {205} % </prompt><userinput>od -bc /tmp/lp</userinput>
0000000  150 151 012
           h   i  \n
0000003</screen>
</informalexample>
</para>

<para>Now we will use the
<application remap=tt>lpf</application>
filter.
Modify the printcap as shown below
and use
<command remap=tt>lpc reread</command>
to cause <application>lpd</application> to reread the configuration information.
<informalexample>
<screen>lp:sd=/var/spool/lpd/%P
  :force_localhost
  :lp=/tmp/lp
  # modify the path to lpf appropriately
  :filter=/usr/local/libexec/filters/lpf</screen>
</informalexample>
</para>

<para>Now reprint the file:
<informalexample>
<screen><prompt>h4: {206} % </prompt><userinput>cp /dev/null /tmp/lp</userinput>
<prompt>h4: {207} % </prompt><userinput>lpr /tmp/hi</userinput>
<prompt>h4: {208} % </prompt><userinput>od -bc /tmp/lp</userinput>
od -bc /tmp/lp
0000000  150 151 015 012
           h   i  \r  \n
0000004</screen>
</informalexample>
</para>

<para>As you see,
<application remap=tt>lpf</application>
changes the <literal/LF/ to a
<literal remap=tt>CR-LF</literal>
sequence.</para>

</sect2>

<sect2><title>Store and Forward Spool Queues</title>

<para>Up to now we have assumed that associated with each spool
queue is a hardware printing device.
When a job is sent to the spool queue the <application>lpd</application>
server will take actions to filter it and then send it to the printing
device.</para>

<para>However,
we can also have <emphasis>store and forward</emphasis>
spool queues.
These queue act to simply buffer jobs and then forward them
to another spooler.
The following printcap entry shows how you can specify a store and
forward queue.
<informalexample>
<screen># store and forward using classical BSD :rm:rp
lp:rp=pr:rm=host
  :sd=/var/spool/lpd/%P
  :server
# store and forward using &LPRng; lp=pr@host
lp:lp=pr@host
  :sd=/var/spool/lpd/%P
  :server</screen>
</informalexample>
</para>

<para>The legacy
<literal remap=tt>:rp</literal>
(remote printer) and
<literal remap=tt>:rm</literal>
(remote host)
format can be used to specify the print queue and destination host
for jobs sent to this queue.
The &LPRng; <literal>:lp=pr@host</literal> format serves the same function,
and has precedence over the
<literal remap=tt>:rm:rp</literal>
form.</para>

<para>Edit the printcap file so it has contents indicated below,
use <command>checkpc -f</command> to check the printcap,
and then use
<command remap=tt>lpc reread</command>
to restart the <application>lpd</application> server.
<informalexample>
<screen>lp:force_localhost
lp:server
  :sd=/var/spool/lpd/%P
  :lp=lp2@localhost
lp2:force_localhost
lp2:server
  :sd=/var/spool/lpd/%P
  :lp=/tmp/lp2</screen>
</informalexample>

Execute the following commands
to print the <filename>/tmp/hi</filename> file
and observe the results:
<informalexample>
<screen><prompt>h4: {209} % </prompt><userinput>lpr /tmp/hi</userinput>
<prompt>h4: {210} % </prompt><userinput>lpq -lll</userinput>
Printer: lp@h4 (dest lp2@localhost)
 Queue: no printable jobs in queue
 Status: sending control file 'cfA029h4.private' \
    to lp2@localhost at 09:39:57.719
 Status: completed sending 'cfA029h4.private' \
    to lp2@localhost at 09:39:57.724
 Status: sending data file 'dfA029h4.private' \
    to lp2@localhost at 09:39:57.727
 Status: completed sending 'dfA029h4.private' \
    to lp2@localhost at 09:39:57.925
 Status: done job 'papowell@h4+29' transfer \
    to lp2@localhost at 09:39:57.926
 Status: subserver pid 29031 exit status 'JSUCC' at 09:39:57.953
 Status: lp@h4.private: job 'papowell@h4+29' printed at 09:39:57.961
 Status: job 'papowell@h4+29' removed at 09:39:57.993
Printer: lp2@h4
 Queue: no printable jobs in queue
 Status: no banner at 09:39:58.054
 Status: printing data file 'dfA029h4.private', size 3 at 09:39:58.054
 Status: printing done 'papowell@h4+29' at 09:39:58.054
 Status: accounting at end at 09:39:58.054
 Status: finished 'papowell@h4+29', status 'JSUCC' at 09:39:58.054
 Status: subserver pid 29033 exit status 'JSUCC' at 09:39:58.056
 Status: lp2@h4.private: job 'papowell@h4+29' printed at 09:39:58.056
 Status: job 'papowell@h4+29' removed at 09:39:58.069</screen>
</informalexample>
</para>

<para>As we see from the status,
our job was sent to the
<literal remap=tt>lp</literal>
spool queue first.
It was store there and then the <application>lpd</application> server transferred it to the
<literal remap=tt>lp2</literal>
spool queue,
where it was printed to the file <filename>/tmp/lp2</filename>.</para>

</sect2>

<sect2><title>Filtering Job Files In Transit</title>

<para>One of the major problems with store and forward operation
is that the destination spool queue may not actually be a
spool queue - it can be a printer.
Many network printers provide an RFC1179 compatible network
interface and act,
for job forwarding purposes,
like a host running a limited capability BSD print spooler.</para>

<para>
By adding a filter to the printcap information we can
modify the format of a job file so that it is compatible with
the destination printer.
</para>

<para>Edit the printcap and <filename>/tmp/testf</filename>
files so they have the contents indicated below,
give <filename>/tmp/testf</filename> executable permissions,
use <command>checkpc -f</command> to check the printcap,
and then use
<command remap=tt>lpc reread</command>
to restart the <application>lpd</application> server.
<informalexample>
<screen># set /tmp/testf to contain the following
#   and chmod 755 /tmp/testf
#!/bin/sh
echo TESTF $0 $@
/bin/cat
exit 0

# printcap
lp:force_localhost
lp:server
  :sd=/var/spool/lpd/%P
  :lp=lp2@localhost
  :filter=/tmp/testf
  :bq_format=ffl
lp2:force_localhost
lp2:server
  :sd=/var/spool/lpd/%P
  :lp=/tmp/lp2</screen>
</informalexample>

Execute the following commands
to print the <filename>/tmp/hi</filename> file
and observe the results:
<informalexample>
<screen><prompt>h4: {211} % </prompt><userinput>lpr /tmp/hi</userinput>
<prompt>h4: {212} % </prompt><userinput>lpq -llll</userinput>
<prompt>h4: {213} % </prompt><userinput>lpq -llll</userinput>
Printer: lp@h4 (dest lp2@localhost)
 Queue: no printable jobs in queue
 Status: no banner at 09:55:53.681
 Status: printing data file 'dfA086h4.private', size 3, \
    IF filter 'testf' at 09:55:53.683
 Status: IF filter finished at 09:55:53.713
 Status: printing done 'papowell@h4+86' at 09:55:53.714
 Status: sending job 'papowell@h4+86' to lp2@localhost at 09:55:53.734
 Status: connecting to 'localhost', attempt 1 at 09:55:53.735
 Status: connected to 'localhost' at 09:55:53.739
 Status: requesting printer lp2@localhost at 09:55:53.740
 Status: sending control file 'cfA086h4.private' 
      to lp2@localhost at 09:55:53.752
 Status: completed sending 'cfA086h4.private' 
      to lp2@localhost at 09:55:53.757
 Status: sending data file 'dfA086h4.private' 
      to lp2@localhost at 09:55:53.758
 Status: completed sending 'dfA086h4.private' 
      to lp2@localhost at 09:55:53.939
 Status: done job 'papowell@h4+86' transfer 
      to lp2@localhost at 09:55:53.940
 Status: subserver pid 29088 exit status 'JSUCC' at 09:55:53.980
 Status: lp@h4.private: job 'papowell@h4+86' printed at 09:55:53.983
 Status: job 'papowell@h4+86' removed at 09:55:53.998
Printer: lp2@h4
 Queue: no printable jobs in queue
 Status: subserver pid 29092 starting at 09:55:54.005
 Status: accounting at start at 09:55:54.005
 Status: opening device '/tmp/lp2' at 09:55:54.005
 Status: printing job 'papowell@h4+86' at 09:55:54.005
 Status: no banner at 09:55:54.006
 Status: printing data file 'dfA086h4.private', size 298 at 09:55:54.006
 Status: printing done 'papowell@h4+86' at 09:55:54.006
 Status: accounting at end at 09:55:54.006
 Status: finished 'papowell@h4+86', status 'JSUCC' at 09:55:54.006
 Status: subserver pid 29092 exit status 'JSUCC' at 09:55:54.008
 Status: lp2@h4.private: job 'papowell@h4+86' printed at 09:55:54.008
 Status: job 'papowell@h4+86' removed at 09:55:54.020</screen>
</informalexample>
</para>

<para>We have displayed a bit more status information so that we can see
what the actions the
<literal remap=tt>lp</literal>
queue carries out.
It first <emphasis>processes</emphasis>
the job data file
using the
<application remap=tt>testf</application>
filter and puts the results in a temporary file.
Then it sends the contents of the temporary file to the
<literal remap=tt>lp2</literal>
queue.
The
<literal remap=tt>lp2</literal>
queue receives the converted job file and then prints it
to the <filename>/tmp/lp2</filename> file in turn.</para>

<para>By default,
each file in a job is processed by a print file and the processed output
is then sent to the destintion as individual job files,
each with the format specified by the value of the
<literal/bq_format/ (default
<literal/f/) option.
The
<literal/bq_format/ option has the format
<literal/iOiO...d/;
each <literal/i/ is the original format and the corresponding <literal/O/
is the output format.
If there is an odd number of characters then the last unmatched character
is used as the default format,
otherwise no translation is done.
For example,
<literal/flrfl/ will cause the <literal/f/ format to be mapped to
<literal/l/,
<literal/r/ to <literal/f/,
and any others to
<literal/l/.
</para>

</sect2>
</sect1>

<sect1><title>Printcap Basics</title>

<para>In the previous sections we have used simple printcap entries to
define how to set up filters and pass parameters to them.
We will now examine the printcap database in more detail.</para>

<para>The &LPRng; server and client software
gets their configuration information from:
<itemizedlist>

<listitem>
<para>Compile time settings which set the default values for the
configuration information.</para>
</listitem>

<listitem>
<para>A <filename>lpd.conf</filename> file that contains values that override the
compile time defaults.
This information can effect the behavior of the <application>lpd</application>
server and clients.</para>
</listitem>

<listitem>
<para>Printcap entries which have configuration information for individual
print queues.
The information in the printcap entries for the queue override the
<filename>lpd.conf</filename> and compile time defaults.
The system printcap file is read first,
followed by the user printcap file.
</para>
</listitem>

<listitem>
<para>Command line and environment variable values.
These can be used to override or select particular configuration
information or to select one of a set of options for use.</para>
</listitem>

</itemizedlist>
</para>

<para>Each print queue or printer has a name which is used to look up
the printcap information for the printer.
The <filename>/etc/printcap</filename> file is the default location for the
printcap information,
although it can also be obtained from database servers,
or generated by programs.
See the

<link linkend="secnis">Using Programs To Get Printcap Information</link>

section for details.</para>

<para>We will use a more complex printcap file to explore how &LPRng;
gets the printcap information.
Put the following lines in the <filename>/etc/printcap</filename>
file:
<informalexample>
<screen># client entry
lp:tc=.client
lp2:tc=.client
.client:
  :lp=%P@localhost
  :force_localhost

lp:server
  :cm=The Main Print Queue
  :lp=/tmp/lp
  :tc=.common

lp2:server
  :cm=The Second Print Queue
  :lp=/tmp/lp2
  :tc=.common

.common:
  :sd=/var/spool/lpd/%P
  :mx=0</screen>
</informalexample>

The
<command>lpc client</command>
command is very useful to see how &LPRng; uses this
printcap information:
<informalexample>
<screen><prompt>h4: {214} % </prompt><userinput>lpc client</userinput>
Config
<prompt>h4: {215} % </prompt><userinput>lpc client all</userinput>
Config
 :lpd_port=2000
 :printcap_path=/var/tmp/LPD/printcap

Names
 :.client=.client
 :.common=.common
 :lp=lp
 :lp2=lp2
 :main=lp

All
 :lp
 :lp2

Printcap Information
lp|main
 :force_localhost
 :lp=lp@localhost
lp2
 :force_localhost
 :lp=lp2@localhost
</screen>
</informalexample>
</para>

<para>The
<command remap=tt>lpc client all</command>
command shows all of the configuration
and printcap information,
and is the handiest one for system debugging and diagnostics.
The
<emphasis>Name</emphasis>
information is the names of the printcap
entries that have been found in the database and
is listed in sorted order.
The
<emphasis >All</emphasis>
are entries that correspond to actual queues or printers
and are listed in the order that they appear in the printcap or
according to an order specified by the system administrator.
(See the

<link linkend="allpc"><literal/all/ Printcap Entry</link>

for details.)</para>

<sect2><title>Printcap Processing Format</title>

<para>Queue or printer names must start with an alphanumeric character,
and contain only alphanumerics,  hyphens (<literal>-</literal>) and underscores (<literal>_</literal>).
To avoid known and nasty problems with sending and receiving print jobs from
case sensitive and case insensitive systems,
&LPRng; brutally lowercases all printcap entry names and printer names.</para>

<para>The printcap file is processed by reading it line by line and
composing the individual printcap entries.
Each entry has an name and one or more aliases.
The entries in the printcap assign values to options.
These can have the format:
<informalexample>
<screen>option=string value \n with escapes
flag         # equivalent to flag=1
flag@        # equivalent to flag=0
option#value # equivalent to option=value</screen>
</informalexample>
</para>

<para>An option will have the last value that occurs in the printcap entry.</para>

<para>Our example shows the use of the
<literal remap=tt>tc</literal>
(<emphasis>t</emphasis>ermcap in<emphasis>c</emphasis>lude)
facility.
The
<literal remap=tt>:tc</literal>
value is a list of printcap entries that should be prefixed
to the <emphasis>start</emphasis>
of the printcap entry in which it appears.
This allows options to be set in the printcap entry which will
override the values in the
<literal remap=tt>:tc</literal>
included entry.
For convenience,
the options are displayed in sorted order.</para>

<para>The &LPRng; clients and <application>lpd</application> server may require a different set of
options for the same spool queue.
The clients require options whose values tell the clients how the
contact the <application>lpd</application> server and transfer a print job or query to it.
The <application>lpd</application> server needs options that tell it how to either print
a job or forward it to another <application>lpd</application> server.
The
<literal remap=tt>:client</literal>
or
<literal remap=tt>:server</literal>
option
marks a printcap entry as for client or <application>lpd</application> server use only;
unmarked entries are used by both server and client.
The
<command remap=tt>lpc client</command>
command shows the printcap information that  the
&LPRng; clients would use.
For example, here is what the <application>lpd</application> server would use:
<informalexample>
<screen><prompt>h4: {216} % </prompt><userinput>lpc server all</userinput>
Config
 :lpd_port=2000
 :printcap_path=/var/tmp/LPD/printcap

Names
 :.client=.client
 :.common=.common
 :lp=lp
 :lp2=lp2
 :main=lp

All
 :lp
 :lp2

Printcap Information
lp|main
 :cm=The Main Print Queue
 :force_localhost
 :lp=/tmp/lp
 :mx=0
 :sd=/var/spool/lpd/%P
 :server
lp2
 :cm=The Second Print Queue
 :force_localhost
 :lp=/tmp/lp2
 :mx=0
 :sd=/var/spool/lpd/%P
 :server</screen>
</informalexample>
</para>

<para>When we select the
<literal remap=tt>server</literal>
printcap information,
we see that the
<literal remap=tt>:sd</literal>
option has been added,
and the
<literal remap=tt>:lp</literal>
replaced with new values.</para>

<para>We can use the
<literal remap=tt>:oh</literal>
(<literal remap=bf>o</literal>n this <literal remap=bf>h</literal>ost)
option to mark printcap entries for use by a selected set of hosts.
For example:
<informalexample>
<screen>lp:oh=10.0.0.0/255.255.255.0,*.private,!10.0.0.10
  :lp=%P@10.0.0.10</screen>
</informalexample>
</para>

<para>The
<literal remap=tt>:oh</literal>
option takes a list of IP addresses and masks or
glob patterns,
and applies these to the IP addresses or list of Fully Qualified
Domain Names for the current host.
If there is a for at least one IP address or pattern
in the list match then the entry is used.
An exclamation mark (<literal remap=tt>!</literal>) inverts the sense of the match,
and the entry is used if the match fails.</para>

<para>Finally,
we can use the
<literal remap=tt>wildcard</literal>
facility to cause a default printcap
entry to be used:
<informalexample>
<screen>lp|*:cm=Wildcard Alias - %P=lp, %Q=wanted
  :lp=%P@10.0.0.10
*:cm=Wildcard Name- %P=wanted, %Q=wanted
  :lp=%P@10.0.0.10</screen>
</informalexample>
</para>

<para>The &LPRng; software first searches the printcap information for
an exact match.
If none is found then it searches for the first wildcard entry that matches
the printer name.
If the wildcard is used as an alias,
then the printcap entry is simply selected for use,
with the printer name and queue name selected as shown above.
We can also use partial matching as well:
<informalexample>
<screen>lp|lp_*
  :lp=%P@10.0.0.10
qt|qt_*
  :lp=%P@10.0.0.12</screen>
</informalexample>
</para>
<para>
In the example above the first entry
matches <literal/lp/ and all printer names starting with <literal/lp_/,
while the second entry
matches <literal/qt/ and all printer names starting with <literal/qt_/.
This can be useful when setting up a family of spool queues as discussed in
later sections.
</para>

</sect2>

<sect2><title>Printcap Information From Programs and Databases</title>

<para>There many administrators store system information on a
database server
and having programs or utilities get their configuration information
from this server.
The use of the database allows easier system administration and
also centralizes the administration.
Rather than build in the various types of database access,
the LPRng software allows the use of programs to obtain printcap
information.
This not only allows any type of database to be used,
but also removes any legal or license restrictions on the redistribution
of the actual software.</para>

<para>We will use very simple example to show how you can use a program
to get printcap information.
First,
you must configure the &LPRng; software to use a program to get the
filter information.
This is done by setting a value in the <filename>lpc.conf</filename> file
(usually <filename>/etc/lpd.conf</filename> or <filename>/usr/local/etc/lpd.conf</filename>).
Copy your <filename>lpd.conf</filename> file to <filename>lpd.conf.bak</filename> and then
add the following line to the end of the file:
<informalexample>
<screen>printcap_path=|/tmp/getpc

<prompt>h4: {217} % </prompt><userinput>cd /etc</userinput>
<prompt>h4: {218} % </prompt><userinput>cp lpd.conf lpd.conf.bak</userinput>
<prompt>h4: {219} % </prompt><userinput>echo 'printcap_path=|/tmp/getpc' &gt;&gt;lpd.conf</userinput></screen>
</informalexample>
</para>

<para>Next,
edit the <filename>/tmp/getpc</filename>
file and set its values as shown below.
<informalexample>
<screen>set /tmp/getpc:

  #!/bin/sh
  # /tmp/getpc
  echo PROG $0 "$@" &gt;&gt;/tmp/trace
  cat &gt;&gt;/tmp/trace
  cat &lt;&lt;EOF
  lp:lp=test@host
  EOF
  exit 0

<prompt>h4: {220} % </prompt><userinput>chmod 755 /tmp/getpc</userinput>
<prompt>h4: {221} % </prompt><userinput>echo testing | /tmp/getpc -aoption</userinput>
lp:lp=test@host
<prompt>h4: {222} % </prompt><userinput>cat /tmp/trace</userinput>
PROG /tmp/getpc -aoption
testing</screen>
</informalexample>
</para>

<para>After you have tested the
<application remap=tt>getpc</application>
script,
use the
<command remap=tt>lpc client all</command>
command:
<informalexample>
<screen><prompt>h4: {223} % </prompt><userinput>lpc client all</userinput>
Config
 :lockfile=/var/tmp/LPD/lpd
 :lpd_port=2000
 :printcap_path=|/tmp/getpc

Names
 :lp=lp

All
 :lp

Printcap Information
lp
 :lp=test@host
<prompt>h4: {224} % </prompt><userinput>cat /tmp/trace</userinput>
<prompt>h4: {225} % </prompt><userinput>cat /tmp/trace</userinput>
PROG /tmp/getpc -Pall -aacct -l66 -sstatus \
  -t2000-05-05-08:40:51.000 -w80 -x0 -y0 acct
all
PROG /tmp/getpc -Pall -aacct -l66 -sstatus \
  -t2000-05-05-08:40:51.000 -w80 -x0 -y0 acct
*</screen>
</informalexample>
</para>

<para>As seen from the <filename>/tmp/trace</filename> file,
the
<command remap=tt>getpc</command>
program is invoked with the standard filter
parameters.
The <literal>-P</literal> command line literal is set to the name of the printcap
entry and the name of the entry is written to
the filter's <acronym/STDIN/.
If the entry is not found,
then the wildcard printcap entry will be requested.
The <literal>-P</literal> literal is <emphasis>not</emphasis>
set to
<literal remap=tt>*</literal>,
as this has the possibility of opening a security loophole
when a shell script parses the filter's command line options.</para>

<para>You restore the original <filename>lpd.conf</filename>
file to restore the system to normal operation.
<informalexample>
<screen><prompt>h4: {226} % </prompt><userinput>cd /etc</userinput>
<prompt>h4: {227} % </prompt><userinput>cp lpd.conf.bak lpd.conf</userinput></screen>
</informalexample>
</para>

<para>When using the program method to return information,
special consideration should be given to the
<literal remap=tt>all</literal>
request.
If there is not an explicit
<literal remap=tt>all</literal>
entry,
then the program should take appropriate steps to enumerate the
values in the database,
or report that there is a missing
<literal remap=tt>all</literal>
entry to the appropriate
administrative authority.</para>

</sect2>
<sect2><title>User Printcap Information</title>
<para>
In addition to the system printcap,
each user can define a private printcap file that will be read
after the system printcap.
Users can define &LPRng; client entries and can augment the system
printcap information.
</para>

<para>
By default, <filename>${HOME}/.printcap</filename> is the
the user printcap file.
Here is a simple example of a user printcap file.
</para>

<informalexample>
<screen># remote printer - default
lp:lp=raw@localhost
  :ifhp=model=laserjet4
  :filter=/usr/local/libexec/filters/ifhp
# direct connection to printer over TCP/IP connection
lp:lp=10.0.0.5%9100
  :direct
  :ifhp=model=phaser
  :filter=/usr/local/libexec/filters/ifhp
</screen>
</informalexample>

<para>
The two examples show how a simple printer definition can be created.
The first example shows how to create a simple way to send a file directly
to a remote print queue after passing it through a filter.
This is usually called <emphasis/client side/ filtering.
</para>

<para>
The second example is more interesting.
Here we do the same thing,  but we open a connection to the remote
port on a host and send the print job.
We do not spool the print job but send it directly.
This is called <emphasis/lightweight lpr/ printing.
</para>

<para>
While the user printcap file is read after the system printcap file,
the order of printcap entries is modified so that any entries that
appeared in the user printcap file will appear before entries in the
system printcap file.
This allows users to  modify the order in which printer entries are
displayed.
</para>
</sect2>

</sect1>

<sect1><title>Banner Printing and the OF filter</title>

<para>Banner or header pages can be printed at the beginning,
end, or both at the beginning or end.
The following flags control how and where banners are printed.
These flags are listed in order of precedence.
<variablelist>
<varlistentry><term> <literal>:sh</literal>
</term>
<listitem>
<para>Suppress all banner printing or header pages.
This prevents any banners from being generated by the <application>lpd</application>
print spooler and if present in a client printcap entry,
will cause <application>lpr</application> not to put any banner printing information in the
control file.
Even if this flag is not present,
then &LPRng; will not print a banner unless a banner printing
program is specified with the
<literal remap=tt>:bp,</literal>
<literal remap=tt>:bs</literal>,
<literal remap=tt>:be</literal>,
or
<literal remap=tt>:sb</literal>
option.</para>

</listitem>
</varlistentry>

<varlistentry><term>
<literal remap=tt>:ab</literal>
</term>

<listitem>
<para>Print a banner or header page,
even if the user has not requested one.
The
<literal remap=tt>:sh</literal>
option has precedence over the
<literal remap=tt>:ab</literal>
option.</para>

</listitem>
</varlistentry>

<varlistentry><term>
<literal remap=tt>:hl</literal>
</term>

<listitem>
<para>The banner (header) is at the end (last page) of the job.</para>

</listitem>
</varlistentry>

<varlistentry><term> <literal>:bs=/... </literal> and  <literal>:be=/... </literal> and  </term>

<listitem>
<para>The
<literal remap=tt>:bs</literal>
and
<literal remap=tt>:be</literal>
options specify that a banner page
is to be generated at the start and end of the job respectively,
using indicated filter program.
If the
<literal remap=tt>:hl</literal>
flag has been set,
only the
<literal remap=tt>:be</literal>
will be used.</para>

</listitem>
</varlistentry>

<varlistentry><term> <literal>:bp=</literal>... </term>

<listitem>
<para>If there is no
<literal remap=tt>:bs</literal>
or
<literal remap=tt>:be</literal>
value when printing
a banner at the start or end of the job respectively,
then use the indicated filter program to generate a banner.</para>

</listitem>
</varlistentry>

<varlistentry><term>
<literal remap=tt>:sb</literal>
and <literal>:bl=....</literal></term>

<listitem>
<para>If there is no program specified to generate the banner
and the
<literal remap=tt>:sb</literal>
flag is set,
send the
<literal remap=tt>:bl</literal>
 (banner line) string
to the printer.</para>

</listitem>
</varlistentry>

<varlistentry><term> <literal>:of=filter</literal> </term>

<listitem>
<para>A filter used to process banners and other non-job file information.</para>

</listitem>
</varlistentry>

<varlistentry><term>
<literal remap=tt>:suspend_of_filter</literal>
</term>

<listitem>
<para>Controls whether the
<literal remap=tt>:of</literal>
filter is suspended or has its input terminated.</para>

</listitem>
</varlistentry>
</variablelist>
</para>

<para>The
<application remap=tt>pclbanner</application>,
<application remap=tt>psbanner</application>,
and
<application remap=tt>lpbanner</application>
programs are part of the &LPRng; distribution and are usually
installed in the same location as the &LPRng; supported filters.
They produce a PCL,
PostScript,
or text banner respectively.</para>

<para>The OF filter (<literal>:of=/path</literal>)
is used to process banner pages and to do any necessary
setup to initialize the printer to handle banner pages.
This filter has the following unusual  behavior:
<itemizedlist>

<listitem>
<para>It must be explicitly specified in the printcap file.
It is not run by default.
</para>

<para>If specified,
it is started at the beginning of job printing
and stays present throughout the entire job printing session.</para>
</listitem>

<listitem>
<para>When printing individual files,
the
<literal remap=tt>:of</literal>
filter is sent a special <emphasis>suspend yourself</emphasis>
two character string,
<literal remap=tt>\031\001</literal>.
This will cause the
<literal remap=tt>:of</literal>
filter to send itself a <acronym>SIGSUSP</acronym>
(suspend) signal.</para>
</listitem>

<listitem>
<para>The
<literal remap=tt>:of</literal>
filter is restarted when any information not part of a
print job file,
such as the
initialization string (<literal remap=tt>:ld</literal>
option),
termination string (<literal remap=tt>:ld</literal>
option),
or form feeds at
start (<literal remap=tt>:fo</literal>),
end (<literal remap=tt>:fq</literal>),
and between job files (<literal remap=tt>:ff_separator</literal>),
and when banners
are generated by the
<literal remap=tt>:bp</literal>,
<literal remap=tt>:bs</literal>,
<literal remap=tt>:be</literal>,
or
<literal remap=tt>:sb</literal>
option and need to be sent to the printer.</para>
</listitem>

</itemizedlist>
</para>

<para>This rather baroque behavior is mostly historical in origin,
and is very much embedded in the existing documentation and methodologies
of the BSD print spooling system.
Originally,
when a printer port was opened,
a special device initialization string was sent by the printer port device
driver;
this usually resulted in an extra page of paper being ejected by the printer.
By opening the device once and then keeping it open,
the print spooler would avoid the wasted paper.
The reason for suspending the
<literal remap=tt>:of</literal>
filter was simply to save the overhead
of creating an extra processes.</para>

<para>Unfortunately,
the
<literal remap=tt>:of</literal>
filter suspension behavior is now a problem rather than
a benefit.
For example,
for many devices to finish printing a page correctly the filter
must be closed in order for it to flush buffers and for the low level
drivers to properly finish.
In order to provide this functionality,
the <literal>suspend_of_filter@</literal> flag can be used.
This will cause the <application>lpd</application> server to close the
<literal remap=tt>:of</literal>
filters input,
rather than sending it the suspend string,
and to restart a new
<literal remap=tt>:of</literal>
filter process when necessary.</para>

</sect1>

<sect1><title>Printing from <application/lpr/ Directly To A Device</title>
<para>
While the most reliable way to print is to send jobs to a print spooler,
sometimes it is desirable to print directly to a printer.
This method is supported by the special
<literal/:direct/
printcap flag or the
<application/lpr/
<literal/-Y/ command line flag.
The following shows the effects of this flag:
</para>
<informalexample>
<screen>lpr -Y -Phost%port file1 file2 ...
Eqivalent to:
  ( for i in file1 file2 ... ; do
    $filter $i
  done ) | tcpip_connection( host%port)

lpr -Y -P/dev/lp file1 file2 ...
Eqivalent to:
  ( for i in file1 file2 ... ; do
    $filter $i
  done ) >>/dev/lp

lpr -Y -P '|/program' file1 file2 ...
Eqivalent to:
  ( for i in file1 file2 ... ; do
    $filter $i
  done ) | /program
</screen>
</informalexample>
<para>
The above examples show how we can use the command line
options to send files directly to a printer.
You can also create a printcap that will do the same:
</para>
<informalexample>
<screen>lp:direct:lp=/tmp/a:remote_support=R
Command:
  lpr -Plp file1 file2 ...
Equivalent to:
lpr -P/tmp/a -Y file1 file2 ...

Example:
<prompt>h4: {228} % </prompt><userinput>lp -P/tmp/a /tmp/hi</userinput>
<prompt>h4: {229} % </prompt><userinput>cat /tmp/a /tmp/hi</userinput>
hi
<prompt>h4: {230} % </prompt><userinput>lp -Plp /tmp/hi</userinput>
<prompt>h4: {231} % </prompt><userinput>cat /tmp/a /tmp/hi</userinput>
hi
hi
</screen>
</informalexample>
<para>
The <application/lpr/ <literal/-X filter/ option allows us to specify
a user filter on the command line.
We will use a simple example to show how this capability could be used
in practice.
Create the <filename>/tmp/pass</filename> file with the
following contents, and give it executable permissions as shown
below:
<informalexample>
<screen>#!/bin/sh
# /tmp/pass file
echo LEADER
cat
echo TRAILER
exit 0
</screen>
</informalexample>
</para>
<para>
Execute the following commands
to print the <filename>/tmp/hi</filename> file
and observe the results:
<informalexample><screen><prompt>h4: {232} % </prompt><userinput>cp /dev/null /tmp/a</userinput>
<prompt>h4: {233} % </prompt><userinput>lpr -P/tmp/a -X /tmp/pass /tmp/hi</userinput>
<prompt>h4: {234} % </prompt><userinput>cat /tmp/a</userinput>
LEADER
hi
TRAILER
</screen>
</informalexample>
</para>
<para>
As we see from the example,
our filter has processed the input file and added the <literal/LEADER/
and <literal/TRAILER/ strings.
In practice,
the actual processing of the input job would be far more elaborate,
and may do such things as incorporate files or other material
available only on the local system.
We can also use a printcap entry:
</para>
<informalexample><screen>lp:direct:lp=/tmp/a:filter=/tmp/pass

<prompt>h4: {235} % </prompt><userinput>cp /dev/null /tmp/a</userinput>
<prompt>h4: {236} % </prompt><userinput>lpr -Plp /tmp/hi</userinput>
<prompt>h4: {237} % </prompt><userinput>cat /tmp/a</userinput>
LEADER
hi
TRAILER
</screen>
</informalexample>
</sect1>

<sect1><title>Moving Jobs From Queue to Queue and Redirecting Queues</title>

<para>The
<command remap=tt>lpc move</command>
command is used to move jobs in one queue to another queue on
an individual basis,
while the
<command remap=tt>lpc redirect</command>
command redirects all incoming jobs
to a new queue.
Edit the printcap file so it has contents indicated below,
use <command>checkpc -f</command> to check the printcap,
and then use
<command remap=tt>lpc reread</command>
to restart the <application>lpd</application> server.
<informalexample>
<screen>lp:force_localhost
lp:server
  :sd=/var/spool/lpd/%P
  :lp=lp2@localhost
lp2:force_localhost
lp2:server
  :sd=/var/spool/lpd/%P
  :lp=/tmp/lp2</screen>
</informalexample>

Execute the following commands
to print the <filename>/tmp/hi</filename> file
and observe the results:
<informalexample>
<screen><prompt>h4: {238} % </prompt><userinput>lpc stop lp lp2</userinput>
Printer: lp@h4
lp@h4.private: stopped
Printer: lp2@h4
lp2@h4.private: stopped
<prompt>h4: {239} % </prompt><userinput>lpr /tmp/hi</userinput>
<prompt>h4: {240} % </prompt><userinput>lpq -a</userinput>
Printer: lp@h4  (printing disabled)
 Queue: 1 printable job
 Server: no server active
 Rank   Owner/ID           Class Job Files      Size Time
1      papowell@h4+659       A   659 /tmp/hi       3 08:04:03
Printer: lp2@h4  (printing disabled)
 Queue: no printable jobs in queue
<prompt>h4: {241} % </prompt><userinput>lpc move lp papowell lp2</userinput>
Printer: lp@h4
lp: selected 'papowell@h4+659'
lp@h4.private: move done
<prompt>h4: {242} % </prompt><userinput>lpq -a</userinput>
Printer: lp@h4  (printing disabled)
 Queue: no printable jobs in queue
 Status: job 'papowell@h4+659' removed at 08:19:24.962
Printer: lp2@h4  (printing disabled)
 Queue: 1 printable job
 Server: no server active
 Rank   Owner/ID           Class Job Files       Size Time
1      papowell@h4+659       A   659 /tmp/hi        3 08:19:24</screen>
</informalexample>
</para>

<para>We first stop the queues so that the jobs will remain in them.
We then use the
<command remap=tt>lpc move fromqueue id toqueue</command>
command to select a job
in the
<emphasis>fromqueue</emphasis>
and move it to the
<emphasis>toqueue</emphasis>.
A list of job numbers, job IDs, or glob patterns to match job
IDs can be used to select the job.</para>

<para>The
<command remap=tt>lpc redirect fromqueue toqueue</command>
will cause all incoming
jobs to be redirected to the specified queue.
You can execute the following commands and observe the results.
<informalexample>
<screen><prompt>h4: {243} % </prompt><userinput>lpc redirect lp lp2</userinput>
Printer: lp@h4
forwarding to 'lp2'
lp@h4.private: redirected
<prompt>h4: {244} % </prompt><userinput>lpq -a</userinput>
Printer: lp@h4  (printing disabled) (redirect lp2)
 Queue: no printable jobs in queue
Printer: lp2@h4  (printing disabled)
 Queue: no printable jobs in queue
<prompt>h4: {245} % </prompt><userinput>lpr /tmp/hi</userinput>
<prompt>h4: {246} % </prompt><userinput>lpq -a</userinput>
Printer: lp@h4  (printing disabled) (redirect lp2)
 Queue: no printable jobs in queue
 Status: job 'papowell@h4+935' removed at 09:08:21.410
Printer: lp2@h4  (printing disabled)
 Queue: 1 printable job
 Server: no server active
 Rank   Owner/ID           Class Job Files        Size Time
1      papowell@h4+935       A   935 /tmp/hi         3 09:08:21</screen>
</informalexample>
</para>

<para>To turn redirection off, use
<command remap=tt>lpc redirect queue off</command>
as shown in the example below:
<informalexample>
<screen><prompt>h4: {247} % </prompt><userinput>lpc redirect lp off</userinput>
Printer: lp@h4
forwarding off
<prompt>h4: {248} % </prompt><userinput>lpq</userinput>
Printer: lp@h4  (printing disabled)
 Queue: no printable jobs in queue
 Status: job 'papowell@h4+935' removed at 09:08:21.410</screen>
</informalexample>
</para>

</sect1>

<sect1><title>Print Job Classes, User Requested Job Priority, and Form Support</title>

<para>The &LPRng; software allows users to assign a class name to print
jobs using the <command>lpr -Cname</command> option.
This causes the <application>lpr</application> command to put the line
<literal remap=tt>Cname</literal>
in the control file.
By default,
the (upper cased) first letter of the class name is used to
assign a user requested priority to the job,
with
<literal remap=tt>A</literal>
being the default lowest priority and
<literal remap=tt>Z</literal>
being the highest.</para>

<para>The <literal>ignore_requested_user_priority</literal>
printcap option can be used to ignore the user requested priority
and jobs will be printed in the normal first-in first-out order.</para>

<para>&LPRng; also makes use of the class information to do form support
and restrict printing to a specific set of classes.
By default the job class information is ignored,
but the
<command remap=tt>lpc class</command>
command can be used to specify one or
more classes (actually glob patterns) to be printed.
This facility can be used to do support printing of jobs that
require a specific form setup.
Here is a simple example of how to use this facility.</para>

<para>Edit the printcap file so it has contents indicated below,
use <command>checkpc -f</command> to check the printcap,
and then use
<command remap=tt>lpc reread</command>
to restart the <application>lpd</application> server.
<informalexample>
<screen>lp:force_localhost
lp:server
  :sd=/var/spool/lpd/%P
  :lp=lp2@localhost
lp2:force_localhost
lp2:server
  :sd=/var/spool/lpd/%P
  :lp=/tmp/lp2</screen>
</informalexample>

Execute the following commands
to print the <filename>/tmp/hi</filename> file
and observe the results:
<informalexample>
<screen><prompt>h4: {249} % </prompt><userinput>lpc class lp red</userinput>
Printer: lp@h4
classes printed 'red'
lp@h4.private: class updated
<prompt>h4: {250} % </prompt><userinput>lpq</userinput>
Printer: lp@h4  (classes red)
 Queue: no printable jobs in queue
<prompt>h4: {251} % </prompt><userinput>lpr /tmp/hi</userinput>
<prompt>h4: {252} % </prompt><userinput>lpq</userinput>
Printer: lp@h4  (classes red)
 Queue: no printable jobs in queue
 Holding: 1 held jobs in queue
 Server: no server active
 Rank   Owner/ID           Class Job Files        Size Time
holdclass papowell@h4+82     A    82 /tmp/hi         3 09:29:52
<prompt>h4: {253} % </prompt><userinput>lpr -Cred /tmp/hi</userinput>
<prompt>h4: {254} % </prompt><userinput>lpq</userinput>
Printer: lp@h4  (classes red)
 Queue: no printable jobs in queue
 Holding: 1 held jobs in queue
 Server: no server active
 Status: job 'papowell@h4+89' removed at 09:30:13.569
 Rank   Owner/ID           Class Job Files        Size Time
holdclass papowell@h4+82     A    82 /tmp/hi         3 09:29:52</screen>
</informalexample>
</para>

<para>As seen in the example,
we set the queue class to
<literal remap=tt>red</literal>,
and then sent a (default) class
<literal remap=tt>A</literal>
job to the printer.
It was not printed, and is listed with
<literal remap=tt>holdclass</literal>
status.
We sent another job which was immediately printed.</para>

<para>We can change the print queue class at any time,
and then new class will then control what jobs are printed.
To disable the class selection, use the
<command remap=tt>lpc class queue off</command>
command.
<informalexample>
<screen><prompt>h4: {255} % </prompt><userinput>lpc class lp off</userinput>
Printer: lp@h4
all classes printed
lp@h4.private: class updated</screen>
</informalexample>
</para>

</sect1>

<sect1><title>Holding and Releasing Jobs</title>

<para>The &LPRng; software has a wide range of facilities to hold or
temporarily prevent jobs from printing.
Jobs can be individually held
or all jobs submitted to a queue can be held
until released by an operator.
Some administrators use the
<command remap=tt>holdall</command>
facility and a
<application remap=tt>cron</application>
script to cause jobs to be printed
at specific times.
The
<command remap=tt>lpc holdall</command>
command causes all jobs submitted
to a queue to be held until released with the
<command remap=tt>lpc release</command>
command.
The
<command remap=tt>lpc noholdall</command>
command disables the
<command remap=tt>holdall</command>
operation.</para>

<para>Edit the printcap file so it has contents indicated below,
use <command>checkpc -f</command> to check the printcap,
and then use
<command remap=tt>lpc reread</command>
to restart the <application>lpd</application> server.
<informalexample>
<screen>lp:force_localhost
lp:server
  :sd=/var/spool/lpd/%P
  :lp=lp2@localhost
lp2:force_localhost
lp2:server
  :sd=/var/spool/lpd/%P
  :lp=/tmp/lp2</screen>
</informalexample>
</para>

<para>Execute the following commands
to print the <filename>/tmp/hi</filename> file
and observe the results:
<informalexample>
<screen><prompt>h4: {256} % </prompt><userinput>lpc holdall lp</userinput>
Printer: lp@h4
lp@h4.private: holdall on
<prompt>h4: {257} % </prompt><userinput>lpq</userinput>
Printer: lp@h4  (holdall)
 Queue: no printable jobs in queue
<prompt>h4: {258} % </prompt><userinput>lpr /tmp/hi</userinput>
<prompt>h4: {259} % </prompt><userinput>lpq</userinput>
Printer: lp@h4  (holdall)
 Queue: no printable jobs in queue
 Holding: 1 held jobs in queue
 Server: no server active
 Rank   Owner/ID           Class Job Files        Size Time
hold   papowell@h4+213       A   213 /tmp/hi         3 09:45:05
<prompt>h4: {260} % </prompt><userinput>lpc release lp 213</userinput>
Printer: lp@h4
lp: selected 'papowell@h4+213'
lp@h4.private: started
<prompt>h4: {261} % </prompt><userinput>lpq</userinput>
Printer: lp@h4  (holdall)
 Queue: no printable jobs in queue
 Status: job 'papowell@h4+213' removed at 09:45:22.570</screen>
</informalexample>
</para>

<para>The
<command remap=tt>lpc holdall</command>
command causes all jobs to be held.
We spool a job,
and then use the
<command remap=tt>lpc release</command>
command to release the selected job.
We disable the
<command remap=tt>holdall</command>
operation using the
<command remap=tt>lpc noholdall</command>
command.
<informalexample>
<screen><prompt>h4: {262} % </prompt><userinput>lpc noholdall lp</userinput>
Printer: lp@h4
lp@h4.private: holdall off</screen>
</informalexample>
</para>

<para>You can also use the
<command remap=tt>lpc hold</command>
command
to select individual jobs in a spool queue to be held.
This command is useful if there is a set of jobs which require
special handling or printing at a later date.
The following example shows how this command is used.
We use the
<command remap=tt>lpc stop</command>
and
<command remap=tt>lpc start</command>
commands
to simulate the normal delays in print spooling operations.
<informalexample>
<screen><prompt>h4: {263} % </prompt><userinput>lpc stop lp</userinput>
Printer: lp@h4
lp@h4.private: stopped
<prompt>h4: {264} % </prompt><userinput>lpq</userinput>
Printer: lp@h4  (printing disabled)
 Queue: no printable jobs in queue
 Status: job 'papowell@h4+495' removed at 10:10:50.629
<prompt>h4: {265} % </prompt><userinput>lpr /tmp/hi</userinput>
<prompt>h4: {266} % </prompt><userinput>lpr /tmp/hi</userinput>
<prompt>h4: {267} % </prompt><userinput>lpq</userinput>
Printer: lp@h4  (printing disabled)
 Queue: 2 printable jobs
 Server: no server active
 Rank   Owner/ID           Class Job Files        Size Time
1      papowell@h4+459       A   459 /tmp/hi         3 10:40:32
2      papowell@h4+461       A   461 /tmp/hi         3 10:40:34
<prompt>h4: {268} % </prompt><userinput>lpc hold lp 459</userinput>
Printer: lp@h4
lp: selected 'papowell@h4+459'
lp@h4.private: updated
<prompt>h4: {269} % </prompt><userinput>lpq</userinput>
Printer: lp@h4  (printing disabled)
 Queue: 1 printable job
 Holding: 1 held jobs in queue
 Server: no server active
 Rank   Owner/ID           Class Job Files        Size Time
1      papowell@h4+461       A   461 /tmp/hi         3 10:40:34
hold   papowell@h4+459       A   459 /tmp/hi         3 10:40:32</screen>
</informalexample>
</para>

<para>In the next example
we show how to use the
<command remap=tt>lpc hold</command>
command to select and hold an individual job.
Then we start the queue and see what happens:
<informalexample>
<screen><prompt>h4: {270} % </prompt><userinput>lpc start</userinput>
Printer: lp@h4
lp@h4.private: started
<prompt>h4: {271} % </prompt><userinput>lpq</userinput>
Printer: lp@h4
 Queue: no printable jobs in queue
 Holding: 1 held jobs in queue
 Server: no server active
 Status: job 'papowell@h4+461' removed at 10:41:24.873
 Rank   Owner/ID           Class Job Files        Size Time
hold   papowell@h4+459       A   459 /tmp/hi         3 10:40:32
<prompt>h4: {272} % </prompt><userinput>lpc release lp 459</userinput>
Printer: lp@h4
lp: selected 'papowell@h4+459'
lp@h4.private: started
<prompt>h4: {273} % </prompt><userinput>lpq</userinput>
Printer: lp@h4
 Queue: no printable jobs in queue
 Status: job 'papowell@h4+459' removed at 10:41:39.457</screen>
</informalexample>
</para>

<para>As we see,
the held job is not printed until we release it,
and then is processed normally.</para>

<para>The printcap
<literal remap=tt>:ah</literal>
(autohold)
option has the same effect as the
<command remap=tt>lpc holdall</command>
command
but its actions cannot be disabled by the
<command remap=tt>lpc noholdall</command>
command.</para>

</sect1>

<sect1><title>Load Balance Queues and Printer Pools</title>

<para>A Load Balance Queue provides a way to use multiple printers
for a single print queue.
All jobs are normally sent to the main or
load balance queue
which then dispatches the jobs to
<emphasis>server</emphasis>
queues or printers that do the actual printing
as they become available.
You can also send jobs to the individual server printers
if they have special processing or setups required for a particular
job.
Because all of the server printers are shared by the
load balance queue,
they are said to be in a <emphasis>printer pool</emphasis>.</para>

<para>Edit the printcap file so it have the contents indicated below,
create the <filename>/tmp/lp2</filename> and <filename>/tmp/lp3</filename>
files with
<literal remap=tt>0777</literal>
permissions,
use <command>checkpc -f</command> to check the printcap,
and then use
<command remap=tt>lpc reread</command>
to restart the <application>lpd</application> server.
<informalexample>
<screen># printcap
lp:force_localhost
lp:server
  :sd=/var/spool/lpd/%P
  :sv=lp2,lp3
lp2:force_localhost
lp2:server
  :ss=lp
  :sd=/var/spool/lpd/%P
  :lp=/tmp/lp2
lp3:force_localhost
lp3:server
  :ss=lp
  :sd=/var/spool/lpd/%P
  :lp=/tmp/lp2</screen>
</informalexample>
</para>

<para>The <literal>:sv=...</literal> option flags the queue as a load balance queue
and lists the queues that are used for load balancing.
The <literal>:ss=...</literal> option flags the queue as a server for a load balance
queue and specifies the name of the load balance queue.
When a job is sent to the load balance queue
the <application>lpd</application> server checks to see which server queues are available
and then the first one to become available.</para>

<para>Execute the following commands
to print the <filename>/tmp/hi</filename> file
and observe the results:
<informalexample>
<screen><prompt>h4: {274} % </prompt><userinput>lpq</userinput>
Printer: lp@h4  (subservers lp2, lp3)
 Queue: no printable jobs in queue
 Status: job 'papowell@h4+42' removed at 07:29:57.924
Server Printer: lp2@h4  (serving lp)
 Queue: no printable jobs in queue
Server Printer: lp3@h4  (serving lp)
 Queue: no printable jobs in queue
<prompt>h4: {275} % </prompt><userinput>lpr /tmp/hi</userinput>
<prompt>h4: {276} % </prompt><userinput>lpq</userinput>
Printer: lp@h4  (subservers lp2, lp3)
 Queue: 1 printable job
 Server: pid 4063 active
 Status: waiting for subserver to finish at 07:31:08.074
 Rank   Owner/ID           Class Job Files        Size Time
1      papowell@h4+62        A    62 /tmp/hi         3 07:31:07
Server Printer: lp2@h4  (serving lp)
 Queue: no printable jobs in queue
Server Printer: lp3@h4  (serving lp)
 Queue: no printable jobs in queue
<prompt>h4: {277} % </prompt><userinput>lpq</userinput>
Printer: lp@h4  (subservers lp2, lp3)
 Queue: no printable jobs in queue
 Status: no more jobs to process in load balance queue at 07:31:12.317
Server Printer: lp2@h4  (serving lp)
 Queue: no printable jobs in queue
Server Printer: lp3@h4  (serving lp)
 Queue: no printable jobs in queue
 Status: job 'papowell@h4+62' removed at 07:31:10.311</screen>
</informalexample>
</para>

<para>The first <application>lpq</application> command shows how the status is displayed for a load balance
queue - the queue and its server queues are shown as well.
Next,
we use <application>lpr</application> to print a job (job id <literal>papowell@h4+62</literal>).
We then use a couple of <application>lpq</application> commands to see how the job is first
sent to the
<literal remap=tt>lp</literal>
queue,
which then forwards it to the
<literal remap=tt>lp3</literal>
queue,
which then processes it and removes it.
(For purposes of demonstration we have artificially slowed down the operation
of the load balance queue so that the jobs will remain in the queue
for sufficient time for us to display their status.)
We can send another job to the load balance queue:
<informalexample>
<screen><prompt>h4: {278} % </prompt><userinput>lpr /tmp/hi</userinput>
<prompt>h4: {279} % </prompt><userinput>lpq</userinput>
Printer: lp@h4  (subservers lp2, lp3)
 Queue: no printable jobs in queue
 Status: no more jobs to process in load balance queue at 07:37:17.953
Server Printer: lp2@h4  (serving lp)
 Queue: no printable jobs in queue
 Status: job 'papowell@h4+89' removed at 07:37:15.936
Server Printer: lp3@h4  (serving lp)
 Queue: no printable jobs in queue
 Status: job 'papowell@h4+81' removed at 07:36:40.116</screen>
</informalexample>
</para>

<para>This time we see that the job was put in
<literal remap=tt>lp2</literal>.
The normal load balance queue operation is to use the server queues in round robin order.</para>

<para>While this simple configuration is suitable for a large number of
configurations,
there are situations where server queue must be chosen
<emphasis>dynamically</emphasis>.
For example,
if the server queues are actually transferring jobs to remote clients
then as soon as the job is sent to the remote client the
queue appears empty and available for use.
To correctly check to see if the queue is available,
the status of the remote queue or destination of the server queue must
be checked.</para>

<para>To handle this situation,
a
<literal remap=tt>:chooser</literal>
program or filter can be used.
When the load balance queue is trying to decide where to send a job,
it first checks the server queues to see if they are enabled for printing.
If a
<literal remap=tt>:chooser</literal>
program is specified in the load balance queue printcap
entry,
then it is started with the normal filter options and environment variables,
supplemented as discussed below.
The
<literal remap=tt>:chooser</literal>
program will read a list of candidate queues from its
<acronym/STDIN/,
write the chosen one to its <acronym/STDOUT/,
and then exit.
The <application>lpd</application> server checks the
<literal remap=tt>:chooser</literal>
exit code -
if it is zero (successful) then the chosen queue is used otherwise
the exit code is used for the result value of processing the job.
This allows the chooser process to not only control the destination of the job
but also to hold, remove, or abort the job handling process.
If the
<literal remap=tt>:chooser</literal>
does not specify a queue,
then the job is skipped and another job is chosen.</para>


<para>One side effect of the using a chooser program is that while there are jobs
that can be processed in the queue the <application>lpd</application> server needs to periodically
check to see if a server queue has become available.
If it did this continually then a very high load would be put on the system.
Instead,
the <literal>chooser_interval</literal> option specifies a maximum time in seconds (default 10 seconds)
between the times that the <application>lpd</application> server checks to see if there is an
available server.</para>

<para>
Normally,
the <literal/chooser/ is applied to the first job in the queue.
If the job cannot be printed then <application/lpd/ will wait
for the <literal>chooser_interval</literal> time.
However,
the chooser can also be used to direct jobs by their characteristics,
or other criteria.  This means that the entire spool spool queue has to be scanned
for work.
If the <literal/:chooser_scan_queue/ flag is set to 1,
then all of the jobs are tested to see if they can be sent to an appropriate destination.
</para>

<para>Edit the printcap file so it have the contents indicated below,
create the <filename>/tmp/lp2</filename> and <filename>/tmp/lp3</filename>
files with
<literal remap=tt>0777</literal>
permissions.
Then create the <filename>/tmp/chooser.script</filename>
with the contents indicated below,
and give it
<literal remap=tt>0755</literal>
(executable) permissions.
Make sure that the path to the
<application remap=tt>head</application>
program used in
<filename>chooser.script</filename> is correct.
Use <command>checkpc -f</command> to check the printcap,
and then use
<command remap=tt>lpc reread</command>
to restart the <application>lpd</application> server.
<informalexample>
<screen># printcap
lp:force_localhost
lp:server
  :sd=/var/spool/lpd/%P
  :sv=lp2,lp3
  :chooser=/tmp/chooser.script
lp2:force_localhost
lp2:server
  :ss=lp
  :sd=/var/spool/lpd/%P
  :lp=/tmp/lp2
lp3:force_localhost
lp3:server
  :ss=lp
  :sd=/var/spool/lpd/%P
  :lp=/tmp/lp2

# /tmp/chooser.script

#!/bin/sh
echo CHOOSER $0 $@ &gt;&gt;/tmp/chooser
set &gt;&gt;/tmp/chooser
/usr/bin/head -1
exit 0</screen>
</informalexample>
</para>

<para>Now run the following commands:
<informalexample>
<screen><prompt>h4: {280} % </prompt><userinput>lpr /tmp/hi</userinput>
<prompt>h4: {281} % </prompt><userinput>lpq -lll</userinput>
Printer: lp@h4  (subservers lp2, lp3)
 Queue: no printable jobs in queue
 Status: CHOOSER selected 'lp3' at 14:02:50.605
 Status: transferring 'papowell@h4+178' 
      to subserver 'lp3' at 14:02:50.614
 Status: transfer 'papowell@h4+178' 
      to subserver 'lp3' finished at 14:02:50.624
 Status: job 'papowell@h4+178' removed at 14:02:50.632
 Status: starting subserver 'lp3' at 14:02:50.632
 Status: waiting for server queue process to exit at 14:02:50.651
 Status: subserver pid 10182 exit status 'JSUCC' at 14:02:52.872
 Status: no more jobs to process in load balance queue at 14:02:52.879
Server Printer: lp2@h4  (serving lp)
 Queue: no printable jobs in queue
Server Printer: lp3@h4  (serving lp)
 Queue: no printable jobs in queue
 Status: waiting for subserver to exit at 14:02:50.748
 Status: subserver pid 10183 starting at 14:02:50.820
 Status: accounting at start at 14:02:50.821
 Status: opening device '/tmp/lp3' at 14:02:50.833
 Status: printing job 'papowell@h4+178' at 14:02:50.834
 Status: processing 'dfA178h4.private', size 3, format 'f', \
      IF filter 'none - passthrough' at 14:02:50.838
 Status: printing finished at 14:02:50.839
 Status: accounting at end at 14:02:50.839
 Status: finished 'papowell@h4+178', status 'JSUCC' at 14:02:50.841
 Status: subserver pid 10183 exit status 'JSUCC' at 14:02:50.843
 Status: lp3@h4.private: job 'papowell@h4+178' printed at 14:02:50.856
 Status: job 'papowell@h4+178' removed at 14:02:50.871</screen>
</informalexample>
</para>

<para>As you see from the example above,
the <acronym>CHOOSER</acronym> selected
<literal remap=tt>lp3</literal>
for use.
Let us look at the <filename>/tmp/chooser</filename> file and see how the
<filename>chooser.script</filename> program was run:
<informalexample>
<screen>CHOOSER -Apapowell@h4+113 -CA -D2000-06-01-14:02:13.313 -Hh4.private \
   -J/tmp/hi -Lpapowell -Plp -Qlp -aacct -b3 -d/var/tmp/LPD/lp \
   -hh4.private -j113 -kcfA113h4.private -l66 -npapowell -sstatus \
   -t2000-06-01-14:02:13.379 -w80 -x0 -y0 acct
USER=papowell
LD_LIBRARY_PATH=/lib:/usr/lib:/usr/5lib:/usr/ucblib
HOME=/home/papowell
PRINTCAP_ENTRY=lp
 :chooser=/var/tmp/LPD/chooser
 :lp=/tmp/lp
 :sd=/var/tmp/LPD/lp
 :server
 :sv=lp2,lp3

lp2=change=0x0
 done_time=0x1
 held=0x0
 move=0x0
 printable=0x0
 printer=lp2
 printing_aborted=0x0
 printing_disabled=0x0
 queue_control_file=control.lp2
 server=0
 spooldir=/var/tmp/LPD/lp2
lp3=change=0x0
 done_time=0x2
 held=0x0
 move=0x0
 printable=0x0
 printer=lp3
 printing_aborted=0x0
 printing_disabled=0x0
 queue_control_file=control.lp3
 server=0
 spooldir=/var/tmp/LPD/lp3
PS1=$
OPTIND=1
PS2=&gt;
SPOOL_DIR=/var/tmp/LPD/lp
LOGNAME=papowell
CONTROL=Hh4.private
Ppapowell
J/tmp/hi
CA
Lpapowell
Apapowell@h4+113
D2000-06-01-14:02:13.313
Qlp
N/tmp/hi
fdfA113h4.private
UdfA113h4.private</screen>
</informalexample>
</para>

<para>As you can see,
the program is invoked with the same options as a normal filter.
In addition,
the printcap information for each server queue is passed in an environment
variable with the name of the server queue.
This means that if there is information needed by the chooser program to test
for the availability of hardware,
etc., this can be placed in the printcap information.</para>

<para>One of the limitations of using the
<literal remap=tt>:chooser</literal>
program is that you
may have a high overhead associated with running the program.
The &LPRng; software provides support for linking in a user provided routine
that does the same thing as the
<literal remap=tt>:chooser</literal>
program.
This routine has the following API or interface:
<informalexample>
<screen>Printcap Option: chooser_routine
   chooser_routine@  - default - do not use chooser routine
   chooser_routine   - use chooser routine

Configuration:
   configure --with-chooser_routine=name --with-user_objs=objectfile.o
      defines the CHOOSER_ROUTINE compilation option to name
      includes the objectfile.o in the library.

extern int CHOOSER_ROUTINE( struct line_list *servers,
    struct line_list *available, int *use_subserver );
  servers:    all subserver queues for this load balance queue
  available:  subserver queues to choose from
  use_subserver: chosen subserver queue
  RETURNS:
     0 - use the 'use_subserver' value as index into servers
         list for server to use
     != 0 - set job status to value returned.</screen>
</informalexample>
</para>

<para>See the <filename>LPRng/src/common/lpd_jobs.c</filename> and
<filename>LPRng/src/common/user_objs.c</filename> files
for details of the servers, available, and user_subserver
parameters.
The <filename>user_objs.c</filename> file provides a simple template that
can be used as a starting point for a more complex routine.
You should modify the code in the <filename>user_objs.c</filename> file
and then use the configure options shown above to cause the
<filename>user_objs.c</filename> file to be compiled and linked into the &LPRng; executables.</para>

</sect1>

<sect1><title>Routing Jobs To Print Queues</title>

<para>A <emphasis>routing queue</emphasis>
is similar in concept to a load balance queue
in that it transfers a job to a (different) print queue,
but the job destination is chosen at the time the job is submitted
to the queue
rather than at the time the job is
removed from the queue.
A routing queue can modify the job control file,
multiple copies of the same job can be sent to the same or different
printers,
and the job can be held,
rejected,
or processed immediately.</para>

<para>Edit the printcap file so it have the contents indicated below,
create the <filename>/tmp/lp2</filename> and <filename>/tmp/lp3</filename>
files with
<literal remap=tt>0777</literal>
permissions.
Create the <filename>/tmp/router.script</filename>
with the contents indicated below,
and give it
<literal remap=tt>0755</literal>
(executable) permissions.
Use <command>checkpc -f</command> to check the printcap,
and then use
<command remap=tt>lpc reread</command>
to restart the <application>lpd</application> server.
<informalexample>
<screen># printcap
lp:force_localhost
lp:server
  :lp=/dev/null
  :sd=/var/spool/lpd/%P
  :router=/tmp/router.script
lp2:force_localhost
lp2:server
  :sd=/var/spool/lpd/%P
  :lp=/tmp/lp2
lp3:force_localhost
lp3:server
  :sd=/var/spool/lpd/%P
  :lp=/tmp/lp2

# /tmp/router.script

#!/bin/sh
/bin/cat &lt;&lt;EOF
dest lp2
copies 2
Cred
end
dest lp3
end
EOF
exit 0</screen>
</informalexample>
</para>

<para>The <filename>router.script</filename>
will write the routing information to its <acronym/STDOUT/.
For our example,
we want the destination
<literal remap=tt>lp2</literal>
to get two copies of the job
and we want to change the class to
<literal remap=tt>red</literal>.
Now run the following commands:
<informalexample>
<screen><prompt>h4: {282} % </prompt><userinput>lpc stop all</userinput>
Printer: lp@h4
lp@h4.private: stopped
Printer: lp2@h4
lp2@h4.private: stopped
Printer: lp3@h4
lp3@h4.private: stopped
<prompt>h4: {283} % </prompt><userinput>lpq</userinput>
Printer: lp@h4 (dest lp@localhost) (printing disabled) (dest lp2, lp3)
 Queue: no printable jobs in queue
Printer: lp2@h4  (printing disabled)
 Queue: no printable jobs in queue
Printer: lp3@h4  (printing disabled)
 Queue: no printable jobs in queue
<prompt>h4: {284} % </prompt><userinput>lpr /tmp/hi</userinput>
<prompt>h4: {285} % </prompt><userinput>lpq</userinput>
Printer: lp@h4 (dest lp@localhost) (printing disabled) (dest lp2, lp3)
 Queue: 1 printable job
 Server: no server active
 Rank   Owner/ID           Class Job Files        Size Time
1      papowell@h4+235       A   235 /tmp/hi         3 16:14:22
 -          papowell@h4+235.1       -&gt;lp2 &lt;cpy 0/2&gt;
 -          papowell@h4+235.2       -&gt;lp3
Printer: lp2@h4  (printing disabled)
 Queue: no printable jobs in queue
Printer: lp3@h4  (printing disabled)
 Queue: no printable jobs in queue</screen>
</informalexample>
</para>

<para>The status reported for the spooled job indicates that the job
is routed to
<literal remap=tt>lp2</literal>,
and that two copies will be sent.
The
<literal remap=tt>:destinations</literal>
option in the printcap entry causes
<application>lpq</application> to display the contents of the specified queues.
Now execute the following commands:
<informalexample>
<screen><prompt>h4: {286} % </prompt><userinput>lpc start</userinput>
Printer: lp@h4
lp@h4.private: started
<prompt>h4: {287} % </prompt><userinput>lpq</userinput>
Printer: lp@h4 (dest lp@localhost) (destinations lp2, lp3)
 Queue: no printable jobs in queue
 Status: job 'papowell@h4+235' removed at 16:14:37.491
Printer: lp2@h4  (printing disabled)
 Queue: 2 printable jobs
 Server: no server active
 Rank   Owner/ID           Class Job Files        Size Time
1      papowell@h4+235.1C1   A   235 /tmp/hi         3 16:14:36
2      papowell@h4+235.1C2   A   236 /tmp/hi         3 16:14:37
Printer: lp3@h4  (printing disabled)
 Queue: 1 printable job
 Server: no server active
 Rank   Owner/ID           Class Job Files        Size Time
1      papowell@h4+235.2     A   237 /tmp/hi         3 16:14:37
<prompt>h4: {288} % </prompt><userinput>more /var/spool/lpd/lp2/cfA235*</userinput>
Hh4.private
Ppapowell
J/tmp/hi
Cred
Lpapowell
Apapowell@h4+235.1C1
D2000-06-01-16:03:25.237
Qlp
N/tmp/hi
fdfA235h4.private
UdfA235h4.private</screen>
</informalexample>
</para>

<para>As you can see, two copies of the job has been transferred to
<literal remap=tt>lp2</literal>
and one to
<literal remap=tt>lp3</literal>,
each with a different job number,
If we examine the control file for the jobs in the
<literal remap=tt>lp2</literal>
spool queue,
we will find that the
<literal remap=tt>C</literal>
or class information has been changed to
<literal remap=tt>red</literal>.</para>

<para>For details about all of the capabilities of the routing filter,
see
<link linkend="destinations">Dynamic Routing</link>.
Here is a summary of the information that the routing filter can put into
the routing file.
<variablelist>
<varlistentry><term> dest queue </term>
<listitem>
<para>Route this job to
<literal remap=tt>queue</literal>.
The <literal>queue@host</literal> form will transfer the job to the queue on the named host.</para>

</listitem>
</varlistentry>

<varlistentry><term> copies N </term>

<listitem>
<para>Send N copies of this job to the destination.</para>

</listitem>
</varlistentry>

<varlistentry><term> priority C </term>

<listitem>
<para>Set the job priority letter to
<literal remap=tt>C</literal>,
where C is a single upper case letter.</para>

</listitem>
</varlistentry>

<varlistentry><term> Cvalue </term>

<listitem>
<para>Set the control file line starting with
<literal remap=tt>C</literal>
to
<literal remap=tt>Cvalue</literal>.</para>

</listitem>
</varlistentry>
</variablelist>
</para>

<para>The exit status of the routing filter controls how the job will be processed.
If the exit code is JSUCC (0),
then the job will be processed normally,
JHOLD will hold the job until released,
JREMOVE will remove the job,
and so forth.</para>

</sect1>

<sect1><title>Job Options and the Z Control File Entry</title>

<para>Many printers have special capabilities such as
printing in landscape mode,
duplex printing,
binding,
or stapling.
These capabilities are usually invoked or enabled by the print spooler
sending special printer control commands to the printer
based on values it finds in the control file.
The &LPRng; print spooler uses the
<literal/Z/
line in the control file to specify these options,
while other print spoolers such as the Sun Microsystems Solaris <application/lp/
system pass them on the <literal/S/ line.
</para>
<para>
Job formatting options are specified using the <command>lpr -Z</command> option.
The <application>lpr</application> program concatenates the <literal>-Z</literal> options and puts them
in the control file as a single
<literal remap=tt>Z</literal>
line.
For example:
<informalexample>
<screen><prompt>h4: {289} % </prompt><userinput>lpc stop</userinput>
Printer: lp@h4
lp@h4.private: stopped
<prompt>h4: {290} % </prompt><userinput>lpr -Zthis -Zthat /tmp/hi</userinput>
<prompt>h4: {291} % </prompt><userinput>cat /var/spool/lp/cf*</userinput>
Hh4.private
Ppapowell
J/tmp/hi
CA
Lpapowell
Zthis,that
Apapowell@h4+115
D2000-05-05-10:05:41.351
Qlp
N/tmp/hi
fdfA115h4.private
UdfA115h4.private</screen>
</informalexample>
</para>

<para>As we see, the
<literal remap=tt>Z</literal>
options have been put into the control file on the <literal/Z/  line.
The <literal/Z/ option values are passed to filters on the command line
as the <literal/-Z/ command line option.
These values are used by the <application/ifhp/ filter to determine what control
commands to send to the printer and how to format the print job output.
Because each printer is different and supports a different set
of capabilities
it is impossible to have a set of job options supported across all printers.
The following are supported by the
<application remap=tt>ifhp</application>
configuration
where possible.
Many of these options rely on the printer supporting PostScript
or having the appropriate PCL commands to do the indicated operation.
<itemizedlist>

<listitem>
<para>-Zlandscape -Zportrait<!-- <br> --> -
select landscape or portrait orientation.</para>
</listitem>

<listitem>
<para>-Zduplex -Zsimplex<!-- <br> --> -
select duplex (both sides of a page) or simplex (single side
of a page) printing.</para>
</listitem>

<listitem>
<para>-Zletter -Zlegal -Zledger -Za4 -Za5 -Zenvelope -Ztransparency<!-- <br> --> -
select a paper size</para>
</listitem>

<listitem>
<para>-Zinupper -Zinmiddle -Zinlower<!-- <br> --> -
select input media from the appropriate input tray</para>
</listitem>

<listitem>
<para>-Zmanual<!-- <br> --> -
select input from the manual feed</para>
</listitem>

</itemizedlist>
</para>

<sect2><title>Setting Job Options Using the Printcap</title>
<para>
An alternative to this method of using <application/lpr/ and the <literal/-Z/ option
is to define a set of spool queues which will put the necessary options into the job
control file.
This can be done by the <application/lpr/ program when the job is generated,
or by the <application/lpd/ spooler when the job is processed.
The
The options specified by the
<literal>:prefix_z</literal>, <literal>:append_z</literal>, and <literal>:delete_z</literal>
are prefixed, appended, or deleted from the current set of
<literal remap=tt>Z</literal> control file options by the <application/lpr/
program when the job is submitted and they are specified in the printcap
for the queue,
or by the <application/lpd/ spooler when the job is submitted to the queue.
We can use this capapbility to configure print queues to
a desired set of <literal/Z/ options into the control file.
For example:
<informalexample>
<screen>landscape:lp=%P@server
landscape:server:tc=.common
  :lp=raw@server:append_z=landscape:delete_z=portrait
raw:server:tc=.common:lp=....
  :filter=/usr/local/libexec/filters/ifhp
.common:sd=/var/spool/lpd/%P</screen>
</informalexample>
</para>

<para>
When a job is sent to the <literal/landscape/ queue,
the control file <literal/Z/ line will have the <literal/portrait/
option removed and the <literal/landscape/ option appended.
The <literal/:delete_z/ values are glob patterns
and options that match are removed from the option list.
Options are assumed to be separated by commas or semicolons in the option list.
</para>

</sect2>
<sect2><title>Converting SystemV Options to LPRng Options</title>
<para>On some SystemV <application>lp</application> print spoolers,
the
<command>lp -o option</command>,
puts the option information into the control file
<literal remap=tt/S/
line,
and on other systems on the
puts the option information into the control file
<literal remap=tt/O/
line.

To convert these options to &LPRng; <literal/Z/ options use the
<literal>:prefix_option_to_option=from,from... to</literal>
facility
to prefix the <emphasis/from/ control file lines to the <emphasis/to/
control file line.
For example:
<informalexample>
<screen># System V to LPRng - S and O to Z options
convert:server:tc=.common
  :lp=raw@server:prefix_option_to_option=S,O Z
# LPRng to System V O options
convert:server:tc=.common
    :lp=raw@server:prefix_option_to_option=Z O</screen>
</informalexample>
</para>
</sect2>
<sect2><title>Selecting a Single Option - Muliple Queues</title>

<para>
Here is an example of how you can set up queues that will append the
appropriate <literal/Z/ option to select landscape mode,
do duplex printing, or select
legal or ledger size paper:
<informalexample>
<screen>
landscape:lp=%P@server
landscape:server:tc=.common
    :lp=raw@server:append_z=landscape
duplex:lp=%P@server
duplex:server:tc=.common
    :lp=raw@server:append_z=duplex
ledger:lp=%P@server
ledger:server:tc=.common
    :lp=raw@server:append_z=ledger
legal:lp=%P@server
legal:server:tc=.common
    :lp=raw@server:append_z=legal
raw:server:tc=.common:lp=....
  :filter=/usr/local/libexec/filters/ifhp
.common:sd=/var/spool/lpd/%P</screen>
</informalexample>
</para>
<para>
The problem with this method is that for each option we need to define a queue
whose only purpose is to append the appropriate option and then forward this
to the main print queue.
</para>
</sect2>
<sect2 id=incomingcontrolfilter><title>Selecting Multiple Options - Single Queue </title>

<para>
In the previous section,
we showed how to set up a queue that would append a single option
to the control file <literal/Z/ line.
If we want to have combinations of option options specified by the printer name
then we will have to create a large number of queues
each with a different set of options and each appending a different
set of values.
The problem becomes compounded when we have many printers,
each of which requires these options.
</para>

<para>
The solution to this problem originated with the
<application/apsfilter/ program written by
Andreas Klemm and Thomas Bueschgens.
They made the observeration that if we know the name of the print queue
then we can use this name to select options for the printer.
The &LPRng; provides this functionality by
using wildcard queues and <emphasis/editing/ or <emphasis/filtering/ the control file when the job is
submitted to the spool queue.
</para>
<para>
The <literal>incoming_control_filter=/path</literal> filter
processes the incoming job control or job ticket file.
It can be used to values in the job ticket of incoming jobs.
It reads the control file on its <acronym/STDIN/ and
writes the new or modified  values on <acronym/STDOUT/.
A 0 exit code value causes normal processing of the job,
<literal/JHOLD/ will hold the job,
and any other value will cause the job to be discarded.
The <literal/incoming_control_filter/ filter
can modify priority or other job options,
including using the <literal/move=/ field to cause a job to be
redirect to another spool queue or printer.
Only changes to the jobs options need to be generated by the
<literal>incoming_control_filter=/path</literal> filter.
</para>

<para>
The input and output have the format:
<informalexample>
<screen>
INPUT:
 X&lt;option&gt;       - option from control file
 X=&lt;option&gt;      - alternative option format
 key=&lt;option&gt;    - spooler option
 X==&lt;option&gt;     - option starting with = sign

OUTPUT:
 X                     - delete option or value
 X=                    - delete option or value
 X&lt;option&gt;       - set option value
 X=&lt;option&gt;      - set option value
 key=&lt;option&gt;    - set value of 'key' to option
 key=                  - delete option or value
</screen>
</informalexample>
</para>

<para>
In addition to modifying job options,
the contents of the jobs data files can be modified or the
data files removed.
Any data files with a 0 length will be removed from the job.
If all of the data files have a 0 length then the job will be discarded.
Modification of job options may have unforseen effects on
</para>

<para>
The following shows how we can set up a single queue that will
allow various combinations of options to be selected by the format
of the queue name:
<informalexample>
<screen># for clients
pr|pr_*:lp=%Q@server
# for server
pr|pr_*:server
  :tc=.common:lp=....
  :incoming_control_filter=/usr/local/libexec/filters/update_z
  :filter=/usr/local/libexec/filters/ifhp
.common:sd=/var/spool/lpd/%P</screen>
</informalexample>
</para>
<para>
The <literal/pr/ and <literal/pr_*/ aliases will match printer <literal/pr/
all print queue names starting with <literal/pr_/.
We can then use various suffixes to select job options.
The following filter program uses the
<literal/_landscape/,
<literal/_legal/,
and
<literal/_ledger/ suffixes to set the corresponding option
in the <literal/Z/ file.
This program and other are available in the &LPRng; distribution
in the <filename>UTILS</filename> directory.
You should note that additional options can be specified
as desired.
<informalexample>
<screen>#
#!/usr/bin/perl
# update_z script:
# Determine the options according to the format of the queue name
#  Inspired by the psfilter code of Andreas Klemm
#  and Thomas Bueschgens 
# First, get command line arguments
#
use Getopt::Std;
my(%args,$Q,$Zopts,@file);
getopts(
"A:B:C:D:E:F:G:H:I:J:K:L:M:N:O:P:Q:R:S:T:U:V:W:X:Y:Z:" .
"a:b:cd:e:f:g:h:i:j:k:l:m:n:o:p:q:r:s:t:u:v:w:x:y:z:",
\%args );
# read stdin
@file = &lt;STDIN&gt;;
$Zopts = "";
# first use command line Queue name
$Q = $args{"Q"};
if( not $Q and (($Q) = grep(/^Q/,@file)) ){
   # next use control file Queue name
    chomp $Q if $Q;
}
# now we split up the name and use as parameters for Z options
while( $Q =~ /_([^_]+)/g ){
    # you can add them or test and then add them
    if( $1 eq "landscape"
        or $1 eq "legal"
        or $1 eq "ledger" ){
        $Zopts .= ",$1"
    }
}
if( $Zopts ){
    # remove leading comma
    $Zopts = substr( $Zopts, 1 );
    #replace or prefix Z options
    if( not (grep { s/$/,$Zopts/ if /^Z/; } @file) ){
        print "Z" . $Zopts . "\n";
    }
}
print @file if( @file );
exit 0

Example Input:
...
Z=over
Q=lp_landscape_ledger
...

Example output:

Z=over,landscape,ledger
Q=lp_landscape_ledger
</screen>
</informalexample>
</para>

<para>
The Perl script first uses the <function/getopts/ function to
parse the command line options.
If there is not a command line <literal/-Q/ option then the
control file <literal/Q/ line is used after stripping the trailing
newline.
The queue name is then split up into parts separated by underscores
(<literal/_/) and those used as option names.
As shown in the example,  the literal values are placed in the control file.
You can also use the following code segment to translate short forms of
options into longer ones:

<informalexample>
<screen>while( $Q =~ /_([^_]+)/g ){
    # you can add them or test and then add them
    Zopts .= ",landscape" if( $1 eq "ld" );
    Zopts .= ",ledger" if( $1 eq "11" );
    Zopts .= ",legal" if( $1 eq "15" );
    Zopts .= ",a4" if( $1 eq "a4" );
}</screen>
</informalexample>
</para>

</sect2>
</sect1>
<sect1><title>Interfacing to Non-LPRng Spoolers</title>

<para>
Given the large number of defective RFC1179 implementations that are
currently in use,
there will come a time when the administrator will discover that their
printer with its built-in network interface,
the non-UNIX based print spooler on a mainframe,
or even a new print spooler on a new OS distribution will not accept
jobs from the &LPRng; system.
Usually this is caused by the presence,
absence,
or order of lines in the control file being sent to the remote system.
To deal with this particular problem,
the
<literal remap=tt>:bk</literal>,
<literal remap=tt>:control_file_line_order</literal>,
<literal remap=tt>:nline_after_file</literal>,
and
<literal remap=tt>:control_filter</literal>
options are used.</para>

<para>The
<literal remap=tt>:bk</literal>
(BSD Kompatibility) option causes the lpd server to
remove all but an extremely small subset of lines from the control file,
and to put the lines in the most commonly used order.
In addition
it will make the control and data files names extremely short and simple.
This almost always solves compatibility problems when sending jobs to older
<emphasis>vintage</emphasis>
print spoolers or UNIX systems.</para>

<para>If this does not solve the problem,
then you can specify the allowed control file lines and their order using the
<literal>control_file_line_order=...</literal> option.
For example,
<literal>control_file_line_order=CJPMD</literal>
would allow only control file lines starting with
<literal remap=tt>C</literal>,
<literal remap=tt>J</literal>,
<literal remap=tt>P</literal>,
<literal remap=tt>M</literal>,
and
<literal remap=tt>D</literal>,
and this order in the control file.
Note that this does not provide <emphasis>missing</emphasis>
line values,
it only controls line values that are present in the control file.
You should also use the
<literal remap=tt>:bk</literal>
option as well.</para>

<para>You might run into some <emphasis>really</emphasis>
unusual implementations where
the control file
<literal remap=tt>N</literal>
(file name) information must come <emphasis>after</emphasis>
the control file name.
This is forced by the
<literal remap=tt>:nline_after_file</literal>
option.</para>

<para>If these horrible kludges do not solve your compatibility problems
then we turn to the very large hammer and edit the control file.
The very last step before transfering
the control file to the remote server is to filter it using the
<literal>:control_filter=/path</literal> program specified in the printcap.
The <literal>:control_filter</literal> reads the control file from <acronym/STDIN/
and writes the modified control file to <acronym/STDOUT/.
No consistency checking or validity checks are done on the new control file
and the result is transferred directly to the remote system.
If the
<literal>:control_filter</literal> exits with a 0 status,
then the normal processing continues.
Any other status will cause the transfer operation to be aborted
and an error reported.
</para>

</sect1>

<sect1><title>Debugging, Tracing, and Log Files</title>

<para>The &LPRng; software was designed and written to provide as high a level
of diagnostic information as possible.
This was largely in part due to the problems with portability,
coding errors,
and other human frailties.
Approximately 80% of the &LPRng; source code concerns itself with
checking return values from system functions and producing error messages,
debugging and tracing information,
and various facilities used for regression testing and diagnosis.</para>

<para>The approach used by &LPRng; is to produce
<emphasis>trace</emphasis>
output for the
&LPRng; clients or
<emphasis>log</emphasis>
files for the <application>lpd</application> server
that show the various events or flow of information through the &LPRng; system.
There are several classes or types of actions that can be traced,
and various levels of trace information generated.
The interface used to control these actions are the command line
<literal>-D literals</literal> flags and the
<command remap=tt>lpc debug</command>
command.</para>

<para>First,
we will look at how you can use the debugging facilities for the clients.
Enter the following commands:
<informalexample>
<screen><prompt>h4: {292} % </prompt><userinput>lpr -D=</userinput>
debug usage: -D [ num | key=num | key=str | flag | flag@ | flag+N ]*
  keys recognized: network[+N,@], database[+N,@], lpr[+N,@],
   lpc[+N,@], lprm[+N,@], lpq[+N,@], test=num, job=num, log[+N,@]
<prompt>h4: {293} % </prompt><userinput>lpr -V /tmp/hi</userinput>
LPRng-3.7.2, Copyright 1988-2000 Patrick Powell, &lt;papowell@lprng.com&gt;
sending job 'papowell@h4+981' to lp@localhost
connecting to 'localhost', attempt 1
connected to 'localhost'
requesting printer lp@localhost
sending control file 'cfA981h4.private' to lp@localhost
completed sending 'cfA981h4.private' to lp@localhost
sending data file 'dfA981h4.private' to lp@localhost
completed sending 'dfA981h4.private' to lp@localhost
done job 'papowell@h4+981' transfer to lp@localhost
<prompt>h4: {294} % </prompt><userinput>lpr -D1 /tmp/hi</userinput>
09:38:08.707 h4 [13991] lpr  Get_printer: original printer '&lt;NULL&gt;'
09:38:08.708 h4 [13991] lpr  Get_all_printcap_entries: starting
09:38:08.708 h4 [13991] lpr  Select_pc_info: looking for 'all', depth 0
09:38:08.708 h4 [13991] lpr  Select_pc_info: returning '&lt;NULL&gt;'
09:38:08.708 h4 [13991] lpr  Select_pc_info: looking for '*', depth 0
09:38:08.708 h4 [13991] lpr  Select_pc_info: returning '&lt;NULL&gt;'
09:38:08.708 h4 [13991] lpr  Dump_line_list: Get_all_printcap_entries
...</screen>
</informalexample>
</para>

<para>The <command>lpr -D=</command> causes the <application>lpr</application> (and other &LPRng; programs) to show what debugging flags
are available.
The <command>lpr -V</command> flag causes <application>lpr</application> to run in verbose mode and show its activities.
Finally,
we use <command>lpr -D1</command> to enable the simplest level of debugging.
This will produce a trace of the various activities that <application>lpr</application> carries out.
Try <command>lpr -D2</command>,
<command>lpr -D3</command>,
and so forth to see the increasing amount of detail that you get.</para>

<para>The
<emphasis>network</emphasis>
and
<emphasis>database</emphasis>
debug flags turn on debugging for the network facilities
and the database (<filename>lpd.conf</filename>, <filename>printcap</filename>, and <filename>lpd.perms</filename>) lookups.
Lets see what <command>lpr -Dnetwork</command> shows us:
<informalexample>
<screen>
C<prompt>h4: {295} % </prompt><userinput>lpr -Dnetwork /tmp/hi</userinput>
lp: getconnection: START host localhost, timeout 10, connection_type 1
lp: getconnection: fqdn found localhost.private, h_addr_list count 1
lp: Link_dest_port_num: port 2000 = 2000
lp: getconnection: sock 3, src ip 127.0.0.1, port 65209
lp: getconnection: dest ip 127.0.0.1, port 2000
lp: getconnection: connection to 'localhost' sock 3, errmsg 'No Error'
lp: Link_send: host 'localhost' socket 3, timeout 6000
lp: Link_send: str '^Blp
', count 4, ack 0x80447a0
lp: Link_send: final status NO ERROR
lp: Link_send: host 'localhost' socket 3, timeout 6000
lp: Link_send: str '^B135 cfA276h4.private
', count 22, ack 0x8044370
lp: Link_send: final status NO ERROR
lp: Link_send: host 'localhost' socket 3, timeout 6000
lp: Link_send: str 'Hh4.private
Ppapowell
J/tmp/hi
CA
Lpapowell
Apapowell@h4+276
D2000-06-02-09:44:52.369
Qlp
N/tmp/hi
fdfA276h4.private
UdfA276h4.private
', count 136, ack 0x8044370
lp: Link_send: final status NO ERROR
lp: Link_send: host 'localhost' socket 3, timeout 6000
lp: Link_send: str '^C3 dfA276h4.private
', count 20, ack 0x8044310
lp: Link_send: final status NO ERROR
lp: Link_send: host 'localhost' socket 3, timeout 6000
lp: Link_send: str '', count 1, ack 0x8044310
lp: Link_send: final status NO ERROR</screen>
</informalexample>
</para>

<para>As we see,
we get a detailed exposition of the network connection and transfer steps.
If you need or want more detail,
try using
<command>lpr -Dnetwork+2</command> or
<command>lpr -Dnetwork+3</command>.
You may want to try
<command>lpr -Ddatabase</command> and observe the actions of the <application>lpr</application> program as it
extracts information from the <filename>lpd.conf</filename> and <filename>printcap</filename> files.
If you need or want more detail,
try using
<command>lpr -Ddatabase+2</command> or
<command>lpr -Ddatabase+3</command>.</para>

<para>If you need to trace the activities of the <application>lpd</application> server,
it becomes a little more complex.
The lpd server has a single <emphasis>listening</emphasis>
process that forks and creates
individual processes to handle incoming requests.
Debug or diagnose the main process actions by using <command>lpd -D...</command>.
You may also want to use <command>lpd -F</command> to keep the server in the foreground so you
can kill it off easily.
Needless to say,  you should also redirect the <acronym/STDERR/ and <acronym/STDOUT/ so that it
goes to a file so that you can examine the voluminous records at your
leisure.  The following shows a typical main <application>lpd</application> process debugging session
using the C Shell.
<informalexample>
<screen><prompt>h4: {296} % </prompt><userinput>lpd -F -D1 &gt;&amp;/tmp/logfile &amp;</userinput>
[2] 14299
<prompt>h4: {297} % </prompt><userinput>tail -f /tmp/logfile</userinput>
2000-06-02-09:53:39.716 h4 [1200] Waiting Read_server_status: \
                         select status 1
2000-06-02-09:53:39.716 h4 [1200] Waiting Read_server_status: \
                         read status 1
2000-06-02-09:53:39.716 h4 [1200] Waiting Dump_line_list: \
    Read_server_status - input - 0x8047980, count 0, max 0, list 0x0
2000-06-02-09:53:39.716 h4 [1200] Waiting Read_server_status: \
                         select status 0
2000-06-02-09:53:39.716 h4 [1200] Waiting lpd: LOOP START
2000-06-02-09:53:39.716 h4 [1200] Waiting Get_max_servers: \ 
                         getrlimit returns 64
2000-06-02-09:53:39.716 h4 [1200] Waiting Get_max_servers: \
                         returning 32
2000-06-02-09:53:39.716 h4 [1200] Waiting lpd: \
                         max_servers 32, active 0
2000-06-02-09:53:39.716 h4 [1200] Waiting lpd: \
                         starting select timeout 'yes'

^C
<prompt>h4: {298} % </prompt><userinput>jobs</userinput>
[1]  - Running                lpd -F -D1 &gt;&amp; /tmp/logfile
<prompt>h4: {299} % </prompt><userinput>kill %1</userinput></screen>
</informalexample>
</para>

<para>We start the debugging session by running the <application>lpd</application> server in foreground mode.
This causes it to send its output to <acronym/STDOUT/ and <acronym/STDERR/.
We redirect both of these to a file and put the <application>lpd</application> server in the background.
Then we use <command>tail -f</command> to read from the log file.
Finally,
we kill off the <application>lpd</application> server.</para>

<para>This method is extremely difficult to use,
as all of the output produced by the server and its subprocesses is sent to
a single output file.
If we want to debug the actions concerning a single queue,
then we can use the queue
<emphasis>log file</emphasis>
and
<command remap=tt>lpc debug</command>
command instead.
The following options control debugging of an individual print queue.
<variablelist>
<varlistentry><term>lf=log</term>
<listitem>
<para>The log file for the queue.
The queue server process will open this file and place debugging information
into this file.</para>

</listitem>
</varlistentry>

<varlistentry><term>max_log_file_size=nnn</term>

<listitem>
<para>The maximum size of the log file in K bytes.
When the queue server process first opens this file it will check to see
if the file is larger than the maximum size.
If it is, then it will truncate it.  A zero (0) value suppress truncation.</para>

</listitem>
</varlistentry>

<varlistentry><term>min_log_file_size=nnn</term>

<listitem>
<para>When the log file is truncated only the the last nnn K bytes are retained.</para>

</listitem>
</varlistentry>

<varlistentry><term>db=options</term>

<listitem>
<para>These are debugging options for the spool queue.
These options are permanent and cannot be changed by using the
<command remap=tt>lpc debug</command>
facility.</para>

</listitem>
</varlistentry>
</variablelist>
</para>

<para>The
<command remap=tt>lpc debug</command>
command is used to set the debugging options in force for
the spool queue.
This is done by writing the debug options into the spool queue control file.
Let us see how we can use this facility to trace the actions of printing a file.</para>

<para>Edit the printcap file so it have the contents indicated below,
create the <filename>/tmp/lp</filename> and <filename>/tmp/lp2</filename>
files with
<literal remap=tt>0777</literal>
permissions.
Use <command>checkpc -f</command> to check the printcap,
and then use
<command remap=tt>lpc reread</command>
to restart the <application>lpd</application> server.
<informalexample>
<screen># printcap
lp:force_localhost
lp:server
  :lp=/dev/null
  :sd=/var/spool/lpd/%P
  :lf=log
lp2:force_localhost
lp2:server
  :sd=/var/spool/lpd/%P
  :lp=/tmp/lp2
  :lf=log</screen>
</informalexample>
</para>

<para>Now execute the following commands:
<informalexample>
<screen><prompt>h4: {300} % </prompt><userinput>lpq</userinput>
Printer: lp@h4
 Queue: no printable jobs in queue
<prompt>h4: {301} % </prompt><userinput>lpc debug lp 1</userinput>
Printer: lp@h4
debugging override set to '1'
lp@h4.private: updated
<prompt>h4: {302} % </prompt><userinput>lpc status</userinput>
 Printer Printing Spooling Jobs Server Subserver Redirect Status/(Debug)
lp@h4    enabled  enabled    0    none    none          (1)
<prompt>h4: {303} % </prompt><userinput>lpr /tmp/hi</userinput>
<prompt>h4: {304} % </prompt><userinput>more /var/spool/lpd/lp2/log</userinput>
2000-06-02-10:10:50.589 h4 [1201] (Server) lp: \
   Update_spool_info: printer 'lp'
2000-06-02-10:10:50.590 h4 [1201] (Server) lp: \
   Do_queue_jobs: printable 1, held 0, move 0
2000-06-02-10:10:50.590 h4 [1201] (Server) lp: \
   Do_queue_jobs: after Scan_queue next fd 5
2000-06-02-10:10:50.590 h4 [1201] (Server) lp: \
   Do_queue_jobs: MAIN LOOP
2000-06-02-10:10:50.590 h4 [1201] (Server) lp: \
   Do_queue_jobs: Susr1 before scan 0
2000-06-02-10:10:50.591 h4 [1201] (Server) lp: \
   Do_queue_jobs: chooser '&lt;NULL&gt;', chooser_routine 0</screen>
</informalexample>
</para>

<para>The
<command remap=tt>lpc debug</command>
command sets the debug level to 1.
We can use the
<command remap=tt>lpc status</command>
command to see what debug flags or actions are
currently specified for the spool queue.
We then send a job to the spool queue and examine the log file contents.</para>

<para>Each line in the log file has a timestamp,
the name of the host, the process id that produced it,
and a heading that tells the action or activity that the process is performing,
and the name of the print queue that is being processed and a trace message.
By convention,
the trace message lists the name of the routine that processed it
and then the actual information.
Some messages may extend over several lines,
but each line has the standard header at the start of the line.</para>

<para>The default debug or trace actions were designed to trace problems with
printing,
as these are the most common.
However,
you can also use the
<application>lpr</application>,
<application>lpc</application>,
<application>lprm</application>,
or
<application>lpq</application>
option to cause the <application>lpd</application> server to trace the actions during
the execution of an
<application>lpr</application>,
<application>lpc</application>,
<application>lprm</application>,
or
<application>lpq</application>
request.</para>

<para>The
<emphasis>log</emphasis>
option is used to test various logging facilities and is usually
not used for general purpose debugging.</para>

</sect1>
</chapter>

<chapter id=lprngclients><title>&LPRng; Clients - lpr, lprm, lpq, lpc, lpstat </title>

<para>The &LPRng; software is a true set of client/server applications.
The &LPRng; clients,
<application remap=tt>lpr</application>,
<application remap=tt>lpq</application>,
<application remap=tt>lprm</application>,
and <application>lpc</application> connect to a <application>lpd</application> server using a
TCP/IP connection.
This means that you must have TCP/IP networking enabled on your workstation
to use &LPRng;.</para>

<para>However,
you do not need to have an external network connection to the Internet.
For most single system users,
the <application>lpd</application> server is running on the same workstation as the client program,
and the clients will simply talk to the
<literal remap=tt>localhost</literal>.</para>


<sect1 id="printerinfo"><title>Printer and Server Information
</title>

<para>Options used:
<itemizedlist>

<listitem>
<para>
<envar>PRINTER</envar>, <envar>LPDEST</envar>,
<envar>NPRINTER</envar> <envar>NGPRINTER</envar>
<emphasis>Environment variables</emphasis></para>
</listitem>

<listitem>
<para> <literal>force_localhost</literal> FLAG <emphasis>force clients to send requests to localhost </emphasis></para>
</listitem>

<listitem>
<para> <literal>require_explicit_q</literal> FLAG <emphasis>require queue to be specified</emphasis></para>
</listitem>

</itemizedlist>

</para>

<para>When an &LPRng; client such as
<application>lpr</application>,
<application>lpq</application>,
<application>lprm</application>,
or
<application remap=tt>lpc</application>
needs to communicate with a print server,
the only information they normally need is:
<orderedlist>

<listitem>
<para>The remote printer (<literal remap=tt>:rp</literal>) value
to be used in requests to the <application>lpd</application> print server.
This is sometimes referred to as the <emphasis>printer</emphasis>
or <emphasis>print queue</emphasis>
name.</para>
</listitem>

<listitem>
<para>The remote server (<literal remap=tt>:rm</literal>) which is the
The IP address or hostname of the <emphasis>print server</emphasis>.</para>
</listitem>

<listitem>
<para>The original queue name specified by the user
which may be used as part of the job information sent to the
print server.</para>
</listitem>

</orderedlist>
</para>

<para>&LPRng; has several ways to specify the <emphasis>printer queue</emphasis>
and <emphasis>server</emphasis>
information.</para>
</sect1>

<sect1><title>Command Line -Pprinter@host</title>

<para>The <literal>-P printer@host</literal> literal explicitly provides a printer
and server value.
This is equivalent to the printcap entry shown below:
<informalexample>
<screen>lpr -Plaser@10.0.0.1
  equivalent to printcap entry:
    laser:lp=laser@10.0.0.1
lpq -Plp@myserver
  equivalent to printcap entry:
    lp:lp=lp@myserver</screen>
</informalexample>
</para>

</sect1>

<sect1><title>Command Line -Pprinter</title>

<para>Use the printcap entry with the name or alias
<literal remap=tt>printer</literal>
and use the information in that printcap entry.
Example:
<informalexample>
<screen>lpr -Plp
  look up printcap entry for 'lp'</screen>
</informalexample>
</para>

</sect1>

<sect1><title>PRINTER, LPDEST, NPRINTER, and NGPRINTER Environment Variables</title>

<para>If no command line option is specified,
the &LPRng; clients will use the first defined
<acronym>PRINTER</acronym>,
<acronym>LPDEST</acronym>,
<acronym>NPRINTER</acronym>,
or
<acronym>NGPRINTER</acronym>
environment variable value
and will use the value as though specified as a
<literal>-P$PRINTER</literal> command line literal.
If the value has the form
<literal>-Pprinter@host</literal> the print queue will be
<literal remap=tt>printer</literal>
on server
<literal remap=tt>host</literal>
and not consult the printcap database.
If the value has the form
<literal remap=tt>printer</literal>
then the printcap will be searched
for a
<literal remap=tt>printer</literal>
printcap entry.
For example:
<informalexample>
<screen>export PRINTER=laser@10.0.0.1; lpr
  equivalent to printcap entry:
    laser:lp=laser@10.0.0.1
export PRINTER=pr; lpr
  look up printcap entry for 'lp'</screen>
</informalexample>
</para>

</sect1>

<sect1><title>Wildcard Printcap Entry</title>

<para>If you specify a printcap name or alias as
<literal remap=tt>*</literal>
(wildcard),
then if the &LPRng; system cannot find a printcap with the exact
name then the first matching wildcard will be used.
For example:
<informalexample>
<screen>pr|*:rm=server
  lpr -Ppr2 will match, and result in a printcap entry
    pr|pr2:rm=server

*|pr:rm=server
  lpr -Ppr2 will match, and result in a printcap entry
    pr2|pr:rm=server</screen>
</informalexample>
</para>

</sect1>

<sect1><title>First Printcap Entry</title>

<para>If you do not specify a printer on the command line or in the
environment variables,
and the 
<literal/require_explicit_q/ value is not set
then &LPRng; will search the printcap and use the first valid printcap entry
as the printer.
It will use the first one found as the default.</para>
</sect1>

<sect1 id="defaultrmrp"><title>Default Printer and Server Host
</title>

<para>Options used:
<itemizedlist>

<listitem>
<para> <literal>default_remotehost=</literal><emphasis>default rm (remote host)</emphasis></para>
</listitem>

<listitem>
<para> <literal>default_printer=</literal><emphasis>default rp (remote printer)</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>
If the preceding steps do not yield a printer name and the
<literal/require_explicit_q/ flag is not set
then the
<literal>default_printer</literal>
and
<literal>default_remote_host</literal>
values from the <filename>lpd.conf</filename>
configuration file will be used.
If there is no configuration file,
then the compile time defaults will be used.
</para>

<para>Using the fallback values is usually not a desirable event and may indicate
that you have a misconfigured host, so the
compile time defaults are usually
set to <literal>missingprinter@localhost</literal> to provide an annoying message for users.</para>

</sect1>

<sect1 id="forcelocalhost"><title>Force Connection to Localhost </title>

<para>Options used:
<itemizedlist>

<listitem>
<para> <literal>force_localhost</literal> FLAG <emphasis>force localhost to be remote host</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>The legacy BSD print spooler required an <application>lpd</application> print server
to be running on each host.
During the initial stages of development and deployment,
the default &LPRng; configuration and deployment was to always
allow <emphasis>lightweight</emphasis>
operation,
that is,
clients would always connect to the remote host specified in the
printcap.</para>

<para>While this default was appropriate for experienced system administrators,
novice administrators or those who had already configured
print spooling systems and simply wanted to upgrade found themselves
confused by this change.
This problem resulted in over 700 postings to the &LPRng; mailing list
in a five year period.</para>

<para>This problem was solved by providing a <literal>force_localhost</literal> option
in the configuration,
and setting the default value to
<literal remap=tt>1</literal>
or <acronym>TRUE</acronym>.
When this option is <acronym>TRUE</acronym>,
then all &LPRng; clients will connect to the server on the localhost,
<emphasis>unless</emphasis>
they use the <command>lpr -Pserver@host</command>
command line form.
If lightweight operation is wanted,
the administrator can either compile the
&LPRng;
software with the appropriate value or can explicitly set
the <literal>force_localhost@</literal> flag.
<informalexample>
<screen># default:
lp:lp=lp@10.0.0.1:...
  lpr -Plp -&gt;
    lp:lp=lp@localhost:...
  lpr -Plp@10.0.0.1 -&gt;
    lp:lp=lp@10.0.0.1  (no other options)

lp:lp=lp@10.0.0.1:force_localhost@:...
  lpr -Plp -&gt;
    lp:lp=lp@10.0.0.1:...

To disable at compile time:
  configure --disable-force_localhost</screen>
</informalexample>
</para>

</sect1>

<sect1 id="allowusersetting"><title>User Identification </title>

<para>Options used:
<itemizedlist>

<listitem>
<para> <literal>allow_user_setting=</literal><emphasis>privileged users</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>When an client program sends a command to the <application>lpd</application> server
it may need to provide the name of the user
who is originating the request for service.
This name is obtained by looking up the UID of the user
running the client
in the appropriate user information database;
if the information is not found the UID is used instead.
Also,
the client machine hostname may also be needed.
This is usually determined by using a DNS lookup and trying to determine
if there is a canonical or Fully Qualified Domain Name for the
host and using this.</para>

<para>The <command>lpr -U name@host</command> (and for
<application>lpq</application>, <application>lprm</application>, and <application>lpc</application>)
option allows privileged users to
cause the client software to use the
<literal remap=tt>name</literal>
value as the originator
and
<literal remap=tt>host</literal>
as the machine name.
This allows privileged users to
<emphasis>impersonate</emphasis>
other users.
This is most useful for programs such as Samba and PCNFS,
which need to act as proxies for users.</para>

<para>By default, ROOT (UID 0) is the only user that can masquerade as another
user.
The <literal>allow_user_setting=name,name...</literal> configuration option can be used
to specify a list of names or UIDs that can also perform masquerading.
For example,
if the Samba server was running as user
<literal remap=tt>samba</literal>, then
<literal>allow_user_setting=samba</literal> would allow it to specify the name of print
job originator as a remote user,
and the remote user would not need a login account on the system.</para>

</sect1>
</chapter>

<chapter id=lpr><title><application/lpr/ - Job Spooler Program</title>

<para>The <application>lpr</application> client program is used to submit a job
to a print spooler.
It does this by collecting information about the job,
putting it in a control file,
and then sending the control file and files to be printed to the
print server.
The <application>lpr</application> command line options are used to control or specify
the values placed in the control file and how the job is to be
transferred to the remote host.
In addition,
there are printcap or configuration level options that provide
a further degree of administrative control over additional
facilities.
You can get the currently supported command line options by using
the following command:
<informalexample>
<screen><prompt>h4: {305} % </prompt><userinput>lpr -=</userinput>
lpr: Illegal option '='
 usage summary: lpr [-Pprinter[@host]] [-A] [-B] [-Cclass] [-Fformat]
   [-Jinfo] [-(K|#)copies] [-Q] [-Raccountname]  [-Ttitle]
   [-Uuser[@host]] [-V] [-Zoptions] [-b] [-m mailaddr] [-h]
   [-i indent] [-l] [-w num ] [-r] [-Ddebugopt ] [ filenames ...  ]
 -A          - use authentication specified by AUTH environment variable
 -B          - filter job before sending
 -C class    - job class
 -D debugopt - debugging flags
 -F format   - job format
   -b,-l        - binary or literal format
    c,d,f,g,l,m,p,t,v are also format options
 -J info     - banner and job information
 -K copies, -# copies   - number of copies
 -P printer[@host] - printer on host
 -Q          - put 'queuename' in control file
 -Raccntname - accounting information
 -T title    - title for 'pr' (-p) formatting
 -U username - override user name (restricted)
 -V          - Verbose information during spooling
 -Z options  - options to pass to filter
 -h          - no header or banner page
 -i indent   - indentation
 -m mailaddr - mail final status to mailaddr
 -r          - remove files after spooling
 -w width    - width to use
 PRINTER, NPRINTER environment variables set default printer.</screen>
</informalexample>
</para>

<para>If you are interested in the exact details of the job transfer,
control file,
and data file formats,
please see

<link linkend="rfc1179ref">RFC 1179 - Line Printer Daemon Protocol</link>

for the exact details.</para>

<sect1 id="defaultformat"><title>Job Format Options
</title>

<para>Options used:
<itemizedlist>

<listitem>
<para><literal>default_format=</literal><emphasis>default print job format (f)</emphasis></para>
</listitem>

<listitem>
<para><literal>fx=</literal><emphasis>supported formats for printing</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>The legacy or vintage BSD print spooling system assigned each job
a format.
This format was used by the <application>lpd</application> server
to select an appropriate filter program that would process the job
and format it correctly for the printer.
By convention,
lower case letters were used to specify job formats.</para>

<para>The &LPRng; <application>lpr</application> client supports the legacy or vintage BSD
formats,
and also provides the <literal>-Fx</literal> literal to allow addition formats to
be specified.
If a format is specified the <literal>default_format</literal>
value (usually
<literal remap=tt>f</literal>) is used.
The <literal>fx=...</literal> option value is a list of the
(lower case) characters corresponding to the
formats allowed for a spool queue.
For example, <literal>fx=flv</literal> would allow only
jobs with format
<literal remap=tt>f</literal>,
<literal remap=tt>l</literal>,
or
<literal remap=tt>v</literal>
to be spooled to a queue.
By default, all job formats are allowed.</para>

<para>A couple of job formats that require special treatment:
the
<literal remap=tt>b</literal>
(binary) and its alias the
<literal remap=tt>l</literal>
(literal) format,
and the
<literal remap=tt>p</literal>
(pretty print) format.
The <literal>-b</literal> or <literal>-l</literal> command line literal will select job format
<literal remap=tt>l</literal>
(literal) for the job.
By default,
jobs marked with
<literal remap=tt>l</literal>
format are supposed to have a minimum
amount of handling and passed directly to a printer.
The
<literal remap=tt>p</literal>
(pretty print) format is just the opposite - these jobs
are supposed to be pretty printed according to the
various facilities available to the <application>lpd</application> print spooler.</para>

</sect1>

<sect1><title>Job Pretty Printing, Banners, Priority, and Accounting</title>

<para>Options used:
<itemizedlist>

<listitem>
<para><literal remap=tt>ab</literal> FLAG <emphasis>always print banner</emphasis></para>
</listitem>

<listitem>
<para><literal remap=tt>sh</literal> FLAG <emphasis>suppress header (banner)</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>The legacy or vintage BSD pretty printing facility support
allowed users to specify the
title (<literal>-T title</literal>),
indentation (<literal>-i indent</literal>),
width (<literal>-i indent</literal>) of output for pretty printing.
These probably will not have any effect,
but their values are placed in the control file and sent
to the <application>lpd</application> server.</para>

<para>The job name (<literal>-J name</literal>)
and no banner page (<literal>-h</literal>) literals also cause information
to be placed in the control file.
The <literal>-J</literal> value is used to specify a job name on a banner page,
and is placed in the control file.
The <literal>-h</literal> (no header) has the strange effect of <emphasis>not</emphasis>
putting  <emphasis>banner name</emphasis>
information in the control file.
Apparently,
if you do not have a name for your banner then no name will be sent.</para>

<para>If the
<literal remap=tt>:sh</literal>
option is explicitly specified in a printcap
entry for the <application>lpr</application> client,
then it has the effect of the <literal>-h</literal> literal.
However,
the
<literal remap=tt>:ab</literal>
(Always Print Banner) option can be used on the
print server to always force a banner to be printed even if the
user does not specify a banner.</para>

<para>Finally,
the <literal>-R</literal> literal allows additional accounting information to be
placed in the control file.</para>

</sect1>

<sect1 id="classpriority"><title>Job Class and Priority

</title>

<para>Options used:
<itemizedlist>

<listitem>
<para><literal>break_classname_priority_link</literal> FLAG <emphasis>classname does not set priority</emphasis></para>
</listitem>

<listitem>
<para><literal>default_priority=</literal><emphasis>default priority</emphasis></para>
</listitem>

<listitem>
<para><literal>ignore_requested_user_priority</literal> FLAG <emphasis>ignore requested user priority</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>The <literal>-Cclass</literal>
information is used placed in the control file;
the default class and priority is set by the <literal>default_priority</literal>
option (default
<literal remap=tt>A</literal>).
The first letter of the class information is uppercased
and then used as the job priority.</para>

<para>The
<literal>break_classname_priority_link</literal>
option causes <application>lpr</application> to place class information
in the control file but not to use it to set the job priority.
This is usually used in institutions where users should
not specify their priority.
Finally,
the
<literal>ignore_requested_user_priority</literal>
is actually used by <application>lpd</application> to ignore the priority requested by users.</para>

</sect1>
<sect1 id="jobcopies"><title>Job Copies and Job Size</title>

<para>Options used:
<itemizedlist>

<listitem>
<para><literal>mc=</literal><emphasis>maximum number of copies</emphasis></para>
</listitem>

<listitem>
<para><literal>mx=</literal><emphasis>maximum job size (Kbytes)</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>The
<literal remap=tt>:mc</literal>
(maximum number of copies) and
<literal remap=tt>:mx</literal>
(maximum job size)
options are used by both the <application>lpr</application> and <application>lpd</application>
programs.
The <command>lpr -Kn</command> or <command>lpr -#n</command> option sets the numbers
of copies wanted to
<literal remap=tt>n</literal>;
if this is larger than the
<literal remap=tt>:mc</literal>
value then the <application>lpr</application>
client will not print the job and the <application>lpd</application> server will discard the
job with an error.
The job size is the product of the number of copies and the
sum of the individual file sizes.
This is,
in effect,
the total number of bytes to be transferred to the printer.
If the job size is larger than the
<literal remap=tt>:mx</literal>
limit,
then the <application>lpr</application> client will not print the job and the
<application>lpd</application> server will discard the job with an error.</para>

</sect1>

<sect1 id="jobcompletion"><title>Job Completion Notification Requested

</title>

<para>Options used:
<itemizedlist>

<listitem>
<para> <literal>allow_user_logging</literal> FLAG <emphasis>users can send status</emphasis></para>
</listitem>

<listitem>
<para> <literal>lpr_bsd</literal> FLAG <emphasis>lpr -m acts as legacy BSD flag</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>The <command>lpr -m mailaddr</command> option will put the specified mailaddr
value into the control file.
How this is processed is left to the particular server implementation.
See the

<link linkend="abnormalterm">Abnormal Termination</link>

section for details.</para>

<para>&LPRng; extends the <command>lpr -m mailaddr</command> to allow mail addresses of the form
<literal remap=tt> host[%port][/(TCP|UPD)]</literal>,
and when the job is being transferred or printed
the <application>lpd</application> server to open a connection
to the specified TCP/IP address and send job progress information.
This is a security loophole
and the
<literal>allow_user_logging</literal>
flag must be present to allow this operation.</para>

<para>The <literal>lpr_bsd</literal>
will cause the <command>lpr -m</command>
option not to take an argument, as in the BSD lpr,
and will place the users name and host information in the control
file as the destination for notification.</para>

</sect1>

<sect1><title>Remove Files After Spooling</title>

<para>The <command>lpr -r</command> option is extremely dangerous and has proven
to be fatal to a large number of system administrators,
so it should be used with caution.
Unfortunately,
it is used in a large number of shell scripts and by default in
a vast number of programs so it is present in the &LPRng; software.</para>

<para>Its action is quite simple.
After sending the print job to the spooler,
it will try to unlink the original files.
It will do the unlinking operation as the original user,
but if the original user had permissions to remove, say,
<filename>/etc/password</filename>,
then the file will be removed.</para>

</sect1>

<sect1 id="append-z"><title>The -Z Passthrough to Filter Options </title>

<para>Options used:
<itemizedlist>

<listitem>
<para><literal>append_z=</literal><emphasis>Append values to Z options list</emphasis></para>
</listitem>

<listitem>
<para><literal>prefix_z=</literal><emphasis>Prefix values to Z options list</emphasis></para>
</listitem>

<listitem>
<para><literal>remove_z=</literal><emphasis>Remove values from Z options list</emphasis></para>
</listitem>

<listitem>
<para><literal>prefix_s_to_z</literal> FLAG <emphasis>Prefix control file S to Z information</emphasis></para>
</listitem>

<listitem>
<para><literal>prefix_s_to_z</literal> FLAG <emphasis>Prefix control file Z to S information</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>By convention all of the values specified with the
<command>lpr -Z</command>
option are placed in the job control file and passed to the
<application>lpd</application> server,
which turn passes them to the print filters.
This allows users to pass options that are not part of the
RFC1179 repetoire to filters.</para>

<para>In addition to this simple method of providing options,
we can have the <application>lpr</application> and <application>lpd</application> systems add options to
the control file information.
The <literal>prefix_z</literal>
and <literal>append_z</literal>
options allow us to put Z options at the start or end of
the user provided list;
the <literal>remove_z</literal> removes specified options from the list.</para>

<para>The <literal>prefix_s_to_z</literal>
and <literal>prefix_z_to_s</literal>
options are usually used by the <application>lpd</application> server,
and provides a simple way to have the SystemV
<command>lp -o option</command>
values put into the
<literal remap=tt>Z</literal>
options value.
This is very useful when existing SysV
<application remap=tt>lp</application>
print spooling systems
need to be interfaced to &LPRng; and the options need to be handled in a
meaningful manner.
The <literal>prefix_z_to_s</literal> is similarly used when you want to
forward jobs to a SysV spooling system and have the options
passed correctly.</para>

<para>If present in the printer configuration,
the various requested actions are
are done by the <application>lpr</application> client,
the <application>lpd</application> server on job reception,
and the <application>lpd</application> server again on job processing.
This redundancy ensures that jobs which are
sent to this queue,
arrive at this queue,
or are processed by this queue
have the options set appropriately.</para>

</sect1>

<sect1 id="qq"><title>Record Queue Name in Control File </title>

<para>Options used:
<itemizedlist>

<listitem>
<para>
<literal remap=tt>qq</literal> FLAG <emphasis>Insert queue name into control file</emphasis></para>
</listitem>

<listitem>
<para> <literal>force_queuename=</literal><emphasis>Queuename to be used</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>The
<literal remap=tt>:qq</literal>
use queuename option
tells &LPRng; to record the queue name that a job was originally
queued to in the control file
as the
<literal remap=tt>Q</literal>
information.
The
<literal>force_queuename=...</literal>
entry forces the queue name to be the specified value.
For example,
the following printcap entry and <application>lpr</application>
commands will result in a filter
being passed the arguments shown below.
<informalexample>
<screen>#printcap
  pr1_landscape|pr1_portrait|pr_raw
   :filter=/.../filter
   :lp=/dev/pr
   :qq

lpr -Ppr1_landscape      lpr -Ppr1_portrait
  control file -
    Qpr1_landscape          Qpr1_portrait

filter command line:
  ... -Qpr1_landscape     ... -Qpr1_portrait
</screen>
</informalexample>
</para>

<para>The <literal>force_queuename</literal> can be used for force
a specific value into the control file
<literal remap=tt>Q</literal>
information.
<informalexample>
<screen>john|tom|frank
  :filter=/.../filter
  :lp=/dev/pr
  :force_queuename=office

lpr -Pjohn
  control file -
   Qoffice

filter command line:
  ... -Qoffice</screen>
</informalexample>
</para>

</sect1>

<sect1 id="checkfornonprintable"><title>Check For Nonprintable File </title>

<para>Options used:
<itemizedlist>

<listitem>
<para> <literal>check_for_nonprintable</literal> FLAG <emphasis><application>lpr</application> checks for non-printable file </emphasis></para>
</listitem>

<listitem>
<para> <literal>ml=</literal><emphasis>minimum number of printable characters </emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>By default
<application>lpr</application>
does not check files for
for non-printable characters (i.e., escape characters) at the start
of the print file.
You can set the <literal>check_for_nonprintable</literal> flag to cause it to do so.
The
<literal remap=tt>:ml</literal>
value specifies the number of characters that
are to be checked.
Clearly,  if it is 0, none will be checked.</para>

</sect1>

<sect1 id="lprbounce"><title>Job Filtering By LPR </title>

<para>Options used:
<itemizedlist>

<listitem>
<para> <literal>lpr_bounce</literal> FLAG <emphasis>lpr does filtering</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>Some users would like the advantages of
the filtering and processing capabilities of a lpd daemon
without running a <application>lpd</application> daemon on their system.
By having the <application>lpr</application> 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.
<informalexample>
<screen># Simple example of an lpr_bounce entry
bounce
  :lpr_bounce
  :lp=lp@remote
  :filter=/usr/local/bin/lpf</screen>
</informalexample>
</para>

<para>The
<literal>lpr_bounce</literal>
flag, if present in the printcap entry,
will force
<application remap=tt> lpr </application>
to process the job using the
specified filters and send the outputs of the filters to the remote printer
for further processing.</para>

</sect1>

<sect1 id="rg"><title>Restrict Queue Use to Group Members </title>

<para>Options used:
<itemizedlist>

<listitem>
<para> <literal>rg=</literal><emphasis>Restricted group list</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>The
<literal remap=tt>:rg</literal>
value specifies a list of groups.
If this value is present use of a printer or operation is restricted to only
users in a particular group.
This check is done by both the <application>lpr</application> client and the <application>lpd</application>
server if the option is present.</para>

</sect1>

<sect1 id="safechars"><title>Fixing Bad Control Files and Metacharacters </title>

<para>Options used:
<itemizedlist>

<listitem>
<para> <literal>safe_chars=</literal><emphasis>additional safe characters for control file </emphasis></para>
</listitem>

</itemizedlist>
</para>

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

<para>By default,
&LPRng; will brutally convert a non-conforming RFC1179 control file
into one that is acceptable to most,
if not all,
existing RFC1179 implementations.
In order to prevent problems with
&LPRng; ruthlessly purges all characters but
upper and lower case letters,
spaces, tabs, and <literal>-_.@/:()=,+-%</literal> from the control file,
replacing suspicious characters with underscore (<literal/_/).
In addition,
&LPRng; will ruthlessly regenerate control file entries and data file names
so that they are compliant with all known RFC1179 implementations.</para>

<para>For some installations,
the default set of safe characters may be overly restrictive.
For example,
<emphasis>vintage</emphasis>
software may generate files with
<literal remap=tt>#</literal>
characters
in the
<literal remap=tt>J</literal>
line of the control file.
The replacement of this character by underscore
(<literal/_/) may cause other applications which use the control file
information to stop working.
The <literal>:safe_chars</literal> option allows the user to specify an additional
set of safe characters in the <filename>lpd.conf</filename> configuration file(s).</para>

<para>For example, <literal>:safe_chars=#"</literal> would allow the
<literal remap=tt>#</literal>
and
<literal remap=tt>"</literal>
characters to appear in the control file.</para>

</sect1>

<sect1 id="minfree"><title>Minimum Spool Queue Free Space </title>

<para>Options used:
<itemizedlist>

<listitem>
<para> <literal>minfree=</literal><emphasis>Size in Kbytes</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>If this value is non-zero,
then the <application>lpd</application> receiving server checks to see that there is the
specified number of Kbytes of file space available before
accepting a job.
If there is not enough space then the job
is rejected.</para>

</sect1>

<sect1 id="forcefqdnhostname"><title>FQDN Host Information </title>

<para>Options used:
<itemizedlist>

<listitem>
<para><literal>force_fqdn_hostname</literal> FLAG <emphasis>Update control file with FQDN name</emphasis></para>
</listitem>

<listitem>
<para><literal>force_ipaddr_hostname</literal> FLAG <emphasis>Update control file with IP address of remote host</emphasis></para>
</listitem>

<listitem>
<para><literal>use_shorthost</literal> FLAG <emphasis>short control and data file names</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>Some <application>lpr</application> clients do not put a FQDN host name in their control file.
The <literal>force_fqdn_hostname</literal> flag will cause <application>lpd</application> to put a FQDN
host name in the control file.
This option will assume that the domain is where the connection originated from.</para>

<para>Similarly, the <literal>force_ipaddr_hostname</literal>
flag will cause <application>lpd</application> to put a FQDN
host name in the control file.</para>

<para>Some systems cannot handle FQDN hostnames in control and data file formats.
The <literal>use_shorthost</literal> option will send control and data files with
very short names to the remote print server.</para>

</sect1>
</chapter>

<chapter id=lpq><title><application/lpq/ - Status Monitoring Program </title>

<para>The <application>lpq</application> program is the main method used to monitor queues.
You can obtain the available options by using:
<informalexample>
<screen><prompt>h4: {306} % </prompt><userinput>lpq -=</userinput>
lpq: Illegal option '='
usage: lpq [-aAclV] [-Ddebuglevel] [-Pprinter] [-tsleeptime]
  -a           - all printers
  -c           - clear screen before update
  -l           - increase (lengthen) detailed status information
                 additional l flags add more detail.
  -L           - maximum detailed status information
  -n linecount - linecount lines of detailed status information
  -Ddebuglevel - debug level
  -Pprinter    - specify printer
  -s           - short (summary) format
  -tsleeptime  - sleeptime between updates
  -V           - print version information</screen>
</informalexample>
</para>

<sect1><title><application>lpq</application> Queue Selection (lpq -Pprinter, lpq -a)</title>
<para>
If no queue is specified,
then the default queue is used,
with <literal>-Pprinter@host</literal> causing a direct
connection to the named host.
The <literal>-a</literal> literal selects all queues.
</para>
</sect1>


<sect1><title><application>lpq</application> Job Selection</title>
<para>
Jobs are selected by providing a job number,
or <literal/glob/ pattern that is matched to the user name
and and job identifier.
If multiple patterns are provided each is applied in turn
against the jobs in a queue until all have been tried.
If any one of the patterns match then the job status is displayed.
</para>
<para>
If no selection information
is provided or the <literal>all</literal> pattern is provided
then all jobs in the queue are displayed.
</para>
</sect1>

<sect1><title><application>lpq</application> Short Format (lpq -s)</title>

<para>This is one line per spool queue:
<informalexample>
<screen>% lpq -sa
t1@astart110  (printing disabled) 1 job
t2@astart110  (routed/bounce to t1@h10.private) 0 jobs
t3@astart110  (forwarding to t3a@h10.private)
t3a@astart110  (forwarding to t2@h10.private)
t4@astart110  (subservers t5, t6)  0 jobs
t5@astart110  (serving t4) 0 jobs
t6@astart110  (serving t4) 0 jobs</screen>
</informalexample>
</para>

<para>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
<literal>-a</literal> literal forces status for all queues or the
queues in the
<literal>all</literal>
printcap entry to be returned.</para>

<para>The <literal>stalled_time</literal> (default 120 seconds) printcap option can be used to set a
time after which active jobs will be reported as stalled.</para>

</sect1>

<sect1><title><application>lpq</application> Long Format (lpq, lpq -l, lpq -L)</title>

<para>This is the default status display.
It is a nicely formatted, extremely verbose format
that is suitable for humble human interpretation. For example:
<informalexample>
<screen>% 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@lprng110+202228663 A 10663 /tmp/hi     3 20:22:29
Printer: t2@astart110  'Test Printer 2' (routed/bounce to t1@h10.private)
 Queue: no printable jobs in queue
 Status: finished operations at 16:30:08
Printer: t3@astart110  (forwarding to t3a@h10.private)
Printer: t3a@astart110  (forwarding to t2@h10.private)
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</screen>
</informalexample>
</para>

<para>The <command>lpq -l</command> (longer information)
option causes more of the status information to be printed.
You can use increasing numbers of <command>lpq -l</command> options
(<command> lpq -ll</command> also works) to get more status.
Use <command> lpq -L</command> for the maximum amount of status information.</para>

</sect1>

<sect1><title><application>lpq</application> Verbose Format (lpq -v)</title>

<para>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.
<informalexample>
<screen>% 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@lprng110+094352860' at 09:44:00
 Status: printing 'papowell@lprng110+094352860', \
            closing device at 09:44:00
 Status: printing 'papowell@lprng110+094352860', finished  at 09:44:00
 Status: subserver status 'JSUCC' for 'papowell@lprng110+094352860' \
            on attempt 1 at 09:44:00
 Status: finished operations at 09:44:00
 Job: papowell@lprng110+202228663 status= 1
 Job: papowell@lprng110+202228663 CONTROL=
 - Hh10.private
 - Ppapowell
 - J/tmp/hi
 - CA
 - Lpapowell
 - Apapowell@lprng110+202228663
 - Qt1
 - fdfA010663h10.private
 - N/tmp/hi
 - UdfA010663h10.private
 Job: papowell@lprng110+202228663 HOLDFILE=
 - active 0
 - done 0
 - hold 0
 - move 0
....</screen>
</informalexample>
</para>

</sect1>

<sect1 id="stalledtime"><title>Job Taking Too Long - Stalled </title>

<para>Options used:
<itemizedlist>

<listitem>
<para><literal>stalled_time=</literal><emphasis>seconds after which to report a stalled active job</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>The <literal>stalled_time</literal> option is actually used by the <application>lpd</application>
server to report that a job has been active more than the indicated
time with no change in state.
This is useful for spotting things such a printers that are out of paper,
and so forth.</para>

</sect1>

<sect1><title>Configuring Format and Displayed Information</title>

<para>The following sections describe options that are used by the
<application>lpd</application> server to control how it will return status information
to a <application>lpq</application> request.</para>


<sect2 id="classinstatus"><title>Display Class Information </title>

<para>Options used:
<itemizedlist>

<listitem>
<para> <literal>class_in_status</literal> FLAG <emphasis>show class name in status</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>Setting the <literal>class_in_status</literal> option causes the class name rather
than priority to be displayed in the status information.</para>

</sect2>

<sect2 id="reverselpqformat"><title>Reverse Short and Long <application/lpq/ Formats </title>

<para>Options used:
<itemizedlist>

<listitem>
<para> <literal>reverse_lpq_format=</literal> FLAG <emphasis>reverse <application/lpq/ status format for specified remote systems</emphasis></para>
</listitem>

</itemizedlist>
</para>

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

<para>The <literal>reverse_lpq_format=</literal> specifies a list of printers or IP addresses
for which the <application>lpd</application> server will return LONG status when SHORT is
requested,
and vice versa.
For example:
<informalexample>
<screen>reverse_lpq_format=*.eng.com,130.192.0.0/16</screen>
</informalexample>
</para>

<para>will cause hosts whose Fully Qualified Domain Name (FQDN) ends in
<filename>eng.com</filename> or from subnet
<literal remap=tt>130.192.0.0</literal>
to have reversed
status returned.</para>

</sect2>

<sect2 id="shortstatus"><title>Status Line Length and Line Count
</title>

<para>Options used:
<itemizedlist>

<listitem>
<para> <literal>return_short_status=</literal><emphasis>return short <application/lpq/ status for specified remote systems</emphasis></para>
</listitem>

<listitem>
<para>
<literal remap=tt>short_status_length=</literal><emphasis>short <application/lpq/ status length in lines</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>In order to be compatible with non-&LPRng; client programs,
some administrators would like <application>lpd</application> to return a short or brief
status to normal status queries.</para>

<para>The <literal>return_short_status=</literal> specifies a list of printers or IP addresses
for which the <application>lpd</application> server will return an abbreviated
status when LONG status is requested.
For example:
<informalexample>
<screen>return_short_status=*.eng.com,130.192.0.0/16
short_status_length#3</screen>
</informalexample>
</para>

<para>will cause hosts whose Fully Qualified Domain Name (FQDN) ends in
<filename>eng.com</filename> or from subnet
<literal remap=tt>130.192.0.0</literal>
to get only
3 lines of detailed status returned.</para>

</sect2>

<sect2 id="forcelpqstatus"><title><application>lpq</application> Status Format Determined by Requesting Host Address </title>

<para>Options used:
<itemizedlist>

<listitem>
<para> <literal>force_lpq_status=</literal><emphasis>force <application/lpq/ status format for specified remote systems</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>In order to be compatible with non-&LPRng; client programs which
are totally unpredictable,
this allows the administrator to specify the format for <application/lpq/
status when requests arrive.</para>

<para>The <literal>force_lpq_status=</literal> specifies a list of formats
and printers or IP addresses
for which the <application>lpd</application> server will return status
in the specified format.
The entry has the format
<literal>KEY=list;KEY=list...</literal> where
<acronym>KEY</acronym> is
<literal remap=tt>s</literal>
for short and
<literal remap=tt>l</literal>
for long format,
and list is a list of hosts or IP addresses.
For example:
<informalexample>
<screen>force_lpq_status=s=pc*.eng.com,130.192.12.0/24,l=sun*.eng.com</screen>
</informalexample>
</para>

<para>will cause hosts whose Fully Qualified Domain Name (FQDN) matches
<filename>pc*eng.com</filename> or from subnet
<literal remap=tt>130.192.12.0</literal>
to get short
status returned and hosts which match <filename>sun*.eng.com</filename> get
long status.</para>

</sect2>
</sect1>
</chapter>
<chapter id=lprm><title><application/lprm/ - Job Removal Program </title>
<para>
The <application/lprm/ program is used to remove jobs from
a print queue.
You can obtain the available options by using:
<informalexample>
<screen><prompt>h4: {307} % </prompt><userinput>lprm -=</userinput>
lprm: Illegal option '='
 usage: lprm [-A] [-a | -Pprinter] [-Ddebuglevel] (jobid|user|'all')*
  -a           - all printers
  -A           - use authentication
  -Pprinter    - printer (default PRINTER environment variable)
  -Uuser       - impersonate this user (root or privileged user only)
  -Ddebuglevel - debug level
  -V           - show version information
  user           removes user jobs
  all            removes all jobs
  jobid          removes job number jobid
 Example:
    'lprm -Plp 30' removes job 30 on printer lp
    'lprm -a'      removes all your jobs on all printers
    'lprm -a all'  removes all jobs on all printers
  Note: lprm removes only jobs for which you have removal permission</screen>
</informalexample>
</para>

<sect1><title><application>lprm</application> Queue Selection (lprm -Pprinter, lprm -a)</title>
<para>
If no queue is specified,
then the default queue is used,
with <literal>-Pprinter@host</literal> causing a direct
connection to the named host.
The <literal>-a</literal> literal selects all queues.
</para>
</sect1>



<sect1><title><application>lprm</application> Job Selection</title>
<para>
Jobs are selected by providing a job number,
or <literal/glob/ pattern that is matched to the user name
and and job identifier.
If multiple patterns are provided each is applied in turn
against the jobs in a queue until all have been tried.
If any one of the patterns match then the job status is displayed.
</para>
<para>
If no selection information
is provided then the user name
performing the request is used as the selection pattern,
and only the first job that matches is removed.
</para>
<para>
The <literal>all</literal> pattern will match
all jobs in the queue.
</para>
<para>
The user must have permission to control the queue
or to remove a selected job in order for job removal to succeed.
</para>
</sect1>

</chapter>
<chapter id=lpc><title><application/lpc/ - Administration Program </title>

<para>The
<application>lpc</application> command is the main way that the <application>lpd</application> server
is controlled.
Here is the help information displayed by the command:
<informalexample>
<screen><prompt>h4: {308} % </prompt><userinput>lpc -=</userinput>
lpc: Illegal option '='
usage: lpc [-Ddebuglevel][-Pprinter][-Shost][-Uusername][-V] [command]
 with no command, reads from stdin
  -Ddebuglevel - debug level
  -Pprinter    - printer or printer@host
  -Shost       - connect to lpd server on host
  -Uuser       - identify command as coming from user
  -V           - increase information verbosity
 commands:
 active  (printer[@host])        - check for active server
 abort   (printer[@host] | all)  - stop server
 class   printer[@host] (class | off) - show/set class printing
 disable (printer[@host] | all)  - disable queueing
 debug   (printer[@host] | all) debugparms - set debug level for printer
 down    (printer[@host] | all)  - disable printing and queueing
 enable  (printer[@host] | all)  - enable queueing
 hold    (printer[@host] | all) (name[@host] | job | all)* - hold job
 holdall (printer[@host] | all)  - hold all jobs on
 kill    (printer[@host] | all)  - stop and restart server
 lpd     (printer[@host]) - get LPD PID
 lpq     (printer[@host] | all) (name[@host] | job | all)*   - run <application/lpq/
 lprm    (printer[@host] | all) (name[@host]|host|job| all)* - run <application/lprm/
 msg printer message text- set status message
 move printer (user|jobid)* target - move jobs to new queue
 noholdall (printer[@host] | all)- hold all jobs off
 printcap(printer[@host] | all)  - report printcap values
 quit                            - exit LPC
 redirect(printer[@host] | all) (printer@host | off )*     - redirect jobs
 redo    (printer[@host] | all) (name[@host] | job | all)* - redo jobs
 release (printer[@host] | all) (name[@host] | job | all)* - release jobs
 reread  (printer[@host])        - LPD reread database information
 start   (printer[@host] | all)  - start printing
 status  (printer[@host] | all)  - status of printers
 stop    (printer[@host] | all)  - stop  printing
 topq    (printer[@host] | all) (name[@host] | job | all)*  - reorder job
 up      (printer[@host] | all) - enable printing and queueing
   diagnostic:
      defaultq               - show default queue for LPD server
      defaults               - show default configuration values
      client  (printer | all) - client config and printcap information
      server (printer | all) - server config and printcap</screen>
</informalexample>
</para>

<para>Most of the <application>lpc</application> command line options are common to all

<link linkend="lprngclients">&LPRng; Clients</link>,
with the exception of the <literal>-S server</literal> literal.
This option allows the <application>lpd</application> host to be explicitly specified.</para>

<para>The <application>lpc</application> commands can be classified as
<emphasis>informational</emphasis>,
<emphasis>queue management</emphasis>,
<emphasis>problem management</emphasis>,
<emphasis>job scheduling</emphasis>,
and
<emphasis>diagnostic</emphasis>.</para>


<sect1><title>Informational Commands - status, flush, active, reread</title>

<para>
The
<command remap=tt>lpc status</command>
command
displays the current status of various activities of interest to the
system administrator.
This information includes the process ID of the server and other processes.
</para>

<para>
During normal operation,
when requested for job status information the <application/lpd/ server will create
this information and then save it in a status cache.
When successive requests for the same information arrive,
the cache is checked to see if the information is already in the cache
and there have been no status changes.
If this is the case,
the cached status information is used.
The
<command remap=tt>lpc flush</command>
command will flush (delete) this cache information and cause the
<application/lpd/ server to regenerate it from the original
job files.
</para>

<para>The
<command remap=tt>lpc active</command>
command connects to the print server and
gets the Process ID of the <application>lpd</application> process.
This is useful to determine if the <application>lpd</application> server is running
on the print server.</para>

<para>The
<command remap=tt>reread</command>
command connects to the <application>lpd</application> print server and requests
that the server reread the
<filename>printcap</filename>,
<filename>lpd.conf</filename>,
and
<filename>lpd.perms</filename> database files.</para>

</sect1>

<sect1><title>Queue Management - enable, disable, up, down</title>

<para>The
<command remap=tt>enable</command>
and
<command remap=tt>disable</command>
commands enable and disable <emphasis>queuing</emphasis>
or sending jobs to the
print queue.
The
<command remap=tt>up</command>
command combines the
<command remap=tt>enable</command>
and
<command remap=tt>start</command>
commands
while
the
<command remap=tt>down</command>
command combines the
<command remap=tt>disable</command>
and
<command remap=tt>stop</command>
commands.</para>

</sect1>

<sect1><title>Printing Management - start, stop, up, down</title>

<para>These commands are used to start and stop printing.
The
<command remap=tt>up</command>
combines
<command remap=tt>start</command>
and
<command remap=tt>enable</command>,
while
<command remap=tt>down</command>
combines
<command remap=tt>stop</command>
and
<command remap=tt>disable</command>.</para>

</sect1>

<sect1><title>Problem Management - abort, redo, kill</title>

<para>These commands are usually used when there is a problem
printing a job.
The
<command remap=tt>abort</command>
command is used to kill off all printing activity
associated with a job.
It also has the side effect of stopping printing on the print queue.
The
<command remap=tt>redo</command>
command  will redo or attempt to reprint a job.
The
<command remap=tt>kill</command>
command combines the abort and redo command,
and is useful when there are problems with the job currently being printed
and it should be reprinted.</para>

</sect1>

<sect1><title>Job Scheduling - topq, holdall, noholdall, hold, release</title>

<para>The
<command remap=tt>topq</command>
command effectively puts the select job or jobs at the top
of the queue for printing.</para>

<para>The
<command remap=tt>holdall</command>,
<command remap=tt>noholdall</command>,
and
<command remap=tt>release</command>
commands implement a simple holding queue for jobs.
By default,
<command remap=tt>holdall</command>
is disabled.
When the
<command remap=tt>holdall</command>
command enables it,
then jobs will remain in the spool queue until explicitly released
with the
<command remap=tt>release</command>
command.</para>

<para>The
<command remap=tt>hold</command>
command can also be used to hold individual jobs
until released.</para>

</sect1>

<sect1 id="lpcredirect"><title>Queue Management - class, redirect, move </title>

<para>The
<command remap=tt>class</command>
command is used to restrict printing to jobs
whose class value,
set using the <command>lpr -C class</command> option,
is in the class list or matches one of the classes.
This allows the administrator to restrict printing.
For more details and an example of its use,
see Form Support.</para>

<para>The
<command remap=tt>redirect</command>
command allows the administrator to accepts jobs
at this queue and then to have them forwarded or redirected
to another print queue.
This is useful when a printer temporarily is unavailable.</para>

<para>The
<command remap=tt>move</command>
command allows an individual job (or jobs)
to be forwarded or redirected to another print queue.</para>

</sect1>
</chapter>

<chapter id=checkpc><title><application/checkpc/ - Configuration Validation Utility </title>

<para>The <application>checkpc</application> (check printcap file) is one of the most
useful utilities in the &LPRng; package.</para>

<para>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 <application>checkpc</application> is to remove old entries from queue
directories.</para>

<para>For a new installation, you will want to run
<informalexample>
<screen>checkpc -f -V</screen>
</informalexample>

to set the permissions right. The <literal>-f</literal> flag instructs the
program to correct file permissions. If you don't run this as
<literal remap=tt>ROOT</literal>, you'll receive a warning about that fact and any
<function>chown(2)</function> calls will (most likely) fail.</para>

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


<sect1><title>Maintenance</title>

<para>Later, you will want to use <application>checkpc</application> for the daily
maintenance of your system.
I have this line in user
<literal remap=tt>lp</literal>'s
crontab:
<informalexample>
<screen>32 5 * * * checkpc -t 10K -A3 -r &gt;/dev/null 2&gt;&amp;1</screen>
</informalexample>

This job will:
<orderedlist>

<listitem>
<para>truncate all log and accounting files to 10KB (<literal>-t 10K</literal>).
Actually, it will keep the last 10K from the file, starting on a
complete line.</para>
</listitem>

<listitem>
<para>remove all stale files older than three days (<literal>-A3 -r</literal>).</para>
</listitem>

</orderedlist>

I'm redirecting output to <filename>/dev/null</filename>, because <application>checkpc</application>
is a little noisy to my taste. (But too noisy is better than too
silent :)</para>

</sect1>

<sect1><title>Printcap Information</title>

<para>You can use <command>checkpc -V -P</command>
to examine printcaps and tell you what
they contain.
This is identical to the
<command remap=tt>lpc server all</command>
operation,
but with a higher level of verbosity.</para>

</sect1>
</chapter>

<chapter id=printercomm><title>Printer Communication and Protocols </title>

<para>Common communication methods between a printer and a host system
are network connections,
parallel ports,
or serial ports;
while Fibre Channel, SCSI, USB, FireWire,
InfraRed,
and other interesting technologies have been used,
they are either very specialized or not directly
support by the &LPRng; software.
In this section we will discuss Network,
Parallel Port,
and Serial Printers,
as well as the different protocols and standards that apply to them.</para>
<sect1 id="networkprinter"><title>Network Printers </title>

<para>The most flexible and highest throughput printer interface is
via a network (TCP/IP) connection.
Most high performance printers have a built in network interface,
or you can attach them to a
<emphasis>printer server</emphasis>
box which provides a network interface.
The network interface usually supports multiple network printing protocols.
The most common are the LPD (RFC1179), Socket API, AppSocket, SMB,
and Novell Netware interfaces.
&LPRng; directly supports the LPD (RFC1179) and Socket API interfaces,
and you can use the
<application remap=tt>smbclient</application>
program from the

<link linkend="smb">Samba Software Package</link>
 for the SMB interface.</para>

</sect1>

<sect1><title>RFC1179 (LPD) Connection</title>

<para>In this mode of operation the print server actually operates as a very
limited BSD print spooler.
These limitations include:
<orderedlist>

<listitem>
<para>No error messages or status capability</para>
</listitem>

<listitem>
<para>Limited or very primitive banner printing.
On some systems it may be <emphasis>impossible</emphasis>
to turn banner printing off.</para>
</listitem>

<listitem>
<para>On most known print servers high connection activity caused
by multiple systems attempting to get status or spool jobs
may cause catastrophic failure of the printer.</para>
</listitem>

</orderedlist>
</para>

<para>For the above reasons,
using RFC1179 to transfer jobs to a printer should be regarded as the
least desirable option.
Please see

<link linkend="rfc1179ref">The RFC1189 Protocol</link>

for a detailed discussion of the RFC1179 protocol.</para>

<para>In order to use the RFC1179 transfer operation you must have a printcap entry
for the printer that provides:
<itemizedlist>

<listitem>
<para>The IP address or name of the printer that can be resolved to an IP address</para>
</listitem>

<listitem>
<para>The name of the spool queue.
In practice,
this is usually used only to determine which of several printer ports
on the print server the job will be sent to,
or what type of processing the print server will do.
Most cards usually do not do any processing and simply pass the job
through to the printer.</para>
</listitem>

</itemizedlist>
</para>

<para>The following is an example of a simple printcap entry that
can be used to send a job to a remote printer using the RFC1179 protocol:
<informalexample>
<screen># &LPRng; syntax
# :lp value is 'where to print the job'
lp:
  :lp=raw@10.0.0.1

# OR Vintage BSD Print Spooler Syntax
# (&LPRng; supports this as well)
# :rp = remote printer, :rm = remote machine or host
lp:
  :rp=raw:rm=10.0.0.1</screen>
</informalexample>
</para>

<para>If you wish to transfer jobs to a print spooler without using the
full &LPRng; <application>lpr</application> program,
the Perl <literal>lpr_in_perl</literal> program
in the &LPRng; Distribution <acronym>UTILS</acronym> directory
can be used for testing and tutorial purposes.</para>

</sect1>

<sect1 id="socketapi"><title>Socket API </title>

<para>The Socket API is a very flexible job transfer protocol.
It is widely support by most Print Server manufacturers,
with the
Hewlett Packard JetDirect setting the
<emphasis>de facto</emphasis>
standard.
The Socket API is extremely simple.
<orderedlist>

<listitem>
<para>The user establishes a connection to TCP/IP
port on the Printer or Network Print spooler.
The HP JetDirect uses port 9100 by default,
but other ports are used as well.
This connection may be refused if the printer is busy
printing a job.</para>
</listitem>

<listitem>
<para>When the network connection is established to a system which
has an <emphasis>internal printer</emphasis>
or for which the Network Print Spooler
is an integral part of the system,
the printer usually flushes all internal buffers and readies itself
to receive a new job.
However,
when you are using an external Print Server box,
you may need to send specific initialization sequences to the printer
to ensure that it is reset correctly and is ready to receive new jobs.</para>
</listitem>

<listitem>
<para>When the connection is made,
all bytes sent to the connection are either transferred to
and external interface to directly to a
<emphasis>print buffer</emphasis>
used by the printer's Print Engine.</para>
</listitem>

<listitem>
<para>The connection is bidirectional,
and information sent to the external port by an external printer
or error messages and status generated by the printer's Print Engine
will be transferred over the data link to the user.</para>
</listitem>

<listitem>
<para>The Network Print spooler will keep the connection open
until it is closed by the user.
During this period it may continue to report status or other
information such as printer On Line,
paper outages, and so forth.</para>
</listitem>

<listitem>
<para>If the connection to the printer is
<emphasis>half-closed</emphasis>,
that is,
the <function>shutdown()</function> network system call is used to indicate to the
remote printer that no further data will be sent,
then the printer may immediately terminate the network connection.
This means that no further network or status messages will be
sent to the user.</para>
</listitem>

<listitem>
<para>If the connection is to a External Print Server,
then usually the connection can be immediately re-established.
It is the responsibility of the user to ensure that a the printer
has finished its work before sending a new job.</para>
</listitem>

<listitem>
<para>If the connection is to an internal Print Server,
then usually the printer will not allow the connection to be made,
or will refuse all data transfers on the connection until
the printer finishes with the previous job and all internal buffers
have been cleared.</para>
</listitem>

</orderedlist>
</para>

<para>The following is a sample printcap showing how to use the Socket API:
<informalexample>
<screen>lp:
  # make a socket connection to port 9100
  :lp=10.0.0.2%9100</screen>
</informalexample>
</para>

<para>You can use the
<ulink URL="http://www.l0pht.com/~weld/netcat/">netcat</ulink>
utility by Hobbit <email>Hobbit@avian.org</email>
to test that the Socket interface is available and working.
If <filename>ellipse.ps</filename> is a test file, then:
The simplest and easiest way to print a file to a network printer appears
<informalexample>
<screen>  nc printer.ip.addr 9100 &lt; file
Example:
  nc 10.0.0.25 9100 &lt; ellipse.ps</screen>
</informalexample>
</para>

</sect1>

<sect1 id="appsocket"><title>AppSocket TCP/IP Protocol </title>

<para>The AppSocket interface is supported by Tektronix and some other printer
vendors.
It is similar to the Socket API,
with a couple of significant differences.
<orderedlist>

<listitem>
<para>The printer has two ports for network connections:
a TCP port 9100 for TCP/IP stream connections and a UDP port for UDP
packet connections.</para>
</listitem>

<listitem>
<para>When a 0 length UDP packet or a UDP packet containing only <filename>CR/LF</filename>
is sent to UDP port 9101, the printer will return a packet to the sender
containing print status information.
This information indicates the printers current status (busy, idle, printing)
and any error conditions.</para>
</listitem>

<listitem>
<para>The format,
reliability,
and repeatability of the UDP format and information is totally undocumented,
and the UPD port facility should be regarded as,
at best,
an advisory function of the printer.</para>
</listitem>

<listitem>
<para>To send a job to the printer,
a connection to TCP port is made.
This connection will be refused while the printer is busy or has a connection
to another host.</para>
</listitem>

<listitem>
<para>When the TCP connection is established,
the information to be printed can be sent over the TCP connection.
Bytes sent on this stream will be placed in the input buffer of the
Print Engine and processed.</para>
</listitem>

<listitem>
<para>An end of job (EOJ) sequence indication
in the data stream will cause the printer to terminate the connection.
This is different than the Socket API,
where the printer will keep the connection open.
This means that if the PostScript CTRL-D (end of job) character is sent in
a job,  then the connection will be terminated.</para>
</listitem>

<listitem>
<para>Some models of printers modify this behavior slightly and
will not terminate the connection,
but will simply ignore any data following the EOJ indication.</para>
</listitem>

<listitem>
<para>Some printers support bidirectional AppSocket communication,
and while the connection is open will return error indications or status
information.</para>
</listitem>

<listitem>
<para>Once all the data has been received and the job has finished printing,
the connection will be terminated by the printer.</para>
</listitem>

</orderedlist>
</para>

<para>The
<application remap=tt>ifhp</application>
filter,
one of the helper programs for &LPRng;,
is used with &LPRng; to provide AppSocket support.
For details,
please see the <citation>IFHP-HOWTO</citation>
in the
<ulink URL="http://www.private/"><application>ifhp</application> Distribution</ulink>
and
<link linkend="P450">Tektronix P450 and Family</link>
 for details.
The following is a typical printcap entry for
the AppSocket protocol.
The actual network connection to the printer is made by the
<application remap=tt>ifhp</application>
filter:
<informalexample>
<screen>lp:
  # &LPRng; opens a dummy connection for consistency
  :lp=/dev/null
  # we pass the ifhp filter options indicating that the
  # Tektronics printer will need the appsocket protocol
  # and to use port 35 at 10.0.0.1 to make the connection
  # The ifhp filter may open and close the connection several
  # times during the file transfer in order to ensure that
  # the printer handles the job correctly.
  :ifhp=model=tek,appsocket,dev=10.0.0.1%35
  :filter=/usr/local/libexec/filters/ifhp</screen>
</informalexample>
</para>

</sect1>

<sect1 id="secnetwork"><title>Network Print Server Boxes </title>

<para>A <emphasis/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.</para>

<para>The print server may support multiple printing protocols,
such as

<link linkend="rfc1179">RFC1179</link>

(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.</para>

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

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

<para>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
TCP port 9100.</para>

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

</sect1>

<sect1><title>Network Print Server Configuration Information</title>

<para>The following is a list of print server manufacturers,
models,
and with hints on how to access these boxes with various protocols.</para>

<table id=newps frame=all><title>Network Print Server Configuration Information</title>
<tgroup cols=4 align=left colsep=1 rowsep=1>
<thead>
<row><entry>Manufacturer</entry><entry>Model</entry><entry>RFC1179 Port Name (rp=XXX)</entry><entry>Send to TCP port</entry></row>
</thead>
<tbody>
<row><entry><ulink URL="http://www.cannon.com/">Cannon Printer</ulink></entry><entry>Cannon 460 PS, no hard drive</entry><entry>xjdirect</entry><entry>- Unknown if supported -</entry></row>
<row><entry></entry><entry>Cannon 460 PS hard drive</entry><entry><literal remap=tt>xjprint</literal>
- print immediately,<literal remap=tt>xjhold</literal>
- print later</entry><entry>- Unknown if supported -</entry></row>
<row><entry><ulink URL="http://www.digprod.com/">Digital Products Inc.</ulink></entry><entry>NETPrint Print Server</entry><entry><literal remap=tt>PORT</literal><literal>n</literal>, where <literal>n</literal>
is port on server</entry><entry>- Unknown if supported -</entry></row>
<row><entry><ulink URL="http://www.efi.com/">Electronics For Imaging Inc.</ulink></entry><entry>Fiery RIP i series</entry><entry><literal remap=tt>normalq</literal>
or
<literal remap=tt>urgentq</literal></entry><entry>- Unknown if supported -</entry></row>
<row><entry></entry><entry>Fiery RIP XJ series</entry><entry><literal remap=tt>xjprint</literal></entry><entry>- Unknown if supported -</entry></row>
<row><entry></entry><entry>Fiery RIP XJ+ and SI series</entry><entry><literal>print_</literal><literal>Model</literal>, e.g. <literal>print_DocuColor</literal></entry><entry>- Unknown if supported -</entry></row>
<row><entry></entry><entry>Fiery models ZX2100, ZX3300, X2, X2e</entry><entry><literal remap=tt>print</literal></entry><entry>- Unknown if supported -</entry></row>
<row><entry><ulink URL="http://www.emulex.com/">Emulex Corp.</ulink></entry><entry>NETJet/NETQue print server</entry><entry><acronym>PASSTHRU</acronym></entry><entry>- Unknown if supported -</entry></row>
<row><entry><ulink URL="http://www.extendsys.com/">Extended Systems Inc.</ulink></entry><entry>ExtendNet Print Server</entry><entry><literal remap=tt>Printer<replaceable>n</replaceable></literal>, where <literal>n</literal>
is port on server</entry><entry>- Unknown if supported -</entry></row>
<row><entry><ulink URL="http://www.hp.com/">Hewlett-Packard</ulink></entry><entry>JetDirect interface card</entry><entry><literal remap=tt>raw</literal></entry><entry>9100</entry></row>
<row><entry><ulink URL="http://www.hp.com/">Hewlett-Packard</ulink></entry><entry>JetDirect Multiport Server</entry><entry><literal remap=tt>port 1 - raw1, port 2 - raw2, etc.</literal></entry><entry>port 1 - 9100, port 2 - 9101, etc.</entry></row>
<row><entry><ulink URL="http://www.i-data.com/">I-Data</ulink></entry><entry>Easycom 10 Printserver</entry><entry><literal remap=tt>par1</literal>
(parallel port 1)</entry><entry>- Unknown if supported -</entry></row>
<row><entry></entry><entry>Easycom 100 Printserver</entry><entry><literal remap=tt>LPDPRT1</literal></entry><entry>- Unknown if supported -</entry></row>
<row><entry><ulink URL="http://www.printers.ibm.com/">IBM</ulink></entry><entry>Network Printer 12, 17, 24, and 24PS</entry><entry><acronym>PASS</acronym></entry><entry>- Unknown if supported -</entry></row>
<row><entry><ulink URL="http://www.lantronix.com/">Lantronix</ulink></entry><entry>EPS1, EPS2</entry><entry><literal remap=tt>EPS_<literal>X_S1 (serial) port 1, EPS_X</literal>_P1 (parallel) port 2</literal>, etc.</entry><entry>3001 (port 1), 3002 (port 2), etc.</entry></row>
<row><entry><ulink URL="http://www.qms.com/">QMS</ulink></entry><entry>Various Models</entry><entry><acronym>RAW</acronym></entry><entry>35 (AppSocket)</entry></row>
<row><entry><ulink URL="http://www.tek.com">Tektronix</ulink></entry><entry>Tektronix printer network cards</entry><entry><acronym>PS</acronym> (PostScript), <acronym>PCL</acronym> (PCL), or <acronym>AUTO</acronym>(Auto-selection between PS, PCL, or HPGL). Not reliable.</entry><entry>9100 (AppSocket on some models)</entry></row>
<row><entry><ulink URL="http://www.rosel.com">Rose Electronics</ulink></entry><entry>Microserve Print Servers</entry><entry>lp</entry><entry>9100</entry></row>
<row><entry><ulink URL="http://www.xerox.com/">Xerox</ulink></entry><entry>Models 4505, 4510, 4517, 4520</entry><entry><acronym>PASSTHRU</acronym></entry><entry>2501 (AppSocket on some models)</entry></row>
<row><entry></entry><entry>Model 4512</entry><entry><literal remap=tt>PORT1</literal></entry><entry>10001 (programmable)</entry></row>
<row><entry></entry><entry>Model N17</entry><entry><acronym>RAW</acronym></entry><entry>9100</entry></row>
<row><entry></entry><entry>Models N24 and N32</entry><entry><acronym>RAW</acronym></entry><entry>2000</entry></row>
<row><entry></entry><entry>Models 4900, 4915, 4925, C55</entry><entry><acronym>PS</acronym></entry><entry>2000</entry></row>
<row><entry></entry><entry>Document Centre DC220/230</entry><entry><literal remap=tt>lp</literal></entry><entry>- Unknown if supported -</entry></row>
</tbody>
</tgroup>
</table>

<para>All company, brand, and product names are properties of their respective owners.</para>

</sect1>

<sect1><title>HP JetDirect Interface</title>
<para>
The HP JetDirect Interface is one of the most widely used for network printers.
For this reason it also has the most widely known set of problems.
The user is strongly urged to upgrade to the latest version of firmware
available for the unit.
Problems with older versions of firmware include system lockups that
require powerup level resets.
</para>
<para>
Newer versions of the HP JetDirect Interface have a Web Browser based
configuration system.
After you have assigned an IP address to the printer you can connect to
the configuration port and configure the printer using the
browser.
If you run into configuration problems then you will most likely need to
use the Microsoft Windows based JetDirect Configuration Software to
reset or reconfigure the printer.
</para>
<para>Older HPJetDirect  cards can  be configured only through through the front
panel  or through a set of network files.  Here is a summary
of  the  methods  used  from  UNIX  systems, or to use when you are
desperate, to configure the printer.</para>


<sect2><title>Resetting To Factory Defaults</title>
<para>
Most internal HP JetDirect print servers can be reset to factory
defaults
(or cold-reset) by turning the printer off and holding down the
<literal/Online/ or
<literal/Go/
button while turning the printer back on. The printer control
panel display should read
<literal/Cold Reset/,
<literal/Restoring Factory Settings/,
or something similar.
</para>
</sect2>
<sect2><title>Setting Up IP Networking and Address</title>

<para>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:
<informalexample>
<screen> MENU  -&gt; MIO MENU (use MENU to display MIO MENU)
 ITEM  -&gt; CFG NETWORK=NO*
 +     -&gt; CFG NETWORK=YES
 ENTER -&gt; CFG NETWORK=YES*
 ITEM  -&gt; TCP/IP=OFF* (use ITEM to display TCP/IP)
 +     -&gt; TCP/IP=ON
 ENTER -&gt; TCP/IP=ON*
 ITEM  -&gt; CFG TCP/IP=NO* (use ITEM to display TCP/IP)
 +     -&gt; CFG TCP/IP=YES
 ENTER -&gt; CFG TCP/IP=YES*
 ITEM  -&gt; BOOTP=NO*
     (Enable BOOTP if you want to - see below)
 ITEM  -&gt; 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  -&gt; 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  -&gt; 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  -&gt; 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  -&gt; TIMEOUT=90
      This is the connection timeout value.  It puts a limit
     on time between connections.  A value of 10 is reasonable.</screen>
</informalexample>
</para>

</sect2>

<sect2><title>BOOTP Information</title>

<para>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.
<informalexample>
<screen># 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":</screen>
</informalexample>
</para>

<para>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.
<informalexample>
<screen>#
# 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 selectible 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</screen>
</informalexample>
</para>

</sect2>
<sect2><title>Telnet Configuration</title>
<para>
You can telnet to the JetDirect interface and use the command line
mode.
There is a very simple help facility available that you can invoke
by entering <literal/?/ at the prompt.
</para>
<para>
Different versions of HP JetDirect cards have different commands.
You will have to experiment to find out which ones are supported.
</para>
</sect2>
<sect2><title>Disabling Banner Page Generation</title>
<para>
You can do this by making a telnet connection
to the JetDirect interface and use the command line
mode.
There is a very simple help facility available that you can invoke
by entering <literal/?/ at the prompt.
</para>
<para>
The configuration command <literal/banner: 0/ will disable banners.
You may need to save the configuration after you have changed it
and then do a power up reset of the printer to have it take effect.
</para>
</sect2>
</sect1>

<sect1><title>Problems With Network Print Servers</title>
<para>
Most of the Network Print Servers are implemented using extremely
simply software.  The following is a list of some problems
and what options the &LPRng; software uses to handle them.
</para>


<sect2><title>Network Print Server Not Responding</title>
<para>
Only a single TCP/IP connection is accepted at a time.
This means that when one user is sending a job then the
unit will not accept other connections.
There is another side effect of this problem,
which is that some implementations will accept a network
connection but not read any data from the connection until
the previous connection is finished.
</para>
<para>
The deal with these problems the <literal/connect_timeout/,
<literal/send_job_rw_timeout/,
and
<literal/send_query_rw_timeout/
are used to control job transfer and lpq status gathering.
See
<link linkend="printingjob">Printing Job Files</link>
and
<link linkend="opendevice">Opening the Output Device</link>
for details.
</para>
</sect2>

<sect2><title>Network Print Server Does Not Handle LPQ, LPRM</title>
<para>
Some Network Print Servers do not respond to <literal/lpq/ or
<literal/lprm/ queries correctly.
The <literal/remote_support/ option can be used to solve this
problem by specifying what operations the remote print server
can handle:
<informalexample>
<screen>:remote_support=RMQVC
  R = lpr, M = lprmg, Q = lpq, V = lpq -v, C = lpc
  :remote_support=R  # printer only handles LPR
</screen>
</informalexample>
</para>
</sect2>

<sect2><title>Incomplete Job Transfers</title>

<para>
This is the result of a defective or buggy TCP/IP stacks.
A common problem is the habit that some
Network Print Servers occaisionally discard data at the end of a print job
when a network connection is <emphasis/half-closed/.
A <emphasis/half-closed/ connection is one where one end of the sending
connection indicates that no further data will be sent.
Unfortunately,  the Network Print Server will then try to close the
connection in the other direction.  When this does not immediately succeed,
it will terminate the network connection, discarding any unprinted data.
</para>

<para>
The <literal/half_close/ flag can be used to solve this problem.
See
<link linkend="normalterm">Normal Termination </link>
for more details.
<informalexample>
<screen>lp:lp=lp@remote        # shutdown(fd,WRITE) connection, wait for end
lp:lp=lp@remote:half_close@    # close() connection and do not wait
</screen>
</informalexample>
</para>
</sect2>
</sect1>

<sect1><title>Printing to a SMB (MicroSoft) Printer</title>

<para>Microsoft use the SMB (Simple Message Block) protocol to transfer files
and print jobs to hosts and printers.
SMB can be used over TCP/IP, NetBEUI, IPX,
and other lower level network protocols.</para>

<para>Unfortunately,
most printers do not provide detailed status or error reports
when using the SMB protocol.
There are a very large number of printers that have deficient SMB
support that causes problems when used in a high traffic or
high throughput environment.</para>

<para>It is highly recommended that this protocol not be used unless there
is no alternative.</para>

<para>If you have a printer or a remote print spooler that supports
SMB You can use the

<link linkend="smb">SAMBA</link>

<application remap=tt>smbclient</application>
program to send a print job to an SMB client.
The following is a sample
Shell Script script which you can use:
<informalexample>
<screen>#!/bin/sh -x
# This script is an input filter for printing on a unix machine. It
# uses the smbclient program to print the file to the specified smb-based
# server and service.
# The 'smb' printcap entry below shows how to configure LPRng
# for printing
#
# smb:
#  :lp=|/usr/local/samba/smbprint
#  :sd=/var/spool/smb:
#  :filter= ... filter ...
#
# The /var/spool/smb/.config file should contain:
#   server="PC_SERVER"
#   service="PR_SHARENAME"
#   password="PASSWORD"
#
# Set PC_SERVER to the server, PR_SHARENAME to the printer,
# and PASSWORD to the password for this service.
#
# E.g.
#   server=PAULS_PC
#   service=CJET_371
#   password=""
#
#
config_file=.config
if [ -f $config_file ] ; then
    eval `/bin/cat $config_file`
fi
#
# NOTE You may wish to add the line `echo translate'
# if you want automatic
# CR/LF translation when printing.
(
#   echo translate
    echo "print -"
    /bin/cat
) | /usr/local/bin/smbclient "\\\\$server\\$service" \
   "$password" -U "$server" -N -P 1&gt;&amp;2</screen>
</informalexample>
</para>

<para>If the above script was in <filename>/usr/local/libexec/filters/smbprint</filename>,
the printcap entry for this printer would be:
<informalexample>
<screen>pauls_pc:
  :sd=/var/spool/lpd/%P
  # we filter the output
  :lp=|/usr/local/libexec/filters/smbprint
  # you can add filters if you want to do specific actions
  :ifhp=model=hp4
  :filter=/usr/local/libexec/filters/ifhp</screen>
</informalexample>
</para>

</sect1>

<sect1><title>Printing to AppleTalk Printers</title>

<para>The
<link linkend="appletalk">netatalk</link>

package comes with the
<application remap=tt>pap</application>
program that can be used to transfer jobs using the AppleTalk
protocol.
A printcap entry for a network printer looks like the following:
<informalexample>
<screen>atalk:
  :lp=| -$ /usr/local/atalk/bin/pap -e -p "npbname"
  :sd=/var/spool/lpd/atalk
  :ifhp=model=ps,status@
  :filter=/usr/local/libexec/filters/ifhp</screen>
</informalexample>
</para>

<para>
The <literal/-$/ suppress the addition of extra parameters to the
<application/pap/ command line.
The
<application remap=tt>pap</application>
program must be
SETUID root and executable only by root or group <literal>daemon</literal>.
This can be done by using the following script:
<informalexample>
<screen><prompt>h4: {309} </prompt><userinput>cd /usr/local/atalk/bin</userinput>
<prompt>h4: {310} </prompt><userinput>chown root pap</userinput>
<prompt>h4: {311} </prompt><userinput>chgrp daemon pap</userinput>
<prompt>h4: {312} </prompt><userinput>chmod 550 pap</userinput>
<prompt>h4: {313} </prompt><userinput>chmod s+u pap</userinput></screen>
</informalexample>
</para>

</sect1>

<sect1><title>Parallel Port Printers</title>

<para>When installing a parallel port printer,
there are three elements of concern:
the physical hardware connection (cable and connectors),
the IO device and Operating System support,
and finally the print spooler support for the device.</para>

<para>Originally most parallel port devices were strictly
<emphasis>write only</emphasis>
with a minimum amount of error information -
only hardware signals for
<emphasis>online</emphasis>,
<emphasis>out of paper</emphasis>
and
<emphasis>fault</emphasis>
were present.
Due to the lack of any other way to interface devices to the
Intel based PC platform,
desperate hardware designers in search of a cheap (free?)
way to interface their IO devices developed ways to manipulate the
signals and do <emphasis>bidirectional</emphasis>
communication.
Needless to say,
no two companies developed the same methods,
and no two companies were compatible with any other companies method.</para>

<para>In 1994, the IEEE 1284 standard was first developed,.
The following information is courtesy of Warp Nine Engineering,
of San Diego, CA, from the
<ulink URL="http://www.fapo.com/1284int.htm">http://www.fapo.com/1284int.htm</ulink>
web page.</para>

<para>When IBM introduced the PC, in 1981, the parallel printer port was
included as an alternative to the slower serial port as a means
for driving the latest high performance dot matrix printers. The
parallel port had the capability to transfer 8 bits of data at time
whereas the serial port transmitted one bit at a time. When the PC
was introduced, dot matrix printers were the main peripheral that
used the parallel port. As technology progressed and the need for
greater external connectivity increased, the parallel port became
the means by which you could connect higher performance peripherals.
These peripherals now range from printer sharing devices, portable
disk drives and tape backup to local area network adapters and CD
ROM players.</para>

<para>The problems faced by developers and customers of these peripherals
fall into three categories. First, although the performance of the
PC has increased dramatically, there has been virtually no change
in the parallel port performance or architecture. The maximum data
transfer rate achievable with this architecture is around 150
kilobytes per second and is extremely software intensive. Second,
there is no standard for the electrical interface. This causes many
problems when attempting to guarantee operation across various
platforms. Finally, the lack of design standards forced a distance
limitation of only 6 feet for external cables.</para>

<para>In 1991 there was a meeting of printer manufacturers to start
discussions on developing a new standard for the intelligent control
of printers over a network. These manufacturers, which included
Lexmark, IBM, Texas Instruments and others, formed the Network
Printing Alliance. The NPA defined a set of parameters that, when
implemented in the printer and host, will allow for the complete
control of printer applications and jobs.
While this work was in progress it became apparent that to fully
implement this standard would require a high performance bi-directional
connection to the PC. The usual means of connection, the ordinary
PC parallel port, did not have the capabilities required to meet
the full requirements or abilities of this standard.</para>

<para>The NPA submitted a proposal to the IEEE for the creation of a
committee to develop a new standard for a high speed bi-directional
parallel port for the PC. It was a requirement that this new standard
would remain fully compatible with the original parallel port
software and peripherals, but would increase the data rate capability
to greater than 1M bytes per second, both in and out of the computer.
This committee became the IEEE 1284 committee.
The IEEE 1284 standard, "Standard Signaling Method for a Bi-directional
Parallel Peripheral Interface for Personal Computers", was approved
for final release in March of 1994.</para>

<para>Even if your hardware has support for the
IEEE 1284 high speed bidirectional data transfers,
your Operating System drivers must support it.
Unfortunately,
there is no universal agreement on the capabilities that should
be provided by the low level printer port device drivers,
and the method for supporting them.
A good example of the problem of OS support and drivers
is given by the Linux Parallel Port group,
currently headed by Tim Waugh,
and which is documented in the
<ulink URL="http://people.redhat.com/twaugh/parport/">http://people.redhat.com/twaugh/parport/</ulink>
and
<ulink URL="http://people.redhat.com/twaugh/parport/html/parportguide.html">http://people.redhat.com/twaugh/parport/html/parportguide.html</ulink>
web pages.</para>

<para>Given this state of affairs,
it should be no surprise that there is no support
for bidirectional parallel port printers in &LPRng;.
In fact,
it turns out that there are severe problems with many Unix implementations
that cause extreme headaches.
These include:
<itemizedlist>

<listitem>
<para>While a parallel port may be opened Read/Write,
a <function>read()</function> will either block indefinitely or cause a major system
failure (crash).</para>
</listitem>

<listitem>
<para>There is no buffering of data in the low level driver;
that is,
once a <function>write()</function> starts,
then the process will block until the data is written out.
If the <function>write()</function> call is interrupted by a
<emphasis>signal</emphasis>
and not immediately restarted,
then the status returned by the <function>write()</function>
may be an error indication (<literal>-1</literal>)
or the numbers of bytes that were actually written.
If an error is returned,
then an unknown number were written and the print job
must be aborted.</para>
</listitem>

<listitem>
<para>Many Operating systems do not implement a <function>select()</function>
functionality for the parallel port,
which means that it is difficult to do a multi-threaded implementation.
Instead,
a polling method must be used.</para>
</listitem>

</itemizedlist>
</para>

<para>The good news is that on all known systems,
if the parallel port device is opened exclusively for writing,
and a blocking <function>write()</function> is used,
and the <function>write()</function> is not interrupted,
and there are no device errors,
then data is delivered correctly to the device.</para>

<para>In most UNIX systems the printer port has the name
<filename>/dev/lpt</filename>,
<filename>/dev/prn</filename>,
or something similar.
On most systems the
<application remap=tt>dmesg</application>
utility will print a list of IO devices found
during system configuration.
Use the following commands to get the information and scan
for the device.
You should also make sure that the printer device is
available.
<informalexample>
<screen>dmesg &gt;/tmp/a
grep lp /tmp/a
ls /dev/lp*</screen>
</informalexample>
</para>

<para>Gordon Haverland
<email>haverlan@agric.gov.ab.ca</email> supplied this little script,
that will assist with this:
<informalexample>
<screen>#!/bin/sh
#set -v -x              # uncomment for debugging
PATH=/bin:/usr/bin
printer=
for printer in /dev/lp* ;
do
    echo PRINTER TEST to $printer 1&gt;&amp;2
    for i in 1 2 3 4 5 6 7 8 9;
    do
        echo PRINTER $printer $i &gt; $printer;
    done
    echo -e \\r\\f &gt; $printer
done
exit 0;</screen>
</informalexample>
</para>

<para>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 <emphasis/staircase/ effect, you will see the numbers
marching across the page, otherwise the numbers will all be in
a single column.</para>

</sect1>

<sect1 id="secserial"><title>Serial Printers </title>

<para>If your printer is attached by a serial line,
then you may need to set the serial line characteristics 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.</para>

<para>1. Check to make sure that the line is not enabled for login.
Logins are usually managed by the
<application remap=tt>getty</application>
(BSD)
or
<application remap=tt>ttymon</application>
(Solaris, SystemV).
Check your system documentation and make sure that these daemons are not
managing the serial line.</para>

<para>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 <application/lpd/ server
and change the permissions to 0600.</para>

<para>3. Make sure that you can print a test file on the printer via the
serial port.
This may require setting the line characteristics 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.
<informalexample>
<screen>#!/bin/sh
# 9600, no echo, no CR
FLAGS= 9600 -raw -parenb cs8 crtscts
DEV= /dev/tty01
(stty $FLAGS; stty 1&gt;&amp;2; cat $1 ) &lt;$DEV &gt;$DEV</screen>
</informalexample>
</para>

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

<para>Experience has shown that serially connected printers are the least
reliable and lowest speed.
Where possible,
it is strongly recommended that they be attached to a <emphasis>network print box</emphasis>
which will provide a
Socket API interface and handle the low level network to serial port protocol
conversions.</para>

</sect1>
</chapter>

<chapter id=printcapref><title>Printcap Database </title>

<para>As described in the

<link linkend="overview">Print Spooling Overview</link>,
the heart of the &LPRng; system is information in the <filename>printcap</filename>
file.
The printcap information specifies:
<orderedlist>

<listitem>
<para>The print queues available to users.</para>
</listitem>

<listitem>
<para>How client programs communicate with the <application>lpc</application> print server.</para>
</listitem>

<listitem>
<para>The configuration,
location,
and other information for each print queue on the print server.</para>
</listitem>

<listitem>
<para>How the <application>lpd</application> server processes jobs in each print queue.</para>
</listitem>

</orderedlist>
</para>

<para>In order to explain a complex subject,
we will start with a set of simple printer configurations,
and explain the purpose and effect of each entry in the printcap.</para>

<para>For details about individual printcap
options, see the
<citerefentry/<refentrytitle/printcap/<manvolnum/5//
man page from the &LPRng; distribution,
or use the
<link linkend="optionindex">Index To All The Configuration and Printcap Options</link>
 to find a specific
printcap option and its effects.</para>


<sect1 id="printcapparse"><title>The Printcap Parsing Rules </title>
<para>Options used:
<itemizedlist>

<listitem>
<para>
<literal>client</literal> FLAG <emphasis> printcap entry valid only for client programs </emphasis></para>
</listitem>

<listitem>
<para>
<literal>oh=</literal><emphasis> hosts where printcap entry valid</emphasis></para>
</listitem>

<listitem>
<para>
<literal>server</literal><emphasis> printcap entry valid only for lpd server </emphasis></para>
</listitem>

<listitem>
<para>
<literal>tc</literal><emphasis> add named printcap entry contents </emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>In this section,
we will discuss the remaining tricky parts of the &LPRng; printcap
database:
combined client and server printcaps,
host specific printcap entries,
and the
<literal>tc</literal>
<emphasis>include</emphasis>
facility.</para>

<para>The following is a complete description of how a printcap file is processed:
<orderedlist>

<listitem>
<para>When processing a printcap file,
the &LPRng; software reads and parses each entry individually.
Leading whitespace is removed.
Lines starting with
<literal remap=tt>#</literal>
and blank lines are ignored.</para>
</listitem>

<listitem>
<para>Lines ending with
<literal remap=tt>\</literal>
will have the
<literal remap=tt>\</literal>
discarded,
and all lines of a printcap entry are joined by removing the line separators (<literal>\n</literal>)
and replacing them with a space.</para>
</listitem>

<listitem>
<para>The printcap entry is parsed,
and the printcap name, aliases, and options are determined.
Colons
<literal remap=tt>:</literal>
act as option separators,
and leading and trailing whitespaces are removed.
</para>
</listitem>

<listitem>
<para>Options are sorted and except for the
<literal>tc=...</literal> option only the last option setting is retained.</para>
</listitem>

<listitem>
<para>If an option value requires a colon, then the
<literal/\:/ or
<literal/\072/,
the same escaped character value as used in the C, Perl, tcl, and other programming
languages,
can be used.
</para>
</listitem>

<listitem>
<para>Client programs will discard a printcap entry with a
<literal>server</literal>
option
and
server programs will discard a printcap entry with a
<literal>client</literal>
options.</para>
</listitem>

<listitem>
<para>The
<literal>oh</literal>
(<emphasis remap=bf>o</emphasis>n this
<emphasis remap=bf>h</emphasis>ost) option specifies a list of
IP addresses and mask pairs or glob strings which are used to determine
if this printcap entry is valid for this host
(see discussion below).</para>
</listitem>

<listitem>
<para>After the above processing,
if there is an existing termcap entry with the same name,
the two sets of options are combined,
with the last option setting retained except for the
<literal>tc</literal>
entries
which are combined.</para>
</listitem>

<listitem>
<para>When a printcap entry is actually used,
the printcap entries listed by the
<literal>tc</literal>
include option
are extracted and combined in order.
This allows include entries to appear after the referring printcap entry.
Then printcap options will be combined with the included ones.
This has the effect that the options specified in the printcap entry
will override the ones from the
<literal>tc</literal>
included entries.</para>
</listitem>

<listitem>
<para>Finally,
each string printcap option with a <emphasis>%X</emphasis>
value
has <emphasis>%X</emphasis>
replaced by the following values.
Unspecified values will not be modified.
<informaltable frame=all>
<tgroup cols=3 align=left rowsep=1 colsep=1>
<thead>
<row><entry>Key</entry><entry>Value</entry><entry>Purpose</entry></row>
</thead>
<tbody>
<row><entry><literal>%P</literal></entry><entry>printcap entry primary name</entry></row>
<row><entry><literal>%Q</literal></entry><entry>queue requested</entry></row>
<row><entry><literal>%h</literal></entry><entry>short host name  (host)</entry></row>
<row><entry><literal>%H</literal></entry><entry>fully qualified host name  (host.dns.whatever)</entry></row>
<row><entry><literal>%R</literal></entry><entry>remote printer (rp value)</entry></row>
<row><entry><literal>%M</literal></entry><entry>remote host (rm value)</entry></row>
<row><entry><literal>%D</literal></entry><entry>date in YYYY-MM-DD format</entry></row>
</tbody>
</tgroup>
</informaltable>
</para>
</listitem>

<listitem>
<para>When parsing multiple printcap files,
these are processed in order,
and all of their printcap entries are combined according to the
above procedures.
The
<literal>tc</literal>
resolution and <emphasis>%X</emphasis>
expansion is done after all the files
have been processed.</para>
</listitem>

</orderedlist>
</para>

<para>The following examples show how to use the above rules
to your advantage.
You can combine both client and server printcap information in
a single file
as well as dividing a printcap entry into several parts.
Here is an example:
<informalexample>
<screen># seen by both client and server
lp1:lp=lp@pr1:mx=100
lp1:sd=/usr/local/spool/lp1:mx=0
# seen only by client
lp2:lp=lp@pr2:client
# seen only by server
lp2:lp=/dev/lp:server</screen>
</informalexample>

<orderedlist>

<listitem>
<para>Printcap entries with the same name are combined.
The first printcap entry,
<literal>lp1</literal>,
the information is seen by both client and server.
The next printcap entry,  with the same name
<literal>lp1</literal>,
will be combined with the first one.
The order of options is important - the entries are scanned in order
and an option will have the last value set.
Thus,
after having read both the
<literal>lp1</literal>
printcap entries,
both client and server will have:
<informalexample>
<screen>lp1:lp=lp@pr1
  :mx=0
  :sd=/usr/local/spool/lp1</screen>
</informalexample>
</para>
</listitem>

<listitem>
<para>The
<literal>lp2</literal>
has a client and server version.
This is recommended when complex printcaps on multiple hosts and servers
are used.
Thus, the &LPRng; clients will see:
<informalexample>
<screen>lp1
  :lp=lp@pr1
  :mx=0
  :sd=/usr/local/spool/lp1
lp2
  :client
  :lp=lp@pr2</screen>
</informalexample>

and the server will see:
<informalexample>
<screen>lp1
  :lp=lp@pr1
  :mx=0
  :sd=/usr/local/spool/lp1
lp2
  :lp=/dev/lp
  :server</screen>
</informalexample>
</para>
</listitem>

</orderedlist>
</para>

<para>If you have multiple printers of the same type whose configuration is almost
identical,
then you can define a set of <emphasis>tc</emphasis>
only printcap entries containing common
information
and use the
<literal>tc</literal>
include facility.</para>

<para>Printcap entry names
may start with
period (<literal remap=tt>.</literal>),
question mark (<literal remap=tt>?</literal>),
or
exclamation mark (<literal remap=tt>!</literal>),
followed by one or more
alphanumeric , underscore (<literal>_</literal>) or hyphen (<literal>-</literal>) characters.
Queue or printer names start with an alphanumeric character.
Printcap entries whose names do not start with
an alphanumeric character
can only be used as targets of the
<literal>tc</literal>
include facility.
For example:
<informalexample>
<screen>.hp:
  :sd=/usr/local/spool/%P
  :mx=0
hp1:tc=.hp,.filter
  :lp=lp@10.0.0.1
hp2:tc=.hp,.filter
  :lp=lp@10.0.0.2
.filter
  :filter=/usr/local/libexec/filters/ifhp</screen>
</informalexample>

<orderedlist>

<listitem>
<para>The <filename>.hp</filename> and <filename>.filter</filename> printcap entities are
not spool queue definitions.
After
<literal>tc</literal>
include processing is completed,
the printcap information would resemble:
<informalexample>
<screen>hp1
  :lp=lp@10.0.0.1
  :filter=/usr/local/libexec/filters/ifhp
  :mx=0
  :sd=/usr/local/spool/%P
hp2
  :lp=lp@10.0.0.2
  :filter=/usr/local/libexec/filters/ifhp
  :mx=0
  :sd=/usr/local/spool/%P</screen>
</informalexample>
</para>
</listitem>

<listitem>
<para>The <literal>%X</literal>
processing will replace
<literal remap=tt>%P</literal>
with the
printcap name,
so we would have:
<informalexample>
<screen>hp1
  :lp=lp@10.0.0.1
  :filter=/usr/local/libexec/filters/ifhp
  :mx=0
  :sd=/usr/local/spool/hp1
hp2
  :lp=lp@10.0.0.2
  :filter=/usr/local/libexec/filters/ifhp
  :mx=0
  :sd=/usr/local/spool/hp2</screen>
</informalexample>
</para>
</listitem>

</orderedlist>
</para>

</sect1>

<sect1 id="simple"><title>Simple Client Printcap Entry

</title>

<para>Options used:
<itemizedlist>

<listitem>
<para>
<literal>client</literal> FLAG <emphasis> client printcap entry </emphasis></para>
</listitem>

<listitem>
<para> <literal>lp=</literal><emphasis>destination printer information</emphasis></para>
</listitem>

<listitem>
<para> <literal>rm=</literal><emphasis>remote host (machine)</emphasis></para>
</listitem>

<listitem>
<para> <literal>rp=</literal><emphasis>remote printer</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>I'll use this simple example to explain the basics of the &LPRng;
printcap format
and introduce some of the &LPRng; network configuration options.
Here is a simple printcap file used to provide client programs
(<literal>lpr, lprm,</literal>
etc)
with <emphasis>remote printer</emphasis>
and <emphasis>server</emphasis>
information.
<informalexample>
<screen># printer lp1
lp1|printer1
  :rm=localhost
# printer lp2 with continuation
lp2:\
  :lp=pr@10.0.0.1:client
# printcap lp3, to printer pr, with overrides
lp3:rp=pr:rm=hp.private
  :force_localhost@
# Simplest possible printcap entry - defaults for everything
lp4</screen>
</informalexample>

<orderedlist>

<listitem>
<para>Lines starting with a
<literal remap=tt>#</literal>
sign are comments, and all
leading and trailing <emphasis>whitespace</emphasis>,
i.e. - spaces, tabs, etc, are ignored.
Empty lines are ignored as well.</para>
</listitem>

<listitem>
<para>A printcap entry starts with the printcap entry <emphasis remap=bf>name</emphasis>,
followed by one or more <emphasis>aliases</emphasis>,
followed by one or more options.
In the above example we have three printcap entries:
<literal>lp1</literal>
with an alias
<literal>printer1</literal>
and
<literal>lp2</literal>,
<literal>lp3</literal>, and
<literal>lp4</literal>
with no aliases.</para>
</listitem>

<listitem>
<para>Aliases start with the
<literal remap=tt>|</literal>
character and options with the
<literal remap=tt>:</literal>
character;
tabs and spaces before and after the
<literal remap=tt>|</literal>
or
<literal remap=tt>:</literal>
characters
and at the start and end of lines are ignored.
You can use backslash (<literal remap=tt>\</literal>) at the end of a line to create a multi-line
value for an option.
The backslash will cause the next line to be appended to the
current line;
watch out for comments and ends of printcap entries if you use this facility.
As you can see from the example,
there is no
<literal>Name</literal>
printcap entry - this is part of the
<literal>cm</literal>
option on the previous line.</para>
</listitem>

<listitem>
<para>Options take the form of a
keyword/value pair, i.e.-
<literallayout>:option=value
:option#value   (legacy, not advised for new systems)
:option
:option@</literallayout>
</para>
</listitem>

<listitem>
<para>Option names are case insensitive, but option values are not.
While
<literal>Ts</literal>
and
<literal>ts</literal>
are the same option name,
<literal>ts=Testing</literal> and <literal>ts=testing</literal> have their case preserved.
A string or integer value is specified by <literal>option=value</literal>
or
<literal>option#value</literal>.</para>
</listitem>

<listitem>
<para>The use of the legacy
<literal>option#value</literal>
form is <acronym>NOT</acronym> recommended as some preprocessors
and database systems will treat <emphasis remap=bf>#</emphasis>
as the start of a comment
and delete the remainder of the line.
This has caused great consternation for sysadmins who wonder why their
NIS distributed printcap entries have been mysteriously truncated.</para>
</listitem>

<listitem>
<para>If you want to set a string option to <emphasis>empty</emphasis>
value,
use <literal>option=</literal>. The
<literal>option</literal>
will set it to
<literal remap=tt>1</literal>.
If an option value contains a colon, then use the C (or Perl or Tck/Tk)
string escape
<literal>\072</literal>
to represent the value.</para>
</listitem>

<listitem>
<para>Boolean options are set TRUE (1) if no value follows the keyword and FALSE (0) by
appending a <literal>@</literal>.
For example
<literal>sh</literal>
will set
<literal>sh</literal>
to TRUE and
<literal>sh@</literal> to FALSE.</para>
</listitem>

</orderedlist>
</para>

<para>There may be multiple options on the same line, separated
by colons.</para>

<para>Now let's examine the first printcap entry in detail.
It is reproduced here for convenience:
<informalexample>
<screen># printer lp1
lp1|printer1
  :rm=localhost</screen>
</informalexample>

<orderedlist>

<listitem>
<para>We start with a comment, followed by the printcap entry name and and alias.
Aliases are useful when you want to refer to a single printer or print queue
by different names.
This can be useful in advanced printcap and print queue setups.
By default,
the remote printer name is the printcap entry name.</para>
</listitem>

<listitem>
<para>The
<literal>rm</literal>
(remote machine or host) option specifies the name or IP address
of the <application>lpd</application> host running <application>lpd</application>.
In this example the remote host is
<literal>localhost</literal>
or the machine that the client is running on
and we assume that the <application>lpd</application> server is running on the localhost.
Thus,
we would communicate with printer
<literal>lp1@localhost</literal>.</para>
</listitem>

</orderedlist>
</para>

<para>Let's look at the next printcap entry:
<informalexample>
<screen># printer lp2 with continuation
lp2:\
  :lp=pr@10.0.0.1:client</screen>
</informalexample>

<orderedlist>

<listitem>
<para>The
<literal>lp2</literal>
printcap entry illustrates the use (and abuse)
of the
<literal remap=tt>\</literal>
continuation.
If you think about this,
we have really defined a printcap entry of the form:
<informalexample>
<screen>lp2: :lp=pr@10.0.0.1:client</screen>
</informalexample>

</para>

<para>Luckily, &LPRng; ignores empty options like
<literal remap=tt>::</literal>.
While it is strongly recommended that
<literal remap=tt>\</literal>
be avoided it may be necessary for compatibility with other system utilities.</para>
</listitem>

<listitem>
<para>The <literal>lp=pr@10.0.0.1</literal> literal is an alternate way to
specify a remote queue and server.
If the <literal>force_localhost</literal> default is being used,
then the &LPRng; clients will ignore the
<literal>10.0.0.1</literal>
address
and still connect to <literal>pr@localhost</literal>.
There is further discussion about this in the next section.</para>
</listitem>

<listitem>
<para>The
<literal>client</literal>
option explicitly labels client only printcap information.
The <application>lpd</application> server will ignore any printcap with the
<literal>client</literal>
option.
When constructing complex printcaps,
this option is used to keep ensure that you have consistent printcap information.</para>
</listitem>

</orderedlist>

The following printcap entry shows how to override the
<literal>force_localhost</literal> default,
and force the &LPRng; clients to connect directly to a remote server:
<informalexample>
<screen>lp3:rp=pr:rm=hp.private
  :force_localhost@</screen>
</informalexample>

<orderedlist>

<listitem>
<para>The <literal>rp=</literal>  (remote printer)
remote print queue name to used when sending commands to the <application>lpd</application> print server.</para>
</listitem>

<listitem>
<para>The <literal>force_localhost@</literal> literal is an example of a <emphasis>flag</emphasis>
option.
The <literal>@</literal> sets the literal value to 0 (false).
We set <literal>force_localhost</literal> to false,
which now allows the &LPRng; clients to connect directly to the
specified remote printer.
In this example,
the <filename>hp.private</filename> could be a HP LaserJet Printer with a
JetDirect interface,
which supports the RFC1179 protocol.</para>
</listitem>

<listitem>
<para>One disadvantages of sending a job directly to a printer using the above
method is that <application>lpr</application> program will not
terminate or exit until all of the files have been transferred to the printer,
and this may take a long time
as the printer processes the files as they are received.</para>
</listitem>

</orderedlist>
</para>

<para>Now let's look at the last printcap entry:
<informalexample>
<screen># Simplest possible printcap entry - defaults for everything
lp4</screen>
</informalexample>
</para>

<para>The last example is the simplest possible printcap entry.
This will cause &LPRng; clients to use the default values for everything.
The printer will be
<literal>lp4</literal>,
i.e. - the name of the printcap,
and the server will be
<literal>localhost</literal>
if <literal>force_localhost</literal> is set,
or the value of the <literal>default_remote_host</literal> configuration option
if it is not.</para>

</sect1>

<sect1 id="cm"><title>Simple Server Printcap Example
</title>

<para>Options used:
<itemizedlist>

<listitem>
<para> <literal>cm=</literal><emphasis>comment for status</emphasis></para>
</listitem>

<listitem>
<para> <literal>filter=</literal><emphasis>job filter</emphasis></para>
</listitem>

<listitem>
<para> <literal>lf=</literal><emphasis>log file </emphasis></para>
</listitem>

<listitem>
<para> <literal>af=</literal><emphasis>accounting file </emphasis></para>
</listitem>

<listitem>
<para> <literal>lp=</literal><emphasis>output device</emphasis></para>
</listitem>

<listitem>
<para> <literal>mx=</literal><emphasis>maximum job size</emphasis></para>
</listitem>

<listitem>
<para> <literal>sd=</literal><emphasis>spool directory file </emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>The previous section discussed printcap entries for use by the client programs.
Now we will discuss printcap entries for use by the <application>lpd</application> server.
In simple configurations or when we have the <literal>force_localhost</literal>
option enabled
we can use the same printcap for both &LPRng; clients and the <application>lpd</application>
server.</para>

<para>
<informalexample>
<screen># Local ASCII printer
lp1|printer
  :server
  :cm=Dumb printer
  :lp=/dev/lp1
  :sd=/var/spool/lpd/lp1
  :lf=log:af=acct
  :filter=/usr/local/libexec/filters/ifhp
  :mx=0</screen>
</informalexample>
</para>

<para>
<orderedlist>

<listitem>
<para>The printcap entry name is
<literal>lp1</literal>.
This information will be displayed when requesting status information using
the <application>lpq</application> program.</para>
</listitem>

<listitem>
<para>The
<literal>printer</literal>
alias.
This allows a single spool queue to have multiple names.</para>
</listitem>

<listitem>
<para>The
<literal>:server</literal>
option specifies
this printcap entry is only used by <application>lpd</application>
server.</para>
</listitem>

<listitem>
<para>The
<literal>:cm</literal>
field supplies a information field
for <application>lpq</application> (printer status) output.</para>
</listitem>

<listitem>
<para> The
<literal>:lp</literal>
value
specifies the destination file, device or remote spool queue to which data is sent.
In this example it is the device <filename>/dev/lp1</filename>.
By default,
IO devices are opened for <emphasis>write-only</emphasis>
operation.</para>
</listitem>

<listitem>
<para>The
<literal>:sd=/path</literal>
specifies the <emphasis>spool directory</emphasis>
where print job files
are stored until they are printed.</para>
</listitem>

<listitem>
<para> The
<literal>:lf</literal>
and
<literal>:af</literal>
options specify the names
of the log and accounting files, respectively.
These have the default values
<literal>log</literal>
and
<literal>acct</literal>
respectively.
If not an absolute path, the file is relative to the spool queue directory.
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
<literal>touch</literal>)
or by using the
<emphasis remap=tt>
<link linkend="checkpc">checkpc</link>
</emphasis>
program.
If you do not want a log or accounting file,
then use <literal/:lf=/, i.e. - no value.
</para>
</listitem>

<listitem>
<para>The
<literal remap=tt>:filter=/path</literal>
option specifies a filter program to be used
to process job files.
Filters and print formats are discussed in
<link linkend="filters">Filters</link>.
</para>
</listitem>

<listitem>
<para><literal>mx</literal>
indicates the maximum file size for a print job.
Specifying 0 means that there is no limit.</para>
</listitem>

</orderedlist>
</para>

</sect1>

<sect1><title>Using :oh To Select Printcap Information</title>

<para>When administering a large number of printers over a large area,
it is sometimes desirable to have a <emphasis>default</emphasis>
printer for
each host.
This default printer may be different for each host,
and can be selected by using the
<literal>oh</literal>
entry.
The
<literal>oh</literal>
value is a list of the following entries
<informalexample>
<screen>[!] IP/n     - address + mask length    10.0.0.0/8
[!] IP/IP    - address + mask           10.0.0.0/255.0.0.0
[!] vvv      - glob for hostname        pc*.org.com</screen>
</informalexample>
</para>

<para>The &LPRng; software will determine the hostnames and IP addresses assigned to the
host and then check to see if there is a match in the listed hostnames
or IP addresses.
The optional test inversion (<literal remap=tt>!</literal>) causes the sense of the match
to be inverted.
The list of addresses or entries are tested in sequence until
a match is found.
If no match is found the printcap entry will not be used.
For example:
<informalexample>
<screen>lp:oh=*.admin.org.com,10.0.0.5,10.2.0.0/16:lp=pr1@server1
lp:oh=*.eng.org.com:lp=hp@server2
color:oh=*.eng.org.com:lp=color@server3
color:oh=!*.eng.org.com:lp=color@server4</screen>
</informalexample>
</para>

<para>In the above example,
if our host name is
<filename>booster.admin.org.com</filename>,
then we would use <literal>lp=pr1@server1</literal>,
as the <filename>*.admin.org.com</filename> glob pattern would match our host name.</para>

<para>if our host name is
<filename>booster.dev.org.com</filename> and our IP address is 10.2.0.1,
then we would use <literal>lp=pr1@server1</literal>,
as the <filename>10.2.0.0/16</filename> ip address would be in the specified address range.</para>

<para>Finally we have an example of the use of the match inversion
operator (<literal/!/).
All hosts whose name matches <filename>*.eng.org.com</filename> will use
<literal>color@server3</literal> and the others will use
<literal>color@server4</literal>.</para>

</sect1>

<sect1><title>Using the Wildcard Printcap Entry</title>

<para>The wildcard printcap name
<literal remap=tt>*</literal>
is used to select a default
or printcap entry when a match is not found in the printcap
database.
<informalexample>
<screen># %P and %Q set to printer name
*:lp=%P@server

# %P set to 'printer', %Q set to printer name
printer|*:lp=%P@server</screen>
</informalexample>
</para>

<para>When searching for printcap information,
the &LPRng; software will first search for an exact match
for a printcap entry against the printcap names and aliases.
If non is found,
it then searches for a wildcard entry and uses the first one
found with a wildcard name or alias.</para>

<para>If the wildcard
<literal remap=tt>*</literal>
is the printcap name,
then the printer name
(<literal>%P</literal>
value)
and queue
(<literal>%Q</literal>
value)
are set to the name being searched for.
If the wildcard is an alias,
then then printer name is set to the printcap main entry name
and the queue to the name being searched for.</para>

</sect1>

<sect1><title>Enterprise Strength Printcap Example</title>

<para>Most system administrators want to have a single printcap
file that can be distributed or referenced from
all the hosts under their administrative control.
The following show how several large institutions have
organized their printcap information.
It uses the following principles:
<itemizedlist>

<listitem>
<para>Many times it is necessary to partition groups of users
into areas that have particular printing setups,
but use the same queue name for different printers.
This is done by using the
<literal remap=tt>:oh</literal>
(on this host)
option to select the printcap to be used.</para>
</listitem>

<listitem>
<para>Provide as little information as possible to the
systems that do not have print spoolers.
This is because they will simply forward their requests to
a print spooler and it is not necessary to have the information
for a printer.
As we will see in an example below,
this can be very simple to do.</para>
</listitem>

<listitem>
<para>Provide a default printer so that when the user does
not have a printer explicitly selected then he will
get well behaved results.</para>
</listitem>

<listitem>
<para>For each family of printers,
develop a standard method of interfacing to them.
This means that the filter,
options,
and other things should be identical.</para>
</listitem>

<listitem>
<para>For each server,
provide a uniform set of standards for the spool queues
for a printer.
If the spool queue is simply used to store and forward
jobs,
then provide a standard default operation for this queue.
If the spool queue directly feeds a printer,
use the default options for the printer,
and then refine them with printer specific information.</para>
</listitem>

</itemizedlist>

<informalexample>
<screen># client setups; note brutality of this method that
#  assigns servers to clients based on their names
#  you could do this for IP addresses as well
# lp1 is default printer for Engineering
#   Default client information
.client=force_localhost@
#
lp1:oh=*.eng.com:lp=%P@server1.eng.com:tc=.client
*:oh=*.eng.com:lp=%P@server1.eng.com:tc=.client
color:oh-*.admin.com:lp=%P@server2.admin.com:tc=.client
*:oh=*.admin.com:lp=%P@server2.admin.com:tc=.client
lowres:lp=%P@general.services.com:tc=.client
*:lp=%P@general.services.com:tc=.client

# Standard Printer Configurations
# this is for the HP4simx, we use a standard filter setup
.cf_hp4simx
 :ifhp=model=hp4simx
 :filter=/usr/local/libexec/filters/ifhp
.cf_hplj5000
 :ifhp=model=hp5000
 :filter=/usr/local/libexec/filters/ifhp
# now we define the printers that use them
.pr_eng1:cm=Engineering's Printer 1
 :tc=.cf_hplj5000:lp=10.0.0.2%9100
.pr_eng2:cm=Engineering's Printer 2
 :tc=.cf_hplj5000:lp=10.0.0.23%9100

# now we define the server entries
# We set up server entries and then forward
#  to a single server that sends the the printer

.server
 :sd=/var/spool/lpd/%P
 :mx=0
pr1:oh=!10.0.0.5:lp=%P@10.0.0.5:server:tc=.server
pr1:oh=10.0.0.5:tc=.server
 :tc=.pr_eng1</screen>
</informalexample>
</para>

</sect1>

<sect1><title>Remote Printer Using RFC1179</title>

<para>Options used:
<itemizedlist>

<listitem>
<para> <literal>lp=</literal><emphasis>destination</emphasis></para>
</listitem>

<listitem>
<para> <literal>rm=</literal><emphasis>remote host (machine)</emphasis></para>
</listitem>

<listitem>
<para> <literal>rp=</literal><emphasis>remote printer (machine)</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>You can have the <application>lpd</application> server forward jobs to another server
or print which supports the RFC1179 protocol by using the
following printcap:
<informalexample>
<screen># Simplest
remote|Remote Printer
   :lp=raw@server
# historical
remote:
  rp=raw:rm=server
# Sometimes you have to connect to a non-standard port
special:lp=lp@server%2000</screen>
</informalexample>

<orderedlist>

<listitem>
<para>If the
<literal>lp</literal>
printer entry is present, it will override the
<literal>rm</literal>
and
<literal>rp</literal>
printer entries.</para>
</listitem>

<listitem>
<para>The <literal>lp=pr@host</literal> format specifies that the output device is actually
a remote spool queue,
and jobs should be transferred using RFC1179 protocol.</para>
</listitem>

<listitem>
<para>By default,
&LPRng; will attempt to
<emphasis remap=bf>sanitize</emphasis>
all jobs that it originates or forwards.
This sanitization will result in an RFC1179 compliant
<literal>control file</literal>,
and will not modify any of the job information.</para>
</listitem>

</orderedlist>
</para>

</sect1>

<sect1><title>Remote Printer Using Socket API</title>

<para>If the spool queue destination is a remote printer supporting the
Socket API,
then you can have &LPRng; open a connection directly to the printer.
These include the older Apple printers with TCP/IP support and the
HP JetDirect supported printers.
<informalexample>
<screen># Simplest
remote
   :lp=10.24.2.3%9100</screen>
</informalexample>

<orderedlist>

<listitem>
<para>The <literal>lp=server%port</literal> or <literal>lp=IPaddr%port</literal> format
specifies that <application>lpd</application> should open a TCP/IP connection to the remote
host and simply transfer verbatum the files to be printed.</para>
</listitem>

<listitem>
<para>The
<literal>sh</literal>
and
<literal>sf</literal>
will prevent <application>lpd</application> from trying to generate
banner pages or put form feeds between jobs.</para>
</listitem>

</orderedlist>
</para>

<para>While this is the simplest printcap,
it is also the most dangerous as there is nothing to prevent
a malformed job from being sent to the printer.
The next printcap example is much more robust:
<informalexample>
<screen># Simplest
remote
   :lp=10.24.2.3%9100
   :of=/usr/local/libexec/filters/ifhp
   :filter=/usr/local/libexec/filters/ifhp</screen>
</informalexample>

<orderedlist>

<listitem>
<para>This version will use the
<application remap=tt>ifhp</application>
filter to precondition the printer
and to process jobs.
See
<link linkend="ifhp"><application>ifhp</application> Filter</link>
 for details.
The
<application remap=tt>ifhp</application>
filter will perform the appropriate printer resets,
translate job information,
and ensure correct printer operation in the presence of errors.
It will also produce voluminous error messages and status information.</para>
</listitem>

</orderedlist>
</para>

</sect1>

<sect1><title>Parallel Printer</title>

<para>The parallel printer printcap is very simple.
<informalexample>
<screen># parallel printer
lp:
   :lp=/dev/lpr</screen>
</informalexample>

<orderedlist>

<listitem>
<para>The <literal>lp=/dev/lpr</literal>
specifies that <application>lpd</application> should open the device for APPEND and simply transfer
job files to it.</para>
</listitem>

<listitem>
<para>The
<literal>sh</literal>
and
<literal>sf</literal>
will prevent <application>lpd</application> from trying to generate
banner pages or put form feeds between jobs.</para>
</listitem>

</orderedlist>
</para>

<para>If you discover that UNIX print jobs result in a <emphasis>staircase</emphasis>
appearance,
then you need to force your printer to do <acronym>LF</acronym> (linefeed) to <filename>CR/LF</filename>
(carriage return/line feed) translation,
or do the translation yourself.
<informalexample>
<screen># Simple parallel printer
lp:
   :lp=/dev/lpr
   :filter=/usr/local/bin/lpf</screen>
</informalexample>
</para>

<para>By using the <literal>if=...lpf</literal> filter,
the job will be passed through the
<application>lpf</application>
filter,
which will do the <acronym>LF</acronym> to <filename>CR/LF</filename> translation.</para>

<para>If you have a more complex printer that handles PostScript, PCL, and PJL,
then you will need to use the more powerful
<application remap=tt>ifhp</application>
filter:
<informalexample>
<screen># Simple parallel printer
lp:
   :lp=/dev/lpr
   :ifhp=model=hp4,status@
   :of=/usr/local/libexec/filters/ifhp
   :filter=/usr/local/libexec/filters/ifhp</screen>
</informalexample>
</para>

<para>See
<link linkend="ifhp"><application>ifhp</application> Filter</link>
 for details.
This entry will specify that the printer is an HP4,
and that no status information is available.
This is usually the case with a parallel port.</para>

</sect1>

<sect1 id="serial"><title>Serial Printer
</title>

<para>Options used:
<itemizedlist>

<listitem>
<para>
<literal>br=</literal><emphasis>serial port bit rate</emphasis></para>
</listitem>

<listitem>
<para>
<literal>rw</literal> FLAG <emphasis>device opened RW</emphasis></para>
</listitem>


<listitem>
<para> <literal>stty=</literal><emphasis>stty options for serial port configuration</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>The following is a typical printcap for a serial printer:
<informalexample>
<screen># Local Serial ASCII printer
lp2
  :lp=/dev/ttya
  :rw
  :cm=Serial printer
  :sd=/var/spool/lpd/lp2
  :stty=9600 -echo -crmod -raw -oddp -evenp pass8 cbreak ixon
  :filter=/usr/local/sbin/lpf
  :mx=0</screen>
</informalexample>
</para>

<para>Let's examine the new options:
<orderedlist>

<listitem>
<para>A serial port is usually <emphasis>bidirectional</emphasis>,
and printers will report errors back to the host computer.
The
<literal>rw</literal>
flag will cause the printer port to be opened
<literal>read-write</literal>,
and the <application>lpd</application> server will report status information.</para>
</listitem>

<listitem>
<para>The
<literal>stty</literal>
option specifies the <command>stty(1)</command>
flags and line speed needed to configure the serial line
(See

<link linkend="secserial">Serial Printers</link>

for details).</para>
</listitem>

<listitem>
<para>The legacy
<literal>br</literal>
(bit rate) option
can be used to specify the line speed as well.</para>
</listitem>

</orderedlist>
</para>

</sect1>

<sect1 id="lpdbounce"><title>Bounce Queue

</title>

<para>Options used:
<itemizedlist>

<listitem>
<para> <literal>lpd_bounce</literal> FLAG <emphasis>produces a single file for forwarding</emphasis></para>
</listitem>

<listitem>
<para> <literal>bq_format=</literal><emphasis>format of filtered files</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>When the destination of a spool queue is another spool queue
the job is simply forwarded without any modifications.
However,
sometimes it is essential that the job be modified before
forwarding,
as when the remote spool queue is actually a printer,
and
jobs need to be converted to the format acceptable by
the remote printer or banner pages added.</para>

<para>The <literal>lpd_bounce</literal> flag marks a spool queue as a bounce queue.
<literal>Lpd</literal>
will perform all of the usually job processing steps,
such as banner generation,
filtering files,
etc,
but
saves the output to a file.
This file is then sent to the destination print queue for further
processing.</para>

<para>
<informalexample>
<screen># Simple example of a bounce queue
bounce:lp=bounce@bouncehost
bounce:server
    :lp=lp@remote
    :lpd_bounce
    :sd=/var/spool/lpd/%P
    :filter=/usr/local/bin/lpf
    # uncomment ab if you want banner
    #:ab</screen>
</informalexample>
</para>

<para>Some comments:</para>

<para>
<orderedlist>

<listitem>
<para>The
<literal>lpd_bounce</literal>
option marks the job as a bounce queue,
and the <application>lpd</application> server will process the job through the appropriate
filter programs.</para>
</listitem>

<listitem>
<para>The printcap has filter specifications for different job formats.
These are the programs that will be used by &LPRng; to process the job.</para>
</listitem>

<listitem>
<para>The
<literal>bq_format</literal>
specifies the job format for the output file sent to the remote spool queue.
If not specified, it defaults to
<literal remap=tt>l</literal>
(literal or binary).</para>
</listitem>

<listitem>
<para>The
<literal>ab</literal>
(always print a banner) flag will force a banner to be
added to the job.
The banner generation is done as discussed in

<link linkend="bannerprinting">Banner Printing</link>.</para>
</listitem>

</orderedlist>
</para>

</sect1>

<sect1 id="translateformat"><title>Job Format Translation </title>

<para>Options used:
<itemizedlist>

<listitem>
<para> <literal>translate_format=</literal><emphasis>change outgoing job file formats</emphasis></para>
</listitem>

<listitem>
<para> <literal>translate_incoming_format=</literal><emphasis>change incoming job file formats</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>A rarely encountered problem is when a job is printed with
one format but for compatibility needs to be processed with another.
The simple
<literal>translate_format=vlxf</literal>
option will rename format
<literal remap=tt>x</literal>
files to
<literal remap=tt>f</literal>
format.</para>

<para>This can be used to resolve problems with PC based software,
which spools jobs using the
<literal remap=tt>v</literal>
format.
Unfortunately,
many RCF1179 print spoolers do not understand the
<literal remap=tt>v</literal>
format and mishandle the job.
A simple forwarding queue with the following entries will rename
<literal remap=tt>v</literal>
format to
<literal remap=tt>l</literal>
(binary) format.
<informalexample>
<screen>lp
  :sd=/var/spool/lpd/%P
  :translate_format=vl
  :lp=lp@printerserver</screen>
</informalexample>
</para>

<para>The <literal>translate_incoming_format</literal> will do the same thing,
but this time on incoming jobs.</para>

</sect1>

<sect1 id="destinations"><title>Dynamic Routing

</title>

<para>Options used:
<itemizedlist>

<listitem>
<para> <literal>destinations=</literal><emphasis>destinations for jobs</emphasis></para>
</listitem>

<listitem>
<para> <literal>router=</literal><emphasis>router program</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>&LPRng; has the ability to route a job to one or
more destinations in a dynamic manner.
This is not the same as
<emphasis>load balancing</emphasis>,
as the destinations are hard coded and not able to be changed.
This is accomplished by having
a
<literal>router</literal>
filter generate a set of destinations.
Here is a sample printcap entry:
<informalexample>
<screen>t2|Test Printer 2
    :sd=/var/spool/LPD/t2
    :lf=log
    :destinations=t1@server1,t1@server2,t1@localhost
    :router=/usr/local/LPD/router</screen>
</informalexample>
</para>

<para>When a job arrives at the <application>lpd</application> server,
the 'router' filter
is invoked with the standard filter options which include
the user, host, and other information obtained from the control file.
<acronym/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.</para>

<para>The routing filter exit status is used as follows:
<itemizedlist>

<listitem>
<para>0  (JSUCC) - normal processing</para>
</listitem>

<listitem>
<para>37 (JHOLD) - job is held</para>
</listitem>

<listitem>
<para>any other value - job is deleted from queue</para>
</listitem>

</itemizedlist>
</para>

<para>The router filter writes to <acronym/STDOUT/ a file specifying the destinations
for the job.
The destinations entries in this file file have the following format.
Entry order is not important,
but each destination must end with the 'end' tag.
<informalexample>
<screen>dest (destination queue)
copies (number of copies to be made)
priority (priority letter)
X(controlfile modifications)
end</screen>
</informalexample>
</para>

<para>Example of router output:
<informalexample>
<screen>dest t1@localhost
copies 2
CA
priority B
end
dest t2@localhost
CZ
priority Z
end</screen>
</informalexample>
</para>

<para>In this example,
two copies of the job will be sent to the t1 and t2 spool queue servers.
The Class (C letter value) and job priority information will be rewritten
with the indicated values.</para>

<para>If routing information
is specified by the router filter the job will be sent to the default
destination.</para>

<para><application>lpq</application> will display job information in a slightly different format
for multiple destination jobs.  For example:
<literallayout>Printer: t2@h4 'Test Printer 2' (routed/bounce queue to 't1@h2.private')
  Queue: 1 printable jobs in queue
 Rank  Owner/ID        Class Job Files                         Size Time
active  papowell@h4+707 A 707  /tmp/hi                         3 10:04:49
 - actv papowell@h4+707.1 A 707 -&gt;t1@localhost &lt;cpy 1/2&gt;       3 10:04:49
 -      papowell@lprng2+707.2 A 707 -&gt;t2@localhost                 3 10:04:49</literallayout>
</para>

<para>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@lprng2+707.1C2 will be the job
sent to the first destination, copy two.</para>

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

<para>The optional
<literal>destinations</literal>
entry specifies the possible set of
destinations that the job can be sent to,
and is for informational purposes only.
In order for
<application/lpq/
and
<application/lprm/
to find the job once it has been sent to <application/lpd/,
<application/lpq/
and
<application/lprm/
uses the list of printers in the
<literal>destinations</literal>,
and iterates over list looking for the job that you are interested in.</para>

</sect1>

<sect1 id="loadbalance"><title>Printer Load Balancing
</title>

<para>Options used:
<itemizedlist>

<listitem>
<para> <literal>ss=</literal><emphasis>queue served by printer </emphasis></para>
</listitem>

<listitem>
<para> <literal>sv=</literal><emphasis>printers where jobs are sent (servers)</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>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.
A load balance print queue is one that feeds jobs to other queues
and has a
<literal>sv=q1,q2,...</literal> printcap entry
that specifies the destination or server queues.
These must be print queue entries and have spool directories
on the server.</para>

<para>The service queues have a
<literal>ss=</literal><emphasis>mainqueue</emphasis>
printcap entry
This informs the <application>lpd</application> server that the queue operates under the
control of the <emphasis>mainqueue</emphasis>
print queue,
and is fed jobs from it.</para>

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

<para>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
<emphasis>hide</emphasis>
the load sharing printers,
i.e. - not allow direct spooling to them,
then you would simply remove the non-server entries from the printcap.</para>

</sect1>

<sect1 id="printcappath"><title>Locations of Printcap Files

</title>

<para>Options used:
<itemizedlist>

<listitem>
<para> <literal>printcap_path=</literal><emphasis>printcap file locations</emphasis></para>
</listitem>

<listitem>
<para> <literal>lpd_printcap_path=</literal><emphasis>lpd server printcap information</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>The
<literal>printcap_path </literal>
and
<literal> lpd_printcap_path </literal>
configuration options (see
<link linkend="configfile">lpd.conf(5)</link>
) specify a set of paths for the
printcap information.
If the
<literal>lpd_printcap_path</literal> is nonblank then
the 
<application>lpd</application>
server uses it,
otherwise it uses the
<literal>printcap_path</literal> information.
Client programs use only <literal>printcap_path</literal>.
The path names can be separated with whitespace,
commas, semicolons, or colons.
The default values are:
<informalexample>
<screen>printcap_path      ${sysconfdir}/printcap
lpd_printcap_path  (blank or empty)</screen>
</informalexample>
</para>


<sect2><title>Separate Server and Client Printcap Files</title>

<para>Since only the <application/lpd/ server uses the
printcap file specified by the
<literal>lpd_printcap_path</literal>,
you can place server specific information there.
If this file is used it must also contain any required information
in the <literal/printcap_path/ file.</para>

</sect2>

<sect2 id="allpc"><title><literal/all/ Printcap Entry </title>

<para>The
<literal>all</literal>
printcap name and
<literal>all</literal>
option
is reserved to provide a list of printers
available for use by the spooling software.
This is a desperation,
last ditch,
back to the wall option for
administrators
with systems that do not have ways to provide a list of printcap entries.
The 'all' printcap entry has the form:
<informalexample>
<screen>all:all=pr1,pr2,...</screen>
</informalexample>
</para>

<para>The value of the
<literal>all</literal>
option should be a list of printcap names
whose values will then be extracted.</para>

</sect2>
</sect1>

<sect1 id="secoh"><title>Single Printcap File for Large Installation </title>

<para>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
<literal>rdist</literal>.
By using the
<literal>server</literal>
and
<literal>oh</literal>
tags,
information for the specific sites can be separated out.
For example:
<informalexample>
<screen># printcap
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</screen>
</informalexample>
</para>

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

<para>Also,
the lpd daemons on
<literal>serverhost1</literal>
and
<literal>serverhost2</literal>
would extract the additional
information for
<literal>pr1</literal>
and
<literal>pr2</literal>
respectively,
overriding the common
<literal>lp</literal>
entries.</para>

</sect1>

<sect1><title>Management Strategies for Large Installations</title>

<para>One very effective way to organize print spooling is to have a small
number of print servers running a
<application>lpd</application> 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.</para>

<para>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
<filename>printcap</filename>
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

<link linkend="secnis">Using Programs To Get Printcap Information</link>

for details.</para>

</sect1>

<sect1 id="secnis"><title>Using Programs To Get Printcap Information </title>

<para>In the
<filename>lpd.conf</filename>
file you can specify:
<informalexample>
<screen>printcap_path=|program</screen>
</informalexample>

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
<acronym/STDIN/.
The filter
should supply the printcap information on
<literal>stdout</literal>
and exit with a 0
(success) error code.  By convention,  the printcap name 'all'
requests a printcap entry that lists all printers.</para>

<para>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
<literal>nismatch</literal>
or
<literal>ypmatch</literal>
programs,
the complexity of incorporating vendor specific code is avoided.</para>


<sect2><title>How to use NIS and &LPRng;</title>

<para>This note is based on material sent to the
<link linkend="maillist">lprng@lprng.com</link>
mailing list by
Paul Haldane
<email>paul@ucs.ed.ac.uk</email>.</para>

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

<para>The input to NIS is a normal printcap file:
<informalexample>
<screen># Classical printcap entry
lp23a|lp23|lp|main printhost printer - KB, EUCS front Door:\
        :lp=lp23a@printhost:\
        :sd=/var/spool/lpr/lp23a:

#lprng printcap entry
lplabel|lpl|TEST - Labels printer:
        :lp=:rm=printhost:rp=lplabel:
        :sd=/var/spool/lpr/lplabel:
        :rg=lpadm:mx=1:</screen>
</informalexample>
</para>

<para>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).
<informalexample>
<screen>PRINTCAP=${sysconfdir}/printcap
# warning : [  ] is actually [&lt;space&gt;&lt;tab&gt;] in the script
printcap.time: $(PRINTCAP) Makefile
  if [ -f $(PRINTCAP) ]; then \
    sed &lt; $(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' &gt; .printcap.$$$$; \
    cat .printcap.$$$$; \
    if [ $$? = 0 -a -s .printcap.$$$$ ]; then \
      awk &lt;.printcap.$$$$ '{ FS=":"; OFS="\t"; } { \
          n = split($$1, names, "|"); \
          for (i=1; i&lt;=n; i++) \
              if (length(names[i]) &gt; 0 \
              &amp;&amp; names[i] !~ /[ \t]/) \
                  print names[i], $$0; \
      }' | $(MAKEDBM) - $(YPDBDIR)/$(DOM)/printcap.byname; \
      awk &lt;.printcap.$$$$ '{ FS=":"; OFS="\t"; } { \
          n = split($$1, names, "|"); \
          if (n &amp;&amp; length(names[1]) &gt; 0 &amp;&amp; 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</screen>
</informalexample>
</para>

<para>To specify that you want YP database rather than file access,
use the following entry in your <filename>/etc/lpd.conf</filename> file:
<informalexample>
<screen>printcap_path |/usr/local/libexec/pcfilter</screen>
</informalexample>
</para>

<para>Put the following shell script in /usr/local/libexec/pcfilter
<informalexample>
<screen>#!/bin/sh
#/usr/local/libexec/filters/pcfilter
read key
# specify the full pathname to the ypmatch program
# the location depends on the version of Solaris or your
# system install
/full/pathname/to/ypmatch "$key" printcap.byname</screen>
</informalexample>
</para>

<para>
You can test this by using:
<informalexample>
<screen><prompt>h4: {314} # </prompt><userinput>lpc client pr</userinput>
pr
 :lp=pr@server
<prompt>h4: {315} # </prompt><userinput>lpc server pr</userinput>
pr
 :lp=pr@server</screen>
</informalexample>
</para>

</sect2>

<sect2><title>How to use NIS and &LPRng; - Sven Rudolph</title>

<para>
<informalexample>
<screen> Date: Wed, 11 Sep 1996 00:11:02 +0200
From: Sven Rudolph &lt;sr1@os.inf.tu-dresden.de&gt;
To: lprng@lprng.com
Subject: Using :oh=server: with NIS</screen>
</informalexample>
</para>

<para>When I use a cluster-wide printcap,
I want the entries for each printer to appear, e.g.:
<informalexample>
<screen>---------- start of printcap snippet
lp1
 :lp=lp1@server
lp2
 :lp=lp2@server
lp1
 :server:oh=servername
 :sd=/var/spool/lpd/lp1
 :lp=/dev/lp1
 :mx=0
---------- end of printcap snippet</screen>
</informalexample>
</para>

<para>When I create a NIS map out of this
the printer name is used as a key
and must be unique. The NIS makedbm will 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.
<orderedlist>

<listitem>
<para>Assumptions
</para>

<para>Perl is available at the YP server in <command>/usr/bin/perl</command>.
A Bourne Shell is available at all clients in <command>/bin/sh</command>
The printcap that is to be exported is in <filename>/etc/printcap</filename>.
The printcap is written in the new format. 
In the examples the printer is called lp1.</para>
</listitem>

<listitem>
<para>Add the following to your YP <filename>Makefile</filename>
(<filename>/var/yp/Makefile</filename>) on the YP server
(these lines are for Debian GNU/Linux, other systems might require
other modifications):
<informalexample>
<screen>---------- 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</screen>
</informalexample>
</para>
</listitem>

<listitem>
<para>Install the programs <application>match_printcap</application>
and <application>normalize_printcap</application> in the
<filename>/usr/lib/yp</filename> directory;
<application>normalize_printcap</application>
is only required on the YP server.
The <application>normalize_printcap</application> processes only the &LPRng; printcap format.
<informalexample>
<screen>---------- start of /usr/lib/yp/normalize_printcap
#! /usr/bin/perl
$debug = 0;
$line = "";
$new = "";
while (&lt;&gt;) {
    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 &gt;=0) {
    print "all_1\tall:all=".join(",",@pr)."\n";
}
---------- end of /usr/lib/yp/normalize_printcap</screen>
</informalexample>

</para>

<para>The result of processing the sample printcap file is:
<informalexample>
<screen>lp1_1 lp1:lp=lp1@server
lp1_2 lp1:server:oh=servername:sd=/var/spool/lpd/lp1:lp=/dev/lp1:mx=0
lp2_1 lp2:lp=lp2@server
all_1 all:all=lp1,lp2</screen>
</informalexample>
</para>

<para>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
<literal>ypmatch</literal>:
<informalexample>
<screen>---------- start of /usr/lib/yp/match_printcap
#!/bin/sh
read p
n=1
# specify the full pathname to ypmatch - this depends on your
# OS version and installation
while /full/pathname/to/ypmatch "${p}_${n}" printcap 2&gt;/dev/null; do
    n=`expr $n + 1`
done
---------- end of /usr/lib/yp/match_printcap</screen>
</informalexample>
</para>
</listitem>

<listitem>
<para>Now test the YP arrangement:
<informalexample>
<screen><prompt>h4: {316} # </prompt> cd /var/yp; make
    # this should create the printcap map
<prompt>h4: {317} # </prompt> ypcat printcap
    # should provide the whole normalized printcap
<prompt>h4: {318} # </prompt> echo lp1 |/usr/lib/yp/match_printcap
    # yields lp1 printcap</screen>
</informalexample>
</para>
</listitem>

<listitem>
<para>Modify the <literal>printcap_path</literal> entry in the
<filename>lpd.conf</filename> file:
<informalexample>
<screen>printcap_path=|/usr/lib/yp/match_printcap</screen>
</informalexample>
</para>
</listitem>

<listitem>
<para>Test the use of the printcap path entry:
<informalexample>
<screen><prompt>h4: {319} # </prompt> lpc client lp1 # shows the printcap for lp1
<prompt>h4: {320} # </prompt> lpc server lp1 # shows the printcap for lp1
</screen>
</informalexample>
</para>
</listitem>

<listitem>
<para>Restart the lpd server and check to see that it accesses
the right printcap information.
Use the same <application>lpq</application> command,
and then try
<command remap=tt>lpc printcap lp1</command>.</para>
</listitem>

</orderedlist>
</para>

</sect2>
</sect1>

<sect1><title>Lexmark Printers</title>

<para>Some Lexmark printers do not send
<emphasis>end of job</emphasis>
status back unless configured
to do so.
Here is what is needed to force this.
(This capability has been incorporated into the
<application remap=tt>ifhp</application>
filter.)
<informalexample>
<screen>From: Matt White &lt;whitem@bofh.usask.ca&gt;
To: lprng@lprng.com
Date: Wed, 21 Jan 1998 18:25:50 -0600 (CST)
Subject: Re: [LPRng] ifhp with Lexmark Optra N printer

On Wed, 21 Jan 1998, Simon Greaves wrote:

&gt; Apologies in advance if this is way off mark, but we've been
&gt; evaluating a commercial print charging package (Geomica) which
&gt; works by talking to the printer in what I think is a similar way
&gt; to the ifhp filters. Lexmarks are currently a big headache because
&gt; they seem to fail to return the message that they have finished
&gt; printing which screws things up somewhat. In our case, it is believed
&gt; to be a problem with the Lexmark firmware which they are looking
&gt; 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 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 &amp; Science  University of Saskatchewan
-----------------------------------------------------------</screen>
</informalexample>
</para>

</sect1>

<sect1 id="P450"><title>Tektronix Phaser Printers </title>

<para>The
<application remap=tt>ifhp</application>
filter supports the

<link linkend="appsocket">AppSocket</link>
 protocol used by Tektronix.
You will need a printcap similar to the following:
<informalexample>
<screen>phaser:
 :sd=/var/spool/lpd/%P
 # need a dummy device for output
 :lp=/dev/null
 # You need to specify the IP address of the printer's network interface
 #  In this example it is 10.0.0.1 - replace with the correct value
 # The filter will actually open the connection.
 :filter=/usr/local/libexec/filters/ifhp
 :ifhp=dev=10.0.0.1%9100,model=tek</screen>
</informalexample>
</para>

</sect1>

<sect1><title>Duplex Printing</title>

<para>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 <application>ifhp</application> filter makes 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.
<orderedlist>

<listitem>
<para>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.</para>
</listitem>

<listitem>
<para>Check your printer manual to discover the EXACT form of the
<literal>enter duplex mode</literal>
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.
</para>

<para>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.</para>
</listitem>

<listitem>
<para>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.</para>
</listitem>

<listitem>
<para>Read the <application>ifhp</application> documentation,
and create a configuration section in the <filename>ifhp.conf</filename> file
for your printer.</para>
</listitem>

</orderedlist>
</para>

<para>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.
<blockquote>
<para><emphasis>Patrick Powell</emphasis></para>
</blockquote>
</para>

</sect1>

<sect1><title>Solaris, Newsprint and FrameMaker</title>

<para>The following is a guide to using &LPRng; and
Sun Microsystems Newsprint by
Christopher Hylands, Ptolemy Project Manager
of the University of California.</para>

<para>The Sun Newsprint printer is actually
an OEM version of the  Tektronix 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
<filename>http://ptolemy.eecs.berkeley.edu/~cxh/lprng.html</filename>.</para>

<para>Looking through the mailing list logs, it looks like everyone was
having a hard time getting lprng to work with Sun's brain-dead
newsprinters.  I tried using GhostScript, but the fonts were, IMHO,
ugly, so I spent a little time getting the newsprint fonts to work.</para>

<para>The key thing was to grab the file
<filename>/usr/newsprint/lpd/if</filename>
from a SunOS4.1.3 newsprint installation.
If you cannot get this code,
then the installation will be extremely difficult.</para>

<para>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:
<informalexample>
<screen>chown daemon /devices/sbus@1,f8000000/SUNW,lpvi@1,300000:lpvi0</screen>
</informalexample>
</para>

<para>We use the following simple
<literal>:if</literal>
script.
<informalexample>
<screen>#/bin/sh
# extremely simple filter script
/bin/cat</screen>
</informalexample>
</para>

<para>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 <command> /etc/lp/printers/xsp524</command>.</para>

<para>The printcap entry looks like:
<informalexample>
<screen>sp524|524:
    :mx=0
    :lp=:rm=doppler:rp=xsp524:
    :sd=/var/spool/lpd/sp524d:
    :lf=/var/spool/lpd/sp524d/log:
xsp524|Sun SPARCprinter NeWSprint printer:
    :mx=0:rs:
    :lp=/dev/lpvi0:
    :sd=/etc/lp/printers/xsp524:
    :lf=/etc/lp/printers/xsp524/log:
    :af=/var/spool/lpd/xsp524/acct:
    :filter=/usr/local/libexec/newsprint/if:</screen>
</informalexample>
</para>

<para>The
<filename>/usr/local/libexec/newsprint/if</filename>
was copied from
<filename>/usr/newsprint/lpd/if</filename>
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.
<orderedlist>

<listitem>
<para>First, set the path in the script.
You may also need to change defaults to suit your preferences:
<informalexample>
<screen>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</screen>
</informalexample>
</para>
</listitem>

<listitem>
<para>You will also need a
<filename>/etc/lp/printers/printername/.params</filename>
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.</para>
</listitem>

<listitem>
<para>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</para>
</listitem>

<listitem>
<para>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
<informalexample>
<screen>sunxm.s5.sparc)
    lpr -P"$PRINTER" "$FILE"
    #lp -c -d"$PRINTER" "$FILE"</screen>
</informalexample>
</para>
</listitem>

</orderedlist>

<informalexample>
<screen>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)</screen>
</informalexample>
</para>

</sect1>
</chapter>

<chapter id=spoolqueue><title>Spool Queues and Files
</title>

<para>When files are accepted by the <application>lpd</application> 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.</para>

<para>For descriptive purposes,
we will use the following printcap entry as a guide:</para>

<para>
<informalexample>
<screen>pr|alias
  :sd=/var/lpd/pr_public
  :cd=/var/lpd/pr</screen>
</informalexample>
</para>


<sect1><title>Spool Queue</title>

<para>
<itemizedlist>

<listitem>
<para> <literal>sd=</literal><emphasis>Spool queue directory name</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>The
<option>:sd</option>
option in the printcap entry specifies the spool queue
directory.
If there is no
<option>:sd</option>
entry or value,
then the printer can only be used by the clients such as <application>lpq</application>
to locate the destination for a print job.
All information,
files,
etc.,
for a print queue is stored in the spool directory.</para>

</sect1>

<sect1 id="queuelockfile"><title>Queue Lock File </title>

<para>
<itemizedlist>

<listitem>
<para> <literal>spool_lock_file</literal><emphasis>spool queue lock file - default %P</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>When the <application>lpd</application> 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 specified by the
<literal>queue_lock_file</literal> option
(default
<literal>%P</literal>
- the %P is expanded to the print queue name)
is used.
In our example,
the lock file would be:
<filename>/var/lpd/pr/pr</filename>.</para>

<para>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 <application>lpq</application> can determine queue activity.</para>

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

</sect1>

<sect1 id="queuecontrolfile"><title>Spool Control File </title>

<para>
<itemizedlist>

<listitem>
<para> <literal>spool_control_file</literal><emphasis>spool queue control file - default control.%P</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>The spool control file is used to control the operations of the
spooler,
and is in the spool or control directory.
The file name specified by the
<literal>queue_control_file</literal> option
(default
<literal>control.%P</literal>
- the %P is expanded to the print queue name);
in our example,
the control file would be:
<filename>/var/lpd/pr/control.pr</filename>.</para>

<para>The <application>lpc</application> program sends spool control requests to the
<application>lpd</application> 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:
<informalexample>
<screen>key value</screen>
</informalexample>
</para>

<para>The following keys and their values are currently supported.</para>

<informaltable frame=all>
<tgroup cols=3 align=left rowsep=1 colsep=1>
<thead>
<row><entry>Key</entry><entry>Value</entry><entry>Purpose</entry></row>
</thead>
<tbody>
<row><entry> <literal remap=tt>printing_disabled</literal>
</entry><entry> 0 or 1
</entry><entry> disable printing of jobs in queue <!-- <br> -->
</entry></row>
<row><entry> <literal remap=tt>spooling_disabled</literal>
</entry><entry> 0 or 1
</entry><entry> disable placing jobs in queue <!-- <br> -->
</entry></row>
<row><entry> <literal remap=tt>holdall</literal>
</entry><entry> 0 or 1
</entry><entry> hold jobs until released <!-- <br> -->
</entry></row>
<row><entry> <literal remap=tt>redirect</literal>
</entry><entry> <literal remap=tt>printer</literal>
</entry><entry> transfer jobs to indicated printer <!-- <br> -->
</entry></row>
<row><entry> <literal remap=tt>class</literal>
</entry><entry> <literal remap=tt>glob expression</literal>
</entry><entry> print only jobs whose class matches glob expression <!-- <br> -->
</entry></row>
<row><entry> <literal remap=tt>server_order</literal>
</entry><entry> <literal remap=tt>printer name list</literal>
</entry><entry> preferred order of printer use <!-- <br> -->
</entry></row>
<row><entry> <literal remap=tt>debug</literal>
</entry><entry> <literal remap=tt>debugging options</literal>
</entry><entry> debugging and tracing <!-- <br> -->
</entry></row>
</tbody>
</tgroup>
</informaltable>

<para>The <literal>printing_disabled</literal> and <literal>spooling_disabled</literal>
are managed using the
<command remap=tt>lpc start</command>,
<command remap=tt>lpc stop</command>,

<command remap=tt>lpc enable</command>
and
<command remap=tt>lpc disable</command>
commands.
Similarly,
<literal>holdall</literal>
is enabled and disabled by
<literal>holdall</literal>
and
<literal>noholdall</literal>
commands respectively.
When holdall is enabled,
jobs placed in the print queue will be held until they are explicitly
released for printing by an
<command remap=tt>lpc release</command>
command.</para>

<para>The
<literal>redirect</literal>
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
<command remap=tt>lpc redirect off</command>
removes the redirect entry from the
control file.</para>

<para>The
<literal>class</literal>
entry is similar in operation to the
<literal>holdall</literal>,
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.</para>

<para>The <literal>server_order</literal>
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 <emphasis>round robin</emphasis>
use of printers,
rather than having all jobs printed to the first printer in the
list of printers.</para>

<para>The
<literal>debug</literal>
entry is set by the
<command remap=tt>lpc debug</command>
command,
and is used to enable or disable debugging and tracing information
for a spool queue.
This facility is for diagnostic purposes only.</para>

</sect1>

<sect1 id="logfiles"><title>Log and Status Files </title>

<para>
<itemizedlist>

<listitem>
<para> <literal>create_files</literal><emphasis>create log, accounting and status files</emphasis></para>
</listitem>

<listitem>
<para> <literal>lf=</literal><emphasis>log file name (default: log)</emphasis></para>
</listitem>

<listitem>
<para>
<literal>max_log_file_size#</literal><emphasis>maximum log file size (Kbytes)</emphasis></para>
</listitem>

<listitem>
<para>
<literal>min_log_file_size#</literal><emphasis>minimum log file size (Kbytes)</emphasis></para>
</listitem>

<listitem>
<para>
<literal>max_accounting_file_size#</literal><emphasis>maximum accounting file size (Kbytes)</emphasis></para>
</listitem>

<listitem>
<para>
<literal>min_accounting_file_size#</literal><emphasis>minimum accounting file size (Kbytes)</emphasis></para>
</listitem>

<listitem>
<para>
<literal>max_status_line#</literal><emphasis>maximum status line length (characters) </emphasis></para>
</listitem>

<listitem>
<para>
<literal>max_status_size#</literal><emphasis>maximum status file size (Kbytes)</emphasis></para>
</listitem>

<listitem>
<para>
<literal>min_status_size#</literal><emphasis>minimum status file size (Kbytes)</emphasis></para>
</listitem>

<listitem>
<para> <literal>ps=</literal><emphasis>filter status file name (default: status)</emphasis></para>
</listitem>

<listitem>
<para> <literal>queue_status_file=</literal><emphasis>queue status file (default: status.%P)</emphasis></para>
</listitem>

<listitem>
<para> <literal>short_status_date=</literal><emphasis>display short (hh:mm) timestamp (default: true)</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>During operation,
the <application>lpd</application> server records the current printing operations
in the spool queue status file specified by the <literal>spool_status_file</literal> option
(default
<literal>status.%P</literal>
- the %P is expanded to the print queue name);
for our example, this would be
<filename>/var/lpd/pr/status.pr</filename>.
In order to prevent this file from growing too large,
the server will periodically truncate the file.
You can force creation of these files by setting the
<literal>create_files</literal> option.
The
<literal>max_status_size</literal> configuration or printcap option
sets the maximum size (in Kbytes) of the status file;
if the file exceeds this,  only the last
<literal>min_status_size</literal> bytes
or 25% of the maximum size (default if not specified)
will be preserved.</para>

<para>
The server logs its operations in the log file specified by the
<literal>lf</literal>
(log file) option (default is <literal>lf=log</literal>).
The <literal>max_log_file_size</literal>
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
<literal>min_log_file_size</literal> bytes
or 25% of the maximum file size.
Again, the last portion of the log file is preserved.
If the <literal>max_log_file_size</literal> value is 0,
then the log file grows without limit.</para>

<para>
The server records accounting information in the log file specified by the
accounting file
<literal>af</literal>
(accounting file) option (default is <literal>af=acct</literal>).
The <literal>max_accounting_file_size</literal>
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
<literal>min_accounting_file_size</literal> bytes
or 25% of the maximum file size.
Again, the last portion of the log file is preserved.
If the <literal>max_accounting_file_size</literal> value is 0,
then the log file grows without limit.
See
<link linkend="opendevice">Open The Output Device</link>
and
<link linkend="accountingserver">Accounting Printcap Options</link>
for more details.
</para>

<para>Some filters require an additional filter status file
that they use for recording additional filter status or
other operational information.
The
<literal>ps</literal>
names this file,
and it is passed to a print filter using the
<literal>$s</literal>
option
(see

<link linkend="filteroptions">Filter Command Line Options and Environment Variables</link>
).</para>

<para>The <acronym/STDERR/ output for filters is put into the printer
status file.
This allows the filter to produce informative messages that can be displayed
as part of the user status.
In addition,
a separate status file specified by the
<literal>ps</literal>
(Printer Status) can be used as well.
This file is
<emphasis>not</emphasis>
truncated by the &LPRng; system.</para>

<para>When reporting status information,
the length of line returned can be a problem.
The
<literal>max_status_line#79</literal>
option restricts the status line
to a maximum of 79 characters.</para>

<para>The <literal>short_status_date</literal> (default is true)
option causes short (hour:minute) timestamps to be displayed
on status queries.</para>

</sect1>

<sect1 id="jobfiles"><title>Job Files
</title>

<para>
<itemizedlist>

<listitem>
<para>
<literal>longnumber</literal><emphasis>long job number</emphasis></para>
</listitem>

<listitem>
<para> <literal>default_priority=</literal><emphasis>default job priority</emphasis></para>
</listitem>

<listitem>
<para> <literal>nline_after_file</literal><emphasis>N line after data file</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>A print job consists of a control file and one or more data files.

<link linkend="rfc1179">RFC1179</link>

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:
<informalexample>
<screen>Hh4.private
J/tmp/file1 /tmp/file2
CA
Lpapowell
Ppapowell
fdfA002230h4.private
N/tmp/file1
UdfA002230h4.private
fdfB002230h4.private
N/tmp/file2
UdfB002230h4.private</screen>
</informalexample>
</para>

<para>The first part of the control file contains general information generated
by the <application>lpr</application> 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 (.).</para>

<para>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
<emphasis>format</emphasis>
to be used to process the file.
See

<link linkend="printingjob">print file formats</link>

for more information about its use.</para>

<table id=cfline frame=all><title>Control File Lines</title>
<tgroup cols=3 align=left colsep=1 rowsep=1>
<thead>
<row><entry>Key</entry><entry>Meaning</entry><entry>Generated By</entry></row>
</thead>
<tbody>
<row><entry>Key</entry><entry>Meaning</entry><entry>Generated By</entry></row>
<row><entry>A</entry><entry>identifier *</entry><entry>&LPRng; internal</entry></row>
<row><entry>C</entry><entry>class</entry><entry>lpr -C class</entry></row>
<row><entry>D</entry><entry>date</entry><entry>lpr</entry></row>
<row><entry>H</entry><entry>originating host</entry><entry>lpr</entry></row>
<row><entry>I</entry><entry>indent</entry><entry>lpr -i indent</entry></row>
<row><entry>J</entry><entry>jobname</entry><entry>lpr -J jobname (default: list of files)</entry></row>
<row><entry>L</entry><entry>bnrname</entry><entry>lpr -U username</entry></row>
<row><entry>N</entry><entry>filename</entry><entry>(see text)</entry></row>
<row><entry>M</entry><entry>mailname</entry><entry>lpr -m mailname</entry></row>
<row><entry>P</entry><entry>logname</entry><entry>lpr</entry></row>
<row><entry>Q</entry><entry>queuename</entry><entry>lpr -Q</entry></row>
<row><entry>R</entry><entry>accntname</entry><entry>lpr -R accntname</entry></row>
<row><entry>S</entry><entry>slinkdata *</entry><entry>lpr</entry></row>
<row><entry>T</entry><entry>prtitle</entry><entry>lpr -T prtitle</entry></row>
<row><entry>U</entry><entry>unlnkfile</entry><entry>(see text)</entry></row>
<row><entry>W</entry><entry>width</entry><entry>lpr -w width</entry></row>
<row><entry>Z</entry><entry>zopts *</entry><entry>lpr -Z zopts</entry></row>
<row><entry>1</entry><entry>font1</entry><entry>lpr -1 font1</entry></row>
<row><entry>2</entry><entry>font2</entry><entry>lpr -2 font2</entry></row>
<row><entry>3</entry><entry>font3</entry><entry>lpr -3 font3</entry></row>
<row><entry>4</entry><entry>font4</entry><entry>lpr -4 font4</entry></row>
</tbody>
</tgroup>
</table>

<para>The entries marked with * are used only by &LPRng;.
<literal remap=tt>N</literal>
and
<literal remap=tt>U</literal>
lines
are associated with a print file.
The
<literal remap=tt>N</literal>
line is the original name of the print
file.
By default,
&LPRng; places this line <emphasis>before</emphasis>
the corresponding data file.
You can use the
<literal>nline_after_file</literal>
option to have &LPRng; place the N line after the data file line.
The
<literal remap=tt>U</literal>
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.</para>

<para>The names of control and data files follow a very strict pattern.
Control files have the format
<literal>cfX</literal><replaceable>number</replaceable><emphasis>host</emphasis>,
where X is an upper case letter,
<emphasis>number</emphasis>
is (usually) a 3 digit number,
and <emphasis remap=bf>host</emphasis>
is the host name.

<link linkend="rfc1179">RFC1179</link>

restricted the total length of the control file name to 32 characters;
&LPRng; has a much looser limit.</para>

<para>Data file names must follow the same pattern as the control file name,
and have the format
<literal>dfX</literal><replaceable>number</replaceable><emphasis>host</emphasis>.
The X can be in the range A-Za-z,
allowing at most 52 data files for a job.
The <emphasis>number</emphasis>
and <emphasis remap=bf>host</emphasis>
must be identical to the corresponding
control file.</para>

<para>By convention,
&LPRng; uses the X of the control file name to set a priority for the
job.
A job with control file name
<literal>cfA...</literal>
will have <emphasis>lower</emphasis>
format
than a job with format
<literal>cfB...</literal>,
and so forth.
The <application>lpr</application>
program uses the first letter of the class name
or an explicit priority value to set the letter value.
If none of these are specified, then the
<literal>default_priority</literal> value from the configuration or printcap
entry is used.</para>

<para>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
<literal>longnumber</literal>
option will use 6 digit job numbers.
This must be used with care when operating with non-&LPRng; software.</para>

</sect1>

<sect1><title>Job Hold File</title>

<para>The information used to control the printing
of a job is string in a <emphasis>hold file</emphasis>.
The entries in this file have the form:
<informalexample>
<screen>key=[value]</screen>
</informalexample>
</para>

<para>The following is an example of a hold file:
<informalexample>
<screen>server=0
A=papowell@astart.com
J=jobname
transfername=cfA001astart.com
datafiles=N=file\002transfername=dfA001astart.com
subserver=0
attempt=3
error=cannot open printer
hold=0
priority=0
remove=0
routed=0</screen>
</informalexample>
</para>

<para>
The contents of the control file are stored as X=&lt;value>
entries, where X is the upper case letter corresponding to the control
file entry.
The <literal/datafiles/
entry contains the data file information,
as a set of fields separated by field separator characters.
</para>

<para>The
<literal>server</literal>
and
<literal>subserver</literal>
entry records the process ID of the server process
and the subserver process that is printing the job.
The
<literal>attempt</literal>
field records the total number of attempts to print the job.
The
<literal>error</literal>
field records any error that would prevent the job from being printed.
This information is reported by the <application>lpq</application> program.</para>

<para>The
<literal>hold</literal>
field is non-zero when the

<command remap=tt>lpc hold</command>
command is used to
explicitly prevent the job from being printed;

<command remap=tt>lpc release</command>
will clear the field and allow the job to be printed.</para>

<para>The
<literal>priority</literal>
field is modified by the

<command remap=tt>lpc topq</command>
command and is used to provide an overriding priority to printing the file.</para>

<para>The
<literal>remove</literal>
field is non-zero when the file has been printed and should be removed.</para>

<para>The
<literal>routed</literal>
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

<link linkend="destinations">routing filter</link>.
The information is recorded by information in the hold file.
The following is an example of routing information.
Normally this information is stored in a compressed format
with one line per destination,
but for clarity this has been broken out into plain text form:</para>

<para>
<informalexample>
<screen>active 0
attempt 0
done 0
hold 0
priority 0
remove 0
routed 880892602
route 1
  dest t1
  ident papowell@h4+705.1
  error
  copies 1
  copy_done 0
  status 0
  active 0
  attempt 0
  done 0
  hold 0
  sequence 0
  priority B
  CB
  end
route 2
  dest t1
  ident papowell@h4+705.2
  error
  copies 0
  copy_done 0
  status 0
  active 0
  attempt 0
  done 0
  hold 0
  sequence 1
  end</screen>
</informalexample>
</para>

<para>Routing information lines start with
<literal>route</literal>
followed by individual routing entry information.
The
<literal>route</literal>
<literal>dest</literal>,
<literal>copies</literal>,
<literal>priority</literal>,
and
<literal>Xnnnn</literal>
entries are
derived from the output of the router program;
other fields are used during the printing process.
The <literal>copy_done</literal> records the numbers of copies done,
while the
<literal>done</literal>
records that the entry has been completed.
The
<literal>status</literal>
is the process ID of the server process
doing the printing.</para>

<para>The output from  route filter  that generated the above file was:
<informalexample>
<screen>dest t1
copies 1
priority B
CB
end
dest t1
end</screen>
</informalexample>
</para>

</sect1>

<sect1 id="ah"><title>Job State </title>

<para>Options used:
<itemizedlist>

<listitem>
<para>
<literal>ah</literal> FLAG <emphasis>Automatically hold jobs</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>A job can be in the following state:
<orderedlist>

<listitem>
<para>Initial.
This is the state during job submission.
Jobs in the initial state do not have any status displayed for them.</para>
</listitem>

<listitem>
<para>Held.
Once a job is submitted,
it can either be printed or <emphasis>held</emphasis>.
The
<literal>ah</literal>
printcap option specifies that all jobs are
automatically held on submission.
The

<command remap=tt>lpc release</command>
and

<command remap=tt>lpc redo</command>
command will cause these jobs to be printed and
the <application>lprm</application> command can remove these jobs.</para>
</listitem>

<listitem>
<para>Active.
The job is being processed for printing or transfer to another queue.</para>
</listitem>

<listitem>
<para>Pending.
Jobs which can be printed but are not active.
This can be due to the printer being busy or
the job <emphasis remap=bf>class</emphasis>
not being printed.</para>
</listitem>

<listitem>
<para>Error.
Jobs which have encountered an error during printing.
The

<command remap=tt>lpc release</command>
and

<command remap=tt>lpc redo</command>
command will cause these jobs to be printed and
the <application>lprm</application> command can remove these jobs.</para>
</listitem>

<listitem>
<para>Done.
Jobs which have completed printing,
but which are not yet removed from the print queue.
See the
<emphasis remap=tt>
<link linkend="normalterm">save_when_done</link>
</emphasis>
flag for more information.
The <application>lprm</application> command can remove these jobs.</para>
</listitem>

</orderedlist>
</para>

<para>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 <application>lpc</application> command.</para>

</sect1>

<sect1 id="useidentifier"><title>Job Identifier </title>

<para>For each job in a spool queue,
the &LPRng; software creates a unique identifier.
This identifier is recorded in the control file
<literal remap=tt>A</literal>
line.
It can be used by the various client programs for identifying jobs,
and is displayed by the <application>lpq</application> program as status information.</para>

</sect1>
</chapter>

<chapter id=configfile><title>Configuration File, Defaults and Overrides </title>
<para>Options used:
<itemizedlist>

<listitem>
<para> <literal>allow_getenv</literal> FLAG <emphasis>use GETENV environment variable</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>The &LPRng; options are obtained as follows:
<itemizedlist>

<listitem>
<para>The compile time defaults.
These are in the <filename>LPRng/src/common/vars.c</filename>
file.</para>
</listitem>

<listitem>
<para>If the &LPRng; software has been compiled with the regression testing
<acronym>GETENV</acronym> option enabled,
the configuration information in the file specified by the
<literal>LPD_CONF</literal> environment variable will be used.
This can only be used if you are not setuid ROOT or as ROOT
as it opens severe security loopholes.</para>
</listitem>

<listitem>
<para>The file specified by the <literal>config_file</literal> compile time
option, usually <filename>/etc/lpd.conf</filename> or <filename>/usr/local/etc/lpd.conf</filename>,
and referred to a the <filename>lpd.conf</filename> file.
If the <literal>config_file</literal> option value has
the form <filename>|/pathname</filename>,
then <filename>/pathname</filename> must be an executable program
and will be run with the standard set of filter
options.
It must write configuration option values to its <acronym/STDOUT/
and exit with a 0 status.</para>
</listitem>

<listitem>
<para>In order to protect system security,
the
<filename>lpd.conf</filename>
(and the <filename>printcap</filename>)
file should be read only.</para>
</listitem>

<listitem>
<para>If the <literal>require_configfiles</literal> option is set in the compile time
options,
then the preceeding step must be successful,
i.e. - there must be a configuration file or the program
must execute and exit with a 0 status.</para>
</listitem>

<listitem>
<para>If a printer or spooling operation is done,
then the values in the <filename>printcap</filename>
entry for the spooler are used to override
the default and <filename>ifhp.conf</filename> file values.</para>
</listitem>

</itemizedlist>
</para>


<sect1><title>Configuration File Format</title>

<para>The configuration file format is similar to
a the fields of a printcap entry
with the difference that the leading colon is optional
and there can only be one option per line:
<informalexample>
<screen># comment
# set option value to 1 or ON
ab
:ab
# set option value to 0 or OFF
ab@
:ab
# set option value to string
str=name</screen>
</informalexample>
</para>

<para>During system installation the &LPRng; software processes the
default values in the <filename>LPRng/src/common/vars.c</filename>
file and generates a sample <filename>lpd.conf</filename> file that has the
format:
<informalexample>
<screen># Purpose: always print banner, ignore lpr -h option
#   default ab@ (FLAG off)
# Purpose: query accounting server when connected
#   default achk@ (FLAG off)
# Purpose: accounting at end (see also af, la, ar, as)
#   default ae=jobend $H $n $P $k $b $t  (STRING)
# Purpose: name of accounting file (see also la, ar)
#   default af=acct  (STRING)

# change:
# --- we change the af value to none, i.e. - no accounting
# --- file by default
af=</screen>
</informalexample>
</para>

<para>You can change option values by editing the file
as shown above then then
to force the <application>lpd</application> server to use the new options,
use the
<command remap=tt>lpc reread</command>
command.</para>

</sect1>

<sect1><title>Legacy Compatibility</title>

<para>The following arguments have been provided for compatibility with
legacy systems.</para>

</sect1>
</chapter>

<chapter id=jobsteps><title>Job Processing</title>

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

<para>Assume the
<literal>pr</literal>
printcap entry has the form:
<informalexample>
<screen>pr
    :lp=/dev/lp  OR  :lp=rp@rm
    :sd=/var/spool/lpd/pr
    :lf=log
    :filter=/usr/local/bin/lpf</screen>
</informalexample>
</para>

<para>Assume that we have used the following command to print
a set of files.
<informalexample>
<screen>lpr -Ppr file1 file2</screen>
</informalexample>
</para>

<para>This will create a control file
in the
<filename>/var/spool/lpd/pr</filename>
directory with the following contents (this is an example -
in practice there may be minor differences between the example
and an actual control file):
<informalexample>
<screen>Hh4.private
J/tmp/file1 /tmp/file2
CA
Lpapowell
Ppapowell
fdfA002230h4.private
N/tmp/file1
UdfA002230h4.private
fdfB002230h4.private
N/tmp/file2
UdfB002230h4.private</screen>
</informalexample>
</para>

<para>
We will refer to this example throughout the following sections.
</para>


<sect1 id="configsetup"><title>Configuration and Setup Options
</title>

<para>Options used:
<itemizedlist>

<listitem>
<para>
<literal>ipv6</literal> FLAG <emphasis>use IPV6 Network facilities </emphasis></para>
</listitem>

<listitem>
<para> <literal>default_tmp_dir=</literal><emphasis>temporary directory</emphasis></para>
</listitem>

<listitem>
<para> <literal>lockfile=</literal><emphasis>lpd server lock file</emphasis></para>
</listitem>

<listitem>
<para> <literal>report_server_as=</literal><emphasis>server name for status reports</emphasis></para>
</listitem>

<listitem>
<para>
<literal>spool_dir_perms=</literal><emphasis>spool directory permissions </emphasis></para>
</listitem>

<listitem>
<para>
<literal>spool_file_perms=</literal><emphasis>spool file permissions </emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>The
<literal>ipv6</literal>
specifies that the IPV6 protocol,
rather than IPV4 will be used.</para>

<para>The
<literal>lockfile</literal>
specifies the location of the
lock file used by the <application>lpd</application> server.
This file has the port number in the <literal>lpd_port</literal> value appended to form a unique
lock file name.</para>

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

<para>The <literal>report_server_as</literal> 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.</para>

<para>The <literal>default_tmp_dir</literal> option specifies a temporary
directory to be used to hold files or information temporarily
if there is no spool directory available.</para>

</sect1>
<sect1 id="submitting"><title>Submitting Jobs and Service Requests</title>


<para>Options used:
<itemizedlist>

<listitem>
<para> <literal>lpd_port=</literal><emphasis>Listening Port for <application/lpd/</emphasis></para>
</listitem>

<listitem>
<para> <literal>unix_socket_path=</literal><emphasis>Unix socket for <application/lpd/ connections</emphasis></para>
</listitem>

</itemizedlist>
</para>
<para>
After the <application/lpd/ server has done its initialization,
it will attempt to bind to the <application/lpd/ listening port specified by the
<literal/lpd_port/ value.
This value has the format <literal>[ipaddr%]port</literal>.
If the <literal>ipaddr</literal> is specified then the lpd server binds to the
interface with the specified address otherwise it binds to all interfaces.
The port value can be a number or name the name of a service;
The port corresponding to the service name is used.
The <literal/printer/ services port is 515.
If the port binding operation is successful
and the server has not been request to run in <emphasis/foreground/ mode
by the <literal/-F/ command line option,
then a child process is forked and the parent process will exit.
The child process then takes steps to disconnect itself from the control terminal
of the process that started it.
</para>
<para>
The <literal/unix_socket_path/ option specifies the pathname of a <emphasis/fifo/
socket that local processes can use instead of the TCP/IP port.
</para>
<para>
The main <application/lpd/ process will then start a
<emphasis/queue checking/ process that will check all of the spool queues
used by the server for queues that have pending jobs.
This process sends a message to the main <application/lpd/ process requesting
that it start a service process for this queue.
</para>
<para>
The <application/lpd/ process will then sit in a loop waiting for
one of the following events:
</para>
<orderedlist>
<listitem><para>
An incoming connection request.
If the maximum number of children has not been exceeded,
then a new process will be forked to handle this connection.
</para></listitem>
<listitem><para>
A child process exiting.
The server will check to see if there is a pending request to start
a server for a queue that could not be accommodated due to too many
processes running.
</para></listitem>
<listitem><para>
A request to start a service process for a queue.
If the number of active processes is less than the maximum
allowed
a service process will be started,
otherwise the request will be placed on a list for service when the
number of processes active decreases.
</para></listitem>
<listitem><para>
A timeout alarm for the queue rescanning operation.
This is discussed in the next section in detail.
</para></listitem>
</orderedlist>
<para>
When connection is accepted by the <application/lpd/ spooler,
the following steps are taken to processes the job.
</para>
<orderedlist>
<listitem><para>
First,
a timeout is established for the transfer of the information from client
to the <application/lpd/ server.
This is done to prevent a denial of service attack by processes that do
not close connections in a timely manner.
</para></listitem>
<listitem><para>
A single line is read into an internal buffer.
This line must be terminated with a <literal/NEWLINE/ character.
</para></listitem>
<listitem><para>
The input line is parsed and the actions required are determined.
</para></listitem>
<listitem><para>
If the activity requires access to the spool queue information,
then the current directory of the process is changed to the spool
directory.
This allows all file accesses to then be relative to this directory.
</para></listitem>
<listitem><para>
If the processing requires starting a spool queue server process,
a message is sent to the main <application/lpd/ server process
to start a spool queue server process.
By having all the processes serving spool queues children of the main
server process it is possible to monitor
and limit the total number of active processes.
This is important on systems with a very large number of queues.
</para></listitem>
<listitem><para>
After the processing of the original request has been completed,
the process with then check to see if the Spool Queue for the printer
should be processed.
</para></listitem>
</orderedlist>

</sect1>

<sect1 id="reception"><title>Job Reception </title>
<itemizedlist>

<listitem>
<para>
<literal>longnumber</literal> FLAG <emphasis>Long job number (6 digits)</emphasis></para>
</listitem>

<listitem>
<para>
<literal>fifo</literal> FLAG <emphasis>enforce FIFO order for reception</emphasis></para>
</listitem>

<listitem>
<para> <literal>lpd_listen_port=</literal><emphasis><application/lpd/ will listen on this port </emphasis></para>
</listitem>

<listitem>
<para> <literal>incoming_control_filter=</literal><emphasis>filter to modify incoming job control file </emphasis></para>
</listitem>

<listitem>
<para> <literal>translate_incoming_format=</literal><emphasis>change data file formats</emphasis></para>
</listitem>

<listitem>
<para> <literal>accounting_fixupname=</literal><emphasis>change accounting name infomration</emphasis></para>
</listitem>

</itemizedlist>

<para>
When a print job is received,
the <application/lpd/ server will assign a job number to the
new job.  Historically these have been in the range of 0 to 999,
but the
<literal/longnumber/
option allows numbers from 0 to 999,999 to be assigned.
The server then checks to see that all of the data files for a job have been
transferred correctly.
</para>

<para>
The <literal/fifo/ flag forces all jobs receieved from a particular
host to be processed in First In, First Out (fifo) order.
No new jobs will be processed until the incoming job has been released
into the spool queue.
</para>

<para>
If an incoming control file filter is specified,
then the incoming job's control file will be passed through the
<literal/incoming_control_filter/
filter if it is specified.
This allows the modification of the control file.
</para>
<para>
The majority of control file modifications are simple job file format
changes.
The
<literal/translate_incoming_format/ option provides a simple
way to do this.
See the
<emphasis remap=tt>
<link linkend="translateformat">translate_format</link>
</emphasis>
for details.
</para>

<para>
The <literal/accounting_namefixup/ option was introduced to allow
a simple mapping of host and user names to names to be used for
accounting purposes.
By convention,
the <literal/R/ field in the job control file specifies the name
to be used for accounting purposes.
<informalexample>
<screen>
accounting_namefixup=list[,list]*
   where list is:   host(,host*)[=user(,user*)]
</screen>
</informalexample>
The incoming job is checked to see if the originating host
is in the list of hosts;  the first matching one found is used.
</para>
<para>
Each host list has the format: host,host... where host has the
same format used for the <literal/oh/ and other host name matching
options.  You can  use '!host' to invert matching.  For example:
<literal/host1,127.*,!somehost/.
</para>
<para>
When a host match is found,  the name to be used for the user
is determined from the user list; if none is specified then no
changes are made.  Each entry in the user list has the format
<literal/${option}/
or
<literal/name/; the
<literal/${option}/
values are extracted from
the control file (capital letters) or printcap/configuration
information (lower case letters/names).  The first non-empty
value list value used.
For example, the
<literal/${R},${L},${accounting_name},anon/
will select the control file 'R' option value,
then the 'L' option value,
then the printcap/config option 'accounting_name' value,
and then finally the 'anon' value.
</para>
<para>
The control file is then passed through the
<literal/router/ routing filter.
This allows the incoming job to be redirected to one or more
print queues.
For details about all of the capabilities of the routing filter,
see
<link linkend="destinations">Dynamic Routing</link>.
</para>

<para>
Finally,
the <application/lpd/ server is requested to start a spooling process
that will print the newly arrived job.
</para>

</sect1>

<sect1 id="spoolq"><title>Spool Queue Processing </title>

<para>Options used:
<itemizedlist>

<listitem>
<para> <literal>lpd_force_poll=</literal><emphasis>Force <application/lpd/ to periodically poll print queues </emphasis></para>
</listitem>

<listitem>
<para>
<literal>lpd_poll_time=</literal><emphasis>Time between polls</emphasis></para>
</listitem>

<listitem>
<para>
<literal>max_servers_active=</literal><emphasis>Maximum number of active servers</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>When the <application>lpd</application> server starts,
it will fork a set of subserver processes,
each which will handle an individual queue.</para>

<para>If a system has a large number of queues,
then this forking operation may result in the <application>lpd</application> server
exhausting the process resources.
To control this,  the
<literal>max_servers_active</literal> 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.</para>

<para>Due to the limits on the number of processes,
there may be times when a job is placed in a queue,
but the <application>lpd</application> server is unable to start handling the job.
When all of the children of the main <application>lpd</application> server have
exited,
the server starts a timer.
After <literal>lpd_poll_time</literal> 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.</para>

<para>The <literal>lpd_force_poll</literal> 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.</para>

</sect1>

<sect1 id="opendevice"><title>Opening the Output Device

</title>

<para>Options used:
<itemizedlist>

<listitem>
<para>
<literal>achk</literal> FLAG <emphasis>Accounting check at start</emphasis></para>
</listitem>

<listitem>
<para> <literal>af=</literal><emphasis>Accounting File</emphasis></para>
</listitem>

<listitem>
<para>
<literal>ar</literal> FLAG <emphasis>Remote printer accounting enabled</emphasis></para>
</listitem>

<listitem>
<para> <literal>as=</literal><emphasis>Accounting at start</emphasis></para>
</listitem>

<listitem>
<para>
<literal>connect_grace=</literal><emphasis>Time between jobs</emphasis></para>
</listitem>

<listitem>
<para>
<literal>connect_interval=</literal><emphasis>Connection interval</emphasis></para>
</listitem>

<listitem>
<para>
<literal>connect_timeout=</literal><emphasis>Connection timeout</emphasis></para>
</listitem>

<listitem>
<para> <literal>control_filter=</literal><emphasis>Control file filter</emphasis></para>
</listitem>

<listitem>
<para>
<literal>ff=</literal><emphasis>form feed</emphasis></para>
</listitem>

<listitem>
<para>
<literal>fo</literal> FLAG <emphasis>form feed on open</emphasis></para>
</listitem>

<listitem>
<para>
<literal>la</literal> FLAG <emphasis>Local printer accounting enabled</emphasis></para>
</listitem>

<listitem>
<para> <literal>ld=</literal><emphasis>leader on open (initialization string)</emphasis></para>
</listitem>

<listitem>
<para>
<literal>lk</literal> FLAG <emphasis>Lock IO device</emphasis></para>
</listitem>

<listitem>
<para> <literal>lp=</literal><emphasis>IO device pathname</emphasis></para>
</listitem>

<listitem>
<para>
<literal>nb</literal> FLAG <emphasis>Nonblocking device open</emphasis></para>
</listitem>

<listitem>
<para>
<literal>network_connect_grace=</literal><emphasis>Interval in secs between jobs</emphasis></para>
</listitem>

<listitem>
<para> <literal>of=</literal><emphasis>of filter</emphasis></para>
</listitem>

<listitem>
<para>
<literal>retry_econnrefused</literal> FLAG <emphasis>Retry if open failed</emphasis></para>
</listitem>

<listitem>
<para>
<literal>retry_nolink</literal> FLAG <emphasis>Retry if open failed</emphasis></para>
</listitem>

<listitem>
<para>
<literal>rm=</literal><emphasis>the remote machine to send the job to</emphasis></para>
</listitem>

<listitem>
<para>
<literal>rp=</literal><emphasis>the remote print queue to send the job to</emphasis></para>
</listitem>

<listitem>
<para>
<literal>rw</literal> FLAG <emphasis>device opened RW flag</emphasis></para>
</listitem>

<listitem>
<para> <literal>server_tmp_dir=</literal><emphasis>temporary directory</emphasis></para>
</listitem>

</itemizedlist>

Sequence of Operations:
<orderedlist>

<listitem>
<para>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 <literal>server_tmp_dir</literal> directory.</para>
</listitem>

<listitem>
<para>If the accounting file specified by
<literal>af</literal>
exists,
it is opened (af_fd) and the af_fd is passed as file descriptor
3 to all filters.
If the
<literal>af</literal>
value has the form <literal>af=|/program</literal>
then the program is started and the program <acronym/STDIN/ is used as af_fd.
If the
<literal>af</literal>
value has the form <literal>af=host%port</literal>,
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 <acronym/STDIN/ (file descriptor 0)
is actually opened read/write, and is used when information is needed
from the accounting filter or remote server.
See

<link linkend="accountingserver">Accounting Printcap Options</link>

for more information on the &LPRng; accounting support.</para>
</listitem>

<listitem>
<para>
<anchor id="accountstart">
If
<literal>la</literal>
(local accounting) is true and we are printing a job
or
<literal>ar</literal>
(remote accounting) is true and we are transferring a job,
the
<literal>as</literal>
value is examined.
If it is a filter (program) specification,
then the program is started with its <acronym/STDIN/ attached to
<filename>/dev/null</filename>, <acronym/STDOUT/ will be read by the print spooler,
<acronym/STDERR/ output will be written to the error log.
The lpd program will wait until the accounting filter program terminates,
and examine the error code for action, as for the other filters
(see

<link linkend="errorcodes">errorcodes</link>
 below).
If the exit status is 0,
(JSUCC) then the printing process will continue,
if JHOLD the job will be held,
if JREMOVE the job will be removed,
if JFAIL the job processing will terminate with a JFAIL indication,
otherwise
the job processing will terminate with a JABORT indication.</para>
</listitem>

<listitem>
<para>If the accounting filter exited with a JSUCC (no error code)
and the
<literal>achk</literal>
(accounting check) flag is set,
the line read from the accounting filter <acronym/STDOUT/ will be examined.
This line should be
<literal>accept</literal>,
<literal>hold</literal>,
<literal>fail</literal>,
<literal>remove</literal>,
otherwise the job processing terminates with a JABORT indication.
An
<literal>accept</literal>
will allow the job to be printed,
<literal>hold</literal>
will hold the job,
<literal>fail</literal>
will cause the job to fail,
<literal>remove</literal>
will cause the job to be removed.</para>
</listitem>

<listitem>
<para>If the
<literal>connect_grace</literal>
value is non-zero and the server is opening a device or
<literal>network_connect_grace</literal> 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.</para>
</listitem>

<listitem>
<para>The
<literal>lp</literal>
option is checked to determine the type of IO device.
<informaltable frame=all>
<tgroup cols=2 align=left rowsep=1 colsep=1>
<thead>
<row><entry>Format</entry><entry>Meaning</entry></row>
</thead>
<tbody>
<row><entry><filename>/pathname</filename></entry><entry>Absolute pathname of IO device</entry></row>
<row><entry><literal>pr@host</literal></entry><entry>transfer to
<literal>pr</literal>
on remote
<literal>host</literal></entry></row>
<row><entry><literal>host%port</literal></entry><entry>open a TCP/IP connection to port on host. host can be name or IP address</entry></row>
<row><entry><literal>|filter</literal></entry><entry>run the filter program; it <acronym/STDIN/ will be used as device</entry></row>
</tbody>
</tgroup>
</informaltable>
</para>
</listitem>

<listitem>
<para>The IO device specified by
<literal>lp</literal>
is opened write-only or read-write if the
<literal>rw</literal>
flag is true, and the resulting file descriptor is io_fd.
If the
<literal>nb</literal>
flag is set,
a non-blocking open will be done as well.
If the
<literal>lk</literal>
(lock device) flag is true,
the device will be locked against use by other <application/lpd/ servers.</para>
</listitem>

<listitem>
<para>If a
<literal>host%port</literal>
combination,
a TCP/IP connection will be opened to the remote port and the connection will
be used as io_fd.</para>
</listitem>

<listitem>
<para>If a filter program is specified,
the filter program will be run and the <acronym/STDIN/ of the filter will be
used as the device file descriptor.</para>
</listitem>

<listitem>
<para>If a <literal>rp@rm</literal> combination,
or none of the above combinations are true and the
<literal>rm</literal>
and
<literal>rp</literal>
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.</para>
</listitem>

<listitem>
<para>If the <literal>connect_timeout</literal> 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.</para>
</listitem>

<listitem>
<para>If a connection is to a network address
(i.e. - <function>connect()</function> system call)
and the connection attempt fails with an <acronym>ECONNREFUSED</acronym>
error,
if the <literal>retry_econnrefused</literal>
flag is set then the connection attempt is retried,
but this time using an alternative port number.
See

<link linkend="rfc1179ref">RFC1179</link>
 for details.
This is repeated until all of the possible originating port numbers
are exhausted.</para>
</listitem>

<listitem>
<para>If the open or connect operation fails,
and the <literal>retry_nolink</literal> flag is set,
then the server will pause for a minimum of
<literal>connect_grace</literal> plus a multiple of
<literal>connect_interval</literal> seconds
based on the number of attempts
before retrying the open operation.
Note that the interval may increase as the number of attempts
increases.</para>
</listitem>

<listitem>
<para>If printing a job and the
<literal remap=tt>:of</literal>
filter is specified,
it is created with its <acronym/STDOUT/ (fd 1) attached to the io_fd.
Its stdin (of_fd) will be used in the steps listed below.
If there is no
<literal remap=tt>:of</literal>
filter,
then the of_fd value will be the io_fd descriptor.</para>
</listitem>

<listitem>
<para>If transferring a job and the <literal>control_filter</literal> option is specified,
then the program specified by the <literal>control_filter</literal>
value will be run. It will have its <acronym/STDIN/ set to the control file,
and its <acronym/STDOUT/ output will be used as the new value of the control file
to transfer to the remote host.
See

<link linkend="filteroptions">Filter Command Line Options and Environment Variables</link>

for details of options passed to the control filter,
and

<link linkend="errorcodes">errorcodes</link>
 for the exit codes of the filter.</para>
</listitem>

<listitem>
<para>If the operation is a job transfer, the operation proceeds as outlined in

<link linkend="rfc1179ref">RFC1179</link>,
and then the

<link linkend="normalterm">Normal Termination</link>
 operations are
carried out.</para>
</listitem>

<listitem>
<para>If the operation is a print operation
and the
<literal>ld</literal>
(leader on open) value is provided,
the string
is translated (escapes removed)
and written to the of_fd file descriptor.</para>
</listitem>

<listitem>
<para>If the
<literal>fo</literal>
(form feed on open) flag is true, then the
<literal>ff</literal>
(form feed) string
is translated (escapes removed)
and written to the of_fd file descriptor.</para>
</listitem>

</orderedlist>
</para>

</sect1>

<sect1 id="bannerprinting"><title>Printing Banners </title>

<para>Options used:
<itemizedlist>

<listitem>
<para>
<literal>ab</literal> FLAG <emphasis>Always print banner (default FALSE)</emphasis></para>
</listitem>

<listitem>
<para> <literal>be=</literal><emphasis>End banner generator program</emphasis></para>
</listitem>

<listitem>
<para> <literal>bl=</literal><emphasis>Short banner line format</emphasis></para>
</listitem>

<listitem>
<para> <literal>bp=</literal><emphasis>Banner generator program</emphasis></para>
</listitem>

<listitem>
<para> <literal>bs=</literal><emphasis>Start banner generator</emphasis></para>
</listitem>

<listitem>
<para> <literal>generate_banner</literal> FLAG <emphasis>Generate banner for forwarded jobs</emphasis></para>
</listitem>

<listitem>
<para>
<literal>hl</literal> FLAG <emphasis>Banner (header) Last</emphasis></para>
</listitem>

<listitem>
<para> <literal>of=</literal><emphasis>Banner and File Separator Filter</emphasis></para>
</listitem>

<listitem>
<para>
<literal>sb</literal> FLAG <emphasis>Short banner (default FALSE)</emphasis></para>
</listitem>

<listitem>
<para>
<literal>sh</literal> FLAG <emphasis>Suppress header (banners) (default FALSE)</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>Banner printing is one of the more complicated configuration options
of &LPRng;.
This is due mainly to historical evolution of the software,
as well as a lack of a well defined standard for
filter responsibilities.
In the original BSD print spoolers,
the philosophy was that banner printing should be delegated to the
filters,
as they were the most aware of the capabilities of the printers.
This required an
<emphasis>out of band</emphasis>
method to convey banner printing information to the filter,
and resulted in a complicated interface.
The original interface was:
<orderedlist>

<listitem>
<para>The filter doing banner printing was invoked as a special
<literal remap=tt>:of</literal>
filter,
or passed a special flag.</para>
</listitem>

<listitem>
<para>The print spooling software would send a special
<emphasis remap=bf>single line</emphasis>
of information telling it what the banner information should be.
Note that this line was never documented except for the source code,
and was inconsistent from version to version.
Also,
there was no indication of what to do with additional lines,
if any.</para>
</listitem>

<listitem>
<para>The filter would generate the banner,
discard the line,
and then pass other lines to the output device.</para>
</listitem>

</orderedlist>
</para>

<para>Adding to the confusion,
the original print spoolers had a
<literal remap=tt>:sh</literal>
(suppress header or banner)
flag,
which was supposed to suppress banner printing.
It did this by having the print spooler not generate the magic banner
information line.</para>

<para>A more sophisticated banner printing system would allow the
print spooler software to generate the banner,
and would then have the
<literal remap=tt>:of</literal>
filter act as a pass through.
Thus, we need configure the
<literal remap=tt>:of</literal>
filter NOT to use the first
line as banner printing information,
and to pass through all information to the device.</para>

<para>Complicating this whole mess is the
<literal>ld</literal>
 (leader option)
and
<literal>tr</literal>
 (trailer option)
which is a string sent to the output device (<literal remap=tt>:of</literal>
filter)
when the device (filter) is initialized or terminated.
This can sometimes be interpreted as the banner line,
leading to unexpected results.</para>

<para>Sequence of Operations:
<orderedlist>

<listitem>
<para>If the
<literal>sh</literal>
(suppress header) flag is true, no banner is
printed,
and the actions in this section are skipped.
No <emphasis>banner information line</emphasis>
is generated for the
<literal remap=tt>:of</literal>
filter,
and no banner printing program is invoked.
If there is an
<literal remap=tt>:of</literal>
filter and it is expecting such a line
and you have
<literal>ld</literal>
or
<literal>tr</literal>
information then
you may get unexpected results
(actually, catastrophic failure is a better term, but I digress).</para>
</listitem>

<listitem>
<para>If the
<literal>hl</literal>
(header last) flag is true the banner is printed at the end
of the job
and the actions in this section are done at the end of the job.</para>
</listitem>

<listitem>
<para>If the user does not want banner pages she can use the
<command>lpr -h </command> option.
This will cause the <application>lpr</application> program
to delete the
<literal remap=tt>L</literal>
(banner name) line in the control file.
If there is no
<literal remap=tt>L</literal>
line in the control file
and
<literal>ab</literal>
(always print a banner) is false
(the default),
then no banner is printed
and the other actions in this section are skipped.
If
<literal>ab</literal>
is true
and the
<literal remap=tt>L</literal>
line is missing then the
<literal remap=tt>N</literal>
(user login name) is used;
if it is missing as well,
then ANONYMOUS is used for the user name.</para>
</listitem>

<listitem>
<para>If a banner printing program is specified by
<literal>bp</literal>,
<literal>bs</literal>,
or
<literal>be</literal>
options,
then &LPRng; will invoke the
program to generate a banner and then send the generated
banner to the printer
via the
<literal remap=tt>:of</literal>
filter.
The banner printing program will be invoked using
the standard filter command line flags
(see

<link linkend="filteroptions">Filter Command Line Options and Environment Variables</link>

for details),
with is <acronym/STDIN/ attached to /dev/null
and <acronym/STDOUT/ attached to a file to hold the output banner.</para>
</listitem>

<listitem>
<para>If no banner printing program is specified
and the
<literal>sb</literal>
(short banner) option is TRUE (default is true),
then the <literal>bl=...</literal> (banner line) option value
is expanded and sent to the <literal>of_fd</literal>  (<literal remap=tt>:of</literal>
filter or
device.
The default
<literal>bl</literal>
value is:
<literal>bl=$-'C:$-'n Job: $-'J Date: $-'t</literal>.
Using our example, this will get translated to:
<informalexample>
<screen>papowell:A Job: file1 file2 Date: Thu Nov 27 23:02:04 PST 1997</screen>
</informalexample>
</para>
</listitem>

<listitem>
<para>If no banner printing program is specified and we have <literal>sb@</literal>
(no short banner)
then we skip banner generation,
i.e. - we do <emphasis>not</emphasis>
send a banner generation line
to the output (<literal remap=tt>:of</literal>
filter).</para>
</listitem>

<listitem>
<para>If the queue is a normal forwarding queue,
then the
<literal>generate_banner</literal>
option will invoke the
<literal>bp</literal>,
<literal>bs</literal>
or
<literal>be</literal>
program as appropriate to create a banner page file which is then made the
first (default) or last (<literal>hl</literal>
flag or <literal>be=...</literal> present)
file in a job.
This option has no effect in other types of queues.
See the
<emphasis remap=tt>
<link linkend="translateformat">translate_format</link>
</emphasis>
option as well.</para>
</listitem>

</orderedlist>
</para>

</sect1>

<sect1 id="printingjob"><title>Printing Job Files


</title>

<para>Options used:
<itemizedlist>

<listitem>
<para> <literal>Xf=</literal><emphasis>Format Filter</emphasis></para>
</listitem>

<listitem>
<para>
<literal>sf</literal> FLAG <emphasis> Suppress Form Feed Separators</emphasis></para>
</listitem>

<listitem>
<para> <literal>if=</literal><emphasis>Default F Format Filter</emphasis></para>
</listitem>

<listitem>
<para> <literal>pr=</literal><emphasis>pr formatting program</emphasis></para>
</listitem>

<listitem>
<para> <literal>send_job_rw_timeout=</literal><emphasis> print job read/write timeout </emphasis></para>
</listitem>

<listitem>
<para> <literal>send_query_rw_timeout=</literal><emphasis> status query operation read/write timeout </emphasis></para>
</listitem>

<listitem>
<para>
<literal>sf</literal> FLAG <emphasis>Suppress form feed between job files</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>Sequence of Operations:
for each job in listed in the control file,
the following operations are done in turn.
<orderedlist>

<listitem>
<para>If there is an
<literal remap=tt>:of</literal>
filter present,
the suspend string
<literal>\031\001</literal>
is written to of_fd
and the no further action is taken until the of filter is suspended.</para>
</listitem>

<listitem>
<para>The control file line for the job is examined,
and the first letter of the data file specification is used as the format.</para>
</listitem>

<listitem>
<para>If the format is
<literal remap=tt>p</literal>,
the job is first processed by the program specified by the
<literal>pr</literal>
program,
and the program output used as the print file.</para>
</listitem>

<listitem>
<para>If the format is
<literal remap=tt>f</literal>,
<literal remap=tt>l</literal>,
or
<literal remap=tt>p</literal>
then the
<literal>:if</literal>
filter is used,
otherwise the keyword
<literal>Xf</literal>
is used.
Note that certain formats such as
<literal>p, a, l</literal>, may not be used as formats.</para>
</listitem>

<listitem>
<para>The filter program is started with an appropriate set of command line options
(see

<link linkend="filteroptions">Filter Command Line Options and Environment Variables</link>
),
and with its <acronym/STDOUT/ attached to the printing device (io_fd),
<acronym/STDERR/ to a pipe which results in the output being written
to the status file.
If debugging is enabled,
then the <acronym/STDERR/ output is also written to the error log file (lf).</para>
</listitem>

<listitem>
<para>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 <literal>send_job_rw_timeout</literal> value is used.
When doing a status or query operation,
the <literal>send_query_rw_timeout</literal> 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.</para>
</listitem>

<listitem>
<para><anchor id="errorcodes">
<application>lpd</application> will then wait for the filter to exit.
The following exit codes are used by <application>lpd</application>:
<informalexample>
<screen>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
(Unused) 4, 35
(Unused) 5, 36
JHOLD    6, 37   Hold this job - reprint later
JNOSPOOL 7, 38   No spooling to this queue
JNOPRINT 8, 39   No printing from this queue
JSIGNAL  9,  40   Killed by unrecognized signal
JFAILNORETRY 10, 41 Failed, no retry
Other            Abort - terminate queue processing</screen>
</informalexample>
</para>
</listitem>

<listitem>
<para>If the filter exit status was JSUCC (0), or no error indicated,
then processing will continue otherwise the job termination takes
(see

<link linkend="abnormalterm">Abnormal Termination</link>
).</para>
</listitem>

<listitem>
<para>If the
<literal remap=tt>:of</literal>
filter is present,
then it is reactivated with a <command>kill -CONT</command> signal.</para>
</listitem>

<listitem>
<para>The the
<literal>sf</literal>
(suppress FF print file separators )
is turned off a form feed is sent between each file of a job.</para>
</listitem>

</orderedlist>
</para>

</sect1>

<sect1><title>Printing Banner At End of Job </title>

<para>Options used:
<itemizedlist>

<listitem>
<para>
<literal>hl</literal> FLAG <emphasis>Header (Banner) Last</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>The actions taken in this step are identical to those for the

<link linkend="bannerprinting">Printing Banner</link>,
with the exception that the
<literal>be</literal>
(end banner program) is used to select the banner generation program
rather than the
<literal>bs</literal>
(start banner program).</para>

<para>If we have
<literal>hl</literal>
true,
then we print a banner at the end of the job,
rather than start.</para>

</sect1>

<sect1 id="normalterm"><title>Normal Termination
</title>

<para>Options used:
<itemizedlist>

<listitem>
<para>
<literal>fq</literal> FLAG <emphasis>Form Feed on Close</emphasis></para>
</listitem>

<listitem>
<para>
<literal>la</literal> FLAG <emphasis>Local Printer Accounting</emphasis></para>
</listitem>

<listitem>
<para> <literal>tr=</literal><emphasis>Trailer on Close</emphasis></para>
</listitem>

<listitem>
<para> <literal>ae=</literal><emphasis>Accounting at end</emphasis></para>
</listitem>

<listitem>
<para> <literal>save_when_done</literal> FLAG <emphasis>Save when done</emphasis></para>
</listitem>

<listitem>
<para> <literal>save_on_error</literal> FLAG <emphasis>Do not delete on error</emphasis></para>
</listitem>

<listitem>
<para> <literal>done_jobs=N</literal><emphasis>Save status of last N jobs</emphasis></para>
</listitem>

<listitem>
<para> <literal>wait_for_eof</literal> FLAG <emphasis>Wait for EOF before closing device</emphasis></para>
</listitem>

<listitem>
<para> <literal>socket_linger</literal><emphasis>socket linger timeout</emphasis></para>
</listitem>

<listitem>
<para> <literal>half_close</literal> FLAG <emphasis>use shutdown() and not close()</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>Sequence of Operations:
<orderedlist>

<listitem>
<para>If we are printing and the
<literal>fq</literal>
flag is set and the
<literal>sf</literal>
(suppress interfile FF) flag is set,
then the
<literal>ff</literal>
(form feed) string
will be interpreted and sent to the of_fd.</para>
</listitem>

<listitem>
<para>If we are printing, the
<literal>tr</literal>
(trailer) string
will be interpreted and sent to the of_fd.</para>
</listitem>

<listitem>
<para>If printing and the
<literal>la</literal>
(local printer accounting) flag is set
or transferring a job and the
<literal>ar</literal>
(remote accounting) flag is set,
the
<literal>ae</literal>
is examined and accounting is done as described
for the
<emphasis remap=tt>
<link linkend="accountstart">as</link>
 field.</emphasis></para>
</listitem>

<listitem>
<para>If the
<literal remap=tt>:of</literal>
filter is present,
its <acronym/STDIN/ is closed,
and the <application>lpd</application> server waits for it to exit.
The exit status is used as described above.</para>
</listitem>

<listitem>
<para>If the device is a socket or network connection,
the socket linger time is set to
<literal>socket_linger</literal> value if nonzero.
</para></listitem>

<listitem><para>
If the <literal/half_shut/ flag is set,
then a <literal/shutdown(fd,WR_DONE)/ will be done on the connection.
This tells the TCP/IP stack that all data transmission has been
completed.  Errors or other information can still be read from the
connection.
If the <literal/half_shut/ flag is clear,
then a <literal/close(fd)/ will be done and
no errors or other information will be read.
</para></listitem>

<listitem><para>
If the <literal>wait_for_eof</literal> option is true (default)
then a read is done on the connection until an EOF is found.
The device (io_fd) is then closed.</para>
</listitem>

<listitem>
<para>The job is marked as completed in the spool queue.</para>
</listitem>

<listitem>
<para>
If the <literal>save_when_done</literal> flag is clear
and the <literal>done_jobs</literal> and
<literal>done_jobs_max_age</literal> values are zero (0), 
the job is removed.</para>
</listitem>

<listitem>
<para>
If the <literal>done_jobs</literal> or
<literal>done_jobs_max_age</literal> values are nonzero,
the spool queue is periodically checked and for an excess number
of jobs or jobs with old status.
This action is suppressed if either the <literal/save_when_done/ or <literal/save_on_error/
flag is set.
</para>
</listitem>


</orderedlist>
</para>

</sect1>

<sect1 id="abnormalterm"><title>Abnormal Termination </title>

<para>Options used:
<itemizedlist>

<listitem>
<para> <literal>mail_from=</literal><emphasis>Mail from user name</emphasis></para>
</listitem>

<listitem>
<para> <literal>mail_operator_on_error=</literal><emphasis>Mail to operator on error</emphasis></para>
</listitem>

<listitem>
<para>
<literal>send_try=</literal><emphasis>Maximum printing or transfer attempts</emphasis></para>
</listitem>

<listitem>
<para> <literal>save_on_error</literal> FLAG <emphasis>Do not delete on error</emphasis></para>
</listitem>

<listitem>
<para> <literal>done_jobs=N</literal><emphasis>Save status of last N jobs</emphasis></para>
</listitem>

<listitem>
<para> <literal>done_jobs_max_age=N</literal><emphasis>Remove status when older than N seconds</emphasis></para>
</listitem>

<listitem>
<para> <literal>send_failure_action=</literal><emphasis>Action on Failure</emphasis></para>
</listitem>

<listitem>
<para> <literal>sendmail=</literal><emphasis>sendmail path name and options</emphasis></para>
</listitem>

<listitem>
<para> <literal>stop_on_abort</literal> FLAG <emphasis>Stop processing queue on filter abort</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>If the job processing terminates abnormally,
the following sequence of events occurs:
<orderedlist>

<listitem>
<para>The job is marked as having an error during processing.</para>
</listitem>

<listitem>
<para>The <application/lpd/ server will attempt to kill all filters and other associated processes
by sending a SIGINT and SIGCONT (<command>kill -INT</command> and <command>kill -CONT</command>)
to them.</para>
</listitem>

<listitem>
<para>If there is a <literal>mail_operator_on_error</literal> value,
the specified operator will be mailed an error indication.
The
<literal>sendmail</literal>
option specifies the pathname of the
<emphasis>sendmail</emphasis>
program and the options needed to have it read
mail addresses from its standard input.
For example, <literal>sendmail=/usr/sbin/sendmail -oi -t</literal>
is a commonly used set of options.</para>
</listitem>

<listitem>
<para>The <literal>mail_from</literal> value specifies the user name used for
mail origination.  If not specified, the default is to use the print spool
queue or printer name.</para>
</listitem>

<listitem>
<para>If there is a <literal>send_failure_action</literal> specified,
then it is decoded and the corresponding action taken.
If the value is
<literal>remove</literal>,
<literal>hold</literal>,
<literal>abort</literal>,
or
<literal>retry</literal>,
then the job is removed, held, aborted, or retried.
If the value is <filename>|/program</filename>,
the program is executed and
the number of attempts are written to the filter <acronym/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.</para>
</listitem>

<listitem>
<para>If the status is ABORT and the
<literal>stop_on_abort</literal>
flag is set,
then further processing of jobs is terminated.
The job is not removed from the queue.</para>
</listitem>

<listitem>
<para>If the error status indicates removal,
the <literal>save_on_error</literal> flag is clear,
and the <literal>done_jobs</literal> and
<literal>done_jobs_max_age</literal> values are zero (0), 
then the job is removed from the spool queue.</para>
</listitem>

<listitem>
<para>If the error status indicates that no further operations should
be performed on the queue,
then the <application>lpd</application> server will stop processing jobs.</para>
</listitem>

<listitem>
<para>If the error code indicated that the job should be retried,
and the
<literal>send_try</literal> value is 0 or the number of attempts is less than
the <literal>send_try</literal> 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 <literal>max_connect_interval</literal> seconds.
If <literal>max_connect_interval</literal> is 0, there is no limit on the interval value.</para>
</listitem>

</orderedlist>
</para>

</sect1>

<sect1 id="bk"><title>Forwarding Jobs
</title>

<para>Options:
<itemizedlist>

<listitem>
<para>
<literal>bk</literal><emphasis>Berkeley compatible control file</emphasis></para>
</listitem>

<listitem>
<para> <literal>bq_format=</literal><emphasis>format of filtered output</emphasis></para>
</listitem>

<listitem>
<para> <literal>lpd_bounce</literal><emphasis>filter job and transfer output </emphasis></para>
</listitem>

<listitem>
<para> <literal>control_filter=</literal><emphasis>Control file filter</emphasis></para>
</listitem>

<listitem>
<para> <literal>control_file_line_order=</literal><emphasis>Control file line order</emphasis></para>
</listitem>

<listitem>
<para> <literal>nline_after_file</literal><emphasis>N line after data file</emphasis></para>
</listitem>

<listitem>
<para> <literal>send_data_first</literal><emphasis>send data files first</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>If a spool queue is doing store and forward operations,
then rather than printing a job the control files and data files
are sent to the remote printer.
In order to do this,
the following items must be arranged.
<itemizedlist>

<listitem>
<para>If necessary,
the job must be processed by filters on the local host.</para>
</listitem>

<listitem>
<para>The control file must be prepared and updated according to the
requirements of the remote site.</para>
</listitem>

<listitem>
<para>A connection must be established to the remote site.</para>
</listitem>

<listitem>
<para>The data files and control files must be transferred to the remote
site.</para>
</listitem>

</itemizedlist>
</para>

<para>One of the more serious problems is when a print spooler (LPR) program
does not generate print jobs in a manner compatible with a remote system.
While &LPRng; performs checks for improper implementations of RFC1179,
it will try to accept a job
even under the most severe abuse of the protocol.
However,
other spoolers are not so forgiving.
Some spoolers require that the contents of the control file
be in <emphasis remap=bf>exactly</emphasis>
the order that the original 1988 BSD <application/lpr/ software
generated them.
While some entries can be missing,
all the entries present in the file must be in an explicit order.</para>

<para>The
<literal>bk</literal>
(Berkeley <application/lpd/ compatible control file) option
causes <application/lpr/ and <application/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.
You can use the <literal>control_file_line_order</literal> option to specify
an even more restricted set,
and use the <literal>nline_after_file</literal> option to have the file information
line (<literal remap=tt>N</literal>
value) come after the data file entry.</para>

<para>However,
there are some very odd commercial implementations that require
<emphasis>more</emphasis>
information than is present.
To assist with this,
the <literal>control_filter</literal> option can be used.
This specifies a program that will process the control file
before it is sent to a remote destination.
The <literal>control_filter</literal> program is run with the standard set of
filter options.
<acronym/STDIN/ is attached to the control file and
the <acronym/STDOUT/ will be used as the control file value
sent to the remote host.</para>

<para>The exit code of the
<literal>control_filter</literal>
is used to determine whether to proceed in processing.
See

<link linkend="errorcodes">Errorcodes</link>
 for details.</para>

<para>Sequence of Operations:
<orderedlist>

<listitem>
<para>A copy of the control file information is made and
the copy will be modified during processing,
rather than the original.</para>
</listitem>

<listitem>
<para>If the <literal>lpd_bounce</literal> option is specified
then a temporary file is created and the job is printed
using the procedures for printing to a device,
but to the file.
This includes all of the filter operations,
banners, and so forth.
The working copy of the control file is set to have the temporary
file as the data file to be sent to the remote destination,
and the data file format is set to the <literal>bq_format</literal> value.</para>
</listitem>

<listitem>
<para>The control file is rewritten according to the requirements
of the routing information, if any.
For each destination in the routing information and each copy,
a new job identifier value will be generated.</para>
</listitem>

<listitem>
<para>The control file is rewritten according to the
<literal>bk</literal>
and <literal>control_file_line_order</literal> options.
If a control filter is specified,
the control filter program is run
and the output of the program is used as the new control file.</para>
</listitem>

<listitem>
<para>A connection is made to the remote host,
and the data and control files are transferred to the remote host
using the RFC1179 protocol.
If the <literal>send_data_first</literal> option is specified the data files
are sent first.</para>
</listitem>

<listitem>
<para>If the job was sent successfully,
the job status is updated in the same manner as for a printed job.</para>
</listitem>

</orderedlist>
</para>

</sect1>

<sect1 id="debugging"><title>Debugging </title>

<para>Options used:
<itemizedlist>

<listitem>
<para> <literal>debugging=</literal><emphasis>debugging options</emphasis></para>
</listitem>

<listitem>
<para> <literal>full_time</literal> FLAG <emphasis>full or extended time format</emphasis></para>
</listitem>

<listitem>
<para> <literal>ms_time_resolution</literal> FLAG <emphasis>millisecond time resolution</emphasis></para>
</listitem>

<listitem>
<para> <literal>syslog_device=</literal><emphasis>syslog alternative device</emphasis></para>
</listitem>

<listitem>
<para> <literal>use_info_cache</literal> FLAG <emphasis>cache printcap and other information</emphasis></para>
</listitem>

</itemizedlist>
</para>

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

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

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

<para>A typical debug entry has the format
<literal>2,network+1,database</literal>.
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.
<itemizedlist>

<listitem>
<para>nnn - general purpose debugging level</para>
</listitem>

<listitem>
<para>network - network debugging</para>
</listitem>

<listitem>
<para>database - database debugging</para>
</listitem>

<listitem>
<para>receive - job or command reception debugging</para>
</listitem>

<listitem>
<para>print - detailed job printing debugging</para>
</listitem>

</itemizedlist>
</para>

<para>The <literal>full_time</literal> flag forces the logging and other information
which has timestamps to have a full (year, month, day, etc.) timestamp.
The
<literal>ms_time_resolution</literal> flag forces millisecond time resolution
in the time stamp.
</para>

<para>The <literal>use_info_cache</literal> (default ON) causes <application>lpd</application>
to cache printcap and configuration information.
This is desirable except when trying to change values in printcap files and
test the results.
By using <literal>use_info_cache@</literal> in the configuration information,
you can get immediate responses.
Also, see

<link linkend="lpcreread">lpc reread</link>

for another method.</para>

</sect1>
</chapter>

<chapter id=filters><title>Filters </title>

<para>This section gives an overview of how &LPRng; uses filter programs,
and gives a detailed discussion of how the printcap options and
filters interact.</para>


<sect1><title>Filter Functions</title>

<para>Print filters are one of the most powerful tools in BSD-style printer
systems.</para>

<para>In general UNIX terms, a <emphasis>filter</emphasis>
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 :))</para>

<para>In the context of a BSD-style print spooler (and also &LPRng;), the term
<emphasis>filter</emphasis>
refers to a program that processes file while it is
being transferred to a printer.</para>

<para>The filter is executed with <acronym/STDIN/ reading from the file to be
printed
<acronym/STDOUT/ to the printer device or a temporary file.
<acronym/STDERR/ (file handle 2) is redirected to the status file,
and file handle3 to an accounting file or program.</para>

<para>A filter can be as simple as a <acronym>LF</acronym> to <filename>CR/LF</filename>
translator,
or it can incorporate a complete
accounting system, automatic file type translations,
or even redirect the job to another printing system.</para>

<para>The
<application>lpf</application>
filter supplied as part of the &LPRng; distribution is a
a very simple CR to CR/LF conversion filter.
The
<application remap=tt>ifhp</application>
filter provides support for more complex PostScript,
PCL,
and text printers.</para>

</sect1>

<sect1 id="exitcodes"><title>Filter Exit Codes </title>

<para>When a filter exits,
the exit code value is used by the parent process to determine
what actions to take.
Since filters are used in several places in the printing process,
not just to do format conversion,
there is a large number of recognized exit values.
<informalexample>
<screen>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
(Unused) 4, 35   (Unused)
(Unused) 5, 36   (Unused)
JHOLD    6, 37   Hold this job - reprint later
JNOSPOOL 7, 38   No spooling to this queue
JNOPRINT 8, 39   No printing from this queue
JSIGNAL  9,  40   Killed by unrecognized signal
JFAILNORETRY 10, 41 Failed, no retry
Other            Abort - terminate queue processing</screen>
</informalexample>
</para>


<sect2 id="jsucc"><title>JSUCC </title>

<para>A zero or <acronym>JSUCC</acronym>
exit value always indicates success;
a non-zero exit value indicates failure or a problem condition
and requires special handling by the parent process.</para>

</sect2>

<sect2 id="jfail"><title>JFAIL </title>

<para>When printing or performing some action that can be repeated,
such as connecting to a remote printer,
a 1 or <acronym>JFAIL</acronym>
status indicates a transient failure condition.
Depending on various configuration options,
the printing or other operation can be retried.</para>

</sect2>

<sect2 id="jabort"><title>JABORT </title>

<para>The 2 or <acronym>JABORT</acronym>
is a more serious error,
and indicates that there is no expectation that the
operation would succeed if retried.
It may also indicate that no other similar operation should
be performed.
Jobs whose print filters exit with <acronym>JABORT</acronym> are usually unprintable,
and by default are removed from the print queue.</para>

</sect2>

<sect2 id="jremove"><title>JREMOVE </title>

<para>The <acronym>JREMOVE</acronym>
status indicates that the job should be removed from the print queue.
This is a refinement of the <acronym>JFAIL</acronym> and <acronym>JABORT</acronym>
status.
The job is usually unconditionally removed from the print queue,
even if it is normally kept in the queue for reprinting.
This status is usually returned by filters which are responsible for
permission checking and is returned when the user has no permission to
print.</para>

</sect2>

<sect2 id="jhold"><title>JHOLD </title>

<para>The <acronym>JREMOVE</acronym>
status indicates that the job should be held and reprinted at
a later time.
This status is returned by various filters during the processing
of a job,
and usually indicates that the resources needed for a job
are not available.
Held jobs need to be explicitly released by the administrator.</para>

</sect2>

<sect2 id="jnospool"><title>JNOSPOOL and JNOPRINT
</title>

<para>The <acronym>JNOSPOOL</acronym> and <acronym>JNOPRINT</acronym>
are used as part of the management of load balancing queues
and the <emphasis>check idle</emphasis>
filter.
&LPRng; has the ability to run a program to check to see if a spool
queue is available for printing on a dynamic basis.
If the filter that does this checking exits with
<acronym>JNOSPOOL</acronym> or <acronym>JNOPRINT</acronym>
then jobs should not be sent to the spool queue.</para>

</sect2>

<sect2 id="jsignal"><title>JSIGNAL </title>

<para>This status is usually returned when the exiting process
is terminated by a signal or abort,
and does not exit using the
<literal>exit</literal>
facility.
It is usually handled like a <acronym>JABORT</acronym> exit status,
and is the indication of a severe and possibly non-restartable
system failure.</para>

</sect2>

<sect2 id="jnoretry"><title>JFAILNORETRY </title>

<para>This code is used under an extremely odd set of circumstances
and was used to support a sophisticated print retry system.</para>

<para>Normally when a print filter or other filter returns this code,
it is treated as <acronym>JFAIL</acronym>.
The job is marked as having an error condition
and is not
<literal>immediately</literal>
retried.
Other jobs can then be tried for printing in the queue.
It is not removed from the print queue,
but marked as
<literal>unprintable</literal>.</para>

<para>When a <emphasis>round-robin retry</emphasis>
print scheduling
algorithm is used,
if there are no other jobs available for printing then the
jobs that failed with <acronym>JFAILNORETRY</acronym> are retried.
Thus,
jobs that are submitted go to the head of the queue for printing,
and jobs that are pending for repeat are printed after them.
This algorithm is deprecated,
and that the details of this algorithm are undocumented.</para>

</sect2>

<sect2 id="jother"><title>Other Values </title>

<para>If a filter exits with other than the indicated value,
or a value inappropriate for its purpose,
then the result is treated like <acronym>JABORT</acronym>.</para>

</sect2>
</sect1>

<sect1 id="printjobformats"><title>Print Job Formats </title>
<para>Options used:
<itemizedlist>

<listitem>
<para><literal>:if</literal>,
<literal>cf</literal>,
<literal>df</literal>,
<literal>gf</literal>,
<literal>nf</literal>,
<literal remap=tt>:of</literal>,
<literal>rf</literal>,
<literal>tf</literal>,
<literal>vf</literal>,
<literal>X</literal><literal remap=tt>f</literal>,
<emphasis>Filter programs </emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>&LPRng; has inherited a set of so-called `<emphasis remap=bf>print formats</emphasis>' from its
BSD ancestor.
The format was used to specify the type of file that was being printed.
The <application>lpd</application> server
used the print format to select the filter for processing the file.
The de<emphasis remap=bf>f</emphasis>ault format is
<literal remap=tt>f</literal>.</para>

<para>The user can specify the format (i.e., the file type) by giving
the appropriate option to <application>lpr</application>:</para>

<para>
<itemizedlist>

<listitem>
<para><literal>-b</literal> or <literal>-l</literal>: Binary (literal) file. No processing should
be done.
The
<literal remap=tt>l</literal>
format is recorded as the file format.</para>
</listitem>

<listitem>
<para><literal>-c</literal>: cifplot(1) output.</para>
</listitem>

<listitem>
<para><literal>-d</literal>: TeX DVI file.</para>
</listitem>

<listitem>
<para><literal>-g</literal>: Output from the plot(3X) routines.</para>
</listitem>

<listitem>
<para><literal>-n</literal> or <literal>-t</literal>: (di)troff output.</para>
</listitem>

<listitem>
<para><literal>-p</literal>: Text file that should be pre-processed by the
<literal>pr</literal>
command, and then by the standard text filter.</para>
</listitem>

<listitem>
<para><literal>-v</literal>: Benson Varian raster image.</para>
</listitem>

</itemizedlist>
</para>

<para>Alternatively, one can also use
<literal>-Fx</literal>, where
<literal remap=tt>x</literal>
is the format specifier.
(E.g., <literal>-Fc</literal>
instead of <literal>-c</literal>.)
This last form also allows you to use other
(non-standard) format specifiers.</para>

<para>The filter for format
<literal remap=tt>X</literal>
is the value for the
<literal>Xf</literal>
printcap
option,
with some minor exceptions.
The following
<literal>Xf</literal>
options have a pre-defined meaning.</para>

<para>
<itemizedlist>

<listitem>
<para><literal>:if</literal>
The
<literal remap=tt>f</literal>
format filter,
i.e. - for the default
<literal remap=tt>f</literal>
format.
All print jobs are passed
through this one, unless another format is selected.</para>
</listitem>

<listitem>
<para><anchor id="cf">
<literal>cf</literal>
Cifplot data filter (for <literal>-c</literal> format).</para>
</listitem>

<listitem>
<para><anchor id="df">
<literal>df</literal>
Filter for DVI files (<literal>-d</literal>).</para>
</listitem>

<listitem>
<para><anchor id="gf">
<literal>gf</literal>
Graph data filter (<literal>-g</literal>).</para>
</listitem>

<listitem>
<para><anchor id="nf">
<literal>nf</literal>
Ditroff data filter (<literal>-n</literal>).</para>
</listitem>

<listitem>
<para><literal remap=tt>:of</literal>
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

<link linkend="ofdetails">OF Filter</link>
for details.</para>
</listitem>

<listitem>
<para><anchor id="rf">
<literal>rf</literal>
Filter for Fortran style files (<literal>-r</literal>).</para>
</listitem>

<listitem>
<para><anchor id="tf">
<literal>tf</literal>
Troff filter (<literal>-t</literal>).</para>
</listitem>

<listitem>
<para><anchor id="vf">
<literal>vf</literal>
(Versatek) raster image filter (<literal>-v</literal>).</para>
</listitem>

</itemizedlist>

</para>
</sect1>

<sect1 id="ofdetails"><title>OF Filter</title>

<para>The
<literal remap=tt>:of</literal>
filter is used to process banners and job separators.
The
<literal remap=tt>:of</literal>
filter is responsible for performing appropriate
processing of this information and sending to the printer
for action.</para>

<para>While the various file filters are invoked on a once per print file basis,
the
<literal remap=tt>:of</literal>
filter is invoked on a once per print job basis.</para>

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

<para>The
<literal remap=tt>:of</literal>
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

<link linkend="jobsteps">Job Processing Steps and Printcap Options</link>

for details.)</para>

<para>During operation,
the
<application>lpd</application> server will send the special
<emphasis remap=bf>stop</emphasis>
sequence of
<literal>\031\001</literal>
to the
<literal remap=tt>:of</literal>
filter.
The filter must then suspend itself using a
<command>kill -STOP</command> operation.
The <application>lpd</application> server will detect that the
<literal remap=tt>:of</literal>
filter has suspended itself and then
will perform other printing operations.</para>

<para>After the other printing operations have been completed,
the
<literal remap=tt>:of</literal>
will then be sent a
<command>kill -CONT</command> signal.</para>

<para>This sequence will continue until all information has been printed,
and then the
<literal remap=tt>:of</literal>
filter's <acronym/STDIN/ will be closed.
The filter will then perform whatever cleanup operations are needed,
update accounting or other information,
and exit.</para>

</sect1>

<sect1 id="pr"><title><application>lpr</application> -p format </title>

<para>Options used:
<itemizedlist>

<listitem>
<para><literal>pr=</literal><emphasis>pr program for p format</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>The <literal>-p</literal> format is requires filtering the
the input files by the
<literal>pr</literal>
utility
and then passing the result through the
<literal>:if</literal>
filter.</para>

<para>This is widely regarded as a kludge and may not be supported
on your print spooler.</para>

</sect1>

<sect1><title><application>lpr</application> binary (-l) format</title>

<para>The binary (or literal) format is <literal>-l</literal>.
The
<literal>:if</literal>
filter
is used to process the file,
and is invoked with the
<literal>-c</literal>
(<literal remap=tt>c</literal>ancel processing?) flag.</para>

<para>The filter will not modify the file when sending it to the printer,
but may apply various setups to the printer.</para>

</sect1>

<sect1 id="chainingfilters"><title>Chaining Filters </title>

<para>If a filter command has a pipe (<literal remap=tt>|</literal>) or IO redirection indicator (<literal remap=tt>&lt;</literal>
or
<literal>&gt;</literal>) in it,
or starts with an open parenthesis (<literal/(/),
the filter is run by passing the entire command to the shell
specified by the
<literal>shell</literal>
configuration option.
This allows a wide variety of options and operations to be carried out.
The
<literal>$*</literal>
value should be used to have the filter options passed
to the correct entry in the filter chain.</para>

<para>For example,
<informalexample>
<screen>lp:
  :filter=( echo "starting `date`" &gt;/var/log/status; /usr/local/ifhp $* )
</screen>
</informalexample>
</para>

</sect1>

<sect1 id="filteroptions"><title>Filter Command Line Options and Environment Variables </title>

<para>Options used:
<itemizedlist>

<listitem>
<para> <literal>bk_filter_options=</literal><emphasis>Backwards Compatible Filter options</emphasis></para>
</listitem>

<listitem>
<para> <literal>bk_of_filter_options=</literal><emphasis>Backwards Compatible OF Filter options</emphasis></para>
</listitem>

<listitem>
<para>
<literal>bkf</literal> FLAG <emphasis>Backwards Compatible Filters</emphasis></para>
</listitem>

<listitem>
<para> <literal>filter_ld_path=</literal><emphasis>Filter LD_LIBRARY_PATH environment</emphasis></para>
</listitem>

<listitem>
<para> <literal>filter_options=</literal><emphasis>Filter options</emphasis></para>
</listitem>

<listitem>
<para> <literal>filter_path=</literal><emphasis>Filter PATH environment</emphasis></para>
</listitem>

<listitem>
<para> <literal>of_filter_options=</literal><emphasis>OF Filter options</emphasis></para>
</listitem>

<listitem>
<para> <literal>pass_env=</literal><emphasis>Environment variables to copy to Filter environment</emphasis></para>
</listitem>

<listitem>
<para>
<literal>pl=</literal><emphasis>line count for page</emphasis></para>
</listitem>

<listitem>
<para>
<literal>pw=</literal><emphasis>column count for page</emphasis></para>
</listitem>

<listitem>
<para>
<literal>px=</literal><emphasis>pixel width for page</emphasis></para>
</listitem>

<listitem>
<para>
<literal>py=</literal><emphasis>pixel length for page</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>A filter (or program) specification in the &LPRng; printcap database
has the form:
<informalexample>
<screen>:option=| [flags] /path [argument | "argument" | 'argument' ]*
:option=[flags]   /path [argument | "argument" | 'argument' ]*</screen>
</informalexample>
</para>

<para>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.
Arguments in single or double quotes are passed as a single value,
as for a shell.</para>

<para>The sequence of operations to run a filter is as follows:
<orderedlist>

<listitem>
<para>The program must be specified with an absolute path name.</para>
</listitem>

<listitem>
<para>By default, the program is run as the user if invoked from a client
program such as <application>lpr</application>, <application>lpc</application>, etc.
If invoked from <application>lpd</application>,  it is run as the
user ID specified by the
<literal>:user</literal>
(default <literal>daemon</literal>) configuration entry.</para>
</listitem>


<listitem>
<para>The
<literal/filter_path/ (default <literal>/bin:/usr/bin:/usr/local/bin</literal>,
and
<literal/filter_ld_path/ (default <literal>/lib:/usr/lib:/usr/local/lib</literal>,
configuration options specifies the value of the
<literal/PATH/
and
<literal/LD_LIBRARY_PATH/
environment variables.</para>
</listitem>

<listitem>
<para>The
<literal/filter_path/ (default <literal>/bin:/usr/bin:/usr/local/bin</literal>,
and
<literal/filter_ld_path/ (default <literal>/lib:/usr/lib:/usr/local/lib</literal>,
configuration option specifies the value of the
<literal/PATH/
and
<literal/LD_LIBRARY_PATH/
environment variables.
The other enviroment variables are described in 
<link linkend="filterenv">
&LPRng; ftp mirror sites
Filter Environment Variables
</link>
</listitem>

<listitem>
<para><acronym>ROOT</acronym> Flag.
If the ROOT flag is specified the
filter is executed with Userid and Effective Userid ROOT (User ID 0).
By default it is executed with the
<literal>user</literal>
and
<literal>group</literal>
configuration option user and group ids.
Running a filter as ROOT is extremely dangerous,
and should only be used for programs that require
root permissions to open files or make network connections
from privileged ports.</para>
</listitem>

<listitem>
<para>
<literal/$-/
or
<literal/-$/ Flag.
This flag suppresses appending options to the filter command line.
If the
<emphasis remap=bf>$-</emphasis> or <literal>-$</literal> flag
is not present,
the
<literal>:filter_options</literal>
or <literal>:of_filter_options</literal> for the <option>:of</option> filter
values are appended to the filter command line.
If the
<literal>:bkf</literal>
(Berkeley <application/lpd/ filter compatible flag) is <literal/TRUE/
then the
<literal>:bk_filter_options</literal> and <literal>:bk_of_filter_options</literal>
values are used instead of the <literal>:filter_options</literal> and
<literal>:of_filter_options</literal> values.
</para>
<table id=printfiltercommand><title>Print Filter Command Line Options</title>
<tgroup cols=2 align=left>
<thead>
<row><entry>Option</entry><entry>DefaultValue</entry></row>
</thead>
<tbody>
<row><entry>Option</entry><entry>DefaultValue</entry></row>
<row><entry><literal>filter_options</literal></entry><entry>$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</entry></row>
<row><entry><literal>of_filter_options</literal></entry><entry>(same as <literal>filter_options</literal>)</entry></row>
<row><entry><literal>bk_filter_options</literal></entry><entry>$P $w $l $x $y $F $c $L $i $J $C $0n $0h $-a</entry></row>
<row><entry><literal>bk_of_filter_options</literal></entry><entry>$w $l $x $y</entry></row>
</tbody>
</tgroup>
</table>

</listitem>

<listitem>
<para>By default,
for programs that are not being invoked as print job file filters,
the
<literal>filter_options</literal>
arguments are added.
For print job filters, if the
<literal>:bkf</literal>
flag is set,
then the
<literal>bk_filter_options</literal>
and
<literal>bk_of_filter_options</literal>
entries are used.
The default
<literal>bk</literal>
filter options are the same as originally used
with the BSD <application/lpr/ filters.
For the
<literal remap=tt>:of</literal>
filter,
either the <literal>of_filter_options</literal>
or <literal>bk_of_filter_options</literal> arguments will be added.</para>
</listitem>

<listitem>
<para>The program arguments will then be scanned and interpreted.
Arguments of the form
<literal remap=tt>$</literal><literal>letter</literal>
will be
translated into values from the
print job control file and/or printcap entry.
The letters have the following meaning:
<table id=filteroptionflags><title>Filter Command Line Options and Values</title>
<tgroup cols=2>
<thead>
<row><entry>Option</entry><entry>Purpose or Value</entry></row>
</thead>
<tbody>
<row><entry><literal>a </literal></entry><entry>printcap
<literal>af</literal>
(accounting file name)</entry></row>
<row><entry><literal>b </literal></entry><entry>job size (in K bytes)</entry></row>
<row><entry><literal remap=tt>c </literal></entry><entry>binary file (<literal remap=tt>l</literal>
format for print file)</entry></row>
<row><entry><literal>d </literal></entry><entry>printcap
<literal>cd</literal>
or
<literal>sd</literal>
entry</entry></row>
<row><entry><literal>e </literal></entry><entry>print job data file name (currently being processed)</entry></row>
<row><entry><literal>f </literal></entry><entry>print job original name when spooled for printing (N info from control file)</entry></row>
<row><entry><literal>h </literal></entry><entry>print job originating host (H info from control file)</entry></row>
<row><entry><literal>i </literal></entry><entry>indent request (I info from control file)</entry></row>
<row><entry><literal>j </literal></entry><entry>job number in spool queue</entry></row>
<row><entry><literal>k </literal></entry><entry>print job control file name</entry></row>
<row><entry><literal>l </literal></entry><entry>printcap
<literal>pl</literal>
(page length)</entry></row>
<row><entry><literal>m </literal></entry><entry>printcap
<literal>co</literal></entry></row>
<row><entry><literal>n </literal></entry><entry>user name (L info from control file)</entry></row>
<row><entry><literal>p </literal></entry><entry>remote printer (when processing for bounce queue)</entry></row>
<row><entry><literal>r </literal></entry><entry>remote host (when processing for bounce queue)</entry></row>
<row><entry><literal>s </literal></entry><entry>printcap
<literal>sf</literal>
(status file)</entry></row>
<row><entry><literal>t </literal></entry><entry>time in common UNIX format</entry></row>
<row><entry><literal>w </literal></entry><entry>printcap
<literal>pw</literal>
(page width)</entry></row>
<row><entry><literal>x </literal></entry><entry>printcap
<literal>px</literal>
(page x dimension)</entry></row>
<row><entry><literal>y </literal></entry><entry>printcap
<literal>py</literal>
(page y dimension)</entry></row>
<row><entry><literal>F </literal></entry><entry>print file format</entry></row>
<row><entry><literal>P </literal></entry><entry>printer name</entry></row>
<row><entry><literal>S </literal></entry><entry>printcap
<literal>cm</literal>
(comment field)</entry></row>
<row><entry>Capital letter</entry><entry>Corresponding line from control file</entry></row>
<row><entry>{key}</entry><entry>printcap value for
<literal>key</literal></entry></row>
</tbody>
</tgroup>
</table>
</para>
</listitem>

<listitem>
<para>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 frame=all><title>Filter Command Line Option Format</title>
<tgroup cols=2 rowsep=1 colsep=1 align=left>
<thead>
<row><entry>Form</entry><entry>TranslatedValue</entry></row>
</thead>
<tbody>
<row><entry><literal> $x </literal></entry> <entry><literal/'-x/<replaceable/value/<literal/'/</entry></row>
<row><entry><literal> $-x </literal></entry><entry><literal/   '/<replaceable/value/<literal/'/</entry></row>
<row><entry><literal> $0x </literal></entry><entry><literal/-x '/<replaceable/value/<literal/'/</entry></row>
<row><entry><literal> $'x </literal></entry><entry><literal/-x  /<replaceable/value/<literal//</entry></row>
</tbody>
</tgroup>
</table>
</para>

<para>Each entry in quotes is treated as a single value,
as in /bin/sh.
The
<literal>$'x</literal>
does not quote the value.
Combinations of the various flags are allowed.  For example,
<literal>$-x</literal>
would simply substitute the value for
<literal remap=tt>x</literal>,
and then pass the whitespace separated components as individual arguments.
This last form is useful for adding in additional flags on the command line.</para>
</listitem>

<listitem>
<para>The command line is parsed,
metacharacters are ruthlessly stripped from all arguments and pathnames
and replaced by <literal>_</literal> (underscores),
and an argument list suitable for the
<literal>execve</literal>
system call
is formed.</para>
</listitem>

<listitem>
<para>A sanitized environment is set up for the program execution,
with the following environment variables.

<table frame=all id=filterenv><title>Filter Environment Variables</title>
<tgroup cols=2 align=left colsep=1 rowsep=1>
<thead>
<row><entry>Variable Name</entry><entry>Meaning</entry></row>
</thead>
<tbody>
<row><entry><literal> CONTROL </literal></entry><entry>control file image</entry></row>
<row><entry><literal> HF </literal></entry><entry>hold file image</entry></row>
<row><entry><literal> DATAFILES </literal></entry><entry>list of data file names</entry></row>
<row><entry><literal> HOME </literal></entry><entry>Home directory (client only)</entry></row>
<row><entry><literal> IFS </literal></entry> <entry><literal>" \t"</literal></entry></row>
<row><entry><literal> LD_LIBRARY_PATH </literal></entry> <entry><literal>:filter_ld_path </literal> configuration information</entry></row>
<row><entry><literal> LOGDIR </literal></entry><entry>Home directory (client only)</entry></row>
<row><entry><literal> LOGNAME </literal></entry><entry><literal/L/ control file line</entry></row>
<row><entry><literal> PATH </literal></entry><entry><literal>filter_path</literal> configuration information</entry></row>
<row><entry><literal> PRINTCAP_ENTRY </literal></entry><entry>printcap information</entry></row>
<row><entry><literal> SHELL </literal></entry><entry><option/:sh/ configuration information (default <filename>/bin/sh</filename>)</entry></row>
<row><entry><literal> SPOOL_DIR </literal></entry> <entry><literal>:sd</literal> printcap information</entry></row>
<row><entry><literal> TZ </literal></entry><entry>Time zone</entry></row>
<row><entry><literal> USER </literal></entry><entry>User name (client only)</entry></row>
</tbody>
</tgroup>
</table>
</para>
</listitem>

<listitem>
<para>If the filter is to be run by a client program such as <application>lpr</application>,
then the environment variables specified by the
<literal>pass_env</literal> configuration or printcap option will be
extracted from the environment,
have any metacharacters removed,
and then placed in the environment variable list.
Commonly, the
<acronym>PGPPASS</acronym>,
<acronym>PGPPASSFD</acronym>,
and <acronym>PGPPATH</acronym> are specified.</para>
</listitem>

<listitem>
<para>The program is started,
with <acronym/STDIN/, <acronym/STDOUT/, and <acronym/STDERR/ attached to the appropriate files or
file descriptors.
If none is specified, then they are attached to
<filename>/dev/null</filename>.</para>
</listitem>

</orderedlist>
</para>

</sect1>

<sect1><title>&LPRng; Supported Filters</title>

<para>There already exists a large library of ready-to-use filters. Some of
them have &LPRng;-specific versions, which can be found at the

<link linkend="secftp">&LPRng; ftp mirror sites</link>.</para>


<sect2><title>Filter Support Conventions</title>

<para>By convention,
most filters are either totally standalone (very rare)
or require a set of support files or configuration files.
There are two types of configuration files:
per print queue configuration information
and global configuration information.</para>

<para>Since a print filter executes
with the spool queue directory as the current directory,
most filters put per print queue configuration information
in a file in the spool directory.
Some <emphasis>vintage</emphasis>
filters insist on having these files <emphasis>hidden</emphasis>
with names such as <emphasis remap=bf><filename>.setup</filename></emphasis>.
This can make it difficult for administrators to determine where the
configuration files are.</para>

<para>Global configuration files are usually
placed in commonly accessible directories such as
<emphasis remap=tt><filename>/usr/local/libexec/filters</filename></emphasis>
and its subdirectories.
This allows the &LPRng; administrator to set the privileges on these
directories such that only the <application>lpd</application> process can
access them.</para>

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

<para>
An alternative to using information in a file is to place options in the
printcap entry and have the filter extract them from the
<envar/PRINTCAP_ENTRY/ environment variable value.
This is much easier to implement,
but is specific to &LPRng;.
</para>

</sect2>
</sect1>

<sect1 id="lpf"><title>lpf </title>

<para>Source code:

<link linkend="secftp">&LPRng; Distribution</link>
</para>

<para>This filter is distributed as part of the &LPRng; source code,
and has a very limited functionality.
By default,
it only translates
<literal>\n</literal>
to
<literal>\r\n</literal>
sequences,
and detects the OF Filter Stop sequence when invoked as an OF filter.
<itemizedlist>

<listitem>
<para>Options:<!-- <br> -->
<literal>-Tcrlf</literal> - suppress
<literal>\n</literal>
to
<literal>\r\n</literal>
translation</para>
</listitem>

</itemizedlist>
</para>

</sect1>

<sect1 id="ifhp"><title><application>ifhp</application> Filter </title>

<para>Source code:

<link linkend="secftp">&LPRng; Distribution, ifhp-&lt;em&gt;version&lt;/em&gt;.tgz</link>
</para>

<para>The <application>ifhp</application> filter supports a wide variety of <emphasis>smart</emphasis>
printers,
or to be more specific,
printers which support PostScript, PCL or PJL languages.
For details on using the
<application remap=tt>ifhp</application> filter
see the
<application remap=tt>ifhp</application> filter
documentation for details.
The following is a quick set of examples of printcap entries:
<informalexample>
<screen># network connection to jet direct box,
#   no banners, HP compatible
lp
  :lp=ipaddr%9100
  :filter=/usr/local/libexec/filters/ifhp
#
# banner added,  model information added
#
lp
  :lp=ipaddr%9100
  :ifhp=model=hp4
  :bp=/usr/local/libexec/filters/pclbanner
  :of=/usr/local/libexec/filters/ifhp
  :filter=/usr/local/libexec/filters/ifhp
#
# for a parallel port printer or when you want VERY fast
#  throughput, no pagecounts, error messages, etc.  The
#
lp
  :lp=/dev/lp0
  :ifhp=model=hp4,status@
  :filter=/usr/local/libexec/filters/ifhp</screen>
</informalexample>
</para>
</sect1>
</chapter>

<chapter id=permsref><title>Permissions and Authentication </title>

<para>The contents of the <filename>/etc/lpd.perms</filename> file
are used to control access to the <application>lpd</application> server facilities.
The model used for permission granting is similar to packet filters.
An incoming request is tested against a list of rules,
and the first match found determines the action to be taken.
The action is either <acronym>ACCEPT</acronym> or the request is granted,
or <acronym>REJECT</acronym> and the request is denied.
You can also establish a default action.</para>

<para>The following is a sample <filename>lpd.perms</filename> file.</para>

<para>
<informalexample>
<screen># 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</screen>
</informalexample>
</para>

<para>Each line of the permissions file is a rule.
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.</para>

<para>The sense of a pattern match can be inverted using the NOT keyword.
For example,
the rules with
<literal>ACCEPT NOT REMOTEUSER=john,bill</literal>
succeeds only if the REMOTEUSER value is defined and
is not
<literal>john</literal>
or
<literal>bill</literal>.</para>

<para>Each entry in a rule is a keyword which has is assigned a value or
list of values followed by an optional set of patterns that are matched
against these values.
The following table is a summary of the available keywords.</para>

<table frame=all id=permskeywords><title>Permission Keywords and Purpose</title>
<tgroup cols=2 align=left rowsep=1 colsep=1>
<thead>
<row><entry>Keyword</entry><entry>Match</entry></row>
</thead>
<tbody>
<row><entry><acronym>DEFAULT</acronym></entry><entry>default result</entry></row>
<row><entry><acronym>SERVICE</acronym></entry><entry>Checking lpC, lpR, lprM, lpQ, and Printing</entry></row>
<row><entry><acronym>USER</acronym></entry><entry>P (logname) field name in print job control file.</entry></row>
<row><entry><acronym>REMOTEUSER</acronym></entry><entry>user name in request from remote host.</entry></row>
<row><entry><acronym>HOST</acronym></entry><entry>DNS and IP address information for the H (host) field name in print job control file</entry></row>
<row><entry><acronym>REMOTEHOST</acronym></entry><entry>DNS and IP address information for the connection from the remote host making the request</entry></row>
<row><entry><acronym>IP</acronym></entry><entry>Alias for HOST</entry></row>
<row><entry><acronym>REMOTEIP</acronym></entry><entry>Alias for REMOTEHOST</entry></row>
<row><entry><acronym>REMOTEPORT</acronym></entry><entry>Originating TCP/IP port for the connection from the remote host making the request</entry></row>
<row><entry><acronym>PORT</acronym></entry><entry>Alias for PORT</entry></row>
<row><entry><acronym>UNIXSOCKET</acronym></entry><entry>Connection is on a UNIX socket, i.e. from localhost</entry></row>
<row><entry><acronym>SAMEUSER</acronym></entry><entry>USER and REMOTEUSER matches</entry></row>
<row><entry><acronym>SERVER</acronym></entry><entry>request originates on lpd server</entry></row>
<row><entry><acronym>FORWARD</acronym></entry><entry>destination of job is not host</entry></row>
<row><entry><acronym>REMOTEGROUP</acronym></entry><entry>REMOTEUSER is in the specified group or netgroup in the <application>lpd</application> server group database.</entry></row>
<row><entry><acronym>GROUP</acronym></entry><entry>USER is in the specified group or netgroup in the <application>lpd</application> server group database.</entry></row>
<row><entry><acronym>LPC</acronym></entry><entry>LPC command in the LPC request.</entry></row>
<row><entry><acronym>CONTROLLINE</acronym></entry><entry>match a line in control file</entry></row>
<row><entry><acronym>AUTH</acronym></entry><entry>authentication type</entry></row>
<row><entry><acronym>AUTHUSER</acronym></entry><entry>authenticated user</entry></row>
<row><entry><acronym>AUTHSAMEUSER</acronym></entry><entry>same authenticated user</entry></row>
<row><entry><acronym>AUTHFROM</acronym></entry><entry>authenticated forwarder</entry></row>
<row><entry><acronym>AUTHJOB</acronym></entry><entry>authenticated job in queue</entry></row>
<row><entry><acronym>AUTHCA</acronym></entry><entry>SSL signing certificates for job</entry></row>
</tbody>
</tgroup>
</table>

<sect1 id="defaultpermission"><title>Permission Checking Algorithm </title>

<para>Options used:
<itemizedlist>

<listitem>
<para> <literal>default_permission=</literal><emphasis>Default Permission (accept)</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>The <application>lpd</application> server uses the following algorithm to do
permission checks.
<orderedlist>

<listitem>
<para>The configuration information initially establishes
a default permission using the <literal>default_permission</literal>
configuration value.
This is used if an explicit permission is not determined
by the other steps in this algorithm.</para>
</listitem>

<listitem>
<para>Each line of the permissions file is a lists of tests (patterns)
and a permission value that is used if all of the tests (patterns)
on the line are successful.
A DEFAULT line sets the default result if all lines fail.</para>
</listitem>

<listitem>
<para>Each line is executed in sequence until a match is found.
The first matching line terminates the permission checking
and the corresponding permission value is used.</para>
</listitem>

<listitem>
<para>Each keyword has a value (or set of values) that are matched against
a set of patterns.
If the keyword does not have a value (or the <emphasis>null</emphasis>
value)
then the match will fail.
Initially,
all the keywords have a
<literal>null</literal>
value.</para>
</listitem>

<listitem>
<para>When a connection is received by the <application>lpd</application> server,
REMOTEHOST
and
REMOTEPORT
are set to the the IP addresses and hostnames, and the TCP/IP port
of the host originating the IP address respectively.
REMOTEIP and IFHP are aliases for REMOTEPORT
and PORT is an alias for REMOTEPORT
and are provided for backwards compatibility with older versions of &LPRng;.
If the connection was on a UNIX socket, then the
UNIXSOCKET flag is set.
For example,
a request originating from
<literal>10.0.0.2</literal>, port 1011 would set
REMOTEIP to 10.0.0.2 and PORT to 1011.</para>
</listitem>

<listitem>
<para>The REMOTEHOST value is set to the result of doing a reverse DNS lookup
on the REMOTEIP address.
This value is the list of names <emphasis>and</emphasis>
ip addresses in standard
IP notation (nnn.nnn.nnn.nnn) that are returned by the
lookup.
If the DNS lookup fails
then the REMOTEHOST value is set to the REMOTEIP value.
For example,
lookup of 10.0.0.2 would result in the names
<filename>h2.private</filename> and <filename>patrick.private</filename>,
and the only IP address assigned to it was
<literal>10.0.0.2</literal>.
The REMOTEHOST value would then be the list
<literal>h2.private,patrick.private,10.0.0.2</literal>.</para>
</listitem>

<listitem>
<para>The SERVICE value is set to
<literal remap=tt>X</literal>
and then the permissions database is scanned for a matching entry.
The result is the permission value of the first matching line or the default
permission.
If the result is REJECT then the connection is closed.</para>
</listitem>

<listitem>
<para>Next, a single line is read from the connection.
This line contains the request type,
the print queue name,
and depending on the request type an optional user name and options.
The SERVICE value is set to
<literal>R,</literal>
<literal>Q,</literal>
<literal>M,</literal>
and
<literal>C,</literal>
for a
<literal>lpR</literal>,
<literal>lpQ</literal>,
<literal>lprM</literal>,
and
<application>lpc</application>
request respectively and PRINTER to the print queue name.</para>
</listitem>

<listitem>
<para>If the request is for an <application>lpc</application> operation,
the LPC value is set to the name of the operation.
For example,
and

<command remap=tt>lpc lpd</command>
operation</para>
</listitem>

<listitem>
<para>If the request contains a user name then REMOTEUSER is assigned the user name.</para>
</listitem>

<listitem>
<para>If the request originates from the <application>lpd</application> server as determined by
the connection arriving from the
<literal>localhost</literal>
address or an
address assigned to one of the network interfaces for this host
then the SERVER value is set to true (or matches).</para>
</listitem>

<listitem>
<para>If the request is for an authenticated transfer,
(see

<link linkend="authref">Authentication and Encryption</link>
),
then the authentication procedures are carried out.
After they have been performed,
the AUTH value is set to true,
AUTHTYPE is set to the name of the authentication method,
AUTHUSER to the authenticated identifier of the originator of the request,
and AUTHFROM to the authenticated identifier of the originator of the connection.</para>
</listitem>

<listitem>
<para>Other matching keywords such as REMOTEGROUP use values set at this time.
These are discussed in the next section.</para>
</listitem>

<listitem>
<para>The permission database is rescanned,
this time to see if there is permission to operate on the
specified spool queue.
The permission database is first checked to see
if the requesting user has control (SERVICE=C) permission.
If they do,
then they can perform any operation on the spool queue.
The scan is then repeated for the actual request.</para>
</listitem>

<listitem>
<para>If there is no permission to perform the operation
then an error code and messages is returned on the
requesting connection.</para>
</listitem>

<listitem>
<para>If the operation is for a spool queue or server,
no other permissions checking is done.
This includes the <application>lpq</application> command,
and most of the <application>lpc</application> commands control queue operations.</para>
</listitem>

<listitem>
<para>If the operation is for for individual jobs in a spool queue,
then the queue is scanned and job information is extracted
for each job in the queue.
The USER value is set to the job control file
<literal remap=tt>P</literal>
line.
The value of the
<literal remap=tt>H</literal>
line in the control file is used to perform a
DNS lookup,
and the HOST value is set to the results of this lookup.
IP is an alias for HOST,
and is retained for backwards compatibility.</para>
</listitem>

<listitem>
<para>The SAMEUSER value is set to true (or match) if the REMOTEUSER
value is identical to the USER value.
Similarly,
SAMEHOST is set to true if the REMOTEHOST value matches the HOST value.
See the following sections for other keywords such as GROUP.</para>
</listitem>

<listitem>
<para>The permission checking is done for each individual job in a
spool queue,
and if it succeeds the action is carried out on the job.</para>
</listitem>

</orderedlist>
</para>

<para>These checks are applied on the arrival of a job from an external
connection.
Unfortunately,
there are a set of print spooler implementations that do not
handle job rejection due to lack of permissions.
These printers will continually and repeatedly attempt to send a job
for which there is no printing permission until the job is removed
by administrative action.
To accommodate these printers,
we must accept jobs for printing and then dispose of them.
This is done by using the SERVICE=P (printing) checks.
These checks are performed
<emphasis/after/
the job has been accepted.
</para>

<para>
<orderedlist>

<listitem>
<para>When a print spool is active and is printing or forwarding jobs,
before it processes a job it will read the job control file
and set the <acronym>USER</acronym>  and <acronym>HOST</acronym>
values as discussed in the previous sections.
It will also set the <acronym>AUTH</acronym>,
<acronym>AUTHUSER</acronym>,
and <acronym>AUTHJOB</acronym>
values as well,
if the job was spooled by using an authenticated method.</para>
</listitem>

<listitem>
<para>The permissions database will be scanned
and the resulting permission determined.
Note that the values of the REMOTE keys are undefined,
and tests using them will have unpredictable effects.</para>
</listitem>

<listitem>
<para>If the job does not have permission to be printed,
it will normally be removed from the spool queue.</para>
</listitem>

</orderedlist>
</para>

<para>While this model is very simple
it can handle a wide range of situations.
However,
it is really based on the simple <emphasis remap=bf>trust</emphasis>
that
users will not <emphasis>impersonate</emphasis>
other users or hosts.
If this is not the case,
then more elaborate procedures based on encryption and
authentication are called for.</para>

<para>There is a problem with permissions checking for <application>lpq</application> (SERVICE=Q)
requests.
Since the user name is not passed as part of the request,
it is impossible to use the REMOTEUSER clause to restrict <application>lpq</application>
operations.</para>

<para>The <literal>SERVICE=R</literal> and <literal>SERVICE=P</literal>
facilities are provided to handle problems with print spoolers that
do not recognize a <emphasis>lack of permission</emphasis>
error code,
and will indefinitely retry sending a job to the <application>lpd</application> server.
If this is the case,
then the <literal>SERVICE=R</literal> clause can be used to accept jobs,
and then the <literal>SERVICE=P</literal> clause will cause the <application>lpd</application>
server to remove of the job when it is scheduled for printing.</para>

</sect1>

<sect1><title>Rule Matching Procedures</title>

<para>
<informalexample>
<screen>[not] key                                 assigned value
[not] key=pattern                         substring match
[not] key=pattern1,pattern2,pattern3,...  glob and exact
[not] key=IP1/mask1,IP2/mask2,...         IP address</screen>
</informalexample>
</para>

<para>Each of the indicated values is matched against a list of patterns.
The following types of matches are used:
<orderedlist>

<listitem>
<para>assigned value.
The keyword has an assigned value which is true (match) or false (no match).
Examples are SAMEHOST and SERVER.</para>
</listitem>

<listitem>
<para>substring match.
The indicated entry is present as a substring in the pattern.</para>
</listitem>

<listitem>
<para>GLOB matches.
The pattern is interpreted as a GLOB style pattern,
where * matches 0 or more characters,
and ? matches a single character,
and
<literal>[L-H]</literal>
specifies a range of characters from
<literal remap=tt>L</literal>
to
<literal remap=tt>H</literal>,
in ASCII order.</para>
</listitem>

<listitem>
<para>IP address match.  The address must be specified in the
standard <filename>nn.nn.nn.nn</filename> format.
The mask must be either an integer number
corresponding to the number of significant bits,
or in the standard <filename>nn.nn.nn.nn</filename> format.
Addresses are compared by doing
<informalexample>
<screen>( IPaddr XOR IP ) AND mask</screen>
</informalexample>

</para>

<para>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.</para>
</listitem>

<listitem>
<para>integer range match.
The pattern has the form
<literal>low-high</literal>,
where low and high are integer numbers.
The match succeeds if the value is in the specified range.</para>
</listitem>

<listitem>
<para>Same IP Address Match.
This compares two lists of IP addresses;
a match is found when there is one or more common addresses.</para>
</listitem>

</orderedlist>
</para>


<sect2><title>DEFAULT</title>

<para>
<informalexample>
<screen>DEFAULT ACCEPT
DEFAULT REJECT</screen>
</informalexample>
</para>

<para>The DEFAULT rule specifies the default if no rule matches.
Normally,
there is one DEFAULT entry in a permissions file.
<informalexample>
<screen>Example:

DEFAULT ACCEPT</screen>
</informalexample>
</para>

</sect2>

<sect2><title>SERVICE</title>

<para>Match type: substring</para>

<para>The SERVICE key is based on the type of request.
<informaltable frame=all>
<tgroup cols=2 align=left rowsep=1 colsep=1>
<thead>
<row><entry>Key</entry><entry>Request</entry></row>
</thead>
<tbody>
<row><entry>Key</entry><entry>Request</entry></row>
<row><entry><literal remap=tt>C</literal></entry><entry>LPC Control Request</entry></row>
<row><entry><literal remap=tt>M</literal></entry><entry><application>lprm</application> Removal Request</entry></row>
<row><entry><literal remap=tt>P</literal></entry><entry>Printing</entry></row>
<row><entry><literal remap=tt>Q</literal></entry><entry><application>lpq</application> Status Request</entry></row>
<row><entry><literal remap=tt>R</literal></entry><entry><application>lpr</application> Job Transfer</entry></row>
<row><entry><literal remap=tt>X</literal></entry><entry>Connection Request</entry></row>
</tbody>
</tgroup>
</informaltable>
</para>

<para>Each of the above codes corresponds either directly to the user command,
or a set of subcommands.</para>

<para>If you have an LPC request,
you can add an <literal>LPC=xxx</literal> clause to refine the
permissions checking to allow or disallow
<application>lpc</application> commands such as
<command remap=tt>lpc status, printcap, active, </command>.
<informalexample>
<screen>Example:

# control only from root on server
ACCEPT SERVICE=C SERVER USER=root
REJECT SERVICE=C
# accept all others
ACCEPT SERVICE=*</screen>
</informalexample>
</para>

</sect2>

<sect2><title>USER</title>

<para>Match type: GLOB</para>

<para>The USER information is taken from the
<literal remap=tt>P</literal>
(person or logname)
information in the print job control file.
<informalexample>
<screen>Example:

# we allow jobs to be spooled
ACCEPT SERVICE=R
# now we do the checking at print time
ACCEPT SERVICE=P USER=root
REJECT SERVICE=P</screen>
</informalexample>
</para>

</sect2>

<sect2><title>REMOTEUSER</title>

<para>Match type: GLOB</para>

<para>The REMOTEUSER information is taken from the user information sent
with a service request.</para>

<para>Note that one of the flaws of

<link linkend="rfc1179">RFC1179</link>

is that an <application/lpq/ (print status)
request does not provide a REMOTEUSER name.
<informalexample>
<screen>Example:

ACCEPT SERVICE=C REMOTEUSER=root,papowell,admin SERVER
ACCEPT SERVICE=C LPC=status,lpd REMOTEUSER=admin
REJECT SERVICE=C</screen>
</informalexample>
</para>

</sect2>

<sect2><title>HOST</title>

<para>Match type: GLOB</para>

<para>The
<literal remap=tt>H</literal>
(host) information in the print job control file
is used to do a DNS lookup,
and the resulting list of names and addresses is used for matching purposes.
<informalexample>
<screen>Example:

# we allow jobs to be spooled
ACCEPT SERVICE=R
# now we do the checking at print time
# allow from our private subnet
ACCEPT SERVICE=P HOST=10.0.0.0/8,*.othernet.com
REJECT SERVICE=P</screen>
</informalexample>
</para>

</sect2>

<sect2><title>REMOTEHOST</title>

<para>Match type: GLOB</para>

<para>The REMOTEHOST information is obtained by doing a reverse IP name lookup
on the remote host IP address
and the resulting list of names and addresses is used for matching purposes.
If there is no FQDN available,
then the IP address in text form will be used.
<informalexample>
<screen>Example:

# allow from our private subnet
ACCEPT SERVICE=R REMOTEHOST=10.0.0.0/8,*.othernet.com
REJECT SERVICE=R</screen>
</informalexample>
</para>

</sect2>

<sect2><title>REMOTEPORT</title>

<para>Match type: integer range</para>

<para>The REMOTEPORT value is the originating port of the TCP/IP connection.
The match succeeds if it is in the specified range.
<informalexample>
<screen>Example:

# require connections to originate from privileged port
ACCEPT SERVICE=X REMOTEPORT=1-1023
REJECT SERVICE=X</screen>
</informalexample>
</para>

</sect2>

<sect2><title>PORT</title>

<para>Alias for REMOTEPORT.</para>

</sect2>

<sect2><title>IP</title>

<para>Alias for HOST.</para>

</sect2>

<sect2><title>REMOTEIP</title>

<para>Alias for REMOTEHOST.</para>

</sect2>

<sect2><title>LPC</title>

<para>Match type: GLOB</para>

<para>The requested <application>lpc</application> command.
This allows the following permissions line to be used:
<informalexample>
<screen>Example:

#allow remoteuser admin on server to use LPC topq and hold
ACCEPT SERVICE=C SERVER REMOTEUSER=root
ACCEPT LPC=topq,hold SERVER REMOTEUSER=papowell
REJECT SERVICE=C</screen>
</informalexample>
</para>

</sect2>

<sect2><title>SAMEUSER</title>

<para>Match type: exact string match</para>

<para>Both the REMOTEUSER and USER information must be present and identical.
<informalexample>
<screen>Example:

# LPC users can do anything
ACCEPT SERVICE=C SERVER REMOTEUSER=root
REJECT SERVICE=C
# allow users who sent jobs from the same host to remove them
ACCEPT SERVICE=M SAMEUSER SAMEHOST
REJECT SERVICE=M</screen>
</informalexample>
</para>

</sect2>

<sect2><title>SAMEHOST</title>

<para>Match type: Same IP Address</para>

<para>The REMOTEHOST and HOST address lists are checked;
if there is a common value the match succeeds.
<informalexample>
<screen>Example:

# allow root on the same host as user
# to remove files
ACCEPT SERVICE=M SAMEHOST REMOTEUSER=root
REJECT SERVICE=M</screen>
</informalexample>
</para>

</sect2>

<sect2><title>SERVER</title>

<para>Match type: Matching IP Address</para>

<para>One of the REMOTEHOST addresses
must be the same as one of the addresses of the <application>lpd</application> server host,
or must be one of the addresses found by looking up the
<literal>localhost</literal>
name using <function>gethostbyname()</function>.
<informalexample>
<screen>Example:

# allow root on the server full LPC permissions
ACCEPT SERVICE=C SERVER REMOTEUSER=root
REJECT SERVICE=C</screen>
</informalexample>
</para>

</sect2>

<sect2><title>FORWARD</title>

<para>Match type: Address Match</para>

<para>The list of REMOTEHOST and HOST addresses must not have a common entry.
This is usually the case when a remote <application>lpd</application> server is forwarding
jobs to the <application>lpd</application> server.
<informalexample>
<screen>Example:

# do not accept forwarded jobs or requests
REJECT SERVICE=* FORWARD</screen>
</informalexample>
</para>

</sect2>

<sect2><title>GROUP</title>

<para>Match type: modified GLOB</para>

<para>The USER must be present in
one of the groups in <filename>/etc/group</filename> or whatever permissions mechanism is used
to determine group ownership
which matches the GLOB pattern.
If the pattern has the form <literal>@name</literal>,
then a check to see if the user is in the named netgroup is done.
<informalexample>
<screen>Example:

ACCEPT SERVICE=P GROUP=admin,@netgroup
REJECT SERVICE=P</screen>
</informalexample>
</para>

</sect2>

<sect2><title>REMOTEGROUP</title>

<para>The same rules as for GROUP,
but using the REMOTEUSER value.
<informalexample>
<screen>Example:

ACCEPT SERVICE=R REMOTEGROUP=admin,@netgroup
REJECT SERVICE=R</screen>
</informalexample>
</para>

</sect2>

<sect2><title>CONTROLLINE</title>

<para>Match type: GLOB</para>

<para>A <acronym>CONTROLLINE</acronym> pattern has the form
<informalexample>
<screen>X=pattern1,pattern2,...</screen>
</informalexample>
</para>

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

<para>This pattern can be used to select only files with specific control
file information for printing.</para>

</sect2>

<sect2><title>AUTH</title>

<para>Match type: value</para>

<para>If the current transfer or the transfer used to send a job
was authenticated,
then AUTH is true or matches.
<informalexample>
<screen>Example:

# reject all non-authenticated transfers
REJECT NOT AUTH</screen>
</informalexample>
</para>

</sect2>

<sect2><title>AUTHTYPE</title>

<para>Match type: glob</para>

<para>If the current transfer or the transfer used to send a job
was authenticated,
then AUTHTYPE is set to the name of the authentication method.
<informalexample>
<screen>Example:

# require kerberos, pgp, or md5 authentication
REJECT NOT AUTHTYPE=kerberos*,pgp,md5</screen>
</informalexample>
</para>

</sect2>

<sect2><title>AUTHUSER</title>

<para>Match type: GLOB</para>

<para>The AUTHUSER rule will check to see if the authenticated user identification
matches the pattern.
<informalexample>
<screen>Example:

ACCEPT SERVICE=C AUTHTYPE=kerberos* AUTHUSER=admin@ASTART.COM</screen>
</informalexample>
</para>

</sect2>

<sect2><title>IFIP</title>

<para>Match type: IPmatch, but for IPV6 as well as IPV4</para>

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

<para>The IFIP (interface IP) field can be used to check the IP address
of the interface
that accepted the network connection,
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 canonical host name.  Some systems which use the Domain Name Server
(DNS) system may add the canonical system name as an alias.</para>

<para>This entry is deprecated and may not be supported in future releases.</para>

</sect2>
</sect1>

<sect1 id="permspath"><title>Permission File Location </title>

<para>Options used:
<itemizedlist>

<listitem>
<para> <literal>perms_path=</literal><emphasis> path </emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>The <literal>perms_path=</literal> configuration variable specifies the
location of the default permissions file.
The default value is:
<informalexample>
<screen>perms_path=${sysconfdir}/lpd.perms</screen>
</informalexample>
</para>

<para>The <filename>lpd.perms</filename> file can be obtained by running a program,
in a similar manner to the <filename>printcap</filename> file.
See
<link linkend="filters">Filters</link>
for details on how
the program would be invoked.
For example, assume the configuration information specified:
<informalexample>
<screen>perms_path=|/usr/local/libexec/get_perms</screen>
</informalexample>
</para>

<para>The <application>lpd</application> server will write either a blank line
for connection (<literal>SERVICE=X</literal>) and global <application>lpc</application> permissions
(<literal>SERVICE=C</literal> and <literal>LPC=reread,lpd,default</literal>)
or the name of the spool queue to the <literal>get_perms</literal>
<acronym/STDIN/,
and expects to read permission information from its <acronym/STDOUT/.
If the filter method is used,
it should always return the complete set of connection (<literal remap=tt>X</literal>)
and control (<literal remap=tt>C</literal>
service values.</para>

</sect1>

<sect1><title>Example Permission File</title>

<para>
<informalexample>
<screen># allow root on server to control jobs
ACCEPT SERVICE=C SERVER REMOTEUSER=root
ACCEPT SERVICE=C LPC=lpd
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</screen>
</informalexample>
</para>

<para>In the above sample, we first specify that
lp<literal remap=tt>C</literal>
commands from user
<literal>root</literal>
on the lpd server will be accepted.
This is traditionally the way that most lpc commands operate.
We also allow anybody to use the
<command remap=tt>lpc lpd</command>
command.
We reject any other <application>lpc</application> requests.</para>

<para>We accept
lpr<literal remap=tt>M</literal>
requests from the host and user that submitted the job,
as well as from root on the server,
and reject any others.</para>

<para>Finally,
all other types of commands (lpq, lpr) are allowed by default.</para>

</sect1>

<sect1><title>Complex Permission Checking</title>

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

<para>For example
<literal>IP=10.3.4.0/24</literal> would match all hosts with the IP
addresses
<literal>IP=10.3.4.0</literal> to
<literal>IP=10.3.4.255</literal>.</para>

<para>Similarly, the HOST pattern can specify a set of hostnames
or patterns to match against based on the GLOB notation.</para>

<para>For example
<literal>REMOTEHOST=*.private</literal>
would match all hosts with a DNS entry which ended with
<filename>private</filename>.</para>

<para>The NOT keyword reverses the match sense.  For example
<literal>REJECT NOT REMOTEHOST=*.private,*.murphy.com</literal>
would reject all requests from hosts which did not have a DNS entry
ending in
<filename>private</filename>
or
<filename>murphy.com</filename>.</para>

</sect1>

<sect1><title>More Examples</title>

<para>The following is a more complex lpd.perms file.
<informalexample>
<screen># 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.private or the server to use control
#facilities.
ACCEPT SERVICE=C SERVER REMOTEUSER=root
ACCEPT SERVICE=C REMOTEHOST=astart1.private REMOTEUSER=papowell
#Allow root on talker.private to control printer hpjet
ACCEPT SERVICE=C HOST=talker.private 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</screen>
</informalexample>
</para>

</sect1>

<sect1 id="authref"><title>Authentication and Encryption </title>

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

<para>&LPRng; provides native support for the MIT Kerberos 4 extensions and
Kerberos 5 authentication.</para>

<para>&LPRng; uses the OpenSSL libraries to support SSL 
authentication and encrypted data transfers.</para>

<para>&LPRng; has native support for the PGP (Pretty Good Privacy) program
and can sign and optionally encrypt command and responses between servers
and clients.
Due to legal restrictions,
an external PGP program must be used for this purpose.</para>

<para>A simple MD5 hash based authentication scheme is also provided as an
example to illustrate how new or different authentication methods
can be adddd.</para>

<para>Finally,
&LPRng; provide a general purpose interface allowing users to insert their
own authentication methods,
either at the program level or at the code level.</para>

<para>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:
<orderedlist>

<listitem>
<para>The received command must be checked for consistency,  and the
remote user and host must be determined.</para>
</listitem>

<listitem>
<para>The remote user and host must be authenticated.</para>
</listitem>

<listitem>
<para>The command and/or spooling operation must be carried out.</para>
</listitem>

<listitem>
<para>The results must be returned to the remote system.</para>
</listitem>

</orderedlist>
</para>

<para>

To accomplish these goals,
the following printcap entries are used:

<itemizedlist>
<listitem id="authtype"> <para> <literal>
auth=
</literal><emphasis>
AUTHTYPE
</emphasis>
- pgp, kerberos, etc
</para>
</listitem>

<listitem id="authtypepath"> <para> <literal>
AUTHTYPE_path=
</literal><emphasis>
pathname
</emphasis>
- the pathname of a program to be used to support
this authentication type
</para>
</listitem>

<listitem id="authtypeid"> <para> <literal>
AUTHTYPE_id=
</literal><emphasis>
identification
</emphasis>
- the identification of the server for the
authentication method.  For example, the
kerberos principal for the server, the PGP
key id for the server, and so forth.
</para>
</listitem>

<listitem id="authtypeserverkey"> <para> <literal>
AUTHTYPE_server_key=
</literal><emphasis>
identification
</emphasis>
- location of a file on the server where a key
used to unlock or encrypt a message is kept.
</para>
</listitem>

<listitem id="authtypeforwardid"> <para> <literal>
AUTHTYPE_forward_id=
</literal><emphasis>
identification
</emphasis>
- the identification of the remote destination,
This is used by the server when forwarding a
job to a remote destination.  By default, the
AUTHTYPE_id value is used by the server as its
identification.
</para>
</listitem>

<listitem id="authtypedefaultclient"> <para> <literal>
AUTHTYPE_default_client=
</literal><emphasis>
identification
</emphasis>
- when forwarding a job and the job arrived
via an unauthenticated method,  use this as the
default client identification.
</para>
</listitem>
</itemizedlist>
</para>

</sect1>

<sect1><title>User Identification</title>

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

<para>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 is 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.</para>

</sect1>

<sect1><title>RFC1179 Protocol Extensions</title>

<para>The RFC1179 protocol specifies that a <application/lpd/ server command sent on
a connection has the form:
<informalexample>
<screen>\nnn[additional fields]\n</screen>
</informalexample>
</para>

<para><literal>\nnn</literal>
is a one octet (byte) value with the following meaning:</para>

<para>
<informalexample>
<screen>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</screen>
</informalexample>
</para>

<para>The &LPRng; system extends the protocol with the following additional
types:
<informalexample>
<screen>REQ_CONTROL 6    do control operation
REQ_BLOCK   7    transfer a block format print job
REQ_SECURE  8    do operation with authentication</screen>
</informalexample>
</para>

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

</sect1>

<sect1 id="auth"><title>Authentication Operations
</title>

<para>Options used:
<itemizedlist>

<listitem>
<para> <literal>auth=</literal><emphasis>client to server authentication type</emphasis></para>
</listitem>

<listitem>
<para> <literal>auth_forward=</literal><emphasis>server to server authentication type</emphasis></para>
</listitem>

<listitem>
<para> <literal>XX_id=</literal><emphasis>server identification</emphasis></para>
</listitem>

<listitem>
<para> <literal>XX_forward_id=</literal><emphasis>Server identification</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>A &LPRng; client
<application/lpr/,
<application/lpq/,
<application/lprm/,
or
<application/lpc/
to <application>lpd</application> server authenticated transfer
proceeds as follows.
If an authenticated transfer is specified by the
<literal>auth=protocol</literal> entry in the printcap or configuration information,
the client sends a request for an authenticated transfer
to the server.</para>

<para>Part of the authentication request is the authentication type.
If authentication type <acronym>XX</acronym> is requested
the server will examine the information in the printcap and configuration
entries for an <literal>XX_id</literal> value.
If this value is present then the server supports authentication of this type.
Further permission checks are carried out and finally the
server will accept or reject the authentication request.
If the request is accepted the server returns a positive
acknowledgment (single 0 byte) to the requester,
otherwise it returns a nonzero value and an error message.</para>

<para>If the request is accepted
then an authentication specific protocol exchange is carried out between
client and server.
The commands and/or data files
are encrypted and/or signed and transferred to the server.
The protocol specific software on the server will then decrypt and/or check
signatures,
perform the requested actions,
and in turn generate a status information.
The status information is encrypted and/or signed by the server
and sent to the client,
where the client decrypts and/or checked for correct signature.</para>

<para>
The <literal>auth</literal> authentication type is used as a prefix to look
up authentication specific information in the printcap or configuration information.
For example,
if <literal>auth=kerberos</literal>,
then
<literal>kerberos_id</literal>,
<literal>kerberos_forward_id</literal>,
and other information would be extracted from the printcap or configuration
information.
</para>

<para>A <application>lpd</application> server to <application>lpd</application> server authenticated transfer
proceeds as follows.
If an authenticated transfer is specified by the
<literal>auth_forward=protocol</literal> entry in the printcap or configuration information,
the originating server sends a request for an authenticated transfer
to the destination server.
The originating server plays the part of the client
and performs the same set of actions.</para>

<para>The following printcap or user level information needs to be provided
for an authenticated exchange.
<orderedlist>

<listitem>
<para>The
<literal>auth</literal>
option specifies the authentication type to be used
for client to server transfers.
For example,
<literal>auth=kerberos</literal>
would specify Kerberos 5 authentication with encrypted data transfers,
<literal>auth=k5conn</literal>
would specify Kerberos 5 authentication with non-encrypted data transfers,
<literal>auth=kerberos4</literal> would specify Kerberos 4 authentication,
<literal>auth=pgp</literal> would specify PGP authentication,
<literal>auth=md5</literal> would specify MD5 authentication,
etc.
The special form <literal>auth@</literal> specifies no authentication.</para>
</listitem>

<listitem>
<para>The <literal>auth_forward</literal> option specifies the authentication type to be used
for server to server transfers.
For example,
<literal>auth_forward=kerberos5</literal> would specify Kerberos 5 authentication,
etc.
The special form <literal>auth_forward@</literal> specifies no authentication.</para>
</listitem>

<listitem>
<para>The <literal>AUTHTYPE_id</literal> option specifies
an identification key for the destination server to be used for in client to
server transfers.
For example,
<literal>kerberos_id</literal> would specify the principal name of the
<literal/lpd/ server to be used by the client for the client to server
kerberos authentication.
</listitem>

<listitem>
<para>When forwarding a job to a remote server,
the <literal>AUTHTYPE_forward_id</literal> option specifies
the identification to be used for the destination in server to
server transfers.
For example,
<literal>kerberos_id</literal> would specify the principal name of the
originating
<literal/lpd/ server and
the <literal>kerberos_forward_id</literal> option specifies
the destination server kerberos id.
</listitem>

<listitem>
<para>When forwarding a job to a remote server,
if the original job does not have an authenticated user name or
identification,
then the
<literal>AUTHTYPE_default_client_id</literal> option specifies the
name to be used to identify the job.
As discussed in later sections,
this will be the <literal/A/ record value of the transferred control file.
</listitem>

<listitem>
<para>The authenticated transfer request sent to a server has one of the
following forms, depending on the originator:
<informalexample>
<screen>\008printer C user_id authtype \n  - for commands (lpq, lpc, etc.)
\008printer C user_id authtype size\n - for print jobs (lpr)
\008printer F server_id authtype \n - forwarded commands (lpq, lpc, etc.)
\008printer F server_id authtype size\n - forwarded print jobs (lpr)</screen>
</informalexample>
</para>

<para>The single character with the
<literal>\008</literal>
value signals that this
is an authentication request
the
<literal>printer</literal>
is the name of a print queue,
and the
<literal remap=tt>C</literal>
(client) or
<literal remap=tt>F</literal>
indicates that the request is from
a client program or is a forwarded request from a server.
The <literal>user_id</literal> or <literal>server_id</literal> field is an identifier supplied by
the originator and is discussed below.
If the
<literal>size</literal>
value is present then the request
is for a job transfer and this value represents the job size.
It is used to determine if there is sufficient space in the spool queue
for the job.</para>
</listitem>

<listitem>
<para>The <literal>user_id</literal> or <literal>server_id</literal> fields in the authentication
request are obtained as follows.
If the request originates from a client,
then the <literal>user_id</literal> is the user name of the originator obtained from
password information.
If the request originates from a server,
then the  <literal>server_id</literal> is the printcap or configuration
<literal>xx_id=server_id</literal> value,
where
<literal>xx</literal>
is the value of the <literal>auth_forward=xx</literal> entry.</para>
</listitem>

<listitem>
<para>When the authenticated transfer request is received,
the destination will either return a single zero byte,
or a non-zero byte value followed by additional refusal information.
A refusal terminates the protocol exchange.</para>
</listitem>

<listitem>
<para>Further exchanges are then determined by the authentication
protocol specific requirements.</para>
</listitem>

<listitem>
<para>Once the initial exchanges have been completed
a user file and/or command will be transferred to the destination server.</para>
</listitem>

<listitem>
<para>An authentication protocol specific <acronym>AUTHFROM</acronym> and <acronym>AUTHUSER</acronym>
strings will be supplied
to the lpd server for purposes of permission checking.</para>
</listitem>

<listitem>
<para>The lpd server then carries out the requested operation,
and will write error and status information into a file.</para>
</listitem>

<listitem>
<para>After the requested activity has finished,
protocol specific module transfer the status information in the
file to the requesting system
and terminate the protocol exchange.</para>
</listitem>

</orderedlist>
</para>

</sect1>

<sect1><title>Permission Checking</title>

<para>When an authenticated transfer has been performed,
the following permission information will be provided.
<itemizedlist>

<listitem>
<para>AUTH<!-- <br> -->
This value is
<literal>true</literal>
or
<literal>match</literal>
if an authenticated request
was received.</para>
</listitem>

<listitem>
<para>AUTHTYPE=authtype<!-- <br> -->
This has the value of the
<literal>authtype</literal>
field in the authentication
request.</para>
</listitem>

<listitem>
<para>AUTHUSER=userinfo<!-- <br> -->
This is the <acronym>AUTHUSER</acronym> information provided by the authentication
protocol,
and is usually the originating user's identification.</para>
</listitem>

<listitem>
<para>AUTHFROM=frominfo<!-- <br> -->
This is the <acronym>AUTHUSER</acronym> information provided by the authentication
protocol,
and is usually the originating system (user or lpd server) identification.</para>
</listitem>

<listitem>
<para>AUTHSAMEUSER<!-- <br> -->
This item has effect only when checking jobs in a spool queue.
The <acronym>AUTHUSER</acronym> information from the request is compared to the
<acronym>AUTHUSER</acronym> information from the request that created a job.
If they are identical,  the match succeeds.</para>
</listitem>

<listitem>
<para>AUTHJOB
This item has effect only when checking jobs in a spool queue.
If the job was transfered using an authentication protocol the match succeeds.<!-- <br> --></para>
</listitem>

</itemizedlist>
</para>

<para>For example,  to reject non-authenticated operations, the following
line could be put in the permissions file.
<informalexample>
<screen>REJECT NOT AUTH</screen>
</informalexample>
</para>

<para>If a remote server has id information FFEDBEEFDEAF,  then the
following will accept only forwarded jobs from this server.
<informalexample>
<screen>ACCEPT AUTH AUTHFROM=FFEDBEEFDEAF
REJECT AUTH
REJECT NOT AUTH</screen>
</informalexample>
</para>

<para>To allow only authenticated users to remove jobs you can use:
<informalexample>
<screen>ACCEPT AUTH SERVICE=R,M,L,P AUTHSAMEUSER
REJECT AUTH
REJECT NOT AUTH</screen>
</informalexample>
</para>

</sect1>

<sect1><title>PGP Authentication Support</title>

<para>PGP is a well known encryption and authentication program.
For more details see the web site
<ulink URL="http://www.pgp.net">http://www.pgp.net</ulink>
or the ftp site
<ulink URL="ftp://ftp.pgp.net">ftp://ftp.pgp.net</ulink>.</para>

<para>&LPRng; has greatly simplified the use of PGP for authentication
by building in support as follows.
<itemizedlist>

<listitem>
<para>The
<literal>user</literal>
and
<literal>group</literal>
configuration entry (defaults
<literal>daemon</literal> and <literal>daemon</literal> respectively) specify the user and group id
used by the <application>lpd</application> server for file and program execution.
PGP uses the current user id of the PGP process to determine the locations
of various configuration files and information.
In this discussion we will assume that <application>lpd</application> runs as uid <literal>daemon</literal>.</para>
</listitem>

<listitem>
<para>By default,
the PGP program expects the public and secret key rings to be in the
<filename>$HOME/.pgp/</filename> directory
to be readable only by the user.
In order to set up PGP authentication,
make sure that the <literal>daemon</literal> account has a home directory.
The <literal>daemon</literal> user should not allow logins or have its login password disabled.</para>
</listitem>


<listitem>
<para>Each PGP key has an associated identifier.
It is recommended that the <application>lpd</application> key be <literal>lpr@hostname</literal>,
where hostname is the fully qualified domain name of the server.
</para>
</listitem>

<listitem>
<para>
Create the public and private keys for the server.
For security reasons the <literal/daemon/ account should not have login capabilities.
<informalexample>
<screen>
#> su /bin/sh      # start root shell
%> HOME=/tmp
%> export HOME
%> mkdir /tmp/.pgp
%> pgp -kg
      # select 1024 or longer keys
      # set the user id to be lpr@hostname as discussed above
      # set the pass phrase
%> ls /tmp/.pgp
pubring.bak   pubring.pgp  randseed.bin  secring.pgp
%> cd /tmp/.pgp
%> pgp -kxa lpr@hostname serverkey pubring.pgp  # creates serverkey.asc
      # you will want to give serverkey.asc to users to add to their
      # public key ring
%> mkdir ~daemon/.pgp
%> cp * ~daemon/.pgp
%> chown daemon ~daemon/.pgp ~daemon/.pgp/*
%> chmod 700 ~daemon/.pgp
%> chmod 644 ~daemon/.pgp/*
</screen>
</informalexample>
</para>
</listitem>


<listitem>
<para>Next,
place the passphrase for the <literal>daemon</literal>
user in
<filename>~daemon/.pgp/serverkey</filename>,
and make sure it has owner <literal>daemon</literal>
and
<literal>600</literal>
permissions (read/write only by <literal>daemon</literal>).
This is extremely important.
If other users can read this file then security will be severely compromised.</para>
</listitem>

<listitem>
<para>Next, distribute the <literal>servername.asc</literal>
file to users.
&LPRng; server.
This is usually done by placing the key file in a well known file location
or making it available to users by some form of Public Key Distribution system
(PKD).
</listitem>

<listitem>
<para>Users add the <literal>serverkey.asc</literal> key to
their public key using:
<informalexample>
<screen>pgp -ka serverkey.asc</screen>
</informalexample>
</para>
</listitem>

<listitem>
<para>Finally,  the administrator will need to add the users public keys to the
<literal>daemon</literal>public key ring file <literal>pubkey.pgp</literal>.
This can most easily be done by
copying all of the users public keys (in ASCII text format) to a single file
(<filename>/tmp/keyfile</filename>)and using:
<informalexample>
<screen>su daemon
pgp -ka /tmp/keyfile ~daemon/.pgp/pubring.pgp</screen>
</informalexample>
</para>
</listitem>

<listitem>
<para>If the <application>lpd</application> server is using PGP to forward jobs or requests,
the destination server's public key must be put in the originating
servers public keyring.  For example:
<informalexample>
<screen>su daemon
pgp -ka /tmp/lpd.keyfile ~daemon/.pgp/pubring.pgp</screen>
</informalexample>
</para>
</listitem>

</itemizedlist>
</para>


<sect2 id="pgppath"><title>Printcap Configuration

</title>

<para>Options used:
<itemizedlist>

<listitem>
<para><literal>pgp_path=</literal><emphasis>path to PGP program</emphasis></para>
</listitem>

<listitem>
<para><literal>pgp_id=</literal><emphasis>destination server key used by clients</emphasis></para>
</listitem>

<listitem>
<para><literal>pgp_forward_id=</literal><emphasis>destination server used by server</emphasis></para>
</listitem>

<listitem>
<para><literal>pgp_server_key=</literal><emphasis>path to server passphrase file</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>Example printcap entry:
<informalexample>
<screen>pr:
    :lp=pr@wayoff
    :auth=pgp
    :pgp_id=lpr@wayoff.com
    :pgp_path=/usr/local/bin/pgp
pr:server
    :lp=pr@faroff
    :auth_forward=pgp
    :pgp_id=lpr@wayoff.com
    :pgp_path=/usr/bin/pgp
    :pgp_forward_id=lpr@faroff.com</screen>
</informalexample>
</para>

<para>The <literal>pgp_path</literal> value is the path to the PGP program.
The program must be executable by all users.</para>

<para>The <literal>pgp_id</literal> value is the id used by PGP to look extract keys from
key rings.
When doing a client to server transfer this will be supplied as the id
to be used for the destination,
and the user's public keyring will be checked for a key corresponding to
this id.
When a request arrives at the server,
the server will use this value as the id of a key in its private key ring.
Finally,
when a server is forwarding a request to a remote server,
it will use this value
as the id of the key in its private key ring to be used to sign
or encode the destination information.</para>

<para>The <literal>pgp_forward_id</literal> value is used by the <application>lpd</application> server as the id
to use to find a key for the destination.</para>

<para>The <literal>pgp_server_key</literal> is the path to the file containing the server passphrase.
This file will be read by <application>lpd</application> to get the passphrase to unlock the server's
keyring.</para>

</sect2>

<sect2><title>User Files and Environment Variables</title>

<para>Options used:
<itemizedlist>

<listitem>
<para><literal>PGPPASSFILE=</literal><emphasis>File to read PGP passphrase from</emphasis></para>
</listitem>

<listitem>
<para><literal>PGPPASSFD=</literal><emphasis>File descriptor to read PGP passphrase from</emphasis></para>
</listitem>

<listitem>
<para><literal>PGPPASS=</literal><emphasis>PGP passphrase</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>One problem with using PGP is the need to have users input their
passphrases.
The following methods can be used.
<itemizedlist>

<listitem>
<para>Put the passphrase in a file,
say <filename>$(HOME)/.pgp/.hidden</filename>,
and set the <acronym>PGPPASSFILE</acronym> environment variable to the file name.
This file will be opened and read by PGP to get the passphrase.
This file should be owned by the user and have
<literal>0600</literal>
or read/write
only by user permissions.</para>
</listitem>

<listitem>
<para>A more subtle solution is to use the <acronym>PGPPASSFD</acronym> environment variable
facility.
This causes PGP to read the passphrase from a file descriptor.
If the user puts his passphrase in a file,  say
<filename>$(HOME)/.pgp/.hidden</filename>,
then the following shell script can be used:
<informalexample>
<screen>#!/bin/sh
#  /usr/local/bin/pgplpr script - passphrase in $(HOME)/.pgp/.hidden
#
PGPASSFD=3 3&lt;$(HOME)/.pgp/.hidden lpr "$@"</screen>
</informalexample>
</para>
</listitem>

<listitem>
<para>The least desirable method is to put the passphrase in the
<acronym>PGPPASS</acronym> environment variable.
Since the
<literal>ps</literal>
command can be used to list the environment variables
of processes,
this is highly undesirable and should not be used under any circumstances.</para>
</listitem>

</itemizedlist>
</para>

</sect2>
</sect1>

<sect1 id=kerberos><title>Using Kerberos 5 for Authentication</title>

<para>&LPRng; Kerberos 5 authentication is
based on the
Kerberos5-1.2.5 release as of 3 June 2002 and is compatible
with the
Kerberos5-1.3.6 release as of 20 Jan 2005.
The Kerberos distribution was obtained
from MIT from the
<emphasis remap=tt><ulink URL="http://web.mit.edu/kerberos/www/">http://web.mit.edu/kerberos/www/</ulink></emphasis>
Website.</para>

<para>The following sections briefly describes
how to set up and test the Kerberos software
and then how to configure &LPRng; to use Kerberos.</para>


<sect2 id=k5conn><title>Using AUTH kerberos and k5conn</title>
<para>
There are two methods supported for Kerberos authentication:
<literal>kerberos</literal>
and
<literal>k5conn</literal>.
Both methods support authentication using the Kerberos key and key server methodology.
However,
using
<literal>kerberos</literal>
will cause transfers of files and information to be done using encryption while
<literal>k5conn</literal>
will only perform authentication and transfer files and information without
encryption.
As the overhead for encryption can be very high,
when security of information is not a concern
then the
<literal>k5conn</literal>
should be used.
</para>
</sect2>

<sect2><title>&LPRng; Configuration</title>

<para>The following <literal/configure/ options are used to enable
Kerberos support:
<informalexample>
<screen>
--enable-kerberos         enable Kerberos V support
--enable-mit_kerberos4    enable MIT Kerberos 4 support
--disable-kerberos_checks disable Kerberos sanity checks
</screen>
</informalexample>
</para>

<para>
The <literal/--enable-kerberos/ option will 
cause <literal/configure/ to search for the
include files such as
<filename>krb5.h</filename>
and the
<literal>krb5</literal>
support libraries.
libraries.
If it finds these,
then Kerberos authentication will be included.
The <literal/--enable-mit_kerberos/ enable searching for 
the Kerberos 4 include files and support libraries.
If these are found then
MIT Kerberos 4 compatibility will
be enabled.
The <literal/--disable-kerberos_checks/ will disable checking for libraries
and simply enable the various options.
</para>

</sect2>

<sect2><title>Kerberos Installation Procedure </title>

<para>
<orderedlist>

<listitem>
<para> Get the Kerberos 5 distribution.</para>
</listitem>

<listitem>
<para> Compile and install the distribution.</para>
</listitem>

<listitem>
<para> Create the
<filename>/etc/krb5.conf</filename> and
<filename>/usr/local/var/krb5kdc/kdc.conf</filename>,
files using templates from the files in the
Kerberos distribution's
<filename>src/config-files</filename> directory.
See the Kerberos Installation Guide and the Kerberos System Administrators Guide for
details.</para>
</listitem>

<listitem>
<para> Start up the KDC and KADMIN servers - you might want to put
the following in your
<filename>rc.local</filename>
or equivalent system startup files:
<informalexample>
<screen>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</screen>
</informalexample>
</para>
</listitem>

<listitem>
<para> Use kadmin (or kadmin.local) to create principals for your users.</para>
</listitem>

<listitem>
<para> Use kadmin (or kadmin.local) to create principals for the
<application>lpd</application> servers.  The recommended method is to use
<filename>lpr/hostname@REALM</filename>
as a template for the principal name,
i.e. -
<literal>lpr/astart1.private@ASTART.COM</literal>
for an example.
You should use fully qualified domain names for the principals.
Do not assign the principal a password.
<informalexample>
<screen>Example:

#&gt; kadmin   OR #> kadmin.local 
kadmin: addprinc -randkey lpr/wayoff.private@ASTART.COM
quit</screen>
</informalexample>
</para>
</listitem>

<listitem>
<para> Extract the keytab for each server:
<informalexample>
<screen>
Example:
#> kadmin   OR #> kadmin.local 
ktadd -k /etc/lpr.wayoff.private  lpr/wayoff.private@ASTART.COM
quit</screen>
</informalexample>
</para>
</listitem>

<listitem>
<para>The
<literal>/etc/lpr.wayoff.private</literal>
file contains the keytab information which is the
equivalent of a password for a server program.
You should create these files and then
copy the appropriate
<literal>keytab</literal>
file  to <filename>/etc/lpd.keytab</filename> file on each server.
See the warnings about of keytab files in the
Kerberos Installation and  Kerberos  Administration manuals.
You should copy the file using an encrypted connection,
set the permissions to read only by the owner (<literal>400</literal>),
and set the owner to
<literal>daemon</literal> or the user that <application>lpd</application> will run as.
<informalexample>
<screen>#&gt; chmod 400 lpr.wayoff.com
#&gt; scp lpr.wayoff.com root@wayoff.com:/etc/lpd.keytab
#&gt; ssh -l root wayoff.com
# wayoff &gt; chmod 400 /etc/lpd.keytab  
# wayoff &gt; chown daemon /etc/lpd.keytab  
# wayoff &gt; ls -l /etc/lpd.keytab  
-rw-------  1 daemon  wheel  128 Jan 16 11:06 /etc/lpd.keytab</screen>
</informalexample>
</para>
</listitem>

<listitem>
<para>If you want to have MIT Kerberos4 printing compatibility
then you will need to set up Kerberos 4
<literal>servertabs</literal>
instead of Kerberos 5 keytabs.
Assuming that you have put the Kerberos 5 keytab in <filename>/etc/lpd.keytab</filename>,
then you extract the Kerberos 4 srvtab version of the Kerberos 5 keytab using
the following commands.
You must put the key in the <filename>/etc/srvtab</filename> file
in order to be compatible with the Kerberos 4 support.
<informalexample>
<screen><prompt>h4: {321} # </prompt><userinput>ktuil</userinput>
<userinput>rkt /etc/lpd.keytab</userinput>
<userinput>wst /etc/srvtab</userinput></screen>
</informalexample>
</para>
</listitem>

</orderedlist>
</para>

</sect2>

<sect2><title>&LPRng; Configuration</title>

<para>The &LPRng;  software needs to be configured so that it can find the
Kerberos libraries and include files.
By default,
the include files are installed in
<filename>/usr/local/include</filename>
and the libraries in
<filename>/usr/local/lib</filename>.
Use the following steps to configure &LPRng; so that it uses these
directories during configuration and installation:
<informalexample>
<screen>cd .../LPRng
rm -f config.cache
CPPFLAGS="-I/usr/local/include -I/usr/include/kerberosIV" \
  LDFLAGS="-L/usr/local/lib -L/usr/lib/kerberosIV" \
  ./configure
make clean all
su
make install</screen>
</informalexample>
</para>

</sect2>

<sect2><title>Printcap Entries</title>

<para>Options used:
<itemizedlist>

<listitem>
<para><literal>auth=kerberos5</literal><emphasis>use Kerberos5 authentication</emphasis></para>
</listitem>

<listitem>
<para><literal>kerberos_id=</literal><emphasis>server prinicpal name (for client use)</emphasis></para>
</listitem>

<listitem>
<para><literal>kerberos_server_principal=</literal><emphasis>alias for kerberos_id</emphasis></para>
</listitem>

<listitem>
<para><literal>kerberos_forward_id=</literal><emphasis>destination server used by server</emphasis></para>
</listitem>

<listitem>
<para><literal>kerberos_forward_principal=</literal><emphasis>alias for kerberos_forward_id</emphasis></para>
</listitem>

<listitem>
<para><literal>kerberos_keytab=</literal><emphasis>location of the lpd server keytab file</emphasis></para>
</listitem>

<listitem>
<para><literal>kerberos_service=</literal><emphasis>service to be used</emphasis></para>
</listitem>

<listitem>
<para><literal>kerberos_life=</literal><emphasis>lpd server ticket lifetime</emphasis></para>
</listitem>

<listitem>
<para><literal>kerberos_renew=</literal><emphasis>lpd server ticket renew</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>Example printcap entry:
<informalexample>
<screen>pr:client
    :lp=pr@wayoff
    :auth=kerberos5
    :kerberos_id=lpr/wayoff.private@ASTART.COM
pr:server
    :lp=pr@faroff.private
    :auth_forward=kerberos5
    :kerberos_id=lpr/wayoff.private@ASTART.COM
    :kerberos_forward_id=lpr/faroff.private@ASTART.COM
    :kerberos_keytab=/etc/lpd.keytab

OR If you want to use Kerberos 4 authentication to the server
pr:client
    :lp=pr@wayoff
    :auth=kerberos4
    :kerberos_id=lpr/wayoff.private@ASTART.COM
# support both Kerberos 4 and 5 on server
pr:server
    :lp=pr@faroff.private
    :auth_forward=kerberos5
    :kerberos_id=lpr/wayoff.private@ASTART.COM
    :kerberos_forward_id=lpr/faroff.private@ASTART.COM
    :kerberos_keytab=/etc/lpd.keytab
</screen>
</informalexample>
</para>

<para>The printcap configuration for Kerberos authentication is very simple.</para>

<para>The <literal>kerberos_id</literal> is the principal name of the lpd server
that clients will connect to.
For backwards compatibility,
<literal>kerberos_server_principal</literal> can also be used.
This values is used to obtain a ticket for the <application>lpd</application> server,
and is the only entry required for client to server authentication.</para>

<para>The other entries are used by the <application>lpd</application> server.
<literal>kerberos_keytab</literal>
entry is the location of the keytab file to be used by the server.
This contains the passphrase used by the server to authenticate itself
and get a ticket from the ticket server.</para>

<para>The <literal>kerberos_id</literal> value is also used by the server during the
authentication process to make sure that the correct principal name
was used by the request originator.
This check has saved many hours of pain in trying to determine why
authentication is failing.</para>

<para>The
<literal>kerberos_life</literal> and <literal>kerberos_renew</literal>
set the lifetime and renewability
of the lpd server Kerberos tickets.
These values should not be modified unless you are familiar with the
Kerberos system.
There are extensive notes in the &LPRng; source code concerning these values.
The <literal>kerberos_service</literal> value supplies the name of the service
to be used when generating a ticket.
It is stronly recommended that the <literal>kerberos_id</literal> entry
be used instead.</para>

</sect2>

<sect2><title>User Environment Variables and Files</title>

<para>In order to use kerberos authentication,
the user will need to obtain a ticket from the Kerberos ticket server.
This is done using
<literal>kinit</literal>.</para>

<para>No other actions are required by the user.</para>

</sect2>
</sect1>

<sect1><title>Using Kerberos 4 for Authentication</title>

<para>&LPRng; has built-in support for the Project Athena extensions to the
RFC1179 protocol.
These provide an extremely simple authentication protocol
using an initial credential exchange.
After the initial exchange the usual RFC1179 protocol is used.</para>

<para>During configuration,
if the <filename>krb.h</filename> (Kerberos 4) include file is found,
then this is enabled by default.</para>


<sect2><title>Printcap Entries</title>

<para>Options used:
<itemizedlist>

<listitem>
<para><literal>auth=kerberos4</literal><emphasis>use Kerberos4 authentication</emphasis></para>
</listitem>

<listitem>
<para><literal>kerberos_id=</literal><emphasis>destination server key used by clients</emphasis></para>
</listitem>

<listitem>
<para><literal>kerberos_server_principal=</literal><emphasis>alias for kerberos_id</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>Example printcap entry:
<informalexample>
<screen>pr:
    :lp=pr@wayoff
    :auth=kerberos4
    :kerberos_id=lpr/wayoff.private@ASTART.COM</screen>
</informalexample>
</para>

<para>The configuration information for Kerberos4 and Kerberos5 is identical
and differ only in the authentication type.
Note that only client to server authentication is supported.</para>

</sect2>
</sect1>

<sect1><title>Using SSL for Authentication</title>
<para>&LPRng; has built-in support for using SSL as an
authentication method.
The implementation is based on OpenSSL 0.9.6c
and the associated libraries as of
of 3 June 2002.
The distribution was obtained
from the OpenSSL group from the
<emphasis remap=tt><ulink URL="http://www.openssl.org">http://www.openssl.org</ulink></emphasis>
Website.
</para>

<para>
SSL authentication is based a private key/secret key technology,
where the various keys are placed in files (or data structures) called
<emphasis/certificates/ or <literal/certs/,
and the certificates are
<emphasis/signed/ by calculating a checksum over the certificate,
encypting the checksum and other information using the private key
of a
<emphasis/signing/ certificate. 
The top level or <emphasis/root/ certificate is signed by its own key;
lower level signing certificates can be created which are signed by the
top level or root certificate,
and in turn can sign other signing certificates.
User certificates can be created and signed by a signing certificate
which can be used in the SSL protocol for authentication purposes.
The following objects are needed to use SSL encryption.
</para>

<para>
<orderedlist>

<listitem>
<para>
A top level or root certificates and a set of signing certificates.
By convention,
these are stored in the
<filename>/etc/lpd/ssl.ca</filename>
directory;
the root certificate is usually the
<filename>ca.crt</filename>
file.
</para>
</listitem>

<listitem>
<para>
Each server has a certificate and private key  file which are used to identify the
server and sign the SSL messages.
The private key file is usually stored in an encrypted form and a password
is required unlock the file.
By convention,
the server files are stored in the
<filename>/etc/lpd/ssl.server</filename>
directory;
the 
<filename>server.crt</filename>
file contains the server certificate and (encrypted) private key;
the
<filename>server.pwd</filename>
file contains the password to decrypt the private key.
</para>
</listitem>

<listitem>
<para>
Each user has a certificate and private key  file which are used to identify the
user and sign the SSL messages.
The private key file is usually stored in an encrypted form and a password
is required unlock the file.
By convention,
the user files are stored in the
<filename>${HOME}/.lpr</filename>
directory;
the 
<filename>client.crt</filename>
file contains the client certificate and (encrypted) private key;
the
<filename>client.pwd</filename>
file contains the password to decrypt the private key.
</para>
</listitem>

<listitem>
<para>
A utility to create and manage the SSL certificate files.
</para>
</listitem>

</orderedlist>

</para>

<para>
The locations of the SSL files can be specified by various options
to <application/configure/ facility
and by values in the the <literal/lpd.conf/ file.
</para>

<sect2><title>Certificate Management</title>
<para>
The <application/lprng_cert/ utility is used to set up the various
directories and files required for SSL authentication.
This code was derived from similar facilities developed for
the <literal/mod_ssl/ extensions to the <application/Apache/
web server.
This interactive utility is very verbose and has extensive comments and
assistance. 
<informalexample>
<screen>h110: {111} % lprng_certs
lprng_certs -- LPRng SSL Certificate Management
Copyright (c) 2002 Patrick Powell
Based on CCA by Ralf S. Engelschall
(Copyright (c) 1998-2001 Ralf S. Engelschall, All Rights Reserved.)

usage: lprng_certs option
  init              - make directory structure
  newca             - make new root CA and default values for certs
  defaults          - set new default values for certs
  gen               - generate user, server, or signing cert
  verify [cert]     - verify cert file
  index [dir]       - make certificate index files in directory dir
  encrypt keyfile   - set or change password on private key file
</screen>
</informalexample>
</para>

<para>
The <literal/lprng_certs init/ option will create the
necessary directories for the &LPRng; software on a system.
The <literal/lprng_certs newca/ option will create the root level
certificate and set up a set of defaults for the creation of other
certificates.
The <literal/lprng_certs defaults/ option allows viewing and editting of the various
default values.
The <literal/lprng_certs gen/ option is used to create and sign new certificate files.
The OpenSSL software assumes that the file names of the signing certificate files have a special
format; the <literal/lprng_certs index/ creates links of the required format to the
certificate files.
Finally,
the <literal/lprng_certs verify/ and
the <literal/lprng_certs encrypt/ facilities can be used to
verify that the certificate files have the proper format and to
change the private key password respectively.
</para>

</sect2>

<sect2><title>Creating Root Certificate</title>
<para>
The <literal/lprng_certs newca/
option is used to create a new root signing certificate and to establish defaults.
<informalexample>
<screen>h110: {112} #> lprng_certs newca
lprng_certs -- LPRng SSL Certificate Management
Copyright (c) 2002 Patrick Powell
Based on CCA by Ralf S. Engelschall
(Copyright (c) 1998-2001 Ralf S. Engelschall, All Rights Reserved.)

INITIALIZATION - SET DEFAULTS
...  
______________________________________________________________________

STEP 1: Generating RSA private key for CA (1024 bit)
______________________________________________________________________

STEP 2: Generating X.509 certificate signing request for CA
______________________________________________________________________

STEP 3: Generating X.509 certificate for CA signed by itself
______________________________________________________________________

RESULT:
/etc/lpd/ssl.ca/ca.crt:
/C=US/ST=California/L=San Diego/O=Astart/OU=Certificate Authority/\
 CN=Astart CA/Email=id@astart.com
error 18 at 0 depth lookup:self signed certificate
OK
______________________________________________________________________

STEP 4. Encrypting RSA private key with a pass phrase for security
The contents of the certificate key file (the generated private
key) should be echo kept secret, especially so if it is used to
sign Certificates or for User authentication.  SSL experts strongly
recommend you to encrypt the key file with a Triple-DES cipher and
a Pass Phrase.  When using LPRng, you provide the password via a
file specified by the LPR_SSL_PASSWORD environent variable, or in
the ${HOME}/.lpr/client.pwd file.  The LPD server uses the
ssl_server_password_file option to specify the location of a file
containing the password.  See the LPRng Reference Manual for details, or the
printcap(5) man page.

key file is /etc/lpd/ssl.ca/ca.key
Encrypt the private key now? [Y/n]: y
Fine, you're using an encrypted private key to sign CERTS.
</screen>
</informalexample>
</para>
</sect2>

<sect2><title>Creating Client and Server Certificates</title>
<para>
The <literal/lprng_certs gen/
option allows the creation of client and server identification
certificates.
By convention,
these are created in a default directory and the
system administrator then copies them to the appropriate client
or server directory.

<informalexample>
<screen>h110: {112} #> lprng_certs gen
lprng_certs -- LPRng SSL Certificate Management
Copyright (c) 2002 Patrick Powell
Based on CCA by Ralf S. Engelschall
(Copyright (c) 1998-2001 Ralf S. Engelschall, All Rights Reserved.)

CERTIFICATE GENERATION
What type of certificate? User/Server/Signing Authority/Help? [u/s/a/H]
Create in '/etc/lpd/ssl.certs' [return for yes, or specify directory]
CERT name 'user-10'? [return for yes, or specify name] papowell
CERT name 'papowell'? [return for yes, or specify name] 
Creating papowell in /etc/lpd/ssl.certs
Sign with Certificate '/etc/lpd/ssl.ca/ca.crt' \
   [return for yes, ? for list, or specify cert file] ?
Possible CERTS in directory '/etc/lpd/ssl.ca' are:
/etc/lpd/ssl.ca/ca.crt
/etc/lpd/ssl.ca/signer1.crt
/etc/lpd/ssl.ca/tsign.crt
Sign with Certificate '/etc/lpd/ssl.ca/ca.crt'  \
  [return for yes, ? for list, or specify cert file] signer1
Match Found /etc/lpd/ssl.ca/signer1.crt
Sign with Certificate '/etc/lpd/ssl.ca/signer1.crt'  \
  [return for yes, ? for list, or specify cert file]
Private key in /etc/lpd/ssl.ca/signer1.crt

Generating user Certificate [papowell] 

STEP 1: Generating RSA private key for user (1024 bit)

STEP 2: Generating X.509 certificate signing request for user
....

STEP 3: Generating X.509 certificate signed by /etc/lpd/ssl.ca/signer1.crt
...

RESULT:
/etc/lpd/ssl.certs/papowell.crt: OK

STEP 4. Enrypting RSA private key /etc/lpd/ssl.certs/papowell.key
  with a pass phrase for security

Encrypt the private key now? [Y/n]: Fine, you're using an encrypted
  private key to sign CERTS.

STEP 5: Combine CERT and KEY file
Generate single CERT and KEY file? [Y/n] 

Use the following commands to examine the CERT and KEY files:
   openssl x509 -text -in /etc/lpd/ssl.certs/papowell.crt
   openssl rsa -text -in /etc/lpd/ssl.certs/papowell.crt
</screen>
</informalexample>
</para>

<para>
After the certificate file has been created,
then it should be copied to the appropriate location:
<filename>/etc/lpd/ssl.server/server.crt</filename>
and the password in
<filename>/etc/lpd/ssl.server/server.pwd</filename>,
for a server
or
<filename>${HOME}/.lpr/client.crt</filename>
and the password in
<filename>${HOME}/.lpr/client.pwd</filename>
for a user.
</para>
</sect2>

<sect2><title>Creating Signing Certificates</title>
<para>
Having only one signing certificate, i.e. - the root certificate,
may make it difficult to delegate authority for the creation of user
certificates and/or server certificates.
The <literal/lprng_certs gen/ facility can be used to create a
certificate that can be used to sign other certificates.
</para>

<sect2><title>Permissions and Certificate Revocation</title>
<para>
The certificate revocation facility is not implemented in
&LPRng;, due to various technical and management issues.
Instead, the
<literal/AUTHUSER/ and
<literal/AUTHCA/ and
</para>

</sect1>

<sect1><title>Using MD5 for Authentication</title>

<para>&LPRng; has built-in support for using MD5 digests as an
authentication method.
The implementation is provided as an example of how to
add user level authentication into the &LPRng; system.</para>

<para>The method used to do authentication is very simple.
Each user has a file containing a set of keys that are used to salt an
md5 hash.
The information being transferred has its md5 checksum calculated using
this salt,
and is then transferred to the destination,
along with the md5 hash result.
At the destination the server will get the user id,
obtain the salt value from a key file,
and then calculate the md5 hash value.
If the two are in agreement,
authentication is successful.</para>

<para>The keyfile used for md5 authentication contains an id followed by
a text string whose binary value is used as a hash key:
<informalexample>
<screen>id1=key
id2=key

Example:

lpr@h2=tadf79asd%^1asdf
lpr@h1=fdfa%$^&amp;^%$
</screen>
</informalexample>
</para>


<sect2><title>Printcap Entries</title>

<para>Options used:
<itemizedlist>

<listitem>
<para><literal>auth=md5</literal><emphasis>use MD5 authentication</emphasis></para>
</listitem>

<listitem>
<para><literal>auth_forward=md5</literal><emphasis>forward using MD5 authentication</emphasis></para>
</listitem>

<listitem>
<para><literal>md5_id=</literal><emphasis>id for server</emphasis></para>
</listitem>

<listitem>
<para><literal>md5_forward_id=</literal><emphasis>id for server</emphasis></para>
</listitem>

<listitem>
<para><literal>md5_server_keyfile=</literal><emphasis>server keyfile</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>Example printcap entry:
<informalexample>
<screen>pr:
    :lp=pr@wayoff
    :auth=md5
    :md5_id=lpr@wayoff.com
pr:server
    :auth_forward=md5
    :md5_id=lpr@wayoff.com
    :md5_server_keyfile
    :md5_forward_id=lpr@faroff.com</screen>
</informalexample>
</para>

<para>The <literal>md5_id</literal> value is used by the client to obtain
a hash key that is used to salt the md5 calculation for client to server
transfers.
The <literal>md5_forward_id</literal> value is used by the server to obtain
a hash key that is used to salt the md5 calculation for server to server transfers.</para>

<para>The <literal>md5_server_keyfile</literal> contains the keys of users;
the id sent as the connection information is used to obtain the key from the file.</para>

<para>To set up md5 authentication,
all that is needed is the following.
<itemizedlist>

<listitem>
<para>For each user generate a key and place it in the server keyfile.
This file should have the form:
<informalexample>
<screen>user1@host1=asdfasdfadf
user2@host2=a8789087asddasdf</screen>
</informalexample>
</para>
</listitem>

<listitem>
<para>Assign a key to the server, and set its printcap entry to this key.
<informalexample>
<screen>pr:
    :lp=pr@wayoff
    :auth=md5
    :md5_id=lpr@wayoff.com</screen>
</informalexample>
</para>
</listitem>

<listitem>
<para>For each user, create a user key file with the following format:
<informalexample>
<screen>lpr@wayoff = user1@host1 asdfasdfadf</screen>
</informalexample>

The first entry corresponds to the <literal>md5_id</literal> value in the printcap.
The second field is the <acronym>AUTHUSER</acronym> value supplied to the server
and which will be used to look up the key in the servers key file.
Finally,
the last field is the salt value for the md5 calculation.</para>
</listitem>

</itemizedlist>
</para>

</sect2>

<sect2><title>User Environment Variables and Files</title>

<para>Options used:
<itemizedlist>

<listitem>
<para><literal>MD5KEYFILE=5</literal><emphasis>location of user keyfile</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>The
<literal>MD5KEYFILE</literal>
environment variable contains the path to the
user keytab file.</para>

</sect2>
</sect1>

<sect1><title>Adding Authentication Support</title>

<para>Additional types of authentication support can be added very easily to
&LPRng; by using the following conventions and guidelines.</para>

<para>First,
the authentication method can be connection based or transfer based.
Connection based authentication involves the &LPRng; client or server
opening a connection to the remote server,
having the authentication protocol provide authentication information,
and then having no further interaction with the system.
This is the easiest to implement and understand method.
Code needs to be provided to do a simple authentication exchange
between the two ends of the connection,
after which no other action needs to be taken.</para>

<para>Transfer based authentication is more complex,
but allows encrypted transfers of information between the two systems.
A connection is established between client and server (or server and server),
and an initial protocol exchange is performed.
Then the authentication module transfers the command or job information
to the destination,
where is it unpacked and/or decrypted.
The internal <application/lpd/ server facilities are then invoked by the authentication
module,
which also provides a destination for any error message or information
destined for the client.
The authentication module will encrypt or encode this information and then
send it to the client program.
This type of authentication is more complex,
but provides a higher degree of security and reliability than the
simple connection based system.</para>


<sect2><title>Printcap Support</title>

<para>By convention,
printcap entries
<literal>auth=XXX</literal>
and
<literal>auth_forward=XXX</literal>
specifies that authentication protocol <acronym>XXX</acronym>
is to be used for client to server
and for server to server transfers respectively.</para>

<para>Similarly,
the server receiving an authentication request must have a
<literal>XXX_id=name</literal> entry in the printcap or configuration information.
This allows several different authentication protocols to be accepted
by a server.</para>

<para>By convention,
printcap and configuration entries of the form
<literal>XXX_key</literal>
contain configuration information for the <acronym>XXX</acronym> authentication protocol.
As part of the authentication support process the <literal>XXX_key</literal> values
are extracted from the printcap and configuration files
and placed in a simple database for the authentication support module.</para>

<para>If you are using a routing filter,
then you can also place
<literal>XXX_key</literal>
information in the routing entry for each file,
and this will be used for sending the job to the specified destination.</para>

</sect2>

<sect2><title>Code Support</title>

<para>The <filename>LPRng/src/common/sendauth.c</filename>
file has the following entries at the end.
<informalexample>
<screen>#define SENDING
#include "user_auth.stub"

struct security SendSecuritySupported[] = {
  /* name,       config_tag, connect,    send,   receive */
  { "kerberos4", "kerberos", Send_krb4_auth, 0, 0 },
  { "kerberos*", "kerberos", 0,           Krb5_send },
  { "pgp",       "pgp",      0,           Pgp_send },
#if defined(USER_SEND)
 USER_SEND
#endif
  {0}
};</screen>
</informalexample>

This is an example of how to add user level authentication support.
The <filename>user_auth.stub</filename>
file contains the source code for the various modules authentication
modules.
You can replace this file with your own version
if desired.
The following fields are used.
<variablelist>
<varlistentry><term> name </term>
<listitem>
<para>The authentication name.
The <literal>auth=XXX</literal> printcap or configuration value will cause the
<literal>name</literal>
fields to be searched using a
glob match.</para>

</listitem>
</varlistentry>

<varlistentry><term> config_tag</term>

<listitem>
<para>When a match is found,
the <literal>config_tag</literal> value is used to search
the printcap and configuration entries for information.
If the <literal>config_tag</literal> field has value <acronym>XXX</acronym>,
then entries with keys <literal>XXX_key</literal> will be extracted for use
by the authentication code.</para>

</listitem>
</varlistentry>

<varlistentry><term>connect</term>

<listitem>
<para>Routine to call to support
<literal>connection</literal>
level authentication.
This routine is responsible for connection establishment and
protocol handshake.
If the value is 0,
then the
<literal>send</literal>
field value will be used.</para>

</listitem>
</varlistentry>

<varlistentry><term>send</term>

<listitem>
<para>Routine to call to support
<literal>transfer</literal>
level authentication.
The
<literal>send</literal>
routine is provided a file and a connection to the remote server,
and is responsible for the transferring files.</para>

</listitem>
</varlistentry>
</variablelist>
</para>

<para>The
<filename>LPRng/src/common/lpd_secure.c</filename>
file has the following information at the end:
<informalexample>
<screen>#define RECEIVE 1
#include "user_auth.stub"

 struct security ReceiveSecuritySupported[] = {
    /* name, config_tag, connect, send, receive */
#if defined(HAVE_KRB_H) &amp;&amp; defined(MIT_KERBEROS4)
    { "kerberos4", "kerberos",  0, 0, 0 },
#endif
#if defined(HAVE_KRB5_H)
    { "kerberos*", "kerberos",   0, 0, Krb5_receive },
#endif
    { "pgp",       "pgp",   0, 0, Pgp_receive, },
#if defined(USER_RECEIVE)
/* this should have the form of the entries above */
 USER_RECEIVE
#endif
    {0}
};</screen>
</informalexample>
</para>

<para>This information matches the same information in the <filename>sendauth.c</filename>
file.
When the authentication request arrives at the server,
the
<literal>name</literal>
field values are searched for a match,
and then the <literal>config_tag</literal> value is used to get extract configuration
information from the database for the protocol.</para>

<para>The
<literal>receive</literal>
routine is then called and is expected to handle the remaining
steps of the authentication protocol.
If the routine exits with a 0 value then the lpd server expects
<literal>connection</literal>
level authentication has been done and proceeds to
simply transfer information using the standard RFC1179 protocol steps.
A non-zero return value indicates an error and an error is reported
to the other end of the connection.</para>

<para>If the
<literal>receive</literal>
module is to perform
<literal>transfer</literal>
level authentication,
then the module carries out the necessary steps to transfer the command and/or
job information.
It then calls the necessary internal &LPRng; routine to implement the desired
services.
After finishing the requested work,
these routines return to the calling authentication module,
which then will transfer data, close the connection to the
remote system,
and return to the calling system.
The combination of 0 return value and closed connection
indicates successful transfer level authentication to the server.</para>

<para>The <filename>user_auth.stub</filename> file contains the following code that sets the
<literal>USER_SEND</literal> variable:
<informalexample>
<screen>#if defined(SENDING)
extern int md5_send();
#  define USER_SEND \
  { "md5", "md5", md5_send, 0, md5_receive },
#endif</screen>
</informalexample>
</para>

<para>If the <acronym>SENDING</acronym> value has been defined,
this causes the prototype for <function>md5_send()</function> to be place in the file
and the <literal>USER_SEND</literal> value to be defined.
This will cause the
<literal>md5</literal>
authentication information to be placed in the
correct table.</para>

</sect2>

<sect2><title>Connection and Transfer Authentication</title>

<para>Rather than go into a detailed description of the code,
the <filename>user_auth.stub</filename> file contains extremely detailed examples
as well as several working versions of authentication information.
It is recommended that the user start with one of these and then
modify it to suit themselves.</para>

</sect2>
</sect1>
</chapter>

<chapter id=accountingref><title>Accounting </title>

<para>In Academic institutions, avoiding printing accounting has been
regarded as a challenge,  an ongoing game of fat cat and poor starving
mouse, between the brutal and corrupt Administration and the poor, downtrodden, over-charged
student.
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...</para>

<para>
There are two approaches to printing accounting:
use software determine the number of pages that should be printed
or use a hardware pagecounter facility on the printer to accurately
determine the number of pages used.
While the software method works well in a relatively error
and security compromise free environment
and where print jobs do not jam,
for accurate account a hardware level pagecounter or some other
method must be used.
LPRng provides facilities to use either method to determine page counts.
</para>

<para>
There are also two methods available to store the actual accounting information:
the simple <emphasis/save to file/ method and the more complex
<emphasis/save to program/ method.
LPRng provides support for both methods.
</para>

<sect1 id="accountingserver"><title>Accounting Printcap Options </title>

<para>The accounting facilities are controlled and enabled by the following
entries in the printcap file.
</para>

<informaltable frame=all>
<tgroup cols=3 align=left rowsep=1 colsep=1>
<thead>
<row><entry>Tag</entry><entry>Default Value</entry><entry>Purpose</entry></row>
</thead>
<tbody>
<row><entry>af</entry><entry>NULL</entry><entry>accounting file name</entry></row>
<row><entry>max_accounting_file_size</entry><entry>100</entry><entry>max accounting file size (in Kbytes)</entry></row>
<row><entry>min_accounting_file_size</entry><entry>10</entry><entry>min accounting file size (in Kbytes)</entry></row>
<row><entry>as</entry><entry>"jobstart $H $n $P $k $b $t"</entry><entry>accounting info for job start</entry></row>
<row><entry>ae</entry><entry>"jobend $H $n $P $k $b $t"</entry><entry>accounting info for job end</entry></row>
<row><entry>achk</entry><entry>FALSE</entry></row>
<row><entry>la</entry><entry>TRUE</entry><entry>do accounting for 'local' printer</entry></row>
<row><entry>ar</entry><entry>FALSE</entry><entry>do accounting for 'remote' transfers</entry></row>
</tbody>
</tgroup>
</informaltable>

</sect1>

<sect1><title>Accounting Information</title>

<para>
The <literal/:as=.../ and <literal/:ae=.../ options specify the
the start of job and end of job accounting information
or a set of programs to be run to record the start and end of job
accounting information. 
The option values are expanded using the same methods as for the
filter options.
For example:

<informalexample>
<screen>:as=jobstart $H $n $P $k $b $t
  jobstart '-Hh4.private' '-nroot' '-Pps' '-kcfA938h4.private' \
    '-b1093' '-tNov  5 19:39:25'
:ae=jobend $H $n $P $k $b $t
  jobend '-Hh4.private' '-nroot' '-Pps' '-kcfA938h4.private' \
    '-b1093' '-tNov  5 19:39:59'</screen>
</informalexample>
</para>

<para>
If the options have the form <literal>:as=|/path ...</literal>,
then the specified program is run to record the information.
By convention the accounting information is passed as command line
values to the program.
The programs are run in the same manner as a print filter.
When the <literal/:as/ or <literal/:ae/ value specifies
a program then logging using the <literal/:af/ option
values in the next section is not performed.
For example:
</para>

<informalexample>
<screen>:as=|/usr/local/libexec/jobstart $H $n $P $k $b $t
  /usr/local/libexec/jobstart '-Hh4.private' '-nroot' \
  '-Pps' '-kcfA938h4.private' '-b1093' '-tNov  5 19:39:25'
:ae=jobend $H $n $P $k $b $t
  jobend '-Hh4.private' '-nroot' '-Pps' '-kcfA938h4.private' \
    '-b1093' '-tNov  5 19:39:59'</screen>
</informalexample>

</sect1>

<sect1><title>Accounting File</title>

<para>
The Accounting File (<literal/:af=/)
option value specifies the destination of accounting information.
If the format of the <literal/:af/ option is
<literal>:as=| ... </literal>,
then the value is assumed to be a program to be run to record
start and end of job information.
The program is run in the same manner as a print filter.
The values of the <literal/:as/ and <literal/:ae/ options are written
to the program's <literal/STDIN/ and the output from
the program's <literal/STDOUT/
is used as described below for authorization.
</para>

<para>
If the <literal/:af=/ option has the format <literal/host%port/
then a TCP/IP connection is opened to the specified port on the indicated host.
The values of the <literal/:as/ and <literal/:ae/ options are written
to the remote host.
The port that the connection originates from will be in the range
set by the configuration or printcap
<emphasis remap=tt>
<link linkend="lpdport">originate_port</link>
</emphasis>
option.
</para>

<para>
Finally,
if the <literal/:af=/ has neither of these formats then
it will be treated as a pathname to a file.
If the file exists or the <literal/create_files/ option is true,
then the file will be opened and the 
values of the <literal/:as/ and <literal/:ae/ options are written
to the file.
The accounting file should be periodically truncated.

</para>
<para>

By convention
the <literal/:af=/ value is passed to filters as a command line option.
LPRng will pass the option value only if it is specifies a file or network destination.
This implies that
accounting information can be written to the accounting file
or network destinations by the print spooler,
<literal/:of/ filters,
or print file filters.
The filters are responsible for opening the accounting file or network connection.
</para>

<para>
The following is an example of information written to the accounting file:
<informalexample>
<screen>jobstart '-Hh4.private' '-nroot' '-Pps' '-kcfA938h4.private' \
'-b1093' '-tNov  5 19:39:25'
start '-p12942' '-kcfA938h4.private' '-nroot' '-hh4.private' '-Pps' \
'-c0' '-Fo' '-tSun Nov  5 19:39:25 1995'
filestart '-p12944' '-kcfA938h4.private' '-nroot' '-hh4.private' '-Pps' \
'-c0' '-Ff' '-tSun Nov  5 19:39:27 1995'
fileend '-p12944' '-kcfA938h4.private' '-nroot' '-hh4.private' '-Pps' \
'-b3' '-c0' '-Ff' '-tSun Nov  5 19:39:58 1995'
end '-p12942' '-kcfA938h4.private' '-nroot' '-hh4.private' '-Pps' \
'-b2' '-c0' '-Fo' '-tSun Nov  5 19:39:59 1995'
jobend '-Hh4.private' '-nroot' '-Pps' '-kcfA938h4.private' \
'-b1093' '-tNov  5 19:39:59'</screen>
</informalexample>
</para>

<para>The
<literal>jobstart</literal>
and
<literal>jobend</literal>
lines are written by <application/lpd/
and are the expanded
<literal/:as/
and
<literal/:ae/
values.
The
<literal>start</literal>
and
<literal>end</literal>
line are added by the <literal/:of/ filter.
This filter usually queries the printer and gets printer
dependent accounting information such as the pagecounter value.
The <literal/:of/ filter is then suspended and the job is processed
by the various format dependent filters.
The the <literal/filestart/ and <literal/fileend/
lines are produced by the other filters.
</para>

<para id="maxaccount">
The
<literal/max_accounting_file_size/ and
<literal/min_accounting_file_size/
are used by LPRng to control the accounting file size.
When the accounting file size exceeds the
<literal/max_accounting_file_size/ (in Kbytes),
it is truncated to 
<literal/min_accounting_file_size/ (in Kbytes).
If <literal/max_accounting_file_size/ is 0 (zero), then the file is allowed
to grow without limit.
</para>

</sect1>
<sect1><title>Authorization and Quotas</title>

<para>
In addition to simply recording accounting information the
accounting procedures can be used to check print quotas or update
databases.
This is done by using the
Accounting Check <literal/:achk/ flag
and the
<literal/:as/,
<literal/:ae/,
and
<literal/:af/ network connection or program capabilities.
</para>

<para>
If the <literal/:achk/ flag is set and the
<literal/:as=/ option specifies a program to be run,
or the
<literal/:af=/ option specifies a program to be run or a network connection
then output of the program or information read from the
network connection is used to control the handling of the job.
If the
<literal/:as=/ option specifies a program to be run then
the program is run and the exit code and output is saved.
If the <literal/:as=/ option specifies a string and the
<literal/:af=/ option specifies a program to be run or a remote host
to be contacted then the <literal/:as=/ value is written to the
program STDIN or remote host.
The program STDOUT or network connection is read and saved and the
program exit code is saved.
</para>
<para>
If the information was read from a program,
then the exit code of the program is checked:
<informalexample><screen>
Exit Code    Action
JSUCC (0)    process data read
JFAIL        retry with JFAIL status
JHOLD        hold job
JREMOVE      remove job
JABORT       abort processing jobs
other        abort processing jobs
</screen></informalexample>
</para>

<para>
If the information was read from a network connection or the
program exited with JSUCC (0) then the
start of the first line of the information read is used.
If this line starts with the following case insensitive words
then the following actions are taken: 
<informalexample><screen>
Word         Action
(blank)      process job
ACCEPT       process job
FAIL         retry with JFAIL status
HOLD         hold job
REMOVE       remove job
(other)      abort processing jobs
</screen></informalexample>
</para>

<para>
These facilities can be used to implement a wide variety of quota
mechanisms.
The most simple method is to create a script or program that can be
run as the <literal/:as=/ program.
This would connect to a database server or check a database to see if
user quotas had been exceeded. 
If they had,  then it would return a <literal/REMOVE/ or <literal/HOLD/
status as appropriate.
</para>

<sect1><title>Accessing Printer Hardware Pagecounters</title>

<para>The following is from Hewlett-Packard documentation,
<ulink URL="http://www.hp.com/cposupport/printers/support_doc/bpl02119.html">
http://www.hp.com/cposupport/printers/support_doc/bpl02119.html</ulink></para>

<para>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.
<informalexample>
<screen>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</screen>
</informalexample>
</para>

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

<para>Example</para>

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

<para>
The HP LaserJet 4L/5L/6L
printers differ from that of the other printers in that they do not have any NVRAM
available for storing page count values.
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.
</para>

</sect1>

<sect1><title>Reliable Accounting</title>
<para>
In order to do reliable accounting,
the printer must be queueried for the current value of the pagecounter
at the start and end of jobs and this information stored in the accounting file.
The <application remap=tt>ifhp</application>
filter can be configured to obtain the pagecounter values
and to record them at the start and end of each part of a print job.
We can use the <literal/:of/ filter to read the pagecounter value
at the start and end of a job,
and have the other file files record information as well.
The <literal/ifhp/ filter will record the pagecounter information
with the <literal/-p/ option.
We need simply take the difference of the starting and ending pagecounter
values to find the number of pages used by a job.
<informalexample>
<screen>
Example:
 lpd generates:  
  jobstart  - from the lpd.conf 'as=' option
  jobend    - from the lpd.conf 'ae=' option
    -H   - host name
    -n   - user name
    -P   - printer
    -k   - control file name
    -b   - byte count of job/file
    -t   - current printing time

 ifhp filter generates:
   start/end         - of filter, for entire job
   filestart/fileend - if or other filter, for each file
   (options above are same)
    -A  - identifier information
    -q  - process id of filter
    -p  - current value of page counter, 0 indicates no
          page counter on printer or it is not readable

jobstart '-Hh110.private' '-nroot' '-Plp' \
   '-kcfA129h110.private' '-b48780' '-t2001-10-19-09:36:36.000'
                           ^^^ bytes in file

start '-q26130' '-p105340' '-t2001-10-19-09:36:38.330' \
                 ^^^^^^^  starting page counter value for job
    '-Aroot@h110+129' '-nroot' '-Plp' 
filestart '-q26132' '-p105340' '-t2001-10-19-09:36:38.350' \
                      ^^^^^^^  starting page counter value for file
    '-Aroot@h110+129' '-nroot' '-Plp' 
fileend '-b19' '-T435' '-q26132' '-p105359' '-t2001-10-19-09:43:51.504'
                                  ^^^^^^^  ending page countvalue for file
         ^^^ number of pages printed for this file
    '-Aroot@h110+129' '-nroot' '-Plp'
end '-b19' '-T435' '-q26130' '-p105359' '-t2001-10-19-09:43:51.504'
                                  ^^^^^^^  ending page countvalue for job
     ^^^ number of pages printed for this job
    '-Aroot@h110+129' '-nroot' '-Plp'
jobend '-Hh110.private' '-nroot' '-Plp' \
    '-kcfA129h110.private' '-b48780' '-t2001-10-19-09:43:51.000'
                           ^^^ bytes in file
</screen>

</informalexample>
</para>

<para>If for some reason the job is
killed or terminates due to error conditions,
the <literal/:of/ filter may not
get to record the ending value for the job.
This can lead to accounting files with the following entries:
<informalexample>
<screen>start '-p100' '-q20005' '-Fo' '-kcfA100taco' '-uuser' '-hhost' '-R...
filestart '-p101' '-q20005' '-Ff' '-kcfA100taco' '-uuser' '-hhost' '-R...
start '-p110' '-q20005' '-Fo' '-kcfA101taco' '-uuser' '-hhost' '-R...
filestart '-p112' '-q20010' '-Fo' '-kcfA101taco' '-uuser' '-hhost' '-R...
end '-p112' '-q20010' '-Fo' '-kcfA101taco' '-uuser' '-hhost' '-R...</screen>
</informalexample>
</para>

<para>
The missing <literal/end/ is a clear indication that the user's job has been terminated.
We simply use the pagecounter value determined at the start of the next job
to find the numbers of pages used for this job.
</para>

</sect1>

<sect1><title>&LPRng; accounting.pl Utility</title>

<para>The &LPRng;
<filename>accounting.pl</filename>
utility provides the basic framework for using the
<literal/:as=|/,
<literal/:ae=|/,
and pagecounter information written to the accounting file
to do reliable accounting,
and may be found in the &LPRng; distribution <literal/UTILS/
directory.
Usually this is modified according to local site needs
and installed in the filter directory.
</para>
<para>
The utility maintains the accounting file by inserting a
<literal/START/ record at the start of a job
and an
<literal/END/ record at the end of the job.
It is assumed that the last <literal/END/ record in the file
marks the last place that accounting was completed.
</para>

<para>The following shows the printcap entry for
using the <literal/accounting.pl/ utility.
The <literal/start/ and <literal/end/
options are used to specify that the utility is being called at
the job start or end.
<informalexample>
<screen>printer
 :af=acct
 :as=|/usr/local/libexec/filters/accounting.pl start
 :ae=|/usr/local/libexec/filters/accounting.pl end</screen>
</informalexample>
</para>

<para>
At the start of each job the utilty writes
a <literal/START/ record into the accounting file.
This record can contain information suitable for use by local site.
The exit code and information written to the utility <acronym/STDOUT/
is used by the <literal/lpd/ server to determine if the job is to be printed.
This allows job quotas to be implemented in a simple way by having
the <literal/accounting.pl/ utility query a database with the user quotas
and reject the job if the user's quota is exceeded.
</para>

<para>
At the end of the job,
the utilitity will read the accounting file and use the recorded information
to update the accounting information.
In order to make this reliable,
the following steps are taken.

<orderedlist>
<listitem><para>
The accounting file is read and scanned for the last
<literal/END/
record.
If there is none,
then the next step starts at the beginning of the accounting file.
</para></listitem><listitem><para>
The file is scanned for <literal/START/ lines and <literal/pagecounter/
information determined at the start of a job.
</para></listitem><listitem><para>
If the last line in the accounting file does not indicate successful
completetion of the job and contain pagecounting information,
then the accounting procedure is abandoned until the next job
completes successfully.
</para></listitem><listitem><para>
If the last line in the accounting file indicates successful
completion,
then its pagecounter value is used as the last page counter value.
</para></listitem><listitem><para>
Job information is updated by finding the start and end pagecounter
values for each job.
It is possible that a job will not have a pagecounter value recorded
at its start;  in this case the page usage will be 0,
as it did not even get initialized.
</para></listitem><listitem><para>
After determining the accounting information,
the procedure will then update and databases and the accounting
file.
During this update,
interrupts should be disabled and the amount of time taken to update
the accounting information and/or file should be minimized.
</para></listitem>
</orderedlist>
</para>

<para>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 <acronym>END</acronym> line is written to the
accounting file,
the local database or remote accounting server can be updated.</para>

</sect1>
</chapter>

<chapter id=rfc1179ref><title>RFC 1179 - Line Printer Daemon Protocol </title>

<para>RFC1179 can be obtained from the &LPRng; distribution, in the LPRng_DOC/rfc1179 directory,
or from one of many sites which mirror the RFCs.</para>

<para>This RFC is an <emphasis>informational</emphasis>
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 <application/lpd/ print server,
and left out many details dealing with error recover,
error messages,
extensions to the protocol,
etc.</para>

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


<sect1 id="lpdport"><title>Ports and Connections </title>

<para>Options used:
<itemizedlist>

<listitem>
<para> <literal>lpd_port=</literal><emphasis>Port for <application/lpd/ connections</emphasis></para>
</listitem>

<listitem>
<para> <literal>lpd_listen_port=</literal><emphasis>Port for <application/lpd/ to accept connection</emphasis></para>
</listitem>

<listitem>
<para> <literal>originate_port=</literal><emphasis>Ports to originate connections on</emphasis></para>
</listitem>

<listitem>
<para> <literal>reuse_addr</literal> FLAG <emphasis>Set SO_REUSEADDR flag on connection</emphasis></para>
</listitem>

<listitem>
<para> <literal>retry_econnrefused</literal> FLAG <emphasis>Retry on connect ECONNREFUSED error</emphasis></para>
</listitem>

<listitem>
<para> <literal>retry_nolink</literal> FLAG <emphasis>Retry on device open or connection ffailure</emphasis></para>
</listitem>

<listitem>
<para> <literal>unix_socket_path</literal> PATH <emphasis>UNIX FIFO pathname for local connections</emphasis></para>
</listitem>

<listitem>
<para>
<literal>socket_linger=</literal><emphasis>socket linger timeout</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>RFC1179 requires that the <application>lpd</application> server listen for TCP/IP connections
on port 515.
This port is registered with the Internet Naming Authority,
and the <filename>/etc/services</filename> file or TCP/IP services database usually has an entry:
<informalexample>
<screen>printer     515/tcp     spooler     # line printer spooler</screen>
</informalexample>
</para>

<para>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 <emphasis>reserved</emphasis>
and <emphasis>privileged</emphasis>
ports.
By convention,
ports in the range 1-1023 can only <emphasis remap=bf>bound</emphasis>
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.</para>

<para>In a UNIX environment,  this means that the user programs
<application>lpr</application>,
<application>lpq</application>,
<application>lprm</application>,
and
<application>lpc</application>
would have to be SETUID root.</para>

<para>As experience has shown, for security purposes,
the fewer programs that need to have privileged status,
the better.
&LPRng; uses the
<literal>lpd_port=printer</literal> configuration option to set the port for the
connections to a <application/lpd/ server.
By default, this is port 515, but can be set to other values.
This port value is used to make connections to a remote <application/lpd/ server.
The
<literal>lpd_listen_port=printer</literal> configuration option can be used to specify
a port for the <application/lpd/ to listen for incoming requests.
If no <literal>lpd_listen_port</literal> value is specified the
<literal>lpd_port</literal> value will be used as the <application/lpd/ listening port.
</para>

<para>
The <literal/unix_socket_path/ option specifies the pathname of a UNIX FIFO or
socket that can be used for connections the <application/lpd/ server if the
client and server are on the same host.
The  use of a local FIFO restricts connections from outside hosts.
The UNIX FIFO path should be to a node in a directory that is writable by
by the <application/lpd/ server and not other non-privileged processes.
<para>

<para>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 <emphasis>stale</emphasis>
packets.
When a connection between
<literal>sourcehost, sourceport</literal>
and
<literal>desthost, destport</literal>
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
<literal>sourcehost, sourceport</literal>
and
<literal>desthost, destport</literal>
for a period long
enough for all <emphasis>stale</emphasis>
packets to be removed from the system.
This is approximately 10 minutes long.</para>

<para>In order to simplify assignments of ports,
timing out connections, and other matters,
many TCP/IP packages do keep track of explicit connections
<emphasis>originating</emphasis>
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 <emphasis remap=bf>to</emphasis>
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.</para>

<para>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:<!-- <br> -->
<literal>host 1, port 721</literal>
and
<literal>host 2, port 515</literal><!-- <br> -->
<literal>host 1, port 722</literal>
and
<literal>host 2, port 515</literal><!-- <br> -->
<literal>host 1, port 723</literal>
and
<literal>host 2, port 515</literal><!-- <br> -->
<literal>host 1, port 724</literal>
and
<literal>host 2, port 515</literal><!-- <br> -->
<literal>host 1, port 725</literal>
and
<literal>host 2, port 515</literal><!-- <br> -->
<literal>host 1, port 726</literal>
and
<literal>host 2, port 515</literal><!-- <br> -->
<literal>host 1, port 727</literal>
and
<literal>host 2, port 515</literal><!-- <br> -->
<literal>host 1, port 728</literal>
and
<literal>host 2, port 515</literal><!-- <br> -->
<literal>host 1, port 729</literal>
and
<literal>host 2, port 515</literal><!-- <br> -->
<literal>host 1, port 730</literal>
and
<literal>host 2, port 515</literal><!-- <br> -->
<literal>host 1, port 731</literal>
and
<literal>host 2, port 515</literal></para>

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

<para>The &LPRng; code has tried to provide several methods to deal with
these problems.
Firstly,
the
<literal>originate_port=512 1023</literal>
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
<literal>originate_port=721 731</literal>,
but it turns out that most BSD <application/lpd/ based implementations only
check for a <emphasis>reserved</emphasis>
originating port.
By using 512 ports we get a greatly reduced rate of errors due
to lack of ports due to pending timeouts.</para>

<para>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
<function>getsockopt()</function>
and
<function>setsockopt()</function>
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
<function>setsockopt()</function>
facility can be used to set the
<literal>SO_REUSEADDR</literal>
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.</para>

<para>It would appear that by setting
<literal>SO_REUSEADDR</literal>
on the originating end that we have solved our problems.
However,
unless the destination end of the connection sets its
<literal>SO_REUSEADDR</literal>
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.</para>

<para>The
<literal>reuse_addr</literal>
flag (default off) forces
the &LPRng; software to set the
<literal>SO_REUSEADDR</literal>
flag on originating connections.
As indicated,
this will allow ports to be reused immediately for outgoing connections,
rather than waiting for a timeout.</para>

<para>While the
<literal>reuse_addr</literal>
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
<function>connect()</function> 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.</para>

<para>The <literal>retry_econnrefused</literal> (default on) flag is used to
specify that we retry connections in this manner.
When this is set,
a
<literal>connection refused</literal>
error causes the connection to be retried using a new port.
This will be repeated until all available ports have been tried.</para>

<para>When
printing a job and the <application>lpd</application> server connection to a remote
site or device open fails,
the <literal>retry_nolink</literal> (default on)
will cause the attempt to be retried indefinitely.
The combination of <literal>retry_econnrefused</literal> and <literal>retry_nolink</literal>
will provide robust connection attempts to remote systems.</para>

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

<para>The <function>setsockopt()</function> 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 <literal>socket_linger</literal> period.
If <literal>socket_linger</literal> is 0,
then no SO_LINGER operation is done.</para>

<para>In summary, if you experience problems with connection failures due
to port exhaustion,
first try setting the
<literal>reuse_port</literal> flag,
and you should see a reduction.
Check to ensure that the <literal>retry_econnrefused</literal>
and <literal>retry_nolink</literal> 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 <literal>connect_retry</literal>
interval, say <literal>connect_retry=120</literal> (2 minutes).</para>

</sect1>

<sect1 id="remotesupport"><title>Protocol Requests and Replies </title>

<para>Options used:
<itemizedlist>

<listitem>
<para> <literal>remote_support=</literal><emphasis>Remote operations supported</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>After a connection has been established,
a request can be sent to the <application>lpd</application>
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.
</para>

<table frame=all id=rf1179commands><title>RFC1179 Commands</title>
<tgroup cols=4 align=left rowsep=1 colsep=1>
<thead>
<row><entry>NNN</entry><entry>RFC1179</entry><entry>Operation</entry><entry>program</entry></row>
</thead>
<tbody>
<row><entry>1</entry><entry>yes</entry><entry>start print</entry><entry><application>lpc</application></entry></row>
<row><entry>2</entry><entry>yes</entry><entry>transfer a printer job</entry><entry><application>lpr</application></entry></row>
<row><entry>3</entry><entry>yes</entry><entry>print short form of queue status</entry><entry><application>lpr</application></entry></row>
<row><entry>4</entry><entry>yes</entry><entry>print long form of queue status</entry><entry><application>lpr</application></entry></row>
<row><entry>5</entry><entry>yes</entry><entry>remove jobs</entry><entry><application>lprm</application></entry></row>
<row><entry>6</entry><entry>&LPRng;</entry><entry>do control operation</entry><entry><application>lpc</application></entry></row>
<row><entry>7</entry><entry>&LPRng;</entry><entry>transfer a block format print job</entry><entry><application>lpr</application></entry></row>
<row><entry>8</entry><entry>&LPRng;</entry><entry>secure command transfer</entry><entry><application>lpc</application></entry></row>
<row><entry>9</entry><entry>&LPRng;</entry><entry>verbose status information</entry><entry><application>lpr</application></entry></row>
</tbody>
</tgroup>
</table>

<para>After the request has been sent,
then a reply will be returned.
In general the reply has the following form:
<informalexample>
<screen>\000\n    Success
\NNN\n    Failure (NNN is error code)
text\n    Text or status information</screen>
</informalexample>
</para>

<para>As can be seen,
this protocol is extremely simple,
but there are a set of problems due to the loosely written language of RFC1179.
<orderedlist>

<listitem>
<para>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.</para>
</listitem>

<listitem>
<para>The original RFC1179 protocol did not provide any way to do remote control
of queues or <application/lpd/ servers.
This has been added to the protocol.
As a side effect,
if you try to use
<application>lpc</application> to control a non-&LPRng; printer,
it will not work.</para>
</listitem>

<listitem>
<para>You can specify that a network printer is non-&LPRng; by using the
<literal>remote_support=RQVMC</literal> option and specify the operations
supported by the printer.
The letters R, Q, M, and C stand for
<application>lpr</application>,
<application>lpq</application>,
<application>lprm</application>,
and <application>lpc</application> operations respectively,
and indicate that these are supported.
If <literal>remote_support</literal> does not allow a particular operation,
then the &LPRng; software will not send a corresponding request to the printer.
For example,
<literal>remote_support=R</literal>
would restrict operations to spooling jobs only,
and the &LPRng; software would not query the printer for status.</para>
</listitem>

</orderedlist>
</para>

</sect1>

<sect1 id="jobtransfer"><title>Job Transfer </title>

<para>Options used:
<itemizedlist>

<listitem>
<para>
<literal>longnumber</literal> FLAG <emphasis>Long job number (6 digits)</emphasis></para>
</listitem>

<listitem>
<para> <literal>send_data_first</literal> FLAG <emphasis>Send data files first</emphasis></para>
</listitem>

<listitem>
<para> <literal>use_shorthost</literal><emphasis>Use short hostname</emphasis></para>
</listitem>

</itemizedlist>
</para>

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

<para>A file transfer request has the form:
<informaltable frame=all>
<tgroup cols=2 align=left rowsep=1 colsep=1>
<thead>
<row><entry>Command</entry><entry>Purpose</entry></row>
</thead>
<tbody>
<row><entry>\001\n</entry><entry>abort</entry></row>
<row><entry>\002nnnn cfname</entry><entry>control file transfer</entry></row>
<row><entry>\003nnnn dfname</entry><entry>data file transfer</entry></row>
</tbody>
</tgroup>
</informaltable>
</para>

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

<para>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:
<informaltable frame=all>
<tgroup cols=2 align=left rowsep=1 colsep=1>
<thead>
<row><entry>Status</entry><entry>Purpose</entry></row>
</thead>
<tbody>
<row><entry>\000</entry><entry>Accepted, proceed</entry></row>
<row><entry>\nnn</entry><entry>Rejected with error code</entry></row>
</tbody>
</tgroup>
</informaltable>
</para>

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

<para>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 <application/lpd/ based systems:
<informaltable frame=all>
<tgroup cols=2 align=left rowsep=1 colsep=1>
<thead>
<row><entry>Code</entry><entry>Error</entry></row>
</thead>
<tbody>
<row><entry>\000</entry><entry>Accepted, proceed</entry></row>
<row><entry>\001</entry><entry>Queue not accepting jobs</entry></row>
<row><entry>\002</entry><entry>Queue temporarily full, retry later</entry></row>
<row><entry>\003</entry><entry>Bad job format, do not retry</entry></row>
</tbody>
</tgroup>
</informaltable>
</para>

<para>When the sender gets the reply indicating success,
it sends the
<literal>nnnn</literal>
bytes of the control or data file,
followed by a
<literal>\000</literal>
octet.
The receiver will then reply as above;
a single
<literal>\000</literal>
octet indicating success.</para>

<para>The above procedure is carried out until all data files and the control
file of a job are transferred.</para>

<para>RFC1179 is silent on the following issues:
<orderedlist>

<listitem>
<para>When sending a job,
do you send the control file first, followed by the data file(s),
or the data files first?</para>
</listitem>

<listitem>
<para>When sending multiple jobs,
can you send them on a single connection,
or do you have to establish a new connection for each job?</para>
</listitem>

</orderedlist>
</para>

<para>&LPRng; will <emphasis>accept</emphasis>
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 <literal>send_data_first</literal> printcap option can be used to force
data files to be sent first.</para>

<para>RFC1179 states that:
<blockquote>
<para>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.</para>
</blockquote>
</para>

<para>The <emphasis>should</emphasis>
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:
<orderedlist>

<listitem>
<para> 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
<literal>longnumber</literal>
option will allow &LPRng; to use a 6 digit
job number for files in the print queue.</para>
</listitem>

<listitem>
<para>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 <literal>use_shorthost</literal> option will force it to
use short host names in control and data files.</para>
</listitem>

<listitem>
<para>The
<literal>cfA</literal>
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.
<literal>cfA</literal>) and
but can range to Z (highest, i.e.
<literal>cfZ</literal>).
All known spoolers except &LPRng; seem to ignore the actual value of
the letter.</para>
</listitem>

</orderedlist>
</para>

</sect1>

<sect1><title>Data File Transfer</title>

<para>As mentioned before
a data file is transferred using the command below.
<informaltable frame=all>
<tgroup cols=2 align=left rowsep=1 colsep=1>
<thead>
<row><entry>Command</entry><entry>Purpose</entry></row>
</thead>
<tbody>
<row><entry>\003nnnn dfname</entry><entry>data file transfer</entry></row>
</tbody>
</tgroup>
</informaltable>
</para>

<para>From RFC1179:
<blockquote>
<para>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.</para>
</blockquote>
</para>

<para>There are several surprises in RFC1179.
<orderedlist>

<listitem>
<para>Apparently a job should only consist of a single data file.
This is a severe limitation,  and in fact the BSD <application/lpr/ and other
print spoolers process jobs with multiple data files.
By convention, these data files have names of the form
<literal>dfA</literal>,
<literal>dfB</literal>,
...
<literal>dfZ</literal>,
<literal>dfa</literal>,
<literal>dfz</literal>.</para>
</listitem>

<listitem>
<para>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.</para>
</listitem>

<listitem>
<para>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.</para>
</listitem>

<listitem>
<para>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.
</para>

<para>When <emphasis/piping/ into the <application>lpr</application> program,
this can be very useful as it eliminates the need to create temporary
files on the local host.
Note that some print spoolers do not use this interpretation,
and this option should be used carefully.</para>
</listitem>

</orderedlist>
</para>

</sect1>

<sect1><title>Control File Contents</title>

<para>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:
<informalexample>
<screen>Hh4.private
J(stdin)
CA
Lpapowell
Apapowell@h4+955
Ppapowell
fdfA955h4.private
N(stdin)
UdfA955h4.private</screen>
</informalexample>
</para>

<para>The following are the letters and their meanings in the control file.
</para>
<table frame=all id=cflines><title>Control File Lines and Purpose</title>
<tgroup cols=3 align=left rowsep=1 colsep=1>
<thead>
<row><entry>Letter</entry><entry>Defined</entry><entry>Purpose</entry></row>
</thead>
<tbody>
<row><entry>A</entry><entry>&LPRng;</entry><entry>Identifier for job</entry></row>
<row><entry>C</entry><entry>RFC1179</entry><entry>Class for banner page</entry></row>
<row><entry>H</entry><entry>RFC1179</entry><entry>Host name</entry></row>
<row><entry>I</entry><entry>RFC1179</entry><entry>Indent Printing</entry></row>
<row><entry>J</entry><entry>RFC1179</entry><entry>Job name for banner page</entry></row>
<row><entry>L</entry><entry>RFC1179</entry><entry>Print banner page</entry></row>
<row><entry>M</entry><entry>RFC1179</entry><entry>Mail When Printed</entry></row>
<row><entry>N</entry><entry>RFC1179</entry><entry>Name of source file</entry></row>
<row><entry>P</entry><entry>RFC1179</entry><entry>User identification</entry></row>
<row><entry>Q</entry><entry>&LPRng;</entry><entry>Queue name</entry></row>
<row><entry>R</entry><entry>&LPRng;</entry><entry>Accounting info</entry></row>
<row><entry>S</entry><entry>RFC1179</entry><entry>Symbolic link data</entry></row>
<row><entry>T</entry><entry>RFC1179</entry><entry>Title for pr</entry></row>
<row><entry>U</entry><entry>RFC1179</entry><entry>Unlink data file</entry></row>
<row><entry>W</entry><entry>RFC1179</entry><entry>Width of output</entry></row>
<row><entry>Z</entry><entry>&LPRng;</entry><entry>Filter options</entry></row>
<row><entry>1</entry><entry>RFC1179</entry><entry>troff R font</entry></row>
<row><entry>2</entry><entry>RFC1179</entry><entry>troff I font</entry></row>
<row><entry>3</entry><entry>RFC1179</entry><entry>troff B font</entry></row>
<row><entry>4</entry><entry>RFC1179</entry><entry>troff S font</entry></row>
<row><entry>c</entry><entry>RFC1179</entry><entry>Plot CIF file</entry></row>
<row><entry>d</entry><entry>RFC1179</entry><entry>Print DVI file</entry></row>
<row><entry>f</entry><entry>RFC1179</entry><entry>Print formatted file</entry></row>
<row><entry>g</entry><entry>RFC1179</entry><entry>Plot file</entry></row>
<row><entry>k</entry><entry>RFC1179</entry><entry>Reserved for use by Kerberized &LPRng; clients and servers.</entry></row>
<row><entry>l</entry><entry>RFC1179</entry><entry>Print file leaving control characters</entry></row>
<row><entry>n</entry><entry>RFC1179</entry><entry>Print ditroff output file</entry></row>
<row><entry>o</entry><entry>RFC1179</entry><entry>Print Postscript output file</entry></row>
<row><entry>p</entry><entry>RFC1179</entry><entry>Print file with 'pr' format</entry></row>
<row><entry>t</entry><entry>RFC1179</entry><entry>Print troff output file</entry></row>
<row><entry>v</entry><entry>RFC1179</entry><entry>Print raster file</entry></row>
<row><entry>z</entry><entry>RFC1179</entry><entry>Reserved for future use with the Palladium print system.</entry></row>
</tbody>
</tgroup>
</table>

<para>The
<literal remap=tt>A</literal>
(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: <literal>papowell@h4+955</literal>
is job number 995 submitted by papowell from host h4.</para>

<para>The
<literal remap=tt>C</literal>
(Class)
line is set by the <command>lpr -C class</command> option,
and the value can be used to control printing.
For example,
the
<command remap=tt>lpc class zone </command>
command would restrict job printing to
only jobs with class
<literal>zone</literal>.</para>

<para>The
<literal remap=tt>H</literal>
(hostname),
<literal remap=tt>P</literal>
(username),
and
<literal remap=tt>J</literal>
(jobname)
fields are used to identify the host and user which sent the job,
and to provide information to be displayed by <application>lpq</application>
when reporting job status.</para>

<para>The
<literal remap=tt>L</literal>
(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 absence suppresses banner pages.
The <command>lpr -h</command> option suppresses putting this line into the
control file.
Usually the
<literal remap=tt>L</literal>
field is a duplicate of the
<literal remap=tt>P</literal>
field.</para>

<para>The
<literal remap=tt>M</literal>
(mail information)
field supplies a mail address for &LPRng; to send mail to when
a job is completed.
See

<link linkend="jobcompletion">Job Completion Notification Requested</link>

for more details.</para>

<para>The
<literal remap=tt>N</literal>
(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.</para>

<para>The
<literal remap=tt>I</literal>
(indent)
and
<literal remap=tt>W</literal>
(width)
fields are supposed to specify a page indent and width for printing.
These fields are passed to filters if they are present.</para>

<para>The
<literal remap=tt>Q</literal>
(queue name)
field is an &LPRng; extension,
and contains the name of the print queue the job was originally sent to.
See

<link linkend="qq">qq printcap option</link>
 for details.</para>

<para>The
<literal remap=tt>R</literal>
(accounting info) field was added by &LPRng; to allow
a specified account to be billed for job printing.
The <command>lpr -Rname</command> option can be used to specify the accounting name.</para>

<para>The
<literal remap=tt>S</literal>
(symbolic link)
and
<literal remap=tt>U</literal>
(unlink after printing)
lines were used by the original BSD <application/lpd/ print system to control
how it passed files to the print server.
&LPRng; ignores these lines.
In fact, it will remove
<literal remap=tt>S</literal>
lines and force the
<literal remap=tt>U</literal>
lines to refer only to job data files.
This closes a nasty security loophole on non-&LPRng; print spoolers.</para>

<para>The
<literal remap=tt>T</literal>
(pr job title) is used with the <command>lpr -p</command>
operation to supply a banner to the
<literal>pr</literal>
program.</para>

<para>The
<literal remap=tt>Z</literal>
(filter options) value is specified with
<command>lpr -Zoption</command> and is passed to the data file filters
during the printing operation.
See
<link linkend="filters">Filters</link>
for details on how the
this is used during the printing process.</para>

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

<link linkend="filters">Filters</link>
 for details on how the
data file formats are used during the printing process.</para>

</sect1>

<sect1><title><application>lpq</application> Requests</title>

<para>The RFC1179 protocol specifies that <application>lpq</application> print status
requests can be sent to the <application>lpd</application> server.
The lpq requests have the format:
<informalexample>
<screen>\003printer [id]* \n    short
\004printer [id]* \n    long
\011printer [id]* \n    &LPRng; extension- verbose</screen>
</informalexample>
</para>

<para>The <application>lpd</application> print server will then return queue status
and close the data connection.</para>

<para>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 <application/lpq/ status format has been changed from different versions.</para>

<para>See

<link linkend=lpq><application/lpq/ - Status Monitoring Program</link>
for information on the formats returned.</para>

<para>The
<literal>id</literal>
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
<literal remap=tt>A</literal>,
<literal remap=tt>H</literal>,
or
<literal remap=tt>P</literal>
fields match any of the id values.</para>

<para>Note that since there is no identification of the information requestor,
then restriction of information is almost impossible.</para>

</sect1>

<sect1><title><application>lprm</application> Requests</title>

<para>The RFC1179 protocol specifies that <application>lprm</application> job removal
requests can be sent to the <application>lpd</application> server.
The lpq requests have the format:
<informalexample>
<screen>\005printer user [id]* \n</screen>
</informalexample>
</para>

<para>The <application>lpd</application> print server will search the specified print queue
and remove any job whose ID, hostname, or user name information
from the control file
<literal remap=tt>A</literal>,
<literal remap=tt>H</literal>,
or
<literal remap=tt>P</literal>
fields match any of the id values
and for which the user has permission to perform a removal operation.
See
<link linkend="permsref">Permissions and Authentication</link>
for details.</para>

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

</sect1>

<sect1 id="lpcreread"><title>LPC Requests </title>

<para>&LPRng; has extended the RFC1179 protocol to allow queue and printer control
commands to be sent to the <application/lpd/ server.
The format of these commands are:</para>

<para>
<informalexample>
<screen>\006printer user key [options]</screen>
</informalexample>
</para>

<para>The following commands are supported.
</para>
<table frame=all id=lpccommands><title>LPC Commands</title>
<tgroup cols=2 align=left rowsep=1 colsep=1>
<thead>
<row><entry>Command</entry><entry>Operation</entry></row>
</thead>
<tbody>
<row><entry>Command</entry><entry>Operation</entry></row>
<row><entry><literal> active [printer[@host]]</literal></entry><entry>check to see if server accepting connections</entry></row>
<row><entry><literal> abort   (printer[@host] | all)  </literal></entry><entry>terminate server process printing job</entry></row>
<row><entry><literal> disable (printer[@host] | all)  </literal></entry><entry>disable queueing</entry></row>
<row><entry><literal> debug   (printer[@host] | all) debugparms </literal></entry><entry>set debug level for printer</entry></row>
<row><entry><literal> enable  (printer[@host] | all)  </literal></entry><entry>enable queueing</entry></row>
<row><entry><literal> hold    (printer[@host] | all) (name[@host] | job | all)* </literal></entry><entry>hold job</entry></row>
<row><entry><literal> holdall (printer[@host] | all)  </literal></entry><entry>hold all jobs on</entry></row>
<row><entry><literal> kill    (printer[@host] | all)  </literal></entry><entry>stop and restart server</entry></row>
<row><entry><literal> lpd [printer[@host]]  </literal></entry><entry>get <application/lpd/ PID for server</entry></row>
<row><entry><literal> lpq (printer[@host] | all) (name[@host] | job | all)*     </literal></entry><entry>invoke <application/lpq/</entry></row>
<row><entry><literal> lprm (printer[@host] | all) (name[@host]|host|job| all)*  </literal></entry><entry>invoke <application/lprm/</entry></row>
<row><entry><literal> move printer (user|jobid)* target </literal></entry><entry>move jobs to new queue</entry></row>
<row><entry><literal> noholdall (printer[@host] | all)  </literal></entry><entry>hold all jobs off</entry></row>
<row><entry><literal> printcap (printer[@host] | all) </literal></entry><entry>report printcap values</entry></row>
<row><entry><literal> quit                            </literal></entry><entry>exit LPC</entry></row>
<row><entry><literal> redirect (printer[@host] | all) (printer@host | off )*    </literal></entry><entry>redirect jobs</entry></row>
<row><entry><literal> release  (printer[@host] | all) (name[@host] | job | all)* </literal></entry><entry>release job</entry></row>
<row><entry><literal> reread [printer[@host]]</literal></entry><entry><application/lpd/ reread database information</entry></row>
<row><entry><literal> start   (printer[@host] | all)  </literal></entry><entry>start printing</entry></row>
<row><entry><literal> status  (printer[@host] | all)  </literal></entry><entry>status of printers</entry></row>
<row><entry><literal> stop    (printer[@host] | all)  </literal></entry><entry>stop printing</entry></row>
<row><entry><literal> topq    (printer[@host] | all) (name[@host] | job | all)* </literal></entry><entry>reorder job</entry></row>
<row><entry><literal> defaultq                         </literal></entry><entry>default queue for <application/lpd/ server</entry></row>
<row><entry><literal> local    (printer | all)  </literal></entry><entry>client printcap and configuration information</entry></row>
<row><entry><literal> server    (printer | all)  </literal></entry><entry>server printcap and configuration information</entry></row>
</tbody>
</tgroup>
</table>

<para>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 <application/lpd/ print spooling system as well:
<itemizedlist>

<listitem>
<para><literal> start, stop, enable, disable </literal><!-- <br> -->
 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.</para>
</listitem>

<listitem>
<para><literal> abort, kill </literal><!-- <br> -->
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.</para>
</listitem>

<listitem>
<para><literal> topq </literal>
Places selected jobs at the top of the print queue.</para>
</listitem>

<listitem>
<para><literal> status </literal><!-- <br> -->
Shows a status display of the print spools on the server.</para>
</listitem>

</itemizedlist>
</para>

<para>The following commands are extensions to the basic set provided by the
BSD <application/lpd/ system.
<itemizedlist>

<listitem>
<para><literal> lpq, lprm </literal><!-- <br> -->
Invokes the lpq or lprm program from lpc.
Useful when in the interactive mode.</para>
</listitem>

<listitem>
<para><literal> hold, holdall, release </literal><!-- <br> -->
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.</para>
</listitem>

<listitem>
<para><literal> move, redirect </literal><!-- <br> -->
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.</para>
</listitem>

<listitem>
<para><literal> active, lpd, reread </literal><!-- <br> -->
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 <application>lpd</application> server.
The reread command causes a SIGHUP signal to be sent to the lpd process,
causing it to reread the
<filename>lpd.conf</filename>,
<filename>printcap</filename>,
and
<filename>lpd.perms</filename> files.
This is done when configuration information has
been modified and the administrator wants to have the server use the
new information.</para>
</listitem>

<listitem>
<para><literal> debug </literal><!-- <br> -->
This is a desperation facility for developers that allows dynamic enabling
of debug information generation.
Not normally used in general operation.</para>
</listitem>

<listitem>
<para><literal> local, server  </literal><!-- <br> -->
These commands will print out the configuration information in the
local
<filename>lpd.conf</filename> file,
as well as the printcap information for the specified printers;
<literal>client</literal>
prints what the &LPRng; clients (<literal>lpr, lpq, ...</literal>) would use
while
<literal>server</literal>
prints what the &LPRng; server (<application>lpd</application>) would use if running on this host.
This is an extremely useful diagnostic tool for administrators.
Not normally used in general operation.</para>
</listitem>

</itemizedlist>
</para>

</sect1>

<sect1 id="sendblockformat"><title>Block Job Transfer
</title>

<para>Options used:
<itemizedlist>

<listitem>
<para> <literal>send_block_format</literal> FLAG <emphasis>Transfer job as a block</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>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 <literal>send_block_format</literal> 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.</para>

<para>The transfer command line has the form:
<informalexample>
<screen>\007printer size\n</screen>
</informalexample>
</para>

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

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

</sect1>

<sect1><title>Authenticated Transfer</title>

<para>RFC1179 does not provide any authentication or encryption mechanism
for the transfer of jobs or commands to the <application>lpd</application>
print server.
The Authenticated Transfer operation was added to allow an encrypted
or authenticated transfer of print jobs or commands.</para>

<para>Since there are various restrictions on the incorporation of authentication
facilities into programs,
&LPRng; supports authentication by providing a simple interface to
encryption programs.</para>

<para>The idea is that when authentication is required when sending a job,
&LPRng; will generate a block transfer job as described for the

<link linkend="sendblockformat">Block Job Transfer</link>

operation,
and then invoke a set of programs to encryt and transfer the file,
and encrypt and transfer the returned status.</para>

<para>Similarly,
when sending a command,
the command information will be placed in a file
and the encrypted file will be transferred.</para>

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

<para>See

<link linkend="authref">Authentication and Encryption</link>

for details on the authentication interface.</para>

</sect1>
</chapter>
<chapter id=FAQ><title>The Most Frequently Asked Questions </title>

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

<para>Some of these answers will reference other material in this FAQ,
or the &LPRng; man pages.</para>


<sect1><title>Why do I get malformed from address errors?</title>

<para>This is the number one question asked by most &LPRng; users
who try to use &LPRng; with network printers or other systems
supporting <link linkend="rfc1179">RFC1179</link> printing.
For details about &LPRng; and RFC1179, see
<link linkend="rfc1179ref">RFC1179 and &LPRng;</link>.</para>

<para>The
<literal> malformed from address </literal>
error is usually reported when
trying to send a print job from &LPRng; to other BSD <application/lpr/ or RFC1179
<application/lpr/ implementations, or with network connected printers
that have a built in <application/lpd/ server.
This is due to the following RFC1179 rule:
<blockquote>
<para>Servers originate a connection from ports in the range 721-731.</para>
</blockquote>
</para>

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

<para>IMPLICATION:  in order to do use a reserved port,  the program
must have root privileges.  This means the LPR, <application/lpd/, <application/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.</para>

<para>WHAT TO DO:<!-- <br> -->
When installing &LPRng;  you will need to install the executables
SUID root.
In the <filename>src/Makefile</filename>,  you can remove the comment from the line
<informalexample>
<screen>PERMS=SUID_ROOT_PERMS</screen>
</informalexample>

and then do
<literal> make install</literal>.
This will install the executables
SUID, and owned by root.</para>

</sect1>

<sect1><title>It was working normally, then I get connection refused errors</title>

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

<link linkend="rfc1179ref">RFC1179 and &LPRng;</link>,
but here is a quick explanation.</para>

<para>A TCP/IP connection is usually specified as between
<literal>srchost:srcport, desthost:destport</literal>,
although in practice the order of source (src) and destination
(dest) is not important.</para>

<para>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
<literal>host:port</literal>
that was previously active
for a specified timeout period,
usually 10 minutes.</para>

<para>Some TCP/IP implementations go further:  they will not allow
<acronym>ANY</acronym> connection to be <emphasis remap=bf>originated</emphasis>
(via the <function>bind()</function> system call or API)
from a port that was active,
or accepted on a port that was active for this timeout period.</para>

<para>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
<literal>1.1.1.1:721</literal>
to
<literal>1.1.1.2:515</literal>.
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
<literal>1.1.1.1:721</literal>
to
<literal>1.1.1.2:515</literal>
we get an error such as
"address already in use"
or "connection refused".</para>

<para>Luckily,  we can use port 722 to originate a connection,
and we can connect from
<literal>1.1.1.1:722</literal>
to
<literal>1.1.1.2:515</literal>.
We continue on, until we come to port 731,
and then we need to wait for our timeouts.</para>

<para>SOLUTION:</para>

<para>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
<informalexample>
<screen>originate_port=721 731</screen>
</informalexample>

value in the defaults (<filename>LPRng/src/common/defaults.c</filename>) file or in the <filename>lpd.conf</filename>
file.  I recommend the following:
<informalexample>
<screen>originate_port=512 1022</screen>
</informalexample>

This is, in fact, now the default in &LPRng; software.
If you get the infamous
<literal>malformed from address</literal>
error message from your spooler, then
you will have to set originate_port=721 731,  and live with
a delayed throughput.</para>

</sect1>

<sect1><title>Job is not in print queue, but it gets printed!</title>

<para>In the original BSD <application/lpd/ implementation,
the <application/lpr/ program copied users files to a special spool queue directory,
and then caused the <application/lpd/ server to peek in the directory and print
the files.</para>

<para>This type of operation required spool directory space,
special SETUID programs,
and a slew of headaches in system security and management.</para>

<para>The LPR, <application/lpq/, and other user programs in the &LPRng; suite use TCP/IP
connections and transfer jobs directly to a <application/lpd/ server running on
a remote host,
or even the local host if appropriate.
Note that this type of operation does not require a <application/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.</para>

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

<para>Method 1: Explicit Printer Address</para>

<para>You can force a job to be sent directly to the
<literal> pr </literal>
serviced by the <application/lpd/ server on
<literal>host</literal>
by using the form:
<informalexample>
<screen>lpr -Ppr@host file</screen>
</informalexample>
</para>

<para>You can also set the <acronym>PRINTER</acronym> environment variable to
a similar form, and get the same effect:
<informalexample>
<screen>PRINTER=pr@host; export PRINTER;
lpr file</screen>
</informalexample>
</para>

<para>Method 2: User and Server Printcap Entries</para>

<para>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 <filename>printcap</filename> 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 <application/lpd/ server.
Here are a couple of examples:
<informalexample>
<screen># 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@</screen>
</informalexample>
</para>

<para>As you can see,
the
<literal>server</literal>
keyword indicates that the printcap entry is only for the server.
The <application/lpr/ utility will send the job to the host, while the <application/lpd/ server
will print it on <filename>/dev/lp</filename>.</para>

<para>Note that the <literal>lp=...</literal> information overrides the
<literal remap=tt>:rp:</literal>
(remote printer)
and
<literal remap=tt>:rm:</literal>
(remote machine) fields if they are present.</para>

<para>Method 3: Force sending to server on
<literal>localhost</literal></para>

<para>The
<literal>force_localhost</literal>
printcap or configuration flag forces non-<application/lpd/ applications to send all
requests and print jobs to the server running on the local host.</para>

<para>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 <filename>lpd.conf</filename> file for general
application,  or have a printcap entry of the following form:
<informalexample>
<screen># for all utilities
pr:lp=pr@host:force_localhost</screen>
</informalexample>
</para>

<para>The <application/lpd/ server will ignore the
<literal>force_localhost</literal> flag,
and send jobs to the
<literal>pr</literal>
queue on the
<literal>host</literal>
machine.
However, the LPR, <application/lpq/, etc., utilities will send their requests to the
server running on the local host.</para>

</sect1>

<sect1><title>Job disappears and is never printed, but lpr works</title>

<para>This is a rather disconcerting problem,
and usually occurs when sending jobs to either a network printer or
a nonconforming

<link linkend="rfc1179">RFC1179</link>

print spooler.
For details about &LPRng; and RFC1179, see

<link linkend="rfc1179ref">RFC1179 and &LPRng;</link>,
but here is a quick explanation.</para>

<para>An <application/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.</para>

<para>SOLUTION:</para>

<para>Set the <literal>send_data_first</literal> flag in the printcap for the particular
printer,  or in the <filename>lpd.conf</filename> configuration file.  This is:
<informalexample>
<screen>:send_data_first:  (printcap)
send_data_first    (lpd.conf)</screen>
</informalexample>
</para>

<para>Note that some printers/servers INSIST on the control file first;
You can clear the flag using <literal>send_job_first@</literal> if you need to.</para>

</sect1>

<sect1><title>I get messages about bad control file format</title>

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

<para>&LPRng; quite happily will accept jobs from poor or nonconforming RFC1179
spooler programs,
and fix them up to be conformant.</para>

<para>If you are sending jobs to one of a non-conforming spooler,
you can force &LPRng; to send jobs with only the fields described
in RFC1179 by setting the
the
<literal> :bk: </literal>
(BacKwards compatible) flag in the
printcap for your printer.</para>

</sect1>

<sect1 id="rfc1179"><title>What is RFC 1179, the Line Printer Daemon Protocol?  </title>

<para>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 <application/lpd/ software did its job.</para>

<para>From the RFC Introduction:
<blockquote>
<para>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.</para>
</blockquote>
</para>

<para>Having said this,
the RFC then goes on to describe the protocol used
by a particular implementation of <application/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.</para>

<para>Comment by Patrick Powell
<literal> &lt;papowell@lprng.com&gt; </literal>:
<blockquote>

<para>Since 1988,
there have been a large number of print spooling systems developed which
claim RFC1179 conformance,
but which are mutually incompatible.</para>

<para>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 <application/lpd/
based systems.</para>

<para>&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 <emphasis>tune</emphasis>
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.</para>
</blockquote>
</para>

<para>For a detailed explanation
about &LPRng; and RFC1179, see

<link linkend="rfc1179ref">RFC1179 and &LPRng;</link>.</para>

</sect1>

<sect1><title>I want to replace lp, lpstat, etc, but my programs need them</title>

<para>&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, SystemV uses a totally different set of commands,
incompatible with the BSD ones.</para>

<para>The good news is that the &LPRng; binaries include an emulation for the
SystemV commands.
(See

<link linkend="lpsimulation">lp Simulation</link>

for details.
Briefly, you create links to the appropriate programs,
and invoke them by the link names.
<emphasis>Actually, these links are installed by default in recent versions.</emphasis></para>

<para>
<informalexample>
<screen>ln -s lpr lp
ln -s lpq lpstat
ln -s lprm cancel</screen>
</informalexample>
</para>

<para>If you make these links, calling
<literal>lp</literal>,
<literal>lpstat</literal>
and
<literal>cancel</literal>
will give you a (partial) SVR4 emulation. They have
their own man pages, which you should read if you need the emulation.</para>

<para>Since it is a <emphasis remap=bf>partial</emphasis>
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

<link linkend="lpsimulation">lp Simulation</link>

for more details or additional suggestions.</para>

</sect1>
</chapter>


<chapter id=remotelogger><title>Remote Logger Operation</title>

<para>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 implemented in &LPRng;.</para>


<sect1 id="logger"><title>Logger Network Communication </title>

<para>Options used:
<itemizedlist>

<listitem>
<para><literal>logger_destination=</literal><emphasis>logger information destination</emphasis></para>
</listitem>

<listitem>
<para><literal>logger_pathname=</literal><emphasis>pathname of temp file for log information</emphasis></para>
</listitem>

<listitem>
<para><literal>logger_max_size=</literal><emphasis>max size in K of temp file for log information</emphasis></para>
</listitem>

<listitem>
<para><literal>logger_timeout=</literal><emphasis>time between connection attempts</emphasis></para>
</listitem>

</itemizedlist>
</para>

<para>The printcap/configuration variable <literal>logger_destination</literal> specifies
a destination in the standard
<literal>host%port</literal>
notation used by &LPRng;.
Host is the destination host, and can be a name or IP address.
Port is the port on the destination host.
A TCP/IP connection is made to the indicated port.</para>

<para>Log information is save in a temporary file specified by
<literal>logger_path</literal>,
and up to
<literal>logger_max_size</literal>  K bytes of data will be saved.</para>

<para>If a connection cannot be made to the
<literal>logger_destination</literal>,
then every
<literal>logger_timeout</literal> seconds a new connection attempt will be made.
If <literal>logger_timeout</literal> is 0,
then a connection attempt will be made every time new data arrives to be logged.</para>

</sect1>

<sect1><title>Logger Messages</title>

<para>Log messages consist of a single line
terminated with a newline (<literal>\n</literal>) character.</para>

<para>Each log message reports a system event or status change of the
LPD server.
When the connection is first established,
a complete dump of the status of the LPD server is sent.
After this,
only status update messages are sent.
The remote monitor can force a status dump by simply closing and
reopening the connection.</para>

</sect1>

<sect1><title>Message Format</title>

<para>Each message is encoded as a URI escaped string.
That is, non-alphanumeric characters are encoded as the 3 character
sequence
<literal>%xx</literal>,  where
<literal>xx</literal>
is the hexadecimal value of the character.
The message has the format <literal>key=value</literal>,
where
<literal>key</literal>
indicates the message type.
For example:
<informalexample>
<screen>
dump=host=h4.private%0aprinter=t1%0aprocess=1613%0a
update_time=1999-03-23-20:32:17.148%0a\
value=queue=holdall 0%25250aprinting_aborted=0x0%25250a\
printing_disabled=0x0%25250aspooling_disabled=\
0x0%25250a%250a%0a</screen>
</informalexample>
</para>

<para>The following keys are used:
<orderedlist>

<listitem>
<para>dump<!-- <br> -->
A status dump of the current contents of a print queue.</para>
</listitem>

</orderedlist>
</para>

<para>Each message has a set of headers and a value.
For example,
the decoded dump message from the previous section would be:
<informalexample>
<screen>host=h4.private
printer=t1
process=1613
update_time=1999-03-23-20:32:17.148
value=queue=holdall 0%250aprinting_aborted=0x0%250a\
printing_disabled=0x0%250aspooling_disabled=0x0%250a%0a</screen>
</informalexample>
</para>

<para>Each line consists of a key and a value.
The
<literal>host</literal>
key indicates the host name,
<literal>printer</literal>
is the print queue,
<literal>process</literal>
is the process which generated the report or action,
<literal>update_time</literal> is the time at which the report was generated,
and
<literal>value</literal>
is the value of the report.</para>

<para>The decoded
<literal>value</literal>
of the above report is:
<informalexample>
<screen>queue='holdall 0%0aprinting_aborted=0x0%0aprinting_disabled=0x0%0a\
spooling_disabled=0x0%0a</screen>
</informalexample>
</para>

<para>The
<literal>queue</literal>
key provides the current value of the queue control file.</para>

</sect1>

<sect1><title>Dump Messages</title>

<para>Dump messages are generated at the start of operations,
and consist of a list of queue status messages.</para>

</sect1>

<sect1><title>LPD Messages</title>

<para>These are used to indicate LPD startup or change in operation.
<informalexample>
<screen>Decode: lpd=host=h4.private%0aprocess=1672%0aupdate_time=1999-03-23-20:5
1:10.507%0avalue=Starting%0a
host=h4.private
process=1672
update_time=1999-03-23-20:51:10.507
value=Starting
lpd: 'Starting'</screen>
</informalexample>
</para>

</sect1>

<sect1><title>Job Status Messages - UPDATE</title>

<para>Update messages are used to report changes in the queue contents,
such as job arrival.
<informalexample>
<screen>Decode: update=host=h4.private%0aidentifier=papowell@h4+676%0anumber=
 ...
host=h4.private
identifier=papowell@h4+676
number=676
printer=t1
process=1677
update_time=1999-03-23-20:51:17.197
value=bnrname=papowell%0acf_esc_image=Apapowell@h4+676%250aCA%250aD1999-03-
  ...</screen>
</informalexample>
</para>

<para>This update message reports the arrival of a new job at the queue.
The
<literal>value</literal>
field reports the control file contents:
<informalexample>
<screen>
class=A
date=1999-03-23-20:51:17.151
file_hostname=h4.private
fromhost=h4.private
held=0x0
hf_name=/var/tmp/LPD/t1/hfA676
hold_class=0x0
hold_time=0x0
identifier=papowell@h4+676
job_time=0x36f86f45
jobname=/tmp/hi
logname=papowell
number=676
priority=A
queuename=t1
size=3
transfername=cfA676h4.private
update_time=1999-03-23-20:51:17.187</screen>
</informalexample>
</para>

<para>The <literal>update_time</literal> field in the section above is the time that the
job information was last updated.
The <literal>cf_esc_image</literal> value is the URL escaped control file information.</para>

</sect1>

<sect1><title>Printer Status Messages - PRSTATUS</title>

<para>These messages report printing or other activity related to a job.
<informalexample>
<screen>Decode: prstatus=host=h4.private%0aidentifier=papowell@h4+676%0anumber=
676%0aprinter=t1%0aprocess=1692%0aupdate_time=1999-03-23-21:02:04.855%0avalue=
finished 'papowell@h4+676'%252c status 'JSUCC'%0a

host=h4.private
identifier=papowell@h4+676
number=676
printer=t1
process=1692
update_time=1999-03-23-21:02:04.855
value=finished 'papowell@h4+676'%2c status 'JSUCC'
PRSTATUS: 'finished 'papowell@h4+676', status 'JSUCC''</screen>
</informalexample>
</para>

</sect1>
</chapter>
<appendix id="optionindex"><title>Index To All The Configuration and Printcap Options </title>

<table frame=all id=lprngoptions><title>&LPRng; Options</title>
<tgroup cols=2 colsep=1 rowsep=1 align=left>
<thead>
<row><entry>Option </entry><entry> Purpose or Value </entry></row>
</thead>
<tbody>
<row><entry><link linkend="bannerprinting">ab</link></entry><entry>
always print banner, ignore lpr -h option <!-- <br> -->
<row><entry><link linkend="reception">accounting_namefixup</link></entry><entry>
update accounting name information<!-- <br> -->
</entry></row><row><entry><link linkend="opendevice">achk</link></entry><entry>
query accounting server when connected <!-- <br> -->
</entry></row><row><entry><link linkend="normalterm">ae</link></entry><entry>
accounting at end (see also af, la, ar, as) <!-- <br> -->
</entry></row><row><entry><link linkend="opendevice">af</link></entry><entry>
name of accounting file (see also la, ar) <!-- <br> -->
</entry></row><row><entry><link linkend="ah">ah</link></entry><entry>
automatically hold all jobs <!-- <br> -->
</entry></row><row><entry><link linkend="configfile">allow_getenv</link></entry><entry>
Allow use of LPD_CONF <!-- <br> -->
</entry></row><row><entry><link linkend="jobcompletion">allow_user_logging</link></entry><entry>
allow users to request logging info using lpr -mhost%port <!-- <br> -->
</entry></row><row><entry><link linkend="allowusersetting">allow_user_setting</link></entry><entry>
allow privileged user to impersonate other users<!-- <br> -->
</entry></row><row><entry><link linkend="opendevice">ar</link></entry><entry>
enable remote transfer accounting (if af is set) <!-- <br> -->
</entry></row><row><entry><link linkend="opendevice">as</link></entry><entry>
accounting at start (see also af, la, ar) <!-- <br> -->
</entry></row><row><entry><link linkend="auth">use_auth</link></entry><entry>
authentication type to use <!-- <br> -->
</entry></row><row><entry><link linkend="auth">auth_forward</link></entry><entry>
authentication type for forwarding<!-- <br> -->
</entry></row><row><entry><link linkend="bannerprinting">be</link></entry><entry>
Banner at End Generation Program <!-- <br> -->
</entry></row><row><entry><link linkend="bk">bk</link></entry><entry>
Berkeley <application/lpd/ job file format <!-- <br> -->
</entry></row><row><entry><link linkend="filteroptions">bk_filter_options</link></entry><entry>
Berkeley <application/lpd/ filter options <!-- <br> -->
</entry></row><row><entry><link linkend="filteroptions">bk_of_filter_options</link></entry><entry>
Berkeley <application/lpd/ OF filter options <!-- <br> -->
</entry></row><row><entry><link linkend="filteroptions">bkf</link></entry><entry>
backwards-compatible filters: use simple parameters <!-- <br> -->
</entry></row><row><entry><link linkend="bannerprinting">bl</link></entry><entry>
short banner line sent to banner printer <!-- <br> -->
</entry></row><row><entry><link linkend="bannerprinting">bp</link></entry><entry>
Banner Generation Program (see bs, be) <!-- <br> -->
</entry></row><row><entry><link linkend="lpdbounce">bq_format</link></entry><entry>
Format of bounce queue output <!-- <br> -->
</entry></row><row><entry><link linkend="serial">br</link></entry><entry>
Serial port bit rate (see ty) <!-- <br> -->
</entry></row><row><entry><link linkend="bannerprinting">bs</link></entry><entry>
Banner at Start Generation Program <!-- <br> -->
</entry></row><row><entry><link linkend="checkfornonprintable">check_for_nonprintable</link></entry><entry>
<application/lpr/ checks for nonprintable file <!-- <br> -->
</entry></row><row><entry><link linkend="classinstatus">class_in_status</link></entry><entry>
Show job class name in
<application>lpq</application>
status information <!-- <br> -->
</entry></row><row><entry><link linkend="printcapparse">client</link></entry><entry>
Mark printcap entry for client programs only <!-- <br> -->
</entry></row><row><entry><link linkend="cm">cm</link></entry><entry>
comment identifying printer (<application/lpq/) <!-- <br> -->
</entry></row><row><entry><link linkend="configfile">config_file</link></entry><entry>
configuration file <!-- <br> -->
</entry></row><row><entry><link linkend="opendevice">connect_grace</link></entry><entry>
connection control for remote printers <!-- <br> -->
</entry></row><row><entry><link linkend="opendevice">connect_interval</link></entry><entry>
connection control for remote printers <!-- <br> -->
</entry></row><row><entry><link linkend="opendevice">connect_timeout</link></entry><entry>
connection control for remote printers <!-- <br> -->
</entry></row><row><entry><link linkend="opendevice">connect_try</link></entry><entry>
connection control for remote printers <!-- <br> -->
</entry></row><row><entry><link linkend="logfiles">create_files</link></entry><entry>
create spool queue files <!-- <br> -->
</entry></row><row><entry><link linkend="opendevice">control_filter</link></entry><entry>
control file filter <!-- <br> -->
</entry></row><row><entry><link linkend="debugging">db</link></entry><entry>
debug options for queue <!-- <br> -->
</entry></row><row><entry><link linkend="defaultformat">default_format</link></entry><entry>
default job format <!-- <br> -->
</entry></row><row><entry><link linkend="defaultpermission">default_permission</link></entry><entry>
default permission for files <!-- <br> -->
</entry></row><row><entry><link linkend="defaultrmrp">default_printer</link></entry><entry>
default printer <!-- <br> -->
</entry></row><row><entry><link linkend="classpriority">default_priority</link></entry><entry>
default job priority <!-- <br> -->
</entry></row><row><entry><link linkend="defaultrmrp">default_remote_host</link></entry><entry>
default remote host <!-- <br> -->
</entry></row><row><entry><link linkend="configsetup">default_tmp_dir</link></entry><entry>
default directory for temp files <!-- <br> -->
</entry></row><row><entry><link linkend="destinations">destinations</link></entry><entry>
printers that a route filter may return and we should query <!-- <br> -->
</entry></row><row><entry><link linkend="normalterm">done_jobs</link></entry><entry>
save status for last N jobs <!-- <br> -->
</entry></row><row><entry><link linkend="normalterm">done_jobs_max_age</link></entry><entry>
remove status older than N seconds <!-- <br> -->
</entry></row><row><entry><link linkend="reception">fifo</link></entry><entry>
enforce FIFO job ordering<!-- <br> -->
</entry></row><row><entry><link linkend="opendevice">ff</link></entry><entry>
string to send for a form feed <!-- <br> -->
</entry></row><row><entry><link linkend="filteroptions">filter_ld_path</link></entry><entry>
filter LD_LIBRARY_PATH value <!-- <br> -->
</entry></row><row><entry><link linkend="filteroptions">filter_options</link></entry><entry>
filter options <!-- <br> -->
</entry></row><row><entry><link linkend="filteroptions">filter_path</link></entry><entry>
filter PATH environment variable <!-- <br> -->
</entry></row><row><entry><link linkend="opendevice">fo</link></entry><entry>
send form feed when device is opened <!-- <br> -->
</entry></row><row><entry><link linkend="forcefqdnhostname">force_fqdn_hostname</link></entry><entry>
force FQDN hostname value in control file <!-- <br> -->
</entry></row><row><entry><link linkend="forcelocalhost">force_localhost</link></entry><entry>
force clients to send all requests to localhost <!-- <br> -->
</entry></row><row><entry><link linkend="qq">force_queuename</link></entry><entry>
force use of this queuename if none provided <!-- <br> -->
</entry></row><row><entry><link linkend="normalterm">fq</link></entry><entry>
send form feed when device is closed <!-- <br> -->
</entry></row><row><entry><link linkend="debugging">full_time</link></entry><entry>
use extended time format <!-- <br> -->
</entry></row><row><entry><link linkend="bannerprinting">generate_banner</link></entry><entry>
generate banner page for forwarded jobs<!-- <br> -->
</entry></row><row><entry><link linkend="usergroup">group</link></entry><entry>
Effective Group ID (EGID) for SUID ROOT programs <!-- <br> -->
</entry></row><row><entry><link linkend="normalterm">half_close</link></entry><entry>
Use shutdown(fd,1) when sending job to remote printer instead of close(fd)
</entry></row><row><entry><link linkend="bannerprinting">hl</link></entry><entry>
Header (banner) last, at end of job <!-- <br> -->
</entry></row><row><entry><link linkend="classpriority">ignore_requested_user_priority</link></entry><entry>
Ignore requested user priority <!-- <br> -->
</entry></row><row><entry><link linkend="reception">incoming_control_filter</link></entry><entry>
incoming job control filter <!-- <br> -->
</entry></row><row><entry><link linkend="printjobformats">if</link></entry><entry>
default (f, l) filter program <!-- <br> -->
</entry></row><row><entry><link linkend="configsetup">ipv6</link></entry><entry>
using IPV6 conventions <!-- <br> -->
</entry></row><row><entry><link linkend="kerberos">kerberos_keytab</link></entry><entry>
kerberos keytab file location <!-- <br> -->
</entry></row><row><entry><link linkend="kerberos">kerberos_life</link></entry><entry>
kerberos key lifetime <!-- <br> -->
</entry></row><row><entry><link linkend="kerberos">kerberos_renew</link></entry><entry>
kerberos key renewal time <!-- <br> -->
</entry></row><row><entry><link linkend="kerberos">kerberos_forward_principal</link></entry><entry>
kerberos remote principle name for forwarding <!-- <br> -->
</entry></row><row><entry><link linkend="kerberos">kerberos_server_principal</link></entry><entry>
kerberos remote server principle name <!-- <br> -->
</entry></row><row><entry><link linkend="kerberos">kerberos_service</link></entry><entry>
kerberos default service <!-- <br> -->
</entry></row><row><entry><link linkend="k5conn">k5conn</link></entry><entry>
kerberos authentication support (value for auth)<!-- <br> -->
</entry></row><row><entry><link linkend="opendevice">la</link></entry><entry>
enable local printer accounting (if af is set) <!-- <br> -->
</entry></row><row><entry><link linkend="opendevice">ld</link></entry><entry>
leader string sent on printer open <!-- <br> -->
</entry></row><row><entry><link linkend="logfiles">lf</link></entry><entry>
error log file for spool queue <!-- <br> -->
</entry></row><row><entry><link linkend="opendevice">lk</link></entry><entry>
lock the IO device <!-- <br> -->
</entry></row><row><entry><link linkend="configsetup">lockfile</link></entry><entry>
lpd lock file <!-- <br> -->
</entry></row><row><entry><link linkend="logger">logger_destination</link></entry><entry>
destination for logging information <!-- <br> -->
</entry></row><row><entry><link linkend="logger">logger_timeout</link></entry><entry>
intervals between connection attempts <!-- <br> -->
</entry></row><row><entry><link linkend="logger">logger_pathname</link></entry><entry>
temp file for log information <!-- <br> -->
</entry></row><row><entry><link linkend="logger">logger_max_size</link></entry><entry>
max size in Kbytes of temp file for log information <!-- <br> -->
</entry></row><row><entry><link linkend="jobtransfer">longnumber</link></entry><entry>
use long job number when a job is submitted <!-- <br> -->
</entry></row><row><entry><link linkend="simple">lp</link></entry><entry>
printer device name or specification <!-- <br> -->
</entry></row><row><entry><link linkend="lpdbounce">lpd_bounce</link></entry><entry>
force lpd to filter job before forwarding <!-- <br> -->
</entry></row><row><entry><link linkend="spoolq">lpd_force_poll</link></entry><entry>
force lpd to poll idle printers <!-- <br> -->
</entry></row><row><entry><link linkend="spoolq">lpd_poll_time</link></entry><entry>
interval between lpd printer polls <!-- <br> -->
</entry></row><row><entry><link linkend="lpdport">lpd_port</link></entry><entry>
lpd listening port <!-- <br> -->
</entry></row><row><entry><link linkend="printcappath">lpd_printcap_path</link></entry><entry>
lpd printcap path <!-- <br> -->
</entry></row><row><entry><link linkend="lprbounce">lpr_bounce</link></entry><entry>
lpr does filtering as in bounce queue <!-- <br> -->
</entry></row><row><entry><link linkend="jobcompletion">lpr_bsd</link></entry><entry>
lpr does filtering as in bounce queue <!-- <br> -->
</entry></row><row><entry><link linkend="abnormalterm">mail_from</link></entry><entry>
mail user from user name <!-- <br> -->
</entry></row><row><entry><link linkend="abnormalterm">mail_operator_on_error</link></entry><entry>
mail to this operator on error <!-- <br> -->
</entry></row><row><entry><link linkend="maxaccount">max_accounting_file_size</link></entry><entry> maximum size (in K) of accounting file <!-- <br> -->
</entry></row><row><entry><link linkend="abnormalterm">max_connect_interval</link></entry><entry>
maximum time between connection attempts <!-- <br> -->
</entry></row><row><entry><link linkend="logfiles">max_log_file_size</link></entry><entry>
maximum size (in K) of spool queue log file <!-- <br> -->
</entry></row><row><entry><link linkend="spoolq">max_servers_active</link></entry><entry>
maximum number of lpd queue servers that can be active <!-- <br> -->
</entry></row><row><entry><link linkend="logfiles">max_status_line</link></entry><entry>
maximum length of status line <!-- <br> -->
</entry></row><row><entry><link linkend="logfiles">max_status_size</link></entry><entry> maximum size (in K) of status file <!-- <br> -->
</entry></row><row><entry><link linkend="jobcopies">mc</link></entry><entry>
maximum copies allowed <!-- <br> -->
</entry></row><row><entry><link linkend="maxaccount">min_accounting_file_size</link></entry><entry> minimum size (in K) of accounting file <!-- <br> -->
</entry></row><row><entry><link linkend="logfiles">min_log_file_size</link></entry><entry>
minimum size (in K) of spool queue log file <!-- <br> -->
</entry></row><row><entry><link linkend="logfiles">min_status_size</link></entry><entry>
minimum size to reduce status file to <!-- <br> -->
</entry></row><row><entry><link linkend="minfree">minfree</link></entry><entry>
minimum amount of free space needed <!-- <br> -->
</entry></row><row><entry><link linkend="checkfornonprintable">ml</link></entry><entry>
minimum number of printable characters for printable check <!-- <br> -->
</entry></row><row><entry><link linkend="debugging">ms_time_resolution</link></entry><entry>
millisecond time resolution <!-- <br> -->
</entry></row><row><entry><link linkend="jobcopies">mx</link></entry><entry>
maximum job size (1Kb blocks, 0 = unlimited) <!-- <br> -->
</entry></row><row><entry><link linkend="opendevice">nb</link></entry><entry>
use nonblocking device open <!-- <br> -->
</entry></row><row><entry><link linkend="opendevice">network_connect_grace</link></entry><entry>
pause between transferring jobs to remote printer <!-- <br> -->
</entry></row><row><entry><link linkend="jobfiles">nline_after_file</link></entry><entry>
N line after file name <!-- <br> -->
</entry></row><row><entry><link linkend="bannerprinting">of</link></entry><entry>
banner output filter <!-- <br> -->
</entry></row><row><entry><link linkend="filteroptions">of_filter_options</link></entry><entry>
OF filter options <!-- <br> -->
</entry></row><row><entry><link linkend="printcapparse">oh</link></entry><entry>
Printcap entry valid only on these hosts <!-- <br> -->
</entry></row><row><entry><link linkend="lpdport">originate_port</link></entry><entry>
originate connections from these ports <!-- <br> -->
</entry></row><row><entry><link linkend="filteroptions">pass_env</link></entry><entry>
clients pass these environment variables to filters <!-- <br> -->
</entry></row><row><entry><link linkend="permspath">perms_path</link></entry><entry>
lpd.perms files <!-- <br> -->
</entry></row><row><entry><link linkend="filteroptions">pl</link></entry><entry>
page length (in lines) <!-- <br> -->
</entry></row><row><entry><link linkend="pr">pr</link></entry><entry>
pr program for p format <!-- <br> -->
</entry></row><row><entry><link linkend="printcappath">printcap_path</link></entry><entry>
 printcap file <!-- <br> -->
</entry></row><row><entry><link linkend="logfiles">ps</link></entry><entry>
printer status file name <!-- <br> -->
</entry></row><row><entry><link linkend="filteroptions">pw</link></entry><entry>
page width (in characters) <!-- <br> -->
</entry></row><row><entry><link linkend="filteroptions">px</link></entry><entry>
page width in pixels (horizontal) <!-- <br> -->
</entry></row><row><entry><link linkend="filteroptions">py</link></entry><entry>
page length in pixels (vertical) <!-- <br> -->
</entry></row><row><entry><link linkend="queuelockfile">queue_lock_file</link></entry><entry>
queue lock file name<!-- <br> -->
</entry></row><row><entry><link linkend="queuecontrolfile">queue_control_file</link></entry><entry>
queue control file name<!-- <br> -->
</entry></row><row><entry><link linkend="logfiles">queue_status_file</link></entry><entry>
queue status file name<!-- <br> -->
</entry></row><row><entry><link linkend="qq">qq</link></entry><entry>
put queue name in control file <!-- <br> -->
</entry></row><row><entry><link linkend="remotesupport">remote_support</link></entry><entry>
operations allowed to remote host <!-- <br> -->
</entry></row><row><entry><link linkend="configsetup">report_server_as</link></entry><entry>
server name for status reports <!-- <br> -->
</entry></row><row><entry><link linkend="lpdport">retry_econnrefused</link></entry><entry>
Retry on connect ECONNREFUSED errors <!-- <br> -->
</entry></row><row><entry><link linkend="lpdport">retry_nolink</link></entry><entry>
Retry device open or connect failures <!-- <br> -->
</entry></row><row><entry><link linkend="shortstatus">return_short_status</link></entry><entry>
return short lpq status when request arrives from specified host <!-- <br> -->
</entry></row><row><entry><link linkend="lpdport">reuse_addr</link></entry><entry>
set SO_REUSEADDR on outgoing ports <!-- <br> -->
</entry></row><row><entry><link linkend="reverselpqformat">reverse_lpq_format</link></entry><entry>
reverse lpq format when request arrives from specified host <!-- <br> -->
</entry></row><row><entry><link linkend="rg">rg</link></entry><entry>
clients allow only users in this group access to printer <!-- <br> -->
</entry></row><row><entry><link linkend="simple">rm</link></entry><entry>
remote machine (hostname) (with rp) <!-- <br> -->
</entry></row><row><entry><link linkend="destinations">router</link></entry><entry>
routing filter, returns destinations <!-- <br> -->
</entry></row><row><entry><link linkend="simple">rp</link></entry><entry>
remote printer name (with rm) <!-- <br> -->
</entry></row><row><entry><link linkend="opendevice">rw</link></entry><entry>
open printer for reading and writing <!-- <br> -->
</entry></row><row><entry><link linkend="safechars">safe_chars</link></entry><entry>
additional safe characters in control file lines <!-- <br> -->
</entry></row><row><entry><link linkend="abnormalterm">save_on_error</link></entry><entry>
save job when an error <!-- <br> -->
</entry></row><row><entry><link linkend="normalterm">save_when_done</link></entry><entry>
save job when done <!-- <br> -->
</entry></row><row><entry><link linkend="bannerprinting">sb</link></entry><entry>
short banner (one line only) <!-- <br> -->
</entry></row><row><entry><link linkend="spoolqueue">sd</link></entry><entry>
spool directory pathname <!-- <br> -->
</entry></row><row><entry><link linkend="sendblockformat">send_block_format</link></entry><entry>
send block of data, rather than individual files <!-- <br> -->
</entry></row><row><entry><link linkend="jobtransfer">send_data_first</link></entry><entry>
send data files first in job transfer <!-- <br> -->
</entry></row><row><entry><link linkend="abnormalterm">send_failure_action</link></entry><entry>
failure action to take after send_try attempts failed <!-- <br> -->
</entry></row><row><entry><link linkend="printingjob">send_job_rw_timeout</link></entry><entry>
print job read/write timeout <!-- <br> -->
</entry></row><row><entry><link linkend="printingjob">send_query_rw_timeout</link></entry><entry>
status query operation read/write timeout <!-- <br> -->
</entry></row><row><entry><link linkend="abnormalterm">send_try</link></entry><entry>
maximum number of times to try sending job <!-- <br> -->
</entry></row><row><entry><link linkend="abnormalterm">sendmail</link></entry><entry>
sendmail program <!-- <br> -->
</entry></row><row><entry><link linkend="printcapparse">server</link></entry><entry>
Mark printcap entry for lpd server program only <!-- <br> -->
</entry></row><row><entry><link linkend="opendevice">server_tmp_dir</link></entry><entry>
server temporary file directory <!-- <br> -->
</entry></row><row><entry><link linkend="printingjob">sf</link></entry><entry>
suppress form feeds separating data files in job <!-- <br> -->
</entry></row><row><entry><link linkend="bannerprinting">sh</link></entry><entry>
suppress header (banner) pages <!-- <br> -->
</entry></row><row><entry><link linkend="logfiles">short_status_date</link></entry><entry>
short (hh:mm) timestamp format for status <!-- <br> -->
</entry></row><row><entry><link linkend="shortstatus">short_status_length</link></entry><entry>
short lpq status length in lines <!-- <br> -->
</entry></row><row><entry><link linkend="lpdport">socket_linger</link></entry><entry>
set the SO_LINGER socket option <!-- <br> -->
</entry></row><row><entry><link linkend="configsetup">spool_dir_perms</link></entry><entry>
spool directory permissions <!-- <br> -->
</entry></row><row><entry><link linkend="configsetup">spool_file_perms</link></entry><entry>
spool file permissions <!-- <br> -->
</entry></row><row><entry><link linkend="loadbalance">ss</link></entry><entry>
name of queue that server serves (with sv) <!-- <br> -->
</entry></row><row><entry><link linkend="stalledtime">stalled_time</link></entry><entry>
time after which to report active job stalled <!-- <br> -->
</entry></row><row><entry><link linkend="abnormalterm">stop_on_abort</link></entry><entry>
stop processing queue on filter abort <!-- <br> -->
</entry></row><row><entry><link linkend="serial">stty</link></entry><entry>
stty commands to set output line characteristics <!-- <br> -->
</entry></row><row><entry><link linkend="loadbalance">sv</link></entry><entry>
names of servers for queue (with ss) <!-- <br> -->
</entry></row><row><entry><link linkend="debugging">syslog_device</link></entry><entry>
name of syslog device <!-- <br> -->
</entry></row><row><entry><link linkend="printcapparse">tc</link></entry><entry>
Include indicated printcap entries in current entry<!-- <br> -->
</entry></row><row><entry><link linkend="normalterm">tr</link></entry><entry>
trailer string to send before closing printer <!-- <br> -->
</entry></row><row><entry><link linkend="translateformat">translate_format</link></entry><entry>
translate data format in control file <!-- <br> -->
</entry></row><row><entry><link linkend="lpdport">unix_socket_path</link></entry><entry>
FIFO path for local process connections<!-- <br> -->
</entry></row><row><entry><link linkend="debugging">use_info_cache</link></entry><entry>
read and cache information <!-- <br> -->
</entry></row><row><entry><link linkend="jobtransfer">use_shorthost</link></entry><entry>
Use short hostname for lpr control and data file names <!-- <br> -->
</entry></row><row><entry><link linkend="usergroup">user</link></entry><entry>
Effective User ID (EUID) for SUID ROOT programs <!-- <br> -->
</entry></row><row><entry><link linkend="normalterm">wait_for_eof</link></entry><entry>
Wait for EOF on connection <!-- <br> -->
</entry></row>
</tbody>
</tgroup>
</table>
</appendix>
<appendix id=license><title>License</title>
<para>
<programlisting>
&license;
</programlisting>
</para>
</appendix>

<appendix id=testing><title>Testing and Diagnostic Facilities</title>

<para>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 <emphasis>Test Version</emphasis>
to be run in parallel with the
normal &LPRng; software.</para>

<para>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
(<filename>/tmp</filename> is common) and the &LPRng; software tested.</para>

<para>The test version of the software will use the <literal>LPD_CONF</literal>
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.</para>


<sect1><title>Compiling the Test Version</title>

<para>Edit <filename>src/Makefile</filename>, and uncomment the indicated line.
Then run
<literal>make</literal>
to regenerate the distribution.
<informalexample>
<screen>#### ****** TESTING AND SECURITY LOOPHOLE ******************************
# Define 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.
#CF := $(CF) -DGETENV
</screen>
</informalexample>
</para>

</sect1>

<sect1><title>Setting Up The Test Version Spool Queues</title>

<para>The &LPRng; <acronym>TESTSUPPORT</acronym> directory contains a set of shell scripts
and files that need to be installed in the appropriate directory.
The following steps are used.
<orderedlist>

<listitem>
<para>First,
you need to set up your <acronym>HOST</acronym> environment variable to the fully
qualified domain name of your host
and your <acronym>USER</acronym> environment variable to your user name.
This is done in order to get values to put into the Test Version configuration files.</para>
</listitem>

<listitem>
<para>In the <acronym>TESTSUPPORT</acronym> directory,
edit the
<literal>Makefile</literal>,
and specify the location of the
<literal>Test Version</literal>
spool queues.
The default location is <filename>/tmp</filename>;
since on most systems these files are deleted or are available to everybody,
a more secure location should most likely be used.
<emphasis remap=bf>DO NOT USE THE RAW TESTFILE DIRECTORY</emphasis>.
These files need to be copied and placed in another directory.</para>
</listitem>

<listitem>
<para>The <literal>LPD_CONF</literal> environment variable should be set to the
location of the installed <filename>lpd.conf</filename> file.</para>
</listitem>

<listitem>
<para>In the <acronym>TESTSUPPORT</acronym> directory,
run
<literal>make</literal>.
This will copy and install the necessary files.</para>
</listitem>

</orderedlist>
</para>

<para>Example:
<informalexample>
<screen>  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 h4.private
      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=h4.private; 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</screen>
</informalexample>
</para>

</sect1>

<sect1><title>Running the Test Version Software</title>

<para>Set your current directory to the location of the compiled
<literal>Test Version</literal>
executables.
Execute the various executables using <filename>./cmd</filename>,
or set
<literal remap=tt>.</literal>
<emphasis remap=bf> as the first entry in the PATH </emphasis>.
If it is not the first entry,
then the standard system executables will be used.
<orderedlist>

<listitem>
<para> Run <filename>./checkpc</filename>.
this will print out the various values for the spool queues in the
<literal>Test Version</literal>
setup.
If the
<literal>t1</literal>,
<literal>t2</literal>,... spool queues are not displayed,
make sure that the LPD_CONF environment variable is set correctly and that you
are using the
<literal>Test Version</literal>
executable.</para>
</listitem>

<listitem>
<para>Run
<literal>./checkpc -f</literal>.
This will fix up the (deliberately introduced) problems in the spool queues.</para>
</listitem>

<listitem>
<para>Next,  run
<literal>./lpd -F</literal>
in one window,
and then run
<literal>./lpq -a </literal>
in another window.
This will check that the server is working.</para>
</listitem>

<listitem>
<para>You can now amuse yourself by sending jobs,
setting up permissions checking,
and other chores.</para>
</listitem>

<listitem>
<para>When everything appears to be working correctly,
you can then remove the
<literal>Test Version</literal>
flag from the
<filename>src/Makefile</filename>, recompile,
and install the &LPRng; software.</para>
</listitem>

</orderedlist>
</para>

</sect1>
</appendix>
&genindex.sgml;
</book>
