1#
2# CDDL HEADER START
3#
4# The contents of this file are subject to the terms of the
5# Common Development and Distribution License (the "License").
6# You may not use this file except in compliance with the License.
7#
8# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9# or http://www.opensolaris.org/os/licensing.
10# See the License for the specific language governing permissions
11# and limitations under the License.
12#
13# When distributing Covered Code, include this CDDL HEADER in each
14# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15# If applicable, add the following below this CDDL HEADER, with the
16# fields enclosed by brackets "[]" replaced with your own identifying
17# information: Portions Copyright [yyyy] [name of copyright owner]
18#
19# CDDL HEADER END
20#
21#
22# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23# Use is subject to license terms.
24#
25
26###########
27##
28## Network Standard printer interface program.
29##
30###########
31
32#####
33# We can't do much except exit if spooler/scheduler
34# cancels us.
35#####
36trap 'eval exit_clean 15' 15
37
38####
39#
40# Send standard error messages to /dev/null rather than to
41# the spooler. Avoids "Terminated" messages that shell puts out
42# when gets SIGTERM. Save standard error so it can be used
43# when we need it
44####
45exec 5>&2 2>/dev/null 3>&1
46
47####
48# set some global variables
49####
50
51: ${LPTMPDIR:=/tmp}
52: ${SPOOLDIR:=/usr/spool/lp}
53: ${LOCALPATH:=${SPOOLDIR}/bin}
54PATH="/bin:/usr/bin:${LOCALPATH}"
55exit_code=0
56
57
58# ${LPTELL} is the name of a program that will send its
59# standard input to the Spooler. It is used to forward
60# the description of a printer fault to the Spooler,
61# which uses it in an alert to the administrator.
62#####
63if [ ! -x "${LPTELL:=${LOCALPATH}/lp.tell}" ]
64then
65        fake_lptell () {
66                header="no"
67                while read line
68                do
69                        if [ "no" = "${header}" ]
70                        then
71                                errmsg ERROR ${E_IP_UNKNOWN} \
72                "unknown printer/interface failure" \
73                "consult your system administrator;
74                reasons for failure (if any) follow:"
75                                header=yes
76                        fi
77                        echo "${line}" >&2
78                done
79                return 1
80        }
81        LPTELL=fake_lptell
82fi
83
84#####
85# ${LPTSOLSEPARATOR} is the name of a program to put banner and trailer
86# pages around the job.
87#####
88if [ -x ${LOCALPATH}/lp.tsol_separator ]
89then
90	LPTSOLSEPARATOR=${LOCALPATH}/lp.tsol_separator
91else
92	echo "${LOCALPATH}/lp.tsol_separator not found." >&2
93	exit 1
94fi
95
96#####
97# Error message formatter:
98#
99# Invoke as
100#
101#       errmsg severity message-number problem help
102#
103# where severity is "ERROR" or "WARNING", message-number is
104# a unique identifier, problem is a short description of the
105# problem, and help is a short suggestion for fixing the problem.
106#####
107
108LP_ERR_LABEL="UX:lp"
109E_IP_ARGS=1
110E_IP_OPTS=2
111#E_IP_FILTER=3
112E_IP_UNKNOWN=5
113E_IP_BADFILE=6
114E_IP_ERRORS=12 	# (in slow.filter)
115
116errmsg () {
117
118        case $1 in
119        ERROR )
120                sev="  ERROR";
121                ;;
122        WARNING )
123                sev="WARNING";
124                ;;
125        esac
126
127        echo "${LP_ERR_LABEL}:$2 ${sev}: $3
128        TO FIX: $4" >&5
129}
130
131###########
132##
133## Check arguments
134###########
135
136parse () {
137        echo "`expr \"$1\" : \"^[^=]*=\(.*\)\"`"
138}
139
140#####
141##
142## Error Cleanup and Exit
143##
144#####
145
146exit_clean()
147{
148
149	if [ -f "${LPTMPDIR}/pr_eexit_code.$$" ]
150	then
151		/bin/rm ${LPTMPDIR}/pr_eexit_code.$$
152	fi
153
154	if [ -f "${LPTMPDIR}/small_banner.$$" ]
155	then
156		/bin/rm ${LPTMPDIR}/small_banner.$$
157	fi
158
159	if [ -f "${LPTMPDIR}/banner.exit_code.$$" ]
160	then
161		/bin/rm ${LPTMPDIR}/banner.exit_code.$$
162	fi
163
164	if [ -f "${LPTMPDIR}/banner.errmsg.$$" ]
165	then
166		/bin/rm ${LPTMPDIR}/banner.errmsg.$$
167	fi
168
169	if [ -f "${tmpfile}" ]
170	then
171		/bin/rm "${tmpfile}"
172	fi
173
174	exit $1
175}
176
177#####
178#
179# This program is invoked as
180#
181# ${SPOOLDIR}/.../printer request-id user title copies options files...
182#
183# The first three arguments are simply reprinted on the banner page,
184# the fourth (copies) is used to control the number of copies to print,
185# the fifth (options) is a blank separated list (in a single argument)
186# of user or Spooler supplied options (without the -o prefix),
187# and the last arguments are the files to print.
188#####
189
190if [ $# -lt 5 ]
191then
192
193        errmsg ERROR ${E_IP_ARGS} \
194                "wrong number of arguments to interface program" \
195                "consult your system administrator"
196        exit 1
197fi
198
199printer=`basename $0`
200request_id=$1
201user_name=$2
202title=$3
203copies=$4
204option_list=$5
205
206shift 5
207files="$*"
208
209
210#
211# debug sent to file if defined in /etc/syslog.conf
212# syslog.conf entry:
213#	lpr.debug	/path/filename
214#
215logger -p lpr.debug -t "tsol_netstandard: ${request_id}" " "
216logger -p lpr.debug -t "tsol_netstandard: ${request_id}" "INPUT"
217logger -p lpr.debug -t "tsol_netstandard: ${request_id}" "    \
218    printer : ${printer}"
219logger -p lpr.debug -t "tsol_netstandard: ${request_id}" "    \
220    request_id : ${request_id}"
221logger -p lpr.debug -t "tsol_netstandard: ${request_id}" "    \
222    user_name : ${user_name}"
223logger -p lpr.debug -t "tsol_netstandard: ${request_id}" "    title : ${title}"
224logger -p lpr.debug -t "tsol_netstandard: ${request_id}" "    \
225    copies : ${copies}"
226logger -p lpr.debug -t "tsol_netstandard: ${request_id}" "    \
227    option_list : ${option_list}"
228logger -p lpr.debug -t "tsol_netstandard: ${request_id}" "    files : ${files}"
229logger -p lpr.debug -t "tsol_netstandard: ${request_id}" "	 \
230    spooler_key ${SPOOLER_KEY}"
231
232####
233# default: do print a banner
234####
235nobanner=no
236nolabels="no"
237nofilebreak="no"
238inlist=
239data_file_flag=
240
241for i in ${option_list}
242do
243        case "${inlist}${i}" in
244
245        nobanner )
246                nobanner="yes"
247                ;;
248
249        nofilebreak )
250                nofilebreak="yes"
251                ;;
252
253	nolabels )
254		nolabels="yes"
255		;;
256
257        #####
258        #
259        # If you want to add simple options (e.g. -o simple)
260        # identify them here.
261        #####
262#       simple )
263#               simple="yes"
264# 		;;
265
266        cpi=pica )
267                cpi=10
268                ;;
269        cpi=elite )
270                cpi=12
271                ;;
272        cpi=* )
273                cpi=`parse ${i}`
274                ;;
275
276        lpi=* )
277                lpi=`parse ${i}`
278                ;;
279
280        length=* )
281                length=`parse ${i}`
282                ;;
283
284        width=* )
285                width=`parse ${i}`
286                ;;
287        dest=* )
288                dest="-d `parse ${i}`"
289                ;;
290
291        protocol=* )
292                protocol="-P `parse ${i}`"
293                ;;
294        bsdctrl=* )
295		controlfile="-c `parse ${i}`"
296                ;;
297        timeout=* )
298                timeout="-t `parse ${i}`"
299                ;;
300
301        data-file-type=* )
302                data_file_flag="-f `parse ${i}`"
303                ;;
304
305        #####
306        #
307        # If you want to add simple-value options (e.g. -o value=a)
308        # identify them here.
309        #####
310#       value=* )
311#		value=`parse ${i}`
312#		;;
313
314        #####
315        #
316        # If you want to add options that,
317        # take a list (e.g. -o lopt='a b c'), identif
318        # them here and below (look for LOPT).
319        #####
320
321#	flist=* | lpd=* | options=* )
322        flist=* | lpd=* )
323#LOPT   stty=* | flist=* | lpd=* | lopt=* )
324
325                inlist=`expr "${inlist}${i}" : "^\([^=]*=\)"`
326                case "${i}" in
327                ${inlist}\'*\' )
328                        item=`expr "${i}" : "^[^=]*='*\(.*\)'\$"`
329                        ;;
330                ${inlist}\' )
331                        continue
332                        ;;
333                ${inlist}\'* )
334                        item=`expr "${i}" : "^[^=]*='*\(.*\)\$"`
335                        ;;
336                ${inlist}* )
337                        item=`expr "${i}" : "^[^=]*=\(.*\)\$"`
338                        ;;
339                *\' )
340                        item=`expr "${i}" : "^\(.*\)'\$"`
341                        ;;
342                * )
343                        item="${i}"
344                        ;;
345                esac
346
347                #####
348                #
349                # We don't dare use "eval" because a clever user could
350                # put something in an option value that we'd end up
351                # exec'ing.
352                #####
353                case "${inlist}" in
354                flist= )
355                        flist="${flist} ${item}"
356                        ;;
357                lpd= )
358                        lpd="${lpd} ${item}"
359                        ;;
360#LOPT		lopt= )
361#LOPT                   lopt="${lopt} ${item}"
362#LOPT			;;
363#		options= )
364#			options="${options} ${item}"
365#			;;
366                esac
367
368                case "${i}" in
369                ${inlist}\'*\' )
370                        inlist=
371                        ;;
372                ${inlist}\'* )
373                        ;;
374                *\' | ${inlist}* )
375                        inlist=
376                        ;;
377                esac
378                ;;
379
380        * )
381                errmsg WARNING ${E_IP_OPTS} \
382                        "unrecognized \"-o ${i}\" option" \
383                        "check the option, resubmit if necessary
384                printing continues"
385                ;;
386        esac
387done
388
389logger -p lpr.debug -t "tsol_netstandard: ${request_id}"  "term : ${TERM}"
390
391if [ -z "${FILTER}" ]
392then
393        #####
394        #
395        # If no filter is being used, we use netpr to push the
396	# file to the printer.
397        # (QUOTES ARE IMPORTANT!)
398        #####
399
400        case "$TERM" in
401                PS )
402                        # make the "postscript" printers use cat
403			# (TSOL banners are added during filtering, so we have
404			# to use some filter.)
405                        FILTER=/bin/cat
406                ;;
407                PSR )
408                        # make the "reverse postscript" printers reverse the
409                        # output and the use postio to talk to the printer
410                        #FILTER="/usr/lib/lp/postscript/postreverse "
411                        #FILTER=
412                        FILTER="/usr/lib/lp/postscript/postreverse "
413                ;;
414                * )
415                        # We don't know the type, so just assume that the
416                        # input and output are the same. Use netpr.
417                        #FILTER=/bin/cat
418			FILTER=
419                ;;
420        esac
421fi
422
423####
424# sets default value for ordering of data and control files with
425# bsd protocol. Default: data files first. Administrator
426# may set to control file first with lpadmin -o bsdctrl=first
427####
428
429banner_flag=""
430case "${nobanner}" in
431	yes )
432		banner_flag="-b"
433	;;
434esac
435
436NETPR="/usr/lib/lp/bin/netpr ${banner_flag} ${data_file_flag} \
437	-I ${request_id} -U ${user_name} \
438	-p ${printer} ${dest} -T \"${title}\"  \
439	${timeout}  ${protocol} ${controlfile} "
440LPTELL_OPTS="-l"        # netpr sends LaserWriter style messages back
441
442logger -p lpr.debug -t "tsol_netstandard: ${request_id}" "NETPR= ${NETPR}"
443logger -p lpr.debug -t "tsol_netstandard: ${request_id}" "filter : ${FILTER}"
444
445node=`uname -n`
446pid=$$
447tmpfile=${LPTMPDIR}/${node}.${pid}
448
449logger -p lpr.debug -t "tsol_netstandard: ${request_id}" "tmpfile : ${tmpfile}"
450
451#####
452#
453# Set up filter for banner page
454#
455#####
456banner_filter=
457case "${TERM}" in
458PS | PSR )
459	banner_filter=" | /usr/lib/lp/postscript/postprint "
460	LPTELL_OPTS="-l"
461	;;
462esac
463
464#####
465#
466# Build temporary file that is the banner page
467#
468#####
469PAD="#####${NL}"
470CR="\r"
471NL="${CR}\n"
472FF=
473
474small_banner() {
475        echo "${CR}\c"
476        echo "${PAD}\c"
477        echo "#####  User: ${user_name}${NL}\c"
478        if [ -n "${title}" ]
479        then
480                echo "##### Title: ${title}${NL}\c"
481        fi
482        echo "#####  Date: `LANG=C date '+%a %H:%M %h %d, %Y'`${NL}\c"
483        echo "#####   Job: ${request_id}${NL}\c"
484        echo "${PAD}\c"
485        if [ -n "${FF}" ]
486        then
487                echo "${CR}${FF}\c"
488        fi
489}
490
491#####
492#
493# Doing small banner as we don't know what printer is out there
494#
495#####
496banner=small_banner
497
498## Skip this for PS/PSR printers, since lp.tsol_separator handles the banners
499if [ "no" = "${nobanner}" -a "${TERM}" != "PSR" -a "${TERM}" != "PS" ]
500then
501	eval "${banner} ${banner_filter}" 2>&1 1>${LPTMPDIR}/small_banner.$$
502fi
503
504###########
505##
506## Surround the job by PostScript code to produce banner
507## and trailerpages and page headers and footers.
508##
509###########
510
511BANNER_EXIT_CODE=${LPTMPDIR}/banner.exit_code.$$
512echo 0 > ${BANNER_EXIT_CODE}
513TSOLSEPARATOR_LOG=${LPTMPDIR}/banner.errmsg.$$
514
515tsol_bannerize () {
516	TSOLSEPARATOR_OPTS="-e ${TSOLSEPARATOR_LOG}"
517
518	if [ "yes" = "${nolabels}" ]
519	then
520		TSOLSEPARATOR_OPTS="${TSOLSEPARATOR_OPTS} -l"
521	fi
522
523	if [ "yes" = "${nobanner}" ]
524	then
525		TSOLSEPARATOR_OPTS="${TSOLSEPARATOR_OPTS} -t /dev/null -b /dev/null"
526	fi
527
528	if [ "${TERM}" = "PSR" ]
529	then
530		TSOLSEPARATOR_OPTS="${TSOLSEPARATOR_OPTS} -r"
531	fi
532
533	# Get rid of the #, TAB and NL characters in the title
534	tsol_title=`echo $title`
535	tsol_title=`echo $tsol_title | sed 's/#//g'`
536
537	logger -p lpr.debug -t "tsol_netstandard: ${request_id}" \
538	    "banner command: ${LPTSOLSEPARATOR} ${TSOLSEPARATOR_OPTS} \
539	    ${printer} ${request_id} ${user_name} \"${tsol_title}\" ${file}"
540	${LPTSOLSEPARATOR} ${TSOLSEPARATOR_OPTS} ${printer} \
541	    ${request_id} ${user_name} "${tsol_title}" ${file}
542
543	echo $? > ${BANNER_EXIT_CODE}
544	true
545}
546
547bannerize=tsol_bannerize
548
549if [ "yes" = "${nobanner}" -a  "yes" = "${nolabels}" ]
550then
551	bannerize=cat
552fi
553
554if [ "${TERM}" != "PSR" -a "${TERM}" != "PS" ]
555then
556	bannerize=cat
557fi
558
559#####
560#
561# Print banner page before job unless PS or PSR.
562#
563#####
564
565if [ "no" = "${nobanner}" -a "${TERM}" != "PSR" -a "${TERM}" != "PS" ]
566then
567	(
568		eval ${NETPR} ${LPTMPDIR}/small_banner.$$ 2>&1
569		echo $? > ${LPTMPDIR}/pr_eexit_code.$$
570	) | ${LPTELL} ${LPTELL_OPTS} ${printer}
571
572	exit_code=`cat ${LPTMPDIR}/pr_eexit_code.$$`
573	logger -p lpr.debug -t "tsol_netstandard: ${request_id}"	\
574		"banner page exit code : ${exit_code}"
575
576fi
577
578i=1
579while [ $i -le $copies ]
580do
581        for file in ${files}
582        do
583                if [ -r "${file}" ]
584                then
585
586			if [ ! -z "${FILTER}" ]
587			then
588 				(
589					#####
590					# There is a filter, use it
591					#
592					# Put 0<${file} before the "eval" to keep
593					# clever users from giving a file name that
594					# evaluates as something to execute.
595					# Redirect stderr to stdout so LPTELL will
596					# get error messages from pipe.
597					#####
598					0<${file} $bannerize | eval ${FILTER} 2>&1 1>${tmpfile}
599					echo $? > ${LPTMPDIR}/pr_eexit_code.$$
600				) | ${LPTELL} ${LPTELL_OPTS} ${printer}
601
602				# if lp.tsol_separator had an error, send its logged
603				# error message to LPTELL.
604				banner_exit_code=`cat ${BANNER_EXIT_CODE}`
605				if [ -n "${banner_exit_code}" -a \
606					0 -ne "${banner_exit_code}" -a \
607					 -n "${LPTELL}" -a \
608					-r "${TSOLSEPARATOR_LOG}" ]
609				then
610					cat ${TSOLSEPARATOR_LOG} | ${LPTELL} ${printer}
611					echo 77 > ${LPTMPDIR}/pr_eexit_code
612				fi
613
614				exit_code=`cat ${LPTMPDIR}/pr_eexit_code.$$`
615				logger -p lpr.debug \
616				    -t "tsol_netstandard: ${request_id}" \
617					"filter exit_code : ${exit_code}"
618
619 			 	if [ -n "${exit_code}" ]
620 				then
621					if [ "${exit_code}" -eq 0 ]
622					then
623						printfile=${tmpfile}
624					else
625						####
626						# The filter did not succeed, so don't try to print
627						####
628							printfile=
629					fi
630				fi
631
632			else
633				printfile=${file}
634			fi
635
636			logger -p lpr.debug \
637			    -t "tsol_netstandard: ${request_id}" \
638				"printfile : ${printfile}"
639
640			#####
641			# Print the file
642			#####
643
644			if [ -r "${printfile}" ]
645			then
646				(
647					eval ${NETPR} ${printfile} 2>&1
648					echo $? > ${LPTMPDIR}/pr_eexit_code.$$
649				) | ${LPTELL} ${LPTELL_OPTS} ${printer}
650
651				exit_code=`cat ${LPTMPDIR}/pr_eexit_code.$$`
652				logger -p lpr.debug \
653				    -t "tsol_netstandard: ${request_id}" \
654					"netpr exit_code : ${exit_code}"
655
656#				if [ -f "${tmpfile}" ]
657#				then
658#					/bin/rm "${tmpfile}"
659#				fi
660
661				if [ -n "${exit_code}" ]
662				then
663					if [ "${exit_code}" -eq 0 ]
664					then
665						printone=yes
666					else
667						if [ "${exit_code}" -lt 128 ]
668						then
669							noprint=yes
670						else
671							retry=yes
672						fi
673					fi
674				fi
675
676
677			else
678
679				errmsg WARNING ${E_IP_BADFILE} \
680				"cannot read temporary file \"${printfile}\""\
681					"see if file still exists,
682			or consult your system administrator;
683			printing continues"
684
685			fi
686		else
687
688                        #####
689                        #
690                        # Don't complain about not being able to read
691                        # a file on second and subsequent copies, unless
692                        # we've not complained yet. This removes repeated
693                        # messages about the same file yet reduces the
694                        # chance that the user can remove a file and not
695                        # know that we had trouble finding it.
696                        #####
697
698                        if [ "${i}" -le 1 -o -z "${badfileyet}" ]
699                        then
700                                errmsg WARNING ${E_IP_BADFILE} \
701                                        "cannot read file \"${file}\"" \
702                                        "see if the file still exists and is readable,
703                or consult your system administrator;
704                printing continues"
705                                badfileyet=yes
706                        fi
707
708		fi
709
710# for file in ${files}
711	done
712	i=`expr $i + 1`
713done
714
715#####
716#
717# If printing in reverse order, print the banner page now
718# Skip this for TSOL, since lp.tsol_separator handles the banners
719#
720#####
721
722#
723# if [ "no" = "${nobanner}" -a "${TERM}" = "PSR" ]
724# then
725# (
726# 	eval ${NETPR} ${LPTMPDIR}/small_banner.$$ 2>&1
727#	echo $? > ${LPTMPDIR}/pr_eexit_code.$$
728# ) | ${LPTELL} ${LPTELL_OPTS} ${printer}
729# fi
730
731exit_code=`cat ${LPTMPDIR}/pr_eexit_code.$$`
732logger -p lpr.debug -t "tsol_netstandard: ${request_id}"     \
733                "banner page exit code : ${exit_code}"
734
735if [ -n "${printone}" -a -z "${retry}" -a -z "${noprint}" ]
736then
737       	exit_code=`expr 0`
738else
739        if [ -n "${retry}" -a -z "${printone}" -a -z "${noprint}" ]
740        then
741                exit_code=`expr 129`
742        else
743		exit_code=`expr 1`
744	fi
745fi
746
747logger -p lpr.debug -t "tsol_netstandard: ${request_id}" \
748	"FINAL exit_code : ${exit_code}"
749
750exit_clean ${exit_code}
751