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