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