LPRngLogo LPRngTool

LPRng Reference Manual

6 Sep 2007 (For LPRng-3.8.32)

Patrick A Powell


Astart Technologies
7330 Opportunity Road,
San Diego, CA 92111
Phone 858-874-6543
Fax 858-751-2435

The LPRng; Printing Software consists of the LPRng print spooler, the ifhp print filter, and the LPRngTool graphical user interface.

The LPRng print spooler is an enhanced, extended, and portable implementation of the Berkeley lpr print spooler functionality. While providing the same interface and meeting RFC1179 requirements, the implementation is completely independent and provides support for the following features: lightweight (no databases needed) lpr, lpc, and lprm programs; dynamic redirection of print queues; printer pooling and load balancing; automatic job holding; highly verbose diagnostics; client programs do not need to run SETUID root; greatly enhanced security checks; load balancing across multiple printers; and a greatly improved permission and authorization mechanism. The source software compiles and runs on a wide variety of UNIX systems, and is compatible with other print spoolers and network printers that use the lpr interface and meet RFC1179 requirements. Included in the LPRng print spooler distribution is a set of customizable banner page generation programs.

The SVR4 lp and lpstat functionality is provided by a set of emulator programs, and LPRng can be easily integrated with the Samba SMB support package. For users that require secure and/or authenticated printing support, LPRng supports SSL (using OpenSSL), Kerberos 5, MIT Kerberos 4 extensions to LPR, PGP, and simple MD5 based authentication. Additional authentication support is extremely simple to add.

The ifhp print filter converts print jobs into formats compatible with PostScript, PCL, text, and other printers and provides diagnostic and error information as well as accounting information.

The ;LPRngTool& Graphical User Interface provides a simple to use configuration and monitoring tool. It allows users to monitor printers and generate printcap entries in a simple manner, as well as providing extensive help and diagnostics.

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


Table of Contents
Preface
Introduction
Acknowledgements
Shell Prompts
Typographic Conventions
Notes, warnings, and examples
1. Introduction
1.1. What is LPRng?
1.2. Additional Resources
1.3. Frequently Asked Questions
1.4. License, Copyright, and Disclaimer
1.5. Commercial Support
1.6. Web Site
1.7. FTP Sites
1.8. Mailing List
1.9. PGP Public Key
1.10. References and Standards
1.10.1. RFCs
1.10.2. PostScript
1.10.3. HP PCL 5
1.10.4. HP PJL
1.10.5. PDF
2. Installation
2.1. Getting Source Code and Support Programs
2.2. PATH Environment Variable and Utilities
2.3. Network Mounted File System and Spool Directories
2.4. Daemon User and Daemon Group
2.5. Configuration
2.6. System and User Printcap, lpd.conf, and lpd.perms files
2.7. Checking System Installation with checkpc
2.8. Compilation and Install
2.9. Installation Problems
2.10. Updating Print Spooler Software and Startup Scripts
2.10.1. SunOS, Linux, and BSD Derived Systems
2.10.2. Solaris, HP-UX, and other SysVR4 Derived Systems
2.11. Emulation for UNIX SystemV lp and lpstat
2.12. SAMBA and LPRng
2.13. Security Concerns
3. System Specific Notes
3.1. Solaris
3.2. Linux
3.3. AIX
3.4. AppleTalk Support
4. Print Spooling Tutorial
4.1. Overview
4.2. Sample Printcap Entry
4.3. Setting Up the Tutorial Configuration
4.4. Restoring Original Configuration
4.5. Printing a File and Checking Status
4.6. Selecting the Print Queue
4.7. Controlling the Print Queue
4.8. Job Removal
4.9. Print Job Filters
4.9.1. Control Files and Filter Options
4.9.2. Filter Environment Variables
4.9.3. Using Command Line and Printcap Options In Filters
4.9.4. Filter Exit Codes
4.9.5. Job Formats and Filter Selection
4.10. Job File Format Conversion with Filters
4.10.1. Simple Filter with File Format Detection
4.10.2. The ifhp Filter
4.10.3. The Jaggies - LF to CR-LF Conversion With lpf
4.10.4. Store and Forward Spool Queues
4.10.5. Filtering Job Files In Transit
4.11. Printcap Basics
4.11.1. Printcap Processing Format
4.11.2. Printcap Information From Programs and Databases
4.11.3. User Printcap Information
4.12. Banner Printing and the OF filter
4.13. Printing from lpr Directly To A Device
4.14. Moving Jobs From Queue to Queue and Redirecting Queues
4.15. Print Job Classes, User Requested Job Priority, and Form Support
4.16. Holding and Releasing Jobs
4.17. Load Balance Queues and Printer Pools
4.18. Routing Jobs To Print Queues
4.19. Job Options and the Z Control File Entry
4.19.1. Setting Job Options Using the Printcap
4.19.2. Converting SystemV Options to LPRng Options
4.19.3. Selecting a Single Option - Muliple Queues
4.19.4. Selecting Multiple Options - Single Queue
4.20. Interfacing to Non-LPRng Spoolers
4.21. Debugging, Tracing, and Log Files
5. LPRng Clients - lpr, lprm, lpq, lpc, lpstat
5.1. Printer and Server Information
5.2. Command Line -Pprinter@host
5.3. Command Line -Pprinter
5.4. PRINTER, LPDEST, NPRINTER, and NGPRINTER Environment Variables
5.5. Wildcard Printcap Entry
5.6. First Printcap Entry
5.7. Default Printer and Server Host
5.8. Force Connection to Localhost
5.9. User Identification
6. lpr - Job Spooler Program
6.1. Job Format Options
6.2. Job Pretty Printing, Banners, Priority, and Accounting
6.3. Job Class and Priority
6.4. Job Copies and Job Size
6.5. Job Completion Notification Requested
6.6. Remove Files After Spooling
6.7. The -Z Passthrough to Filter Options
6.8. Record Queue Name in Control File
6.9. Check For Nonprintable File
6.10. Job Filtering By LPR
6.11. Restrict Queue Use to Group Members
6.12. Fixing Bad Control Files and Metacharacters
6.13. Minimum Spool Queue Free Space
6.14. FQDN Host Information
7. lpq - Status Monitoring Program
7.1. lpq Queue Selection (lpq -Pprinter, lpq -a)
7.2. lpq Job Selection
7.3. lpq Short Format (lpq -s)
7.4. lpq Long Format (lpq, lpq -l, lpq -L)
7.5. lpq Verbose Format (lpq -v)
7.6. Job Taking Too Long - Stalled
7.7. Configuring Format and Displayed Information
7.7.1. Display Class Information
7.7.2. Reverse Short and Long lpq Formats
7.7.3. Status Line Length and Line Count
7.7.4. lpq Status Format Determined by Requesting Host Address
8. lprm - Job Removal Program
8.1. lprm Queue Selection (lprm -Pprinter, lprm -a)
8.2. lprm Job Selection
9. lpc - Administration Program
9.1. Informational Commands - status, flush, active, reread
9.2. Queue Management - enable, disable, up, down
9.3. Printing Management - start, stop, up, down
9.4. Problem Management - abort, redo, kill
9.5. Job Scheduling - topq, holdall, noholdall, hold, release
9.6. Queue Management - class, redirect, move
10. checkpc - Configuration Validation Utility
10.1. Maintenance
10.2. Printcap Information
11. Printer Communication and Protocols
11.1. Network Printers
11.2. RFC1179 (LPD) Connection
11.3. Socket API
11.4. AppSocket TCP/IP Protocol
11.5. Network Print Server Boxes
11.6. Network Print Server Configuration Information
11.7. HP JetDirect Interface
11.7.1. Resetting To Factory Defaults
11.7.2. Setting Up IP Networking and Address
11.7.3. BOOTP Information
11.7.4. Telnet Configuration
11.7.5. Disabling Banner Page Generation
11.8. Problems With Network Print Servers
11.8.1. Network Print Server Not Responding
11.8.2. Network Print Server Does Not Handle LPQ, LPRM
11.8.3. Incomplete Job Transfers
11.9. Printing to a SMB (MicroSoft) Printer
11.10. Printing to AppleTalk Printers
11.11. Parallel Port Printers
11.12. Serial Printers
12. Printcap Database
12.1. The Printcap Parsing Rules
12.2. Simple Client Printcap Entry
12.3. Simple Server Printcap Example
12.4. Using :oh To Select Printcap Information
12.5. Using the Wildcard Printcap Entry
12.6. Enterprise Strength Printcap Example
12.7. Remote Printer Using RFC1179
12.8. Remote Printer Using Socket API
12.9. Parallel Printer
12.10. Serial Printer
12.11. Bounce Queue
12.12. Job Format Translation
12.13. Dynamic Routing
12.14. Printer Load Balancing
12.15. Locations of Printcap Files
12.15.1. Separate Server and Client Printcap Files
12.15.2. all Printcap Entry
12.16. Single Printcap File for Large Installation
12.17. Management Strategies for Large Installations
12.18. Using Programs To Get Printcap Information
12.18.1. How to use NIS and LPRng
12.18.2. How to use NIS and LPRng - Sven Rudolph
12.19. Lexmark Printers
12.20. Tektronix Phaser Printers
12.21. Duplex Printing
12.22. Solaris, Newsprint and FrameMaker
13. Spool Queues and Files
13.1. Spool Queue
13.2. Queue Lock File
13.3. Spool Control File
13.4. Log and Status Files
13.5. Job Files
13.6. Job Hold File
13.7. Job State
13.8. Job Identifier
14. Configuration File, Defaults and Overrides
14.1. Configuration File Format
14.2. Legacy Compatibility
15. Job Processing
15.1. Configuration and Setup Options
15.2. Submitting Jobs and Service Requests
15.3. Job Reception
15.4. Spool Queue Processing
15.5. Opening the Output Device
15.6. Printing Banners
15.7. Printing Job Files
15.8. Printing Banner At End of Job
15.9. Normal Termination
15.10. Abnormal Termination
15.11. Forwarding Jobs
15.12. Debugging
16. Filters
16.1. Filter Functions
16.2. Filter Exit Codes
16.2.1. JSUCC
16.2.2. JFAIL
16.2.3. JABORT
16.2.4. JREMOVE
16.2.5. JHOLD
16.2.6. JNOSPOOL and JNOPRINT
16.2.7. JSIGNAL
16.2.8. JFAILNORETRY
16.2.9. Other Values
16.3. Print Job Formats
16.4. OF Filter
16.5. lpr -p format
16.6. lpr binary (-l) format
16.7. Chaining Filters
16.8. Filter Command Line Options and Environment Variables
16.9. LPRng Supported Filters
16.9.1. Filter Support Conventions
16.10. lpf
16.11. ifhp Filter
17. Permissions and Authentication
17.1. Permission Checking Algorithm
17.2. Rule Matching Procedures
17.2.1. DEFAULT
17.2.2. SERVICE
17.2.3. USER
17.2.4. REMOTEUSER
17.2.5. HOST
17.2.6. REMOTEHOST
17.2.7. REMOTEPORT
17.2.8. PORT
17.2.9. IP
17.2.10. REMOTEIP
17.2.11. LPC
17.2.12. SAMEUSER
17.2.13. SAMEHOST
17.2.14. SERVER
17.2.15. FORWARD
17.2.16. GROUP
17.2.17. REMOTEGROUP
17.2.18. CONTROLLINE
17.2.19. AUTH
17.2.20. AUTHTYPE
17.2.21. AUTHUSER
17.2.22. IFIP
17.3. Permission File Location
17.4. Example Permission File
17.5. Complex Permission Checking
17.6. More Examples
17.7. Authentication and Encryption
17.8. User Identification
17.9. RFC1179 Protocol Extensions
17.10. Authentication Operations
17.11. Permission Checking
17.12. PGP Authentication Support
17.12.1. Printcap Configuration
17.12.2. User Files and Environment Variables
17.13. Using Kerberos 5 for Authentication
17.13.1. Using AUTH kerberos and k5conn
17.13.2. LPRng Configuration
17.13.3. Kerberos Installation Procedure
17.13.4. LPRng Configuration
17.13.5. Printcap Entries
17.13.6. User Environment Variables and Files
17.14. Using Kerberos 4 for Authentication
17.14.1. Printcap Entries
17.15. Using SSL for Authentication
17.15.1. Certificate Management
17.15.2. Creating Root Certificate
17.15.3. Creating Client and Server Certificates
17.15.4. Creating Signing Certificates
17.15.5. Permissions and Certificate Revocation
17.16. Using MD5 for Authentication
17.16.1. Printcap Entries
17.16.2. User Environment Variables and Files
17.17. Adding Authentication Support
17.17.1. Printcap Support
17.17.2. Code Support
17.17.3. Connection and Transfer Authentication
18. Accounting
18.1. Accounting Printcap Options
18.2. Accounting Information
18.3. Accounting File
18.4. Authorization and Quotas
18.5. Accessing Printer Hardware Pagecounters
18.6. Reliable Accounting
18.7. LPRng accounting.pl Utility
19. RFC 1179 - Line Printer Daemon Protocol
19.1. Ports and Connections
19.2. Protocol Requests and Replies
19.3. Job Transfer
19.4. Data File Transfer
19.5. Control File Contents
19.6. lpq Requests
19.7. lprm Requests
19.8. LPC Requests
19.9. Block Job Transfer
19.10. Authenticated Transfer
20. The Most Frequently Asked Questions
20.1. Why do I get malformed from address errors?
20.2. It was working normally, then I get connection refused errors
20.3. Job is not in print queue, but it gets printed!
20.4. Job disappears and is never printed, but lpr works
20.5. I get messages about bad control file format
20.6. What is RFC 1179, the Line Printer Daemon Protocol?
20.7. I want to replace lp, lpstat, etc, but my programs need them
21. Remote Logger Operation
21.1. Logger Network Communication
21.2. Logger Messages
21.3. Message Format
21.4. Dump Messages
21.5. LPD Messages
21.6. Job Status Messages - UPDATE
21.7. Printer Status Messages - PRSTATUS
A. Index To All The Configuration and Printcap Options
B. License
C. Testing and Diagnostic Facilities
C.1. Compiling the Test Version
C.2. Setting Up The Test Version Spool Queues
C.3. Running the Test Version Software
Index
List of Tables
4-1. Filter Options
4-2. Job Formats and Filter Selection
4-3. :ifhp= Options
11-1. Network Print Server Configuration Information
13-1. Control File Lines
16-1. Print Filter Command Line Options
16-2. Filter Command Line Options and Values
16-3. Filter Command Line Option Format
16-4. Filter Environment Variables
17-1. Permission Keywords and Purpose
19-1. RFC1179 Commands
19-2. Control File Lines and Purpose
19-3. LPC Commands
A-1. LPRng Options

Preface

Introduction

The LPRng Print Spooler provides the essential printing services for UNIX and UNIX-like operating systems. It can be configured to work in small, large, or enterprise level environments. The ifhp Print Filter is used with LPRng to convert print jobs into a format compatible with a particular printer and the while it may briefly describe the ifhp operation, Finally, the LPRngTool Graphical User Interface provides an easy to use configuration and monitoring tool for the LPRng print spooler.

This document is the basic reference for the LPRng print spooler software; the ifhp documentation and LPRngTool should be consulted for details about their operation.


Acknowledgements

I would like to thank all of the LPRng users who so relentlessly tried the incredible number of permutations and combinations of printers and software, and whose requests for just one more feature led to the development of the software.


Shell Prompts

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

User Prompt
Normal user %
root #

Typographic Conventions

The following table describes the typographic conventions used in this book.

Meaning Examples
The name of commands, files, and directories. On screen computer output.

Edit your .login file.

Use ls -a to list all files.

You have mail.
What you type, when contrasted with on-screen computer output.
% su
Password:
Manual page references. Use su(1) to change user names.
User and group names Only root can do this.
Emphasis You must do this.
Command line variables; replace with the real name or variable. To delete a file, type rm filename
Environment variables $HOME is your home directory.

Notes, warnings, and examples

Within the text appear notes, warnings, and examples.

Note: Notes are represented like this, and contain information that you should take note of, as it may affect what you do.

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

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


Chapter 1. Introduction

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


1.1. What is LPRng?

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

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

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

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

In 1992 while at San Diego State University Prof. Powell redesigned and reimplemented the PLP code and named the result LPRng. The goals of the LPRng project were to build a server system that was as close to user abuse proof as possible, that would provide services limited only by the inherent capacities of the support system, RFC1179 compliant, and with extensive debugging capabilities to allow quick and easy diagnostics of problems.

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

As a side effect of this work, many security problems that could develop were identified and steps taken to ensure that they were not present in LPRng. For example, LPRng clients such as lpr, lprm, lpc, and lpq can run as ordinary users programs, the lpd server can run as a non-root user once a network port has been opened, and all text formatting operations done by LPRng use a very restricted and highly secure version of the snprintf function.


1.2. Additional Resources

The main LPRng documentation is the LPRng Reference Manual, which is available in several formats. Information about LPRng and the latest release can be found on the LPRng web page http://www.lprng.com/LPRng.html

The ifhp documentation is the IFHP-HOWTO, which is available in the ifhp distribution. Information about ifhp and the latest release can be found on the LPRng web page http://www.lprng.com/LPRng.html

There is also a mailing list at lprng@lprng.com. To post to the list you must subscribe by sending send an email to lprng-request@lprng.com, with the message subject or body containing the word `subscribe' or `help'.

Several presentations of LPRng and print spooling software have been made at the Large Installation System Administrator (LISA) conferences. The presentation at the LISA 98 conference is in the PowerPoint file LISA98.ppt in the LPRng distribution documentation.


1.3. Frequently Asked Questions

There are a list of Frequently Asked Questions that appear regularly on the LPRng mailing list. See The Most Frequently Asked Questions.


1.4. License, Copyright, and Disclaimer

The LPRng Print Spooler and the ifhp Print Filter software are distributed under the GNU Public License (GPL) and the Artistic License. Users can choose to redistribute or use the software under a license that is appropriate for their purpose. Other licenses and distribution agreements are available by contacting AStArt Technologies for information.

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


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.8. Mailing List

To join the LPRng mailing list, please send mail to lprng-request@lprng.com 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@lprng.com>

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

1.10. References and Standards

The following references and standards have been used in the development of the LPRng software.


1.10.1. RFCs

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

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

The [RFC1179 - Line Printer Daemon Protocol] describes the protocol used to transfer jobs from client program to print server. See RFC1179 for more a discussion of this protocol and more details about the RFC. The rfc1179.txt file is included in the LPRng distribution documentation.


1.10.2. PostScript

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

The [PostScript Language Reference Manual] contains a great deal of technical information about the PostScript Language, and is the language reference manual.

The [PostScript Language Tutorial and Cookbook] is a very nice and easy to read introduction to PostScript programming, and has some very useful utilities. Combined with GhostScript and the gv display program you can very easily learn to write your own small PostScript programs, and more importantly, can learn to understand the contents of PostScript files.

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


1.10.3. HP PCL 5

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


1.10.4. HP PJL

