#
# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License, Version 1.0 only
# (the "License"). You may not use this file except in compliance
# with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#pragma ident "%Z%%M% %I% %E% SMI"
###########
##
## Network Standard printer interface program.
##
###########
#####
# We can't do much except exit if spooler/scheduler
# cancels us.
#####
trap 'eval exit_clean 15' 15
####
#
# Send standard error messages to /dev/null rather than to
# the spooler. Avoids "Terminated" messages that shell puts out
# when gets SIGTERM. Save standard error so it can be used
# when we need it
####
exec 5>&2 2>/dev/null 3>&1
####
# set some global variables
####
: ${LPTMPDIR:=/tmp}
: ${SPOOLDIR:=/usr/spool/lp}
: ${LOCALPATH:=${SPOOLDIR}/bin}
PATH="/bin:/usr/bin:${LOCALPATH}"
exit_code=0
# ${LPTELL} is the name of a program that will send its
# standard input to the Spooler. It is used to forward
# the description of a printer fault to the Spooler,
# which uses it in an alert to the administrator.
#####
if [ ! -x "${LPTELL:=${LOCALPATH}/lp.tell}" ]
then
fake_lptell () {
header="no"
while read line
do
if [ "no" = "${header}" ]
then
errmsg ERROR ${E_IP_UNKNOWN} \
"unknown printer/interface failure" \
"consult your system administrator;
reasons for failure (if any) follow:"
header=yes
fi
echo "${line}" >&2
done
return 1
}
LPTELL=fake_lptell
fi
#####
# Error message formatter:
#
# Invoke as
#
# errmsg severity message-number problem help
#
# where severity is "ERROR" or "WARNING", message-number is
# a unique identifier, problem is a short description of the
# problem, and help is a short suggestion for fixing the problem.
#####
LP_ERR_LABEL="UX:lp"
E_IP_ARGS=1
E_IP_OPTS=2
#E_IP_FILTER=3
E_IP_UNKNOWN=5
E_IP_BADFILE=6
E_IP_ERRORS=12 # (in slow.filter)
errmsg () {
case $1 in
ERROR )
sev=" ERROR";
;;
WARNING )
sev="WARNING";
;;
esac
echo "${LP_ERR_LABEL}:$2 ${sev}: $3
TO FIX: $4" >&5
}
###########
##
## Check arguments
###########
parse () {
echo "`expr \"$1\" : \"^[^=]*=\(.*\)\"`"
}
#####
##
## Error Cleanup and Exit
##
#####
exit_clean()
{
if [ -f "${LPTMPDIR}/pr_eexit_code.$$" ]
then
/bin/rm ${LPTMPDIR}/pr_eexit_code.$$
fi
if [ -f "${LPTMPDIR}/small_banner.$$" ]
then
/bin/rm ${LPTMPDIR}/small_banner.$$
fi
if [ -f "${tmpfile}" ]
then
/bin/rm "${tmpfile}"
fi
exit $1
}
#####
#
# This program is invoked as
#
# ${SPOOLDIR}/.../printer request-id user title copies options files...
#
# The first three arguments are simply reprinted on the banner page,
# the fourth (copies) is used to control the number of copies to print,
# the fifth (options) is a blank separated list (in a single argument)
# of user or Spooler supplied options (without the -o prefix),
# and the last arguments are the files to print.
#####
if [ $# -lt 5 ]
then
errmsg ERROR ${E_IP_ARGS} \
"wrong number of arguments to interface program" \
"consult your system administrator"
exit 1
fi
printer=`basename $0`
request_id=$1
user_name=$2
title=$3
copies=$4
option_list=$5
shift 5
files="$*"
#
# debug sent to file if defined in /etc/syslog.conf
# syslog.conf entry:
#
logger -p lpr.debug -t "netstandard: ${request_id}" " "
logger -p lpr.debug -t "netstandard: ${request_id}" "INPUT"
logger -p lpr.debug -t "netstandard: ${request_id}" " printer : ${printer}"
logger -p lpr.debug -t "netstandard: ${request_id}" " request_id : ${request_id}"
logger -p lpr.debug -t "netstandard: ${request_id}" " user_name : ${user_name}"
logger -p lpr.debug -t "netstandard: ${request_id}" " title : ${title}"
logger -p lpr.debug -t "netstandard: ${request_id}" " copies : ${copies}"
logger -p lpr.debug -t "netstandard: ${request_id}" " option_list : ${option_list}"
logger -p lpr.debug -t "netstandard: ${request_id}" " files : ${files}"
logger -p lpr.debug -t "netstandard: ${request_id}" " spooler_key ${SPOOLER_KEY}"
####
# default: do print a banner
####
nobanner=no
nofilebreak="no"
inlist=
data_file_flag=
for i in ${option_list}
do
case "${inlist}${i}" in
nobanner )
nobanner="yes"
;;
nofilebreak )
nofilebreak="yes"
;;
#####
#
# If you want to add simple options (e.g. -o simple)
# identify them here.
#####
# simple )
# simple="yes"
# ;;
cpi=pica )
cpi=10
;;
cpi=elite )
cpi=12
;;
cpi=* )
cpi=`parse ${i}`
;;
lpi=* )
lpi=`parse ${i}`
;;
length=* )
length=`parse ${i}`
;;
width=* )
width=`parse ${i}`
;;
dest=* )
dest="-d `parse ${i}`"
;;
protocol=* )
protocol="-P `parse ${i}`"
;;
bsdctrl=* )
controlfile="-c `parse ${i}`"
;;
timeout=* )
timeout="-t `parse ${i}`"
;;
data-file-type=* )
data_file_flag="-f `parse ${i}`"
;;
#####
#
# If you want to add simple-value options (e.g. -o value=a)
# identify them here.
#####
# value=* )
# value=`parse ${i}`
# ;;
#####
#
# If you want to add options that,
# take a list (e.g. -o lopt='a b c'), identif
# them here and below (look for LOPT).
#####
# flist=* | lpd=* | options=* )
flist=* | lpd=* )
#LOPT stty=* | flist=* | lpd=* | lopt=* )
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
flist= )
flist="${flist} ${item}"
;;
lpd= )
lpd="${lpd} ${item}"
;;
#LOPT lopt= )
#LOPT lopt="${lopt} ${item}"
#LOPT ;;
# options= )
# options="${options} ${item}"
# ;;
esac
case "${i}" in
${inlist}\'*\' )
inlist=
;;
${inlist}\'* )
;;
*\' | ${inlist}* )
inlist=
;;
esac
;;
* )
errmsg WARNING ${E_IP_OPTS} \
"unrecognized \"-o ${i}\" option" \
"check the option, resubmit if necessary
printing continues"
;;
esac
done
logger -p lpr.debug -t "netstandard: ${request_id}" "term : ${TERM}"
if [ -z "${FILTER}" ]
then
#####
#
# If no filter is being used, we use netpr to push the
# file to the printer.
# (QUOTES ARE IMPORTANT!)
#####
case "$TERM" in
PS )
# make the "postscript" printers use netpr
FILTER=
;;
PSR )
# make the "reverse postscript" printers reverse the
# output and the use postio to talk to the printer
#FILTER="/usr/lib/lp/postscript/postreverse "
#FILTER=
FILTER="/usr/lib/lp/postscript/postreverse "
;;
* )
# We don't know the type, so just assume that the
# input and output are the same. Use netpr.
#FILTER=/bin/cat
FILTER=
;;
esac
fi
####
# sets default value for ordering of data and control files with
# bsd protocol. Default: data files first. Administrator
# may set to control file first with lpadmin -o bsdctrl=first
####
banner_flag=""
case "${nobanner}" in
yes )
banner_flag="-b"
;;
esac
NETPR="/usr/lib/lp/bin/netpr ${banner_flag} ${data_file_flag} \
-I ${request_id} -U ${user_name} \
-p ${printer} ${dest} -T \"${title}\" \
${timeout} ${protocol} ${controlfile} "
LPTELL_OPTS="-l" # netpr sends LaserWriter style messages back
logger -p lpr.debug -t "netstandard: ${request_id}" "NETPR= ${NETPR}"
logger -p lpr.debug -t "netstandard: ${request_id}" "filter : ${FILTER}"
node=`uname -n`
pid=$$
tmpfile=${LPTMPDIR}/${node}.${pid}
logger -p lpr.debug -t "netstandard: ${request_id}" "tmpfile : ${tmpfile}"
#####
#
# Set up filter for banner page
#
#####
banner_filter=
case "${TERM}" in
PS | PSR )
banner_filter=" | /usr/lib/lp/postscript/postprint "
LPTELL_OPTS="-l"
;;
esac
#####
#
# Build temporary file that is the banner page
#
#####
PAD="#####${NL}"
CR="\r"
NL="${CR}\n"
FF=
small_banner() {
echo "${CR}\c"
echo "${PAD}\c"
echo "##### User: ${user_name}${NL}\c"
if [ -n "${title}" ]
then
echo "##### Title: ${title}${NL}\c"
fi
echo "##### Date: `LANG=C date '+%a %H:%M %h %d, %Y'`${NL}\c"
echo "##### Job: ${request_id}${NL}\c"
echo "${PAD}\c"
if [ -n "${FF}" ]
then
echo "${CR}${FF}\c"
fi
}
#####
#
# Doing small banner as we don't know what printer is out there
#
#####
banner=small_banner
if [ "no" = "${nobanner}" ]
then
eval "${banner} ${banner_filter}" 2>&1 1>${LPTMPDIR}/small_banner.$$
fi
#####
#
# Print banner page before job unless PSR
#
#####
if [ "no" = "${nobanner}" -a "${TERM}" != "PSR" ]
then
(
eval ${NETPR} ${LPTMPDIR}/small_banner.$$ 2>&1
echo $? > ${LPTMPDIR}/pr_eexit_code.$$
) | ${LPTELL} ${LPTELL_OPTS} ${printer}
exit_code=`cat ${LPTMPDIR}/pr_eexit_code.$$`
logger -p lpr.debug -t "netstandard: ${request_id}" \
"banner page exit code : ${exit_code}"
fi
i=1
while [ $i -le $copies ]
do
for file in ${files}
do
if [ -r "${file}" ]
then
if [ ! -z "${FILTER}" ]
then
(
#####
# There is a filter, use it
#
# Put 0<${file} before the "eval" to keep
# clever users from giving a file name that
# evaluates as something to execute.
# Redirect stderr to stdout so LPTELL will
# get error messages from pipe.
#####
0<${file} eval ${FILTER} 2>&1 1>${tmpfile}
echo $? > ${LPTMPDIR}/pr_eexit_code.$$
) | ${LPTELL} ${LPTELL_OPTS} ${printer}
exit_code=`cat ${LPTMPDIR}/pr_eexit_code.$$`
logger -p lpr.debug -t "netstandard: ${request_id}" \
"filter exit_code : ${exit_code}"
if [ -n "${exit_code}" ]
then
if [ "${exit_code}" -eq 0 ]
then
printfile=${tmpfile}
else
####
# The filter did not succeed, so don't try to print
####
printfile=
fi
fi
else
printfile=${file}
fi
logger -p lpr.debug -t "netstandard: ${request_id}" \
"printfile : ${printfile}"
#####
# Print the file
#####
if [ -r "${printfile}" ]
then
(
eval ${NETPR} ${printfile} 2>&1
echo $? > ${LPTMPDIR}/pr_eexit_code.$$
) | ${LPTELL} ${LPTELL_OPTS} ${printer}
exit_code=`cat ${LPTMPDIR}/pr_eexit_code.$$`
logger -p lpr.debug -t "netstandard: ${request_id}" \
"netpr exit_code : ${exit_code}"
if [ -f "${tmpfile}" ]
then
/bin/rm "${tmpfile}"
fi
if [ -n "${exit_code}" ]
then
if [ "${exit_code}" -eq 0 ]
then
printone=yes
else
if [ "${exit_code}" -lt 128 ]
then
noprint=yes
else
retry=yes
fi
fi
fi
else
errmsg WARNING ${E_IP_BADFILE} \
"cannot read temporary file \"${printfile}\""\
"see if file still exists,
or consult your system administrator;
printing continues"
fi
else
#####
#
# Don't complain about not being able to read
# a file on second and subsequent copies, unless
# we've not complained yet. This removes repeated
# messages about the same file yet reduces the
# chance that the user can remove a file and not
# know that we had trouble finding it.
#####
if [ "${i}" -le 1 -o -z "${badfileyet}" ]
then
errmsg WARNING ${E_IP_BADFILE} \
"cannot read file \"${file}\"" \
"see if the file still exists and is readable,
or consult your system administrator;
printing continues"
badfileyet=yes
fi
fi
# for file in ${files}
done
i=`expr $i + 1`
done
#####
#
# If printing in reverse order, print the banner page now
#
#####
if [ "no" = "${nobanner}" -a "${TERM}" = "PSR" ]
then
(
eval ${NETPR} ${LPTMPDIR}/small_banner.$$ 2>&1
echo $? > ${LPTMPDIR}/pr_eexit_code.$$
) | ${LPTELL} ${LPTELL_OPTS} ${printer}
fi
exit_code=`cat ${LPTMPDIR}/pr_eexit_code.$$`
logger -p lpr.debug -t "netstandard: ${request_id}" \
"banner page exit code : ${exit_code}"
if [ -n "${printone}" -a -z "${retry}" -a -z "${noprint}" ]
then
exit_code=`expr 0`
else
if [ -n "${retry}" -a -z "${printone}" -a -z "${noprint}" ]
then
exit_code=`expr 129`
else
exit_code=`expr 1`
fi
fi
logger -p lpr.debug -t "netstandard: ${request_id}" \
"FINAL exit_code : ${exit_code}"
exit_clean ${exit_code}