  LPRng-HOWTO
  Patrick Powell papowell@astart.com
  21 Dec 1998 (For LPRng-3.5.4)

  The LPRng software is an enhanced, extended, and portable implementa-
  tion of the Berkeley LPR print spooler functionality. While providing
  the same interface and meeting RFC1179 requirements, the implementa-
  tion is completely new and provides support for the following fea-
  tures: lightweight (no databases needed) lpr, lpc, and lprm programs;
  dynamic redirection of print queues; automatic job holding; highly
  verbose diagnostics; multiple printers serving a single queue; client
  programs do not need to run SUID root; greatly enhanced security
  checks; and a greatly improved permission and authorization mechanism.
  The source software compiles and runs on a wide variety of UNIX sys-
  tems, and is compatible with other print spoolers and network printers
  that use the LPR interface and meet RFC1179 requirements. The package
  comes with filters for PostScript and HP printers, as well as the
  usual 'dumb' printers. Note that the PostScript and HP filters do page
  counting and produce accounting information accounting. In addition,
  there are a wide variety of other filters that can do page formatting,
  and produce banner pages. For users that require compatibility with
  the SVR4 lp and lpstat interface, lpr and lpq will simulate this
  interface, eliminating the need for another print spooler package. In
  addition, a publically available PCNFSD server is distributed with
  LPRng, and interfaces with the PC/DOS/Windows based NFS style print
  spoolers. For users that require secure and/or authenticated printing
  support, LPRng supports Kerberos 5, MIT Kerberos 4 extensions to LPR,
  and PGP authentication methods.  Additional authentication support is
  extremely simple to add.
  ______________________________________________________________________

  Table of Contents


































  1. Introduction

     1.1 What is LPRng?
     1.2 More Information
     1.3 Copyright
     1.4 Disclaimer
     1.5 Commercial Support
     1.6 Web Site
     1.7 FTP Sites
     1.8 Mailing List
     1.9 PGP Public Key

  2. The Most Frequently Asked Questions

     2.1 Why do I get malformed from address errors?
     2.2 It was working normally, then I get connection refused errors
     2.3 Job is not in print queue, but it gets printed!
     2.4 Job disappears and is never printed, but lpr works
     2.5 I get messages about bad control file format
     2.6 What is RFC 1179, the Line Printer Daemon Protocol?
     2.7 I want to replace lp, lpstat, etc, but my programs need them
     2.8 What are the drawbacks to LPRng?

  3. Installing your printer

     3.1 Parallel Printers
     3.2 Serial Printers
        3.2.1 Converting BSD
     3.3 Network Printers

  4. Installing the programs

     4.1 Files and Setup
     4.2 Source Code and Installation
        4.2.1 Building the software
        4.2.2 Problems with compilation
     4.3 Preparing to run the daemon
        4.3.1 Removing Existing Facilities
        4.3.2 Startup Scripts
     4.4 Replacing UNIX SystemV lp, lpstat Printing Services
     4.5 Setuid ROOT and Security Issues
     4.6 System specific notes
     4.7 Solaris 2.4, 2.5, 2.6
     4.8 Solaris, Newsprint and FrameMaker
     4.9 Linux
     4.10 AIX
     4.11 Appletalk Support
     4.12 Apple to LPRng Printing
     4.13 LPRng to Appletalk Printing
     4.14 SAMBA Support
        4.14.1 What is SMB
        4.14.2 Samba and LPRng
     4.15 Printer Specific notes
     4.16 HP Deskjet
     4.17 HP LaserJet IIISiMX
     4.18 HP Deskjet 1600CM
     4.19 HP JetDirect Interface
        4.19.1 Setting Up IP Networking and Address
        4.19.2 BOOTP Information
        4.19.3 Paper Tray Selection
     4.20 Lexmark Printers
     4.21 Tektronix P450 and Family
     4.22 Duplex Printing
     4.23 Test Version  and Portability Testing
        4.23.1 Compiling the Test Version
        4.23.2 Setting Up The Test Version Spool Queues
        4.23.3 Running the Test Version Software
        4.23.4 Portability Testing

  5. /etc/printcap Print Spool Database File

     5.1 Index To All The Options
     5.2 A very simple example
     5.3 A serial printer queue
     5.4 A remote printer queue
     5.5 Network Printing With /etc/printcap
     5.6 Spooling To Local Server
     5.7 Shared printcap files
     5.8 Master Printcap Files
     5.9 Printcap options for lpr, lpq, lprm, and lpc
        5.9.1 lp, rm and rp
     5.10 Bounce queues
     5.11 LPR Filtering
     5.12 Dynamic Routing
     5.13 Support for Network Print Servers
     5.14 Printer load balancing
        5.14.1 Multi-server print queue
        5.14.2 Checking Busy Status of Server Queues
        5.14.3 Using a router filter
     5.15 The Missing Details
        5.15.1 Spool (Control) Directory Printcap File
        5.15.2 Separate Printcap Files for LPD
        5.15.3 Printcap Entry all
        5.15.4 More Example Printcap Entries
        5.15.5 PC-NFS Print Spooler
     5.16 Management Strategy for Large Sites
     5.17 Using Programs To Get Printcap Information
        5.17.1 How to use NIS and LPRng
        5.17.2 How to use NIS and LPRng - Sven Rudolph
     5.18 The Record Queue Name qq and force_queuename flags
     5.19 Using the check_for_nonprintable Flag
     5.20 The fd Forwarding Off Option
     5.21 The rg Restrict Use to Group Members Option
     5.22 The fx Allowed Formats Option
     5.23 Fixing Bad Control Files and Metacharacters
        5.23.1 Defective RFC1179 Implementations
        5.23.2 OS/2
        5.23.3 Serious Security Loophole
        5.23.4 Fixing Bad Jobs
        5.23.5 Using the bk Option and Control File Filters
     5.24 Maximum Copies
     5.25 The minfree Minimum Spool Queue Space Option
     5.26 Debugging
     5.27 LPD Specific
     5.28 Legacy Compatibility
        5.28.1 The allow_duplicate_args Option
        5.28.2 The class_in_status Options
        5.28.3 The reverse_lpq_format Option
        5.28.4 The return_short_status and short_status_length Options
        5.28.5 The force_lpq_status Options
        5.28.6 The ignore_requested_user_priority and force_fqdn_host Options
        5.28.7 The lpr_bsd Options
     5.29 Compatibility with BSD printcap

  6. Filters

     6.1 What are filters?
     6.2 What are print formats?
     6.3 OF Filter
        6.3.1 The filter_poll Option
     6.4 The lpr -p  format and pr option
     6.5 The lpr -l  format and binary format
     6.6 Job Processing and Printcap Options
        6.6.1 Opening the Output Device
        6.6.2 Printing Banner At Beginning
        6.6.3 Printing Job Files
        6.6.4 Printing Banner At End
        6.6.5 Normal Termination
        6.6.6 Abnormal Termination
        6.6.7 LPD Spool Queue Processing
     6.7 Filter Command Line Flags
     6.8 Bounce queues and filters: caveats
     6.9 The lpr -p  format and :pr filter
     6.10 Changing Filter Formats
     6.11 LPRng Supported Filters
        6.11.1 Filter Distribution Conventions
        6.11.2 lpf
     6.12 IFHP Filter
        6.12.1 Printer Capabilities
        6.12.2 hpif Options
        6.12.3 Parallel Port Printer
        6.12.4 Printing Banners
        6.12.5 Error Logging
        6.12.6 Accounting Information
        6.12.7 Accounting
     6.13 lp_pipe Filters
     6.14 apsfilter Filter
     6.15 Using your own filters

  7. Spool Queues and Files

     7.1 Spool Queue and Control Queue
     7.2 Job State
     7.3 Printer Lock File
     7.4 Spool Control File
     7.5 Log and Status Files
     7.6 Job Control File
     7.7 Job Hold File
     7.8 Job Identifier

  8. /etc/lpd.conf Configuration File

     8.1 Configuration File Format
     8.2 Obtaining Configuration Information
     8.3 Useful Configuration Options
        8.3.1 default_format
        8.3.2 default_permission=ACCEPT
        8.3.3 default_priority=A
        8.3.4 default_remote_host=localhost
        8.3.5 default_tmp_dir=/tmp
        8.3.6 domain_name=domain.name

  9. The /etc/lpd.perms Permissions File

     9.1 Information for matching
     9.2 Permission Checks
     9.3 Match Procedure
        9.3.1 DEFAULT
        9.3.2 SERVICE
        9.3.3 USER
        9.3.4 REMOTEUSER
        9.3.5 HOST
        9.3.6 REMOTEHOST
        9.3.7 PORT
        9.3.8 IP
        9.3.9 REMOTEIP
        9.3.10 LPC
        9.3.11 SAMEUSER
        9.3.12 SAMEHOST
        9.3.13 SERVER
        9.3.14 FORWARD
        9.3.15 GROUP
        9.3.16 REMOTEGROUP
        9.3.17 CONTROLLINE
        9.3.18 AUTH
        9.3.19 AUTHUSER
        9.3.20 FWDUSER
        9.3.21 IFIP
     9.4 Permission File Location
     9.5 Example Permission File
     9.6 Complex Permission Checking
     9.7 More Examples

  10. Running the software

     10.1 LPRng's little helper: checkpc
     10.2 Starting the daemon

  11. Accounting

     11.1 Printer Accounting Reality Check
     11.2 How HP Printers Implement Page Counters
     11.3 Accounting Printcap Options
     11.4 Accounting File
     11.5 Remote Server Accounting
     11.6 Using Filters For Accounting
     11.7 Accounting Utility accounting.pl

  12. Authentication and Encryption

     12.1 Authentication
     12.2 Identifiers
     12.3 RFC1179 Protocol Extensions
     12.4 Client Operations for Client To lpd Server Authentication
     12.5 Server Operations for Client To lpd Server Authentication
     12.6 lpd Server to Server Authentication
     12.7 Permission Checking
     12.8 Using PGP for Authentication
        12.8.1 PGP Configuration
        12.8.2 Permissions
        12.8.3 Client Configuration
     12.9 Using Kerberos 5 for Authentication
        12.9.1 Kerberos Installation Procedure
        12.9.2 Testing Transfers
        12.9.3 Explicit Server Principal Name
     12.10 Using Kerberos 4 for Authentication

  13. Status Monitoring and Logging

     13.1 LPQ status reporting
     13.2 LPQ Short Format (lpq -s)
     13.3 LPQ Long Format (lpq default, lpq -l, lpq -L)
     13.4 LPQ Verbose Format (lpq -v)
     13.5 lpc status
     13.6 Remote Logger Operation
        13.6.1 Logger Network Communication
        13.6.2 Logger Messages
        13.6.3 Message Format
        13.6.4 Dump Messages
        13.6.5 LPD Messages
        13.6.6 Job Status Messages - UPDATE
        13.6.7 Printer Status Messages - PRSTATUS
     13.7 LPR -mhost%port and user logging support

  14. RFC 1179 - Line Printer Daemon Protocol

     14.1 Ports and Connections
     14.2 Protocol Requests and Replies
     14.3 Job Transfer
     14.4 Data File Transfer
     14.5 Control File Contents
     14.6 LPQ Requests
     14.7 LPRM Requests
     14.8 LPC Requests
     14.9 Block Job Transfer
     14.10 Authenticated Transfer

  15. Acknowledgements



  ______________________________________________________________________

  1.  Introduction

  This document is an attempt to provide some simple guidelines to set
  up LPRng. It is aimed at system managers who want to replace their
  vendor's printer daemon and its clients by LPRng.

  LPRng can be configured in many ways, making it suitable for a broad
  range of applications. It has a large amount of options which are
  explained in the extensive documentation.

  Despite (or due to) the extent of the documenting files, many new
  users experience problems in setting up the package. One of the
  reasons is that most of the LPRng documentation assumed that you
  already have a working knowledge of a BSD-style printer spooler.

  In this document, I will first try to give some simple guidelines that
  should enable you to put together a simple working configuration
  without being overwhelmed by options.  Next, there are some detailed
  descriptions of the way that LPRng does things.  Finally, there are
  detailed discussions about various LPRng facilities that try to
  encapsulate common configurations and operations that administrators
  and users encounter.

  1.1.  What is LPRng?

  Gordon Haverland (haverlan@agric.gov.ab.ca) put it this way:


       LPRng is a print spooling system. It was designed to mimic
       the Berkeley (University of California - Berkeley) Line
       Printers (LPR) package, first found on Berkeley derivatives
       of the Unix operating system. The LPRng package supports
       users being able to print a document with little or no
       knowledge of the content or special processing required to
       print the document; on a stand-alone machine, on a LAN, or
       remotely. New (as compared to Berkeley LPR) features
       include: lightweight lpr, lpc and lprm programs; dynamic
       redirection of print queues; automatic job holding; highly
       verbose diagnostics; multiple printers per queue; and
       enhanced security (SUID not required in most environments),
       improved file permissions, authorization checks, ...), etc.


  Patrick Powell (<papowell@astart.com>), the author of LPRng, has the
  following comments:


  LPRng started life in 1986 as PLP (Public Line Printer), a
  replacement for the original BSD LPD code.  This was a one-
  shot effort to develop code that could be freely
  redistributed without the restrictions of the BSD/AT&T
  license, and would allow non-license sites to fix and patch
  problems that they were having with print spooling.  (This
  was before the Free Software Foundation.)  From 1988 to
  1992, various groups added features, hacked, and modified
  the PLP code, coordinated largely by Justin Mason
  (<jmason@iona.ie>), who also started and sponsors the LPRng
  mailing list (see below).

  In 1992,  I redesigned and reimplemented the PLP code and
  named the result LPRng.  The goals of the LPRng project were
  to build a server system that was as close to user abuse
  proof as possible, that would provide services limited only
  by the inherent capacities of the support system, RFC1179
  compliant, and with extensive debugging capabilities to
  allow quick and easy diagnostics of problems.  Over the
  period from 1994-1997, LPRng users have suggested
  extensions, provided patches, and added facilities.  I have
  tried to incorporate these in a controlled and cautious
  manner.

  As a side effect of this work, many security problems that
  could develop were identified and steps taken to ensure that
  they were not present in LPRng.  For example, if you want,
  you can run LPRng clients such as lpr, lprm, lpc, and lpq as
  a non-privileged user; this reduces the chances of users
  exploiting faults in the code and gaining root privileges.
  Bounds checking is performed on all input and formatting
  (for example, snprintf() rather than sprintf() is used), as
  well as other preventive measures where appropriate.


  In most cases, LPRng is not a drop-in replacement for BSD LPR. For
  most installations, you will need adaptations.  However, most of the
  changes are minor, and many of the LPRng facilities are backwards
  compatible with existing BSD print spooler facilities.


  1.2.  More Information

  Much of the material in the LPRng documentation has been included in
  this document.  For example, previous releases of LPRng had a large
  selection of README files; most of these are now incorporated into the
  HOWTO document.

  Current information about LPRng and the latest release can be found on
  the LPRng web page:

  http://www.astart.com/LPRng.html

  There is also a mailing list at lprng@iona.com. To subscribe, send an
  email to lprng-request@iona.com. The body should contain only the word
  `subscribe'. To get off the list later on, repeat the same procedure,
  but use the word `unsubscribe'.

  Several presentations of LPRng and print spooling software have been
  made at the Large Scale Installation Administrator (LISA) conferences
  and are in the LPRng distribution and available on web sites.  LPRng -
  An Enhanced Printer Spooler System was presented at the LISA95
  conference, and is in the LPRng distribution as LPRng_DOC/LPRng-
  LISA95.ps.  On a more general topic, the slides for the LISA97
  tutorial on Printers and Network Print Spooling are also in the LPRng
  distribution in the LPRng_DOC/LISA97 directory.
  The LPRng distribution also has a set of man pages (in the man/
  directory) that are the reference for the LPRng operation.  When in
  doubt, please consult them.

  1.3.  Copyright

  Material included in this document from the LPRng distribution
  Copyright Patrick Powell 1988-1997, where applicable.

  The rights to distribute this document complete or in part are hereby
  granted for non-commercial purposes. Partial reproductions must
  acknowledge the source.

  Permission to distribute this file together with LPRng and `derived
  works' (as defined in the LPRng license) is explicitly granted. This
  is allowed independent of the license under which the software is
  distributed.

  Citing the document is allowed as long as the source is acknowledged.

  1.4.  Disclaimer

  THE MATERIAL IN THIS HOWTO IS PROVIDED WITHOUT FEE AND AS-IS WITH NO
  WARRANTY REGARDING FITNESS OF USE FOR ANY PURPOSE. THE AUTHOR AND ALL
  CONTRIBUTORS ARE NOT LIABLE FOR ANY DAMAGES, DIRECT OR INDIRECT,
  RESULTING FROM THE USE OF INFORMATION PROVIDED IN THIS DOCUMENT.

  1.5.  Commercial Support

  AStArt Technologies provides commercial support and enhancements for
  the LPRng and other network software.  AStArt provides network and
  system consulting services for UNIX and NT systems, as well as real
  time and network software.

  1.6.  Web Site

  Web Page:

  http://www.astart.com/lprng.html

  1.7.  FTP Sites

  The software may be obtained from
  ftp://ftp.astart.com/pub/LPRng(Main site)

  Mirrors:
  ftp://ftp.sage-au.org.au/pub/printing/spooler/lprng (AU)
  ftp://sunsite.ualberta.ca/pub/Mirror/LPRng/ (CA)
  ftp://ftp.informatik.uni-hamburg.de/pub/os/unix/utils/LPRng (DE)
  ftp://ftp.uni-paderborn.de/pub/unix/printer/plp/LPRng (DE)
  ftp://ftp.iona.ie/pub/plp/LPRng (IE)
  ftp://ftp.chembio.ntnu.no/pub/mirrors/LPRng (NO)
  ftp://ftp.mono.org/pub/LPRng (UK)
  ftp://ftp.cs.columbia.edu/pub/archives/pkg/LPRng (US)
  ftp://ftp.cs.umn.edu/pub/LPRng (US)
  ftp://ftp.iona.com/pub/plp/LPRng (US)
  ftp://uiarchive.uiuc.edu/pub/packages/LPRng (US)

  1.8.  Mailing List

  To join the LPRng mailing list, please send mail to lprng-
  request@iona.ie with the word 'subscribe' in the BODY.

  The LPRng mailing list is archived on
  http://www.findmail.com/list/lprng

  1.9.  PGP Public Key

  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:


       Type Bits/KeyID    Date       User ID
       pub  1024/00D95C9D 1997/01/31 Patrick A. Powell <papowell@astart.com>
                                     Patrick A. Powell <papowell@sdsu.edu>

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

       mQCNAzLygTQAAAEEANBW5fPYjN3wSAnP9xWOUc3CvsMUxjip0cN2sY5qrdoJyIhn
       qbAspBopR+tGQfyp5T7C21yfWRRnfXmoJ3FVtgToAsJUYmzoSFY08eDx+rmSqCLe
       rdJjX8aG8jVXpGipEo9U4QsUK+OKzx3/y/OaK4cizoWqKvy1l4lEzDsA2VydAAUT
       tCdQYXRyaWNrIEEuIFBvd2VsbCA8cGFwb3dlbGxAYXN0YXJ0LmNvbT6JAJUDBRA0
       XonoiUTMOwDZXJ0BAQ2cBAC7zU9Fn3sC3x0USJ+3vjhg/qA+Gjb5Fi1dJd4solc4
       vJvtf0UL/1/rGipbR+A0XHpHzJUMP9ZfJzKZjaK/d0ZBNlS3i+JnypypeQiAqo9t
       FV0OyUCwDfWybgAORuAa2V6UJnAhvj/7TpxMmCApolaIb4yFyKunHa8aBxN+17Ro
       rrQlUGF0cmljayBBLiBQb3dlbGwgPHBhcG93ZWxsQHNkc3UuZWR1PokAlQMFEDLy
       gTSJRMw7ANlcnQEBYBYD/0zTeoiDNnI+NjaIei6+6z6oakqO70qFVx0FG3aP3kRH
       WlDhdtFaAuaMRh+RItHfFfcHhw5K7jiJdgKiTgGfj5Vt3OdHYkeeh/sddqgf9YnS
       tpj0u5NfrotPTUw39n6YTgS5/aW0PQfO9dx7jVUcGeod1TGXTe9mIhDMwDJI4J14
       =3Zbp
       -----END PGP PUBLIC KEY BLOCK-----




  2.  The Most Frequently Asked Questions

  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.

  Some of these answers will reference other material in this FAQ, or
  the LPRng man pages.

  2.1.  Why do I get malformed from address errors?

  This is the number one question asked by most LPRng users who try to
  use LPRng with network printers or other systems supporting
  ``RFC1179'' printing.  For details about LPRng and RFC1179, see
  ``RFC1179 and LPRng''.

  The
   malformed from address  error is usually reported when trying to send
  a print job from LPRng to other BSD LPR or RFC1179 LPR
  implementations, or with network connected printers that have a built
  in LPR server.  This is due to the following RFC1179 rule:

       Servers originate a connection from ports in the range
       721-731.


  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.

  IMPLICATION:  in order to do use a reserved port,  the program must
  have root privileges.  This means the LPR, LPD, LPQ, etc., programs
  must be installed SUID root.  This can open up a can of worms with
  regard to security,  but LPRng has been designed to take as much
  paranoid care as possible to avoid problems.
  WHAT TO DO:
  When installing LPRng,  you will need to install the executables SUID
  root.  In the src/Makefile,  you can remove the comment from the line


       PERMS=SUID_ROOT_PERMS




  and then do  make install.  This will install the executables SUID,
  and owned by root.

  2.2.  It was working normally, then I get connection refused errors

  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 ``RFC1179 and LPRng'', but here is a quick
  explanation.

  A TCP/IP connection is usually specified as between srchost:srcport,
  desthost:destport, although in practice the order of source (src) and
  destination (dest) is not important.

  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 host:port that was previously
  active for a specified timeout period, usually 10 minutes.

  Some TCP/IP implementations go further:  they will not allow ANY
  connection to be originated (via the bind() system call or API) from a
  port that was active, or accepted on a port that was active for this
  timeout period.

  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 1.1.1.1:721 to 1.1.1.2:515.  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 1.1.1.1:721 to 1.1.1.2:515 we get an error such as
  "address already in use" or "connection refused".

  Luckily,  we can use port 722 to originate a connection, and we can
  connect from 1.1.1.1:722 to 1.1.1.2:515.  We continue on, until we
  come to port 731, and then we need to wait for our timeouts.

  SOLUTION:

  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


       originate_port=721 731




  value in the defaults (LPRng/src/common/defaults.c) file or in the
  lpd.conf file.  I recommend the following:


  originate_port=512 1022




  This is, in fact, now the default in LPRng software.  If you get the
  infamous malformed from address error message from your spooler, then
  you will have to set originate_port=721 731,  and live with a delayed
  throughput.

  2.3.  Job is not in print queue, but it gets printed!

  In the original BSD LPD implementation, the LPR program copied users
  files to a special spool queue directory, and then caused the LPD
  server to peek in the directory and print the files.

  This type of operation required spool directory space, special SETUID
  programs, and a slew of headaches in system security and management.

  The LPR, LPQ, and other user programs in the LPRng suite use TCP/IP
  connections and transfer jobs directly to a LPD server running on a
  remote host, or even the local host if appropriate.  Note that this
  type of operation does not require a LPD server to run on each local
  machine.  In fact,  you can have a single host system performing all
  of your printing.  This type of operation is very similar to a central
  mail server versus individual systems, each having their own mail
  server and queues.

  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.

  Method 1: Explicit Printer Address

  You can force a job to be sent directly to the  pr serviced by the LPD
  server on host by using the form:


       lpr -Ppr@host file




  You can also set the PRINTER environment variable to a similar form,
  and get the same effect:


       PRINTER=pr@host; export PRINTER;
       lpr file




  Method 2: User and Server Printcap Entries

  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 /etc/printcap
  file for printcap entries, combining information for the same printer
  into a single entry.  Information found later in the printcap file
  will override earlier information.  In addition, you can tag entries
  as either being used for all utilities or just for the LPD server.
  Here are a couple of examples:

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




  As you can see, the server keyword indicates that the printcap entry
  is only for the printer.  The LPR utility will send the job to the
  host, while the LPD server will print it on /dev/lp.

  Note that the lp=... information overrides the :rp: (remote printer)
  and :rm: (remote machine) fields if they are present.

  Method 3: Force sending to server on localhost

  The force_localhost printcap or configuration flag forces non-LPD
  applications to send all requests and print jobs to the server running
  on the local host.

  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 /etc/lpd.conf
  file for general application,  or have a printcap entry of the
  following form:


       # for all utilities
       pr:lp=pr@host:force_localhost




  The LPD server will ignore the force_localhost flag, and send jobs to
  the pr queue on the host machine.  However, the LPR, LPQ, etc.,
  utilities will send their requests to the server running on the local
  host.

  2.4.  Job disappears and is never printed, but lpr works

  This is a rather disconcerting problem, and usually occurs when
  sending jobs to either a network printer or a nonconforming
  ``RFC1179'' print spooler.  For details about LPRng and RFC1179, see
  ``RFC1179 and LPRng'', but here is a quick explanation.

  An LPD job consists of a control file,  which contains information
  about the job,  and one or more data files.  RFC1179 is silent on the
  order that jobs are sent;  however some implementations REQUIRE that
  the data files be sent first,  followed by the control file.

  SOLUTION:

  Set the send_data_first flag in the printcap for the particular
  printer,  or in the lpd.conf configuration file.  This is:




  :send_data_first:  (printcap)
  send_data_first    (lpd.conf)




  Note that some printers/servers INSIST on the control file first; You
  can clear the flag using send_job_first@ if you need to.

  2.5.  I get messages about bad control file format

  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.

  LPRng quite happily will accept jobs from poor or nonconforming
  RFC1179 spooler programs, and fix them up to be comformant.  See
  ``Fixing Bad Jobs'' for details.

  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  :bk:  (BacKwards compatible) flag in the printcap for
  your printer.

  2.6.  What is RFC 1179, the Line Printer Daemon Protocol?

  RFC1179 defines a standard method by which print jobs can be
  transferred using the TCP/IP protocol between hosts.  The standard was
  developed by simply detailing the way that a version of the BSD LPD
  software did its job.

  From the RFC Introduction:

       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.


  Having said this, the RFC then goes on to describe the protocol used
  by a particular implementation of LPD.  The problem was that the RFC
  did not provide any way to put extensions to the operations into the
  system, and failed to specify such interesting details as the order in
  which print jobs and their components could be transferred.

  Comment by Patrick Powell  <papowell@astart.com> :


       Since 1988, there have been a large number of print spooling
       systems developed which claim RFC1179 conformance, but which
       are mutually incompatible.

       Rather than live with the limited capabilities of the
       RFC1179 standard, LPRng has extended them by adding
       capabilities to perform remote control of print spoolers,
       encrypted and authenticated data transfers, and other
       operations missing from the RFC1179 specification.  However,
       great effort was made to be backwards compatible with older
       and other LPD based systems.

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


  For a detailed explanation about LPRng and RFC1179, see ``RFC1179 and
  LPRng''.

  2.7.  I want to replace lp, lpstat, etc, but my programs need them

  LPRng was designed as a replacement the BSD printing system. As such,
  it inherited its command names and options from the latter. As you
  might know, System V uses a totally different set of commands,
  incompatible with the BSD ones.

  The good news is that the LPRng binaries include an emulation for the
  System V commands.  (See ``lp Simulation'' for details.  Briefly, you
  create links to the appropriate programs, and invoke them by the link
  names.  Actually, these links are installed by default in recent
  versions.



       ln -s lpr lp
       ln -s lpq lpstat
       ln -s lprm cancel




  If you make these links, calling lp, lpstat and cancel will give you a
  (partial) SVR4 emulation. They have their own man pages, which you
  should read if you need the emulation.

  Since it is a partial 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
  ``lp Simulation'' for more details or additional suggestions.

  2.8.  What are the drawbacks to LPRng?

  There are many reasons to run LPRng, and most of them are related to
  the extra features it has, compared to vanilla BSD LPR. A short list
  is given in section ``What is LPRng?''. (A more elaborate description
  can be found in the LPRng package itself.)

  On the other hand, there are also reasons not to switch to LPRng:

  o  It is a complex system, and you'll probably need a lot of time to
     get it working.

  o  Switching from a System V system will require even more work.  On
     the other hand, getting System V printing to work correctly for you
     may be even more work.

  o  You don't need any of the enhanced features, and are not worried
     about security issues.

  o  While there are many resources and books devoted to the old BSD
     printer daemon, documentation for LPRng is rather limited: apart
     from the Introduction documents in the distribution package,
     there's only this HOWTO (at least, to the best of my knowledge).


  You should take these elements into account while deciding whether to
  stick with your old software or not. If you do not decide in favor of
  LPRng, you can stop reading here.

  3.  Installing your printer

  In the following text, I will assume that your printer itself is
  working properly. I.e., you are able to send data to the printer in
  such a way that it responds by - well, printing the text :)

  How to ensure this is highly system-dependent and falls outside the
  scope of this document.  However,  here are some hints.

  3.1.  Parallel Printers

  Gordon Haverland <haverlan@agric.gov.ab.ca> supplied this little
  script, that will put you on the right track:


       #!/bin/sh
       #set -v -x              # uncomment for debugging
       PATH=/bin:/usr/bin
       printer=
       for $i in $*;
       do
           case $i in
               /dev/*) printer=$i ;;
               *) ;;
           esac
       done

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

       echo PRINTER TEST to $printer 1>&2
       for i in 1 2 3 4 5 6 7 8 9;
       do
           echo $i > $printer;
       done
       echo -e \\r\\f > $printer
       exit 0;




  If your printer is connected to the device name you provided, then you
  should get a page of something out.  If the output suffers from the
  ``staircase'' effect, you will see the numbers ``marching'' across the
  page, otherwise the numbers will all be in a single column.

  3.2.  Serial Printers

  If your printer is attached by a serial line, then you may need to set
  the serial line characteristictics before sending the job to the
  printer.  Here are a set of guidelines to following when attaching a
  serial port printer to a serial line.

  1. Check to make sure that the line is not enabled for login.  Logins
  are usually managed by the getty (BSD) or ttymon (Solaris, SystemV).
  Check your system documentation and make sure that these daemons are
  not managing the serial line.


  2. Check the permissions and ownership of the serial line.  For the
  most easy testing, set the permissions to 0666 (everybody can open for
  reading and writing).  After you have made sure that you can send jobs
  to the printer, you might want to change the ownership of the serial
  line to the LPD server and change the permissions to 0600.

  3. Make sure that you can print a test file on the printer via the
  serial port.  This may require setting the line characteristictics and
  then sending a file to the printer.  You should try to use 8 bit, no
  parity, with hardware flow control and no special character
  interpretation, and definitely no LF to CR/LF translation.  The
  problem is that different versions of UNIX systems have different sets
  of stty(1) commands to do this.  The following simple test script can
  help in this.


       #!/bin/sh
       # 9600, no echo, no CR
       FLAGS= 9600 -raw -parenb cs8 crtscts
       DEV= /dev/tty01
       (stty $FLAGS; stty 1>&2; cat $1 ) <$DEV >$DEV




  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.

  3.2.1.




  Converting BSD fc,fs,xc,xs  To LPRng stty

  Justin Mason <jmason@iona.ie> and Updated by Patrick Powell
  <papowell@sdsu.edu>:

  One of the worst things about old LPDs is that they expected you to
  give it line characteristictics as octal numbers, using the
  fc,fs,xc,xs printcap parameters. There are a number of disadvantages
  to this, compared to using the stty parameter.

  The stty format is portable, as it is supported on all UNIXes, rather
  than simply BSD-derived ones; it's also much easier to understand than
  the fc,fs,xc,xs format -- compare the two:


       # new format
           :stty=pass8 -parity tabs litout nl:
       # old format
           :fc#0000374:fs#0000003:xc#0:xs#0040040:




  Note, also, that sy uses the same symbolic names as the stty command,
  which is (relatively) well documented.

  Anyway, assuming you have a set of printcaps which use the fc,fs,xc,xs
  method, you can convert them to sy strings using the following table.


  Each parameter is an octal number, composed by logical-or'ing the bits
  for the desired parameters together.  The bit patterns below have been
  worked out from SunOS 4.1.2 /usr/include/sys/ttold.h.  For example,
  the following fc,fs,xc,xs set:



       :fc#0000374:fs#0000003:xc#0:xs#0040040:




  converts to the following sy string:


       :stty=-parity -echo -crmod -raw -lcase tandem cbreak litout decctq:




  Quite often, the resulting strings seem a little complicated, but
  usually they can be broken down using combination modes. See the
  printcap(5) manual page for details of which combination modes LPRng
  supports.

  In order to help you,  the 'xlate' program can be used to translate
  from the old to the new form.  For example:


       #> xlate ":fc#0000374:fs#0000003:xc#0:xs#0040040:"
       fc = 374
         clear LCASE (simulate lower case) try '-lcase'
         clear ECHO (echo input) try '-echo'
         clear CRMOD (map \r to \r\n on output) try '-crmod'
         clear RAW (no i/o processing) try '-raw'
         clear ODDP (get/send odd parity) try '-oddp'
         clear EVENP (get/send even parity) try '-evenp'
         clear ANYP (get any parity/send none) try '-parity? anyp? pass8? (caution)'
       fs = 3
         set TANDEM (send stopc on out q full) try 'tandem'
         set CBREAK (half-cooked mode) try 'cbreak'
       xc = 0
       xs = 40040
         set LITOUT (literal output) try 'litout'
         set DECCTQ (only ^Q starts after ^S) try 'decctlq'




  Note that when you clear odd and even parity,  then you get no parity
  or any parity.  You should use -parity to be compatible.  You might
  also want to add 'pass8' as well.  Thus, using the xlate output, you
  would try:


          :stty=-lcase -echo -crmod -raw -parity pass8 tandem cbreak litout decctlq:




  Bits used in the fc, fs parameters:





  TANDEM    00000001    /* send stopc on out q full */
  CBREAK    00000002    /* half-cooked mode */
  LCASE     00000004    /* simulate lower case */
  ECHO      00000010    /* echo input */
  CRMOD     00000020    /* map \r to \r\n on output */
  RAW       00000040    /* no i/o processing */
  ODDP      00000100    /* get/send odd parity */
  EVENP     00000200    /* get/send even parity */
  ANYP      00000300    /* get any parity/send none */
  NLDELAY   00001400    /* \n delay */
  NL0       00000000
  NL1       00000400    /* tty 37 */
  NL2       00001000    /* vt05 */
  NL3       00001400
  TBDELAY   00006000    /* horizontal tab delay */
    TAB0    00000000
    TAB1    00002000    /* tty 37 */
    TAB2    00004000
  XTABS     00006000    /* expand tabs on output */
  CRDELAY   00030000    /* \r delay */
      CR0   00000000
      CR1   00010000    /* tn 300 */
      CR2   00020000    /* tty 37 */
      CR3   00030000    /* concept 100 */
  VTDELAY   00040000    /* vertical tab delay */
      FF0   00000000
      FF1   00040000    /* tty 37 */
  BSDELAY   00100000    /* \b delay */
      BS0   00000000
      BS1   00100000
  ALLDELAY  00177400




  Bits used in the xc, xs parameters:


       CRTBS     00000001    /* do backspacing for crt */
       PRTERA    00000002    /* \ ... / erase */
       CRTERA    00000004    /* " \b " to wipe out char */
       TILDE     00000010    /* hazeltine tilde kludge */
       MDMBUF    00000020    /* start/stop output on carrier intr */
       LITOUT    00000040    /* literal output */
       TOSTOP    00000100    /* SIGSTOP on background output */
       FLUSHO    00000200    /* flush output to terminal */
       NOHANG    00000400    /* no SIGHUP on carrier drop */
       CRTKIL    00002000    /* kill line with " \b " */
       PASS8     00004000    /* pass 8 bits */
       CTLECH    00010000    /* echo control chars as ^X */
       PENDIN    00020000    /* tp->t_rawq needs reread */
       DECCTQ    00040000    /* only ^Q starts after ^S */
       NOFLSH    00100000    /* no output flush on signal */




  The following program translates the various bits to stty flags.








  #include <stdio.h>
  #include <string.h>
  struct bits{
    char *name;
    int bitfields;
    char *comment;
    char *try;
    int mask;
  };
  /* f flags - used with the TIOCGET and the struct sgttyb.sg_flags field */
  struct bits tiocget[] = {
  { "TANDEM",00000001, "send stopc on out q full", "tandem" },
  { "CBREAK",00000002, "half-cooked mode", "cbreak" },
  { "LCASE",00000004, "simulate lower case", "lcase" },
  { "ECHO",00000010, "echo input", "echo" },
  { "CRMOD",00000020, "map \\r to \\r\\n on output", "crmod" },
  { "RAW",00000040, "no i/o processing", "raw" },
  { "ODDP",00000100, "get/send odd parity", "oddp" },
  { "EVENP",00000200, "get/send even parity", "evenp" },
  { "ANYP",00000300, "get any parity/send none", "parity? anyp? pass8? (caution)" },
  { "NL0",0000000, "new line delay", "nl??",00001400 },
  { "NL1",00000400, "new line delay tty 37", "nl??",00001400  },
  { "NL2",00001000, "new line delay vt05", "nl??",00001400  },
  { "NL3",00001400, "new line delay", "nl??",00001400  },
  { "TAB0",00000000, "tab expansion delay", "tab??",00006000 },
  { "TAB1",00002000, "tab expansion delay tty 37", "tab??",00006000  },
  { "TAB2",00004000, "tab expansion delay", "tab??",00006000  },
  { "XTABS",00006000, "expand tabs on output", "tabs" },
  { "CR0",00000000, "cr??", "",00030000 },
  { "CR1",00010000, "tn 300", "cr??",00030000 },
  { "CR2",00020000, "tty 37", "cr??",00030000 },
  { "CR3",00030000, "concept 100", "cr??",00030000},
  { "FF1",00040000, "form feed delay tty 37", "ff??" },
  { "BS1",0010000, "backspace timing", "bs??" },
  { 0 } };
  /* x flags - used with the TIOCLGET and the struct sgttyb.sg_flags field */
  struct bits tiolget[] = {
  { "CRTBS",00000001, "do backspacing for crt", "crterase" },
  { "PRTERA",00000002, "\\ ... / erase", "prterase" },
  { "CRTERA",00000004, "\"\\b\" to wipe out char", "crterase" },
  { "TILDE",00000010, "hazeltine tilde kludge", "don't even think about this" },
  { "MDMBUF",00000020, "start/stop output on carrier intr", "crtscts" },
  { "LITOUT",00000040, "literal output", "litout" },
  { "TOSTOP",00000100, "SIGSTOP on background output", "tostop" },
  { "FLUSHO",00000200, "flush output to terminal", "noflsh?? (caution)" },
  { "NOHANG",00000400, "no SIGHUP on carrier drop", "nohand" },
  { "CRTKIL",00002000, "kill line with \"\\b\"", "crtkill" },
  { "PASS8",00004000, "pass 8 bits", "pass8" },
  { "CTLECH",00010000, "echo control chars as ^X", "echok" },
  { "PENDIN",00020000, "tp->t_rawq needs reread", "don't even think about this" },
  { "DECCTQ",00040000, "only ^Q starts after ^S", "decctlq? -ixany? (caution)" },
  { "NOFLSH",00100000, "no output flush on signal", "noflsh" },
  { 0 } };
  char *msg[] = {
   "xlate optionstrings",
   "  Example",
   "  xlate \":fc#0000374:fs#0000003:xc#0:xs#0040040:\"",
      0
  };
  usage()
  {
      char **m;
      for( m = msg; *m; ++m ){ fprintf( stderr, "%s\n", *m ); }
      exit( 1 );
  }
  main( argc, argv )
      int argc;
      char *argv[];
  {
      char *s, *end, *value;
      int c, v, set;
      struct bits *table;
      if( argc != 2 ) usage();
      for( s = argv[1]; s && *s; s = end ){
          end = strchr( s, ':' );
          if( end ){
              *end++ = 0;
          }
          while( (c = *s) && isspace( c ) ) ++s;
          if( c == 0 ) continue;
          /* now translate option */
          value = strchr( s, '#' );
          if( value == 0 ) usage();
          *value++ = 0;
          v = strtol( value, (char **)0, 0 );
          printf( "%s = %o\n", s, v);
          switch( s[0] ){
              case 'f': table = tiocget; break;
              case 'x': table = tiolget; break;
              default: usage();
          }
          switch( s[1] ){
              case 's': set = 1; break;
              case 'c': set = 0; break;
              default: usage();
          }
          /* now we scan down the values */
          for(; table->name; ++table ){
              if( (table->bitfields & v)
                  && ( ((table->bitfields & v) ^ table->bitfields)
              & (table->mask?table->mask:~0) ) == 0 ){
                  printf( "  %s %s (%s) try '%s%s'\n",
                      set?"set":"clear",
                      table->name, table->comment,
                      set? "":"-", table->try );
              }
          }
      }
  }




  3.3.  Network Printers

  If you have a network printer, then you may have several options.
  First, your printer may have built-in LPD server support.  However,
  this may not function as expected.  It has been observed that most of
  these builtin servers do not correctly handle large jobs or jobs with
  complex PJL, PCL, or PostScript with binary information.

  On the other hand, your printer may have a TCP/IP port that connects
  directly to the print engine.  For example, on the HP JetDirect cards,
  sending a print file to port 9100  will cause the print engine to
  process and print it.  In addition, this channel may provide status
  and other information during the printing process.  You can use the
  netcat utility by Hobbit <Hobbit@avian.org> to send files directly to
  the printer.  The simplest and easiest way to print a file to a
  network printer appears to be:  nc printer.ip.addr 9100 < file .



  4.  Installing the programs

  The basic components of the LPRng system are the executables and the
  database files.  This section deals with generating and installing the
  executable files.

  4.1.  Files and Setup

  The LPRng system can run in several different manners.  However, for
  most users it will require the executables for the server lpd, and the
  client applications for job submission - lpr, job status - lpq, job
  removal - lprm, and server management - lprc, and the /etc/lpd.conf
  and /etc/printcap files.

  By default, all the LPRng executables are installed in /usr/local/bin,
  which differs from other UNIX lpr systems, which scatter them in
  various hidden and arcane locations.  Note that the original printing
  system executables will need to be renamed or removed after installing
  LPRng.

  The ``/etc/lpd.conf'' file contains the configuration information for
  the server and application programs.  The LPRng system has a compiled
  in set of defaults that should be suitable for most user applications.
  In fact,  the default /etc/lpd.conf does not override any of the
  precompiled values.

  The ``/etc/printcap'' file contains the printer database information.
  This information can override the defaults in /etc/lpd.conf

  4.2.  Source Code and Installation

  If you have a binary distribution, you can skip this section. However,
  since LPRng is a rapidly evolving package, I would advise you to check
  whether there is a newer stable version available on one of the ``FTP
  sites.''  There should be a link to this stable version called LPRng-
  stable.tar.gz.

  You should also look at the ``System specific notes'' to see if there
  are any special things that you need to do for your system.

  4.2.1.  Building the software

  Before you start to build the software, you should read the README.1st
  and README.installation files in the distribution.  If you have GNU
  Make, do:


       ./configure;
         #if you want internationalization,
         # ./configure --enable-nls
       make clean all;
       su   # you must do the following commands as root
       make install
       # if  you have not installed LPRng before,
       # install default lpd.perms and lpd.conf file in /etc
       if [ ! -f /etc/lpd.perms ]; then
           make default;
       fi;
       # update permissions,  create files needed for LPRng, check
       # /etc/printcap file for problems.  Do as root:
       ./src/checkpc -f





  If you have BSD make do:


       ./configure;
         #if you want internationalization,
         # ./configure --enable-nls
       make -f Makefile.bsd clean all;
       su   # you must do the following commands as root
       make -f Makefile.bsd install
       # if  you have not installed LPRng before,
       # install default lpd.perms and lpd.conf file in /etc
       if [ ! -f /etc/lpd.perms ]; then
           make -f Makefile.bsd default;
       fi;
       # update permissions,  create files needed for LPRng, check
       # /etc/printcap file for problems.  Do as root:
       ./src/checkpc -f




  Use the configure --bindir option to specify the location of the
  binaries, or edit src/Makefile or src/Makefile.bsd after running
  configure.  The lines you have to change are:


       INSTALL_BIN =     ${exec_prefix}/bin
       # where daemons are installed: lpd
       #INSTALL_LIB =     ${prefix}/lib
       INSTALL_LIB =     ${prefix}/sbin
       # where maintenance commands are installed: checkpc, setstatus
       INSTALL_MAINT =   ${exec_prefix}/sbin




  By default, all LPRng executables are placed in /usr/local/bin.

  4.2.2.  Problems with compilation

  If you have problems compiling the package, you can try these things:

  1. Try gcc instead of your vendor's C compiler.  This is the standard
     compiler used for LPRng.  Almost without exception, if you have a
     ANSI C compatible compiler and libraries a POSIX compatible
     standard set of system support routines, LPRng will compile and run
     out of the box.  The main problems are with missing or modified
     system support routines, but configure will usually detect this and
     set flags to use suitable alternatives.

  2. The configure and the make should be run on the target host,
     especially if the target host has a different version of the
     operating system.  This is extremely important for SunOS or
     Solaris, where there tend to be changes in the system's include
     files between versions as well as support libraries.

  3. configure and the LPRng software tends to make the assumption that
     newer versions will not have the same problems that older versions
     have had.

  If you are not familiar with GNU configure, read the file INSTALL for
  instructions.

  Also read the notes for your OS in section ``System-dependent notes''
  for specific installation help (if any).

  4.3.  Preparing to run the daemon

  Don't be too impatient. Take your time to completely read through this
  HOWTO. Things will be a lot easier when you first set up the
  configuration files, and then start the new lpd.

  During the course of these steps, you will have to change some files.
  Be sure to keep a copy of the original file(s)!

  4.3.1.  Removing Existing Facilities

  Here is a summary and some scripts to help you prepare your site for
  running the server.

  1. Kill off the old server.


       BSD or Linux:
         ps -aux |grep lpd
         kill (pid of lpd server)
       System V:
         ps -ea |grep lpsched
         kill (pid of lpsched server)




  2. You should remove or rename the existing print system executables.
     Use the following script and examine the /tmp/candidates file for
     possible programs.  Remove or rename the non-LPRng versions of the
     programs.


       # you might want to track down the old lpr, lpq, lprm binaries
       find /usr -type file -name lp\* \; -print >/tmp/candidates
       find /sbin -type file -name lp\* \; -print >>/tmp/candidates

       # example rename
       mv /usr/libexec/lpd /usr/libexec/lpd.orig
       # example link
       ln -s /usr/local/bin/lpd /usr/libexec/lpd




  3. Try starting and running lpd before permanently installing it.  You
     should do the next steps as root.


       /usr/local/bin/lpd;                # start up LPD
       lpq;                               # test it with LPQ




  4.3.2.  Startup Scripts

  Don't forget to modify your system print startup files, i.e. - the
  /etc/rc file in most BSD based UNIX systems, or those in /etc/init.d
  or /sbin/init.d on System V and Linux.  You will need to have them
  reference the LPRng lpd and not the original system executable.  For
  systems that use the System V run-level scripts, you will also likely
  need to install a symbolic link in the default system run-level
  directory (perhaps /etc/rc3.d or /sbin/rc3.d) pointing to the master
  copy of the startup script in the init.d directory.

  Here is the core of a typical UNIX SystemV or LINUX startup file that
  can be used to start up and shut down the server.  Note that you will
  most likely need to modify the echo lines.


       #!/bin/sh
       case "$1" in
         start) # Start daemons.
           echo "Starting lpd: \c"; /usr/local/bin/lpd; bin/echo;
           ;;
         stop) # Stop daemons.
           echo "Shutting down lpd: \c"
           server=`/usr/local/bin/lpc -Pany@localhost lpd \
           | awk '{for(i=1;i<=NF;++i){v=$i+0;if(v>1){ print v;exit;}}}'`
           if [ -n "$server" ]; then
             echo " server pid $server";
             kill $server;
           else
              echo " no server active";
           fi;
           ;;
           *)
              echo "Usage: lpd {start|stop}"
              exit 1
            ;;
       esac




  4.4.  Replacing UNIX SystemV lp, lpstat Printing Services

  Many UNIX utilities in the Solaris and HP UNIX environment use the
  UNIX System V lp and lpstat programs.  It is almost impossible to
  modify their operation to use the LPRng lpr or lpq programs, as they
  depend on various return codes and information.

  Here are Patrick Powell's comments on this:

       After fighting with LP simulation,  I finally decided to
       make the interface part of the LPR/LPQ package.  If LPR is
       invoked as LP, then it will 'act' like a 'semi-compatible'
       LP; similarly for LPQ and LPSTAT, and LPRM and CANCEL.


  To get this functionality, you need to either make a symbolic link or
  a hard link to the appropriate executable.


       cd /usr/local/bin
       ln -s lpr lp
       ln -s lpq lpstat
       ln -s lprm cancel
       cd /usr/sbin
       ln -s /usr/local/bin/lpr lp
       ln -s /usr/local/bin/lpq lpstat
       ln -s /usr/local/bin/lprm cancel




  See the man pages for lp, lpstat, and cancel in the LPRng/man
  directory.  Note that not all the functions of the original lp
  programs are supported.  These man pages should be installed to
  replace the normal lp, etc, man pages.

  For some purposes,  the rather rugged lpstat simulation provided by
  this method does not work.  Garrett D'Amore <garrett@qualcomm.com> has
  written some much improved versions; take a look at
  http://people.qualcomm.com/garrett/ for details.

  4.5.

  Setuid ROOT and Security Issues

  By default, LPRng executables are installed setuid ROOT.  When
  running, they normally perform all operations with the user's
  effective UID, and perform all other operations set to the user and
  group specified by the user=daemon and group=daemon printcap entries,
  except for a very few places where they take extreme precautions
  against actions that could cause security breaches, change the EUID to
  ROOT, and then immediately change back to the normal operation.

  As a scan of the various CERT Security Advisories will indicate, many
  programs that run SUID root can be serious security loopholes.  While
  LPRng has been designed and implemented with security and paranoia in
  mind, there is still the possibility that user level or LPD processes
  can have an exposed security loophole.

  To reduce the risk, the user level utilities such as lpr, lprm, lpq,
  and lpc can be installed non-setuid.  This effectively closes several
  possible security loopholes.  To install the executables as non-
  setuid, the distribution src/Makefile must have the following lines
  commented out, and then LPRng must be reinstalled:


       edit src/Makefile
          # comment out the next line to have LPRng installed non-setuid
          PERMS=$(SUID_ROOT_PERMS)
       make install




  4.6.  System specific notes

  The following are a set of suggestions and recommendations for
  specific systems.

  4.7.  Solaris 2.4, 2.5, 2.6

  The Sun Solaris operating system is derived from the System V UNIX
  baseline.  Use the following installation procedure.

  1. First, install the LPRng software and then rename all of the
     existing Solaris print facilities.  See the ``Installation''
     section for details.  You should especially look out for lp,
     lpstat, lpsched, lpadmin, and other executables used by Solaris for
     print support.

  2. Next, make sure you update the /etc/rc startup files.  During the
     startup or initialization, Solaris will invoke a set of individual
     startup files.  You will find that the startupfile files are
     usually links to a common one in the /etc/init.d directory.








  ># grep -l lpsched /etc/rc* /etc/rc*/* init.d/* init.d/*/*
  /etc/rc0.d/K20lp
  /etc/rc2.d/K20lp
  /etc/rc2.d/S80lp
  /etc/init.d/lp
  ># ls -l /etc/rc0.d/K20lp
  lrwxrwxr-x  1 root  bin  1 Dec 29 23:39 /etc/rc0.d/K20lp -> ../../init.d/lp




  3. Replace the existing lp startup file with one similar to the
     ``startup script'' in the previous section.

  4. Check the /etc/inetd.conf file for a line like:


       printer stream tcp nowait root /usr/lib/print/in.lpd in.lpd





  and remove it if it is present.

  5. Reboot.  Don't try to be fancy and kill off processes, use the
     nlsadmin command, or other insanity.  This is brutal,  but appears
     to be necessary in order to ensure that the networking support is
     set up correctly.

  6. When the system restarts, try using lpq to check to see if the lpd
     server is active.

  James P. Dugal <jpd@usl.ed> has also makde the following suggestions
  as well.


       From: "Dugal James P." <jpd@usl.edu>
       To: lprng@iona.com
       Subject: Re: [LPRng] start up trouble

       Here are some more tips for Solaris:

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

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

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






  4.8.  Solaris, Newsprint and FrameMaker

  The following is a guide to using LPRng and Sun Microsystems Newsprint
  by Christopher Hylands, Ptolemy Project Manager of the University of
  California.

  The Sun Newsprint printer is actually an OEM version of the  Textronix
  PhaserII; Sun Microsystems appears to have dropped support for
  Newsprint, and the recommended migration path is to buy a PostScript
  printer.  If you want more information on using the Newsprint system,
  notes are available via
  http://ptolemy.eecs.berkeley.edu/~cxh/lprng.html.

  Looking through the mailing list logs, it looks like everyone was
  having a hard time getting lprng to work with Sun's braindead
  newsprinters.  I tried using ghostscript, but the fonts were, IMHO,
  ugly, so I spent a little time getting the newsprint fonts to work.

  The key thing was to grab the file /usr/newsprint/lpd/if from a
  SunOS4.1.3 newsprint installation.  If you cannot get this code, then
  the installation will be extremely difficult.

  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:


       chown daemon /devices/sbus@1,f8000000/SUNW,lpvi@1,300000:lpvi0




  We use the following simple if script.


       #/bin/sh
       # extremely simple filter script
       /bin/cat




  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  /etc/lp/printers/xsp524.

  The printcap entry looks like:


       sp524|524:
           :mx#0:sf:sh:sb:
           :lp=:rm=doppler:rp=xsp524:mx#0:
           :sd=/var/spool/lpd/sp524d:
           :lf=/var/spool/lpd/sp524d/log:
       xsp524|Sun SPARCprinter NeWSprint printer:
           :mx#0:sf:sb:sh:rs:
           :lp=/dev/lpvi0:
           :sd=/etc/lp/printers/xsp524:
           :lf=/etc/lp/printers/xsp524/log:
           :af=/var/spool/lpd/xsp524/acct:
           :if=/usr/local/lib/newsprint/if:


  The /usr/local/lib/newsprint/if was copied from /usr/newsprint/lpd/if
  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.

  1. First, set the path in the script.  You may also need to change
     defaults to suit your preferences:


       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




  2. You will also need a /etc/lp/printers/printername/.params 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.

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

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


       sunxm.s5.sparc)
           lpr -P"$PRINTER" "$FILE"
           #lp -c -d"$PRINTER" "$FILE"






       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)




  4.9.  Linux

  At the time of this writing (Jan 1998), the three major Linux
  distributions (Slackware, Red Hat and Debian) carry an older version
  of LPRng. Users of those systems should download the latest stable
  release, and install that instead of the distributed binaries.

  This is mostly important for Slackware 3.2 users, as this version
  installs LPRng by default.  Patrick Volkerding changed the default
  back to BSD LPR in Slackware 3.3, as many users had experienced
  problems because they didn't realize they weren't using the BSD
  software.

  Debian's dselect utility lets you choose between all packages. Amongst
  these are LPRng, as well as the traditional LPR software.
  You have to make sure your kernel is configured correctly. The
  documentation for the kernel sources in /usr/src/linux/Documentation/
  and the Kernel-HOWTO will help you to generate a new kernel if needed.
  You will need to set the following options:

  o  Networking support (CONFIG_NET)

  o  TCP/IP support (CONFIG_INET)

  o  If your printer is connected to a parallel port, you will also need
     the `Parallel Printer Support' (CONFIG_PRINTER).  You can use this
     as a module if you want.

  o  For a serial printer, answer `Yes' when asked if you want `Support
     for serial devices' (CONFIG_SERIAL). This is also available as a
     module.

  Once you have done this, the current releases of LPRng will install
  and run without problems.  See the ``Installing the programs'' section
  for details on how to install LPRng and deactivate the existing print
  support.

  You may need to update the printcap file and filters.  See
  ``/etc/printcap Print Spool Database File'' for details.

  4.10.  AIX

  This information was supplied by Dirk Nitschke, as of August 1997, and
  describes how to install the LPRng package on a workstation running
  AIX 4.1.x and possibly 3.x.x as well.  Dirk would be interested in any
  comments or corrections.

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

  OK, how to replace the AIX printing system? There is no group daemon
  on AIX. Therefore you have to change the default group for file
  ownership and process permissions. We decided to use the printq group.
  The user daemon exists on AIX but we have chosen lpd as the user who
  runs lpd and all filters and owns the spooling directories.  You can
  change the values for group, server_user and user in your lpd.conf
  file or in the sources src/common/default.c. This is an example for
  lpd.conf:


       # lpd.conf for AIX (change group, server_user and user)
       group=printq
       server_user=lpd
       user=lpd




  Compile and install the LPRng package. Create your printcap, spooling
  directories, accounting and logfiles and so on.  Don't forget to use
  ``checkpc'' to make sure that all the permissions are set correctly
  and the necessary files are created.

  Then stop all print queues defined on your workstation. Use


  # chque -q queuename -a "up = FALSE"




  for this (yes, blanks around = are needed).

  If you have local printers attached to your system you will have an
  lpd running. Stop this daemon using SMIT (Print Spooling, Manage Print
  Server, Stop the Print Server Subsystem). Choosing both also removes
  lpd from /etc/inittab. Maybe it's faster to do this by hand:


       # stopsrc -p'pid of /usr/sbin/lpd'
       # rmitab "lpd"




  Now delete all print queues (managed by qdaemon) defined on your
  system. You can use SMIT for this or the commands {mk,ch,rm}que,
  {mk,ch,rm}quedev, {mk,ch,rm}virprt. The SMIT fast path is smit rmpq.

  To start the new lpd at system startup you have to add an entry to
  /etc/inittab:


       # mkitab "lpd:2:once:/full/path/lpd"




  Some work has to be done if have have a local printer attached to your
  workstation. You have to create a device file like /dev/lp0. The SMIT
  fast path for this is smit mkdev. Choose Printer/Plotter and then
  Printer/Plotter Devices. Now Add a Printer/Plotter. To create a
  parallel printer device select the following:


       Plotter type:              opp Other parallel printer
       Printer/Plotter Interface: parallel
       Parent Adapter:            ppa0 Available




  Now define the characteristictics of the device:


       Port Number: p




  (p is for parallel).  Go to the field


       Send all characters to printer UNMODIFIED   no




  and select yes! We have had a lot of trouble with no.  This is very
  important! Expect erroneous output if you choose no. If you have
  already created a device file, change the characteristictics! SMIT's
  fast path is smit chdev.
  Finally remove all AIX printing commands like qprt, lp, cancel, lpr,
  lprm. You will find a lot of them in /usr/bin. Do not remove enq and
  friends if you want to use the general queueing facility.

  Now you can start your new lpd.

  4.11.  Appletalk Support

  Netatalk is used to communicate from TCP/IP to Appletalk printers and
  vice versa.  The netalk distribution FAQ is at:

  http://www.umich.edu/~rsug/netatalk

  There are two issues with using netatalk.  The first issue has to do
  with printing to LPRng-served printers from Macs.  The second issue
  has to do with printing from LPRng to network printers that speak
  AppleTalk.


  4.12.  Apple to LPRng Printing

  After you have installed and gotten netatalk working, you can use the
  following configuration file to print from a Macintosh to an LPRng
  printer.


       From edan@mtu.edu Mon Sep 29 21:31:25 1997
       Date: Tue, 30 Sep 1997 00:04:58 -0400 (EDT)
       From: Edan Idzerda <edan@mtu.edu>
       To: lprng@iona.com
       Subject: Re: [LPRng] Netatalk configuration file
       > Somebody posted a very nice Netatalk papd.conf file
       > that showed the setup for LPRng.  If anybody has this
       > handy could you post it?
       Well, *I* use:
       Your 32 Character Printer Name:\
               :pr=|/your/path/to/lpr -Pprintername
               :ppd=/your/path/to/ppd/files/yourprinter.ppd
       --
       Edan Idzerda    <edan@mtu.edu>
       System Administrator -- Michigan Technological University, Houghton MI USA




  4.13.  LPRng to Appletalk Printing

  The netatalk package comes with a PostScript filter called psf.  After
  compilation, it is in (e.g.) netatalk-1.4b2/etc/psf and will be
  installed in (e.g.) /usr/local/atalk/etc/.  After installation, there
  will also be a directory /usr/local/atalk/etc/filters that contains
  ifpap, ofpap, et al.  These are just symlinks to psf, and psf will do
  the appropriate thing based on how it was invoked.  If there's a 'pap'
  in the name, psf uses AppleTalk to talk to the printer.  See psf(8)
  for more information.

  A printcap entry for a network printer looks like the following:









  dave|Dave's 32 Character Printer Name:\
      :sd=/var/spool/dave:\
      :lf=/var/adm/lpd-errs:\
      :lo=lock:\
      :if=/usr/local/atalk/etc/filters/ifpap:\
      :of=/usr/local/atalk/etc/filters/ofpap:\
      :lp=/var/spool/dave/null




  There are three caveats to using netatalk this way.

  1. The first is that psf (i.e., the filters) needs to run as root.
     You can accomplish this in one of two ways.  The first is to
     uncomment the following line in src/Makefile and recompile:


       # ROOT_CFLAG=-DROOT_PERMS_TO_FILTER_SECURITY_LOOPHOLE





  The filter lines then need to look like the following:


           :if=ROOT /usr/local/atalk/etc/filters/ifpap:\
           :of=ROOT /usr/local/atalk/etc/filters/ofpap:\




  The alternative is to make psf setuid root.  To minimize the risk, you
  may want to make psf executable only by group daemon.  (I haven't
  tested the first option.  I'm currently using the second option.)


  2. The second caveat is that each network printer needs a .paprc in
     its spool directory.  For instance, /var/spool/dave/.paprc looks
     like this:


       Dave's 32 Character Printer Name:LaserWriter@Your AppleTalk Zone





  See psf(8) and pap(1) for more information.


  3. The third caveat concerns the lp line in the printcap entry.  For a
     single printer, this can be /dev/null.  If the host spools to more
     than one AppleTalk printer, you shouldn't use /dev/null for lp.
     You should use mknod to create a null device for each printer.  See
     psf(8).



       Chad Mynhier <mynhier@cs.utk.edu>
       Lab Engineer, CS Department
       University of Tennessee, Knoxville



  4.14.  SAMBA Support

  From the http://www.samba.org Web Site.


  4.14.1.  What is SMB

  This is a big question.

  The very short answer is that it is the protocol by which a lot of PC-
  related machines share files and printers and other information such
  as lists of available files and printers. Operating systems that
  support this natively include Windows NT, OS/2, and Linux and add on
  packages that achieve the same thing are available for DOS, Windows,
  VMS, Unix of all kinds, MVS, and more. There is no reason why Apple
  Macs and indeed any Web browser should not be able to speak this
  protocol, and current development (in which the Samba team is heavily
  involved) is aimed at exactly that. Alternatives to SMB include
  Netware, NFS, Appletalk, Banyan Vines, Decnet etc; many of these have
  advantages but none are both public specifications and widely
  implemented in desktop machines by default.

  The Common Internet Filesystem is what the new SMB initiative is
  called. For details watch http://www.samba.org.

  WHAT CAN SAMBA DO?

  Here is a very short list of what samba includes, and what it does.

  o  a SMB server, to provide Windows NT and LAN Manager-style file and
     print services to SMB clients such as Windows 95, Warp Server,
     smbfs and others.

  o  a Netbios (rfc1001/1002) nameserver, which among other things gives
     browsing support. Samba can be the master browser on your LAN if
     you wish.

  o  a ftp-like SMB client so you can access PC resources (disks and
     printers) from unix, Netware and other operating systems

  o  a tar extension to the client for backing up PCs

  For a much better overview have a look at the web site at
  http://www.samba.org and browse the user survey.

  Related packages include:

  o  smbfs, a Linux-only filesystem allowing you to mount remote SMB
     filesystems from PCs on your Linux box. This is included as
     standard with Linux 2.0 and later.

  o  tcpdump-smb, a extension to tcpdump to allow you to investigate SMB
     networking problems over netbeui and tcp/ip.

  o  smblib, a library of smb functions which are designed to make it
     easy to smb-ise any particular application.

  FTP SITE and WEB SITE

  The main anonymous ftp and web site for this software is found using
  http://www.samba.org.

  As well as general information and documentation, this also has
  searchable archives of the mailing list and a user survey that shows
  who else is using this package. Have you registered with the survey
  yet? :-)
  4.14.2.  Samba and LPRng

  The SAMBA code is very easy to configure.  See the SAMBA documentation
  for details,  but you only need to modify the samba.conf file and put
  in the pathnames of the LPRng facilities.  The following is a sample.



       From: Sascha Ottolski <alzhimer@cs.tu-berlin.de>
       Subject: Re: [LPRng] lprng-3.2.6 and smb on Linux
       webnut@conc.tds.net said:

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

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

       Note: the path= value specifies the spool directory where
       the print files are temporarily stored.  This should NOT
       be the LPRng spool directory,  but some other directory that
       Samba has write permissions for.






       From: "Pascal A. Dupuis" <dupuis@lei.ucl.ac.be>
       Subject: Re: LPRng-3.2.10 and SAMBA




  I include the smbprint script used to send stdin to a NetBEUI printer.


















  #!/bin/sh -x
  # This script is an input filter for printcap printing on a unix machine. It
  # uses the smbclient program to print the file to the specified smb-based
  # server and service.
  # For example you could have a printcap entry like this
  #
  # smb:lp=/dev/null:sd=/usr/spool/smb:sh:if=/usr/local/samba/smbprint
  #
  # which would create a unix printer called "smb" that will print via this
  # script. You will need to create the spool directory /usr/spool/smb with
  # appropriate permissions and ownerships for your system.
  #
  # The /usr/spool/smb/.config file should contain:
  #   server=PC_SERVER
  #   service=PR_SHARENAME
  #   password="password"
  #
  # Set these to the server and service you wish to print to
  # In this example I have a WfWg PC called "lapland" that has a printer
  # exported called "printer" with no password.
  #
  # E.g.
  #   server=PAULS_PC
  #   service=CJET_371
  #   password=""
  # Should read the following variables set in the config file:
  #   server, service, password
  config_file=.config
  eval `cat $config_file`
  # echo "server $server, service $service" 2>&1
  (
  # NOTE You may wish to add the line `echo translate' if you want automatic
  # CR/LF translation when printing.
  #       echo translate
      echo "print -"
      cat
  ) | /usr/local/bin/smbclient "\\\\$server\\$service" \
     $password -U $server -N -P
  # comment preceeding line and uncomment following
  # to just test for correct filter working
  #) | cat > /dev/null




  4.15.  Printer Specific notes

  This is a small collection of miscellaneous notes about printers and
  applications.

  4.16.  HP Deskjet















  From: jarausch@igpm.rwth-aachen.de (Helmut Jarausch)
  Subject: Re: Using gs (ghostscript) as a filter?
  To: lprng@iona.com
  Cc: Rick Gaine <rgaine@nbcs.rutgers.edu>
  Sender: majordomo-owner@iona.com
  Reply-To: lprng@iona.com
  >>
  >> Hello All:
  >>
  >> I would like to use LPRng 3.1.4 with an HP LaserJet 4P.  I'd like to be
  >> able to use gs to convert PostScript files so that I can print them on my
  >> HP 4P.  Can I do this with LPRng?  If so, could someone semd me a printcap
  >> entry?  I'd appreciate it.  I am not sure how I will be cconnecting the
  >> printer yet, but I am thinking either serial or network.  Probably serial
  >> though.  Thanks for any help.




  This printcap works for my Deskjet:


       djps
           :cm=Local Deskjet(GhostScript)
           :sd=/var/spool/djps:sf:sh:mx#0
           :lp=/dev/plp
           :if=/usr/LOCAL/bin/LPRng/ps_to_deskjet:




  and this is the script /usr/LOCAL/bin/LPRng/ps_to_deskjet


       #!/bin/sh
       nice -19 /usr/LOCAL/bin/gs -sDEVICE=cdj550 -sPAPERSIZE=a4 -sOutputFile=- -q -r300 -






       Helmut Jarausch
       Lehrstuhl f. Numerische Mathematik
       Institute of Technology
       RWTH Aachen
       D 52056 Aachen, Germany




  4.17.  HP LaserJet IIISiMX














  > From majordomo-owner@iona.com Mon Aug 31 11:17:26 1998
  > To: lprng@iona.ie
  > Subject: [LPRng] problems printing PS-level2 jobs on LJIIIsi's...
  > Date: Mon, 31 Aug 1998 15:06:22 -0400
  > From: "John Saroglou" <johny@yorku.ca>
  >
  > Greetings...
  >
  > I'm wondering if someone got around the problem of printing
  > Postscript(R) Level 2  jobs on Laser Jet IIIsi printers.
  > Our printers are direct network printers talking to a
  > print server running solaris 2.6 and lprng-3.5.1.
  >
  > Is there a fix (possible drivers?) for such problem?
  >
  > Thanks in advance.




  The LaserJet IIISi does not support PostScript level 2, only level 1
  (really, it is called 3SiMX).  The Windows (you are under Windows,
  right?) HP driver for 3Si/3Si MX PostScript should produce only PS
  level 1.

  Beware: latest version of Adobe Windows PS driver produces *ONLY* PS
  level 2.

  So, if you have (or receive) level 2 files, read them using
  ghostscript and print a screen dump :-) or as a bitmap.

  You can too convert them into PDF (using either Adobe distiller or
  ghostscript ps2pdf) then use acroread to print the result. acroread
  can produce either level 1 or level 2 PostScript. THE definite
  solution !

  This trouble has nothing to do with the way they are connected or
  driven, it is only a driver problem.

  I believe that HP had once a PS level2 update, but the price was so
  high that bying a new printer was a better solution!


       Bertrand
       --
       | Bertrand DECOUTY              | mailto:Bertrand.Decouty@irisa.fr   |
       | IRISA - INRIA (Atelier)       | PHONE : 0299847346 / 0299847100    |
       | Campus de Beaulieu            | FAX   : +33 (0) 299842534          |
       | F-35042 Rennes Cedex - FRANCE | http://www.irisa.fr/               |




  <!

  4.18.  HP Deskjet 1600CM

  The following printcap entry was originally posted by Olaf Lotzkat
  (Sysadm), Faculty of Computer Science, TU Dresden, Germany
  >Olaf_Lotzkat@inf.tu-dresden.de<, and has been update for the ifhp
  filter.





  # HP DeskJet 1600CM
  tinte|:
      :lp=dj1600cm%9100:
      :mx#0:rw:sf:
      :ps=status:af=acct:lf=log:sd=/lpspool/tinte:fx=flpv:
      :if=/usr/local/lib/filters/ifhp ????
      :of=/usr/local/lib/filters/ofhp \
       -Tpagecount=off,forcepagecount=on,infostatus=off,sync=off,banner=off:
      :vf=/usr/local/lib/filters/ifhp -c:
      :bp=/usr/local/lib/filters/psbanner:




  >

  4.19.  HP JetDirect Interface

  The  HPJetDirect  card  can  be configured through the front panel  or
  through a set of network files.  Here is a summary of  the  methods
  used  from  UNIX  systems, or when you are desperate, to configure the
  printer.

  4.19.1.  Setting Up IP Networking and Address

  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:


        MENU  -> MIO MENU (use MENU to display MIO MENU)
        ITEM  -> CFG NETWORK=NO*
        +     -> CFG NETWORK=YES
        ENTER -> CFG NETWORK=YES*
        ITEM  -> TCP/IP=OFF* (use ITEM to display TCP/IP)
        +     -> TCP/IP=ON
        ENTER -> TCP/IP=ON*
        ITEM  -> CFG TCP/IP=NO* (use ITEM to display TCP/IP)
        +     -> CFG TCP/IP=YES
        ENTER -> CFG TCP/IP=YES*
        ITEM  -> BOOTP=NO*
            (Enable BOOTP if you want to - see below)
        ITEM  -> IP BYTE 1=0*
            This is IP address MSB byte.
            Use +- keys to change value, and then ENTER to change
            Use ITEM keys to get IP BYTE=2,3,4
        ITEM  -> SM BYTE 1=255*
             This is the subnet mask value
            Use +- keys to change value, and then ENTER to change
            Use ITEM keys to get IP BYTE=2,3,4
        ITEM  -> LG BYTE 1=255*
            This is the Syslog server (LoGger) IP address
            Use +- keys to change value, and then ENTER to change
            Use ITEM keys to get IP BYTE=2,3,4
        ITEM  -> GW BYTE 1=255*
            This is the subnet gateway (router) IP address
            Use +- keys to change value, and then ENTER to change
            Use ITEM keys to get IP BYTE=2,3,4
        ITEM  -> TIMEOUT=90
             This is the connection timeout value.  It puts a limit
            on time between connections.  A value of 10 is reasonable.





  4.19.2.  BOOTP Information

  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.


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




  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.












  #
  # Example HP Network Peripheral Interface configuration file
  #
  # Comments begin with '#' and end at the end of the line.
  # Blank lines are ignored.  Entries cannot span lines.

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

  name: picasso

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

  location: 1st floor, south wall

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

  contact: Phil, ext 1234

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

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

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

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

  idle-timeout: 120

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

  get-community-name: blue

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

  set-community-name: yellow

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

  trap-community-name: red

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

  trap-dest: 15.1.2.3
  trap-dest: 15.2.3.4

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

  authentication-trap: on

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

  syslog-facility: local2

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

  subnets-local: on

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

  old-idle-mode: off



  4.19.3.  Paper Tray Selection

  Be careful with your paper tray selection.  You should configure the
  printer,  using the front panel switches,  to select the FIRST paper
  tray.  See your printer documentation on this.  Unfortunately,
  different models of HP printers have different methods of handling
  paper trays.

  4.20.  Lexmark Printers

  Some Lexmark printers do not send end of job status back unless
  configured to do so.  Here is what is needed to force this.


       Date: Wed, 21 Jan 1998 18:25:50 -0600 (CST)
       From: Matt White <whitem@bofh.usask.ca>
       To: lprng@iona.com
       Subject: Re: [LPRng] ifhp with Lexmark Optra N printer

       On Wed, 21 Jan 1998, Simon Greaves wrote:

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

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

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

       Basically, it causes the printer to wait until it is finished printing
       before actually reporting that it is done.  I've got 3 Optra S printers
       running with ifhp right now with no extra options (just defaults).

       ---------------------------------------------------------------------
       - Matt White                         whitem@arts.usask.ca           -
       - Network Technical Support          http://arts.usask.ca/~whitem   -
       - College of Arts & Science          University of Saskatchewan     -
       ---------------------------------------------------------------------





  4.21.  Tektronix P450 and Family

  The Tektronix P450 has a very odd network interface.  You can open a
  TCP (stream) connection to port 9100 and send a file to be printed on
  the connection.

  When a UDP datagram is sent to UDP Port 9101, the printer resonds with
  status information.  This apparently is the poor man's SNMP, but I
  digress.  Here is a clever implementation of a filter that handles
  this printer.


  From: Russ Thacher <thacher@brl.uiuc.edu>
  To: lprng@iona.com
  Subject: Re: [LPRng] Tektronix P450 & psfilter

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

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

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

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

  --
  Russ Thacher
  Systems Administrator, UIUC Bioacoustics Research Lab

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

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

  pop @ARGV;

  # Get all the filter options LPRng knows

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

  # set default exit status (JFAIL)

  $! = 1;

  # Set default error messages

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

  # Get current time/date

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

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

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

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

  # Setup network info for printers

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

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

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

  $myip = gethostbyname(hostname());

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

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

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

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

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

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

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

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

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

  # Get the initial page count

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

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

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

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

  # open LPRng accounting file

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

  # Start shoving data out to printer

  # Set print/transparency by queue name

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

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

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

  close TCPSOCK or die $tcpcloseerr;

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

  $realudpout = "";

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

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

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

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

  close TCPSOCK or die $tcpcloseerr;

  # Get date/time again

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

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

  # Update accounting file upon close

  $pages = $pagecount2 - $pagecount1;

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

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

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





  4.22.  Duplex Printing

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

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

  2. Check your printer manual to discover the EXACT form of the enter
     duplex mode 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.

     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.

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

  4. Read the IFHP documentation, and create a configuration section in
     the ifhp.conf file for your printer.

  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.

       Patrick Powell


  4.23.  Test Version  and Portability Testing

  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 Test Version to be run in parallel with the normal LPRng
  software.

  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 (/tmp
  is common) and the LPRng software tested.

  The test version of the software will use the LPD_CONF 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.

  4.23.1.  Compiling the Test Version

  Edit src/Makefile, and uncomment the indicated line.  Then run make to
  regenerate the distribution.


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




  4.23.2.  Setting Up The Test Version Spool Queues

  The LPRng TESTSUPPORT directory contains a set of shell scripts and
  files that need to be installed in the appropriate directory.  The
  following steps are used.
  1. First, you need to set up your HOST environment variable to the
     fully qualified domain name of your host and your USER environment
     variable to your user name.  This is done in order to get values to
     put into the Test Version configuration files.

  2. In the TESTSUPPORT directory, edit the Makefile, and specify the
     location of the Test Version spool queues.  The default location is
     /tmp; since on most systems these files are deleted or are
     available to everybody, a more secure location should most likely
     be used.  DO NOT USE THE RAW TESTFILE DIRECTORY.  These files need
     to be copied and placed in another directory.

  3. The LPD_CONF environment variable should be set to the location of
     the installed lpd.conf file.

  4. In the TESTSUPPORT directory, run make.  This will copy and install
     the necessary files.

  Example:


         CSH:
           setenv HOST {fully qualified domain name};
           setenv USER `whoami`
           setenv LPD_CONF /tmp/LPD/lpd.conf
           set path=( /tmp/LPD $path )
           unsetenv PRINTER
          Example:
             setenv HOST astart1.astart.com
             setenv USER papowell
             setenv LPD_CONF /tmp/LPD/lpd.conf
             set path=( /tmp/LPD $path )
             unsetenv PRINTER
         Bourne Shell:
           HOST={fully qualified domain name}; export HOST;
           USER='whoami'; export USER
           LPD_CONF=/tmp/LPD/lpd.conf.$HOST; export LPD_CONF
           PATH=/tmp/LPD:$PATH; export PATH
           PRINTER=; export PRINTER
          Example:
             HOST=astart1.astart.com; export HOST
             USER=papowell; export USER
             LPD_CONF=/tmp/LPD/lpd.conf.$HOST; export LPD_CONF
             PATH=/tmp/LPD:$PATH; export PATH
             PRINTER=; export PRINTER
         cd TESTSUPPORT
         make




  4.23.3.  Running the Test Version Software

  Set your current directory to the location of the compiled Test
  Version executables.  Execute the various executables using ./cmd, or
  set .  as the first entry in the PATH .  If it is not the first entry,
  then the standard system executables will be used.

  1. Run ./checkpc.  this will print out the various values for the
     spool queues in the Test Version setup.  If the t1, t2,... spool
     queues are not displayed, make sure that the LPD_CONF environment
     variable is set correctly and that you are using the Test Version
     executable.

  2. Run ./checkpc -f.  This will fix up the (deliberately introduced)
     problems in the spool queues.
  3. Next,  run ./lpd -F in one window, and then run ./lpq -a  in
     another window.  This will check that the server is working.

  4. You can now amuse yourself by sending jobs, setting up permissions
     checking, and other chores.

  5. When everything appears to be working correctly, you can then
     remove the Test Version flag from the src/Makefile, recompile, and
     install the LPRng software.

  4.23.4.  Portability Testing

  You should ignore the information in this section UNLESS you are
  trying to do a port to a new or whacko version of a UNIX system.
  LPRng has been tested on just about every version of UNIX that
  supports POSIX capabilities, and if it is having problems then most
  likely it is due to non-portability issues.  However, if you feel that
  your system is not POSIX compatible, or you are having serious
  problems due to LPRng's use of the system facilities, feel free to try
  the following tests.

  Needless to say, if you identify problems,  please inform the
  developers and they will most likely assist you in resolving them.

  Set your current directory to the location of the compiled Test
  Version executables.  Execute the various executables using ./cmd, or
  set .  as the first entry in the PATH .  If it is not the first entry,
  then the standard system executables will be used.

  1. Run ./checkpc -T /tmp/a.  This will perform a limited set of tests
     of the LPRng functionality.  Note that some of them will fail as
     checkpc is not running SUID ROOT and the /tmp/a is not a serial
     device.  ALL of the non-SUID related messages should indicate
     success.

  2. Set checkpc to setuid ROOT, and then rerun the tests.


       chown root checkpc
       chmod u+s checkpc
       ./checkpc -T /tmp/a





  The SETUID tests should now succeed.  If they do not, then you have a
  VERY odd UNIX system, and you are on your own on this one.  See the
  comments in src/common/setuid.c for help.

  3. Now run tests for serial line control and locking:


       ./checkpc -T /dev/ttya   # or an appropriate UNUSED tty device





  You most likely will have to attach a terminal or modem to the serial
  device in order to cause the open() to succeed, as most serial device
  drivers block when DSR is not enabled.  Check the messages concerning
  the stty actions.  Make sure that the appropriate changes have taken
  place.

  You may get errors about device lock failing.  This is due to whacko
  differences in the ways that different UNIX systems (or versions of
  the same UNIX system) implement serial device locking.  My advice is
  to ignore this problem unless you INSIST on having multiple users of
  the same serial printing port, in which case you are asking for
  serious trouble, and you are on your own.  I am not interested in
  patches or queries on problems on serial device locking problems.  In
  fact, this facility is only used if the lk printcap flag is TRUE
  (default FALSE).

  5.  /etc/printcap Print Spool Database File

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

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

  5.1.

  Index To All The Options



  5.2.


  A very simple example

  Options used:

  o  cm=comment for status

  o  if=default job file filter

  o  lf=log file

  o  mx#maximum job size

  o  lp=output device

  o  sd=spool directory file

  o  sh  suppress headers (banners)

  o  sf  suppress form feeds between files

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



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




   Option                               Meaning
   ``ab''                               always print banner, ignore lpr -h option
   ``achk''                             query accounting server when connected
   ``ae''                               accounting at end (see also af, la, ar, as)
   ``af''                               name of accounting file (see also la, ar)
   ``ah''                               automatically hold all jobs
   ``allow_duplicate_args''             Allow duplicate command line arguments (legacy requirement)
   ``allow_getenv''                     Allow use of LPD_CONF
   ``allow_user_logging''               allow users to request logging info using lpr -mhost%port
   ``ar''                               enable remote transfer accounting (if af is set)
   ``as''                               accounting at start (see also af, la, ar)
   ``use_auth''                         authentication type to use
   ``auth_client_filter''               client to server authentication transfer program
   ``auth_forward''                     server to server authentication method
   ``auth_forward_filter''              server to server authentication transfer program
   ``auth_receive_filter''              server receive authentication program
   ``auth_server_id''                   server id for authentication
   ``be''                               Banner at End Generation Program
   ``bk''                               Berkeley LPD job file format
   ``bk_filter_options''                Berkeley LPD filter options
   ``bk_of_filter_options''             Berkeley LPD OF filter options
   ``bkf''                              backwards-compatible filters: use simple paramters
   ``bl''                               short banner line sent to banner printer
   ``bp''                               Banner Generation Program (see bs, be)
   ``bq''                               Use filters on bounce queue jobs
   ``bq_format''                        Format of bounce queue output
   ``br''                               Serial port bit rate (see ty)
   ``bs''                               Banner at Start Generation Program
   ``cd''                               control directory
   ``check_for_nonprintable''           LPR checks for nonprintable file
   ``check_idle''                       program used to check for idle printer
   ``class_in_status''                  Show job class name in lpq status information
   ``cm''                               comment identifying printer (LPQ)
   ``config_file''                      configuration file
   ``connect_grace''                    connection control for remote printers
   ``connect_interval''                 connection control for remote printers
   ``connect_timeout''                  connection control for remote printers
   ``connect_try''                      connection control for remote printers
   ``control_filter''                   control file filter
   ``db''                               debug options for queue
   ``default_format''                   default job format
   ``default_permission''               default permission for files
   ``default_printer''                  default printer
   ``default_priority''                 default job priority
   ``default_remote_host''              default remote host
   ``default_tmp_dir''                  default directory for temp files
   ``destinations''                     printers that a route filter may return and we should query
   ``fc''                               OBSOLETE, see sy
   ``fd''                               forwarded jobs not accepted
   ``ff''                               string to send for a form feed
   ``filter_ld_path''                   filter LD_LIBRARY_PATH value
   ``filter_options''                   filter options
   ``filter_path''                      filter PATH environment variable
   ``filter_poll''                      interval to check for OF filter output
   ``fo''                               send form feed when device is opened
   ``force_fqdn_hostname''              force FQDN HOST value in control file
   ``force_localhost''                  force clients to send all requests to localhost
   ``force_queuename''                  force use of this queuename if none provided
   ``fq''                               send form feed when device is closed
   ``fs''                               OBSOLETE (see sy)
   ``full_time''                        use extended time format
   ``fx''                               valid output filter formats
   ``group''                            Effective Group ID (EGID) for SUID ROOT programs
   ``hl''                               Header (banner) last, at end of job
                                        Ignore control file and data file name format errors
  |``ignore_requested_user_priority'' | Ignore requested user priority                              |
  |``if''                             | default (f, l) filter program                               |
  |``ipv6''                           | using IPV6 conventions                                      |
  |``kerberos_keytab''                | kerberos keytab file location                               |
  |``kerberos_life''                  | kerberos key lifetime                                       |
  |``kerberos_renew''                 | kerberos key renewal time                                   |
  |``kerberos_forward_principal''     | kerberos remote principle name for forwarding               |
  |``kerberos_server_principal''      | kerberos remote server principle name                       |
  |``kerberos_service''               | kerberos default service                                    |
  |``la''                             | enable local printer accounting (if af is set)              |
  |``ld''                             | leader string sent on printer open                          |
  |``lf''                             | error log file for spool queue                              |
  |``lk''                             | lock the IO device                                          |
  |``lockfile''                       | lpd lock file                                               |
  |``logger_destination''             | destination for logging information                         |
  |``logger_timeout''                 | intervals between connection attempts                       |
  |``logger_pathname''                | temp file for log information                               |
  |``logger_max_size''                | max size in Kbytes of temp file for log information         |
  |``longnumber''                     | use long job number when a job is submitted                 |
  |``lp''                             | printer device name or specification                        |
  |``lpd_bounce''                     | force lpd to filter job before forwarding                   |
  |``lpd_force_poll''                 | force lpd to poll idle printers                             |
  |``lpd_poll_time''                  | interval between lpd printer polls                          |
  |``lpd_port''                       | lpd listening port                                          |
  |``lpd_printcap_path''              | lpd printcap path                                           |
  |``lpr_bounce''                     | lpr does filtering as in bounce queue                       |
  |``lpr_bsd''                        | lpr does filtering as in bounce queue                       |

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

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

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

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

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

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


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

   ``mail_from''                mail user from user name
   ``mail_operator_on_error''   mail to this operator on error
   ``max_connect_interval''     maximum time between connection attempts
   ``max_log_file_size''        maximum size (in K) of spool queue log file
   ``max_servers_active''       maximum number of lpd queue servers that can be active
   ``max_status_line''          maximum length of status line
   ``max_status_size''          maximum size (in K) of status file
   ``mc''                       maximum copies allowed
   ``min_log_file_size''        minimum size (in K) of spool queue log file
   ``min_status_size''          minimum size to reduce status file to
   ``minfree''                  minimum amount of free space needed
   ``ml''                       minimum number of printable characters for printable check
   ``ms_time_resolution''       millisecond time resolution
   ``mx''                       maximum job size (1Kb blocks, 0 = unlimited)
   ``nb''                       use nonblocking device open
   ``network_connect_grace''    pause between transferring jobs to remote printer
   ``of''                       banner output filter
   ``of_filter_options''        OF filter options
   ``originate_port''           originate connections from these ports
   ``pass_env''                 clients pass these environment variables to filters
   ``perms_path''               lpd.perms files
   ``pl''                       page length (in lines)
   ``pr''                       pr program for p format
   ``printcap_path''            /etc/printcap files
   ``ps''                       printer status file name
   ``pw''                       page width (in characters)
   ``px''                       page width in pixels (horizontal)
   ``py''                       page length in pixels (vertical)
   ``qq''                       put queue name in control file
   ``remote_support''           operations allowed to remote host
   ``report_server_as''         server name for status reports
   ``retry_econnrefused''       Retry on connect ECONNREFUSED errors
   ``retry_nolink''             Retry device open or connect failures
   ``return_short_status''      return short lpq status when request arrives from specified host
   ``reuse_addr''               set SO_REUSEADDR on outgoing ports
   ``reverse_lpq_format''       reverse lpq format when request arrives from specified host
   ``rg''                       clients allow only users in this group access to printer
   ``rm''                       remote machine (hostname) (with rp)
   ``router''                   routing filter, returns destinations
   ``rp''                       remote printer name (with rm)
   ``rw''                       open printer for reading and writing
   ``safe_chars''               additional safe characters in control file lines
   ``save_on_error''            save job when an error
   ``save_when_done''           save job when done
   ``sb''                       short banner (one line only)
   ``sd''                       spool directory pathname
   ``send_block_format''        send block of data, rather than individual files
   ``send_data_first''          send data files first in job transfer
   ``send_failure_action''      failure action to take after send_try attempts failed
   ``send_job_rw_timeout''      print job read/write timeout
   ``send_query_rw_timeout''    status query operation read/write timeout
   ``send_try''                 maximum number of times to try sending job
   ``sendmail''                 sendmail program
   ``server_tmp_dir''           server temporary file directory
   ``sf''                       suppress form feeds separating data files in job
   ``sh''                       suppress header (banner) pages
   ``short_status_length''      short lpq status length in lines
   ``socket_linger''            set the SO_LINGER socket option
   ``spool_dir_perms''          spool directory permissions
   ``spool_file_perms''         spool file permissions
   ``spread_jobs''              amount to spread jobs to avoid collisions
   ``ss''                       name of queue that server serves (with sv)
   ``stalled_time''             time after which to report active job stalled
   ``stop_on_abort''            stop processing queue on filter abort
   ``stty''                     stty commands to set output line characteristictics
  |``sv''                     | names of servers for queue (with ss)                             |
  |``syslog_device''          | name of syslog device                                            |
  |``tr''                     | trailer string to send before closing printer                    |
  |``translate_format''       | translate data format in control file                            |
  |``use_date''               | force date in control file                                       |
  |``use_identifier''         | force identifier in control file                                 |
  |``use_info_cache''         | read and cache information                                       |
  |``use_queuename''          | put queue name in control file (alias for qq)                    |
  |``use_shorthost''          | Use short hostname for lpr control and data file names           |
  |``user''                   | Effective User ID (EUID) for SUID ROOT programs                  |
  |``xc''                     | OBSOLETE (see sy)                                                |
  |``xs''                     | OBSOLETE (see ty)                                                |
  |``xt''                     | formats supported on printer                                     |

  maintain these types of files.


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


  1. First of all, there's the lp entry. It specifies the file (or
     location) to which data is sent.  Here, it is the device /dev/lp1.
     (This is system-dependent---check your vendor's manual for
     information on devices.) The absolute pathname indicates we have a
     local printer or file.

  2. The cm field can be used to supply a comment to be used as a header
     in lpq output.

  3. sd specifies the spool directory.  This is where files are stored
     until they are printed.

  4. The lf and af options specify the location for the log and
     accounting files, respectively. They are not strictly needed for
     the printer to function, but a log file is highly recommended, in
     case things go wrong.  One special point to note: if these files
     don't exist, they will not be created, and no logging or accounting
     will be done. You will need to create them manually (e.g., by using
     touch) or by using the `` <tt>checkpc</tt>'' program.  LPRng is
     very fussy about permissions - you should run checkpc to make sure
     that the log and status files have the correct ownership and
     permissions.

  5. The if entry specifies a `filter' for the `text' format. The filter
     will be applied to any incoming job (of a certain format), even the
     ones coming from another host.  Filters and print formats are
     discussed in section ``Filters''. This particular filter would
     translate UNIX LF line endings to DOS CR/LF, to prevent the
     `staircase effect' and expand tabs to spaces.  As this problem also
     occurs with other printing daemons, it is likely that your system
     has a similar utility.

  6. mx indicates the maximum file size for a print job.  Supplying 0
     here means that there is no limit.

  7. Finally, I have given the sh (suppress headers) flag. This will
     inhibit banner pages.

  5.3.

  A serial printer queue

  Options used:

  o  br#serial port bit rate

  o  stty=stty options for serial port configuration

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


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




  5.4.  A remote printer queue

  Options used:

  o  lp=destination

  o  rm=remote host (machine)

  o  rp=remote printer (machine)

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

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

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

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

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


       lpr -Ppr@server printfile




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


       PRINTER=pr@server; export PRINTER;
       lpr printfile




  5.5.  Network Printing With /etc/printcap

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

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

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



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




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

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

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

  5.6.  Spooling To Local Server

  Options used:

  o  force_localhost  force clients to send requests to localhost

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


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




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

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

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

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

  5.7.  Shared printcap files

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


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




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

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


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




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


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




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


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


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




  5.8.  Master Printcap Files

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

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


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




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

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

  You can also specify network addresses and subnet masks as well.  In
  this case, if the host matches the network address then it will use
  the information.
  5.9.  Printcap options for lpr, lpq, lprm, and lpc

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

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

  5.9.1.





  lp, rm and rp

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

  1. If the user program is invoked with -Pxxx argument, then the lp
     option is assigned the value xxx.

  2. If no explicit value is specified and the PRINTER environment
     variable has value xxx, then the lp option is assigned value xxx.

  3. If lp has a value of the form rp@rm or rp@rm%port, then the rp, rm,
     and lpd_port options are assigned the indicated values.

  4. If rm or rp does not have a value, then they are assigned the
     default_host (usually localhost) and default_printer (usually lp)
     option values.

  5. A connection is made to lpd_port on host rm and the file transfer
     or command is sent as specified in ``RFC1179''.

  See the ``Opening Output Device'' section for additional details.

  5.10.



  Bounce queues

  Options used:

  o  lpd_bounce=lpd does filtering and then forwards

  o  bq=destination for processed job

  o  bq_format=format of processed job

  Normally, when using a remote queue, the print job is transmitted to
  the server computer without any modifications.  There are
  circumstances when modifications must be made to a job before
  forwarding, such as the desire to convert a job to the format
  acceptable by the remote printer or to add banner pages.

  LPRng supports this capability by specifing that a print queue is
  actually a bounce queue rather than a simple forwarding queue.  The
  term bounce queue came from the notion that jobs will bounce through
  this print queue, getting processed by the specified filters, and the
  filter output will then be forwarded.
  LPRng will perform all of the usually job processing steps, such as
  banner generation, filtering files, etc, saving the output text in a
  file.  This file is then sent to the destination print queue for
  further processing.

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



       # Simple example of a bounce queue
       bounce:lp=bounce@bouncehost
       bounce:server
           :bq=lp@remote
           :sd=/usr/spool/lpd/bounce
           :if=/usr/local/bin/lpf
           :vf=/usr/local/bin/lpf
           :bq_format=l
           # uncomment ab if you want banner
           #ab
       # Even Simpler example of a bounce queue
       bounce:lp=bounce@bouncehost
       bounce:server
               :lpd_bounce
           :lp=lp@remote
           :sd=/usr/spool/lpd/bounce
           :if=/usr/local/bin/lpf
           :vf=/usr/local/bin/lpf
           :bq_format=l
           # uncomment ab if you want banner
           #ab




  Some comments:


  1. For clients, lp=bounce@bouncehost entry specifies the queue and
     hostname where the filtering will be done.  This information is
     used by the lpr program to determine where to send the print job.

  2. If the lpd_bounce option is used, the filtered output is then
     transmitted to the queue and hostname specified by the lp option
     (or the rm and rp options).  This is the preferred method.

  3. If the bq option is used, the filtered output is then transmitted
     to the queue and hostname specified by the bq option.  It is
     recommended that this option not be used.

  4. We need a spool directory (sd) on the lpd server host to hold the
     files and temporary output while they are processed.

  5. Next, we need to indicate the filters to be used for different
     printer formats.  Oops, I still didn't tell you anything about
     filters. Just a moment, it's on its way. See section ``Filters''.

  6. The print job will be processed by the filters, banner pages
     generated, etc., and the entire output will be sent to the
     destination as a single file.

  7. The bq_format specifies the format for the output file.  If not
     specified, it default to l (literal or binary).


  8. You do not need to use a bounce queue and all of the associated
     filters to simply change the format names.  The simple
     translate_format=vlxf option will rename format x files to f and
     there is no filter for format x.  See ``translate_format'' for more
     details.

  9. The ab (always print a banner) flag will force a banner to be added
     to the job.  The banner generation is done as discussed in ``Banner
     Printing''.

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


  5.11.  LPR Filtering

  Options used:

  o  lpr_bounce  lpr does filtering

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


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




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

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

  5.12.


  Dynamic Routing

  Options used:

  o  destinations=destinations for jobs

  o  router=router program

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


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




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

  The routing filter exit status is used as follows:

  o  0  (JSUCC) - normal processing

  o  37 (JHOLD) - job is held

  o  any other value - job is deleted from queue

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


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




  Example of router output:


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




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

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



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




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

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

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

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


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




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

  5.13.  Support for Network Print Servers

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

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

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

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

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



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

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

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


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


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




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

  Filters are discussed in section ``Filters''.

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

  5.14.  Printer load balancing

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

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

  I'll give two examples for this situation.

  5.14.1.

  Multi-server print queue

  Options used:

  o  ss=queue served by printer

  o  sv=printers where jobs are sent (servers)

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

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

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

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

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

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














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




  and so on for the other 4 laserN queues.

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

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

  5.14.2.  Checking Busy Status of Server Queues

  Options used:

  o  check_idle=check for idle printer program

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

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

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

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



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




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

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


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




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








































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

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

  my $debug = 0;
  my $optind;

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

  $printer = $ARGV[0];

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

  $long = 0;  # short

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

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

  if( !defined($printer) or $printer =~ /^-/ ){
      print STDERR "$0: no printer value\n";
      exit( $JABORT );
  }
  while( checkstatus( $printer, $long ) ){
      print STDERR "XX CHECK_REMOTE sleeping\n" if $debug;
      sleep(10);
  }

  exit $JSUCC;

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

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

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

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

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

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

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




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


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




  5.14.3.  Using a router filter

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

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

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



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




  The perl script bouncer.pl looks like this:



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




  5.15.


  The Missing Details

  Options used:

  o  printcap_path=printcap file locations

  o  lpd_printcap_path=additional server printcap file locations

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

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

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

  The common defaults for the printcap locations are:


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




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


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

           realpr:tc=@common:tc=@morecommon




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

  1. Lines ending with a \ indicate continuation to the next line.  In
     practice,  the \ is replaced with space(s) when line joining is
     done.

  2. Blank lines and lines whose first nonwhitespace character is a #
     are ignored, except if it follows a continuation line.  (Which
     makes sense.)

  3. All leading and trailing whitespace on a line are removed.

  4. A printcap entry consists of a name, 0 or more aliases, and data
     entries.
     - name starts with an alphabetic character; dummy entries can start
     _, @ or .
     - alias starts with a | followed by the alias
     - fields or data entry starts with : followed by the entry

  5. Field or data entry
     name - main or cannonical name for printcap entry
     |name - alias name for printcap entry
     :key   - set the key to ON  (1)
     :key@  - set the key to OFF (0)
     :key#nnn - set the key to nnn, where nnn follows C language
     conventions
     :key=string - set the key to the string value to end of line


  6. Printcap entries whose cannonical name starts with _, @, or .
     (period) (eg.- @name) are treated like dummy entries.  They can be
     referenced with :tc=entry:, but will be ignored otherwise.

  7. The tc=f1:tc=f2:... acts similar to a file inclusion operator, but
     substitutes printcap entries.  The specified tc entries are
     logically append to the end of the current printcap entry, and the
     appended information will override the previous information.  Note
     that you can have multiple :tc: entries.

  8. key=value.%X
     a selected set of %X values are expanded when the printcap entry is
     used by the client or server program.  The following values are
     expanded:
     P - printcap cannonical or main name
     H - Fully Qualified Domain Name for host
     h - short name for host
     R - Fully Qualified Domain Name for remote host host
     r - short name for remote host host

  9. The oh entry specifies that a particular printcap entry can only be
     used by a host with a matching host name or IP address.  See
     ``Master Printcap Files'' for details.

  10.
     The server entry specifies that a particular printcap entry can
     only be used by the lpd server, and is ignored by other programs.
     See ``Shared Printcap Files'' for details.

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

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

  5.15.1.  Spool (Control) Directory Printcap File

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

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

  5.15.2.  Separate Printcap Files for LPD

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

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

  5.15.3.  Printcap Entry all

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

  all:all=pr1,pr2,...




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

  5.15.4.  More Example Printcap Entries

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






















































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




  5.15.5.  PC-NFS Print Spooler

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










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




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

  5.16.  Management Strategy for Large Sites

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

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

  5.17.  Using Programs To Get Printcap Information

  In the lpd.conf file you can specify:


       printcap_path=|program




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

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

  5.17.1.  How to use NIS and LPRng

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






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




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

  The input to NIS is a normal printcap file:


       # Classical printcap entry
       lp23a|lp23|lp|main printhost printer - KB, EUCS front Door:\
               :lp=lp23a@printhost:\
               :sd=/usr/spool/lpr/lp23a:

       #lprng printcap entry
       lplabel|lpl|TEST - Labels printer:
               :lp=:rm=printhost:rp=lplabel:
               :sd=/usr/spool/lpr/lplabel:
               :rg=lpadm:mx#1:




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































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




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


       printcap_path |/usr/local/lib/pcfilter




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


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





  5.17.2.  How to use NIS and LPRng - Sven Rudolph






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




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


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




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

  1. Assumptions

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

     In the examples the printer is called lp1 .

  2. Add the following to your YP Makefile (/var/yp/Makefile) on the YP
     server :


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





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

  3. Install the programs match_printcap and normalize_printcap to
     /usr/lib/yp.  normalize_printcap is only required on the YP server.
     The normalize_printcap processes only the LPRng printcap format.


       ---------- start of /usr/lib/yp/normalize_printcap
       #! /usr/bin/perl
       $debug = 0;
       $line = "";
       $new = "";
       while (<>) {
           chomp;
           next if ( /^\s*\#.*/ );
           s/^\s*$//;
           next if ( $_ eq '' );
           print "new: " . $_ . "\n" if $debug;;
           if (/^\s/) { # continuation line
               $line = $line.$_;
               print "continued: $line\n" if $debug;
               next;
           } else {
               $line =~ s/\s+\:/:/g;
               $line =~ s/\:\s+/:/g;
               $line =~ s/\:\s*\:/:/g;
               print "line: $line\n" if $debug;
               push(@lines, $line) if $line;
               $line = $_;
           }
       }
       $line =~ s/\s+\:/:/g;
       $line =~ s/\:\s+/:/g;
       $line =~ s/\:\s*\:/:/g;
       push(@lines,$line) if $line;
       @lines = sort(@lines);
       foreach $line (@lines) {
           ($printers) = split(/\:/,$line);
           @printers = split(/\|/,$printers);
           foreach $printer (@printers) {
             $num{$printer}++;
             push(@allprinters,$printer);
             print "allprinters: @allprinters\n" if $debug;
             print $printer."_".$num{$printer}."\t$line\n";
           }
       }
       @pr = keys %num;
       print "printers @pr\n" if $debug;
       if ($#allprinters >=0) {
           print "all_1\tall:all=".join(",",@pr)."\n";
       }
       ---------- end of /usr/lib/yp/normalize_printcap





  The result of processing the sample printcap file is:


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




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


       ---------- start of /usr/lib/yp/match_printcap
       #!/bin/sh
       read p
       n=1
       while ypmatch "${p}_${n}" printcap 2>/dev/null; do
           n=`expr $n + 1`
       done
       ---------- end of /usr/lib/yp/match_printcap




  4. Now test the YP arrangement:


       $ cd /var/yp; make # this should create the printcap map
       $ ypcat printcap # should provide the whole normalized printcap
       $ echo lp1 |/usr/lib/yp/match_printcap # yields lp1 printcap




  5. Add the printcap_path entry to /etc/lpd.conf:


       printcap_path=|/usr/lib/yp/match_printcap




  6. Test the use of the printcap path entry:


       $ lpq -Plp1 # shows the status of lp1




  7. Restart the lpd server and check to see that it accesses the right
     printcap information.  Use the same lpq command, and then try lpc
     printcap lp1.

  5.18.


  The Record Queue Name qq and force_queuename flags

  Options used:

  o  qq  Insert queue name into control file

  o  use_queuename  (alias for qq)

  o  force_queuename= Queuename to be used

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

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


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




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

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


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




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

  5.19.


  Using the check_for_nonprintable Flag

  Options used:

  o  check_for_nonprintable  LPR checks for non-printable file

  o  ml#  minimum number of printable characters

  o  xt  (alias for check_for_nonprintable)

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

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

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

  5.20.  The fd Forwarding Off Option

  Options used:

  o  fd  Forwarding off

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

  This was a wimpy attempt to prevent job spoofing.




  5.21.  The rg Restrict Use to Group Members Option

  Options used:

  o  rg=Restricted group list

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

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

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

  5.22.  The fx Allowed Formats Option

  Options used:

  o  fx=supported formats for printing

  The fx option restricts the formats supported by a spool queue.  The
  lpr program uses these to check if a requested format is supported.
  By default, all formats are supported.

  5.23.  Fixing Bad Control Files and Metacharacters

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

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

  5.23.1.  Defective RFC1179 Implementations

  Options used:

  o  safe_chars=additional safe characters for control file

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

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

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


       unexpected end of input



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

  5.23.2.  OS/2

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

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

  5.23.3.  Serious Security Loophole

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


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




  This would create a job file with the line:


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




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


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




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


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




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


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


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

  5.23.4.  Fixing Bad Jobs

  In addition to simple implementation RFC1179 implementation errors,
  many PC based systems send messages or control files with non-ASCII
  characters or meta characters in them.

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

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

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

  In addition, LPRng will ruthlessly regenerate control file entries and
  data file names so that they are compliant.

  5.23.5.

  Using the bk Option and Control File Filters

  Options:

  o  bk  Berkeley compatible control file

  o  control_filter=Control file filter

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

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

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

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

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

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

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


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




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

  Also, see ``Control Filters'' for more information.

  5.24.  Maximum Copies

  Options used:

  o  mc#maximum copies

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

  5.25.  The minfree Minimum Spool Queue Space Option

  Options used:

  o  minfree#alias for mi

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

  5.26.





  Debugging

  Options used:

  o  debugging=debugging options

  o  full_time  full or extended time format

  o  ms_time_resolution  millisecond time resolution

  o  syslog_device=syslog alternative device

  o  use_date  put date information in control file

  o  use_info_cache  cache printcap and other information

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

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

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

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

  o  nnn - general purpose debugging level

  o  network - network debugging

  o  database - database debugging

  o  receive - job or command reception debugging

  o  print - detailed job printing debugging

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

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

  5.27.





  LPD Specific

  Options used:

  o  ipv6  use IPV6 Network facilities

  o  lockfile=lpd server lock file

  o  report_server_as=server name for status reports

  o  spool_dir_perms#spool directory permissions

  o  spool_file_perms#spool file permissions

  o  spread_jobs#job number spread


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

  The lockfile specifies the location of the lock file used by the lpd
  server.

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

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

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

  5.28.  Legacy Compatibility

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

  5.28.1.  The allow_duplicate_args Option

  Options used:

  o  allow_duplicate_args  allow lpr to have duplicate arguments

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

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

  5.28.2.  The class_in_status Options

  Options used:

  o  class_in_status  show class name in status

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

  5.28.3.  The reverse_lpq_format Option

  Options used:

  o  reverse_lpq_format= reverse LPQ status format for specified remote
     systems

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


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


       reverse_lpq_format=*.eng.com,130.192.0.0/16




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

  5.28.4.

  The return_short_status and short_status_length Options

  Options used:

  o  return_short_status= return short LPQ status for specified remote
     systems

  o  short_status_length# short LPQ status length in lines

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

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


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




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

  5.28.5.  The force_lpq_status Options

  Options used:

  o  force_lpq_status= force LPQ status format for specified remote
     systems

  In order to be compatible with non-LPRng client programs which are
  totally unpredictable, this allows the administrator to specify the
  format for LPQ status when requests arrrive.

  The force_lpq_status= specifies a list of formats and printers or IP
  addresses for which the lpd server will return status in the sepcified
  format.  The entry has the format KEY=list;KEY=list... where KEY is s
  for short and l for long format, and list is a list of hosts or IP
  addresses.  For example:


       force_lpq_status=s=pc*.eng.com,130.192.12.0/24,l=sun*.eng.com




  will cause hosts whose Fully Qualified Domain Name (FQDN) matches
  pc*eng.com or from subnet 130.192.12.0 to get short status returned
  and hosts which match sun*.eng.com get long status.

  5.28.6.

  The ignore_requested_user_priority and force_fqdn_host Options

  Options used:

  o  ignore_requested_user_priorityprevent users from queue jumping

  o  force_fqdn_hostnameforce FQDN hostname in control file

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

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

  5.28.7.  The lpr_bsd Options

  This will force the lpr -m (send mail to user) option not to take an
  arguement, as in the BSD lpr.  The value of the $USER environment
  variable will be used as the return address.

  5.29.  Compatibility with BSD printcap

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

  For example, the fc/fs/xc/xs flag fields were used to specify serial
  line options and are no longer supported.  The flag fields and their
  meanings are version and OS dependent and were not portable.  The stty
  value is a subset of stty(1) options, and should be able to support
  whatever configuration you require.  See ``Converting BSD fc,fs,xc,xs
  To LPRng stty'' for details.

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

  6.  Filters

  This section gives an overview of how LPRng uses filter programs, and
  gives a detailed discussion of how the printcap options and filters
  interact.

  6.1.  What are filters?

  Print filters are one of the most powerful tools in BSD-style printer
  systems.

  In general UNIX terms, a filter 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 :))


  In the context of a BSD-style print spooler (and also LPRng), the term
  filter refers to a program that does processing on a file that is
  submitted to the printer. As such, it is a specific example of the
  general class of programs called `filters'.

  Usually the filter is executed with STDIN reading from the file to be
  printed or program generating the output and STDOUT to the printer
  device.  STDERR (file handle 2) is redirected to a log file, and file
  handle 3 to an accounting file.

  A filter can be as simple as a LF to CR/LF translator (the example
  used before), or it can incorporate a complete accounting system,
  automatic file type translations, or even redirect the job to another
  printing system.

  As part of the LPRng project, the following filters are supported.
  See the associated documentation for details.

  o  Distributed in the LPRng source distribution
     ``lpf'' a very simple CR/LF or passthrough filter.

  o  Distributed in the FILTERS_LPRng distribution:
     ``ifhp'' HP PCL and PJL printer filters

  o   The supported filters and other facilities are available from
     ftp://ftp.astart.com/pub/LPRng

  6.2.  What are print formats?

  Options used:

  o  if, cf, df, gf, nf, of, rf, tf, vf, Xf,   Filter programs

  LPRng has inherited a set of so-called `print formats' from its BSD
  ancestor.  The format was originally used to specify the type of file
  that was being printed.  The lpd daemon would use the print format to
  select the required filter for processing the file.  The default
  format is f.

  The user can specify the format (i.e., the file type) by giving the
  appropriate option to lpr:


  o  -b or -l: Binary (literal) file. No processing should be done.  The
     l format is recorded as the file format.

  o  -c: cifplot(1) output.

  o  -d: TeX DVI file.

  o  -g: Output from the plot(3X) routines.

  o  -n or -t: (di)troff output.

  o  -p: Text file that should be pre-processed by the pr command, and
     then by the standard text filter.

  o  -v: Benson Varian raster image.

  Alternatively, one can also use -Fx, where x is the format specifier.
  (E.g., -Fc instead of -c.)  This last form also allows you to use
  other (non-standard) format specifiers.

  The filter for format X is the value for the Xf printcap option, with
  some minor exceptions.  The following Xf options have a pre-defined
  meaning.
  o  if The f format filter, i.e. - for the default f format.  All print
     jobs are passed through this one, unless another format is
     selected.

  o   cf Cifplot data filter (for -c format).

  o   df Filter for DVI files (-d).

  o   gf Graph data filter (-g).

  o   nf Ditroff data filter (-n).

  o  of 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 ``of'' filter for details.

  o   rf Filter for Fortran style files (-r).

  o   tf Troff filter (-t).

  o   vf (Versatek) raster image filter (-v).


  6.3.  OF Filter

  The of filter is used to process banners and job separators.  The of
  filter is responsible for performing appropriate processing of this
  information and sending to the printer for action.

  While the various file filters are invoked on a once per print file
  basis, the of filter is invoked on a once per print job basis.

  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.

  The of 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 ``Job Processing Steps and Printcap Options'' for
  details.)

  During operation, the lpd server will send the special stop sequence
  of \031\001 to the of filter.  The filter must then suspend itself
  using a kill -STOP operation.  The lpd server will detect that the of
  filter has suspended itself and then will perform other printing
  operations.

  After the other printing operations have been completed, the of will
  then be sent a kill -CONT signal.

  This sequence will continue until all information has been printed,
  and then the of filter's STDIN will be closed.  The filter will then
  perform whatever cleanup operations are needed, update accounting or
  other information, and exit.

  6.3.1.  The filter_poll Option

  Options used:

  o  filter_poll=interval to check for of output

  While the of filter operation is very simple in concept, there are
  some minor implementation and performance problems with it.  The lpd
  server reads and reports any error messages generated by the of filter
  on FD 2 (stderr).  The of filter may write these error messages before
  it exits or suspends itself.  If there is insufficient buffering in
  the operating system pipe buffer, then the of filter may block until
  the information is read by the lpd server.  If the lpd server is
  waiting for the filter to exit, then we get a deadlock situation.

  This deadlock is resolved by having the lpd server block for
  filter_poll (default 30) seconds, and then check to see if there is
  any input to be read.  This option will effect performance only when
  the of filter produces a large amount of debugging or trace messages,
  and when there is insufficient system pipe buffers to hold them all.

  6.4.  The lpr -p  format and pr option

  Options used:

  o  pr=pr program for p format

  The -p format is implemented by sending the file through the program
  specified by the pr printcap utility (default is /bin/pr), and passing
  the result to the normal :if filter.

  6.5.  The lpr -l  format and binary format

  The binary (or literal) format is indicated by format type -l.  The if
  filter is used to process the file, and is invoked with the -c (cancel
  processing?) flag.

  6.6.  Job Processing and Printcap Options

  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.

  Assume the pr printcap entry has the form:


       pr
           :lp=/dev/lp  OR  :lp=rp@rm
           :sd=/var/spool/lpd/pr
           :lf=log
           :of=/usr/local/bin/lpf
           :if=/usr/local/bin/lpf




  Assume that we have used the following command to print a set of
  files.


       lpr -Ppr file1 file2




  This will create a control file in the /var/spool/lpd/pr directory
  with the following contents (this is an example - in practice there
  may be minor differences between the example and an actual control
  file):





  Hastart4.astart.com
  J/tmp/file1 /tmp/file2
  CA
  Lpapowell
  Ppapowell
  fdfA002230astart4.astart.com
  N/tmp/file1
  UdfA002230astart4.astart.com
  fdfB002230astart4.astart.com
  N/tmp/file2
  UdfB002230astart4.astart.com





  6.6.1.


















  Opening the Output Device

  Options used:

  o  achk  Accounting check at start

  o  af=Accounting File

  o  ar  Remote printer accounting enabled

  o  as=Accounting at start

  o  connect_grace#  Time between jobs

  o  connect_interval#  Connection interval

  o  connect_timeout#  Connection timeout

  o  control_filter=Control file filter

  o  ff  form feed

  o  fo  form feed on open

  o  la  Local printer accounting enabled

  o  ld=leader on open (initialization string)

  o  lk  Lock IO device


  o  lp=IO device pathname

  o  nb  Nonblocking device open

  o  network_connect_grace#  Time between jobs

  o  of=of filter

  o  retry_econnrefused#  Retry if open failed

  o  retry_nolink#  Retry if open failed

  o  rm  the remote machine to send the job to

  o  rp  the remote print queue to send the job to

  o  rw  device opened RW flag

  o  server_tmp_dir=temporary directory

     Sequence of Operations:

  1. 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 server_tmp_dir directory.

  2. If the accounting file specified by af exists, it is opened (af_fd)
     and the af_fd is passed as file descriptor 3 to all filters.  If
     the af value has the form af=|/program then the program is started
     and the program STDIN is used as af_fd.  If the af value has the
     form af=host%port, then a TCP/IP connection to the corresponding
     port on the remote host is made and the port used as af_fd.  In the
     latter two cases,  the filter STDIN (file descriptor 0) is actually
     opened read/write, and is used when information is needed from the
     accounting filter or remote server.  See ``Accounting Printcap
     Options'' for more information on the LPRng accounting support.

  3. If the connect_grace value is non-zero and the server is opening a
     device or network_connect_grace 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.

  4. The lp option is checked to determine the type of IO device.
  |          |                                                                          |
  |Format    | Meaning                                                                  |
  |/pathname | Absolute pathname of IO device                                           |
  |pr@host   | transfer to pr on remote host                                            |
  |host%port | open a TCP/IP connection to port on host. host can be name or IP address |
  ||filter   | run the filter program; it STDIN will be used as device                  |


  5. The IO device specified by lp is opened write-only or read-write if
     the rw flag is true, and the resulting file descriptor is io_fd.
     If the nb flag is set, a non-blocking open will be done as well.
     If the lk (lock device) flag is true, the device will be locked
     against use by other LPD servers.

  6. If a host%port combination, a TCP/IP connection will be opened to
     the remote port and the connection will be used as io_fd.

  7. If a filter program is specified, the filter program will be run
     and the STDIN of the filter will be used as the device file
     descriptor.


  8. If a rp@rm combination, or none of the above combinations are true
     and the rm and rp 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.

  9. If the connect_timeout 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.

  10.
     If a connection is to a network address (i.e. - connect() system
     call) and the connection attempt fails with an ECONNREFUSED error,
     if the retry_econnrefused flag is set then the connection attempt
     is retried, but this time using an alternative port number.  See
     ``RFC1179'' for details.  This is repeated until all of the
     possible originating port numbers are exhausted.

  11.
     If the open or connect operation fails, and the retry_nolink flag
     is set, then the server will pause for a minimum of connect_grace
     plus a multiple of connect_interval seconds based on the number of
     attempts before retrying the open operation.  Note that the
     interval may increase as the number of attempts increases.

  12.
     If printing a job and the of filter is specified, it is created
     with its STDOUT (fd 1) attached to the io_fd.  Its stdin (of_fd)
     will be used in the steps listed below.  If there is no of filter,
     then the of_fd value will be the io_fd descriptor.

  13.
     If transferring a job and the control_filter option is specified,
     then the program specified by the control_filter value will be run.
     It will have its STDIN set to the control file, and its STDOUT
     output will be used as the new value of the control file to
     transfer to the remote host.  See ``Filter Command Line Flags'' for
     details of options passed to the control filter, and ``errorcodes''
     for the exit codes of the filter.

  14.

     If la (local accounting) is true and we are printing a job or ar
     (remote accounting) is true and we are transferring a job, the as
     value is examined.  If it is a filter (program) specification, then
     the program is started with its STDIN attached to /dev/null and
     STDOUT to the io_fd, STDERR to the error file, and file descriptor
     3 to the accounting file descriptor af_fd.  The lpd program will
     wait until it terminates, and examine the error code for action, as
     for the filters (see ``errorcodes'' below).  If it is a string,
     then it is interpreted, the escape sequences replaced with the
     appropriate information,  and written to the accounting file.

  15.
     If the achk (accounting check) flag is set, a line is read from the
     accounting filter af_fd file descriptor.  This line should be
     accept, otherwise the job processing terminates with a JFAIL
     indication.

  16.
     If the operation is a job transfer, the operation proceeds as
     outlined in ``RFC1179'', and then the ``Normal Termination''
     operations are carried out.

  17.
     If the operation is a print operation and the ld (leader on open)
     value is provided, the string is translated (escapes removed) and
     written to the of_fd file descriptor.

  18.
     If the fo (form feed on open) flag is true, then the ff (form feed)
     string is translated (escapes removed) and written to the of_fd
     file descriptor.

  6.6.2.









  Printing Banner At Beginning

  Options used:

  o  ab  Always print banner (default FALSE)

  o  be=End banner generator program

  o  bl=Short banner line format

  o  bp=Banner generator program

  o  bs=Start banner generator

  o  hl  Banner (header) Last

  o  of=Banner and File Separator Filter

  o  sb  Short banner (default FALSE)

  o  sh  Suppress header (banners) (default FALSE)

     Sequence of Operations:

  1. If the sh (suppress header) flag is true, no banner is printed, and
     the actions in this section are skipped.

  2. If the hl flag is true, the banner is printed at the end of the
     job, and the actions in this section are skipped.

  3. If the user does not supply a banner name, (the L line in the
     control file) and ab (always print a banner) is false (the
     default), then no banner is printed.  If no name is supplied and ab
     is true, then ANONYMOUS is used.

  4. There are two types of banners - short and long.  If the sb flag is
     set, then we send the bl (banner line) contents directly to the
     of_fd; By default the bl value is: bl=$-'C:$-'n Job: $-'J Date:
     $-'t (See ``Filter Command Line Flags'' for details.)  This will
     get translated to:
     papowell:A Job: file1 file2 Date: Thu Nov 27 23:02:04 PST 1997

  5. If the sb flag is clear, we will generate a long banner using a
     program instead.  If bs (start banner) program is specified, then
     it is used to generate a banner, otherwise if the bp (banner)
     program is specified, then it is used to generate a banner.  If no
     program is available, we skip the banner generation.  The banner
     generator program is started with the normal command line flags
     (see ``Filter Command Line Flags''), with its STDOUT attached to
     the of_fd descriptor.  The short banner string described in the
     previous step is written to the STDIN.  The banner printer is
     responsible for generating a banner appropriate to the printing
     device.

  6. The ff (form feed) string will be interpreted and sent to the
     of_fd.

  6.6.3.



  Printing Job Files

  Options used:

  o  Xf=Format Filter

  o  if=Default F Format Filter

  o  pr=pr formatting program

  o  send_job_rw_timeout= print job read/write timeout

  o  send_query_rw_timeout= status query operation read/write timeout

  o  sf  Suppress FF Print File Separators

  Sequence of Operations: for each job in listed in the control file,
  the following operations are done in turn.

  1. If there is an of filter present, the suspend string \031\001 is
     written to of_fd and the no further action is taken until the of
     filter is suspended.

  2. The control file line for the job is examined, and the first letter
     of the data file specification is used as the format.

  3. If the format is p, the job is first processed by the program
     specified by the pr program, and the program output used as the
     print file.

  4. If the format is f, l, or p then the if filter is used, otherwise
     the keyword Xf is used.  Note that certain formats such as p, a, l,
     may not be used as formats.

  5. The filter program is started with an appropriate set of command
     line options (see ``Filter Command Line Flags''), and with its
     STDOUT attached to the printing device (io_fd), STDERR to the log
     file (lf), and file descriptor 3 to the accounting fd af_fd.

  6. 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 send_job_rw_timeout value is used.  When doing a
     status or query operation, the send_query_rw_timeout 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.

  7.  lpd will then wait for the filter to exit.  The exit status can be
     as follows:




  Key      Value   Meaning
  JSUCC    0       Successful
  JFAIL    1, 32   Failed - retry later
  JABORT   2, 33   Abort - terminate queue processing
  JREMOVE  3, 34   Failed - remove job
  JHOLD    6, 37   Failed - hold this job
  Other            Abort - terminate queue processing




  8. If the filter exit status was JSUCC (0), or no error indicated,
     then processing will continue otherwise the job termination takes
     (see ``Abnormal Termination'').

  9. If the of filter is present, then it is reactivated with a kill
     -CONT signal.

  10.
     If the sf (suppress FF print file separators ) is false, then the
     ff (form feed) string will be interpreted and sent to the of_fd.

  6.6.4.  Printing Banner At End

  Options used:

  o  hl  Header (Banner) Last

  The actions taken in this step are identical to those for the
  ``Printing Banner At Beginning'', with the exception that the be (end
  banner program) is used in the procedure rather than the bs (start
  banner program).

  6.6.5.




  Normal Termination

  Options used:

  o  fq  Form Feed on Close

  o  la  Local Printer Accounting

  o  tr=Trailer on Close

  o  ae=Accounting at end

  o  save_when_done  Save when done

  Sequence of Operations:

  1. If we are printing and the fq flag is set and the sf (suppress
     interfile FF) flag is set, then the ff (form feed) string will be
     interpreted and sent to the of_fd.

  2. If we are printing, the tr (trailer) string will be interpreted and
     sent to the of_fd.

  3. If printing and the la (local printer accounting) flag is set or
     transferring a job and the ar (remote accounting) flag is set, the
     ae is examined and accounting is done as described for the ``as''
     field.

  4. If the of filter is present, its STDIN is closed, and the lpd
     server waits for it to exit.  The exit status is used as described
     above.

  5. The device (io_fd) is closed.

  6. The job is marked as completed in the spool queue.

  7. If the save_when_done flag is not specified, the job is removed.

  6.6.6.








  Abnormal Termination

  Options used:

  o  mail_from=Mail from user name

  o  mail_operator_on_error=Mail to operator on error

  o  send_try#  Maximum Print or Transfer Attempts

  o  save_on_error  Do not delete on error

  o  send_failure_action=Action on Failure

  o  sendmail=sendmail path name and options

  o  stop_on_abort  Stop processing queue on filter abort

  If the job processing terminates abnormally, the following sequence of
  events occurs:

  1. The job is marked as having an error during processing.

  2. The LPD server will attempt to kill all filters and other
     associated process by using a sequence of kill -INT, kill -QUIT,
     and finally kill -KILL operations.

  3. If there is a mail_operator_on_error value, the specified operator
     will be mailed an error indication.  The sendmail option specifies
     the pathname of the sendmail program and the options needed to have
     it read mail addresses from its standard input.  For example,
     sendmail=/usr/sbin/sendmail -oi -t is a commonly used set of
     options.

  4. The mail_from value specifies the user name used for mail
     origination.  If not specified, the default is to use the print
     spool queue or printer name.

  5. If there is a send_failure_action specified, then it is decoded and
     the corresponding action taken.  If the value is remove, hold,
     abort, or retry, then the job is removed, held, aborted, or
     retried.  If the value is |/program, the program is executed and
     the number of attempts are written to the filter STDIN.  The exit
     status of the filter will be used to determine the consequent
     actions.  That is, JSUCC (0) will be success, and the standard
     success action will be taken; JFAIL will cause retry, JREMOVE will
     cause the job to be removed, JHOLD will cause the job to be held,
     JABORT or other status will abort processing.

  6. If the status is ABORT and the stop_on_abort flag is set, then
     further processing of jobs is terminated.  The job is not removed
     from the queue.

  7. If the error status indicates removal, and the save_on_error flag
     is clear then the job is removed from the spool queue.

  8. If the error status indicates that no further operations should be
     performed on the queue, then the lpd server will stop processing
     jobs.

  9. If the error code indicated that the job should be retried, and the
     send_try value is 0 or the number of attempts is less than the
     send_try 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 max_connect_interval seconds.  If
     max_connect_interval is 0, there is no limit on the interval value.

  6.6.7.


  LPD Spool Queue Processing

  Options used:

  o  lpd_force_poll=Force LPD to periodically poll print queues

  o  lpd_poll_time#Time between polls

  o  max_servers_active#Maximum number of active servers

  When the lpd server starts, it will fork a set of subserver processes,
  each which will handle an individual queue.

  If a system has a large number of queues, then this forking operation
  may result in the lpd server exhausting the process resources.  To
  control this,  the max_servers_active 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.

  Due to the limits on the number of processes, there may be times when
  a job is placed in a queue, but the lpd server is unable to start
  handling the job.  When all of the children of the main lpd server
  have exited, the server starts a timer.  After lpd_poll_time 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.

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

  6.7.











  Filter Command Line Flags

  Options used:

  o  bk_filter_options=Backwards Compatible Filter options

  o  bk_of_filter_options=Backwards Compatible OF Filter options

  o  bkf  Backwards Compatible Filters

  o  filter_ld_path=Filter LD_LIBRARY_PATH environment

  o  filter_options=Filter options

  o  filter_path=Filter PATH environment

  o  of_filter_options=OF Filter options

  o  pass_env=Environment variables to copy to Filter environment

  o  pl#line count for page

  o  pw#column count for page

  o  px#pixel width for page

  o  py#pixel length for page

  A filter (or program) specification in the LPRng printcap database
  usually has the form:


       :option=| [flags] /path [arguments]
       :option=[flags] /path [arguments]




  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.

  The sequence of operations to run a filter is as follows:

  1. The program must be specified with an absolute path name.

  2. By default, the program is run as the user if invoked from a client
     program such as lpr, lpc, etc.  If invoked from lpd,  it is run as
     the server_user user (default daemon) configuration entry.

  3. The flags control how the program is to be run.  The following
     flags are supported:

  o  ROOT This opens a horrible security loophole, as it will run the
     program as ROOT.  To enable this option, you must set various
     compilation flags, and perform other arcane operations.  This is
     deliberately done to make administrators read the warnings and
     admonitions.

     The alternative to ROOT is to have a setuid ROOT executable.  Under
     NO circumstances should you run a shell script setuid ROOT, with
     general execute permissions on it.

  o  -$ This very odd looking flag is used to suppress the addition of
     additional command line arguments specified by the value of
     filter_options to the program command line.
  4. If the -$ flag is not specified, the arguments determined by the
     value of the bkf (Berkeley LPD filter compatible flag) flag are
     added to the filter command line.  If bkf is false the
     filter_options are added for OF filters and of_filter_options are
     added for non-OF filters; if it is true, then the bk_filter_options
     and bk_of_filter_options are added for OF and non-OF filters
     respectively.

  |                     |                                                                                  |
  |Option               | DefaultValue                                                                     |
  |filter_options       | $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 |
  |of_filter_options    | (same as filter_options)                                                         |
  |bk_filter_options    | $P $w $l $x $y $F $c $L $i $J $C $0n $0h $-a                                     |
  |bk_of_filter_options | $w $l $x $y                                                                      |



  5. By default, for programs that are not being invoked as print job
     file filters, the filter_options arguments are added.  For print
     job filters, if the bkf flag is set, then the bk_filter_options and
     bk_of_filter_options entries are used.  The default bk filter
     options are the same as originally used with the BSD LPR filters.
     For the of filter, either the of_filter_options or
     bk_of_filter_options arguments will be added.

  6. The program arguments will then be scanned and interpreted.
     Arguments of the form $letter will be translated into values from
     the print job control file and/or printcap entry.  The letters have
     the following meaning:
  |               |                                                                              |
  |Letter         | TranslatedValue                                                              |
  |a              | printcap af (accounting file name)                                           |
  |b              | job size (in K bytes)                                                        |
  |c              | binary file (l format for print file)                                        |
  |d              | printcap cd or sd entry                                                      |
  |e              | print job data file name (currently being processed)                         |
  |f              | print job original name when spooled for printing (N info from control file) |
  |h              | print job originating host (H info from control file)                        |
  |i              | indent request (I info from control file)                                    |
  |j              | job number in spool queue                                                    |
  |k              | print job control file name                                                  |
  |l              | printcap pl (page length)                                                    |
  |m              | printcap co                                                                  |
  |n              | user name (L info from control file)                                         |
  |p              | remote printer (when processing for bounce queue)                            |
  |r              | remote host (when processing for bounce queue)                               |
  |s              | printcap sf (status file)                                                    |
  |t              | time in common UNIX format                                                   |
  |w              | printcap pw (page width)                                                     |
  |x              | printcap px (page x dimension)                                               |
  |y              | printcap py (page y dimension)                                               |
  |F              | print file format                                                            |
  |P              | printer name                                                                 |
  |S              | printcap cm (comment field)                                                  |
  |Capital letter | Corresponding line from control file                                         |
  |{key}          | printcap value for key                                                       |


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


     Each entry in quotes is treated as a single value, as in /bin/sh.
     The $'x does not quote the value.  Combinations of the various
     flags are allowed.  For example, $-x would simply substitute the
                        |      |                 |
                        |Form  | TranslatedValue |
                        | $x   | '-xvalue'       |
                        | $-x  |  'value'        |
                        | $0x  |  -x 'value'     |
                        | $'x  |  -x value       |

     value for x, and then pass the whitespace separated components as
     individual arguments.  This last form is useful for adding in
     additional flags on the command line.

  8. The command line is parsed, metacharacters are ruthlessly stripped
     from all arguments and pathnames and replaced by _ (underscores),
     and an argument list suitable for the execve system call is formed.

  9. A sanitized environment is set up for the program execution, with
     the following environment variables.

     |                  |                                            |
     | USER             | User name (client only)                    |
     | LOGNAME          | L control file info                        |
     | HOME             | Home directory (client only)               |
     | LOGDIR           | Home directory (client only)               |
     | PATH             | filter_path configuration information      |
     | LD_LIBRARY_PATH  |  filter_ld_path  configuration information |
     | SHELL            | /bin/sh                                    |
     | IFS              | " \t"                                      |
     | TZ               | Time zone                                  |
     | SPOOL_DIR        | sd printcap info                           |
     | CONTROL_DIR      | cd printcap info                           |
     | PRINTCAP_ENTRY   | printcap info                              |
     | CONTROL          | control file                               |



  10.
     If the filter is to be run by a client program such as lpr, then
     the environment variables specified by the pass_env configuration
     or printcap option will be extracted from the environment, have any
     metacharacters removed, and then placed in the environment variable
     list.  Commonly, the PGPPASS, PGPPASSFD, and PGPPATH are specified.

  11.
     The program is started, with STDIN, STDOUT, and STDERR attached to
     the appropriate files or file descriptors.  If none is specified,
     then they are attached to /dev/null.

  6.8.  Bounce queues and filters: caveats

  There are a few situations in which a filter of a bounce queue will
  behave differently from an ordinary queue.

  6.9.  The lpr -p  format and :pr filter

  The -p format doesn't behave as expected. Instead of lpr running pr,
  the job is forwarded to the lpd server, which will use the program
  specified by the :pf option.  If you do not have the this program on
  your server, then job printing will fail.

  6.10.  Changing Filter Formats

  Use the
   translate_format=oNoN...  printcap option to simply rename formats in
  a print job before forwarding .  Its value takes the form of old/new
  pairs of formats. For example:

  translate_format=xf




  The x format file will now be renamed with the f format before
  forwarding.

  6.11.  LPRng Supported Filters

  There already exists a large library of ready-to-use filters. Some of
  them have LPRng-specific versions, which can be found at the ``LPRng
  ftp mirror sites''.

  6.11.1.  Filter Distribution Conventions

  By convention, most filters are either totally standalone (very rare),
  or require a set of support files.  There are two types of support
  files: per print queue configuration information and global support
  information.

  Since a print filter will execute with the current directory set to
  the spool queue directory, most filters expect that per print queue
  configuration information should be kept in the spool directory.  Most
  vintage filters insist on having these files hidden with names such as
  .setup.  This can make it difficult for administrators to determine
  where the configuration files are.

  It is strongly recommended that filters and information be placed in
  commonly accessible directories such as /usr/local/lib/filters, and
  the executables in subdirectories.  This allows the LPRng
  administrator to set the privileges on these directories such that
  only the lpd process can access them.

  Most of the LPRng supported filters can either be used as a if or of
  filter.  The filter will examine the format type passed by the -FX
  command line argument, and if it is o it will perform as an of filter.

  Alternatively, the filter will check the filename in the pathname by
  which is was invoked.  If the name has the substring of in the
  filename, then it assumes it is to act as an of filter.  This allows
  symbolic links to be made to a common filter executable, each of which
  corresponds to the filter name by which it is to be invoked.

  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.

  By convention, all LPRng supported filters use the


       -Tkey=value[,key=value]




  convention for specifying filter configuration option values.

  6.11.2.  lpf

  Source code: ``LPRng Distribution''

  This filter is distributed as part of the LPRng source code, and has a
  very limited functionality.  By default, it only translates \n to \r\n
  sequences, and detects the OF Filter Stop sequence when invoked as an
  OF filter.

  o  Options:
     -Tcrlf - suppress \n to \r\n translation

  6.12.  IFHP Filter

  Source code: ``LPRng Distribution, ifhp-<em>version</em>.tgz''

  This filter supports a wide variety of Hewlett-Packard printers, or to
  be more specific, printers which support the Hewlett-Packard PCL
  and/or PJL languages.  In addition, they try to detect PostScript jobs
  and send the correct commands to the printers to enable PostScript
  rather than PJL operation.

  6.12.1.  Printer Capabilities

  As explained in ``Setting Up Your Printer'', you can have a parallel
  (unidirectional), serial (bidirectional), or network (bidirectional)
  connection.  When using a bidirectional connection, you can sometime
  obtain or gratuitously receive error and/or status information from
  the printer.

  Some printers will spontaneously generate error messages when printing
  a job on a bidirectional interface.  Usually, though, it it necessary
  to force the printer to provide status in a reasonable format.

  Some printers have the capability of printing either PCL or
  PostScript; some require special setup commands and some will
  autosense which type of job is being printed.

  If you are printing text, and not using a Page Description Language
  like PostScript or PCL, then you may want to download a font to the
  printer.  This is especially the case when you are trying to print
  text files in a non-English font.

  Some printers will provide a hardware page counter value when
  requested; however, the means of requesting differ from model to
  model.

  Sometimes you want to generate a special banner for a particular
  printer, and need to put in some dynamic information.  While this can
  be done by the lpd server using the bp program specification, it turns
  out that non-LPRng systems which want to use the ifhp want to have the
  same facilities.  Thus,  you need to have some way to get the same
  effect as the bp option,  but at the filter level.

  Having done lpd banner generation and printing, why not have the
  filter run an accounting script as well?

  At this point,  I suspect that the reader is beginning to suspect that
  making a general purpose filter to support all of these possibilities
  is difficult.  That is incorrect.  It is extremely difficult.

  6.12.2.  hpif Options

  These options are specified by the -Tkey=value [key=value]* on the
  command line.


  6.12.3.  Parallel Port Printer

  On a parallel port printer, you cannot get status, or do much besides
  set up the printer to either handle PostScript or do autosensee.  The
  following is a typical printcap entry:

  |                                      |                                                                                                                                                                                                                                                                                                                                                                                                                           |
  |Option                                | Purpose                                                                                                                                                                                                                                                                                                                                                                                                                   |
  |accounting=accounting_script_pathname | Invoke the accounting script with a subset of theoptions passed to the filter. In addition, the-bpagecount option indicates the number of pagesprinted for the job.                                                                                                                                                                                                                                                       |
  |autodetect=[on|off*]                  | The printer has or does not have job type autode-tect capability. Do not download fonts or try todetermine job type if autodetect is on.                                                                                                                                                                                                                                                                                  |
  |banner=[on|off*]                      | If banner is on, then the ifhp filter will attemptto print a banner using information passed on thecommand line or on the standard input. The titleoption can be used to specify additional titleinformation on the banner. See BANNERS below fordetails.                                                                                                                                                                 |
  |cartridge=[*on|off]                   | (Alias for postscript)If cartridge is on, the printer has PostScript sup-port. The filter will try to determine if a job ispostscript and send Printer Job Language commandsto put the printer in PostScript mode.                                                                                                                                                                                                        |
  |debug=debuglevel                      | Sets the debugging level; 2 is the default; alarger number causes more verbose error messages.                                                                                                                                                                                                                                                                                                                            |
  |defaultfont=fontname                  | Sets the default font to be downloaded; default isNONE.                                                                                                                                                                                                                                                                                                                                                                   |
  |dev=/device or dev=host%port          | Open the specified device or connection to remotehost; by default ifhp filter uses file descriptor 1(stdout). If the optional orig_port is specified,connections will be originated from this port.Some printers require that connections originatefrom a port in the range 1-1024.                                                                                                                                       |
  |infostatus=[*on|off]                  | The PJL INFOSTATUS request is not supported on someHP printers. Use this to turn the status requestoff. Note that you cannot get real time reports ofthe printer status if you do this. This will alsosuppress getting pagecount information using thePJL facilities.                                                                                                                                                     |
  |forcepagecount=[on|off*]              | If you have a printer that has PostScript pagecount information support, you can set infostatusto OFF and forcepagecount to ON. This will causethe PostScript facility to be used. If you setcartridge or postscript to OFF then this will notbe done.                                                                                                                                                                    |
  |logall                                | Save all of the error and information messages fromthe printer in the log file. This is useful whenyou wish to examine returned status from theprinter.                                                                                                                                                                                                                                                                   |
  |model=(C5M|III|IIID|IIISi|IV*)        | The model of HP printer. C5M is Color 5M, III isHP LaserJet 3, IIID is HP LaserJet 3D, etc. Addi-tional printers may be added or defined at varioustimes - please consult the source for details.This selects various timing and format characteristic-tics. This is a desperation parameter for userswith antique or non-conforming PJL based equipment;read the source code for details on the particularpeculiarities. |
  |pagecount=[on*|off]                   | Get the hardware pagecounter value for accounting.Some printers such as the HP LJ4s do not have hard-ware support for pagecounters, and return bogusnumbers. Use this to suppress attempting to getvalid information. If your printer does supportPostScript, then you can get the page count valueusing PostScript by setting forcepagecount to ON.                                                                      |
  |plp=[on|off*]                         | Return PLP status values on exit; by default LPRngstatus values are returned.                                                                                                                                                                                                                                                                                                                                             |
  |postscript=[on*|off]                  | The printer has postscript support.                                                                                                                                                                                                                                                                                                                                                                                       |
  |quiet=[on|off*]                       | If set, do not report common status messages.                                                                                                                                                                                                                                                                                                                                                                             |
  |retries=count                         | The number of times to retry connecting to theprinter.                                                                                                                                                                                                                                                                                                                                                                    |
  |sleep=time                            | The number of seconds to wait before trying to con-nect to the printer.                                                                                                                                                                                                                                                                                                                                                   |
  |status=[*on|off]                      | When on, the printer is treated as a write onlydevice and is not queried for pagecount and statusinformation. Set status=OFF for parallel printers.If status is OFF, then the ifhp filter simply addsjob control language headers, fonts, and trailersto the jobs.                                                                                                                                                        |
  |stty=stty flags                       | if the output device is a serial line, set the linecharacters according to the stty flags. Theseflags are (most likely) identical to those avail-able with the stty(1) command on the host system.                                                                                                                                                                                                                        |
  |summary=[filename|host%port]          | This option specifies that summary or informationalmessages should be placed in the specified file orsent, using the UDP protocol, to the indicated hostand port address. This allows remote monitoring ofthe printing and error activity. The undocumentedprogram included with the filter distribution is asimple program that can be used to perform the mon-itoring.                                                  |
  |sync=[*on|off]                        | Try to synchronize communications with printer.This will ensure that the printer has been reset,and no problems involving the previous job willresult.                                                                                                                                                                                                                                                                    |
  |tbcp=[on|*off]                        | When invoked as an IF filter and transferring aPostScript job, the filter will use the AdobeTagged Binary Communications protocol. This allowsbinary data to be transferred and not interpretedas control information.                                                                                                                                                                                                    |
  |title=line[/line]*                    | The title information is printed on the bannerpage; it consists of a list of / separated lineswhich are added to the banner information.                                                                                                                                                                                                                                                                                  |
  |wrap=[on|off*]                        | enables or disables line wrapping in PCLmode.                                                                                                                                                                                                                                                                                                                                                                             |

  pr:.... options
    :of=/usr/local/lib/filters/ifhp -Tstatus=off
    :if=/usr/local/lib/filters/ifhp -Tstatus=off




  You might want to also look at the autodetect or postscript options.

  6.12.4.  Printing Banners

  By default, the ifhp filter when used as an OF filter will interpret
  the first line to it as a short banner line, and use the information
  on this line to produce a PCL based banner.  The short banner line
  should have the format:

  class:username Job: jobinfo Date: dateformat



       Example:
         A:papowell Job: (stdin) Date: Sun Dec 14 07:13:34 PST 1997




  This is produced by the default short banner line option value:


       bl=$-'C:$-'n Job: $-'J Date: $-'t




  If you want to suppress banner printing, then you need to suppress
  generation of this short banner line.  If you want to have the lpd
  program to generate the default long special banner, then you need to
  suppress ifhp from interpreting the information sent to is as banner
  information.  Finally, you may want to have lpd invoke the bp (banner
  program) and have its output used as the banner.  Here are the various
  possible ways:



       # no banner at all, use :sh: - suppress headers
       lp:....
         :sh
         :of=/usr/local/lib/filters/ifhp
       # have ifhp generate banner from short banner input
       lp:....
         :sb
         :of=/usr/local/lib/filters/ifhp
       # have LPD generate long banner, have of filter pass it
       lp:...
         :sb@
         :of=/usr/local/lib/filters/ifhp -Tbanner=off
       # have LPD invoke bp banner generation program, have of filter pass it
       # bp programs require short banner on STDIN to work, so we need to
       # generate short banner
       lp:...
         :sb
         :bp=/usr/local/lib/filters/banner_program
         :of=/usr/local/lib/filters/ifhp -Tbanner=off




  The ifhp banner is generated in PCL, and uses the minimum PCL
  facilities.  Since when you send a banner to an autosensing printer
  you cause it to enter the requested mode, the if filter (ifhp) will
  need to reset the printer to autosense mode.  The ifhp filter
  automatically does this.

  If you want very fancy banners, the banner.sh (PCL) and psbanner.sh
  (PostScript) banner generating programs in the IFHP distribution make
  a good starting point.

  6.12.5.  Error Logging

  Error logging and reporting is done by the ifhp filter as follows.

  1. Messages are produced by the actions of the ifhp software.  This
     are logged to the STDERR output of the filter.

  2. Messages are produced by status returned from the printer, when the
     -Tstatus=on (default) option is enabled.  These are classified
     according to the Hewlett-Packard Printer Job Language error status
     definitions, and logged to the STDERR output of the filter.

  3. In addition to error messages, ongoing status messages are also
     produced.  If the printcap entry has a ps=statusfile entry and the
     statusfile exists and is writeable, then the error and status
     messages will be written to the log file.

  4. If the message concerns a serious matter or has been returned from
     the printer as an 'ALERT' in it, then the message can also be sent
     to a 'summaryfile'.  This file can be either a file OR a UDP socket
     on a host. This is specified with the -Tsummary=summaryfile option.
     For example,


           ifhp -Tsummary=taco%3000



  on host taco.

  5. If you do not want the filter to report status on its STDERR
     output, use the -Tquiet option to suppress this, or compile it with
     the -DQUIET option.

  6.12.6.  Accounting Information

  Doing printer accounting  is not simple.  Read ``LPRng Accounting''
  for more information.

  In order to help aid in accounting, by default the ifhp filter will
  query the printer to get the current value of the hardware page
  counter value, if there is such a thing on the printer.
  Unfortunately, due to different types of printers and errors in their
  PJL, PCL, and PostScript implementations, several different methods
  need to be used.

  1. Only a printer with a bidirectional port will return status, so you
     need to have a bidirectional connection.

  2. If the printer is still printing a job, then getting the value of
     the hardware page counter will be useless; you need to wait until
     the printer is idle, i.e. - synchronize your operations with the
     printer.

     Unfortunately, some printers return an idle indication even when
     they are printing pages of the previous job.  This means that the
     printer has to be polled, and only when it is idle and the
     pagecounter value has been stable for a reasonable time (5
     seconds?)  can you trust the page counter value.  This slows down
     job printing very seriously.

     Some of the newer PJL printers have a PJL TEOJ, or return end of
     job indication when the last page of a job has been printed.  If
     you have this capability, you can speed up printing.

  3. If your printer supports Hewlett-Packard Printer Job Language PJL
     INFO PAGECOUNT facility, then it will first be tried to get the
     page count.

  4. If your printer does not return pagecount information using the PJL
     facility and it has PostScript support (default), then a small
     PostScript job will be sent to the printer requesting the
     systemdict pagecounter value.  Unfortunately, different
     implementations and versions of PostScript will need different
     programs.  The PostScript Printer Definition file for the printer
     will have the correct script that is needed.  The default script
     that is used is:


        /ps { print flush } def
        (\tPAGECOUNT ) ps
        statusdict begin pagecount end == flush




  5. To confuse matters totally, some printers which can do PostScript
     interpretation do not support PJL PAGECOUNT reporting.  You can use
     the PostScript method to get the pagecount information, but you
     cannot get status.

  6. The pagecounter information is obtained at the start and end of
     processing a job, and is printed in the accounting file and also on
     File Descriptor 3 (if it is open).  This information has the
     format:



         start -ppagecounter -Ff -kjob -uuser -hhost -R...
         end  -ppages -qpagecounter -Ff -kjob -uuser -hhost -R...




  When we use the OF filter and/or banners,  we will see the individual
  jobs bracketed by the OF filter records:



           start -p100 -Fo -kcfA100taco -uuser -hhost -R...
           start -p101 -Ff -kcfA100taco -uuser -hhost -R...
           end  -p1 -q102 -Ff -kcfA100taco -uuser -hhost -R...
           start -p102 -Ff -kcfA100taco -uuser -hhost -R...




  We can use the various job numbers and other information to track page
  usage.

  The following are a selected set of printcap entries that can be used
  to get page counting information:


       # use defaults, try to get pagecount using all methods, wait for stable
       # value of pagecount before proceeding
       pr:...
         :of=/usr/local/lib/filter/ifhp
       # printer support PJL True End of Job and PAGECOUNT
       pr:...
         :of=/usr/local/lib/filter/ifhp -Ttrue_eoj=on
       # no PJL INFO status available, but you can get page count using postscript
       pr:...
         :of=/usr/local/lib/filter/ifhp -Tinfostatus=off,forcepagecount




  You should try connecting to your printer directly and testing the
  accounting facilities.  You can do this by using the ifhp -Tdev=...
  facility.  For example:


       ifhp '-Tdev=/dev/ttyb,stty=38400 -echo -crmod -raw -oddp \
       -evenp ixon pass8 -ixany cbreak' -Tdebug=5 <ellipse.ps
       ifhp -Tdev=astart14%9100 -Tdebug=5 -Ttrue_eoj <ellipse.ps




  6.12.7.  Accounting

  The psfilter uses the same methods for doing accounting as the ifhp
  filter.  See ``IFHP Accounting'' for details.

  Consult your printer's PostScript Printer Description file to
  determine the PostScript script needed to do accounting.  You may need
  to modify the default one supplied in the psfilter code.


  Always test that the printer returns the right accounting information
  using a test similar to the following:


       psfilter -Tdebug=8 '-Tdev=/dev/ttyb,stty=38400 -echo -crmod -raw -oddp \
       -evenp ixon pass8 -ixany cbreak' <ellipse.ps
       psfilter -Tdebug=8 -Tdev=astart14%9100 -Ttrue_eoj <ellipse.ps




  6.13.  lp_pipe Filters

  Source code: ``LPRng Distribution, part of
  FILTERS_LPRng-<version>.tgz''

  The lp_pipe family of filters was developed to act as a network pipe
  to network devices.  They are largely replaced by the lp=host%port,
  facility.

  o  tcp-pipe: uses a tcp socket (OBSOLETED by lp=host%port), but good
     starting point if you have special device requirements;

  o  annex-pipe: supports annex terminal server.

  6.14.  apsfilter Filter

  Source code: ``LPRng Distribution, apsfilter-<version>.tgz''

  The apsfilter is basically a simple front end to the a2ps program
  (See: http://www-inf.enst.fr/~demaille/a2ps/ for details), and is an
  example of a MagicFilter that has powerful processing capability.  The
  apsfilter program sets up options for the a2ps program and then
  invokes it.

  The a2ps program can convert just about any type of file into
  PostScript, and then by using the GhostScript facility can convert
  this to the output compatible with a particular printer.

  Combined with the LPRng qq and force_queuename options, we can set up
  virtual queues that do various types of reformatting.  Here is a
  sample set of printcap entries:


       # seen by users - note that the queue name is put into control file,
       #  and we then send it to the frontend@host queue for processing
       raw:qq:lp=frontend@host
       twoup:qq:lp=frontend@host
       landscape:qq:lp=frontend@host
       frontend:lp=frontend@host:force_queuename=raw
       # frontend does the job conversions and accounting
       frontend:server
         :lp=/dev/lp:force_queuename=raw
         :if=/usr/local/lib/filter/apsfilter




  6.15.  Using your own filters

  If you already have a working setup, with its own specific filter
  programs, you might want to keep them. Or, you might want to write a
  set of your own.

  See the source code in the ``LPRng Distribution,
  FILTERS_LPRng-<version>.tgz'' files for examples.
  7.

  Spool Queues and Files

  When files are accepted by the lpd 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.

  For descriptive purposes, we will use the following printcap entry as
  a guide:



       pr|alias
         :sd=/var/lpd/pr_public
         :cd=/var/lpd/pr




  7.1.  Spool Queue and Control Queue


  o  sd=Spool queue directory name

  o  cd=Control queue directory name

  The sd option in the printcap entry specifies the spool queue
  directory.  If there is no sd entry or value, then the printer can
  only be used by the clients such as lpr to locate the destination for
  a print job.

  Normally, all information, files, etc., for a print queue is stored in
  the spool directory.  However, some software packages such as the PC-
  NFS spooling package from Sun Microsystems originally required
  read/write access to the directory in order to create print jobs on
  behalf of the user.  To prevent unauthorized or accidental tampering
  with LPRng operations, the cd (control directory) entry specifies that
  only the print job files should be placed in the spool queue, and that
  all the control and informational files should be in the control
  directory.

  By default, the cd value will be the same as the sd value unless
  explicitly overridden in the printcap entry.

  7.2.  Job State

  Options used:

  o  ah  Automatically hold jobs

  A job can be in the following state:

  1. Initial.  This is the state during job submission.  Jobs in the
     initial state do not have any status displayed for them.

  2. Held.  Once a job is submitted, it can either be printed or held.
     The ah printcap option specifies that all jobs are automatically
     held on submission.  The lpc release and lpc redo command will
     cause these jobs to be printed and the lprm command can remove
     these jobs.

  3. Active.  The job is being processed for printing or transfer to
     another queue.

  4. Pending.  Jobs which can be printed but are not active.

  5. Error.  Jobs which have encountered an error during printing.  The
     lpc release and lpc redo command will cause these jobs to be
     printed and the lprm command can remove these jobs.

  6. Done.  Jobs which have completed printing, but which are not yet
     removed from the print queue.  See the ``save_when_done'' flag for
     more information.  The lprm command can remove these jobs.

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

  7.3.  Printer Lock File

  When the lpd server starts printing, it will fork individual worker
  processes to service each queue.  To prevent multiple processes from
  working on the same queue, a printer lock file with the cannonical
  spool queue name is used.  In our example, the lock file would be:
  /var/lpd/pr/pr.

  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 lpq can determine queue activity.

  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.

  7.4.  Spool Control File

  The spool control file is used to control the operations of the
  spooler, and is in the spool or control directory.  The file name has
  the form control.printer; in our example, the control file would be:
  /var/lpd/pr/control.pr.

  The lpc program sends spool control requests to the lpd 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:


       key value




  The following keys and their values are currently supported.

  |                  |                   |                                                     |
  |Key               | Values            | Purpose                                             |
  |printing_disabled | 0 or 1            | disable printing of jobs in queue                   |
  |spooling_disabled | 0 or 1            | disable placing jobs in queue                       |
  |holdall           | 0 or 1            | hold jobs until released                            |
  |redirect          | printer           | transfer jobs to indicated printer                  |
  |class             | glob expression   | print only jobs whose class matches glob expression |
  |server_order      | printer name list | preferred order of printer use                      |
  |debug             | debugging options | debugging and tracing                               |


  The printing_disabled and spooling_disabled are managed using the lpc
  start, lpc stop, lpc enable and lpc disable commands.  Similary,
  holdall is enabled and disabled by holdall and noholdall commands
  respectively.  When holdall is enabled, jobs placed in the print queue
  will be held until they are explicitly released for printing by an lpc
  release command.

  The redirect 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 lpc redirect off removes the redirect entry from the
  control file.

  The class entry is similar in operation to the holdall, 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.

  The server_order 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 round robin use of printers,
  rather than having all jobs printed to the first printer in the list
  of printers.

  The debug entry is set by the lpc debug command, and is used to enable
  or disable debugging and tracing information for a spool queue.  This
  facility is for diagnostic purposes only.

  7.5.






  Log and Status Files


  o  lf=log file name (default: log)

  o  max_log_file_size#  maximum log file size (Kbytes)

  o  min_log_file_size#  minimum log file size (Kbytes)

  o  max_status_line#  maximum status line length (characters)

  o  max_status_size#  maximum status file size (Kbytes)

  o  min_status_size#  minimum status file size (Kbytes)

  o  ps=filter status file name (default: status)

  During operation, the lpd server records the current printing
  operations in the status.printer file.  For our example, this would be
  /var/lpd/pr/status.pr.  In order to prevent this file from growing too
  large, the server will periodically truncate the file.  The
  max_status_size configuration or printcap option sets the maximum size
  (in Kbytes) of the status file; if the file exceeds this,  only the
  last min_status_size bytes or 25% of the maximum size (default if not
  specified) will be preserved.

  Similarly, the server logs its operations in the log file specified by
  the lf (log file) option (default is lf=log).  The max_log_file_size
  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
  min_log_file_size bytes or 25% of the maximum file size.  Again, the
  last portion of the log file is preserved.  If the max_log_file_size
  value is 0, then the log file grows without limit.

  Some filters require an additional filter status file that they use
  for recording additional filter status or other operational
  information.  The ps names this file, and it is passed to a print
  filter using the $s option (see ``Filter Command Line Flags'').

  The log file (lf) is opened and used as the STDERR output for filters
  and debugging information from the lpd server.

  When reporting status information, the length of line returned can be
  a problem.  The max_status_line#79 option restricts the status line to
  a maximum of 79 characters.

  7.6.  Job Control File


  o  longnumber   long job number

  o  default_priority=default job priority

  A print job consists of a control file and one or more data files.
  ``RFC1179'' specifies the general format of these files and how they
  are to be transfered between servers.  LPRng has extended the contents
  of the control files and the transfer protocol to provide a more
  powerful set of features, but has extensive provisions for backwards
  compatibility with non-LPRng software.  A sample control file is shown
  below:


       Hastart4.astart.com
       J/tmp/file1 /tmp/file2
       CA
       Lpapowell
       Ppapowell
       fdfA002230astart4.astart.com
       N/tmp/file1
       UdfA002230astart4.astart.com
       fdfB002230astart4.astart.com
       N/tmp/file2
       UdfB002230astart4.astart.com




  The first part of the control file contains general information
  generated by the lpr 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 (.).

  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 format to be used
  to process the file.  See ``print file formats'' for more information
  about its use.


  The entries marked with * are used only by LPRng.  N and U lines are
  associated with a print file.  The N line is the original name of the
  print file.  The U 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.

  The names of control and data files follow a very strict pattern.
  Control files have the format cfXnumberhost, where X is an upper case
  letter, number is (usually) a 3 digit number, and host is the host
  name.  ``RFC1179'' restricted the total length of the control file
  name to 32 characters; LPRng has a much looser limit.
    |    |                  |                                         |
    |Key | Meaning          | Generated By                            |
    |A   | identifier *     | LPRng internal                          |
    |C   | class            | lpr -C class                            |
    |D   | date             | lpr                                     |
    |H   | originating host | lpr                                     |
    |I   | indent           | lpr -i indent                           |
    |J   | jobname          | lpr -J jobname (default: list of files) |
    |L   | bnrname          | lpr -U username                         |
    |N   | filename         | (see text)                              |
    |M   | mailname         | lpr -m mailname                         |
    |P   | logname          | lpr                                     |
    |Q   | queuename        | lpr -Q                                  |
    |R   | accntname        | lpr -R accntname                        |
    |S   | slinkdata *      | lpr                                     |
    |T   | prtitle          | lpr -T prtitle                          |
    |U   | unlnkfile        | (see text)                              |
    |W   | width            | lpr -w width                            |
    |Z   | zopts *          | lpr -Z zopts                            |
    |1   | font1            | lpr -1 font1                            |
    |2   | font2            | lpr -2 font2                            |
    |3   | font3            | lpr -3 font3                            |
    |4   | font4            | lpr -4 font4                            |

  Data file names must follow the same pattern as the control file name,
  and have the format dfXnumberhost.  The X can be in the range A-Za-z,
  allowing at most 52 data files for a job.  The number and host must be
  identical to the corresponding control file.

  By convention, LPRng uses the X of the control file name to set a
  priority for the job.  A job with control file name cfA...  will have
  lower format than a job with format cfB..., and so forth.  The lpr
  program uses the first letter of the class name or an explicit
  priority indication to set the letter value.  If none of these are
  specified, then the default_priority value from the configuration or
  printcap entry is used.

  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 longnumber option
  will use 6 digit job numbers.  This must be used with care when
  operating with non-LPRng software.

  7.7.  Job Hold File

  Associated with each control file is a hold file that has additional
  information controlling the printing operations.  The entries in this
  file have the form:


       key [value]




  The following is an example of a hold file:










  server 0
  subserver 0
  attempt 3
  error cannot open printer
  hold 0
  priority 0
  remove 0
  routed 0




  The server and subserver entry records the process ID of the server
  process and the subserver process that is printing the job.  The
  attempt field records the total number of attempts to print the job.
  The error field records any error that would prevent the job from
  being printed.  This information is reported by the lpq program.

  The hold field is non-zero when the lpc hold command is used to
  explicitly prevent the job from being printed; lpc release will clear
  the field and allow the job to be printed.

  The priority field is modified by the lpc topq command and is used to
  provide an overriding priority to printing the file.

  The remove field is non-zero when the file has been printed and should
  be removed.

  The routed 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 ``routing filter''.  The
  information is recorded by information in the hold file.  The
  following is an example of routing information:

































  active 0
  attempt 0
  done 0
  hold 0
  priority 0
  remove 0
  routed 880892602
  route dest t1
  route ident papowell@astart4+705.1
  route error
  route copies 1
  route copy_done 0
  route status 0
  route active 0
  route attempt 0
  route done 0
  route hold 0
  route sequence 0
  route priority B
  route CB
  route end
  route dest t1
  route ident papowell@astart4+705.2
  route error
  route copies 0
  route copy_done 0
  route status 0
  route active 0
  route attempt 0
  route done 0
  route hold 0
  route sequence 1
  route end




  Routing information lines start with route followed by individual
  routing entry information.  The route dest, copies, priority, and
  Xnnnn entries are derived from the output of the router program; other
  fields are used during the printing process.  The copy_done records
  the numbers of copies done, while the done records that the entry has
  been completed.  The status is the process ID of the server process
  doing the printing.

  The output from  route filter  that generated the above file was:


       dest t1
       copies 1
       priority B
       CB
       end
       dest t1
       end




  7.8.  Job Identifier

  Options:

  o  use_identifier  put job identifier in control file


  For each job in a spool queue, the LPRng software creates a unique
  identifier.  This identifier is recorded in the control file A line.
  It can be used by the various client programs for identifying jobs,
  and is displayed by the lpq program as status information.

  8.

  /etc/lpd.conf Configuration File

  The values in the LPRng configuration file (default: /etc/lpd.conf)
  specify values for global options and default values for printcap
  options.  See the man pages for lpd.conf(5) and printcap(5) for a
  complete list of configuration variables and their effects.

  8.1.  Configuration File Format

  The LPRng distribution contains a template lpd.conf file which can be
  installed as /etc/lpd.conf.  The configuration file has the following
  format:


       # Default version of the lpd.conf file
       # ae=jobend $H $n $P $k $b $t
       # allow_getenv
       # ar
       # architecture
       # as=jobstart $H $n $P $k $b $t
       # bk_filter_options=$P $w $l $x $y $F $c $L $i $J $C $0n $0h $-a
       # bk_of_filter_options=$w $l $x $y
       # bl=$-'C:$-'n Job: $-'J Date: $-'t
       # check_for_nonprintable
         check_for_nonprintable@
       # client_auth_command
       ...




  The file uses the same notation for ``printcap'' entries, but does not
  use the : (colon) separator.  A line starting with # is a comment.

  To change the default value of an option, remove the comment character
  and edit the entry.  In the above example, the default value (1 or
  TRUE) for check_for_nonprintable has been changed to 0 or off.

  To force the lpd server to use the new options, use the lpc reread
  command.

  As for printcap entries, the %X combinations are interpreted when a
  configuration entry is used for configuration or defaults, and can be
  used to do site and host dependent customization.  This interpolation
  is done when the default or configuration value is used in a printcap
  context.  See ``Missing Details'' for more information.

  8.2.  Obtaining Configuration Information

  The location of the configuration file is compiled into the LPRng
  software.  The config_file entry in the compilation defaults is
  normally set to search for configuration information in the following
  files:


       config_file=/etc/lpd.conf:/usr/etc/lpd.conf



  To change any of the config_file specifications, the the
  LPRng/src/common/vars.c file will need to be modified and the LPRng
  software recompiled.

  In addition, LPRng has a special debug mode.  When compiled with the
  -DGET_ENV option enabled, this sets the value of the allow_getenv
  option to 1.  LPRng can then use the value of the LPD_CONF environment
  variable instead of the compiled in config_file value.
   This is a possible security loophole, and should not be used when
  running SETUID ROOT or as ROOT.  To enable this option, see the Test
  Version comments in the  LPRng/src/Makefile.

  8.3.  Useful Configuration Options

  The following variables are used to set default behavior for the LPRng
  software, or are commonly used for configuration of LPRng operations.

  8.3.1.  default_format

  Default format for printing.  Usually, default_format=f, but setting
  it to default_format=l will cause all files spooled by lpr to be
  spooled as binary files.

  8.3.2.  default_permission=ACCEPT

  The default permissions to use when checking for printing or other
  permissions.

  8.3.3.  default_priority=A

  The default priority for a print job.

  8.3.4.  default_remote_host=localhost

  The default lpd server host.

  8.3.5.  default_tmp_dir=/tmp

  The default directory for temporary files.

  This option can be used to specify a default printer. If no value is
  given (default), the first printer in the printcap file will be used
  when no printer is specified.

  8.3.6.  domain_name=domain.name

  You will only need to set this if LPRng can't determine your hosts
  domain name itself.  This is usually a desperation option when DNS or
  some other database system is not available.

  9.  The /etc/lpd.perms Permissions File

  This file is used to specify the restrictions on the use of the LPRng
  software, printers, and other facilities.  The model used for
  permission granting is similar that used in the rule set used by
  packet filters.  An incoming server request is tested against a list
  of rules, and the first match that is found determines the action to
  be taken.  The following is an example of a lpd.perms file.








  # 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




  The structure of the lpd.perms file was inspired by network packet
  filter configuration files.  When the LPD server gets a request from a
  remote client program, it performs the checks specified by the rules
  in the lpd.perms to decide whether to accept or reject the request.

  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.

  The sense of a pattern match can be inverted using the NOT keyword.
  For example, the rules with ACCEPT NOT USER=john,bill succeeds only if
  USER is defined and the USER value is not john or bill.

  The following patterns and matching are applied.

       |            |                                             |
       |Keyword     | Match                                       |
       |DEFAULT     | default result                              |
       |SERVICE     | lpC Status and User, lpR, lprM, lpQ request |
       |USER        | user name in print job                      |
       |REMOTEUSER  | user making request                         |
       |HOST        | host name in print job                      |
       |REMOTEHOST  | host making request                         |
       |IP          | IP address and mask of host in print job    |
       |REMOTEIP    | IP address and mask of host making request  |
       |PORT        | TCP/IP port of host making request          |
       |SAMEUSER    | USER and REMOTEUSER same                    |
       |SAMEHOST    | HOST and REMOTEHOST same                    |
       |SERVER      | request originates on lpd server            |
       |FORWARD     | destination of job is not host              |
       |GROUP       | USER is in the specified group              |
       |LPC         | LPC command requested                       |
       |REMOTEGROUP | REMOTEUSER is in the specified group        |
       |CONTROLLINE | match a line in control file                |
       |AUTH        | authentication type                         |
       |AUTHUSER    | authenticated user                          |
       |AUTHFROM    | authenticated forwarder                     |
       |AUTHJOB     | authenticated job in queue                  |


  Most of the patterns can be lists of alternative values to match, and
  can even contain wild cards.  The full details of the rules and
  keywords are detailed in the lpd.conf(5) man page.

  9.1.  Information for matching

  In order to do matching, the lpd server obtains and sets up the
  following information:


  1. If the request is coming over a network connection, then the IP
     address (REMOTEIP) port (PORT) of the source of the connection and
     FQDN of the remote host (REMOTEHOST) are obtained and the indicated
     values are set.  To be specific, the IP address of the remote host
     is obtained using getpeername().  The gethostbyaddr() is used to
     look up the host's fully qualified domain name, which is then
     assigned to the REMOTEHOST value.  The REMOTEIP value is the set or
     list of IP addresses that could be used by this host.  This is
     possible in the IPV6 environment.

  2. If the request contains the name of the user, then REMOTEUSER is
     assigned the name.

  3. If the request contains the name of the printer, then PRINTER is
     assigned the name.

  4. If a print job is being printed, then the USER, HOST, IP, and
     PRINTER are set to the user name, host, and printer information in
     the control file for the print job.  To be specific, the HOST entry
     in the control file is used by gethostbyname() to get the fully
     qualified domain name of the host.  The IP value is assigned a set
     or list of IP addresses that could be used by this host.

  5. If one of the optional authentication methods is being used, (see
     ``Authentication and Encryption''), then AUTH is true and AUTHTYPE
     is set to the type of authentication used.  AUTHUSER to the
     authenticated originating user of the request and AUTHFROM is when
     the originating program is a server.  The AUTHSAMEUSER will be true
     when the remote client authentication information matches the
     authentication information used to create the job on the server.
     The AUTHJOB will be true when checking for job permissions and the
     job has been authenticated.

  9.2.  Permission Checks

  When a connection is made to the lpd server, the originating site's IP
  address and hostname are determined, and a check with SERVICE=X is
  made.  The REMOTEHOST, REMOTEIP, and PORT will be defined for the
  purposes of this check.

  If the result is to accept the connection, then the request is then
  read from the connection, and the SERVICE, REMOTEUSER and PRINTER will
  be defined.  A further check is performed to determine if the service
  request would be accepted.

  When performing a service activity and a particular job is to be acted
  on, the USER, HOST, and other control file information will be
  available, and a further check can be performed.

  If a rule is specified and the particular value is not defined, then a
  rule will fail to match.

  9.3.  Match Procedure



       key=pattern                         substring match
       key=pattern1,pattern2,pattern3,...  glob and exact
       key=IP1/mask1,IP2/mask2,...         IP address




  Each of the indicated values is matched against a list of patterns.
  The following types of matches are used:

  1. substring match.  The indicated entry is present as a substring in
     the pattern.

  2. GLOB matches.  The pattern is interpreted as a GLOB style pattern,
     where * matches 0 or more characters, and ? matches a single
     character, and [L-H] specifies a range of characters from L to H,
     in ASCII order.

  3. IP address match.  The address must be specified in the standard
     nn.nn.nn.nn format.  The mask must be either an integer number
     corresponding to the number of significant bits, or in the standard
     nn.nn.nn.nn format.  Addresses are compared by doing


       ( IPaddr XOR IP ) AND mask





  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.

  4. integer range match.  The pattern has the form low-high, where low
     and high are integer numbers.  The match succeeds if the value is
     in the specified range.

  5. Same IP Address Match.  This compares two lists of IP addresses; a
     match is found when there is one or more common addresses.

  9.3.1.  DEFAULT



       DEFAULT ACCEPT
       DEFAULT REJECT




  The DEFAULT rule specifies the default if no rule matches.  Normally,
  there is one DEFAULT entry in a permissions file.

  9.3.2.  SERVICE

  Match type: substring

  The SERVICE key is based on the type of request.
                       |    |                      |
                       |Key | Request              |
                       |C   | LPC Control Request  |
                       |M   | LPRM Removal Request |
                       |P   | Job Printing         |
                       |Q   | LPQ Status Request   |
                       |R   | LPR Job Transfer     |
                       |S   | LPC Status Request   |
                       |X   | Connection Request   |


  Each of the above codes corresponds either directly to the user
  command, or a set of subcommands.

  All the LPC subcommands are SERVICE=C; status commands such as lpc
  status, printcap, active, or lpd are SERVICE=S commands as well.

  9.3.3.  USER

  Match type: GLOB

  The USER information is taken from the P (person or logname)
  information in the print job control file.

  9.3.4.  REMOTEUSER

  Match type: GLOB

  The REMOTEUSER information is taken from the user information sent
  with a service request.

  Note that one of the flaws of ``RFC1179'' is that an LPQ (print
  status) request does not provide a REMOTEUSER name.

  9.3.5.  HOST

  Match type: GLOB

  The HOST information is taken from the H (host) information in the
  print job control file.

  9.3.6.  REMOTEHOST

  Match type: GLOB

  The REMOTEHOST information is obtained by doing a reverse IP name
  lookup on the remote host address.  If there is no FQDN available,
  then the IP address in text form will be used.

  9.3.7.  PORT

  Match type: integer range

  The PORT value is obtained from the originating port of the TCP/IP
  connection.  The match succeeds if it is in the specified range.

  9.3.8.  IP

  Match type: IPaddr

  The IP information is obtained by doing a DNS lookup on the H (host)
  information in the control file.  If there is no host information, the
  IP address is undefined.  Note that for a single host name there may
  be multiple IP addresses; address matches are performed against the
  list of addresses and succeeds if there is one or more individual
  address matches.

  9.3.9.  REMOTEIP

  Match type: IPaddr

  The REMOTEIP information is the IP address of the host making the
  service request.  Note that the REMOTEIP value is obtained by using
  the gethostbyaddr lookup to obtain the DNS information for the remote
  host.  This information may include multiple IP addresses; address
  matches are performed against the list of addresses and succeeds if
  there is one or more individual address matches.

  9.3.10.  LPC

  Match type: GLOB


  If you are doing an LPC command, this matches the command.  This
  allows the following permissions line to be used:


       #allow remoteuser admin on server to use LPC topq and hold
       ACCEPT LPC=topq,hold SERVER REMOTEUSER=x





  9.3.11.  SAMEUSER

  Match type: exact string match

  Both the REMOTEUSER and USER information must be present and
  identical.

  9.3.12.  SAMEHOST

  Match type: Same IP Address

  The REMOTEHOST and HOST address lists are checked; if there is a
  common value the match succeeds.

  9.3.13.  SERVER

  Match type: Same IP Address

  One of the REMOTEHOST addresses must be the same as one of the
  addresses of the lpd server host, or must be one of the addresses
  found by looking up the localhost name using gethostbyname().

  9.3.14.  FORWARD

  Match type: Address Match

  The list of REMOTEHOST and HOST addresses must not have a common
  entry.  This is identical to NOT SAMEHOST.  This is usually the case
  when a remote lpd server is forwarding jobs to the lpd server.

  9.3.15.  GROUP

  Match type: modified GLOB

  If the pattern does not start with a @ character, then the USER
  information must be present and the USER must be present in one of the
  groups in /etc/group or whatever permissions mechanism is used to
  determine group ownership which matches the GLOB pattern.

  If the pattern starts with a @ character, then the USER information
  must be present and the user must be in the specified netgroup.  This
  match will be performed only if the netgroup mechanism is supported on
  the system and the specified netgroup exists.  No wildcard match will
  be done for netgroups.

  9.3.16.  REMOTEGROUP

  The same rules as for GROUP, but using the REMOTEUSER value.

  9.3.17.  CONTROLLINE

  Match type: GLOB

  A CONTROLLINE pattern has the form

  X=pattern1,pattern2,...




  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.

  This pattern can be used to select only files with specific control
  file information for printing.

  9.3.18.  AUTH

  Match type: GLOB

  The AUTH value can be NONE, indicating that no authentication was
  done.  If authentication was done, then AUTH=USER checks to see if
  there was user information, and AUTH=FWD checks to see if there was
  forwarding system identification.

  9.3.19.  AUTHUSER

  Match type: GLOB

  If AUTH=USER check succeeds, the AUTHUSER rule will check to see if
  the user identification matches the pattern.

  9.3.20.  FWDUSER

  Match type: GLOB

  If AUTH=FWD check succeeds, the FWDUSER rule will check to see if the
  forwarding system identification matches the pattern.

  9.3.21.  IFIP

  Match type: IPmatch, but for IPV6 as well as IPV4

  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.

  The IFIP (interface IP) field can be used to check the IP address of
  the origination of the request,  as reported by the information
  returned by the accept() system call.  Note that this information may
  be IPV4 or IPV6 information,  depending on the origination of the
  system.  This information is used by gethostbyaddr() to obtain the
  originating host fully qualified domain name (FQDN) and set of IP
  addresses.  Note that this FQDN will be for the originating interface,
  and may not be the cannonical host name.  Some systems which use the
  Domain Name Server (DNS) system may add the cannonical system name as
  an alias.

  9.4.  Permission File Location

  Options used:

  o  perms_path= directory path list

  The perms_path= configuration variable specifies the location of the
  default permissions file.  The default value is:


  perms_path=/etc/lpd.perms:/usr/etc/lpd.perms




  The lpd.perms file can be obtained by running a program, in a similar
  manner to the /etc/printcap file.  See ``Filters'' for details on how
  the program would be invoked.  For example, assume the configuration
  information specified:


       perms_path=|/usr/local/libexec/get_perms




  Then the get_perms program would be invoked with STDIN attached to
  /dev/null and the complete set of permission information would be read
  from its STDOUT.

  9.5.  Example Permission File



       # 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




  In the above sample, we first specify that lpC commands from user root
  on the lpd server will be accepted.  This is traditionally the way
  that most lpc commands operate.

  Next,  we reject any other lpc requests.

  We accept lprM requests from the host and user that submitted the job,
  as well as from root on the server, and reject any others.

  Finally, all other types of commands (lpq, lpr) are allowed by
  default.

  9.6.  Complex Permission Checking

  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.

  For example IP=10.3.4.0/24 would match all hosts with the IP addresses
  IP=10.3.4.0 to IP=10.3.4.255.

  Similarly, the HOST pattern can specify a set of hostnames or patterns
  to match against based on the GLOB notation.

  For example REMOTEHOST=*.astart.com would match all hosts with a DNS
  entry which ended with astart.com.
  The NOT keyword reverses the match sense.  For example REJECT NOT
  REMOTEHOST=*.astart.com,*.murpy.com would reject all requests from
  hosts which did not have a DNS entry ending in astart.com or
  murphy.com.

  9.7.  More Examples

  The following is a more complex lpd.perms file.


       # All operations allowed except those specifically forbidden
       DEFAULT ACCEPT
       #Reject connections which do not originate from hosts with an
       # address on 130.191.0.0 or from localhost,
       # or name is not assigned to Engineering pc's
       REJECT SERVICE=X NOT IFIP=130.191.0.0/16,127.0.0.1/32
       REJECT SERVICE=X NOT REMOTEHOST=engpc*
       #Do not allow anybody but root or papowell on
       #astart1.astart.com or the server to use control
       #facilities.
       ACCEPT SERVICE=C SERVER REMOTEUSER=root
       ACCEPT SERVICE=C REMOTEHOST=astart1.astart.com REMOTEUSER=papowell
       #Allow root on talker.astart.com to control printer hpjet
       ACCEPT SERVICE=C HOST=talker.astart.com PRINTER=hpjet REMOTEUSER=root
       #Reject all others
       REJECT SERVICE=C
       #Do not allow forwarded jobs or requests
       REJECT SERVICE=R,C,M FORWARD
       # allow same user on originating host to remove a job
       ACCEPT SERVICE=M SAMEHOST SAMEUSER
       # allow root on server to remove a job
       ACCEPT SERVICE=M SERVER REMOTEUSER=root




  10.  Running the software


  10.1.  LPRng's little helper: checkpc

  The program checkpc (check printcap file) is one of the most useful
  utilities in the LPRng package.

  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 checkpc is to remove old entries from queue directories.

  For a new installation, you will want to run


       checkpc -f




  to set the permissions right. The -f flag instructs the program to
  correct file permissions. If you don't run this as root, you'll
  receive a warning about that fact and any chown(2) calls will (most
  likely) fail.

  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.

  These are the permissions of my spool directory:


       drwx--S---   2 lp       lp           1024 Jul 22 21:15 ./
       drwxr-xr-x  16 root     root         1024 May 29 21:55 ../
       -rw-------   1 lp       lp          10222 Jul 23 05:32 acct
       -rw-------   1 lp       lp              0 Feb 14 21:14 control.lp1
       -rw-------   1 lp       lp          10229 Jul 23 05:32 log
       -rw-------   1 lp       lp              5 Jul 22 21:13 lp1
       -rw-------   1 lp       lp           9064 Jul 22 21:15 status.lp1
       -rw-------   1 lp       lp              5 Jul 22 21:13 unspooler.lp1




  And this is lpd's master directory:


       drwx--S---   2 lp       lp           1024 May 11 18:44 ./
       drwxr-xr-x  16 root     root         1024 May 29 21:55 ../
       -rw-------   1 lp       lp              0 Feb 18 07:05 lpd.lock.duff
       -rw-------   1 lp       lp              3 Jul 13 22:42 lpd.lock.duff.printer
       -rw-------   1 lp       lp              0 Apr  1 22:40 lpd.log.duff




  Later, you will want to use checkpc for the daily maintenance of your
  system. I have this line in user lp's crontab:


       32 5 * * * checkpc -t 10K -A3 -r >/dev/null 2>&1




  This job will:

  1. truncate all log and accounting files to 10KB (-t 10K).  Actually,
     it will keep the last 10K from the file, starting on a complete
     line.

  2. remove all stale files older than three days (-A3 -r).

     I'm redirecting output to /dev/null, because checkpc is a little
     noisy to my taste. (But too noisy is better than too silent :)

  10.2.  Starting the daemon

  Now comes the moment of truth: will it work? (I hope so, otherwise it
  means there are errors here.)  Where should I run the daemon?

  In order to work, vanilla LPR needs to be run on all computers on the
  network. This is because a job is first transmitted to the local lpd,
  and then (if needed) to the remote host.

  LPRng eliminates the local lpd from this chain, and connects directly
  to the remote daemon (except in the case of a bounce queue).
  Therefore, you won't need to start a daemon on all machines.

  In short: where do you need the daemon? Only on those machines where
  you have spool directories.  Almost there...


  These are the last steps in the installation:

  o  Kill your old lpd/lpsched.

  o  Start the newly installed lpd program.

  o  Print a sample file using the new lpr:


       lpr /etc/printcap




  If it works, you can remove your old printing software, and change
  your system startup files to run the new daemon automatically.

  Then, read the rest of the documentation to build whatever complex
  configuration you need.

  11.  Accounting

  The LPRng method for doing accounting is based on experiences in a
  Academic environment,  where avoiding printing accounting procedures
  has long been practiced.  While the LPRng procedures are not
  bombproof, they do provide a wide range of facilities,  with various
  degrees of trust built into them.

  11.1.  Printer Accounting Reality Check

  The following was written by Patrick Powell <papowell@astart.com> in
  response to the expressions of frustration that are periodically
  vented in the ``lprng@iona.ie'' mailing list.  While this addresses
  the use of a particular set of printer filters, i.e. - the ``ifhp''
  set, the comments are appropriate to other issues.

  In Academic institutions, avoiding printing accounting has been
  regarded as a challenge,  an ongoing game of fat cat and poor starving
  mouse, between the Administration and the downtrodden, poor,
  overcharged student.  The following is a lighthearted ramble down the
  dark lane of printing accounting.

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

  The accounting procedures put into the LPRng and the hpif filters may
  appear to be extraordinarily complex,  but believe me, they are not.
  Firstly, we make the assumption that the printer has some sort of non-
  volatile page counter mechanism that is reliable and impervious to
  power on/off cycles.  Without this mechanism the enterprising student
  ummm... user will simply turn off the printer.  Software that prescans
  jobs for line counts and pages is notoriously unreliable,  given even
  the most modest efforts of users to hide these procedures.   The cost
  of running a PostScript simulator simply to do accounting has its
  flaws; without ensuring that the simulator has all of the interesting
  security loopholes closed, such as opening files, etc.,  it can become
  a trap door to hell for the system administrator.

  Secondly,  we must make the assumption that the student... uhhh...
  user will not be able to tinker with the page counter mechanism, i.e.-
  they will not be able to roll back the odometer on the printer, FOR
  THE DURATION OF A SINGLE JOB.  I will digress and point out that a
  student actually did this for a challenge;  it only took him a couple
  of weeks of study and a fully equipped microcontroller lab, and two
  (2) laser printers which he ruined in the experiment.  HP was not
  amused when we sent them back under warranty,  claiming that this our
  'normal lab usage.'

  Lastly,  you should not mind a small amount of pilferage, or a few
  pages here and there being charged to the wrong account.

  How Does It Work?

  The ifhp filter records the page counter value at the start and end of
  each part of a print job. Each record has the form:


       start -ppagecounter -Ff -kjob -uuser -hhost -R...
       end  -ppages -qpagecounter -Ff -kjob -uuser -hhost -R...




  When we use the OF filter and/or banners,  we will see the individual
  jobs bracketed by the OF filter records:


       start -p100 -Fo -kcfA100taco -uuser -hhost -R...
       start -p101 -Ff -kcfA100taco -uuser -hhost -R...
       end  -p1 -q102 -Ff -kcfA100taco -uuser -hhost -R...
       start -p102 -Ff -kcfA100taco -uuser -hhost -R...
       end  -p3 -q105 -Ff -kcfA100taco -uuser -hhost -R...
       end  -p5 -q105 -Fo -kcfA100taco -uuser -hhost -R...




  It should be clear from the above that all we need to do is to add up
  the values for the -Fo (OF) filter lines and we are done.

  Unfortunately,  this is too simplistic.  If for some reason the job is
  killed or terminates due to error conditions,  the OF filter may not
  get to finish its work.  Thus,  we may see the following:


       start -p100 -Fo -kcfA100taco -uuser -hhost -R...
       start -p101 -Ff -kcfA100taco -uuser -hhost -R...
       start -p110 -Fo -kcfA101taco -uuser -hhost -R...




  This is a clear indication that the user's job has been terminated.
  In this case we need to use the differences between pagecounters of
  the start records to do accounting.

  There is a caveat to all of this;  that is the problem of the last
  dead job in the list.  If the last line in the accounting file is:


       start -p110 -Fo -kcfA101taco -uuser -hhost -R...




  is the last job finished or did it abort?

  Who Used Up 2000 Pages of Paper Today?

  Now we move on to the problem of real time accounting.  Due to limited
  budgets, etc., many institutions would like to strictly enforce limits
  on paper use by students. As jobs are printed their accounts should be
  docked for the amount of paper use.  One way to do this is to have an
  external accounting procedure update a shared database.  The ifhp
  filter has provision for a shell script to be called at the end of
  print job; this is done by both the OF and IF filter.  Thus, we can
  blithely assume that there is a central database carefully getting
  updates from the LPRng software, probably from dozens of different
  printers, and updating the accounting information.

  The first question to be asked is simple:  is this worth it?  Perhaps
  doing accounting as a batch job once an hour/four times a day/once a
  day is cheaper than building an running such a database.  If it costs
  $5K/year for the database software, you might just consider ignoring
  the 10,000 pages that get lost in the shuffle and use a simple set of
  awk/sed/perl scripts to update a database once an hour.

  BAD JOBS - Who Do We Bill?

  We inevitably run into an interesting question: what happens if a job
  does not complete correctly?

  If you use the completion of the OF filter as a success status, I have
  to point out that many students... ummm... users soon find ways to
  send jobs to the printer that will cause it to lock up after their
  output has been printed. These jobs require power cycling of the
  printer and restarting the filter; a bit extreme, perhaps, but it has
  happened.

  I suggest that you simply adopt a 'bill to last user of record'
  attitude,  using the pagecount information as follows:


       start OF -- starting point for THIS job
       start IF --  nice information, but not useful
       start IF --
       end   OF -- ending point for this job - can record infomation
       start OF --
       if no end OF for previous job,  then treat as end OF and
         update accounting.




  Now somebody is sure to complain that they got charged for a bunch of
  pages that they did not use.  This is inevitable;  always carry a can
  of oil for the squeaky wheels.  I might make the observation that once
  is accident, twice is coincidence, but three times is malice; be wary
  of the constant complainer and check out not only him or her but also
  their co-workers.

  How Do We Update the Database?

  I suggest that database update be done as follows:

  You maintain a 'last page reported' counter for each printer in the
  database.  When a successful job reports in,  check to see that
  pagecount + joblength ==  newpagecount;

  If this is not the case,  then you have had a some unsuccessful jobs.
  In this case I strongly recommend that you have a means to request the
  accounting reporting program to go back through the accounting file
  and find the last report for the page counter value and try to
  backtrack through the accounting files.  The accounting file is one of
  the first things to be attacked by students... Ummm...  users.  It
  should NOT be kept on an NFS exported or mounted file system.  It
  should be carefully pruned and copied, perhaps on an hourly basis.
  Now some administrators have fallen in love with network based
  printers; do not believe ANYTHING that comes over a network connection
  without some form of authentication;  PGP has some very nice Public
  Key mechansims for handling this.  This is a major weakness in using a
  database for keeping track of accounting - a weak authentication
  mechanism may lead to denial of service attacks by students flooding
  the database with bogus print usage reports;  suddenly NOBODY can
  print and the administrator is driven to turning off accounting.

  Good luck.  I am never surprised when I encounter yet another wrinkle
  in this area.

  Patrick ("You call me a Bean Counter?  Guido,  break this kid's
  fingers
          with an adding machine!") Powell

  11.2.  How HP Printers Implement Page Counters

  The following is from
  http://www.hp.com/cposupport/printers/support_doc/bpl02119.html

  HP LaserJet Printer Family - Page Count

  Description Of The Page Count Feature On HP LaserJet 4 Family Printers

  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.


       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




  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.

  Example

  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.

  HP LaserJet 4L/5L/6L Printers

  The reason that the page count method for the HP LaserJet 4L/5L/6L
  printers differ from that of the other printers is that the HP
  LaserJet 4L/5L/6L printers do not have any NVRAM available. Thus, no
  way exists for the printer to retain a page count value once the
  printer is powered off. The HP LaserJet 4L/5L/6L printers have only a
  single page count value that increases in increments of one until the
  printer is powered off. At that point, the page count value is reset
  and begins from 0 once again.


  11.3.  Accounting Printcap Options

  The accounting facilities are controlled and enabled by the following
  entries in the printcap file.  The default value is indicated.

  |                  |                              |                                      |
  |Tag               | Default Value                | Purpose                              |
  |af                | NULL                         | accounting file name                 |
  |as                | "jobstart $H $n $P $k $b $t" | accounting info for job start        |
  |ae                | "jobend $H $n $P $k $b $t"   | accounting info for job end          |
  |accounting_server | NULL                         |                                      |
  |achk              | FALSE                        |                                      |
  |la                | TRUE                         | do accounting for 'local' printer    |
  |ar                | FALSE                        | do accounting for 'remote' transfers |



  11.4.  Accounting File

  The most common method of accounting is to record the start and end
  times of a job and its size to the accounting file. A typical entry
  for the printcap defaults are shown below.


       jobstart -H'taco.astart.com' -n'root' -P'ps' -k'cfA938taco.astart.com' \
       -b'1093' -t'Nov  5 19:39:59'
       start -p'12942' -k'cfA938taco.astart.com' -n'root' -h'taco.astart.com' -P'ps' \
       -c'0' -F'o' -t'Sun Nov  5 19:39:25 1995'
       start -p'12944' -k'cfA938taco.astart.com' -n'root' -h'taco.astart.com' -P'ps' \
       -c'0' -F'f' -t'Sun Nov  5 19:39:27 1995'
       end -p'12944' -k'cfA938taco.astart.com' -n'root' -h'taco.astart.com' -P'ps' \
       -b'3' -c'0' -F'f' -t'Sun Nov  5 19:39:58 1995'
       end -p'12942' -k'cfA938taco.astart.com' -n'root' -h'taco.astart.com' -P'ps' \
       -b'2' -c'0' -F'o' -t'Sun Nov  5 19:39:59 1995'
       jobend -H'taco.astart.com' -n'root' -P'ps' -k'cfA938taco.astart.com' \
       -b'1093' -t'Nov  5 19:39:59'




  The jobstart and jobend lines are added by the LPD server,  as
  specified by the as and ae printcap options; the -b (byte count)
  indicates the numbers of bytes in the job.

  The start and end lines are produced by the filters; the of filter has
  an -Fo, and the if filter a -Ff entry.  The filters in the LPRng
  distribution produce the indicated output format by default.  The -p
  value is the current value of a page counter device (if any), and the
  -b value indicates the total number of pages used.

  It should be clear that a simple AWK or Perl script will be able to
  process an accounting file and update accounting information for
  accounting purposes;  the usual problems with truncation, time stamps,
  etc., are left as an exercise for the system administrator.

  Note that the accounting file must exist; LPRng will not create it
  (and also will not create the log file).  This prevents accidentally
  growing log and accounting files.

  11.5.  Remote Server Accounting

  To accommodate even more aggressive and centralized accounting, a
  method to make a connection to a print server and send information to
  the server has been provided as well.  If achk option is set, it is
  assumed that the af entry specifies a connection to server on a remote
  host.  The lpd server will send the as string to the server, and then
  wait for a single line of text from the remote server.  If the first
  word on the return line is accept or hold, the job will be either
  accepted for printing or held.  Any other value will cause the job to
  be deleted.

  At the end of the job the ae string will be sent to the server.  No
  response is expected.  Example:



       :af=accounting.site.com%2300,tcp
       :achk
       :as=starting
       :ae=ending




  The port that the connection originates from will be in the range set
  by the configuration or printcap ``originate_port'' option.

  11.6.  Using Filters For Accounting

  Some sites have expressed interest in using a central accounting
  mechanism to check that users have permissions.  This can be done by
  using the an alternative form of the as (accounting start) and ae
  (accounting end) printcap tags.  If the as and ae are filter
  specifications,  then a filter is invoked.  If the as (accounting
  start) filter returns a non-zero exit status,  then its value is used
  to handle the job as indicated by the ``Abnormal Termination'' codes
  for filters.  At the end of the job the :ae: filter will be invoked in
  a similar manner, but its exit status is ignored.

  When using an accounting filter, the STDIN  is attached (read/write)
  to the accounting file or remote host specified by the af printcap
  option, STDOUT to the output device, and STDERR to the log file.  The
  filter program would be invoked with the default filter options.

  For example, here is a sample entry to check and update accounting


       printer
       :as=|/usr/local/lib/filters/accounting.pl start
       :ae=|/usr/local/lib/filters/accounting.pl end


  11.7.  Accounting Utility accounting.pl

  In order to provide a framework for doing using the outlined
  accounting methods,  the LPRng distribution UTILS directory has a
  accounting.pl script.  This script does the following.

  1. It is assumed that the accounting filter is invoked with the
     following printcap entry.  The start and end is used by the filter
     to determine at which point in the accounting process it is
     invoked.


       printer
       :as=|/usr/local/lib/filters/accounting.pl start
       :ae=|/usr/local/lib/filters/accounting.pl end




  2. It maintains the accounting file as a set of entries in the
     following format:


       START [job identification]
       start -pnn ...
       ...
       end -pnn+pagecount ...
       END -ppagecount [job identification]




  3. Each time the filter is invoked with the start tags, it will add a
     START record to the end of the accounting file.

  4. When it is invoked with the end option, it will update the
     accounting file and add an END entry.

  5. It will handle aborted jobs by looking for jobs with have a START
     entry and a following start line and assuming that they progressed
     to the point of starting print operations, i.e. - the printer page
     counter was accessed and reported.  It will then look for the next
     START entry with a following start line, and assume that the pages
     between the two points were used by the aborted job.

  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 END line is
  written to the accounting file, the local database or remote
  accounting server can be updated.

  12.  Authentication and Encryption

  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.

  LPRng provides native support for the LPR extensions used by MIT and
  the Kerberos 4 implementation.  In addition, it provides Kerberos 5
  based authentication.

  LPRng also supports the use of the PGP (Pretty Good Privacy) program
  and can sign and optionally encrypt command and reponses between
  servers and clients.

  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.

  12.1.  Authentication

  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:

  1. The received command must be checked for consistency,  and the
     remote user and host must be determined.

  2. The remote user and host must be authenticated.

  3. The command and/or spooling operation must be carried out.

  4. The results must be returned to the remote system.


  12.2.  Identifiers

  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.

  When we look into the problem of authentication,  we will possibly
  have a more global user identification to deal with, the
  authentication identifier (AuthID).  One way to deal with this problem
  is to give LPRng intimate knowledge of the UserID and AuthID
  relationship.  While this is possible,  it may be difficult to deal
  with in a simple and extensible manner.  An alternate solution is to
  provide a mapping service,  where the authentication procedure
  provides a map between the UserID and AuthID.

  12.3.  RFC1179 Protocol Extensions

  The RFC1179 protocol specifies that a LPD server command sent on a
  connection has the form:


       \nnn[additional fields]\n




  \nnn is a one octet (byte) value with the following meaning:



       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




  The LPRng system extends the protocol with the following additional
  types:

  REQ_CONTROL 6    do control operation
  REQ_BLOCK   7    transfer a block format print job
  REQ_SECURE  8    do operation with authentication
  REQ_VERBOSE 9    verbose status information
  REQ_LPSTAT 10    lpstat simulation




  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.

  12.4.


  Client Operations for Client To lpd Server Authentication

  Options used:

  o  auth=client to server authentication type

  o  auth_client_filter=client to server transfer program

  o  auth_forward=server to server authentication type

  o  auth_forward_filter=server to server transfer program

  o  auth_forward_id=Server identification

  o  auth_receive_filter=server to server transfer program

  o  auth_server_id=server identification

  This section describes the general purpose interface used for client
  to server authentication.

  The LPRng client will generate a set of commands and place them in a
  file.  The file is then encrypted and/or signed by the appropriate
  authentication method, and is transferred to the server.  The server
  will then decrypt and/or check the signature, perform the requested
  actions, and in turn generate a file of status information.  This file
  is encrypted and/or signed by the server, and sent to the client,
  where it is in turn decrypted and/or checked for correct signature.
  These activities are controlled by the following printcap or
  configuration options.

  1. The  auth  option specifies the authentication type to be used for
     client to server transfers.  For example, auth=pgp would specify
     PGP authentication, auth=kerberos5 would specify Kerberos 5
     authentication, auth=kerberos4 would specify Kerberos 4
     authentication, and auth=user would specify using user provided
     filters for authentication.

  2. For client to server operations, the server id is the value of the
     auth_server_id option.

  3. The auth_forward> option specifies the authentication type to be
     used for server to server transfers.

  4. For server to server operations, the id of the originating server
     is specified by auth_server_id.  and the remote server by
     auth_forward_id.
  5. When doing client to server transfers, the originating user is
     determined by using the current UID of the program as the search
     value for getpwuid().

  The LPRng client will open a connection to the server and send a
  command with the following format:


       \008printer C userid auth\n         - for commands
       \008printer C userid auth jobsize\n - for print jobs




  Note that \008 is a one byte code indicating an authenticated
  transfer.  Printer is the spool queue name, C in the character 'C'
  indicating a client request, userid is the login id of the user, auth
  is the the value of the auth option, and jobsize is the size of the
  job file to be printed.

  On reception of this command,  the server will send a one byte success
  code.  If an error is indicated by a non-zero response, additional
  error status may follow the non-zero success code byte.  At the end of
  this information the connection will be terminated.  The values used
  by LPRng are:


       ACK_SUCCESS 0   success
       ACK_STOP_Q  1   failed; no spooling to the remote queue
       ACK_RETRY   2   failed; retry later
       ACK_FAIL    3   failed; job rejected, no retry




  If the success code is zero, client will use the auth_client_filter to
  encrypt and/or sign a data file to be transferred to the server.  The
  authentication program will have the following IO assignments:


       FD  Options Purpose
       0  R/W     connection to remote host
       1  W       output for returned status
       2  W       errors




  Command line arguments:


       program -C -Pprinter -nuser -Aauth -Rauth_server_id -Ttempfile




  The  tempfile  will contain either a command line as would be
  transferred using the standard RFC1179 protocol, or a print job in
  block format.  See ``RFC1179 Protocol'' for details.  The client
  authenticator program will open and transfer the contents of tempfile
  to the server authenticator, using FD 0 and a format compatible with
  the underlying authentication mechanism.

  If the transfer fails the client authenticator will log error
  information on FD 2 and then exit with error code JFAIL.

  The server will send the client authentication program any error or
  logging information over the FD 0 connection, in a form appropriate to
  the authentication operation.  The client authenticator will write
  this information to FD 1.  If data transfer or authentication fails,
  the authenticator will write an error message to FD 2 and exit with
  error code JFAIL.

  If no error has occured the client authenticator will then exit with
  error code JSUCC.


  12.5.

  Server Operations for Client To lpd Server Authentication

  Options used:

  o  auth_receive_filter=Server (lpd) authentication program

  o  auth_server_id=Server identification

  When an authentication command arrives at the server, it has the
  following form:


       \008printer C userid auth\n          - for commands
       \008printer C userid auth jobsize\n - for print jobs




  The server will attempt to find the printcap for the specified
  printer.  For some operations this printer will be a dummy entry; this
  will simply cause the following operations to use the default
  information in the lpd configuration.

  If a print job is being performed and the spool queue does not exist,
  then the job will be rejected.  A non-zero error code will be written
  to the connection and the operation will terminate.

  The auth value is used to set the AUTHTYPE permission checking value.
  If the AUTHTYPE is not built in, and the auth value does not match the
  printcap or configuration auth option value then authentication will
  fail.  An error message will be logged to the server log file, and a
  non-zero error code and message will be written to the connection to
  the remote client program.

  Many authentication programs require that the users provide some form
  of key or identification.  The auth_server_id option is used for this
  purpose.

  The server will start the server authenticator program and provide the
  following open file descriptors for it.  The program will run as the
  same UID as the lpd server.  If this is a print job transfer, the
  current directory will be the spool directory of the print queue.


       FD  Options Purpose
       0  R/W     socket connection to remote host (R/W)
       1  W       pipe or file descriptor,  for information for server
       2  W       error log
       3  R       pipe or file descriptor,  for responses to client




  Command line arguments:


       program -S -PPRINTER -nUSER -aAUTHTYPE -Rauth_server_id -Ttempfile




  The PRINTER, USER, and AUTHTYPE, are obtained from the original
  command.

  The authentication filter will read the file transferred by the client
  authenticator, decrypt it, and place the decrypted values in the
  tempfile.  It will then write a from_id string to FD 1, which will be
  read by the LPD server and used as the identification of the
  originating end of the connection.

  If the originating program is an LPRng client, then the from_id value
  will be the user identification for the authentication protocol; if
  the originating program is an LPRng server, this value will be the
  server identificatio for the authentication protocol.

  After writing this value, the transfer program will close FD 1.  At
  this point the LPD server will use the contents of the tempfile to
  perform the various requested actions.

  If the transfer step or authentication fails,  then the server
  authenticator will write an error message to FD 2 and exit with error
  code JFAIL.

  The lpd server will record the authentication information returned by
  the server in the AUTHUSER permissions key.

  The lpd server will perform the usual permissions checks, with the
  addition of the indicated permission keys and associated values.
  During this process, any error messages or logging information
  normally returned to client programs will be written to the
  authentication program FD 3.

  The lpd server will carry out either the commands or print job
  specified in the temporary file.  During this process, any error
  messages or logging information normally returned to client programs
  will be written to the authentication program FD 3.

  At the end of the operations, the FD 3 file descriptor will be closed
  and the lpd server will wait for the authentication process to exit.

  The server authentication process will read input from FD 3 until the
  end of input, and then transfer the received information to the client
  side authenticator.  It may use the tempfile to hold the information
  during the reading and transfer process.

  If the transfer of the logging information fails, then the
  authenticator process will exit with error code JFAIL, otherwise it
  will exit with error code JSUCC.

  12.6.

  lpd Server to Server Authentication

  Options used:

  o  auth_forward=Server to server authentication type

  o  auth_forward_id=Destination server authentication id

  The Server to Server authentication procedure is used by one server to
  forward jobs or commands to another server.  It should be noted that
  this forwarding operation puts an implicit trust in the security of
  the client to server to server chain.  The lpd server will perform an
  authenticated transfer to another server when it either needs to
  transfer a job to a remote printer or when it needs to propagate a
  lpq, lprm, or lprc operation.

  The procedure used to by the server to send commands and/or jobs is
  identical to that used by a client, with the minor modification that
  the server is identified as the originating endpoint of the
  connection, and the client authentication information is transferred
  in the file.

  When propagating a command, the server uses the authentication
  information provided for the remote user by the client to server
  authentication program.  When propagating or forwarding a job, the
  server will use the authentication information stored in the job
  control or hold file.  This information will be represented as
  AUTHUSER in the following discussion.

  The auth_forward option value specifies the type of authentication to
  be used to forward authentication, and the sending server uses the
  auth_server_id as its identification, and the auth_forward_id as the
  identification of the remote server.  If there is no user
  authentication information, then a normal, non-authenticated transfer
  will be done.

  The auth_forward_filter will be used for the forwarding operation.

  The sending server takes the part of the client, and will transfer a
  job acting similar to a client.  The initial information transfer from
  the sending server will have the format:


       \008printer F server_user authtype \n          - for commands
       \008printer F server_user authtype controlfilename\n - for print jobs




  The sending server will invoke its authenticator with the arguments:


       auth_forward_filter -F -Pprinter -nserver_user -aauthtype \
       -Rremote_user -Ttempfile




  The tempfile containing the job or command information to be sent will
  have the form:


       user_authentication_info\n
       \n
       <normal file contents>




  That is, the user authentication information is place in the tempfile.

  The tempfile will be transferred to the remote server in the same
  fashion as for a user job.  Any error or logging information returned
  will either be written to the lpd log file or to the previous lpd
  process in the transfer chain.

  On the destination server the same operations for receiving an
  authentication request from a client is performed.  The AUTHUSER,
  AUTHFROM, and AUTHTYPE values will be the derived from the
  authentication request as for the client.  The AUTHSAMEUSER will
  compare the remote client authentication information and the
  authentication information used to create the job.

  When the remote server receives the authentication request, it will
  carry out the same actions as for a client to server transfer,
  modified as follows:

  1. The lpd server will remove the first line of the transferred file,
     which contains the user authentication information, and set
     AUTHUSER to this value.

  2. Authentication is performed using the indicated values.

  3. If authentication succeeds, then the command line or print job
     control file is processed in the normal manner.  This might now add
     more permissions values to tags, but the authentication information
     will not be changed.

  12.7.  Permission Checking

  The following patterns and values can be used to check that a
  particular type of authentication has been used, and what the
  authenticated user information is.

  o  AUTH - authentication is used

  o  AUTHTYPE=globmatch

     This matches the type of authentication request.  Built in values
     include kerberos4, kerberos5, and pgp.

  o  AUTHUSER=globmatch

     The originating auth_user_id value.

  o  AUTHFROM=globmatch

     When a command received from a server (i.e.- forwarded by a
     server), the value is the the forwarding servers authentication
     information, otherwise it is NULL.

  o  AUTHSAMEUSER

     The originating auth_user_id value is compared to the value used to
     create the job.  If they are identical,  the match succeeds.

  For example,  to reject non-authenticated operations, the following
  line could be put in the permissions file.


       REJECT NOT AUTH




  To reject server forwarded authentication as well, we use the
  following.  Note that the ?  forces a value to be present.



  REJECT AUTH AUTHFROM=?*




  If a remote server has id information FFEDBEEFDEAF,  then the
  following will accept only forwarded jobs from this server.  Note that
  AUTHFROM will only match on authenticated transfers; FWDUSER will only
  match on forwarded transfers.


       ACCEPT AUTH AUTHFROM=FFEDBEEFDEAF
       REJECT AUTH
       REJECT NOT AUTH




  To allow only authenticated users to remove jobs you can use:


       ACCEPT AUTH SERVICE=R,M,L,P AUTHSAMEUSER
       REJECT AUTH
       REJECT NOT AUTH




  12.8.  Using PGP for Authentication

  PGP is a well known encryption and authentication program.  For more
  details see the web site http://www.pgp.net or the ftp site
  ftp://ftp.pgp.net.

  LPRng has greatly simplified the use of PGP for authentication by
  building in support as follows.

  The LPD server usually runs as user daemon, and opens files as daemon.
  The system administrator should establish a home directory for daemon,
  and use the PGP key generation facility to create a public and private
  key for the daemon user.  By default, the PGP program puts the public
  and secret key rings in the $HOME/.pgp/ directory, and sets them to be
  readable only by the user.  You should log in temporarily as daemon,
  run the pgp -kg command, and then disable logins for daemon.

  The user id chosen for the LPD server should be easily used to
  identify the server.  For example, lpr@hostname, where hostname is the
  fully qualified domain name of the server is userful.

  The next step is to place the passphrase in a file that is only
  readable by daemon, say ~daemon/.pgp/serverkey, with owned by daemon,
  and with 600 permissions (read/write only by daemon).  This is
  extremely important, as if any other users can read this file then
  security will be severely compromised.

  The next step is to distribute the lpr@hostname public key to all
  users of the LPRng server, and to place the public keys of LPRng users
  in the daemon public key ring.  This can be done using:








  pgp -kxa userid destfile keyfile

  Example:
  > pgp -kxa lpr@astart /tmp/lprkey ~daemon/.pgp/pubring.pgp
  Key for user ID: lpr@astart
  512-bit key, key ID BB261B89, created 1999/01/01

  Transport armor file: /tmp/lprkey.asc
  Key extracted to file '/tmp/lprkey.asc'.




  User can add the lpr@astart key to their public key rings using:


       pgp -ka /tmp/lprkey.asc




  Finally,  the administrator will need to add users public keys to the
  daemon users public key ring.  This can most easily be done by copying
  all the keys (in ASCII text form) to a single file (/tmp/keyfile)and
  using:


       pgp -ka /tmp/keyfile ~daemon/.pgp/pubring.pgp




  12.8.1.


  PGP Configuration

  Options used:

  o  pgp_path=path to PGP program

  o  pgp_server_key=path to server passphrase file

  o  pgp_passphrase=user passphrase file in PGPPATH or $HOME/.pgp

  The pgp_path option is the path to the PGP program.

  The pgp_server_key is the path to the file containing the server
  passphrase.  This file will be read by lpd to get the passphrase to
  unlock the server's keyring.

  The LPRng client software will check to see if the pgp_passphrase
  value (default clientkey) file in $PGPPATH, and if not present, then
  in $HOME/.pgp.  By default, the pass_env option is
  pass_env=PGPPASS,PGPPATH,PGPPASSFD and will pass the values of the
  PGPPATH, PGPPASS, and PGPPASSFD environment variables.  See below for
  a method to use these.

  Example printcap entry:







  pr:
      :lp=pr@wayoff
      :auth=pgp
      :auth_server_id=lpr@wayoff.com
      :pgp_path=/usr/local/bin/pgp
      :pgp_passphrase=.mypass




  One problem with using PGP is the need to have users input their
  passphrases.  If the user is daring,  then the pass phrase can be put
  in the file:
    ~/.pgp/clientkey .  This file will be read by the LPRng client
  program the contents passed to PGP as the passphrase.  This file MUST
  have 0400 permissions (read only by user) and MUST owned by the user.

  A more subtle solution is to use the PGPPASSFD environment variable
  facility.  This causes PGP to read the passphrase from a file
  descriptor.  If the user puts his passphrase in a file,  say
  $(HOME)/.pgp/.hidden, then the following shell script can be used.


       #!/bin/sh
       #  /usr/local/bin/pgplpr script - passphrase in $(HOME)/.pgp/.hidden
       #
       PGPASSFD=3 3<$(HOME)/.pgp/.hidden lpr "$@"




  12.8.2.  Permissions

  If you wish to enforce the use of authentication,  then you should
  modify the lpd.perms file.  Here are some examples.


       # force authentication
       REJECT NO AUTH
       REJECT NO AUTHTYPE=pgp




  12.8.3.  Client Configuration

  One problem with using PGP is the need to have users input their
  passphrases.  If the user is daring,  then the pass phrase can be put
  in the file:
    ~/.pgp/clientkey .  This file will be read by the LPRng client
  program the contents passed to PGP as the passphrase.  This file MUST
  have 0400 permissions (read only by user) and MUST owned by the user.

  A more subtle solution is to use the PGPPASSFD environment variable
  facility.  This causes PGP to read the passphrase from a file
  descriptor.  If the user puts his passphrase in a file,  say
  $(HOME)/.pgp/.hidden, then the following shell script can be used.


       #!/bin/sh
       #  /usr/local/bin/pgplpr script - passphrase in $(HOME)/.pgp/.hidden
       #
       PGPASSFD=3 3<$(HOME)/.pgp/.hidden lpr "$@"



  By using the -V (verbose) flag,  users can see the results of the PGP
  interaction.

  12.9.  Using Kerberos 5 for Authentication

  LPRng Kerberos 5 authentication is based on the Kerberos5-1.0.5
  release as of March 28, 1999.  This was obtained from MIT:

  1. ftp to ATHENA-DIST.MIT.EDU (18.159.0.42), login anonymous, password
     your_email_address

  2. Change into the directory '/pub/kerberos/

  3. Get the README files and look at the details of using FTP to get
     the distribution.  Note that there are also patches available which
     you might want to use.

  Note that the distribution has only the most superficial
  documentation.  There are no man pages for any of the support
  libraries, etc. etc.

  12.9.1.





  Kerberos Installation Procedure


  1. Get the Kerberos 5 distribution.

  2. Compile and install the distribution.

  3. Create the /etc/krb5.conf, /usr/local/var/krb5kdc/kdc.conf files
     using templates from the src/conf-files subdirectory.  See the
     Installation notes and the System Administrators Guide.

  4. Don't forget to create the /usr/local/var/krb5kdc/kdc.acl file; I
     did and it took me HOURS to figure out what was wrong...

  5. Start up the KDC and KADMIN servers - you might want to put the
     following in your rc.local or equivalent file:


       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




  6. use kadmin (or kadmin.local) to create principals for your users.

  7. Now you need to create principals for the lprng servers.  I have
     been using lpr/hostname.REALM as a template- i.e.
     lpr/astart1.astart.com@ASTART.COM for an example.

     Do this for all the servers.  You should use fully qualified domain
     names for the principals.

  8. Now you need to extract the keytab for each of the servers:



  kadmin ...
  ktadd -k file_for_host  lpr/hostname.REALM





  The 'file_for_host' contains the keytab information, which is the
  equivalent information for the server.

  9. Copy the 'file_for_host' to the server (you might want to encrypt
     or use a secure transfer for this).  You need to put this in
     /etc/lpd.keytab.  Make sure that this file is readable only by user
     daemon, as it will try to read the file to get its server key.


       #> ls -l /etc/lpd.keytab
       -rw-------  1 daemon  wheel  128 Jan 16 11:06 /etc/lpd.keytab




  10.
     Modify (uncomment) the following entries in /etc/lpd.conf:


       auth=kerberos5
       kerberos_keytab=/etc/lpd.keytab
       kerberos_service=lpr
       # optional - explicit prinipal name for server, used by clients
       #kerberos_server_principal=lpr/hostname@REALM
       # optional - forwarding to another server with authentication
       # kerberos_forward_principal=lpr/hostname@REALM





  The kerberos_keytab entry is the location of the keytab file;
  kerberos_service is the service that will be used to generate a server
  principal name.  This is the lpr used in the above key generation
  operations.

  kerberos_life and kerberos_renew determine the lifetime and
  renewability of Kerberos tickets.  The lifetime defaults to 10 hours,
  and the ticket will be refreshed when it expires if necessary.

  11.
     You might like to check out the authentication using the sclient
     and sserver test programs.  These link in the kerberos
     authentication and allow you to test it without all of LPD being
     involved.














  cd LPRng/src; make sserver sclient
  usage: sserver [-D] [-p port] [-s service] [-S keytab] file
     -D turns debugging on
     1. opens TCP   port 'port' (default 1234)
     2. waits for a connection
     3. when a connection comes in,  uses 'service' to get the principal
         name of the server,  and looks up the key in keytab file.
     4. Goes through the kerberos authentication.
     5. Copies the input from remote server to 'file'
     6. exits.
   usage: sclient [-D] [-p port] [-s service] host file
     -D turns debugging on
     1. opens a connection to port on host (i.e. - host%port)
     2. does the authentication.  You must have done kinit to get
         for your ticket to be valid.
     3. sends the file to remote host.





  To test this, start up sserver on one host/window, then run sclient.
  The error messages are pretty straight forward, and when in doubt,
  look at the source code which has more than sufficient information.

  12.9.2.  Testing Transfers

  Restart the server, and then try getting information using LPQ.

  You can turn on tracing at LPQ to see if authentication is being used
  and is working:


       lpq -Dnetwork,database




  If the lpq works, then try send a job and see if the transfer is
  successful.

  12.9.3.  Explicit Server Principal Name

  If you are using printers in different domains,  then you can put the
  explicit principal name of the server in the printcap file, using the
  server_principal entry.  For example:


       lp_offsite
           :lp=printer@erehwon.org
           :auth=kerberos5
           :auth_server_id=lpr/erehwon.org@BLUESKY.ORG




  12.10.  Using Kerberos 4 for Authentication

  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.

  To enable Kerberos 4 support, you must modify the LPRng/src/Makefile
  and recompile the LPRng code.  You should be aware that this is not a
  supported extension, and is provided as a courtesy to MIT and Project
  Athena.

  13.

  Status Monitoring and Logging

  Options used:

  o  stalled_time#time after which to report a stalled active job

  The most commonly used tool for LPRng status is LPQ.  However, the LPC
  command can be used, and you can also get real time logging of status
  to a remote host.


  13.1.  LPQ status reporting

  The LPQ status display produced by LPRng has three formats.

  13.2.  LPQ Short Format (lpq -s)

  This is one line per spool queue:


       % lpq -sa
       t1@astart110  (printing disabled) 1 job
       t2@astart110  (routed/bounce to t1@astart110.astart.com) 0 jobs
       t3@astart110  (forwarding to t3a@astart110.astart.com)
       t3a@astart110  (forwarding to t2@astart110.astart.com)
       t4@astart110  (subservers t5, t6)  0 jobs
       t5@astart110  (serving t4) 0 jobs
       t6@astart110  (serving t4) 0 jobs




  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
  -a option forces status for all queues or the queues in the all
  printcap entry to be returned.

  The stalled_time (default 120 seconds) printcap option can be used to
  set a time after which active jobs will be reported as stalled.

  13.3.  LPQ Long Format (lpq default, lpq -l, lpq -L)

  This is the default status display.  It is a nicely formatted,
  extremely verbose format that is suitable for humble human
  interpretation. For example:
















  % lpq -a
  Printer: t1@astart110  'Test Printer 1' (printing disabled)
   Queue: 1 printable job
   Server: no server active
   Status: finished operations at 09:44:00
   Rank   Owner/ID                   Class Job  Files               Size Time
  1       papowell@astart110+202228663    A 10663 /tmp/hi               3 20:22:29
  Printer: t2@astart110  'Test Printer 2' (routed/bounce to t1@astart110.astart.com)
   Queue: no printable jobs in queue
   Status: finished operations at 16:30:08
  Printer: t3@astart110  (forwarding to t3a@astart110.astart.com)
  Printer: t3a@astart110  (forwarding to t2@astart110.astart.com)
  Printer: t4@astart110  (subservers t5, t6)
   Queue: no printable jobs in queue
   Status: finished operations at 09:44:06
  Server Printer: t5@astart110  (serving t4)
   Queue: no printable jobs in queue
   Status: finished operations at 09:44:06
  Server Printer: t6@astart110  (serving t4)
   Queue: no printable jobs in queue
   Status: finished operations at 09:10:00




  The lpq -l (longer information) option causes more of the status
  information to be printed.  You can use increasing numbers of lpq -l
  options ( lpq -ll also works) to get more status.  Use  lpq -L for the
  maximum amount of status information.

  13.4.  LPQ Verbose Format (lpq -v)

  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.





























  % lpq -v
  Printer: t1@astart110
   Comment: Test Printer 1
   Printing: no
   Spooling: yes
   Queue: 1 printable job
   Server: no server active
   Status: accounting at end 'papowell@astart110+094352860' at 09:44:00
   Status: printing 'papowell@astart110+094352860', closing device at 09:44:00
   Status: printing 'papowell@astart110+094352860', finished  at 09:44:00
   Status: subserver status 'JSUCC' for 'papowell@astart110+094352860' \
              on attempt 1 at 09:44:00
   Status: finished operations at 09:44:00
   Job: papowell@astart110+202228663 status= 1
   Job: papowell@astart110+202228663 CONTROL=
   - Hastart110.astart.com
   - Ppapowell
   - J/tmp/hi
   - CA
   - Lpapowell
   - Apapowell@astart110+202228663
   - Qt1
   - fdfA010663astart110.astart.com
   - N/tmp/hi
   - UdfA010663astart110.astart.com
   Job: papowell@astart110+202228663 HOLDFILE=
   - active 0
   - done 0
   - hold 0
   - move 0
  ....




  13.5.  lpc status

  The LPC status command is used to show the status of the queues
  currently being managed by the LPRng server.  Note that this form of
  the command is supported only by LPRng, and is not backwards
  compatible with BSD LPR implementations.


       %lpc status all
        Printer           Printing Spooling Jobs  Server   Slave Redirect Status/Debug
       lw4@astart4         enabled  enabled    0    none    none
       lw5@astart4         enabled  enabled    0    none    none




  The status display has a heading line and summary of the server
  status.

  13.6.  Remote Logger Operation

  Several sites have wanted a way to provide central logging of job
  status and/or information.  In order to do this,  the following
  functionality is provided with LPRng.

  13.6.1.



  Logger Network Communication

  Options used:

  o  logger_destination=logger information destination

  o  logger_pathname=pathname of temp file for log information

  o  logger_max_size=max size in K of temp file for log information

  o  logger_timeout=time between connection attempts

  The printcap/configuration variable logger_destination specifies a
  destination in the standard host%port 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.

  Log information is save in a temporary file specified by logger_path,
  and up to logger_max_size  K bytes of data will be saved.

  If a connection cannot be made to the logger_destination, then every
  logger_timeout seconds a new connection attempt will be made.  If
  logger_timeout is 0, then a connection attempt will be made every time
  new data arrives to be logged.

  13.6.2.  Logger Messages

  Log messages consist of a single line terminated with a newline (\n)
  character.

  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.

  13.6.3.  Message Format

  Each message is encoded as a URI escaped string.  That is, non-
  alphanumeric characters are encoded as the 3 character sequence %xx,
  where xx is the hexadecimal value of the character.  The message has
  the format key=value, where key indicates the message type.  For
  example:


       dump=host=astart4.astart.com%0aprinter=t1%0aprocess=1613%0aupdate_time=1999-03-2
         3-20:32:17.148%0avalue=queue=holdall 0%25250aprinting_aborted=0x0%25250aprinting
         _disabled=0x0%25250aspooling_disabled=0x0%25250a%250a%0a




  The following keys are used:

  1. dump
     A status dump of the current contents of a print queue.

  Each message has a set of headers and a value.  For example, the
  decoded dump message from the previous section would be:








  host=astart4.astart.com
  printer=t1
  process=1613
  update_time=1999-03-23-20:32:17.148
  value=queue=holdall 0%250aprinting_aborted=0x0%250aprinting_disabled=0x0%250aspo
    oling_disabled=0x0%250a%0a




  Each line consists of a key and a value.  The host key indicates the
  host name, printer is the print queue, process is the process which
  generated the report or action, update_time is the time at which the
  report was generated, and value is the value of the report.

  The decoded value of the above report is:


       queue='holdall 0%0aprinting_aborted=0x0%0aprinting_disabled=0x0%0aspooling_dis
         abled=0x0%0a




  The queue key provides the current value of the queue control file.

  13.6.4.  Dump Messages

  Dump messages are generated at the start of operations, and consist of
  a list of queue status messages.

  13.6.5.  LPD Messages

  These are used to indicate LPD startup or change in operation.


       Decode: lpd=host=astart4.astart.com%0aprocess=1672%0aupdate_time=1999-03-23-20:5
       1:10.507%0avalue=Starting%0a
       host=astart4.astart.com
       process=1672
       update_time=1999-03-23-20:51:10.507
       value=Starting
       lpd: 'Starting'




  13.6.6.  Job Status Messages - UPDATE

  Update messages are used to report changes in the queue contents, such
  as job arrival.


       Decode: update=host=astart4.astart.com%0aidentifier=papowell@astart4+676%0anumbe
        ...
       host=astart4.astart.com
       identifier=papowell@astart4+676
       number=676
       printer=t1
       process=1677
       update_time=1999-03-23-20:51:17.197
       value=bnrname=papowell%0acf_esc_image=Apapowell@astart4+676%250aCA%250aD1999-03-
         ...



  This update message reports the arrival of a new job at the queue.
  The value field reports the control file contents:


       cf_esc_image=Apapowell@astart4+676%0aCA%0aD1999-03-23-20:51:17.151%0aHastart4.as
          tart.com%0aJ/tmp/hi%0aLpapowell%0aPpapowell%0aQt1%0aN/tmp/hi%0afdfA676astart4.as
          tart.com%0aUdfA676astart4.astart.com%0a
       class=A
       date=1999-03-23-20:51:17.151
       file_hostname=astart4.astart.com
       fromhost=astart4.astart.com
       held=0x0
       hf_name=/var/tmp/LPD/t1/hfA676
       hold_class=0x0
       hold_time=0x0
       identifier=papowell@astart4+676
       job_time=0x36f86f45
       jobname=/tmp/hi
       logname=papowell
       number=676
       priority=A
       queuename=t1
       size=3
       transfername=cfA676astart4.astart.com
       update_time=1999-03-23-20:51:17.187




  The update_time field in the section above is the time that the job
  information was last updated.  The cf_esc_image value is the URL
  escaped control file information.

  13.6.7.  Printer Status Messages - PRSTATUS

  These messages report printing or other activity related to a job.


       Decode: prstatus=host=astart4.astart.com%0aidentifier=papowell@astart4+676%0anum
       ber=676%0aprinter=t1%0aprocess=1692%0aupdate_time=1999-03-23-21:02:04.855%0avalu
       e=finished 'papowell@astart4+676'%252c status 'JSUCC'%0a

       host=astart4.astart.com
       identifier=papowell@astart4+676
       number=676
       printer=t1
       process=1692
       update_time=1999-03-23-21:02:04.855
       value=finished 'papowell@astart4+676'%2c status 'JSUCC'
       PRSTATUS: 'finished 'papowell@astart4+676', status 'JSUCC''




  13.7.

  LPR -mhost%port and user logging support

  The lpr -m option is used to request that lpd send mail to the user
  when a job has completed.  LPRng extends this to allow mail addresses
  of the form
   host[%port][/(TCP|UPD)] to request that logging information be sent
  to the user as well.

  The administrator should be aware that this is a possible security
  loophole, and that the allow_user_logging flag must be enabled to
  allow this operation.

  14.  RFC 1179 - Line Printer Daemon Protocol

  RFC1179 can be obtained from the LPRng distribution, in the
  LPRng_DOC/rfc1179 directory, or from one of many sites which mirror
  the RFCs.

  This RFC is an informational RFC, which means that the information in
  it is meant as a guide to users, and not as a fixed standard.  In
  addition, the RFC tried to document the behavior of the BSD LPD print
  server, and left out many details dealing with error recover, error
  messages, extensions to the protocol, etc.

  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.

  14.1.





  Ports and Connections

  Options used:

  o  lpd_port=Port for LPD to accept connection

  o  originate_port=Ports to originate connections on

  o  reuse_addr  Set SO_REUSEADDR flag on connection

  o  retry_econnrefused  Retry on connect ECONNREFUSED error

  o  retry_nolink  Retry on device open or connection ffailure

  o  socket_linger#  Linger time for sockets

  RFC1179 requires that the lpd server listen for TCP/IP connections on
  port 515.  This port is registered with the Internet Naming Authority,
  and the /etc/services file or TCP/IP services database usually has an
  entry:


       printer     515/tcp     spooler     # line printer spooler




  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 reserved and privileged ports.  By convention,
  ports in the range 1-1023 can only bound 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.

  In a UNIX environment,  this means that the user programs lpr, lprm,
  lpq, and lpc would have to be SETUID root.

  As experience has shown, for security purposes, the fewer programs
  that need to have privileged status, the better.  LPRng uses the
  lpd_port=printer configuration option to set the actual port to be
  use.  By default, this is port 515, but can be set to other values.


  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 stale
  packets.  When a connection between sourcehost, sourceport and
  desthost, destport 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 sourcehost, sourceport and desthost, destport for a
  period long enough for all stale packets to be removed from the
  system.  This is approximately 10 minutes long.

  In order to simplify assignments of ports, timing out connections, and
  other matters, many TCP/IP packages do keep track of explicit
  connections originating 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 to 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.

  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:
  host 1, port 721 and host 2, port 515
  host 1, port 722 and host 2, port 515
  host 1, port 723 and host 2, port 515
  host 1, port 724 and host 2, port 515
  host 1, port 725 and host 2, port 515
  host 1, port 726 and host 2, port 515
  host 1, port 727 and host 2, port 515
  host 1, port 728 and host 2, port 515
  host 1, port 729 and host 2, port 515
  host 1, port 730 and host 2, port 515
  host 1, port 731 and host 2, port 515

  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.

  The LPRng code has tried to provide several methods to deal with these
  problems.  Firstly, the originate_port=512 1023 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 originate_port=721 731, but it turns out that most BSD LPD
  based implementations only check for a reserved originating port.  By
  using 512 ports we get a greatly reduced rate of errors due to lack of
  ports due to pending timeouts.

  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 getsockopt() and
  setsockopt() 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 setsockopt() facility can be used to set the
  SO_REUSEADDR 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.

  It would appear that by setting SO_REUSEADDR on the originating end
  that we have solved our problems.  However, unless the destination end
  of the connection sets its SO_REUSEADDR 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.

  The reuse_addr flag (default off) forces the LPRng software to set the
  SO_REUSEADDR flag on originating connections.  As indicated, this will
  allow ports to be reused immediately for outgoing connections, rather
  than waiting for a timeout.

  While the reuse_addr 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
  connect() 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.

  The retry_econnrefused (default on) flag is used to specify that we
  retry connections in this manner.  When this is set, a connection
  refused error causes the connection to be retried using a new port.
  This will be repeated until all available ports have been tried.

  When printing a job and the lpd server connection to a remote site or
  device open fails, the retry_nolink (default on) will cause the
  attempt to be retried indefinately.  The combination of
  retry_econnrefused and retry_nolink will provide robust connection
  attempts to remote systems.

  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.

  The setsockopt() 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 socket_linger period.  If
  socket_linger is 0, then no SO_LINGER operation is done.

  In summary, if you experience problems with connection failures due to
  port exhaustion, first try setting the reuse_port flag, and you should
  see a reduction.  Check to ensure that the retry_econnrefused and
  retry_nolink 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 connect_retry interval, say
  connect_retry=120 (2 minutes).

  14.2.  Protocol Requests and Replies

  Options used:

  o  remote_support=Remote operations supported

  After a connection has been established, a request can be sent to the
  lpd 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.


       \NNNprinter[ options]\n
         NNN    Operation




      |    |         |                                   |         |
      |NNN | RFC1179 | Operation                         | program |
      |1   | yes     | start print                       | lpc     |
      |2   | yes     | transfer a printer job            | lpr     |
      |3   | yes     | print short form of queue status  | lpq     |
      |4   | yes     | print long form of queue status   | lpq     |
      |5   | yes     | remove jobs                       | lprm    |
      |6   | LPRng   | do control operation              | lpc     |
      |7   | LPRng   | transfer a block format print job | lpr     |
      |8   | LPRng   | secure command transfer           | lpc     |
      |9   | LPRng   | verbose status information        | lpq     |


  After the request has been sent, then a reply will be returned.  In
  general the reply has the following form:


       \000\n    Success
       \NNN\n    Failure (NNN is error code)
       text\n    Text or status information




  As can be seen, this protocol is extremely simple, but there are a set
  of problems due to the loosely written language of RFC1179.

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

  2. The original RFC1179 protocol did not provide any way to do remote
     control of queues or LPD servers.  This has been added to the
     protocol.  As a side effect, if you try to use lpc to control a
     non-LPRng printer, it will not work.

  3. You can specify that a network printer is non-LPRng by using the
     remote_support=RQVMC option and specify the operations supported by
     the printer.  The letters R, Q, V, M, and C stand for lpr, lpq, lpq
     -v (verbose), verbose lpq, lprm, and lpc operations respectively,
     and indicate that these are supported.  If remote_support does not
     allow a particular operation, then the LPRng software will not send
     a corresponding request to the printer.  For example,
     remote_support=R would restrict operations to spooling jobs only,
     and the LPRng software would not query the printer for status.

  14.3.


  Job Transfer

  Options used:

  o  longnumber  Long job number (6 digits)


  o  send_data_first  Send data files first

  o  use_shorthost  Use short hostname

  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.

  A file transfer request has the form:
            |                |                       |   |   |
            |Command         | Purpose               |   |   |
            |\001\n          | abort                 |   |   |
            |\002nnnn cfname | control file transfer |   |   |
            |\003nnnn dfname | data file transfer    |   |   |


  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.

  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:
               |       |                          |   |   |
               |Status | Purpose                  |   |   |
               |\000   | Accepted, proceed        |   |   |
               |\nnn   | Rejected with error code |   |   |


  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.

  If LPRng sends a reject code, as an extension to RFC1179 it also sends
  an error message.   Note that the values for error codes are not
  defined, nor are their causes.  LPRng uses the following values for
  error codes, which appear to be compatible with many, but not all, of
  the BSD LPD based systems:
           |     |                                     |   |   |
           |Code | Error                               |   |   |
           |\000 | Accepted, proceed                   |   |   |
           |\001 | Queue not accepting jobs            |   |   |
           |\002 | Queue temporarily full, retry later |   |   |
           |\003 | Bad job format, do not retry        |   |   |


  When the sender gets the reply indicating success, it sends the nnnn
  bytes of the control or data file, followed by a \000 octet.  The
  receiver will then reply as above; a single \000 octet indicating
  success.

  The above procedure is carried out until all data files and the
  control file of a job are transferred.

  RFC1179 is silent on the following issues:

  1. When sending a job, do you send the control file first, followed by
     the data file(s), or the data files first?

  2. When sending multiple jobs, can you send them on a single
     connection, or do you have to establish a new connection for each
     job?


  LPRng will accept 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 send_data_first printcap option can be used to force
  data files to be sent first.

  RFC1179 states that:

       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.


  The should 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:

  1. 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 longnumber
     option will allow LPRng to use a 6 digit job number for files in
     the print queue.

  2. 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 use_shorthost option will
     force it to use short host names in control and data files.

  3. The cfA 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. cfA) and but can range to Z (highest, i.e. cfZ).  All
     known spoolers except LPRng seem to ignore the actual value of the
     letter.

  14.4.  Data File Transfer

  As discussed, a data file is transferred using the command below.
              |                |                    |   |   |
              |Command         | Purpose            |   |   |
              |\003nnnn dfname | data file transfer |   |   |


  From RFC1179:

       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 con-
       structed the data file.  Interpretation of the contents of
       the data file is determined by the contents of the corre-
       sponding control file.


  There are several surprises in RFC1179.

  1. Apparently a job should only consist of a single data file.  This
     is a severe limitation,  and in fact the BSD LPR and other print
     spoolers process jobs with multiple data files.  By convention,
     these data files have names of the form dfA, dfB, ...  dfZ, dfa,
     dfz.

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

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

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

     When 'piping' into the lpr program, this can be very useful as it
     eliminates the need to create temporary files on the senders host.
     The lpr -k option for details.  Note that some print spoolers do
     not use this interpretation, and this option should be used
     carefully.

  14.5.  Control File Contents

  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:


       Hastart4.astart.com
       J(stdin)
       CA
       Lpapowell
       Apapowell@astart4+955
       Ppapowell
       fdfA955astart4.astart.com
       N(stdin)
       UdfA955astart4.astart.com




  The following are the letters and their meanings in the control file.

  The A (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: papowell@astart4+955 is job number 995 submitted by
  papowell from host astart4.

  The C (Class) line is set by the lpr -C class option, and the value
  can be used to control printing.  For example, the lpc class zone
  command would restrict job printing to only jobs with class zone.

  The H (hostname), P (username), and J (jobname) fields are used to
  identify the host and user which sent the job, and to provide
  information to be displayed by lpq when reporting job status.

  The L (print banner page) field is one that has caused many problems
  for users.  RFC1179 indicates that its presence causes the banner page
  to be printed, and its absense suppresses banner pages.  The lpr -h
  option suppresses putting this line into the control file.  Usually
  the L field is a duplicate of the P field.

  The M (mail information) field supplies a mail address for LPRng to
  send mail to when a job is completed.  See ``LPR -m and user logging''
  for more details.

  |  |         |                                                          |   |
  |X | RFC1179 | Meaning                                                  |   |
  |A | LPRng   | Identifier for job                                       |   |
  |C | RFC1179 | Class for banner page                                    |   |
  |H | RFC1179 | Host name                                                |   |
  |I | RFC1179 | Indent Printing                                          |   |
  |J | RFC1179 | Job name for banner page                                 |   |
  |L | RFC1179 | Print banner page                                        |   |
  |M | RFC1179 | Mail When Printed                                        |   |
  |N | RFC1179 | Name of source file                                      |   |
  |P | RFC1179 | User identification                                      |   |
  |Q | LPRng   | Queue name                                               |   |
  |R | LPRng   | Accounting info                                          |   |
  |S | RFC1179 | Symbolic link data                                       |   |
  |T | RFC1179 | Title for pr                                             |   |
  |U | RFC1179 | Unlink data file                                         |   |
  |W | RFC1179 | Width of output                                          |   |
  |Z | LPRng   | Filter options                                           |   |
  |1 | RFC1179 | troff R font                                             |   |
  |2 | RFC1179 | troff I font                                             |   |
  |3 | RFC1179 | troff B font                                             |   |
  |4 | RFC1179 | troff S font                                             |   |
  |c | RFC1179 | Plot CIF file                                            |   |
  |d | RFC1179 | Print DVI file                                           |   |
  |f | RFC1179 | Print formatted file                                     |   |
  |g | RFC1179 | Plot file                                                |   |
  |k | RFC1179 | Reserved for use by Kerberized LPR clients and servers.  |   |
  |l | RFC1179 | Print file leaving control characters                    |   |
  |n | RFC1179 | Print ditroff output file                                |   |
  |o | RFC1179 | Print Postscript output file                             |   |
  |p | RFC1179 | Print file with 'pr' format                              |   |
  |t | RFC1179 | Print troff output file                                  |   |
  |v | RFC1179 | Print raster file                                        |   |
  |z | RFC1179 | Reserved for future use with the Palladium print system. |   |

  The N (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.

  The I (indent) and W (width) fields are supposed to specify a page
  indent and width for printing.  These fields are passed to filters if
  they are present.

  The Q (queue name) field is an LPRng extension, and contains the name
  of the print queue the job was originally sent to.  See ``qq printcap
  option'' for details.

  The R (accounting info) field was added by LPRng to allow a specified
  account to be billed for job printing.  The lpr -Rname option can be
  used to specify the accounting name.

  The S (symbolic link) and U (unlink after printing) lines were used by
  the original BSD LPD print system to control how it passed files to
  the print server.  LPRng ignores these lines.  In fact, it will remove
  S lines and force the U lines to refer only to job data files.  This
  closes a nasty security loophole on non-LPRng print spoolers.

  The T (pr job title) is used with the lpr -p operation to supply a
  banner to the pr program.

  The Z (filter options) value is specified with lpr -Zoption and is
  passed to the data file filters during the printing operation.  See
  ``Filters'' for details on how the this is used during the printing
  process.


  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
  ``Filters'' for details on how the data file formats are used during
  the printing process.

  14.6.  LPQ Requests

  The RFC1179 protocol specifies that lpq print status requests can be
  sent to the lpd server.  The lpq requests have the format:


       \003printer [id]* \n    short
       \004printer [id]* \n    long
       \009printer [id]* \n    LPRng extension- verbose




  The lpd print server will then return queue status and close the data
  connection.

  RFC1179 does not state in any manner what the format of the queue
  status should be.  Thus, implementors have been free to augment or
  change the status as they like.  Even the BSD LPR status format has
  been changed from different versions.

  See ``Status Monitoring and Logging'' for information on the formats
  returned.

  The id 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 A, H, or P fields match any of the id values.

  Note that since there is no identification of the information
  requestor, then restriction of information is almost impossible.


  14.7.  LPRM Requests

  The RFC1179 protocol specifies that lprm job removal requests can be
  sent to the lpd server.  The lpq requests have the format:


       \005printer user [id]* \n




  The lpd print server will search the specified print queue and remove
  any job whose ID, hostname, or user name information from the control
  file A, H, or P fields match any of the id values and for which the
  user has permission to perform a removal operation.  See the
  ``/etc/lpd.perms'' file for details on permissions.

  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.

  14.8.  LPC Requests

  LPRng has extended the RFC1179 protocol to allow queue and printer
  control commands to be sent to the LPD server.  The format of these
  commands are:



       \006printer user key [options]




  The following commands are supported.
  |                                                             |                                               |   |   |
  |Command                                                      | Operation                                     |   |   |
  | active [printer[@host]]                                     | check to see if server accepting connections  |   |   |
  | abort   (printer[@host] | all)                              | terminate server process printing job         |   |   |
  | disable (printer[@host] | all)                              | disable queueing                              |   |   |
  | debug   (printer[@host] | all) debugparms                   | set debug level for printer                   |   |   |
  | 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 for server                        |   |   |
  | lpq (printer[@host] | all) (name[@host] | job | all)*       | invoke LPQ                                    |   |   |
  | lprm (printer[@host] | all) (name[@host]|host|job| all)*    | invoke LPRM                                   |   |   |
  | 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                                 |   |   |
  | release  (printer[@host] | all) (name[@host] | job | all)*  | release job                                   |   |   |
  | 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                                   |   |   |
  | defaultq                                                    | default queue for LPD server                  |   |   |
  | local    (printer | all)                                    | client printcap and configuration information |   |   |
  | server    (printer | all)                                   | server printcap and configuration information |   |   |


  Many of these commands support extremely specialized operations for
  print queue management, However, the following are the most commonly
  used and are supported by the BSD LPD print spooling system as well:

  o   start, stop, enable, disable
     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.

  o   abort, kill
     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.

  o   topq Places selected jobs at the top of the print queue.

  o   status
     Shows a status display of the print spools on the server.

  The following commands are extensions to the basic set provided by the
  BSD LPD system.

  o   lpq, lprm
     Invokes the lpq or lprm program from lpc.  Useful when in the
     interactive mode.

  o   hold, holdall, release
     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.

  o   move, redirect
     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.

  o   active, lpd, reread
     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 lpd server.  The reread command causes a SIGHUP signal
     to be sent to the lpd process, causing it to reread the
     /etc/lpd.conf, /etc/printcap, or /etc/lpd.perms files.  This is
     usually done when some important configuration information has been
     modified and the administrator wants to have the server use the new
     information.

  o   debug
     This is a desperation facility for developers that allows dynamic
     enabling of debug information generation.  Not normally used in
     general operation.

  o   local, server
     These commands will print out the configuration information in the
     local /etc/lpd.conf file, as well as the printcap information for
     the specified printers; local prints what the LPRng clients (lpr,
     lpq, ...) would use while server prints what the LPRng server (lpd)
     would use if running on this host.  This is an extremely useful
     diagnostic tool for administrators.  Not normally used in general
     operation.

  14.9.

  Block Job Transfer

  Options used:

  o  send_block_format  Transfer job as a block

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

  The transfer command line has the form:


       \006printer user@host size\n




  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.

  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.

  14.10.  Authenticated Transfer

  RFC1179 does not provide any authentication or encryption mechanism
  for the transfer of jobs or commands to the lpd print server.  The
  Authenticated Transfer operation was added to allow an encrypted or
  authenticated transfer of print jobs or commands.

  Since there are various restrictions on the incorporation of
  authentication facilities into programs, LPRng supports authentication
  by providing a simple interface to encryption programs.

  The idea is that when authentication is required when sending a job,
  LPRng will generate a block transfer job as described for the ``Block
  Transfer operation,'' and then invoke a set of programs to encryt and
  transfer the file, and encrypt and transfer the returned status.

  Similarly, when sending a command, the command information will be
  placed in a file and the encrypted file will be transferred.

  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.

  See ``Authentication and Encryption'' for details on the
  authentication interface.

  15.  Acknowledgements

  I'd like to thank the proof-readers from the LPRng mailing list, in
  particular: Lars Anderson, Bertrand Decouty, Horst Fickenscher, Philip
  Griffith, Gordon Haverland, John Perkins, Richard S. Shuford, James H.
  Young and the ones I forgot.

  Finally,  Patrick would like to thank all of the LPRng users who so
  relentlessly tried the incredible number of permutations and
  combinations of printers and software, and made requests for just one
  more feature.

