The Hewlett-Packard (HP) Printer Job Language is used to control various features of HP printers. The [Printer Job Language Reference Manual] is also available from Hewlett-Packard (http://www.hp.com) through their [Developer Program].


1.10.5. PDF

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


Chapter 2. Installation

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


2.1. Getting Source Code and Support Programs

  1. Obtain the latest or stable version of the LPRng source code from a LPRng FTP Site.

  2. Obtain the latest or stable version of the ifhp filter source code from a LPRng FTP Site. This filter is used to support PostScript, PCL, and text printers.

  3. Obtain the following GNU programs from one of the many GNU Software Mirror Sites and install them. See the directions in the GNU Zip distribution for details.

    GNU gzip Compression Utility

    Used to generate the compressed LPRng distribution.

    GNU tar Archive Utility

    GNU tar supports gzip compression and decompression and is used to generate the LPRng distribution.

    GNU make

    LPRng requires GNU make for configuration and installation.

    GNU gcc Compiler or ANSI C Compiler

    LPRng requires and ANSI C compiler. If you do not have an ANSI C compiler then please use the GNU gcc compiler.

  4. Solaris Sparc and X86 Binaries for GCC and Make can be obtained from http://sunfreeware.com/.

  5. While the following are not essential to LPRng they are used by the ifhp filter.

    file - File Identification Utility

    The Open Source file utility by Ian F. Darwin can be obtained from ftp://ftp.astron.com/pub/file/. or ftp://ftp.lprng.com/pub/LPRng/UNIXTOOLS/file/. This is a greatly improved version of the original UNIX file utility and may be used by the ifhp filter to do file recognition.

    gs - GhostScript

    GhostScript can be obtained from http://www.cs.wisc.edu/~ghost/ or http://www.ghostscript.com. GhostScript is a PostScript interpreter that allows you to translate PostScript to various printer compatible formats such as PCL, as well as displaying the code on a terminal. You might also want to get the PDF extensions that allows GhostScript to read and print PDF files.

    gv - GhostView

    Of course you will want to get the gv program that uses GhostScript to display PostScript on an X terminal. It can be obtained from http://wwwthep.physik.uni-mainz.de/~plass/gv/


2.2. PATH Environment Variable and Utilities

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

PATH=/usr/local/bin:/bin:/sbin:/usr/bin

If you are compiling the distribution on a Solaris system you will need to use the utilities in the /usr/ccs/bin directory. This directory must be in the PATH after the directory where the GNU utilities have been installed. For example:

PATH=/usr/local/bin:/bin:/sbin:/usr/bin:/usr/ccs/bin

Check to see that the GNU make utility and not the default OS make is being used by default. Use make -v to determine what version you are using:

h4: {1} % make -v
GNU Make version 3.78, by Richard Stallman and Roland McGrath.
Copyright (C) 1988-1999


2.3. Network Mounted File System and Spool Directories

WarningIt is strongly recommended that the print spool directories and essential printer configuration file files should NOT be in an NFS mounted file system or on a file system which is NFS exported.

The LPRng lpd print server makes heavy use of file locking to coordinate and synchronize process activities. Given the historical and ongoing problems of file locking on and NFS mounted file system, not to mention the high overhead for doing file locking in a distributed environment, it is strongly recommended that spool directories NOT be on an NFS mounted partition or on a partition that is NFS exported.

This warning extends to other network file systems as well.

In addition, the configuration file such as /etc/printcap must be available or printing will not function. Care should be taken to ensure that these files are stable and available at all times.


2.4. Daemon User and Daemon Group

The lpd server is started at system initialization time and initially runs as ROOT (Effective UID 0). It performs all file and other operations with non-privileged user daemon Effective UID and group daemon Effective GID, and does a setuid() to these UID and GID values when running programs. The client programs such as lpr operate with the effective user IDs of the user which started them.

Most UNIX systems already have user daemon and group daemon, or a similar ones. If suitable user and group IDs are not present then the appropriate system administration tools should be used to create them. The configuration --with-userid=UID and --with-groupid=GID can be used to specify the user and group IDs. The user ID must not have login capability.


2.5. Configuration

The LPRng package consists of the following executables and configuration files:

  • lpd - the lpd print server program

  • lpr, lpq, lprm, lpc, and lpstat client programs for printing, status queries, job removal, server configuration, and System V lpstat emulation respectively.

  • printcap print queue database file which is used by all the server and client programs

  • lpd.conf LPRng configuration options which is used by all the server and client programs

  • lpd.perms permission information which is used by the lpd server to control user actions.

LPRng uses the configure script generated by the GNU autoconf utility to generate a set of Makefiles. These are used by GNU make to compile and install the LPRng software. The following Makefile variables and values are set by configure to specify the location of the LPRng software:

Configure Variable Default Value Expanded Default Value Override
${prefix} /usr/local --prefix=PATH  
${exec_prefix} ${prefix} /usr/local --execprefix= PATH
${bindir} ${exec_prefix}/bin /usr/local/bin --bindir= PATH
${sbindir} ${exec_prefix}/sbin /usr/local/sbin --sbindir= PATH
${libexecdir} ${exec_prefix} /libexec /usr/local/libexec --libexecdir= PATH
${sysconfdir} ${prefix}/etc /usr/local/etc --sysconfdir= PATH
${mandir} ${prefix}/man /usr/local/man --mandir= PATH

These are used to install the following files:

Configure Variable Files
${bindir} lpr, lprm, lpq, lpstat
${sbindir} lpc, checkpc, lpd
${libexecdir}/filters lpf, pclbanner, psbanner, lpbanner
${sysconfdir} lpd.conf, lpd.perms, printcap
${mandir}/man[1-9] man pages

You can set explicit values for the paths by using the override --name=PATH. For example:

./configure --prefix=/usr --sysconfdir=/etc \
    --mandir=/usr/share/man
Variable Value Files
${bindir} /usr/bin /usr/bin/{lpr,lprm,lpq,lpstat}
${sbindir} /usr/sbin /usr/sbin/{lpc,checkpc,lpd}
${libexecdir}/filters /usr/libexec/filters /usr/libexec/filters{lpf, pclbanner, psbanner, lpbanner}
${sysconfdir} /etc /etc/{lpd.conf,lpd.perms,printcap}
${mandir}/man[1-9] /usr/share/man /usr/share/man/man[1-9]/{man pages}

In addition to these standard configure options the following options provided.

--disable-setuid

Install the executables without setuid ROOT permissions. Non-setuid clients and programs are inherently more secure than SETUID programs, and system administrators would be well advised to install them without SETUID root permissions. Please see Security Considerations for more details about this option.

--enable-priv_ports

Require connections to the lpd server to come from a privileged port (range 1-1023). By default LPRng will allow connections from any port. Please see Security Considerations for more details about this option.

--disable-force_localhost

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

--disable-require_configfiles

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

--enable-kerberos

Include support for Kerberos 5 authenticated transfers.

--enable-mit_kerberos4

Include support for MIT Kerberos 4 authenticated transfers.

--disable-kerberos_checks

Disable checks for kerberos support libraries, etc.

--with-lpddir=DIR

lpd executable directory (default ${sbindir}). For historical configuration compatibility.

--with-filterdir=DIR

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

--with-lpd_conf_path=PATH

Path of lpd.conf file. For historical configuration compatibility.

--with-lpd_perms_path=PATH

Path of lpd.perms file. For historical configuration compatibility.

--with-printcap_path=PATH

Path of printcap file. For historical configuration compatibility.

--with-ld_library_path=PATHLIST

Set the LD_LIBRARY_PATH environment variable of filters to this value.

--with-filter_path=PATHLIST

Set the PATH environment variable of filters to this value.

--with-userid=NAME

Run LPRng as this user, default daemon

--with-groupid=NAME

Run LPRng as this group, default daemon

--with-lockfile=PATH

The lockfile path. This will be expanded to PATH.server or PATH.port allowing multiple LPRng servers to run on a single host.

--with-filterdir=PATH

Location of the filters installed by LPRng.

--with-done_jobs=N

retain status of last N done jobs.

--with-done_jobs_max_age=N

remove status of done jobs older than N seconds.

--with-chooser_routine=NAME

name of chooser routine provided by user

--with-order_routine=NAME

name of order routine provided by user

--with-user_objs=NAME

object file with routines provided by user

--with-user_include=NAME

include file with templates for routines provided by user

--disable-strip

Do not strip the executables before installing. For debugging and diagnostic purposes.

--with-unixsocketpath=PATHNAME

the pathname of the UNIX socket (off or blank to disable)

--disable-ssl

disable ssl support

--with-openssl=DIR

root location for OpenSSL

--with-openssl-inc

OpenSSL include files

--with-openssl-lib

OpenSSL library files

--with-ssl_ca_file=FILE

ssl Certificate Authority CERT file (default ${sysconfdir}/lpd/ssl.ca/ca.crt)

--with-ssl_ca_key=KEY

ssl Certificate Authority private key file (default ${sysconfdir}/lpd/ssl.ca/ca.key)

--with-ssl_certs_dir=DIR

ssl Certificate Authority certs working directory (default ${sysconfdir}/lpd/ssl.certs/)

--with-ssl_server_cert=FILE

ssl server certificate file (default ${sysconfdir}/lpd/ssl.server/server.crt)

--with-ssl_server_password_file=FILE

ssl server private key in password file (default ${sysconfdir}/lpd/ssl.server/server.pwd)

It is recommended that you use one of the following configurations:

  1. If you already have a print spooling system installed and want to install LPRng for testing purposes or as an alternative to the existing system and keep your existing print spooling system, use:

    ./configure
    
    use defaults for file locations and permissions:
      /usr/local/{bin,sbin,libexec/filters,man}
    requires lpd to run on the local host
    executables installed setuid ROOT
    
  2. If you have manual pages in /usr/share/man, your existing print spooling system has executables in /usr/bin and /usr/sbin, and you want to replace your existing print spooling system, use:

    ./configure --prefix=/usr --sysconfdir=/etc \
      --mandir=/usr/share/man
    
    executables and files in
       /usr/{bin,sbin,libexec/filters}
       /usr/share/man/man[0-9]
    requires lpd to run on the local host
    everything installed setuid ROOT
    
  3. If you have manual pages in /usr/share/man and allow jobs (by default) to be sent directly to the server host (lightweight operation), use:

    ./configure --prefix=/usr --sysconfdir=/etc \
       --mandir=/usr/share/man --disable-force_localhost
    
    executables and files in
       /usr/{bin,sbin,libexec/filters}
       /usr/share/man/man[0-9]
    does not require lpd to run on the local host
    everything installed setuid ROOT
    

2.6. System and User Printcap, lpd.conf, and lpd.perms files

The system printcap file contains the definitions of the print queues used by LPRng, and is located in the directory specified by the configuration ${sysconfdir} value. For a complete description of the printcap file see Printcap Database. If your system does not have an existing printcap file then a dummy file similar to the following is installed by default:

# dummy printcap file
lp:cm=Dummy Printcap Entry:
 :lp=/dev/null
 :sd=/var/spool/lpd/%P

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

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

The lpd.perms is located in the ${sysconfdir} directory and is only by lpd to determine user permissions for printing activities. For a complete description of the lpd.perms file see Permissions and Authentication for details. During installation the lpd.perms.template file is installed in the ${sysconfdir}/lpd.perms.template and if there is not an existing lpd.perms file is copied to it.

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


2.7. Checking System Installation with checkpc

The checkpc program is used to make sure that the spool directories and files used by LPRng have the correct permissions and are in place. By default, checkpc will check permissions and report if there are any problems. You should run this as root. For example:

h4: {2} # checkpc
Warning - No configuration file '/etc/lpd.conf'
Warning - No lpd only printcap file found in '/etc/lpd_printcap'
Warning -  ** cannot open '/var/run/lpd.printer' - 'Permission denied'
Warning -  bad directory - /var/spool/lpd/lp
Warning -   Printer 'lp' spool dir '/var/spool/lpd/lp' needs fixing

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

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

The permission denied message for /var/run/lpd.printer is serious, as the lpd server uses this as a lock file.

The bad directory message about the spool directory is usually caused by wrong permissions or a missing directory.

The checkpc -f (fix) option causes checkpc to take action to rectify errors. The checkpc -f -V (verbose) option causes the fixup activity to be displayed a well:

h4: {3} # checkpc -f -V
Checking for configuration files '/etc/lpd.conf'
  found '/usr/local/etc/lpd.conf', mod 0100644
Checking for printcap files '/etc/printcap'
  found '/usr/local/etc/printcap', mod 0100644
Checking for lpd only printcap files '/etc/lpd_printcap'
 DaemonUID 1, DaemonGID 1
Using Config file '/etc/lpd.conf'
LPD lockfile '/var/run/lpd.printer'
 Checking directory: '/var/run'
   directory '/var'
   directory '/var/run'
  checking '/var/run/lpd.printer' file

Checking printer 'lp'
 Checking directory: '/var/spool/lp'
   directory '/var'
   directory '/var/spool'
   directory '/var/spool/lp'
  file 'control.lp', zero length file unchanged in 1 hours
  file 'status.lp', zero length file unchanged in 1 hours
  file 'status', zero length file unchanged in 1 hours
  file 'log', zero length file unchanged in 1 hours
  file 'acct', zero length file unchanged in 1 hours
  checking 'control.lp' file
  checking 'status.lp' file
  checking 'status' file
  cleaning 'status' file, 0K bytes: no truncation
  checking 'log' file
  cleaning 'log' file, 0K bytes: no truncation
  checking 'acct' file
  cleaning 'acct' file, 0K bytes: no truncation

checkpc will create the spool directories and any missing files, and fix the permissions of existing files.


2.8. Compilation and Install

Once you have decided on the configuration you want and understand what files need to be installed, then you are ready to do the install. It is extremely simple to extract the files, configure, compile and install the software:

h4: {4} % gunzip -c LPRng-<version>.tgz | tar xvf -
h4: {5} % cd LPRng-<version>
h4: {6} % ./configure  [ ... configuration options ]
h4: {7} % make clean all
h4: {8} % su   # you must do the following commands as root
h4: {9} # make install
h4: {10} # # if checkpc did not run, do the next command
h4: {11} # # make sure checkpc and lpq are in your paths
h4: {12} # # if using CSH, use 'rehash' as well
h4: {13} # checkpc -f
h4: {14} # # check to see if the server is running
h4: {15} # lpq

During installation you may get an error message indicating that the lpd file could not be installed or the lpd server could not be started. If this is the case, then carry out the appropriate steps in Updating Print Spooler Software and Startup Scripts to remove the existing print spooling software and then try to reinstall LPRng. You should also to check the System Specific Notes section for any system specific installation requirements.

While the LPRng installation scripts try hard to set up the lpd server in place, you will still need to reboot your host and make sure that the various printing facilities are working correctly after reboot.


2.9. Installation Problems

Read the notes for your OS in section System-dependent notes for specific installation help (if any).

The following is a list of commonly encountered problems and their solution. If these do not solve your problem, then send mail to the lprng@lprng.com mailing list. You will have to subscribe to the list in order to post to the list.

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

  2. The C Compiler complains about missing files or has a large number of errors. Use gcc instead of your vendor's C compiler.

    configure --with-cc=gcc
    

    If there are messages about missing system files, then you most likely have an incomplete set of system include files, or the include do not properly reference other required include files, or the include files are located in an unusual location. If you are using gcc then make sure that the gcc was carried out correctly on your system. The easiest way to assure this is to recompile and reinstall the gcc compiler.

  3. If you have checked your compiler installation and are still missing libraries or files then the include files may be in /usr/local/include and libraries may be in /usr/local/include and these directories may not searched or used by the compiler by default. This can be fixed by using the --with-cppopts= and --with-ldopts= configure options.

    configure \
      --with-cppopts="-I/usr/local/include -I/usr/include/kerberosIV" \
      --with-ldopts="-L/usr/local/lib -L/usr/lib/kerberosIV"
    
  4. The software compiles but will not run on the system. Make sure that you have followed your system specific rules for compiling and installing setuid ROOT programs on your system. You may need to statically link your executables.

  5. The software was compiled on one system and copied to another system, but will not run on the other system. Try compiling the software on the target system. If it compiles and runs, then you most likely have an issue with libraries or Operating System Versions.

After you have installed the LPRng software and rebooted your system, do the following commands:

h4: {16} # lpq
Printer: lp@astart
 Queue: no printable jobs in queue

If you do not get status displayed, or you get some other error message, then the following are a series of tests can use to check that LPRng is installed correctly.

First we will run lpd in the foreground and are used to make sure that our system configuration is correct. You will need root permissions to do the following steps. Stop the running currently running lpd process. Next, run lpd in foreground mode:

h4: {17} # ps -aux | grep lpd 
daemon   240  0.0  0.0  1292  0  ??  IWs  -     0:00.00 lpd: lpd Waiting
h4: {18} # kill 240
h4: {19} # checkpc -f
h4: {20} # /usr/local/bin/lpd -F -D1
Fatal error - Another print spooler is using TCP printer port

If you get the above error message, then you have either not terminated the running lpd server, there is another process using TCP/IP port 515, or you are not starting the lpd server as ROOT. See the System Specific Notes for details on how to resolve these issues.

Correct the problem and then restart the server. You should see the output indicated below:

h4: {21} # /usr/local/bin/lpd -F -D1
1999-04-05-14:35:14.023 astart27 [2667] Waiting  lpd: LOOP START
1999-04-05-14:35:14.024 astart27 [2667] Waiting  Get_max_servers: \
   getrlimit returns 256
1999-04-05-14:35:14.024 astart27 [2667] Waiting  Get_max_servers: \
   returning 128
1999-04-05-14:35:14.025 astart27 [2667] Waiting  lpd: \
   max_servers 128, active 0
1999-04-05-14:35:14.025 astart27 [2667] Waiting  lpd: \
   starting select timeout 'yes', 600 sec

Now from another window do the following commands:

h4: {22} # lpq
Printer: lp@astart
 Queue: no printable jobs in queue

At this point your LPRng software has been installed and tested. See the Updating Print Spooler Software and Startup Scripts for details on how to automatically start lpd at boot time.


2.10. Updating Print Spooler Software and Startup Scripts

If you are replacing your existing print spooling spooling system, you must shut down and remove the existing print spooler software before installing the LPRng software. This process is fairly system dependent, and requires a small amount of system expertise.

To assist in this process the LPRng installation has a set of preinstall, postinstall, preremove, and postremove scripts in the distribution that may be suitable for your local system use. If these fail to work during the system installation, you will need to carry out the steps described here by hand.


2.10.1. SunOS, Linux, and BSD Derived Systems

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

The lpd print spooler is started by either the rc startup script or by a startup script file in the /etc/rc.d/init.d or /etc/init.d directory. You can first locate the startup commands as follows.

  1. Use the find(1) utility to search the /etc directory for the file that contains the startup command.

    h4: {23} # cd /etc
    h4: {24} # find . -type f -exec grep -l lpd {} \; -print
    ./rc.local
    
  2. Examine each of the files found find the one that starts the lpd print spooler. You can simply comment out the command or change it to start the LPRng lpd print server.

    h4: {25} # more /etc/rc.local
    if [ -f /etc/printcap  -a -f /usr/libexec/lpd ] ; then
      /usr/libexec/lpd ;
    fi
    
    --- change this to
    if [ -f /etc/printcap  -a -f /usr/sbin/lpd ] ; then
      /usr/sbin/lpd ;
    fi
    
  3. If you have an existing printcap file, then you should either copy this to the location used by LPRng or make a symbolic link to it.

Next we kill the currently running lpd process.

h4: {26} # ps -auxw |grep lpd
papowell 23932  0.0  0.3  224  184  p3  S+  10:40AM  0:00.01 grep lpd
daemon  17763  0.0  0.2  448  120  ??  IWs  29Mar99  0:01.35 (lpd)
h4: {27} % kill 135
h4: {28} % kill 135
135: No such process

Next, you should remove or rename the existing print system executables. The following example shows how to use the find utility to track down candidates.

h4: {29} # find /usr -type f -name lp\*  -print >/tmp/candidates
h4: {30} # find /sbin -type f -name lp\*  -print >>/tmp/candidates
h4: {31} # cat /tmp/candidates
/usr/bin/lpunlock
/usr/bin/lpqall.faces
/usr/bin/lpq             <---- old
/usr/bin/lpr             <---- old
/usr/bin/lprm            <---- old
/usr/bin/lptest
/usr/doc/samba-1.9.18p10/examples/printer-accounting/lp-acct
/usr/man/man1/lpq.1
/usr/man/man1/lpr.1
/usr/man/man1/lprm.1
/usr/man/man1/lptest.1
/usr/man/man4/lp.4
/usr/man/man8/lpc.8
/usr/man/man8/lpd.8
/usr/sbin/lpc            <--- old
/usr/sbin/lpd            <--- old
/usr/sbin/lpf            <--- old
h4: {32} # mv /usr/bin/lpq  /usr/bin/lpq.old
h4: {33} # mv /usr/bin/lpr  /usr/bin/lpr.old
h4: {34} # mv /usr/bin/lprm /usr/bin/lprm.old
h4: {35} # mv /usr/sbin/lpc /usr/sbin/lpc.old
h4: {36} # mv /usr/sbin/lpd /usr/sbin/lpd.old
h4: {37} # mv /usr/sbin/lpf /usr/sbin/lpf.old

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

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

Finally, you should reboot your machine and make sure that the lpd print server starts correctly.


2.10.2. Solaris, HP-UX, and other SysVR4 Derived Systems

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

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

The preinstall.solaris script is a file in the LPRng distribution that contains most of the commands needed to remove the Solaris System V printing software. These are explained in detail in the sections below. The procedures outlined below can be used on other SystemVR4 systems.

#!/bin/sh
# This is an effort to automate the setup
#  needed to install the LPRng software on the
#  Solaris OS.  This is effectively a one way path.
#  You are warned.
PATH=/etc:/usr/etc:/usr/bin:/bin:/sbin:/usr/sbin:$PATH
# remove the init.d entry and links
for i in /etc/rc*.d/*lp ; do
    b=`basename $i`;
    d=`dirname $i`;
    mv $i $d/UNUSED.$b.UNUSED
done
# rename files
renameit () {
    for i in $* ; do
        if [ -f $i -a '!' -f $i.old ] ; then
            echo "renaming $i $i.old";
            mv $i $i.old
        fi
    done
}
renameit /usr/bin/lp /usr/bin/lpstat /usr/sbin/lpadmin \
  /usr/sbin/lpfilter /usr/sbin/lpforms /usr/sbin/lpmove \
  /usr/sbin/lpshut /usr/sbin/lpsystem /usr/sbin/lpusers \
  /usr/ucb/lpc /usr/ucb/lpq /usr/ucb/lpr /usr/ucb/lprm \
  /usr/ucb/lptest /usr/lib/lp/lpsched /usr/lib/lp/lpNet
# remove the cron entry
if [ -f /var/spool/cron/crontabs/lp ] ; then
    mv /var/spool/cron/crontabs/lp \
       /var/spool/cron/UNUSED.crontabs.lp
fi
# comment out inetd.conf entry
if egrep '^printer' /etc/inetd.conf >/dev/null 2>& ; then
    mv /etc/inetd.conf /etc/inetd.conf.bak
    sed -e 's/^printer/# printer/' </etc/inetd.conf.bak \
       >/etc/inetd.conf
fi
# remove the nlsadmin entry
nlsadmin -r lpd tcp
nlsadmin -r lp tcp
echo REBOOT SYSTEM and then install LPRng

First, you will need to remove the /etc/rc startup files in the /etc/rc*.d directories that start the lpsched process; see the init program man page for details. You can find these files by using:

h4: {43} # cd /
h4: {44} # find . -type f -exec grep -l lpsched {} \; -print >/tmp/files
h4: {45} # cat /tmp/files
/etc/rc0.d/K20lp
/etc/rc2.d/K20lp
/etc/rc2.d/S80lp
/etc/init.d/lp
h4: {46} # ls -l ` cat /tmp/files `
lrwxrwxr-x 1 root bin 1 Dec 29 23:39 /etc/rc0.d/K20lp -> ../../init.d/lp
lrwxrwxr-x 1 root bin 1 Dec 29 23:39 /etc/rc2.d/K20lp -> ../../init.d/lp
lrwxrwxr-x 1 root bin 1 Dec 29 23:39 /etc/rc2.d/S80lp -> ../../init.d/lp
-rwxr--r-- 5 root sys 460 Sep 1 1998 /etc/rcS.d/K39lp

You can remove these files, or simply comment out all of the executable commands in the /etc/init.d/lp file. Next, find all of the printing related commands and rename them. For example:

h4: {47} # find /usr -type f -name lp\* -print >/etc/printingfiles
h4: {48} # cat /tmp/printingfiles
/usr/bin/lp
/usr/bin/lpstat
/usr/lib/lp/bin/lp.cat
/usr/lib/lp/bin/lp.set
/usr/lib/lp/bin/lp.tell
/usr/lib/lp/lpNet
/usr/lib/lp/lpsched
/usr/lib/lp/lpdata
/usr/sbin/lpadmin
/usr/sbin/lpfilter
/usr/sbin/lpforms
/usr/sbin/lpmove
/usr/sbin/lpshut
/usr/sbin/lpsystem
/usr/sbin/lpusers
/usr/ucb/lpc
/usr/ucb/lpq
/usr/ucb/lpr
/usr/ucb/lprm
/usr/ucb/lptest
h4: {49} # vi /tmp/printingfiles  # remove ones you want to save
h4: {50} # for i in ` cat /tmp/printingfiles ` ; do
>   if [ -f $i -a '!' -f $i.old ] ; then  mv $i $i.old ; fi;
> done

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

Check the /etc/inetd.conf file for a line like the one below and comment it out. This line is not present on all systems.

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

Use nlsadmin to force the TCP/IP listener to release the port, as illustrated below. This may not be present on all system.

h4: {51} # nlsadmin -v tcp
lpd  \x00020203000000000000000000000000  ENABLED  \
  NORPC  root  NOMODULES  /var/spool/lp/fifos/listenBSD  #
0  \x00020ACE000000000000000000000000  ENABLED    \
  NORPC  root  NOMODULES  /usr/lib/saf/nlps_server  #
lp  NOADDR  ENABLED  NORPC  root  NOMODULES \
  /var/spool/lp/fifos/listenS5  #
h4: {52} # nlsadmin -r lpd tcp
h4: {53} # nlsadmin -r lp tcp

Run pmadm -l as shown below.

h2.private: {2} # pmadm -l
PMTAG    PMTYPE   SVCTAG   FLGS ID    <PMSPECIFIC>
zsmon    ttymon   ttya     u    root  /dev/term/a I - /usr/bin/login ...
zsmon    ttymon   ttyb     u    root  /dev/term/b I - /usr/bin/login ...
If you see zsmon entries for SystemV lpsched support, then use pmadm -r to remove them. These may not be present on all system. See the pmadm man page for details on the -r literal.

You must now reboot the host.

h4: {54} # shutdown -y "Whooga! Whooga! Dive! Dive! System going down."

When the system reboots, make sure that there is no process listening on port 515 (printer port) by using:

h4: {55} # telnet localhost 515

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

Compile and/or install the LPRng software. Make sure that the LPRng startup files have been installed correctly in /etc/init.d/lprng and that the symbolic links to the file have been made correctly. The LPRng startup file will usually have the following contents and you should use the same filename formats that the lp startup files had for the links to the /etc/init.d/lprng startup file:

LPD_PATH=/usr/sbin/lpd
SHOWALL=-e
case "$1" in
  start)
        # Start daemons.
        /bin/echo "Starting lpd: \c"
        ${LPD_PATH}
        /bin/echo ""
        ;;
  stop)
        # Stop daemons.
        /bin/echo "Shutting down lpd: \c"
        kill -INT `ps ${SHOWALL} \
           | awk '/lpd/{ print $1;}'` >/dev/null 2>&1
        /bin/echo " server stopped";
        ;;
  *)
        echo "Usage: $0 {start|stop}"
        exit 1
        ;;
esac
Start the lpd server and then test it:
h4: {56} # checkpc -f
h4: {57} # /usr/sbin/lpd (or /usr/local/sbin/lpd)
h4: {58} # lpq
Printer: lp
 Queue: no printable jobs in queue

2.11. Emulation for UNIX SystemV lp and lpstat

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

The LPRng lpstat emulator accepts the lpstat command line options returns status in a format that is close to the one that common lpstat implementations return. Unfortunately, due to the wide variety of different modifications and vendor versions of lpstat there are slight differences between the this status and the status returned by the original lpstat. If this is the case, then there is little to do but to modify the source code for lpstat and compile a version that implements the required format.

If the lpr program is invoked with the name lp, it will simulate the lp options. This can be done by making a symbolic link to the lpr program or by making a copy of the lpr program with the name lp.

h4: {59} # cd /usr/bin
h4: {60} # ln -s lpr lp
h4: {61} # lp /tmp/hi
request id is root@h4+489

Finally, if the lprm program is invoked with the name cancel it will simulate the cancel command. This can be done by making a symbolic link to the lprm program or by making a copy of the lprm program with the name cancel.

h4: {62} # cd /usr/bin
h4: {63} # ln -s lprm cancel
h4: {64} # cancel 489
cancel 513
Printer lp@h9:
  checking perms 'root@h9+513'
  dequeued 'root@h9+513'

Many vintage or legacy applications have fully qualified paths to the lp and lpstat executables, and it may be necessary to make additional symbolic links or copies of the LPRng executables to satisfy their pathname requirements.

h4: {65} # ln -s /usr/bin/lpr /usr/ucb/lpr

2.12. SAMBA and LPRng

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

See the SAMBA doc/text/Printing.txt and related documentation for details on printing. In the samba.conf file [global] section or in the SWAT page for printing configuration you need to specify the that you want to have Samba handle printing, the print, lpq, and lprm commands to be used when a user prints a job, asks for status, or removes a job, and a temporary directory to hold print jobs when they are submitted. The following is a simple example of to set up printing for authenticated users.

[printers]
    path = /var/spool/lpd/samba
    #  ---  do not use the Samba default path = /tmp
    print ok = yes
    printing = lprng
    load printers = yes
    guest ok = no
    printcap name = /etc/printcap
    print command =      /usr/bin/lpr  -P%p -r %s
    lpq command   =      /usr/bin/lpq  -P%p
    lprm command  =      /usr/bin/lprm -P%p %j
    lppause command =    /usr/sbin/lpc hold %p %j
    lpresume command =   /usr/sbin/lpc release %p %j
    queuepause command = /usr/sbin/lpc  stop %p
    queueresume command = /usr/sbin/lpc start %p
  1. Samba will make a copy of the files to be printed in the directory specified by path. If the print operation fails then sometimes the print file is left in the directory.

  2. The directory should have the same ownership and permissions as /tmp, i.e.- owner and group root and bin, with 01777 permissions, where 01000 is the sticky bit.

    A directory whose `sticky bit' is set becomes an append-only directory, or, more accurately, a directory in which the deletion of files is re- stricted. A file in a sticky directory may only be removed or renamed by a user if the user has write permission for the directory and the user is the owner of the file, the owner of the directory, or the super-user. This feature is usefully applied to directories such as /tmp which must be publicly writable but should deny users the license to arbitrarily delete or rename each others' files.

  3. The directory should be examined periodically and files older then a day should be removed. The following command can be used to do this, and should be put in a file that is periodically (one a day) executed by the cron facility:

    find /var/spool/lpd/samba -type f -mtime 2d -exec rm -f {} \;
    
  4. You must specify the print method as printing = lprng. This will allow Samba to parse the LPRng lpq status format correctly.

  5. You must put all of the printers which Samba has access to in the printcap file. Your Samba server may support reading the printcap file by using a program. In this case the printcap file entry can be one of the following:

    [printers]
      #
        printcap name = |/usr/local/libexec/filters/getpc
      # or
        printcap name = |/usr/bin/lpc client all 
    
    #!/bin/sh
    # getpc program
    /usr/bin/lpq -as | /bin/sed -e 's/[@:].*//p'
    

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

  6. Samba can be configured to allow guests or non-authenticated users to spool print jobs. Unfortunately, by default lpr will mark the jobs as submitted by the Samba server, not the remote users. To solve this problem, the lpr -U%U@%M option causes lpr to mark the jobs as submitted by user %U on host %M, instead of the Samba server process. The use of this option is restricted to root and a set of userids listed in the allow_user_setting configuration option. If the userid of the submitter is not in this list, then the option is quietly ignored. The -U%U@M can also be used with the other LPRng commands as well. For example:

    [printers]
        guest ok = yes
        print command =       /usr/bin/lpr  -U%U@%M -P%p -r %s
        lpq command   =       /usr/bin/lpq  -U%U@%M -P%p
        lprm command  =       /usr/bin/lprm -U%U@%M -P%p %j
        lppause command =     /usr/sbin/lpc -U%U@%M hold %p %j
        lpresume command =    /usr/sbin/lpc -U%U@%M release %p %j
        queuepause command =  /usr/sbin/lpc -U%U@%M stop %p
        queueresume command = /usr/sbin/lpc -U%U@%M start %p
    

When Samba gets a request for print queue status, it runs the lpq command program and then parses the output of this command. Unfortunately, different versions of Samba have different ways of parsing the output - some are more flexible than others.

>

One of the problems that might occur is when the LPRng done_jobs feature is enabled. This causes that status of the last few jobs to be retained so that users can see what happened to their jobs. For example:

h110: {588} % lpq
Printer: t1@h110 'Test Printer 1'
 Queue: no printable jobs in queue
 Server: no server active
 Status: job 'papowell@h110+336' saved at 14:42:54.607
 Filter_status: FILTER DONE
 Rank   Owner/ID         Class Job Files    Size Time
done   papowell@h110+336   A   336 /tmp/hi     3 14:42:53

In this example, the done job will have its status displayed by the lpq command. However, this may confuse Samba, and it may report odd or unusual status for your jobs. If the lpq command reports that your job has completed but Samba reports that it is printing or is stopped, then you should disable the done_jobs option in the printcap entry:

lp:
  :done_jobs=0
  :...

2.13. Security Concerns

While the LPRng software has been written with security as the primary goal there is always the problem with undetected errors in the LPRng software that when exploited could compromise system security. The most serious concern is that of gaining ROOT (UID 0) permissions.

The simplest way to handle this problem is to not install LPRng with setuid ROOT permissions. Client programs will be able to connect to the lpd server. Since the lpd server is started by the system startup script with effective UID root, it is the only program in this suite that will have an privileged user id.

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

If inter-operability with non-LPRng print spoolers is not desired, then it is trivial to configure LPRng to use a non-privileged port by using the lpd.conf file. For example, in the /etc/lpd.conf file, you only need to change the indicated lines:

# Purpose: lpd port
#   default lpd_port=printer
lpd_port=2000
# or lpd_port=localhost%2000
The lpd_port specifies the (optional) IP address and port to which the lpd server binds and to which the clients will connect. LPRng applications will connect to port 2000 to transfer jobs and ask for status. You can also use this facility to establish a private set of print spoolers which can be used for testing See Testing and Diagnostic Facilities for more details.

Some legacy print filters are not meta-char-escape proof. For example, suppose that a user decided to spool a job as follows:

h4: {66} # lpr "-J`;rm -rf /;`" /tmp/a
This would create a job file with the line:
J`rm -rf /;`
and gets passed to a print filter as
/usr/local/printfilter  -J`rm -rf /;`
The observant reader will observe that the above line may have the most hideous consequences if it is processed by a shell. For this reason the LPRng software takes extreme precautions and sanitizes control file contents and file names so that they do not contain any control or metacharacters.

Finally, you can use a Unix socket (i.e. - FIFO) for connections to the server on the localhost, and disable the lpd listening socket by setting the lpd_listen_port value to off.


Chapter 3. System Specific Notes

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


3.1. Solaris

The Sun Microsystems Solaris printing system is derived from the System V UNIX system. Please see the Solaris, HP, and SysVR4 Derived Systems installation information for a detailed description of how to install LPRng and remove the Solaris Print Services.

If you want to simply forward jobs from a Solaris system to a BSD print spooling system you can use the following commands to create a printer. Check your specific system references and man pages for options:

h4: {67} # lpsystem -t bsd servername   # add server
h4: {68} # lpadmin -p pr -s servername -T unknown -I any # set up printer
h4: {69} # accept pr   # enable queueing
h4: {70} # enable pr   # enable printing
h4: {71} # lpstat -t   # show status
scheduler is running
system for pr: servername
lp accepting requests since Mon Aug  6 12:00:00 PST 2000
Printer: pr@servername  'Hp : Laserwriter' (printing disabled)
 Queue: 1 printable job
 Rank  Owner/ID            Class Job Files            Size Time
1      papowell@h4+207       A   207 h4.023205           3 18:24:54

The above commands will create the necessary directories and files for the printer. If you want to use the lp -o option syntax to pass options to the LPRng print spooler you will have to enable this by hand. The /etc/printers.conf (this may be in some other directory besides /etc) needs to be modified so that it allows options to be passed using the Solaris convention, which is to put them on the S or O line of the control file. For example:

#
# The preferred method of modifying this file is through the use of
# lpset(1M) or fncreate_printer(1M)
#
pr:bsdaddr=servername,pr,Solaris:
_default:use=pr:

The bsdaddr=host,printer[,Solaris] indicates that the entry is for a remote RFC1179 printer on server host with name printer. The Solaris option indicates that the Solars extensions to the RFC1179 protocol are to be used.


3.2. Linux

There is no universal way to install LPRng cleanly on all of the different Linux systems. The major difficulty is the fragmentation in the various libraries, location of files, and system dependencies. If the LPRng installation procedure does not install the software correctly, please use the following remedial steps.

  1. Check the LPRng web site http://www.lprng.com and see if there is a Linux Release RPM or other binary distribution for your version of Linux. If not, then you will have to do a source install.

  2. Obtain the source distribution and follow the instructions outlined in other sections to compile and install the software. Use the following configuration options which correspond to Linux:

    h4: {72} # ./configure --prefix=/usr \
      --sysconfdir=/etc --mandir=/usr/share/man
    h4: {73} # make clean all install
    h4: {74} # checkpc -f
    
  3. You may need to modify your existing printcap file by adding the :bkf flag as shown below, or replace the entries with other filters.

    lp:\
      :if=/usr/local/libexec/lpr/hplaserjet3:\
      :bkf:\     # added to the printcap by hand
      :...
    
  4. Test the system by printing a file. If this does not work, then you will have to determine if the problem is in the print spooling software or in the filter. See the section on ifhp for directions on how to replace the vendor supplied filters with ifhp.


3.3. 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 or 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 or create a daemon user and group. We decided to use the printq group; on reflection it would have been easier to have created a daemon 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 and user in your lpd.conf file or in the sources src/common/vars.c. This is an example for lpd.conf:

# Purpose: group to run SUID ROOT programs
#   default group=daemon
group=printq
# Purpose: server user for SUID purposes
#   default user=daemon
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:

h4: {75} # topsrc -p'pid of /usr/sbin/lpd'
h4: {76} # 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:

h4: {77} # 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, then Printer/Plotter Devices, then 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 characteristics of the device:
Port Number: p
Option 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 characteristics! SMIT's fast path is smit chdev.

Finally remove all AIX printing commands like qprt, lp, cancel, lpq, and 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.


3.4. AppleTalk Support

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

The University of Michigan doesn't seem to be doing anything more for netatalk, and Adrian Sun has continued development. You'll find his updates at ftp://ftp.cobaltnet.com/pub/users/asun/testing/ He has improved netatalk considerably over the last UMich version. Documentation on netatalk is, unfortunately, almost non-existent.

There are a couple of very active mail lists for Netatalk: netatalk-admins@umich.edu and linux-atalk@netspace.org.

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

Your 32 Character Printer Name:\
        :pr=|/your/path/to/lpr -Pprintername:\
        :op=username.for.printing:\
        :pd=/your/path/to/ppd/files/yourprinter.ppd:

Examples:

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

Chapter 4. Print Spooling Tutorial

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

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


4.1. Overview

+---------+    +-----+    +-----+     +--------+    +---------+
| program | -> | lpr | -> | lpd |  -> | filter | -> | printer |
+---------+    +-----+  * +-----+     +--------+    +---------+
                  *    *     |
               printcap      V
                          +-----+     +--------+    +---------+
                          | lpd |  -> | filter | -> | printer |
                          +-----+     +--------+    +---------+

                           Figure 1

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

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

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

The protocol or commands used to do this job forward and transfer are specified by RFC1179. This protocol specifies how the lpr client program sends a job to the lpd server, as well as how the lpd server forwards jobs to another server. In addition to job submission, RFC1179 specifies commands to obtain queue status, to remove jobs from the queue, and to start and stop print queues.


4.2. Sample Printcap Entry

As described in the Print Spooling Overview, the information in the printcap database is used control printing operations. While there is no RFC specifying its format or content, there is a strong de facto standard for its format. For a complete description of the printcap database see Printcap Database. For a list of all of the printcap and configuration options see Index To All The Configuration and Printcap Options.

Here is a sample printcap suitable for use by the LPRng clients:

LPRng use :lp for destination (device and spool queue)
   lp:lp=lp@h4.private
   lpreal:lp=/dev/lp0

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

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

On the printserver the following is a sample printcap entry suitable for the lpd server:

lp:
  :lp=/dev/lp0
  :sd=/var/spool/lpd/%P
  :filter=/usr/local/libexec/filters/ifhp

The :sd=directory option (spool queue directory) specifies the directory where print jobs will be placed. The %P will be replaced with the name of the spool queue. The :lp literal is now the path to the output device the lpd server will use to print the job. The :filter literal specifies the filter program to use to process the job before sending to the output device.

Here is another example of a printcap entry using the %P notation:

lp:lp=%P@h4.private
This entry will cause all jobs sent to the lp spool queue to be sent to the lp queue on server. %X strings in the printcap entries are expanded as shown:
Key Replaced By
%P printcap entry primary name (printer)
%Q queue requested
%h short host name (host)
%H fully qualified host name (host.dns.whatever)
%R remote printer (rp value)
%M remote host (rm value)
%D date in YYYY-MM-DD format

4.3. Setting Up the Tutorial Configuration

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

We will use a series of simple printcap entries during this tutorial. We will assume that the LPRng system is using the /etc/printcap file. If your system is configured to use another one, then you can make a symbolic link from /etc/printcap or you can simply use your default printcap file.

Save the existing printcap file and then create a new printcap file with the contents as shown below. You will need to have ROOT (superuser) permissions to change the file and perform some of the maintenance operations.

h4: {78} # cd /etc 
h4: {79} # mv printcap printcap.orig
h4: {80} # vi printcap
#  printcap file contents:
lp:sd=/var/spool/lpd/%P
  :force_localhost
  :lp=/tmp/lp
lp2:sd=/var/spool/lpd/%P
  :force_localhost
  :lp=/tmp/lp2
h4: {81} # #  set permissions so everybody can read file
h4: {82} # chmod 666 printcap

We save the original printcap file and create a new one. We give the file world writable permissions so that later we can modify this file without needing to have root permissions. The printcap file has two entries: lp and lp2. Each print queue on the server needs a spool file to hold print jobs, jobs, and the :sd value specifies its location. The %P value is replaced with the name of the printer when it is used. In classical BSD operation each host has an lpd print spooler running on the local host (we use localhost in this manual for simplicity). Files were copied to spool directories on the localhost and then then print spooler would send them to the destination, which could be another print spooler. This meant that each localhost machine had to have a print spooler and spool queue directory structure. Management of this becomes very difficult in large organizations. The force_localhost forces this mode of operation and means that the lpd server and clients must run on the same host.

We use files for the output devices (:lp=) so that we can see easily view the output (and also to save trees). We will also need to have some simple test files. Create the files using the following commands.

h4: {83} # cp /dev/null /tmp/lp
h4: {84} # cp /dev/null /tmp/lp2
h4: {85} # chmod 666  /tmp/lp /tmp/lp2
h4: {86} # echo hi >/tmp/hi
h4: {87} # echo there >/tmp/there

We will use a dummy lpd.perms file that allows all users to do anything. This is useful for testing, but dangerous in a working environment.

h4: {88} # #  we modify the lpd.perms to allow an ordinary user to control
h4: {89} # mv lpd.perms lpd.perms.orig
h4: {90} # echo "DEFAULT ACCEPT" >lpd.perms
h4: {91} # chmod 666 lpd.perms

Finally we run checkpc to make sure that our printcap is correct and to create the necessary spool directories:

h4: {92} # checkpc -f -V
Checking printer 'lp'
 Checking directory: '/var/spool/lp'
   directory '/var'
   directory '/var/spool'
   directory '/var/spool/lp'
 Warning -   changing ownership '/var/spool/lp' to 1/1
   checking 'control.lp' file
   checking 'status.lp' file
   checking 'status' file
   cleaning 'status' file, 0K bytes: no truncation
   checking 'log' file
   cleaning 'log' file, 0K bytes: no truncation
   checking 'acct' file
   cleaning 'acct' file, 0K bytes: no truncation
Checking printer 'lp2'
  Checking directory: '/var/spool/lp2'
    directory '/var'
  ....
h4: {93} # lpc reread
h4: {94} # lpd
h4: {95} # lpq
Printer: lp@h4
   Queue: no printable jobs in queue

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

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


4.4. Restoring Original Configuration

To restore the original configuration, you simply need to restore the original printcap and lpd.perms file and then restart the lpd server. These operations require ROOT permissions.

h4: {96} # su
h4: {97} # cd /etc    OR    cd /usr/local/etc
h4: {98} # mv printcap.orig printcap
h4: {99} # mv lpd.perms.orig lpd.perms
h4: {100} # checkpc -f
h4: {101} # lpc reread
h4: {102} # rm -rf /var/spool/lpd/lp /var/spool/lpd/lp2
h4: {103} # rm -f /tmp/lp /tmp/lp2

4.5. Printing a File and Checking Status

Try the following commands. The commands appear after the prompt, and sample output that you might see is shown.

h4: {104} % lpr -V /tmp/hi
Version LPRng-3.6.14
sending job 'papowell@h4+238' to lp@localhost
connecting to 'localhost', attempt 1
connected to 'localhost'
requesting printer lp@localhost
sending control file 'cfA238h4.private' to lp@localhost
completed sending 'cfA238h4.private' to lp@localhost
sending data file 'dfA238h4.private' to lp@localhost
completed sending 'dfA238h4.private' to lp@localhost
done job 'papowell@h4+238' transfer to lp@localhost

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

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

You can use the lpq command to view the status of the print job.

h4: {105} % lpq
Printer: lp@h4
 Queue: no printable jobs in queue
 Status: job 'papowell@h4+238' removed at 09:39:03.256

If you want to see more status information, use lpq -l, lpq -ll, or even lpq -L. The -L provides alL the status.

h4: {106} % lpq -l
Printer: lp@h4
 Queue: no printable jobs in queue
 Status: lp@h4.private: job 'papowell@h4+238' printed at 09:39:03.112
 Status: job 'papowell@h4+238' removed at 09:39:03.256
h4: {107} % lpq -ll
Printer: lp@h4
 Queue: no printable jobs in queue
 Status: finished 'papowell@h4+238', status 'JSUCC' at 09:39:03.108
 Status: subserver pid 8240 exit status 'JSUCC' at 09:39:03.110
 Status: lp@h4.private: job 'papowell@h4+238' printed at 09:39:03.112
 Status: job 'papowell@h4+238' removed at 09:39:03.256
h4: {108} % lpq -L
Printer: lp@h4
 Queue: no printable jobs in queue
 Status: subserver pid 8240 starting at 09:39:03.105
 Status: accounting at start at 09:39:03.105
 Status: opening device '/tmp/lp' at 09:39:03.105
 Status: printing job 'papowell@h4+238' at 09:39:03.106
 Status: no banner at 09:39:03.107
 Status: printing data file 'dfA238h4.private', size 3 at 09:39:03.107
 Status: printing done 'papowell@h4+238' at 09:39:03.107
 Status: accounting at end at 09:39:03.108
 Status: finished 'papowell@h4+238', status 'JSUCC' at 09:39:03.108
 Status: subserver pid 8240 exit status 'JSUCC' at 09:39:03.110
 Status: lp@h4.private: job 'papowell@h4+238' printed at 09:39:03.112
 Status: job 'papowell@h4+238' removed at 09:39:03.256

There are different status formats available as well. The lpq -s (summary) produces a single line of status per spool queue, while the lpq -v (verbose) produces output that is very suitable for processing with programs such as Perl or awk:

h4: {109} % lpq -s
lp@h4  0 jobs
h4: {110} % lpq -v
Printer: lp@h4
 Printing: no
 Aborted: no
 Spooling: no
 Queue: no printable jobs in queue
 SPOOLCONTROL=
 Status: subserver pid 8240 starting at 09:39:03.105
 Status: accounting at start at 09:39:03.105
 Status: opening device '/tmp/lp' at 09:39:03.105
 Status: printing job 'papowell@h4+238' at 09:39:03.106
 Status: no banner at 09:39:03.107
 Status: printing data file 'dfA238h4.private', size 3 at 09:39:03.107
 Status: printing done 'papowell@h4+238' at 09:39:03.107
 Status: accounting at end at 09:39:03.108
 Status: finished 'papowell@h4+238', status 'JSUCC' at 09:39:03.108
 Status: subserver pid 8240 exit status 'JSUCC' at 09:39:03.110
 Status: lp@h4.private: job 'papowell@h4+238' printed at 09:39:03.112
 Status: job 'papowell@h4+238' removed at 09:39:03.256

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


4.6. Selecting the Print Queue

In the previous section we used the default print queue. How does LPRng determine what print queue to use? First, you can explicitly specify the printer using the lpq -Pprintqueue option and the lpq -a or lpq -Pall to select all print queues:

h4: {111} % lpq -Plp
Printer: lp@h4
 Queue: no printable jobs in queue
h4: {112} % lpq -Plp2
Printer: lp2@h4
 Queue: no printable jobs in queue
h4: {113} % lpq -a
Printer: lp@h4
 Queue: no printable jobs in queue
Printer: lp2@h4
 Queue: no printable jobs in queue

You can combine the lpq -a with the lpq -s option for a summary listing:

h4: {114} % lpq -a
Printer: lp@h4
 Queue: no printable jobs in queue
Printer: lp2@h4
 Queue: no printable jobs in queue
h4: {115} % lpq -s -a
lp@h4  0 jobs
lp2@h4  0 jobs

There is another way to explicitly specify the printqueues listed by lpq -a; see the all Printcap Entry for details.

Users can set their default printer by using the PRINTER (highest priority), LPDEST (next), NPRINTER (next), and NGPRINTER (lowest priority), environment variables. For example:

h4: {116} % setenv PRINTER lp2
h4: {117} % lpq
Printer: lp2@h4
 Queue: no printable jobs in queue
h4: {118} % unsetenv PRINTER
h4: {119} % lpq
Printer: lp@h4
 Queue: no printable jobs in queue

If the printer is not specified on the command line or by the environment variables, then the first printer in the printcap database will be used and then the default printer in the configuration database, and finally the compile time default. Printer and Server Information for details.


4.7. Controlling the Print Queue

The lpc command is used to examine and control the print server operation. The lpc status command displays the administrative status of a print queue. The lpd program caches status and job information in order to improve performance. The lpc flush command will flush the cached information and cause the server to regenerate it. The lpc enable and lpc disable commands enable or disable spooling to the print queue, and the lpc stop and lpc start commands stop and start printing (or transfers) of jobs in the print queue.

Let's look at the status displayed when we use these commands:

h4: {120} % lpc status
 Printer  Printing Spooling Jobs Server Subserver Redirect Status/(Debug)
lp@h4      enabled  enabled    0   none    none
h4: {121} % lpc status all
 Printer  Printing Spooling Jobs Server Subserver Redirect Status/(Debug)
lp@h4      enabled  enabled    0    none    none
lp2@h4     enabled  enabled    0    none    none
h4: {122} % lpc
lpc>status
 Printer  Printing Spooling Jobs Server Subserver Redirect Status/(Debug)
lp@h4      enabled  enabled    0    none    none
lpc>status all
 Printer  Printing Spooling Jobs Server Subserver Redirect Status/(Debug)
lp@h4      enabled  enabled    0    none    none
lp2@h4     enabled  enabled    0    none    none
lpc>quit

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

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

Now let's use the basic spool queue control commands:

h4: {123} % lpc disable
Printer: lp@h4
lp@h4.private: disabled
h4: {124} % lpq
Printer: lp@h4  (spooling disabled)
 Queue: no printable jobs in queue
h4: {125} % lpc enable
Printer: lp@h4
lp@h4.private: enabled
h4: {126} % lpq
Printer: lp@h4
 Queue: no printable jobs in queue
h4: {127} % lpc stop
Printer: lp@h4
lp@h4.private: stopped
h4: {128} % lpq
Printer: lp@h4  (printing disabled)
 Queue: no printable jobs in queue
h4: {129} % lpc start
Printer: lp@h4
lp@h4.private: started
h4: {130} % lpq
Printer: lp@h4
 Queue: no printable jobs in queue

As we can see, the lpc command also reports on the status of the print queue. Let's see what happens when we print to a stopped queue:

h4: {131} % lpc stop
Printer: lp@h4
lp@h4.private: stopped
h4: {132} % lpr /tmp/hi
h4: {133} % lpr /tmp/hi /tmp/there
h4: {134} % lpq
Printer: lp@h4  (printing disabled)
 Queue: 2 printable jobs
 Server: no server active
 Rank   Owner/ID               Class Job Files            Size Time
1      papowell@h4+17920         A 17920 /tmp/hi             3 18:14:22
2      papowell@h4+17922         A 17922 /tmp/hi,/tmp/there  9 18:14:30
h4: {135} % lpc status
 Printer  Printing Spooling Jobs  Server Subserver Redirect Status/(Debug)
lp@h4     disabled  enabled    2    none    none

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

Now let's start the print queue and watch what happens.

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

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

We can also use the lpc command to see the status of a particular job. We can select jobs by the user name, the ID, or the job number. For example:

h4: {139} % lpc stop
Printer: lp@h4
lp@h4.private: stopped
h4: {140} % echo hi |lpr
h4: {141} % echo there | lpr
h4: {142} % echo test |lpr
h4: {143} % lpq
Printer: lp@h4  (printing disabled)
 Queue: 3 printable jobs
 Server: no server active
 Status: job 'papowell@h4+17922' removed at 18:15:13.981
 Rank   Owner/ID            Class Job Files           Size Time
1      papowell@h4+17959      A 17959 (stdin)            3 18:23:24
2      papowell@h4+17962      A 17962 (stdin)            6 18:23:30
3      papowell@h4+17970      A 17970 (stdin)            5 18:23:35
h4: {144} % lpq 17970
Printer: lp@h4  (printing disabled)
 Queue: 3 printable jobs
 Server: no server active
 Status: job 'papowell@h4+17922' removed at 18:15:13.981
 Rank   Owner/ID            Class Job Files           Size Time
3      papowell@h4+17970      A 17970 (stdin)            5 18:23:35
h4: {145} % lpq papowell
Printer: lp@h4  (printing disabled)
 Queue: 3 printable jobs
 Server: no server active
 Status: job 'papowell@h4+17922' removed at 18:15:13.981
 Rank   Owner/ID            Class Job Files           Size Time
1      papowell@h4+17959      A 17959 (stdin)            3 18:23:24
2      papowell@h4+17962      A 17962 (stdin)            6 18:23:30
3      papowell@h4+17970      A 17970 (stdin)            5 18:23:35
h4: {146} % lpq -s 17970
lp@h4  1 jobs
h4: {147} % lpq -s papowell
lp@h4  3 jobs
h4: {148} % lpq -s nobody
lp@h4  0 jobs

We use lpq -Pqueuename to select a specific print queue and lpq -a or lpq -Pall to select all queues:

h4: {149} % lpc -a stop
Printer: lp@h4
lp@h4.private: stopped
Printer: lp2@h4
lp2@h4.private: stopped
h4: {150} % lpc -Pall start
Printer: lp@h4
lp@h4.private: started
Printer: lp2@h4
lp2@h4.private: started

You can use the lpc command in interactive mode:

h4: {151} % lpc
lpc>status
 Printer  Printing Spooling Jobs  Server Subserver Redirect Status/(Debug)
lp@h4      enabled  enabled    3    17990   17993
lpc>status all
 Printer  Printing Spooling Jobs  Server Subserver Redirect Status/(Debug)
lp@h4      enabled  enabled    3    17990   17993
lp2@h4     enabled  enabled    3    none    none
lpc>stop lp
Printer: lp@h4
lp@h4.private: stopped
lpc>start lp
Printer: lp@h4
lp@h4.private: started
lpc>quit

The lpc topq command can be used to put a job (or jobs) at the head of the spool queue. This command is very useful when some job requires priority service. You can select the job by using the job number or the job ID.

h4: {152} % lpc topq lp 17970
Printer: lp@h4
lp: selected 'papowell@h4+17970'
lp@h4.private: started
h4: {153} % lpq
Printer: lp@h4
 Queue: 3 printable jobs
 Server: pid 17999 active
 Rank   Owner/ID          Class Job Files      Size Time
active papowell@h4+17970    A 17970 (stdin)      5 18:23:35
1      papowell@h4+17959    A 17959 (stdin)      3 18:23:24
2      papowell@h4+17962    A 17962 (stdin)      6 18:23:30

4.8. Job Removal

Occasionally we print a file and then change our mind and want to cancel the job. The lprm command allows us to do this.

h4: {154} % lpq
Printer: lp@h4  (printing disabled)
 Queue: 3 printable jobs
 Server: no server active
 Status: job 'papowell@h4+17922' removed at 18:15:13.981
 Rank   Owner/ID          Class Job Files      Size Time
1      papowell@h4+17959    A 17959 (stdin)       3 18:23:24
2      papowell@h4+17962    A 17962 (stdin)       6 18:23:30
3      papowell@h4+17970    A 17970 (stdin)       5 18:23:35
h4: {155} % lprm
Printer lp@h4:
  checking perms 'papowell@h4+17959'
  dequeued 'papowell@h4+17959'
h4: {156} % lpq
Printer: lp@h4  (printing disabled)
 Queue: 2 printable jobs
 Server: no server active
 Status: job 'papowell@h4+17922' removed at 18:15:13.981
 Rank   Owner/ID          Class Job Files      Size Time
1      papowell@h4+17962    A 17962 (stdin)       6 18:23:30
2      papowell@h4+17970    A 17970 (stdin)       5 18:23:35
h4: {157} % lprm 17970
Printer lp@h4:
  checking perms 'papowell@h4+17970'
  dequeued 'papowell@h4+17970'
h4: {158} % lpq
Printer: lp@h4  (printing disabled)
 Queue: 1 printable job
 Server: no server active
 Status: job 'papowell@h4+17922' removed at 18:15:13.981
 Rank   Owner/ID          Class Job Files     Size Time
1      papowell@h4+17962    A 17962 (stdin)      6 18:23:30

By default, the lprm command removes the first job in the queue that the user has permission to remove. Also, as shown in the example, you can remove a job by specifying the job ID or the job number. If you specify a user name, you remove all of the user's jobs. This can be dangerous:

h4: {159} % lpq
Printer: lp@h4  (printing disabled)
 Queue: 3 printable jobs
 Server: no server active
 Status: job 'papowell@h4+17922' removed at 18:15:13.981
 Rank   Owner/ID          Class Job Files     Size Time
1      papowell@h4+17962    A 17962 (stdin)      6 18:23:30
2      papowell@h4+18499    A 18499 /tmp/hi      3 18:56:00
3      papowell@h4+18501    A 18501 /tmp/there   6 18:56:02
h4: {160} % lprm papowell
Printer lp@h4:
  checking perms 'papowell@h4+17962'
  dequeued 'papowell@h4+17962'
  checking perms 'papowell@h4+18499'
  dequeued 'papowell@h4+18499'
  checking perms 'papowell@h4+18501'
  dequeued 'papowell@h4+18501'
h4: {161} % lpq
Printer: lp@h4  (printing disabled)
 Queue: no printable jobs in queue
 Status: job 'papowell@h4+17922' removed at 18:15:13.981

The special user all matches all jobs in a print queue. Clearly you should be careful not to specify lprm all by accident. Even more dangerous is the following command:

h4: {162} % lprm -a all

As you might surmise, this removes all print jobs in all queues, which is an excellent way to purge print queues of all jobs.


4.9. Print Job Filters

A printer usually understands one or more Print Job Languages. Files sent to this printer must be in one of these languages and have the appropriate job format. The most common Print Job Languages are PostScript and PCL. Text files are PCL with no special PCL control sequences.

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

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

When a print job is created the files in the print job are assigned a format. This format was meant as a guide to the print spooler and was to be used to select the filter program for the files in the job. The format was a lower case letter; the f is the default format and indicates normal processing and the l format indicates a literal or binary file. Job files that are flagged as having literal or binary format are usually passed directly to the printer or have at the most a minimal amount of processing. See Print Job Formats for more information about formats and their use with filters.

There are two ways to specify filters: the default :filter=... option and the more specific Xf=... option. The X is a lower case letter corresponding to a format. Here is a sample printcap entry with a filter specification:

lp:sd=/var/spool/lpd/%P
  :filter=/usr/local/lib/filters/ifhp
  :rf=/usr/local/lib/filters/rfilter
All jobs with formats other than r will be processed using the ifhp program while the jobs with the r format will be processed using the rfilter program.

We will set up a very simple filter and use it to demonstrate how filtering is done by the lpd print spooler. First, set up the /tmp/testf file as shown below.

#!/bin/sh
# /tmp/testf - test filter for LPRng
PATH=/bin:/usr/bin; export PATH
echo TESTF $0 "$@" >&2
echo TESTF $0 "$@"
echo ENV
set
echo LEADER
/bin/cat
echo TRAILER
exit 0

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

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

We can test our script, with the results shown below:

h4: {163} % chmod 755 /tmp/testf
h4: {164} % echo hi |/tmp/testf -a1
TESTF /tmp/testf -a1
TESTF /tmp/testf -a1
ENV
USER=papowell
HOSTNAME=h4
...
PATH=/bin:/usr/bin
LEADER
hi
TRAILER

Let's now use this filter. Edit the lp printcap entry so it has contents indicated below, use checkpc -f to check the printcap, and then use lpc reread to restart the lpd server.

lp:sd=/var/spool/lpd/%P
  :force_localhost
  :lp=/tmp/lp
  :filter=/tmp/testf

Execute the following commands to print the /tmp/hi file and observe the results:

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

PS1=$
OPTIND=1
PS2=>
SPOOL_DIR=/var/tmp/LPD/lp
LOGNAME=papowell

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

PATH=/bin:/usr/bin
SHELL=/bin/sh
LOGDIR=/home/papowell
IFS=
PRINTER=lp
LEADER
test Test
TRAILER

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

As we see from the lpq status, lpd passes a large number of command line options to our filter. These options and their meanings are discussed in detail in Filter Command Line Options and Environment Variables. We will discuss these in more detail in the next section.

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


4.9.1. Control Files and Filter Options

When you transfer a print job the lpd print spooler will send the job as two or more files: control file that contains information about the job and data files that contain the information to be printed. Here is sample control file:

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

Lines starting with upper case letters contain job information such as the user who submitted the job. Lines starting with lower case letters indicate the data file to be printed and the corresponding format. For full details about the exact format of the control file see Job Files.

Table 4-1 shows the correspondence between lines in the control file and lpr command line options. The N values are the names of the files that are printed. The U indicates a data file is in a job and is present to meet RCF1179 and vintage print spooler requirements.

Table 4-1. Filter Options

Control File Filter Option Purpose or Value
  -PPrinter Print queue name - printcap information
H -HHost Host Name
P -nUser User Login Name of job originator
J -JJob name lpr -J option or file name
C -CClass Print class (lpr -C option)
L -LBanner Banner page request
A -AJobid Job Id
D -DDate Date or time information
Q -QQueue Original Print queue job was sent to
N -NFilename Filename
f,l,p,... -Ff Datafile format
U   Datafile (historical)

When a print filter processes these jobs the values in the control file are passed on the command line as options starting with upper case letters:

/tmp/testf -Apapowell@h4+26681 -CA \
   -D2000-04-11-21:39:21.877 -Ff -Hh4.private ....
Sometimes we want to pass only a small subset of these command line options to a filter or provide them in a specific order in order to be compatible with legacy print filters. LPRng provides several different ways to do this and we will explore how to control command line options.

If the filter entry starts with -$, this suppresses the automatic addition of command line options; we can then add our own options to the command line. Modify the printcap entry to have the following form:

lp:sd=/var/spool/lpd/%P
  :force_localhost
  :lp=/tmp/lp
  :filter= -$ /tmp/testf '$P' $0P -X$-P ${lp} G\072 or \:

Lets print our /tmp/hi test file and then look at the lpq status:

h4: {169} % cp /dev/null /tmp/lp
h4: {170} % lpr /tmp/hi
h4: {171} % lpq -llll
Printer: lp@h4
 ....
 Status: IF filter msg - 'TESTF /tmp/testf -Plp -P lp -Xlp \
    -Ylp /tmp/lp G: or :' at 01:20:21.560

The -$ suppresses the adding the default literals to the filter command line. You can pass specific options using $X; if the option has a non-null value then it will be expanded in the following format:

Option    Value Expansion
$X        -X<value>
$'X       -X'<value>'
$0X       -X '<value>'
$-X       <value>
$'-X      '<value>'
${X}      control file X option value
$'{X}     control file X option value (in quotes)
${name}   printcap option value if value nonzero length
$'{name}  printcap option value if value nonzero length in quotes
\nnn      single printable character
$*        all options in control file expanded using $X

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

The ${name} option is used to pass a printcap option value. For example, you can pass the value of the printcap option form as shown below. You can experiment with this by using the /tmp/testf filter and printcap shown below.

printcap:
lp:sd=/var/spool/lpd/%P
  :force_localhost
  :lp=/tmp/lp
  :filter=/tmp/testf -F ${form}
  :form=payroll

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

If we have a legacy print filter that was originally written for the BSD print spooler, then we may find that it requires a small number of command line options in a very specific order. We can use the :bkf (BSD Kompatible Filter or BacKwards compatible Filter) flag to pass suitable options. Modify the printcap entry to have the following form:

lp:sd=/var/spool/lpd/%P
  :force_localhost
  :lp=/tmp/lp
  :filter=/tmp/testf
  :bk

Lets print our /tmp/hi test file and then look at the lpq status:

h4: {175} % cp /dev/null /tmp/lp
h4: {176} % lpr /tmp/hi
h4: {177} % lpq -llll
Printer: lp@h4
 ....
 Status: IF filter msg - 'TESTF /tmp/testf -Plp -w80 -l66 \
    -x0 -y0 -Ff -Lpapowell -J/tmp/hi -CA -n papowell \
    -h h4.private acct' at 08:07:46.583

Finally, there are times when we would like the print filter to be a simple shell command or to chain several programs together in a simple pipeline. While this is possible using a print filter, you can also do this in the filter specification. If your filter specification starts with a parenthesis (() or contains the IO redirection for pipeto (|), input redirection (<), or output redirection (>) then the lpd server will use the :shell configuration option value (default /bin/sh) and execute it using:

${shell} -c "( ${if} )"

If this is done, then no command line options are added to the command. However, expansion of $X parameters are still done. Modify the printcap entry to have the following form:

lp:sd=/var/spool/lpd/%P
  :force_localhost
  :lp=/tmp/lp
  :filter=(echo "PREAMBLE"; /tmp/testf; echo "APPENDIX")

Lets print our /tmp/hi test file and then look at the lpq status:

h4: {178} % cp /dev/null /tmp/lp
h4: {179} % lpr /tmp/hi
h4: {180} % lpq -llll
Printer: lp@h4
 ....
 Status: printing data file 'dfA018881h4.private', size 3, \
   IF filter 'echo' at 09:22:11.476
 Status: IF filter msg - 'TESTF /tmp/testf' at 09:22:11.510
 Status: IF filter finished at 09:22:11.514

If we examine the /tmp/lp file we find:

PREAMBLE
TESTF /tmp/testf
ENV
USER=papowell
LD_LIBRARY_PATH=/lib:/usr/lib:/usr/5lib:/usr/ucblib
...
PRINTER=lp
LEADER
hi
TRAILER
APPENDIX

As we expected, no options were passed on the command line. If the printcap is modified to have the following contents, then you will see:

lp:sd=/var/spool/lpd/%P
  :force_localhost
  :lp=/tmp/lp
  :filter=(echo "PREAMBLE"; /tmp/testf $*; echo "APPENDIX")

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

Using the shell invocation is especially useful when you may have a parameter that has an empty string value, and need to pass this as a command line parameter. Modify the /tmp/testf filter, the printcap, and execute the following commands:

printcap:
  lp:sd=/var/spool/lpd/%P
    :force_localhost
    :lp=/tmp/lp
    :filter=( /tmp/testf -F '${form}' )
    :form=

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

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

h4: {186} % more /tmp/lp
TESTF /tmp/testf -F
PARM '-F'
PARM ''
LEADER
hi
TRAILER

As you can see, there are empty parameters passed to the filter. This is due to the combination of the $'{form} and using the :filter=(...) form.


4.9.2. Filter Environment Variables

In this section we will look further at the environment variables passed to the filter. We printed the shell variable values for the filter at the start of the file:

h4: {187} % cat /tmp/lp
/tmp/testf -Plp -P lp -Xlp -Ylp /tmp/lp G:' at 01:20:21.560
ENV
CONTROL=Hh4.private
 Ppapowell
 J/tmp/hi
 CA
 Lpapowell
 Apapowell@h4+105
 D2000-04-12-15:27:26.662
 Qlp
 N/tmp/hi
 fdfA105h4.private
 UdfA105h4.private
HF=H=h4.private
 P=papowell
 J=/tmp/hi
 C=A
 L=papowell
 A=papowell@h4+105
 D=2000-04-12-15:27:26.662
 Q=lp
 transfername=cfA105h4.private
 datafiles=N=/tmp/hi,transfername=fdfA105h4.private,
HOME=/home/daemon
LD_LIBRARY_PATH=/lib:/usr/lib:/usr/5lib:/usr/ucblib
LOGDIR=/home/daemon
LOGNAME=daemon
OPTIND=1
PATH=/bin:/usr/bin:/usr/local/bin
PRINTCAP_ENTRY=lp
 :force_localhost
 :filter=/tmp/testf
 :lp=/tmp/lp
 :sd=/tmp/LPD/lp
PRINTER=lp
PS1=$
PS2=>
SHELL=/bin/sh
SPOOL_DIR=/tmp/LPD/lp
USER=daemon

LEADER
hi
TRAILER

The HOME, USER, SHELL, PS1, and PS2 variables are usually set by the shell, and are reflect the information for the UID of the user running the shell.

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

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


4.9.3. Using Command Line and Printcap Options In Filters

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

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

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

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

Save the current /tmp/testf filter file in /tmp/testf.old and replace it with the following:

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

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

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

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

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

if test -n "$Debug" ; then
    set >/tmp/after
    echo "$@" >>/tmp/after
    diff /tmp/before /tmp/after
fi
/bin/cat
exit 0

Lets print our /tmp/hi test file and then look at the results in /tmp/lp:

h4: {188} % cp /dev/null /tmp/lp
h4: {189} % lpr /tmp/hi
h4: {190} % more /tmp/lp
0a1
> e=dfA021771h4.private
2a4,6
> l=66
> s=status
> L=papowell
10a15,17
> j=021771
> C=A
> J=/tmp/hi
12a20
> a=acct
...
33a58
> Printer=lp
...
hi

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

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

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

The following is an example of how to extract the same information in Perl:

#!/usr/bin/perl
eval 'exec /usr/bin/perl -S $0 ${1+"$@"}'
    if $running_under_some_shell;
            # this emulates #! processing on NIH machines.
            # (remove #! line above if indigestible)

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

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

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

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


4.9.4. Filter Exit Codes

The lpd server uses the exit code of the filter to determine if the filter was successful or unsuccessful. The Filter Exit Codes section discusses these values in detail, but here are the most important:

0 - JSUCC

A JSUCC exit code indicates that the filter was successful in doing its work.

1 - JFAIL

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

2 - JABORT

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

3 - JREMOVE

The JREMOVE exit code will cause the job to be removed from the print queue.

6 - JHOLD

The JHOLD exit code will cause the job to be temporarily prevented from printing until release by the lpc release command.

Other Values

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

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


4.9.5. Job Formats and Filter Selection

In the previous sections we discussed how a print filter was executed and how it could be used. Now we will look at how the lpd spooler chooses a print filter program. Let us re-examine our example print job control file:

Hh4.private
Ppapowell
J/tmp/hi
CA
Lpapowell
Apapowell@h4+105
D2000-04-12-15:27:26.662
Qlp
N/tmp/hi
fdfA105h4.private
UdfA105h4.private

Each data file for a print job has a name with the format dfXnnnh4.private. The df is used to indicate that the file is a data file, and the remainder is a unique name for the file in the job. The X part of the name must be an upper or lower case letter, setting a limit of 52 different files in a single print job.

The fdfA105h4.private line in the control file specifies that we are to print the job using the filter for the f format; the file printing information consists of the format assigned to the file and the name of the file. In the legacy BSD print spoolers, this format was used to select the print filter to be used. LPRng has expanded this by providing a default filter specification.

Table 4-2. Job Formats and Filter Selection

lpr command line option Control File Line Printcap Option For Filter Filter Command Line
(default) fdfAnnn :if=/path /path -Ff ...
-b or -l ldfAnnn :if=/path /path ... -Ff -c
-p pdfAnnn :if=/path pr | format f filter
-c, -d, -n, -r, -t, -v, -FX XdfAnnn :Xf=/path ... /path -FX
any format XdfAnnn :filter=/path ... /path -FX

Table 4-2 shows the rather baroque relationship between the format options specified by the lpr command and the way that lpd uses them. The reason for this complexity lies in the various implementations and variations that occurred during the development and deployment of the original BSD print spooling software.

Here is the complete, arcane, and baroque set of rules that are used to select and print filters. The default format used by lpr is f; unless some other format is specified this is used. The lpr -b and lpr -l (binary and literal literals) are a request to lpd to do as little processing as possible of this file before printing it. Lpd use the :if filter for formats f and l; the l literal causes the the -c filter command line flag to be used as well. The lpr -c, -d, -n, -r, -t, and -v options cause the corresponding format to be used, and for lpd to use the filter specified by the printcap option :Xf, where X is the specified format.

The lpr -p (pretty-print literal) selects p format, and lpd is supposed to use the program specified by the :pr printcap option to format the file and then process the output of this program according to format f. Unpredictable results may occur using this facility.

the lpr -FX allows you to explicitly specify format X where X is a lower case letter, and lpd will use the filter specified by printcap option :Xf, where X is the specified format. If there is no :Xf printcap literal value then the printcap :filter literal value will be used as the filter, and if this is undefined then the file will be passed without processing through to the printing device.

If a filter is not specified for the format then the default filter specified by :filter=/path filter is used, and if there is no default, then the output is sent directly to the output device.

If the :fx=formats is present in a printcap entry, it specifies the formats that are allowed. For example, :fx=lfv would allow only formats l, f, and v to be used on a particular spool queue.

Some Xf options have pre-assigned meanings and cannot be used for filter selection.

Printcap Option Purpose
Printcap Option Purpose
:af=/path Accounting File
:ff=formfeed Form Feed String
:if=/path filter (l,b,p,f formats)
:filter=/path Default filter
:lf=/path Log file
:of=/path OF filter
:sf suppress form feed between job files

The :of filter is a special case and is used for banner printing and accounting purposes. See OF Filter for details.


4.10. Job File Format Conversion with Filters

One of the major problems that face new users to UNIX printing is when they have a printer that has a proprietary print job format such as the HP DeskJet series of printers. The solution to this problem is quite simple: generate your output in PostScript, and then use the GhostScript program to convert the GhostScript output to a format compatible with your printer.

lp:filter=/usr/local/lib/filters/myfilter:...

/tmp/myfilter:

#!/bin/sh
/usr/local/bin/gs -dSAFER -dNOPAUSE -q -sDEVICE=djet500 \
    -sOutputFile=- - && exit 0
exit 2

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

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

  1. The file type should be determined, and only files that are PostScript should be passed to GhostScript.

  2. We may have some conversion routines that can convert files into PostScript files and then we can send them to GhostScript for raster conversion.

  3. If we cannot convert a file, then we should simply terminate the printing and cause the spooler to remove the job.

The ifhp Print Filter program is a companion to the LPRng software and does this type of operation. If you are using Linux, then you may find the RedHat Print Filters (http://www.debian.org) installed and in use on your system. The magicfilter developed by H. Peter Anvin http://www.debian.org is distributed with Debian Linux. The apsfilter by Andreas Klemm http://www.freebsd.org/~andreas/index.html is also widely used, although now most of its functionality is directly available in LPRng. Finally, the a2ps (Ascii to PostScript) converter by Akim Demaille and Miguel Santana is available from www-inf.enst.fr/~demaille/a2ps. This package provides a very nice set of facilities for massaging, mangling, bending, twisting, and being downright nasty with text or other files.


4.10.1. Simple Filter with File Format Detection

Since this is a tutorial, we will demonstrate a simple way to make your own multi-format print filter, and provide insight into how more complex filters work.

The file utility developed by Ian F. Darwin uses a database of file signatures to determine what the contents of a file are. For example:

h4: {191} % cd /tmp
h4: {192} % echo hi >hi
h4: {193} % gzip -c hi >hi.gz
h4: {194} % echo "%!PS-Adobe-3.0" >test.ps
h4: {195} % gzip -c test.ps >test.ps.gz
h4: {196} % file hi hi.gz test.ps test.ps.gz
hi:        ASCII text
hi.gz:     gzip compressed data, deflated
test.ps:   PostScript document text conforming at level 3.0
test.ps.gz: gzip compressed data, deflated
h4: {197} % file - <test.ps
standard input: PostScript document text conforming at level 3.0

If we are given a file, we can now use file to recognize the file type and if the file type is suitable for our printer we can send it to the printer, otherwise we can reject it. The following is a simple yet very powerful shell script that does this.

#!/bin/sh
# set up converters
gs="/usr/local/bin/gs -dSAFER -dNOPAUSE -q -sDEVICE=djet500 \
    -sOutputFile=/dev/fd/3 - 3>&1 1>&2"
a2ps="/usr/local/bin/a2ps -q -B -1 -M Letter --borders=no -o-"
decompress=""
# get the file type
type=`file - | tr A-Z a-z | sed -e 's/  */_/g'`;
echo TYPE $type >&2
case "$type" in
  *gzip_compressed* ) decompress="gunzip -c |" compressed="compressed" ;;
esac

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

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

Copy this to the /tmp/majik file, and give it 0755 (executable) permissions. Here is an example of the output of the script:

h4: {198} % /tmp/majik <test.ps.gz
TYPE standard_input:_gzip_compressed_data,_deflated...
COMPRESSED TYPE standard_input:_postscript_document_level_3.0
gunzip -c | /usr/local/bin/gs -dSAFER -dNOPAUSE -q -sDEVICE=djet500 \
   -sOutputFile=/dev/fd/3 - 3>&1 1>&2
h4: {199} % /tmp/majik </tmp/hi
TYPE standard_input:_ascii_text
 /usr/local/bin/a2ps -q -B -1 -M Letter --borders=no -o- \
  | /usr/local/bin/gs -dSAFER -dNOPAUSE -q -sDEVICE=djet500 \
   -sOutputFile=/dev/fd/3 - 3>&1 1>&2

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

We use a simple shell case statement to determine if we have a compressed file and get a decompression program to use. We reapply the file utility to the decompressed file (if it was compressed) and get the file type.

Finally we use another case statement to get the output converter and then we run the command. For tutorial purposes, we use an echo rather than an exec so we can see the actual command, rather than the output.

Just for completeness, here is majikperl:

#!/usr/bin/perl
eval 'exec /usr/bin/perl -S $0 ${1+"$@"}'
    if $running_under_some_shell;
            # this emulates #! processing on NIH machines.
            # (remove #! line above if indigestible)
my($gs) = "/usr/local/bin/gs -dSAFER -dNOPAUSE -q -sDEVICE=djet500 \
    -sOutputFile=/dev/fd/3 - 3>&1 1>&2";
my($a2ps)="/usr/local/bin/a2ps -q -B -1 -M Letter --borders=no -o-";

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

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

4.10.2. The ifhp Filter

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

The PostScript and PCL printer job languages are supported by most printer manufacturers. However, in order to have a job printed correctly the following steps must be taken.

  1. The printer must be put into a known initial state by sending it the appropriate reset strings or performing a correct set of IO operations.

  2. If accounting is being done, then the printer accounting information must be obtained and recorded. See Accounting for more information about LPRng support for accounting.

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

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

  5. The file must be transferred to the printer and the printer is monitored for any error conditions.

  6. Any required end of job commands are sent to the printer, and the printer monitored for error conditions while the job finishes printing.

  7. If accounting is being done, the printer accounting information such as page count and time used must be obtained and recorded. See Accounting for more information about LPRng support for accounting.

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

We will demonstrate how to add the ifhp filter to your printcap entry. Find the path to the ifhp filter using the find command as we did in the previous exercise. Modify the printcap as shown below and use lpc lpd to restart lpd.

lp:sd=/var/spool/lpd/%P
  :force_localhost
  :lp=/tmp/lp
  :ifhp=model=default
  # modify the path to ifhp appropriately
  :filter=/usr/local/libexec/filters/ifhp

Now print the /tmp/hi and then display /tmp/lp using a text editor such as vi or emacs that shows control characters:

h4: {200} % cp /dev/null /tmp/lp
h4: {201} % lpr /tmp/hi
h4: {202} % vi /tmp/lp
^[%-12345X@PJL
@PJL JOB NAME = "PID 405" DISPLAY = "papowell"
@PJL RDYMSG DISPLAY = "papowell"
@PJL USTATUSOFF
@PJL USTATUS JOB = ON
@PJL USTATUS DEVICE = ON
@PJL USTATUS PAGE = ON
@PJL USTATUS TIMED = 10
@PJL ENTER LANGUAGE = PCL
^]E^]&^]&k2G^]&s0C^]&l0O^]9^](s0P^](s10.00H^](s4099Thi
^]E^]%-12345X@PJL
@PJL RDYMSG DISPLAY = "papowell"
@PJL EOJ NAME = "PID 405"
@PJL USTATUSOFF
@PJL USTATUS JOB = ON
@PJL USTATUS DEVICE = ON
@PJL USTATUS PAGE = ON
@PJL USTATUS TIMED = 10
@PJL RDYMSG DISPLAY = ""
^[%-12345X

The output now contains all of the control sequences and setup codes needed to print a text file on the default printer. The :ifhp=model=default printcap entry is used by ifhp to get the information it needs to perform its operation. The following options are commonly provided in the :ifhp= option to configure the ifhp filter.

Table 4-3. :ifhp= Options

Option Purpose
model=name Use name entry in ifhp.conf
status or status@ Printer does or does not provide status information
sync, sync@, sync=(ps|pjl) Printer does or does not indicate ready to operate at start of job, or use PostScript or PJL code sequence to determine if printer is ready.
pagecount, pagecount@, pagecount=(ps|pjl) Printer does or does not have pagecount support, or use PostScript or PJL code sequence to determine pagecount.
waitend, waitend@, waitend=(ps|pjl) Wait or do not wait for end of job, or send PostScript or PJL code sequence to have printer report end of job.

The model=name entry is used to specify the configuration entry in the ifhp.conf file to be used by ifhp. This entry usually has all of the specific information needed by the ifhp filter.

The status option is the most common option usually provided in a printcap entry. This option is needed when the communication with the printer is write-only and no status information will be returned. If a printer normally supports returning status information then the ifhp.conf configuration entry will indicate this and the ifhp filter will try to get status. When no status is returned it will either terminate operation after a timeout or sit in an endless loop waiting for status. By specifying status@ you will suppress getting status. This also has the effect of doing sync@, pagecount@, and waitend@

The sync option is used to cause ifhp to wait for an end of job indication from the printer before starting the next job. This is usually done in order to make sure that all jobs have been flushed from a printer before starting another job. If you specify sync@ then you may get slightly faster startup but at the expense of losing the ends of previous print jobs.

The pagecount option is used to cause ifhp to get the value of a hardware pagecounter from the printer. If your printer supports such an item then the ifhp.conf configuration option usually indicates this. However, it takes a small amount of time to get the pagecounter information from the printer and you may not need it. Use sync@ if you do not want page counts.

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

For a complete list of all of the ifhp options please see the IFHP documentation.


4.10.3. The Jaggies - LF to CR-LF Conversion With lpf

When printing to vintage hard copy devices or to printers that support a text mode, many UNIX users discover that their output suffers from a case of the jaggies.

Input file:

  This is
  a nice day

Output:

  This is
         a nice day

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

First, locate the lpf filter. You can find it by using the command:

h9: {160} % find /usr/ -type f -name lpf -print
/usr/libexec/lpr/lpf

We will first see what the output is like without lpf, and then see what it does. Modify the lp printcap entry as shown below and then use lpc restart to restart the lpd server.

lp:sd=/var/spool/lpd/%P
  :force_localhost
  :lp=/tmp/lp

Print a file and view the output using the following commands. If you do not have the od (octal dump) program, try using hexdump or some other appropriate program that displays the numerical contents of the file.

h4: {203} % cp /dev/null /tmp/lp
h4: {204} % lpr /tmp/hi
h4: {205} % od -bc /tmp/lp
0000000  150 151 012
           h   i  \n
0000003

Now we will use the lpf filter. Modify the printcap as shown below and use lpc reread to cause lpd to reread the configuration information.

lp:sd=/var/spool/lpd/%P
  :force_localhost
  :lp=/tmp/lp
  # modify the path to lpf appropriately
  :filter=/usr/local/libexec/filters/lpf

Now reprint the file:

h4: {206} % cp /dev/null /tmp/lp
h4: {207} % lpr /tmp/hi
h4: {208} % od -bc /tmp/lp
od -bc /tmp/lp
0000000  150 151 015 012
           h   i  \r  \n
0000004

As you see, lpf changes the LF to a CR-LF sequence.


4.10.4. Store and Forward Spool Queues

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

However, we can also have store and forward spool queues. These queue act to simply buffer jobs and then forward them to another spooler. The following printcap entry shows how you can specify a store and forward queue.

# store and forward using classical BSD :rm:rp
lp:rp=pr:rm=host
  :sd=/var/spool/lpd/%P
  :server
# store and forward using LPRng lp=pr@host
lp:lp=pr@host
  :sd=/var/spool/lpd/%P
  :server

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

Edit the printcap file so it has contents indicated below, use checkpc -f to check the printcap, and then use lpc reread to restart the lpd server.

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

As we see from the status, our job was sent to the lp spool queue first. It was store there and then the lpd server transferred it to the lp2 spool queue, where it was printed to the file /tmp/lp2.


4.10.5. Filtering Job Files In Transit

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

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

Edit the printcap and /tmp/testf files so they have the contents indicated below, give /tmp/testf executable permissions, use checkpc -f to check the printcap, and then use lpc reread to restart the lpd server.

# set /tmp/testf to contain the following
#   and chmod 755 /tmp/testf
#!/bin/sh
echo TESTF $0 $@
/bin/cat
exit 0

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

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

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


4.11. Printcap Basics

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

The LPRng server and client software gets their configuration information from:

  • Compile time settings which set the default values for the configuration information.

  • A lpd.conf file that contains values that override the compile time defaults. This information can effect the behavior of the lpd server and clients.

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

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

Each print queue or printer has a name which is used to look up the printcap information for the printer. The /etc/printcap file is the default location for the printcap information, although it can also be obtained from database servers, or generated by programs. See the Using Programs To Get Printcap Information section for details.

We will use a more complex printcap file to explore how LPRng gets the printcap information. Put the following lines in the /etc/printcap file:

# client entry
lp:tc=.client
lp2:tc=.client
.client:
  :lp=%P@localhost
  :force_localhost

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

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

.common:
  :sd=/var/spool/lpd/%P
  :mx=0
The lpc client command is very useful to see how LPRng uses this printcap information:
h4: {214} % lpc client
Config
h4: {215} % lpc client all
Config
 :lpd_port=2000
 :printcap_path=/var/tmp/LPD/printcap

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

All
 :lp
 :lp2

Printcap Information
lp|main
 :force_localhost
 :lp=lp@localhost
lp2
 :force_localhost
 :lp=lp2@localhost

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


4.11.1. Printcap Processing Format

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

The printcap file is processed by reading it line by line and composing the individual printcap entries. Each entry has an name and one or more aliases. The entries in the printcap assign values to options. These can have the format:

option=string value \n with escapes
flag         # equivalent to flag=1
flag@        # equivalent to flag=0
option#value # equivalent to option=value

An option will have the last value that occurs in the printcap entry.

Our example shows the use of the tc (termcap include) facility. The :tc value is a list of printcap entries that should be prefixed to the start of the printcap entry in which it appears. This allows options to be set in the printcap entry which will override the values in the :tc included entry. For convenience, the options are displayed in sorted order.

The LPRng clients and lpd server may require a different set of options for the same spool queue. The clients require options whose values tell the clients how the contact the lpd server and transfer a print job or query to it. The lpd server needs options that tell it how to either print a job or forward it to another lpd server. The :client or :server option marks a printcap entry as for client or lpd server use only; unmarked entries are used by both server and client. The lpc client command shows the printcap information that the LPRng clients would use. For example, here is what the lpd server would use:

h4: {216} % lpc server all
Config
 :lpd_port=2000
 :printcap_path=/var/tmp/LPD/printcap

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

All
 :lp
 :lp2

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

When we select the server printcap information, we see that the :sd option has been added, and the :lp replaced with new values.

We can use the :oh (on this host) option to mark printcap entries for use by a selected set of hosts. For example:

lp:oh=10.0.0.0/255.255.255.0,*.private,!10.0.0.10
  :lp=%P@10.0.0.10

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

Finally, we can use the wildcard facility to cause a default printcap entry to be used:

lp|*:cm=Wildcard Alias - %P=lp, %Q=wanted
  :lp=%P@10.0.0.10
*:cm=Wildcard Name- %P=wanted, %Q=wanted
  :lp=%P@10.0.0.10

The LPRng software first searches the printcap information for an exact match. If none is found then it searches for the first wildcard entry that matches the printer name. If the wildcard is used as an alias, then the printcap entry is simply selected for use, with the printer name and queue name selected as shown above. We can also use partial matching as well:

lp|lp_*
  :lp=%P@10.0.0.10
qt|qt_*
  :lp=%P@10.0.0.12

In the example above the first entry matches lp and all printer names starting with lp_, while the second entry matches qt and all printer names starting with qt_. This can be useful when setting up a family of spool queues as discussed in later sections.


4.11.2. Printcap Information From Programs and Databases

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

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

printcap_path=|/tmp/getpc

h4: {217} % cd /etc
h4: {218} % cp lpd.conf lpd.conf.bak
h4: {219} % echo 'printcap_path=|/tmp/getpc' >>lpd.conf

Next, edit the /tmp/getpc file and set its values as shown below.

set /tmp/getpc:

  #!/bin/sh
  # /tmp/getpc
  echo PROG $0 "$@" >>/tmp/trace
  cat >>/tmp/trace
  cat <<EOF
  lp:lp=test@host
  EOF
  exit 0

h4: {220} % chmod 755 /tmp/getpc
h4: {221} % echo testing | /tmp/getpc -aoption
lp:lp=test@host
h4: {222} % cat /tmp/trace
PROG /tmp/getpc -aoption
testing

After you have tested the getpc script, use the lpc client all command:

h4: {223} % lpc client all
Config
 :lockfile=/var/tmp/LPD/lpd
 :lpd_port=2000
 :printcap_path=|/tmp/getpc

Names
 :lp=lp

All
 :lp

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

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

You restore the original lpd.conf file to restore the system to normal operation.

h4: {226} % cd /etc
h4: {227} % cp lpd.conf.bak lpd.conf

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


4.11.3. User Printcap Information

In addition to the system printcap, each user can define a private printcap file that will be read after the system printcap. Users can define LPRng client entries and can augment the system printcap information.

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

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

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

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

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


4.12. Banner Printing and the OF filter

Banner or header pages can be printed at the beginning, end, or both at the beginning or end. The following flags control how and where banners are printed. These flags are listed in order of precedence.

:sh

Suppress all banner printing or header pages. This prevents any banners from being generated by the lpd print spooler and if present in a client printcap entry, will cause lpr not to put any banner printing information in the control file. Even if this flag is not present, then LPRng will not print a banner unless a banner printing program is specified with the :bp, :bs, :be, or :sb option.

:ab

Print a banner or header page, even if the user has not requested one. The :sh option has precedence over the :ab option.

:hl

The banner (header) is at the end (last page) of the job.

:bs=/... and :be=/... and

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

:bp=...

If there is no :bs or :be value when printing a banner at the start or end of the job respectively, then use the indicated filter program to generate a banner.

:sb and :bl=....

If there is no program specified to generate the banner and the :sb flag is set, send the :bl (banner line) string to the printer.

:of=filter

A filter used to process banners and other non-job file information.

:suspend_of_filter

Controls whether the :of filter is suspended or has its input terminated.

The pclbanner, psbanner, and lpbanner programs are part of the LPRng distribution and are usually installed in the same location as the LPRng supported filters. They produce a PCL, PostScript, or text banner respectively.

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

  • It must be explicitly specified in the printcap file. It is not run by default.

    If specified, it is started at the beginning of job printing and stays present throughout the entire job printing session.

  • When printing individual files, the :of filter is sent a special suspend yourself two character string, \031\001. This will cause the :of filter to send itself a SIGSUSP (suspend) signal.

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

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

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


4.13. Printing from lpr Directly To A Device

While the most reliable way to print is to send jobs to a print spooler, sometimes it is desirable to print directly to a printer. This method is supported by the special :direct printcap flag or the lpr -Y command line flag. The following shows the effects of this flag:

lpr -Y -Phost%port file1 file2 ...
Eqivalent to:
  ( for i in file1 file2 ... ; do
    $filter $i
  done ) | tcpip_connection( host%port)

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

lpr -Y -P '|/program' file1 file2 ...
Eqivalent to:
  ( for i in file1 file2 ... ; do
    $filter $i
  done ) | /program

The above examples show how we can use the command line options to send files directly to a printer. You can also create a printcap that will do the same:

lp:direct:lp=/tmp/a:remote_support=R
Command:
  lpr -Plp file1 file2 ...
Equivalent to:
lpr -P/tmp/a -Y file1 file2 ...

Example:
h4: {228} % lp -P/tmp/a /tmp/hi
h4: {229} % cat /tmp/a /tmp/hi
hi
h4: {230} % lp -Plp /tmp/hi
h4: {231} % cat /tmp/a /tmp/hi
hi
hi

The lpr -X filter option allows us to specify a user filter on the command line. We will use a simple example to show how this capability could be used in practice. Create the /tmp/pass file with the following contents, and give it executable permissions as shown below:

#!/bin/sh
# /tmp/pass file
echo LEADER
cat
echo TRAILER
exit 0

Execute the following commands to print the /tmp/hi file and observe the results:

h4: {232} % cp /dev/null /tmp/a
h4: {233} % lpr -P/tmp/a -X /tmp/pass /tmp/hi
h4: {234} % cat /tmp/a
LEADER
hi
TRAILER

As we see from the example, our filter has processed the input file and added the LEADER and TRAILER strings. In practice, the actual processing of the input job would be far more elaborate, and may do such things as incorporate files or other material available only on the local system. We can also use a printcap entry:

lp:direct:lp=/tmp/a:filter=/tmp/pass

h4: {235} % cp /dev/null /tmp/a
h4: {236} % lpr -Plp /tmp/hi
h4: {237} % cat /tmp/a
LEADER
hi
TRAILER

4.14. Moving Jobs From Queue to Queue and Redirecting Queues

The lpc move command is used to move jobs in one queue to another queue on an individual basis, while the lpc redirect command redirects all incoming jobs to a new queue. Edit the printcap file so it has contents indicated below, use checkpc -f to check the printcap, and then use lpc reread to restart the lpd server.

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

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

The lpc redirect fromqueue toqueue will cause all incoming jobs to be redirected to the specified queue. You can execute the following commands and observe the results.

h4: {243} % lpc redirect lp lp2
Printer: lp@h4
forwarding to 'lp2'
lp@h4.private: redirected
h4: {244} % lpq -a
Printer: lp@h4  (printing disabled) (redirect lp2)
 Queue: no printable jobs in queue
Printer: lp2@h4  (printing disabled)
 Queue: no printable jobs in queue
h4: {245} % lpr /tmp/hi
h4: {246} % lpq -a
Printer: lp@h4  (printing disabled) (redirect lp2)
 Queue: no printable jobs in queue
 Status: job 'papowell@h4+935' removed at 09:08:21.410
Printer: lp2@h4  (printing disabled)
 Queue: 1 printable job
 Server: no server active
 Rank   Owner/ID           Class Job Files        Size Time
1      papowell@h4+935       A   935 /tmp/hi         3 09:08:21

To turn redirection off, use lpc redirect queue off as shown in the example below:

h4: {247} % lpc redirect lp off
Printer: lp@h4
forwarding off
h4: {248} % lpq
Printer: lp@h4  (printing disabled)
 Queue: no printable jobs in queue
 Status: job 'papowell@h4+935' removed at 09:08:21.410

4.15. Print Job Classes, User Requested Job Priority, and Form Support

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

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

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

Edit the printcap file so it has contents indicated below, use checkpc -f to check the printcap, and then use lpc reread to restart the lpd server.

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

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

We can change the print queue class at any time, and then new class will then control what jobs are printed. To disable the class selection, use the lpc class queue off command.

h4: {255} % lpc class lp off
Printer: lp@h4
all classes printed
lp@h4.private: class updated

4.16. Holding and Releasing Jobs

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

Edit the printcap file so it has contents indicated below, use checkpc -f to check the printcap, and then use lpc reread to restart the lpd server.

lp:force_localhost
lp:server
  :sd=/var/spool/lpd/%P
  :lp=lp2@localhost
lp2:force_localhost
lp2:server
  :sd=/var/spool/lpd/%P
  :lp=/tmp/lp2

Execute the following commands to print the /tmp/hi file and observe the results:

h4: {256} % lpc holdall lp
Printer: lp@h4
lp@h4.private: holdall on
h4: {257} % lpq
Printer: lp@h4  (holdall)
 Queue: no printable jobs in queue
h4: {258} % lpr /tmp/hi
h4: {259} % lpq
Printer: lp@h4  (holdall)
 Queue: no printable jobs in queue
 Holding: 1 held jobs in queue
 Server: no server active
 Rank   Owner/ID           Class Job Files        Size Time
hold   papowell@h4+213       A   213 /tmp/hi         3 09:45:05
h4: {260} % lpc release lp 213
Printer: lp@h4
lp: selected 'papowell@h4+213'
lp@h4.private: started
h4: {261} % lpq
Printer: lp@h4  (holdall)
 Queue: no printable jobs in queue
 Status: job 'papowell@h4+213' removed at 09:45:22.570

The lpc holdall command causes all jobs to be held. We spool a job, and then use the lpc release command to release the selected job. We disable the holdall operation using the lpc noholdall command.

h4: {262} % lpc noholdall lp
Printer: lp@h4
lp@h4.private: holdall off

You can also use the lpc hold command to select individual jobs in a spool queue to be held. This command is useful if there is a set of jobs which require special handling or printing at a later date. The following example shows how this command is used. We use the lpc stop and lpc start commands to simulate the normal delays in print spooling operations.

h4: {263} % lpc stop lp
Printer: lp@h4
lp@h4.private: stopped
h4: {264} % lpq
Printer: lp@h4  (printing disabled)
 Queue: no printable jobs in queue
 Status: job 'papowell@h4+495' removed at 10:10:50.629
h4: {265} % lpr /tmp/hi
h4: {266} % lpr /tmp/hi
h4: {267} % lpq
Printer: lp@h4  (printing disabled)
 Queue: 2 printable jobs
 Server: no server active
 Rank   Owner/ID           Class Job Files        Size Time
1      papowell@h4+459       A   459 /tmp/hi         3 10:40:32
2      papowell@h4+461       A   461 /tmp/hi         3 10:40:34
h4: {268} % lpc hold lp 459
Printer: lp@h4
lp: selected 'papowell@h4+459'
lp@h4.private: updated
h4: {269} % lpq
Printer: lp@h4  (printing disabled)
 Queue: 1 printable job
 Holding: 1 held jobs in queue
 Server: no server active
 Rank   Owner/ID           Class Job Files        Size Time
1      papowell@h4+461       A   461 /tmp/hi         3 10:40:34
hold   papowell@h4+459       A   459 /tmp/hi         3 10:40:32

In the next example we show how to use the lpc hold command to select and hold an individual job. Then we start the queue and see what happens:

h4: {270} % lpc start
Printer: lp@h4
lp@h4.private: started
h4: {271} % lpq
Printer: lp@h4
 Queue: no printable jobs in queue
 Holding: 1 held jobs in queue
 Server: no server active
 Status: job 'papowell@h4+461' removed at 10:41:24.873
 Rank   Owner/ID           Class Job Files        Size Time
hold   papowell@h4+459       A   459 /tmp/hi         3 10:40:32
h4: {272} % lpc release lp 459
Printer: lp@h4
lp: selected 'papowell@h4+459'
lp@h4.private: started
h4: {273} % lpq
Printer: lp@h4
 Queue: no printable jobs in queue
 Status: job 'papowell@h4+459' removed at 10:41:39.457

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

The printcap :ah (autohold) option has the same effect as the lpc holdall command but its actions cannot be disabled by the lpc noholdall command.


4.17. Load Balance Queues and Printer Pools

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

Edit the printcap file so it have the contents indicated below, create the /tmp/lp2 and /tmp/lp3 files with 0777 permissions, use checkpc -f to check the printcap, and then use lpc reread to restart the lpd server.

# printcap
lp:force_localhost
lp:server
  :sd=/var/spool/lpd/%P
  :sv=lp2,lp3
lp2:force_localhost
lp2:server
  :ss=lp
  :sd=/var/spool/lpd/%P
  :lp=/tmp/lp2
lp3:force_localhost
lp3:server
  :ss=lp
  :sd=/var/spool/lpd/%P
  :lp=/tmp/lp2

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

Execute the following commands to print the /tmp/hi file and observe the results:

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

The first lpq command shows how the status is displayed for a load balance queue - the queue and its server queues are shown as well. Next, we use lpr to print a job (job id papowell@h4+62). We then use a couple of lpq commands to see how the job is first sent to the lp queue, which then forwards it to the lp3 queue, which then processes it and removes it. (For purposes of demonstration we have artificially slowed down the operation of the load balance queue so that the jobs will remain in the queue for sufficient time for us to display their status.) We can send another job to the load balance queue:

h4: {278} % lpr /tmp/hi
h4: {279} % lpq
Printer: lp@h4  (subservers lp2, lp3)
 Queue: no printable jobs in queue
 Status: no more jobs to process in load balance queue at 07:37:17.953
Server Printer: lp2@h4  (serving lp)
 Queue: no printable jobs in queue
 Status: job 'papowell@h4+89' removed at 07:37:15.936
Server Printer: lp3@h4  (serving lp)
 Queue: no printable jobs in queue
 Status: job 'papowell@h4+81' removed at 07:36:40.116

This time we see that the job was put in lp2. The normal load balance queue operation is to use the server queues in round robin order.

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

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

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

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

Edit the printcap file so it have the contents indicated below, create the /tmp/lp2 and /tmp/lp3 files with 0777 permissions. Then create the /tmp/chooser.script with the contents indicated below, and give it 0755 (executable) permissions. Make sure that the path to the head program used in chooser.script is correct. Use checkpc -f to check the printcap, and then use lpc reread to restart the lpd server.

# printcap
lp:force_localhost
lp:server
  :sd=/var/spool/lpd/%P
  :sv=lp2,lp3
  :chooser=/tmp/chooser.script
lp2:force_localhost
lp2:server
  :ss=lp
  :sd=/var/spool/lpd/%P
  :lp=/tmp/lp2
lp3:force_localhost
lp3:server
  :ss=lp
  :sd=/var/spool/lpd/%P
  :lp=/tmp/lp2

# /tmp/chooser.script

#!/bin/sh
echo CHOOSER $0 $@ >>/tmp/chooser
set >>/tmp/chooser
/usr/bin/head -1
exit 0

Now run the following commands:

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

As you see from the example above, the CHOOSER selected lp3 for use. Let us look at the /tmp/chooser file and see how the chooser.script program was run:

CHOOSER -Apapowell@h4+113 -CA -D2000-06-01-14:02:13.313 -Hh4.private \
   -J/tmp/hi -Lpapowell -Plp -Qlp -aacct -b3 -d/var/tmp/LPD/lp \
   -hh4.private -j113 -kcfA113h4.private -l66 -npapowell -sstatus \
   -t2000-06-01-14:02:13.379 -w80 -x0 -y0 acct
USER=papowell
LD_LIBRARY_PATH=/lib:/usr/lib:/usr/5lib:/usr/ucblib
HOME=/home/papowell
PRINTCAP_ENTRY=lp
 :chooser=/var/tmp/LPD/chooser
 :lp=/tmp/lp
 :sd=/var/tmp/LPD/lp
 :server
 :sv=lp2,lp3

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

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

One of the limitations of using the :chooser program is that you may have a high overhead associated with running the program. The LPRng software provides support for linking in a user provided routine that does the same thing as the :chooser program. This routine has the following API or interface:

Printcap Option: chooser_routine
   chooser_routine@  - default - do not use chooser routine
   chooser_routine   - use chooser routine

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

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

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


4.18. Routing Jobs To Print Queues

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

Edit the printcap file so it have the contents indicated below, create the /tmp/lp2 and /tmp/lp3 files with 0777 permissions. Create the /tmp/router.script with the contents indicated below, and give it 0755 (executable) permissions. Use checkpc -f to check the printcap, and then use lpc reread to restart the lpd server.

# printcap
lp:force_localhost
lp:server
  :lp=/dev/null
  :sd=/var/spool/lpd/%P
  :router=/tmp/router.script
lp2:force_localhost
lp2:server
  :sd=/var/spool/lpd/%P
  :lp=/tmp/lp2
lp3:force_localhost
lp3:server
  :sd=/var/spool/lpd/%P
  :lp=/tmp/lp2

# /tmp/router.script

#!/bin/sh
/bin/cat <<EOF
dest lp2
copies 2
Cred
end
dest lp3
end
EOF
exit 0

The router.script will write the routing information to its STDOUT. For our example, we want the destination lp2 to get two copies of the job and we want to change the class to red. Now run the following commands:

h4: {282} % lpc stop all
Printer: lp@h4
lp@h4.private: stopped
Printer: lp2@h4
lp2@h4.private: stopped
Printer: lp3@h4
lp3@h4.private: stopped
h4: {283} % lpq
Printer: lp@h4 (dest lp@localhost) (printing disabled) (dest lp2, lp3)
 Queue: no printable jobs in queue
Printer: lp2@h4  (printing disabled)
 Queue: no printable jobs in queue
Printer: lp3@h4  (printing disabled)
 Queue: no printable jobs in queue
h4: {284} % lpr /tmp/hi
h4: {285} % lpq
Printer: lp@h4 (dest lp@localhost) (printing disabled) (dest lp2, lp3)
 Queue: 1 printable job
 Server: no server active
 Rank   Owner/ID           Class Job Files        Size Time
1      papowell@h4+235       A   235 /tmp/hi         3 16:14:22
 -          papowell@h4+235.1       ->lp2 <cpy 0/2>
 -          papowell@h4+235.2       ->lp3
Printer: lp2@h4  (printing disabled)
 Queue: no printable jobs in queue
Printer: lp3@h4  (printing disabled)
 Queue: no printable jobs in queue

The status reported for the spooled job indicates that the job is routed to lp2, and that two copies will be sent. The :destinations option in the printcap entry causes lpq to display the contents of the specified queues. Now execute the following commands:

h4: {286} % lpc start
Printer: lp@h4
lp@h4.private: started
h4: {287} % lpq
Printer: lp@h4 (dest lp@localhost) (destinations lp2, lp3)
 Queue: no printable jobs in queue
 Status: job 'papowell@h4+235' removed at 16:14:37.491
Printer: lp2@h4  (printing disabled)
 Queue: 2 printable jobs
 Server: no server active
 Rank   Owner/ID           Class Job Files        Size Time
1      papowell@h4+235.1C1   A   235 /tmp/hi         3 16:14:36
2      papowell@h4+235.1C2   A   236 /tmp/hi         3 16:14:37
Printer: lp3@h4  (printing disabled)
 Queue: 1 printable job
 Server: no server active
 Rank   Owner/ID           Class Job Files        Size Time
1      papowell@h4+235.2     A   237 /tmp/hi         3 16:14:37
h4: {288} % more /var/spool/lpd/lp2/cfA235*
Hh4.private
Ppapowell
J/tmp/hi
Cred
Lpapowell
Apapowell@h4+235.1C1
D2000-06-01-16:03:25.237
Qlp
N/tmp/hi
fdfA235h4.private
UdfA235h4.private

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

For details about all of the capabilities of the routing filter, see Dynamic Routing. Here is a summary of the information that the routing filter can put into the routing file.

dest queue

Route this job to queue. The queue@host form will transfer the job to the queue on the named host.

copies N

Send N copies of this job to the destination.

priority C

Set the job priority letter to C, where C is a single upper case letter.

Cvalue

Set the control file line starting with C to Cvalue.

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


4.19. Job Options and the Z Control File Entry

Many printers have special capabilities such as printing in landscape mode, duplex printing, binding, or stapling. These capabilities are usually invoked or enabled by the print spooler sending special printer control commands to the printer based on values it finds in the control file. The LPRng print spooler uses the Z line in the control file to specify these options, while other print spoolers such as the Sun Microsystems Solaris lp system pass them on the S line.

Job formatting options are specified using the lpr -Z option. The lpr program concatenates the -Z options and puts them in the control file as a single Z line. For example:

h4: {289} % lpc stop
Printer: lp@h4
lp@h4.private: stopped
h4: {290} % lpr -Zthis -Zthat /tmp/hi
h4: {291} % cat /var/spool/lp/cf*
Hh4.private
Ppapowell
J/tmp/hi
CA
Lpapowell
Zthis,that
Apapowell@h4+115
D2000-05-05-10:05:41.351
Qlp
N/tmp/hi
fdfA115h4.private
UdfA115h4.private

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

  • -Zlandscape -Zportrait - select landscape or portrait orientation.

  • -Zduplex -Zsimplex - select duplex (both sides of a page) or simplex (single side of a page) printing.

  • -Zletter -Zlegal -Zledger -Za4 -Za5 -Zenvelope -Ztransparency - select a paper size

  • -Zinupper -Zinmiddle -Zinlower - select input media from the appropriate input tray

  • -Zmanual - select input from the manual feed


4.19.1. Setting Job Options Using the Printcap

An alternative to this method of using lpr and the -Z option is to define a set of spool queues which will put the necessary options into the job control file. This can be done by the lpr program when the job is generated, or by the lpd spooler when the job is processed. The The options specified by the :prefix_z, :append_z, and :delete_z are prefixed, appended, or deleted from the current set of Z control file options by the lpr program when the job is submitted and they are specified in the printcap for the queue, or by the lpd spooler when the job is submitted to the queue. We can use this capapbility to configure print queues to a desired set of Z options into the control file. For example:

landscape:lp=%P@server
landscape:server:tc=.common
  :lp=raw@server:append_z=landscape:delete_z=portrait
raw:server:tc=.common:lp=....
  :filter=/usr/local/libexec/filters/ifhp
.common:sd=/var/spool/lpd/%P

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


4.19.2. Converting SystemV Options to LPRng Options

On some SystemV lp print spoolers, the lp -o option, puts the option information into the control file S line, and on other systems on the puts the option information into the control file O line. To convert these options to LPRng Z options use the :prefix_option_to_option=from,from... to facility to prefix the from control file lines to the to control file line. For example:

# System V to LPRng - S and O to Z options
convert:server:tc=.common
  :lp=raw@server:prefix_option_to_option=S,O Z
# LPRng to System V O options
convert:server:tc=.common
    :lp=raw@server:prefix_option_to_option=Z O

4.19.3. Selecting a Single Option - Muliple Queues

Here is an example of how you can set up queues that will append the appropriate Z option to select landscape mode, do duplex printing, or select legal or ledger size paper:

landscape:lp=%P@server
landscape:server:tc=.common
    :lp=raw@server:append_z=landscape
duplex:lp=%P@server
duplex:server:tc=.common
    :lp=raw@server:append_z=duplex
ledger:lp=%P@server
ledger:server:tc=.common
    :lp=raw@server:append_z=ledger
legal:lp=%P@server
legal:server:tc=.common
    :lp=raw@server:append_z=legal
raw:server:tc=.common:lp=....
  :filter=/usr/local/libexec/filters/ifhp
.common:sd=/var/spool/lpd/%P

The problem with this method is that for each option we need to define a queue whose only purpose is to append the appropriate option and then forward this to the main print queue.


4.19.4. Selecting Multiple Options - Single Queue

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

The solution to this problem originated with the apsfilter program written by Andreas Klemm and Thomas Bueschgens. They made the observeration that if we know the name of the print queue then we can use this name to select options for the printer. The LPRng provides this functionality by using wildcard queues and editing or filtering the control file when the job is submitted to the spool queue.

The incoming_control_filter=/path filter processes the incoming job control or job ticket file. It can be used to values in the job ticket of incoming jobs. It reads the control file on its STDIN and writes the new or modified values on STDOUT. A 0 exit code value causes normal processing of the job, JHOLD will hold the job, and any other value will cause the job to be discarded. The incoming_control_filter filter can modify priority or other job options, including using the move= field to cause a job to be redirect to another spool queue or printer. Only changes to the jobs options need to be generated by the incoming_control_filter=/path filter.

The input and output have the format:

INPUT:
 X<option>       - option from control file
 X=<option>      - alternative option format
 key=<option>    - spooler option
 X==<option>     - option starting with = sign

OUTPUT:
 X                     - delete option or value
 X=                    - delete option or value
 X<option>       - set option value
 X=<option>      - set option value
 key=<option>    - set value of 'key' to option
 key=                  - delete option or value

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

The following shows how we can set up a single queue that will allow various combinations of options to be selected by the format of the queue name:

# for clients
pr|pr_*:lp=%Q@server
# for server
pr|pr_*:server
  :tc=.common:lp=....
  :incoming_control_filter=/usr/local/libexec/filters/update_z
  :filter=/usr/local/libexec/filters/ifhp
.common:sd=/var/spool/lpd/%P

The pr and pr_* aliases will match printer pr all print queue names starting with pr_. We can then use various suffixes to select job options. The following filter program uses the _landscape, _legal, and _ledger suffixes to set the corresponding option in the Z file. This program and other are available in the LPRng distribution in the UTILS directory. You should note that additional options can be specified as desired.

#
#!/usr/bin/perl
# update_z script:
# Determine the options according to the format of the queue name
#  Inspired by the psfilter code of Andreas Klemm
#  and Thomas Bueschgens 
# First, get command line arguments
#
use Getopt::Std;
my(%args,$Q,$Zopts,@file);
getopts(
"A:B:C:D:E:F:G:H:I:J:K:L:M:N:O:P:Q:R:S:T:U:V:W:X:Y:Z:" .
"a:b:cd:e:f:g:h:i:j:k:l:m:n:o:p:q:r:s:t:u:v:w:x:y:z:",
\%args );
# read stdin
@file = <STDIN>;
$Zopts = "";
# first use command line Queue name
$Q = $args{"Q"};
if( not $Q and (($Q) = grep(/^Q/,@file)) ){
   # next use control file Queue name
    chomp $Q if $Q;
}
# now we split up the name and use as parameters for Z options
while( $Q =~ /_([^_]+)/g ){
    # you can add them or test and then add them
    if( $1 eq "landscape"
        or $1 eq "legal"
        or $1 eq "ledger" ){
        $Zopts .= ",$1"
    }
}
if( $Zopts ){
    # remove leading comma
    $Zopts = substr( $Zopts, 1 );
    #replace or prefix Z options
    if( not (grep { s/$/,$Zopts/ if /^Z/; } @file) ){
        print "Z" . $Zopts . "\n";
    }
}
print @file if( @file );
exit 0

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

Example output:

Z=over,landscape,ledger
Q=lp_landscape_ledger

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

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

4.20. Interfacing to Non-LPRng Spoolers

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

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

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

You might run into some really unusual implementations where the control file N (file name) information must come after the control file name. This is forced by the :nline_after_file option.

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


4.21. Debugging, Tracing, and Log Files

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

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

First, we will look at how you can use the debugging facilities for the clients. Enter the following commands:

h4: {292} % lpr -D=
debug usage: -D [ num | key=num | key=str | flag | flag@ | flag+N ]*
  keys recognized: network[+N,@], database[+N,@], lpr[+N,@],
   lpc[+N,@], lprm[+N,@], lpq[+N,@], test=num, job=num, log[+N,@]
h4: {293} % lpr -V /tmp/hi
LPRng-3.7.2, Copyright 1988-2000 Patrick Powell, <papowell@lprng.com>
sending job 'papowell@h4+981' to lp@localhost
connecting to 'localhost', attempt 1
connected to 'localhost'
requesting printer lp@localhost
sending control file 'cfA981h4.private' to lp@localhost
completed sending 'cfA981h4.private' to lp@localhost
sending data file 'dfA981h4.private' to lp@localhost
completed sending 'dfA981h4.private' to lp@localhost
done job 'papowell@h4+981' transfer to lp@localhost
h4: {294} % lpr -D1 /tmp/hi
09:38:08.707 h4 [13991] lpr  Get_printer: original printer '<NULL>'
09:38:08.708 h4 [13991] lpr  Get_all_printcap_entries: starting
09:38:08.708 h4 [13991] lpr  Select_pc_info: looking for 'all', depth 0
09:38:08.708 h4 [13991] lpr  Select_pc_info: returning '<NULL>'
09:38:08.708 h4 [13991] lpr  Select_pc_info: looking for '*', depth 0
09:38:08.708 h4 [13991] lpr  Select_pc_info: returning '<NULL>'
09:38:08.708 h4 [13991] lpr  Dump_line_list: Get_all_printcap_entries
...

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

The network and database debug flags turn on debugging for the network facilities and the database (lpd.conf, printcap, and lpd.perms) lookups. Lets see what lpr -Dnetwork shows us:

Ch4: {295} % lpr -Dnetwork /tmp/hi
lp: getconnection: START host localhost, timeout 10, connection_type 1
lp: getconnection: fqdn found localhost.private, h_addr_list count 1
lp: Link_dest_port_num: port 2000 = 2000
lp: getconnection: sock 3, src ip 127.0.0.1, port 65209
lp: getconnection: dest ip 127.0.0.1, port 2000
lp: getconnection: connection to 'localhost' sock 3, errmsg 'No Error'
lp: Link_send: host 'localhost' socket 3, timeout 6000
lp: Link_send: str '^Blp
', count 4, ack 0x80447a0
lp: Link_send: final status NO ERROR
lp: Link_send: host 'localhost' socket 3, timeout 6000
lp: Link_send: str '^B135 cfA276h4.private
', count 22, ack 0x8044370
lp: Link_send: final status NO ERROR
lp: Link_send: host 'localhost' socket 3, timeout 6000
lp: Link_send: str 'Hh4.private
Ppapowell
J/tmp/hi
CA
Lpapowell
Apapowell@h4+276
D2000-06-02-09:44:52.369
Qlp
N/tmp/hi
fdfA276h4.private
UdfA276h4.private
', count 136, ack 0x8044370
lp: Link_send: final status NO ERROR
lp: Link_send: host 'localhost' socket 3, timeout 6000
lp: Link_send: str '^C3 dfA276h4.private
', count 20, ack 0x8044310
lp: Link_send: final status NO ERROR
lp: Link_send: host 'localhost' socket 3, timeout 6000
lp: Link_send: str '', count 1, ack 0x8044310
lp: Link_send: final status NO ERROR

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

If you need to trace the activities of the lpd server, it becomes a little more complex. The lpd server has a single listening process that forks and creates individual processes to handle incoming requests. Debug or diagnose the main process actions by using lpd -D.... You may also want to use lpd -F to keep the server in the foreground so you can kill it off easily. Needless to say, you should also redirect the STDERR and STDOUT so that it goes to a file so that you can examine the voluminous records at your leisure. The following shows a typical main lpd process debugging session using the C Shell.

h4: {296} % lpd -F -D1 >&/tmp/logfile &
[2] 14299
h4: {297} % tail -f /tmp/logfile
2000-06-02-09:53:39.716 h4 [1200] Waiting Read_server_status: \
                         select status 1
2000-06-02-09:53:39.716 h4 [1200] Waiting Read_server_status: \
                         read status 1
2000-06-02-09:53:39.716 h4 [1200] Waiting Dump_line_list: \
    Read_server_status - input - 0x8047980, count 0, max 0, list 0x0
2000-06-02-09:53:39.716 h4 [1200] Waiting Read_server_status: \
                         select status 0
2000-06-02-09:53:39.716 h4 [1200] Waiting lpd: LOOP START
2000-06-02-09:53:39.716 h4 [1200] Waiting Get_max_servers: \ 
                         getrlimit returns 64
2000-06-02-09:53:39.716 h4 [1200] Waiting Get_max_servers: \
                         returning 32
2000-06-02-09:53:39.716 h4 [1200] Waiting lpd: \
                         max_servers 32, active 0
2000-06-02-09:53:39.716 h4 [1200] Waiting lpd: \
                         starting select timeout 'yes'

^C
h4: {298} % jobs
[1]  - Running                lpd -F -D1 >& /tmp/logfile
h4: {299} % kill %1

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

This method is extremely difficult to use, as all of the output produced by the server and its subprocesses is sent to a single output file. If we want to debug the actions concerning a single queue, then we can use the queue log file and lpc debug command instead. The following options control debugging of an individual print queue.

lf=log

The log file for the queue. The queue server process will open this file and place debugging information into this file.

max_log_file_size=nnn

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

min_log_file_size=nnn

When the log file is truncated only the the last nnn K bytes are retained.

db=options

These are debugging options for the spool queue. These options are permanent and cannot be changed by using the lpc debug facility.

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

Edit the printcap file so it have the contents indicated below, create the /tmp/lp and /tmp/lp2 files with 0777 permissions. Use checkpc -f to check the printcap, and then use lpc reread to restart the lpd server.

# printcap
lp:force_localhost
lp:server
  :lp=/dev/null
  :sd=/var/spool/lpd/%P
  :lf=log
lp2:force_localhost
lp2:server
  :sd=/var/spool/lpd/%P
  :lp=/tmp/lp2
  :lf=log

Now execute the following commands:

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

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

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

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

The log option is used to test various logging facilities and is usually not used for general purpose debugging.


Chapter 5. LPRng Clients - lpr, lprm, lpq, lpc, lpstat

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

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


5.1. Printer and Server Information

Options used:

  • PRINTER, LPDEST, NPRINTER NGPRINTER Environment variables

  • force_localhost FLAG force clients to send requests to localhost

  • require_explicit_q FLAG require queue to be specified

When an LPRng client such as lpr, lpq, lprm, or lpc needs to communicate with a print server, the only information they normally need is:

  1. The remote printer (:rp) value to be used in requests to the lpd print server. This is sometimes referred to as the printer or print queue name.

  2. The remote server (:rm) which is the The IP address or hostname of the print server.

  3. The original queue name specified by the user which may be used as part of the job information sent to the print server.

LPRng has several ways to specify the printer queue and server information.


5.2. Command Line -Pprinter@host

The -P printer@host literal explicitly provides a printer and server value. This is equivalent to the printcap entry shown below:

lpr -Plaser@10.0.0.1
  equivalent to printcap entry:
    laser:lp=laser@10.0.0.1
lpq -Plp@myserver
  equivalent to printcap entry:
    lp:lp=lp@myserver

5.3. Command Line -Pprinter

Use the printcap entry with the name or alias printer and use the information in that printcap entry. Example:

lpr -Plp
  look up printcap entry for 'lp'

5.4. PRINTER, LPDEST, NPRINTER, and NGPRINTER Environment Variables

If no command line option is specified, the LPRng clients will use the first defined PRINTER, LPDEST, NPRINTER, or NGPRINTER environment variable value and will use the value as though specified as a -P$PRINTER command line literal. If the value has the form -Pprinter@host the print queue will be printer on server host and not consult the printcap database. If the value has the form printer then the printcap will be searched for a printer printcap entry. For example:

export PRINTER=laser@10.0.0.1; lpr
  equivalent to printcap entry:
    laser:lp=laser@10.0.0.1
export PRINTER=pr; lpr
  look up printcap entry for 'lp'

5.5. Wildcard Printcap Entry

If you specify a printcap name or alias as * (wildcard), then if the LPRng system cannot find a printcap with the exact name then the first matching wildcard will be used. For example:

pr|*:rm=server
  lpr -Ppr2 will match, and result in a printcap entry
    pr|pr2:rm=server

*|pr:rm=server
  lpr -Ppr2 will match, and result in a printcap entry
    pr2|pr:rm=server

5.6. First Printcap Entry

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


5.7. Default Printer and Server Host

Options used:

  • default_remotehost=default rm (remote host)

  • default_printer=default rp (remote printer)

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

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


5.8. Force Connection to Localhost

Options used:

  • force_localhost FLAG force localhost to be remote host

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

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

This problem was solved by providing a force_localhost option in the configuration, and setting the default value to 1 or TRUE. When this option is TRUE, then all LPRng clients will connect to the server on the localhost, unless they use the lpr -Pserver@host command line form. If lightweight operation is wanted, the administrator can either compile the LPRng software with the appropriate value or can explicitly set the force_localhost@ flag.

# default:
lp:lp=lp@10.0.0.1:...
  lpr -Plp ->
    lp:lp=lp@localhost:...
  lpr -Plp@10.0.0.1 ->
    lp:lp=lp@10.0.0.1  (no other options)

lp:lp=lp@10.0.0.1:force_localhost@:...
  lpr -Plp ->
    lp:lp=lp@10.0.0.1:...

To disable at compile time:
  configure --disable-force_localhost

5.9. User Identification

Options used:

  • allow_user_setting=privileged users

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

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

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


Chapter 6. lpr - Job Spooler Program

The lpr client program is used to submit a job to a print spooler. It does this by collecting information about the job, putting it in a control file, and then sending the control file and files to be printed to the print server. The lpr command line options are used to control or specify the values placed in the control file and how the job is to be transferred to the remote host. In addition, there are printcap or configuration level options that provide a further degree of administrative control over additional facilities. You can get the currently supported command line options by using the following command:

h4: {305} % lpr -=
lpr: Illegal option '='
 usage summary: lpr [-Pprinter[@host]] [-A] [-B] [-Cclass] [-Fformat]
   [-Jinfo] [-(K|#)copies] [-Q] [-Raccountname]  [-Ttitle]
   [-Uuser[@host]] [-V] [-Zoptions] [-b] [-m mailaddr] [-h]
   [-i indent] [-l] [-w num ] [-r] [-Ddebugopt ] [ filenames ...  ]
 -A          - use authentication specified by AUTH environment variable
 -B          - filter job before sending
 -C class    - job class
 -D debugopt - debugging flags
 -F format   - job format
   -b,-l        - binary or literal format
    c,d,f,g,l,m,p,t,v are also format options
 -J info     - banner and job information
 -K copies, -# copies   - number of copies
 -P printer[@host] - printer on host
 -Q          - put 'queuename' in control file
 -Raccntname - accounting information
 -T title    - title for 'pr' (-p) formatting
 -U username - override user name (restricted)
 -V          - Verbose information during spooling
 -Z options  - options to pass to filter
 -h          - no header or banner page
 -i indent   - indentation
 -m mailaddr - mail final status to mailaddr
 -r          - remove files after spooling
 -w width    - width to use
 PRINTER, NPRINTER environment variables set default printer.

If you are interested in the exact details of the job transfer, control file, and data file formats, please see RFC 1179 - Line Printer Daemon Protocol for the exact details.


6.1. Job Format Options

Options used:

  • default_format=default print job format (f)

  • fx=supported formats for printing

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

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

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


6.2. Job Pretty Printing, Banners, Priority, and Accounting

Options used:

  • ab FLAG always print banner

  • sh FLAG suppress header (banner)

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

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

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

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


6.3. Job Class and Priority

Options used:

  • break_classname_priority_link FLAG classname does not set priority

  • default_priority=default priority

  • ignore_requested_user_priority FLAG ignore requested user priority

The -Cclass information is used placed in the control file; the default class and priority is set by the default_priority option (default A). The first letter of the class information is uppercased and then used as the job priority.

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


6.4. Job Copies and Job Size

Options used:

  • mc=maximum number of copies

  • mx=maximum job size (Kbytes)

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


6.5. Job Completion Notification Requested

Options used:

  • allow_user_logging FLAG users can send status

  • lpr_bsd FLAG lpr -m acts as legacy BSD flag

The lpr -m mailaddr option will put the specified mailaddr value into the control file. How this is processed is left to the particular server implementation. See the Abnormal Termination section for details.

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

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


6.6. Remove Files After Spooling

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

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


6.7. The -Z Passthrough to Filter Options

Options used:

  • append_z=Append values to Z options list

  • prefix_z=Prefix values to Z options list

  • remove_z=Remove values from Z options list

  • prefix_s_to_z FLAG Prefix control file S to Z information

  • prefix_s_to_z FLAG Prefix control file Z to S information

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

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

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

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


6.8. Record Queue Name in Control File

Options used:

  • qq FLAG Insert queue name into control file

  • force_queuename=Queuename to be used

The :qq use queuename option tells LPRng to record the queue name that a job was originally queued to in the control file as the Q information. The force_queuename=... entry forces the queue name to be the specified value. For example, the following printcap entry and lpr commands will result in a filter being passed the arguments shown below.

#printcap
  pr1_landscape|pr1_portrait|pr_raw
   :filter=/.../filter
   :lp=/dev/pr
   :qq

lpr -Ppr1_landscape      lpr -Ppr1_portrait
  control file -
    Qpr1_landscape          Qpr1_portrait

filter command line:
  ... -Qpr1_landscape     ... -Qpr1_portrait

The force_queuename can be used for force a specific value into the control file Q information.

john|tom|frank
  :filter=/.../filter
  :lp=/dev/pr
  :force_queuename=office

lpr -Pjohn
  control file -
   Qoffice

filter command line:
  ... -Qoffice

6.9. Check For Nonprintable File

Options used:

  • check_for_nonprintable FLAG lpr checks for non-printable file

  • ml=minimum number of printable characters

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


6.10. Job Filtering By LPR

Options used:

  • lpr_bounce FLAG lpr does filtering

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

# Simple example of an lpr_bounce entry
bounce
  :lpr_bounce
  :lp=lp@remote
  :filter=/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.


6.11. Restrict Queue Use to Group Members

Options used:

  • 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 check is done by both the lpr client and the lpd server if the option is present.


6.12. Fixing Bad Control Files and Metacharacters

Options used:

  • safe_chars=additional safe characters for control file

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.

By default, LPRng will brutally convert a non-conforming RFC1179 control file into one that is acceptable to most, if not all, existing RFC1179 implementations. In order to prevent problems with LPRng ruthlessly purges all characters but upper and lower case letters, spaces, tabs, and -_.@/:()=,+-% from the control file, replacing suspicious characters with underscore (_). In addition, LPRng will ruthlessly regenerate control file entries and data file names so that they are compliant with all known RFC1179 implementations.

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 by underscore (_) may cause other applications which use the control file information 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.


6.13. Minimum Spool Queue Free Space

Options used:

  • minfree=Size in Kbytes

If this value is non-zero, then the lpd receiving server checks to see that there is the specified number of Kbytes of file space available before accepting a job. If there is not enough space then the job is rejected.


6.14. FQDN Host Information

Options used:

  • force_fqdn_hostname FLAG Update control file with FQDN name

  • force_ipaddr_hostname FLAG Update control file with IP address of remote host

  • use_shorthost FLAG short control and data file names

Some lpr clients 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 the control file. This option will assume that the domain is where the connection originated from.

Similarly, the force_ipaddr_hostname flag will cause lpd to put a FQDN host name in the control file.

Some systems cannot handle FQDN hostnames in control and data file formats. The use_shorthost option will send control and data files with very short names to the remote print server.


Chapter 7. lpq - Status Monitoring Program

The lpq program is the main method used to monitor queues. You can obtain the available options by using:

h4: {306} % lpq -=
lpq: Illegal option '='
usage: lpq [-aAclV] [-Ddebuglevel] [-Pprinter] [-tsleeptime]
  -a           - all printers
  -c           - clear screen before update
  -l           - increase (lengthen) detailed status information
                 additional l flags add more detail.
  -L           - maximum detailed status information
  -n linecount - linecount lines of detailed status information
  -Ddebuglevel - debug level
  -Pprinter    - specify printer
  -s           - short (summary) format
  -tsleeptime  - sleeptime between updates
  -V           - print version information

7.1. lpq Queue Selection (lpq -Pprinter, lpq -a)

If no queue is specified, then the default queue is used, with -Pprinter@host causing a direct connection to the named host. The -a literal selects all queues.


7.2. lpq Job Selection

Jobs are selected by providing a job number, or glob pattern that is matched to the user name and and job identifier. If multiple patterns are provided each is applied in turn against the jobs in a queue until all have been tried. If any one of the patterns match then the job status is displayed.

If no selection information is provided or the all pattern is provided then all jobs in the queue are displayed.


7.3. 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@h10.private) 0 jobs
t3@astart110  (forwarding to t3a@h10.private)
t3a@astart110  (forwarding to t2@h10.private)
t4@astart110  (subservers t5, t6)  0 jobs
t5@astart110  (serving t4) 0 jobs
t6@astart110  (serving t4) 0 jobs

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


7.4. lpq Long Format (lpq, 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@lprng110+202228663 A 10663 /tmp/hi     3 20:22:29
Printer: t2@astart110  'Test Printer 2' (routed/bounce to t1@h10.private)
 Queue: no printable jobs in queue
 Status: finished operations at 16:30:08
Printer: t3@astart110  (forwarding to t3a@h10.private)
Printer: t3a@astart110  (forwarding to t2@h10.private)
Printer: t4@astart110  (subservers t5, t6)
 Queue: no printable jobs in queue
 Status: finished operations at 09:44:06
Server Printer: t5@astart110  (serving t4)
 Queue: no printable jobs in queue
 Status: finished operations at 09:44:06
Server Printer: t6@astart110  (serving t4)
 Queue: no printable jobs in queue
 Status: finished operations at 09:10:00

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.


7.5. 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@lprng110+094352860' at 09:44:00
 Status: printing 'papowell@lprng110+094352860', \
            closing device at 09:44:00
 Status: printing 'papowell@lprng110+094352860', finished  at 09:44:00
 Status: subserver status 'JSUCC' for 'papowell@lprng110+094352860' \
            on attempt 1 at 09:44:00
 Status: finished operations at 09:44:00
 Job: papowell@lprng110+202228663 status= 1
 Job: papowell@lprng110+202228663 CONTROL=
 - Hh10.private
 - Ppapowell
 - J/tmp/hi
 - CA
 - Lpapowell
 - Apapowell@lprng110+202228663
 - Qt1
 - fdfA010663h10.private
 - N/tmp/hi
 - UdfA010663h10.private
 Job: papowell@lprng110+202228663 HOLDFILE=
 - active 0
 - done 0
 - hold 0
 - move 0
....

7.6. Job Taking Too Long - Stalled

Options used:

  • stalled_time=seconds after which to report a stalled active job

The stalled_time option is actually used by the lpd server to report that a job has been active more than the indicated time with no change in state. This is useful for spotting things such a printers that are out of paper, and so forth.


7.7. Configuring Format and Displayed Information

The following sections describe options that are used by the lpd server to control how it will return status information to a lpq request.


7.7.1. Display Class Information

Options used:

  • class_in_status FLAG 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.


7.7.2. Reverse Short and Long lpq Formats

Options used:

  • reverse_lpq_format= FLAG 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.


7.7.3. Status Line Length and Line Count

Options used:

  • return_short_status=return short lpq status for specified remote systems

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


7.7.4. lpq Status Format Determined by Requesting Host Address

Options used:

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

The force_lpq_status= specifies a list of formats and printers or IP addresses for which the lpd server will return status in the specified 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.


Chapter 8. lprm - Job Removal Program

The lprm program is used to remove jobs from a print queue. You can obtain the available options by using:

h4: {307} % lprm -=
lprm: Illegal option '='
 usage: lprm [-A] [-a | -Pprinter] [-Ddebuglevel] (jobid|user|'all')*
  -a           - all printers
  -A           - use authentication
  -Pprinter    - printer (default PRINTER environment variable)
  -Uuser       - impersonate this user (root or privileged user only)
  -Ddebuglevel - debug level
  -V           - show version information
  user           removes user jobs
  all            removes all jobs
  jobid          removes job number jobid
 Example:
    'lprm -Plp 30' removes job 30 on printer lp
    'lprm -a'      removes all your jobs on all printers
    'lprm -a all'  removes all jobs on all printers
  Note: lprm removes only jobs for which you have removal permission

8.1. lprm Queue Selection (lprm -Pprinter, lprm -a)

If no queue is specified, then the default queue is used, with -Pprinter@host causing a direct connection to the named host. The -a literal selects all queues.


8.2. lprm Job Selection

Jobs are selected by providing a job number, or glob pattern that is matched to the user name and and job identifier. If multiple patterns are provided each is applied in turn against the jobs in a queue until all have been tried. If any one of the patterns match then the job status is displayed.

If no selection information is provided then the user name performing the request is used as the selection pattern, and only the first job that matches is removed.

The all pattern will match all jobs in the queue.

The user must have permission to control the queue or to remove a selected job in order for job removal to succeed.


Chapter 9. lpc - Administration Program

The lpc command is the main way that the lpd server is controlled. Here is the help information displayed by the command:

h4: {308} % lpc -=
lpc: Illegal option '='
usage: lpc [-Ddebuglevel][-Pprinter][-Shost][-Uusername][-V] [command]
 with no command, reads from stdin
  -Ddebuglevel - debug level
  -Pprinter    - printer or printer@host
  -Shost       - connect to lpd server on host
  -Uuser       - identify command as coming from user
  -V           - increase information verbosity
 commands:
 active  (printer[@host])        - check for active server
 abort   (printer[@host] | all)  - stop server
 class   printer[@host] (class | off) - show/set class printing
 disable (printer[@host] | all)  - disable queueing
 debug   (printer[@host] | all) debugparms - set debug level for printer
 down    (printer[@host] | all)  - disable printing and queueing
 enable  (printer[@host] | all)  - enable queueing
 hold    (printer[@host] | all) (name[@host] | job | all)* - hold job
 holdall (printer[@host] | all)  - hold all jobs on
 kill    (printer[@host] | all)  - stop and restart server
 lpd     (printer[@host]) - get LPD PID
 lpq     (printer[@host] | all) (name[@host] | job | all)*   - run lpq
 lprm    (printer[@host] | all) (name[@host]|host|job| all)* - run lprm
 msg printer message text- set status message
 move printer (user|jobid)* target - move jobs to new queue
 noholdall (printer[@host] | all)- hold all jobs off
 printcap(printer[@host] | all)  - report printcap values
 quit                            - exit LPC
 redirect(printer[@host] | all) (printer@host | off )*     - redirect jobs
 redo    (printer[@host] | all) (name[@host] | job | all)* - redo jobs
 release (printer[@host] | all) (name[@host] | job | all)* - release jobs
 reread  (printer[@host])        - LPD reread database information
 start   (printer[@host] | all)  - start printing
 status  (printer[@host] | all)  - status of printers
 stop    (printer[@host] | all)  - stop  printing
 topq    (printer[@host] | all) (name[@host] | job | all)*  - reorder job
 up      (printer[@host] | all) - enable printing and queueing
   diagnostic:
      defaultq               - show default queue for LPD server
      defaults               - show default configuration values
      client  (printer | all) - client config and printcap information
      server (printer | all) - server config and printcap

Most of the lpc command line options are common to all LPRng Clients, with the exception of the -S server literal. This option allows the lpd host to be explicitly specified.

The lpc commands can be classified as informational, queue management, problem management, job scheduling, and diagnostic.


9.1. Informational Commands - status, flush, active, reread

The lpc status command displays the current status of various activities of interest to the system administrator. This information includes the process ID of the server and other processes.

During normal operation, when requested for job status information the lpd server will create this information and then save it in a status cache. When successive requests for the same information arrive, the cache is checked to see if the information is already in the cache and there have been no status changes. If this is the case, the cached status information is used. The lpc flush command will flush (delete) this cache information and cause the lpd server to regenerate it from the original job files.

The lpc active command connects to the print server and gets the Process ID of the lpd process. This is useful to determine if the lpd server is running on the print server.

The reread command connects to the lpd print server and requests that the server reread the printcap, lpd.conf, and lpd.perms database files.


9.2. Queue Management - enable, disable, up, down

The enable and disable commands enable and disable queuing or sending jobs to the print queue. The up command combines the enable and start commands while the down command combines the disable and stop commands.


9.3. Printing Management - start, stop, up, down

These commands are used to start and stop printing. The up combines start and enable, while down combines stop and disable.


9.4. Problem Management - abort, redo, kill

These commands are usually used when there is a problem printing a job. The abort command is used to kill off all printing activity associated with a job. It also has the side effect of stopping printing on the print queue. The redo command will redo or attempt to reprint a job. The kill command combines the abort and redo command, and is useful when there are problems with the job currently being printed and it should be reprinted.


9.5. Job Scheduling - topq, holdall, noholdall, hold, release

The topq command effectively puts the select job or jobs at the top of the queue for printing.

The holdall, noholdall, and release commands implement a simple holding queue for jobs. By default, holdall is disabled. When the holdall command enables it, then jobs will remain in the spool queue until explicitly released with the release command.

The hold command can also be used to hold individual jobs until released.


9.6. Queue Management - class, redirect, move

The class command is used to restrict printing to jobs whose class value, set using the lpr -C class option, is in the class list or matches one of the classes. This allows the administrator to restrict printing. For more details and an example of its use, see Form Support.

The redirect command allows the administrator to accepts jobs at this queue and then to have them forwarded or redirected to another print queue. This is useful when a printer temporarily is unavailable.

The move command allows an individual job (or jobs) to be forwarded or redirected to another print queue.


Chapter 10. checkpc - Configuration Validation Utility

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


10.1. Maintenance

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

You can use checkpc -V -P to examine printcaps and tell you what they contain. This is identical to the lpc server all operation, but with a higher level of verbosity.


Chapter 11. Printer Communication and Protocols

Common communication methods between a printer and a host system are network connections, parallel ports, or serial ports; while Fibre Channel, SCSI, USB, FireWire, InfraRed, and other interesting technologies have been used, they are either very specialized or not directly support by the LPRng software. In this section we will discuss Network, Parallel Port, and Serial Printers, as well as the different protocols and standards that apply to them.


11.1. Network Printers

The most flexible and highest throughput printer interface is via a network (TCP/IP) connection. Most high performance printers have a built in network interface, or you can attach them to a printer server box which provides a network interface. The network interface usually supports multiple network printing protocols. The most common are the LPD (RFC1179), Socket API, AppSocket, SMB, and Novell Netware interfaces. LPRng directly supports the LPD (RFC1179) and Socket API interfaces, and you can use the smbclient program from the Samba Software Package for the SMB interface.


11.2. RFC1179 (LPD) Connection

In this mode of operation the print server actually operates as a very limited BSD print spooler. These limitations include:

  1. No error messages or status capability

  2. Limited or very primitive banner printing. On some systems it may be impossible to turn banner printing off.

  3. On most known print servers high connection activity caused by multiple systems attempting to get status or spool jobs may cause catastrophic failure of the printer.

For the above reasons, using RFC1179 to transfer jobs to a printer should be regarded as the least desirable option. Please see The RFC1189 Protocol for a detailed discussion of the RFC1179 protocol.

In order to use the RFC1179 transfer operation you must have a printcap entry for the printer that provides:

  • The IP address or name of the printer that can be resolved to an IP address

  • The name of the spool queue. In practice, this is usually used only to determine which of several printer ports on the print server the job will be sent to, or what type of processing the print server will do. Most cards usually do not do any processing and simply pass the job through to the printer.

The following is an example of a simple printcap entry that can be used to send a job to a remote printer using the RFC1179 protocol:

# LPRng syntax
# :lp value is 'where to print the job'
lp:
  :lp=raw@10.0.0.1

# OR Vintage BSD Print Spooler Syntax
# (LPRng supports this as well)
# :rp = remote printer, :rm = remote machine or host
lp:
  :rp=raw:rm=10.0.0.1

If you wish to transfer jobs to a print spooler without using the full LPRng lpr program, the Perl lpr_in_perl program in the LPRng Distribution UTILS directory can be used for testing and tutorial purposes.


11.3. Socket API

The Socket API is a very flexible job transfer protocol. It is widely support by most Print Server manufacturers, with the Hewlett Packard JetDirect setting the de facto standard. The Socket API is extremely simple.

  1. The user establishes a connection to TCP/IP port on the Printer or Network Print spooler. The HP JetDirect uses port 9100 by default, but other ports are used as well. This connection may be refused if the printer is busy printing a job.

  2. When the network connection is established to a system which has an internal printer or for which the Network Print Spooler is an integral part of the system, the printer usually flushes all internal buffers and readies itself to receive a new job. However, when you are using an external Print Server box, you may need to send specific initialization sequences to the printer to ensure that it is reset correctly and is ready to receive new jobs.

  3. When the connection is made, all bytes sent to the connection are either transferred to and external interface to directly to a print buffer used by the printer's Print Engine.

  4. The connection is bidirectional, and information sent to the external port by an external printer or error messages and status generated by the printer's Print Engine will be transferred over the data link to the user.

  5. The Network Print spooler will keep the connection open until it is closed by the user. During this period it may continue to report status or other information such as printer On Line, paper outages, and so forth.

  6. If the connection to the printer is half-closed, that is, the shutdown() network system call is used to indicate to the remote printer that no further data will be sent, then the printer may immediately terminate the network connection. This means that no further network or status messages will be sent to the user.

  7. If the connection is to a External Print Server, then usually the connection can be immediately re-established. It is the responsibility of the user to ensure that a the printer has finished its work before sending a new job.

  8. If the connection is to an internal Print Server, then usually the printer will not allow the connection to be made, or will refuse all data transfers on the connection until the printer finishes with the previous job and all internal buffers have been cleared.

The following is a sample printcap showing how to use the Socket API:

lp:
  # make a socket connection to port 9100
  :lp=10.0.0.2%9100

You can use the netcat utility by Hobbit to test that the Socket interface is available and working. If ellipse.ps is a test file, then: The simplest and easiest way to print a file to a network printer appears

  nc printer.ip.addr 9100 < file
Example:
  nc 10.0.0.25 9100 < ellipse.ps

11.4. AppSocket TCP/IP Protocol

The AppSocket interface is supported by Tektronix and some other printer vendors. It is similar to the Socket API, with a couple of significant differences.

  1. The printer has two ports for network connections: a TCP port 9100 for TCP/IP stream connections and a UDP port for UDP packet connections.

  2. When a 0 length UDP packet or a UDP packet containing only CR/LF is sent to UDP port 9101, the printer will return a packet to the sender containing print status information. This information indicates the printers current status (busy, idle, printing) and any error conditions.

  3. The format, reliability, and repeatability of the UDP format and information is totally undocumented, and the UPD port facility should be regarded as, at best, an advisory function of the printer.

  4. To send a job to the printer, a connection to TCP port is made. This connection will be refused while the printer is busy or has a connection to another host.

  5. When the TCP connection is established, the information to be printed can be sent over the TCP connection. Bytes sent on this stream will be placed in the input buffer of the Print Engine and processed.

  6. An end of job (EOJ) sequence indication in the data stream will cause the printer to terminate the connection. This is different than the Socket API, where the printer will keep the connection open. This means that if the PostScript CTRL-D (end of job) character is sent in a job, then the connection will be terminated.

  7. Some models of printers modify this behavior slightly and will not terminate the connection, but will simply ignore any data following the EOJ indication.

  8. Some printers support bidirectional AppSocket communication, and while the connection is open will return error indications or status information.

  9. Once all the data has been received and the job has finished printing, the connection will be terminated by the printer.

The ifhp filter, one of the helper programs for LPRng, is used with LPRng to provide AppSocket support. For details, please see the [IFHP-HOWTO] in the ifhp Distribution and Tektronix P450 and Family for details. The following is a typical printcap entry for the AppSocket protocol. The actual network connection to the printer is made by the ifhp filter:

lp:
  # LPRng opens a dummy connection for consistency
  :lp=/dev/null
  # we pass the ifhp filter options indicating that the
  # Tektronics printer will need the appsocket protocol
  # and to use port 35 at 10.0.0.1 to make the connection
  # The ifhp filter may open and close the connection several
  # times during the file transfer in order to ensure that
  # the printer handles the job correctly.
  :ifhp=model=tek,appsocket,dev=10.0.0.1%35
  :filter=/usr/local/libexec/filters/ifhp

11.5. Network Print Server Boxes

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.

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


11.6. Network Print Server Configuration Information

The following is a list of print server manufacturers, models, and with hints on how to access these boxes with various protocols.

Table 11-1. Network Print Server Configuration Information

Manufacturer Model RFC1179 Port Name (rp=XXX) Send to TCP port
Cannon Printer Cannon 460 PS, no hard drive xjdirect - Unknown if supported -
  Cannon 460 PS hard drive xjprint - print immediately,xjhold - print later - Unknown if supported -
Digital Products Inc. NETPrint Print Server PORTn, where n is port on server - Unknown if supported -
Electronics For Imaging Inc. Fiery RIP i series normalq or urgentq - Unknown if supported -
  Fiery RIP XJ series xjprint - Unknown if supported -
  Fiery RIP XJ+ and SI series print_Model, e.g. print_DocuColor - Unknown if supported -
  Fiery models ZX2100, ZX3300, X2, X2e print - Unknown if supported -
Emulex Corp. NETJet/NETQue print server PASSTHRU - Unknown if supported -
Extended Systems Inc. ExtendNet Print Server Printern, where n is port on server - Unknown if supported -
Hewlett-Packard JetDirect interface card raw 9100
Hewlett-Packard JetDirect Multiport Server port 1 - raw1, port 2 - raw2, etc. port 1 - 9100, port 2 - 9101, etc.
I-Data Easycom 10 Printserver par1 (parallel port 1) - Unknown if supported -
  Easycom 100 Printserver LPDPRT1 - Unknown if supported -
IBM Network Printer 12, 17, 24, and 24PS PASS - Unknown if supported -
Lantronix EPS1, EPS2 EPS_X_S1 (serial) port 1, EPS_X_P1 (parallel) port 2, etc. 3001 (port 1), 3002 (port 2), etc.
QMS Various Models RAW 35 (AppSocket)
Tektronix Tektronix printer network cards PS (PostScript), PCL (PCL), or AUTO(Auto-selection between PS, PCL, or HPGL). Not reliable. 9100 (AppSocket on some models)
Rose Electronics Microserve Print Servers lp 9100
Xerox Models 4505, 4510, 4517, 4520 PASSTHRU 2501 (AppSocket on some models)
  Model 4512 PORT1 10001 (programmable)
  Model N17 RAW 9100
  Models N24 and N32 RAW 2000
  Models 4900, 4915, 4925, C55 PS 2000
  Document Centre DC220/230 lp - Unknown if supported -

All company, brand, and product names are properties of their respective owners.


11.7. HP JetDirect Interface

The HP JetDirect Interface is one of the most widely used for network printers. For this reason it also has the most widely known set of problems. The user is strongly urged to upgrade to the latest version of firmware available for the unit. Problems with older versions of firmware include system lockups that require powerup level resets.

Newer versions of the HP JetDirect Interface have a Web Browser based configuration system. After you have assigned an IP address to the printer you can connect to the configuration port and configure the printer using the browser. If you run into configuration problems then you will most likely need to use the Microsoft Windows based JetDirect Configuration Software to reset or reconfigure the printer.

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


11.7.1. Resetting To Factory Defaults

Most internal HP JetDirect print servers can be reset to factory defaults (or cold-reset) by turning the printer off and holding down the Online or Go button while turning the printer back on. The printer control panel display should read Cold Reset, Restoring Factory Settings, or something similar.


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

11.7.3. 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 selectible option strings,
# local0 through local7 (configured to LOG_LOCAL0 through LOG_LOCAL7,
# respectively) are case insensitive.  The default syslog-facility
# for the card is LOG_LPR.

syslog-facility: local2

# This parameter allows the card to treat hosts on other subnets as
# if the hosts were on the card's subnet.  This parameter determines
# the TCP Maximum Segment Size (MSS) advertised by the card to hosts
# on other subnets and affects the card's initial receive-window
# size.  The card will use a TCP MSS of 1460 bytes for local hosts,
# and 536 bytes for a non-local host.  The default is off, that is,
# the card will