Writing an LP Interface Script
By Norman A. Jacobs, April 2002
What Is an LP Interface Script?
An LP interface script is the component of the printing system that performs the final processing of a print job and actually sends it to the printer. Its sole purpose is to "print" the job. Upon completion, the script should either have completed the job and exited with a normal exit status, or have failed to complete, but exited with a non-normal exit status.
Interfaces
An interface script utilizes a number of interfaces both to both gather information for its operation and to return information about its processing and results. These interfaces include the use of:
- Inbound command-line arguments
- Inbound environment variables
- File descriptors (for input and results)
- Received signals
- Exit status code
Command-Line Arguments
The first place an interface script gathers information about what it is to do is in the command-line arguments. The arguments provide information about the job, its submitter, the features requested, and the data to handle.
ArgumentNameDescription
$0.../{printer}This argument is the name of the interface script that was called. On the Solaris OE, this will always be /etc/lp/interfaces/{printer}.
$1Request-idThis is actually the first argument used in calling the interface script. It contains an identifier for the job. The intention is that this identifier is placed on any burst pages printed with the job. The format of the identifier is usually in the form of {printer}-{number}.
$2UserThis the user that submitted the print job. It is generally in the form of user@host and is to be used on any burst pages printed wit the job.
$3TitleThis is a title for the print job. Again, this is to be used on any burst pages printed with the job. A default title of the file name is normally supplied here, but lp -t or lpr -J will override the default.
$4CopiesThis is the number of copies of each document to print.
$5Options (lp -o)This contains a string of "transparent" options that were passed from the command line through the spooling system to this script. Options are generally used to turn on and off printer features like duplex printing, stapling, and so on. It can also be used to pass additional required information like a fax number, if the script is driving a fax modem.
$6+File listThe rest of the arguments contain a list of all of the data files to send to the output device. This list of files is referenced through full path names.
Environment Variables
VariableSubjectDescription
CHARSETThe character set to useWhen this environment variable is set, it contains the name of the character set to be used when printing the job. The value is generally used during printer initialization.
FILTERA fast filter to inline on the way to the printerWhen set, this variable defines a "fast" filter stream to be used inline on the way to the printer. All "good" interface scripts should heed this value when set.
LANGI18n
LC_COLLATEI18n
LC_CTYPEI18n
LC_MESSAGESI18n
LC_MONETARYI18n
LC_NUMERICI18n
LC_TIMEI18nValues to use when localizing any interface script-derived output. These are primarily defined so that they can be passed to any programs called by the script.
PATHDefault path/usr/sbin:/usr/bin
SPOOLER_KEYUsed by lp.tell for backchannel to lpschedThis value is used by lp.tell (private interface) to uniquely specify the instance of the interface script that is running. lp.tell passes this value along with a fault message, or fault clear message to the scheduler when a problem occurs, or is resolved.
TERMPrinter typeThis value contains the printer type of the printer that this interface script is processing for. The type is defined with lpadmin -T.
TZYour time zone--
File Descriptors
File DescriptorDescription
0Unused (lpsched opens /dev/null for reading only at this descriptor)
1Printer device (use /dev/null for network-attached printers)
2Backchannel to lpsched
Signals
Exit Codes
Exit CodeSubjectDescription
0SuccessIf the interface script completed without any error, it should exit with this exit code.
1 - 127Failure, don't restartIf the interface script had an error in processing the job and it's likely that the error is in the data, the script should probably exit with one of these codes.
128N/ADo not use
>128Failure, wait or restartIf the interface script encounters an error that is recoverable, the script should probably exit.
Effective UID
The interface script is run using one of two effective user IDs. If the print job appears to the scheduler to have come from the local host, the euid will be that of the calling user. If the print job appears to have come from a remote source, the euid will be that of lp. This should give the script the necessary privilege to access any of the files it needs to print.
If you are using a set-uid program inside of the interface script to perform some of the processing, you should make sure that you check for any security hole that it may introduce while operating under the interface script, as well as if called by hand.
Example Scripts
Here are a number of sample interface scripts that can be used as a starting point for implementing an interface script of your own.
Example NameDescription
GSinterfaceA simple example of using GhostScript* to drive a PCL printer under LP.
SPARCprinterA simple example of using GhostScript to drive a SPARCprinter under LP.
NeWSprinterCL+A simple example of using GhostScript to drive a NeWSprinterCL+ printer under LP.
PSinterfaceA simple example of driving a PostScript printer under LP.
exampleA template example of an interface script.
* An older version of the GSinterface script can be found in the SFWgs
package on the Solaris companion CD. This article includes an updated
version that allows similar functionality of the NeWSprinterCL+ and
SPARCprinter scripts via the following configuration:
SPARCprinter
# lpadmin -p printer -v /dev/null -TPS -Ipostscript \
-i /opt/sfw/share/ghostscript/interfaces/GSinterface \
-o GS_DEVICE=sparc -o OutputFile=/dev/lpvi0 \
-o banner-type=postscript
NeWSprinterCL+
# lpadmin -p printer -v /dev/bpp0 -TPS -Ipostscript \
-i /opt/sfw/share/ghostscript/interfaces/GSinterface \
-o GS_DEVICE=bjc800"
Finally, here is a shell of an interface script to be used as a start to implementing your own.
#/bin/sh
#
# Copyright (c) 1997, by Sun Microsystems Inc.
# All Rights Reserved
#
#pragma ident "@(#)interface.html 1.2 98/05/22 SM"
#
# This is an example interface script to help you get
# an idea of how to integrate support for your printer
# under lp. This is by no means the definitive source
# for information. You should look at included interface
# scripts as examples and at the Solaris System
# Administrators Answerbook.
#
# This script is run as 'lp' on the print server if the
# job came in via the network. It runs as the submitting
# user if the job is local. You should also note that
# the data files are owned by the user running the script.
#
# Command Line Arguments:
# $0 - .../{printer}
# $1 - request-id
# $2 - user
# $3 - title
# $4 - copies
# $5 - options (lp -o)
# $6+ - file list
# Environment Variables:
# CHARSET - the character set to use
# FILTER - fast filter to inline on the
# way to the printer
# LC_COLLATE - I18n
# LC_CTYPE - I18n
# LC_MESSAGES - I18n
# LC_MONETARY - I18n
# LC_NUMERIC - I18n
# LC_TIME - I18n
# PATH - default path (/usr/sbin:/usr/bin)
# SPOOLER_KEY - used by lp.tell for backchannel
# to lpsched
# TERM - printer type
# TZ - our timezone
# File Descriptors:
# 0 - don't recall
# 1 - printer device (use /dev/null for network
# attached printers)
# 2 - backchannel to lpsched
# Signals:
#
# Exit Codes:
# 0 - success
# 1 - 127 - failure, don't restart
# 128 - don't use
# >128 - failure, wait or restart
#
if [ $# -lt 5 ] ; then
echo "wrong number of arguments to interface
program" 1>&2
exit 1
fi
printer=`basename $0`
request_id=$1
user_name=$2
title=$3
copies=$4
option_list=$5
shift 5
files="$*"
stty=
parse () {
echo "`expr \"$1\" : \"^[^=]*=\(.*\)\"`"
}
nobanner="no"
nofilebreak="no"
inlist=
for i in ${option_list}
do
case "${inlist}${i}" in
nobanner )
nobanner="yes"
;;
nofilebreak )
nofilebreak="yes"
;;
# lp -o key
# key )
# key="whatever"
# ;;
# lp -o key=value
# key=* )
# key=`parse ${i}`
# ;;
# lp -o key='list ...'
# stty=* )
# inlist=`expr "${inlist}${i}" : "^\([^=]*=\)"`
# case "${i}" in
# ${inlist}\'*\' )
# item=`expr "${i}" :
# "^[^=]*='*\(.*\)'\$"`
# ;;
# ${inlist}\' )
# continue
# ;;
# ${inlist}\'* )
# item=`expr "${i}" :
# "^[^=]*='*\(.*\)\$"`
# ;;
# ${inlist}* )
# item=`expr "${i}" : "^[^=]*=\(.*\)\$"`
# ;;
# *\' )
# item=`expr "${i}" : "^\(.*\)'\$"`
# ;;
# * )
# item="${i}"
# ;;
# esac
#
# #####
# #
# # We don't dare use "eval" because a clever
# # user could put something in an option
# # value that we'd end up
# # exec'ing.
# #####
# case "${inlist}" in
# key= )
# key="${key} ${item}"
# ;;
# esac
#
# case "${i}" in
# ${inlist}\'*\' )
# inlist=
# ;;
# ${inlist}\'* )
# ;;
# *\' | ${inlist}* )
# inlist=
# ;;
# esac
# ;;
* )
echo "unrecognized \"-o ${i}\" option check
the option, resubmit if necessary
printing continues" 1>&2
;;
esac
done
#
# should create a banner
#
#
# process the files
#
i=1
while [ $i -le $copies ]
do
for file in $files
do
cat $file
done
i=`expr $i + 1`
done
exit 0
Additional Reading
More technical articles on printing from the Solaris Developer Connection site: